summaryrefslogtreecommitdiff
path: root/kvm/test/x86/access.c
diff options
context:
space:
mode:
Diffstat (limited to 'kvm/test/x86/access.c')
-rw-r--r--kvm/test/x86/access.c708
1 files changed, 0 insertions, 708 deletions
diff --git a/kvm/test/x86/access.c b/kvm/test/x86/access.c
deleted file mode 100644
index a0d88e1e0..000000000
--- a/kvm/test/x86/access.c
+++ /dev/null
@@ -1,708 +0,0 @@
-
-#include "libcflat.h"
-
-#define smp_id() 0
-
-#define true 1
-#define false 0
-
-static _Bool verbose = false;
-
-typedef unsigned long pt_element_t;
-
-#define PAGE_SIZE ((pt_element_t)4096)
-#define PAGE_MASK (~(PAGE_SIZE-1))
-
-#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & PAGE_MASK))
-#define PT_PSE_BASE_ADDR_MASK (PT_BASE_ADDR_MASK & ~(1ull << 21))
-
-#define PT_PRESENT_MASK ((pt_element_t)1 << 0)
-#define PT_WRITABLE_MASK ((pt_element_t)1 << 1)
-#define PT_USER_MASK ((pt_element_t)1 << 2)
-#define PT_ACCESSED_MASK ((pt_element_t)1 << 5)
-#define PT_DIRTY_MASK ((pt_element_t)1 << 6)
-#define PT_PSE_MASK ((pt_element_t)1 << 7)
-#define PT_NX_MASK ((pt_element_t)1 << 63)
-
-#define CR0_WP_MASK (1UL << 16)
-
-#define PFERR_PRESENT_MASK (1U << 0)
-#define PFERR_WRITE_MASK (1U << 1)
-#define PFERR_USER_MASK (1U << 2)
-#define PFERR_RESERVED_MASK (1U << 3)
-#define PFERR_FETCH_MASK (1U << 4)
-
-#define MSR_EFER 0xc0000080
-#define EFER_NX_MASK (1ull << 11)
-
-/*
- * page table access check tests
- */
-
-enum {
- AC_PTE_PRESENT,
- AC_PTE_WRITABLE,
- AC_PTE_USER,
- AC_PTE_ACCESSED,
- AC_PTE_DIRTY,
- AC_PTE_NX,
- AC_PTE_BIT51,
-
- AC_PDE_PRESENT,
- AC_PDE_WRITABLE,
- AC_PDE_USER,
- AC_PDE_ACCESSED,
- AC_PDE_DIRTY,
- AC_PDE_PSE,
- AC_PDE_NX,
- AC_PDE_BIT51,
-
- AC_ACCESS_USER,
- AC_ACCESS_WRITE,
- AC_ACCESS_FETCH,
- AC_ACCESS_TWICE,
- // AC_ACCESS_PTE,
-
- AC_CPU_EFER_NX,
- AC_CPU_CR0_WP,
-
- NR_AC_FLAGS
-};
-
-const char *ac_names[] = {
- [AC_PTE_PRESENT] = "pte.p",
- [AC_PTE_ACCESSED] = "pte.a",
- [AC_PTE_WRITABLE] = "pte.rw",
- [AC_PTE_USER] = "pte.user",
- [AC_PTE_DIRTY] = "pte.d",
- [AC_PTE_NX] = "pte.nx",
- [AC_PTE_BIT51] = "pte.51",
- [AC_PDE_PRESENT] = "pde.p",
- [AC_PDE_ACCESSED] = "pde.a",
- [AC_PDE_WRITABLE] = "pde.rw",
- [AC_PDE_USER] = "pde.user",
- [AC_PDE_DIRTY] = "pde.d",
- [AC_PDE_PSE] = "pde.pse",
- [AC_PDE_NX] = "pde.nx",
- [AC_PDE_BIT51] = "pde.51",
- [AC_ACCESS_WRITE] = "write",
- [AC_ACCESS_USER] = "user",
- [AC_ACCESS_FETCH] = "fetch",
- [AC_ACCESS_TWICE] = "twice",
- [AC_CPU_EFER_NX] = "efer.nx",
- [AC_CPU_CR0_WP] = "cr0.wp",
-};
-
-static inline void *va(pt_element_t phys)
-{
- return (void *)phys;
-}
-
-static unsigned long read_cr0()
-{
- unsigned long cr0;
-
- asm volatile ("mov %%cr0, %0" : "=r"(cr0));
-
- return cr0;
-}
-
-static void write_cr0(unsigned long cr0)
-{
- asm volatile ("mov %0, %%cr0" : : "r"(cr0));
-}
-
-typedef struct {
- unsigned short offset0;
- unsigned short selector;
- unsigned short ist : 3;
- unsigned short : 5;
- unsigned short type : 4;
- unsigned short : 1;
- unsigned short dpl : 2;
- unsigned short p : 1;
- unsigned short offset1;
- unsigned offset2;
- unsigned reserved;
-} idt_entry_t;
-
-typedef struct {
- pt_element_t pt_pool;
- unsigned pt_pool_size;
- unsigned pt_pool_current;
-} ac_pool_t;
-
-typedef struct {
- unsigned flags[NR_AC_FLAGS];
- void *virt;
- pt_element_t phys;
- pt_element_t *ptep;
- pt_element_t expected_pte;
- pt_element_t *pdep;
- pt_element_t expected_pde;
- pt_element_t ignore_pde;
- int expected_fault;
- unsigned expected_error;
- idt_entry_t idt[256];
-} ac_test_t;
-
-typedef struct {
- unsigned short limit;
- unsigned long linear_addr;
-} __attribute__((packed)) descriptor_table_t;
-
-
-static void ac_test_show(ac_test_t *at);
-
-void lidt(idt_entry_t *idt, int nentries)
-{
- descriptor_table_t dt;
-
- dt.limit = nentries * sizeof(*idt) - 1;
- dt.linear_addr = (unsigned long)idt;
- asm volatile ("lidt %0" : : "m"(dt));
-}
-
-unsigned short read_cs()
-{
- unsigned short r;
-
- asm volatile ("mov %%cs, %0" : "=r"(r));
- return r;
-}
-
-unsigned long long rdmsr(unsigned index)
-{
- unsigned a, d;
-
- asm volatile("rdmsr" : "=a"(a), "=d"(d) : "c"(index));
- return ((unsigned long long)d << 32) | a;
-}
-
-void wrmsr(unsigned index, unsigned long long val)
-{
- unsigned a = val, d = val >> 32;
-
- asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(index));
-}
-
-void set_idt_entry(idt_entry_t *e, void *addr, int dpl)
-{
- memset(e, 0, sizeof *e);
- e->offset0 = (unsigned long)addr;
- e->selector = read_cs();
- e->ist = 0;
- e->type = 14;
- e->dpl = dpl;
- e->p = 1;
- e->offset1 = (unsigned long)addr >> 16;
- e->offset2 = (unsigned long)addr >> 32;
-}
-
-void set_cr0_wp(int wp)
-{
- unsigned long cr0 = read_cr0();
-
- cr0 &= ~CR0_WP_MASK;
- if (wp)
- cr0 |= CR0_WP_MASK;
- write_cr0(cr0);
-}
-
-void set_efer_nx(int nx)
-{
- unsigned long long efer;
-
- efer = rdmsr(MSR_EFER);
- efer &= ~EFER_NX_MASK;
- if (nx)
- efer |= EFER_NX_MASK;
- wrmsr(MSR_EFER, efer);
-}
-
-static void ac_env_int(ac_pool_t *pool)
-{
- static idt_entry_t idt[256];
-
- memset(idt, 0, sizeof(idt));
- lidt(idt, 256);
- extern char page_fault, kernel_entry;
- set_idt_entry(&idt[14], &page_fault, 0);
- set_idt_entry(&idt[0x20], &kernel_entry, 3);
-
- pool->pt_pool = 33 * 1024 * 1024;
- pool->pt_pool_size = 120 * 1024 * 1024 - pool->pt_pool;
- pool->pt_pool_current = 0;
-}
-
-void ac_test_init(ac_test_t *at, void *virt)
-{
- wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
- set_cr0_wp(1);
- for (int i = 0; i < NR_AC_FLAGS; ++i)
- at->flags[i] = 0;
- at->virt = virt;
- at->phys = 32 * 1024 * 1024;
-}
-
-int ac_test_bump_one(ac_test_t *at)
-{
- for (int i = 0; i < NR_AC_FLAGS; ++i)
- if (!at->flags[i]) {
- at->flags[i] = 1;
- return 1;
- } else
- at->flags[i] = 0;
- return 0;
-}
-
-_Bool ac_test_legal(ac_test_t *at)
-{
- if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_ACCESS_WRITE])
- return false;
- return true;
-}
-
-int ac_test_bump(ac_test_t *at)
-{
- int ret;
-
- ret = ac_test_bump_one(at);
- while (ret && !ac_test_legal(at))
- ret = ac_test_bump_one(at);
- return ret;
-}
-
-unsigned long read_cr3()
-{
- unsigned long cr3;
-
- asm volatile ("mov %%cr3, %0" : "=r"(cr3));
- return cr3;
-}
-
-void invlpg(void *addr)
-{
- asm volatile ("invlpg (%0)" : : "r"(addr));
-}
-
-pt_element_t ac_test_alloc_pt(ac_pool_t *pool)
-{
- pt_element_t ret = pool->pt_pool + pool->pt_pool_current;
- pool->pt_pool_current += PAGE_SIZE;
- return ret;
-}
-
-_Bool ac_test_enough_room(ac_pool_t *pool)
-{
- return pool->pt_pool_current + 4 * PAGE_SIZE <= pool->pt_pool_size;
-}
-
-void ac_test_reset_pt_pool(ac_pool_t *pool)
-{
- pool->pt_pool_current = 0;
-}
-
-void ac_set_expected_status(ac_test_t *at)
-{
- int pde_valid, pte_valid;
-
- invlpg(at->virt);
-
- if (at->ptep)
- at->expected_pte = *at->ptep;
- at->expected_pde = *at->pdep;
- at->ignore_pde = 0;
- at->expected_fault = 0;
- at->expected_error = PFERR_PRESENT_MASK;
-
- pde_valid = at->flags[AC_PDE_PRESENT]
- && !at->flags[AC_PDE_BIT51]
- && !(at->flags[AC_PDE_NX] && !at->flags[AC_CPU_EFER_NX]);
- pte_valid = pde_valid
- && at->flags[AC_PTE_PRESENT]
- && !at->flags[AC_PTE_BIT51]
- && !(at->flags[AC_PTE_NX] && !at->flags[AC_CPU_EFER_NX]);
- if (at->flags[AC_ACCESS_TWICE]) {
- if (pde_valid) {
- at->expected_pde |= PT_ACCESSED_MASK;
- if (pte_valid)
- at->expected_pte |= PT_ACCESSED_MASK;
- }
- }
-
- if (at->flags[AC_ACCESS_USER])
- at->expected_error |= PFERR_USER_MASK;
-
- if (at->flags[AC_ACCESS_WRITE])
- at->expected_error |= PFERR_WRITE_MASK;
-
- if (at->flags[AC_ACCESS_FETCH])
- at->expected_error |= PFERR_FETCH_MASK;
-
- if (!at->flags[AC_PDE_PRESENT]) {
- at->expected_fault = 1;
- at->expected_error &= ~PFERR_PRESENT_MASK;
- } else if (!pde_valid) {
- at->expected_fault = 1;
- at->expected_error |= PFERR_RESERVED_MASK;
- }
-
- if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PDE_USER])
- at->expected_fault = 1;
-
- if (at->flags[AC_ACCESS_WRITE]
- && !at->flags[AC_PDE_WRITABLE]
- && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER]))
- at->expected_fault = 1;
-
- if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_NX])
- at->expected_fault = 1;
-
- if (!at->flags[AC_PDE_ACCESSED])
- at->ignore_pde = PT_ACCESSED_MASK;
-
- if (!pde_valid)
- goto fault;
-
- if (!at->expected_fault)
- at->expected_pde |= PT_ACCESSED_MASK;
-
- if (at->flags[AC_PDE_PSE]) {
- if (at->flags[AC_ACCESS_WRITE] && !at->expected_fault)
- at->expected_pde |= PT_DIRTY_MASK;
- goto no_pte;
- }
-
- if (!at->flags[AC_PTE_PRESENT]) {
- at->expected_fault = 1;
- at->expected_error &= ~PFERR_PRESENT_MASK;
- } else if (!pte_valid) {
- at->expected_fault = 1;
- at->expected_error |= PFERR_RESERVED_MASK;
- }
-
- if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PTE_USER])
- at->expected_fault = 1;
-
- if (at->flags[AC_ACCESS_WRITE]
- && !at->flags[AC_PTE_WRITABLE]
- && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER]))
- at->expected_fault = 1;
-
- if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PTE_NX])
- at->expected_fault = 1;
-
- if (at->expected_fault)
- goto fault;
-
- at->expected_pte |= PT_ACCESSED_MASK;
- if (at->flags[AC_ACCESS_WRITE])
- at->expected_pte |= PT_DIRTY_MASK;
-
-no_pte:
-fault:
- if (!at->expected_fault)
- at->ignore_pde = 0;
- if (!at->flags[AC_CPU_EFER_NX])
- at->expected_error &= ~PFERR_FETCH_MASK;
-}
-
-void ac_test_setup_pte(ac_test_t *at, ac_pool_t *pool)
-{
- unsigned long root = read_cr3();
-
- if (!ac_test_enough_room(pool))
- ac_test_reset_pt_pool(pool);
-
- at->ptep = 0;
- for (int i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) {
- pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK);
- unsigned index = ((unsigned long)at->virt >> (12 + (i-1) * 9)) & 511;
- pt_element_t pte = 0;
- switch (i) {
- case 4:
- case 3:
- pte = vroot[index];
- pte = ac_test_alloc_pt(pool) | PT_PRESENT_MASK;
- pte |= PT_WRITABLE_MASK | PT_USER_MASK;
- break;
- case 2:
- if (!at->flags[AC_PDE_PSE])
- pte = ac_test_alloc_pt(pool);
- else {
- pte = at->phys & PT_PSE_BASE_ADDR_MASK;
- pte |= PT_PSE_MASK;
- }
- if (at->flags[AC_PDE_PRESENT])
- pte |= PT_PRESENT_MASK;
- if (at->flags[AC_PDE_WRITABLE])
- pte |= PT_WRITABLE_MASK;
- if (at->flags[AC_PDE_USER])
- pte |= PT_USER_MASK;
- if (at->flags[AC_PDE_ACCESSED])
- pte |= PT_ACCESSED_MASK;
- if (at->flags[AC_PDE_DIRTY])
- pte |= PT_DIRTY_MASK;
- if (at->flags[AC_PDE_NX])
- pte |= PT_NX_MASK;
- if (at->flags[AC_PDE_BIT51])
- pte |= 1ull << 51;
- at->pdep = &vroot[index];
- break;
- case 1:
- pte = at->phys & PT_BASE_ADDR_MASK;
- if (at->flags[AC_PTE_PRESENT])
- pte |= PT_PRESENT_MASK;
- if (at->flags[AC_PTE_WRITABLE])
- pte |= PT_WRITABLE_MASK;
- if (at->flags[AC_PTE_USER])
- pte |= PT_USER_MASK;
- if (at->flags[AC_PTE_ACCESSED])
- pte |= PT_ACCESSED_MASK;
- if (at->flags[AC_PTE_DIRTY])
- pte |= PT_DIRTY_MASK;
- if (at->flags[AC_PTE_NX])
- pte |= PT_NX_MASK;
- if (at->flags[AC_PTE_BIT51])
- pte |= 1ull << 51;
- at->ptep = &vroot[index];
- break;
- }
- vroot[index] = pte;
- root = vroot[index];
- }
- ac_set_expected_status(at);
-}
-
-static void ac_test_check(ac_test_t *at, _Bool *success_ret, _Bool cond,
- const char *fmt, ...)
-{
- va_list ap;
- char buf[500];
-
- if (!*success_ret) {
- return;
- }
-
- if (!cond) {
- return;
- }
-
- *success_ret = false;
-
- if (!verbose) {
- ac_test_show(at);
- }
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- printf("FAIL: %s\n", buf);
-}
-
-static int pt_match(pt_element_t pte1, pt_element_t pte2, pt_element_t ignore)
-{
- pte1 &= ~ignore;
- pte2 &= ~ignore;
- return pte1 == pte2;
-}
-
-int ac_test_do_access(ac_test_t *at)
-{
- static unsigned unique = 42;
- int fault = 0;
- unsigned e;
- static unsigned char user_stack[4096];
- unsigned long rsp;
- _Bool success = true;
-
- ++unique;
-
- *((unsigned char *)at->phys) = 0xc3; /* ret */
-
- unsigned r = unique;
- set_cr0_wp(at->flags[AC_CPU_CR0_WP]);
- set_efer_nx(at->flags[AC_CPU_EFER_NX]);
-
- if (at->flags[AC_ACCESS_TWICE]) {
- asm volatile (
- "mov $fixed2, %%rsi \n\t"
- "mov (%[addr]), %[reg] \n\t"
- "fixed2:"
- : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e)
- : [addr]"r"(at->virt)
- : "rsi"
- );
- fault = 0;
- }
-
- asm volatile ("mov $fixed1, %%rsi \n\t"
- "mov %%rsp, %%rdx \n\t"
- "cmp $0, %[user] \n\t"
- "jz do_access \n\t"
- "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax \n\t"
- "pushq %[user_ds] \n\t"
- "pushq %[user_stack_top] \n\t"
- "pushfq \n\t"
- "pushq %[user_cs] \n\t"
- "pushq $do_access \n\t"
- "iretq \n"
- "do_access: \n\t"
- "cmp $0, %[fetch] \n\t"
- "jnz 2f \n\t"
- "cmp $0, %[write] \n\t"
- "jnz 1f \n\t"
- "mov (%[addr]), %[reg] \n\t"
- "jmp done \n\t"
- "1: mov %[reg], (%[addr]) \n\t"
- "jmp done \n\t"
- "2: call *%[addr] \n\t"
- "done: \n"
- "fixed1: \n"
- "int %[kernel_entry_vector] \n\t"
- "back_to_kernel:"
- : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp)
- : [addr]"r"(at->virt),
- [write]"r"(at->flags[AC_ACCESS_WRITE]),
- [user]"r"(at->flags[AC_ACCESS_USER]),
- [fetch]"r"(at->flags[AC_ACCESS_FETCH]),
- [user_ds]"i"(32+3),
- [user_cs]"i"(24+3),
- [user_stack_top]"r"(user_stack + sizeof user_stack),
- [kernel_entry_vector]"i"(0x20)
- : "rsi");
-
- asm volatile (".section .text.pf \n\t"
- "page_fault: \n\t"
- "pop %rbx \n\t"
- "mov %rsi, (%rsp) \n\t"
- "movl $1, %eax \n\t"
- "iretq \n\t"
- ".section .text");
-
- asm volatile (".section .text.entry \n\t"
- "kernel_entry: \n\t"
- "mov %rdx, %rsp \n\t"
- "jmp back_to_kernel \n\t"
- ".section .text");
-
- ac_test_check(at, &success, fault && !at->expected_fault,
- "unexpected fault");
- ac_test_check(at, &success, !fault && at->expected_fault,
- "unexpected access");
- ac_test_check(at, &success, fault && e != at->expected_error,
- "error code %x expected %x", e, at->expected_error);
- ac_test_check(at, &success, at->ptep && *at->ptep != at->expected_pte,
- "pte %x expected %x", *at->ptep, at->expected_pte);
- ac_test_check(at, &success,
- !pt_match(*at->pdep, at->expected_pde, at->ignore_pde),
- "pde %x expected %x", *at->pdep, at->expected_pde);
-
- if (success && verbose) {
- printf("PASS\n");
- }
- return success;
-}
-
-static void ac_test_show(ac_test_t *at)
-{
- char line[5000];
-
- *line = 0;
- strcat(line, "test");
- for (int i = 0; i < NR_AC_FLAGS; ++i)
- if (at->flags[i]) {
- strcat(line, " ");
- strcat(line, ac_names[i]);
- }
- strcat(line, ": ");
- printf("%s", line);
-}
-
-/*
- * This test case is used to triger the bug which is fixed by
- * commit e09e90a5 in the kvm tree
- */
-static int corrupt_hugepage_triger(ac_pool_t *pool)
-{
- ac_test_t at1, at2;
-
- ac_test_init(&at1, (void *)(0x123400000000));
- ac_test_init(&at2, (void *)(0x666600000000));
-
- at2.flags[AC_CPU_CR0_WP] = 1;
- at2.flags[AC_PDE_PSE] = 1;
- at2.flags[AC_PDE_PRESENT] = 1;
- ac_test_setup_pte(&at2, pool);
- if (!ac_test_do_access(&at2))
- goto err;
-
- at1.flags[AC_CPU_CR0_WP] = 1;
- at1.flags[AC_PDE_PSE] = 1;
- at1.flags[AC_PDE_WRITABLE] = 1;
- at1.flags[AC_PDE_PRESENT] = 1;
- ac_test_setup_pte(&at1, pool);
- if (!ac_test_do_access(&at1))
- goto err;
-
- at1.flags[AC_ACCESS_WRITE] = 1;
- ac_set_expected_status(&at1);
- if (!ac_test_do_access(&at1))
- goto err;
-
- at2.flags[AC_ACCESS_WRITE] = 1;
- ac_set_expected_status(&at2);
- if (!ac_test_do_access(&at2))
- goto err;
-
- return 1;
-
-err:
- printf("corrupt_hugepage_triger test fail\n");
- return 0;
-}
-
-int ac_test_exec(ac_test_t *at, ac_pool_t *pool)
-{
- int r;
-
- if (verbose) {
- ac_test_show(at);
- }
- ac_test_setup_pte(at, pool);
- r = ac_test_do_access(at);
- return r;
-}
-
-int ac_test_run(void)
-{
- ac_test_t at;
- ac_pool_t pool;
- int tests, successes;
-
- printf("run\n");
- tests = successes = 0;
- ac_env_int(&pool);
- ac_test_init(&at, (void *)(0x123400000000 + 16 * smp_id()));
- do {
- ++tests;
- successes += ac_test_exec(&at, &pool);
- } while (ac_test_bump(&at));
-
- ++tests;
- successes += corrupt_hugepage_triger(&pool);
-
- printf("\n%d tests, %d failures\n", tests, tests - successes);
-
- return successes == tests;
-}
-
-int main()
-{
- int r;
-
- printf("starting test\n\n");
- r = ac_test_run();
- return r ? 0 : 1;
-}