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