]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
x86/apic: Initialize Secure AVIC APIC backing page
authorNeeraj Upadhyay <Neeraj.Upadhyay@amd.com>
Thu, 28 Aug 2025 07:03:18 +0000 (12:33 +0530)
committerBorislav Petkov (AMD) <bp@alien8.de>
Sun, 31 Aug 2025 19:59:07 +0000 (21:59 +0200)
With Secure AVIC, the APIC backing page is owned and managed by the guest.
Allocate and initialize APIC backing page for all guest CPUs.

The NPT entry for a vCPU's APIC backing page must always be present when the
vCPU is running in order for Secure AVIC to function. A VMEXIT_BUSY is
returned on VMRUN and the vCPU cannot be resumed otherwise.

To handle this, notify GPA of the vCPU's APIC backing page to the hypervisor
by using the SVM_VMGEXIT_SECURE_AVIC GHCB protocol event. Before executing
VMRUN, the hypervisor makes use of this information to make sure the APIC
backing page is mapped in the NPT.

  [ bp: Massage commit message. ]

Co-developed-by: Kishon Vijay Abraham I <kvijayab@amd.com>
Signed-off-by: Kishon Vijay Abraham I <kvijayab@amd.com>
Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Tianyu Lan <tiala@microsoft.com>
Link: https://lore.kernel.org/20250828070334.208401-3-Neeraj.Upadhyay@amd.com
arch/x86/coco/sev/core.c
arch/x86/include/asm/apic.h
arch/x86/include/asm/sev.h
arch/x86/include/uapi/asm/svm.h
arch/x86/kernel/apic/apic.c
arch/x86/kernel/apic/x2apic_savic.c

index f7a549f650e9c4e7dd1a384069eb333e857ede7b..7669aafcad95be5563767bcfc8a8a3593dbc3640 100644 (file)
@@ -1108,6 +1108,28 @@ int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd)
        return 0;
 }
 
+enum es_result savic_register_gpa(u64 gpa)
+{
+       struct ghcb_state state;
+       struct es_em_ctxt ctxt;
+       enum es_result res;
+       struct ghcb *ghcb;
+
+       guard(irqsave)();
+
+       ghcb = __sev_get_ghcb(&state);
+       vc_ghcb_invalidate(ghcb);
+
+       ghcb_set_rax(ghcb, SVM_VMGEXIT_SAVIC_SELF_GPA);
+       ghcb_set_rbx(ghcb, gpa);
+       res = sev_es_ghcb_hv_call(ghcb, &ctxt, SVM_VMGEXIT_SAVIC,
+                                 SVM_VMGEXIT_SAVIC_REGISTER_GPA, 0);
+
+       __sev_put_ghcb(&state);
+
+       return res;
+}
+
 static void snp_register_per_cpu_ghcb(void)
 {
        struct sev_es_runtime_data *data;
index 07ba4935e8732009a16a7026104dc425c0ccde0f..44b4080721a62548a4a8249f9658acb6e730327c 100644 (file)
@@ -305,6 +305,7 @@ struct apic {
 
        /* Probe, setup and smpboot functions */
        int     (*probe)(void);
+       void    (*setup)(void);
        int     (*acpi_madt_oem_check)(char *oem_id, char *oem_table_id);
 
        void    (*init_apic_ldr)(void);
index 02236962fdb108009e419e010d3862688e1f694d..9036122a6d455d97ad09545821ee55e613e60176 100644 (file)
@@ -533,6 +533,7 @@ int snp_svsm_vtpm_send_command(u8 *buffer);
 
 void __init snp_secure_tsc_prepare(void);
 void __init snp_secure_tsc_init(void);
+enum es_result savic_register_gpa(u64 gpa);
 
 static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
 {
@@ -605,6 +606,7 @@ static inline int snp_send_guest_request(struct snp_msg_desc *mdesc,
 static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; }
 static inline void __init snp_secure_tsc_prepare(void) { }
 static inline void __init snp_secure_tsc_init(void) { }
+static inline enum es_result savic_register_gpa(u64 gpa) { return ES_UNSUPPORTED; }
 
 #endif /* CONFIG_AMD_MEM_ENCRYPT */
 
index 9c640a521a67006da1e59df64e167808e9794252..650e3256ea7d7339091b86adfbd317ac5d25d61a 100644 (file)
 #define SVM_VMGEXIT_AP_CREATE                  1
 #define SVM_VMGEXIT_AP_DESTROY                 2
 #define SVM_VMGEXIT_SNP_RUN_VMPL               0x80000018
+#define SVM_VMGEXIT_SAVIC                      0x8000001a
+#define SVM_VMGEXIT_SAVIC_REGISTER_GPA         0
+#define SVM_VMGEXIT_SAVIC_UNREGISTER_GPA       1
+#define SVM_VMGEXIT_SAVIC_SELF_GPA             ~0ULL
 #define SVM_VMGEXIT_HV_FEATURES                        0x8000fffd
 #define SVM_VMGEXIT_TERM_REQUEST               0x8000fffe
 #define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code)       \
index ff4029b57e95676f1ad444d88943d88c495fe551..7874284c1ca7fdcafdaee1501efcf6caac1bcc8e 100644 (file)
@@ -1501,6 +1501,9 @@ static void setup_local_APIC(void)
                return;
        }
 
+       if (apic->setup)
+               apic->setup();
+
        /*
         * If this comes from kexec/kcrash the APIC might be enabled in
         * SPIV. Soft disable it before doing further initialization.
index bea844f28192c1fc9aa7aa639ec7531cf44871bd..948d89497baafdd026b9171c986ec894c3aefccf 100644 (file)
@@ -8,17 +8,47 @@
  */
 
 #include <linux/cc_platform.h>
+#include <linux/percpu-defs.h>
 
 #include <asm/apic.h>
 #include <asm/sev.h>
 
 #include "local.h"
 
+struct secure_avic_page {
+       u8 regs[PAGE_SIZE];
+} __aligned(PAGE_SIZE);
+
+static struct secure_avic_page __percpu *savic_page __ro_after_init;
+
 static int savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
 {
        return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC);
 }
 
+static void savic_setup(void)
+{
+       void *ap = this_cpu_ptr(savic_page);
+       enum es_result res;
+       unsigned long gpa;
+
+       gpa = __pa(ap);
+
+       /*
+        * The NPT entry for a vCPU's APIC backing page must always be
+        * present when the vCPU is running in order for Secure AVIC to
+        * function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot
+        * be resumed if the NPT entry for the APIC backing page is not
+        * present. Notify GPA of the vCPU's APIC backing page to the
+        * hypervisor by calling savic_register_gpa(). Before executing
+        * VMRUN, the hypervisor makes use of this information to make sure
+        * the APIC backing page is mapped in NPT.
+        */
+       res = savic_register_gpa(gpa);
+       if (res != ES_OK)
+               snp_abort();
+}
+
 static int savic_probe(void)
 {
        if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
@@ -30,6 +60,10 @@ static int savic_probe(void)
                /* unreachable */
        }
 
+       savic_page = alloc_percpu(struct secure_avic_page);
+       if (!savic_page)
+               snp_abort();
+
        return 1;
 }
 
@@ -38,6 +72,7 @@ static struct apic apic_x2apic_savic __ro_after_init = {
        .name                           = "secure avic x2apic",
        .probe                          = savic_probe,
        .acpi_madt_oem_check            = savic_acpi_madt_oem_check,
+       .setup                          = savic_setup,
 
        .dest_mode_logical              = false,