Skip to content

Commit

Permalink
Merge branch 'kvm-sev-init2' into HEAD
Browse files Browse the repository at this point in the history
The idea that no parameter would ever be necessary when enabling SEV or
SEV-ES for a VM was decidedly optimistic.  The first source of variability
that was encountered is the desired set of VMSA features, as that affects
the measurement of the VM's initial state and cannot be changed
arbitrarily by the hypervisor.

This series adds all the APIs that are needed to customize the features,
with room for future enhancements:

- a new /dev/kvm device attribute to retrieve the set of supported
  features (right now, only debug swap)

- a new sub-operation for KVM_MEM_ENCRYPT_OP that can take a struct,
  replacing the existing KVM_SEV_INIT and KVM_SEV_ES_INIT

It then puts the new op to work by including the VMSA features as a field
of the The existing KVM_SEV_INIT and KVM_SEV_ES_INIT use the full set of
supported VMSA features for backwards compatibility; but I am considering
also making them use zero as the feature mask, and will gladly adjust the
patches if so requested.

In order to avoid creating *two* new KVM_MEM_ENCRYPT_OPs, I decided that
I could as well make SEV and SEV-ES use VM types.  This allows SEV-SNP
to reuse the KVM_SEV_INIT2 ioctl.

And while at it, KVM_SEV_INIT2 also includes two bugfixes.  First of all,
SEV-ES VM, when created with the new VM type instead of KVM_SEV_ES_INIT,
reject KVM_GET_REGS/KVM_SET_REGS and friends on the vCPU file descriptor
once the VMSA has been encrypted...  which is how the API should have
always behaved.  Second, they also synchronize the FPU and AVX state.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Paolo Bonzini committed Apr 12, 2024
2 parents 531f520 + 8c53183 commit f9cecb3
Show file tree
Hide file tree
Showing 25 changed files with 703 additions and 185 deletions.
2 changes: 2 additions & 0 deletions Documentation/virt/kvm/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8819,6 +8819,8 @@ means the VM type with value @n is supported. Possible values of @n are::

#define KVM_X86_DEFAULT_VM 0
#define KVM_X86_SW_PROTECTED_VM 1
#define KVM_X86_SEV_VM 2
#define KVM_X86_SEV_ES_VM 3

Note, KVM_X86_SW_PROTECTED_VM is currently only for development and testing.
Do not use KVM_X86_SW_PROTECTED_VM for "real" VMs, and especially not in
Expand Down
52 changes: 49 additions & 3 deletions Documentation/virt/kvm/x86/amd-memory-encryption.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,49 @@ are defined in ``<linux/psp-dev.h>``.
KVM implements the following commands to support common lifecycle events of SEV
guests, such as launching, running, snapshotting, migrating and decommissioning.

1. KVM_SEV_INIT
---------------
1. KVM_SEV_INIT2
----------------

The KVM_SEV_INIT command is used by the hypervisor to initialize the SEV platform
The KVM_SEV_INIT2 command is used by the hypervisor to initialize the SEV platform
context. In a typical workflow, this command should be the first command issued.

For this command to be accepted, either KVM_X86_SEV_VM or KVM_X86_SEV_ES_VM
must have been passed to the KVM_CREATE_VM ioctl. A virtual machine created
with those machine types in turn cannot be run until KVM_SEV_INIT2 is invoked.

Parameters: struct kvm_sev_init (in)

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];
};

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.

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).

They behave as if:

* the VM type is KVM_X86_SEV_VM for KVM_SEV_INIT, or KVM_X86_SEV_ES_VM for
KVM_SEV_ES_INIT

* the ``flags`` and ``vmsa_features`` fields of ``struct kvm_sev_init`` are
set to zero

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
might set the debug swap VMSA feature (bit 5) depending on the value of the
``debug_swap`` parameter of ``kvm-amd.ko``.

2. KVM_SEV_LAUNCH_START
-----------------------

Expand Down Expand Up @@ -425,6 +459,18 @@ issued by the hypervisor to make the guest ready for execution.

Returns: 0 on success, -negative on error

Device attribute API
====================

Attributes of the SEV implementation can be retrieved through the
``KVM_HAS_DEVICE_ATTR`` and ``KVM_GET_DEVICE_ATTR`` ioctls on the ``/dev/kvm``
device node, using group ``KVM_X86_GRP_SEV``.

Currently only one attribute is implemented:

* ``KVM_X86_SEV_VMSA_FEATURES``: return the set of all bits that
are accepted in the ``vmsa_features`` of ``KVM_SEV_INIT2``.

Firmware Management
===================

Expand Down
3 changes: 3 additions & 0 deletions arch/x86/include/asm/fpu/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ extern void fpstate_clear_xstate_component(struct fpstate *fps, unsigned int xfe

extern u64 xstate_get_guest_group_perm(void);

extern void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr);


/* KVM specific functions */
extern bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu);
extern void fpu_free_guest_fpstate(struct fpu_guest *gfpu);
Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/asm/kvm-x86-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ KVM_X86_OP(enter_smm)
KVM_X86_OP(leave_smm)
KVM_X86_OP(enable_smi_window)
#endif
KVM_X86_OP_OPTIONAL(dev_get_attr)
KVM_X86_OP_OPTIONAL(mem_enc_ioctl)
KVM_X86_OP_OPTIONAL(mem_enc_register_region)
KVM_X86_OP_OPTIONAL(mem_enc_unregister_region)
Expand Down
8 changes: 6 additions & 2 deletions arch/x86/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -1279,12 +1279,14 @@ enum kvm_apicv_inhibit {
};

struct kvm_arch {
unsigned long vm_type;
unsigned long n_used_mmu_pages;
unsigned long n_requested_mmu_pages;
unsigned long n_max_mmu_pages;
unsigned int indirect_shadow_pages;
u8 mmu_valid_gen;
u8 vm_type;
bool has_private_mem;
bool has_protected_state;
struct hlist_head mmu_page_hash[KVM_NUM_MMU_PAGES];
struct list_head active_mmu_pages;
struct list_head zapped_obsolete_pages;
Expand Down Expand Up @@ -1778,6 +1780,7 @@ struct kvm_x86_ops {
void (*enable_smi_window)(struct kvm_vcpu *vcpu);
#endif

int (*dev_get_attr)(u32 group, u64 attr, u64 *val);
int (*mem_enc_ioctl)(struct kvm *kvm, void __user *argp);
int (*mem_enc_register_region)(struct kvm *kvm, struct kvm_enc_region *argp);
int (*mem_enc_unregister_region)(struct kvm *kvm, struct kvm_enc_region *argp);
Expand Down Expand Up @@ -2152,8 +2155,9 @@ void kvm_mmu_new_pgd(struct kvm_vcpu *vcpu, gpa_t new_pgd);
void kvm_configure_mmu(bool enable_tdp, int tdp_forced_root_level,
int tdp_max_root_level, int tdp_huge_page_level);


#ifdef CONFIG_KVM_PRIVATE_MEM
#define kvm_arch_has_private_mem(kvm) ((kvm)->arch.vm_type != KVM_X86_DEFAULT_VM)
#define kvm_arch_has_private_mem(kvm) ((kvm)->arch.has_private_mem)
#else
#define kvm_arch_has_private_mem(kvm) false
#endif
Expand Down
20 changes: 18 additions & 2 deletions arch/x86/include/uapi/asm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -457,8 +457,13 @@ struct kvm_sync_regs {

#define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001

/* attributes for system fd (group 0) */
#define KVM_X86_XCOMP_GUEST_SUPP 0
/* vendor-independent attributes for system fd (group 0) */
#define KVM_X86_GRP_SYSTEM 0
# define KVM_X86_XCOMP_GUEST_SUPP 0

/* vendor-specific groups and attributes for system fd */
#define KVM_X86_GRP_SEV 1
# define KVM_X86_SEV_VMSA_FEATURES 0

struct kvm_vmx_nested_state_data {
__u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
Expand Down Expand Up @@ -689,6 +694,9 @@ enum sev_cmd_id {
/* Guest Migration Extension */
KVM_SEV_SEND_CANCEL,

/* Second time is the charm; improved versions of the above ioctls. */
KVM_SEV_INIT2,

KVM_SEV_NR_MAX,
};

Expand All @@ -700,6 +708,12 @@ struct kvm_sev_cmd {
__u32 sev_fd;
};

struct kvm_sev_init {
__u64 vmsa_features;
__u32 flags;
__u32 pad[9];
};

struct kvm_sev_launch_start {
__u32 handle;
__u32 policy;
Expand Down Expand Up @@ -856,5 +870,7 @@ struct kvm_hyperv_eventfd {

#define KVM_X86_DEFAULT_VM 0
#define KVM_X86_SW_PROTECTED_VM 1
#define KVM_X86_SEV_VM 2
#define KVM_X86_SEV_ES_VM 3

#endif /* _ASM_X86_KVM_H */
1 change: 1 addition & 0 deletions arch/x86/kernel/fpu/xstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,7 @@ void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr)

return __raw_xsave_addr(xsave, xfeature_nr);
}
EXPORT_SYMBOL_GPL(get_xsave_addr);

#ifdef CONFIG_ARCH_HAS_PKEYS

Expand Down
2 changes: 0 additions & 2 deletions arch/x86/kernel/fpu/xstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ extern int copy_sigframe_from_user_to_xstate(struct task_struct *tsk, const void
extern void fpu__init_cpu_xstate(void);
extern void fpu__init_system_xstate(unsigned int legacy_size);

extern void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr);

static inline u64 xfeatures_mask_supervisor(void)
{
return fpu_kernel_cfg.max_features & XFEATURE_MASK_SUPERVISOR_SUPPORTED;
Expand Down
7 changes: 4 additions & 3 deletions arch/x86/kvm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ kvm-intel-y += vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o \
kvm-intel-$(CONFIG_X86_SGX_KVM) += vmx/sgx.o
kvm-intel-$(CONFIG_KVM_HYPERV) += vmx/hyperv.o vmx/hyperv_evmcs.o

kvm-amd-y += svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o \
svm/sev.o
kvm-amd-$(CONFIG_KVM_HYPERV) += svm/hyperv.o
kvm-amd-y += svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o

kvm-amd-$(CONFIG_KVM_AMD_SEV) += svm/sev.o
kvm-amd-$(CONFIG_KVM_HYPERV) += svm/hyperv.o

ifdef CONFIG_HYPERV
kvm-y += kvm_onhyperv.o
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kvm/cpuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ void kvm_set_cpu_caps(void)
kvm_cpu_cap_mask(CPUID_8000_000A_EDX, 0);

kvm_cpu_cap_mask(CPUID_8000_001F_EAX,
0 /* SME */ | F(SEV) | 0 /* VM_PAGE_FLUSH */ | F(SEV_ES) |
0 /* SME */ | 0 /* SEV */ | 0 /* VM_PAGE_FLUSH */ | 0 /* SEV_ES */ |
F(SME_COHERENT));

kvm_cpu_cap_mask(CPUID_8000_0021_EAX,
Expand Down
Loading

0 comments on commit f9cecb3

Please sign in to comment.