KVM: SEV-SNP support for running an SVSM
cover KVM: SEV-SNP support for running an SVSM
This series is meant to start the discussion around running a guest with a Secure VM Service Module (SVSM) and how to transition a vCPU between one VM Privilege Level (VMPL) and another. This is Proof-of-Concept level code, so definitely not something looking to be merged.
When running under an SVSM, VMPL switches are needed for validating memory and creating vCPU VM Save Area (VMSA) pages. Going forward, different services running in the SVSM will require VMPL switching, e.g. a virtual TPM service or Alternate Injection support. Therefore VMPL switches need to be as fast as possible. The implementation in this series has KVM managing the creation of VMPL levels and transitioning between the levels without transitioning to the userspace VMM.
Going forward, the userspace VMM may need to be aware of VMPL levels. It may be necessary to transition VMPL creation (AP Creation at a specific VMPL level) to the userspace VMM. But keeping VMPL switching within KVM is highly desired for performance reasons.
This PoC code does have some restrictions. For example, when running with Restricted Injection, all injections are blocked as the SVSM is not expecting any injections (currently). This allows for a single APIC instance for now.
The patches can be further split and the change logs improved, but wanted to get this out and get the discussion going.
Implemented in this RFC:
- APIC ID list retrieval to allow for only measuring the BSP and allowing the guest to start all of the APs without having to use a broadcast SIPI
- vCPU creation at a specific VMPL
- vCPU execution at a specific VMPL
- Maintain per-VMPL SEV features
- Implement minimal Restricted Injection
- Blocks all injection when enabled
- SVSM support
- SNP init flag for SVSM support
- Measuring data with specific VMPL permissions
- Measuring only the BSP
Things not yet implemented:
- APIC instance separation
- Restricted Injection support that is multi-VMPL aware
The series is based off of a slightly older kvm next branch:
git://git.kernel.org/pub/scm/virt/kvm/kvm.git next
7c626ce4bae1 ("Linux 6.11-rc3")
---
Carlos Bilbao (1):
KVM: SVM: Maintain per-VMPL SEV features in kvm_sev_info
Tom Lendacky (6):
KVM: SVM: Implement GET_AP_APIC_IDS NAE event
KVM: SEV: Allow for VMPL level specification in AP create
KVM: SVM: Invoke a specified VMPL level VMSA for the vCPU
KVM: SVM: Prevent injection when restricted injection is active
KVM: SVM: Support launching an SVSM with Restricted Injection set
KVM: SVM: Support initialization of an SVSM
arch/x86/include/asm/sev-common.h | 7 + arch/x86/include/asm/svm.h | 9 + arch/x86/include/uapi/asm/kvm.h | 10 + arch/x86/include/uapi/asm/svm.h | 3 + arch/x86/kvm/svm/sev.c | 530 +++++++++++++++++++++++++----- arch/x86/kvm/svm/svm.c | 25 +- arch/x86/kvm/svm/svm.h | 71 +++- arch/x86/kvm/x86.c | 9 + include/uapi/linux/kvm.h | 3 + 9 files changed, 575 insertions(+), 92 deletions(-)
Signature · 1 line
2.43.2
RFC PATCH 1/7 KVM: SVM: Implement GET_AP_APIC_IDS NAE event
Implement the GET_APIC_IDS NAE event to gather and return the list of APIC IDs for all vCPUs in the guest.
--- arch/x86/include/asm/sev-common.h | 1 + arch/x86/include/uapi/asm/svm.h | 1 + arch/x86/kvm/svm/sev.c | 84 ++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 1 deletion(-)
arch/x86/include/asm/sev-common.h +1 -0
@@ -136,6 +136,7 @@ enum psc_op {#define GHCB_HV_FT_SNP BIT_ULL(0)#define GHCB_HV_FT_SNP_AP_CREATION BIT_ULL(1) +#define GHCB_HV_FT_APIC_ID_LIST BIT_ULL(4)#define GHCB_HV_FT_SNP_MULTI_VMPL BIT_ULL(5)/*
arch/x86/include/uapi/asm/svm.h +1 -0
@@ -115,6 +115,7 @@#define SVM_VMGEXIT_AP_CREATE_ON_INIT 0#define SVM_VMGEXIT_AP_CREATE 1#define SVM_VMGEXIT_AP_DESTROY 2 +#define SVM_VMGEXIT_GET_APIC_IDS 0x80000017#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
arch/x86/kvm/svm/sev.c +83 -1
108 lines not on this page · raw message
Signature · 1 line
2.43.2
Borislav Petkov
maintainerRe: [RFC PATCH 1/7] KVM: SVM: Implement GET_AP_APIC_IDS NAE event
Review static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r · line 4134
Quoted 6 lines · Tom Lendacky
(quoted diff) +2
-0@@ -4124,6 +4130,77 @@ static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r return 1; /* resume guest */ } +struct sev_apic_id_desc { + u32 num_entries;
"count" - like in the spec. :-P
Quoted 7 lines
(quoted diff) +7
-0+ u32 apic_ids[]; +}; + +static void sev_get_apic_ids(struct vcpu_svm *svm) +{ + struct ghcb *ghcb = svm->sev_es.ghcb; + struct kvm_vcpu *vcpu = &svm->vcpu, *loop_vcpu;
Review static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r · line 4151
Quoted 10 lines
(quoted diff) +10
-0+ struct kvm *kvm = vcpu->kvm; + unsigned int id_desc_size; + struct sev_apic_id_desc *desc; + kvm_pfn_t pfn; + gpa_t gpa; + u64 pages; + unsigned long i; + int n; + + pages = vcpu->arch.regs[VCPU_REGS_RAX];
Probably should be "num_pages" and a comment should explain what it is:
"State to Hypervisor: is the
number of guest contiguous pages
provided to hold the list of APIC
IDs"
Makes it much easier to follow the code.
Review static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r · line 4156
Quoted 5 lines
(quoted diff) +5
-0+ /* Each APIC ID is 32-bits in size, so make sure there is room */ + n = atomic_read(&kvm->online_vcpus); + /*TODO: is this possible? */ + if (n < 0) + return;
It doesn't look like it but if you wanna be real paranoid you can slap a WARN_ONCE() here or so to scream loudly.
Review static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r · line 4167
Quoted 11 lines
(quoted diff) +11
-0+ id_desc_size = sizeof(*desc); + id_desc_size += n * sizeof(desc->apic_ids[0]); + if (id_desc_size > (pages * PAGE_SIZE)) { + vcpu->arch.regs[VCPU_REGS_RAX] = PFN_UP(id_desc_size); + return; + } + + gpa = svm->vmcb->control.exit_info_1; + + ghcb_set_sw_exit_info_1(ghcb, 2); + ghcb_set_sw_exit_info_2(ghcb, 5);
Uuh, more magic numbers. I guess we need this:
https://lore.kernel.org/r/[email protected]
and more.
And can we write those only once at the end of the function?
Review static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r · line 4171
Quoted 4 lines
(quoted diff) +4
-0+ if (!page_address_valid(vcpu, gpa)) + return; + + pfn = gfn_to_pfn(kvm, gpa_to_gfn(gpa));
Looking at the tree, that gfn_to_pfn() thing is gone now and we're supposed to it this way. Not tested ofc:
arch/x86/kvm/svm/sev.c +3 -3
@@ -4134,7 +4134,7 @@ static void sev_get_apic_ids(struct vcpu_svm *svm)struct kvm *kvm = vcpu->kvm;unsigned int id_desc_size;struct sev_apic_id_desc *desc; -kvm_pfn_t pfn; +struct page *page;gpa_t gpa;u64 pages;unsigned long i;
@@ -4163,8 +4163,8 @@ static void sev_get_apic_ids(struct vcpu_svm *svm)if (!page_address_valid (vcpu, gpa))return ; - pfn =gfn_to_pfn (kvm,gpa_to_gfn (gpa)); -if (is_error_noslot_pfn (pfn)) + page =gfn_to_page (kvm,gpa_to_gfn (gpa)); +if (!page)return ;if (!pages)
Review static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r · line 4176
Quoted 5 lines
(quoted diff) +5
-0+ if (is_error_noslot_pfn(pfn)) + return; + + if (!pages) + return;
That test needs to go right under the assignment of "pages".
Review static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r · line 4184
Quoted 8 lines
(quoted diff) +8
-0+ /* Allocate a buffer to hold the APIC IDs */ + desc = kvzalloc(id_desc_size, GFP_KERNEL_ACCOUNT); + if (!desc) + return; + + desc->num_entries = n; + kvm_for_each_vcpu(i, loop_vcpu, kvm) { + /*TODO: is this possible? */
Well:
#define kvm_for_each_vcpu(idx, vcpup, kvm) \
xa_for_each_range(&kvm->vcpu_array, idx, vcpup, 0, \
(atomic_read(&kvm->online_vcpus) - 1))
^^^^^^^^^^^^^^but, what's stopping kvm_vm_ioctl_create_vcpu() from incrementing it?
I'm guessing this would happen when you start the guest only but I haz no idea.
Quoted 14 lines
(quoted diff) +14
-0+ if (i > n) + break; + + desc->apic_ids[i] = loop_vcpu->vcpu_id; + } + + if (!kvm_write_guest(kvm, gpa, desc, id_desc_size)) { + /* IDs were successfully written */ + ghcb_set_sw_exit_info_1(ghcb, 0); + ghcb_set_sw_exit_info_2(ghcb, 0); + } + + kvfree(desc); +}
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteRFC PATCH 2/7 KVM: SEV: Allow for VMPL level specification in AP create
In reply to Tom LendackyUpdate AP creation to support ADD/DESTROY of VMSAs at levels other than VMPL0 in order to run under an SVSM at VMPL1 or lower. To maintain backwards compatibility, the VMPL is specified in bits 16 to 19 of the AP Creation request in SW_EXITINFO1 of the GHCB.
In order to track the VMSAs at different levels, create arrays for the VMSAs, GHCBs, registered GHCBs and others. When switching VMPL levels, these entries will be used to set the VMSA and GHCB physical addresses in the VMCB for the VMPL level.
In order ensure that the proper responses are returned in the proper GHCB, the GHCB must be unmapped at the current level and saved for restoration later when switching back to that VMPL level.
Additional checks are applied to prevent a non-VMPL0 vCPU from being able to perform an AP creation request at VMPL0. Additionally, a vCPU cannot replace its own VMSA.
--- arch/x86/include/asm/svm.h | 9 ++ arch/x86/include/uapi/asm/svm.h | 2 + arch/x86/kvm/svm/sev.c | 146 +++++++++++++++++++++++--------- arch/x86/kvm/svm/svm.c | 6 +- arch/x86/kvm/svm/svm.h | 45 ++++++++-- arch/x86/kvm/x86.c | 9 ++ 6 files changed, 169 insertions(+), 48 deletions(-)
arch/x86/include/asm/svm.h +9 -0
@@ -294,6 +294,15 @@ static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_ (SVM_SEV_FEAT_RESTRICTED_INJECTION | \ SVM_SEV_FEAT_ALTERNATE_INJECTION) +enum { +SVM_SEV_VMPL0 =0 , +SVM_SEV_VMPL1 , +SVM_SEV_VMPL2 , +SVM_SEV_VMPL3 , + +SVM_SEV_VMPL_MAX +}; +struct vmcb_seg {u16 selector;u16 attrib;
arch/x86/include/uapi/asm/svm.h +2 -0
@@ -115,6 +115,8 @@#define SVM_VMGEXIT_AP_CREATE_ON_INIT 0#define SVM_VMGEXIT_AP_CREATE 1#define SVM_VMGEXIT_AP_DESTROY 2 +#define SVM_VMGEXIT_AP_VMPL_MASK GENMASK(19, 16) +#define SVM_VMGEXIT_AP_VMPL_SHIFT 16#define SVM_VMGEXIT_GET_APIC_IDS 0x80000017#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
arch/x86/kvm/svm/sev.c +107 -39
296 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +4 -2
@@ -1463,8 +1463,10 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu) svm->vmcb01.pa =__sme_set (page_to_pfn (vmcb01_page) <<PAGE_SHIFT );svm_switch_vmcb (svm, &svm->vmcb01); -if (vmsa_page) - svm->sev_es.vmsa =page_address (vmsa_page); +if (vmsa_page) { +vmpl_vmsa (svm,SVM_SEV_VMPL0 ) =page_address (vmsa_page); +vmpl_vmsa_hpa (svm,SVM_SEV_VMPL0 ) =__pa (page_address (vmsa_page)); + } svm->guest_state_loaded = false;
arch/x86/kvm/svm/svm.h +38 -7
66 lines not on this page · raw message
arch/x86/kvm/x86.c +9 -0
@@ -11469,6 +11469,15 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)kvm_vcpu_block (vcpu);kvm_vcpu_srcu_read_lock (vcpu); +/* +* It is possible that the vCPU has never run before. If the +* request is to update the protected guest state (AP Create), +* then ensure that the vCPU can now run. +*/ +if (kvm_test_request (KVM_REQ_UPDATE_PROTECTED_GUEST_STATE , vcpu) && + vcpu->arch.mp_state ==KVM_MP_STATE_UNINITIALIZED ) + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +if (kvm_apic_accept_events (vcpu) <0 ) { r =0 ; goto out;
Signature · 1 line
2.43.2
RFC PATCH 3/7 KVM: SVM: Invoke a specified VMPL level VMSA for the vCPU
In reply to Tom LendackyImplement the SNP Run VMPL NAE event and MSR protocol to allow a guest to request a different VMPL level VMSA be run for the vCPU. This allows the guest to "call" an SVSM to process an SVSM request.
--- arch/x86/include/asm/sev-common.h | 6 ++ arch/x86/kvm/svm/sev.c | 126 +++++++++++++++++++++++++++++- arch/x86/kvm/svm/svm.c | 13 +++ arch/x86/kvm/svm/svm.h | 18 ++++- 4 files changed, 158 insertions(+), 5 deletions(-)
arch/x86/include/asm/sev-common.h +6 -0
@@ -114,6 +114,8 @@ enum psc_op {/* GHCB Run at VMPL Request/Response */ #define GHCB_MSR_VMPL_REQ 0x016 +#define GHCB_MSR_VMPL_LEVEL_POS 32 +#define GHCB_MSR_VMPL_LEVEL_MASK GENMASK_ULL(7, 0)#define GHCB_MSR_VMPL_REQ_LEVEL (v) \/* GHCBData[39:32] */ \ (((u64)(v) & GENMASK_ULL(7, 0) << 32) | \
@@ -121,6 +123,10 @@ enum psc_op { GHCB_MSR_VMPL_REQ)#define GHCB_MSR_VMPL_RESP 0x017 +#define GHCB_MSR_VMPL_ERROR_POS 32 +#define GHCB_MSR_VMPL_ERROR_MASK GENMASK_ULL(31, 0) +#define GHCB_MSR_VMPL_RSVD_POS 12 +#define GHCB_MSR_VMPL_RSVD_MASK GENMASK_ULL(19, 0)#define GHCB_MSR_VMPL_RESP_VAL (v) \/* GHCBData[63:32] */ \ (((u64)(v) & GENMASK_ULL(63, 32)) >> 32)
arch/x86/kvm/svm/sev.c +123 -3
174 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +13 -0
@@ -4253,6 +4253,19 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, } vcpu->arch.regs_dirty =0 ; +if (sev_snp_is_rinj_active (vcpu)) { +/* +* When SEV-SNP is running with restricted injection, the V_IRQ +* bit may be cleared on exit because virtual interrupt support +* is ignored. To support multiple VMPLs, some of which may not +* be running with restricted injection, ensure to reset the +* V_IRQ bit if a virtual interrupt is meant to be active (the +* virtual interrupt priority mask is non-zero). +*/ +if (svm->vmcb->control.int_ctl &V_INTR_PRIO_MASK ) + svm->vmcb->control.int_ctl |=V_IRQ_MASK ; + } +if (unlikely (svm->vmcb->control.exit_code ==SVM_EXIT_NMI ))kvm_before_interrupt (vcpu,KVM_HANDLING_NMI );
arch/x86/kvm/svm/svm.h +16 -2
@@ -198,6 +198,18 @@ struct svm_nested_state {bool force_msr_bitmap_recalc; }; +struct vmpl_switch_sa { +u32 int_state; +u32 exit_int_info; +u32 exit_int_info_err; + +unsigned long cr0; +unsigned long cr2; +unsigned long cr4; +unsigned long cr8; +u64 efer; +}; +#define vmpl_vmsa (s, v) ((s)->sev_es.vmsa_info[(v)].vmsa)#define vmpl_vmsa_gpa (s, v) ((s)->sev_es.vmsa_info[(v)].gpa)#define vmpl_vmsa_hpa (s, v) ((s)->sev_es.vmsa_info[(v)].hpa)
@@ -256,6 +268,8 @@ struct vcpu_sev_es_state {struct mutex snp_vmsa_mutex;/* Used to handle concurrent updates of VMSA. */ unsigned int snp_current_vmpl;unsigned int snp_target_vmpl; + +struct vmpl_switch_sa vssa[SVM_SEV_VMPL_MAX ]; };struct vcpu_svm {
@@ -776,7 +790,7 @@ int sev_cpu_init(struct svm_cpu_data *sd);int sev_dev_get_attr (u32 group,u64 attr,u64 *val);extern unsigned int max_sev_asid;void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code); -void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu); +bool sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu);int sev_gmem_prepare (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,int max_order);void sev_gmem_invalidate (kvm_pfn_t start,kvm_pfn_t end);int sev_private_max_mapping_level (struct kvm *kvm,kvm_pfn_t pfn);
@@ -800,7 +814,7 @@ static inline int sev_cpu_init(struct svm_cpu_data *sd) { return 0; }static inline int sev_dev_get_attr (u32 group,u64 attr,u64 *val) {return -ENXIO ; }#define max_sev_asid 0static inline void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code) {} -static inline void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu) {} +static inline bool sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu) {return false; }static inline int sev_gmem_prepare (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,int max_order) {return 0 ;
Signature · 1 line
2.43.2
RFC PATCH 4/7 KVM: SVM: Maintain per-VMPL SEV features in kvm_sev_info
In reply to Tom LendackyFrom: Carlos Bilbao <[email protected]>
Make struct kvm_sev_info maintain separate SEV features per VMPL, allowing distinct SEV features depending on VMs privilege level.
--- arch/x86/kvm/svm/sev.c | 22 +++++++++++++++------- arch/x86/kvm/svm/svm.h | 4 ++-- 2 files changed, 17 insertions(+), 9 deletions(-)
arch/x86/kvm/svm/sev.c +15 -7
@@ -144,7 +144,7 @@ static bool sev_vcpu_has_debug_swap(struct vcpu_svm *svm)struct kvm_vcpu *vcpu = &svm->vcpu;struct kvm_sev_info *sev = &to_kvm_svm (vcpu->kvm)->sev_info; -return sev->vmsa_features &SVM_SEV_FEAT_DEBUG_SWAP ; +return sev->vmsa_features[cur_vmpl (svm)] &SVM_SEV_FEAT_DEBUG_SWAP ; }/* Must be called with the sev_bitmap_lock held */
@@ -428,7 +428,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, sev->active = true; sev->es_active = es_active; - sev->vmsa_features = data->vmsa_features; + sev->vmsa_features[SVM_SEV_VMPL0 ] = data->vmsa_features; sev->ghcb_version = data->ghcb_version;/*
@@ -440,7 +440,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, sev->ghcb_version =GHCB_VERSION_DEFAULT ;if (vm_type ==KVM_X86_SNP_VM ) - sev->vmsa_features |=SVM_SEV_FEAT_SNP_ACTIVE ; + sev->vmsa_features[SVM_SEV_VMPL0 ] |=SVM_SEV_FEAT_SNP_ACTIVE ; ret =sev_asid_new (sev);if (ret)
@@ -468,7 +468,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,sev_asid_free (sev); sev->asid =0 ; e_no_asid: - sev->vmsa_features =0 ; + sev->vmsa_features[SVM_SEV_VMPL0 ] =0 ; sev->es_active = false; sev->active = false;return ret;
@@ -852,7 +852,7 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm) save->xss = svm->vcpu.arch.ia32_xss; save->dr6 = svm->vcpu.arch.dr6; - save->sev_features = sev->vmsa_features; + save->sev_features = sev->vmsa_features[SVM_SEV_VMPL0 ];/* * Skip FPU and AVX setup with KVM_SEV_ES_INIT to avoid
@@ -1985,7 +1985,7 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) dst->pages_locked = src->pages_locked; dst->enc_context_owner = src->enc_context_owner; dst->es_active = src->es_active; - dst->vmsa_features = src->vmsa_features; +memcpy (dst->vmsa_features, src->vmsa_features,sizeof (dst->vmsa_features)); src->asid =0 ; src->active = false;
@@ -4034,8 +4034,16 @@ static int sev_snp_ap_creation(struct vcpu_svm *svm)/* Interrupt injection mode shouldn't change for AP creation */ sev_features = vcpu->arch.regs[VCPU_REGS_RAX ]; - sev_features ^= sev->vmsa_features; +/* +* The SNPActive feature must at least be set. If the SEV +* features of this AP are zero, this is the first vCPU created at +* this VMPL. +*/ +if (!sev->vmsa_features[vmpl]) + sev->vmsa_features[vmpl] = sev_features |SVM_SEV_FEAT_SNP_ACTIVE ; + + sev_features ^= sev->vmsa_features[vmpl];if (sev_features &SVM_SEV_FEAT_INT_INJ_MODES ) {vcpu_unimpl (vcpu,"vmgexit: invalid AP injection mode [%#lx] from guest\n" , vcpu->arch.regs[VCPU_REGS_RAX ]);
arch/x86/kvm/svm/svm.h +2 -2
@@ -87,7 +87,7 @@ struct kvm_sev_info {unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ u64 ap_jump_table;/* SEV-ES AP Jump Table address */ -u64 vmsa_features; +u64 vmsa_features[SVM_SEV_VMPL_MAX ];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 */
@@ -416,7 +416,7 @@ static __always_inline bool sev_snp_guest(struct kvm *kvm)#ifdef CONFIG_KVM_AMD_SEV struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; -return (sev->vmsa_features &SVM_SEV_FEAT_SNP_ACTIVE ) && +return (sev->vmsa_features[SVM_SEV_VMPL0 ] &SVM_SEV_FEAT_SNP_ACTIVE ) && !WARN_ON_ONCE (!sev_es_guest (kvm));#else return false;
Signature · 1 line
2.43.2
RFC PATCH 5/7 KVM: SVM: Prevent injection when restricted injection is active
In reply to Tom LendackyPrevent injection of exceptions/interrupts when restricted injection is active. This is not full support for restricted injection, but the SVSM is not expecting any injections at all.
--- arch/x86/kvm/svm/sev.c | 30 ++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 6 ++++++ arch/x86/kvm/svm/svm.h | 3 +++ 3 files changed, 39 insertions(+)
arch/x86/kvm/svm/sev.c +30 -0
@@ -5227,3 +5227,33 @@ int sev_private_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn)return level; } + +bool sev_snp_is_rinj_active (struct kvm_vcpu *vcpu) +{ +struct kvm_sev_info *sev; +int vmpl; + +if (!sev_snp_guest (vcpu->kvm)) +return false; + + sev = &to_kvm_svm (vcpu->kvm)->sev_info; + vmpl =to_svm (vcpu)->sev_es.snp_current_vmpl; + +return sev->vmsa_features[vmpl] &SVM_SEV_FEAT_RESTRICTED_INJECTION ; +} + +bool sev_snp_nmi_blocked (struct kvm_vcpu *vcpu) +{ +WARN_ON_ONCE (!sev_snp_is_rinj_active (vcpu)); + +/* NMIs are blocked when restricted injection is active */ +return true; +} + +bool sev_snp_interrupt_blocked (struct kvm_vcpu *vcpu) +{ +WARN_ON_ONCE (!sev_snp_is_rinj_active (vcpu)); + +/* Interrupts are blocked when restricted injection is active */ +return true; +}
arch/x86/kvm/svm/svm.c +6 -0
@@ -3780,6 +3780,9 @@ bool svm_nmi_blocked(struct kvm_vcpu *vcpu)if (!gif_set (svm))return true; +if (sev_snp_is_rinj_active (vcpu)) +return sev_snp_nmi_blocked (vcpu); +if (is_guest_mode (vcpu) &&nested_exit_on_nmi (svm))return false;
@@ -3812,6 +3815,9 @@ bool svm_interrupt_blocked(struct kvm_vcpu *vcpu)if (!gif_set (svm))return true; +if (sev_snp_is_rinj_active (vcpu)) +return sev_snp_interrupt_blocked (vcpu); +if (is_guest_mode (vcpu)) {/* As long as interrupts are being delivered... */ if ((svm->nested.ctl.int_ctl & V_INTR_MASKING_MASK)
arch/x86/kvm/svm/svm.h +3 -0
@@ -761,6 +761,9 @@ void sev_es_vcpu_reset(struct vcpu_svm *svm);void sev_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector);void sev_es_prepare_switch_to_guest (struct vcpu_svm *svm,struct sev_es_save_area *hostsa);void sev_es_unmap_ghcb (struct vcpu_svm *svm); +bool sev_snp_is_rinj_active (struct kvm_vcpu *vcpu); +bool sev_snp_nmi_blocked (struct kvm_vcpu *vcpu); +bool sev_snp_interrupt_blocked (struct kvm_vcpu *vcpu);#ifdef CONFIG_KVM_AMD_SEV int sev_mem_enc_ioctl (struct kvm *kvm,void __user *argp);
Signature · 1 line
2.43.2
RFC PATCH 6/7 KVM: SVM: Support launching an SVSM with Restricted Injection set
In reply to Tom LendackyAllow Restricted Injection to be set in SEV_FEATURES. When set, attempts to inject any interrupts other than #HV will make VMRUN fail. This is done to further reduce the security exposure within the SVSM.
--- arch/x86/kvm/svm/sev.c | 1 + 1 file changed, 1 insertion(+)
arch/x86/kvm/svm/sev.c +1 -0
@@ -3078,6 +3078,7 @@ void __init sev_hardware_setup(void) sev_es_debug_swap_enabled = false; sev_supported_vmsa_features =0 ; + sev_supported_vmsa_features |=SVM_SEV_FEAT_RESTRICTED_INJECTION ;if (sev_es_debug_swap_enabled) sev_supported_vmsa_features |=SVM_SEV_FEAT_DEBUG_SWAP ; }
Signature · 1 line
2.43.2
RFC PATCH 7/7 KVM: SVM: Support initialization of an SVSM
In reply to Tom LendackyAllow for setting VMPL permission as part of the launch sequence and ssing an SNP init flag, limit measuring of the guest vCPUs, to just the BSP.
Indicate full multi-VMPL support to the guest through the GHCB feature bitmap.
--- arch/x86/include/uapi/asm/kvm.h | 10 +++ arch/x86/kvm/svm/sev.c | 123 ++++++++++++++++++++++++-------- arch/x86/kvm/svm/svm.h | 1 + include/uapi/linux/kvm.h | 3 + 4 files changed, 107 insertions(+), 30 deletions(-)
arch/x86/include/uapi/asm/kvm.h +10 -0
@@ -465,6 +465,7 @@ struct kvm_sync_regs {/* vendor-specific groups and attributes for system fd */ #define KVM_X86_GRP_SEV 1# define KVM_X86_SEV_VMSA_FEATURES 0 +# define KVM_X86_SEV_SNP_INIT_FLAGS 1struct kvm_vmx_nested_state_data {__u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE ];
@@ -703,6 +704,8 @@ enum sev_cmd_id {KVM_SEV_SNP_LAUNCH_UPDATE ,KVM_SEV_SNP_LAUNCH_FINISH , +KVM_SEV_SNP_LAUNCH_UPDATE_VMPLS , + KVM_SEV_NR_MAX, };
@@ -856,6 +859,13 @@ struct kvm_sev_snp_launch_update {__u64 pad2[4 ]; }; +struct kvm_sev_snp_launch_update_vmpls { +struct kvm_sev_snp_launch_update lu; +__u8 vmpl3_perms; +__u8 vmpl2_perms; +__u8 vmpl1_perms; +}; +#define KVM_SEV_SNP_ID_BLOCK_SIZE 96#define KVM_SEV_SNP_ID_AUTH_SIZE 4096#define KVM_SEV_SNP_FINISH_DATA_SIZE 32
arch/x86/kvm/svm/sev.c +93 -30
239 lines not on this page · raw message
arch/x86/kvm/svm/svm.h +1 -0
@@ -98,6 +98,7 @@ struct kvm_sev_info {void *guest_req_buf;/* Bounce buffer for SNP Guest Request input */ void *guest_resp_buf;/* Bounce buffer for SNP Guest Request output */ struct mutex guest_req_mutex;/* Must acquire before using bounce buffers */ +unsigned int snp_init_flags; };struct kvm_svm {
include/uapi/linux/kvm.h +3 -0
@@ -1399,6 +1399,9 @@ struct kvm_enc_region {#define KVM_GET_SREGS2 _IOR(KVMIO, 0xcc, struct kvm_sregs2)#define KVM_SET_SREGS2 _IOW(KVMIO, 0xcd, struct kvm_sregs2) +/* Enable SVSM support */ +#define KVM_SEV_SNP_SVSM (1 << 0) +#define KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE (1 << 0)#define KVM_DIRTY_LOG_INITIALLY_SET (1 << 1)
Signature · 1 line
2.43.2