diff mbox

[kvm-unit-tests] x86: Add test for nested VM entry prereqs

Message ID 20180613181948.98657-1-marcorr@google.com (mailing list archive)
State New, archived
Headers show

Commit Message

Marc Orr June 13, 2018, 6:19 p.m. UTC
This patch adds a test for the prereq checks done as a part of a nested
VM launch related to event injection.

Signed-off-by: Marc Orr <marcorr@google.com>
---
 lib/x86/processor.h |   8 ++
 x86/vmx.h           |  27 ++++++
 x86/vmx_tests.c     | 195 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 230 insertions(+)

Comments

Paolo Bonzini June 13, 2018, 7:19 p.m. UTC | #1
On 13/06/2018 20:19, Marc Orr wrote:
> This patch adds a test for the prereq checks done as a part of a nested
> VM launch related to event injection.
> 
> Signed-off-by: Marc Orr <marcorr@google.com>

Oh, of course kvm-unit-tests is great too for a test case. :)

Paolo

> ---
>  lib/x86/processor.h |   8 ++
>  x86/vmx.h           |  27 ++++++
>  x86/vmx_tests.c     | 195 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 230 insertions(+)
> 
> diff --git a/lib/x86/processor.h b/lib/x86/processor.h
> index 247386207bb0..886170cfa163 100644
> --- a/lib/x86/processor.h
> +++ b/lib/x86/processor.h
> @@ -15,6 +15,14 @@
>  #  define S "4"
>  #endif
>  
> +#define DF_VECTOR 8
> +#define TS_VECTOR 10
> +#define NP_VECTOR 11
> +#define SS_VECTOR 12
> +#define GP_VECTOR 13
> +#define PF_VECTOR 14
> +#define AC_VECTOR 17
> +
>  #define X86_CR0_PE     0x00000001
>  #define X86_CR0_MP     0x00000002
>  #define X86_CR0_TS     0x00000008
> diff --git a/x86/vmx.h b/x86/vmx.h
> index bdcaac0edc01..116bbd515beb 100644
> --- a/x86/vmx.h
> +++ b/x86/vmx.h
> @@ -420,6 +420,15 @@ enum Intr_type {
>  
>  #define INTR_INFO_INTR_TYPE_SHIFT       8
>  
> +#define INTR_TYPE_EXT_INTR              (0 << 8) /* external interrupt */
> +#define INTR_TYPE_RESERVED              (1 << 8) /* reserved */
> +#define INTR_TYPE_NMI_INTR		(2 << 8) /* NMI */
> +#define INTR_TYPE_HARD_EXCEPTION	(3 << 8) /* processor exception */
> +#define INTR_TYPE_SOFT_INTR             (4 << 8) /* software interrupt */
> +#define INTR_TYPE_PRIV_SW_EXCEPTION	(5 << 8) /* priv. software exception */
> +#define INTR_TYPE_SOFT_EXCEPTION	(6 << 8) /* software exception */
> +#define INTR_TYPE_OTHER_EVENT           (7 << 8) /* other event */
> +
>  /*
>   * VM-instruction error numbers
>   */
> @@ -712,6 +721,24 @@ static inline bool invvpid(unsigned long type, u64 vpid, u64 gla)
>  	return ret;
>  }
>  
> +static inline int enable_unrestricted_guest(void)
> +{
> +	if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY))
> +		return -1;
> +
> +	if (!(ctrl_cpu_rev[1].clr & CPU_URG))
> +		return -1;
> +
> +	vmcs_write(CPU_EXEC_CTRL0, vmcs_read(CPU_EXEC_CTRL0) | CPU_SECONDARY);
> +	vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) | CPU_URG);
> +	return 0;
> +}
> +
> +static inline void disable_unrestricted_guest(void)
> +{
> +	vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) & ~CPU_URG);
> +}
> +
>  const char *exit_reason_description(u64 reason);
>  void print_vmexit_info();
>  void print_vmentry_failure_info(struct vmentry_failure *failure);
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index 0c1a6952f9ea..57246905982b 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -3561,6 +3561,200 @@ static void try_tpr_threshold_and_vtpr(unsigned threshold, unsigned vtpr)
>  	report_prefix_pop();
>  }
>  
> +static void test_invalid_event_injection(void)
> +{
> +	u32 ent_intr_info_save = vmcs_read(ENT_INTR_INFO);
> +	u32 ent_intr_error_save = vmcs_read(ENT_INTR_ERROR);
> +	u32 ent_inst_len_save = vmcs_read(ENT_INST_LEN);
> +	u32 primary_save = vmcs_read(CPU_EXEC_CTRL0);
> +	u32 secondary_save = vmcs_read(CPU_EXEC_CTRL1);
> +	u64 guest_cr0_save = vmcs_read(GUEST_CR0);
> +	u32 ent_intr_info_base = INTR_INFO_VALID_MASK;
> +	u32 ent_intr_info, ent_intr_err, ent_intr_len;
> +	u32 cnt;
> +
> +	/* Setup */
> +	report_prefix_push("invalid event injection");
> +	vmcs_write(ENT_INTR_ERROR, 0x00000000);
> +	vmcs_write(ENT_INST_LEN, 0x00000001);
> +
> +	/* The field’s interruption type is not set to a reserved value. */
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_RESERVED;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "RESERVED interruption type invalid",
> +			    ent_intr_info);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	/* If the interruption type is NMI, the vector is 2. */
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_NMI_INTR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "(NMI && nr != 2) invalid", ent_intr_info);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	/*
> +	 * If the interruption type
> +	 * is HW exception, the vector is at most 31.
> +	 */
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_HARD_EXCEPTION | 0x20;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "(HW exception && nr > 31) invalid", ent_intr_info);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	/* If the interruption type is other event, the vector is 0. */
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_OTHER_EVENT | 0x1;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "(OTHER EVENT && nr != 0) invalid", ent_intr_info);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	/*
> +	 * deliver-error-code is 1 iff either
> +	 * (a) the "unrestricted guest" VM-execution control is 0
> +	 * (b) CR0.PE is set.
> +	 */
> +	ent_intr_info = ent_intr_info_base | INTR_INFO_DELIVER_CODE_MASK |
> +			INTR_TYPE_HARD_EXCEPTION | GP_VECTOR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "error code <-> (!URG || prot_mode)",
> +			    ent_intr_info);
> +	enable_unrestricted_guest();
> +	vmcs_write(GUEST_CR0, guest_cr0_save & ~X86_CR0_PE & ~X86_CR0_PG);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_HARD_EXCEPTION |
> +			GP_VECTOR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "error code <-> (!URG || prot_mode)",
> +			    ent_intr_info);
> +	vmcs_write(GUEST_CR0, guest_cr0_save | X86_CR0_PE);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	/* deliver-error-code is 1 iff the interruption type is HW exception */
> +	report_prefix_push("error code <-> HW exception");
> +	for (cnt = 0; cnt < 8; cnt++) {
> +		u32 exception_type_mask = cnt << 8;
> +		u32 deliver_error_code_mask =
> +			exception_type_mask != INTR_TYPE_HARD_EXCEPTION ?
> +			INTR_INFO_DELIVER_CODE_MASK : 0;
> +
> +		ent_intr_info = ent_intr_info_base | deliver_error_code_mask |
> +				exception_type_mask | GP_VECTOR;
> +		report_prefix_pushf("VM-entry intr info=0x%x", ent_intr_info);
> +		vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +		test_vmx_controls(false, false);
> +		report_prefix_pop();
> +	}
> +	report_prefix_pop();
> +
> +	/*
> +	 * deliver-error-code is 1 iff the the vector
> +	 * indicates an exception that would normally deliver an error code
> +	 */
> +	report_prefix_push("error code <-> nr normally delivers an error code");
> +	for (cnt = 0; cnt < 32; cnt++) {
> +		u32 deliver_error_code_mask = INTR_INFO_DELIVER_CODE_MASK;
> +
> +		switch (cnt) {
> +		case DF_VECTOR:
> +		case TS_VECTOR:
> +		case NP_VECTOR:
> +		case SS_VECTOR:
> +		case GP_VECTOR:
> +		case PF_VECTOR:
> +		case AC_VECTOR:
> +			deliver_error_code_mask = 0;
> +		}
> +		ent_intr_info = ent_intr_info_base | deliver_error_code_mask |
> +				INTR_TYPE_HARD_EXCEPTION | cnt;
> +		report_prefix_pushf("VM-entry intr info=0x%x", ent_intr_info);
> +		vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +		test_vmx_controls(false, false);
> +		report_prefix_pop();
> +	}
> +	report_prefix_pop();
> +
> +	/* Reserved bits in the field (30:12) are 0. */
> +	report_prefix_push("reserved bits clear");
> +	for (cnt = 12; cnt <= 30; cnt++) {
> +		ent_intr_info = ent_intr_info_base |
> +				INTR_INFO_DELIVER_CODE_MASK |
> +				INTR_TYPE_HARD_EXCEPTION | GP_VECTOR |
> +				(1U << cnt);
> +		report_prefix_pushf("VM-entry intr info=0x%x", ent_intr_info);
> +		vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +		test_vmx_controls(false, false);
> +		report_prefix_pop();
> +	}
> +	report_prefix_pop();
> +
> +	/*
> +	 * If deliver-error-code is 1
> +	 * bits 31:15 of the VM-entry exception error-code field are 0.
> +	 */
> +	ent_intr_info = ent_intr_info_base | INTR_INFO_DELIVER_CODE_MASK |
> +			INTR_TYPE_HARD_EXCEPTION | GP_VECTOR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "VM-entry exception error code[31:15] clear",
> +			    ent_intr_info);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	for (cnt = 15; cnt <= 31; cnt++) {
> +		ent_intr_err = 1U << cnt;
> +		report_prefix_pushf("VM-entry intr error=0x%x", ent_intr_err);
> +		vmcs_write(ENT_INTR_ERROR, ent_intr_err);
> +		test_vmx_controls(false, false);
> +		report_prefix_pop();
> +	}
> +	vmcs_write(ENT_INTR_ERROR, 0x00000000);
> +	report_prefix_pop();
> +
> +	/*
> +	 * If the interruption type is software interrupt, software exception,
> +	 * or privileged software exception, the VM-entry instruction-length
> +	 * field is in the range 0–15.
> +	 */
> +
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_SOFT_EXCEPTION;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "VM-entry instruction-length check", ent_intr_info);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +
> +	/* Instruction length set to -1 (0xFFFFFFFF) should fail */
> +	ent_intr_len = -1;
> +	report_prefix_pushf("VM-entry intr length = 0x%x", ent_intr_len);
> +	vmcs_write(ENT_INST_LEN, ent_intr_len);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	/* Instruction length set to 16 should fail */
> +	ent_intr_len = 0x00000010;
> +	report_prefix_pushf("VM-entry intr length = 0x%x", ent_intr_len);
> +	vmcs_write(ENT_INST_LEN, 0x00000010);
> +	test_vmx_controls(false, false);
> +	report_prefix_pop();
> +
> +	report_prefix_pop();
> +
> +	/* Cleanup */
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info_save);
> +	vmcs_write(ENT_INTR_ERROR, ent_intr_error_save);
> +	vmcs_write(ENT_INST_LEN, ent_inst_len_save);
> +	vmcs_write(CPU_EXEC_CTRL0, primary_save);
> +	vmcs_write(CPU_EXEC_CTRL1, secondary_save);
> +	vmcs_write(GUEST_CR0, guest_cr0_save);
> +	report_prefix_pop();
> +}
> +
>  /*
>   * Test interesting vTPR values for a given TPR threshold.
>   */
> @@ -3807,6 +4001,7 @@ static void vmx_controls_test(void)
>  	test_apic_virt_addr();
>  	test_tpr_threshold();
>  	test_nmi_ctrls();
> +	test_invalid_event_injection();
>  }
>  
>  static bool valid_vmcs_for_vmentry(void)
>
Marc Orr June 14, 2018, 1:34 p.m. UTC | #2
> > This patch adds a test for the prereq checks done as a part of a nested
> > VM launch related to event injection.
> >
> > Signed-off-by: Marc Orr <marcorr@google.com>
>
> Oh, of course kvm-unit-tests is great too for a test case. :)

Great! Do we still need to extend tools/testing/selftests with a test
case then? Sorry if the answer is obvious, but I actually wasn't aware
of those tests before you mentioned them.
Paolo Bonzini June 14, 2018, 1:59 p.m. UTC | #3
On 14/06/2018 15:34, Marc Orr wrote:
>>> This patch adds a test for the prereq checks done as a part of a nested
>>> VM launch related to event injection.
>>>
>>> Signed-off-by: Marc Orr <marcorr@google.com>
>>
>> Oh, of course kvm-unit-tests is great too for a test case. :)
> 
> Great! Do we still need to extend tools/testing/selftests with a test
> case then? Sorry if the answer is obvious, but I actually wasn't aware
> of those tests before you mentioned them.

No, kvm-unit-tests is enough.  The tools/testing tests were contributed
by Google, so I thought it would have been easier for you. :)

Paolo
Jim Mattson June 20, 2018, 5:16 p.m. UTC | #4
On Wed, Jun 13, 2018 at 11:19 AM, Marc Orr <marcorr@google.com> wrote:
> This patch adds a test for the prereq checks done as a part of a nested
> VM launch related to event injection.
>
> Signed-off-by: Marc Orr <marcorr@google.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Marc Orr June 21, 2018, 12:07 a.m. UTC | #5
> + /* The field’s interruption type is not set to a reserved value. */
> + ent_intr_info = ent_intr_info_base | INTR_TYPE_RESERVED;
> + report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +    "RESERVED interruption type invalid",
> +    ent_intr_info);
> + vmcs_write(ENT_INTR_INFO, ent_intr_info);
> + test_vmx_controls(false, false);
> + report_prefix_pop();
>
> What about the positive test case i.e., when INTR_TYPE_RESERVED is not set ?

I'll add a positive test case for interrupt type != INTR_TYPE_RESERVED.

> + /* If the interruption type is NMI, the vector is 2. */
> + ent_intr_info = ent_intr_info_base | INTR_TYPE_NMI_INTR;
> + report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +    "(NMI && nr != 2) invalid", ent_intr_info);
>
> 'nr' is not meaningful in the string. You should probably replace it with 'vector' or some other meaningful description.

I'll change nr to vector.

> + vmcs_write(ENT_INTR_INFO, ent_intr_info);
> + test_vmx_controls(false, false);
> + report_prefix_pop();
>
> We should also add do the positive test case i.e., when the vector value is 2, the test should pass.
> For readability purpose, I think it is useful to add a vector value (say, zero) explicitly in the negative case (the one you have here).

I'll add the positive test case for NMI && vector == 2.
I'll also explicitly set the vector to 0 (i.e., DE_VECTOR).

> + /*
> + * If the interruption type
> + * is HW exception, the vector is at most 31.
> + */
> + ent_intr_info = ent_intr_info_base | INTR_TYPE_HARD_EXCEPTION | 0x20;
> + report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +    "(HW exception && nr > 31) invalid", ent_intr_info);
> + vmcs_write(ENT_INTR_INFO, ent_intr_info);
> + test_vmx_controls(false, false);
> + report_prefix_pop();
>
> We should also add a positive test case i.e., when the vector is 31 or less, the test should pass.

Rather than add the positive test case you're suggesting here, I'll
update the loop below, that sets the "deliver error code" bit for each
interrupt vector to all also check the positive case. This will
achieve the positive test coverage you're suggesting.


> + /* If the interruption type is other event, the vector is 0. */
> + ent_intr_info = ent_intr_info_base | INTR_TYPE_OTHER_EVENT | 0x1;
> + report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +    "(OTHER EVENT && nr != 0) invalid", ent_intr_info);
>
> + vmcs_write(ENT_INTR_INFO, ent_intr_info);
> + test_vmx_controls(false, false);
> + report_prefix_pop();
>
> Nit: It is better to place this test right after the test for INTR_TYPE_RESERVED above because the SDM describes the two rules together.

I'll move it.

>
> According to the SDM, the INTR_TYPE_OTHER_EVENT bits will be zero on processors that don't support the "monitor trap flag" control. Don't we need to include  "monitor trap flag" here ?

Yes, you're right about the SDM and the patch that this test
corresponds to. But the function nested_vmx_setup_ctls_msrs hard-codes
this control as disabled. Since it's not actually configurable (e.g.,
by a KVM ioctl), I decided not to add a test for this case.
diff mbox

Patch

diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 247386207bb0..886170cfa163 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -15,6 +15,14 @@ 
 #  define S "4"
 #endif
 
+#define DF_VECTOR 8
+#define TS_VECTOR 10
+#define NP_VECTOR 11
+#define SS_VECTOR 12
+#define GP_VECTOR 13
+#define PF_VECTOR 14
+#define AC_VECTOR 17
+
 #define X86_CR0_PE     0x00000001
 #define X86_CR0_MP     0x00000002
 #define X86_CR0_TS     0x00000008
diff --git a/x86/vmx.h b/x86/vmx.h
index bdcaac0edc01..116bbd515beb 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -420,6 +420,15 @@  enum Intr_type {
 
 #define INTR_INFO_INTR_TYPE_SHIFT       8
 
+#define INTR_TYPE_EXT_INTR              (0 << 8) /* external interrupt */
+#define INTR_TYPE_RESERVED              (1 << 8) /* reserved */
+#define INTR_TYPE_NMI_INTR		(2 << 8) /* NMI */
+#define INTR_TYPE_HARD_EXCEPTION	(3 << 8) /* processor exception */
+#define INTR_TYPE_SOFT_INTR             (4 << 8) /* software interrupt */
+#define INTR_TYPE_PRIV_SW_EXCEPTION	(5 << 8) /* priv. software exception */
+#define INTR_TYPE_SOFT_EXCEPTION	(6 << 8) /* software exception */
+#define INTR_TYPE_OTHER_EVENT           (7 << 8) /* other event */
+
 /*
  * VM-instruction error numbers
  */
@@ -712,6 +721,24 @@  static inline bool invvpid(unsigned long type, u64 vpid, u64 gla)
 	return ret;
 }
 
+static inline int enable_unrestricted_guest(void)
+{
+	if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY))
+		return -1;
+
+	if (!(ctrl_cpu_rev[1].clr & CPU_URG))
+		return -1;
+
+	vmcs_write(CPU_EXEC_CTRL0, vmcs_read(CPU_EXEC_CTRL0) | CPU_SECONDARY);
+	vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) | CPU_URG);
+	return 0;
+}
+
+static inline void disable_unrestricted_guest(void)
+{
+	vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) & ~CPU_URG);
+}
+
 const char *exit_reason_description(u64 reason);
 void print_vmexit_info();
 void print_vmentry_failure_info(struct vmentry_failure *failure);
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 0c1a6952f9ea..57246905982b 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -3561,6 +3561,200 @@  static void try_tpr_threshold_and_vtpr(unsigned threshold, unsigned vtpr)
 	report_prefix_pop();
 }
 
+static void test_invalid_event_injection(void)
+{
+	u32 ent_intr_info_save = vmcs_read(ENT_INTR_INFO);
+	u32 ent_intr_error_save = vmcs_read(ENT_INTR_ERROR);
+	u32 ent_inst_len_save = vmcs_read(ENT_INST_LEN);
+	u32 primary_save = vmcs_read(CPU_EXEC_CTRL0);
+	u32 secondary_save = vmcs_read(CPU_EXEC_CTRL1);
+	u64 guest_cr0_save = vmcs_read(GUEST_CR0);
+	u32 ent_intr_info_base = INTR_INFO_VALID_MASK;
+	u32 ent_intr_info, ent_intr_err, ent_intr_len;
+	u32 cnt;
+
+	/* Setup */
+	report_prefix_push("invalid event injection");
+	vmcs_write(ENT_INTR_ERROR, 0x00000000);
+	vmcs_write(ENT_INST_LEN, 0x00000001);
+
+	/* The field’s interruption type is not set to a reserved value. */
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_RESERVED;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "RESERVED interruption type invalid",
+			    ent_intr_info);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	/* If the interruption type is NMI, the vector is 2. */
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_NMI_INTR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "(NMI && nr != 2) invalid", ent_intr_info);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	/*
+	 * If the interruption type
+	 * is HW exception, the vector is at most 31.
+	 */
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_HARD_EXCEPTION | 0x20;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "(HW exception && nr > 31) invalid", ent_intr_info);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	/* If the interruption type is other event, the vector is 0. */
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_OTHER_EVENT | 0x1;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "(OTHER EVENT && nr != 0) invalid", ent_intr_info);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	/*
+	 * deliver-error-code is 1 iff either
+	 * (a) the "unrestricted guest" VM-execution control is 0
+	 * (b) CR0.PE is set.
+	 */
+	ent_intr_info = ent_intr_info_base | INTR_INFO_DELIVER_CODE_MASK |
+			INTR_TYPE_HARD_EXCEPTION | GP_VECTOR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "error code <-> (!URG || prot_mode)",
+			    ent_intr_info);
+	enable_unrestricted_guest();
+	vmcs_write(GUEST_CR0, guest_cr0_save & ~X86_CR0_PE & ~X86_CR0_PG);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_HARD_EXCEPTION |
+			GP_VECTOR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "error code <-> (!URG || prot_mode)",
+			    ent_intr_info);
+	vmcs_write(GUEST_CR0, guest_cr0_save | X86_CR0_PE);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	/* deliver-error-code is 1 iff the interruption type is HW exception */
+	report_prefix_push("error code <-> HW exception");
+	for (cnt = 0; cnt < 8; cnt++) {
+		u32 exception_type_mask = cnt << 8;
+		u32 deliver_error_code_mask =
+			exception_type_mask != INTR_TYPE_HARD_EXCEPTION ?
+			INTR_INFO_DELIVER_CODE_MASK : 0;
+
+		ent_intr_info = ent_intr_info_base | deliver_error_code_mask |
+				exception_type_mask | GP_VECTOR;
+		report_prefix_pushf("VM-entry intr info=0x%x", ent_intr_info);
+		vmcs_write(ENT_INTR_INFO, ent_intr_info);
+		test_vmx_controls(false, false);
+		report_prefix_pop();
+	}
+	report_prefix_pop();
+
+	/*
+	 * deliver-error-code is 1 iff the the vector
+	 * indicates an exception that would normally deliver an error code
+	 */
+	report_prefix_push("error code <-> nr normally delivers an error code");
+	for (cnt = 0; cnt < 32; cnt++) {
+		u32 deliver_error_code_mask = INTR_INFO_DELIVER_CODE_MASK;
+
+		switch (cnt) {
+		case DF_VECTOR:
+		case TS_VECTOR:
+		case NP_VECTOR:
+		case SS_VECTOR:
+		case GP_VECTOR:
+		case PF_VECTOR:
+		case AC_VECTOR:
+			deliver_error_code_mask = 0;
+		}
+		ent_intr_info = ent_intr_info_base | deliver_error_code_mask |
+				INTR_TYPE_HARD_EXCEPTION | cnt;
+		report_prefix_pushf("VM-entry intr info=0x%x", ent_intr_info);
+		vmcs_write(ENT_INTR_INFO, ent_intr_info);
+		test_vmx_controls(false, false);
+		report_prefix_pop();
+	}
+	report_prefix_pop();
+
+	/* Reserved bits in the field (30:12) are 0. */
+	report_prefix_push("reserved bits clear");
+	for (cnt = 12; cnt <= 30; cnt++) {
+		ent_intr_info = ent_intr_info_base |
+				INTR_INFO_DELIVER_CODE_MASK |
+				INTR_TYPE_HARD_EXCEPTION | GP_VECTOR |
+				(1U << cnt);
+		report_prefix_pushf("VM-entry intr info=0x%x", ent_intr_info);
+		vmcs_write(ENT_INTR_INFO, ent_intr_info);
+		test_vmx_controls(false, false);
+		report_prefix_pop();
+	}
+	report_prefix_pop();
+
+	/*
+	 * If deliver-error-code is 1
+	 * bits 31:15 of the VM-entry exception error-code field are 0.
+	 */
+	ent_intr_info = ent_intr_info_base | INTR_INFO_DELIVER_CODE_MASK |
+			INTR_TYPE_HARD_EXCEPTION | GP_VECTOR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "VM-entry exception error code[31:15] clear",
+			    ent_intr_info);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	for (cnt = 15; cnt <= 31; cnt++) {
+		ent_intr_err = 1U << cnt;
+		report_prefix_pushf("VM-entry intr error=0x%x", ent_intr_err);
+		vmcs_write(ENT_INTR_ERROR, ent_intr_err);
+		test_vmx_controls(false, false);
+		report_prefix_pop();
+	}
+	vmcs_write(ENT_INTR_ERROR, 0x00000000);
+	report_prefix_pop();
+
+	/*
+	 * If the interruption type is software interrupt, software exception,
+	 * or privileged software exception, the VM-entry instruction-length
+	 * field is in the range 0–15.
+	 */
+
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_SOFT_EXCEPTION;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "VM-entry instruction-length check", ent_intr_info);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+
+	/* Instruction length set to -1 (0xFFFFFFFF) should fail */
+	ent_intr_len = -1;
+	report_prefix_pushf("VM-entry intr length = 0x%x", ent_intr_len);
+	vmcs_write(ENT_INST_LEN, ent_intr_len);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	/* Instruction length set to 16 should fail */
+	ent_intr_len = 0x00000010;
+	report_prefix_pushf("VM-entry intr length = 0x%x", ent_intr_len);
+	vmcs_write(ENT_INST_LEN, 0x00000010);
+	test_vmx_controls(false, false);
+	report_prefix_pop();
+
+	report_prefix_pop();
+
+	/* Cleanup */
+	vmcs_write(ENT_INTR_INFO, ent_intr_info_save);
+	vmcs_write(ENT_INTR_ERROR, ent_intr_error_save);
+	vmcs_write(ENT_INST_LEN, ent_inst_len_save);
+	vmcs_write(CPU_EXEC_CTRL0, primary_save);
+	vmcs_write(CPU_EXEC_CTRL1, secondary_save);
+	vmcs_write(GUEST_CR0, guest_cr0_save);
+	report_prefix_pop();
+}
+
 /*
  * Test interesting vTPR values for a given TPR threshold.
  */
@@ -3807,6 +4001,7 @@  static void vmx_controls_test(void)
 	test_apic_virt_addr();
 	test_tpr_threshold();
 	test_nmi_ctrls();
+	test_invalid_event_injection();
 }
 
 static bool valid_vmcs_for_vmentry(void)