Chenyi Qiang | fdae609 | 2020-11-05 16:18:05 +0800 | [diff] [blame] | 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 | |
Chenyi Qiang | fdae609 | 2020-11-05 16:18:05 +0800 | [diff] [blame] | 8 | #define PTE_PKEY_BIT 59 |
| 9 | #define SUPER_BASE (1 << 23) |
| 10 | #define SUPER_VAR(v) (*((__typeof__(&(v))) (((unsigned long)&v) + SUPER_BASE))) |
| 11 | |
| 12 | volatile int pf_count = 0; |
| 13 | volatile unsigned save; |
| 14 | volatile unsigned test; |
| 15 | |
| 16 | static void set_cr0_wp(int wp) |
| 17 | { |
| 18 | unsigned long cr0 = read_cr0(); |
| 19 | |
Mathias Krause | 3ba5c21 | 2023-04-04 09:53:32 -0700 | [diff] [blame] | 20 | cr0 &= ~X86_CR0_WP; |
Chenyi Qiang | fdae609 | 2020-11-05 16:18:05 +0800 | [diff] [blame] | 21 | if (wp) |
Mathias Krause | 3ba5c21 | 2023-04-04 09:53:32 -0700 | [diff] [blame] | 22 | cr0 |= X86_CR0_WP; |
Chenyi Qiang | fdae609 | 2020-11-05 16:18:05 +0800 | [diff] [blame] | 23 | write_cr0(cr0); |
| 24 | } |
| 25 | |
| 26 | void do_pf_tss(unsigned long error_code); |
| 27 | void do_pf_tss(unsigned long error_code) |
| 28 | { |
| 29 | printf("#PF handler, error code: 0x%lx\n", error_code); |
| 30 | pf_count++; |
| 31 | save = test; |
| 32 | wrmsr(MSR_IA32_PKRS, 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 | "mov 9*8(%rsp), %rdi\n" |
| 43 | #endif |
| 44 | "call do_pf_tss \n\t" |
| 45 | #ifdef __x86_64__ |
| 46 | "pop %r11; pop %r10; pop %r9; pop %r8\n" |
| 47 | "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n" |
| 48 | #endif |
| 49 | "add $"S", %"R "sp\n\t" // discard error code |
| 50 | "iret"W" \n\t" |
| 51 | "jmp pf_tss\n\t" |
| 52 | ); |
| 53 | |
| 54 | static void init_test(void) |
| 55 | { |
| 56 | pf_count = 0; |
| 57 | |
| 58 | invlpg(&test); |
| 59 | invlpg(&SUPER_VAR(test)); |
| 60 | wrmsr(MSR_IA32_PKRS, 0); |
| 61 | set_cr0_wp(0); |
| 62 | } |
| 63 | |
| 64 | int main(int ac, char **av) |
| 65 | { |
| 66 | unsigned long i; |
| 67 | unsigned int pkey = 0x2; |
| 68 | unsigned int pkrs_ad = 0x10; |
| 69 | unsigned int pkrs_wd = 0x20; |
| 70 | |
| 71 | if (!this_cpu_has(X86_FEATURE_PKS)) { |
| 72 | printf("PKS not enabled\n"); |
| 73 | return report_summary(); |
| 74 | } |
| 75 | |
| 76 | setup_vm(); |
| 77 | setup_alt_stack(); |
| 78 | set_intr_alt_stack(14, pf_tss); |
Chenyi Qiang | fdae609 | 2020-11-05 16:18:05 +0800 | [diff] [blame] | 79 | |
| 80 | if (reserve_pages(SUPER_BASE, SUPER_BASE >> 12)) |
| 81 | report_abort("Could not reserve memory"); |
| 82 | |
| 83 | for (i = 0; i < SUPER_BASE; i += PAGE_SIZE) { |
| 84 | *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT); |
| 85 | invlpg((void *)i); |
| 86 | } |
| 87 | |
| 88 | // Present the same 16MB as supervisor pages in the 16MB-32MB range |
| 89 | for (i = SUPER_BASE; i < 2 * SUPER_BASE; i += PAGE_SIZE) { |
| 90 | *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~SUPER_BASE; |
| 91 | *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~PT_USER_MASK; |
| 92 | *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) |= ((unsigned long)pkey << PTE_PKEY_BIT); |
| 93 | invlpg((void *)i); |
| 94 | } |
| 95 | |
| 96 | write_cr4(read_cr4() | X86_CR4_PKS); |
| 97 | write_cr3(read_cr3()); |
| 98 | |
| 99 | init_test(); |
| 100 | set_cr0_wp(1); |
| 101 | wrmsr(MSR_IA32_PKRS, pkrs_ad); |
| 102 | SUPER_VAR(test) = 21; |
| 103 | report(pf_count == 1 && test == 21 && save == 0, |
| 104 | "write to supervisor page when pkrs is ad and wp == 1"); |
| 105 | |
| 106 | init_test(); |
| 107 | set_cr0_wp(0); |
| 108 | wrmsr(MSR_IA32_PKRS, pkrs_ad); |
| 109 | SUPER_VAR(test) = 22; |
| 110 | report(pf_count == 1 && test == 22 && save == 21, |
| 111 | "write to supervisor page when pkrs is ad and wp == 0"); |
| 112 | |
| 113 | init_test(); |
| 114 | set_cr0_wp(1); |
| 115 | wrmsr(MSR_IA32_PKRS, pkrs_wd); |
| 116 | SUPER_VAR(test) = 23; |
| 117 | report(pf_count == 1 && test == 23 && save == 22, |
| 118 | "write to supervisor page when pkrs is wd and wp == 1"); |
| 119 | |
| 120 | init_test(); |
| 121 | set_cr0_wp(0); |
| 122 | wrmsr(MSR_IA32_PKRS, pkrs_wd); |
| 123 | SUPER_VAR(test) = 24; |
| 124 | report(pf_count == 0 && test == 24, |
| 125 | "write to supervisor page when pkrs is wd and wp == 0"); |
| 126 | |
| 127 | init_test(); |
| 128 | set_cr0_wp(0); |
| 129 | wrmsr(MSR_IA32_PKRS, pkrs_wd); |
| 130 | test = 25; |
| 131 | report(pf_count == 0 && test == 25, |
| 132 | "write to user page when pkrs is wd and wp == 0"); |
| 133 | |
| 134 | init_test(); |
| 135 | set_cr0_wp(1); |
| 136 | wrmsr(MSR_IA32_PKRS, pkrs_wd); |
| 137 | test = 26; |
| 138 | report(pf_count == 0 && test == 26, |
| 139 | "write to user page when pkrs is wd and wp == 1"); |
| 140 | |
| 141 | init_test(); |
| 142 | wrmsr(MSR_IA32_PKRS, pkrs_ad); |
| 143 | (void)((__typeof__(&(test))) (((unsigned long)&test))); |
| 144 | report(pf_count == 0, "read from user page when pkrs is ad"); |
| 145 | |
| 146 | return report_summary(); |
| 147 | } |