Commit d7507a94 authored by Sean Christopherson's avatar Sean Christopherson
Browse files

KVM: SVM: Treat exit_code as an unsigned 64-bit value through all of KVM



Fix KVM's long-standing buggy handling of SVM's exit_code as a 32-bit
value.  Per the APM and Xen commit d1bd157fbc ("Big merge the HVM
full-virtualisation abstractions.") (which is arguably more trustworthy
than KVM), offset 0x70 is a single 64-bit value:

  070h 63:0 EXITCODE

Track exit_code as a single u64 to prevent reintroducing bugs where KVM
neglects to correctly set bits 63:32.

Fixes: 6aa8b732 ("[PATCH] kvm: userspace interface")
Cc: Jim Mattson <jmattson@google.com>
Cc: Yosry Ahmed <yosry.ahmed@linux.dev>
Reviewed-by: default avatarYosry Ahmed <yosry.ahmed@linux.dev>
Link: https://patch.msgid.link/20251230211347.4099600-6-seanjc@google.com


Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
parent 405fce69
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -137,8 +137,7 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
	u32 int_vector;
	u32 int_state;
	u8 reserved_3[4];
	u32 exit_code;
	u32 exit_code_hi;
	u64 exit_code;
	u64 exit_info_1;
	u64 exit_info_2;
	u32 exit_int_info;
+16 −16
Original line number Diff line number Diff line
@@ -103,38 +103,38 @@
#define SVM_EXIT_VMGEXIT       0x403

/* SEV-ES software-defined VMGEXIT events */
#define SVM_VMGEXIT_MMIO_READ			0x80000001
#define SVM_VMGEXIT_MMIO_WRITE			0x80000002
#define SVM_VMGEXIT_NMI_COMPLETE		0x80000003
#define SVM_VMGEXIT_AP_HLT_LOOP			0x80000004
#define SVM_VMGEXIT_AP_JUMP_TABLE		0x80000005
#define SVM_VMGEXIT_MMIO_READ			0x80000001ull
#define SVM_VMGEXIT_MMIO_WRITE			0x80000002ull
#define SVM_VMGEXIT_NMI_COMPLETE		0x80000003ull
#define SVM_VMGEXIT_AP_HLT_LOOP			0x80000004ull
#define SVM_VMGEXIT_AP_JUMP_TABLE		0x80000005ull
#define SVM_VMGEXIT_SET_AP_JUMP_TABLE		0
#define SVM_VMGEXIT_GET_AP_JUMP_TABLE		1
#define SVM_VMGEXIT_PSC				0x80000010
#define SVM_VMGEXIT_GUEST_REQUEST		0x80000011
#define SVM_VMGEXIT_EXT_GUEST_REQUEST		0x80000012
#define SVM_VMGEXIT_AP_CREATION			0x80000013
#define SVM_VMGEXIT_PSC				0x80000010ull
#define SVM_VMGEXIT_GUEST_REQUEST		0x80000011ull
#define SVM_VMGEXIT_EXT_GUEST_REQUEST		0x80000012ull
#define SVM_VMGEXIT_AP_CREATION			0x80000013ull
#define SVM_VMGEXIT_AP_CREATE_ON_INIT		0
#define SVM_VMGEXIT_AP_CREATE			1
#define SVM_VMGEXIT_AP_DESTROY			2
#define SVM_VMGEXIT_SNP_RUN_VMPL		0x80000018
#define SVM_VMGEXIT_SAVIC			0x8000001a
#define SVM_VMGEXIT_SNP_RUN_VMPL		0x80000018ull
#define SVM_VMGEXIT_SAVIC			0x8000001aull
#define SVM_VMGEXIT_SAVIC_REGISTER_GPA		0
#define SVM_VMGEXIT_SAVIC_UNREGISTER_GPA	1
#define SVM_VMGEXIT_SAVIC_SELF_GPA		~0ULL
#define SVM_VMGEXIT_HV_FEATURES			0x8000fffd
#define SVM_VMGEXIT_TERM_REQUEST		0x8000fffe
#define SVM_VMGEXIT_HV_FEATURES			0x8000fffdull
#define SVM_VMGEXIT_TERM_REQUEST		0x8000fffeull
#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code)	\
	/* SW_EXITINFO1[3:0] */					\
	(((((u64)reason_set) & 0xf)) |				\
	/* SW_EXITINFO1[11:4] */				\
	((((u64)reason_code) & 0xff) << 4))
#define SVM_VMGEXIT_UNSUPPORTED_EVENT		0x8000ffff
#define SVM_VMGEXIT_UNSUPPORTED_EVENT		0x8000ffffull

/* Exit code reserved for hypervisor/software use */
#define SVM_EXIT_SW				0xf0000000
#define SVM_EXIT_SW				0xf0000000ull

#define SVM_EXIT_ERR           -1
#define SVM_EXIT_ERR           -1ull

#define SVM_EXIT_REASONS \
	{ SVM_EXIT_READ_CR0,    "read_cr0" }, \
+0 −1
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@ void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu)
	struct vcpu_svm *svm = to_svm(vcpu);

	svm->vmcb->control.exit_code = HV_SVM_EXITCODE_ENL;
	svm->vmcb->control.exit_code_hi = 0;
	svm->vmcb->control.exit_info_1 = HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH;
	svm->vmcb->control.exit_info_2 = 0;
	nested_svm_vmexit(svm);
+3 −10
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu,
		 * correctly fill in the high bits of exit_info_1.
		 */
		vmcb->control.exit_code = SVM_EXIT_NPF;
		vmcb->control.exit_code_hi = 0;
		vmcb->control.exit_info_1 = (1ULL << 32);
		vmcb->control.exit_info_2 = fault->address;
	}
@@ -441,7 +440,6 @@ void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu,
	to->int_vector          = from->int_vector;
	to->int_state           = from->int_state;
	to->exit_code           = from->exit_code;
	to->exit_code_hi        = from->exit_code_hi;
	to->exit_info_1         = from->exit_info_1;
	to->exit_info_2         = from->exit_info_2;
	to->exit_int_info       = from->exit_int_info;
@@ -747,8 +745,8 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm,
	enter_guest_mode(vcpu);

	/*
	 * Filled at exit: exit_code, exit_code_hi, exit_info_1, exit_info_2,
	 * exit_int_info, exit_int_info_err, next_rip, insn_len, insn_bytes.
	 * Filled at exit: exit_code, exit_info_1, exit_info_2, exit_int_info,
	 * exit_int_info_err, next_rip, insn_len, insn_bytes.
	 */

	if (guest_cpu_cap_has(vcpu, X86_FEATURE_VGIF) &&
@@ -1018,7 +1016,6 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu)
	if (!nested_vmcb_check_save(vcpu) ||
	    !nested_vmcb_check_controls(vcpu)) {
		vmcb12->control.exit_code    = SVM_EXIT_ERR;
		vmcb12->control.exit_code_hi = -1u;
		vmcb12->control.exit_info_1  = 0;
		vmcb12->control.exit_info_2  = 0;
		goto out;
@@ -1051,7 +1048,6 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu)
	svm->soft_int_injected = false;

	svm->vmcb->control.exit_code    = SVM_EXIT_ERR;
	svm->vmcb->control.exit_code_hi = -1u;
	svm->vmcb->control.exit_info_1  = 0;
	svm->vmcb->control.exit_info_2  = 0;

@@ -1163,7 +1159,6 @@ int nested_svm_vmexit(struct vcpu_svm *svm)

	vmcb12->control.int_state         = vmcb02->control.int_state;
	vmcb12->control.exit_code         = vmcb02->control.exit_code;
	vmcb12->control.exit_code_hi      = vmcb02->control.exit_code_hi;
	vmcb12->control.exit_info_1       = vmcb02->control.exit_info_1;
	vmcb12->control.exit_info_2       = vmcb02->control.exit_info_2;

@@ -1460,7 +1455,7 @@ static int nested_svm_intercept_ioio(struct vcpu_svm *svm)

static int nested_svm_intercept(struct vcpu_svm *svm)
{
	u32 exit_code = svm->vmcb->control.exit_code;
	u64 exit_code = svm->vmcb->control.exit_code;
	int vmexit = NESTED_EXIT_HOST;

	if (svm_is_vmrun_failure(exit_code))
@@ -1532,7 +1527,6 @@ static void nested_svm_inject_exception_vmexit(struct kvm_vcpu *vcpu)
	struct vmcb *vmcb = svm->vmcb;

	vmcb->control.exit_code = SVM_EXIT_EXCP_BASE + ex->vector;
	vmcb->control.exit_code_hi = 0;

	if (ex->has_error_code)
		vmcb->control.exit_info_1 = ex->error_code;
@@ -1708,7 +1702,6 @@ static void nested_copy_vmcb_cache_to_control(struct vmcb_control_area *dst,
	dst->int_vector           = from->int_vector;
	dst->int_state            = from->int_state;
	dst->exit_code            = from->exit_code;
	dst->exit_code_hi         = from->exit_code_hi;
	dst->exit_info_1          = from->exit_info_1;
	dst->exit_info_2          = from->exit_info_2;
	dst->exit_int_info        = from->exit_int_info;
+12 −24
Original line number Diff line number Diff line
@@ -3270,11 +3270,6 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu)
		kvfree(svm->sev_es.ghcb_sa);
}

static u64 kvm_get_cached_sw_exit_code(struct vmcb_control_area *control)
{
	return (((u64)control->exit_code_hi) << 32) | control->exit_code;
}

static void dump_ghcb(struct vcpu_svm *svm)
{
	struct vmcb_control_area *control = &svm->vmcb->control;
@@ -3296,7 +3291,7 @@ static void dump_ghcb(struct vcpu_svm *svm)
	 */
	pr_err("GHCB (GPA=%016llx) snapshot:\n", svm->vmcb->control.ghcb_gpa);
	pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_code",
	       kvm_get_cached_sw_exit_code(control), kvm_ghcb_sw_exit_code_is_valid(svm));
	       control->exit_code, kvm_ghcb_sw_exit_code_is_valid(svm));
	pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_info_1",
	       control->exit_info_1, kvm_ghcb_sw_exit_info_1_is_valid(svm));
	pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_info_2",
@@ -3330,7 +3325,6 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm)
	struct vmcb_control_area *control = &svm->vmcb->control;
	struct kvm_vcpu *vcpu = &svm->vcpu;
	struct ghcb *ghcb = svm->sev_es.ghcb;
	u64 exit_code;

	/*
	 * The GHCB protocol so far allows for the following data
@@ -3364,9 +3358,7 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm)
		__kvm_emulate_msr_write(vcpu, MSR_IA32_XSS, kvm_ghcb_get_xss(svm));

	/* Copy the GHCB exit information into the VMCB fields */
	exit_code = kvm_ghcb_get_sw_exit_code(svm);
	control->exit_code = lower_32_bits(exit_code);
	control->exit_code_hi = upper_32_bits(exit_code);
	control->exit_code = kvm_ghcb_get_sw_exit_code(svm);
	control->exit_info_1 = kvm_ghcb_get_sw_exit_info_1(svm);
	control->exit_info_2 = kvm_ghcb_get_sw_exit_info_2(svm);
	svm->sev_es.sw_scratch = kvm_ghcb_get_sw_scratch_if_valid(svm);
@@ -3379,15 +3371,8 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
{
	struct vmcb_control_area *control = &svm->vmcb->control;
	struct kvm_vcpu *vcpu = &svm->vcpu;
	u64 exit_code;
	u64 reason;

	/*
	 * Retrieve the exit code now even though it may not be marked valid
	 * as it could help with debugging.
	 */
	exit_code = kvm_get_cached_sw_exit_code(control);

	/* Only GHCB Usage code 0 is supported */
	if (svm->sev_es.ghcb->ghcb_usage) {
		reason = GHCB_ERR_INVALID_USAGE;
@@ -3401,7 +3386,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
	    !kvm_ghcb_sw_exit_info_2_is_valid(svm))
		goto vmgexit_err;

	switch (exit_code) {
	switch (control->exit_code) {
	case SVM_EXIT_READ_DR7:
		break;
	case SVM_EXIT_WRITE_DR7:
@@ -3502,15 +3487,19 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
	return 0;

vmgexit_err:
	/*
	 * Print the exit code even though it may not be marked valid as it
	 * could help with debugging.
	 */
	if (reason == GHCB_ERR_INVALID_USAGE) {
		vcpu_unimpl(vcpu, "vmgexit: ghcb usage %#x is not valid\n",
			    svm->sev_es.ghcb->ghcb_usage);
	} else if (reason == GHCB_ERR_INVALID_EVENT) {
		vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n",
			    exit_code);
			    control->exit_code);
	} else {
		vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n",
			    exit_code);
			    control->exit_code);
		dump_ghcb(svm);
	}

@@ -4349,7 +4338,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	struct vmcb_control_area *control = &svm->vmcb->control;
	u64 ghcb_gpa, exit_code;
	u64 ghcb_gpa;
	int ret;

	/* Validate the GHCB */
@@ -4391,8 +4380,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)

	svm_vmgexit_success(svm, 0);

	exit_code = kvm_get_cached_sw_exit_code(control);
	switch (exit_code) {
	switch (control->exit_code) {
	case SVM_VMGEXIT_MMIO_READ:
		ret = setup_vmgexit_scratch(svm, true, control->exit_info_2);
		if (ret)
@@ -4484,7 +4472,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
		ret = -EINVAL;
		break;
	default:
		ret = svm_invoke_exit_handler(vcpu, exit_code);
		ret = svm_invoke_exit_handler(vcpu, control->exit_code);
	}

	return ret;
Loading