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

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
...@@ -59,6 +59,14 @@ ...@@ -59,6 +59,14 @@
#define GHCB_MSR_AP_RESET_HOLD_RESULT_POS 12 #define GHCB_MSR_AP_RESET_HOLD_RESULT_POS 12
#define GHCB_MSR_AP_RESET_HOLD_RESULT_MASK GENMASK_ULL(51, 0) #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 */ /* GHCB GPA Register */
#define GHCB_MSR_REG_GPA_REQ 0x012 #define GHCB_MSR_REG_GPA_REQ 0x012
#define GHCB_MSR_REG_GPA_REQ_VAL(v) \ #define GHCB_MSR_REG_GPA_REQ_VAL(v) \
......
...@@ -3540,6 +3540,32 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) ...@@ -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, set_ghcb_msr_bits(svm, GHCB_MSR_HV_FT_RESP,
GHCB_MSR_INFO_MASK, GHCB_MSR_INFO_POS); GHCB_MSR_INFO_MASK, GHCB_MSR_INFO_POS);
break; 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: { case GHCB_MSR_TERM_REQ: {
u64 reason_set, reason_code; u64 reason_set, reason_code;
...@@ -3552,12 +3578,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) ...@@ -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", pr_info("SEV-ES guest requested termination: %#llx:%#llx\n",
reason_set, reason_code); reason_set, reason_code);
vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT; goto out_terminate;
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;
} }
default: default:
/* Error, keep GHCB MSR value as-is */ /* Error, keep GHCB MSR value as-is */
...@@ -3568,6 +3589,14 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) ...@@ -3568,6 +3589,14 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
control->ghcb_gpa, ret); control->ghcb_gpa, ret);
return 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) int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
...@@ -3603,6 +3632,13 @@ 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); trace_kvm_vmgexit_enter(vcpu->vcpu_id, svm->sev_es.ghcb);
sev_es_sync_from_ghcb(svm); 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); ret = sev_es_validate_vmgexit(svm);
if (ret) if (ret)
return ret; return ret;
......
...@@ -209,6 +209,8 @@ struct vcpu_sev_es_state { ...@@ -209,6 +209,8 @@ struct vcpu_sev_es_state {
u32 ghcb_sa_len; u32 ghcb_sa_len;
bool ghcb_sa_sync; bool ghcb_sa_sync;
bool ghcb_sa_free; bool ghcb_sa_free;
u64 ghcb_registered_gpa;
}; };
struct vcpu_svm { struct vcpu_svm {
...@@ -362,6 +364,11 @@ static __always_inline bool sev_snp_guest(struct kvm *kvm) ...@@ -362,6 +364,11 @@ static __always_inline bool sev_snp_guest(struct kvm *kvm)
#endif #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) static inline void vmcb_mark_all_dirty(struct vmcb *vmcb)
{ {
vmcb->control.clean = 0; vmcb->control.clean = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment