1 /* Basic PCID & INVPCID functionality test */ 2 3 #include "libcflat.h" 4 #include "processor.h" 5 #include "desc.h" 6 7 int nr_passed, nr_tests; 8 9 #define X86_FEATURE_PCID (1 << 17) 10 #define X86_FEATURE_INVPCID (1 << 10) 11 12 #define X86_CR0_PG (1 << 31) 13 #define X86_CR3_PCID_MASK 0x00000fff 14 #define X86_CR4_PCIDE (1 << 17) 15 16 #define X86_IA32_EFER 0xc0000080 17 #define X86_EFER_LMA (1UL << 8) 18 19 struct invpcid_desc { 20 unsigned long pcid : 12; 21 unsigned long rsv : 52; 22 unsigned long addr : 64; 23 }; 24 25 static void report(const char *name, int passed) 26 { 27 ++nr_tests; 28 if (passed) 29 ++nr_passed; 30 printf("%s: %s\n", name, passed ? "PASS" : "FAIL"); 31 } 32 33 int write_cr0_checking(unsigned long val) 34 { 35 asm volatile(ASM_TRY("1f") 36 "mov %0, %%cr0\n\t" 37 "1:": : "r" (val)); 38 return exception_vector(); 39 } 40 41 int write_cr4_checking(unsigned long val) 42 { 43 asm volatile(ASM_TRY("1f") 44 "mov %0, %%cr4\n\t" 45 "1:": : "r" (val)); 46 return exception_vector(); 47 } 48 49 int invpcid_checking(unsigned long type, void *desc) 50 { 51 asm volatile (ASM_TRY("1f") 52 ".byte 0x66,0x0f,0x38,0x82,0x18 \n\t" /* invpcid (%rax), %rbx */ 53 "1:" : : "a" (desc), "b" (type)); 54 return exception_vector(); 55 } 56 57 void test_cpuid_consistency(int pcid_enabled, int invpcid_enabled) 58 { 59 int passed = !(!pcid_enabled && invpcid_enabled); 60 report("CPUID consistency", passed); 61 } 62 63 void test_pcid_enabled(void) 64 { 65 int passed = 0; 66 ulong cr0 = read_cr0(), cr3 = read_cr3(), cr4 = read_cr4(); 67 68 /* try setting CR4.PCIDE, no exception expected */ 69 if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0) 70 goto report; 71 72 /* try clearing CR0.PG when CR4.PCIDE=1, #GP expected */ 73 if (write_cr0_checking(cr0 | X86_CR0_PG) != GP_VECTOR) 74 goto report; 75 76 write_cr4(cr4); 77 78 /* try setting CR4.PCIDE when CR3[11:0] != 0 , #GP expected */ 79 write_cr3(cr3 | 0x001); 80 if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR) 81 goto report; 82 83 passed = 1; 84 85 report: 86 report("Test on PCID when enabled", passed); 87 } 88 89 void test_pcid_disabled(void) 90 { 91 int passed = 0; 92 ulong cr4 = read_cr4(); 93 94 /* try setting CR4.PCIDE, #GP expected */ 95 if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR) 96 goto report; 97 98 passed = 1; 99 100 report: 101 report("Test on PCID when disabled", passed); 102 } 103 104 void test_invpcid_enabled(void) 105 { 106 int passed = 0; 107 ulong cr4 = read_cr4(); 108 struct invpcid_desc desc; 109 desc.rsv = 0; 110 111 /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=1 112 * no exception expected 113 */ 114 desc.pcid = 0; 115 if (invpcid_checking(1, &desc) != 0) 116 goto report; 117 118 /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=1 119 * #GP expected 120 */ 121 desc.pcid = 1; 122 if (invpcid_checking(1, &desc) != GP_VECTOR) 123 goto report; 124 125 if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0) 126 goto report; 127 128 /* try executing invpcid when CR4.PCIDE=1 129 * no exception expected 130 */ 131 desc.pcid = 10; 132 if (invpcid_checking(2, &desc) != 0) 133 goto report; 134 135 passed = 1; 136 137 report: 138 report("Test on INVPCID when enabled", passed); 139 } 140 141 void test_invpcid_disabled(void) 142 { 143 int passed = 0; 144 struct invpcid_desc desc; 145 146 /* try executing invpcid, #UD expected */ 147 if (invpcid_checking(2, &desc) != UD_VECTOR) 148 goto report; 149 150 passed = 1; 151 152 report: 153 report("Test on INVPCID when disabled", passed); 154 } 155 156 int main(int ac, char **av) 157 { 158 struct cpuid _cpuid; 159 int pcid_enabled = 0, invpcid_enabled = 0; 160 161 setup_idt(); 162 163 _cpuid = cpuid(1); 164 if (_cpuid.c & X86_FEATURE_PCID) 165 pcid_enabled = 1; 166 _cpuid = cpuid_indexed(7, 0); 167 if (_cpuid.b & X86_FEATURE_INVPCID) 168 invpcid_enabled = 1; 169 170 test_cpuid_consistency(pcid_enabled, invpcid_enabled); 171 172 if (pcid_enabled) 173 test_pcid_enabled(); 174 else 175 test_pcid_disabled(); 176 177 if (invpcid_enabled) 178 test_invpcid_enabled(); 179 else 180 test_invpcid_disabled(); 181 182 printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed); 183 184 return nr_passed == nr_tests ? 0 : 1; 185 } 186