Skip to contentSkip to conversation

x86: nvmx: fix bug in __enter_guest()

PATCH Superseded Marc Orr 4 participants 7 messages Sep 18, 2019 – Sep 25, 2019

Subsystems (2)

v5 of 7 Superseded Latest is v7 →

lore
Share & tools
lore

Marc Orr

author
#0 lore raw

PATCH v5 1/2 x86: nvmx: fix bug in __enter_guest()

__enter_guest() should only set the launched flag when a launch has succeeded. Thus, don't set the launched flag when the VMX_ENTRY_FAILURE, bit 31, is set in the VMCS exit reason.

---
 x86/vmx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
x86/vmx.c +1 -1
index 6079420db33a..7313c78f15c2 100644
@@ -1820,7 +1820,7 @@ static void __enter_guest(u8 abort_flag, struct vmentry_failure *failure)
 		abort();
 	}
 
-	if (!failure->early) {
+	if (!failure->early && !(vmcs_read(EXI_REASON) & VMX_ENTRY_FAILURE)) {
 		launched = 1;
 		check_for_guest_termination();
 	}
Signature · 1 line
2.23.0.237.gc6a4ce50a0-goog
author
#1 lore raw

PATCH v5 2/2 x86: nvmx: test max atomic switch MSRs

Excerise nested VMX's atomic MSR switch code (e.g., VM-entry MSR-load list) at the maximum number of MSRs supported, as described in the SDM, in the appendix chapter titled "MISCELLANEOUS DATA".

---
v4 -> v5
* Replaced local pointer declarations for MSR lists with existing global declarations.
* Replaced atomic_switch_msr_limit_test_guest() with existing vmcall() funciton.
* Removed redundant assert_exit_reason(VMX_VMCALL).

 lib/alloc_page.c  |   5 ++
 lib/alloc_page.h  |   1 +
 x86/unittests.cfg |   2 +-
 x86/vmx_tests.c   | 134 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 141 insertions(+), 1 deletion(-)
lib/alloc_page.c +5 -0
index 97d13395ff08..ed236389537e 100644
@@ -53,6 +53,11 @@ void free_pages(void *mem, unsigned long size)
 	spin_unlock(&lock);
 }
 
+void free_pages_by_order(void *mem, unsigned long order)
+{
+	free_pages(mem, 1ul << (order + PAGE_SHIFT));
+}
+
 void *alloc_page()
 {
 	void *p;
lib/alloc_page.h +1 -0
index 5cdfec57a0a8..739a91def979 100644
@@ -14,5 +14,6 @@ void *alloc_page(void);
 void *alloc_pages(unsigned long order);
 void free_page(void *page);
 void free_pages(void *mem, unsigned long size);
+void free_pages_by_order(void *mem, unsigned long order);
 
 #endif
x86/unittests.cfg +1 -1
index 694ee3d42f3a..05122cf91ea1 100644
@@ -227,7 +227,7 @@ extra_params = -cpu qemu64,+umip
 
 [vmx]
 file = vmx.flat
-extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test"
+extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test -atomic_switch_overflow_msrs_test"
 arch = x86_64
 groups = vmx
 
x86/vmx_tests.c +134 -0

145 lines not on this page · raw message

Signature · 1 line
2.23.0.237.gc6a4ce50a0-goog
#2 lore raw

Re: [kvm-unit-tests PATCH v5 2/2] x86: nvmx: test max atomic switch MSRs

On Wed, Sep 18, 2019 at 03:23:54PM -0700, Marc Orr wrote:

...

x86/vmx_tests.c static int invalid_msr_entry_failure(struct vmentry_failure *failure) · line 8586

Quoted 3 lines
x86/vmx_tests.c +3 -0
+static void atomic_switch_msr_limit_test_guest(void)
+{
+	vmcall();

I finally dug into the weird double-enter_guest(). Rather than re-enter the guest to cleanup, just remove this vmcall() so that the first VM-Enter invokes hypercall() with HYPERCALL_VMEXIT to set guest_finished.

enter_guest() will verify VM-Enter succeeded, and the guest_finished check verifies the guest did a VMCALL. I don't see any added value in an extra VMCALL.

Quoted 61 lines
x86/vmx_tests.c +61 -0
+}
+
+static void populate_msr_list(struct vmx_msr_entry *msr_list,
+			      size_t byte_capacity, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		msr_list[i].index = MSR_IA32_TSC;
+		msr_list[i].reserved = 0;
+		msr_list[i].value = 0x1234567890abcdef;
+	}
+
+	memset(msr_list + count, 0xff,
+	       byte_capacity - count * sizeof(*msr_list));
+}
+
+static int max_msr_list_size(void)
+{
+	u32 vmx_misc = rdmsr(MSR_IA32_VMX_MISC);
+	u32 factor = ((vmx_misc & GENMASK(27, 25)) >> 25) + 1;
+
+	return factor * 512;
+}
+
+static void atomic_switch_msrs_test(int count)
+{
+	struct vmx_msr_entry *vm_enter_load;
+	struct vmx_msr_entry *vm_exit_load;
+	struct vmx_msr_entry *vm_exit_store;
+	int max_allowed = max_msr_list_size();
+	int byte_capacity = 1ul << (msr_list_page_order + PAGE_SHIFT);
+	/* KVM signals VM-Abort if an exit MSR list exceeds the max size. */
+	int exit_count = MIN(count, max_allowed);
+
+	/*
+	 * Check for the IA32_TSC MSR,
+	 * available with the "TSC flag" and used to populate the MSR lists.
+	 */
+	if (!(cpuid(1).d & (1 << 4))) {
+		report_skip(__func__);
+		return;
+	}
+
+	/* Set L2 guest. */
+	test_set_guest(atomic_switch_msr_limit_test_guest);
+
+	/* Setup atomic MSR switch lists. */
+	vm_enter_load = alloc_pages(msr_list_page_order);
+	vm_exit_load = alloc_pages(msr_list_page_order);
+	vm_exit_store = alloc_pages(msr_list_page_order);
+
+	vmcs_write(ENTER_MSR_LD_ADDR, (u64)vm_enter_load);
+	vmcs_write(EXIT_MSR_LD_ADDR, (u64)vm_exit_load);
+	vmcs_write(EXIT_MSR_ST_ADDR, (u64)vm_exit_store);
+
+	/*
+	 * VM-Enter should succeed up to the max number of MSRs per list, and
+	 * should not consume junk beyond the last entry.
+	 */
+	populate_msr_list(vm_enter_load, byte_capacity, count);

x86/vmx_tests.c static int invalid_msr_entry_failure(struct vmentry_failure *failure) · line 8658

Quoted 10 lines
x86/vmx_tests.c +10 -0
+	populate_msr_list(vm_exit_load, byte_capacity, exit_count);
+	populate_msr_list(vm_exit_store, byte_capacity, exit_count);
+
+	vmcs_write(ENT_MSR_LD_CNT, count);
+	vmcs_write(EXI_MSR_LD_CNT, exit_count);
+	vmcs_write(EXI_MSR_ST_CNT, exit_count);
+
+	if (count <= max_allowed) {
+		enter_guest();
+		skip_exit_vmcall();

If vmcall() is removed, this skip and the one in the else{} can be dropped.

Quoted 11 lines
x86/vmx_tests.c +11 -0
+	} else {
+		u32 exit_reason;
+		u32 exit_reason_want;
+		u32 exit_qual;
+
+		enter_guest_with_invalid_guest_state();
+
+		exit_reason = vmcs_read(EXI_REASON);
+		exit_reason_want = VMX_FAIL_MSR | VMX_ENTRY_FAILURE;
+		report("exit_reason, %u, is %u.",
+		       exit_reason == exit_reason_want, exit_reason,

x86/vmx_tests.c static int invalid_msr_entry_failure(struct vmentry_failure *failure) · line 8679

Quoted 10 lines
x86/vmx_tests.c +10 -0
+		       exit_reason_want);
+
+		exit_qual = vmcs_read(EXI_QUALIFICATION);
+		report("exit_qual, %u, is %u.", exit_qual == max_allowed + 1,
+		       exit_qual, max_allowed + 1);
+
+		/*
+		 * Re-enter the guest with valid counts
+		 * and proceed past the single vmcall instruction.
+		 */

Nit: "Re-enter the guest" should either be "Retry VM-Enter" or simply
"Enter". The reason this code exists is that we never actually entered the guest :-)

E.g. if you drop the vmcall():

/* Enter the guest (with valid counts) to set guest_finished. */

Quoted 39 lines
x86/vmx_tests.c +27 -0
+		vmcs_write(ENT_MSR_LD_CNT, 0);
+		vmcs_write(EXI_MSR_LD_CNT, 0);
+		vmcs_write(EXI_MSR_ST_CNT, 0);
+		enter_guest();
+		skip_exit_vmcall();
+	}
+
+	/* Cleanup. */
+	enter_guest();
+	skip_exit_vmcall();
+	free_pages_by_order(vm_enter_load, msr_list_page_order);
+	free_pages_by_order(vm_exit_load, msr_list_page_order);
+	free_pages_by_order(vm_exit_store, msr_list_page_order);
+}
+
+static void atomic_switch_max_msrs_test(void)
+{
+	atomic_switch_msrs_test(max_msr_list_size());
+}
+
+static void atomic_switch_overflow_msrs_test(void)
+{
+	atomic_switch_msrs_test(max_msr_list_size() + 1);
+}
 
 #define TEST(name) { #name, .v2 = name }
@@ -8660,5 +8791,8 @@ struct vmx_test vmx_tests[] = {
 	TEST(ept_access_test_paddr_read_execute_ad_enabled),
 	TEST(ept_access_test_paddr_not_present_page_fault),
 	TEST(ept_access_test_force_2m_page),
+	/* Atomic MSR switch tests. */
+	TEST(atomic_switch_max_msrs_test),
+	TEST(atomic_switch_overflow_msrs_test),
 	{ NULL, NULL, NULL, NULL, NULL, {0} },
 };
Signature · 1 line
2.23.0.237.gc6a4ce50a0-goog
author
#3 lore raw

Re: [kvm-unit-tests PATCH v5 2/2] x86: nvmx: test max atomic switch MSRs

Quoted 11 lines

x86/vmx_tests.c static int invalid_msr_entry_failure(struct vmentry_failure *failure) · line 8586

Quoted 3 lines
x86/vmx_tests.c +3 -0
+static void atomic_switch_msr_limit_test_guest(void)
+{
+     vmcall();

I finally dug into the weird double-enter_guest(). Rather than re-enter the guest to cleanup, just remove this vmcall() so that the first VM-Enter invokes hypercall() with HYPERCALL_VMEXIT to set guest_finished.

enter_guest() will verify VM-Enter succeeded, and the guest_finished check verifies the guest did a VMCALL. I don't see any added value in an extra VMCALL.

Done. To be consistent with Krish's review, I utilized the existing v2_null_test_guest() function, which is empty.

Quoted 5 lines

x86/vmx_tests.c static int invalid_msr_entry_failure(struct vmentry_failure *failure) · line 8658

Quoted 3 lines
x86/vmx_tests.c +3 -0
+     if (count <= max_allowed) {
+             enter_guest();
+             skip_exit_vmcall();

If vmcall() is removed, this skip and the one in the else{} can be dropped.

Done.

Quoted 5 lines

x86/vmx_tests.c static int invalid_msr_entry_failure(struct vmentry_failure *failure) · line 8679

Quoted 4 lines
x86/vmx_tests.c +4 -0
+             /*
+              * Re-enter the guest with valid counts
+              * and proceed past the single vmcall instruction.
+              */
Quoted 7 lines

Nit: "Re-enter the guest" should either be "Retry VM-Enter" or simply
"Enter". The reason this code exists is that we never actually entered the guest :-)

E.g. if you drop the vmcall():

/* Enter the guest (with valid counts) to set guest_finished. */

Done.

Reviewed-by on v5 2/2
#4 lore raw

Re: [kvm-unit-tests PATCH v5 2/2] x86: nvmx: test max atomic switch MSRs

In reply to Marc Orr
Quoted 203 lines · Marc Orr

Excerise nested VMX's atomic MSR switch code (e.g., VM-entry MSR-load list) at the maximum number of MSRs supported, as described in the SDM, in the appendix chapter titled "MISCELLANEOUS DATA".

---
v4 -> v5
* Replaced local pointer declarations for MSR lists with existing global declarations.
* Replaced atomic_switch_msr_limit_test_guest() with existing vmcall() funciton.
* Removed redundant assert_exit_reason(VMX_VMCALL).

  lib/alloc_page.c  |   5 ++
  lib/alloc_page.h  |   1 +
  x86/unittests.cfg |   2 +-
  x86/vmx_tests.c   | 134 ++++++++++++++++++++++++++++++++++++++++++++++
  4 files changed, 141 insertions(+), 1 deletion(-)
lib/alloc_page.c +5 -0
index 97d13395ff08..ed236389537e 100644
@@ -53,6 +53,11 @@ void free_pages(void *mem, unsigned long size)
  	spin_unlock(&lock);
  }
 
+void free_pages_by_order(void *mem, unsigned long order)
+{
+	free_pages(mem, 1ul << (order + PAGE_SHIFT));
+}
+
  void *alloc_page()
  {
  	void *p;
lib/alloc_page.h +1 -0
index 5cdfec57a0a8..739a91def979 100644
@@ -14,5 +14,6 @@ void *alloc_page(void);
  void *alloc_pages(unsigned long order);
  void free_page(void *page);
  void free_pages(void *mem, unsigned long size);
+void free_pages_by_order(void *mem, unsigned long order);
 
  #endif
x86/unittests.cfg +1 -1
index 694ee3d42f3a..05122cf91ea1 100644
@@ -227,7 +227,7 @@ extra_params = -cpu qemu64,+umip
 
  [vmx]
  file = vmx.flat
-extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test"
+extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test -atomic_switch_overflow_msrs_test"
  arch = x86_64
  groups = vmx
 
x86/vmx_tests.c +131 -0
index f035f24a771a..cc33ea7b5114 100644
@@ -8570,6 +8570,137 @@ static int invalid_msr_entry_failure(struct vmentry_failure *failure)
  	return VMX_TEST_VMEXIT;
  }
 
+/*
+ * The max number of MSRs in an atomic switch MSR list is:
+ * (111B + 1) * 512 = 4096
+ *
+ * Each list entry consumes:
+ * 4-byte MSR index + 4 bytes reserved + 8-byte data = 16 bytes
+ *
+ * Allocate 128 kB to cover max_msr_list_size (i.e., 64 kB) and then some.
+ */
+static const u32 msr_list_page_order = 5;
+
+static void atomic_switch_msr_limit_test_guest(void)
+{
+	vmcall();
+}
+
+static void populate_msr_list(struct vmx_msr_entry *msr_list,
+			      size_t byte_capacity, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		msr_list[i].index = MSR_IA32_TSC;
+		msr_list[i].reserved = 0;
+		msr_list[i].value = 0x1234567890abcdef;
+	}
+
+	memset(msr_list + count, 0xff,
+	       byte_capacity - count * sizeof(*msr_list));
+}
+
+static int max_msr_list_size(void)
+{
+	u32 vmx_misc = rdmsr(MSR_IA32_VMX_MISC);
+	u32 factor = ((vmx_misc & GENMASK(27, 25)) >> 25) + 1;
+
+	return factor * 512;
+}
+
+static void atomic_switch_msrs_test(int count)
+{
+	struct vmx_msr_entry *vm_enter_load;
+	struct vmx_msr_entry *vm_exit_load;
+	struct vmx_msr_entry *vm_exit_store;
+	int max_allowed = max_msr_list_size();
+	int byte_capacity = 1ul << (msr_list_page_order + PAGE_SHIFT);
+	/* KVM signals VM-Abort if an exit MSR list exceeds the max size. */
+	int exit_count = MIN(count, max_allowed);
+
+	/*
+	 * Check for the IA32_TSC MSR,
+	 * available with the "TSC flag" and used to populate the MSR lists.
+	 */
+	if (!(cpuid(1).d & (1 << 4))) {
+		report_skip(__func__);
+		return;
+	}
+
+	/* Set L2 guest. */
+	test_set_guest(atomic_switch_msr_limit_test_guest);
+
+	/* Setup atomic MSR switch lists. */
+	vm_enter_load = alloc_pages(msr_list_page_order);
+	vm_exit_load = alloc_pages(msr_list_page_order);
+	vm_exit_store = alloc_pages(msr_list_page_order);
+
+	vmcs_write(ENTER_MSR_LD_ADDR, (u64)vm_enter_load);
+	vmcs_write(EXIT_MSR_LD_ADDR, (u64)vm_exit_load);
+	vmcs_write(EXIT_MSR_ST_ADDR, (u64)vm_exit_store);
+
+	/*
+	 * VM-Enter should succeed up to the max number of MSRs per list, and
+	 * should not consume junk beyond the last entry.
+	 */
+	populate_msr_list(vm_enter_load, byte_capacity, count);
+	populate_msr_list(vm_exit_load, byte_capacity, exit_count);
+	populate_msr_list(vm_exit_store, byte_capacity, exit_count);
+
+	vmcs_write(ENT_MSR_LD_CNT, count);
+	vmcs_write(EXI_MSR_LD_CNT, exit_count);
+	vmcs_write(EXI_MSR_ST_CNT, exit_count);
+
+	if (count <= max_allowed) {
+		enter_guest();
+		assert_exit_reason(VMX_VMCALL);
+		skip_exit_vmcall();
+	} else {
+		u32 exit_reason;
+		u32 exit_reason_want;
+		u32 exit_qual;
+
+		enter_guest_with_invalid_guest_state();
+
+		exit_reason = vmcs_read(EXI_REASON);
+		exit_reason_want = VMX_FAIL_MSR | VMX_ENTRY_FAILURE;
+		report("exit_reason, %u, is %u.",
+		       exit_reason == exit_reason_want, exit_reason,
+		       exit_reason_want);
+
+		exit_qual = vmcs_read(EXI_QUALIFICATION);
+		report("exit_qual, %u, is %u.", exit_qual == max_allowed + 1,
+		       exit_qual, max_allowed + 1);
+
+		/*
+		 * Re-enter the guest with valid counts
+		 * and proceed past the single vmcall instruction.
+		 */
+		vmcs_write(ENT_MSR_LD_CNT, 0);
+		vmcs_write(EXI_MSR_LD_CNT, 0);
+		vmcs_write(EXI_MSR_ST_CNT, 0);
+		enter_guest();
+		skip_exit_vmcall();
+	}
+
+	/* Cleanup. */
+	enter_guest();
+	skip_exit_vmcall();
+	free_pages_by_order(vm_enter_load, msr_list_page_order);
+	free_pages_by_order(vm_exit_load, msr_list_page_order);
+	free_pages_by_order(vm_exit_store, msr_list_page_order);
+}
+
+static void atomic_switch_max_msrs_test(void)
+{
+	atomic_switch_msrs_test(max_msr_list_size());
+}
+
+static void atomic_switch_overflow_msrs_test(void)
+{
+	atomic_switch_msrs_test(max_msr_list_size() + 1);
+}
 
  #define TEST(name) { #name, .v2 = name }
 

x86/vmx_tests.c struct vmx_test vmx_tests[] = { · line 8798

Quoted 8 lines
x86/vmx_tests.c +3 -0
@@ -8660,5 +8791,8 @@ struct vmx_test vmx_tests[] = {
  	TEST(ept_access_test_paddr_read_execute_ad_enabled),
  	TEST(ept_access_test_paddr_not_present_page_fault),
  	TEST(ept_access_test_force_2m_page),
+	/* Atomic MSR switch tests. */
+	TEST(atomic_switch_max_msrs_test),
+	TEST(atomic_switch_overflow_msrs_test),
  	{ NULL, NULL, NULL, NULL, NULL, {0} },
  };
#5 lore raw

Re: [kvm-unit-tests PATCH v5 2/2] x86: nvmx: test max atomic switch MSRs

In reply to Marc Orr
Quoted 5 lines · Marc Orr

Excerise nested VMX's atomic MSR switch code (e.g., VM-entry MSR-load

Nit: Exercise

Quoted 213 lines

list) at the maximum number of MSRs supported, as described in the SDM, in the appendix chapter titled "MISCELLANEOUS DATA".

---
v4 -> v5
* Replaced local pointer declarations for MSR lists with existing global declarations.
* Replaced atomic_switch_msr_limit_test_guest() with existing vmcall() funciton.
* Removed redundant assert_exit_reason(VMX_VMCALL).

lib/alloc_page.c  |   5 ++
lib/alloc_page.h  |   1 +
x86/unittests.cfg |   2 +-
x86/vmx_tests.c   | 134 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 141 insertions(+), 1 deletion(-)
lib/alloc_page.c +5 -0
index 97d13395ff08..ed236389537e 100644
@@ -53,6 +53,11 @@ void free_pages(void *mem, unsigned long size)
 	spin_unlock(&lock);
 }
 
+void free_pages_by_order(void *mem, unsigned long order)
+{
+	free_pages(mem, 1ul << (order + PAGE_SHIFT));
+}
+
 void *alloc_page()
 {
 	void *p;
lib/alloc_page.h +1 -0
index 5cdfec57a0a8..739a91def979 100644
@@ -14,5 +14,6 @@ void *alloc_page(void);
 void *alloc_pages(unsigned long order);
 void free_page(void *page);
 void free_pages(void *mem, unsigned long size);
+void free_pages_by_order(void *mem, unsigned long order);
 
 #endif
x86/unittests.cfg +0 -0
index 694ee3d42f3a..05122cf91ea1 100644
@@ -227,7 +227,7 @@ extra_params = -cpu qemu64,+umip
 

[vmx]
file = vmx.flat

x86/unittests.cfg +1 -1
-extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test"
+extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test -atomic_switch_overflow_msrs_test"

arch = x86_64
groups = vmx

x86/vmx_tests.c +134 -0
index f035f24a771a..cc33ea7b5114 100644
@@ -8570,6 +8570,137 @@ static int invalid_msr_entry_failure(struct vmentry_failure *failure)
 	return VMX_TEST_VMEXIT;
 }
 
+/*
+ * The max number of MSRs in an atomic switch MSR list is:
+ * (111B + 1) * 512 = 4096
+ *
+ * Each list entry consumes:
+ * 4-byte MSR index + 4 bytes reserved + 8-byte data = 16 bytes
+ *
+ * Allocate 128 kB to cover max_msr_list_size (i.e., 64 kB) and then some.
+ */
+static const u32 msr_list_page_order = 5;
+
+static void atomic_switch_msr_limit_test_guest(void)
+{
+	vmcall();
+}
+
+static void populate_msr_list(struct vmx_msr_entry *msr_list,
+			      size_t byte_capacity, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		msr_list[i].index = MSR_IA32_TSC;
+		msr_list[i].reserved = 0;
+		msr_list[i].value = 0x1234567890abcdef;
+	}
+
+	memset(msr_list + count, 0xff,
+	       byte_capacity - count * sizeof(*msr_list));
+}
+
+static int max_msr_list_size(void)
+{
+	u32 vmx_misc = rdmsr(MSR_IA32_VMX_MISC);
+	u32 factor = ((vmx_misc & GENMASK(27, 25)) >> 25) + 1;
+
+	return factor * 512;
+}
+
+static void atomic_switch_msrs_test(int count)
+{
+	struct vmx_msr_entry *vm_enter_load;
+	struct vmx_msr_entry *vm_exit_load;
+	struct vmx_msr_entry *vm_exit_store;
+	int max_allowed = max_msr_list_size();
+	int byte_capacity = 1ul << (msr_list_page_order + PAGE_SHIFT);
+	/* KVM signals VM-Abort if an exit MSR list exceeds the max size. */
+	int exit_count = MIN(count, max_allowed);
+
+	/*
+	 * Check for the IA32_TSC MSR,
+	 * available with the "TSC flag" and used to populate the MSR lists.
+	 */
+	if (!(cpuid(1).d & (1 << 4))) {
+		report_skip(__func__);
+		return;
+	}
+
+	/* Set L2 guest. */
+	test_set_guest(atomic_switch_msr_limit_test_guest);
+
+	/* Setup atomic MSR switch lists. */
+	vm_enter_load = alloc_pages(msr_list_page_order);
+	vm_exit_load = alloc_pages(msr_list_page_order);
+	vm_exit_store = alloc_pages(msr_list_page_order);
+
+	vmcs_write(ENTER_MSR_LD_ADDR, (u64)vm_enter_load);
+	vmcs_write(EXIT_MSR_LD_ADDR, (u64)vm_exit_load);
+	vmcs_write(EXIT_MSR_ST_ADDR, (u64)vm_exit_store);
+
+	/*
+	 * VM-Enter should succeed up to the max number of MSRs per list, and
+	 * should not consume junk beyond the last entry.
+	 */
+	populate_msr_list(vm_enter_load, byte_capacity, count);
+	populate_msr_list(vm_exit_load, byte_capacity, exit_count);
+	populate_msr_list(vm_exit_store, byte_capacity, exit_count);
+
+	vmcs_write(ENT_MSR_LD_CNT, count);
+	vmcs_write(EXI_MSR_LD_CNT, exit_count);
+	vmcs_write(EXI_MSR_ST_CNT, exit_count);
+
+	if (count <= max_allowed) {
+		enter_guest();
+		assert_exit_reason(VMX_VMCALL);
+		skip_exit_vmcall();
+	} else {
+		u32 exit_reason;
+		u32 exit_reason_want;
+		u32 exit_qual;
+
+		enter_guest_with_invalid_guest_state();
+
+		exit_reason = vmcs_read(EXI_REASON);
+		exit_reason_want = VMX_FAIL_MSR | VMX_ENTRY_FAILURE;
+		report("exit_reason, %u, is %u.",
+		       exit_reason == exit_reason_want, exit_reason,
+		       exit_reason_want);
+
+		exit_qual = vmcs_read(EXI_QUALIFICATION);
+		report("exit_qual, %u, is %u.", exit_qual == max_allowed + 1,
+		       exit_qual, max_allowed + 1);
+
+		/*
+		 * Re-enter the guest with valid counts
+		 * and proceed past the single vmcall instruction.
+		 */
+		vmcs_write(ENT_MSR_LD_CNT, 0);
+		vmcs_write(EXI_MSR_LD_CNT, 0);
+		vmcs_write(EXI_MSR_ST_CNT, 0);
+		enter_guest();
+		skip_exit_vmcall();
+	}
+
+	/* Cleanup. */
+	enter_guest();
+	skip_exit_vmcall();
+	free_pages_by_order(vm_enter_load, msr_list_page_order);
+	free_pages_by_order(vm_exit_load, msr_list_page_order);
+	free_pages_by_order(vm_exit_store, msr_list_page_order);
+}
+
+static void atomic_switch_max_msrs_test(void)
+{
+	atomic_switch_msrs_test(max_msr_list_size());
+}
+
+static void atomic_switch_overflow_msrs_test(void)
+{
+	atomic_switch_msrs_test(max_msr_list_size() + 1);
+}
 
 #define TEST(name) { #name, .v2 = name }
 
@@ -8660,5 +8791,8 @@ struct vmx_test vmx_tests[] = {
 	TEST(ept_access_test_paddr_read_execute_ad_enabled),
 	TEST(ept_access_test_paddr_not_present_page_fault),
 	TEST(ept_access_test_force_2m_page),
+	/* Atomic MSR switch tests. */
+	TEST(atomic_switch_max_msrs_test),
+	TEST(atomic_switch_overflow_msrs_test),
 	{ NULL, NULL, NULL, NULL, NULL, {0} },
 };
Signature · 1 line
2.23.0.237.gc6a4ce50a0-goog
author
#6 lore raw

Re: [kvm-unit-tests PATCH v5 2/2] x86: nvmx: test max atomic switch MSRs

Quoted 3 lines
Quoted 1 line

Excerise nested VMX's atomic MSR switch code (e.g., VM-entry MSR-load

Nit: Exercise

Done.