1 /* Basic PCID & INVPCID functionality test */ 2 3 #include "libcflat.h" 4 #include "processor.h" 5 #include "desc.h" 6 7 struct invpcid_desc { 8 u64 pcid : 12; 9 u64 rsv : 52; 10 u64 addr : 64; 11 }; 12 13 static int invpcid_safe(unsigned long type, void *desc) 14 { 15 asm volatile (ASM_TRY("1f") 16 ".byte 0x66,0x0f,0x38,0x82,0x18 \n\t" /* invpcid (%rax), %rbx */ 17 "1:" : : "a" (desc), "b" (type)); 18 return exception_vector(); 19 } 20 21 static void test_pcid_enabled(void) 22 { 23 int passed = 0; 24 ulong cr0 = read_cr0(), cr3 = read_cr3(), cr4 = read_cr4(); 25 26 /* try setting CR4.PCIDE, no exception expected */ 27 if (write_cr4_safe(cr4 | X86_CR4_PCIDE) != 0) 28 goto report; 29 30 /* try clearing CR0.PG when CR4.PCIDE=1, #GP expected */ 31 if (write_cr0_safe(cr0 & ~X86_CR0_PG) != GP_VECTOR) 32 goto report; 33 34 write_cr4(cr4); 35 36 /* try setting CR4.PCIDE when CR3[11:0] != 0 , #GP expected */ 37 write_cr3(cr3 | 0x001); 38 if (write_cr4_safe(cr4 | X86_CR4_PCIDE) != GP_VECTOR) 39 goto report; 40 write_cr3(cr3); 41 42 passed = 1; 43 44 report: 45 report(passed, "Test on PCID when enabled"); 46 } 47 48 static void test_pcid_disabled(void) 49 { 50 int passed = 0; 51 ulong cr4 = read_cr4(); 52 53 /* try setting CR4.PCIDE, #GP expected */ 54 if (write_cr4_safe(cr4 | X86_CR4_PCIDE) != GP_VECTOR) 55 goto report; 56 57 passed = 1; 58 59 report: 60 report(passed, "Test on PCID when disabled"); 61 } 62 63 static void test_invpcid_enabled(int pcid_enabled) 64 { 65 int passed = 0, i; 66 ulong cr4 = read_cr4(); 67 struct invpcid_desc desc; 68 69 memset(&desc, 0, sizeof(desc)); 70 71 /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=0..3 72 * no exception expected 73 */ 74 for (i = 0; i < 4; i++) { 75 if (invpcid_safe(i, &desc) != 0) 76 goto report; 77 } 78 79 /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=0..1 80 * #GP expected 81 */ 82 desc.pcid = 1; 83 for (i = 0; i < 2; i++) { 84 if (invpcid_safe(i, &desc) != GP_VECTOR) 85 goto report; 86 } 87 88 /* Skip tests that require the PCIDE=1 if PCID isn't supported. */ 89 if (!pcid_enabled) 90 goto success; 91 92 if (write_cr4_safe(cr4 | X86_CR4_PCIDE) != 0) 93 goto report; 94 95 /* try executing invpcid when CR4.PCIDE=1 96 * no exception expected 97 */ 98 desc.pcid = 10; 99 if (invpcid_safe(2, &desc) != 0) 100 goto report; 101 102 success: 103 passed = 1; 104 105 report: 106 report(passed, "Test on INVPCID when enabled"); 107 } 108 109 static void test_invpcid_disabled(void) 110 { 111 int passed = 0; 112 struct invpcid_desc desc; 113 114 /* try executing invpcid, #UD expected */ 115 if (invpcid_safe(2, &desc) != UD_VECTOR) 116 goto report; 117 118 passed = 1; 119 120 report: 121 report(passed, "Test on INVPCID when disabled"); 122 } 123 124 int main(int ac, char **av) 125 { 126 int pcid_enabled = 0, invpcid_enabled = 0; 127 128 if (this_cpu_has(X86_FEATURE_PCID)) 129 pcid_enabled = 1; 130 if (this_cpu_has(X86_FEATURE_INVPCID)) 131 invpcid_enabled = 1; 132 133 if (pcid_enabled) 134 test_pcid_enabled(); 135 else 136 test_pcid_disabled(); 137 138 if (invpcid_enabled) 139 test_invpcid_enabled(pcid_enabled); 140 else 141 test_invpcid_disabled(); 142 143 return report_summary(); 144 } 145