[PATCH 10/11] x86/xip: resolve alternative instructions at build

From: Jim Kukunas
Date: Mon Mar 23 2015 - 03:55:54 EST


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 <james.t.kukunas@xxxxxxxxxxxxxxx>
---
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 <james.t.kukunas@xxxxxxxxxxxxxxx>
+ *
+ * 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 <asm/asm.h>
#include <asm/ptrace.h>

+#ifdef CONFIG_XIP_KERNEL
+#include <asm/alternative-xip.h>
+#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 <asm/io.h>
#include <asm/fixmap.h>

+#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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/