From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752262AbbCWHzv (ORCPT ); Mon, 23 Mar 2015 03:55:51 -0400 Received: from mga11.intel.com ([192.55.52.93]:59190 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752137AbbCWHzL (ORCPT ); Mon, 23 Mar 2015 03:55:11 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.11,450,1422950400"; d="scan'208";a="544809151" From: Jim Kukunas To: Linux Kernel , tom.zanussi@linux.intel.com Cc: Arjan van de Ven , "H. Peter Anvin" , tglx@linutronix.de, mingo@redhat.com, x86@kernel.org Subject: [PATCH 10/11] x86/xip: resolve alternative instructions at build Date: Mon, 23 Mar 2015 00:46:39 -0700 Message-Id: <1427096800-30452-11-git-send-email-james.t.kukunas@linux.intel.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1427096800-30452-1-git-send-email-james.t.kukunas@linux.intel.com> References: <1427096800-30452-1-git-send-email-james.t.kukunas@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Since the .text section can't be updated at run-time, remove the .alternatives sections and update the .text at build time. To pick the proper instructions, Kconfig options are exposed for each X86_FEATURE that needed to be resolved. Each X86_FEATURE gets a corresponding CONFIG_XIP_ENABLE_X86_FEATURE_ option. Based on whether this option is set, a resolver macro is setup to either generate that instruction, or the fallback. The resolver needs to be defined for each FEATURE, and the proper one is chosen via preprocessor string pasting. This approach is horrific and ugly. A better approach might be to add an additional build step that, after generating the vmlinux file, goes through the alternatives section and performs the fixups on the file. At the very least, a script like mkcapflags.sh could generate the resolver functions automatically. But since it's adding Kconfig options, it would need to run unconditionally before any of the config related Makefile targets. Signed-off-by: Jim Kukunas --- arch/x86/Kconfig | 45 +++++++++ arch/x86/include/asm/alternative-xip.h | 161 +++++++++++++++++++++++++++++++++ arch/x86/include/asm/alternative.h | 5 + arch/x86/kernel/alternative.c | 7 ++ arch/x86/kernel/cpu/bugs.c | 2 + arch/x86/kernel/setup.c | 2 + arch/x86/kernel/smpboot.c | 2 + arch/x86/kernel/vmlinux.lds.S | 2 + arch/x86/vdso/vma.c | 2 + 9 files changed, 228 insertions(+) create mode 100644 arch/x86/include/asm/alternative-xip.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f5fa02c..dff781d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -313,6 +313,51 @@ config XIP_BASE help The physical address for the beginning of the vmlinux file. +menu "XIP Alternative Instructions" + depends on XIP_KERNEL + + config XIP_ENABLE_X86_FEATURE_POPCNT + bool "Enable POPCNT alternative instructions" + default n + + config XIP_ENABLE_X86_BUG_11AP + bool "Enable 11AP alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_XMM2 + bool "Enable XMM2 alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_MFENCE_RDTSC + bool "Enable MFENCE_RDTSC alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_LFENCE_RDTSC + bool "Enable LFENCE_RDTSC alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_3DNOW + bool "Enable 3DNOW alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_XMM + bool "Enabled XMM alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_CLFLUSHOPT + bool "Enable CLFLUSHOPT alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_XSAVEOPT + bool "Enable XSAVEOPT alternative instructions" + default n + + config XIP_ENABLE_X86_FEATURE_XSAVES + bool "Enable XSAVES alternative instructions" + default n + +endmenu + config SMP bool "Symmetric multi-processing support" ---help--- diff --git a/arch/x86/include/asm/alternative-xip.h b/arch/x86/include/asm/alternative-xip.h new file mode 100644 index 0000000..84f544e --- /dev/null +++ b/arch/x86/include/asm/alternative-xip.h @@ -0,0 +1,161 @@ +#ifndef _ASM_X86_ALTERNATIVE_XIP_H +#define _ASM_X86_ALTERNATIVE_XIP_H + +/* + * Alternative instruction fixup for XIP + * + * Copyright (C) 2014 Intel Corporation + * Author: Jim Kukunas + * + * Since the kernel text is executing from storage and is + * read-only, we can't update the opcodes in-flight. Instead, + * resolve the alternatives at build time through preprocessor + * (ab)use. + */ + +#ifdef CONFIG_SMP +#define LOCK_PREFIX "\n\tlock; " +#else +#define LOCK_PREFIX "" +#endif + +extern int poke_int3_handler(struct pt_regs *regs); + +/* TODO hook up to something like mkcapflags.sh */ +/* Unfortunately, each X86_FEATURE will need a corresponding define like this */ +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_POPCNT +#define RESOLVE_X86_FEATURE_POPCNT(old, new) new +#define RESOLVE_2_X86_FEATURE_POPCNT(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_POPCNT(old, new) old +#define RESOLVE_2_X86_FEATURE_POPCNT(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_BUG_11AP +#define RESOLVE_X86_BUG_11AP(old, new) new +#define RESOLVE_2_X86_BUG_11AP(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_BUG_11AP(old, new) old +#define RESOLVE_2_X86_BUG_11AP(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_XMM2 +#define RESOLVE_X86_FEATURE_XMM2(old, new) new +#define RESOLVE_2_X86_FEATURE_XMM2(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_XMM2(old, new) old +#define RESOLVE_2_X86_FEATURE_XMM2(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_MFENCE_RDTSC +#define RESOLVE_X86_FEATURE_MFENCE_RDTSC(old, new) new +#define RESOLVE_2_X86_FEATURE_MFENCE_RDTSC(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_MFENCE_RDTSC(old, new) old +#define RESOLVE_2_X86_FEATURE_MFENCE_RDTSC(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_LFENCE_RDTSC +#define RESOLVE_X86_FEATURE_LFENCE_RDTSC(old, new) new +#define RESOLVE_2_X86_FEATURE_LFENCE_RDTSC(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_LFENCE_RDTSC(old, new) old +#define RESOLVE_2_X86_FEATURE_LFENCE_RDTSC(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_3DNOW +#define RESOLVE_X86_FEATURE_3DNOW(old, new) new +#define RESOLVE_2_X86_FEATURE_3DNOW(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_3DNOW(old, new) old +#define RESOLVE_2_X86_FEATURE_3DNOW(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_XMM +#define RESOLVE_X86_FEATURE_XMM(old, new) new +#define RESOLVE_2_X86_FEATURE_XMM(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_XMM(old, new) old +#define RESOLVE_2_X86_FEATURE_XMM(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_CLFLUSHOPT +#define RESOLVE_X86_FEATURE_CLFLUSHOPT(old, new) new +#define RESOLVE_2_X86_FEATURE_CLFLUSHOPT(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_CLFLUSHOPT(old, new) old +#define RESOLVE_2_X86_FEATURE_CLFLUSHOPT(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_XSAVEOPT +#define RESOLVE_X86_FEATURE_XSAVEOPT(old, new) new +#define RESOLVE_2_X86_FEATURE_XSAVEOPT(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_XSAVEOPT(old, new) old +#define RESOLVE_2_X86_FEATURE_XSAVEOPT(old, new1, resovle1, new2) \ + resolve1(old, new1) +#endif + +#ifdef CONFIG_XIP_ENABLE_X86_FEATURE_XSAVES +#define RESOLVE_X86_FEATURE_XSAVES(old, new) new +#define RESOLVE_2_X86_FEATURE_XSAVES(old, new1, resolve1, new2) new2 +#else +#define RESOLVE_X86_FEATURE_XSAVES(old, new) old +#define RESOLVE_2_X86_FEATURE_XSAVES(old, new1, resolve1, new2) \ + resolve1(old, new1) +#endif + +#define ASM_OUTPUT2(a...) a +#define ASM_NO_INPUT_CLOBBER(clbr...) "i" (0) : clbr + +#define __ALTERNATIVE(oldinstr, newinstr, resolve) \ + resolve(oldinstr, newinstr) + +#define __ALTERNATIVE_2(oldinstr, instr1, resolve1, instr2, resolve2) \ + resolve2(oldinstr, instr1, resolve1, instr2) + +#define ALTERNATIVE(oldinstr, newinstr, feature) \ + RESOLVE_##feature(oldinstr, newinstr) + +#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ + RESOLVE_2_##feature2(oldinstr, newinstr1, feature1, newinstr2) + +#define alternative(oldinstr, newinstr, feature) \ + asm volatile(__ALTERNATIVE(oldinstr, newinstr, RESOLVE_##feature) \ + : : : "memory") + +#define alternative_input(oldinstr, newinstr, feature, input...) \ + asm volatile(__ALTERNATIVE(oldinstr, newinstr, RESOLVE_##feature) \ + : : "i" (0), input) + +#define alternative_input_2(oldinstr, newinstr1, feature1, newinstr2, \ + feature2, input...) \ + asm volatile( \ + __ALTERNATIVE_2(oldinstr, newinstr1, RESOLVE_##feature1,\ + newinstr2, RESOLVE_2_ ##feature2) \ + : : "i" (0), ## input) + +#define alternative_io(oldinstr, newinstr, feature, output, input...) \ + asm volatile(__ALTERNATIVE(oldinstr, newinstr, RESOLVE_##feature) \ + : output : "i" (0), ##input) + +#define alternative_call(oldfunc, newfunc, feature, output, input...) \ + asm volatile(__ALTERNATIVE("call %P[[old]", "call %P[new]", \ + RESOLVE_##feature) : output : [old] "i" (oldfunc), \ + [new] "i" (newfunc), input) + +#define alternative_call_2(oldfunc, newfunc1, feature1, newfunc2, feature2, \ + output, intput...) \ + asm volatile(ALTERNATIVE_2("call %P[old]", "call %P[new1]", feature1, \ + "call %P[new2]", feature2) \ + : output : [old] "i" (oldfunc), [new1] "i" (newfunc1), \ + [new2] "i" (newfunc2), input) +#endif diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h index 473bdbe..559a2cd 100644 --- a/arch/x86/include/asm/alternative.h +++ b/arch/x86/include/asm/alternative.h @@ -7,6 +7,10 @@ #include #include +#ifdef CONFIG_XIP_KERNEL +#include +#else + /* * Alternative inline assembly for SMP. * @@ -242,4 +246,5 @@ extern void *text_poke(void *addr, const void *opcode, size_t len); extern int poke_int3_handler(struct pt_regs *regs); extern void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler); +#endif /* !CONFIG_XIP_KERNEL */ #endif /* _ASM_X86_ALTERNATIVE_H */ diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 703130f..c337552 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -21,6 +21,8 @@ #include #include +#ifndef CONFIG_XIP_KERNEL + #define MAX_PATCH_LEN (255-1) static int __initdata_or_module debug_alternative; @@ -590,6 +592,8 @@ static void do_sync_core(void *info) sync_core(); } +#endif /* !CONFIG_XIP_KERNEL */ + static bool bp_patching_in_progress; static void *bp_int3_handler, *bp_int3_addr; @@ -611,6 +615,8 @@ int poke_int3_handler(struct pt_regs *regs) } +#ifndef CONFIG_XIP_KERNEL + /** * text_poke_bp() -- update instructions on live kernel on SMP * @addr: address to patch @@ -674,4 +680,5 @@ void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler) return addr; } +#endif /* !CONFIG_XIP_KERNEL */ diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 0344534..4c184a5 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -83,7 +83,9 @@ void __init check_bugs(void) init_utsname()->machine[1] = '0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86); +#ifndef CONFIG_XIP_KERNEL alternative_instructions(); +#endif /* * kernel_fpu_begin/end() in check_fpu() relies on the patched diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index f044453..ed541b9 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1275,7 +1275,9 @@ void __init setup_arch(char **cmdline_p) mcheck_init(); +#ifndef CONFIG_XIP_KERNEL arch_init_ideal_nops(); +#endif register_refined_jiffies(CLOCK_TICK_RATE); diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index febc6aa..bebd323 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -797,7 +797,9 @@ static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle) unsigned long timeout; /* Just in case we booted with a single CPU. */ +#ifndef CONFIG_XIP_KERNEL alternatives_enable_smp(); +#endif idle->thread.sp = (unsigned long) (((struct pt_regs *) (THREAD_SIZE + task_stack_page(idle))) - 1); diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 59a9edb..fc66ebf 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -228,6 +228,7 @@ SECTIONS } #endif +#ifndef CONFIG_XIP_KERNEL /* * start address and size of operations which during runtime * can be patched with virtualization friendly instructions or @@ -261,6 +262,7 @@ SECTIONS .altinstr_replacement : AT(ADDR(.altinstr_replacement) - LOAD_OFFSET) { *(.altinstr_replacement) } +#endif /* * struct iommu_table_entry entries are injected in this section. diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c index 1c9f750..ba336ef 100644 --- a/arch/x86/vdso/vma.c +++ b/arch/x86/vdso/vma.c @@ -34,9 +34,11 @@ void __init init_vdso_image(const struct vdso_image *image) image->text_mapping.pages[i] = virt_to_page(image->data + i*PAGE_SIZE); +#ifndef CONFIG_XIP_KERNEL apply_alternatives((struct alt_instr *)(image->data + image->alt), (struct alt_instr *)(image->data + image->alt + image->alt_len)); +#endif } struct linux_binprm; -- 2.1.0