Skip to contentSkip to conversation

svm: INIT test and test_run on selected vcpu

PATCH Applied Cathy Avery 2 participants 5 messages Jul 17, 2020 – Nov 5, 2020 KVM

lore
Share & tools
lore
#0 lore raw

v2 cover svm: INIT test and test_run on selected vcpu

INIT intercept test and the ability to execute test_run on a selected vcpu

Changes from v1:

1) Incorporated feedback:
- DR6/DR7/CR2/DEBUGCTL should not be need.
- HSAVE should be set to a different page for each vCPU
- The on_cpu to set EFER should be in setup_svm
- The on_cpu to set cr0/cr3/cr4 should be in setup_vm.

2) Execute tests on selected vcpu using on_cpu_async so the tests may use the on_cpu functions without causing an ipi_lock deadlock.

3) Added additional test svm_init_startup_test which inits the vcpu and restarts with sipi.

Cathy Avery (3):
svm: Add ability to execute test via test_run on a vcpu other than
vcpu 0
svm: INIT and STARTUP ipi test
svm: INIT intercept test

 lib/x86/vm.c    | 18 +++++++++
 lib/x86/vm.h    |  7 ++++
 x86/cstart64.S  |  1 +
 x86/svm.c       | 24 +++++++++++-
 x86/svm.h       |  2 +
 x86/svm_tests.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 148 insertions(+), 1 deletion(-)
Signature · 1 line
2.20.1
#1 lore raw

PATCH v2 1/3 svm: Add ability to execute test via test_run on a vcpu other than vcpu 0

When running tests that can result in a vcpu being left in an indeterminate state it is useful to be able to run the test on a vcpu other than 0. This patch allows test_run to be executed on any vcpu indicated by the on_vcpu member of the svm_test struct. The initialized state of the vcpu0 registers used to populate the vmcb is carried forward to the other vcpus.

---
 lib/x86/vm.c | 18 ++++++++++++++++++
 lib/x86/vm.h |  7 +++++++
 x86/svm.c    | 24 +++++++++++++++++++++++-
 x86/svm.h    |  2 ++
 4 files changed, 50 insertions(+), 1 deletion(-)
lib/x86/vm.c +18 -0
index 41d6d96..e223bb4 100644
@@ -2,6 +2,7 @@
 #include "libcflat.h"
 #include "vmalloc.h"
 #include "alloc_page.h"
+#include "smp.h"
 
 pteval_t *install_pte(pgd_t *cr3,
 		      int pte_level,
@@ -139,9 +140,18 @@ static void setup_mmu_range(pgd_t *cr3, phys_addr_t start, size_t len)
 	install_pages(cr3, phys, max - phys, (void *)(ulong)phys);
 }
 
+static void set_additional_vcpu_vmregs(struct vm_vcpu_info *info)
+{
+	write_cr3(info->cr3);
+	write_cr4(info->cr4);
+	write_cr0(info->cr0);
+}
+
 void *setup_mmu(phys_addr_t end_of_memory)
 {
     pgd_t *cr3 = alloc_page();
+    struct vm_vcpu_info info;
+    int i;
 
     memset(cr3, 0, PAGE_SIZE);
 
@@ -166,6 +176,14 @@ void *setup_mmu(phys_addr_t end_of_memory)
     printf("cr0 = %lx\n", read_cr0());
     printf("cr3 = %lx\n", read_cr3());
     printf("cr4 = %lx\n", read_cr4());
+
+    info.cr3 = read_cr3();
+    info.cr4 = read_cr4();
+    info.cr0 = read_cr0();
+
+    for (i = 1; i < cpu_count(); i++)
+        on_cpu(i, (void *)set_additional_vcpu_vmregs, &info);
+
     return cr3;
 }
 
lib/x86/vm.h +7 -0
index 8750a1e..3a1432f 100644
@@ -45,4 +45,11 @@ static inline void *current_page_table(void)
 
 void split_large_page(unsigned long *ptep, int level);
 void force_4k_page(void *addr);
+
+struct vm_vcpu_info {
+        u64 cr3;
+        u64 cr4;
+        u64 cr0;
+};
+
 #endif
x86/svm.c +23 -1
index d8c8272..975c477 100644
@@ -275,6 +275,17 @@ static void test_run(struct svm_test *test)
 	irq_enable();
 
 	report(test->succeeded(test), "%s", test->name);
+
+        if (test->on_vcpu)
+	    test->on_vcpu_done = true;
+}
+
+static void set_additional_vpcu_msr(void *msr_efer)
+{
+	void *hsave = alloc_page();
+
+	wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
+	wrmsr(MSR_EFER, (ulong)msr_efer | EFER_SVME | EFER_NX);
 }
 
 static void setup_svm(void)
@@ -294,6 +305,9 @@ static void setup_svm(void)
 	if (!npt_supported())
 		return;
 
+	for (i = 1; i < cpu_count(); i++)
+		on_cpu(i, (void *)set_additional_vpcu_msr, (void *)rdmsr(MSR_EFER));
+
 	printf("NPT detected - running all tests with NPT enabled\n");
 
 	/*
@@ -396,7 +410,15 @@ int main(int ac, char **av)
 		if (svm_tests[i].supported && !svm_tests[i].supported())
 			continue;
 		if (svm_tests[i].v2 == NULL) {
-			test_run(&svm_tests[i]);
+			if (svm_tests[i].on_vcpu) {
+				if (cpu_count() <= svm_tests[i].on_vcpu)
+					continue;
+				on_cpu_async(svm_tests[i].on_vcpu, (void *)test_run, &svm_tests[i]);
+				while (!svm_tests[i].on_vcpu_done)
+					cpu_relax();
+			}
+			else
+				test_run(&svm_tests[i]);
 		} else {
 			vmcb_ident(vmcb);
 			v2_test = &(svm_tests[i]);
x86/svm.h +2 -0
index f8e7429..1e60d52 100644
@@ -348,6 +348,8 @@ struct svm_test {
 	ulong scratch;
 	/* Alternative test interface. */
 	void (*v2)(void);
+	int on_vcpu;
+	bool on_vcpu_done;
 };
 
 struct regs {
Signature · 1 line
2.20.1
maintainer Applied
#2 lore raw

Re: [PATCH kvm-unit-tests v2 1/3] svm: Add ability to execute test via test_run on a vcpu other than vcpu 0

Quoted 132 lines · Cathy Avery

When running tests that can result in a vcpu being left in an indeterminate state it is useful to be able to run the test on a vcpu other than 0. This patch allows test_run to be executed on any vcpu indicated by the on_vcpu member of the svm_test struct. The initialized state of the vcpu0 registers used to populate the vmcb is carried forward to the other vcpus.

---
  lib/x86/vm.c | 18 ++++++++++++++++++
  lib/x86/vm.h |  7 +++++++
  x86/svm.c    | 24 +++++++++++++++++++++++-
  x86/svm.h    |  2 ++
  4 files changed, 50 insertions(+), 1 deletion(-)
lib/x86/vm.c +18 -0
index 41d6d96..e223bb4 100644
@@ -2,6 +2,7 @@
  #include "libcflat.h"
  #include "vmalloc.h"
  #include "alloc_page.h"
+#include "smp.h"
 
  pteval_t *install_pte(pgd_t *cr3,
  		      int pte_level,
@@ -139,9 +140,18 @@ static void setup_mmu_range(pgd_t *cr3, phys_addr_t start, size_t len)
  	install_pages(cr3, phys, max - phys, (void *)(ulong)phys);
  }
 
+static void set_additional_vcpu_vmregs(struct vm_vcpu_info *info)
+{
+	write_cr3(info->cr3);
+	write_cr4(info->cr4);
+	write_cr0(info->cr0);
+}
+
  void *setup_mmu(phys_addr_t end_of_memory)
  {
      pgd_t *cr3 = alloc_page();
+    struct vm_vcpu_info info;
+    int i;
 
      memset(cr3, 0, PAGE_SIZE);
 
@@ -166,6 +176,14 @@ void *setup_mmu(phys_addr_t end_of_memory)
      printf("cr0 = %lx\n", read_cr0());
      printf("cr3 = %lx\n", read_cr3());
      printf("cr4 = %lx\n", read_cr4());
+
+    info.cr3 = read_cr3();
+    info.cr4 = read_cr4();
+    info.cr0 = read_cr0();
+
+    for (i = 1; i < cpu_count(); i++)
+        on_cpu(i, (void *)set_additional_vcpu_vmregs, &info);
+
      return cr3;
  }
 
lib/x86/vm.h +7 -0
index 8750a1e..3a1432f 100644
@@ -45,4 +45,11 @@ static inline void *current_page_table(void)
 
  void split_large_page(unsigned long *ptep, int level);
  void force_4k_page(void *addr);
+
+struct vm_vcpu_info {
+        u64 cr3;
+        u64 cr4;
+        u64 cr0;
+};
+
  #endif
x86/svm.c +23 -1
index d8c8272..975c477 100644
@@ -275,6 +275,17 @@ static void test_run(struct svm_test *test)
  	irq_enable();
 
  	report(test->succeeded(test), "%s", test->name);
+
+        if (test->on_vcpu)
+	    test->on_vcpu_done = true;
+}
+
+static void set_additional_vpcu_msr(void *msr_efer)
+{
+	void *hsave = alloc_page();
+
+	wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
+	wrmsr(MSR_EFER, (ulong)msr_efer | EFER_SVME | EFER_NX);
  }
 
  static void setup_svm(void)
@@ -294,6 +305,9 @@ static void setup_svm(void)
  	if (!npt_supported())
  		return;
 
+	for (i = 1; i < cpu_count(); i++)
+		on_cpu(i, (void *)set_additional_vpcu_msr, (void *)rdmsr(MSR_EFER));
+
  	printf("NPT detected - running all tests with NPT enabled\n");
 
  	/*
@@ -396,7 +410,15 @@ int main(int ac, char **av)
  		if (svm_tests[i].supported && !svm_tests[i].supported())
  			continue;
  		if (svm_tests[i].v2 == NULL) {
-			test_run(&svm_tests[i]);
+			if (svm_tests[i].on_vcpu) {
+				if (cpu_count() <= svm_tests[i].on_vcpu)
+					continue;
+				on_cpu_async(svm_tests[i].on_vcpu, (void *)test_run, &svm_tests[i]);
+				while (!svm_tests[i].on_vcpu_done)
+					cpu_relax();
+			}
+			else
+				test_run(&svm_tests[i]);
  		} else {
  			vmcb_ident(vmcb);
  			v2_test = &(svm_tests[i]);
x86/svm.h +0 -0
index f8e7429..1e60d52 100644

x86/svm.h struct svm_test { · line 355

Quoted 8 lines
x86/svm.h +2 -0
@@ -348,6 +348,8 @@ struct svm_test {
  	ulong scratch;
  	/* Alternative test interface. */
  	void (*v2)(void);
+	int on_vcpu;
+	bool on_vcpu_done;
  };
 
  struct regs {

Queued, thanks.

Paolo

#3 lore raw

PATCH v2 2/3 svm: INIT and STARTUP ipi test

In reply to Cathy Avery

Init the vcpu and issue the STARTUP ipi to indicate the vcpu should execute its startup routine.

---
 x86/cstart64.S  |  1 +
 x86/svm_tests.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+)
x86/cstart64.S +1 -0
index 3ae98d3..dfd7320 100644
@@ -7,6 +7,7 @@
 .globl tss_descr
 .globl gdt64_desc
 .globl online_cpus
+.globl cpu_online_count
 
 ipi_vector = 0x20
 
x86/svm_tests.c +57 -0

75 lines not on this page · raw message

Signature · 1 line
2.20.1
#4 lore raw

PATCH v2 3/3 svm: INIT intercept test

In reply to Cathy Avery

INIT vcpu 2 and intercept the INIT. This test
will leave the vcpu in an unusable state.

---
 x86/svm_tests.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
x86/svm_tests.c +40 -0
index 698eb20..b43c19f 100644
@@ -1939,6 +1939,43 @@ static bool init_startup_check(struct svm_test *test)
     return cpu_online_count == orig_cpu_count;
 }
 
+static volatile bool init_intercept;
+
+static void init_intercept_prepare(struct svm_test *test)
+{
+    init_intercept = false;
+    vmcb_ident(vmcb);
+    vmcb->control.intercept |= (1ULL << INTERCEPT_INIT);
+}
+
+static void init_intercept_test(struct svm_test *test)
+{
+    apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_INIT | APIC_INT_ASSERT, 0);
+}
+
+static bool init_intercept_finished(struct svm_test *test)
+{
+    vmcb->save.rip += 3;
+
+    if (vmcb->control.exit_code != SVM_EXIT_INIT) {
+        report(false, "VMEXIT not due to init intercept. Exit reason 0x%x",
+               vmcb->control.exit_code);
+
+        return true;
+        }
+
+    init_intercept = true;
+
+    report(true, "INIT to vcpu intercepted");
+
+    return true;
+}
+
+static bool init_intercept_check(struct svm_test *test)
+{
+    return init_intercept;
+}
+
 #define TEST(name) { #name, .v2 = name }
 
 /*
@@ -2255,6 +2292,9 @@ struct svm_test svm_tests[] = {
     { "svm_init_startup_test", smp_supported, init_startup_prepare,
       default_prepare_gif_clear, null_test,
       init_startup_finished, init_startup_check },
+    { "svm_init_intercept_test", smp_supported, init_intercept_prepare,
+      default_prepare_gif_clear, init_intercept_test,
+      init_intercept_finished, init_intercept_check, .on_vcpu = 2 },
     TEST(svm_guest_state_test),
     { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };
Signature · 1 line
2.20.1