Commit 0c76b1d0 authored by Brijesh Singh's avatar Brijesh Singh Committed by Paolo Bonzini
Browse files

KVM: SEV: Add support to handle GHCB GPA register VMGEXIT



SEV-SNP guests are required to perform a GHCB GPA registration. Before
using a GHCB GPA for a vCPU the first time, a guest must register the
vCPU GHCB GPA. If hypervisor can work with the guest requested GPA then
it must respond back with the same GPA otherwise return -1.

On VMEXIT, verify that the GHCB GPA matches with the registered value.
If a mismatch is detected, then abort the guest.

Signed-off-by: default avatarBrijesh Singh <brijesh.singh@amd.com>
Signed-off-by: default avatarAshish Kalra <ashish.kalra@amd.com>
Signed-off-by: default avatarMichael Roth <michael.roth@amd.com>
Message-ID: <20240501085210.2213060-9-michael.roth@amd.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent ad27ce15
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -59,6 +59,14 @@
#define GHCB_MSR_AP_RESET_HOLD_RESULT_POS	12
#define GHCB_MSR_AP_RESET_HOLD_RESULT_MASK	GENMASK_ULL(51, 0)

/* Preferred GHCB GPA Request */
#define GHCB_MSR_PREF_GPA_REQ		0x010
#define GHCB_MSR_GPA_VALUE_POS		12
#define GHCB_MSR_GPA_VALUE_MASK		GENMASK_ULL(51, 0)

#define GHCB_MSR_PREF_GPA_RESP		0x011
#define GHCB_MSR_PREF_GPA_NONE		0xfffffffffffff

/* GHCB GPA Register */
#define GHCB_MSR_REG_GPA_REQ		0x012
#define GHCB_MSR_REG_GPA_REQ_VAL(v)			\
+42 −6
Original line number Diff line number Diff line
@@ -3540,6 +3540,32 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
		set_ghcb_msr_bits(svm, GHCB_MSR_HV_FT_RESP,
				  GHCB_MSR_INFO_MASK, GHCB_MSR_INFO_POS);
		break;
	case GHCB_MSR_PREF_GPA_REQ:
		if (!sev_snp_guest(vcpu->kvm))
			goto out_terminate;

		set_ghcb_msr_bits(svm, GHCB_MSR_PREF_GPA_NONE, GHCB_MSR_GPA_VALUE_MASK,
				  GHCB_MSR_GPA_VALUE_POS);
		set_ghcb_msr_bits(svm, GHCB_MSR_PREF_GPA_RESP, GHCB_MSR_INFO_MASK,
				  GHCB_MSR_INFO_POS);
		break;
	case GHCB_MSR_REG_GPA_REQ: {
		u64 gfn;

		if (!sev_snp_guest(vcpu->kvm))
			goto out_terminate;

		gfn = get_ghcb_msr_bits(svm, GHCB_MSR_GPA_VALUE_MASK,
					GHCB_MSR_GPA_VALUE_POS);

		svm->sev_es.ghcb_registered_gpa = gfn_to_gpa(gfn);

		set_ghcb_msr_bits(svm, gfn, GHCB_MSR_GPA_VALUE_MASK,
				  GHCB_MSR_GPA_VALUE_POS);
		set_ghcb_msr_bits(svm, GHCB_MSR_REG_GPA_RESP, GHCB_MSR_INFO_MASK,
				  GHCB_MSR_INFO_POS);
		break;
	}
	case GHCB_MSR_TERM_REQ: {
		u64 reason_set, reason_code;

@@ -3552,12 +3578,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
		pr_info("SEV-ES guest requested termination: %#llx:%#llx\n",
			reason_set, reason_code);

		vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
		vcpu->run->system_event.type = KVM_SYSTEM_EVENT_SEV_TERM;
		vcpu->run->system_event.ndata = 1;
		vcpu->run->system_event.data[0] = control->ghcb_gpa;

		return 0;
		goto out_terminate;
	}
	default:
		/* Error, keep GHCB MSR value as-is */
@@ -3568,6 +3589,14 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
					    control->ghcb_gpa, ret);

	return ret;

out_terminate:
	vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
	vcpu->run->system_event.type = KVM_SYSTEM_EVENT_SEV_TERM;
	vcpu->run->system_event.ndata = 1;
	vcpu->run->system_event.data[0] = control->ghcb_gpa;

	return 0;
}

int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
@@ -3603,6 +3632,13 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
	trace_kvm_vmgexit_enter(vcpu->vcpu_id, svm->sev_es.ghcb);

	sev_es_sync_from_ghcb(svm);

	/* SEV-SNP guest requires that the GHCB GPA must be registered */
	if (sev_snp_guest(svm->vcpu.kvm) && !ghcb_gpa_is_registered(svm, ghcb_gpa)) {
		vcpu_unimpl(&svm->vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", ghcb_gpa);
		return -EINVAL;
	}

	ret = sev_es_validate_vmgexit(svm);
	if (ret)
		return ret;
+7 −0
Original line number Diff line number Diff line
@@ -209,6 +209,8 @@ struct vcpu_sev_es_state {
	u32 ghcb_sa_len;
	bool ghcb_sa_sync;
	bool ghcb_sa_free;

	u64 ghcb_registered_gpa;
};

struct vcpu_svm {
@@ -362,6 +364,11 @@ static __always_inline bool sev_snp_guest(struct kvm *kvm)
#endif
}

static inline bool ghcb_gpa_is_registered(struct vcpu_svm *svm, u64 val)
{
	return svm->sev_es.ghcb_registered_gpa == val;
}

static inline void vmcb_mark_all_dirty(struct vmcb *vmcb)
{
	vmcb->control.clean = 0;