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 write_cr3(cr3); 83 84 passed = 1; 85 86 report: 87 report("Test on PCID when enabled", passed); 88 } 89 90 void test_pcid_disabled(void) 91 { 92 int passed = 0; 93 ulong cr4 = read_cr4(); 94 95 /* try setting CR4.PCIDE, #GP expected */ 96 if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR) 97 goto report; 98 99 passed = 1; 100 101 report: 102 report("Test on PCID when disabled", passed); 103 } 104 105 void test_invpcid_enabled(void) 106 { 107 int passed = 0; 108 ulong cr4 = read_cr4(); 109 struct invpcid_desc desc; 110 desc.rsv = 0; 111 112 /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=1 113 * no exception expected 114 */ 115 desc.pcid = 0; 116 if (invpcid_checking(1, &desc) != 0) 117 goto report; 118 119 /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=1 120 * #GP expected 121 */ 122 desc.pcid = 1; 123 if (invpcid_checking(1, &desc) != GP_VECTOR) 124 goto report; 125 126 if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0) 127 goto report; 128 129 /* try executing invpcid when CR4.PCIDE=1 130 * no exception expected 131 */ 132 desc.pcid = 10; 133 if (invpcid_checking(2, &desc) != 0) 134 goto report; 135 136 passed = 1; 137 138 report: 139 report("Test on INVPCID when enabled", passed); 140 } 141 142 void test_invpcid_disabled(void) 143 { 144 int passed = 0; 145 struct invpcid_desc desc; 146 147 /* try executing invpcid, #UD expected */ 148 if (invpcid_checking(2, &desc) != UD_VECTOR) 149 goto report; 150 151 passed = 1; 152 153 report: 154 report("Test on INVPCID when disabled", passed); 155 } 156 157 int main(int ac, char **av) 158 { 159 struct cpuid _cpuid; 160 int pcid_enabled = 0, invpcid_enabled = 0; 161 162 setup_idt(); 163 164 _cpuid = cpuid(1); 165 if (_cpuid.c & X86_FEATURE_PCID) 166 pcid_enabled = 1; 167 _cpuid = cpuid_indexed(7, 0); 168 if (_cpuid.b & X86_FEATURE_INVPCID) 169 invpcid_enabled = 1; 170 171 test_cpuid_consistency(pcid_enabled, invpcid_enabled); 172 173 if (pcid_enabled) 174 test_pcid_enabled(); 175 else 176 test_pcid_disabled(); 177 178 if (invpcid_enabled) 179 test_invpcid_enabled(); 180 else 181 test_invpcid_disabled(); 182 183 printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed); 184 185 return nr_passed == nr_tests ? 0 : 1; 186 } 187