1defad3c7SHuaitong Han #include "libcflat.h"
2a6fcc0fbSClaudio 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 PTE_PKEY_BIT 59
9a6fcc0fbSClaudio Imbrenda #define USER_BASE (1 << 23)
10defad3c7SHuaitong Han #define USER_VAR(v) (*((__typeof__(&(v))) (((unsigned long)&v) + USER_BASE)))
11defad3c7SHuaitong Han
12defad3c7SHuaitong Han volatile int pf_count = 0;
13defad3c7SHuaitong Han volatile unsigned save;
14defad3c7SHuaitong Han volatile unsigned test;
15defad3c7SHuaitong Han
set_cr0_wp(int wp)1606846df5SThomas Huth static void set_cr0_wp(int wp)
17defad3c7SHuaitong Han {
18defad3c7SHuaitong Han unsigned long cr0 = read_cr0();
19defad3c7SHuaitong Han
20*3ba5c21fSMathias Krause cr0 &= ~X86_CR0_WP;
21defad3c7SHuaitong Han if (wp)
22*3ba5c21fSMathias Krause cr0 |= X86_CR0_WP;
23defad3c7SHuaitong Han write_cr0(cr0);
24defad3c7SHuaitong Han }
25defad3c7SHuaitong Han
2606846df5SThomas Huth void do_pf_tss(unsigned long error_code);
do_pf_tss(unsigned long error_code)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"
41d0285a18SChenyi Qiang "mov 9*8(%rsp),%rdi\n"
42defad3c7SHuaitong Han #endif
43defad3c7SHuaitong Han "call do_pf_tss \n\t"
44defad3c7SHuaitong Han #ifdef __x86_64__
45defad3c7SHuaitong Han "pop %r11; pop %r10; pop %r9; pop %r8\n"
46defad3c7SHuaitong Han "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n"
47defad3c7SHuaitong Han #endif
48defad3c7SHuaitong Han "add $"S", %"R "sp\n\t" // discard error code
49defad3c7SHuaitong Han "iret"W" \n\t"
50defad3c7SHuaitong Han "jmp pf_tss\n\t"
51defad3c7SHuaitong Han );
52defad3c7SHuaitong Han
init_test(void)537db17e21SThomas Huth static void init_test(void)
54defad3c7SHuaitong Han {
55defad3c7SHuaitong Han pf_count = 0;
56defad3c7SHuaitong Han
57defad3c7SHuaitong Han invlpg(&test);
58defad3c7SHuaitong Han invlpg(&USER_VAR(test));
59defad3c7SHuaitong Han write_pkru(0);
60defad3c7SHuaitong Han set_cr0_wp(0);
61defad3c7SHuaitong Han }
62defad3c7SHuaitong Han
main(int ac,char ** av)63defad3c7SHuaitong Han int main(int ac, char **av)
64defad3c7SHuaitong Han {
65defad3c7SHuaitong Han unsigned long i;
66defad3c7SHuaitong Han unsigned int pkey = 0x2;
67defad3c7SHuaitong Han unsigned int pkru_ad = 0x10;
68defad3c7SHuaitong Han unsigned int pkru_wd = 0x20;
69defad3c7SHuaitong Han
70badc98caSKrish Sadhukhan if (!this_cpu_has(X86_FEATURE_PKU)) {
7132b9603cSRadim Krčmář printf("PKU not enabled\n");
7232b9603cSRadim Krčmář return report_summary();
73defad3c7SHuaitong Han }
74defad3c7SHuaitong Han
75defad3c7SHuaitong Han setup_vm();
76defad3c7SHuaitong Han setup_alt_stack();
77defad3c7SHuaitong Han set_intr_alt_stack(14, pf_tss);
78defad3c7SHuaitong Han
79a6fcc0fbSClaudio Imbrenda if (reserve_pages(USER_BASE, USER_BASE >> 12))
80a6fcc0fbSClaudio Imbrenda report_abort("Could not reserve memory");
81defad3c7SHuaitong Han for (i = 0; i < USER_BASE; i += PAGE_SIZE) {
82d10d16e1SAlexander Gordeev *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~PT_USER_MASK;
83defad3c7SHuaitong Han *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT);
84defad3c7SHuaitong Han invlpg((void *)i);
85defad3c7SHuaitong Han }
86defad3c7SHuaitong Han
87defad3c7SHuaitong Han for (i = USER_BASE; i < 2 * USER_BASE; i += PAGE_SIZE) {
88defad3c7SHuaitong Han *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~USER_BASE;
89defad3c7SHuaitong Han *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT);
90defad3c7SHuaitong Han invlpg((void *)i);
91defad3c7SHuaitong Han }
92defad3c7SHuaitong Han
93defad3c7SHuaitong Han write_cr4(read_cr4() | X86_CR4_PKE);
94defad3c7SHuaitong Han write_cr3(read_cr3());
95defad3c7SHuaitong Han
96defad3c7SHuaitong Han init_test();
97defad3c7SHuaitong Han set_cr0_wp(1);
98defad3c7SHuaitong Han write_pkru(pkru_ad);
99defad3c7SHuaitong Han test = 21;
100a299895bSThomas Huth report(pf_count == 0 && test == 21,
101a299895bSThomas Huth "write to supervisor page when pkru is ad and wp == 1");
102defad3c7SHuaitong Han
103defad3c7SHuaitong Han init_test();
104defad3c7SHuaitong Han set_cr0_wp(0);
105defad3c7SHuaitong Han write_pkru(pkru_ad);
106defad3c7SHuaitong Han test = 22;
107a299895bSThomas Huth report(pf_count == 0 && test == 22,
108a299895bSThomas Huth "write to supervisor page when pkru is ad and wp == 0");
109defad3c7SHuaitong Han
110defad3c7SHuaitong Han init_test();
111defad3c7SHuaitong Han set_cr0_wp(1);
112defad3c7SHuaitong Han write_pkru(pkru_wd);
113defad3c7SHuaitong Han test = 23;
114a299895bSThomas Huth report(pf_count == 0 && test == 23,
115a299895bSThomas Huth "write to supervisor page when pkru is wd and wp == 1");
116defad3c7SHuaitong Han
117defad3c7SHuaitong Han init_test();
118defad3c7SHuaitong Han set_cr0_wp(0);
119defad3c7SHuaitong Han write_pkru(pkru_wd);
120defad3c7SHuaitong Han test = 24;
121a299895bSThomas Huth report(pf_count == 0 && test == 24,
122a299895bSThomas Huth "write to supervisor page when pkru is wd and wp == 0");
123defad3c7SHuaitong Han
124defad3c7SHuaitong Han init_test();
125defad3c7SHuaitong Han write_pkru(pkru_wd);
126defad3c7SHuaitong Han set_cr0_wp(0);
127defad3c7SHuaitong Han USER_VAR(test) = 25;
128a299895bSThomas Huth report(pf_count == 0 && test == 25,
129a299895bSThomas Huth "write to user page when pkru is wd and wp == 0");
130defad3c7SHuaitong Han
131defad3c7SHuaitong Han init_test();
132defad3c7SHuaitong Han write_pkru(pkru_wd);
133defad3c7SHuaitong Han set_cr0_wp(1);
134defad3c7SHuaitong Han USER_VAR(test) = 26;
135a299895bSThomas Huth report(pf_count == 1 && test == 26 && save == 25,
136a299895bSThomas Huth "write to user page when pkru is wd and wp == 1");
137defad3c7SHuaitong Han
138defad3c7SHuaitong Han init_test();
139defad3c7SHuaitong Han write_pkru(pkru_ad);
140defad3c7SHuaitong Han (void)USER_VAR(test);
141a299895bSThomas Huth report(pf_count == 1 && save == 26, "read from user page when pkru is ad");
142defad3c7SHuaitong Han
143defad3c7SHuaitong Han // TODO: implicit kernel access from ring 3 (e.g. int)
144defad3c7SHuaitong Han
145defad3c7SHuaitong Han return report_summary();
146defad3c7SHuaitong Han }
147