xref: /openbmc/linux/arch/x86/hyperv/ivm.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1810a5212STianyu Lan // SPDX-License-Identifier: GPL-2.0
2810a5212STianyu Lan /*
3810a5212STianyu Lan  * Hyper-V Isolation VM interface with paravisor and hypervisor
4810a5212STianyu Lan  *
5810a5212STianyu Lan  * Author:
6810a5212STianyu Lan  *  Tianyu Lan <Tianyu.Lan@microsoft.com>
7810a5212STianyu Lan  */
8810a5212STianyu Lan 
9faff4406STianyu Lan #include <linux/bitfield.h>
10810a5212STianyu Lan #include <linux/hyperv.h>
11810a5212STianyu Lan #include <linux/types.h>
12810a5212STianyu Lan #include <linux/slab.h>
13faff4406STianyu Lan #include <asm/svm.h>
14faff4406STianyu Lan #include <asm/sev.h>
15810a5212STianyu Lan #include <asm/io.h>
16812b0597SMichael Kelley #include <asm/coco.h>
17812b0597SMichael Kelley #include <asm/mem_encrypt.h>
18810a5212STianyu Lan #include <asm/mshyperv.h>
19faff4406STianyu Lan #include <asm/hypervisor.h>
20c957f1f3SJuergen Gross #include <asm/mtrr.h>
2144676bb9STianyu Lan #include <asm/io_apic.h>
2244676bb9STianyu Lan #include <asm/realmode.h>
2344676bb9STianyu Lan #include <asm/e820/api.h>
2444676bb9STianyu Lan #include <asm/desc.h>
25b9b4fe3aSDexuan Cui #include <uapi/asm/vmx.h>
26faff4406STianyu Lan 
27faff4406STianyu Lan #ifdef CONFIG_AMD_MEM_ENCRYPT
2820c89a55STianyu Lan 
2920c89a55STianyu Lan #define GHCB_USAGE_HYPERV_CALL	1
3020c89a55STianyu Lan 
31faff4406STianyu Lan union hv_ghcb {
32faff4406STianyu Lan 	struct ghcb ghcb;
3320c89a55STianyu Lan 	struct {
3420c89a55STianyu Lan 		u64 hypercalldata[509];
3520c89a55STianyu Lan 		u64 outputgpa;
3620c89a55STianyu Lan 		union {
3720c89a55STianyu Lan 			union {
3820c89a55STianyu Lan 				struct {
3920c89a55STianyu Lan 					u32 callcode        : 16;
4020c89a55STianyu Lan 					u32 isfast          : 1;
4120c89a55STianyu Lan 					u32 reserved1       : 14;
4220c89a55STianyu Lan 					u32 isnested        : 1;
4320c89a55STianyu Lan 					u32 countofelements : 12;
4420c89a55STianyu Lan 					u32 reserved2       : 4;
4520c89a55STianyu Lan 					u32 repstartindex   : 12;
4620c89a55STianyu Lan 					u32 reserved3       : 4;
4720c89a55STianyu Lan 				};
4820c89a55STianyu Lan 				u64 asuint64;
4920c89a55STianyu Lan 			} hypercallinput;
5020c89a55STianyu Lan 			union {
5120c89a55STianyu Lan 				struct {
5220c89a55STianyu Lan 					u16 callstatus;
5320c89a55STianyu Lan 					u16 reserved1;
5420c89a55STianyu Lan 					u32 elementsprocessed : 12;
5520c89a55STianyu Lan 					u32 reserved2         : 20;
5620c89a55STianyu Lan 				};
5720c89a55STianyu Lan 				u64 asunit64;
5820c89a55STianyu Lan 			} hypercalloutput;
5920c89a55STianyu Lan 		};
6020c89a55STianyu Lan 		u64 reserved2;
6120c89a55STianyu Lan 	} hypercall;
62faff4406STianyu Lan } __packed __aligned(HV_HYP_PAGE_SIZE);
63faff4406STianyu Lan 
64*a67f6b60SDexuan Cui /* Only used in an SNP VM with the paravisor */
6549d6a3c0STianyu Lan static u16 hv_ghcb_version __ro_after_init;
6649d6a3c0STianyu Lan 
67*a67f6b60SDexuan Cui /* Functions only used in an SNP VM with the paravisor go here. */
hv_ghcb_hypercall(u64 control,void * input,void * output,u32 input_size)6820c89a55STianyu Lan u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size)
6920c89a55STianyu Lan {
7020c89a55STianyu Lan 	union hv_ghcb *hv_ghcb;
7120c89a55STianyu Lan 	void **ghcb_base;
7220c89a55STianyu Lan 	unsigned long flags;
7320c89a55STianyu Lan 	u64 status;
7420c89a55STianyu Lan 
7520c89a55STianyu Lan 	if (!hv_ghcb_pg)
7620c89a55STianyu Lan 		return -EFAULT;
7720c89a55STianyu Lan 
7820c89a55STianyu Lan 	WARN_ON(in_nmi());
7920c89a55STianyu Lan 
8020c89a55STianyu Lan 	local_irq_save(flags);
8120c89a55STianyu Lan 	ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
8220c89a55STianyu Lan 	hv_ghcb = (union hv_ghcb *)*ghcb_base;
8320c89a55STianyu Lan 	if (!hv_ghcb) {
8420c89a55STianyu Lan 		local_irq_restore(flags);
8520c89a55STianyu Lan 		return -EFAULT;
8620c89a55STianyu Lan 	}
8720c89a55STianyu Lan 
8820c89a55STianyu Lan 	hv_ghcb->ghcb.protocol_version = GHCB_PROTOCOL_MAX;
8920c89a55STianyu Lan 	hv_ghcb->ghcb.ghcb_usage = GHCB_USAGE_HYPERV_CALL;
9020c89a55STianyu Lan 
9120c89a55STianyu Lan 	hv_ghcb->hypercall.outputgpa = (u64)output;
9220c89a55STianyu Lan 	hv_ghcb->hypercall.hypercallinput.asuint64 = 0;
9320c89a55STianyu Lan 	hv_ghcb->hypercall.hypercallinput.callcode = control;
9420c89a55STianyu Lan 
9520c89a55STianyu Lan 	if (input_size)
9620c89a55STianyu Lan 		memcpy(hv_ghcb->hypercall.hypercalldata, input, input_size);
9720c89a55STianyu Lan 
9820c89a55STianyu Lan 	VMGEXIT();
9920c89a55STianyu Lan 
10020c89a55STianyu Lan 	hv_ghcb->ghcb.ghcb_usage = 0xffffffff;
10120c89a55STianyu Lan 	memset(hv_ghcb->ghcb.save.valid_bitmap, 0,
10220c89a55STianyu Lan 	       sizeof(hv_ghcb->ghcb.save.valid_bitmap));
10320c89a55STianyu Lan 
10420c89a55STianyu Lan 	status = hv_ghcb->hypercall.hypercalloutput.callstatus;
10520c89a55STianyu Lan 
10620c89a55STianyu Lan 	local_irq_restore(flags);
10720c89a55STianyu Lan 
10820c89a55STianyu Lan 	return status;
10920c89a55STianyu Lan }
11020c89a55STianyu Lan 
rd_ghcb_msr(void)11149d6a3c0STianyu Lan static inline u64 rd_ghcb_msr(void)
11249d6a3c0STianyu Lan {
11349d6a3c0STianyu Lan 	return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
11449d6a3c0STianyu Lan }
11549d6a3c0STianyu Lan 
wr_ghcb_msr(u64 val)11649d6a3c0STianyu Lan static inline void wr_ghcb_msr(u64 val)
11749d6a3c0STianyu Lan {
11849d6a3c0STianyu Lan 	native_wrmsrl(MSR_AMD64_SEV_ES_GHCB, val);
11949d6a3c0STianyu Lan }
12049d6a3c0STianyu Lan 
hv_ghcb_hv_call(struct ghcb * ghcb,u64 exit_code,u64 exit_info_1,u64 exit_info_2)12149d6a3c0STianyu Lan static enum es_result hv_ghcb_hv_call(struct ghcb *ghcb, u64 exit_code,
12249d6a3c0STianyu Lan 				   u64 exit_info_1, u64 exit_info_2)
12349d6a3c0STianyu Lan {
12449d6a3c0STianyu Lan 	/* Fill in protocol and format specifiers */
12549d6a3c0STianyu Lan 	ghcb->protocol_version = hv_ghcb_version;
12649d6a3c0STianyu Lan 	ghcb->ghcb_usage       = GHCB_DEFAULT_USAGE;
12749d6a3c0STianyu Lan 
12849d6a3c0STianyu Lan 	ghcb_set_sw_exit_code(ghcb, exit_code);
12949d6a3c0STianyu Lan 	ghcb_set_sw_exit_info_1(ghcb, exit_info_1);
13049d6a3c0STianyu Lan 	ghcb_set_sw_exit_info_2(ghcb, exit_info_2);
13149d6a3c0STianyu Lan 
13249d6a3c0STianyu Lan 	VMGEXIT();
13349d6a3c0STianyu Lan 
13449d6a3c0STianyu Lan 	if (ghcb->save.sw_exit_info_1 & GENMASK_ULL(31, 0))
13549d6a3c0STianyu Lan 		return ES_VMM_ERROR;
13649d6a3c0STianyu Lan 	else
13749d6a3c0STianyu Lan 		return ES_OK;
13849d6a3c0STianyu Lan }
13949d6a3c0STianyu Lan 
hv_ghcb_terminate(unsigned int set,unsigned int reason)140611d4c71SGuilherme G. Piccoli void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason)
14149d6a3c0STianyu Lan {
14249d6a3c0STianyu Lan 	u64 val = GHCB_MSR_TERM_REQ;
14349d6a3c0STianyu Lan 
14449d6a3c0STianyu Lan 	/* Tell the hypervisor what went wrong. */
14549d6a3c0STianyu Lan 	val |= GHCB_SEV_TERM_REASON(set, reason);
14649d6a3c0STianyu Lan 
14749d6a3c0STianyu Lan 	/* Request Guest Termination from Hypvervisor */
14849d6a3c0STianyu Lan 	wr_ghcb_msr(val);
14949d6a3c0STianyu Lan 	VMGEXIT();
15049d6a3c0STianyu Lan 
15149d6a3c0STianyu Lan 	while (true)
15249d6a3c0STianyu Lan 		asm volatile("hlt\n" : : : "memory");
15349d6a3c0STianyu Lan }
15449d6a3c0STianyu Lan 
hv_ghcb_negotiate_protocol(void)15549d6a3c0STianyu Lan bool hv_ghcb_negotiate_protocol(void)
15649d6a3c0STianyu Lan {
15749d6a3c0STianyu Lan 	u64 ghcb_gpa;
15849d6a3c0STianyu Lan 	u64 val;
15949d6a3c0STianyu Lan 
16049d6a3c0STianyu Lan 	/* Save ghcb page gpa. */
16149d6a3c0STianyu Lan 	ghcb_gpa = rd_ghcb_msr();
16249d6a3c0STianyu Lan 
16349d6a3c0STianyu Lan 	/* Do the GHCB protocol version negotiation */
16449d6a3c0STianyu Lan 	wr_ghcb_msr(GHCB_MSR_SEV_INFO_REQ);
16549d6a3c0STianyu Lan 	VMGEXIT();
16649d6a3c0STianyu Lan 	val = rd_ghcb_msr();
16749d6a3c0STianyu Lan 
16849d6a3c0STianyu Lan 	if (GHCB_MSR_INFO(val) != GHCB_MSR_SEV_INFO_RESP)
16949d6a3c0STianyu Lan 		return false;
17049d6a3c0STianyu Lan 
17149d6a3c0STianyu Lan 	if (GHCB_MSR_PROTO_MAX(val) < GHCB_PROTOCOL_MIN ||
17249d6a3c0STianyu Lan 	    GHCB_MSR_PROTO_MIN(val) > GHCB_PROTOCOL_MAX)
17349d6a3c0STianyu Lan 		return false;
17449d6a3c0STianyu Lan 
17549d6a3c0STianyu Lan 	hv_ghcb_version = min_t(size_t, GHCB_MSR_PROTO_MAX(val),
17649d6a3c0STianyu Lan 			     GHCB_PROTOCOL_MAX);
17749d6a3c0STianyu Lan 
17849d6a3c0STianyu Lan 	/* Write ghcb page back after negotiating protocol. */
17949d6a3c0STianyu Lan 	wr_ghcb_msr(ghcb_gpa);
18049d6a3c0STianyu Lan 	VMGEXIT();
18149d6a3c0STianyu Lan 
18249d6a3c0STianyu Lan 	return true;
18349d6a3c0STianyu Lan }
18449d6a3c0STianyu Lan 
hv_ghcb_msr_write(u64 msr,u64 value)185b9b4fe3aSDexuan Cui static void hv_ghcb_msr_write(u64 msr, u64 value)
186faff4406STianyu Lan {
187faff4406STianyu Lan 	union hv_ghcb *hv_ghcb;
188faff4406STianyu Lan 	void **ghcb_base;
189faff4406STianyu Lan 	unsigned long flags;
190faff4406STianyu Lan 
191faff4406STianyu Lan 	if (!hv_ghcb_pg)
192faff4406STianyu Lan 		return;
193faff4406STianyu Lan 
194faff4406STianyu Lan 	WARN_ON(in_nmi());
195faff4406STianyu Lan 
196faff4406STianyu Lan 	local_irq_save(flags);
197faff4406STianyu Lan 	ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
198faff4406STianyu Lan 	hv_ghcb = (union hv_ghcb *)*ghcb_base;
199faff4406STianyu Lan 	if (!hv_ghcb) {
200faff4406STianyu Lan 		local_irq_restore(flags);
201faff4406STianyu Lan 		return;
202faff4406STianyu Lan 	}
203faff4406STianyu Lan 
204faff4406STianyu Lan 	ghcb_set_rcx(&hv_ghcb->ghcb, msr);
205faff4406STianyu Lan 	ghcb_set_rax(&hv_ghcb->ghcb, lower_32_bits(value));
206faff4406STianyu Lan 	ghcb_set_rdx(&hv_ghcb->ghcb, upper_32_bits(value));
207faff4406STianyu Lan 
20849d6a3c0STianyu Lan 	if (hv_ghcb_hv_call(&hv_ghcb->ghcb, SVM_EXIT_MSR, 1, 0))
209faff4406STianyu Lan 		pr_warn("Fail to write msr via ghcb %llx.\n", msr);
210faff4406STianyu Lan 
211faff4406STianyu Lan 	local_irq_restore(flags);
212faff4406STianyu Lan }
213faff4406STianyu Lan 
hv_ghcb_msr_read(u64 msr,u64 * value)214b9b4fe3aSDexuan Cui static void hv_ghcb_msr_read(u64 msr, u64 *value)
215faff4406STianyu Lan {
216faff4406STianyu Lan 	union hv_ghcb *hv_ghcb;
217faff4406STianyu Lan 	void **ghcb_base;
218faff4406STianyu Lan 	unsigned long flags;
219faff4406STianyu Lan 
220faff4406STianyu Lan 	/* Check size of union hv_ghcb here. */
221faff4406STianyu Lan 	BUILD_BUG_ON(sizeof(union hv_ghcb) != HV_HYP_PAGE_SIZE);
222faff4406STianyu Lan 
223faff4406STianyu Lan 	if (!hv_ghcb_pg)
224faff4406STianyu Lan 		return;
225faff4406STianyu Lan 
226faff4406STianyu Lan 	WARN_ON(in_nmi());
227faff4406STianyu Lan 
228faff4406STianyu Lan 	local_irq_save(flags);
229faff4406STianyu Lan 	ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
230faff4406STianyu Lan 	hv_ghcb = (union hv_ghcb *)*ghcb_base;
231faff4406STianyu Lan 	if (!hv_ghcb) {
232faff4406STianyu Lan 		local_irq_restore(flags);
233faff4406STianyu Lan 		return;
234faff4406STianyu Lan 	}
235faff4406STianyu Lan 
236faff4406STianyu Lan 	ghcb_set_rcx(&hv_ghcb->ghcb, msr);
23749d6a3c0STianyu Lan 	if (hv_ghcb_hv_call(&hv_ghcb->ghcb, SVM_EXIT_MSR, 0, 0))
238faff4406STianyu Lan 		pr_warn("Fail to read msr via ghcb %llx.\n", msr);
239faff4406STianyu Lan 	else
240faff4406STianyu Lan 		*value = (u64)lower_32_bits(hv_ghcb->ghcb.save.rax)
241faff4406STianyu Lan 			| ((u64)lower_32_bits(hv_ghcb->ghcb.save.rdx) << 32);
242faff4406STianyu Lan 	local_irq_restore(flags);
243faff4406STianyu Lan }
244faff4406STianyu Lan 
245*a67f6b60SDexuan Cui /* Only used in a fully enlightened SNP VM, i.e. without the paravisor */
246*a67f6b60SDexuan Cui static u8 ap_start_input_arg[PAGE_SIZE] __bss_decrypted __aligned(PAGE_SIZE);
247*a67f6b60SDexuan Cui static u8 ap_start_stack[PAGE_SIZE] __aligned(PAGE_SIZE);
248*a67f6b60SDexuan Cui static DEFINE_PER_CPU(struct sev_es_save_area *, hv_sev_vmsa);
249d3a9d7e4SDexuan Cui 
250*a67f6b60SDexuan Cui /* Functions only used in an SNP VM without the paravisor go here. */
251d3a9d7e4SDexuan Cui 
25244676bb9STianyu Lan #define hv_populate_vmcb_seg(seg, gdtr_base)			\
25344676bb9STianyu Lan do {								\
25444676bb9STianyu Lan 	if (seg.selector) {					\
25544676bb9STianyu Lan 		seg.base = 0;					\
25644676bb9STianyu Lan 		seg.limit = HV_AP_SEGMENT_LIMIT;		\
25744676bb9STianyu Lan 		seg.attrib = *(u16 *)(gdtr_base + seg.selector + 5);	\
25844676bb9STianyu Lan 		seg.attrib = (seg.attrib & 0xFF) | ((seg.attrib >> 4) & 0xF00); \
25944676bb9STianyu Lan 	}							\
26044676bb9STianyu Lan } while (0)							\
26144676bb9STianyu Lan 
snp_set_vmsa(void * va,bool vmsa)26244676bb9STianyu Lan static int snp_set_vmsa(void *va, bool vmsa)
26344676bb9STianyu Lan {
26444676bb9STianyu Lan 	u64 attrs;
26544676bb9STianyu Lan 
26644676bb9STianyu Lan 	/*
26744676bb9STianyu Lan 	 * Running at VMPL0 allows the kernel to change the VMSA bit for a page
26844676bb9STianyu Lan 	 * using the RMPADJUST instruction. However, for the instruction to
26944676bb9STianyu Lan 	 * succeed it must target the permissions of a lesser privileged
27044676bb9STianyu Lan 	 * (higher numbered) VMPL level, so use VMPL1 (refer to the RMPADJUST
27144676bb9STianyu Lan 	 * instruction in the AMD64 APM Volume 3).
27244676bb9STianyu Lan 	 */
27344676bb9STianyu Lan 	attrs = 1;
27444676bb9STianyu Lan 	if (vmsa)
27544676bb9STianyu Lan 		attrs |= RMPADJUST_VMSA_PAGE_BIT;
27644676bb9STianyu Lan 
27744676bb9STianyu Lan 	return rmpadjust((unsigned long)va, RMP_PG_SIZE_4K, attrs);
27844676bb9STianyu Lan }
27944676bb9STianyu Lan 
snp_cleanup_vmsa(struct sev_es_save_area * vmsa)28044676bb9STianyu Lan static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
28144676bb9STianyu Lan {
28244676bb9STianyu Lan 	int err;
28344676bb9STianyu Lan 
28444676bb9STianyu Lan 	err = snp_set_vmsa(vmsa, false);
28544676bb9STianyu Lan 	if (err)
28644676bb9STianyu Lan 		pr_err("clear VMSA page failed (%u), leaking page\n", err);
28744676bb9STianyu Lan 	else
28844676bb9STianyu Lan 		free_page((unsigned long)vmsa);
28944676bb9STianyu Lan }
29044676bb9STianyu Lan 
hv_snp_boot_ap(int cpu,unsigned long start_ip)29144676bb9STianyu Lan int hv_snp_boot_ap(int cpu, unsigned long start_ip)
29244676bb9STianyu Lan {
29344676bb9STianyu Lan 	struct sev_es_save_area *vmsa = (struct sev_es_save_area *)
29444676bb9STianyu Lan 		__get_free_page(GFP_KERNEL | __GFP_ZERO);
29544676bb9STianyu Lan 	struct sev_es_save_area *cur_vmsa;
29644676bb9STianyu Lan 	struct desc_ptr gdtr;
29744676bb9STianyu Lan 	u64 ret, retry = 5;
29844676bb9STianyu Lan 	struct hv_enable_vp_vtl *start_vp_input;
29944676bb9STianyu Lan 	unsigned long flags;
30044676bb9STianyu Lan 
30144676bb9STianyu Lan 	if (!vmsa)
30244676bb9STianyu Lan 		return -ENOMEM;
30344676bb9STianyu Lan 
30444676bb9STianyu Lan 	native_store_gdt(&gdtr);
30544676bb9STianyu Lan 
30644676bb9STianyu Lan 	vmsa->gdtr.base = gdtr.address;
30744676bb9STianyu Lan 	vmsa->gdtr.limit = gdtr.size;
30844676bb9STianyu Lan 
30944676bb9STianyu Lan 	asm volatile("movl %%es, %%eax;" : "=a" (vmsa->es.selector));
31044676bb9STianyu Lan 	hv_populate_vmcb_seg(vmsa->es, vmsa->gdtr.base);
31144676bb9STianyu Lan 
31244676bb9STianyu Lan 	asm volatile("movl %%cs, %%eax;" : "=a" (vmsa->cs.selector));
31344676bb9STianyu Lan 	hv_populate_vmcb_seg(vmsa->cs, vmsa->gdtr.base);
31444676bb9STianyu Lan 
31544676bb9STianyu Lan 	asm volatile("movl %%ss, %%eax;" : "=a" (vmsa->ss.selector));
31644676bb9STianyu Lan 	hv_populate_vmcb_seg(vmsa->ss, vmsa->gdtr.base);
31744676bb9STianyu Lan 
31844676bb9STianyu Lan 	asm volatile("movl %%ds, %%eax;" : "=a" (vmsa->ds.selector));
31944676bb9STianyu Lan 	hv_populate_vmcb_seg(vmsa->ds, vmsa->gdtr.base);
32044676bb9STianyu Lan 
32144676bb9STianyu Lan 	vmsa->efer = native_read_msr(MSR_EFER);
32244676bb9STianyu Lan 
32344676bb9STianyu Lan 	asm volatile("movq %%cr4, %%rax;" : "=a" (vmsa->cr4));
32444676bb9STianyu Lan 	asm volatile("movq %%cr3, %%rax;" : "=a" (vmsa->cr3));
32544676bb9STianyu Lan 	asm volatile("movq %%cr0, %%rax;" : "=a" (vmsa->cr0));
32644676bb9STianyu Lan 
32744676bb9STianyu Lan 	vmsa->xcr0 = 1;
32844676bb9STianyu Lan 	vmsa->g_pat = HV_AP_INIT_GPAT_DEFAULT;
32944676bb9STianyu Lan 	vmsa->rip = (u64)secondary_startup_64_no_verify;
33044676bb9STianyu Lan 	vmsa->rsp = (u64)&ap_start_stack[PAGE_SIZE];
33144676bb9STianyu Lan 
33244676bb9STianyu Lan 	/*
33344676bb9STianyu Lan 	 * Set the SNP-specific fields for this VMSA:
33444676bb9STianyu Lan 	 *   VMPL level
33544676bb9STianyu Lan 	 *   SEV_FEATURES (matches the SEV STATUS MSR right shifted 2 bits)
33644676bb9STianyu Lan 	 */
33744676bb9STianyu Lan 	vmsa->vmpl = 0;
33844676bb9STianyu Lan 	vmsa->sev_features = sev_status >> 2;
33944676bb9STianyu Lan 
34044676bb9STianyu Lan 	ret = snp_set_vmsa(vmsa, true);
34144676bb9STianyu Lan 	if (!ret) {
34244676bb9STianyu Lan 		pr_err("RMPADJUST(%llx) failed: %llx\n", (u64)vmsa, ret);
34344676bb9STianyu Lan 		free_page((u64)vmsa);
34444676bb9STianyu Lan 		return ret;
34544676bb9STianyu Lan 	}
34644676bb9STianyu Lan 
34744676bb9STianyu Lan 	local_irq_save(flags);
34844676bb9STianyu Lan 	start_vp_input = (struct hv_enable_vp_vtl *)ap_start_input_arg;
34944676bb9STianyu Lan 	memset(start_vp_input, 0, sizeof(*start_vp_input));
35044676bb9STianyu Lan 	start_vp_input->partition_id = -1;
35144676bb9STianyu Lan 	start_vp_input->vp_index = cpu;
35244676bb9STianyu Lan 	start_vp_input->target_vtl.target_vtl = ms_hyperv.vtl;
35344676bb9STianyu Lan 	*(u64 *)&start_vp_input->vp_context = __pa(vmsa) | 1;
35444676bb9STianyu Lan 
35544676bb9STianyu Lan 	do {
35644676bb9STianyu Lan 		ret = hv_do_hypercall(HVCALL_START_VP,
35744676bb9STianyu Lan 				      start_vp_input, NULL);
35844676bb9STianyu Lan 	} while (hv_result(ret) == HV_STATUS_TIME_OUT && retry--);
35944676bb9STianyu Lan 
36044676bb9STianyu Lan 	local_irq_restore(flags);
36144676bb9STianyu Lan 
36244676bb9STianyu Lan 	if (!hv_result_success(ret)) {
36344676bb9STianyu Lan 		pr_err("HvCallStartVirtualProcessor failed: %llx\n", ret);
36444676bb9STianyu Lan 		snp_cleanup_vmsa(vmsa);
36544676bb9STianyu Lan 		vmsa = NULL;
36644676bb9STianyu Lan 	}
36744676bb9STianyu Lan 
36844676bb9STianyu Lan 	cur_vmsa = per_cpu(hv_sev_vmsa, cpu);
36944676bb9STianyu Lan 	/* Free up any previous VMSA page */
37044676bb9STianyu Lan 	if (cur_vmsa)
37144676bb9STianyu Lan 		snp_cleanup_vmsa(cur_vmsa);
37244676bb9STianyu Lan 
37344676bb9STianyu Lan 	/* Record the current VMSA page */
37444676bb9STianyu Lan 	per_cpu(hv_sev_vmsa, cpu) = vmsa;
37544676bb9STianyu Lan 
37644676bb9STianyu Lan 	return ret;
37744676bb9STianyu Lan }
37844676bb9STianyu Lan 
379*a67f6b60SDexuan Cui #else
hv_ghcb_msr_write(u64 msr,u64 value)380*a67f6b60SDexuan Cui static inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
hv_ghcb_msr_read(u64 msr,u64 * value)381*a67f6b60SDexuan Cui static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
382d3a9d7e4SDexuan Cui #endif /* CONFIG_AMD_MEM_ENCRYPT */
383d3a9d7e4SDexuan Cui 
384*a67f6b60SDexuan Cui #ifdef CONFIG_INTEL_TDX_GUEST
hv_tdx_msr_write(u64 msr,u64 val)385*a67f6b60SDexuan Cui static void hv_tdx_msr_write(u64 msr, u64 val)
386*a67f6b60SDexuan Cui {
387*a67f6b60SDexuan Cui 	struct tdx_hypercall_args args = {
388*a67f6b60SDexuan Cui 		.r10 = TDX_HYPERCALL_STANDARD,
389*a67f6b60SDexuan Cui 		.r11 = EXIT_REASON_MSR_WRITE,
390*a67f6b60SDexuan Cui 		.r12 = msr,
391*a67f6b60SDexuan Cui 		.r13 = val,
392*a67f6b60SDexuan Cui 	};
393*a67f6b60SDexuan Cui 
394*a67f6b60SDexuan Cui 	u64 ret = __tdx_hypercall(&args);
395*a67f6b60SDexuan Cui 
396*a67f6b60SDexuan Cui 	WARN_ONCE(ret, "Failed to emulate MSR write: %lld\n", ret);
397*a67f6b60SDexuan Cui }
398*a67f6b60SDexuan Cui 
hv_tdx_msr_read(u64 msr,u64 * val)399*a67f6b60SDexuan Cui static void hv_tdx_msr_read(u64 msr, u64 *val)
400*a67f6b60SDexuan Cui {
401*a67f6b60SDexuan Cui 	struct tdx_hypercall_args args = {
402*a67f6b60SDexuan Cui 		.r10 = TDX_HYPERCALL_STANDARD,
403*a67f6b60SDexuan Cui 		.r11 = EXIT_REASON_MSR_READ,
404*a67f6b60SDexuan Cui 		.r12 = msr,
405*a67f6b60SDexuan Cui 	};
406*a67f6b60SDexuan Cui 
407*a67f6b60SDexuan Cui 	u64 ret = __tdx_hypercall_ret(&args);
408*a67f6b60SDexuan Cui 
409*a67f6b60SDexuan Cui 	if (WARN_ONCE(ret, "Failed to emulate MSR read: %lld\n", ret))
410*a67f6b60SDexuan Cui 		*val = 0;
411*a67f6b60SDexuan Cui 	else
412*a67f6b60SDexuan Cui 		*val = args.r11;
413*a67f6b60SDexuan Cui }
414*a67f6b60SDexuan Cui 
hv_tdx_hypercall(u64 control,u64 param1,u64 param2)415*a67f6b60SDexuan Cui u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2)
416*a67f6b60SDexuan Cui {
417*a67f6b60SDexuan Cui 	struct tdx_hypercall_args args = { };
418*a67f6b60SDexuan Cui 
419*a67f6b60SDexuan Cui 	args.r10 = control;
420*a67f6b60SDexuan Cui 	args.rdx = param1;
421*a67f6b60SDexuan Cui 	args.r8  = param2;
422*a67f6b60SDexuan Cui 
423*a67f6b60SDexuan Cui 	(void)__tdx_hypercall_ret(&args);
424*a67f6b60SDexuan Cui 
425*a67f6b60SDexuan Cui 	return args.r11;
426*a67f6b60SDexuan Cui }
427*a67f6b60SDexuan Cui 
428*a67f6b60SDexuan Cui #else
hv_tdx_msr_write(u64 msr,u64 value)429*a67f6b60SDexuan Cui static inline void hv_tdx_msr_write(u64 msr, u64 value) {}
hv_tdx_msr_read(u64 msr,u64 * value)430*a67f6b60SDexuan Cui static inline void hv_tdx_msr_read(u64 msr, u64 *value) {}
431*a67f6b60SDexuan Cui #endif /* CONFIG_INTEL_TDX_GUEST */
432*a67f6b60SDexuan Cui 
433d3a9d7e4SDexuan Cui #if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)
hv_ivm_msr_write(u64 msr,u64 value)434*a67f6b60SDexuan Cui void hv_ivm_msr_write(u64 msr, u64 value)
435*a67f6b60SDexuan Cui {
436*a67f6b60SDexuan Cui 	if (!ms_hyperv.paravisor_present)
437*a67f6b60SDexuan Cui 		return;
438*a67f6b60SDexuan Cui 
439*a67f6b60SDexuan Cui 	if (hv_isolation_type_tdx())
440*a67f6b60SDexuan Cui 		hv_tdx_msr_write(msr, value);
441*a67f6b60SDexuan Cui 	else if (hv_isolation_type_snp())
442*a67f6b60SDexuan Cui 		hv_ghcb_msr_write(msr, value);
443*a67f6b60SDexuan Cui }
444*a67f6b60SDexuan Cui 
hv_ivm_msr_read(u64 msr,u64 * value)445*a67f6b60SDexuan Cui void hv_ivm_msr_read(u64 msr, u64 *value)
446*a67f6b60SDexuan Cui {
447*a67f6b60SDexuan Cui 	if (!ms_hyperv.paravisor_present)
448*a67f6b60SDexuan Cui 		return;
449*a67f6b60SDexuan Cui 
450*a67f6b60SDexuan Cui 	if (hv_isolation_type_tdx())
451*a67f6b60SDexuan Cui 		hv_tdx_msr_read(msr, value);
452*a67f6b60SDexuan Cui 	else if (hv_isolation_type_snp())
453*a67f6b60SDexuan Cui 		hv_ghcb_msr_read(msr, value);
454*a67f6b60SDexuan Cui }
455faff4406STianyu Lan 
456810a5212STianyu Lan /*
457810a5212STianyu Lan  * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
458810a5212STianyu Lan  *
459810a5212STianyu Lan  * In Isolation VM, all guest memory is encrypted from host and guest
460810a5212STianyu Lan  * needs to set memory visible to host via hvcall before sharing memory
461810a5212STianyu Lan  * with host.
462810a5212STianyu Lan  */
hv_mark_gpa_visibility(u16 count,const u64 pfn[],enum hv_mem_host_visibility visibility)463810a5212STianyu Lan static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
464810a5212STianyu Lan 			   enum hv_mem_host_visibility visibility)
465810a5212STianyu Lan {
46655e544e1SNischala Yelchuri 	struct hv_gpa_range_for_visibility *input;
467810a5212STianyu Lan 	u16 pages_processed;
468810a5212STianyu Lan 	u64 hv_status;
469810a5212STianyu Lan 	unsigned long flags;
470810a5212STianyu Lan 
471810a5212STianyu Lan 	/* no-op if partition isolation is not enabled */
472810a5212STianyu Lan 	if (!hv_is_isolation_supported())
473810a5212STianyu Lan 		return 0;
474810a5212STianyu Lan 
475810a5212STianyu Lan 	if (count > HV_MAX_MODIFY_GPA_REP_COUNT) {
476810a5212STianyu Lan 		pr_err("Hyper-V: GPA count:%d exceeds supported:%lu\n", count,
477810a5212STianyu Lan 			HV_MAX_MODIFY_GPA_REP_COUNT);
478810a5212STianyu Lan 		return -EINVAL;
479810a5212STianyu Lan 	}
480810a5212STianyu Lan 
481810a5212STianyu Lan 	local_irq_save(flags);
48255e544e1SNischala Yelchuri 	input = *this_cpu_ptr(hyperv_pcpu_input_arg);
48355e544e1SNischala Yelchuri 
484810a5212STianyu Lan 	if (unlikely(!input)) {
485810a5212STianyu Lan 		local_irq_restore(flags);
486810a5212STianyu Lan 		return -EINVAL;
487810a5212STianyu Lan 	}
488810a5212STianyu Lan 
489810a5212STianyu Lan 	input->partition_id = HV_PARTITION_ID_SELF;
490810a5212STianyu Lan 	input->host_visibility = visibility;
491810a5212STianyu Lan 	input->reserved0 = 0;
492810a5212STianyu Lan 	input->reserved1 = 0;
493810a5212STianyu Lan 	memcpy((void *)input->gpa_page_list, pfn, count * sizeof(*pfn));
494810a5212STianyu Lan 	hv_status = hv_do_rep_hypercall(
495810a5212STianyu Lan 			HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY, count,
496810a5212STianyu Lan 			0, input, &pages_processed);
497810a5212STianyu Lan 	local_irq_restore(flags);
498810a5212STianyu Lan 
499810a5212STianyu Lan 	if (hv_result_success(hv_status))
500810a5212STianyu Lan 		return 0;
501810a5212STianyu Lan 	else
502810a5212STianyu Lan 		return -EFAULT;
503810a5212STianyu Lan }
504810a5212STianyu Lan 
505810a5212STianyu Lan /*
506812b0597SMichael Kelley  * hv_vtom_set_host_visibility - Set specified memory visible to host.
507810a5212STianyu Lan  *
508810a5212STianyu Lan  * In Isolation VM, all guest memory is encrypted from host and guest
509810a5212STianyu Lan  * needs to set memory visible to host via hvcall before sharing memory
510810a5212STianyu Lan  * with host. This function works as wrap of hv_mark_gpa_visibility()
511810a5212STianyu Lan  * with memory base and size.
512810a5212STianyu Lan  */
hv_vtom_set_host_visibility(unsigned long kbuffer,int pagecount,bool enc)513812b0597SMichael Kelley static bool hv_vtom_set_host_visibility(unsigned long kbuffer, int pagecount, bool enc)
514810a5212STianyu Lan {
515812b0597SMichael Kelley 	enum hv_mem_host_visibility visibility = enc ?
516812b0597SMichael Kelley 			VMBUS_PAGE_NOT_VISIBLE : VMBUS_PAGE_VISIBLE_READ_WRITE;
517810a5212STianyu Lan 	u64 *pfn_array;
518810a5212STianyu Lan 	int ret = 0;
519812b0597SMichael Kelley 	bool result = true;
520810a5212STianyu Lan 	int i, pfn;
521810a5212STianyu Lan 
522810a5212STianyu Lan 	pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
523810a5212STianyu Lan 	if (!pfn_array)
524812b0597SMichael Kelley 		return false;
525810a5212STianyu Lan 
526810a5212STianyu Lan 	for (i = 0, pfn = 0; i < pagecount; i++) {
527810a5212STianyu Lan 		pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE);
528810a5212STianyu Lan 		pfn++;
529810a5212STianyu Lan 
530810a5212STianyu Lan 		if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) {
531810a5212STianyu Lan 			ret = hv_mark_gpa_visibility(pfn, pfn_array,
532810a5212STianyu Lan 						     visibility);
533812b0597SMichael Kelley 			if (ret) {
534812b0597SMichael Kelley 				result = false;
535810a5212STianyu Lan 				goto err_free_pfn_array;
536812b0597SMichael Kelley 			}
537810a5212STianyu Lan 			pfn = 0;
538810a5212STianyu Lan 		}
539810a5212STianyu Lan 	}
540810a5212STianyu Lan 
541810a5212STianyu Lan  err_free_pfn_array:
542810a5212STianyu Lan 	kfree(pfn_array);
543812b0597SMichael Kelley 	return result;
544810a5212STianyu Lan }
545846da38dSTianyu Lan 
hv_vtom_tlb_flush_required(bool private)546812b0597SMichael Kelley static bool hv_vtom_tlb_flush_required(bool private)
547812b0597SMichael Kelley {
548812b0597SMichael Kelley 	return true;
549812b0597SMichael Kelley }
550812b0597SMichael Kelley 
hv_vtom_cache_flush_required(void)551812b0597SMichael Kelley static bool hv_vtom_cache_flush_required(void)
552812b0597SMichael Kelley {
553812b0597SMichael Kelley 	return false;
554812b0597SMichael Kelley }
555812b0597SMichael Kelley 
hv_is_private_mmio(u64 addr)556812b0597SMichael Kelley static bool hv_is_private_mmio(u64 addr)
557812b0597SMichael Kelley {
558812b0597SMichael Kelley 	/*
559812b0597SMichael Kelley 	 * Hyper-V always provides a single IO-APIC in a guest VM.
560812b0597SMichael Kelley 	 * When a paravisor is used, it is emulated by the paravisor
561812b0597SMichael Kelley 	 * in the guest context and must be mapped private.
562812b0597SMichael Kelley 	 */
563812b0597SMichael Kelley 	if (addr >= HV_IOAPIC_BASE_ADDRESS &&
564812b0597SMichael Kelley 	    addr < (HV_IOAPIC_BASE_ADDRESS + PAGE_SIZE))
565812b0597SMichael Kelley 		return true;
566812b0597SMichael Kelley 
567812b0597SMichael Kelley 	/* Same with a vTPM */
568812b0597SMichael Kelley 	if (addr >= VTPM_BASE_ADDRESS &&
569812b0597SMichael Kelley 	    addr < (VTPM_BASE_ADDRESS + PAGE_SIZE))
570812b0597SMichael Kelley 		return true;
571812b0597SMichael Kelley 
572812b0597SMichael Kelley 	return false;
573812b0597SMichael Kelley }
574812b0597SMichael Kelley 
hv_vtom_init(void)575812b0597SMichael Kelley void __init hv_vtom_init(void)
576812b0597SMichael Kelley {
577d3a9d7e4SDexuan Cui 	enum hv_isolation_type type = hv_get_isolation_type();
578d3a9d7e4SDexuan Cui 
579d3a9d7e4SDexuan Cui 	switch (type) {
580d3a9d7e4SDexuan Cui 	case HV_ISOLATION_TYPE_VBS:
581d3a9d7e4SDexuan Cui 		fallthrough;
582812b0597SMichael Kelley 	/*
583812b0597SMichael Kelley 	 * By design, a VM using vTOM doesn't see the SEV setting,
584812b0597SMichael Kelley 	 * so SEV initialization is bypassed and sev_status isn't set.
585812b0597SMichael Kelley 	 * Set it here to indicate a vTOM VM.
586d3a9d7e4SDexuan Cui 	 *
587d3a9d7e4SDexuan Cui 	 * Note: if CONFIG_AMD_MEM_ENCRYPT is not set, sev_status is
588d3a9d7e4SDexuan Cui 	 * defined as 0ULL, to which we can't assigned a value.
589812b0597SMichael Kelley 	 */
590d3a9d7e4SDexuan Cui #ifdef CONFIG_AMD_MEM_ENCRYPT
591d3a9d7e4SDexuan Cui 	case HV_ISOLATION_TYPE_SNP:
592812b0597SMichael Kelley 		sev_status = MSR_AMD64_SNP_VTOM;
593da86eb96SBorislav Petkov (AMD) 		cc_vendor = CC_VENDOR_AMD;
594d3a9d7e4SDexuan Cui 		break;
595d3a9d7e4SDexuan Cui #endif
596d3a9d7e4SDexuan Cui 
597d3a9d7e4SDexuan Cui 	case HV_ISOLATION_TYPE_TDX:
598d3a9d7e4SDexuan Cui 		cc_vendor = CC_VENDOR_INTEL;
599d3a9d7e4SDexuan Cui 		break;
600d3a9d7e4SDexuan Cui 
601d3a9d7e4SDexuan Cui 	default:
602d3a9d7e4SDexuan Cui 		panic("hv_vtom_init: unsupported isolation type %d\n", type);
603d3a9d7e4SDexuan Cui 	}
604d3a9d7e4SDexuan Cui 
605812b0597SMichael Kelley 	cc_set_mask(ms_hyperv.shared_gpa_boundary);
606812b0597SMichael Kelley 	physical_mask &= ms_hyperv.shared_gpa_boundary - 1;
607812b0597SMichael Kelley 
608812b0597SMichael Kelley 	x86_platform.hyper.is_private_mmio = hv_is_private_mmio;
609812b0597SMichael Kelley 	x86_platform.guest.enc_cache_flush_required = hv_vtom_cache_flush_required;
610812b0597SMichael Kelley 	x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
611812b0597SMichael Kelley 	x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
612c957f1f3SJuergen Gross 
613c957f1f3SJuergen Gross 	/* Set WB as the default cache mode. */
614c957f1f3SJuergen Gross 	mtrr_overwrite_state(NULL, 0, MTRR_TYPE_WRBACK);
615812b0597SMichael Kelley }
616812b0597SMichael Kelley 
617d3a9d7e4SDexuan Cui #endif /* defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST) */
618812b0597SMichael Kelley 
hv_get_isolation_type(void)61971290be1SMichael Kelley enum hv_isolation_type hv_get_isolation_type(void)
62071290be1SMichael Kelley {
62171290be1SMichael Kelley 	if (!(ms_hyperv.priv_high & HV_ISOLATION))
62271290be1SMichael Kelley 		return HV_ISOLATION_TYPE_NONE;
62371290be1SMichael Kelley 	return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
62471290be1SMichael Kelley }
62571290be1SMichael Kelley EXPORT_SYMBOL_GPL(hv_get_isolation_type);
62671290be1SMichael Kelley 
62771290be1SMichael Kelley /*
62871290be1SMichael Kelley  * hv_is_isolation_supported - Check system runs in the Hyper-V
62971290be1SMichael Kelley  * isolation VM.
63071290be1SMichael Kelley  */
hv_is_isolation_supported(void)63171290be1SMichael Kelley bool hv_is_isolation_supported(void)
63271290be1SMichael Kelley {
63371290be1SMichael Kelley 	if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
63471290be1SMichael Kelley 		return false;
63571290be1SMichael Kelley 
63671290be1SMichael Kelley 	if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
63771290be1SMichael Kelley 		return false;
63871290be1SMichael Kelley 
63971290be1SMichael Kelley 	return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
64071290be1SMichael Kelley }
64171290be1SMichael Kelley 
64271290be1SMichael Kelley DEFINE_STATIC_KEY_FALSE(isolation_type_snp);
64371290be1SMichael Kelley 
64471290be1SMichael Kelley /*
645e3131f1cSDexuan Cui  * hv_isolation_type_snp - Check if the system runs in an AMD SEV-SNP based
64671290be1SMichael Kelley  * isolation VM.
64771290be1SMichael Kelley  */
hv_isolation_type_snp(void)64871290be1SMichael Kelley bool hv_isolation_type_snp(void)
64971290be1SMichael Kelley {
65071290be1SMichael Kelley 	return static_branch_unlikely(&isolation_type_snp);
65171290be1SMichael Kelley }
652d6e2d652STianyu Lan 
65308e9d120SDexuan Cui DEFINE_STATIC_KEY_FALSE(isolation_type_tdx);
65408e9d120SDexuan Cui /*
65508e9d120SDexuan Cui  * hv_isolation_type_tdx - Check if the system runs in an Intel TDX based
65608e9d120SDexuan Cui  * isolated VM.
65708e9d120SDexuan Cui  */
hv_isolation_type_tdx(void)65808e9d120SDexuan Cui bool hv_isolation_type_tdx(void)
65908e9d120SDexuan Cui {
66008e9d120SDexuan Cui 	return static_branch_unlikely(&isolation_type_tdx);
66108e9d120SDexuan Cui }
662