cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cpa-test.c (5757B)


      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/memblock.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/kdebug.h>
     18
     19/*
     20 * Only print the results of the first pass:
     21 */
     22static __read_mostly int print = 1;
     23
     24enum {
     25	NTEST			= 3 * 100,
     26	NPAGES			= 100,
     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
     39static int pte_testbit(pte_t pte)
     40{
     41	return pte_flags(pte) & _PAGE_SOFTW1;
     42}
     43
     44struct split_state {
     45	long lpg, gpg, spg, exec;
     46	long min_exec, max_exec;
     47};
     48
     49static 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
    110static unsigned long addr[NTEST];
    111static unsigned int len[NTEST];
    112
    113static struct page *pages[NPAGES];
    114static unsigned long addrs[NPAGES];
    115
    116/* Change the global bit on random pages in the direct mapping */
    117static int pageattr_test(void)
    118{
    119	struct split_state sa, sb, sc;
    120	unsigned long *bm;
    121	pte_t *pte, pte0;
    122	int failed = 0;
    123	unsigned int level;
    124	int i, k;
    125	int err;
    126
    127	if (print)
    128		printk(KERN_INFO "CPA self-test:\n");
    129
    130	bm = vzalloc((max_pfn_mapped + 7) / 8);
    131	if (!bm) {
    132		printk(KERN_ERR "CPA Cannot vmalloc bitmap\n");
    133		return -ENOMEM;
    134	}
    135
    136	failed += print_split(&sa);
    137
    138	for (i = 0; i < NTEST; i++) {
    139		unsigned long pfn = prandom_u32() % max_pfn_mapped;
    140
    141		addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT);
    142		len[i] = prandom_u32() % NPAGES;
    143		len[i] = min_t(unsigned long, len[i], max_pfn_mapped - pfn - 1);
    144
    145		if (len[i] == 0)
    146			len[i] = 1;
    147
    148		pte = NULL;
    149		pte0 = pfn_pte(0, __pgprot(0)); /* shut gcc up */
    150
    151		for (k = 0; k < len[i]; k++) {
    152			pte = lookup_address(addr[i] + k*PAGE_SIZE, &level);
    153			if (!pte || pgprot_val(pte_pgprot(*pte)) == 0 ||
    154			    !(pte_val(*pte) & _PAGE_PRESENT)) {
    155				addr[i] = 0;
    156				break;
    157			}
    158			if (k == 0) {
    159				pte0 = *pte;
    160			} else {
    161				if (pgprot_val(pte_pgprot(*pte)) !=
    162					pgprot_val(pte_pgprot(pte0))) {
    163					len[i] = k;
    164					break;
    165				}
    166			}
    167			if (test_bit(pfn + k, bm)) {
    168				len[i] = k;
    169				break;
    170			}
    171			__set_bit(pfn + k, bm);
    172			addrs[k] = addr[i] + k*PAGE_SIZE;
    173			pages[k] = pfn_to_page(pfn + k);
    174		}
    175		if (!addr[i] || !pte || !k) {
    176			addr[i] = 0;
    177			continue;
    178		}
    179
    180		switch (i % 3) {
    181		case 0:
    182			err = change_page_attr_set(&addr[i], len[i], PAGE_CPA_TEST, 0);
    183			break;
    184
    185		case 1:
    186			err = change_page_attr_set(addrs, len[1], PAGE_CPA_TEST, 1);
    187			break;
    188
    189		case 2:
    190			err = cpa_set_pages_array(pages, len[i], PAGE_CPA_TEST);
    191			break;
    192		}
    193
    194
    195		if (err < 0) {
    196			printk(KERN_ERR "CPA %d failed %d\n", i, err);
    197			failed++;
    198		}
    199
    200		pte = lookup_address(addr[i], &level);
    201		if (!pte || !pte_testbit(*pte) || pte_huge(*pte)) {
    202			printk(KERN_ERR "CPA %lx: bad pte %Lx\n", addr[i],
    203				pte ? (u64)pte_val(*pte) : 0ULL);
    204			failed++;
    205		}
    206		if (level != PG_LEVEL_4K) {
    207			printk(KERN_ERR "CPA %lx: unexpected level %d\n",
    208				addr[i], level);
    209			failed++;
    210		}
    211
    212	}
    213	vfree(bm);
    214
    215	failed += print_split(&sb);
    216
    217	for (i = 0; i < NTEST; i++) {
    218		if (!addr[i])
    219			continue;
    220		pte = lookup_address(addr[i], &level);
    221		if (!pte) {
    222			printk(KERN_ERR "CPA lookup of %lx failed\n", addr[i]);
    223			failed++;
    224			continue;
    225		}
    226		err = change_page_attr_clear(&addr[i], len[i], PAGE_CPA_TEST, 0);
    227		if (err < 0) {
    228			printk(KERN_ERR "CPA reverting failed: %d\n", err);
    229			failed++;
    230		}
    231		pte = lookup_address(addr[i], &level);
    232		if (!pte || pte_testbit(*pte)) {
    233			printk(KERN_ERR "CPA %lx: bad pte after revert %Lx\n",
    234				addr[i], pte ? (u64)pte_val(*pte) : 0ULL);
    235			failed++;
    236		}
    237
    238	}
    239
    240	failed += print_split(&sc);
    241
    242	if (failed) {
    243		WARN(1, KERN_ERR "NOT PASSED. Please report.\n");
    244		return -EINVAL;
    245	} else {
    246		if (print)
    247			printk(KERN_INFO "ok.\n");
    248	}
    249
    250	return 0;
    251}
    252
    253static int do_pageattr_test(void *__unused)
    254{
    255	while (!kthread_should_stop()) {
    256		schedule_timeout_interruptible(HZ*30);
    257		if (pageattr_test() < 0)
    258			break;
    259		if (print)
    260			print--;
    261	}
    262	return 0;
    263}
    264
    265static int start_pageattr_test(void)
    266{
    267	struct task_struct *p;
    268
    269	p = kthread_create(do_pageattr_test, NULL, "pageattr-test");
    270	if (!IS_ERR(p))
    271		wake_up_process(p);
    272	else
    273		WARN_ON(1);
    274
    275	return 0;
    276}
    277device_initcall(start_pageattr_test);