The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support
@ 2026-07-30  1:48 Melody Wang
  2026-07-30  1:48 ` [PATCH 1/7] x86/sev: Add support for Alternate Injection Melody Wang
                   ` (6 more replies)
  0 siblings, 7 replies; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang

Alternate Injection is a method to provide secure interrupt delivery for
SEV-SNP guests against malicious injection attacks. By handing over the
control of the interrupt injection to the guest itself, the security is
applied. Alternate Injection use Secure VM Service Module (SVSM), and APIC
emulation in the SVSM to secure interrupt delivery.

This is the guest side patches. The patch set includes the following:
1. Add support for enabling Alternate Injection.
2. Add support for the SVSM APIC protocol which uses a subset of the
   X2APIC MSRs.
3. Add support to allow the guest OS to request Alternate
   Injection.

Thanks,
Melody

Melody Wang (7):
  x86/sev: Add support for Alternate Injection
  x86/apic: Add an SVSM APIC driver
  x86/sev: Allow the guest to configure interrupt vectors for the
    hypervisor
  x86/sev: Route unsupported APIC register accesses to the hypervisor
    APIC emulation
  x86/sev: Add a function to contain all SEV-specific setup operations
  x86/sev: Register the guest with the SVSM APIC protocol
  x86/sev: Indicate that Alternate Injection is supported in the guest

 arch/x86/Kconfig                        |  14 ++
 arch/x86/boot/compressed/sev.c          |  37 +++-
 arch/x86/boot/compressed/sev.h          |   6 +
 arch/x86/boot/startup/sev-startup.c     |  15 ++
 arch/x86/boot/startup/sme.c             |   3 +
 arch/x86/coco/core.c                    |   3 +
 arch/x86/coco/sev/core.c                |  14 +-
 arch/x86/coco/sev/svsm.c                |   6 +
 arch/x86/include/asm/cpufeatures.h      |   1 +
 arch/x86/include/asm/msr-index.h        |   4 +-
 arch/x86/include/asm/sev-common.h       |   1 +
 arch/x86/include/asm/sev.h              |  29 ++-
 arch/x86/kernel/apic/Makefile           |   1 +
 arch/x86/kernel/apic/svsm_apic.c        | 255 ++++++++++++++++++++++++
 arch/x86/kernel/apic/x2apic_savic.c     |   8 +-
 drivers/firmware/efi/libstub/x86-stub.c |  20 +-
 include/linux/cc_platform.h             |   8 +
 17 files changed, 392 insertions(+), 33 deletions(-)
 create mode 100644 arch/x86/kernel/apic/svsm_apic.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/7] x86/sev: Add support for Alternate Injection
  2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
@ 2026-07-30  1:48 ` Melody Wang
  2026-07-31 15:27   ` Tom Lendacky
  2026-07-30  1:48 ` [PATCH 2/7] x86/apic: Add an SVSM APIC driver Melody Wang
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang

The Alternate Injection feature is a method to protect an AMD
confidential computing guest from malicious injection attacks. It allows
the guest to control the interrupt injection.

When this feature is enabled, interrupts are injected with the help of
an agent called a Secure VM Service Module (SVSM) which executes in the
security realm of the guest and uses Restricted Injection as the sole
method to receive interrupts from the hypervisor.

Add support for Alternate Injection enablement.

Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
 arch/x86/Kconfig                   | 14 ++++++++++++++
 arch/x86/boot/compressed/sev.c     |  2 +-
 arch/x86/coco/core.c               |  3 +++
 arch/x86/coco/sev/core.c           |  2 +-
 arch/x86/include/asm/cpufeatures.h |  1 +
 arch/x86/include/asm/msr-index.h   |  4 ++--
 include/linux/cc_platform.h        |  8 ++++++++
 7 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index e725b439d0a2..7ec5d2c4a8f0 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -493,6 +493,20 @@ config AMD_SECURE_AVIC
 
 	  If you don't know what to do here, say N.
 
+config AMD_ALTERNATE_INJ
+	bool "Support Alternate Injection"
+	depends on X86_X2APIC && AMD_MEM_ENCRYPT
+	help
+	  Enable AMD Alternate Injection support for SNP guests.
+
+	  The Alternate Injection feature of SEV-SNP enhances the security of
+	  a confidential VM by preventing the untrusted host from presenting
+	  unexpected interrupts or exceptions, while still preserving the
+	  standard interrupt dispatch semantics inherent to the x86
+	  architecture.
+
+	  If you don't know what to do here, say N.
+
 config X86_POSTED_MSI
 	bool "Enable MSI and MSI-x delivery by posted interrupts"
 	depends on X86_64 && IRQ_REMAP
diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index c6512f2ea31e..fc2029746c50 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -179,7 +179,7 @@ bool sev_es_check_ghcb_fault(unsigned long address)
 #define SNP_FEATURES_IMPL_REQ	(MSR_AMD64_SNP_VTOM |			\
 				 MSR_AMD64_SNP_REFLECT_VC |		\
 				 MSR_AMD64_SNP_RESTRICTED_INJ |		\
-				 MSR_AMD64_SNP_ALT_INJ |		\
+				 MSR_AMD64_SNP_ALTERNATE_INJ |		\
 				 MSR_AMD64_SNP_DEBUG_SWAP |		\
 				 MSR_AMD64_SNP_VMPL_SSS |		\
 				 MSR_AMD64_SNP_SECURE_TSC |		\
diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c
index 989ca9f72ba3..aabda3ddc0e2 100644
--- a/arch/x86/coco/core.c
+++ b/arch/x86/coco/core.c
@@ -107,6 +107,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
 	case CC_ATTR_SNP_SECURE_AVIC:
 		return sev_status & MSR_AMD64_SNP_SECURE_AVIC;
 
+	case CC_ATTR_SNP_ALTERNATE_INJECTION:
+		return sev_status & MSR_AMD64_SNP_ALTERNATE_INJ;
+
 	default:
 		return false;
 	}
diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
index ecd77d3217f3..197119807230 100644
--- a/arch/x86/coco/sev/core.c
+++ b/arch/x86/coco/sev/core.c
@@ -78,7 +78,7 @@ static const char * const sev_status_feat_names[] = {
 	[MSR_AMD64_SNP_VTOM_BIT]		= "vTom",
 	[MSR_AMD64_SNP_REFLECT_VC_BIT]		= "ReflectVC",
 	[MSR_AMD64_SNP_RESTRICTED_INJ_BIT]	= "RI",
-	[MSR_AMD64_SNP_ALT_INJ_BIT]		= "AI",
+	[MSR_AMD64_SNP_ALTERNATE_INJ_BIT]	= "AI",
 	[MSR_AMD64_SNP_DEBUG_SWAP_BIT]		= "DebugSwap",
 	[MSR_AMD64_SNP_PREVENT_HOST_IBS_BIT]	= "NoHostIBS",
 	[MSR_AMD64_SNP_BTB_ISOLATION_BIT]	= "BTBIsol",
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 3d0940a3b9f3..70dedbf0969f 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -454,6 +454,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_ALTERNATE_INJECTION	(19*32+13) /* SEV Alternate 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/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e927..e4eab2210518 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -721,8 +721,8 @@
 #define MSR_AMD64_SNP_REFLECT_VC	BIT_ULL(MSR_AMD64_SNP_REFLECT_VC_BIT)
 #define MSR_AMD64_SNP_RESTRICTED_INJ_BIT 5
 #define MSR_AMD64_SNP_RESTRICTED_INJ	BIT_ULL(MSR_AMD64_SNP_RESTRICTED_INJ_BIT)
-#define MSR_AMD64_SNP_ALT_INJ_BIT	6
-#define MSR_AMD64_SNP_ALT_INJ		BIT_ULL(MSR_AMD64_SNP_ALT_INJ_BIT)
+#define MSR_AMD64_SNP_ALTERNATE_INJ_BIT	6
+#define MSR_AMD64_SNP_ALTERNATE_INJ	BIT_ULL(MSR_AMD64_SNP_ALTERNATE_INJ_BIT)
 #define MSR_AMD64_SNP_DEBUG_SWAP_BIT	7
 #define MSR_AMD64_SNP_DEBUG_SWAP	BIT_ULL(MSR_AMD64_SNP_DEBUG_SWAP_BIT)
 #define MSR_AMD64_SNP_PREVENT_HOST_IBS_BIT 8
diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
index 559353ad64ac..90ef67e85cd3 100644
--- a/include/linux/cc_platform.h
+++ b/include/linux/cc_platform.h
@@ -104,6 +104,14 @@ enum cc_attr {
 	 * to run SEV-SNP guests with full Secure AVIC capabilities.
 	 */
 	CC_ATTR_SNP_SECURE_AVIC,
+
+	/**
+	 * @CC_ATTR_SNP_ALTERNATE_INJECTION: AMD Alternate Injection enabled on the host.
+	 *
+	 * The host kernel is running with the necessary features
+	 * needed to run Alternate Injection enabled guests.
+	 */
+	CC_ATTR_SNP_ALTERNATE_INJECTION,
 };
 
 #ifdef CONFIG_ARCH_HAS_CC_PLATFORM
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/7] x86/apic: Add an SVSM APIC driver
  2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
  2026-07-30  1:48 ` [PATCH 1/7] x86/sev: Add support for Alternate Injection Melody Wang
@ 2026-07-30  1:48 ` Melody Wang
  2026-07-31 17:39   ` Tom Lendacky
  2026-07-30  1:48 ` [PATCH 3/7] x86/sev: Allow the guest to configure interrupt vectors for the hypervisor Melody Wang
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang

The SVSM specification mandates the use of a SVSM APIC protocol instead
of any other APICs when an Alternate Injection guest talks to the SVSM.

Add such a SVSM APIC driver (which implements a subset of an X2APIC),
for the APIC emulation supported by the SVSM.

Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
 arch/x86/coco/sev/svsm.c         |   6 +
 arch/x86/include/asm/sev.h       |   9 ++
 arch/x86/kernel/apic/Makefile    |   1 +
 arch/x86/kernel/apic/svsm_apic.c | 252 +++++++++++++++++++++++++++++++
 4 files changed, 268 insertions(+)
 create mode 100644 arch/x86/kernel/apic/svsm_apic.c

diff --git a/arch/x86/coco/sev/svsm.c b/arch/x86/coco/sev/svsm.c
index 916d62cd17dc..e2d3bc3c26eb 100644
--- a/arch/x86/coco/sev/svsm.c
+++ b/arch/x86/coco/sev/svsm.c
@@ -354,3 +354,9 @@ bool snp_svsm_vtpm_probe(void)
 	/* Check platform commands contains TPM_SEND_COMMAND - platform command 8 */
 	return call.rcx_out & BIT_ULL(8);
 }
+
+int svsm_do_call(struct svsm_call *call)
+{
+	call->caa = svsm_get_caa();
+	return svsm_perform_call_protocol(call);
+}
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 594cfa19cbd4..f958f78e1db8 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -435,6 +435,13 @@ struct svsm_call {
 #define SVSM_VTPM_QUERY			0
 #define SVSM_VTPM_CMD			1
 
+#define SVSM_APIC_CALL(x)		((3ULL << 32) | (x))
+#define SVSM_APIC_QUERY_FEATURES	0
+#define SVSM_APIC_CONFIG_EMULATION	1
+#define SVSM_APIC_READ_REGISTER		2
+#define SVSM_APIC_WRITE_REGISTER	3
+#define SVSM_APIC_CONFIG_VECTOR		4
+
 #ifdef CONFIG_AMD_MEM_ENCRYPT
 
 extern u8 snp_vmpl;
@@ -519,6 +526,7 @@ u64 sev_get_status(void);
 void sev_show_status(void);
 int prepare_pte_enc(struct pte_enc_desc *d);
 void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot);
+int svsm_do_call(struct svsm_call *call);
 void snp_kexec_finish(void);
 void snp_kexec_begin(void);
 
@@ -611,6 +619,7 @@ static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long a
 static inline void setup_ghcb(void) { }
 static inline void __init
 early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned long npages) { }
+static inline int svsm_do_call(struct svsm_call *call) { return 0; }
 static inline void __init
 early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, unsigned long npages) { }
 static inline void snp_set_memory_shared(unsigned long vaddr, unsigned long npages) { }
diff --git a/arch/x86/kernel/apic/Makefile b/arch/x86/kernel/apic/Makefile
index 581db89477f9..d23b1c4c0e14 100644
--- a/arch/x86/kernel/apic/Makefile
+++ b/arch/x86/kernel/apic/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_SMP)		+= ipi.o
 ifeq ($(CONFIG_X86_64),y)
 # APIC probe will depend on the listing order here
 obj-$(CONFIG_X86_NUMACHIP)	+= apic_numachip.o
+obj-$(CONFIG_AMD_ALTERNATE_INJ)	+= svsm_apic.o
 obj-$(CONFIG_X86_UV)		+= x2apic_uv_x.o
 obj-$(CONFIG_AMD_SECURE_AVIC)	+= x2apic_savic.o
 obj-$(CONFIG_X86_X2APIC)	+= x2apic_phys.o
diff --git a/arch/x86/kernel/apic/svsm_apic.c b/arch/x86/kernel/apic/svsm_apic.c
new file mode 100644
index 000000000000..7040a1ca8b55
--- /dev/null
+++ b/arch/x86/kernel/apic/svsm_apic.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD Alternate Injection Support (SEV-SNP Guests)
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ *
+ * Author: Melody Wang <huibo.wang@amd.com>
+ */
+
+#include <linux/cpumask.h>
+#include <linux/cc_platform.h>
+
+#include <asm/apic.h>
+#include <asm/sev.h>
+
+#include "local.h"
+
+extern u8 snp_vmpl;
+
+static int svsm_apic_probe(void)
+{
+	if (!cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) || !snp_vmpl)
+		return 0;
+
+	/* Alternate Injection and Secure AVIC are mutually exclusive */
+	if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
+		return 0;
+
+	if (!x2apic_mode) {
+		pr_err("Alternate Injection in non x2APIC mode impossible. Terminating.\n");
+		sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+	}
+
+	pr_info("Alternate Injection SVSM APIC enabled\n");
+
+	return 1;
+}
+
+static int svsm_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+	return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) && snp_vmpl;
+}
+
+static void svsm_apic_msr_write(u32 reg, u32 v)
+{
+	u32 msr = APIC_BASE_MSR + (reg >> 4);
+	struct svsm_call call = {};
+	int ret;
+
+	switch (reg) {
+	case APIC_ID:
+	case APIC_TASKPRI:
+	case APIC_PROCPRI:
+	case APIC_EOI:
+	case APIC_ISR ... APIC_ISR + 0x70:
+	case APIC_TMR ... APIC_TMR + 0x70:
+	case APIC_IRR ... APIC_IRR + 0x70:
+	case APIC_ICR:
+	case APIC_SELF_IPI:
+		call.rax = SVSM_APIC_CALL(SVSM_APIC_WRITE_REGISTER);
+		call.rcx = msr;
+		call.rdx = v;
+
+		ret = svsm_do_call(&call);
+		if (ret) {
+			pr_err("SVSM_APIC_WRITE_REGISTER: 0x%x, error: %d\n", reg, ret);
+			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+		}
+		break;
+	default:
+		pr_err("SVSM_APIC_WRITE_REGISTER 0x%x not supported\n", reg);
+		break;
+	}
+}
+
+static u32 svsm_apic_msr_read(u32 reg)
+{
+	u32 msr = APIC_BASE_MSR + (reg >> 4);
+	struct svsm_call call = {};
+	int ret;
+
+	switch (reg) {
+	case APIC_ID:
+	case APIC_TASKPRI:
+	case APIC_PROCPRI:
+	case APIC_EOI:
+	case APIC_ISR ... APIC_ISR + 0x70:
+	case APIC_TMR ... APIC_TMR + 0x70:
+	case APIC_IRR ... APIC_IRR + 0x70:
+	case APIC_ICR:
+	case APIC_SELF_IPI:
+		call.rax = SVSM_APIC_CALL(SVSM_APIC_READ_REGISTER);
+		call.rcx = msr;
+
+		ret = svsm_do_call(&call);
+		if (ret) {
+			pr_err("SVSM_APIC_READ_REGISTER: 0x%x, error: %d\n", reg, ret);
+			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+		}
+		break;
+	default:
+		pr_err("SVSM_APIC_READ_REGISTER: 0x%x not supported\n", reg);
+		return 0;
+	}
+
+	return call.rdx_out;
+}
+
+static inline void svsm_apic_msr_eoi(void)
+{
+	svsm_apic_msr_write(APIC_EOI, APIC_EOI_ACK);
+}
+
+static inline u64 svsm_apic_icr_read(void)
+{
+	u32 reg;
+	struct svsm_call call = {};
+	int ret;
+
+	reg = APIC_ICR;
+
+	call.rax = SVSM_APIC_CALL(SVSM_APIC_READ_REGISTER);
+	call.rcx = APIC_BASE_MSR + (reg >> 4);
+
+	ret = svsm_do_call(&call);
+	if (ret) {
+		pr_err("svsm_apic_icr_read error: %d\n", ret);
+		sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+	}
+
+	return call.rdx_out;
+}
+
+static void svsm_apic_icr_write(u32 low, u32 id)
+{
+	u64 icr_data;
+	u32 reg;
+	struct svsm_call call = {};
+	int ret;
+
+	reg = APIC_ICR;
+	icr_data = ((u64)id) << 32 | low;
+
+	call.rax = SVSM_APIC_CALL(SVSM_APIC_WRITE_REGISTER);
+	call.rcx = APIC_BASE_MSR + (reg >> 4);
+	call.rdx = icr_data;
+
+	ret = svsm_do_call(&call);
+	if (ret) {
+		pr_err("svsm_apic_icr_write error: %d\n", ret);
+		sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+	}
+}
+
+static void __svsm_apic_send_IPI_dest(unsigned int apicid, int vector, unsigned int dest)
+{
+	svsm_apic_icr_write(__prepare_ICR(0, vector, dest), apicid);
+}
+
+static void svsm_apic_send_IPI(int cpu, int vector)
+{
+	u32 dest = per_cpu(x86_cpu_to_apicid, cpu);
+
+	__svsm_apic_send_IPI_dest(dest, vector, APIC_DEST_PHYSICAL);
+}
+
+static void __svsm_apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest)
+{
+	unsigned long query_cpu;
+	unsigned long this_cpu;
+
+	guard(irqsave)();
+
+	this_cpu = smp_processor_id();
+	for_each_cpu(query_cpu, mask) {
+		if (apic_dest == APIC_DEST_ALLBUT && this_cpu == query_cpu)
+			continue;
+
+		__svsm_apic_send_IPI_dest(per_cpu(x86_cpu_to_apicid, query_cpu),
+					  vector, APIC_DEST_PHYSICAL);
+	}
+}
+
+static void svsm_apic_send_IPI_mask(const struct cpumask *mask, int vector)
+{
+	__svsm_apic_send_IPI_mask(mask, vector, APIC_DEST_ALLINC);
+}
+
+static void svsm_apic_send_IPI_mask_allbutself(const struct cpumask *mask, int vector)
+{
+	__svsm_apic_send_IPI_mask(mask, vector, APIC_DEST_ALLBUT);
+}
+
+static void __svsm_apic_send_IPI_shorthand(int vector, u32 which)
+{
+	svsm_apic_icr_write(__prepare_ICR(which, vector, 0), 0);
+}
+
+static void svsm_apic_send_IPI_allbutself(int vector)
+{
+	__svsm_apic_send_IPI_shorthand(vector, APIC_DEST_ALLBUT);
+}
+
+static void svsm_apic_send_IPI_all(int vector)
+{
+	__svsm_apic_send_IPI_shorthand(vector, APIC_DEST_ALLINC);
+}
+
+static void svsm_apic_send_IPI_self(int vector)
+{
+	__svsm_apic_send_IPI_shorthand(vector, APIC_DEST_SELF);
+}
+
+static u32 svsm_apic_get_apic_id(u32 id)
+{
+	return id;
+}
+
+static struct apic svsm_apic __ro_after_init = {
+
+	.name				= "svsm apic",
+	.probe				= svsm_apic_probe,
+	.acpi_madt_oem_check		= svsm_acpi_madt_oem_check,
+
+	.dest_mode_logical		= false,
+
+	.disable_esr			= 0,
+
+	.cpu_present_to_apicid		= default_cpu_present_to_apicid,
+
+	.max_apic_id			= UINT_MAX,
+	.x2apic_set_max_apicid		= true,
+	.get_apic_id			= svsm_apic_get_apic_id,
+
+	.calc_dest_apicid		= apic_default_calc_apicid,
+
+	.send_IPI			= svsm_apic_send_IPI,
+	.send_IPI_mask			= svsm_apic_send_IPI_mask,
+	.send_IPI_mask_allbutself	= svsm_apic_send_IPI_mask_allbutself,
+	.send_IPI_allbutself		= svsm_apic_send_IPI_allbutself,
+	.send_IPI_all			= svsm_apic_send_IPI_all,
+	.send_IPI_self			= svsm_apic_send_IPI_self,
+	.nmi_to_offline_cpu		= true,
+
+	.read				= svsm_apic_msr_read,
+	.write				= svsm_apic_msr_write,
+	.eoi				= svsm_apic_msr_eoi,
+	.icr_read			= svsm_apic_icr_read,
+	.icr_write			= svsm_apic_icr_write,
+
+};
+apic_driver(svsm_apic);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 3/7] x86/sev: Allow the guest to configure interrupt vectors for the hypervisor
  2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
  2026-07-30  1:48 ` [PATCH 1/7] x86/sev: Add support for Alternate Injection Melody Wang
  2026-07-30  1:48 ` [PATCH 2/7] x86/apic: Add an SVSM APIC driver Melody Wang
@ 2026-07-30  1:48 ` Melody Wang
  2026-07-31 17:52   ` Tom Lendacky
  2026-07-30  1:48 ` [PATCH 4/7] x86/sev: Route unsupported APIC register accesses to the hypervisor APIC emulation Melody Wang
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang

The SVSM APIC protocol supports 5 API calls. SVSM_APIC_CONFIGURE_VECTOR
(shortened to SVSM_APIC_CONFIG_VECTOR for brevity), call 4, provides for
the guest to configure an interrupt vector which the guest allows and
the hypervisor can use to signal interrupts for it.

Implement this call, and make the default interrupt setting permissive
when detecting an SVSM.

Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
 arch/x86/boot/startup/sev-startup.c | 15 +++++++++++++++
 arch/x86/boot/startup/sme.c         |  3 +++
 arch/x86/include/asm/sev.h          |  9 +++++++++
 3 files changed, 27 insertions(+)

diff --git a/arch/x86/boot/startup/sev-startup.c b/arch/x86/boot/startup/sev-startup.c
index 789e99d38d17..8dea0548e004 100644
--- a/arch/x86/boot/startup/sev-startup.c
+++ b/arch/x86/boot/startup/sev-startup.c
@@ -191,6 +191,21 @@ static void __init svsm_setup(struct cc_blob_sev_info *cc_info)
 	boot_svsm_caa_pa = pa;
 }
 
+/* Configure the APIC IRQ vectors with Alternate Injection */
+void __init svsm_config_vectors(void)
+{
+	struct svsm_call call = {};
+
+	if (sev_status & MSR_AMD64_SNP_ALTERNATE_INJ) {
+		call.caa = rip_rel_ptr(&boot_svsm_ca_page);
+		call.rax = SVSM_APIC_CALL(SVSM_APIC_CONFIG_VECTOR);
+		call.rcx = SVSM_IRQ_ENABLE_ALL << 8;
+
+		if (svsm_call_msr_protocol(&call))
+			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+	}
+}
+
 bool __init snp_init(struct boot_params *bp)
 {
 	struct cc_blob_sev_info *cc_info;
diff --git a/arch/x86/boot/startup/sme.c b/arch/x86/boot/startup/sme.c
index c07a2c381ed1..664e7a549b34 100644
--- a/arch/x86/boot/startup/sme.c
+++ b/arch/x86/boot/startup/sme.c
@@ -566,6 +566,9 @@ void __init sme_enable(struct boot_params *bp)
 	physical_mask	&= ~me_mask;
 	cc_vendor	= CC_VENDOR_AMD;
 	cc_set_mask(me_mask);
+
+	if (snp_vmpl)
+		svsm_config_vectors();
 }
 
 #ifdef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index f958f78e1db8..f8a5b5cf939a 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -34,6 +34,13 @@ enum es_result {
 	ES_RETRY,		/* Retry instruction emulation */
 };
 
+enum svsm_vec_enable {
+	SVSM_IRQ_DISABLE_SINGLE,
+	SVSM_IRQ_ENABLE_SINGLE,
+	SVSM_IRQ_DISABLE_ALL,
+	SVSM_IRQ_ENABLE_ALL,
+};
+
 struct es_fault_info {
 	unsigned long vector;
 	unsigned long error_code;
@@ -518,6 +525,7 @@ void snp_set_memory_shared(unsigned long vaddr, unsigned long npages);
 void snp_set_memory_private(unsigned long vaddr, unsigned long npages);
 void snp_set_wakeup_secondary_cpu(void);
 bool snp_init(struct boot_params *bp);
+void svsm_config_vectors(void);
 void snp_dmi_setup(void);
 int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, struct svsm_attest_call *input);
 void snp_accept_memory(phys_addr_t start, phys_addr_t end);
@@ -626,6 +634,7 @@ static inline void snp_set_memory_shared(unsigned long vaddr, unsigned long npag
 static inline void snp_set_memory_private(unsigned long vaddr, unsigned long npages) { }
 static inline void snp_set_wakeup_secondary_cpu(void) { }
 static inline bool snp_init(struct boot_params *bp) { return false; }
+static inline void svsm_config_vectors(void) { }
 static inline void snp_dmi_setup(void) { }
 static inline int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, struct svsm_attest_call *input)
 {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 4/7] x86/sev: Route unsupported APIC register accesses to the hypervisor APIC emulation
  2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
                   ` (2 preceding siblings ...)
  2026-07-30  1:48 ` [PATCH 3/7] x86/sev: Allow the guest to configure interrupt vectors for the hypervisor Melody Wang
@ 2026-07-30  1:48 ` Melody Wang
  2026-07-31 18:37   ` Tom Lendacky
  2026-07-30  1:48 ` [PATCH 5/7] x86/sev: Add a function to contain all SEV-specific setup operations Melody Wang
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang

The SVSM APIC emulation supports only a subset of the X2APIC MSRs.
Therefore, route the unsupported ones to the hypervisor's X2APIC
emulation.

Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
 arch/x86/coco/sev/core.c            | 12 ++++++------
 arch/x86/include/asm/sev-common.h   |  1 +
 arch/x86/include/asm/sev.h          |  8 ++++----
 arch/x86/kernel/apic/svsm_apic.c    | 11 +++++++----
 arch/x86/kernel/apic/x2apic_savic.c |  8 ++++----
 5 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
index 197119807230..b293a36c3594 100644
--- a/arch/x86/coco/sev/core.c
+++ b/arch/x86/coco/sev/core.c
@@ -972,7 +972,7 @@ int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd)
 	return 0;
 }
 
-u64 savic_ghcb_msr_read(u32 reg)
+u64 hvs_ghcb_msr_read(u32 reg)
 {
 	u64 msr = APIC_BASE_MSR + (reg >> 4);
 	struct pt_regs regs = { .cx = msr };
@@ -988,9 +988,9 @@ u64 savic_ghcb_msr_read(u32 reg)
 
 	res = __vc_handle_msr(ghcb, &ctxt, false);
 	if (res != ES_OK) {
-		pr_err("Secure AVIC MSR (0x%llx) read returned error (%d)\n", msr, res);
+		pr_err("Hypervisor MSR (0x%llx) read returned error (%d)\n", msr, res);
 		/* MSR read failures are treated as fatal errors */
-		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
+		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_APIC_MSR_FAIL);
 	}
 
 	__sev_put_ghcb(&state);
@@ -998,7 +998,7 @@ u64 savic_ghcb_msr_read(u32 reg)
 	return regs.ax | regs.dx << 32;
 }
 
-void savic_ghcb_msr_write(u32 reg, u64 value)
+void hvs_ghcb_msr_write(u32 reg, u64 value)
 {
 	u64 msr = APIC_BASE_MSR + (reg >> 4);
 	struct pt_regs regs = {
@@ -1018,9 +1018,9 @@ void savic_ghcb_msr_write(u32 reg, u64 value)
 
 	res = __vc_handle_msr(ghcb, &ctxt, true);
 	if (res != ES_OK) {
-		pr_err("Secure AVIC MSR (0x%llx) write returned error (%d)\n", msr, res);
+		pr_err("Hypervisor MSR (0x%llx) write returned error (%d)\n", msr, res);
 		/* MSR writes should never fail. Any failure is fatal error for SNP guest */
-		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
+		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_APIC_MSR_FAIL);
 	}
 
 	__sev_put_ghcb(&state);
diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
index 01a6e4dbe423..a41c52a16e31 100644
--- a/arch/x86/include/asm/sev-common.h
+++ b/arch/x86/include/asm/sev-common.h
@@ -209,6 +209,7 @@ struct snp_psc_desc {
 #define GHCB_TERM_SECURE_TSC		10	/* Secure TSC initialization failed */
 #define GHCB_TERM_SVSM_CA_REMAP_FAIL	11	/* SVSM is present but CA could not be remapped */
 #define GHCB_TERM_SAVIC_FAIL		12	/* Secure AVIC-specific failure */
+#define GHCB_TERM_APIC_MSR_FAIL		13	/* APIC MSR failure */
 
 #define GHCB_RESP_CODE(v)		((v) & GHCB_MSR_INFO_MASK)
 
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index f8a5b5cf939a..491a891a7694 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -549,8 +549,8 @@ void __init snp_secure_tsc_prepare(void);
 void __init snp_secure_tsc_init(void);
 enum es_result savic_register_gpa(u64 gpa);
 enum es_result savic_unregister_gpa(u64 *gpa);
-u64 savic_ghcb_msr_read(u32 reg);
-void savic_ghcb_msr_write(u32 reg, u64 value);
+u64 hvs_ghcb_msr_read(u32 reg);
+void hvs_ghcb_msr_write(u32 reg, u64 value);
 
 static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
 {
@@ -659,8 +659,8 @@ static inline void __init snp_secure_tsc_init(void) { }
 static inline void sev_evict_cache(void *va, int npages) {}
 static inline enum es_result savic_register_gpa(u64 gpa) { return ES_UNSUPPORTED; }
 static inline enum es_result savic_unregister_gpa(u64 *gpa) { return ES_UNSUPPORTED; }
-static inline void savic_ghcb_msr_write(u32 reg, u64 value) { }
-static inline u64 savic_ghcb_msr_read(u32 reg) { return 0; }
+static inline void hvs_ghcb_msr_write(u32 reg, u64 value) { }
+static inline u64 hvs_ghcb_msr_read(u32 reg) { return 0; }
 
 #endif	/* CONFIG_AMD_MEM_ENCRYPT */
 
diff --git a/arch/x86/kernel/apic/svsm_apic.c b/arch/x86/kernel/apic/svsm_apic.c
index 7040a1ca8b55..a4e7ba7e9985 100644
--- a/arch/x86/kernel/apic/svsm_apic.c
+++ b/arch/x86/kernel/apic/svsm_apic.c
@@ -68,7 +68,8 @@ static void svsm_apic_msr_write(u32 reg, u32 v)
 		}
 		break;
 	default:
-		pr_err("SVSM_APIC_WRITE_REGISTER 0x%x not supported\n", reg);
+		pr_debug("SVSM_APIC_WRITE_REGISTER to HV (0x%x, val:0x%x)\n", reg, v);
+		hvs_ghcb_msr_write(reg, v);
 		break;
 	}
 }
@@ -77,6 +78,7 @@ static u32 svsm_apic_msr_read(u32 reg)
 {
 	u32 msr = APIC_BASE_MSR + (reg >> 4);
 	struct svsm_call call = {};
+	u64 val;
 	int ret;
 
 	switch (reg) {
@@ -93,17 +95,18 @@ static u32 svsm_apic_msr_read(u32 reg)
 		call.rcx = msr;
 
 		ret = svsm_do_call(&call);
+		val = call.rdx_out;
 		if (ret) {
 			pr_err("SVSM_APIC_READ_REGISTER: 0x%x, error: %d\n", reg, ret);
 			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
 		}
 		break;
 	default:
-		pr_err("SVSM_APIC_READ_REGISTER: 0x%x not supported\n", reg);
-		return 0;
+		val = hvs_ghcb_msr_read(reg);
+		pr_debug("SVSM_APIC_READ_REGISTER from HV 0x%x, val: 0x%llx\n", reg, val);
 	}
 
-	return call.rdx_out;
+	return val;
 }
 
 static inline void svsm_apic_msr_eoi(void)
diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c
index 4bc6d7e018a5..71040f77dfaf 100644
--- a/arch/x86/kernel/apic/x2apic_savic.c
+++ b/arch/x86/kernel/apic/x2apic_savic.c
@@ -72,7 +72,7 @@ static u32 savic_read(u32 reg)
 	case APIC_LVT0:
 	case APIC_LVT1:
 	case APIC_LVTERR:
-		return savic_ghcb_msr_read(reg);
+		return hvs_ghcb_msr_read(reg);
 	case APIC_ID:
 	case APIC_LVR:
 	case APIC_TASKPRI:
@@ -193,7 +193,7 @@ static void savic_icr_write(u32 icr_low, u32 icr_high)
 
 	icr_data = ((u64)icr_high) << 32 | icr_low;
 	if (dsh != APIC_DEST_SELF)
-		savic_ghcb_msr_write(APIC_ICR, icr_data);
+		hvs_ghcb_msr_write(APIC_ICR, icr_data);
 	apic_set_reg64(this_cpu_ptr(savic_page), APIC_ICR, icr_data);
 }
 
@@ -210,7 +210,7 @@ static void savic_write(u32 reg, u32 data)
 	case APIC_LVTTHMR:
 	case APIC_LVTPC:
 	case APIC_LVTERR:
-		savic_ghcb_msr_write(reg, data);
+		hvs_ghcb_msr_write(reg, data);
 		break;
 	case APIC_TASKPRI:
 	case APIC_EOI:
@@ -316,7 +316,7 @@ static void savic_eoi(void)
 		 * interrupts. Return to the guest from GHCB protocol event takes
 		 * care of re-evaluating interrupt state.
 		 */
-		savic_ghcb_msr_write(APIC_EOI, 0);
+		hvs_ghcb_msr_write(APIC_EOI, 0);
 	} else {
 		/*
 		 * Hardware clears APIC_ISR and re-evaluates the interrupt state
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 5/7] x86/sev: Add a function to contain all SEV-specific setup operations
  2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
                   ` (3 preceding siblings ...)
  2026-07-30  1:48 ` [PATCH 4/7] x86/sev: Route unsupported APIC register accesses to the hypervisor APIC emulation Melody Wang
@ 2026-07-30  1:48 ` Melody Wang
  2026-07-31 19:15   ` Tom Lendacky
  2026-07-30  1:48 ` [PATCH 6/7] x86/sev: Register the guest with the SVSM APIC protocol Melody Wang
  2026-07-30  1:48 ` [PATCH 7/7] x86/sev: Indicate that Alternate Injection is supported in the guest Melody Wang
  6 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang, Ard Biesheuvel

To make the code clean in the boot phase, add a sev_prepare() wrapper
which contains early SEV-specific checks in order to have all that code
in a single place.

No functional changes.

Signed-off-by: Melody Wang <huibo.wang@amd.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/sev.c          |  9 +++++++++
 arch/x86/include/asm/sev.h              |  3 +++
 drivers/firmware/efi/libstub/x86-stub.c | 20 ++++++--------------
 3 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index fc2029746c50..655291a03dcc 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -511,3 +511,12 @@ bool early_is_sevsnp_guest(void)
 	}
 	return true;
 }
+
+u64 sev_prepare(void)
+{
+	u64 unsupported = snp_get_unsupported_features(sev_get_status());
+	if (unsupported)
+		return unsupported;
+
+	return 0;
+}
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 491a891a7694..b430c1aab403 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -613,6 +613,8 @@ static inline void sev_evict_cache(void *va, int npages)
 	}
 }
 
+u64 sev_prepare(void);
+
 #else	/* !CONFIG_AMD_MEM_ENCRYPT */
 
 #define snp_vmpl 0
@@ -661,6 +663,7 @@ static inline enum es_result savic_register_gpa(u64 gpa) { return ES_UNSUPPORTED
 static inline enum es_result savic_unregister_gpa(u64 *gpa) { return ES_UNSUPPORTED; }
 static inline void hvs_ghcb_msr_write(u32 reg, u64 value) { }
 static inline u64 hvs_ghcb_msr_read(u32 reg) { return 0; }
+static inline u64 sev_prepare(void) { return 0; }
 
 #endif	/* CONFIG_AMD_MEM_ENCRYPT */
 
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index cef32e2c82d8..95fa16fc9887 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -783,19 +783,6 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle)
 	return EFI_SUCCESS;
 }
 
-static bool have_unsupported_snp_features(void)
-{
-	u64 unsupported;
-
-	unsupported = snp_get_unsupported_features(sev_get_status());
-	if (unsupported) {
-		efi_err("Unsupported SEV-SNP features detected: 0x%llx\n",
-			unsupported);
-		return true;
-	}
-	return false;
-}
-
 static void efi_get_seed(void *seed, int size)
 {
 	efi_get_random_bytes(size, seed);
@@ -919,6 +906,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
 	unsigned long kernel_entry;
 	struct setup_header *hdr;
 	efi_status_t status;
+	u64 unsup_feats;
 
 	efi_system_table = sys_table_arg;
 	/* Check if we were booted by the EFI firmware */
@@ -933,8 +921,12 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
 
 	hdr = &boot_params->hdr;
 
-	if (have_unsupported_snp_features())
+	unsup_feats = sev_prepare();
+	if (unsup_feats) {
+		efi_err("Unsupported SEV-SNP features detected: 0x%llx\n",
+			unsup_feats);
 		efi_exit(handle, EFI_UNSUPPORTED);
+	}
 
 	if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES)) {
 		efi_dxe_table = get_efi_config_table(EFI_DXE_SERVICES_TABLE_GUID);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 6/7] x86/sev: Register the guest with the SVSM APIC protocol
  2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
                   ` (4 preceding siblings ...)
  2026-07-30  1:48 ` [PATCH 5/7] x86/sev: Add a function to contain all SEV-specific setup operations Melody Wang
@ 2026-07-30  1:48 ` Melody Wang
  2026-07-31 19:18   ` Tom Lendacky
  2026-07-30  1:48 ` [PATCH 7/7] x86/sev: Indicate that Alternate Injection is supported in the guest Melody Wang
  6 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang

The SVSM APIC protocol supports 5 calls. SVSM_APIC_CONFIGURE_EMULATION
(shortened to SVSM_APIC_CONFIG_EMULATION for brevity), call 1, provides
the controls whether the guest can make use of the SVSM APIC protocol.

Implement this call, and register Alternate Injection for the guest
by default.

Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
 arch/x86/boot/compressed/sev.c | 17 +++++++++++++++++
 arch/x86/boot/compressed/sev.h |  6 ++++++
 2 files changed, 23 insertions(+)

diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index 655291a03dcc..a98185b3869a 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -518,5 +518,22 @@ u64 sev_prepare(void)
 	if (unsupported)
 		return unsupported;
 
+	/* Register Alternate Injection */
+	if (early_is_sevsnp_guest() && snp_vmpl) {
+		struct svsm_call call = {};
+		int ret;
+
+		if (!(sev_get_status() & MSR_AMD64_SNP_ALTERNATE_INJ))
+			return 0;
+
+		call.caa = (struct svsm_ca *)boot_svsm_caa_pa;
+		call.rax = SVSM_APIC_CALL(SVSM_APIC_CONFIG_EMULATION);
+		call.rcx = SVSM_AI_REGISTER;
+
+		ret = svsm_call_msr_protocol(&call);
+		if (ret)
+			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+	}
+
 	return 0;
 }
diff --git a/arch/x86/boot/compressed/sev.h b/arch/x86/boot/compressed/sev.h
index 22637b416b46..dd058af2e7aa 100644
--- a/arch/x86/boot/compressed/sev.h
+++ b/arch/x86/boot/compressed/sev.h
@@ -12,6 +12,12 @@
 
 #include <asm/shared/msr.h>
 
+enum svsm_ai_ctrl {
+	SVSM_AI_DISABLE,
+	SVSM_AI_DEREGISTER,
+	SVSM_AI_REGISTER,
+};
+
 void snp_accept_memory(phys_addr_t start, phys_addr_t end);
 u64 sev_get_status(void);
 bool early_is_sevsnp_guest(void);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 7/7] x86/sev: Indicate that Alternate Injection is supported in the guest
  2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
                   ` (5 preceding siblings ...)
  2026-07-30  1:48 ` [PATCH 6/7] x86/sev: Register the guest with the SVSM APIC protocol Melody Wang
@ 2026-07-30  1:48 ` Melody Wang
  6 siblings, 0 replies; 19+ messages in thread
From: Melody Wang @ 2026-07-30  1:48 UTC (permalink / raw)
  To: x86; +Cc: LKML, Tom Lendacky, Melody Wang

Now the Alternate Injection support is complete, add it to the present
SNP features.

Signed-off-by: Melody Wang <huibo.wang@amd.com>
---
 arch/x86/boot/compressed/sev.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index a98185b3869a..21731527fa49 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -197,6 +197,12 @@ bool sev_es_check_ghcb_fault(unsigned long address)
 #define SNP_FEATURE_SECURE_AVIC		0
 #endif
 
+#ifdef CONFIG_AMD_ALTERNATE_INJ
+#define SNP_FEATURE_ALTERNATE_INJ	MSR_AMD64_SNP_ALTERNATE_INJ
+#else
+#define SNP_FEATURE_ALTERNATE_INJ	0
+#endif
+
 /*
  * SNP_FEATURES_IMPL is the mask of SNP features that are implemented
  * by the guest kernel. As and when a new feature is implemented in the
@@ -204,7 +210,8 @@ bool sev_es_check_ghcb_fault(unsigned long address)
  */
 #define SNP_FEATURES_IMPL	(MSR_AMD64_SNP_DEBUG_SWAP |	\
 				 MSR_AMD64_SNP_SECURE_TSC |	\
-				 SNP_FEATURE_SECURE_AVIC)
+				 SNP_FEATURE_SECURE_AVIC |	\
+				 SNP_FEATURE_ALTERNATE_INJ)
 
 u64 snp_get_unsupported_features(u64 status)
 {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/7] x86/sev: Add support for Alternate Injection
  2026-07-30  1:48 ` [PATCH 1/7] x86/sev: Add support for Alternate Injection Melody Wang
@ 2026-07-31 15:27   ` Tom Lendacky
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Lendacky @ 2026-07-31 15:27 UTC (permalink / raw)
  To: Melody Wang, x86; +Cc: LKML

On 7/29/26 20:48, Melody Wang wrote:
> The Alternate Injection feature is a method to protect an AMD
> confidential computing guest from malicious injection attacks. It allows
> the guest to control the interrupt injection.
> 
> When this feature is enabled, interrupts are injected with the help of
> an agent called a Secure VM Service Module (SVSM) which executes in the
> security realm of the guest and uses Restricted Injection as the sole
> method to receive interrupts from the hypervisor.
> 
> Add support for Alternate Injection enablement.

But you haven't added any support for it here. You've added a Kconfig
option (do we really need it?) that is unused and renamed the ALT_INJ bit
to ALTERNATE_INJ, which introduces a lot of churn.

I don't think the rename is needed, but if you really want to do it then
it should be a patch on its own.

And only introduce the Kconfig option when you're going to actually use it.

Thanks,
Tom

> 
> Signed-off-by: Melody Wang <huibo.wang@amd.com>
> ---
>  arch/x86/Kconfig                   | 14 ++++++++++++++
>  arch/x86/boot/compressed/sev.c     |  2 +-
>  arch/x86/coco/core.c               |  3 +++
>  arch/x86/coco/sev/core.c           |  2 +-
>  arch/x86/include/asm/cpufeatures.h |  1 +
>  arch/x86/include/asm/msr-index.h   |  4 ++--
>  include/linux/cc_platform.h        |  8 ++++++++
>  7 files changed, 30 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index e725b439d0a2..7ec5d2c4a8f0 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -493,6 +493,20 @@ config AMD_SECURE_AVIC
>  
>  	  If you don't know what to do here, say N.
>  
> +config AMD_ALTERNATE_INJ
> +	bool "Support Alternate Injection"
> +	depends on X86_X2APIC && AMD_MEM_ENCRYPT
> +	help
> +	  Enable AMD Alternate Injection support for SNP guests.
> +
> +	  The Alternate Injection feature of SEV-SNP enhances the security of
> +	  a confidential VM by preventing the untrusted host from presenting
> +	  unexpected interrupts or exceptions, while still preserving the
> +	  standard interrupt dispatch semantics inherent to the x86
> +	  architecture.
> +
> +	  If you don't know what to do here, say N.
> +
>  config X86_POSTED_MSI
>  	bool "Enable MSI and MSI-x delivery by posted interrupts"
>  	depends on X86_64 && IRQ_REMAP
> diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
> index c6512f2ea31e..fc2029746c50 100644
> --- a/arch/x86/boot/compressed/sev.c
> +++ b/arch/x86/boot/compressed/sev.c
> @@ -179,7 +179,7 @@ bool sev_es_check_ghcb_fault(unsigned long address)
>  #define SNP_FEATURES_IMPL_REQ	(MSR_AMD64_SNP_VTOM |			\
>  				 MSR_AMD64_SNP_REFLECT_VC |		\
>  				 MSR_AMD64_SNP_RESTRICTED_INJ |		\
> -				 MSR_AMD64_SNP_ALT_INJ |		\
> +				 MSR_AMD64_SNP_ALTERNATE_INJ |		\
>  				 MSR_AMD64_SNP_DEBUG_SWAP |		\
>  				 MSR_AMD64_SNP_VMPL_SSS |		\
>  				 MSR_AMD64_SNP_SECURE_TSC |		\
> diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c
> index 989ca9f72ba3..aabda3ddc0e2 100644
> --- a/arch/x86/coco/core.c
> +++ b/arch/x86/coco/core.c
> @@ -107,6 +107,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
>  	case CC_ATTR_SNP_SECURE_AVIC:
>  		return sev_status & MSR_AMD64_SNP_SECURE_AVIC;
>  
> +	case CC_ATTR_SNP_ALTERNATE_INJECTION:
> +		return sev_status & MSR_AMD64_SNP_ALTERNATE_INJ;
> +
>  	default:
>  		return false;
>  	}
> diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
> index ecd77d3217f3..197119807230 100644
> --- a/arch/x86/coco/sev/core.c
> +++ b/arch/x86/coco/sev/core.c
> @@ -78,7 +78,7 @@ static const char * const sev_status_feat_names[] = {
>  	[MSR_AMD64_SNP_VTOM_BIT]		= "vTom",
>  	[MSR_AMD64_SNP_REFLECT_VC_BIT]		= "ReflectVC",
>  	[MSR_AMD64_SNP_RESTRICTED_INJ_BIT]	= "RI",
> -	[MSR_AMD64_SNP_ALT_INJ_BIT]		= "AI",
> +	[MSR_AMD64_SNP_ALTERNATE_INJ_BIT]	= "AI",
>  	[MSR_AMD64_SNP_DEBUG_SWAP_BIT]		= "DebugSwap",
>  	[MSR_AMD64_SNP_PREVENT_HOST_IBS_BIT]	= "NoHostIBS",
>  	[MSR_AMD64_SNP_BTB_ISOLATION_BIT]	= "BTBIsol",
> diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
> index 3d0940a3b9f3..70dedbf0969f 100644
> --- a/arch/x86/include/asm/cpufeatures.h
> +++ b/arch/x86/include/asm/cpufeatures.h
> @@ -454,6 +454,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_ALTERNATE_INJECTION	(19*32+13) /* SEV Alternate 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/msr-index.h b/arch/x86/include/asm/msr-index.h
> index 18c4be75e927..e4eab2210518 100644
> --- a/arch/x86/include/asm/msr-index.h
> +++ b/arch/x86/include/asm/msr-index.h
> @@ -721,8 +721,8 @@
>  #define MSR_AMD64_SNP_REFLECT_VC	BIT_ULL(MSR_AMD64_SNP_REFLECT_VC_BIT)
>  #define MSR_AMD64_SNP_RESTRICTED_INJ_BIT 5
>  #define MSR_AMD64_SNP_RESTRICTED_INJ	BIT_ULL(MSR_AMD64_SNP_RESTRICTED_INJ_BIT)
> -#define MSR_AMD64_SNP_ALT_INJ_BIT	6
> -#define MSR_AMD64_SNP_ALT_INJ		BIT_ULL(MSR_AMD64_SNP_ALT_INJ_BIT)
> +#define MSR_AMD64_SNP_ALTERNATE_INJ_BIT	6
> +#define MSR_AMD64_SNP_ALTERNATE_INJ	BIT_ULL(MSR_AMD64_SNP_ALTERNATE_INJ_BIT)
>  #define MSR_AMD64_SNP_DEBUG_SWAP_BIT	7
>  #define MSR_AMD64_SNP_DEBUG_SWAP	BIT_ULL(MSR_AMD64_SNP_DEBUG_SWAP_BIT)
>  #define MSR_AMD64_SNP_PREVENT_HOST_IBS_BIT 8
> diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
> index 559353ad64ac..90ef67e85cd3 100644
> --- a/include/linux/cc_platform.h
> +++ b/include/linux/cc_platform.h
> @@ -104,6 +104,14 @@ enum cc_attr {
>  	 * to run SEV-SNP guests with full Secure AVIC capabilities.
>  	 */
>  	CC_ATTR_SNP_SECURE_AVIC,
> +
> +	/**
> +	 * @CC_ATTR_SNP_ALTERNATE_INJECTION: AMD Alternate Injection enabled on the host.
> +	 *
> +	 * The host kernel is running with the necessary features
> +	 * needed to run Alternate Injection enabled guests.
> +	 */
> +	CC_ATTR_SNP_ALTERNATE_INJECTION,
>  };
>  
>  #ifdef CONFIG_ARCH_HAS_CC_PLATFORM


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/7] x86/apic: Add an SVSM APIC driver
  2026-07-30  1:48 ` [PATCH 2/7] x86/apic: Add an SVSM APIC driver Melody Wang
@ 2026-07-31 17:39   ` Tom Lendacky
  2026-08-20  5:06     ` Melody Wang
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Lendacky @ 2026-07-31 17:39 UTC (permalink / raw)
  To: Melody Wang, x86; +Cc: LKML

On 7/29/26 20:48, Melody Wang wrote:
> The SVSM specification mandates the use of a SVSM APIC protocol instead
> of any other APICs when an Alternate Injection guest talks to the SVSM.

I would reword this to be more informative and describe why the SVSM APIC
protocol is needed (similar to what the APM states). Something like...

Alternate Injection replaces hypervisor-based interrupt queuing and event
injection, requiring guest-controlled queuing and injection. In order to
perform this guest-controlled queuing and injection, an SVSM is used to
update the guest VMSA to perform the required actions.

The guest uses the SVSM APIC protocol to communicate with the SVSM to
perform selected APIC related operations instead of using standard APIC
MSR access.

Reword as needed to better explain it.

> 
> Add such a SVSM APIC driver (which implements a subset of an X2APIC),
> for the APIC emulation supported by the SVSM.
> 
> Signed-off-by: Melody Wang <huibo.wang@amd.com>
> ---
>  arch/x86/coco/sev/svsm.c         |   6 +
>  arch/x86/include/asm/sev.h       |   9 ++
>  arch/x86/kernel/apic/Makefile    |   1 +
>  arch/x86/kernel/apic/svsm_apic.c | 252 +++++++++++++++++++++++++++++++
>  4 files changed, 268 insertions(+)
>  create mode 100644 arch/x86/kernel/apic/svsm_apic.c
> 
> diff --git a/arch/x86/coco/sev/svsm.c b/arch/x86/coco/sev/svsm.c
> index 916d62cd17dc..e2d3bc3c26eb 100644
> --- a/arch/x86/coco/sev/svsm.c
> +++ b/arch/x86/coco/sev/svsm.c
> @@ -354,3 +354,9 @@ bool snp_svsm_vtpm_probe(void)
>  	/* Check platform commands contains TPM_SEND_COMMAND - platform command 8 */
>  	return call.rcx_out & BIT_ULL(8);
>  }
> +
> +int svsm_do_call(struct svsm_call *call)
> +{
> +	call->caa = svsm_get_caa();
> +	return svsm_perform_call_protocol(call);
> +}

This helper is unneeded, all it does is set the calling area and then
issue svsm_perform_call_protocol(). You're already setting other call
values where you use this helper, so just add setting the calling area to
those and directly issue svsm_perform_call_protocol().

> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 594cfa19cbd4..f958f78e1db8 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -435,6 +435,13 @@ struct svsm_call {
>  #define SVSM_VTPM_QUERY			0
>  #define SVSM_VTPM_CMD			1
>  
> +#define SVSM_APIC_CALL(x)		((3ULL << 32) | (x))
> +#define SVSM_APIC_QUERY_FEATURES	0
> +#define SVSM_APIC_CONFIG_EMULATION	1
> +#define SVSM_APIC_READ_REGISTER		2
> +#define SVSM_APIC_WRITE_REGISTER	3
> +#define SVSM_APIC_CONFIG_VECTOR		4
> +
>  #ifdef CONFIG_AMD_MEM_ENCRYPT
>  
>  extern u8 snp_vmpl;
> @@ -519,6 +526,7 @@ u64 sev_get_status(void);
>  void sev_show_status(void);
>  int prepare_pte_enc(struct pte_enc_desc *d);
>  void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot);
> +int svsm_do_call(struct svsm_call *call);
>  void snp_kexec_finish(void);
>  void snp_kexec_begin(void);
>  
> @@ -611,6 +619,7 @@ static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long a
>  static inline void setup_ghcb(void) { }
>  static inline void __init
>  early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned long npages) { }
> +static inline int svsm_do_call(struct svsm_call *call) { return 0; }
>  static inline void __init
>  early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, unsigned long npages) { }
>  static inline void snp_set_memory_shared(unsigned long vaddr, unsigned long npages) { }
> diff --git a/arch/x86/kernel/apic/Makefile b/arch/x86/kernel/apic/Makefile
> index 581db89477f9..d23b1c4c0e14 100644
> --- a/arch/x86/kernel/apic/Makefile
> +++ b/arch/x86/kernel/apic/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_SMP)		+= ipi.o
>  ifeq ($(CONFIG_X86_64),y)
>  # APIC probe will depend on the listing order here
>  obj-$(CONFIG_X86_NUMACHIP)	+= apic_numachip.o
> +obj-$(CONFIG_AMD_ALTERNATE_INJ)	+= svsm_apic.o
>  obj-$(CONFIG_X86_UV)		+= x2apic_uv_x.o
>  obj-$(CONFIG_AMD_SECURE_AVIC)	+= x2apic_savic.o
>  obj-$(CONFIG_X86_X2APIC)	+= x2apic_phys.o
> diff --git a/arch/x86/kernel/apic/svsm_apic.c b/arch/x86/kernel/apic/svsm_apic.c
> new file mode 100644
> index 000000000000..7040a1ca8b55
> --- /dev/null
> +++ b/arch/x86/kernel/apic/svsm_apic.c

Should this be name x2apic-svsm.c, similar to x2apic-savic.c?

> @@ -0,0 +1,252 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * AMD Alternate Injection Support (SEV-SNP Guests)
> + *
> + * Copyright (C) 2026 Advanced Micro Devices, Inc.
> + *
> + * Author: Melody Wang <huibo.wang@amd.com>
> + */
> +
> +#include <linux/cpumask.h>
> +#include <linux/cc_platform.h>
> +
> +#include <asm/apic.h>
> +#include <asm/sev.h>
> +
> +#include "local.h"
> +
> +extern u8 snp_vmpl;

This is already in the asm/sev.h file.

> +
> +static int svsm_apic_probe(void)
> +{
> +	if (!cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) || !snp_vmpl)

Do you need both checks? Shouldn't the attribute only be set if the
feature is set and the VMPL level is greater than 0?

> +		return 0;
> +
> +	/* Alternate Injection and Secure AVIC are mutually exclusive */
> +	if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
> +		return 0;

What happens in this situation? Will Secure AVIC still work if Alternate
Injection is enabled?

> +
> +	if (!x2apic_mode) {
> +		pr_err("Alternate Injection in non x2APIC mode impossible. Terminating.\n");
> +		sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> +	}
> +
> +	pr_info("Alternate Injection SVSM APIC enabled\n");
> +

Should the probe routine query the SVSM for the APIC protocol?

> +	return 1;
> +}
> +
> +static int svsm_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
> +{
> +	return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) && snp_vmpl;
> +}
> +
> +static void svsm_apic_msr_write(u32 reg, u32 v)
> +{
> +	u32 msr = APIC_BASE_MSR + (reg >> 4);
> +	struct svsm_call call = {};
> +	int ret;
> +
> +	switch (reg) {
> +	case APIC_ID:
> +	case APIC_TASKPRI:
> +	case APIC_PROCPRI:
> +	case APIC_EOI:
> +	case APIC_ISR ... APIC_ISR + 0x70:
> +	case APIC_TMR ... APIC_TMR + 0x70:
> +	case APIC_IRR ... APIC_IRR + 0x70:
> +	case APIC_ICR:
> +	case APIC_SELF_IPI:
> +		call.rax = SVSM_APIC_CALL(SVSM_APIC_WRITE_REGISTER);
> +		call.rcx = msr;
> +		call.rdx = v;
> +
> +		ret = svsm_do_call(&call);
> +		if (ret) {
> +			pr_err("SVSM_APIC_WRITE_REGISTER: 0x%x, error: %d\n", reg, ret);
> +			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);

Can this be a new SEV_TERM_SET_LINUX value?

> +		}
> +		break;
> +	default:
> +		pr_err("SVSM_APIC_WRITE_REGISTER 0x%x not supported\n", reg);
> +		break;
> +	}
> +}
> +
> +static u32 svsm_apic_msr_read(u32 reg)
> +{
> +	u32 msr = APIC_BASE_MSR + (reg >> 4);
> +	struct svsm_call call = {};
> +	int ret;
> +
> +	switch (reg) {
> +	case APIC_ID:
> +	case APIC_TASKPRI:
> +	case APIC_PROCPRI:
> +	case APIC_EOI:
> +	case APIC_ISR ... APIC_ISR + 0x70:
> +	case APIC_TMR ... APIC_TMR + 0x70:
> +	case APIC_IRR ... APIC_IRR + 0x70:
> +	case APIC_ICR:
> +	case APIC_SELF_IPI:
> +		call.rax = SVSM_APIC_CALL(SVSM_APIC_READ_REGISTER);
> +		call.rcx = msr;
> +
> +		ret = svsm_do_call(&call);
> +		if (ret) {
> +			pr_err("SVSM_APIC_READ_REGISTER: 0x%x, error: %d\n", reg, ret);
> +			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> +		}
> +		break;
> +	default:
> +		pr_err("SVSM_APIC_READ_REGISTER: 0x%x not supported\n", reg);
> +		return 0;
> +	}
> +
> +	return call.rdx_out;
> +}

The read and write are very similar. Can you have a common function that
takes a reg paramter, value parameter (that is input and output), and a
mode parameter (read/write) and then have small read and write functions?

> +
> +static inline void svsm_apic_msr_eoi(void)
> +{
> +	svsm_apic_msr_write(APIC_EOI, APIC_EOI_ACK);
> +}
> +
> +static inline u64 svsm_apic_icr_read(void)
> +{
> +	u32 reg;
> +	struct svsm_call call = {};

Move this up above the reg declaration.

> +	int ret;
> +
> +	reg = APIC_ICR;
> +
> +	call.rax = SVSM_APIC_CALL(SVSM_APIC_READ_REGISTER);
> +	call.rcx = APIC_BASE_MSR + (reg >> 4);
> +
> +	ret = svsm_do_call(&call);
> +	if (ret) {
> +		pr_err("svsm_apic_icr_read error: %d\n", ret);
> +		sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> +	}
> +
> +	return call.rdx_out;
> +}
> +
> +static void svsm_apic_icr_write(u32 low, u32 id)
> +{
> +	u64 icr_data;
> +	u32 reg;
> +	struct svsm_call call = {};

Move this to the top.

> +	int ret;
> +
> +	reg = APIC_ICR;
> +	icr_data = ((u64)id) << 32 | low;
> +
> +	call.rax = SVSM_APIC_CALL(SVSM_APIC_WRITE_REGISTER);
> +	call.rcx = APIC_BASE_MSR + (reg >> 4);
> +	call.rdx = icr_data;
> +
> +	ret = svsm_do_call(&call);
> +	if (ret) {
> +		pr_err("svsm_apic_icr_write error: %d\n", ret);
> +		sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> +	}
> +}
> +
> +static void __svsm_apic_send_IPI_dest(unsigned int apicid, int vector, unsigned int dest)
> +{
> +	svsm_apic_icr_write(__prepare_ICR(0, vector, dest), apicid);
> +}

I don't think this routine is needed, just do the svsm_apic_icr_write() in
the svsm_apic_send_IPI() below and then ...

> +
> +static void svsm_apic_send_IPI(int cpu, int vector)
> +{
> +	u32 dest = per_cpu(x86_cpu_to_apicid, cpu);
> +
> +	__svsm_apic_send_IPI_dest(dest, vector, APIC_DEST_PHYSICAL);
> +}
> +
> +static void __svsm_apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest)
> +{
> +	unsigned long query_cpu;
> +	unsigned long this_cpu;
> +
> +	guard(irqsave)();
> +
> +	this_cpu = smp_processor_id();
> +	for_each_cpu(query_cpu, mask) {
> +		if (apic_dest == APIC_DEST_ALLBUT && this_cpu == query_cpu)
> +			continue;
> +
> +		__svsm_apic_send_IPI_dest(per_cpu(x86_cpu_to_apicid, query_cpu),
> +					  vector, APIC_DEST_PHYSICAL);

... this can just invoke svsm_apic_send_IPI(), right?

Thanks,
Tom

> +	}
> +}
> +
> +static void svsm_apic_send_IPI_mask(const struct cpumask *mask, int vector)
> +{
> +	__svsm_apic_send_IPI_mask(mask, vector, APIC_DEST_ALLINC);
> +}
> +
> +static void svsm_apic_send_IPI_mask_allbutself(const struct cpumask *mask, int vector)
> +{
> +	__svsm_apic_send_IPI_mask(mask, vector, APIC_DEST_ALLBUT);
> +}
> +
> +static void __svsm_apic_send_IPI_shorthand(int vector, u32 which)
> +{
> +	svsm_apic_icr_write(__prepare_ICR(which, vector, 0), 0);
> +}
> +
> +static void svsm_apic_send_IPI_allbutself(int vector)
> +{
> +	__svsm_apic_send_IPI_shorthand(vector, APIC_DEST_ALLBUT);
> +}
> +
> +static void svsm_apic_send_IPI_all(int vector)
> +{
> +	__svsm_apic_send_IPI_shorthand(vector, APIC_DEST_ALLINC);
> +}
> +
> +static void svsm_apic_send_IPI_self(int vector)
> +{
> +	__svsm_apic_send_IPI_shorthand(vector, APIC_DEST_SELF);
> +}
> +
> +static u32 svsm_apic_get_apic_id(u32 id)
> +{
> +	return id;
> +}
> +
> +static struct apic svsm_apic __ro_after_init = {
> +
> +	.name				= "svsm apic",
> +	.probe				= svsm_apic_probe,
> +	.acpi_madt_oem_check		= svsm_acpi_madt_oem_check,
> +
> +	.dest_mode_logical		= false,
> +
> +	.disable_esr			= 0,
> +
> +	.cpu_present_to_apicid		= default_cpu_present_to_apicid,
> +
> +	.max_apic_id			= UINT_MAX,
> +	.x2apic_set_max_apicid		= true,
> +	.get_apic_id			= svsm_apic_get_apic_id,
> +
> +	.calc_dest_apicid		= apic_default_calc_apicid,
> +
> +	.send_IPI			= svsm_apic_send_IPI,
> +	.send_IPI_mask			= svsm_apic_send_IPI_mask,
> +	.send_IPI_mask_allbutself	= svsm_apic_send_IPI_mask_allbutself,
> +	.send_IPI_allbutself		= svsm_apic_send_IPI_allbutself,
> +	.send_IPI_all			= svsm_apic_send_IPI_all,
> +	.send_IPI_self			= svsm_apic_send_IPI_self,
> +	.nmi_to_offline_cpu		= true,
> +
> +	.read				= svsm_apic_msr_read,
> +	.write				= svsm_apic_msr_write,
> +	.eoi				= svsm_apic_msr_eoi,
> +	.icr_read			= svsm_apic_icr_read,
> +	.icr_write			= svsm_apic_icr_write,
> +
> +};
> +apic_driver(svsm_apic);


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/7] x86/sev: Allow the guest to configure interrupt vectors for the hypervisor
  2026-07-30  1:48 ` [PATCH 3/7] x86/sev: Allow the guest to configure interrupt vectors for the hypervisor Melody Wang
@ 2026-07-31 17:52   ` Tom Lendacky
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Lendacky @ 2026-07-31 17:52 UTC (permalink / raw)
  To: Melody Wang, x86; +Cc: LKML

On 7/29/26 20:48, Melody Wang wrote:
> The SVSM APIC protocol supports 5 API calls. SVSM_APIC_CONFIGURE_VECTOR
> (shortened to SVSM_APIC_CONFIG_VECTOR for brevity), call 4, provides for
> the guest to configure an interrupt vector which the guest allows and
> the hypervisor can use to signal interrupts for it.
> 
> Implement this call, and make the default interrupt setting permissive
> when detecting an SVSM.

Maybe expand on this to say that you are allowing all interrupts. And its
not when you detect only an SVSM, it is when you detect that and Alternate
Injection is enabled.

> 
> Signed-off-by: Melody Wang <huibo.wang@amd.com>
> ---
>  arch/x86/boot/startup/sev-startup.c | 15 +++++++++++++++
>  arch/x86/boot/startup/sme.c         |  3 +++
>  arch/x86/include/asm/sev.h          |  9 +++++++++
>  3 files changed, 27 insertions(+)
> 
> diff --git a/arch/x86/boot/startup/sev-startup.c b/arch/x86/boot/startup/sev-startup.c
> index 789e99d38d17..8dea0548e004 100644
> --- a/arch/x86/boot/startup/sev-startup.c
> +++ b/arch/x86/boot/startup/sev-startup.c
> @@ -191,6 +191,21 @@ static void __init svsm_setup(struct cc_blob_sev_info *cc_info)
>  	boot_svsm_caa_pa = pa;
>  }
>  
> +/* Configure the APIC IRQ vectors with Alternate Injection */
> +void __init svsm_config_vectors(void)

s/svsm_config_vectors/svsm_config_alt_inj_vectors/

> +{
> +	struct svsm_call call = {};
> +
> +	if (sev_status & MSR_AMD64_SNP_ALTERNATE_INJ) {

Might as well include the check for snp_vmpl here or put this check below
where this function is called so that you don't have two spread out checks.

> +		call.caa = rip_rel_ptr(&boot_svsm_ca_page);
> +		call.rax = SVSM_APIC_CALL(SVSM_APIC_CONFIG_VECTOR);
> +		call.rcx = SVSM_IRQ_ENABLE_ALL << 8;

Why isn't the shift part of the enum so that you don't have to do it here?

> +
> +		if (svsm_call_msr_protocol(&call))
> +			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);

New SEV_TERM_SET_LINUX value?

> +	}
> +}
> +
>  bool __init snp_init(struct boot_params *bp)
>  {
>  	struct cc_blob_sev_info *cc_info;
> diff --git a/arch/x86/boot/startup/sme.c b/arch/x86/boot/startup/sme.c
> index c07a2c381ed1..664e7a549b34 100644
> --- a/arch/x86/boot/startup/sme.c
> +++ b/arch/x86/boot/startup/sme.c
> @@ -566,6 +566,9 @@ void __init sme_enable(struct boot_params *bp)
>  	physical_mask	&= ~me_mask;
>  	cc_vendor	= CC_VENDOR_AMD;
>  	cc_set_mask(me_mask);
> +
> +	if (snp_vmpl)
> +		svsm_config_vectors();

Add a comment before the if as to why this is being done.

Thanks,
Tom

>  }
>  
>  #ifdef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index f958f78e1db8..f8a5b5cf939a 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -34,6 +34,13 @@ enum es_result {
>  	ES_RETRY,		/* Retry instruction emulation */
>  };
>  
> +enum svsm_vec_enable {
> +	SVSM_IRQ_DISABLE_SINGLE,
> +	SVSM_IRQ_ENABLE_SINGLE,
> +	SVSM_IRQ_DISABLE_ALL,
> +	SVSM_IRQ_ENABLE_ALL,
> +};
> +
>  struct es_fault_info {
>  	unsigned long vector;
>  	unsigned long error_code;
> @@ -518,6 +525,7 @@ void snp_set_memory_shared(unsigned long vaddr, unsigned long npages);
>  void snp_set_memory_private(unsigned long vaddr, unsigned long npages);
>  void snp_set_wakeup_secondary_cpu(void);
>  bool snp_init(struct boot_params *bp);
> +void svsm_config_vectors(void);
>  void snp_dmi_setup(void);
>  int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, struct svsm_attest_call *input);
>  void snp_accept_memory(phys_addr_t start, phys_addr_t end);
> @@ -626,6 +634,7 @@ static inline void snp_set_memory_shared(unsigned long vaddr, unsigned long npag
>  static inline void snp_set_memory_private(unsigned long vaddr, unsigned long npages) { }
>  static inline void snp_set_wakeup_secondary_cpu(void) { }
>  static inline bool snp_init(struct boot_params *bp) { return false; }
> +static inline void svsm_config_vectors(void) { }
>  static inline void snp_dmi_setup(void) { }
>  static inline int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, struct svsm_attest_call *input)
>  {


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 4/7] x86/sev: Route unsupported APIC register accesses to the hypervisor APIC emulation
  2026-07-30  1:48 ` [PATCH 4/7] x86/sev: Route unsupported APIC register accesses to the hypervisor APIC emulation Melody Wang
@ 2026-07-31 18:37   ` Tom Lendacky
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Lendacky @ 2026-07-31 18:37 UTC (permalink / raw)
  To: Melody Wang, x86; +Cc: LKML

On 7/29/26 20:48, Melody Wang wrote:
> The SVSM APIC emulation supports only a subset of the X2APIC MSRs.
> Therefore, route the unsupported ones to the hypervisor's X2APIC
> emulation.

Explain a bit more why this is needed? How will the hypervisor use this,
maybe give an example.

> 
> Signed-off-by: Melody Wang <huibo.wang@amd.com>
> ---
>  arch/x86/coco/sev/core.c            | 12 ++++++------
>  arch/x86/include/asm/sev-common.h   |  1 +
>  arch/x86/include/asm/sev.h          |  8 ++++----
>  arch/x86/kernel/apic/svsm_apic.c    | 11 +++++++----
>  arch/x86/kernel/apic/x2apic_savic.c |  8 ++++----

You could do the function rename as a separate pre-patch and then this
patch would be purley SVSM support. Up to you.

>  5 files changed, 22 insertions(+), 18 deletions(-)
> 
> diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
> index 197119807230..b293a36c3594 100644
> --- a/arch/x86/coco/sev/core.c
> +++ b/arch/x86/coco/sev/core.c
> @@ -972,7 +972,7 @@ int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd)
>  	return 0;
>  }
>  
> -u64 savic_ghcb_msr_read(u32 reg)
> +u64 hvs_ghcb_msr_read(u32 reg)

Not sure that hvs is really that descriptive.

sev_apic_ghcb_msr_read ?

>  {
>  	u64 msr = APIC_BASE_MSR + (reg >> 4);
>  	struct pt_regs regs = { .cx = msr };
> @@ -988,9 +988,9 @@ u64 savic_ghcb_msr_read(u32 reg)
>  
>  	res = __vc_handle_msr(ghcb, &ctxt, false);
>  	if (res != ES_OK) {
> -		pr_err("Secure AVIC MSR (0x%llx) read returned error (%d)\n", msr, res);
> +		pr_err("Hypervisor MSR (0x%llx) read returned error (%d)\n", msr, res);

Same here with Hypervisor... maybe "APIC MSR via GHCB..." ?

>  		/* MSR read failures are treated as fatal errors */
> -		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
> +		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_APIC_MSR_FAIL);

Alteratively you could pass in the code to use so that it is unique for
Secure AVIC and Alternate Injection.

>  	}
>  
>  	__sev_put_ghcb(&state);
> @@ -998,7 +998,7 @@ u64 savic_ghcb_msr_read(u32 reg)
>  	return regs.ax | regs.dx << 32;
>  }
>  
> -void savic_ghcb_msr_write(u32 reg, u64 value)
> +void hvs_ghcb_msr_write(u32 reg, u64 value)
>  {
>  	u64 msr = APIC_BASE_MSR + (reg >> 4);
>  	struct pt_regs regs = {
> @@ -1018,9 +1018,9 @@ void savic_ghcb_msr_write(u32 reg, u64 value)
>  
>  	res = __vc_handle_msr(ghcb, &ctxt, true);
>  	if (res != ES_OK) {
> -		pr_err("Secure AVIC MSR (0x%llx) write returned error (%d)\n", msr, res);
> +		pr_err("Hypervisor MSR (0x%llx) write returned error (%d)\n", msr, res);
>  		/* MSR writes should never fail. Any failure is fatal error for SNP guest */
> -		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
> +		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_APIC_MSR_FAIL);
>  	}
>  
>  	__sev_put_ghcb(&state);
> diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
> index 01a6e4dbe423..a41c52a16e31 100644
> --- a/arch/x86/include/asm/sev-common.h
> +++ b/arch/x86/include/asm/sev-common.h
> @@ -209,6 +209,7 @@ struct snp_psc_desc {
>  #define GHCB_TERM_SECURE_TSC		10	/* Secure TSC initialization failed */
>  #define GHCB_TERM_SVSM_CA_REMAP_FAIL	11	/* SVSM is present but CA could not be remapped */
>  #define GHCB_TERM_SAVIC_FAIL		12	/* Secure AVIC-specific failure */
> +#define GHCB_TERM_APIC_MSR_FAIL		13	/* APIC MSR failure */
>  
>  #define GHCB_RESP_CODE(v)		((v) & GHCB_MSR_INFO_MASK)
>  
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index f8a5b5cf939a..491a891a7694 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -549,8 +549,8 @@ void __init snp_secure_tsc_prepare(void);
>  void __init snp_secure_tsc_init(void);
>  enum es_result savic_register_gpa(u64 gpa);
>  enum es_result savic_unregister_gpa(u64 *gpa);
> -u64 savic_ghcb_msr_read(u32 reg);
> -void savic_ghcb_msr_write(u32 reg, u64 value);
> +u64 hvs_ghcb_msr_read(u32 reg);
> +void hvs_ghcb_msr_write(u32 reg, u64 value);
>  
>  static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
>  {
> @@ -659,8 +659,8 @@ static inline void __init snp_secure_tsc_init(void) { }
>  static inline void sev_evict_cache(void *va, int npages) {}
>  static inline enum es_result savic_register_gpa(u64 gpa) { return ES_UNSUPPORTED; }
>  static inline enum es_result savic_unregister_gpa(u64 *gpa) { return ES_UNSUPPORTED; }
> -static inline void savic_ghcb_msr_write(u32 reg, u64 value) { }
> -static inline u64 savic_ghcb_msr_read(u32 reg) { return 0; }
> +static inline void hvs_ghcb_msr_write(u32 reg, u64 value) { }
> +static inline u64 hvs_ghcb_msr_read(u32 reg) { return 0; }
>  
>  #endif	/* CONFIG_AMD_MEM_ENCRYPT */
>  
> diff --git a/arch/x86/kernel/apic/svsm_apic.c b/arch/x86/kernel/apic/svsm_apic.c
> index 7040a1ca8b55..a4e7ba7e9985 100644
> --- a/arch/x86/kernel/apic/svsm_apic.c
> +++ b/arch/x86/kernel/apic/svsm_apic.c
> @@ -68,7 +68,8 @@ static void svsm_apic_msr_write(u32 reg, u32 v)
>  		}
>  		break;
>  	default:
> -		pr_err("SVSM_APIC_WRITE_REGISTER 0x%x not supported\n", reg);
> +		pr_debug("SVSM_APIC_WRITE_REGISTER to HV (0x%x, val:0x%x)\n", reg, v);

Not sure what makes this different from the SVSM calls that don't have a
pr_debug(), so you can probably remove the pr_debug().

> +		hvs_ghcb_msr_write(reg, v);
>  		break;
>  	}
>  }
> @@ -77,6 +78,7 @@ static u32 svsm_apic_msr_read(u32 reg)
>  {
>  	u32 msr = APIC_BASE_MSR + (reg >> 4);
>  	struct svsm_call call = {};
> +	u64 val;
>  	int ret;
>  
>  	switch (reg) {
> @@ -93,17 +95,18 @@ static u32 svsm_apic_msr_read(u32 reg)
>  		call.rcx = msr;
>  
>  		ret = svsm_do_call(&call);
> +		val = call.rdx_out;
>  		if (ret) {
>  			pr_err("SVSM_APIC_READ_REGISTER: 0x%x, error: %d\n", reg, ret);
>  			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>  		}
>  		break;
>  	default:
> -		pr_err("SVSM_APIC_READ_REGISTER: 0x%x not supported\n", reg);
> -		return 0;
> +		val = hvs_ghcb_msr_read(reg);
> +		pr_debug("SVSM_APIC_READ_REGISTER from HV 0x%x, val: 0x%llx\n", reg, val);

Ditto.

Thanks,
Tom

>  	}
>  
> -	return call.rdx_out;
> +	return val;
>  }
>  
>  static inline void svsm_apic_msr_eoi(void)
> diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c
> index 4bc6d7e018a5..71040f77dfaf 100644
> --- a/arch/x86/kernel/apic/x2apic_savic.c
> +++ b/arch/x86/kernel/apic/x2apic_savic.c
> @@ -72,7 +72,7 @@ static u32 savic_read(u32 reg)
>  	case APIC_LVT0:
>  	case APIC_LVT1:
>  	case APIC_LVTERR:
> -		return savic_ghcb_msr_read(reg);
> +		return hvs_ghcb_msr_read(reg);
>  	case APIC_ID:
>  	case APIC_LVR:
>  	case APIC_TASKPRI:
> @@ -193,7 +193,7 @@ static void savic_icr_write(u32 icr_low, u32 icr_high)
>  
>  	icr_data = ((u64)icr_high) << 32 | icr_low;
>  	if (dsh != APIC_DEST_SELF)
> -		savic_ghcb_msr_write(APIC_ICR, icr_data);
> +		hvs_ghcb_msr_write(APIC_ICR, icr_data);
>  	apic_set_reg64(this_cpu_ptr(savic_page), APIC_ICR, icr_data);
>  }
>  
> @@ -210,7 +210,7 @@ static void savic_write(u32 reg, u32 data)
>  	case APIC_LVTTHMR:
>  	case APIC_LVTPC:
>  	case APIC_LVTERR:
> -		savic_ghcb_msr_write(reg, data);
> +		hvs_ghcb_msr_write(reg, data);
>  		break;
>  	case APIC_TASKPRI:
>  	case APIC_EOI:
> @@ -316,7 +316,7 @@ static void savic_eoi(void)
>  		 * interrupts. Return to the guest from GHCB protocol event takes
>  		 * care of re-evaluating interrupt state.
>  		 */
> -		savic_ghcb_msr_write(APIC_EOI, 0);
> +		hvs_ghcb_msr_write(APIC_EOI, 0);
>  	} else {
>  		/*
>  		 * Hardware clears APIC_ISR and re-evaluates the interrupt state


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 5/7] x86/sev: Add a function to contain all SEV-specific setup operations
  2026-07-30  1:48 ` [PATCH 5/7] x86/sev: Add a function to contain all SEV-specific setup operations Melody Wang
@ 2026-07-31 19:15   ` Tom Lendacky
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Lendacky @ 2026-07-31 19:15 UTC (permalink / raw)
  To: Melody Wang, x86; +Cc: LKML, Ard Biesheuvel

On 7/29/26 20:48, Melody Wang wrote:
> To make the code clean in the boot phase, add a sev_prepare() wrapper
> which contains early SEV-specific checks in order to have all that code
> in a single place.
> 
> No functional changes.

I think this needs to be re-worked. Looking ahead to the next patch, if
anything is later added to the prepare routine that also returns, it will
all be attributed to the unsupported features. Right now you avoid that by
terminating in sev_prepare() in the next patch.

You might need some form of callback for error conditions or something
else, but as is, I think this is too fragile.

Thanks,
Tom

> 
> Signed-off-by: Melody Wang <huibo.wang@amd.com>
> Cc: Ard Biesheuvel <ardb@kernel.org>
> ---
>  arch/x86/boot/compressed/sev.c          |  9 +++++++++
>  arch/x86/include/asm/sev.h              |  3 +++
>  drivers/firmware/efi/libstub/x86-stub.c | 20 ++++++--------------
>  3 files changed, 18 insertions(+), 14 deletions(-)
> 
> diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
> index fc2029746c50..655291a03dcc 100644
> --- a/arch/x86/boot/compressed/sev.c
> +++ b/arch/x86/boot/compressed/sev.c
> @@ -511,3 +511,12 @@ bool early_is_sevsnp_guest(void)
>  	}
>  	return true;
>  }
> +
> +u64 sev_prepare(void)
> +{
> +	u64 unsupported = snp_get_unsupported_features(sev_get_status());
> +	if (unsupported)
> +		return unsupported;
> +
> +	return 0;
> +}
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 491a891a7694..b430c1aab403 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -613,6 +613,8 @@ static inline void sev_evict_cache(void *va, int npages)
>  	}
>  }
>  
> +u64 sev_prepare(void);
> +
>  #else	/* !CONFIG_AMD_MEM_ENCRYPT */
>  
>  #define snp_vmpl 0
> @@ -661,6 +663,7 @@ static inline enum es_result savic_register_gpa(u64 gpa) { return ES_UNSUPPORTED
>  static inline enum es_result savic_unregister_gpa(u64 *gpa) { return ES_UNSUPPORTED; }
>  static inline void hvs_ghcb_msr_write(u32 reg, u64 value) { }
>  static inline u64 hvs_ghcb_msr_read(u32 reg) { return 0; }
> +static inline u64 sev_prepare(void) { return 0; }
>  
>  #endif	/* CONFIG_AMD_MEM_ENCRYPT */
>  
> diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
> index cef32e2c82d8..95fa16fc9887 100644
> --- a/drivers/firmware/efi/libstub/x86-stub.c
> +++ b/drivers/firmware/efi/libstub/x86-stub.c
> @@ -783,19 +783,6 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle)
>  	return EFI_SUCCESS;
>  }
>  
> -static bool have_unsupported_snp_features(void)
> -{
> -	u64 unsupported;
> -
> -	unsupported = snp_get_unsupported_features(sev_get_status());
> -	if (unsupported) {
> -		efi_err("Unsupported SEV-SNP features detected: 0x%llx\n",
> -			unsupported);
> -		return true;
> -	}
> -	return false;
> -}
> -
>  static void efi_get_seed(void *seed, int size)
>  {
>  	efi_get_random_bytes(size, seed);
> @@ -919,6 +906,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
>  	unsigned long kernel_entry;
>  	struct setup_header *hdr;
>  	efi_status_t status;
> +	u64 unsup_feats;
>  
>  	efi_system_table = sys_table_arg;
>  	/* Check if we were booted by the EFI firmware */
> @@ -933,8 +921,12 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
>  
>  	hdr = &boot_params->hdr;
>  
> -	if (have_unsupported_snp_features())
> +	unsup_feats = sev_prepare();
> +	if (unsup_feats) {
> +		efi_err("Unsupported SEV-SNP features detected: 0x%llx\n",
> +			unsup_feats);
>  		efi_exit(handle, EFI_UNSUPPORTED);
> +	}
>  
>  	if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES)) {
>  		efi_dxe_table = get_efi_config_table(EFI_DXE_SERVICES_TABLE_GUID);


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 6/7] x86/sev: Register the guest with the SVSM APIC protocol
  2026-07-30  1:48 ` [PATCH 6/7] x86/sev: Register the guest with the SVSM APIC protocol Melody Wang
@ 2026-07-31 19:18   ` Tom Lendacky
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Lendacky @ 2026-07-31 19:18 UTC (permalink / raw)
  To: Melody Wang, x86; +Cc: LKML

On 7/29/26 20:48, Melody Wang wrote:
> The SVSM APIC protocol supports 5 calls. SVSM_APIC_CONFIGURE_EMULATION
> (shortened to SVSM_APIC_CONFIG_EMULATION for brevity), call 1, provides
> the controls whether the guest can make use of the SVSM APIC protocol.
> 
> Implement this call, and register Alternate Injection for the guest
> by default.
> 
> Signed-off-by: Melody Wang <huibo.wang@amd.com>
> ---
>  arch/x86/boot/compressed/sev.c | 17 +++++++++++++++++
>  arch/x86/boot/compressed/sev.h |  6 ++++++
>  2 files changed, 23 insertions(+)
> 
> diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
> index 655291a03dcc..a98185b3869a 100644
> --- a/arch/x86/boot/compressed/sev.c
> +++ b/arch/x86/boot/compressed/sev.c
> @@ -518,5 +518,22 @@ u64 sev_prepare(void)
>  	if (unsupported)
>  		return unsupported;
>  
> +	/* Register Alternate Injection */
> +	if (early_is_sevsnp_guest() && snp_vmpl) {
> +		struct svsm_call call = {};
> +		int ret;
> +
> +		if (!(sev_get_status() & MSR_AMD64_SNP_ALTERNATE_INJ))
> +			return 0;
> +
> +		call.caa = (struct svsm_ca *)boot_svsm_caa_pa;
> +		call.rax = SVSM_APIC_CALL(SVSM_APIC_CONFIG_EMULATION);
> +		call.rcx = SVSM_AI_REGISTER;
> +
> +		ret = svsm_call_msr_protocol(&call);
> +		if (ret)
> +			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);

Shouldn't this really be where the permissive setting of allowing all
interrupts be done, too, instead of where it is done in patch #3?

Thanks,
Tom

> +	}
> +
>  	return 0;
>  }
> diff --git a/arch/x86/boot/compressed/sev.h b/arch/x86/boot/compressed/sev.h
> index 22637b416b46..dd058af2e7aa 100644
> --- a/arch/x86/boot/compressed/sev.h
> +++ b/arch/x86/boot/compressed/sev.h
> @@ -12,6 +12,12 @@
>  
>  #include <asm/shared/msr.h>
>  
> +enum svsm_ai_ctrl {
> +	SVSM_AI_DISABLE,

Isn't the zero value "Query APIC Emulation", not disable?

> +	SVSM_AI_DEREGISTER,

s/DEREGISTER/UNREGISTER/

Thanks,
Tom

> +	SVSM_AI_REGISTER,
> +};
> +
>  void snp_accept_memory(phys_addr_t start, phys_addr_t end);
>  u64 sev_get_status(void);
>  bool early_is_sevsnp_guest(void);


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/7] x86/apic: Add an SVSM APIC driver
  2026-07-31 17:39   ` Tom Lendacky
@ 2026-08-20  5:06     ` Melody Wang
  2026-08-20 15:43       ` Tom Lendacky
  0 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-08-20  5:06 UTC (permalink / raw)
  To: Tom Lendacky; +Cc: LKML, x86

Hi Tom,

On 7/31/26 10:39 AM, Tom Lendacky wrote:
>> +int svsm_do_call(struct svsm_call *call)
>> +{
>> +	call->caa = svsm_get_caa();
>> +	return svsm_perform_call_protocol(call);
>> +}
> 
> This helper is unneeded, all it does is set the calling area and then
> issue svsm_perform_call_protocol(). You're already setting other call
> values where you use this helper, so just add setting the calling area to
> those and directly issue svsm_perform_call_protocol().

Since svsm_perform_call_protocol() and svsm_get_caa() are all internal 
functions to coco (arch/x86/coco/sev/internal.h), I can not call them 
directly from the apic driver, so perhaps it is better that I have this 
helper function here.

>> diff --git a/arch/x86/kernel/apic/svsm_apic.c b/arch/x86/kernel/apic/svsm_apic.c
>> new file mode 100644
>> +static int svsm_apic_probe(void)
>> +{
>> +	if (!cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) || !snp_vmpl)
> 
> Do you need both checks? Shouldn't the attribute only be set if the
> feature is set and the VMPL level is greater than 0?

Yes, the cc_platform_has() just checks if the sev_status has the 
MSR_AMD64_SNP_ALTERNATE_INJ bit set.

>> +	/* Alternate Injection and Secure AVIC are mutually exclusive */
>> +	if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
>> +		return 0;
> 
> What happens in this situation? Will Secure AVIC still work if Alternate
> Injection is enabled?

Secure AVIC and Alternate Injection are mutually exclusive, if the 
sev_status has secure AVIC set, we should not probe svsm apic:

"15.36.21.2 VMRUN and #VMEXIT
Secure AVIC mode is mutually exclusive with Restricted Injection, 
Alternate Injection, and
hypervisor controlled AVIC modes. If the SecureAvic bit is set to 1, and 
the AVIC Enable bit in the
VMCB is set to 1 or the RestrictedInjection or AlternateInjection bits 
in SEV_FEATURES are set to 1,
VMRUN will fail with #VMEXIT(VMEXIT_INVALID)."

I'm thinking I should probably check for secure AVIC first and if set, 
return 0.

>> +
>> +	if (!x2apic_mode) {
>> +		pr_err("Alternate Injection in non x2APIC mode impossible. Terminating.\n");
>> +		sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>> +	}
>> +
>> +	pr_info("Alternate Injection SVSM APIC enabled\n");
>> +
> 
> Should the probe routine query the SVSM for the APIC protocol?

Right now, the query for the APIC protocol is hardcoded as 0 which means 
basic APIC functionality related to interrupt delivery. So it is not 
needed to query the APIC protocol now. In the future, when the SVSM code 
changes with different set, we can adjust the guest code accordingly.

> 
>> +	return 1;
>> +}
>> +
>> +static int svsm_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
>> +{
>> +	return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) && snp_vmpl;
>> +}
>> +
>> +static void svsm_apic_msr_write(u32 reg, u32 v)
>> +{
>> +	u32 msr = APIC_BASE_MSR + (reg >> 4);
>> +	struct svsm_call call = {};
>> +	int ret;
>> +
>> +	switch (reg) {
>> +	case APIC_ID:
>> +	case APIC_TASKPRI:
>> +	case APIC_PROCPRI:
>> +	case APIC_EOI:
>> +	case APIC_ISR ... APIC_ISR + 0x70:
>> +	case APIC_TMR ... APIC_TMR + 0x70:
>> +	case APIC_IRR ... APIC_IRR + 0x70:
>> +	case APIC_ICR:
>> +	case APIC_SELF_IPI:
>> +		call.rax = SVSM_APIC_CALL(SVSM_APIC_WRITE_REGISTER);
>> +		call.rcx = msr;
>> +		call.rdx = v;
>> +
>> +		ret = svsm_do_call(&call);
>> +		if (ret) {
>> +			pr_err("SVSM_APIC_WRITE_REGISTER: 0x%x, error: %d\n", reg, ret);
>> +			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> 
> Can this be a new SEV_TERM_SET_LINUX value?
> 
>> +		}
>> +		break;
>> +	default:
>> +		pr_err("SVSM_APIC_WRITE_REGISTER 0x%x not supported\n", reg);
>> +		break;
>> +	}
>> +}
>> +
>> +static u32 svsm_apic_msr_read(u32 reg)
>> +{
>> +	u32 msr = APIC_BASE_MSR + (reg >> 4);
>> +	struct svsm_call call = {};
>> +	int ret;
>> +
>> +	switch (reg) {
>> +	case APIC_ID:
>> +	case APIC_TASKPRI:
>> +	case APIC_PROCPRI:
>> +	case APIC_EOI:
>> +	case APIC_ISR ... APIC_ISR + 0x70:
>> +	case APIC_TMR ... APIC_TMR + 0x70:
>> +	case APIC_IRR ... APIC_IRR + 0x70:
>> +	case APIC_ICR:
>> +	case APIC_SELF_IPI:
>> +		call.rax = SVSM_APIC_CALL(SVSM_APIC_READ_REGISTER);
>> +		call.rcx = msr;
>> +
>> +		ret = svsm_do_call(&call);
>> +		if (ret) {
>> +			pr_err("SVSM_APIC_READ_REGISTER: 0x%x, error: %d\n", reg, ret);
>> +			sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>> +		}
>> +		break;
>> +	default:
>> +		pr_err("SVSM_APIC_READ_REGISTER: 0x%x not supported\n", reg);
>> +		return 0;
>> +	}
>> +
>> +	return call.rdx_out;
>> +}
> 
> The read and write are very similar. Can you have a common function that
> takes a reg paramter, value parameter (that is input and output), and a
> mode parameter (read/write) and then have small read and write functions?

Yes, and Sashiko pointed that I need to prevent preemption for the caa 
call, I agree with it, but I think I should prevent interrupts here - I 
should do native_local_irq_save(), because there should not be any 
interrupts during a caa call as those things are not reentrant. Thoughts?

Thanks,
Melody

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/7] x86/apic: Add an SVSM APIC driver
  2026-08-20  5:06     ` Melody Wang
@ 2026-08-20 15:43       ` Tom Lendacky
  2026-08-21  3:19         ` Melody Wang
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Lendacky @ 2026-08-20 15:43 UTC (permalink / raw)
  To: Melody Wang; +Cc: LKML, x86

On 8/20/26 00:06, Melody Wang wrote:
> Hi Tom,

Hi Melody,

> 
> On 7/31/26 10:39 AM, Tom Lendacky wrote:
>>> +int svsm_do_call(struct svsm_call *call)
>>> +{
>>> +    call->caa = svsm_get_caa();
>>> +    return svsm_perform_call_protocol(call);
>>> +}
>>
>> This helper is unneeded, all it does is set the calling area and then
>> issue svsm_perform_call_protocol(). You're already setting other call
>> values where you use this helper, so just add setting the calling area to
>> those and directly issue svsm_perform_call_protocol().
> 
> Since svsm_perform_call_protocol() and svsm_get_caa() are all internal
> functions to coco (arch/x86/coco/sev/internal.h), I can not call them
> directly from the apic driver, so perhaps it is better that I have this
> helper function here.

Why not move svsm_get_caa() and svsm_perform_call_protocol() from
internal.h then and make them available? Or create a callable function
that lives in arch/x86/coco/sev/svsm.c that builds the svsm_call struct
and performs the SVSM call?
> 
>>> diff --git a/arch/x86/kernel/apic/svsm_apic.c b/arch/x86/kernel/apic/
>>> svsm_apic.c
>>> new file mode 100644
>>> +static int svsm_apic_probe(void)
>>> +{
>>> +    if (!cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) || !snp_vmpl)
>>
>> Do you need both checks? Shouldn't the attribute only be set if the
>> feature is set and the VMPL level is greater than 0?
> 
> Yes, the cc_platform_has() just checks if the sev_status has the
> MSR_AMD64_SNP_ALTERNATE_INJ bit set.

Isn't that all that is needed? If the attribute is set none of the other
injection methods can be used. If anything, you should terminate if
alternate injection is enabled and you are running at VMPL0, because
nothing can update the VMSA to set the injection/irq fields.

> 
>>> +    /* Alternate Injection and Secure AVIC are mutually exclusive */
>>> +    if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
>>> +        return 0;
>>
>> What happens in this situation? Will Secure AVIC still work if Alternate
>> Injection is enabled?
> 
> Secure AVIC and Alternate Injection are mutually exclusive, if the
> sev_status has secure AVIC set, we should not probe svsm apic:
> 
> "15.36.21.2 VMRUN and #VMEXIT
> Secure AVIC mode is mutually exclusive with Restricted Injection,
> Alternate Injection, and
> hypervisor controlled AVIC modes. If the SecureAvic bit is set to 1, and
> the AVIC Enable bit in the
> VMCB is set to 1 or the RestrictedInjection or AlternateInjection bits
> in SEV_FEATURES are set to 1,
> VMRUN will fail with #VMEXIT(VMEXIT_INVALID)."
> 
> I'm thinking I should probably check for secure AVIC first and if set,
> return 0.

If the VMRUN fails because both are set, how can you possibly be running
in the guest with both set? So I see no need to check for Secure AVIC.
> 
>>> +
>>> +    if (!x2apic_mode) {
>>> +        pr_err("Alternate Injection in non x2APIC mode impossible.
>>> Terminating.\n");
>>> +        sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>>> +    }
>>> +
>>> +    pr_info("Alternate Injection SVSM APIC enabled\n");
>>> +
>>
>> Should the probe routine query the SVSM for the APIC protocol?
> 
> Right now, the query for the APIC protocol is hardcoded as 0 which means
> basic APIC functionality related to interrupt delivery. So it is not

What if the version of the SVSM that is running doesn't have the APIC
emulation protocol?

> needed to query the APIC protocol now. In the future, when the SVSM code
> changes with different set, we can adjust the guest code accordingly.
> 
>>
>>> +    return 1;
>>> +}
>>> +
>>> +static int svsm_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
>>> +{
>>> +    return x2apic_enabled() &&
>>> cc_platform_has(CC_ATTR_SNP_ALTERNATE_INJECTION) && snp_vmpl;
>>> +}
>>> +
>>> +static void svsm_apic_msr_write(u32 reg, u32 v)
>>> +{
>>> +    u32 msr = APIC_BASE_MSR + (reg >> 4);
>>> +    struct svsm_call call = {};
>>> +    int ret;
>>> +
>>> +    switch (reg) {
>>> +    case APIC_ID:
>>> +    case APIC_TASKPRI:
>>> +    case APIC_PROCPRI:
>>> +    case APIC_EOI:
>>> +    case APIC_ISR ... APIC_ISR + 0x70:
>>> +    case APIC_TMR ... APIC_TMR + 0x70:
>>> +    case APIC_IRR ... APIC_IRR + 0x70:
>>> +    case APIC_ICR:
>>> +    case APIC_SELF_IPI:
>>> +        call.rax = SVSM_APIC_CALL(SVSM_APIC_WRITE_REGISTER);
>>> +        call.rcx = msr;
>>> +        call.rdx = v;
>>> +
>>> +        ret = svsm_do_call(&call);
>>> +        if (ret) {
>>> +            pr_err("SVSM_APIC_WRITE_REGISTER: 0x%x, error: %d\n",
>>> reg, ret);
>>> +            sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>>
>> Can this be a new SEV_TERM_SET_LINUX value?
>>
>>> +        }
>>> +        break;
>>> +    default:
>>> +        pr_err("SVSM_APIC_WRITE_REGISTER 0x%x not supported\n", reg);
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static u32 svsm_apic_msr_read(u32 reg)
>>> +{
>>> +    u32 msr = APIC_BASE_MSR + (reg >> 4);
>>> +    struct svsm_call call = {};
>>> +    int ret;
>>> +
>>> +    switch (reg) {
>>> +    case APIC_ID:
>>> +    case APIC_TASKPRI:
>>> +    case APIC_PROCPRI:
>>> +    case APIC_EOI:
>>> +    case APIC_ISR ... APIC_ISR + 0x70:
>>> +    case APIC_TMR ... APIC_TMR + 0x70:
>>> +    case APIC_IRR ... APIC_IRR + 0x70:
>>> +    case APIC_ICR:
>>> +    case APIC_SELF_IPI:
>>> +        call.rax = SVSM_APIC_CALL(SVSM_APIC_READ_REGISTER);
>>> +        call.rcx = msr;
>>> +
>>> +        ret = svsm_do_call(&call);
>>> +        if (ret) {
>>> +            pr_err("SVSM_APIC_READ_REGISTER: 0x%x, error: %d\n",
>>> reg, ret);
>>> +            sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>>> +        }
>>> +        break;
>>> +    default:
>>> +        pr_err("SVSM_APIC_READ_REGISTER: 0x%x not supported\n", reg);
>>> +        return 0;
>>> +    }
>>> +
>>> +    return call.rdx_out;
>>> +}
>>
>> The read and write are very similar. Can you have a common function that
>> takes a reg paramter, value parameter (that is input and output), and a
>> mode parameter (read/write) and then have small read and write functions?
> 
> Yes, and Sashiko pointed that I need to prevent preemption for the caa
> call, I agree with it, but I think I should prevent interrupts here - I
> should do native_local_irq_save(), because there should not be any
> interrupts during a caa call as those things are not reentrant. Thoughts?

svsm_perform_call_protocol() already disables interrupts.

Thanks,
Tom

> 
> Thanks,
> Melody


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/7] x86/apic: Add an SVSM APIC driver
  2026-08-20 15:43       ` Tom Lendacky
@ 2026-08-21  3:19         ` Melody Wang
  2026-08-21 14:40           ` Tom Lendacky
  0 siblings, 1 reply; 19+ messages in thread
From: Melody Wang @ 2026-08-21  3:19 UTC (permalink / raw)
  To: Tom Lendacky; +Cc: LKML, x86

Hi Tom,

On 8/20/26 8:43 AM, Tom Lendacky wrote:
> Why not move svsm_get_caa() and svsm_perform_call_protocol() from
> internal.h then and make them available? Or create a callable function
> that lives in arch/x86/coco/sev/svsm.c that builds the svsm_call struct
> and performs the SVSM call?

The current svsm_do_call() actually is a callable function living in 
arch/x86/coco/sev/svsm.c except it doesn't build the call struct inside. 
I feel this way it is more clear.

What would be the advantage of building the svsm call?

> Isn't that all that is needed? If the attribute is set none of the other
> injection methods can be used. If anything, you should terminate if
> alternate injection is enabled and you are running at VMPL0, because
> nothing can update the VMSA to set the injection/irq fields.

Ok, agreed.

> If the VMRUN fails because both are set, how can you possibly be running
> in the guest with both set? So I see no need to check for Secure AVIC.

Do you mean there are already enough places to prevent this fault, so 
the guest does not need to enforce it too?

I feel it does not hurt to do it there too, in the probe function. That 
belongs there anyway.

> What if the version of the SVSM that is running doesn't have the APIC
> emulation protocol?

The Alternate Injection spec says:

"The APIC Protocol is supported only as long as Alternate Injection is 
enabled."

To me, this reads like the APIC protocol is always there when Alternate 
Injection is enabled so I can assume it's present in that case.

So I don't need to query it.

Or am I misreading the spec?

>> needed to query the APIC protocol now. In the future, when the SVSM code
>> changes with different set, we can adjust the guest code accordingly.
>>

>>> The read and write are very similar. Can you have a common function that
>>> takes a reg paramter, value parameter (that is input and output), and a
>>> mode parameter (read/write) and then have small read and write functions?
>>
>> Yes, and Sashiko pointed that I need to prevent preemption for the caa
>> call, I agree with it, but I think I should prevent interrupts here - I
>> should do native_local_irq_save(), because there should not be any
>> interrupts during a caa call as those things are not reentrant. Thoughts?
> 
> svsm_perform_call_protocol() already disables interrupts.

The problem is in svsm_get_caa(), when the task is preempted and 
migrated to a different CPU after fetching the per-CPU caa. Since this 
is preemptible code, after migration it is already wrong.

The hypercall will execute on the new CPU but use the original CPU's 
caa. This was found by Sashiko's review, I think this is a problem, so I 
need to disable preemption until svsm_perform_call_protocol() is finished.

-- 
Thanks,
Melody


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/7] x86/apic: Add an SVSM APIC driver
  2026-08-21  3:19         ` Melody Wang
@ 2026-08-21 14:40           ` Tom Lendacky
  2026-08-25  2:06             ` Melody Wang
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Lendacky @ 2026-08-21 14:40 UTC (permalink / raw)
  To: Melody Wang; +Cc: LKML, x86

On 8/20/26 22:19, Melody Wang wrote:
> Hi Tom,
> 
> On 8/20/26 8:43 AM, Tom Lendacky wrote:
>> Why not move svsm_get_caa() and svsm_perform_call_protocol() from
>> internal.h then and make them available? Or create a callable function
>> that lives in arch/x86/coco/sev/svsm.c that builds the svsm_call struct
>> and performs the SVSM call?
> 
> The current svsm_do_call() actually is a callable function living in arch/
> x86/coco/sev/svsm.c except it doesn't build the call struct inside. I feel
> this way it is more clear.
> 
> What would be the advantage of building the svsm call?

Isolation of the svsm_call struct usage. Similar to what was done for the
vTPM support (see snp_svsm_vtpm_send_command() and snp_svsm_vtpm_probe()).

> 
>> Isn't that all that is needed? If the attribute is set none of the other
>> injection methods can be used. If anything, you should terminate if
>> alternate injection is enabled and you are running at VMPL0, because
>> nothing can update the VMSA to set the injection/irq fields.
> 
> Ok, agreed.
> 
>> If the VMRUN fails because both are set, how can you possibly be running
>> in the guest with both set? So I see no need to check for Secure AVIC.
> 
> Do you mean there are already enough places to prevent this fault, so the
> guest does not need to enforce it too?
> 
> I feel it does not hurt to do it there too, in the probe function. That
> belongs there anyway.

Sure, it just seems unnecessary since Secure AVIC won't work if Alternate
Injection is enabled. So giving it priority is meaningless.

> 
>> What if the version of the SVSM that is running doesn't have the APIC
>> emulation protocol?
> 
> The Alternate Injection spec says:
> 
> "The APIC Protocol is supported only as long as Alternate Injection is
> enabled."
> 
> To me, this reads like the APIC protocol is always there when Alternate
> Injection is enabled so I can assume it's present in that case.
> 
> So I don't need to query it.
> 
> Or am I misreading the spec?

Sure, you can assume that. The worst that will happen is your call will
fail if the SVSM hasn't implemented the spec correctly.

> 
>>> needed to query the APIC protocol now. In the future, when the SVSM code
>>> changes with different set, we can adjust the guest code accordingly.
>>>
> 
>>>> The read and write are very similar. Can you have a common function that
>>>> takes a reg paramter, value parameter (that is input and output), and a
>>>> mode parameter (read/write) and then have small read and write functions?
>>>
>>> Yes, and Sashiko pointed that I need to prevent preemption for the caa
>>> call, I agree with it, but I think I should prevent interrupts here - I
>>> should do native_local_irq_save(), because there should not be any
>>> interrupts during a caa call as those things are not reentrant. Thoughts?
>>
>> svsm_perform_call_protocol() already disables interrupts.
> 
> The problem is in svsm_get_caa(), when the task is preempted and migrated
> to a different CPU after fetching the per-CPU caa. Since this is
> preemptible code, after migration it is already wrong.
> 
> The hypercall will execute on the new CPU but use the original CPU's caa.
> This was found by Sashiko's review, I think this is a problem, so I need
> to disable preemption until svsm_perform_call_protocol() is finished.

If that is the case, then the fix would be to move the setting of the CAA
into the svsm_perform_call_protocol() function and just make that function
external, which will eliminate the need for svsm_do_call().

You would be a pre-patch then for fixing all the callers of
svsm_perform_call_protocol().

Thanks,
Tom

> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/7] x86/apic: Add an SVSM APIC driver
  2026-08-21 14:40           ` Tom Lendacky
@ 2026-08-25  2:06             ` Melody Wang
  0 siblings, 0 replies; 19+ messages in thread
From: Melody Wang @ 2026-08-25  2:06 UTC (permalink / raw)
  To: Tom Lendacky; +Cc: LKML, x86

Hi Tom,

On 8/21/26 7:40 AM, Tom Lendacky wrote:

> If that is the case, then the fix would be to move the setting of the CAA
> into the svsm_perform_call_protocol() function and just make that function
> external, which will eliminate the need for svsm_do_call().
> 
> You would be a pre-patch then for fixing all the callers of
> svsm_perform_call_protocol().

This is the code, do you think this is what is in your mind?

diff --git a/arch/x86/coco/sev/svsm.c b/arch/x86/coco/sev/svsm.c
index e2d3bc3c26eb..f036a85a043b 100644
--- a/arch/x86/coco/sev/svsm.c
+++ b/arch/x86/coco/sev/svsm.c
@@ -74,6 +74,9 @@ int svsm_perform_call_protocol(struct svsm_call *call)

         flags = native_local_irq_save();

+       if (!call->caa)
+               call->caa = svsm_get_caa();
+
         ghcb = __sev_get_ghcb(&state);

         do {
@@ -87,6 +90,7 @@ int svsm_perform_call_protocol(struct svsm_call *call)

         return ret;
  }
+EXPORT_SYMBOL_GPL(svsm_perform_call_protocol);

  static u64 svsm_build_ca_from_pfn_range(u64 pfn, u64 pfn_end, bool action,
                                         struct svsm_pvalidate_call *pc)
@@ -321,7 +325,6 @@ int snp_svsm_vtpm_send_command(u8 *buffer)
  {
         struct svsm_call call = {};

-       call.caa = svsm_get_caa();
         call.rax = SVSM_VTPM_CALL(SVSM_VTPM_CMD);
         call.rcx = __pa(buffer);

@@ -345,7 +348,6 @@ bool snp_svsm_vtpm_probe(void)
         if (!snp_vmpl)
                 return false;

-       call.caa = svsm_get_caa();
         call.rax = SVSM_VTPM_CALL(SVSM_VTPM_QUERY);

         if (svsm_perform_call_protocol(&call))
@@ -354,9 +356,3 @@ bool snp_svsm_vtpm_probe(void)
         /* Check platform commands contains TPM_SEND_COMMAND - platform 
command 8 */
         return call.rcx_out & BIT_ULL(8);
  }
-
-int svsm_do_call(struct svsm_call *call)
-{
-       call->caa = svsm_get_caa();
-       return svsm_perform_call_protocol(call);
-}
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index f958f78e1db8..9013b392ad40 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -526,7 +526,6 @@ u64 sev_get_status(void);
  void sev_show_status(void);
  int prepare_pte_enc(struct pte_enc_desc *d);
  void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot);
-int svsm_do_call(struct svsm_call *call);
  void snp_kexec_finish(void);
  void snp_kexec_begin(void);

@@ -536,7 +535,7 @@ void snp_msg_free(struct snp_msg_desc *mdesc);
  int snp_send_guest_request(struct snp_msg_desc *mdesc, struct 
snp_guest_req *req);

  int snp_svsm_vtpm_send_command(u8 *buffer);
-
+int svsm_perform_call_protocol(struct svsm_call *call);
  void __init snp_secure_tsc_prepare(void);
  void __init snp_secure_tsc_init(void);
  enum es_result savic_register_gpa(u64 gpa);
@@ -619,7 +618,6 @@ static inline int rmpadjust(unsigned long vaddr, 
bool rmp_psize, unsigned long a
  static inline void setup_ghcb(void) { }
  static inline void __init
  early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, 
unsigned long npages) { }
-static inline int svsm_do_call(struct svsm_call *call) { return 0; }
  static inline void __init
  early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, 
unsigned long npages) { }
  static inline void snp_set_memory_shared(unsigned long vaddr, unsigned 
long npages) { }
@@ -645,6 +643,7 @@ static inline void snp_msg_free(struct snp_msg_desc 
*mdesc) { }
  static inline int snp_send_guest_request(struct snp_msg_desc *mdesc,
                                          struct snp_guest_req *req) { 
return -ENODEV; }
  static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return 
-ENODEV; }
+static inline int svsm_perform_call_protocol(struct svsm_call *call) { 
return -ENODEV; }
  static inline void __init snp_secure_tsc_prepare(void) { }
  static inline void __init snp_secure_tsc_init(void) { }
  static inline void sev_evict_cache(void *va, int npages) {}

Thanks,
Melody

> 
> Thanks,
> Tom
> 
>>
> 

-- 
Thanks,
Melody


^ permalink raw reply related	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-08-25  2:06 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30  1:48 [PATCH 0/7] Alternate Injection: Secure Interrupt Delivery for SEV-SNP Guests - Guest Support Melody Wang
2026-07-30  1:48 ` [PATCH 1/7] x86/sev: Add support for Alternate Injection Melody Wang
2026-07-31 15:27   ` Tom Lendacky
2026-07-30  1:48 ` [PATCH 2/7] x86/apic: Add an SVSM APIC driver Melody Wang
2026-07-31 17:39   ` Tom Lendacky
2026-08-20  5:06     ` Melody Wang
2026-08-20 15:43       ` Tom Lendacky
2026-08-21  3:19         ` Melody Wang
2026-08-21 14:40           ` Tom Lendacky
2026-08-25  2:06             ` Melody Wang
2026-07-30  1:48 ` [PATCH 3/7] x86/sev: Allow the guest to configure interrupt vectors for the hypervisor Melody Wang
2026-07-31 17:52   ` Tom Lendacky
2026-07-30  1:48 ` [PATCH 4/7] x86/sev: Route unsupported APIC register accesses to the hypervisor APIC emulation Melody Wang
2026-07-31 18:37   ` Tom Lendacky
2026-07-30  1:48 ` [PATCH 5/7] x86/sev: Add a function to contain all SEV-specific setup operations Melody Wang
2026-07-31 19:15   ` Tom Lendacky
2026-07-30  1:48 ` [PATCH 6/7] x86/sev: Register the guest with the SVSM APIC protocol Melody Wang
2026-07-31 19:18   ` Tom Lendacky
2026-07-30  1:48 ` [PATCH 7/7] x86/sev: Indicate that Alternate Injection is supported in the 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