|  | /* Basic PCID & INVPCID functionality test */ | 
|  |  | 
|  | #include "libcflat.h" | 
|  | #include "processor.h" | 
|  | #include "desc.h" | 
|  |  | 
|  | struct invpcid_desc { | 
|  | unsigned long pcid : 12; | 
|  | unsigned long rsv  : 52; | 
|  | unsigned long addr : 64; | 
|  | }; | 
|  |  | 
|  | static int write_cr0_checking(unsigned long val) | 
|  | { | 
|  | asm volatile(ASM_TRY("1f") | 
|  | "mov %0, %%cr0\n\t" | 
|  | "1:": : "r" (val)); | 
|  | return exception_vector(); | 
|  | } | 
|  |  | 
|  | static int invpcid_checking(unsigned long type, void *desc) | 
|  | { | 
|  | asm volatile (ASM_TRY("1f") | 
|  | ".byte 0x66,0x0f,0x38,0x82,0x18 \n\t" /* invpcid (%rax), %rbx */ | 
|  | "1:" : : "a" (desc), "b" (type)); | 
|  | return exception_vector(); | 
|  | } | 
|  |  | 
|  | static void test_cpuid_consistency(int pcid_enabled, int invpcid_enabled) | 
|  | { | 
|  | int passed = !(!pcid_enabled && invpcid_enabled); | 
|  | report(passed, "CPUID consistency"); | 
|  | } | 
|  |  | 
|  | static void test_pcid_enabled(void) | 
|  | { | 
|  | int passed = 0; | 
|  | ulong cr0 = read_cr0(), cr3 = read_cr3(), cr4 = read_cr4(); | 
|  |  | 
|  | /* try setting CR4.PCIDE, no exception expected */ | 
|  | if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0) | 
|  | goto report; | 
|  |  | 
|  | /* try clearing CR0.PG when CR4.PCIDE=1, #GP expected */ | 
|  | if (write_cr0_checking(cr0 & ~X86_CR0_PG) != GP_VECTOR) | 
|  | goto report; | 
|  |  | 
|  | write_cr4(cr4); | 
|  |  | 
|  | /* try setting CR4.PCIDE when CR3[11:0] != 0 , #GP expected */ | 
|  | write_cr3(cr3 | 0x001); | 
|  | if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR) | 
|  | goto report; | 
|  | write_cr3(cr3); | 
|  |  | 
|  | passed = 1; | 
|  |  | 
|  | report: | 
|  | report(passed, "Test on PCID when enabled"); | 
|  | } | 
|  |  | 
|  | static void test_pcid_disabled(void) | 
|  | { | 
|  | int passed = 0; | 
|  | ulong cr4 = read_cr4(); | 
|  |  | 
|  | /* try setting CR4.PCIDE, #GP expected */ | 
|  | if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR) | 
|  | goto report; | 
|  |  | 
|  | passed = 1; | 
|  |  | 
|  | report: | 
|  | report(passed, "Test on PCID when disabled"); | 
|  | } | 
|  |  | 
|  | static void test_invpcid_enabled(void) | 
|  | { | 
|  | int passed = 0; | 
|  | ulong cr4 = read_cr4(); | 
|  | struct invpcid_desc desc; | 
|  | desc.rsv = 0; | 
|  |  | 
|  | /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=1 | 
|  | * no exception expected | 
|  | */ | 
|  | desc.pcid = 0; | 
|  | if (invpcid_checking(1, &desc) != 0) | 
|  | goto report; | 
|  |  | 
|  | /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=1 | 
|  | * #GP expected | 
|  | */ | 
|  | desc.pcid = 1; | 
|  | if (invpcid_checking(1, &desc) != GP_VECTOR) | 
|  | goto report; | 
|  |  | 
|  | if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0) | 
|  | goto report; | 
|  |  | 
|  | /* try executing invpcid when CR4.PCIDE=1 | 
|  | * no exception expected | 
|  | */ | 
|  | desc.pcid = 10; | 
|  | if (invpcid_checking(2, &desc) != 0) | 
|  | goto report; | 
|  |  | 
|  | passed = 1; | 
|  |  | 
|  | report: | 
|  | report(passed, "Test on INVPCID when enabled"); | 
|  | } | 
|  |  | 
|  | static void test_invpcid_disabled(void) | 
|  | { | 
|  | int passed = 0; | 
|  | struct invpcid_desc desc; | 
|  |  | 
|  | /* try executing invpcid, #UD expected */ | 
|  | if (invpcid_checking(2, &desc) != UD_VECTOR) | 
|  | goto report; | 
|  |  | 
|  | passed = 1; | 
|  |  | 
|  | report: | 
|  | report(passed, "Test on INVPCID when disabled"); | 
|  | } | 
|  |  | 
|  | int main(int ac, char **av) | 
|  | { | 
|  | int pcid_enabled = 0, invpcid_enabled = 0; | 
|  |  | 
|  | setup_idt(); | 
|  |  | 
|  | if (this_cpu_has(X86_FEATURE_PCID)) | 
|  | pcid_enabled = 1; | 
|  | if (this_cpu_has(X86_FEATURE_INVPCID)) | 
|  | invpcid_enabled = 1; | 
|  |  | 
|  | test_cpuid_consistency(pcid_enabled, invpcid_enabled); | 
|  |  | 
|  | if (pcid_enabled) | 
|  | test_pcid_enabled(); | 
|  | else | 
|  | test_pcid_disabled(); | 
|  |  | 
|  | if (invpcid_enabled) | 
|  | test_invpcid_enabled(); | 
|  | else | 
|  | test_invpcid_disabled(); | 
|  |  | 
|  | return report_summary(); | 
|  | } |