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> 12c822f58aSNeeraj 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*8c79a68dSNeeraj Upadhyay static inline void *get_reg_bitmap(unsigned int cpu, unsigned int offset) 31*8c79a68dSNeeraj Upadhyay { 32*8c79a68dSNeeraj Upadhyay return &per_cpu_ptr(savic_page, cpu)->regs[offset]; 33*8c79a68dSNeeraj Upadhyay } 34*8c79a68dSNeeraj Upadhyay 35*8c79a68dSNeeraj Upadhyay static inline void update_vector(unsigned int cpu, unsigned int offset, 36*8c79a68dSNeeraj Upadhyay unsigned int vector, bool set) 37*8c79a68dSNeeraj Upadhyay { 38*8c79a68dSNeeraj Upadhyay void *bitmap = get_reg_bitmap(cpu, offset); 39*8c79a68dSNeeraj Upadhyay 40*8c79a68dSNeeraj Upadhyay if (set) 41*8c79a68dSNeeraj Upadhyay apic_set_vector(vector, bitmap); 42*8c79a68dSNeeraj Upadhyay else 43*8c79a68dSNeeraj Upadhyay apic_clear_vector(vector, bitmap); 44*8c79a68dSNeeraj Upadhyay } 45*8c79a68dSNeeraj Upadhyay 46c822f58aSNeeraj Upadhyay #define SAVIC_ALLOWED_IRR 0x204 47c822f58aSNeeraj Upadhyay 48c822f58aSNeeraj Upadhyay /* 49c822f58aSNeeraj Upadhyay * When Secure AVIC is enabled, RDMSR/WRMSR of the APIC registers 50c822f58aSNeeraj Upadhyay * result in #VC exception (for non-accelerated register accesses) 51c822f58aSNeeraj Upadhyay * with VMEXIT_AVIC_NOACCEL error code. The #VC exception handler 52c822f58aSNeeraj Upadhyay * can read/write the x2APIC register in the guest APIC backing page. 53c822f58aSNeeraj Upadhyay * 54c822f58aSNeeraj Upadhyay * Since doing this would increase the latency of accessing x2APIC 55c822f58aSNeeraj Upadhyay * registers, instead of doing RDMSR/WRMSR based accesses and 56c822f58aSNeeraj Upadhyay * handling the APIC register reads/writes in the #VC exception handler, 57c822f58aSNeeraj Upadhyay * the read() and write() callbacks directly read/write the APIC register 58c822f58aSNeeraj Upadhyay * from/to the vCPU's APIC backing page. 59c822f58aSNeeraj Upadhyay */ 60c822f58aSNeeraj Upadhyay static u32 savic_read(u32 reg) 61c822f58aSNeeraj Upadhyay { 62c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 63c822f58aSNeeraj Upadhyay 64c822f58aSNeeraj Upadhyay switch (reg) { 65c822f58aSNeeraj Upadhyay case APIC_LVTT: 66c822f58aSNeeraj Upadhyay case APIC_TMICT: 67c822f58aSNeeraj Upadhyay case APIC_TMCCT: 68c822f58aSNeeraj Upadhyay case APIC_TDCR: 69c822f58aSNeeraj Upadhyay case APIC_ID: 70c822f58aSNeeraj Upadhyay case APIC_LVR: 71c822f58aSNeeraj Upadhyay case APIC_TASKPRI: 72c822f58aSNeeraj Upadhyay case APIC_ARBPRI: 73c822f58aSNeeraj Upadhyay case APIC_PROCPRI: 74c822f58aSNeeraj Upadhyay case APIC_LDR: 75c822f58aSNeeraj Upadhyay case APIC_SPIV: 76c822f58aSNeeraj Upadhyay case APIC_ESR: 77c822f58aSNeeraj Upadhyay case APIC_LVTTHMR: 78c822f58aSNeeraj Upadhyay case APIC_LVTPC: 79c822f58aSNeeraj Upadhyay case APIC_LVT0: 80c822f58aSNeeraj Upadhyay case APIC_LVT1: 81c822f58aSNeeraj Upadhyay case APIC_LVTERR: 82c822f58aSNeeraj Upadhyay case APIC_EFEAT: 83c822f58aSNeeraj Upadhyay case APIC_ECTRL: 84c822f58aSNeeraj Upadhyay case APIC_SEOI: 85c822f58aSNeeraj Upadhyay case APIC_IER: 86c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3): 87c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 88c822f58aSNeeraj Upadhyay case APIC_ICR: 89c822f58aSNeeraj Upadhyay return (u32)apic_get_reg64(ap, reg); 90c822f58aSNeeraj Upadhyay case APIC_ISR ... APIC_ISR + 0x70: 91c822f58aSNeeraj Upadhyay case APIC_TMR ... APIC_TMR + 0x70: 92c822f58aSNeeraj Upadhyay if (WARN_ONCE(!IS_ALIGNED(reg, 16), 93c822f58aSNeeraj Upadhyay "APIC register read offset 0x%x not aligned at 16 bytes", reg)) 94c822f58aSNeeraj Upadhyay return 0; 95c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 96c822f58aSNeeraj Upadhyay /* IRR and ALLOWED_IRR offset range */ 97c822f58aSNeeraj Upadhyay case APIC_IRR ... APIC_IRR + 0x74: 98c822f58aSNeeraj Upadhyay /* 99c822f58aSNeeraj Upadhyay * Valid APIC_IRR/SAVIC_ALLOWED_IRR registers are at 16 bytes strides from 100c822f58aSNeeraj Upadhyay * their respective base offset. APIC_IRRs are in the range 101c822f58aSNeeraj Upadhyay * 102c822f58aSNeeraj Upadhyay * (0x200, 0x210, ..., 0x270) 103c822f58aSNeeraj Upadhyay * 104c822f58aSNeeraj Upadhyay * while the SAVIC_ALLOWED_IRR range starts 4 bytes later, in the range 105c822f58aSNeeraj Upadhyay * 106c822f58aSNeeraj Upadhyay * (0x204, 0x214, ..., 0x274). 107c822f58aSNeeraj Upadhyay * 108c822f58aSNeeraj Upadhyay * Filter out everything else. 109c822f58aSNeeraj Upadhyay */ 110c822f58aSNeeraj Upadhyay if (WARN_ONCE(!(IS_ALIGNED(reg, 16) || 111c822f58aSNeeraj Upadhyay IS_ALIGNED(reg - 4, 16)), 112c822f58aSNeeraj Upadhyay "Misaligned APIC_IRR/ALLOWED_IRR APIC register read offset 0x%x", reg)) 113c822f58aSNeeraj Upadhyay return 0; 114c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 115c822f58aSNeeraj Upadhyay default: 116c822f58aSNeeraj Upadhyay pr_err("Error reading unknown Secure AVIC reg offset 0x%x\n", reg); 117c822f58aSNeeraj Upadhyay return 0; 118c822f58aSNeeraj Upadhyay } 119c822f58aSNeeraj Upadhyay } 120c822f58aSNeeraj Upadhyay 121c822f58aSNeeraj Upadhyay #define SAVIC_NMI_REQ 0x278 122c822f58aSNeeraj Upadhyay 123c822f58aSNeeraj Upadhyay static void savic_write(u32 reg, u32 data) 124c822f58aSNeeraj Upadhyay { 125c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 126c822f58aSNeeraj Upadhyay 127c822f58aSNeeraj Upadhyay switch (reg) { 128c822f58aSNeeraj Upadhyay case APIC_LVTT: 129c822f58aSNeeraj Upadhyay case APIC_LVT0: 130c822f58aSNeeraj Upadhyay case APIC_LVT1: 131c822f58aSNeeraj Upadhyay case APIC_TMICT: 132c822f58aSNeeraj Upadhyay case APIC_TDCR: 133c822f58aSNeeraj Upadhyay case APIC_SELF_IPI: 134c822f58aSNeeraj Upadhyay case APIC_TASKPRI: 135c822f58aSNeeraj Upadhyay case APIC_EOI: 136c822f58aSNeeraj Upadhyay case APIC_SPIV: 137c822f58aSNeeraj Upadhyay case SAVIC_NMI_REQ: 138c822f58aSNeeraj Upadhyay case APIC_ESR: 139c822f58aSNeeraj Upadhyay case APIC_LVTTHMR: 140c822f58aSNeeraj Upadhyay case APIC_LVTPC: 141c822f58aSNeeraj Upadhyay case APIC_LVTERR: 142c822f58aSNeeraj Upadhyay case APIC_ECTRL: 143c822f58aSNeeraj Upadhyay case APIC_SEOI: 144c822f58aSNeeraj Upadhyay case APIC_IER: 145c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3): 146c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data); 147c822f58aSNeeraj Upadhyay break; 148c822f58aSNeeraj Upadhyay case APIC_ICR: 149c822f58aSNeeraj Upadhyay apic_set_reg64(ap, reg, (u64)data); 150c822f58aSNeeraj Upadhyay break; 151c822f58aSNeeraj Upadhyay /* ALLOWED_IRR offsets are writable */ 152c822f58aSNeeraj Upadhyay case SAVIC_ALLOWED_IRR ... SAVIC_ALLOWED_IRR + 0x70: 153c822f58aSNeeraj Upadhyay if (IS_ALIGNED(reg - 4, 16)) { 154c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data); 155c822f58aSNeeraj Upadhyay break; 156c822f58aSNeeraj Upadhyay } 157c822f58aSNeeraj Upadhyay fallthrough; 158c822f58aSNeeraj Upadhyay default: 159c822f58aSNeeraj Upadhyay pr_err("Error writing unknown Secure AVIC reg offset 0x%x\n", reg); 160c822f58aSNeeraj Upadhyay } 161c822f58aSNeeraj Upadhyay } 162c822f58aSNeeraj Upadhyay 163*8c79a68dSNeeraj Upadhyay static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set) 164*8c79a68dSNeeraj Upadhyay { 165*8c79a68dSNeeraj Upadhyay update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set); 166*8c79a68dSNeeraj Upadhyay } 167*8c79a68dSNeeraj Upadhyay 168b8c3c9f5SNeeraj Upadhyay static void savic_setup(void) 169b8c3c9f5SNeeraj Upadhyay { 170b8c3c9f5SNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 171b8c3c9f5SNeeraj Upadhyay enum es_result res; 172b8c3c9f5SNeeraj Upadhyay unsigned long gpa; 173b8c3c9f5SNeeraj Upadhyay 17445e2cef5SNeeraj Upadhyay /* 17545e2cef5SNeeraj Upadhyay * Before Secure AVIC is enabled, APIC MSR reads are intercepted. 17645e2cef5SNeeraj Upadhyay * APIC_ID MSR read returns the value from the hypervisor. 17745e2cef5SNeeraj Upadhyay */ 17845e2cef5SNeeraj Upadhyay apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID)); 17945e2cef5SNeeraj Upadhyay 180b8c3c9f5SNeeraj Upadhyay gpa = __pa(ap); 181b8c3c9f5SNeeraj Upadhyay 182b8c3c9f5SNeeraj Upadhyay /* 183b8c3c9f5SNeeraj Upadhyay * The NPT entry for a vCPU's APIC backing page must always be 184b8c3c9f5SNeeraj Upadhyay * present when the vCPU is running in order for Secure AVIC to 185b8c3c9f5SNeeraj Upadhyay * function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot 186b8c3c9f5SNeeraj Upadhyay * be resumed if the NPT entry for the APIC backing page is not 187b8c3c9f5SNeeraj Upadhyay * present. Notify GPA of the vCPU's APIC backing page to the 188b8c3c9f5SNeeraj Upadhyay * hypervisor by calling savic_register_gpa(). Before executing 189b8c3c9f5SNeeraj Upadhyay * VMRUN, the hypervisor makes use of this information to make sure 190b8c3c9f5SNeeraj Upadhyay * the APIC backing page is mapped in NPT. 191b8c3c9f5SNeeraj Upadhyay */ 192b8c3c9f5SNeeraj Upadhyay res = savic_register_gpa(gpa); 193b8c3c9f5SNeeraj Upadhyay if (res != ES_OK) 194b8c3c9f5SNeeraj Upadhyay snp_abort(); 195b8c3c9f5SNeeraj Upadhyay } 196b8c3c9f5SNeeraj Upadhyay 19730c2b98aSNeeraj Upadhyay static int savic_probe(void) 19830c2b98aSNeeraj Upadhyay { 19930c2b98aSNeeraj Upadhyay if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC)) 20030c2b98aSNeeraj Upadhyay return 0; 20130c2b98aSNeeraj Upadhyay 20230c2b98aSNeeraj Upadhyay if (!x2apic_mode) { 20330c2b98aSNeeraj Upadhyay pr_err("Secure AVIC enabled in non x2APIC mode\n"); 20430c2b98aSNeeraj Upadhyay snp_abort(); 20530c2b98aSNeeraj Upadhyay /* unreachable */ 20630c2b98aSNeeraj Upadhyay } 20730c2b98aSNeeraj Upadhyay 208b8c3c9f5SNeeraj Upadhyay savic_page = alloc_percpu(struct secure_avic_page); 209b8c3c9f5SNeeraj Upadhyay if (!savic_page) 210b8c3c9f5SNeeraj Upadhyay snp_abort(); 211b8c3c9f5SNeeraj Upadhyay 21230c2b98aSNeeraj Upadhyay return 1; 21330c2b98aSNeeraj Upadhyay } 21430c2b98aSNeeraj Upadhyay 21530c2b98aSNeeraj Upadhyay static struct apic apic_x2apic_savic __ro_after_init = { 21630c2b98aSNeeraj Upadhyay 21730c2b98aSNeeraj Upadhyay .name = "secure avic x2apic", 21830c2b98aSNeeraj Upadhyay .probe = savic_probe, 21930c2b98aSNeeraj Upadhyay .acpi_madt_oem_check = savic_acpi_madt_oem_check, 220b8c3c9f5SNeeraj Upadhyay .setup = savic_setup, 22130c2b98aSNeeraj Upadhyay 22230c2b98aSNeeraj Upadhyay .dest_mode_logical = false, 22330c2b98aSNeeraj Upadhyay 22430c2b98aSNeeraj Upadhyay .disable_esr = 0, 22530c2b98aSNeeraj Upadhyay 22630c2b98aSNeeraj Upadhyay .cpu_present_to_apicid = default_cpu_present_to_apicid, 22730c2b98aSNeeraj Upadhyay 22830c2b98aSNeeraj Upadhyay .max_apic_id = UINT_MAX, 22930c2b98aSNeeraj Upadhyay .x2apic_set_max_apicid = true, 23030c2b98aSNeeraj Upadhyay .get_apic_id = x2apic_get_apic_id, 23130c2b98aSNeeraj Upadhyay 23230c2b98aSNeeraj Upadhyay .calc_dest_apicid = apic_default_calc_apicid, 23330c2b98aSNeeraj Upadhyay 23430c2b98aSNeeraj Upadhyay .nmi_to_offline_cpu = true, 23530c2b98aSNeeraj Upadhyay 236c822f58aSNeeraj Upadhyay .read = savic_read, 237c822f58aSNeeraj Upadhyay .write = savic_write, 23830c2b98aSNeeraj Upadhyay .eoi = native_apic_msr_eoi, 23930c2b98aSNeeraj Upadhyay .icr_read = native_x2apic_icr_read, 24030c2b98aSNeeraj Upadhyay .icr_write = native_x2apic_icr_write, 241*8c79a68dSNeeraj Upadhyay 242*8c79a68dSNeeraj Upadhyay .update_vector = savic_update_vector, 24330c2b98aSNeeraj Upadhyay }; 24430c2b98aSNeeraj Upadhyay 24530c2b98aSNeeraj Upadhyay apic_driver(apic_x2apic_savic); 246