summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyril Chemparathy <cyril@ti.com>2012-09-21 11:56:00 -0400
committerTushar Behera <tushar.behera@linaro.org>2013-01-22 11:41:16 +0530
commit64d23c35d49ca87ceeaddca0e5b83c3a768dbc72 (patch)
tree1b1bf5b3f4fec368851177b9b7f098d56340564a
parentdf73886ed4aa86fb68cfe0dc8b448f2736d018f4 (diff)
downloadlinux-topics-64d23c35d49ca87ceeaddca0e5b83c3a768dbc72.tar.gz
ARM: add self test for runtime patch mechanism
This patch adds basic sanity tests to ensure that the instruction patching results in valid instruction encodings. This is done by verifying the output of the patch process against a vector of assembler generated instructions at init time. Signed-off-by: Cyril Chemparathy <cyril@ti.com>
-rw-r--r--arch/arm/Kconfig12
-rw-r--r--arch/arm/kernel/runtime-patch.c75
2 files changed, 87 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4e4abbfd929..10ba697f8f3 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -219,6 +219,18 @@ config NEED_MACH_GPIO_H
definitions for this platform. The need for mach/gpio.h should
be avoided when possible.
+config ARM_RUNTIME_PATCH_TEST
+ bool "Self test runtime patching mechanism" if ARM_RUNTIME_PATCH
+ default y
+ help
+ Select this to enable init time self checking for the runtime kernel
+ patching mechanism. This enables an ISA specific set of tests that
+ ensure that the instructions generated by the patch process are
+ consistent with those generated by the assembler at compile time.
+
+ Only disable this option if you need to shrink the kernel to the
+ minimal size.
+
config NEED_MACH_IO_H
bool
help
diff --git a/arch/arm/kernel/runtime-patch.c b/arch/arm/kernel/runtime-patch.c
index 28a6367fac0..0be9ef314a3 100644
--- a/arch/arm/kernel/runtime-patch.c
+++ b/arch/arm/kernel/runtime-patch.c
@@ -168,6 +168,78 @@ static int apply_patch_imm8(const struct patch_info *p)
return 0;
}
+#ifdef CONFIG_ARM_RUNTIME_PATCH_TEST
+
+struct patch_test_imm8 {
+ u16 imm;
+ u16 shift;
+ u32 insn;
+};
+
+static void __init __used __naked __patch_test_code_imm8(void)
+{
+ __asm__ __volatile__ (
+
+ /* a single test case */
+ " .macro test_one, imm, sft\n"
+ " .hword \\imm\n"
+ " .hword \\sft\n"
+ " add r1, r2, #(\\imm << \\sft)\n"
+ " .endm\n"
+
+ /* a sequence of tests at 'inc' increments of shift */
+ " .macro test_seq, imm, sft, max, inc\n"
+ " test_one \\imm, \\sft\n"
+ " .if \\sft < \\max\n"
+ " test_seq \\imm, (\\sft + \\inc), \\max, \\inc\n"
+ " .endif\n"
+ " .endm\n"
+
+ /* an empty record to mark the end */
+ " .macro test_end\n"
+ " .hword 0, 0\n"
+ " .word 0\n"
+ " .endm\n"
+
+ /* finally generate the test sequences */
+ " test_seq 0x41, 0, 24, 1\n"
+ " test_seq 0x81, 0, 24, 2\n"
+ " test_end\n"
+ : : : "r1", "r2", "cc");
+}
+
+static void __init test_patch_imm8(void)
+{
+ u32 test_code_addr = (u32)(&__patch_test_code_imm8);
+ struct patch_test_imm8 *test = (void *)(test_code_addr & ~1);
+ u32 ninsn, insn, patched_insn;
+ int i, err;
+
+ insn = test[0].insn;
+ for (i = 0; test[i].insn; i++) {
+ err = do_patch_imm8(insn, test[i].imm << test[i].shift, &ninsn);
+ __patch_text(&patched_insn, ninsn);
+
+ if (err) {
+ pr_err("rtpatch imm8: failed at imm %x, shift %d\n",
+ test[i].imm, test[i].shift);
+ } else if (patched_insn != test[i].insn) {
+ pr_err("rtpatch imm8: failed, need %x got %x\n",
+ test[i].insn, patched_insn);
+ } else {
+ pr_debug("rtpatch imm8: imm %x, shift %d, %x -> %x\n",
+ test[i].imm, test[i].shift, insn,
+ patched_insn);
+ }
+ }
+}
+
+static void __init runtime_patch_test(void)
+{
+ test_patch_imm8();
+}
+#endif
+
int runtime_patch(const void *table, unsigned size)
{
const struct patch_info *p = table, *end = (table + size);
@@ -189,5 +261,8 @@ void __init runtime_patch_kernel(void)
const void *start = &__runtime_patch_table_begin;
const void *end = &__runtime_patch_table_end;
+#ifdef CONFIG_ARM_RUNTIME_PATCH_TEST
+ runtime_patch_test();
+#endif
BUG_ON(runtime_patch(start, end - start));
}