xref: /linux/arch/x86/kernel/apic/x2apic_savic.c (revision 8c79a68de1d2d63537f2a318e5a3b27744c835ad)
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