xref: /kvm-unit-tests/x86/pku.c (revision 2c96b77ec9d3b1fcec7525174e23a6240ee05949)
1 #include "libcflat.h"
2 #include <alloc_page.h>
3 #include "x86/desc.h"
4 #include "x86/processor.h"
5 #include "x86/vm.h"
6 #include "x86/msr.h"
7 
8 #define CR0_WP_MASK      (1UL << 16)
9 #define PTE_PKEY_BIT     59
10 #define USER_BASE        (1 << 23)
11 #define USER_VAR(v)      (*((__typeof__(&(v))) (((unsigned long)&v) + USER_BASE)))
12 
13 volatile int pf_count = 0;
14 volatile unsigned save;
15 volatile unsigned test;
16 
17 static void set_cr0_wp(int wp)
18 {
19     unsigned long cr0 = read_cr0();
20 
21     cr0 &= ~CR0_WP_MASK;
22     if (wp)
23         cr0 |= CR0_WP_MASK;
24     write_cr0(cr0);
25 }
26 
27 void do_pf_tss(unsigned long error_code);
28 void do_pf_tss(unsigned long error_code)
29 {
30     pf_count++;
31     save = test;
32     write_pkru(0);
33 }
34 
35 extern void pf_tss(void);
36 
37 asm ("pf_tss: \n\t"
38 #ifdef __x86_64__
39     // no task on x86_64, save/restore caller-save regs
40     "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n"
41     "push %r8; push %r9; push %r10; push %r11\n"
42     "mov 9*8(%rsp),%rdi\n"
43 #endif
44     "call do_pf_tss \n\t"
45 #ifdef __x86_64__
46     "pop %r11; pop %r10; pop %r9; pop %r8\n"
47     "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n"
48 #endif
49     "add $"S", %"R "sp\n\t" // discard error code
50     "iret"W" \n\t"
51     "jmp pf_tss\n\t"
52     );
53 
54 static void init_test(void)
55 {
56     pf_count = 0;
57 
58     invlpg(&test);
59     invlpg(&USER_VAR(test));
60     write_pkru(0);
61     set_cr0_wp(0);
62 }
63 
64 int main(int ac, char **av)
65 {
66     unsigned long i;
67     unsigned int pkey = 0x2;
68     unsigned int pkru_ad = 0x10;
69     unsigned int pkru_wd = 0x20;
70 
71     if (!this_cpu_has(X86_FEATURE_PKU)) {
72         printf("PKU not enabled\n");
73         return report_summary();
74     }
75 
76     setup_vm();
77     setup_alt_stack();
78     set_intr_alt_stack(14, pf_tss);
79 
80     if (reserve_pages(USER_BASE, USER_BASE >> 12))
81         report_abort("Could not reserve memory");
82     for (i = 0; i < USER_BASE; i += PAGE_SIZE) {
83         *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~PT_USER_MASK;
84         *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT);
85         invlpg((void *)i);
86     }
87 
88     for (i = USER_BASE; i < 2 * USER_BASE; i += PAGE_SIZE) {
89         *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~USER_BASE;
90         *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT);
91         invlpg((void *)i);
92     }
93 
94     write_cr4(read_cr4() | X86_CR4_PKE);
95     write_cr3(read_cr3());
96 
97     init_test();
98     set_cr0_wp(1);
99     write_pkru(pkru_ad);
100     test = 21;
101     report(pf_count == 0 && test == 21,
102            "write to supervisor page when pkru is ad and wp == 1");
103 
104     init_test();
105     set_cr0_wp(0);
106     write_pkru(pkru_ad);
107     test = 22;
108     report(pf_count == 0 && test == 22,
109            "write to supervisor page when pkru is ad and wp == 0");
110 
111     init_test();
112     set_cr0_wp(1);
113     write_pkru(pkru_wd);
114     test = 23;
115     report(pf_count == 0 && test == 23,
116            "write to supervisor page when pkru is wd and wp == 1");
117 
118     init_test();
119     set_cr0_wp(0);
120     write_pkru(pkru_wd);
121     test = 24;
122     report(pf_count == 0 && test == 24,
123            "write to supervisor page when pkru is wd and wp == 0");
124 
125     init_test();
126     write_pkru(pkru_wd);
127     set_cr0_wp(0);
128     USER_VAR(test) = 25;
129     report(pf_count == 0 && test == 25,
130            "write to user page when pkru is wd and wp == 0");
131 
132     init_test();
133     write_pkru(pkru_wd);
134     set_cr0_wp(1);
135     USER_VAR(test) = 26;
136     report(pf_count == 1 && test == 26 && save == 25,
137            "write to user page when pkru is wd and wp == 1");
138 
139     init_test();
140     write_pkru(pkru_ad);
141     (void)USER_VAR(test);
142     report(pf_count == 1 && save == 26, "read from user page when pkru is ad");
143 
144     // TODO: implicit kernel access from ring 3 (e.g. int)
145 
146     return report_summary();
147 }
148