diff options
author | Vegard Nossum <vegard.nossum@gmail.com> | 2009-02-27 05:35:55 -0500 |
---|---|---|
committer | Vegard Nossum <vegard.nossum@gmail.com> | 2009-06-15 09:49:22 -0400 |
commit | ac61a7579625ddfca3b2e0aa298879a94d15884d (patch) | |
tree | adb11c2513bfdb4825e1ac213f8116d7280ac4a9 /arch/x86 | |
parent | eb63657e1314ae4af5e19a61db8dc1b6e935775a (diff) |
kmemcheck: add opcode self-testing at boot
We've had some troubles in the past with weird instructions. This
patch adds a self-test framework which can be used to verify that
a certain set of opcodes are decoded correctly. Of course, the
opcodes which are not tested can still give the wrong results.
In short, this is just a safeguard to catch unintentional changes
in the opcode decoder. It does not mean that errors can't still
occur!
[rebased for mainline inclusion]
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/mm/kmemcheck/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/mm/kmemcheck/kmemcheck.c | 25 | ||||
-rw-r--r-- | arch/x86/mm/kmemcheck/selftest.c | 69 | ||||
-rw-r--r-- | arch/x86/mm/kmemcheck/selftest.h | 6 |
4 files changed, 85 insertions, 17 deletions
diff --git a/arch/x86/mm/kmemcheck/Makefile b/arch/x86/mm/kmemcheck/Makefile index 4666b7a778be..520b3bce4095 100644 --- a/arch/x86/mm/kmemcheck/Makefile +++ b/arch/x86/mm/kmemcheck/Makefile | |||
@@ -1 +1 @@ | |||
obj-y := error.o kmemcheck.o opcode.o pte.o shadow.o | obj-y := error.o kmemcheck.o opcode.o pte.o selftest.o shadow.o | ||
diff --git a/arch/x86/mm/kmemcheck/kmemcheck.c b/arch/x86/mm/kmemcheck/kmemcheck.c index 6931e5115bcd..2c55ed098654 100644 --- a/arch/x86/mm/kmemcheck/kmemcheck.c +++ b/arch/x86/mm/kmemcheck/kmemcheck.c | |||
@@ -29,8 +29,10 @@ | |||
29 | #include "error.h" | 29 | #include "error.h" |
30 | #include "opcode.h" | 30 | #include "opcode.h" |
31 | #include "pte.h" | 31 | #include "pte.h" |
32 | #include "selftest.h" | ||
32 | #include "shadow.h" | 33 | #include "shadow.h" |
33 | 34 | ||
35 | |||
34 | #ifdef CONFIG_KMEMCHECK_DISABLED_BY_DEFAULT | 36 | #ifdef CONFIG_KMEMCHECK_DISABLED_BY_DEFAULT |
35 | # define KMEMCHECK_ENABLED 0 | 37 | # define KMEMCHECK_ENABLED 0 |
36 | #endif | 38 | #endif |
@@ -47,8 +49,6 @@ int kmemcheck_enabled = KMEMCHECK_ENABLED; | |||
47 | 49 | ||
48 | int __init kmemcheck_init(void) | 50 | int __init kmemcheck_init(void) |
49 | { | 51 | { |
50 | printk(KERN_INFO "kmemcheck: Initialized\n"); | ||
51 | |||
52 | #ifdef CONFIG_SMP | 52 | #ifdef CONFIG_SMP |
53 | /* | 53 | /* |
54 | * Limit SMP to use a single CPU. We rely on the fact that this code | 54 | * Limit SMP to use a single CPU. We rely on the fact that this code |
@@ -61,25 +61,18 @@ int __init kmemcheck_init(void) | |||
61 | } | 61 | } |
62 | #endif | 62 | #endif |
63 | 63 | ||
64 | if (!kmemcheck_selftest()) { | ||
65 | printk(KERN_INFO "kmemcheck: self-tests failed; disabling\n"); | ||
66 | kmemcheck_enabled = 0; | ||
67 | return -EINVAL; | ||
68 | } | ||
69 | |||
70 | printk(KERN_INFO "kmemcheck: Initialized\n"); | ||
64 | return 0; | 71 | return 0; |
65 | } | 72 | } |
66 | 73 | ||
67 | early_initcall(kmemcheck_init); | 74 | early_initcall(kmemcheck_init); |
68 | 75 | ||
69 | #ifdef CONFIG_KMEMCHECK_DISABLED_BY_DEFAULT | ||
70 | # define KMEMCHECK_ENABLED 0 | ||
71 | #endif | ||
72 | |||
73 | #ifdef CONFIG_KMEMCHECK_ENABLED_BY_DEFAULT | ||
74 | # define KMEMCHECK_ENABLED 1 | ||
75 | #endif | ||
76 | |||
77 | #ifdef CONFIG_KMEMCHECK_ONESHOT_BY_DEFAULT | ||
78 | # define KMEMCHECK_ENABLED 2 | ||
79 | #endif | ||
80 | |||
81 | int kmemcheck_enabled = KMEMCHECK_ENABLED; | ||
82 | |||
83 | /* | 76 | /* |
84 | * We need to parse the kmemcheck= option before any memory is allocated. | 77 | * We need to parse the kmemcheck= option before any memory is allocated. |
85 | */ | 78 | */ |
diff --git a/arch/x86/mm/kmemcheck/selftest.c b/arch/x86/mm/kmemcheck/selftest.c new file mode 100644 index 000000000000..036efbea8b28 --- /dev/null +++ b/arch/x86/mm/kmemcheck/selftest.c | |||
@@ -0,0 +1,69 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | |||
3 | #include "opcode.h" | ||
4 | #include "selftest.h" | ||
5 | |||
6 | struct selftest_opcode { | ||
7 | unsigned int expected_size; | ||
8 | const uint8_t *insn; | ||
9 | const char *desc; | ||
10 | }; | ||
11 | |||
12 | static const struct selftest_opcode selftest_opcodes[] = { | ||
13 | /* REP MOVS */ | ||
14 | {1, "\xf3\xa4", "rep movsb <mem8>, <mem8>"}, | ||
15 | {4, "\xf3\xa5", "rep movsl <mem32>, <mem32>"}, | ||
16 | |||
17 | /* MOVZX / MOVZXD */ | ||
18 | {1, "\x66\x0f\xb6\x51\xf8", "movzwq <mem8>, <reg16>"}, | ||
19 | {1, "\x0f\xb6\x51\xf8", "movzwq <mem8>, <reg32>"}, | ||
20 | |||
21 | /* MOVSX / MOVSXD */ | ||
22 | {1, "\x66\x0f\xbe\x51\xf8", "movswq <mem8>, <reg16>"}, | ||
23 | {1, "\x0f\xbe\x51\xf8", "movswq <mem8>, <reg32>"}, | ||
24 | |||
25 | #ifdef CONFIG_X86_64 | ||
26 | /* MOVZX / MOVZXD */ | ||
27 | {1, "\x49\x0f\xb6\x51\xf8", "movzbq <mem8>, <reg64>"}, | ||
28 | {2, "\x49\x0f\xb7\x51\xf8", "movzbq <mem16>, <reg64>"}, | ||
29 | |||
30 | /* MOVSX / MOVSXD */ | ||
31 | {1, "\x49\x0f\xbe\x51\xf8", "movsbq <mem8>, <reg64>"}, | ||
32 | {2, "\x49\x0f\xbf\x51\xf8", "movsbq <mem16>, <reg64>"}, | ||
33 | {4, "\x49\x63\x51\xf8", "movslq <mem32>, <reg64>"}, | ||
34 | #endif | ||
35 | }; | ||
36 | |||
37 | static bool selftest_opcode_one(const struct selftest_opcode *op) | ||
38 | { | ||
39 | unsigned size; | ||
40 | |||
41 | kmemcheck_opcode_decode(op->insn, &size); | ||
42 | |||
43 | if (size == op->expected_size) | ||
44 | return true; | ||
45 | |||
46 | printk(KERN_WARNING "kmemcheck: opcode %s: expected size %d, got %d\n", | ||
47 | op->desc, op->expected_size, size); | ||
48 | return false; | ||
49 | } | ||
50 | |||
51 | static bool selftest_opcodes_all(void) | ||
52 | { | ||
53 | bool pass = true; | ||
54 | unsigned int i; | ||
55 | |||
56 | for (i = 0; i < ARRAY_SIZE(selftest_opcodes); ++i) | ||
57 | pass = pass && selftest_opcode_one(&selftest_opcodes[i]); | ||
58 | |||
59 | return pass; | ||
60 | } | ||
61 | |||
62 | bool kmemcheck_selftest(void) | ||
63 | { | ||
64 | bool pass = true; | ||
65 | |||
66 | pass = pass && selftest_opcodes_all(); | ||
67 | |||
68 | return pass; | ||
69 | } | ||
diff --git a/arch/x86/mm/kmemcheck/selftest.h b/arch/x86/mm/kmemcheck/selftest.h new file mode 100644 index 000000000000..8fed4fe11f95 --- /dev/null +++ b/arch/x86/mm/kmemcheck/selftest.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef ARCH_X86_MM_KMEMCHECK_SELFTEST_H | ||
2 | #define ARCH_X86_MM_KMEMCHECK_SELFTEST_H | ||
3 | |||
4 | bool kmemcheck_selftest(void); | ||
5 | |||
6 | #endif | ||