* [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support
@ 2026-11-09 3:18 Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 1/8] x86/sev: Define the #HV doorbell page structure Melody Wang
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang
Hi all,
This v5 of the Restricted Injection hypervisor support patches.
The previous version:
https://lore.kernel.org/all/cover.1777048608.git.huibo.wang@amd.com
This is based on d4b7fb647204f0c81dfeae2d1a708e4d858e0c94 (kvm/next).
This series addresses v4 upstream review comments from Sashiko, Tom, and
Joerg.
The main changes in v5 include:
1. Guest-Driven IRR-to-ISR Transition: Instead of moving the vector from
IRR to ISR directly upon reading (matching legacy interrupt injection),
KVM now waits for the guest acknowledgment before moving the interrupt
vector from IRR to ISR.
2. No-EOI Handling: Added handling for the 'no_eoi_required' case.
3. HLT Intercept and Architectural Alignment: Added a new patch to properly
intercept HLT when Restricted Injection is active. This addresses the
guest's loss of control over HLT/interrupt interactions by immediate guest
re-entry on pending events.
4. Sleep-in-Atomic and Scheduling Fixes: Resolved critical issues caused by
kvm_vcpu_map() calling paths in the HLT fast-path (sleeping with local
IRQs disabled) and during normal vCPU block polling (incorrect
TASK_INTERRUPTIBLE/TASK_RUNNING state transitions causing livelocks).
5. Refined Cancel Injection: When canceling injection, KVM now only cancels
the #HV event while preserving the pending interrupt information in the
doorbell page.
6. Priority-Based Vector Overwriting: KVM now overwrites the interrupt
vector in the doorbell page when a higher-priority vector appears,
avoiding sending an unnecessary duplicate #HV.
7. Documentation and Review Feedback: Improved several comments for clarity
and implemented code fixes for valid issues pointed out by Sashiko during
review.
This patch set remains an RFC. There is an alternative approach that I
will present at KVM Forum 2026 in the talk: "COCONUT-SVSM, KVM Planes,
and Secure Interrupt Injection - Upstream". I plan to update this series
further after gathering feedback from the KVM Forum community.
Thanks,
Melody
Changelog:
---------
v4
This is v4 of the Restricted Injection hypervisor support patches.
The previous version was submitted here:
https://lore.kernel.org/all/20241127225539.5567-1-huibo.wang@amd.com
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.
v3
This is v3 of the Restricted Injection hypervisor support patches.
The previous, v2 version was submitted here:
https://lore.kernel.org/r/cover.1725945912.git.huibo.wang@amd.com
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/cover.1722989996.git.huibo.wang@amd.com
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
Melody Wang (8):
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: Handle HLT intercept when Restricted Injection is enabled.
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 | 3 +
arch/x86/include/asm/kvm_host.h | 4 +-
arch/x86/include/asm/sev-common.h | 1 +
arch/x86/include/asm/svm.h | 35 +++
arch/x86/include/uapi/asm/svm.h | 6 +
arch/x86/kvm/i8259.c | 45 +++-
arch/x86/kvm/irq.c | 51 +++-
arch/x86/kvm/irq.h | 3 +
arch/x86/kvm/lapic.c | 46 +++-
arch/x86/kvm/lapic.h | 3 +
arch/x86/kvm/svm/sev.c | 403 ++++++++++++++++++++++++++++-
arch/x86/kvm/svm/svm.c | 96 ++++++-
arch/x86/kvm/svm/svm.h | 35 +++
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 | 9 +
18 files changed, 735 insertions(+), 22 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v5 1/8] x86/sev: Define the #HV doorbell page structure
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
@ 2026-11-09 3:18 ` Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 2/8] KVM: SVM: Add support for the SEV-SNP #HV doorbell page NAE event Melody Wang
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang, Thomas Lendacky
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.
Co-developed-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
arch/x86/include/asm/svm.h | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index aa63431ba92c..3b77eaac3d07 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -253,6 +253,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];
+} __packed;
+
+struct hvdb {
+ struct hvdb_events events;
+
+ /* Remainder of the page is for software use */
+ u8 reserved[PAGE_SIZE - sizeof(struct hvdb_events)];
+} __packed;
/* AVIC */
#define AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK (0xFFULL)
@@ -569,6 +602,8 @@ static inline void __unused_size_checks(void)
BUILD_BUG_ON(sizeof(struct vmcb_control_area) != EXPECTED_VMCB_CONTROL_AREA_SIZE);
BUILD_BUG_ON(offsetof(struct vmcb, save) != EXPECTED_VMCB_CONTROL_AREA_SIZE);
BUILD_BUG_ON(sizeof(struct ghcb) != EXPECTED_GHCB_SIZE);
+ BUILD_BUG_ON(sizeof(struct hvdb_events) != 64);
+ BUILD_BUG_ON(sizeof(struct hvdb) != 4096);
/* Check offsets of reserved fields */
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v5 2/8] KVM: SVM: Add support for the SEV-SNP #HV doorbell page NAE event
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 1/8] x86/sev: Define the #HV doorbell page structure Melody Wang
@ 2026-11-09 3:18 ` Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 3/8] KVM: SVM: Inject #HV when Restricted Injection is active Melody Wang
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang, Thomas Lendacky
To support Restricted Injection, the SEV-SNP guest must register
a doorbell page for use with #HV.
The #HV doorbell page NAE event allows the guest to register a doorbell
page. This event consists of four actions: GET_PREFERRED, SET, QUERY,
CLEAR. Implement it per the GHCB specification.
Co-developed-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
arch/x86/include/uapi/asm/svm.h | 5 +++
arch/x86/kvm/svm/sev.c | 68 +++++++++++++++++++++++++++++++++
arch/x86/kvm/svm/svm.c | 3 ++
arch/x86/kvm/svm/svm.h | 2 +
4 files changed, 78 insertions(+)
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index 010a45c9f614..d84a13ac4627 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -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
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 5705723f1f41..da07fe476f3d 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4346,6 +4346,68 @@ static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r
return 1; /* resume guest */
}
+static int sev_snp_hv_doorbell_page(struct vcpu_svm *svm)
+{
+ struct kvm_vcpu *vcpu = &svm->vcpu;
+ struct kvm_host_map hvdb_map;
+ gpa_t hvdb_gpa;
+ u64 request;
+
+ if (!sev_snp_guest(vcpu->kvm))
+ return -EINVAL;
+
+ request = svm->vmcb->control.exit_info_1;
+ hvdb_gpa = svm->vmcb->control.exit_info_2;
+
+ switch (request) {
+ case SVM_VMGEXIT_HVDB_GET_PREFERRED:
+ ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, ~0ULL);
+ break;
+ case SVM_VMGEXIT_HVDB_SET:
+ svm->sev_es.hvdb_gpa = INVALID_PAGE;
+
+ if (!PAGE_ALIGNED(hvdb_gpa)) {
+ vcpu_unimpl(vcpu, "vmgexit: unaligned #HV doorbell page address [%#llx] from guest\n",
+ hvdb_gpa);
+ return -EINVAL;
+ }
+
+ if (!page_address_valid(vcpu, hvdb_gpa)) {
+ vcpu_unimpl(vcpu, "vmgexit: invalid #HV doorbell page address [%#llx] from guest\n",
+ hvdb_gpa);
+ return -EINVAL;
+ }
+
+ /* Map and unmap the GPA just to be sure the GPA is valid */
+ if (kvm_vcpu_map(vcpu, gpa_to_gfn(hvdb_gpa), &hvdb_map)) {
+ vcpu_unimpl(vcpu, "vmgexit: error mapping #HV doorbell page [%#llx] from guest\n",
+ hvdb_gpa);
+ return -EINVAL;
+ }
+ kvm_vcpu_unmap(vcpu, &hvdb_map);
+
+ svm->sev_es.hvdb_gpa = hvdb_gpa;
+ fallthrough;
+ case SVM_VMGEXIT_HVDB_QUERY:
+ if (svm->sev_es.hvdb_gpa == INVALID_PAGE)
+ ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, 0);
+ else
+ ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, svm->sev_es.hvdb_gpa);
+ break;
+ case SVM_VMGEXIT_HVDB_CLEAR:
+ svm->sev_es.hvdb_gpa = INVALID_PAGE;
+ break;
+ default:
+ svm->sev_es.hvdb_gpa = INVALID_PAGE;
+
+ vcpu_unimpl(vcpu, "vmgexit: invalid #HV doorbell page request [%#llx] from guest\n",
+ request);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
{
struct vmcb_control_area *control = &svm->vmcb->control;
@@ -4497,6 +4559,7 @@ static bool is_snp_only_vmgexit(u64 exit_code)
case SVM_VMGEXIT_GUEST_REQUEST:
case SVM_VMGEXIT_EXT_GUEST_REQUEST:
case SVM_VMGEXIT_PSC:
+ case SVM_VMGEXIT_HVDB_PAGE:
return true;
default:
return false;
@@ -4680,6 +4743,11 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
return snp_handle_ext_guest_req(svm, control->exit_info_1,
control->exit_info_2);
+ case SVM_VMGEXIT_HVDB_PAGE:
+ if (sev_snp_hv_doorbell_page(svm))
+ svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
+
+ return 1;
case SVM_VMGEXIT_UNSUPPORTED_EVENT:
/*
* Note, the _guest_ is reporting an unsupported #VC, i.e. this
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7d59d301e1e5..da7abf0c39fe 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -1291,6 +1291,9 @@ static void __svm_vcpu_reset(struct kvm_vcpu *vcpu)
svm->nmi_masked = false;
svm->awaiting_iret_completion = false;
+
+ if (is_sev_snp_guest(vcpu))
+ svm->sev_es.hvdb_gpa = INVALID_PAGE;
}
static void svm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index e958943b8162..f212f2e68d87 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -276,6 +276,8 @@ struct vcpu_sev_es_state {
gpa_t snp_guest_vmsa_gpa;
bool snp_ap_waiting_for_reset;
bool snp_has_guest_vmsa;
+
+ gpa_t hvdb_gpa;
};
struct vcpu_svm {
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v5 3/8] KVM: SVM: Inject #HV when Restricted Injection is active
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 1/8] x86/sev: Define the #HV doorbell page structure Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 2/8] KVM: SVM: Add support for the SEV-SNP #HV doorbell page NAE event Melody Wang
@ 2026-11-09 3:18 ` Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 4/8] KVM: SVM: Inject NMIs " Melody Wang
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang, Thomas Lendacky
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 <thomas.lendacky@amd.com>
Signed-off-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 2 +-
arch/x86/kvm/i8259.c | 45 +++--
arch/x86/kvm/irq.c | 51 +++++-
arch/x86/kvm/irq.h | 3 +
arch/x86/kvm/lapic.c | 34 +++-
arch/x86/kvm/lapic.h | 1 +
arch/x86/kvm/svm/sev.c | 253 +++++++++++++++++++++++++++++
arch/x86/kvm/svm/svm.c | 24 ++-
arch/x86/kvm/svm/svm.h | 28 ++++
10 files changed, 421 insertions(+), 21 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index e213c9ae3e30..2074c2050c55 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -96,6 +96,7 @@ KVM_X86_OP_OPTIONAL_RET0(set_tss_addr)
KVM_X86_OP_OPTIONAL_RET0(set_identity_map_addr)
KVM_X86_OP_OPTIONAL_RET0(get_mt_mask)
KVM_X86_OP_OPTIONAL_RET0(tdp_has_smep)
+KVM_X86_OP_OPTIONAL_RET0(is_noack_required)
KVM_X86_OP(load_mmu_pgd)
KVM_X86_OP_OPTIONAL_RET0(set_external_spte)
KVM_X86_OP_OPTIONAL(free_external_spt)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 3af7395c2430..4de443140c53 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1618,7 +1618,7 @@ struct kvm_x86_ops {
void (*enable_nmi_window)(struct kvm_vcpu *vcpu);
void (*enable_irq_window)(struct kvm_vcpu *vcpu);
void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr);
-
+ bool (*is_noack_required)(struct kvm_vcpu *vcpu);
const bool x2apic_icr_is_split;
const unsigned long required_apicv_inhibits;
bool allow_apicv_in_x2apic_without_x2apic_virtualization;
diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c
index 6a942ac622d5..63853a612a83 100644
--- a/arch/x86/kvm/i8259.c
+++ b/arch/x86/kvm/i8259.c
@@ -229,7 +229,7 @@ static inline void pic_intack(struct kvm_kpic_state *s, int irq)
}
-int kvm_pic_read_irq(struct kvm *kvm)
+static int __kvm_pic_read_irq(struct kvm *kvm, unsigned int *pending, bool ack)
{
int irq, irq2, intno;
struct kvm_pic *s = kvm->arch.vpic;
@@ -239,32 +239,57 @@ int kvm_pic_read_irq(struct kvm *kvm)
pic_lock(s);
irq = pic_get_irq(&s->pics[0]);
if (irq >= 0) {
- pic_intack(&s->pics[0], irq);
+ if (ack)
+ pic_intack(&s->pics[0], irq);
if (irq == 2) {
irq2 = pic_get_irq(&s->pics[1]);
- if (irq2 >= 0)
- pic_intack(&s->pics[1], irq2);
- else
+ if (irq2 >= 0) {
+ if (ack)
+ pic_intack(&s->pics[1], irq2);
+ intno = s->pics[1].irq_base + irq2;
+ } else {
/*
* spurious IRQ on slave controller
*/
- irq2 = 7;
- intno = s->pics[1].irq_base + irq2;
+ if (ack) {
+ irq2 = 7;
+ intno = s->pics[1].irq_base + irq2;
+ } else {
+ intno = -1;
+ }
+ }
} else
intno = s->pics[0].irq_base + irq;
} else {
/*
* spurious IRQ on host controller
*/
- irq = 7;
- intno = s->pics[0].irq_base + irq;
+ if (ack) {
+ irq = 7;
+ intno = s->pics[0].irq_base + irq;
+ } else {
+ intno = -1;
+ }
}
- pic_update_irq(s);
+
+ if (ack)
+ pic_update_irq(s);
+ else
+ *pending = hweight8(s->pics[0].irr) + hweight8(s->pics[1].irr);
pic_unlock(s);
return intno;
}
+int kvm_pic_read_irq(struct kvm *kvm)
+{
+ return __kvm_pic_read_irq(kvm, 0, true);
+}
+int kvm_pic_read_irq_noack(struct kvm *kvm, unsigned int *pending)
+{
+ return __kvm_pic_read_irq(kvm, pending, false);
+}
+
static void kvm_pic_reset(struct kvm_kpic_state *s)
{
int irq;
diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index 871977288272..061a6fee9261 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -125,7 +125,7 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_cpu_has_interrupt);
* Read pending interrupt(from non-APIC source)
* vector and intack.
*/
-int kvm_cpu_get_extint(struct kvm_vcpu *v)
+static int __kvm_cpu_get_extint(struct kvm_vcpu *v, unsigned int *pending, bool ack)
{
if (!kvm_cpu_has_extint(v)) {
WARN_ON(!lapic_in_kernel(v));
@@ -141,31 +141,68 @@ int kvm_cpu_get_extint(struct kvm_vcpu *v)
#endif
#ifdef CONFIG_KVM_IOAPIC
- if (pic_in_kernel(v->kvm))
- return kvm_pic_read_irq(v->kvm); /* PIC */
+ if (pic_in_kernel(v->kvm)) {
+ if (ack)
+ return kvm_pic_read_irq(v->kvm); /* PIC */
+ else
+ return kvm_pic_read_irq_noack(v->kvm, pending); /* PIC */
+ }
#endif
WARN_ON_ONCE(!irqchip_split(v->kvm));
return get_userspace_extint(v);
}
+
+int kvm_cpu_get_extint(struct kvm_vcpu *v)
+{
+ return __kvm_cpu_get_extint(v, 0, true);
+}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_cpu_get_extint);
+int kvm_cpu_get_extint_noack(struct kvm_vcpu *v, unsigned int *pending)
+{
+ return __kvm_cpu_get_extint(v, pending, false);
+}
/*
* Read pending interrupt vector and intack.
*/
-int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
+static int __kvm_cpu_get_interrupt(struct kvm_vcpu *v, unsigned int *pending, bool ack)
{
- int vector = kvm_cpu_get_extint(v);
+ int vector;
+
+ if (ack)
+ vector = kvm_cpu_get_extint(v);
+ else
+ vector = kvm_cpu_get_extint_noack(v, pending);
if (vector != -1)
return vector; /* PIC */
- vector = kvm_apic_has_interrupt(v); /* APIC */
- if (vector != -1)
+ if (ack)
+ vector = kvm_apic_has_interrupt(v); /* APIC */
+ else
+ vector = kvm_apic_has_interrupt_noack(v, pending); /* APIC */
+
+ if (ack && vector != -1)
kvm_apic_ack_interrupt(v, vector);
return vector;
}
+int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
+{
+ bool ack = kvm_x86_call(is_noack_required)(v) ? false : true;
+
+ return __kvm_cpu_get_interrupt(v, 0, ack);
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_cpu_get_interrupt);
+
+int kvm_cpu_get_interrupt_noack(struct kvm_vcpu *v, unsigned int *pending)
+{
+
+ return __kvm_cpu_get_interrupt(v, pending, false);
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_cpu_get_interrupt_noack);
+
void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu)
{
if (lapic_in_kernel(vcpu))
diff --git a/arch/x86/kvm/irq.h b/arch/x86/kvm/irq.h
index a74f03858004..2e917d089f1e 100644
--- a/arch/x86/kvm/irq.h
+++ b/arch/x86/kvm/irq.h
@@ -64,6 +64,7 @@ struct kvm_pic {
int kvm_pic_init(struct kvm *kvm);
void kvm_pic_destroy(struct kvm *kvm);
int kvm_pic_read_irq(struct kvm *kvm);
+int kvm_pic_read_irq_noack(struct kvm *kvm, unsigned int *pending);
void kvm_pic_update_irq(struct kvm_pic *s);
int kvm_pic_set_irq(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
int irq_source_id, int level, bool line_status);
@@ -116,7 +117,9 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v);
int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
int kvm_cpu_has_extint(struct kvm_vcpu *v);
int kvm_cpu_get_extint(struct kvm_vcpu *v);
+int kvm_cpu_get_extint_noack(struct kvm_vcpu *v, unsigned int *pending);
int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
+int kvm_cpu_get_interrupt_noack(struct kvm_vcpu *v, unsigned int *pending);
static inline void kvm_warn_on_lost_irq(struct kvm_vcpu *vcpu)
{
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index e1f3cea14765..037a4599d6f1 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -757,6 +757,23 @@ void kvm_apic_clear_irr(struct kvm_vcpu *vcpu, int vec)
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_apic_clear_irr);
+static unsigned int apic_count_irr(struct kvm_lapic *apic)
+{
+ unsigned int count = 0;
+ u32 reg;
+
+ if (!apic->irr_pending)
+ return 0;
+
+ for (int i = 1; i < 8; i++) {
+ reg = kvm_lapic_get_reg(apic, APIC_IRR + i * 0x10);
+ if (reg)
+ count += hweight32(reg);
+ }
+
+ return count;
+}
+
static void *apic_vector_to_isr(int vec, struct kvm_lapic *apic)
{
return apic->regs + APIC_ISR + APIC_VECTOR_TO_REG_OFFSET(vec);
@@ -3172,7 +3189,7 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu)
return -ENOMEM;
}
-int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)
+static int __kvm_apic_has_interrupt(struct kvm_vcpu *vcpu, unsigned int *pending, bool ack)
{
struct kvm_lapic *apic = vcpu->arch.apic;
u32 ppr;
@@ -3183,11 +3200,26 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)
if (apic->guest_apic_protected)
return -1;
+ if (!ack) {
+ *pending = apic_count_irr(apic);
+ return apic_search_irr(apic);
+
__apic_update_ppr(apic, &ppr);
return apic_has_interrupt_for_ppr(apic, ppr);
}
+
+int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)
+{
+ return __kvm_apic_has_interrupt(vcpu, 0, true);
+}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_apic_has_interrupt);
+int kvm_apic_has_interrupt_noack(struct kvm_vcpu *vcpu, unsigned int *pending)
+{
+ return __kvm_apic_has_interrupt(vcpu, pending, false);
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_apic_has_interrupt_noack);
+
int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu)
{
u32 lvt0 = kvm_lapic_get_reg(vcpu->arch.apic, APIC_LVT0);
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index bd1098c89d99..8530a6333bdd 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -127,6 +127,7 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu);
void kvm_free_lapic(struct kvm_vcpu *vcpu);
int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu);
+int kvm_apic_has_interrupt_noack(struct kvm_vcpu *vcpu, unsigned int *pending);
void kvm_apic_ack_interrupt(struct kvm_vcpu *vcpu, int vector);
int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu);
int kvm_apic_accept_events(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index da07fe476f3d..e4a074df7434 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -36,6 +36,8 @@
#include "svm_ops.h"
#include "cpuid.h"
#include "trace.h"
+#include "lapic.h"
+#include "irq.h"
#define GHCB_VERSION_MAX 2ULL
#define GHCB_VERSION_MIN 1ULL
@@ -4387,6 +4389,8 @@ static int sev_snp_hv_doorbell_page(struct vcpu_svm *svm)
kvm_vcpu_unmap(vcpu, &hvdb_map);
svm->sev_es.hvdb_gpa = hvdb_gpa;
+ svm->sev_es.pending_vector = 0;
+ svm->sev_es.no_eoi_required = 0;
fallthrough;
case SVM_VMGEXIT_HVDB_QUERY:
if (svm->sev_es.hvdb_gpa == INVALID_PAGE)
@@ -4396,6 +4400,8 @@ static int sev_snp_hv_doorbell_page(struct vcpu_svm *svm)
break;
case SVM_VMGEXIT_HVDB_CLEAR:
svm->sev_es.hvdb_gpa = INVALID_PAGE;
+ svm->sev_es.pending_vector = 0;
+ svm->sev_es.no_eoi_required = 0;
break;
default:
svm->sev_es.hvdb_gpa = INVALID_PAGE;
@@ -5474,3 +5480,250 @@ 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;
+}
+
+void sev_snp_handle_ri_ack(struct kvm_vcpu *vcpu, struct hvdb *hvdb, int num_pending)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+
+ /* Guest ack interrupt, move it from irr to isr */
+ if (svm->sev_es.pending_vector && !hvdb->events.vector) {
+ kvm_apic_ack_interrupt(vcpu, svm->sev_es.pending_vector);
+ svm->sev_es.pending_vector = 0;
+ }
+
+ /*
+ * no_eoi_required was set last time, means maybe an EOI is pending for the
+ * interrupt it was just acked by the guest, if the guest cleared
+ * no_eoi_requirement that means guest will not wrmsr EOI but let the hypervisor
+ * send a EOI for it, this way, the VMEXIT which is expensive is saved.
+ */
+ if (svm->sev_es.no_eoi_required &&
+ !hvdb->events.no_eoi_required &&
+ !svm->sev_es.pending_vector) {
+ kvm_lapic_set_eoi(vcpu);
+ svm->sev_es.no_eoi_required = 0;
+ }
+ /*
+ * If there is only one interrupt pending in IRR, no_eoi_required should be set,
+ * and the host should record it. If there is multiple interrupt pending in IRR,
+ * no_eoi_required should not be set since some interrupts need to be delovered
+ * immediately.
+ */
+ if (num_pending == 1) {
+ hvdb->events.no_eoi_required = 1;
+ svm->sev_es.no_eoi_required = 1;
+ } else {
+ hvdb->events.no_eoi_required = 0;
+ svm->sev_es.no_eoi_required = 0;
+ }
+}
+
+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;
+ svm->sev_es.pending_vector = vcpu->arch.interrupt.nr;
+
+ prepare_hv_injection(svm, hvdb);
+
+ unmap_hvdb(vcpu, &hvdb_map);
+}
+
+static void __sev_snp_reinject(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;
+ }
+
+ 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)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+
+ if (!sev_snp_is_rinj_active(vcpu))
+ return false;
+
+ if (svm->sev_es.cancel_flag) {
+ __sev_snp_reinject(vcpu);
+ svm->sev_es.cancel_flag = false;
+ return true;
+ }
+
+ __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.no_further_signal = 0;
+ svm->sev_es.cancel_flag = true;
+
+out:
+ unmap_hvdb(vcpu, &hvdb_map);
+}
+
+bool sev_snp_blocked(enum inject_type type, struct kvm_vcpu *vcpu)
+{
+ struct kvm_host_map hvdb_map;
+ struct hvdb *hvdb;
+ bool blocked;
+ int irq;
+
+ /* 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 */
+ if (type == INJECT_IRQ) {
+ irq = kvm_cpu_get_interrupt(vcpu);
+ if (irq < hvdb->events.vector && hvdb->events.vector)
+ blocked = !!hvdb->events.vector;
+ else
+ blocked = false;
+ }
+
+ unmap_hvdb(vcpu, &hvdb_map);
+
+ return blocked;
+}
+
+void sev_snp_update_ri_status(struct kvm_vcpu *vcpu)
+{
+ struct kvm_host_map hvdb_map;
+ struct hvdb *hvdb;
+ unsigned int num_pending;
+
+ if (!sev_snp_is_rinj_active(vcpu))
+ return;
+
+ hvdb = map_hvdb(vcpu, &hvdb_map);
+ if (!hvdb)
+ return;
+
+ kvm_cpu_get_interrupt_noack(vcpu, &num_pending);
+ sev_snp_handle_ri_ack(vcpu, hvdb, num_pending);
+
+ unmap_hvdb(vcpu, &hvdb_map);
+}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index da7abf0c39fe..601807a8f2fa 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -397,6 +397,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)
@@ -3875,9 +3878,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)
@@ -4052,6 +4057,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)
@@ -4090,6 +4098,14 @@ static int svm_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)
return 1;
}
+static bool svm_is_noack_required(struct kvm_vcpu *vcpu)
+{
+ if (!sev_snp_is_rinj_active(vcpu))
+ return false;
+
+ return true;
+}
+
static void svm_enable_irq_window(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
@@ -4415,6 +4431,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;
@@ -4646,6 +4664,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
svm_complete_interrupts(vcpu);
+ sev_snp_update_ri_status(vcpu);
/*
* Update the cache after completing interrupts to get an accurate
* NextRIP, e.g. when re-injecting a soft interrupt.
@@ -5407,6 +5426,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.enable_nmi_window = svm_enable_nmi_window,
.enable_irq_window = svm_enable_irq_window,
.update_cr8_intercept = svm_update_cr8_intercept,
+ .is_noack_required = svm_is_noack_required,
.x2apic_icr_is_split = true,
.set_virtual_apic_mode = avic_refresh_virtual_apic_mode,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index f212f2e68d87..f0db48d62fcf 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -58,6 +58,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
@@ -278,6 +282,9 @@ struct vcpu_sev_es_state {
bool snp_has_guest_vmsa;
gpa_t hvdb_gpa;
+ u8 pending_vector;
+ u8 no_eoi_required;
+ bool cancel_flag;
};
struct vcpu_svm {
@@ -1019,6 +1026,19 @@ void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
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);
+void sev_snp_update_ri_status(struct kvm_vcpu *vcpu);
+void sev_snp_handle_ri_ack(struct kvm_vcpu *vcpu, struct hvdb *hvdb, int num_pending);
+static inline bool sev_snp_is_rinj_active(struct kvm_vcpu *vcpu)
+{
+ struct kvm_sev_info *sev = to_kvm_sev_info(vcpu->kvm);
+
+ 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)
{
@@ -1046,6 +1066,14 @@ 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 void sev_snp_update_ri_status(struct kvm_vcpu *vcpu)i {};
+static inline void sev_snp_handle_ri_ack(struct kvm_vcpu *vcpu, struct hvdb *hvdb, int num_pending) {};
+static inline bool sev_snp_is_rinj_active(struct kvm_vcpu *vcpu) { return false; }
#endif
/* vmenter.S */
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v5 4/8] KVM: SVM: Inject NMIs when Restricted Injection is active
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
` (2 preceding siblings ...)
2026-11-09 3:18 ` [RFC PATCH v5 3/8] KVM: SVM: Inject #HV when Restricted Injection is active Melody Wang
@ 2026-11-09 3:18 ` Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 5/8] KVM: SVM: Inject MCEs " Melody Wang
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang, Thomas Lendacky
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 NMIs.
Co-developed-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
arch/x86/kvm/svm/sev.c | 14 +++++++++++---
arch/x86/kvm/svm/svm.c | 8 ++++++++
arch/x86/kvm/svm/svm.h | 1 +
3 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index e4a074df7434..f80dfd876d5e 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5565,8 +5565,12 @@ static void __sev_snp_inject(enum inject_type type, struct kvm_vcpu *vcpu)
return;
}
- hvdb->events.vector = vcpu->arch.interrupt.nr;
- svm->sev_es.pending_vector = vcpu->arch.interrupt.nr;
+ if (type == INJECT_NMI) {
+ hvdb->events.nmi = 1;
+ } else if (type == INJECT_IRQ) {
+ hvdb->events.vector = vcpu->arch.interrupt.nr;
+ svm->sev_es.pending_vector = vcpu->arch.interrupt.nr;
+ }
prepare_hv_injection(svm, hvdb);
@@ -5675,6 +5679,8 @@ void sev_snp_cancel_injection(struct kvm_vcpu *vcpu)
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.no_further_signal = 0;
svm->sev_es.cancel_flag = true;
@@ -5696,7 +5702,9 @@ bool sev_snp_blocked(enum inject_type type, struct kvm_vcpu *vcpu)
return true;
/* Indicate interrupts blocked based on guest acknowledgment */
- if (type == INJECT_IRQ) {
+ if (type == INJECT_NMI)
+ blocked = hvdb->events.nmi;
+ else if (type == INJECT_IRQ) {
irq = kvm_cpu_get_interrupt(vcpu);
if (irq < hvdb->events.vector && hvdb->events.vector)
blocked = !!hvdb->events.vector;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 601807a8f2fa..75e86592f1c9 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -3795,6 +3795,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)
@@ -3809,6 +3812,8 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu)
svm->nmi_masked = true;
svm_set_iret_intercept(svm);
}
+
+status:
++vcpu->stat.nmi_injections;
}
@@ -4025,6 +4030,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;
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index f0db48d62fcf..2909f57508de 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -60,6 +60,7 @@ extern struct kvm_x86_ops svm_x86_ops __initdata;
enum inject_type {
INJECT_IRQ,
+ INJECT_NMI,
};
/*
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v5 5/8] KVM: SVM: Inject MCEs when Restricted Injection is active
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
` (3 preceding siblings ...)
2026-11-09 3:18 ` [RFC PATCH v5 4/8] KVM: SVM: Inject NMIs " Melody Wang
@ 2026-11-09 3:18 ` Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 7/8] KVM: SVM: Add support for the SEV-SNP #HV IPI NAE event Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 8/8] KVM: SVM: Enable Restricted Injection for an SEV-SNP guest Melody Wang
6 siblings, 0 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang, Thomas Lendacky
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 <thomas.lendacky@amd.com>
Signed-off-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/svm/sev.c | 17 +++++++++++++++--
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 | 6 ++++++
9 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 2074c2050c55..e3da9fb0b2b6 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -80,6 +80,7 @@ KVM_X86_OP(inject_exception)
KVM_X86_OP(cancel_injection)
KVM_X86_OP(interrupt_allowed)
KVM_X86_OP(nmi_allowed)
+KVM_X86_OP(mce_allowed)
KVM_X86_OP(get_nmi_mask)
KVM_X86_OP(set_nmi_mask)
KVM_X86_OP(enable_nmi_window)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 4de443140c53..fa83477ebcab 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1606,6 +1606,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. */
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index f80dfd876d5e..6a1262c274ff 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5565,7 +5565,9 @@ static void __sev_snp_inject(enum inject_type type, struct kvm_vcpu *vcpu)
return;
}
- if (type == INJECT_NMI) {
+ if (type == INJECT_MCE) {
+ hvdb->events.mce = 1;
+ } else if (type == INJECT_NMI) {
hvdb->events.nmi = 1;
} else if (type == INJECT_IRQ) {
hvdb->events.vector = vcpu->arch.interrupt.nr;
@@ -5602,6 +5604,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,
@@ -5682,6 +5689,10 @@ 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.no_further_signal = 0;
svm->sev_es.cancel_flag = true;
@@ -5702,7 +5713,9 @@ bool sev_snp_blocked(enum inject_type type, struct kvm_vcpu *vcpu)
return true;
/* Indicate interrupts blocked based on guest acknowledgment */
- if (type == INJECT_NMI)
+ if (type == INJECT_MCE)
+ blocked = hvdb->events.mce;
+ else if (type == INJECT_NMI)
blocked = hvdb->events.nmi;
else if (type == INJECT_IRQ) {
irq = kvm_cpu_get_interrupt(vcpu);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 75e86592f1c9..ad99b2b1b07b 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -4114,6 +4114,22 @@ static bool svm_is_noack_required(struct kvm_vcpu *vcpu)
return true;
}
+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);
@@ -5429,6 +5445,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,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 2909f57508de..e19b398a3df5 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -61,6 +61,7 @@ extern struct kvm_x86_ops svm_x86_ops __initdata;
enum inject_type {
INJECT_IRQ,
INJECT_NMI,
+ INJECT_MCE,
};
/*
@@ -826,6 +827,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);
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
index 4c52ab8d0786..e083e2965cd9 100644
--- a/arch/x86/kvm/vmx/main.c
+++ b/arch/x86/kvm/vmx/main.c
@@ -828,6 +828,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))
@@ -962,6 +971,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),
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 612ab07d4100..2b391c282b51 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -5273,6 +5273,11 @@ int vmx_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)
return 1;
}
+int vmx_mce_allowed(struct kvm_vcpu *vcpu)
+{
+ return 1;
+}
+
int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr)
{
void __user *ret;
diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h
index 054fd14bb2e1..f68316638f21 100644
--- a/arch/x86/kvm/vmx/x86_ops.h
+++ b/arch/x86/kvm/vmx/x86_ops.h
@@ -93,6 +93,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);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 79468ddfe473..1492a06a6935 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7673,6 +7673,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;
+ }
+
kvm_inject_exception(vcpu);
vcpu->arch.exception.pending = false;
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v5 7/8] KVM: SVM: Add support for the SEV-SNP #HV IPI NAE event
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
` (4 preceding siblings ...)
2026-11-09 3:18 ` [RFC PATCH v5 5/8] KVM: SVM: Inject MCEs " Melody Wang
@ 2026-11-09 3:18 ` Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 8/8] KVM: SVM: Enable Restricted Injection for an SEV-SNP guest Melody Wang
6 siblings, 0 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang, Thomas Lendacky
The #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.
Co-developed-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
arch/x86/include/uapi/asm/svm.h | 1 +
arch/x86/kvm/lapic.c | 12 ++++++++++++
arch/x86/kvm/lapic.h | 2 ++
arch/x86/kvm/svm/sev.c | 21 +++++++++++++++++++++
4 files changed, 36 insertions(+)
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index d84a13ac4627..1c0165e9db16 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -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
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 037a4599d6f1..3a83bdd13b5a 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -3673,3 +3673,15 @@ void kvm_lapic_exit(void)
static_key_deferred_flush(&apic_sw_disabled);
WARN_ON(static_branch_unlikely(&apic_sw_disabled.key));
}
+
+int kvm_xapic_x2apic_send_ipi(struct kvm_vcpu *vcpu, u64 data)
+{
+ struct kvm_lapic *apic = vcpu->arch.apic;
+
+ if (!kvm_lapic_enabled(vcpu))
+ return 1;
+
+ kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
+ return 0;
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_xapic_x2apic_send_ipi);
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 8530a6333bdd..e5e6730bfcca 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -194,6 +194,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_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu);
static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 9aafa6167ae6..b7c2993f8c63 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4414,6 +4414,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;
@@ -4566,6 +4582,7 @@ static bool is_snp_only_vmgexit(u64 exit_code)
case SVM_VMGEXIT_EXT_GUEST_REQUEST:
case SVM_VMGEXIT_PSC:
case SVM_VMGEXIT_HVDB_PAGE:
+ case SVM_VMGEXIT_HV_IPI:
return true;
default:
return false;
@@ -4754,6 +4771,10 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
return 1;
+ case SVM_VMGEXIT_HV_IPI:
+ if (sev_snp_hv_ipi(svm))
+ svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
+ return 1;
case SVM_VMGEXIT_UNSUPPORTED_EVENT:
/*
* Note, the _guest_ is reporting an unsupported #VC, i.e. this
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v5 8/8] KVM: SVM: Enable Restricted Injection for an SEV-SNP guest
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
` (5 preceding siblings ...)
2026-11-09 3:18 ` [RFC PATCH v5 7/8] KVM: SVM: Add support for the SEV-SNP #HV IPI NAE event Melody Wang
@ 2026-11-09 3:18 ` Melody Wang
6 siblings, 0 replies; 8+ messages in thread
From: Melody Wang @ 2026-11-09 3:18 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: LKML, kvm, Joerg Roedel, x86, Melody Wang, Thomas Lendacky
Enable 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.
Co-developed-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Thomas Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/sev-common.h | 1 +
arch/x86/kvm/svm/sev.c | 21 ++++++++++++++++++++-
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index f70ee74b5f92..c491c355fabd 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -466,6 +466,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 */
diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
index 01a6e4dbe423..ee17a3541b55 100644
--- a/arch/x86/include/asm/sev-common.h
+++ b/arch/x86/include/asm/sev-common.h
@@ -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)
/*
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index b7c2993f8c63..a7b290d3fd42 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -42,7 +42,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
@@ -3261,6 +3263,9 @@ 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_supported_vmsa_features |= SVM_SEV_FEAT_RESTRICTED_INJECTION;
}
void sev_hardware_unsetup(void)
@@ -4862,10 +4867,22 @@ 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;
+ svm->vmcb->control.int_ctl &= ~V_GIF_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;
@@ -4932,6 +4949,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 (sev_snp_guest(vcpu->kvm))
+ sev_snp_init_vmcb(svm);
}
void sev_init_vmcb(struct vcpu_svm *svm, bool init_event)
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-11-09 3:20 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-09 3:18 [RFC PATCH v5 0/8] Add SEV-SNP restricted injection hypervisor support Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 1/8] x86/sev: Define the #HV doorbell page structure Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 2/8] KVM: SVM: Add support for the SEV-SNP #HV doorbell page NAE event Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 3/8] KVM: SVM: Inject #HV when Restricted Injection is active Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 4/8] KVM: SVM: Inject NMIs " Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 5/8] KVM: SVM: Inject MCEs " Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 7/8] KVM: SVM: Add support for the SEV-SNP #HV IPI NAE event Melody Wang
2026-11-09 3:18 ` [RFC PATCH v5 8/8] KVM: SVM: Enable Restricted Injection for an SEV-SNP guest Melody Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®