GNU Linux-libre 4.14.266-gnu1
[releases.git] / arch / x86 / mm / pageattr-test.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * self test for change_page_attr.
4  *
5  * Clears the a test pte bit on random pages in the direct mapping,
6  * then reverts and compares page tables forwards and afterwards.
7  */
8 #include <linux/bootmem.h>
9 #include <linux/kthread.h>
10 #include <linux/random.h>
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/mm.h>
14 #include <linux/vmalloc.h>
15
16 #include <asm/cacheflush.h>
17 #include <asm/pgtable.h>
18 #include <asm/kdebug.h>
19
20 /*
21  * Only print the results of the first pass:
22  */
23 static __read_mostly int print = 1;
24
25 enum {
26         NTEST                   = 400,
27 #ifdef CONFIG_X86_64
28         LPS                     = (1 << PMD_SHIFT),
29 #elif defined(CONFIG_X86_PAE)
30         LPS                     = (1 << PMD_SHIFT),
31 #else
32         LPS                     = (1 << 22),
33 #endif
34         GPS                     = (1<<30)
35 };
36
37 #define PAGE_CPA_TEST   __pgprot(_PAGE_CPA_TEST)
38
39 static int pte_testbit(pte_t pte)
40 {
41         return pte_flags(pte) & _PAGE_SOFTW1;
42 }
43
44 struct split_state {
45         long lpg, gpg, spg, exec;
46         long min_exec, max_exec;
47 };
48
49 static int print_split(struct split_state *s)
50 {
51         long i, expected, missed = 0;
52         int err = 0;
53
54         s->lpg = s->gpg = s->spg = s->exec = 0;
55         s->min_exec = ~0UL;
56         s->max_exec = 0;
57         for (i = 0; i < max_pfn_mapped; ) {
58                 unsigned long addr = (unsigned long)__va(i << PAGE_SHIFT);
59                 unsigned int level;
60                 pte_t *pte;
61
62                 pte = lookup_address(addr, &level);
63                 if (!pte) {
64                         missed++;
65                         i++;
66                         continue;
67                 }
68
69                 if (level == PG_LEVEL_1G && sizeof(long) == 8) {
70                         s->gpg++;
71                         i += GPS/PAGE_SIZE;
72                 } else if (level == PG_LEVEL_2M) {
73                         if ((pte_val(*pte) & _PAGE_PRESENT) && !(pte_val(*pte) & _PAGE_PSE)) {
74                                 printk(KERN_ERR
75                                         "%lx level %d but not PSE %Lx\n",
76                                         addr, 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 (addr < s->min_exec)
88                                 s->min_exec = addr;
89                         if (addr > s->max_exec)
90                                 s->max_exec = addr;
91                 }
92         }
93         if (print) {
94                 printk(KERN_INFO
95                         " 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n",
96                         s->spg, s->lpg, s->gpg, s->exec,
97                         s->min_exec != ~0UL ? s->min_exec : 0,
98                         s->max_exec, missed);
99         }
100
101         expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed;
102         if (expected != i) {
103                 printk(KERN_ERR "CPA max_pfn_mapped %lu but expected %lu\n",
104                         max_pfn_mapped, expected);
105                 return 1;
106         }
107         return err;
108 }
109
110 static unsigned long addr[NTEST];
111 static unsigned int len[NTEST];
112
113 /* Change the global bit on random pages in the direct mapping */
114 static int pageattr_test(void)
115 {
116         struct split_state sa, sb, sc;
117         unsigned long *bm;
118         pte_t *pte, pte0;
119         int failed = 0;
120         unsigned int level;
121         int i, k;
122         int err;
123         unsigned long test_addr;
124
125         if (print)
126                 printk(KERN_INFO "CPA self-test:\n");
127
128         bm = vzalloc((max_pfn_mapped + 7) / 8);
129         if (!bm) {
130                 printk(KERN_ERR "CPA Cannot vmalloc bitmap\n");
131                 return -ENOMEM;
132         }
133
134         failed += print_split(&sa);
135
136         for (i = 0; i < NTEST; i++) {
137                 unsigned long pfn = prandom_u32() % max_pfn_mapped;
138
139                 addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT);
140                 len[i] = prandom_u32() % 100;
141                 len[i] = min_t(unsigned long, len[i], max_pfn_mapped - pfn - 1);
142
143                 if (len[i] == 0)
144                         len[i] = 1;
145
146                 pte = NULL;
147                 pte0 = pfn_pte(0, __pgprot(0)); /* shut gcc up */
148
149                 for (k = 0; k < len[i]; k++) {
150                         pte = lookup_address(addr[i] + k*PAGE_SIZE, &level);
151                         if (!pte || pgprot_val(pte_pgprot(*pte)) == 0 ||
152                             !(pte_val(*pte) & _PAGE_PRESENT)) {
153                                 addr[i] = 0;
154                                 break;
155                         }
156                         if (k == 0) {
157                                 pte0 = *pte;
158                         } else {
159                                 if (pgprot_val(pte_pgprot(*pte)) !=
160                                         pgprot_val(pte_pgprot(pte0))) {
161                                         len[i] = k;
162                                         break;
163                                 }
164                         }
165                         if (test_bit(pfn + k, bm)) {
166                                 len[i] = k;
167                                 break;
168                         }
169                         __set_bit(pfn + k, bm);
170                 }
171                 if (!addr[i] || !pte || !k) {
172                         addr[i] = 0;
173                         continue;
174                 }
175
176                 test_addr = addr[i];
177                 err = change_page_attr_set(&test_addr, len[i], PAGE_CPA_TEST, 0);
178                 if (err < 0) {
179                         printk(KERN_ERR "CPA %d failed %d\n", i, err);
180                         failed++;
181                 }
182
183                 pte = lookup_address(addr[i], &level);
184                 if (!pte || !pte_testbit(*pte) || pte_huge(*pte)) {
185                         printk(KERN_ERR "CPA %lx: bad pte %Lx\n", addr[i],
186                                 pte ? (u64)pte_val(*pte) : 0ULL);
187                         failed++;
188                 }
189                 if (level != PG_LEVEL_4K) {
190                         printk(KERN_ERR "CPA %lx: unexpected level %d\n",
191                                 addr[i], level);
192                         failed++;
193                 }
194
195         }
196         vfree(bm);
197
198         failed += print_split(&sb);
199
200         for (i = 0; i < NTEST; i++) {
201                 if (!addr[i])
202                         continue;
203                 pte = lookup_address(addr[i], &level);
204                 if (!pte) {
205                         printk(KERN_ERR "CPA lookup of %lx failed\n", addr[i]);
206                         failed++;
207                         continue;
208                 }
209                 test_addr = addr[i];
210                 err = change_page_attr_clear(&test_addr, len[i], PAGE_CPA_TEST, 0);
211                 if (err < 0) {
212                         printk(KERN_ERR "CPA reverting failed: %d\n", err);
213                         failed++;
214                 }
215                 pte = lookup_address(addr[i], &level);
216                 if (!pte || pte_testbit(*pte)) {
217                         printk(KERN_ERR "CPA %lx: bad pte after revert %Lx\n",
218                                 addr[i], pte ? (u64)pte_val(*pte) : 0ULL);
219                         failed++;
220                 }
221
222         }
223
224         failed += print_split(&sc);
225
226         if (failed) {
227                 WARN(1, KERN_ERR "NOT PASSED. Please report.\n");
228                 return -EINVAL;
229         } else {
230                 if (print)
231                         printk(KERN_INFO "ok.\n");
232         }
233
234         return 0;
235 }
236
237 static int do_pageattr_test(void *__unused)
238 {
239         while (!kthread_should_stop()) {
240                 schedule_timeout_interruptible(HZ*30);
241                 if (pageattr_test() < 0)
242                         break;
243                 if (print)
244                         print--;
245         }
246         return 0;
247 }
248
249 static int start_pageattr_test(void)
250 {
251         struct task_struct *p;
252
253         p = kthread_create(do_pageattr_test, NULL, "pageattr-test");
254         if (!IS_ERR(p))
255                 wake_up_process(p);
256         else
257                 WARN_ON(1);
258
259         return 0;
260 }
261 device_initcall(start_pageattr_test);