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