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/mm.h>
  12
  13#include <asm/cacheflush.h>
  14#include <asm/pgtable.h>
  15#include <asm/kdebug.h>
  16
  17/*
  18 * Only print the results of the first pass:
  19 */
  20static __read_mostly int print = 1;
  21
  22enum {
  23        NTEST                   = 400,
  24#ifdef CONFIG_X86_64
  25        LPS                     = (1 << PMD_SHIFT),
  26#elif defined(CONFIG_X86_PAE)
  27        LPS                     = (1 << PMD_SHIFT),
  28#else
  29        LPS                     = (1 << 22),
  30#endif
  31        GPS                     = (1<<30)
  32};
  33
  34#define PAGE_CPA_TEST   __pgprot(_PAGE_CPA_TEST)
  35
  36static int pte_testbit(pte_t pte)
  37{
  38        return pte_flags(pte) & _PAGE_UNUSED1;
  39}
  40
  41struct split_state {
  42        long lpg, gpg, spg, exec;
  43        long min_exec, max_exec;
  44};
  45
  46static int print_split(struct split_state *s)
  47{
  48        long i, expected, missed = 0;
  49        int err = 0;
  50
  51        s->lpg = s->gpg = s->spg = s->exec = 0;
  52        s->min_exec = ~0UL;
  53        s->max_exec = 0;
  54        for (i = 0; i < max_pfn_mapped; ) {
  55                unsigned long addr = (unsigned long)__va(i << PAGE_SHIFT);
  56                unsigned int level;
  57                pte_t *pte;
  58
  59                pte = lookup_address(addr, &level);
  60                if (!pte) {
  61                        missed++;
  62                        i++;
  63                        continue;
  64                }
  65
  66                if (level == PG_LEVEL_1G && sizeof(long) == 8) {
  67                        s->gpg++;
  68                        i += GPS/PAGE_SIZE;
  69                } else if (level == PG_LEVEL_2M) {
  70                        if ((pte_val(*pte) & _PAGE_PRESENT) && !(pte_val(*pte) & _PAGE_PSE)) {
  71                                printk(KERN_ERR
  72                                        "%lx level %d but not PSE %Lx\n",
  73                                        addr, level, (u64)pte_val(*pte));
  74                                err = 1;
  75                        }
  76                        s->lpg++;
  77                        i += LPS/PAGE_SIZE;
  78                } else {
  79                        s->spg++;
  80                        i++;
  81                }
  82                if (!(pte_val(*pte) & _PAGE_NX)) {
  83                        s->exec++;
  84                        if (addr < s->min_exec)
  85                                s->min_exec = addr;
  86                        if (addr > s->max_exec)
  87                                s->max_exec = addr;
  88                }
  89        }
  90        if (print) {
  91                printk(KERN_INFO
  92                        " 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n",
  93                        s->spg, s->lpg, s->gpg, s->exec,
  94                        s->min_exec != ~0UL ? s->min_exec : 0,
  95                        s->max_exec, missed);
  96        }
  97
  98        expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed;
  99        if (expected != i) {
 100                printk(KERN_ERR "CPA max_pfn_mapped %lu but expected %lu\n",
 101                        max_pfn_mapped, expected);
 102                return 1;
 103        }
 104        return err;
 105}
 106
 107static unsigned long addr[NTEST];
 108static unsigned int len[NTEST];
 109
 110/* Change the global bit on random pages in the direct mapping */
 111static int pageattr_test(void)
 112{
 113        struct split_state sa, sb, sc;
 114        unsigned long *bm;
 115        pte_t *pte, pte0;
 116        int failed = 0;
 117        unsigned int level;
 118        int i, k;
 119        int err;
 120        unsigned long test_addr;
 121
 122        if (print)
 123                printk(KERN_INFO "CPA self-test:\n");
 124
 125        bm = vzalloc((max_pfn_mapped + 7) / 8);
 126        if (!bm) {
 127                printk(KERN_ERR "CPA Cannot vmalloc bitmap\n");
 128                return -ENOMEM;
 129        }
 130
 131        failed += print_split(&sa);
 132
 133        for (i = 0; i < NTEST; i++) {
 134                unsigned long pfn = prandom_u32() % max_pfn_mapped;
 135
 136                addr[i] = (unsigned long)__va(pfn << PAGE_SHIFT);
 137                len[i] = prandom_u32() % 100;
 138                len[i] = min_t(unsigned long, len[i], max_pfn_mapped - pfn - 1);
 139
 140                if (len[i] == 0)
 141                        len[i] = 1;
 142
 143                pte = NULL;
 144                pte0 = pfn_pte(0, __pgprot(0)); /* shut gcc up */
 145
 146                for (k = 0; k < len[i]; k++) {
 147                        pte = lookup_address(addr[i] + k*PAGE_SIZE, &level);
 148                        if (!pte || pgprot_val(pte_pgprot(*pte)) == 0 ||
 149                            !(pte_val(*pte) & _PAGE_PRESENT)) {
 150                                addr[i] = 0;
 151                                break;
 152                        }
 153                        if (k == 0) {
 154                                pte0 = *pte;
 155                        } else {
 156                                if (pgprot_val(pte_pgprot(*pte)) !=
 157                                        pgprot_val(pte_pgprot(pte0))) {
 158                                        len[i] = k;
 159                                        break;
 160                                }
 161                        }
 162                        if (test_bit(pfn + k, bm)) {
 163                                len[i] = k;
 164                                break;
 165                        }
 166                        __set_bit(pfn + k, bm);
 167                }
 168                if (!addr[i] || !pte || !k) {
 169                        addr[i] = 0;
 170                        continue;
 171                }
 172
 173                test_addr = addr[i];
 174                err = change_page_attr_set(&test_addr, len[i], PAGE_CPA_TEST, 0);
 175                if (err < 0) {
 176                        printk(KERN_ERR "CPA %d failed %d\n", i, err);
 177                        failed++;
 178                }
 179
 180                pte = lookup_address(addr[i], &level);
 181                if (!pte || !pte_testbit(*pte) || pte_huge(*pte)) {
 182                        printk(KERN_ERR "CPA %lx: bad pte %Lx\n", addr[i],
 183                                pte ? (u64)pte_val(*pte) : 0ULL);
 184                        failed++;
 185                }
 186                if (level != PG_LEVEL_4K) {
 187                        printk(KERN_ERR "CPA %lx: unexpected level %d\n",
 188                                addr[i], level);
 189                        failed++;
 190                }
 191
 192        }
 193        vfree(bm);
 194
 195        failed += print_split(&sb);
 196
 197        for (i = 0; i < NTEST; i++) {
 198                if (!addr[i])
 199                        continue;
 200                pte = lookup_address(addr[i], &level);
 201                if (!pte) {
 202                        printk(KERN_ERR "CPA lookup of %lx failed\n", addr[i]);
 203                        failed++;
 204                        continue;
 205                }
 206                test_addr = addr[i];
 207                err = change_page_attr_clear(&test_addr, len[i], PAGE_CPA_TEST, 0);
 208                if (err < 0) {
 209                        printk(KERN_ERR "CPA reverting failed: %d\n", err);
 210                        failed++;
 211                }
 212                pte = lookup_address(addr[i], &level);
 213                if (!pte || pte_testbit(*pte)) {
 214                        printk(KERN_ERR "CPA %lx: bad pte after revert %Lx\n",
 215                                addr[i], pte ? (u64)pte_val(*pte) : 0ULL);
 216                        failed++;
 217                }
 218
 219        }
 220
 221        failed += print_split(&sc);
 222
 223        if (failed) {
 224                WARN(1, KERN_ERR "NOT PASSED. Please report.\n");
 225                return -EINVAL;
 226        } else {
 227                if (print)
 228                        printk(KERN_INFO "ok.\n");
 229        }
 230
 231        return 0;
 232}
 233
 234static int do_pageattr_test(void *__unused)
 235{
 236        while (!kthread_should_stop()) {
 237                schedule_timeout_interruptible(HZ*30);
 238                if (pageattr_test() < 0)
 239                        break;
 240                if (print)
 241                        print--;
 242        }
 243        return 0;
 244}
 245
 246static int start_pageattr_test(void)
 247{
 248        struct task_struct *p;
 249
 250        p = kthread_create(do_pageattr_test, NULL, "pageattr-test");
 251        if (!IS_ERR(p))
 252                wake_up_process(p);
 253        else
 254                WARN_ON(1);
 255
 256        return 0;
 257}
 258
 259module_init(start_pageattr_test);
 260