1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * AMD Secure AVIC Support (SEV-SNP Guests) 4 * 5 * Copyright (C) 2024 Advanced Micro Devices, Inc. 6 * 7 * Author: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com> 8 */ 9 10 #include <linux/cc_platform.h> 11 #include <linux/percpu-defs.h> 12 #include <linux/align.h> 13 14 #include <asm/apic.h> 15 #include <asm/sev.h> 16 17 #include "local.h" 18 19 struct secure_avic_page { 20 u8 regs[PAGE_SIZE]; 21 } __aligned(PAGE_SIZE); 22 23 static struct secure_avic_page __percpu *savic_page __ro_after_init; 24 25 static int savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id) 26 { 27 return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC); 28 } 29 30 static inline void *get_reg_bitmap(unsigned int cpu, unsigned int offset) 31 { 32 return &per_cpu_ptr(savic_page, cpu)->regs[offset]; 33 } 34 35 static inline void update_vector(unsigned int cpu, unsigned int offset, 36 unsigned int vector, bool set) 37 { 38 void *bitmap = get_reg_bitmap(cpu, offset); 39 40 if (set) 41 apic_set_vector(vector, bitmap); 42 else 43 apic_clear_vector(vector, bitmap); 44 } 45 46 #define SAVIC_ALLOWED_IRR 0x204 47 48 /* 49 * When Secure AVIC is enabled, RDMSR/WRMSR of the APIC registers 50 * result in #VC exception (for non-accelerated register accesses) 51 * with VMEXIT_AVIC_NOACCEL error code. The #VC exception handler 52 * can read/write the x2APIC register in the guest APIC backing page. 53 * 54 * Since doing this would increase the latency of accessing x2APIC 55 * registers, instead of doing RDMSR/WRMSR based accesses and 56 * handling the APIC register reads/writes in the #VC exception handler, 57 * the read() and write() callbacks directly read/write the APIC register 58 * from/to the vCPU's APIC backing page. 59 */ 60 static u32 savic_read(u32 reg) 61 { 62 void *ap = this_cpu_ptr(savic_page); 63 64 switch (reg) { 65 case APIC_LVTT: 66 case APIC_TMICT: 67 case APIC_TMCCT: 68 case APIC_TDCR: 69 case APIC_ID: 70 case APIC_LVR: 71 case APIC_TASKPRI: 72 case APIC_ARBPRI: 73 case APIC_PROCPRI: 74 case APIC_LDR: 75 case APIC_SPIV: 76 case APIC_ESR: 77 case APIC_LVTTHMR: 78 case APIC_LVTPC: 79 case APIC_LVT0: 80 case APIC_LVT1: 81 case APIC_LVTERR: 82 case APIC_EFEAT: 83 case APIC_ECTRL: 84 case APIC_SEOI: 85 case APIC_IER: 86 case APIC_EILVTn(0) ... APIC_EILVTn(3): 87 return apic_get_reg(ap, reg); 88 case APIC_ICR: 89 return (u32)apic_get_reg64(ap, reg); 90 case APIC_ISR ... APIC_ISR + 0x70: 91 case APIC_TMR ... APIC_TMR + 0x70: 92 if (WARN_ONCE(!IS_ALIGNED(reg, 16), 93 "APIC register read offset 0x%x not aligned at 16 bytes", reg)) 94 return 0; 95 return apic_get_reg(ap, reg); 96 /* IRR and ALLOWED_IRR offset range */ 97 case APIC_IRR ... APIC_IRR + 0x74: 98 /* 99 * Valid APIC_IRR/SAVIC_ALLOWED_IRR registers are at 16 bytes strides from 100 * their respective base offset. APIC_IRRs are in the range 101 * 102 * (0x200, 0x210, ..., 0x270) 103 * 104 * while the SAVIC_ALLOWED_IRR range starts 4 bytes later, in the range 105 * 106 * (0x204, 0x214, ..., 0x274). 107 * 108 * Filter out everything else. 109 */ 110 if (WARN_ONCE(!(IS_ALIGNED(reg, 16) || 111 IS_ALIGNED(reg - 4, 16)), 112 "Misaligned APIC_IRR/ALLOWED_IRR APIC register read offset 0x%x", reg)) 113 return 0; 114 return apic_get_reg(ap, reg); 115 default: 116 pr_err("Error reading unknown Secure AVIC reg offset 0x%x\n", reg); 117 return 0; 118 } 119 } 120 121 #define SAVIC_NMI_REQ 0x278 122 123 static void savic_write(u32 reg, u32 data) 124 { 125 void *ap = this_cpu_ptr(savic_page); 126 127 switch (reg) { 128 case APIC_LVTT: 129 case APIC_LVT0: 130 case APIC_LVT1: 131 case APIC_TMICT: 132 case APIC_TDCR: 133 case APIC_SELF_IPI: 134 case APIC_TASKPRI: 135 case APIC_EOI: 136 case APIC_SPIV: 137 case SAVIC_NMI_REQ: 138 case APIC_ESR: 139 case APIC_LVTTHMR: 140 case APIC_LVTPC: 141 case APIC_LVTERR: 142 case APIC_ECTRL: 143 case APIC_SEOI: 144 case APIC_IER: 145 case APIC_EILVTn(0) ... APIC_EILVTn(3): 146 apic_set_reg(ap, reg, data); 147 break; 148 case APIC_ICR: 149 apic_set_reg64(ap, reg, (u64)data); 150 break; 151 /* ALLOWED_IRR offsets are writable */ 152 case SAVIC_ALLOWED_IRR ... SAVIC_ALLOWED_IRR + 0x70: 153 if (IS_ALIGNED(reg - 4, 16)) { 154 apic_set_reg(ap, reg, data); 155 break; 156 } 157 fallthrough; 158 default: 159 pr_err("Error writing unknown Secure AVIC reg offset 0x%x\n", reg); 160 } 161 } 162 163 static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set) 164 { 165 update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set); 166 } 167 168 static void savic_setup(void) 169 { 170 void *ap = this_cpu_ptr(savic_page); 171 enum es_result res; 172 unsigned long gpa; 173 174 /* 175 * Before Secure AVIC is enabled, APIC MSR reads are intercepted. 176 * APIC_ID MSR read returns the value from the hypervisor. 177 */ 178 apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID)); 179 180 gpa = __pa(ap); 181 182 /* 183 * The NPT entry for a vCPU's APIC backing page must always be 184 * present when the vCPU is running in order for Secure AVIC to 185 * function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot 186 * be resumed if the NPT entry for the APIC backing page is not 187 * present. Notify GPA of the vCPU's APIC backing page to the 188 * hypervisor by calling savic_register_gpa(). Before executing 189 * VMRUN, the hypervisor makes use of this information to make sure 190 * the APIC backing page is mapped in NPT. 191 */ 192 res = savic_register_gpa(gpa); 193 if (res != ES_OK) 194 snp_abort(); 195 } 196 197 static int savic_probe(void) 198 { 199 if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC)) 200 return 0; 201 202 if (!x2apic_mode) { 203 pr_err("Secure AVIC enabled in non x2APIC mode\n"); 204 snp_abort(); 205 /* unreachable */ 206 } 207 208 savic_page = alloc_percpu(struct secure_avic_page); 209 if (!savic_page) 210 snp_abort(); 211 212 return 1; 213 } 214 215 static struct apic apic_x2apic_savic __ro_after_init = { 216 217 .name = "secure avic x2apic", 218 .probe = savic_probe, 219 .acpi_madt_oem_check = savic_acpi_madt_oem_check, 220 .setup = savic_setup, 221 222 .dest_mode_logical = false, 223 224 .disable_esr = 0, 225 226 .cpu_present_to_apicid = default_cpu_present_to_apicid, 227 228 .max_apic_id = UINT_MAX, 229 .x2apic_set_max_apicid = true, 230 .get_apic_id = x2apic_get_apic_id, 231 232 .calc_dest_apicid = apic_default_calc_apicid, 233 234 .nmi_to_offline_cpu = true, 235 236 .read = savic_read, 237 .write = savic_write, 238 .eoi = native_apic_msr_eoi, 239 .icr_read = native_x2apic_icr_read, 240 .icr_write = native_x2apic_icr_write, 241 242 .update_vector = savic_update_vector, 243 }; 244 245 apic_driver(apic_x2apic_savic); 246