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