Add unit test for basic functionality of PCID/INVPCID feature exposing in kvm. Changes from v1: Hard code 'invpcid' instruction Signed-off-by: Junjie Mao <junjie.mao@xxxxxxxxx> --- config-x86-common.mak | 2 + config-x86_64.mak | 3 +- x86/README | 1 + x86/pcid.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 4 + 5 files changed, 194 insertions(+), 1 deletions(-) create mode 100644 x86/pcid.c diff --git a/config-x86-common.mak b/config-x86-common.mak index c8fbda7..0273b64 100644 --- a/config-x86-common.mak +++ b/config-x86-common.mak @@ -90,6 +90,8 @@ $(TEST_DIR)/s3.elf: $(cstart.o) $(TEST_DIR)/s3.o $(TEST_DIR)/pmu.elf: $(cstart.o) $(TEST_DIR)/pmu.o +$(TEST_DIR)/pcid.elf: $(cstart.o) $(TEST_DIR)/pcid.o + arch_clean: $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \ $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o diff --git a/config-x86_64.mak b/config-x86_64.mak index b99cf85..4e525f5 100644 --- a/config-x86_64.mak +++ b/config-x86_64.mak @@ -6,7 +6,8 @@ CFLAGS += -D__x86_64__ tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \ $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \ - $(TEST_DIR)/xsave.flat $(TEST_DIR)/rmap_chain.flat + $(TEST_DIR)/xsave.flat $(TEST_DIR)/rmap_chain.flat \ + $(TEST_DIR)/pcid.flat tests += $(TEST_DIR)/svm.flat include config-x86-common.mak diff --git a/x86/README b/x86/README index 215fb2f..d644abd 100644 --- a/x86/README +++ b/x86/README @@ -13,3 +13,4 @@ smptest: run smp_id() on every cpu and compares return value to number tsc: write to tsc(0) and write to tsc(100000000000) and read it back vmexit: long loops for each: cpuid, vmcall, mov_from_cr8, mov_to_cr8, inl_pmtimer, ipi, ipi+halt kvmclock_test: test of wallclock, monotonic cycle and performance of kvmclock +pcid: basic functionality test of PCID/INVPCID feature \ No newline at end of file diff --git a/x86/pcid.c b/x86/pcid.c new file mode 100644 index 0000000..de0f6fe --- /dev/null +++ b/x86/pcid.c @@ -0,0 +1,185 @@ +/* Basic PCID & INVPCID functionality test */ + +#include "libcflat.h" +#include "processor.h" +#include "desc.h" + +int nr_passed, nr_tests; + +#define X86_FEATURE_PCID (1 << 17) +#define X86_FEATURE_INVPCID (1 << 10) + +#define X86_CR0_PG (1 << 31) +#define X86_CR3_PCID_MASK 0x00000fff +#define X86_CR4_PCIDE (1 << 17) + +#define X86_IA32_EFER 0xc0000080 +#define X86_EFER_LMA (1UL << 8) + +struct invpcid_desc { + unsigned long pcid : 12; + unsigned long rsv : 52; + unsigned long addr : 64; +}; + +static void report(const char *name, int passed) +{ + ++nr_tests; + if (passed) + ++nr_passed; + printf("%s: %s\n", name, passed ? "PASS" : "FAIL"); +} + +int write_cr0_checking(unsigned long val) +{ + asm volatile(ASM_TRY("1f") + "mov %0, %%cr0\n\t" + "1:": : "r" (val)); + return exception_vector(); +} + +int write_cr4_checking(unsigned long val) +{ + asm volatile(ASM_TRY("1f") + "mov %0, %%cr4\n\t" + "1:": : "r" (val)); + return exception_vector(); +} + +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(); +} + +void test_cpuid_consistency(int pcid_enabled, int invpcid_enabled) +{ + int passed = !(!pcid_enabled && invpcid_enabled); + report("CPUID consistency", passed); +} + +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; + + passed = 1; + +report: + report("Test on PCID when enabled", passed); +} + +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("Test on PCID when disabled", passed); +} + +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("Test on INVPCID when enabled", passed); +} + +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("Test on INVPCID when disabled", passed); +} + +int main(int ac, char **av) +{ + struct cpuid _cpuid; + int pcid_enabled = 0, invpcid_enabled = 0; + + setup_idt(); + + _cpuid = cpuid(1); + if (_cpuid.c & X86_FEATURE_PCID) + pcid_enabled = 1; + _cpuid = cpuid_indexed(7, 0); + if (_cpuid.b & 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(); + + printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed); + + return nr_passed == nr_tests ? 0 : 1; +} diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 065020a..1f6dc8a 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -68,3 +68,7 @@ extra_params = -cpu qemu64,-svm file = kvmclock_test.flat smp = 2 extra_params = --append "10000000 `date +%s`" + +[pcid] +file = pcid.flat +extra_params = -cpu qemu64,+pcid \ No newline at end of file -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html