Add SEV-SNP restricted injection hypervisor support
v4 cover Add SEV-SNP restricted injection hypervisor support
Hi all,
This is v4 of the Restricted Injection hypervisor support patches.
The previous version was submitted here:
https://lore.kernel.org/all/[email protected]
The new series are rebased on commit 6b802031877a995456c528095c41d1948546bf45 (kvm/next).
Restricted Injection (RI) is not a prerequisite for Planes support. Even though the SVSM requires RI, RI support can be submitted independently. Since the number of Planes patches is large, submit RI as its own series.
Changelog:
----------
v3
This is v3 of the Restricted Injection hypervisor support patches.
The previous, v2 version was submitted here:
https://lore.kernel.org/r/[email protected]
Since then, the series experienced the following changes:
1. Remove unused field in struct hvdb_events.
2. Add comments explaining sev_snp_blocked() with no_further_signal and vector. Explain that only a single interrupt vector is presented by the hypervisor in the doorbell page. Only when the interrupt is acked, the next interrupt is presented.
3. Add warning in sev_snp_cancel_injection() when vector is not #HV_VECTOR.
4. Update hvdb_map() failure handling. When it fails, no interrupt will be injected.
5. Remove sev_snp_queue_exception() in svm_update_soft_interrupt_rip(), since the soft interrupt is only BP_VECTOR and OF_VECTOR, so it will always return false, then sev_snp_queue_exception() will be executed in svm_inject_exception() always.
6. Add new #HV IPI feature in XAPIC and X2APIC mode, test each mode with three IPI types: broadcast, self-IPI, and allbutself.
v2
Hi all,
This is a v2 of the restricted injection hypervisor support patches.
The previous version was submitted here:
https://lore.kernel.org/r/[email protected]
Since the previous submission, one issue reported by the kernel test robot was fixed.
All comments and review feedback are appreciated.
Thanks.
v1
Operating systems may not handle unexpected interrupt or exception sequences. A malicious hypervisor can inject random interrupt or exception sequences, putting guest drivers or guest OS kernels into an unexpected state, which could lead to security issues.
To address this concern, SEV-SNP restricts the injection of interrupts and exceptions to those only allowed by the guest. Restricted Injection disables all hypervisor-based interrupt queuing and event injection for all vectors, allowing only a single vector, #HV (28), which is reserved for SNP guest use but is never generated by hardware. #HV is only permitted to be injected into VMSAs that execute with Restricted Injection.
Guests operating with Restricted Injection are expected to communicate with the hypervisor about events via a software-managed para-virtualization interface. This interface can utilize #HV injection as a doorbell to inform the guest that new events have occurred. This patch set implements Restricted Injection on the KVM side directly into VMPL0.
Overview:
The GHCB 2.0 specification[1] defines #HV doorbell page and the #HV doorbell page NAE event allows for an SEV-SNP guest to register a doorbell page for use with the hypervisor injection exception (#HV). When Restricted Injection is active, only #HV exceptions can be injected into the guest, and the hypervisor follows the GHCB #HV doorbell communication to inject the exception or interrupt. Restricted Injection can be enabled by setting the bit in vmsa_features.
The patchset is rebased on the kvm/next (commit 15e1c3d65975524c5c792fcd59f7d89f00402261).
Testing:
The patchset has been tested with the sev-snp guest, ovmf and qemu supporting restricted injection.
Four test sets:
1.ls -lr /
2.apt update
3.fio
4.perf
Thanks
Melody
Melody Wang (7):
x86/sev: Define the #HV doorbell page structure
KVM: SVM: Add support for the SEV-SNP #HV doorbell page NAE event
KVM: SVM: Inject #HV when Restricted Injection is active
KVM: SVM: Inject NMIs when Restricted Injection is active
KVM: SVM: Inject MCEs when Restricted Injection is active
KVM: SVM: Add support for the SEV-SNP #HV IPI NAE event
KVM: SVM: Enable Restricted Injection for an SEV-SNP guest
arch/x86/include/asm/cpufeatures.h | 1 + arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 1 + arch/x86/include/asm/sev-common.h | 1 + arch/x86/include/asm/svm.h | 33 +++ arch/x86/include/uapi/asm/svm.h | 6 + arch/x86/kvm/lapic.c | 24 ++- arch/x86/kvm/lapic.h | 2 + arch/x86/kvm/svm/sev.c | 312 ++++++++++++++++++++++++++++- arch/x86/kvm/svm/svm.c | 42 +++- arch/x86/kvm/svm/svm.h | 26 +++ arch/x86/kvm/vmx/main.c | 10 + arch/x86/kvm/vmx/vmx.c | 5 + arch/x86/kvm/vmx/x86_ops.h | 1 + arch/x86/kvm/x86.c | 7 + 15 files changed, 468 insertions(+), 4 deletions(-)
Signature · 1 line
2.43.0
PATCH v4 1/7 x86/sev: Define the #HV doorbell page structure
Restricted injection is a feature which enforces additional interrupt and event injection security protections for a SEV-SNP guest. It disables all hypervisor-based interrupt queuing and event injection of all vectors except a new exception vector, #HV (28), which is reserved for SNP guest use, but never generated by hardware. #HV is only allowed to be injected into VMSAs that execute with Restricted Injection.
The guests running with the SNP restricted injection feature active limit the host to ringing a doorbell with a #HV exception.
Define two fields in the #HV doorbell page: a pending event field, and an EOI assist.
Create the structure definition for the #HV doorbell page as per GHCB specification.
--- arch/x86/include/asm/svm.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
arch/x86/include/asm/svm.h +33 -0
@@ -252,6 +252,39 @@ struct __attribute__ ((__packed__)) vmcb_control_area {#define SVM_TSC_RATIO_MAX 0x000000ffffffffffULL#define SVM_TSC_RATIO_DEFAULT 0x0100000000ULL +/* +* Hypervisor doorbell page: +* +* Used when Restricted Injection is enabled for a VM. One page in size that +* is shared between the guest and hypervisor to communicate exception and +* interrupt events. +*/ +struct hvdb_events { +/* First 64 bytes of HV doorbell page defined in GHCB specification */ +union { +struct { +/* Non-maskable event indicators */ +u16 vector:8 , + nmi:1 , + mce:1 , + reserved2:5 , + no_further_signal:1 ; + }; + +u16 pending_events; + }; + +u8 no_eoi_required; + +u8 reserved3[61 ]; +}; + +struct hvdb { +struct hvdb_events events; + +/* Remainder of the page is for software use */ +u8 reserved[PAGE_SIZE -sizeof (struct hvdb_events )]; +};/* AVIC */ #define AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK (0xFFULL)
Signature · 1 line
2.43.0
PATCH v4 2/7 KVM: SVM: Add support for the SEV-SNP #HV doorbell page NAE event
In reply to Melody WangTo support Restricted Injection, the SEV-SNP guest must register a doorbell page for use with #HV. This is done using the #HV doorbell page NAE event.
This event consists of four actions: GET_PREFERRED, SET, QUERY, CLEAR. Implement it per the GHCB specification.
--- arch/x86/include/uapi/asm/svm.h | 5 +++ arch/x86/kvm/svm/sev.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 3 ++ arch/x86/kvm/svm/svm.h | 2 + 4 files changed, 81 insertions(+)
arch/x86/include/uapi/asm/svm.h +5 -0
@@ -117,6 +117,11 @@#define SVM_VMGEXIT_AP_CREATE_ON_INIT 0#define SVM_VMGEXIT_AP_CREATE 1#define SVM_VMGEXIT_AP_DESTROY 2 +#define SVM_VMGEXIT_HVDB_PAGE 0x80000014ull +#define SVM_VMGEXIT_HVDB_GET_PREFERRED 0 +#define SVM_VMGEXIT_HVDB_SET 1 +#define SVM_VMGEXIT_HVDB_QUERY 2 +#define SVM_VMGEXIT_HVDB_CLEAR 3#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018ull#define SVM_VMGEXIT_SAVIC 0x8000001aull#define SVM_VMGEXIT_SAVIC_REGISTER_GPA 0
arch/x86/kvm/svm/sev.c +71 -0
89 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +3 -0
@@ -1266,6 +1266,9 @@ static void __svm_vcpu_reset(struct kvm_vcpu *vcpu) svm->nmi_masked = false; svm->awaiting_iret_completion = false; + +if (is_sev_es_guest (vcpu)) + svm->sev_es.hvdb_gpa =INVALID_PAGE ; }static void svm_vcpu_reset (struct kvm_vcpu *vcpu,bool init_event)
arch/x86/kvm/svm/svm.h +2 -0
@@ -267,6 +267,8 @@ struct vcpu_sev_es_state {gpa_t snp_vmsa_gpa;bool snp_ap_waiting_for_reset;bool snp_has_guest_vmsa; + +gpa_t hvdb_gpa; };struct vcpu_svm {
Signature · 1 line
2.43.0
PATCH v4 3/7 KVM: SVM: Inject #HV when Restricted Injection is active
In reply to Melody WangWhen Restricted Injection is active, only #HV exceptions can be injected into the SEV-SNP guest. Detect that, and then follow the #HV doorbell communication from the GHCB specification to inject the interrupt or exception.
--- arch/x86/kvm/svm/sev.c | 164 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 14 +++- arch/x86/kvm/svm/svm.h | 21 ++++++ 3 files changed, 197 insertions(+), 2 deletions(-)
arch/x86/kvm/svm/sev.c +164 -0
@@ -5321,3 +5321,167 @@ void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa)free_page ((unsigned long )vmsa); } + +static void prepare_hv_injection (struct vcpu_svm *svm,struct hvdb *hvdb) +{ +if (hvdb->events.no_further_signal) +return ; + + svm->vmcb->control.event_inj =HV_VECTOR | +SVM_EVTINJ_TYPE_EXEPT | +SVM_EVTINJ_VALID ; + svm->vmcb->control.event_inj_err =0 ; + + hvdb->events.no_further_signal =1 ; +} + +static void unmap_hvdb (struct kvm_vcpu *vcpu,struct kvm_host_map *map) +{ +kvm_vcpu_unmap (vcpu, map); +} + +static struct hvdb *map_hvdb (struct kvm_vcpu *vcpu,struct kvm_host_map *map) +{ +struct vcpu_svm *svm =to_svm (vcpu); + +if (!VALID_PAGE (svm->sev_es.hvdb_gpa)) +return NULL ; + +if (kvm_vcpu_map (vcpu,gpa_to_gfn (svm->sev_es.hvdb_gpa), map)) { +vcpu_unimpl (vcpu,"snp: error mapping #HV doorbell page [%#llx] from guest\n" , + svm->sev_es.hvdb_gpa); + +return NULL ; + } + +return map->hva; +} + +static void __sev_snp_inject (enum inject_type type,struct kvm_vcpu *vcpu) +{ +struct vcpu_svm *svm =to_svm (vcpu); +struct kvm_host_map hvdb_map; +struct hvdb *hvdb; + + hvdb =map_hvdb (vcpu, &hvdb_map); +if (!hvdb) { +WARN_ONCE (1 ,"Restricted Injection enabled, hvdb page mapping failed\n" ); +return ; + } + + hvdb->events.vector = vcpu->arch.interrupt.nr; + +prepare_hv_injection (svm, hvdb); + +unmap_hvdb (vcpu, &hvdb_map); +} + +bool sev_snp_queue_exception (struct kvm_vcpu *vcpu) +{ +struct vcpu_svm *svm =to_svm (vcpu); + +if (!sev_snp_is_rinj_active (vcpu)) +return false; + +/* +* Restricted Injection is enabled, only #HV is supported. +* If the vector is not HV_VECTOR, do not inject the exception, +* then return true to skip the original injection path. +*/ +if (WARN_ONCE (vcpu->arch.exception.vector !=HV_VECTOR , +"Restricted Injection enabled, exception vector %u injection not supported\n" , + vcpu->arch.exception.vector)) +return true; + +/* +* An intercept likely occurred during #HV delivery, so re-inject it +* using the current HVDB pending event values. +*/ + svm->vmcb->control.event_inj =HV_VECTOR | +SVM_EVTINJ_TYPE_EXEPT | +SVM_EVTINJ_VALID ; + svm->vmcb->control.event_inj_err =0 ; + +return true; +} + +bool sev_snp_inject (enum inject_type type,struct kvm_vcpu *vcpu) +{ +if (!sev_snp_is_rinj_active (vcpu)) +return false; + +__sev_snp_inject (type, vcpu); + +return true; +} + +void sev_snp_cancel_injection (struct kvm_vcpu *vcpu) +{ +struct vcpu_svm *svm =to_svm (vcpu); +struct kvm_host_map hvdb_map; +struct hvdb *hvdb; + +if (!sev_snp_is_rinj_active (vcpu)) +return ; + +if (!svm->vmcb->control.event_inj) +return ; + +if (WARN_ONCE ((svm->vmcb->control.event_inj &SVM_EVTINJ_VEC_MASK ) !=HV_VECTOR , +"Restricted Injection enabled, %u vector not supported\n" , + svm->vmcb->control.event_inj &SVM_EVTINJ_VEC_MASK )) +return ; + +/* +* Copy the information in the doorbell page into the event injection +* fields to complete the cancellation flow. +*/ + hvdb =map_hvdb (vcpu, &hvdb_map); +if (!hvdb) +return ; + +if (!hvdb->events.pending_events) { +/* No pending events, then event_inj field should be 0 */ +WARN_ON_ONCE (svm->vmcb->control.event_inj); + goto out; + } + +/* Copy info back into event_inj field (replaces #HV) */ + svm->vmcb->control.event_inj =SVM_EVTINJ_VALID ; + +if (hvdb->events.vector) + svm->vmcb->control.event_inj |= hvdb->events.vector | +SVM_EVTINJ_TYPE_INTR ; + + hvdb->events.pending_events =0 ; + +out: +unmap_hvdb (vcpu, &hvdb_map); +} + +/* +* sev_snp_blocked() is for each vector - interrupt, NMI and MCE. It is +* checking if there is an interrupt handled by the guest when +* another interrupt is pending. So hvdb->events.vector will be used for +* checking while no_further_signal is signaling to the guest that a #HV +* is presented by the hypervisor. So no_further_signal is checked when +* a #HV needs to be presented to the guest. +*/ +bool sev_snp_blocked (enum inject_type type,struct kvm_vcpu *vcpu) +{ +struct kvm_host_map hvdb_map; +struct hvdb *hvdb; +bool blocked; + +/* Indicate interrupts are blocked if doorbell page can't be mapped */ + hvdb =map_hvdb (vcpu, &hvdb_map); +if (!hvdb) +return true; + +/* Indicate interrupts blocked based on guest acknowledgment */ + blocked = !!hvdb->events.vector; + +unmap_hvdb (vcpu, &hvdb_map); + +return blocked; +}
arch/x86/kvm/svm/svm.c +12 -2
@@ -392,6 +392,9 @@ static void svm_inject_exception(struct kvm_vcpu *vcpu) svm_update_soft_interrupt_rip(vcpu, ex->vector)) return; +if (sev_snp_queue_exception (vcpu)) +return ; + svm->vmcb->control.event_inj = ex->vector |SVM_EVTINJ_VALID | (ex->has_error_code ?SVM_EVTINJ_VALID_ERR :0 )
@@ -3807,9 +3810,11 @@ static void svm_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) }trace_kvm_inj_virq (intr->nr, intr->soft, reinjected); - ++vcpu->stat.irq_injections; - svm->vmcb->control.event_inj = intr->nr |SVM_EVTINJ_VALID | type; +if (!sev_snp_inject (INJECT_IRQ , vcpu)) + svm->vmcb->control.event_inj = intr->nr |SVM_EVTINJ_VALID | type; + + ++vcpu->stat.irq_injections; }static void svm_fixup_nested_rips (struct kvm_vcpu *vcpu)
@@ -3984,6 +3989,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_blocked (INJECT_IRQ , vcpu); +if (is_guest_mode (vcpu)) {/* As long as interrupts are being delivered... */ if ((svm->nested.ctl.int_ctl & V_INTR_MASKING_MASK)
@@ -4334,6 +4342,8 @@ static void svm_cancel_injection(struct kvm_vcpu *vcpu)struct vcpu_svm *svm =to_svm (vcpu);struct vmcb_control_area *control = &svm->vmcb->control; +sev_snp_cancel_injection (vcpu); + control->exit_int_info = control->event_inj; control->exit_int_info_err = control->event_inj_err; control->event_inj =0 ;
arch/x86/kvm/svm/svm.h +21 -0
@@ -55,6 +55,10 @@ extern int tsc_aux_uret_slot __ro_after_init;extern struct kvm_x86_ops svm_x86_ops__initdata ; +enum inject_type { +INJECT_IRQ , +}; +/* * Clean bits in VMCB. * VMCB_ALL_CLEAN_MASK might also need to
@@ -968,6 +972,17 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);int sev_gmem_max_mapping_level (struct kvm *kvm,kvm_pfn_t pfn,bool is_private);struct vmcb_save_area *sev_decrypt_vmsa (struct kvm_vcpu *vcpu);void sev_free_decrypted_vmsa (struct kvm_vcpu *vcpu,struct vmcb_save_area *vmsa); +bool sev_snp_queue_exception (struct kvm_vcpu *vcpu); +bool sev_snp_inject (enum inject_type type,struct kvm_vcpu *vcpu); +void sev_snp_cancel_injection (struct kvm_vcpu *vcpu); +bool sev_snp_blocked (enum inject_type type,struct kvm_vcpu *vcpu); +static inline bool sev_snp_is_rinj_active (struct kvm_vcpu *vcpu) +{ +struct kvm_sev_info *sev = &to_kvm_svm (vcpu->kvm)->sev_info; + +return is_sev_snp_guest (vcpu) && + (sev->vmsa_features &SVM_SEV_FEAT_RESTRICTED_INJECTION ); +};#else static inline struct page *snp_safe_alloc_page_node (int node,gfp_t gfp) {
@@ -1005,6 +1020,12 @@ static inline struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu) return NULL; }static inline void sev_free_decrypted_vmsa (struct kvm_vcpu *vcpu,struct vmcb_save_area *vmsa) {} + +static inline bool sev_snp_queue_exception (struct kvm_vcpu *vcpu) {return false; } +static inline bool sev_snp_inject (enum inject_type type,struct kvm_vcpu *vcpu) {return false; } +static inline void sev_snp_cancel_injection (struct kvm_vcpu *vcpu) {} +static inline bool sev_snp_blocked (enum inject_type type,struct kvm_vcpu *vcpu) {return false; } +static inline bool sev_snp_is_rinj_active (struct kvm_vcpu *vcpu) {return false; }#endif /* vmenter.S */
Signature · 1 line
2.43.0
Re: [PATCH v4 3/7] KVM: SVM: Inject #HV when Restricted Injection is active
Quoted 29 lines · Melody Wang
arch/x86/kvm/svm/sev.c +29
-0+void sev_snp_cancel_injection (struct kvm_vcpu *vcpu) +{ +struct vcpu_svm *svm =to_svm (vcpu); +struct kvm_host_map hvdb_map; +struct hvdb *hvdb; + +if (!sev_snp_is_rinj_active (vcpu)) +return ; + +if (!svm->vmcb->control.event_inj) +return ; + +if (WARN_ONCE ((svm->vmcb->control.event_inj &SVM_EVTINJ_VEC_MASK ) !=HV_VECTOR , +"Restricted Injection enabled, %u vector not supported\n" , + svm->vmcb->control.event_inj &SVM_EVTINJ_VEC_MASK )) +return ; + +/* +* Copy the information in the doorbell page into the event injection +* fields to complete the cancellation flow. +*/ + hvdb =map_hvdb (vcpu, &hvdb_map); +if (!hvdb) +return ; + +if (!hvdb->events.pending_events) { +/* No pending events, then event_inj field should be 0 */ +WARN_ON_ONCE (svm->vmcb->control.event_inj); + goto out;
arch/x86/kvm/svm/sev.c void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa) · line 5456
Quoted 10 lines
arch/x86/kvm/svm/sev.c +10
-0+ } + +/* Copy info back into event_inj field (replaces #HV) */ + svm->vmcb->control.event_inj =SVM_EVTINJ_VALID ; + +if (hvdb->events.vector) + svm->vmcb->control.event_inj |= hvdb->events.vector | +SVM_EVTINJ_TYPE_INTR ; + + hvdb->events.pending_events =0 ;
Does this also need to clear no_further_signal?
-Joerg
Re: [PATCH v4 3/7] KVM: SVM: Inject #HV when Restricted Injection is active
Hi Joerg,
Quoted 34 lines · Joerg Rodel
Quoted 33 lines · Melody Wang
arch/x86/kvm/svm/sev.c +33
-0+void sev_snp_cancel_injection (struct kvm_vcpu *vcpu) +{ +struct vcpu_svm *svm =to_svm (vcpu); +struct kvm_host_map hvdb_map; +struct hvdb *hvdb; + +if (!sev_snp_is_rinj_active (vcpu)) +return ; + +if (!svm->vmcb->control.event_inj) +return ; + +if (WARN_ONCE ((svm->vmcb->control.event_inj &SVM_EVTINJ_VEC_MASK ) !=HV_VECTOR , +"Restricted Injection enabled, %u vector not supported\n" , + svm->vmcb->control.event_inj &SVM_EVTINJ_VEC_MASK )) +return ; + +/* +* Copy the information in the doorbell page into the event injection +* fields to complete the cancellation flow. +*/ + hvdb =map_hvdb (vcpu, &hvdb_map); +if (!hvdb) +return ; + +if (!hvdb->events.pending_events) { +/* No pending events, then event_inj field should be 0 */ +WARN_ON_ONCE (svm->vmcb->control.event_inj); + goto out; + } + +/* Copy info back into event_inj field (replaces #HV) */ + svm->vmcb->control.event_inj =SVM_EVTINJ_VALID ;
Quoted 8 lines
arch/x86/kvm/svm/sev.c void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa) · line 5456
Quoted 6 lines
arch/x86/kvm/svm/sev.c +6
-0+ +if (hvdb->events.vector) + svm->vmcb->control.event_inj |= hvdb->events.vector | +SVM_EVTINJ_TYPE_INTR ; + + hvdb->events.pending_events =0 ;Does this also need to clear
no_further_signal?
Yes, the no_further_signal is a bit in pending_events so it is getting cleared already. :)
Thanks,
Melody
Quoted 2 lines
-Joerg
Signature · 2 lines
Thanks, Melody
Re: [PATCH v4 3/7] KVM: SVM: Inject #HV when Restricted Injection is active
In reply to Melody WangQuoted 33 lines · Melody Wang
When Restricted Injection is active, only #HV exceptions can be injected into the SEV-SNP guest. Detect that, and then follow the #HV doorbell communication from the GHCB specification to inject the interrupt or exception.
Co-developed-by Thomas Lendacky <[email protected]>Signed-off-by Thomas Lendacky <[email protected]>Signed-off-by Melody Wang <[email protected]>--- arch/x86/kvm/svm/sev.c | 164 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 14 +++- arch/x86/kvm/svm/svm.h | 21 ++++++ 3 files changed, 197 insertions(+), 2 deletions(-)arch/x86/kvm/svm/sev.c +0
-0arch/x86/kvm/svm/svm.h +4
-0@@ -55,6 +55,10 @@ extern int tsc_aux_uret_slot __ro_after_init;extern struct kvm_x86_ops svm_x86_ops__initdata ; +enum inject_type { +INJECT_IRQ , +}; +/* * Clean bits in VMCB. * VMCB_ALL_CLEAN_MASK might also need to
arch/x86/kvm/svm/svm.h void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end); · line 981
Quoted 10 lines
arch/x86/kvm/svm/svm.h +7
-0@@ -968,6 +972,17 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);int sev_gmem_max_mapping_level (struct kvm *kvm,kvm_pfn_t pfn,bool is_private);struct vmcb_save_area *sev_decrypt_vmsa (struct kvm_vcpu *vcpu);void sev_free_decrypted_vmsa (struct kvm_vcpu *vcpu,struct vmcb_save_area *vmsa); +bool sev_snp_queue_exception (struct kvm_vcpu *vcpu); +bool sev_snp_inject (enum inject_type type,struct kvm_vcpu *vcpu); +void sev_snp_cancel_injection (struct kvm_vcpu *vcpu); +bool sev_snp_blocked (enum inject_type type,struct kvm_vcpu *vcpu); +static inline bool sev_snp_is_rinj_active (struct kvm_vcpu *vcpu) +{ +struct kvm_sev_info *sev = &to_kvm_svm (vcpu->kvm)->sev_info;
You can use to_kvm_sev_info() here.
Thanks,
Tom
Quoted 20 lines
arch/x86/kvm/svm/svm.h +10
-0+ +return is_sev_snp_guest (vcpu) && + (sev->vmsa_features & SVM_SEV_FEAT_RESTRICTED_INJECTION); +};#else static inline struct page *snp_safe_alloc_page_node (int node,gfp_t gfp) {@@ -1005,6 +1020,12 @@ static inline struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu) return NULL; }static inline void sev_free_decrypted_vmsa (struct kvm_vcpu *vcpu,struct vmcb_save_area *vmsa) {} + +static inline bool sev_snp_queue_exception (struct kvm_vcpu *vcpu) {return false; } +static inline bool sev_snp_inject (enum inject_type type,struct kvm_vcpu *vcpu) {return false; } +static inline void sev_snp_cancel_injection (struct kvm_vcpu *vcpu) {} +static inline bool sev_snp_blocked (enum inject_type type,struct kvm_vcpu *vcpu) {return false; } +static inline bool sev_snp_is_rinj_active (struct kvm_vcpu *vcpu) {return false; }#endif /* vmenter.S */
PATCH v4 4/7 KVM: SVM: Inject NMIs when Restricted Injection is active
In reply to Melody WangWhen Restricted Injection is active, only #HV exceptions can be injected into the SEV-SNP guest.
Detect that, and then follow the #HV doorbell communication from the GHCB specification to inject NMIs.
--- arch/x86/kvm/svm/sev.c | 19 ++++++++++++++++--- arch/x86/kvm/svm/svm.c | 8 ++++++++ arch/x86/kvm/svm/svm.h | 1 + 3 files changed, 25 insertions(+), 3 deletions(-)
arch/x86/kvm/svm/sev.c +16 -3
@@ -5369,7 +5369,10 @@ static void __sev_snp_inject(enum inject_type type, struct kvm_vcpu *vcpu)return ; } - hvdb->events.vector = vcpu->arch.interrupt.nr; +if (type ==INJECT_NMI ) + hvdb->events.nmi =1 ; +else + hvdb->events.vector = vcpu->arch.interrupt.nr;prepare_hv_injection (svm, hvdb);
@@ -5449,10 +5452,17 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)/* Copy info back into event_inj field (replaces #HV) */ svm->vmcb->control.event_inj =SVM_EVTINJ_VALID ; +/* +* KVM only injects a single event each time (prepare_hv_injection), +* so when events.nmi is true, the vector will be zero +*/ if (hvdb->events.vector) svm->vmcb->control.event_inj |= hvdb->events.vector |SVM_EVTINJ_TYPE_INTR ; +if (hvdb->events.nmi) + svm->vmcb->control.event_inj |=SVM_EVTINJ_TYPE_NMI ; + hvdb->events.pending_events =0 ; out:
@@ -5478,8 +5488,11 @@ bool sev_snp_blocked(enum inject_type type, struct kvm_vcpu *vcpu)if (!hvdb)return true; -/* Indicate interrupts blocked based on guest acknowledgment */ - blocked = !!hvdb->events.vector; +/* Indicate NMIs and interrupts blocked based on guest acknowledgment */ +if (type ==INJECT_NMI ) + blocked = hvdb->events.nmi; +else + blocked = !!hvdb->events.vector;unmap_hvdb (vcpu, &hvdb_map);
arch/x86/kvm/svm/svm.c +8 -0
@@ -3727,6 +3727,9 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu) {struct vcpu_svm *svm =to_svm (vcpu); +if (sev_snp_inject (INJECT_NMI , vcpu)) + goto status; + svm->vmcb->control.event_inj =SVM_EVTINJ_VALID |SVM_EVTINJ_TYPE_NMI ;if (svm->nmi_l1_to_l2)
@@ -3741,6 +3744,8 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu) svm->nmi_masked = true;svm_set_iret_intercept (svm); } + +status: ++vcpu->stat.nmi_injections; }
@@ -3957,6 +3962,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_blocked (INJECT_NMI , vcpu); +if (is_guest_mode (vcpu) &&nested_exit_on_nmi (svm))return false;
arch/x86/kvm/svm/svm.h +1 -0
@@ -57,6 +57,7 @@ extern struct kvm_x86_ops svm_x86_ops __initdata;enum inject_type {INJECT_IRQ , +INJECT_NMI , };/*
Signature · 1 line
2.43.0
PATCH v4 5/7 KVM: SVM: Inject MCEs when Restricted Injection is active
In reply to Melody WangWhen Restricted Injection is active, only #HV exceptions can be injected into the SEV-SNP guest.
Detect that, and then follow the #HV doorbell communication from the GHCB specification to inject the MCEs.
--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/sev.c | 16 ++++++++++++++-- arch/x86/kvm/svm/svm.c | 17 +++++++++++++++++ arch/x86/kvm/svm/svm.h | 2 ++ arch/x86/kvm/vmx/main.c | 10 ++++++++++ arch/x86/kvm/vmx/vmx.c | 5 +++++ arch/x86/kvm/vmx/x86_ops.h | 1 + arch/x86/kvm/x86.c | 7 +++++++ 9 files changed, 58 insertions(+), 2 deletions(-)
arch/x86/include/asm/kvm-x86-ops.h +1 -0
@@ -79,6 +79,7 @@ KVM_X86_OP(inject_exception) KVM_X86_OP(cancel_injection) KVM_X86_OP(interrupt_allowed) KVM_X86_OP(nmi_allowed) +KVM_X86_OP_OPTIONAL(mce_allowed) KVM_X86_OP(get_nmi_mask) KVM_X86_OP(set_nmi_mask) KVM_X86_OP(enable_nmi_window)
arch/x86/include/asm/kvm_host.h +1 -0
@@ -1860,6 +1860,7 @@ struct kvm_x86_ops {void (*cancel_injection)(struct kvm_vcpu *vcpu);int (*interrupt_allowed)(struct kvm_vcpu *vcpu,bool for_injection);int (*nmi_allowed)(struct kvm_vcpu *vcpu,bool for_injection); +int (*mce_allowed)(struct kvm_vcpu *vcpu);bool (*get_nmi_mask)(struct kvm_vcpu *vcpu);void (*set_nmi_mask)(struct kvm_vcpu *vcpu,bool masked);/* Whether or not a virtual NMI is pending in hardware. */
arch/x86/kvm/svm/sev.c +14 -2
@@ -5371,6 +5371,8 @@ static void __sev_snp_inject(enum inject_type type, struct kvm_vcpu *vcpu)if (type ==INJECT_NMI ) hvdb->events.nmi =1 ; +else if (type ==INJECT_MCE ) + hvdb->events.mce =1 ;else hvdb->events.vector = vcpu->arch.interrupt.nr;
@@ -5386,6 +5388,11 @@ bool sev_snp_queue_exception(struct kvm_vcpu *vcpu)if (!sev_snp_is_rinj_active (vcpu))return false; +if (vcpu->arch.exception.vector ==MC_VECTOR ) { +__sev_snp_inject (INJECT_MCE , vcpu); +return true; + } +/* * Restricted Injection is enabled, only #HV is supported. * If the vector is not HV_VECTOR, do not inject the exception,
@@ -5454,7 +5461,7 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)/* * KVM only injects a single event each time (prepare_hv_injection), -* so when events.nmi is true, the vector will be zero +* so when events.nmi is true, the MCE and vector will be zero. */ if (hvdb->events.vector) svm->vmcb->control.event_inj |= hvdb->events.vector |
@@ -5463,6 +5470,9 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)if (hvdb->events.nmi) svm->vmcb->control.event_inj |=SVM_EVTINJ_TYPE_NMI ; +if (hvdb->events.mce) + svm->vmcb->control.event_inj |=MC_VECTOR |SVM_EVTINJ_TYPE_EXEPT ; + hvdb->events.pending_events =0 ; out:
@@ -5488,9 +5498,11 @@ bool sev_snp_blocked(enum inject_type type, struct kvm_vcpu *vcpu)if (!hvdb)return true; -/* Indicate NMIs and interrupts blocked based on guest acknowledgment */ +/* Indicate NMIs, MCEs and interrupts blocked based on guest acknowledgment */ if (type ==INJECT_NMI ) blocked = hvdb->events.nmi; +else if (type ==INJECT_MCE ) + blocked = hvdb->events.mce;else blocked = !!hvdb->events.vector;
arch/x86/kvm/svm/svm.c +17 -0
@@ -4038,6 +4038,22 @@ static int svm_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)return 1 ; } +bool svm_mce_blocked (struct kvm_vcpu *vcpu) +{ +if (sev_snp_is_rinj_active (vcpu)) +return sev_snp_blocked (INJECT_MCE , vcpu); + +return false; +} + +static int svm_mce_allowed (struct kvm_vcpu *vcpu) +{ +if (svm_mce_blocked (vcpu)) +return 0 ; + +return 1 ; +} +static void svm_enable_irq_window (struct kvm_vcpu *vcpu) {struct vcpu_svm *svm =to_svm (vcpu);
@@ -5351,6 +5367,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = { .cancel_injection = svm_cancel_injection, .interrupt_allowed = svm_interrupt_allowed, .nmi_allowed = svm_nmi_allowed, + .mce_allowed = svm_mce_allowed, .get_nmi_mask = svm_get_nmi_mask, .set_nmi_mask = svm_set_nmi_mask, .enable_nmi_window = svm_enable_nmi_window,
arch/x86/kvm/svm/svm.h +2 -0
@@ -58,6 +58,7 @@ extern struct kvm_x86_ops svm_x86_ops __initdata;enum inject_type {INJECT_IRQ ,INJECT_NMI , +INJECT_MCE , };/*
@@ -798,6 +799,7 @@ void svm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4);void disable_nmi_singlestep (struct vcpu_svm *svm);bool svm_smi_blocked (struct kvm_vcpu *vcpu);bool svm_nmi_blocked (struct kvm_vcpu *vcpu); +bool svm_mce_blocked (struct kvm_vcpu *vcpu);bool svm_interrupt_blocked (struct kvm_vcpu *vcpu);void svm_set_gif (struct vcpu_svm *svm,bool value);int svm_invoke_exit_handler (struct kvm_vcpu *vcpu,u64 exit_code);
arch/x86/kvm/vmx/main.c +10 -0
@@ -812,6 +812,15 @@ static void vt_cancel_hv_timer(struct kvm_vcpu *vcpu) }#endif +static int vt_mce_allowed (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return 0 ; + +return vmx_mce_allowed (vcpu); +} + +static void vt_setup_mce (struct kvm_vcpu *vcpu) {if (is_td_vcpu (vcpu))
@@ -945,6 +954,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .cancel_injection = vt_op(cancel_injection), .interrupt_allowed = vt_op(interrupt_allowed), .nmi_allowed = vt_op(nmi_allowed), + .mce_allowed = vt_op(mce_allowed), .get_nmi_mask = vt_op(get_nmi_mask), .set_nmi_mask = vt_op(set_nmi_mask), .enable_nmi_window = vt_op(enable_nmi_window),
arch/x86/kvm/vmx/vmx.c +5 -0
@@ -5210,6 +5210,11 @@ int vmx_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)return !vmx_interrupt_blocked (vcpu); } +int vmx_mce_allowed (struct kvm_vcpu *vcpu) +{ +return 1 ; +} +int vmx_set_tss_addr (struct kvm *kvm,unsigned int addr) {void __user *ret;
arch/x86/kvm/vmx/x86_ops.h +1 -0
@@ -92,6 +92,7 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu);void vmx_cancel_injection (struct kvm_vcpu *vcpu);int vmx_interrupt_allowed (struct kvm_vcpu *vcpu,bool for_injection);int vmx_nmi_allowed (struct kvm_vcpu *vcpu,bool for_injection); +int vmx_mce_allowed (struct kvm_vcpu *vcpu);bool vmx_get_nmi_mask (struct kvm_vcpu *vcpu);void vmx_set_nmi_mask (struct kvm_vcpu *vcpu,bool masked);void vmx_enable_nmi_window (struct kvm_vcpu *vcpu);
arch/x86/kvm/x86.c +7 -0
@@ -10789,6 +10789,12 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu,kvm_update_dr7 (vcpu); } +if (vcpu->arch.exception.vector ==MC_VECTOR ) { + r =static_call (kvm_x86_mce_allowed)(vcpu); +if (!r) + goto out_except; + } +kvm_inject_exception (vcpu); vcpu->arch.exception.pending = false;
@@ -10796,6 +10802,7 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, can_inject = false; } +out_except:/* Don't inject interrupts if the user asked to avoid doing so */ if (vcpu->guest_debug &KVM_GUESTDBG_BLOCKIRQ )
Signature · 1 line
2.43.0
Re: [PATCH v4 5/7] KVM: SVM: Inject MCEs when Restricted Injection is active
arch/x86/kvm/x86.c static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, · line 10795
Quoted 12 lines · Melody Wang
arch/x86/kvm/x86.c +4
-0@@ -10789,6 +10789,12 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu,kvm_update_dr7 (vcpu); } +if (vcpu->arch.exception.vector ==MC_VECTOR ) { + r =static_call (kvm_x86_mce_allowed)(vcpu); +if (!r) + goto out_except;
If this path is taken the warning at the bottom of the function will trigger. I think the target needs to be out: instead of out_except.
-Joerg
Re: [PATCH v4 5/7] KVM: SVM: Inject MCEs when Restricted Injection is active
Hi Joerg,
Quoted 9 lines · Joerg Rodel
Quoted 8 lines · Melody Wang
arch/x86/kvm/x86.c +0
-0@@ -10789,6 +10789,12 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu,kvm_update_dr7 (vcpu); }
Quoted 8 lines
arch/x86/kvm/x86.c static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, · line 10795
Quoted 4 lines
arch/x86/kvm/x86.c +4
-0+if (vcpu->arch.exception.vector ==MC_VECTOR ) { + r =static_call (kvm_x86_mce_allowed)(vcpu); +if (!r) + goto out_except;If this path is taken the warning at the bottom of the function will trigger. I think the target needs to be out: instead of
out_except.
Yes, I think you are right. If MC is not allowed, I should simply return directly instead of noodling through the rest of the function.
Thanks,
Melody
Quoted 1 line
-Joerg
Signature · 2 lines
Thanks, Melody
Re: [PATCH v4 5/7] KVM: SVM: Inject MCEs when Restricted Injection is active
In reply to Melody WangQuoted 49 lines · Melody Wang
When Restricted Injection is active, only #HV exceptions can be injected into the SEV-SNP guest.
Detect that, and then follow the #HV doorbell communication from the GHCB specification to inject the MCEs.
Co-developed-by Thomas Lendacky <[email protected]>Signed-off-by Thomas Lendacky <[email protected]>Signed-off-by Melody Wang <[email protected]>--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/sev.c | 16 ++++++++++++++-- arch/x86/kvm/svm/svm.c | 17 +++++++++++++++++ arch/x86/kvm/svm/svm.h | 2 ++ arch/x86/kvm/vmx/main.c | 10 ++++++++++ arch/x86/kvm/vmx/vmx.c | 5 +++++ arch/x86/kvm/vmx/x86_ops.h | 1 + arch/x86/kvm/x86.c | 7 +++++++ 9 files changed, 58 insertions(+), 2 deletions(-)arch/x86/kvm/svm/sev.c +7
-0@@ -5371,6 +5371,8 @@ static void __sev_snp_inject(enum inject_type type, struct kvm_vcpu *vcpu)if (type ==INJECT_NMI ) hvdb->events.nmi =1 ; +else if (type ==INJECT_MCE ) + hvdb->events.mce =1 ;else hvdb->events.vector = vcpu->arch.interrupt.nr;@@ -5386,6 +5388,11 @@ bool sev_snp_queue_exception(struct kvm_vcpu *vcpu)if (!sev_snp_is_rinj_active (vcpu))return false; +if (vcpu->arch.exception.vector ==MC_VECTOR ) { +__sev_snp_inject (INJECT_MCE , vcpu); +return true; + } +/* * Restricted Injection is enabled, only #HV is supported. * If the vector is not HV_VECTOR, do not inject the exception,
arch/x86/kvm/svm/sev.c void sev_snp_cancel_injection(struct kvm_vcpu *vcpu) · line 5464
Quoted 5 lines
arch/x86/kvm/svm/sev.c +1
-1@@ -5454,7 +5461,7 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)/* * KVM only injects a single event each time (prepare_hv_injection), -* so when events.nmi is true, the vector will be zero +* so when events.nmi is true, the MCE and vector will be zero.
This is confusing. I think you mean to say
so when events.nmi or events.mce is true, the vector will be zero.
correct?
Thanks,
Tom
Quoted 13 lines
arch/x86/kvm/svm/sev.c +3
-0*/ if (hvdb->events.vector) svm->vmcb->control.event_inj |= hvdb->events.vector |@@ -5463,6 +5470,9 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)if (hvdb->events.nmi) svm->vmcb->control.event_inj |=SVM_EVTINJ_TYPE_NMI ; +if (hvdb->events.mce) + svm->vmcb->control.event_inj |=MC_VECTOR |SVM_EVTINJ_TYPE_EXEPT ; + hvdb->events.pending_events =0 ; out:
Re: [PATCH v4 5/7] KVM: SVM: Inject MCEs when Restricted Injection is active
Hi Tom,
Quoted 58 lines · Tom Lendacky
Quoted 49 lines · Melody Wang
When Restricted Injection is active, only #HV exceptions can be injected into the SEV-SNP guest.
Detect that, and then follow the #HV doorbell communication from the GHCB specification to inject the MCEs.
Co-developed-by Thomas Lendacky <[email protected]>Signed-off-by Thomas Lendacky <[email protected]>Signed-off-by Melody Wang <[email protected]>--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/sev.c | 16 ++++++++++++++-- arch/x86/kvm/svm/svm.c | 17 +++++++++++++++++ arch/x86/kvm/svm/svm.h | 2 ++ arch/x86/kvm/vmx/main.c | 10 ++++++++++ arch/x86/kvm/vmx/vmx.c | 5 +++++ arch/x86/kvm/vmx/x86_ops.h | 1 + arch/x86/kvm/x86.c | 7 +++++++ 9 files changed, 58 insertions(+), 2 deletions(-)arch/x86/kvm/svm/sev.c +7
-0@@ -5371,6 +5371,8 @@ static void __sev_snp_inject(enum inject_type type, struct kvm_vcpu *vcpu)if (type ==INJECT_NMI ) hvdb->events.nmi =1 ; +else if (type ==INJECT_MCE ) + hvdb->events.mce =1 ;else hvdb->events.vector = vcpu->arch.interrupt.nr;@@ -5386,6 +5388,11 @@ bool sev_snp_queue_exception(struct kvm_vcpu *vcpu)if (!sev_snp_is_rinj_active (vcpu))return false; +if (vcpu->arch.exception.vector ==MC_VECTOR ) { +__sev_snp_inject (INJECT_MCE , vcpu); +return true; + } +/* * Restricted Injection is enabled, only #HV is supported. * If the vector is not HV_VECTOR, do not inject the exception, arch/x86/kvm/svm/sev.c void sev_snp_cancel_injection(struct kvm_vcpu *vcpu) · line 5464
Quoted 5 lines
arch/x86/kvm/svm/sev.c +1
-1@@ -5454,7 +5461,7 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)/* * KVM only injects a single event each time (prepare_hv_injection), -* so when events.nmi is true, the vector will be zero +* so when events.nmi is true, the MCE and vector will be zero. This is confusing. I think you mean to say
Quoted 4 lines
so when events.nmi or events.mce is true, the vector will be zero.
correct?
Yes, I will update the comment.
Thanks,
Melody
Quoted 16 lines
Thanks,
TomQuoted 13 lines
arch/x86/kvm/svm/sev.c +3
-0*/ if (hvdb->events.vector) svm->vmcb->control.event_inj |= hvdb->events.vector |@@ -5463,6 +5470,9 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)if (hvdb->events.nmi) svm->vmcb->control.event_inj |=SVM_EVTINJ_TYPE_NMI ; +if (hvdb->events.mce) + svm->vmcb->control.event_inj |=MC_VECTOR |SVM_EVTINJ_TYPE_EXEPT ; + hvdb->events.pending_events =0 ; out:
Signature · 2 lines
Thanks, Melody
PATCH v4 6/7 KVM: SVM: Add support for the SEV-SNP #HV IPI NAE event
In reply to Melody WangThe #HV IPI NAE event allows the guest to send an IPI to other vCPUs in the guest when Restricted Injection is enabled. Implement the NAE event as per GHCB specification.
--- arch/x86/include/uapi/asm/svm.h | 1 + arch/x86/kvm/lapic.c | 24 +++++++++++++++++++++++- arch/x86/kvm/lapic.h | 2 ++ arch/x86/kvm/svm/sev.c | 26 ++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-)
arch/x86/include/uapi/asm/svm.h +1 -0
@@ -122,6 +122,7 @@#define SVM_VMGEXIT_HVDB_SET 1#define SVM_VMGEXIT_HVDB_QUERY 2#define SVM_VMGEXIT_HVDB_CLEAR 3 +#define SVM_VMGEXIT_HV_IPI 0x80000015ull#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018ull#define SVM_VMGEXIT_SAVIC 0x8000001aull#define SVM_VMGEXIT_SAVIC_REGISTER_GPA 0
arch/x86/kvm/lapic.c +23 -1
@@ -2556,7 +2556,7 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)static int apic_mmio_write (struct kvm_vcpu *vcpu,struct kvm_io_device *this,gpa_t address,int len,const void *data) { -struct kvm_lapic *apic =to_lapic (this); +struct kvm_lapic *apic = this ?to_lapic (this) : vcpu->arch.apic;unsigned int offset = address - apic->base_address;u32 val;
@@ -3581,3 +3581,25 @@ void kvm_lapic_exit(void)static_key_deferred_flush (&apic_sw_disabled);WARN_ON (static_branch_unlikely (&apic_sw_disabled.key)); } + +/* Send IPI by writing ICR with MSR write when X2APIC enabled, with mmio write when XAPIC enabled */ +int kvm_xapic_x2apic_send_ipi (struct kvm_vcpu *vcpu,u64 data) +{ +u32 icr_msr_addr =APIC_BASE_MSR + (APIC_ICR >>4 ); +struct kvm_lapic *apic = vcpu->arch.apic; +gpa_t gpa = apic->base_address +APIC_ICR ; + +if (!kvm_lapic_enabled (vcpu)) +return 1 ; + +if (vcpu->arch.apic_base &X2APIC_ENABLE ) { +if (!kvm_x2apic_msr_write (vcpu, icr_msr_addr, data)) +return 0 ; + }else { +if (!apic_mmio_write (vcpu,NULL , gpa,4 , &data)) +return 0 ; + } + +return 1 ; +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_xapic_x2apic_send_ipi);
arch/x86/kvm/lapic.h +2 -0
@@ -156,6 +156,8 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data);int kvm_lapic_set_pv_eoi (struct kvm_vcpu *vcpu,u64 data,unsigned long len);void kvm_lapic_exit (void ); +int kvm_xapic_x2apic_send_ipi (struct kvm_vcpu *vcpu,u64 data); +u64 kvm_lapic_readable_reg_mask (struct kvm_lapic *apic);static inline void kvm_lapic_set_irr (int vec,struct kvm_lapic *apic)
arch/x86/kvm/svm/sev.c +26 -0
@@ -35,6 +35,7 @@#include "svm_ops.h" #include "cpuid.h" #include "trace.h" +#include "lapic.h" #define GHCB_VERSION_MAX 2ULL#define GHCB_VERSION_MIN 1ULL
@@ -3554,6 +3555,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) goto vmgexit_err;break ;case SVM_VMGEXIT_HVDB_PAGE : +case SVM_VMGEXIT_HV_IPI :if (!is_sev_snp_guest (vcpu)) goto vmgexit_err;break ;
@@ -4362,6 +4364,22 @@ static int sev_snp_hv_doorbell_page(struct vcpu_svm *svm)return 0 ; } +static int sev_snp_hv_ipi (struct vcpu_svm *svm) +{ +struct kvm_vcpu *vcpu = &svm->vcpu; +u64 icr_info; + +if (!is_sev_snp_guest (vcpu)) +return -EINVAL ; + + icr_info = svm->vmcb->control.exit_info_1; + +if (kvm_xapic_x2apic_send_ipi (vcpu, icr_info)) +return -EINVAL ; + +return 0 ; +} +static int sev_handle_vmgexit_msr_protocol (struct vcpu_svm *svm) {struct vmcb_control_area *control = &svm->vmcb->control;
@@ -4635,6 +4653,14 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)ghcb_set_sw_exit_info_2 (svm->sev_es.ghcb,GHCB_ERR_INVALID_INPUT ); } + ret =1 ; +break ; +case SVM_VMGEXIT_HV_IPI : +if (sev_snp_hv_ipi (svm)) { +ghcb_set_sw_exit_info_1 (svm->sev_es.ghcb,2 ); +ghcb_set_sw_exit_info_2 (svm->sev_es.ghcb,GHCB_ERR_INVALID_INPUT ); + } + ret =1 ;break ;case SVM_VMGEXIT_UNSUPPORTED_EVENT :
Signature · 1 line
2.43.0
PATCH v4 7/7 KVM: SVM: Enable Restricted Injection for an SEV-SNP guest
In reply to Melody WangEnable Restricted Injection in an SEV-SNP guest by setting the corresponding bit in the VMSA SEV features field (SEV_FEATURES[3]) from QEMU.
Add Restricted Injection to the supported hypervisor features.
--- arch/x86/include/asm/cpufeatures.h | 1 + arch/x86/include/asm/sev-common.h | 1 + arch/x86/kvm/svm/sev.c | 26 +++++++++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-)
arch/x86/include/asm/cpufeatures.h +1 -0
@@ -453,6 +453,7 @@#define X86_FEATURE_SNP_SECURE_TSC (19*32+ 8)/* SEV-SNP Secure TSC */ #define X86_FEATURE_V_TSC_AUX (19*32+ 9)/* Virtual TSC_AUX */ #define X86_FEATURE_SME_COHERENT (19*32+10)/* hardware-enforced cache coherency */ +#define X86_FEATURE_RESTRICTED_INJECTION (19*32+12)/* Restricted Injection */ #define X86_FEATURE_DEBUG_SWAP (19*32+14)/* "debug_swap" SEV-ES full debug state swap support */ #define X86_FEATURE_RMPREAD (19*32+21)/* RMPREAD instruction */ #define X86_FEATURE_SEGMENTED_RMP (19*32+23)/* Segmented RMP support */
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_SNP_RINJ (BIT_ULL(2) | GHCB_HV_FT_SNP_AP_CREATION)#define GHCB_HV_FT_SNP_MULTI_VMPL BIT_ULL(5)/*
arch/x86/kvm/svm/sev.c +25 -1
@@ -40,7 +40,9 @@#define GHCB_VERSION_MAX 2ULL#define GHCB_VERSION_MIN 1ULL -#define GHCB_HV_FT_SUPPORTED (GHCB_HV_FT_SNP | GHCB_HV_FT_SNP_AP_CREATION) +#define GHCB_HV_FT_SUPPORTED (GHCB_HV_FT_SNP | \ + GHCB_HV_FT_SNP_AP_CREATION | \ + GHCB_HV_FT_SNP_RINJ)/* * The GHCB spec essentially states that all non-zero error codes other than
@@ -64,6 +66,10 @@ module_param_named(sev_es, sev_es_enabled, bool, 0444);static bool __ro_after_init sev_snp_enabled = true;module_param_named (sev_snp, sev_snp_enabled, bool,0444 ); +/* enable/disable SEV-SNP Restricted Injection support */ +static bool sev_snp_restricted_injection_enabled = true; +module_param_named (restricted_injection, sev_snp_restricted_injection_enabled, bool,0444 ); +static unsigned int __ro_after_init nr_ciphertext_hiding_asids;module_param_named (ciphertext_hiding_asids, nr_ciphertext_hiding_asids, uint,0444 );
@@ -3224,6 +3230,12 @@ void __init sev_hardware_setup(void)if (sev_snp_enabled && tsc_khz &&cpu_feature_enabled (X86_FEATURE_SNP_SECURE_TSC )) sev_supported_vmsa_features |=SVM_SEV_FEAT_SECURE_TSC ; + +if (!sev_snp_enabled || !cpu_feature_enabled (X86_FEATURE_RESTRICTED_INJECTION )) + sev_snp_restricted_injection_enabled = false; + +if (sev_snp_restricted_injection_enabled) + sev_supported_vmsa_features |=SVM_SEV_FEAT_RESTRICTED_INJECTION ; }void sev_hardware_unsetup (void )
@@ -4740,10 +4752,20 @@ void sev_vcpu_after_set_cpuid(struct vcpu_svm *svm) vcpu->arch.reserved_gpa_bits &= ~(1UL << (best->ebx &0x3f )); } +static void sev_snp_init_vmcb (struct vcpu_svm *svm) +{ +struct kvm_sev_info *sev = &to_kvm_svm (svm->vcpu.kvm)->sev_info; + +/* V_NMI is not supported when Restricted Injection is enabled */ +if (sev->vmsa_features &SVM_SEV_FEAT_RESTRICTED_INJECTION ) + svm->vmcb->control.int_ctl &= ~V_NMI_ENABLE_MASK ; +} +static void sev_es_init_vmcb (struct vcpu_svm *svm,bool init_event) {struct kvm_sev_info *sev =to_kvm_sev_info (svm->vcpu.kvm);struct vmcb *vmcb = svm->vmcb01.ptr; +struct kvm_vcpu *vcpu = &svm->vcpu; svm->vmcb->control.misc_ctl |=SVM_MISC_ENABLE_SEV_ES ;
@@ -4810,6 +4832,8 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm, bool init_event)set_ghcb_msr (svm,GHCB_MSR_SEV_INFO ((__u64 )sev->ghcb_version,GHCB_VERSION_MIN , sev_enc_bit)); +if (is_sev_snp_guest (vcpu)) +sev_snp_init_vmcb (svm); }void sev_init_vmcb (struct vcpu_svm *svm,bool init_event)
Signature · 1 line
2.43.0
Re: [PATCH v4 7/7] KVM: SVM: Enable Restricted Injection for an SEV-SNP guest
arch/x86/kvm/svm/sev.c module_param_named(sev_es, sev_es_enabled, bool, 0444); · line 71
Quoted 3 lines · Melody Wang
arch/x86/kvm/svm/sev.c +3
-0+/* enable/disable SEV-SNP Restricted Injection support */ +static bool sev_snp_restricted_injection_enabled = true; +module_param_named (restricted_injection, sev_snp_restricted_injection_enabled, bool,0444 );
Any particular reason for a module parameter? I think it is fine to just offer the feature when supported by the hardware and enable it when the VMM (or the guest, when planes land) requests it.
-Joerg
Re: [PATCH v4 7/7] KVM: SVM: Enable Restricted Injection for an SEV-SNP guest
Hi Joerg,
Quoted 9 lines · Joerg Rodel
arch/x86/kvm/svm/sev.c module_param_named(sev_es, sev_es_enabled, bool, 0444); · line 71
Quoted 3 lines · Melody Wang
arch/x86/kvm/svm/sev.c +3
-0+/* enable/disable SEV-SNP Restricted Injection support */ +static bool sev_snp_restricted_injection_enabled = true; +module_param_named (restricted_injection, sev_snp_restricted_injection_enabled, bool,0444 );Any particular reason for a module parameter? I think it is fine to just offer the feature when supported by the hardware and enable it when the VMM (or the guest, when planes land) requests it.
Yes, you are right, I will delete it now, and only if it is really needed I can add it back.
Thanks,
Melody
Quoted 1 line
-Joerg
Signature · 2 lines
Thanks, Melody