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 = 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