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