Skip to content

Commit

Permalink
KVM: SEV: Allow per-guest configuration of GHCB protocol version
Browse files Browse the repository at this point in the history
The GHCB protocol version may be different from one guest to the next.
Add a field to track it for each KVM instance and extend KVM_SEV_INIT2
to allow it to be configured by userspace.

Now that all SEV-ES support for GHCB protocol version 2 is in place, go
ahead and default to it when creating SEV-ES guests through the new
KVM_SEV_INIT2 interface. Keep the older KVM_SEV_ES_INIT interface
restricted to GHCB protocol version 1.

Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
Message-ID: <20240501071048.2208265-5-michael.roth@amd.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Michael Roth authored and Paolo Bonzini committed May 7, 2024
1 parent 8d1a36e commit 4af663c
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 6 deletions.
11 changes: 9 additions & 2 deletions Documentation/virt/kvm/x86/amd-memory-encryption.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,19 @@ Returns: 0 on success, -negative on error
struct kvm_sev_init {
__u64 vmsa_features; /* initial value of features field in VMSA */
__u32 flags; /* must be 0 */
__u32 pad[9];
__u16 ghcb_version; /* maximum guest GHCB version allowed */
__u16 pad1;
__u32 pad2[8];
};

It is an error if the hypervisor does not support any of the bits that
are set in ``flags`` or ``vmsa_features``. ``vmsa_features`` must be
0 for SEV virtual machines, as they do not have a VMSA.

``ghcb_version`` must be 0 for SEV virtual machines, as they do not issue GHCB
requests. If ``ghcb_version`` is 0 for any other guest type, then the maximum
allowed guest GHCB protocol will default to version 2.

This command replaces the deprecated KVM_SEV_INIT and KVM_SEV_ES_INIT commands.
The commands did not have any parameters (the ```data``` field was unused) and
only work for the KVM_X86_DEFAULT_VM machine type (0).
Expand All @@ -112,7 +118,8 @@ They behave as if:
KVM_SEV_ES_INIT

* the ``flags`` and ``vmsa_features`` fields of ``struct kvm_sev_init`` are
set to zero
set to zero, and ``ghcb_version`` is set to 0 for KVM_SEV_INIT and 1 for
KVM_SEV_ES_INIT.

If the ``KVM_X86_SEV_VMSA_FEATURES`` attribute does not exist, the hypervisor only
supports KVM_SEV_INIT and KVM_SEV_ES_INIT. In that case, note that KVM_SEV_ES_INIT
Expand Down
4 changes: 3 additions & 1 deletion arch/x86/include/uapi/asm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,9 @@ struct kvm_sev_cmd {
struct kvm_sev_init {
__u64 vmsa_features;
__u32 flags;
__u32 pad[9];
__u16 ghcb_version;
__u16 pad1;
__u32 pad2[8];
};

struct kvm_sev_launch_start {
Expand Down
32 changes: 29 additions & 3 deletions arch/x86/kvm/svm/sev.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
#include "cpuid.h"
#include "trace.h"

#define GHCB_VERSION_MAX 1ULL
#define GHCB_VERSION_MAX 2ULL
#define GHCB_VERSION_DEFAULT 2ULL
#define GHCB_VERSION_MIN 1ULL

#define GHCB_HV_FT_SUPPORTED GHCB_HV_FT_SNP
Expand Down Expand Up @@ -268,12 +269,24 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
if (data->vmsa_features & ~valid_vmsa_features)
return -EINVAL;

if (data->ghcb_version > GHCB_VERSION_MAX || (!es_active && data->ghcb_version))
return -EINVAL;

if (unlikely(sev->active))
return -EINVAL;

sev->active = true;
sev->es_active = es_active;
sev->vmsa_features = data->vmsa_features;
sev->ghcb_version = data->ghcb_version;

/*
* Currently KVM supports the full range of mandatory features defined
* by version 2 of the GHCB protocol, so default to that for SEV-ES
* guests created via KVM_SEV_INIT2.
*/
if (sev->es_active && !sev->ghcb_version)
sev->ghcb_version = GHCB_VERSION_DEFAULT;

ret = sev_asid_new(sev);
if (ret)
Expand Down Expand Up @@ -307,13 +320,22 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
{
struct kvm_sev_init data = {
.vmsa_features = 0,
.ghcb_version = 0,
};
unsigned long vm_type;

if (kvm->arch.vm_type != KVM_X86_DEFAULT_VM)
return -EINVAL;

vm_type = (argp->id == KVM_SEV_INIT ? KVM_X86_SEV_VM : KVM_X86_SEV_ES_VM);

/*
* KVM_SEV_ES_INIT has been deprecated by KVM_SEV_INIT2, so it will
* continue to only ever support the minimal GHCB protocol version.
*/
if (vm_type == KVM_X86_SEV_ES_VM)
data.ghcb_version = GHCB_VERSION_MIN;

return __sev_guest_init(kvm, argp, &data, vm_type);
}

Expand Down Expand Up @@ -2897,6 +2919,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
{
struct vmcb_control_area *control = &svm->vmcb->control;
struct kvm_vcpu *vcpu = &svm->vcpu;
struct kvm_sev_info *sev = &to_kvm_svm(vcpu->kvm)->sev_info;
u64 ghcb_info;
int ret = 1;

Expand All @@ -2907,7 +2930,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)

switch (ghcb_info) {
case GHCB_MSR_SEV_INFO_REQ:
set_ghcb_msr(svm, GHCB_MSR_SEV_INFO(GHCB_VERSION_MAX,
set_ghcb_msr(svm, GHCB_MSR_SEV_INFO((__u64)sev->ghcb_version,
GHCB_VERSION_MIN,
sev_enc_bit));
break;
Expand Down Expand Up @@ -3268,11 +3291,14 @@ void sev_init_vmcb(struct vcpu_svm *svm)

void sev_es_vcpu_reset(struct vcpu_svm *svm)
{
struct kvm_vcpu *vcpu = &svm->vcpu;
struct kvm_sev_info *sev = &to_kvm_svm(vcpu->kvm)->sev_info;

/*
* Set the GHCB MSR value as per the GHCB specification when emulating
* vCPU RESET for an SEV-ES guest.
*/
set_ghcb_msr(svm, GHCB_MSR_SEV_INFO(GHCB_VERSION_MAX,
set_ghcb_msr(svm, GHCB_MSR_SEV_INFO((__u64)sev->ghcb_version,
GHCB_VERSION_MIN,
sev_enc_bit));
}
Expand Down
1 change: 1 addition & 0 deletions arch/x86/kvm/svm/svm.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ struct kvm_sev_info {
struct list_head regions_list; /* List of registered regions */
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
u64 vmsa_features;
u16 ghcb_version; /* Highest guest GHCB protocol version allowed */
struct kvm *enc_context_owner; /* Owner of copied encryption context */
struct list_head mirror_vms; /* List of VMs mirroring */
struct list_head mirror_entry; /* Use as a list entry of mirrors */
Expand Down

0 comments on commit 4af663c

Please sign in to comment.