18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * self test for change_page_attr. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Clears the a test pte bit on random pages in the direct mapping, 68c2ecf20Sopenharmony_ci * then reverts and compares page tables forwards and afterwards. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/memblock.h> 98c2ecf20Sopenharmony_ci#include <linux/kthread.h> 108c2ecf20Sopenharmony_ci#include <linux/random.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 178c2ecf20Sopenharmony_ci#include <asm/kdebug.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * Only print the results of the first pass: 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistatic __read_mostly int print = 1; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum { 258c2ecf20Sopenharmony_ci NTEST = 3 * 100, 268c2ecf20Sopenharmony_ci NPAGES = 100, 278c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64 288c2ecf20Sopenharmony_ci LPS = (1 << PMD_SHIFT), 298c2ecf20Sopenharmony_ci#elif defined(CONFIG_X86_PAE) 308c2ecf20Sopenharmony_ci LPS = (1 << PMD_SHIFT), 318c2ecf20Sopenharmony_ci#else 328c2ecf20Sopenharmony_ci LPS = (1 << 22), 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci GPS = (1<<30) 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define PAGE_CPA_TEST __pgprot(_PAGE_CPA_TEST) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int pte_testbit(pte_t pte) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci return pte_flags(pte) & _PAGE_SOFTW1; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct split_state { 458c2ecf20Sopenharmony_ci long lpg, gpg, spg, exec; 468c2ecf20Sopenharmony_ci long min_exec, max_exec; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int print_split(struct split_state *s) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci long i, expected, missed = 0; 528c2ecf20Sopenharmony_ci int err = 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci s->lpg = s->gpg = s->spg = s->exec = 0; 558c2ecf20Sopenharmony_ci s->min_exec = ~0UL; 568c2ecf20Sopenharmony_ci s->max_exec = 0; 578c2ecf20Sopenharmony_ci for (i = 0; i < max_pfn_mapped; ) { 588c2ecf20Sopenharmony_ci unsigned long addr = (unsigned long)__va(i << PAGE_SHIFT); 598c2ecf20Sopenharmony_ci unsigned int level; 608c2ecf20Sopenharmony_ci pte_t *pte; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci pte = lookup_address(addr, &level); 638c2ecf20Sopenharmony_ci if (!pte) { 648c2ecf20Sopenharmony_ci missed++; 658c2ecf20Sopenharmony_ci i++; 668c2ecf20Sopenharmony_ci continue; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (level == PG_LEVEL_1G && sizeof(long) == 8) { 708c2ecf20Sopenharmony_ci s->gpg++; 718c2ecf20Sopenharmony_ci i += GPS/PAGE_SIZE; 728c2ecf20Sopenharmony_ci } else if (level == PG_LEVEL_2M) { 738c2ecf20Sopenharmony_ci if ((pte_val(*pte) & _PAGE_PRESENT) && !(pte_val(*pte) & _PAGE_PSE)) { 748c2ecf20Sopenharmony_ci printk(KERN_ERR 758c2ecf20Sopenharmony_ci "%lx level %d but not PSE %Lx\n", 768c2ecf20Sopenharmony_ci addr, level, (u64)pte_val(*pte)); 778c2ecf20Sopenharmony_ci err = 1; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci s->lpg++; 808c2ecf20Sopenharmony_ci i += LPS/PAGE_SIZE; 818c2ecf20Sopenharmony_ci } else { 828c2ecf20Sopenharmony_ci s->spg++; 838c2ecf20Sopenharmony_ci i++; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci if (!(pte_val(*pte) & _PAGE_NX)) { 868c2ecf20Sopenharmony_ci s->exec++; 878c2ecf20Sopenharmony_ci if (addr < s->min_exec) 888c2ecf20Sopenharmony_ci s->min_exec = addr; 898c2ecf20Sopenharmony_ci if (addr > s->max_exec) 908c2ecf20Sopenharmony_ci s->max_exec = addr; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci if (print) { 948c2ecf20Sopenharmony_ci printk(KERN_INFO 958c2ecf20Sopenharmony_ci " 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n", 968c2ecf20Sopenharmony_ci s->spg, s->lpg, s->gpg, s->exec, 978c2ecf20Sopenharmony_ci s->min_exec != ~0UL ? s->min_exec : 0, 988c2ecf20Sopenharmony_ci s->max_exec, missed); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed; 1028c2ecf20Sopenharmony_ci if (expected != i) { 1038c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA max_pfn_mapped %lu but expected %lu\n", 1048c2ecf20Sopenharmony_ci max_pfn_mapped, expected); 1058c2ecf20Sopenharmony_ci return 1; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci return err; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic unsigned long addr[NTEST]; 1118c2ecf20Sopenharmony_cistatic unsigned int len[NTEST]; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic struct page *pages[NPAGES]; 1148c2ecf20Sopenharmony_cistatic unsigned long addrs[NPAGES]; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* Change the global bit on random pages in the direct mapping */ 1178c2ecf20Sopenharmony_cistatic int pageattr_test(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct split_state sa, sb, sc; 1208c2ecf20Sopenharmony_ci unsigned long *bm; 1218c2ecf20Sopenharmony_ci pte_t *pte, pte0; 1228c2ecf20Sopenharmony_ci int failed = 0; 1238c2ecf20Sopenharmony_ci unsigned int level; 1248c2ecf20Sopenharmony_ci int i, k; 1258c2ecf20Sopenharmony_ci int err; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (print) 1288c2ecf20Sopenharmony_ci printk(KERN_INFO "CPA self-test:\n"); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci bm = vzalloc((max_pfn_mapped + 7) / 8); 1318c2ecf20Sopenharmony_ci if (!bm) { 1328c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA Cannot vmalloc bitmap\n"); 1338c2ecf20Sopenharmony_ci return -ENOMEM; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci failed += print_split(&sa); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci for (i = 0; i < NTEST; i++) { 1398c2ecf20Sopenharmony_ci unsigned long pfn = prandom_u32() % max_pfn_mapped; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT); 1428c2ecf20Sopenharmony_ci len[i] = prandom_u32() % NPAGES; 1438c2ecf20Sopenharmony_ci len[i] = min_t(unsigned long, len[i], max_pfn_mapped - pfn - 1); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (len[i] == 0) 1468c2ecf20Sopenharmony_ci len[i] = 1; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci pte = NULL; 1498c2ecf20Sopenharmony_ci pte0 = pfn_pte(0, __pgprot(0)); /* shut gcc up */ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (k = 0; k < len[i]; k++) { 1528c2ecf20Sopenharmony_ci pte = lookup_address(addr[i] + k*PAGE_SIZE, &level); 1538c2ecf20Sopenharmony_ci if (!pte || pgprot_val(pte_pgprot(*pte)) == 0 || 1548c2ecf20Sopenharmony_ci !(pte_val(*pte) & _PAGE_PRESENT)) { 1558c2ecf20Sopenharmony_ci addr[i] = 0; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci if (k == 0) { 1598c2ecf20Sopenharmony_ci pte0 = *pte; 1608c2ecf20Sopenharmony_ci } else { 1618c2ecf20Sopenharmony_ci if (pgprot_val(pte_pgprot(*pte)) != 1628c2ecf20Sopenharmony_ci pgprot_val(pte_pgprot(pte0))) { 1638c2ecf20Sopenharmony_ci len[i] = k; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci if (test_bit(pfn + k, bm)) { 1688c2ecf20Sopenharmony_ci len[i] = k; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci __set_bit(pfn + k, bm); 1728c2ecf20Sopenharmony_ci addrs[k] = addr[i] + k*PAGE_SIZE; 1738c2ecf20Sopenharmony_ci pages[k] = pfn_to_page(pfn + k); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci if (!addr[i] || !pte || !k) { 1768c2ecf20Sopenharmony_ci addr[i] = 0; 1778c2ecf20Sopenharmony_ci continue; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci switch (i % 3) { 1818c2ecf20Sopenharmony_ci case 0: 1828c2ecf20Sopenharmony_ci err = change_page_attr_set(&addr[i], len[i], PAGE_CPA_TEST, 0); 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci case 1: 1868c2ecf20Sopenharmony_ci err = change_page_attr_set(addrs, len[1], PAGE_CPA_TEST, 1); 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci case 2: 1908c2ecf20Sopenharmony_ci err = cpa_set_pages_array(pages, len[i], PAGE_CPA_TEST); 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (err < 0) { 1968c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA %d failed %d\n", i, err); 1978c2ecf20Sopenharmony_ci failed++; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci pte = lookup_address(addr[i], &level); 2018c2ecf20Sopenharmony_ci if (!pte || !pte_testbit(*pte) || pte_huge(*pte)) { 2028c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA %lx: bad pte %Lx\n", addr[i], 2038c2ecf20Sopenharmony_ci pte ? (u64)pte_val(*pte) : 0ULL); 2048c2ecf20Sopenharmony_ci failed++; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci if (level != PG_LEVEL_4K) { 2078c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA %lx: unexpected level %d\n", 2088c2ecf20Sopenharmony_ci addr[i], level); 2098c2ecf20Sopenharmony_ci failed++; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci vfree(bm); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci failed += print_split(&sb); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci for (i = 0; i < NTEST; i++) { 2188c2ecf20Sopenharmony_ci if (!addr[i]) 2198c2ecf20Sopenharmony_ci continue; 2208c2ecf20Sopenharmony_ci pte = lookup_address(addr[i], &level); 2218c2ecf20Sopenharmony_ci if (!pte) { 2228c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA lookup of %lx failed\n", addr[i]); 2238c2ecf20Sopenharmony_ci failed++; 2248c2ecf20Sopenharmony_ci continue; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci err = change_page_attr_clear(&addr[i], len[i], PAGE_CPA_TEST, 0); 2278c2ecf20Sopenharmony_ci if (err < 0) { 2288c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA reverting failed: %d\n", err); 2298c2ecf20Sopenharmony_ci failed++; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci pte = lookup_address(addr[i], &level); 2328c2ecf20Sopenharmony_ci if (!pte || pte_testbit(*pte)) { 2338c2ecf20Sopenharmony_ci printk(KERN_ERR "CPA %lx: bad pte after revert %Lx\n", 2348c2ecf20Sopenharmony_ci addr[i], pte ? (u64)pte_val(*pte) : 0ULL); 2358c2ecf20Sopenharmony_ci failed++; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci failed += print_split(&sc); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (failed) { 2438c2ecf20Sopenharmony_ci WARN(1, KERN_ERR "NOT PASSED. Please report.\n"); 2448c2ecf20Sopenharmony_ci return -EINVAL; 2458c2ecf20Sopenharmony_ci } else { 2468c2ecf20Sopenharmony_ci if (print) 2478c2ecf20Sopenharmony_ci printk(KERN_INFO "ok.\n"); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int do_pageattr_test(void *__unused) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 2568c2ecf20Sopenharmony_ci schedule_timeout_interruptible(HZ*30); 2578c2ecf20Sopenharmony_ci if (pageattr_test() < 0) 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci if (print) 2608c2ecf20Sopenharmony_ci print--; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int start_pageattr_test(void) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct task_struct *p; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci p = kthread_create(do_pageattr_test, NULL, "pageattr-test"); 2708c2ecf20Sopenharmony_ci if (!IS_ERR(p)) 2718c2ecf20Sopenharmony_ci wake_up_process(p); 2728c2ecf20Sopenharmony_ci else 2738c2ecf20Sopenharmony_ci WARN_ON(1); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_cidevice_initcall(start_pageattr_test); 278