aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/mm/pageattr-test.c
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2008-01-30 07:33:43 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:33:43 -0500
commitfa2d8369a1e0e5dd0fdaaba9d40ca5187f4cd20b (patch)
tree0cd340b81cea0b6f84993c005e7f6e0b4b5aa63a /arch/x86/mm/pageattr-test.c
parentf0646e43acb18f0e00b00085dc88bc3f403e7930 (diff)
x86: c_p_a(), add simple self test at boot
Since change_page_attr() is tricky code it is good to have some regression test code. This patch maps and unmaps some random pages in the direct mapping at boot and then dumps the state and does some simple sanity checks. Add it with a CONFIG option. Signed-off-by: Andi Kleen <ak@suse.de> Acked-by: Jan Beulich <jbeulich@novell.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/mm/pageattr-test.c')
-rw-r--r--arch/x86/mm/pageattr-test.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/arch/x86/mm/pageattr-test.c b/arch/x86/mm/pageattr-test.c
new file mode 100644
index 000000000000..4b22eb47f23a
--- /dev/null
+++ b/arch/x86/mm/pageattr-test.c
@@ -0,0 +1,233 @@
1/*
2 * self test for change_page_attr.
3 *
4 * Clears the global bit on random pages in the direct mapping, then reverts
5 * and compares page tables forwards and afterwards.
6 */
7
8#include <linux/mm.h>
9#include <linux/random.h>
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/bootmem.h>
13#include <asm/cacheflush.h>
14#include <asm/pgtable.h>
15#include <asm/kdebug.h>
16
17enum {
18 NTEST = 400,
19#ifdef CONFIG_X86_64
20 LOWEST_LEVEL = 4,
21 LPS = (1 << PMD_SHIFT),
22#elif defined(CONFIG_X86_PAE)
23 LOWEST_LEVEL = 3,
24 LPS = (1 << PMD_SHIFT),
25#else
26 LOWEST_LEVEL = 3, /* lookup_address lies here */
27 LPS = (1 << 22),
28#endif
29 GPS = (1<<30)
30};
31
32#ifdef CONFIG_X86_64
33#include <asm/proto.h>
34#define max_mapped end_pfn_map
35#else
36#define max_mapped max_low_pfn
37#endif
38
39struct split_state {
40 long lpg, gpg, spg, exec;
41 long min_exec, max_exec;
42};
43
44static __init int print_split(struct split_state *s)
45{
46 int printed = 0;
47 long i, expected, missed = 0;
48 int err = 0;
49
50 s->lpg = s->gpg = s->spg = s->exec = 0;
51 s->min_exec = ~0UL;
52 s->max_exec = 0;
53 for (i = 0; i < max_mapped; ) {
54 int level;
55 pte_t *pte;
56 unsigned long adr = (unsigned long)__va(i << PAGE_SHIFT);
57
58 pte = lookup_address(adr, &level);
59 if (!pte) {
60 if (!printed) {
61 dump_pagetable(adr);
62 printk("CPA %lx no pte level %d\n", adr, level);
63 printed = 1;
64 }
65 missed++;
66 i++;
67 continue;
68 }
69
70 if (level == 2 && sizeof(long) == 8) {
71 s->gpg++;
72 i += GPS/PAGE_SIZE;
73 } else if (level != LOWEST_LEVEL) {
74 if (!(pte_val(*pte) & _PAGE_PSE)) {
75 printk("%lx level %d but not PSE %Lx\n",
76 adr, level, (u64)pte_val(*pte));
77 err = 1;
78 }
79 s->lpg++;
80 i += LPS/PAGE_SIZE;
81 } else {
82 s->spg++;
83 i++;
84 }
85 if (!(pte_val(*pte) & _PAGE_NX)) {
86 s->exec++;
87 if (adr < s->min_exec)
88 s->min_exec = adr;
89 if (adr > s->max_exec)
90 s->max_exec = adr;
91 }
92 }
93 printk("CPA mapping 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n",
94 s->spg, s->lpg, s->gpg, s->exec,
95 s->min_exec != ~0UL ? s->min_exec : 0, s->max_exec, missed);
96 expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed;
97 if (expected != i) {
98 printk("CPA max_mapped %lu but expected %lu\n",
99 max_mapped, expected);
100 return 1;
101 }
102 return err;
103}
104
105static __init int state_same(struct split_state *a, struct split_state *b)
106{
107 return a->lpg == b->lpg && a->gpg == b->gpg && a->spg == b->spg &&
108 a->exec == b->exec;
109}
110
111static unsigned long addr[NTEST] __initdata;
112static unsigned len[NTEST] __initdata;
113
114/* Change the global bit on random pages in the direct mapping */
115static __init int exercise_pageattr(void)
116{
117 int i, k;
118 pte_t *pte, pte0;
119 int level;
120 int err;
121 struct split_state sa, sb, sc;
122 int failed = 0;
123 unsigned long *bm;
124
125 printk("CPA exercising pageattr\n");
126
127 bm = vmalloc((max_mapped + 7) / 8);
128 if (!bm) {
129 printk("CPA Cannot vmalloc bitmap\n");
130 return -ENOMEM;
131 }
132 memset(bm, 0, (max_mapped + 7) / 8);
133
134 failed += print_split(&sa);
135 srandom32(100);
136 for (i = 0; i < NTEST; i++) {
137 unsigned long pfn = random32() % max_mapped;
138 addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT);
139 len[i] = random32() % 100;
140 len[i] = min_t(unsigned long, len[i], max_mapped - pfn - 1);
141 if (len[i] == 0)
142 len[i] = 1;
143
144 pte = NULL;
145 pte0 = pfn_pte(0, __pgprot(0)); /* shut gcc up */
146 for (k = 0; k < len[i]; k++) {
147 pte = lookup_address(addr[i] + k*PAGE_SIZE, &level);
148 if (!pte || pgprot_val(pte_pgprot(*pte)) == 0) {
149 addr[i] = 0;
150 break;
151 }
152 if (k == 0)
153 pte0 = *pte;
154 else if (pgprot_val(pte_pgprot(*pte)) !=
155 pgprot_val(pte_pgprot(pte0))) {
156 len[i] = k;
157 break;
158 }
159 if (test_bit(pfn + k, bm)) {
160 len[i] = k;
161 break;
162 }
163 __set_bit(pfn + k, bm);
164 }
165 if (!addr[i] || !pte || !k) {
166 addr[i] = 0;
167 continue;
168 }
169
170 err = change_page_attr(virt_to_page(addr[i]), len[i],
171 pte_pgprot(pte_clrhuge(pte_clrglobal(pte0))));
172 if (err < 0) {
173 printk("CPA %d failed %d\n", i, err);
174 failed++;
175 }
176
177 pte = lookup_address(addr[i], &level);
178 if (!pte || pte_global(*pte) || pte_huge(*pte)) {
179 printk("CPA %lx: bad pte %Lx\n", addr[i],
180 pte ? (u64)pte_val(*pte) : 0ULL);
181 failed++;
182 }
183 if (level != LOWEST_LEVEL) {
184 printk("CPA %lx: unexpected level %d\n", addr[i],
185 level);
186 failed++;
187 }
188
189 }
190 vfree(bm);
191 global_flush_tlb();
192
193 failed += print_split(&sb);
194
195 printk("CPA reverting everything\n");
196 for (i = 0; i < NTEST; i++) {
197 if (!addr[i])
198 continue;
199 pte = lookup_address(addr[i], &level);
200 if (!pte) {
201 printk("CPA lookup of %lx failed\n", addr[i]);
202 failed++;
203 continue;
204 }
205 err = change_page_attr(virt_to_page(addr[i]), len[i],
206 pte_pgprot(pte_mkglobal(*pte)));
207 if (err < 0) {
208 printk("CPA reverting failed: %d\n", err);
209 failed++;
210 }
211 pte = lookup_address(addr[i], &level);
212 if (!pte || !pte_global(*pte)) {
213 printk("CPA %lx: bad pte after revert %Lx\n", addr[i],
214 pte ? (u64)pte_val(*pte) : 0ULL);
215 failed++;
216 }
217
218 }
219 global_flush_tlb();
220
221 failed += print_split(&sc);
222 if (!state_same(&sa, &sc))
223 failed++;
224
225 if (failed)
226 printk("CPA selftests NOT PASSED. Please report.\n");
227 else
228 printk("CPA selftests PASSED\n");
229
230 return 0;
231}
232
233module_init(exercise_pageattr);