linux/arch/x86/mm/pageattr-test.c
<<
>>
Prefs
   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 = vzalloc((max_pfn_mapped + 7) / 8);
 127        if (!bm) {
 128                printk(KERN_ERR "CPA Cannot vmalloc bitmap\n");
 129                return -ENOMEM;
 130        }
 131
 132        failed += print_split(&sa);
 133        srandom32(100);
 134
 135        for (i = 0; i < NTEST; i++) {
 136                unsigned long pfn = random32() % max_pfn_mapped;
 137
 138                addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT);
 139                len[i] = random32() % 100;
 140                len[i] = min_t(unsigned long, len[i], max_pfn_mapped - pfn - 1);
 141
 142                if (len[i] == 0)
 143                        len[i] = 1;
 144
 145                pte = NULL;
 146                pte0 = pfn_pte(0, __pgprot(0)); /* shut gcc up */
 147
 148                for (k = 0; k < len[i]; k++) {
 149                        pte = lookup_address(addr[i] + k*PAGE_SIZE, &level);
 150                        if (!pte || pgprot_val(pte_pgprot(*pte)) == 0 ||
 151                            !(pte_val(*pte) & _PAGE_PRESENT)) {
 152                                addr[i] = 0;
 153                                break;
 154                        }
 155                        if (k == 0) {
 156                                pte0 = *pte;
 157                        } else {
 158                                if (pgprot_val(pte_pgprot(*pte)) !=
 159                                        pgprot_val(pte_pgprot(pte0))) {
 160                                        len[i] = k;
 161                                        break;
 162                                }
 163                        }
 164                        if (test_bit(pfn + k, bm)) {
 165                                len[i] = k;
 166                                break;
 167                        }
 168                        __set_bit(pfn + k, bm);
 169                }
 170                if (!addr[i] || !pte || !k) {
 171                        addr[i] = 0;
 172                        continue;
 173                }
 174
 175                test_addr = addr[i];
 176                err = change_page_attr_set(&test_addr, len[i], PAGE_CPA_TEST, 0);
 177                if (err < 0) {
 178                        printk(KERN_ERR "CPA %d failed %d\n", i, err);
 179                        failed++;
 180                }
 181
 182                pte = lookup_address(addr[i], &level);
 183                if (!pte || !pte_testbit(*pte) || pte_huge(*pte)) {
 184                        printk(KERN_ERR "CPA %lx: bad pte %Lx\n", addr[i],
 185                                pte ? (u64)pte_val(*pte) : 0ULL);
 186                        failed++;
 187                }
 188                if (level != PG_LEVEL_4K) {
 189                        printk(KERN_ERR "CPA %lx: unexpected level %d\n",
 190                                addr[i], level);
 191                        failed++;
 192                }
 193
 194        }
 195        vfree(bm);
 196
 197        failed += print_split(&sb);
 198
 199        for (i = 0; i < NTEST; i++) {
 200                if (!addr[i])
 201                        continue;
 202                pte = lookup_address(addr[i], &level);
 203                if (!pte) {
 204                        printk(KERN_ERR "CPA lookup of %lx failed\n", addr[i]);
 205                        failed++;
 206                        continue;
 207                }
 208                test_addr = addr[i];
 209                err = change_page_attr_clear(&test_addr, len[i], PAGE_CPA_TEST, 0);
 210                if (err < 0) {
 211                        printk(KERN_ERR "CPA reverting failed: %d\n", err);
 212                        failed++;
 213                }
 214                pte = lookup_address(addr[i], &level);
 215                if (!pte || pte_testbit(*pte)) {
 216                        printk(KERN_ERR "CPA %lx: bad pte after revert %Lx\n",
 217                                addr[i], pte ? (u64)pte_val(*pte) : 0ULL);
 218                        failed++;
 219                }
 220
 221        }
 222
 223        failed += print_split(&sc);
 224
 225        if (failed) {
 226                WARN(1, KERN_ERR "NOT PASSED. Please report.\n");
 227                return -EINVAL;
 228        } else {
 229                if (print)
 230                        printk(KERN_INFO "ok.\n");
 231        }
 232
 233        return 0;
 234}
 235
 236static int do_pageattr_test(void *__unused)
 237{
 238        while (!kthread_should_stop()) {
 239                schedule_timeout_interruptible(HZ*30);
 240                if (pageattr_test() < 0)
 241                        break;
 242                if (print)
 243                        print--;
 244        }
 245        return 0;
 246}
 247
 248static int start_pageattr_test(void)
 249{
 250        struct task_struct *p;
 251
 252        p = kthread_create(do_pageattr_test, NULL, "pageattr-test");
 253        if (!IS_ERR(p))
 254                wake_up_process(p);
 255        else
 256                WARN_ON(1);
 257
 258        return 0;
 259}
 260
 261module_init(start_pageattr_test);
 262