From: Swapnil Paratey <swapnil.paratey@xxxxxxx> 1) Exercise the SVM rules for the various bit settings of the controlling SVM MSRs 2) Spelling correction of "available" --- x86/svm.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++- x86/unittests.cfg | 5 +++ 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/x86/svm.c b/x86/svm.c index 162632c..b71178a 100644 --- a/x86/svm.c +++ b/x86/svm.c @@ -40,6 +40,8 @@ u64 runs; u8 *io_bitmap; u8 io_bitmap_area[16384]; +static int matched; + static bool npt_supported(void) { return cpuid(0x8000000A).d & 1; @@ -1008,6 +1010,85 @@ static bool lat_svm_insn_check(struct test *test) latclgi_min, clgi_sum / LATENCY_RUNS); return true; } + +static void set_msr_vm_cr(void *data) +{ + u64 msr_value; + msr_value = *((u64 *)(data)); + wrmsr(MSR_VM_CR, msr_value); +} + +static void set_msr_efer(void *data) +{ + u64 msr_value; + msr_value = *((u64 *)(data)); + wrmsr(MSR_EFER, msr_value); +} + +static void test_svm_virtual_machine_control() +{ + bool svm_enabled, svme_disabled; + u64 msr_value; + + svm_enabled = ((rdmsr(MSR_EFER) & 0x1000) == 0x1000); + svme_disabled = ((rdmsr(MSR_VM_CR) & 0x10) == 0x10); + + if(svm_enabled == false) { + wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME); + report("test enable SVM if EFER_SVME bit is disabled", + ((rdmsr(MSR_EFER) & EFER_SVME) == EFER_SVME)); + } + + if(svme_disabled == true) { + report("test MSR_VM_CR SVMe Disable bit is set", svme_disabled); + return ; + } + + /* Test MSR_VM_CR bit-sets when EFER_SVME bit is set */ + msr_value = 0x00; + report("test MSR_VM_CR reset when EFER_SVME bit is set - no #GP", + !test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + msr_value = 0x10; + report("test MSR_VM_CR SVMeDisable bit set when EFER_SVME bit is set - #GP", + test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + msr_value = 0x08; + report("test MSR_VM_CR control lock bit set when EFER_SVME set - #GP", + test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + msr_value = 0x00; + report("test MSR_VM_CR reset when EFER_SVME bit is set - #GP", + test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + + /* MSR_VM_CR SVMe Disable bit once set does not unset - cannot be cleared on KVM + * Host behavior is different than KVM behavior - host allows setting VM_CR_SVMEDIS on EFER_SVME being unset + * If there's no BIOS lock bit set, and no SVM Key, the VMCR can be cleared on the host + * KVM implements a soft-lock without implementing the SVM Key though + * KVM/SVM does not implement lock bits and gives #GP for reset instead */ + + /* Clear the EFER_SVME and test for #GP on VM_CR_SVMEDIS bit sets */ + msr_value = rdmsr(MSR_EFER); + msr_value &= ~EFER_SVME; + wrmsr(MSR_EFER, msr_value); + + msr_value = 0x00; + report("test MSR_VM_CR reset when EFER_SVME bit is clear - no #GP", + !test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + msr_value = 0x10; + report("test MSR_VM_CR SVMeDis bit set when EFER_SVME bit clear - no #GP", + !test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + msr_value = 0x08; + report("test MSR_VM_CR control lock bit set when EFER_SVME clear - no #GP", + !test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + msr_value = 0x00; + report("test MSR_VM_CR reset when EFER_SVME bit is clear - no #GP", + !test_for_exception(GP_VECTOR, &set_msr_vm_cr, (void*)(&msr_value))); + + /* VM_CR_SVMEDIS is set at this point, test EFER #GP behaviors */ + msr_value = 0x1000; + report("test Setting EFER_SVME bit when VM_CR_SVMEDIS is set - #GP", + test_for_exception(GP_VECTOR, &set_msr_efer, (void*)(&msr_value))); +} + + static struct test tests[] = { { "null", default_supported, default_prepare, null_test, default_finished, null_check }, @@ -1054,7 +1135,42 @@ static struct test tests[] = { lat_svm_insn_finished, lat_svm_insn_check }, }; -int main(int ac, char **av) +static bool test_wanted(const char *name, const char *filters[], int filter_count) +{ + int i; + bool positive = false; + bool match = false; + char clean_name[strlen(name) + 1]; + char *c; + const char *n; + + /* Replace spaces with underscores. */ + n = name; + c = &clean_name[0]; + do *c++ = (*n == ' ') ? '_' : *n; + while (*n++); + + for (i = 0; i < filter_count; i++) { + const char *filter = filters[i]; + + if (filter[0] == '-') { + if (simple_glob(clean_name, filter + 1)) + return false; + } else { + positive = true; + match |= simple_glob(clean_name, filter); + } + } + + if (!positive || match) { + matched++; + return true; + } else { + return false; + } +} + +int main(int ac, const char **av) { int i, nr; struct vmcb *vmcb; @@ -1063,12 +1179,17 @@ int main(int ac, char **av) smp_init(); if (!(cpuid(0x80000001).c & 4)) { - printf("SVM not availble\n"); + printf("SVM not available\n"); return report_summary(); } setup_svm(); + if(test_wanted("test_svm_virtual_machine_control", av, ac)) { + test_svm_virtual_machine_control(); + return report_summary(); + } + vmcb = alloc_page(); nr = ARRAY_SIZE(tests); diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 22c62d5..19407af 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -194,6 +194,11 @@ smp = 2 extra_params = -cpu qemu64,+svm arch = x86_64 +[svm_test_virtual_machine_control] +file = svm.flat +extra_params = -cpu host,+svm -append test_svm_virtual_machine_control +arch = x86_64 + [taskswitch] file = taskswitch.flat arch = i386 -- 2.14.3