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