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>
112c6978eaSNeeraj Upadhyay #include <linux/cpumask.h>
12b8c3c9f5SNeeraj Upadhyay #include <linux/percpu-defs.h>
13c822f58aSNeeraj Upadhyay #include <linux/align.h>
1430c2b98aSNeeraj Upadhyay
1530c2b98aSNeeraj Upadhyay #include <asm/apic.h>
1630c2b98aSNeeraj Upadhyay #include <asm/sev.h>
1730c2b98aSNeeraj Upadhyay
1830c2b98aSNeeraj Upadhyay #include "local.h"
1930c2b98aSNeeraj Upadhyay
20b8c3c9f5SNeeraj Upadhyay struct secure_avic_page {
21b8c3c9f5SNeeraj Upadhyay u8 regs[PAGE_SIZE];
22b8c3c9f5SNeeraj Upadhyay } __aligned(PAGE_SIZE);
23b8c3c9f5SNeeraj Upadhyay
24b8c3c9f5SNeeraj Upadhyay static struct secure_avic_page __percpu *savic_page __ro_after_init;
25b8c3c9f5SNeeraj Upadhyay
savic_acpi_madt_oem_check(char * oem_id,char * oem_table_id)2630c2b98aSNeeraj Upadhyay static int savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
2730c2b98aSNeeraj Upadhyay {
2830c2b98aSNeeraj Upadhyay return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC);
2930c2b98aSNeeraj Upadhyay }
3030c2b98aSNeeraj Upadhyay
get_reg_bitmap(unsigned int cpu,unsigned int offset)318c79a68dSNeeraj Upadhyay static inline void *get_reg_bitmap(unsigned int cpu, unsigned int offset)
328c79a68dSNeeraj Upadhyay {
338c79a68dSNeeraj Upadhyay return &per_cpu_ptr(savic_page, cpu)->regs[offset];
348c79a68dSNeeraj Upadhyay }
358c79a68dSNeeraj Upadhyay
update_vector(unsigned int cpu,unsigned int offset,unsigned int vector,bool set)368c79a68dSNeeraj Upadhyay static inline void update_vector(unsigned int cpu, unsigned int offset,
378c79a68dSNeeraj Upadhyay unsigned int vector, bool set)
388c79a68dSNeeraj Upadhyay {
398c79a68dSNeeraj Upadhyay void *bitmap = get_reg_bitmap(cpu, offset);
408c79a68dSNeeraj Upadhyay
418c79a68dSNeeraj Upadhyay if (set)
428c79a68dSNeeraj Upadhyay apic_set_vector(vector, bitmap);
438c79a68dSNeeraj Upadhyay else
448c79a68dSNeeraj Upadhyay apic_clear_vector(vector, bitmap);
458c79a68dSNeeraj Upadhyay }
468c79a68dSNeeraj Upadhyay
47c822f58aSNeeraj Upadhyay #define SAVIC_ALLOWED_IRR 0x204
48c822f58aSNeeraj Upadhyay
49c822f58aSNeeraj Upadhyay /*
50c822f58aSNeeraj Upadhyay * When Secure AVIC is enabled, RDMSR/WRMSR of the APIC registers
51c822f58aSNeeraj Upadhyay * result in #VC exception (for non-accelerated register accesses)
52c822f58aSNeeraj Upadhyay * with VMEXIT_AVIC_NOACCEL error code. The #VC exception handler
53c822f58aSNeeraj Upadhyay * can read/write the x2APIC register in the guest APIC backing page.
54c822f58aSNeeraj Upadhyay *
55c822f58aSNeeraj Upadhyay * Since doing this would increase the latency of accessing x2APIC
56c822f58aSNeeraj Upadhyay * registers, instead of doing RDMSR/WRMSR based accesses and
57c822f58aSNeeraj Upadhyay * handling the APIC register reads/writes in the #VC exception handler,
58c822f58aSNeeraj Upadhyay * the read() and write() callbacks directly read/write the APIC register
59c822f58aSNeeraj Upadhyay * from/to the vCPU's APIC backing page.
60c822f58aSNeeraj Upadhyay */
savic_read(u32 reg)61c822f58aSNeeraj Upadhyay static u32 savic_read(u32 reg)
62c822f58aSNeeraj Upadhyay {
63c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page);
64c822f58aSNeeraj Upadhyay
65c822f58aSNeeraj Upadhyay switch (reg) {
66c822f58aSNeeraj Upadhyay case APIC_LVTT:
67c822f58aSNeeraj Upadhyay case APIC_TMICT:
68c822f58aSNeeraj Upadhyay case APIC_TMCCT:
69c822f58aSNeeraj Upadhyay case APIC_TDCR:
708e371430SNeeraj Upadhyay case APIC_LVTTHMR:
718e371430SNeeraj Upadhyay case APIC_LVTPC:
728e371430SNeeraj Upadhyay case APIC_LVT0:
738e371430SNeeraj Upadhyay case APIC_LVT1:
748e371430SNeeraj Upadhyay case APIC_LVTERR:
75ea7d792eSNeeraj Upadhyay return savic_ghcb_msr_read(reg);
76c822f58aSNeeraj Upadhyay case APIC_ID:
77c822f58aSNeeraj Upadhyay case APIC_LVR:
78c822f58aSNeeraj Upadhyay case APIC_TASKPRI:
79c822f58aSNeeraj Upadhyay case APIC_ARBPRI:
80c822f58aSNeeraj Upadhyay case APIC_PROCPRI:
81c822f58aSNeeraj Upadhyay case APIC_LDR:
82c822f58aSNeeraj Upadhyay case APIC_SPIV:
83c822f58aSNeeraj Upadhyay case APIC_ESR:
84c822f58aSNeeraj Upadhyay case APIC_EFEAT:
85c822f58aSNeeraj Upadhyay case APIC_ECTRL:
86c822f58aSNeeraj Upadhyay case APIC_SEOI:
87c822f58aSNeeraj Upadhyay case APIC_IER:
88c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3):
89c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg);
90c822f58aSNeeraj Upadhyay case APIC_ICR:
91c822f58aSNeeraj Upadhyay return (u32)apic_get_reg64(ap, reg);
92c822f58aSNeeraj Upadhyay case APIC_ISR ... APIC_ISR + 0x70:
93c822f58aSNeeraj Upadhyay case APIC_TMR ... APIC_TMR + 0x70:
94c822f58aSNeeraj Upadhyay if (WARN_ONCE(!IS_ALIGNED(reg, 16),
95c822f58aSNeeraj Upadhyay "APIC register read offset 0x%x not aligned at 16 bytes", reg))
96c822f58aSNeeraj Upadhyay return 0;
97c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg);
98c822f58aSNeeraj Upadhyay /* IRR and ALLOWED_IRR offset range */
99c822f58aSNeeraj Upadhyay case APIC_IRR ... APIC_IRR + 0x74:
100c822f58aSNeeraj Upadhyay /*
101c822f58aSNeeraj Upadhyay * Valid APIC_IRR/SAVIC_ALLOWED_IRR registers are at 16 bytes strides from
102c822f58aSNeeraj Upadhyay * their respective base offset. APIC_IRRs are in the range
103c822f58aSNeeraj Upadhyay *
104c822f58aSNeeraj Upadhyay * (0x200, 0x210, ..., 0x270)
105c822f58aSNeeraj Upadhyay *
106c822f58aSNeeraj Upadhyay * while the SAVIC_ALLOWED_IRR range starts 4 bytes later, in the range
107c822f58aSNeeraj Upadhyay *
108c822f58aSNeeraj Upadhyay * (0x204, 0x214, ..., 0x274).
109c822f58aSNeeraj Upadhyay *
110c822f58aSNeeraj Upadhyay * Filter out everything else.
111c822f58aSNeeraj Upadhyay */
112c822f58aSNeeraj Upadhyay if (WARN_ONCE(!(IS_ALIGNED(reg, 16) ||
113c822f58aSNeeraj Upadhyay IS_ALIGNED(reg - 4, 16)),
114c822f58aSNeeraj Upadhyay "Misaligned APIC_IRR/ALLOWED_IRR APIC register read offset 0x%x", reg))
115c822f58aSNeeraj Upadhyay return 0;
116c822f58aSNeeraj Upadhyay return apic_get_reg(ap, reg);
117c822f58aSNeeraj Upadhyay default:
118c822f58aSNeeraj Upadhyay pr_err("Error reading unknown Secure AVIC reg offset 0x%x\n", reg);
119c822f58aSNeeraj Upadhyay return 0;
120c822f58aSNeeraj Upadhyay }
121c822f58aSNeeraj Upadhyay }
122c822f58aSNeeraj Upadhyay
123c822f58aSNeeraj Upadhyay #define SAVIC_NMI_REQ 0x278
124c822f58aSNeeraj Upadhyay
1252c6978eaSNeeraj Upadhyay /*
1262c6978eaSNeeraj Upadhyay * On WRMSR to APIC_SELF_IPI register by the guest, Secure AVIC hardware
1272c6978eaSNeeraj Upadhyay * updates the APIC_IRR in the APIC backing page of the vCPU. In addition,
1282c6978eaSNeeraj Upadhyay * hardware evaluates the new APIC_IRR update for interrupt injection to
1292c6978eaSNeeraj Upadhyay * the vCPU. So, self IPIs are hardware-accelerated.
1302c6978eaSNeeraj Upadhyay */
self_ipi_reg_write(unsigned int vector)1312c6978eaSNeeraj Upadhyay static inline void self_ipi_reg_write(unsigned int vector)
1322c6978eaSNeeraj Upadhyay {
1332c6978eaSNeeraj Upadhyay native_apic_msr_write(APIC_SELF_IPI, vector);
1342c6978eaSNeeraj Upadhyay }
1352c6978eaSNeeraj Upadhyay
send_ipi_dest(unsigned int cpu,unsigned int vector,bool nmi)1369de196f5SNeeraj Upadhyay static void send_ipi_dest(unsigned int cpu, unsigned int vector, bool nmi)
1372c6978eaSNeeraj Upadhyay {
1389de196f5SNeeraj Upadhyay if (nmi)
1399de196f5SNeeraj Upadhyay apic_set_reg(per_cpu_ptr(savic_page, cpu), SAVIC_NMI_REQ, 1);
1409de196f5SNeeraj Upadhyay else
1412c6978eaSNeeraj Upadhyay update_vector(cpu, APIC_IRR, vector, true);
1422c6978eaSNeeraj Upadhyay }
1432c6978eaSNeeraj Upadhyay
send_ipi_allbut(unsigned int vector,bool nmi)1449de196f5SNeeraj Upadhyay static void send_ipi_allbut(unsigned int vector, bool nmi)
1452c6978eaSNeeraj Upadhyay {
1462c6978eaSNeeraj Upadhyay unsigned int cpu, src_cpu;
1472c6978eaSNeeraj Upadhyay
1482c6978eaSNeeraj Upadhyay guard(irqsave)();
1492c6978eaSNeeraj Upadhyay
1502c6978eaSNeeraj Upadhyay src_cpu = raw_smp_processor_id();
1512c6978eaSNeeraj Upadhyay
1522c6978eaSNeeraj Upadhyay for_each_cpu(cpu, cpu_online_mask) {
1532c6978eaSNeeraj Upadhyay if (cpu == src_cpu)
1542c6978eaSNeeraj Upadhyay continue;
1559de196f5SNeeraj Upadhyay send_ipi_dest(cpu, vector, nmi);
1562c6978eaSNeeraj Upadhyay }
1572c6978eaSNeeraj Upadhyay }
1582c6978eaSNeeraj Upadhyay
self_ipi(unsigned int vector,bool nmi)1599de196f5SNeeraj Upadhyay static inline void self_ipi(unsigned int vector, bool nmi)
1602c6978eaSNeeraj Upadhyay {
1612c6978eaSNeeraj Upadhyay u32 icr_low = APIC_SELF_IPI | vector;
1622c6978eaSNeeraj Upadhyay
1639de196f5SNeeraj Upadhyay if (nmi)
1649de196f5SNeeraj Upadhyay icr_low |= APIC_DM_NMI;
1659de196f5SNeeraj Upadhyay
1662c6978eaSNeeraj Upadhyay native_x2apic_icr_write(icr_low, 0);
1672c6978eaSNeeraj Upadhyay }
1682c6978eaSNeeraj Upadhyay
savic_icr_write(u32 icr_low,u32 icr_high)1692c6978eaSNeeraj Upadhyay static void savic_icr_write(u32 icr_low, u32 icr_high)
1702c6978eaSNeeraj Upadhyay {
1712c6978eaSNeeraj Upadhyay unsigned int dsh, vector;
1722c6978eaSNeeraj Upadhyay u64 icr_data;
1739de196f5SNeeraj Upadhyay bool nmi;
1742c6978eaSNeeraj Upadhyay
1752c6978eaSNeeraj Upadhyay dsh = icr_low & APIC_DEST_ALLBUT;
1762c6978eaSNeeraj Upadhyay vector = icr_low & APIC_VECTOR_MASK;
1779de196f5SNeeraj Upadhyay nmi = ((icr_low & APIC_DM_FIXED_MASK) == APIC_DM_NMI);
1782c6978eaSNeeraj Upadhyay
1792c6978eaSNeeraj Upadhyay switch (dsh) {
1802c6978eaSNeeraj Upadhyay case APIC_DEST_SELF:
1819de196f5SNeeraj Upadhyay self_ipi(vector, nmi);
1822c6978eaSNeeraj Upadhyay break;
1832c6978eaSNeeraj Upadhyay case APIC_DEST_ALLINC:
1849de196f5SNeeraj Upadhyay self_ipi(vector, nmi);
1852c6978eaSNeeraj Upadhyay fallthrough;
1862c6978eaSNeeraj Upadhyay case APIC_DEST_ALLBUT:
1879de196f5SNeeraj Upadhyay send_ipi_allbut(vector, nmi);
1882c6978eaSNeeraj Upadhyay break;
1892c6978eaSNeeraj Upadhyay default:
1909de196f5SNeeraj Upadhyay send_ipi_dest(icr_high, vector, nmi);
1912c6978eaSNeeraj Upadhyay break;
1922c6978eaSNeeraj Upadhyay }
1932c6978eaSNeeraj Upadhyay
1942c6978eaSNeeraj Upadhyay icr_data = ((u64)icr_high) << 32 | icr_low;
1952c6978eaSNeeraj Upadhyay if (dsh != APIC_DEST_SELF)
1962c6978eaSNeeraj Upadhyay savic_ghcb_msr_write(APIC_ICR, icr_data);
1972c6978eaSNeeraj Upadhyay apic_set_reg64(this_cpu_ptr(savic_page), APIC_ICR, icr_data);
1982c6978eaSNeeraj Upadhyay }
1992c6978eaSNeeraj Upadhyay
savic_write(u32 reg,u32 data)200c822f58aSNeeraj Upadhyay static void savic_write(u32 reg, u32 data)
201c822f58aSNeeraj Upadhyay {
202c822f58aSNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page);
203c822f58aSNeeraj Upadhyay
204c822f58aSNeeraj Upadhyay switch (reg) {
205c822f58aSNeeraj Upadhyay case APIC_LVTT:
206c822f58aSNeeraj Upadhyay case APIC_TMICT:
207c822f58aSNeeraj Upadhyay case APIC_TDCR:
208ea7d792eSNeeraj Upadhyay case APIC_LVT0:
209ea7d792eSNeeraj Upadhyay case APIC_LVT1:
2108e371430SNeeraj Upadhyay case APIC_LVTTHMR:
2118e371430SNeeraj Upadhyay case APIC_LVTPC:
2128e371430SNeeraj Upadhyay case APIC_LVTERR:
2138e371430SNeeraj Upadhyay savic_ghcb_msr_write(reg, data);
2148e371430SNeeraj Upadhyay break;
215c822f58aSNeeraj Upadhyay case APIC_TASKPRI:
216c822f58aSNeeraj Upadhyay case APIC_EOI:
217c822f58aSNeeraj Upadhyay case APIC_SPIV:
218c822f58aSNeeraj Upadhyay case SAVIC_NMI_REQ:
219c822f58aSNeeraj Upadhyay case APIC_ESR:
220c822f58aSNeeraj Upadhyay case APIC_ECTRL:
221c822f58aSNeeraj Upadhyay case APIC_SEOI:
222c822f58aSNeeraj Upadhyay case APIC_IER:
223c822f58aSNeeraj Upadhyay case APIC_EILVTn(0) ... APIC_EILVTn(3):
224c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data);
225c822f58aSNeeraj Upadhyay break;
226c822f58aSNeeraj Upadhyay case APIC_ICR:
2272c6978eaSNeeraj Upadhyay savic_icr_write(data, 0);
2282c6978eaSNeeraj Upadhyay break;
2292c6978eaSNeeraj Upadhyay case APIC_SELF_IPI:
2302c6978eaSNeeraj Upadhyay self_ipi_reg_write(data);
231c822f58aSNeeraj Upadhyay break;
232c822f58aSNeeraj Upadhyay /* ALLOWED_IRR offsets are writable */
233c822f58aSNeeraj Upadhyay case SAVIC_ALLOWED_IRR ... SAVIC_ALLOWED_IRR + 0x70:
234c822f58aSNeeraj Upadhyay if (IS_ALIGNED(reg - 4, 16)) {
235c822f58aSNeeraj Upadhyay apic_set_reg(ap, reg, data);
236c822f58aSNeeraj Upadhyay break;
237c822f58aSNeeraj Upadhyay }
238c822f58aSNeeraj Upadhyay fallthrough;
239c822f58aSNeeraj Upadhyay default:
240c822f58aSNeeraj Upadhyay pr_err("Error writing unknown Secure AVIC reg offset 0x%x\n", reg);
241c822f58aSNeeraj Upadhyay }
242c822f58aSNeeraj Upadhyay }
243c822f58aSNeeraj Upadhyay
send_ipi(u32 dest,unsigned int vector,unsigned int dsh)2442c6978eaSNeeraj Upadhyay static void send_ipi(u32 dest, unsigned int vector, unsigned int dsh)
2452c6978eaSNeeraj Upadhyay {
2462c6978eaSNeeraj Upadhyay unsigned int icr_low;
2472c6978eaSNeeraj Upadhyay
2482c6978eaSNeeraj Upadhyay icr_low = __prepare_ICR(dsh, vector, APIC_DEST_PHYSICAL);
2492c6978eaSNeeraj Upadhyay savic_icr_write(icr_low, dest);
2502c6978eaSNeeraj Upadhyay }
2512c6978eaSNeeraj Upadhyay
savic_send_ipi(int cpu,int vector)2522c6978eaSNeeraj Upadhyay static void savic_send_ipi(int cpu, int vector)
2532c6978eaSNeeraj Upadhyay {
2542c6978eaSNeeraj Upadhyay u32 dest = per_cpu(x86_cpu_to_apicid, cpu);
2552c6978eaSNeeraj Upadhyay
2562c6978eaSNeeraj Upadhyay send_ipi(dest, vector, 0);
2572c6978eaSNeeraj Upadhyay }
2582c6978eaSNeeraj Upadhyay
send_ipi_mask(const struct cpumask * mask,unsigned int vector,bool excl_self)2592c6978eaSNeeraj Upadhyay static void send_ipi_mask(const struct cpumask *mask, unsigned int vector, bool excl_self)
2602c6978eaSNeeraj Upadhyay {
2612c6978eaSNeeraj Upadhyay unsigned int cpu, this_cpu;
2622c6978eaSNeeraj Upadhyay
2632c6978eaSNeeraj Upadhyay guard(irqsave)();
2642c6978eaSNeeraj Upadhyay
2652c6978eaSNeeraj Upadhyay this_cpu = raw_smp_processor_id();
2662c6978eaSNeeraj Upadhyay
2672c6978eaSNeeraj Upadhyay for_each_cpu(cpu, mask) {
2682c6978eaSNeeraj Upadhyay if (excl_self && cpu == this_cpu)
2692c6978eaSNeeraj Upadhyay continue;
2702c6978eaSNeeraj Upadhyay send_ipi(per_cpu(x86_cpu_to_apicid, cpu), vector, 0);
2712c6978eaSNeeraj Upadhyay }
2722c6978eaSNeeraj Upadhyay }
2732c6978eaSNeeraj Upadhyay
savic_send_ipi_mask(const struct cpumask * mask,int vector)2742c6978eaSNeeraj Upadhyay static void savic_send_ipi_mask(const struct cpumask *mask, int vector)
2752c6978eaSNeeraj Upadhyay {
2762c6978eaSNeeraj Upadhyay send_ipi_mask(mask, vector, false);
2772c6978eaSNeeraj Upadhyay }
2782c6978eaSNeeraj Upadhyay
savic_send_ipi_mask_allbutself(const struct cpumask * mask,int vector)2792c6978eaSNeeraj Upadhyay static void savic_send_ipi_mask_allbutself(const struct cpumask *mask, int vector)
2802c6978eaSNeeraj Upadhyay {
2812c6978eaSNeeraj Upadhyay send_ipi_mask(mask, vector, true);
2822c6978eaSNeeraj Upadhyay }
2832c6978eaSNeeraj Upadhyay
savic_send_ipi_allbutself(int vector)2842c6978eaSNeeraj Upadhyay static void savic_send_ipi_allbutself(int vector)
2852c6978eaSNeeraj Upadhyay {
2862c6978eaSNeeraj Upadhyay send_ipi(0, vector, APIC_DEST_ALLBUT);
2872c6978eaSNeeraj Upadhyay }
2882c6978eaSNeeraj Upadhyay
savic_send_ipi_all(int vector)2892c6978eaSNeeraj Upadhyay static void savic_send_ipi_all(int vector)
2902c6978eaSNeeraj Upadhyay {
2912c6978eaSNeeraj Upadhyay send_ipi(0, vector, APIC_DEST_ALLINC);
2922c6978eaSNeeraj Upadhyay }
2932c6978eaSNeeraj Upadhyay
savic_send_ipi_self(int vector)2942c6978eaSNeeraj Upadhyay static void savic_send_ipi_self(int vector)
2952c6978eaSNeeraj Upadhyay {
2962c6978eaSNeeraj Upadhyay self_ipi_reg_write(vector);
2972c6978eaSNeeraj Upadhyay }
2982c6978eaSNeeraj Upadhyay
savic_update_vector(unsigned int cpu,unsigned int vector,bool set)2998c79a68dSNeeraj Upadhyay static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set)
3008c79a68dSNeeraj Upadhyay {
3018c79a68dSNeeraj Upadhyay update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set);
3028c79a68dSNeeraj Upadhyay }
3038c79a68dSNeeraj Upadhyay
savic_eoi(void)30443b6687aSNeeraj Upadhyay static void savic_eoi(void)
30543b6687aSNeeraj Upadhyay {
30643b6687aSNeeraj Upadhyay unsigned int cpu;
30743b6687aSNeeraj Upadhyay int vec;
30843b6687aSNeeraj Upadhyay
30943b6687aSNeeraj Upadhyay cpu = raw_smp_processor_id();
31043b6687aSNeeraj Upadhyay vec = apic_find_highest_vector(get_reg_bitmap(cpu, APIC_ISR));
31143b6687aSNeeraj Upadhyay if (WARN_ONCE(vec == -1, "EOI write while no active interrupt in APIC_ISR"))
31243b6687aSNeeraj Upadhyay return;
31343b6687aSNeeraj Upadhyay
31443b6687aSNeeraj Upadhyay /* Is level-triggered interrupt? */
31543b6687aSNeeraj Upadhyay if (apic_test_vector(vec, get_reg_bitmap(cpu, APIC_TMR))) {
31643b6687aSNeeraj Upadhyay update_vector(cpu, APIC_ISR, vec, false);
31743b6687aSNeeraj Upadhyay /*
31843b6687aSNeeraj Upadhyay * Propagate the EOI write to the hypervisor for level-triggered
31943b6687aSNeeraj Upadhyay * interrupts. Return to the guest from GHCB protocol event takes
32043b6687aSNeeraj Upadhyay * care of re-evaluating interrupt state.
32143b6687aSNeeraj Upadhyay */
32243b6687aSNeeraj Upadhyay savic_ghcb_msr_write(APIC_EOI, 0);
32343b6687aSNeeraj Upadhyay } else {
32443b6687aSNeeraj Upadhyay /*
32543b6687aSNeeraj Upadhyay * Hardware clears APIC_ISR and re-evaluates the interrupt state
32643b6687aSNeeraj Upadhyay * to determine if there is any pending interrupt which can be
32743b6687aSNeeraj Upadhyay * delivered to CPU.
32843b6687aSNeeraj Upadhyay */
32943b6687aSNeeraj Upadhyay native_apic_msr_eoi();
33043b6687aSNeeraj Upadhyay }
33143b6687aSNeeraj Upadhyay }
33243b6687aSNeeraj Upadhyay
savic_teardown(void)333c8018325SNeeraj Upadhyay static void savic_teardown(void)
334c8018325SNeeraj Upadhyay {
335c8018325SNeeraj Upadhyay /* Disable Secure AVIC */
336c8018325SNeeraj Upadhyay native_wrmsrq(MSR_AMD64_SAVIC_CONTROL, 0);
337c8018325SNeeraj Upadhyay savic_unregister_gpa(NULL);
338c8018325SNeeraj Upadhyay }
339c8018325SNeeraj Upadhyay
savic_setup(void)340b8c3c9f5SNeeraj Upadhyay static void savic_setup(void)
341b8c3c9f5SNeeraj Upadhyay {
342b8c3c9f5SNeeraj Upadhyay void *ap = this_cpu_ptr(savic_page);
343b8c3c9f5SNeeraj Upadhyay enum es_result res;
344b8c3c9f5SNeeraj Upadhyay unsigned long gpa;
345b8c3c9f5SNeeraj Upadhyay
34645e2cef5SNeeraj Upadhyay /*
34745e2cef5SNeeraj Upadhyay * Before Secure AVIC is enabled, APIC MSR reads are intercepted.
34845e2cef5SNeeraj Upadhyay * APIC_ID MSR read returns the value from the hypervisor.
34945e2cef5SNeeraj Upadhyay */
35045e2cef5SNeeraj Upadhyay apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID));
35145e2cef5SNeeraj Upadhyay
352b8c3c9f5SNeeraj Upadhyay gpa = __pa(ap);
353b8c3c9f5SNeeraj Upadhyay
354b8c3c9f5SNeeraj Upadhyay /*
355b8c3c9f5SNeeraj Upadhyay * The NPT entry for a vCPU's APIC backing page must always be
356b8c3c9f5SNeeraj Upadhyay * present when the vCPU is running in order for Secure AVIC to
357b8c3c9f5SNeeraj Upadhyay * function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot
358b8c3c9f5SNeeraj Upadhyay * be resumed if the NPT entry for the APIC backing page is not
359b8c3c9f5SNeeraj Upadhyay * present. Notify GPA of the vCPU's APIC backing page to the
360b8c3c9f5SNeeraj Upadhyay * hypervisor by calling savic_register_gpa(). Before executing
361b8c3c9f5SNeeraj Upadhyay * VMRUN, the hypervisor makes use of this information to make sure
362b8c3c9f5SNeeraj Upadhyay * the APIC backing page is mapped in NPT.
363b8c3c9f5SNeeraj Upadhyay */
364b8c3c9f5SNeeraj Upadhyay res = savic_register_gpa(gpa);
365b8c3c9f5SNeeraj Upadhyay if (res != ES_OK)
366*d4bc3b11SBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
367869e36b9SNeeraj Upadhyay
368c4074ab8SNeeraj Upadhyay native_wrmsrq(MSR_AMD64_SAVIC_CONTROL,
369c4074ab8SNeeraj Upadhyay gpa | MSR_AMD64_SAVIC_EN | MSR_AMD64_SAVIC_ALLOWEDNMI);
370b8c3c9f5SNeeraj Upadhyay }
371b8c3c9f5SNeeraj Upadhyay
savic_probe(void)37230c2b98aSNeeraj Upadhyay static int savic_probe(void)
37330c2b98aSNeeraj Upadhyay {
37430c2b98aSNeeraj Upadhyay if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
37530c2b98aSNeeraj Upadhyay return 0;
37630c2b98aSNeeraj Upadhyay
37730c2b98aSNeeraj Upadhyay if (!x2apic_mode) {
37830c2b98aSNeeraj Upadhyay pr_err("Secure AVIC enabled in non x2APIC mode\n");
379*d4bc3b11SBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
38030c2b98aSNeeraj Upadhyay /* unreachable */
38130c2b98aSNeeraj Upadhyay }
38230c2b98aSNeeraj Upadhyay
383b8c3c9f5SNeeraj Upadhyay savic_page = alloc_percpu(struct secure_avic_page);
384b8c3c9f5SNeeraj Upadhyay if (!savic_page)
385*d4bc3b11SBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
386b8c3c9f5SNeeraj Upadhyay
38730c2b98aSNeeraj Upadhyay return 1;
38830c2b98aSNeeraj Upadhyay }
38930c2b98aSNeeraj Upadhyay
39030c2b98aSNeeraj Upadhyay static struct apic apic_x2apic_savic __ro_after_init = {
39130c2b98aSNeeraj Upadhyay
39230c2b98aSNeeraj Upadhyay .name = "secure avic x2apic",
39330c2b98aSNeeraj Upadhyay .probe = savic_probe,
39430c2b98aSNeeraj Upadhyay .acpi_madt_oem_check = savic_acpi_madt_oem_check,
395b8c3c9f5SNeeraj Upadhyay .setup = savic_setup,
396c8018325SNeeraj Upadhyay .teardown = savic_teardown,
39730c2b98aSNeeraj Upadhyay
39830c2b98aSNeeraj Upadhyay .dest_mode_logical = false,
39930c2b98aSNeeraj Upadhyay
40030c2b98aSNeeraj Upadhyay .disable_esr = 0,
40130c2b98aSNeeraj Upadhyay
40230c2b98aSNeeraj Upadhyay .cpu_present_to_apicid = default_cpu_present_to_apicid,
40330c2b98aSNeeraj Upadhyay
40430c2b98aSNeeraj Upadhyay .max_apic_id = UINT_MAX,
40530c2b98aSNeeraj Upadhyay .x2apic_set_max_apicid = true,
40630c2b98aSNeeraj Upadhyay .get_apic_id = x2apic_get_apic_id,
40730c2b98aSNeeraj Upadhyay
40830c2b98aSNeeraj Upadhyay .calc_dest_apicid = apic_default_calc_apicid,
40930c2b98aSNeeraj Upadhyay
4102c6978eaSNeeraj Upadhyay .send_IPI = savic_send_ipi,
4112c6978eaSNeeraj Upadhyay .send_IPI_mask = savic_send_ipi_mask,
4122c6978eaSNeeraj Upadhyay .send_IPI_mask_allbutself = savic_send_ipi_mask_allbutself,
4132c6978eaSNeeraj Upadhyay .send_IPI_allbutself = savic_send_ipi_allbutself,
4142c6978eaSNeeraj Upadhyay .send_IPI_all = savic_send_ipi_all,
4152c6978eaSNeeraj Upadhyay .send_IPI_self = savic_send_ipi_self,
4162c6978eaSNeeraj Upadhyay
41730c2b98aSNeeraj Upadhyay .nmi_to_offline_cpu = true,
41830c2b98aSNeeraj Upadhyay
419c822f58aSNeeraj Upadhyay .read = savic_read,
420c822f58aSNeeraj Upadhyay .write = savic_write,
42143b6687aSNeeraj Upadhyay .eoi = savic_eoi,
42230c2b98aSNeeraj Upadhyay .icr_read = native_x2apic_icr_read,
4232c6978eaSNeeraj Upadhyay .icr_write = savic_icr_write,
4248c79a68dSNeeraj Upadhyay
4258c79a68dSNeeraj Upadhyay .update_vector = savic_update_vector,
42630c2b98aSNeeraj Upadhyay };
42730c2b98aSNeeraj Upadhyay
42830c2b98aSNeeraj Upadhyay apic_driver(apic_x2apic_savic);
429