1 #include "libcflat.h"
2 #include <alloc_page.h>
3 #include "x86/desc.h"
4 #include "x86/processor.h"
5 #include "x86/vm.h"
6 #include "x86/msr.h"
7
8 #define PTE_PKEY_BIT 59
9 #define USER_BASE (1 << 23)
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
set_cr0_wp(int wp)16 static void set_cr0_wp(int wp)
17 {
18 unsigned long cr0 = read_cr0();
19
20 cr0 &= ~X86_CR0_WP;
21 if (wp)
22 cr0 |= X86_CR0_WP;
23 write_cr0(cr0);
24 }
25
26 void do_pf_tss(unsigned long error_code);
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 "mov 9*8(%rsp),%rdi\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
init_test(void)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
main(int ac,char ** av)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 (!this_cpu_has(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
79 if (reserve_pages(USER_BASE, USER_BASE >> 12))
80 report_abort("Could not reserve memory");
81 for (i = 0; i < USER_BASE; i += PAGE_SIZE) {
82 *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~PT_USER_MASK;
83 *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT);
84 invlpg((void *)i);
85 }
86
87 for (i = USER_BASE; i < 2 * USER_BASE; i += PAGE_SIZE) {
88 *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~USER_BASE;
89 *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT);
90 invlpg((void *)i);
91 }
92
93 write_cr4(read_cr4() | X86_CR4_PKE);
94 write_cr3(read_cr3());
95
96 init_test();
97 set_cr0_wp(1);
98 write_pkru(pkru_ad);
99 test = 21;
100 report(pf_count == 0 && test == 21,
101 "write to supervisor page when pkru is ad and wp == 1");
102
103 init_test();
104 set_cr0_wp(0);
105 write_pkru(pkru_ad);
106 test = 22;
107 report(pf_count == 0 && test == 22,
108 "write to supervisor page when pkru is ad and wp == 0");
109
110 init_test();
111 set_cr0_wp(1);
112 write_pkru(pkru_wd);
113 test = 23;
114 report(pf_count == 0 && test == 23,
115 "write to supervisor page when pkru is wd and wp == 1");
116
117 init_test();
118 set_cr0_wp(0);
119 write_pkru(pkru_wd);
120 test = 24;
121 report(pf_count == 0 && test == 24,
122 "write to supervisor page when pkru is wd and wp == 0");
123
124 init_test();
125 write_pkru(pkru_wd);
126 set_cr0_wp(0);
127 USER_VAR(test) = 25;
128 report(pf_count == 0 && test == 25,
129 "write to user page when pkru is wd and wp == 0");
130
131 init_test();
132 write_pkru(pkru_wd);
133 set_cr0_wp(1);
134 USER_VAR(test) = 26;
135 report(pf_count == 1 && test == 26 && save == 25,
136 "write to user page when pkru is wd and wp == 1");
137
138 init_test();
139 write_pkru(pkru_ad);
140 (void)USER_VAR(test);
141 report(pf_count == 1 && save == 26, "read from user page when pkru is ad");
142
143 // TODO: implicit kernel access from ring 3 (e.g. int)
144
145 return report_summary();
146 }
147