130c2b98aSNeeraj Upadhyay // SPDX-License-Identifier: GPL-2.0-only 230c2b98aSNeeraj Upadhyay /* 330c2b98aSNeeraj Upadhyay * AMD Secure AVIC Support (SEV-SNP Guests) 430c2b98aSNeeraj Upadhyay * 530c2b98aSNeeraj Upadhyay * Copyright (C) 2024 Advanced Micro Devices, Inc. 630c2b98aSNeeraj Upadhyay * 730c2b98aSNeeraj Upadhyay * Author: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com> 830c2b98aSNeeraj Upadhyay */ 930c2b98aSNeeraj Upadhyay 1030c2b98aSNeeraj Upadhyay #include <linux/cc_platform.h> 11b8c3c9f5SNeeraj Upadhyay #include <linux/percpu-defs.h> 12*c822f58aSNeeraj Upadhyay #include <linux/align.h> 1330c2b98aSNeeraj Upadhyay 1430c2b98aSNeeraj Upadhyay #include <asm/apic.h> 1530c2b98aSNeeraj Upadhyay #include <asm/sev.h> 1630c2b98aSNeeraj Upadhyay 1730c2b98aSNeeraj Upadhyay #include "local.h" 1830c2b98aSNeeraj Upadhyay 19b8c3c9f5SNeeraj Upadhyay struct secure_avic_page { 20b8c3c9f5SNeeraj Upadhyay u8 regs[PAGE_SIZE]; 21b8c3c9f5SNeeraj Upadhyay } __aligned(PAGE_SIZE); 22b8c3c9f5SNeeraj Upadhyay 23b8c3c9f5SNeeraj Upadhyay static struct secure_avic_page __percpu *savic_page __ro_after_init; 24b8c3c9f5SNeeraj Upadhyay 2530c2b98aSNeeraj Upadhyay static int savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id) 2630c2b98aSNeeraj Upadhyay { 2730c2b98aSNeeraj Upadhyay return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC); 2830c2b98aSNeeraj Upadhyay } 2930c2b98aSNeeraj Upadhyay 30*c822f58aSNeeraj Upadhyay #define SAVIC_ALLOWED_IRR 0x204 31*c822f58aSNeeraj Upadhyay 32*c822f58aSNeeraj Upadhyay /* 33*c822f58aSNeeraj Upadhyay * When Secure AVIC is enabled, RDMSR/WRMSR of the APIC registers 34*c822f58aSNeeraj Upadhyay * result in #VC exception (for non-accelerated register accesses) 35*c822f58aSNeeraj Upadhyay * with VMEXIT_AVIC_NOACCEL error code. The #VC exception handler 36*c822f58aSNeeraj Upadhyay * can read/write the x2APIC register in the guest APIC backing page. 37*c822f58aSNeeraj Upadhyay * 38*c822f58aSNeeraj Upadhyay * Since doing this would increase the latency of accessing x2APIC 39*c822f58aSNeeraj Upadhyay * registers, instead of doing RDMSR/WRMSR based accesses and 40*c822f58aSNeeraj Upadhyay * handling the APIC register reads/writes in the #VC exception handler, 41*c822f58aSNeeraj Upadhyay * the read() and write() callbacks directly read/write the APIC register 42*c822f58aSNeeraj Upadhyay * from/to the vCPU's APIC backing page. 43*c822f58aSNeeraj Upadhyay */ 44*c822f58aSNeeraj Upadhyay static u32 savic_read(u32 reg) 45*c822f58aSNeeraj Upadhyay { 46*c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 47*c822f58aSNeeraj Upadhyay 48*c822f58aSNeeraj Upadhyay switch (reg) { 49*c822f58aSNeeraj Upadhyay case APIC_LVTT: 50*c822f58aSNeeraj Upadhyay case APIC_TMICT: 51*c822f58aSNeeraj Upadhyay case APIC_TMCCT: 52*c822f58aSNeeraj Upadhyay case APIC_TDCR: 53*c822f58aSNeeraj Upadhyay case APIC_ID: 54*c822f58aSNeeraj Upadhyay case APIC_LVR: 55*c822f58aSNeeraj Upadhyay case APIC_TASKPRI: 56*c822f58aSNeeraj Upadhyay case APIC_ARBPRI: 57*c822f58aSNeeraj Upadhyay case APIC_PROCPRI: 58*c822f58aSNeeraj Upadhyay case APIC_LDR: 59*c822f58aSNeeraj Upadhyay case APIC_SPIV: 60*c822f58aSNeeraj Upadhyay case APIC_ESR: 61*c822f58aSNeeraj Upadhyay case APIC_LVTTHMR: 62*c822f58aSNeeraj Upadhyay case APIC_LVTPC: 63*c822f58aSNeeraj Upadhyay case APIC_LVT0: 64*c822f58aSNeeraj Upadhyay case APIC_LVT1: 65*c822f58aSNeeraj Upadhyay case APIC_LVTERR: 66*c822f58aSNeeraj Upadhyay case APIC_EFEAT: 67*c822f58aSNeeraj Upadhyay case APIC_ECTRL: 68*c822f58aSNeeraj Upadhyay case APIC_SEOI: 69*c822f58aSNeeraj Upadhyay case APIC_IER: 70*c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3): 71*c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 72*c822f58aSNeeraj Upadhyay case APIC_ICR: 73*c822f58aSNeeraj Upadhyay return (u32)apic_get_reg64(ap, reg); 74*c822f58aSNeeraj Upadhyay case APIC_ISR ... APIC_ISR + 0x70: 75*c822f58aSNeeraj Upadhyay case APIC_TMR ... APIC_TMR + 0x70: 76*c822f58aSNeeraj Upadhyay if (WARN_ONCE(!IS_ALIGNED(reg, 16), 77*c822f58aSNeeraj Upadhyay "APIC register read offset 0x%x not aligned at 16 bytes", reg)) 78*c822f58aSNeeraj Upadhyay return 0; 79*c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 80*c822f58aSNeeraj Upadhyay /* IRR and ALLOWED_IRR offset range */ 81*c822f58aSNeeraj Upadhyay case APIC_IRR ... APIC_IRR + 0x74: 82*c822f58aSNeeraj Upadhyay /* 83*c822f58aSNeeraj Upadhyay * Valid APIC_IRR/SAVIC_ALLOWED_IRR registers are at 16 bytes strides from 84*c822f58aSNeeraj Upadhyay * their respective base offset. APIC_IRRs are in the range 85*c822f58aSNeeraj Upadhyay * 86*c822f58aSNeeraj Upadhyay * (0x200, 0x210, ..., 0x270) 87*c822f58aSNeeraj Upadhyay * 88*c822f58aSNeeraj Upadhyay * while the SAVIC_ALLOWED_IRR range starts 4 bytes later, in the range 89*c822f58aSNeeraj Upadhyay * 90*c822f58aSNeeraj Upadhyay * (0x204, 0x214, ..., 0x274). 91*c822f58aSNeeraj Upadhyay * 92*c822f58aSNeeraj Upadhyay * Filter out everything else. 93*c822f58aSNeeraj Upadhyay */ 94*c822f58aSNeeraj Upadhyay if (WARN_ONCE(!(IS_ALIGNED(reg, 16) || 95*c822f58aSNeeraj Upadhyay IS_ALIGNED(reg - 4, 16)), 96*c822f58aSNeeraj Upadhyay "Misaligned APIC_IRR/ALLOWED_IRR APIC register read offset 0x%x", reg)) 97*c822f58aSNeeraj Upadhyay return 0; 98*c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 99*c822f58aSNeeraj Upadhyay default: 100*c822f58aSNeeraj Upadhyay pr_err("Error reading unknown Secure AVIC reg offset 0x%x\n", reg); 101*c822f58aSNeeraj Upadhyay return 0; 102*c822f58aSNeeraj Upadhyay } 103*c822f58aSNeeraj Upadhyay } 104*c822f58aSNeeraj Upadhyay 105*c822f58aSNeeraj Upadhyay #define SAVIC_NMI_REQ 0x278 106*c822f58aSNeeraj Upadhyay 107*c822f58aSNeeraj Upadhyay static void savic_write(u32 reg, u32 data) 108*c822f58aSNeeraj Upadhyay { 109*c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 110*c822f58aSNeeraj Upadhyay 111*c822f58aSNeeraj Upadhyay switch (reg) { 112*c822f58aSNeeraj Upadhyay case APIC_LVTT: 113*c822f58aSNeeraj Upadhyay case APIC_LVT0: 114*c822f58aSNeeraj Upadhyay case APIC_LVT1: 115*c822f58aSNeeraj Upadhyay case APIC_TMICT: 116*c822f58aSNeeraj Upadhyay case APIC_TDCR: 117*c822f58aSNeeraj Upadhyay case APIC_SELF_IPI: 118*c822f58aSNeeraj Upadhyay case APIC_TASKPRI: 119*c822f58aSNeeraj Upadhyay case APIC_EOI: 120*c822f58aSNeeraj Upadhyay case APIC_SPIV: 121*c822f58aSNeeraj Upadhyay case SAVIC_NMI_REQ: 122*c822f58aSNeeraj Upadhyay case APIC_ESR: 123*c822f58aSNeeraj Upadhyay case APIC_LVTTHMR: 124*c822f58aSNeeraj Upadhyay case APIC_LVTPC: 125*c822f58aSNeeraj Upadhyay case APIC_LVTERR: 126*c822f58aSNeeraj Upadhyay case APIC_ECTRL: 127*c822f58aSNeeraj Upadhyay case APIC_SEOI: 128*c822f58aSNeeraj Upadhyay case APIC_IER: 129*c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3): 130*c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data); 131*c822f58aSNeeraj Upadhyay break; 132*c822f58aSNeeraj Upadhyay case APIC_ICR: 133*c822f58aSNeeraj Upadhyay apic_set_reg64(ap, reg, (u64)data); 134*c822f58aSNeeraj Upadhyay break; 135*c822f58aSNeeraj Upadhyay /* ALLOWED_IRR offsets are writable */ 136*c822f58aSNeeraj Upadhyay case SAVIC_ALLOWED_IRR ... SAVIC_ALLOWED_IRR + 0x70: 137*c822f58aSNeeraj Upadhyay if (IS_ALIGNED(reg - 4, 16)) { 138*c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data); 139*c822f58aSNeeraj Upadhyay break; 140*c822f58aSNeeraj Upadhyay } 141*c822f58aSNeeraj Upadhyay fallthrough; 142*c822f58aSNeeraj Upadhyay default: 143*c822f58aSNeeraj Upadhyay pr_err("Error writing unknown Secure AVIC reg offset 0x%x\n", reg); 144*c822f58aSNeeraj Upadhyay } 145*c822f58aSNeeraj Upadhyay } 146*c822f58aSNeeraj Upadhyay 147b8c3c9f5SNeeraj Upadhyay static void savic_setup(void) 148b8c3c9f5SNeeraj Upadhyay { 149b8c3c9f5SNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 150b8c3c9f5SNeeraj Upadhyay enum es_result res; 151b8c3c9f5SNeeraj Upadhyay unsigned long gpa; 152b8c3c9f5SNeeraj Upadhyay 153b8c3c9f5SNeeraj Upadhyay gpa = __pa(ap); 154b8c3c9f5SNeeraj Upadhyay 155b8c3c9f5SNeeraj Upadhyay /* 156b8c3c9f5SNeeraj Upadhyay * The NPT entry for a vCPU's APIC backing page must always be 157b8c3c9f5SNeeraj Upadhyay * present when the vCPU is running in order for Secure AVIC to 158b8c3c9f5SNeeraj Upadhyay * function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot 159b8c3c9f5SNeeraj Upadhyay * be resumed if the NPT entry for the APIC backing page is not 160b8c3c9f5SNeeraj Upadhyay * present. Notify GPA of the vCPU's APIC backing page to the 161b8c3c9f5SNeeraj Upadhyay * hypervisor by calling savic_register_gpa(). Before executing 162b8c3c9f5SNeeraj Upadhyay * VMRUN, the hypervisor makes use of this information to make sure 163b8c3c9f5SNeeraj Upadhyay * the APIC backing page is mapped in NPT. 164b8c3c9f5SNeeraj Upadhyay */ 165b8c3c9f5SNeeraj Upadhyay res = savic_register_gpa(gpa); 166b8c3c9f5SNeeraj Upadhyay if (res != ES_OK) 167b8c3c9f5SNeeraj Upadhyay snp_abort(); 168b8c3c9f5SNeeraj Upadhyay } 169b8c3c9f5SNeeraj Upadhyay 17030c2b98aSNeeraj Upadhyay static int savic_probe(void) 17130c2b98aSNeeraj Upadhyay { 17230c2b98aSNeeraj Upadhyay if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC)) 17330c2b98aSNeeraj Upadhyay return 0; 17430c2b98aSNeeraj Upadhyay 17530c2b98aSNeeraj Upadhyay if (!x2apic_mode) { 17630c2b98aSNeeraj Upadhyay pr_err("Secure AVIC enabled in non x2APIC mode\n"); 17730c2b98aSNeeraj Upadhyay snp_abort(); 17830c2b98aSNeeraj Upadhyay /* unreachable */ 17930c2b98aSNeeraj Upadhyay } 18030c2b98aSNeeraj Upadhyay 181b8c3c9f5SNeeraj Upadhyay savic_page = alloc_percpu(struct secure_avic_page); 182b8c3c9f5SNeeraj Upadhyay if (!savic_page) 183b8c3c9f5SNeeraj Upadhyay snp_abort(); 184b8c3c9f5SNeeraj Upadhyay 18530c2b98aSNeeraj Upadhyay return 1; 18630c2b98aSNeeraj Upadhyay } 18730c2b98aSNeeraj Upadhyay 18830c2b98aSNeeraj Upadhyay static struct apic apic_x2apic_savic __ro_after_init = { 18930c2b98aSNeeraj Upadhyay 19030c2b98aSNeeraj Upadhyay .name = "secure avic x2apic", 19130c2b98aSNeeraj Upadhyay .probe = savic_probe, 19230c2b98aSNeeraj Upadhyay .acpi_madt_oem_check = savic_acpi_madt_oem_check, 193b8c3c9f5SNeeraj Upadhyay .setup = savic_setup, 19430c2b98aSNeeraj Upadhyay 19530c2b98aSNeeraj Upadhyay .dest_mode_logical = false, 19630c2b98aSNeeraj Upadhyay 19730c2b98aSNeeraj Upadhyay .disable_esr = 0, 19830c2b98aSNeeraj Upadhyay 19930c2b98aSNeeraj Upadhyay .cpu_present_to_apicid = default_cpu_present_to_apicid, 20030c2b98aSNeeraj Upadhyay 20130c2b98aSNeeraj Upadhyay .max_apic_id = UINT_MAX, 20230c2b98aSNeeraj Upadhyay .x2apic_set_max_apicid = true, 20330c2b98aSNeeraj Upadhyay .get_apic_id = x2apic_get_apic_id, 20430c2b98aSNeeraj Upadhyay 20530c2b98aSNeeraj Upadhyay .calc_dest_apicid = apic_default_calc_apicid, 20630c2b98aSNeeraj Upadhyay 20730c2b98aSNeeraj Upadhyay .nmi_to_offline_cpu = true, 20830c2b98aSNeeraj Upadhyay 209*c822f58aSNeeraj Upadhyay .read = savic_read, 210*c822f58aSNeeraj Upadhyay .write = savic_write, 21130c2b98aSNeeraj Upadhyay .eoi = native_apic_msr_eoi, 21230c2b98aSNeeraj Upadhyay .icr_read = native_x2apic_icr_read, 21330c2b98aSNeeraj Upadhyay .icr_write = native_x2apic_icr_write, 21430c2b98aSNeeraj Upadhyay }; 21530c2b98aSNeeraj Upadhyay 21630c2b98aSNeeraj Upadhyay apic_driver(apic_x2apic_savic); 217