1/* 2 * self test for change_page_attr. 3 * 4 * Clears the a test pte bit on random pages in the direct mapping, 5 * then reverts and compares page tables forwards and afterwards. 6 */ 7#include <linux/bootmem.h> 8#include <linux/kthread.h> 9#include <linux/random.h> 10#include <linux/kernel.h> 11#include <linux/init.h> 12#include <linux/mm.h> 13 14#include <asm/cacheflush.h> 15#include <asm/pgtable.h> 16#include <asm/kdebug.h> 17 18/* 19 * Only print the results of the first pass: 20 */ 21static __read_mostly int print = 1; 22 23enum { 24 NTEST = 400, 25#ifdef CONFIG_X86_64 26 LPS = (1 << PMD_SHIFT), 27#elif defined(CONFIG_X86_PAE) 28 LPS = (1 << PMD_SHIFT), 29#else 30 LPS = (1 << 22), 31#endif 32 GPS = (1<<30) 33}; 34 35#define PAGE_CPA_TEST __pgprot(_PAGE_CPA_TEST) 36 37static int pte_testbit(pte_t pte) 38{ 39 return pte_flags(pte) & _PAGE_UNUSED1; 40} 41 42struct split_state { 43 long lpg, gpg, spg, exec; 44 long min_exec, max_exec; 45}; 46 47static int print_split(struct split_state *s) 48{ 49 long i, expected, missed = 0; 50 int err = 0; 51 52 s->lpg = s->gpg = s->spg = s->exec = 0; 53 s->min_exec = ~0UL; 54 s->max_exec = 0; 55 for (i = 0; i < max_pfn_mapped; ) { 56 unsigned long addr = (unsigned long)__va(i << PAGE_SHIFT); 57 unsigned int level; 58 pte_t *pte; 59 60 pte = lookup_address(addr, &level); 61 if (!pte) { 62 missed++; 63 i++; 64 continue; 65 } 66 67 if (level == PG_LEVEL_1G && sizeof(long) == 8) { 68 s->gpg++; 69 i += GPS/PAGE_SIZE; 70 } else if (level == PG_LEVEL_2M) { 71 if (!(pte_val(*pte) & _PAGE_PSE)) { 72 printk(KERN_ERR 73 "%lx level %d but not PSE %Lx\n", 74 addr, level, (u64)pte_val(*pte)); 75 err = 1; 76 } 77 s->lpg++; 78 i += LPS/PAGE_SIZE; 79 } else { 80 s->spg++; 81 i++; 82 } 83 if (!(pte_val(*pte) & _PAGE_NX)) { 84 s->exec++; 85 if (addr < s->min_exec) 86 s->min_exec = addr; 87 if (addr > s->max_exec) 88 s->max_exec = addr; 89 } 90 } 91 if (print) { 92 printk(KERN_INFO 93 " 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, 96 s->max_exec, missed); 97 } 98 99 expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed; 100 if (expected != i) { 101 printk(KERN_ERR "CPA max_pfn_mapped %lu but expected %lu\n", 102 max_pfn_mapped, expected); 103 return 1; 104 } 105 return err; 106} 107 108static unsigned long addr[NTEST]; 109static unsigned int len[NTEST]; 110 111/* Change the global bit on random pages in the direct mapping */ 112static int pageattr_test(void) 113{ 114 struct split_state sa, sb, sc; 115 unsigned long *bm; 116 pte_t *pte, pte0; 117 int failed = 0; 118 unsigned int level; 119 int i, k; 120 int err; 121 unsigned long test_addr; 122 123 if (print) 124 printk(KERN_INFO "CPA self-test:\n"); 125 126 bm = vmalloc((max_pfn_mapped + 7) / 8); 127 if (!bm) { 128 printk(KERN_ERR "CPA Cannot vmalloc bitmap\n"); 129 return -ENOMEM; 130 } 131 memset(bm, 0, (max_pfn_mapped + 7) / 8); 132 133 failed += print_split(&sa); 134 srandom32(100); 135 136 for (i = 0; i < NTEST; i++) { 137 unsigned long pfn = random32() % max_pfn_mapped; 138 139 addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT); 140 len[i] = random32() % 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 237static 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 249static 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 262module_init(start_pageattr_test); 263