diff mbox

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

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

Commit Message

Marc Orr June 26, 2018, 1:48 a.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>
---
Changelog since v2:
- Changed positive test case for "RESERVED interruption type invalid" to use
INTR_TYPE_EXT_INTR interruption type.
- Added a test case for "error code <-> (!URG || prot_mode)", where unrestricted
guest is disabled.
- Added tests for INTR_TYPE_PRIV_SW_EXCEPTION and INTR_TYPE_SOFT_INTR.
- Annotated logs with [-] for negative test case, where VM-entry should fail and
[+] for positive test case, where VM-entry should succeed.

Changelog since v1:
- Add positive test case for interrupt type != INTR_TYPE_RESERVED
- Renamed nr to vector
- Add positive test case for interrupt type == INTR_TYPE_NMI_INTR
- Explicitly encode 0 vecotr (i.e., DE_VECTOR)
- Updated deliver errorcode checks to test postive cases
- Moved test for INTR_TYPE_OTHER_EVENT after test for INTR_TYPE_RESERVED

 lib/x86/processor.h |   8 ++
 x86/types.h         |   1 +
 x86/vmx.h           |  27 +++++
 x86/vmx_tests.c     | 276 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 312 insertions(+)

Comments

Paolo Bonzini June 26, 2018, 11:27 a.m. UTC | #1
On 26/06/2018 03:48, 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>
> ---
> Changelog since v2:
> - Changed positive test case for "RESERVED interruption type invalid" to use
> INTR_TYPE_EXT_INTR interruption type.
> - Added a test case for "error code <-> (!URG || prot_mode)", where unrestricted
> guest is disabled.
> - Added tests for INTR_TYPE_PRIV_SW_EXCEPTION and INTR_TYPE_SOFT_INTR.
> - Annotated logs with [-] for negative test case, where VM-entry should fail and
> [+] for positive test case, where VM-entry should succeed.
> 
> Changelog since v1:
> - Add positive test case for interrupt type != INTR_TYPE_RESERVED
> - Renamed nr to vector
> - Add positive test case for interrupt type == INTR_TYPE_NMI_INTR
> - Explicitly encode 0 vecotr (i.e., DE_VECTOR)
> - Updated deliver errorcode checks to test postive cases
> - Moved test for INTR_TYPE_OTHER_EVENT after test for INTR_TYPE_RESERVED
> 
>  lib/x86/processor.h |   8 ++
>  x86/types.h         |   1 +
>  x86/vmx.h           |  27 +++++
>  x86/vmx_tests.c     | 276 ++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 312 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/types.h b/x86/types.h
> index fd22743990f1..047556e854d6 100644
> --- a/x86/types.h
> +++ b/x86/types.h
> @@ -3,6 +3,7 @@
>  
>  #define DE_VECTOR 0
>  #define DB_VECTOR 1
> +#define NMI_VECTOR 2
>  #define BP_VECTOR 3
>  #define OF_VECTOR 4
>  #define BR_VECTOR 5
> 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..4eb8fefb9bbf 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -3561,6 +3561,281 @@ 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 | DE_VECTOR;
> +	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();
> +
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_EXT_INTR |
> +			DE_VECTOR;
> +	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(true, 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 | DB_VECTOR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "(OTHER EVENT && vector != 0) 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 (negative case). */
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_NMI_INTR | DE_VECTOR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "(NMI && vector != 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 NMI, the vector is 2 (positive case). */
> +	ent_intr_info = ent_intr_info_base | INTR_TYPE_NMI_INTR | NMI_VECTOR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "(NMI && vector == 2) valid [+]", ent_intr_info);
> +	vmcs_write(ENT_INTR_INFO, ent_intr_info);
> +	test_vmx_controls(true, 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 && vector > 31) 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_TYPE_HARD_EXCEPTION |
> +			GP_VECTOR;
> +	report_prefix_pushf("%s, VM-entry intr info=0x%x",
> +			    "error code <-> (!URG || prot_mode) [-]",
> +			    ent_intr_info);
> +	disable_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_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);
> +	disable_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(true, false);
> +	report_prefix_pop();
> +
> +	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 <-> vector delivers error code");
> +	for (cnt = 0; cnt < 32; cnt++) {
> +		bool has_error_code = false;
> +		u32 deliver_error_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:
> +			has_error_code = true;
> +		}
> +
> +		/* Negative case */
> +		deliver_error_code_mask = has_error_code ?
> +						0 :
> +						INTR_INFO_DELIVER_CODE_MASK;
> +		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();
> +
> +		/* Positive case */
> +		deliver_error_code_mask = has_error_code ?
> +						INTR_INFO_DELIVER_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(true, 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.
> +	 */
> +
> +	for (cnt = 0; cnt < 3; cnt++) {
> +		switch (cnt) {
> +		case 0:
> +			ent_intr_info = ent_intr_info_base |
> +					INTR_TYPE_SOFT_INTR;
> +			break;
> +		case 1:
> +			ent_intr_info = ent_intr_info_base |
> +					INTR_TYPE_SOFT_EXCEPTION;
> +			break;
> +		case 2:
> +			ent_intr_info = ent_intr_info_base |
> +					INTR_TYPE_PRIV_SW_EXCEPTION;
> +			break;
> +		}
> +		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 +4082,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)
> 

Queued, thanks.

Paolo
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/types.h b/x86/types.h
index fd22743990f1..047556e854d6 100644
--- a/x86/types.h
+++ b/x86/types.h
@@ -3,6 +3,7 @@ 
 
 #define DE_VECTOR 0
 #define DB_VECTOR 1
+#define NMI_VECTOR 2
 #define BP_VECTOR 3
 #define OF_VECTOR 4
 #define BR_VECTOR 5
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..4eb8fefb9bbf 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -3561,6 +3561,281 @@  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 | DE_VECTOR;
+	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();
+
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_EXT_INTR |
+			DE_VECTOR;
+	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(true, 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 | DB_VECTOR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "(OTHER EVENT && vector != 0) 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 (negative case). */
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_NMI_INTR | DE_VECTOR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "(NMI && vector != 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 NMI, the vector is 2 (positive case). */
+	ent_intr_info = ent_intr_info_base | INTR_TYPE_NMI_INTR | NMI_VECTOR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "(NMI && vector == 2) valid [+]", ent_intr_info);
+	vmcs_write(ENT_INTR_INFO, ent_intr_info);
+	test_vmx_controls(true, 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 && vector > 31) 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_TYPE_HARD_EXCEPTION |
+			GP_VECTOR;
+	report_prefix_pushf("%s, VM-entry intr info=0x%x",
+			    "error code <-> (!URG || prot_mode) [-]",
+			    ent_intr_info);
+	disable_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_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);
+	disable_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(true, false);
+	report_prefix_pop();
+
+	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 <-> vector delivers error code");
+	for (cnt = 0; cnt < 32; cnt++) {
+		bool has_error_code = false;
+		u32 deliver_error_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:
+			has_error_code = true;
+		}
+
+		/* Negative case */
+		deliver_error_code_mask = has_error_code ?
+						0 :
+						INTR_INFO_DELIVER_CODE_MASK;
+		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();
+
+		/* Positive case */
+		deliver_error_code_mask = has_error_code ?
+						INTR_INFO_DELIVER_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(true, 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.
+	 */
+
+	for (cnt = 0; cnt < 3; cnt++) {
+		switch (cnt) {
+		case 0:
+			ent_intr_info = ent_intr_info_base |
+					INTR_TYPE_SOFT_INTR;
+			break;
+		case 1:
+			ent_intr_info = ent_intr_info_base |
+					INTR_TYPE_SOFT_EXCEPTION;
+			break;
+		case 2:
+			ent_intr_info = ent_intr_info_base |
+					INTR_TYPE_PRIV_SW_EXCEPTION;
+			break;
+		}
+		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 +4082,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)