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 30c822f58aSNeeraj Upadhyay #define SAVIC_ALLOWED_IRR 0x204 31c822f58aSNeeraj Upadhyay 32c822f58aSNeeraj Upadhyay /* 33c822f58aSNeeraj Upadhyay * When Secure AVIC is enabled, RDMSR/WRMSR of the APIC registers 34c822f58aSNeeraj Upadhyay * result in #VC exception (for non-accelerated register accesses) 35c822f58aSNeeraj Upadhyay * with VMEXIT_AVIC_NOACCEL error code. The #VC exception handler 36c822f58aSNeeraj Upadhyay * can read/write the x2APIC register in the guest APIC backing page. 37c822f58aSNeeraj Upadhyay * 38c822f58aSNeeraj Upadhyay * Since doing this would increase the latency of accessing x2APIC 39c822f58aSNeeraj Upadhyay * registers, instead of doing RDMSR/WRMSR based accesses and 40c822f58aSNeeraj Upadhyay * handling the APIC register reads/writes in the #VC exception handler, 41c822f58aSNeeraj Upadhyay * the read() and write() callbacks directly read/write the APIC register 42c822f58aSNeeraj Upadhyay * from/to the vCPU's APIC backing page. 43c822f58aSNeeraj Upadhyay */ 44c822f58aSNeeraj Upadhyay static u32 savic_read(u32 reg) 45c822f58aSNeeraj Upadhyay { 46c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 47c822f58aSNeeraj Upadhyay 48c822f58aSNeeraj Upadhyay switch (reg) { 49c822f58aSNeeraj Upadhyay case APIC_LVTT: 50c822f58aSNeeraj Upadhyay case APIC_TMICT: 51c822f58aSNeeraj Upadhyay case APIC_TMCCT: 52c822f58aSNeeraj Upadhyay case APIC_TDCR: 53c822f58aSNeeraj Upadhyay case APIC_ID: 54c822f58aSNeeraj Upadhyay case APIC_LVR: 55c822f58aSNeeraj Upadhyay case APIC_TASKPRI: 56c822f58aSNeeraj Upadhyay case APIC_ARBPRI: 57c822f58aSNeeraj Upadhyay case APIC_PROCPRI: 58c822f58aSNeeraj Upadhyay case APIC_LDR: 59c822f58aSNeeraj Upadhyay case APIC_SPIV: 60c822f58aSNeeraj Upadhyay case APIC_ESR: 61c822f58aSNeeraj Upadhyay case APIC_LVTTHMR: 62c822f58aSNeeraj Upadhyay case APIC_LVTPC: 63c822f58aSNeeraj Upadhyay case APIC_LVT0: 64c822f58aSNeeraj Upadhyay case APIC_LVT1: 65c822f58aSNeeraj Upadhyay case APIC_LVTERR: 66c822f58aSNeeraj Upadhyay case APIC_EFEAT: 67c822f58aSNeeraj Upadhyay case APIC_ECTRL: 68c822f58aSNeeraj Upadhyay case APIC_SEOI: 69c822f58aSNeeraj Upadhyay case APIC_IER: 70c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3): 71c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 72c822f58aSNeeraj Upadhyay case APIC_ICR: 73c822f58aSNeeraj Upadhyay return (u32)apic_get_reg64(ap, reg); 74c822f58aSNeeraj Upadhyay case APIC_ISR ... APIC_ISR + 0x70: 75c822f58aSNeeraj Upadhyay case APIC_TMR ... APIC_TMR + 0x70: 76c822f58aSNeeraj Upadhyay if (WARN_ONCE(!IS_ALIGNED(reg, 16), 77c822f58aSNeeraj Upadhyay "APIC register read offset 0x%x not aligned at 16 bytes", reg)) 78c822f58aSNeeraj Upadhyay return 0; 79c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 80c822f58aSNeeraj Upadhyay /* IRR and ALLOWED_IRR offset range */ 81c822f58aSNeeraj Upadhyay case APIC_IRR ... APIC_IRR + 0x74: 82c822f58aSNeeraj Upadhyay /* 83c822f58aSNeeraj Upadhyay * Valid APIC_IRR/SAVIC_ALLOWED_IRR registers are at 16 bytes strides from 84c822f58aSNeeraj Upadhyay * their respective base offset. APIC_IRRs are in the range 85c822f58aSNeeraj Upadhyay * 86c822f58aSNeeraj Upadhyay * (0x200, 0x210, ..., 0x270) 87c822f58aSNeeraj Upadhyay * 88c822f58aSNeeraj Upadhyay * while the SAVIC_ALLOWED_IRR range starts 4 bytes later, in the range 89c822f58aSNeeraj Upadhyay * 90c822f58aSNeeraj Upadhyay * (0x204, 0x214, ..., 0x274). 91c822f58aSNeeraj Upadhyay * 92c822f58aSNeeraj Upadhyay * Filter out everything else. 93c822f58aSNeeraj Upadhyay */ 94c822f58aSNeeraj Upadhyay if (WARN_ONCE(!(IS_ALIGNED(reg, 16) || 95c822f58aSNeeraj Upadhyay IS_ALIGNED(reg - 4, 16)), 96c822f58aSNeeraj Upadhyay "Misaligned APIC_IRR/ALLOWED_IRR APIC register read offset 0x%x", reg)) 97c822f58aSNeeraj Upadhyay return 0; 98c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg); 99c822f58aSNeeraj Upadhyay default: 100c822f58aSNeeraj Upadhyay pr_err("Error reading unknown Secure AVIC reg offset 0x%x\n", reg); 101c822f58aSNeeraj Upadhyay return 0; 102c822f58aSNeeraj Upadhyay } 103c822f58aSNeeraj Upadhyay } 104c822f58aSNeeraj Upadhyay 105c822f58aSNeeraj Upadhyay #define SAVIC_NMI_REQ 0x278 106c822f58aSNeeraj Upadhyay 107c822f58aSNeeraj Upadhyay static void savic_write(u32 reg, u32 data) 108c822f58aSNeeraj Upadhyay { 109c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page); 110c822f58aSNeeraj Upadhyay 111c822f58aSNeeraj Upadhyay switch (reg) { 112c822f58aSNeeraj Upadhyay case APIC_LVTT: 113c822f58aSNeeraj Upadhyay case APIC_LVT0: 114c822f58aSNeeraj Upadhyay case APIC_LVT1: 115c822f58aSNeeraj Upadhyay case APIC_TMICT: 116c822f58aSNeeraj Upadhyay case APIC_TDCR: 117c822f58aSNeeraj Upadhyay case APIC_SELF_IPI: 118c822f58aSNeeraj Upadhyay case APIC_TASKPRI: 119c822f58aSNeeraj Upadhyay case APIC_EOI: 120c822f58aSNeeraj Upadhyay case APIC_SPIV: 121c822f58aSNeeraj Upadhyay case SAVIC_NMI_REQ: 122c822f58aSNeeraj Upadhyay case APIC_ESR: 123c822f58aSNeeraj Upadhyay case APIC_LVTTHMR: 124c822f58aSNeeraj Upadhyay case APIC_LVTPC: 125c822f58aSNeeraj Upadhyay case APIC_LVTERR: 126c822f58aSNeeraj Upadhyay case APIC_ECTRL: 127c822f58aSNeeraj Upadhyay case APIC_SEOI: 128c822f58aSNeeraj Upadhyay case APIC_IER: 129c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3): 130c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data); 131c822f58aSNeeraj Upadhyay break; 132c822f58aSNeeraj Upadhyay case APIC_ICR: 133c822f58aSNeeraj Upadhyay apic_set_reg64(ap, reg, (u64)data); 134c822f58aSNeeraj Upadhyay break; 135c822f58aSNeeraj Upadhyay /* ALLOWED_IRR offsets are writable */ 136c822f58aSNeeraj Upadhyay case SAVIC_ALLOWED_IRR ... SAVIC_ALLOWED_IRR + 0x70: 137c822f58aSNeeraj Upadhyay if (IS_ALIGNED(reg - 4, 16)) { 138c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data); 139c822f58aSNeeraj Upadhyay break; 140c822f58aSNeeraj Upadhyay } 141c822f58aSNeeraj Upadhyay fallthrough; 142c822f58aSNeeraj Upadhyay default: 143c822f58aSNeeraj Upadhyay pr_err("Error writing unknown Secure AVIC reg offset 0x%x\n", reg); 144c822f58aSNeeraj Upadhyay } 145c822f58aSNeeraj Upadhyay } 146c822f58aSNeeraj 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 153*45e2cef5SNeeraj Upadhyay /* 154*45e2cef5SNeeraj Upadhyay * Before Secure AVIC is enabled, APIC MSR reads are intercepted. 155*45e2cef5SNeeraj Upadhyay * APIC_ID MSR read returns the value from the hypervisor. 156*45e2cef5SNeeraj Upadhyay */ 157*45e2cef5SNeeraj Upadhyay apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID)); 158*45e2cef5SNeeraj Upadhyay 159b8c3c9f5SNeeraj Upadhyay gpa = __pa(ap); 160b8c3c9f5SNeeraj Upadhyay 161b8c3c9f5SNeeraj Upadhyay /* 162b8c3c9f5SNeeraj Upadhyay * The NPT entry for a vCPU's APIC backing page must always be 163b8c3c9f5SNeeraj Upadhyay * present when the vCPU is running in order for Secure AVIC to 164b8c3c9f5SNeeraj Upadhyay * function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot 165b8c3c9f5SNeeraj Upadhyay * be resumed if the NPT entry for the APIC backing page is not 166b8c3c9f5SNeeraj Upadhyay * present. Notify GPA of the vCPU's APIC backing page to the 167b8c3c9f5SNeeraj Upadhyay * hypervisor by calling savic_register_gpa(). Before executing 168b8c3c9f5SNeeraj Upadhyay * VMRUN, the hypervisor makes use of this information to make sure 169b8c3c9f5SNeeraj Upadhyay * the APIC backing page is mapped in NPT. 170b8c3c9f5SNeeraj Upadhyay */ 171b8c3c9f5SNeeraj Upadhyay res = savic_register_gpa(gpa); 172b8c3c9f5SNeeraj Upadhyay if (res != ES_OK) 173b8c3c9f5SNeeraj Upadhyay snp_abort(); 174b8c3c9f5SNeeraj Upadhyay } 175b8c3c9f5SNeeraj Upadhyay 17630c2b98aSNeeraj Upadhyay static int savic_probe(void) 17730c2b98aSNeeraj Upadhyay { 17830c2b98aSNeeraj Upadhyay if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC)) 17930c2b98aSNeeraj Upadhyay return 0; 18030c2b98aSNeeraj Upadhyay 18130c2b98aSNeeraj Upadhyay if (!x2apic_mode) { 18230c2b98aSNeeraj Upadhyay pr_err("Secure AVIC enabled in non x2APIC mode\n"); 18330c2b98aSNeeraj Upadhyay snp_abort(); 18430c2b98aSNeeraj Upadhyay /* unreachable */ 18530c2b98aSNeeraj Upadhyay } 18630c2b98aSNeeraj Upadhyay 187b8c3c9f5SNeeraj Upadhyay savic_page = alloc_percpu(struct secure_avic_page); 188b8c3c9f5SNeeraj Upadhyay if (!savic_page) 189b8c3c9f5SNeeraj Upadhyay snp_abort(); 190b8c3c9f5SNeeraj Upadhyay 19130c2b98aSNeeraj Upadhyay return 1; 19230c2b98aSNeeraj Upadhyay } 19330c2b98aSNeeraj Upadhyay 19430c2b98aSNeeraj Upadhyay static struct apic apic_x2apic_savic __ro_after_init = { 19530c2b98aSNeeraj Upadhyay 19630c2b98aSNeeraj Upadhyay .name = "secure avic x2apic", 19730c2b98aSNeeraj Upadhyay .probe = savic_probe, 19830c2b98aSNeeraj Upadhyay .acpi_madt_oem_check = savic_acpi_madt_oem_check, 199b8c3c9f5SNeeraj Upadhyay .setup = savic_setup, 20030c2b98aSNeeraj Upadhyay 20130c2b98aSNeeraj Upadhyay .dest_mode_logical = false, 20230c2b98aSNeeraj Upadhyay 20330c2b98aSNeeraj Upadhyay .disable_esr = 0, 20430c2b98aSNeeraj Upadhyay 20530c2b98aSNeeraj Upadhyay .cpu_present_to_apicid = default_cpu_present_to_apicid, 20630c2b98aSNeeraj Upadhyay 20730c2b98aSNeeraj Upadhyay .max_apic_id = UINT_MAX, 20830c2b98aSNeeraj Upadhyay .x2apic_set_max_apicid = true, 20930c2b98aSNeeraj Upadhyay .get_apic_id = x2apic_get_apic_id, 21030c2b98aSNeeraj Upadhyay 21130c2b98aSNeeraj Upadhyay .calc_dest_apicid = apic_default_calc_apicid, 21230c2b98aSNeeraj Upadhyay 21330c2b98aSNeeraj Upadhyay .nmi_to_offline_cpu = true, 21430c2b98aSNeeraj Upadhyay 215c822f58aSNeeraj Upadhyay .read = savic_read, 216c822f58aSNeeraj Upadhyay .write = savic_write, 21730c2b98aSNeeraj Upadhyay .eoi = native_apic_msr_eoi, 21830c2b98aSNeeraj Upadhyay .icr_read = native_x2apic_icr_read, 21930c2b98aSNeeraj Upadhyay .icr_write = native_x2apic_icr_write, 22030c2b98aSNeeraj Upadhyay }; 22130c2b98aSNeeraj Upadhyay 22230c2b98aSNeeraj Upadhyay apic_driver(apic_x2apic_savic); 223