162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Hyper-V Isolation VM interface with paravisor and hypervisor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author:
662306a36Sopenharmony_ci *  Tianyu Lan <Tianyu.Lan@microsoft.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/hyperv.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <asm/svm.h>
1462306a36Sopenharmony_ci#include <asm/sev.h>
1562306a36Sopenharmony_ci#include <asm/io.h>
1662306a36Sopenharmony_ci#include <asm/coco.h>
1762306a36Sopenharmony_ci#include <asm/mem_encrypt.h>
1862306a36Sopenharmony_ci#include <asm/mshyperv.h>
1962306a36Sopenharmony_ci#include <asm/hypervisor.h>
2062306a36Sopenharmony_ci#include <asm/mtrr.h>
2162306a36Sopenharmony_ci#include <asm/io_apic.h>
2262306a36Sopenharmony_ci#include <asm/realmode.h>
2362306a36Sopenharmony_ci#include <asm/e820/api.h>
2462306a36Sopenharmony_ci#include <asm/desc.h>
2562306a36Sopenharmony_ci#include <uapi/asm/vmx.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#ifdef CONFIG_AMD_MEM_ENCRYPT
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define GHCB_USAGE_HYPERV_CALL	1
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciunion hv_ghcb {
3262306a36Sopenharmony_ci	struct ghcb ghcb;
3362306a36Sopenharmony_ci	struct {
3462306a36Sopenharmony_ci		u64 hypercalldata[509];
3562306a36Sopenharmony_ci		u64 outputgpa;
3662306a36Sopenharmony_ci		union {
3762306a36Sopenharmony_ci			union {
3862306a36Sopenharmony_ci				struct {
3962306a36Sopenharmony_ci					u32 callcode        : 16;
4062306a36Sopenharmony_ci					u32 isfast          : 1;
4162306a36Sopenharmony_ci					u32 reserved1       : 14;
4262306a36Sopenharmony_ci					u32 isnested        : 1;
4362306a36Sopenharmony_ci					u32 countofelements : 12;
4462306a36Sopenharmony_ci					u32 reserved2       : 4;
4562306a36Sopenharmony_ci					u32 repstartindex   : 12;
4662306a36Sopenharmony_ci					u32 reserved3       : 4;
4762306a36Sopenharmony_ci				};
4862306a36Sopenharmony_ci				u64 asuint64;
4962306a36Sopenharmony_ci			} hypercallinput;
5062306a36Sopenharmony_ci			union {
5162306a36Sopenharmony_ci				struct {
5262306a36Sopenharmony_ci					u16 callstatus;
5362306a36Sopenharmony_ci					u16 reserved1;
5462306a36Sopenharmony_ci					u32 elementsprocessed : 12;
5562306a36Sopenharmony_ci					u32 reserved2         : 20;
5662306a36Sopenharmony_ci				};
5762306a36Sopenharmony_ci				u64 asunit64;
5862306a36Sopenharmony_ci			} hypercalloutput;
5962306a36Sopenharmony_ci		};
6062306a36Sopenharmony_ci		u64 reserved2;
6162306a36Sopenharmony_ci	} hypercall;
6262306a36Sopenharmony_ci} __packed __aligned(HV_HYP_PAGE_SIZE);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Only used in an SNP VM with the paravisor */
6562306a36Sopenharmony_cistatic u16 hv_ghcb_version __ro_after_init;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Functions only used in an SNP VM with the paravisor go here. */
6862306a36Sopenharmony_ciu64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	union hv_ghcb *hv_ghcb;
7162306a36Sopenharmony_ci	void **ghcb_base;
7262306a36Sopenharmony_ci	unsigned long flags;
7362306a36Sopenharmony_ci	u64 status;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!hv_ghcb_pg)
7662306a36Sopenharmony_ci		return -EFAULT;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	WARN_ON(in_nmi());
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	local_irq_save(flags);
8162306a36Sopenharmony_ci	ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
8262306a36Sopenharmony_ci	hv_ghcb = (union hv_ghcb *)*ghcb_base;
8362306a36Sopenharmony_ci	if (!hv_ghcb) {
8462306a36Sopenharmony_ci		local_irq_restore(flags);
8562306a36Sopenharmony_ci		return -EFAULT;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	hv_ghcb->ghcb.protocol_version = GHCB_PROTOCOL_MAX;
8962306a36Sopenharmony_ci	hv_ghcb->ghcb.ghcb_usage = GHCB_USAGE_HYPERV_CALL;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	hv_ghcb->hypercall.outputgpa = (u64)output;
9262306a36Sopenharmony_ci	hv_ghcb->hypercall.hypercallinput.asuint64 = 0;
9362306a36Sopenharmony_ci	hv_ghcb->hypercall.hypercallinput.callcode = control;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (input_size)
9662306a36Sopenharmony_ci		memcpy(hv_ghcb->hypercall.hypercalldata, input, input_size);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	VMGEXIT();
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	hv_ghcb->ghcb.ghcb_usage = 0xffffffff;
10162306a36Sopenharmony_ci	memset(hv_ghcb->ghcb.save.valid_bitmap, 0,
10262306a36Sopenharmony_ci	       sizeof(hv_ghcb->ghcb.save.valid_bitmap));
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	status = hv_ghcb->hypercall.hypercalloutput.callstatus;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	local_irq_restore(flags);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return status;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline u64 rd_ghcb_msr(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic inline void wr_ghcb_msr(u64 val)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	native_wrmsrl(MSR_AMD64_SEV_ES_GHCB, val);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic enum es_result hv_ghcb_hv_call(struct ghcb *ghcb, u64 exit_code,
12262306a36Sopenharmony_ci				   u64 exit_info_1, u64 exit_info_2)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	/* Fill in protocol and format specifiers */
12562306a36Sopenharmony_ci	ghcb->protocol_version = hv_ghcb_version;
12662306a36Sopenharmony_ci	ghcb->ghcb_usage       = GHCB_DEFAULT_USAGE;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	ghcb_set_sw_exit_code(ghcb, exit_code);
12962306a36Sopenharmony_ci	ghcb_set_sw_exit_info_1(ghcb, exit_info_1);
13062306a36Sopenharmony_ci	ghcb_set_sw_exit_info_2(ghcb, exit_info_2);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	VMGEXIT();
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (ghcb->save.sw_exit_info_1 & GENMASK_ULL(31, 0))
13562306a36Sopenharmony_ci		return ES_VMM_ERROR;
13662306a36Sopenharmony_ci	else
13762306a36Sopenharmony_ci		return ES_OK;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_civoid __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	u64 val = GHCB_MSR_TERM_REQ;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* Tell the hypervisor what went wrong. */
14562306a36Sopenharmony_ci	val |= GHCB_SEV_TERM_REASON(set, reason);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* Request Guest Termination from Hypvervisor */
14862306a36Sopenharmony_ci	wr_ghcb_msr(val);
14962306a36Sopenharmony_ci	VMGEXIT();
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	while (true)
15262306a36Sopenharmony_ci		asm volatile("hlt\n" : : : "memory");
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cibool hv_ghcb_negotiate_protocol(void)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	u64 ghcb_gpa;
15862306a36Sopenharmony_ci	u64 val;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Save ghcb page gpa. */
16162306a36Sopenharmony_ci	ghcb_gpa = rd_ghcb_msr();
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Do the GHCB protocol version negotiation */
16462306a36Sopenharmony_ci	wr_ghcb_msr(GHCB_MSR_SEV_INFO_REQ);
16562306a36Sopenharmony_ci	VMGEXIT();
16662306a36Sopenharmony_ci	val = rd_ghcb_msr();
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (GHCB_MSR_INFO(val) != GHCB_MSR_SEV_INFO_RESP)
16962306a36Sopenharmony_ci		return false;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (GHCB_MSR_PROTO_MAX(val) < GHCB_PROTOCOL_MIN ||
17262306a36Sopenharmony_ci	    GHCB_MSR_PROTO_MIN(val) > GHCB_PROTOCOL_MAX)
17362306a36Sopenharmony_ci		return false;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	hv_ghcb_version = min_t(size_t, GHCB_MSR_PROTO_MAX(val),
17662306a36Sopenharmony_ci			     GHCB_PROTOCOL_MAX);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Write ghcb page back after negotiating protocol. */
17962306a36Sopenharmony_ci	wr_ghcb_msr(ghcb_gpa);
18062306a36Sopenharmony_ci	VMGEXIT();
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return true;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void hv_ghcb_msr_write(u64 msr, u64 value)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	union hv_ghcb *hv_ghcb;
18862306a36Sopenharmony_ci	void **ghcb_base;
18962306a36Sopenharmony_ci	unsigned long flags;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!hv_ghcb_pg)
19262306a36Sopenharmony_ci		return;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	WARN_ON(in_nmi());
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	local_irq_save(flags);
19762306a36Sopenharmony_ci	ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
19862306a36Sopenharmony_ci	hv_ghcb = (union hv_ghcb *)*ghcb_base;
19962306a36Sopenharmony_ci	if (!hv_ghcb) {
20062306a36Sopenharmony_ci		local_irq_restore(flags);
20162306a36Sopenharmony_ci		return;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	ghcb_set_rcx(&hv_ghcb->ghcb, msr);
20562306a36Sopenharmony_ci	ghcb_set_rax(&hv_ghcb->ghcb, lower_32_bits(value));
20662306a36Sopenharmony_ci	ghcb_set_rdx(&hv_ghcb->ghcb, upper_32_bits(value));
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (hv_ghcb_hv_call(&hv_ghcb->ghcb, SVM_EXIT_MSR, 1, 0))
20962306a36Sopenharmony_ci		pr_warn("Fail to write msr via ghcb %llx.\n", msr);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	local_irq_restore(flags);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void hv_ghcb_msr_read(u64 msr, u64 *value)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	union hv_ghcb *hv_ghcb;
21762306a36Sopenharmony_ci	void **ghcb_base;
21862306a36Sopenharmony_ci	unsigned long flags;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Check size of union hv_ghcb here. */
22162306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union hv_ghcb) != HV_HYP_PAGE_SIZE);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!hv_ghcb_pg)
22462306a36Sopenharmony_ci		return;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	WARN_ON(in_nmi());
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	local_irq_save(flags);
22962306a36Sopenharmony_ci	ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
23062306a36Sopenharmony_ci	hv_ghcb = (union hv_ghcb *)*ghcb_base;
23162306a36Sopenharmony_ci	if (!hv_ghcb) {
23262306a36Sopenharmony_ci		local_irq_restore(flags);
23362306a36Sopenharmony_ci		return;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ghcb_set_rcx(&hv_ghcb->ghcb, msr);
23762306a36Sopenharmony_ci	if (hv_ghcb_hv_call(&hv_ghcb->ghcb, SVM_EXIT_MSR, 0, 0))
23862306a36Sopenharmony_ci		pr_warn("Fail to read msr via ghcb %llx.\n", msr);
23962306a36Sopenharmony_ci	else
24062306a36Sopenharmony_ci		*value = (u64)lower_32_bits(hv_ghcb->ghcb.save.rax)
24162306a36Sopenharmony_ci			| ((u64)lower_32_bits(hv_ghcb->ghcb.save.rdx) << 32);
24262306a36Sopenharmony_ci	local_irq_restore(flags);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/* Only used in a fully enlightened SNP VM, i.e. without the paravisor */
24662306a36Sopenharmony_cistatic u8 ap_start_input_arg[PAGE_SIZE] __bss_decrypted __aligned(PAGE_SIZE);
24762306a36Sopenharmony_cistatic u8 ap_start_stack[PAGE_SIZE] __aligned(PAGE_SIZE);
24862306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct sev_es_save_area *, hv_sev_vmsa);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/* Functions only used in an SNP VM without the paravisor go here. */
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci#define hv_populate_vmcb_seg(seg, gdtr_base)			\
25362306a36Sopenharmony_cido {								\
25462306a36Sopenharmony_ci	if (seg.selector) {					\
25562306a36Sopenharmony_ci		seg.base = 0;					\
25662306a36Sopenharmony_ci		seg.limit = HV_AP_SEGMENT_LIMIT;		\
25762306a36Sopenharmony_ci		seg.attrib = *(u16 *)(gdtr_base + seg.selector + 5);	\
25862306a36Sopenharmony_ci		seg.attrib = (seg.attrib & 0xFF) | ((seg.attrib >> 4) & 0xF00); \
25962306a36Sopenharmony_ci	}							\
26062306a36Sopenharmony_ci} while (0)							\
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int snp_set_vmsa(void *va, bool vmsa)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	u64 attrs;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/*
26762306a36Sopenharmony_ci	 * Running at VMPL0 allows the kernel to change the VMSA bit for a page
26862306a36Sopenharmony_ci	 * using the RMPADJUST instruction. However, for the instruction to
26962306a36Sopenharmony_ci	 * succeed it must target the permissions of a lesser privileged
27062306a36Sopenharmony_ci	 * (higher numbered) VMPL level, so use VMPL1 (refer to the RMPADJUST
27162306a36Sopenharmony_ci	 * instruction in the AMD64 APM Volume 3).
27262306a36Sopenharmony_ci	 */
27362306a36Sopenharmony_ci	attrs = 1;
27462306a36Sopenharmony_ci	if (vmsa)
27562306a36Sopenharmony_ci		attrs |= RMPADJUST_VMSA_PAGE_BIT;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return rmpadjust((unsigned long)va, RMP_PG_SIZE_4K, attrs);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int err;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	err = snp_set_vmsa(vmsa, false);
28562306a36Sopenharmony_ci	if (err)
28662306a36Sopenharmony_ci		pr_err("clear VMSA page failed (%u), leaking page\n", err);
28762306a36Sopenharmony_ci	else
28862306a36Sopenharmony_ci		free_page((unsigned long)vmsa);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ciint hv_snp_boot_ap(int cpu, unsigned long start_ip)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct sev_es_save_area *vmsa = (struct sev_es_save_area *)
29462306a36Sopenharmony_ci		__get_free_page(GFP_KERNEL | __GFP_ZERO);
29562306a36Sopenharmony_ci	struct sev_es_save_area *cur_vmsa;
29662306a36Sopenharmony_ci	struct desc_ptr gdtr;
29762306a36Sopenharmony_ci	u64 ret, retry = 5;
29862306a36Sopenharmony_ci	struct hv_enable_vp_vtl *start_vp_input;
29962306a36Sopenharmony_ci	unsigned long flags;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (!vmsa)
30262306a36Sopenharmony_ci		return -ENOMEM;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	native_store_gdt(&gdtr);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	vmsa->gdtr.base = gdtr.address;
30762306a36Sopenharmony_ci	vmsa->gdtr.limit = gdtr.size;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	asm volatile("movl %%es, %%eax;" : "=a" (vmsa->es.selector));
31062306a36Sopenharmony_ci	hv_populate_vmcb_seg(vmsa->es, vmsa->gdtr.base);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	asm volatile("movl %%cs, %%eax;" : "=a" (vmsa->cs.selector));
31362306a36Sopenharmony_ci	hv_populate_vmcb_seg(vmsa->cs, vmsa->gdtr.base);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	asm volatile("movl %%ss, %%eax;" : "=a" (vmsa->ss.selector));
31662306a36Sopenharmony_ci	hv_populate_vmcb_seg(vmsa->ss, vmsa->gdtr.base);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	asm volatile("movl %%ds, %%eax;" : "=a" (vmsa->ds.selector));
31962306a36Sopenharmony_ci	hv_populate_vmcb_seg(vmsa->ds, vmsa->gdtr.base);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	vmsa->efer = native_read_msr(MSR_EFER);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	asm volatile("movq %%cr4, %%rax;" : "=a" (vmsa->cr4));
32462306a36Sopenharmony_ci	asm volatile("movq %%cr3, %%rax;" : "=a" (vmsa->cr3));
32562306a36Sopenharmony_ci	asm volatile("movq %%cr0, %%rax;" : "=a" (vmsa->cr0));
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	vmsa->xcr0 = 1;
32862306a36Sopenharmony_ci	vmsa->g_pat = HV_AP_INIT_GPAT_DEFAULT;
32962306a36Sopenharmony_ci	vmsa->rip = (u64)secondary_startup_64_no_verify;
33062306a36Sopenharmony_ci	vmsa->rsp = (u64)&ap_start_stack[PAGE_SIZE];
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/*
33362306a36Sopenharmony_ci	 * Set the SNP-specific fields for this VMSA:
33462306a36Sopenharmony_ci	 *   VMPL level
33562306a36Sopenharmony_ci	 *   SEV_FEATURES (matches the SEV STATUS MSR right shifted 2 bits)
33662306a36Sopenharmony_ci	 */
33762306a36Sopenharmony_ci	vmsa->vmpl = 0;
33862306a36Sopenharmony_ci	vmsa->sev_features = sev_status >> 2;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ret = snp_set_vmsa(vmsa, true);
34162306a36Sopenharmony_ci	if (!ret) {
34262306a36Sopenharmony_ci		pr_err("RMPADJUST(%llx) failed: %llx\n", (u64)vmsa, ret);
34362306a36Sopenharmony_ci		free_page((u64)vmsa);
34462306a36Sopenharmony_ci		return ret;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	local_irq_save(flags);
34862306a36Sopenharmony_ci	start_vp_input = (struct hv_enable_vp_vtl *)ap_start_input_arg;
34962306a36Sopenharmony_ci	memset(start_vp_input, 0, sizeof(*start_vp_input));
35062306a36Sopenharmony_ci	start_vp_input->partition_id = -1;
35162306a36Sopenharmony_ci	start_vp_input->vp_index = cpu;
35262306a36Sopenharmony_ci	start_vp_input->target_vtl.target_vtl = ms_hyperv.vtl;
35362306a36Sopenharmony_ci	*(u64 *)&start_vp_input->vp_context = __pa(vmsa) | 1;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	do {
35662306a36Sopenharmony_ci		ret = hv_do_hypercall(HVCALL_START_VP,
35762306a36Sopenharmony_ci				      start_vp_input, NULL);
35862306a36Sopenharmony_ci	} while (hv_result(ret) == HV_STATUS_TIME_OUT && retry--);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	local_irq_restore(flags);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (!hv_result_success(ret)) {
36362306a36Sopenharmony_ci		pr_err("HvCallStartVirtualProcessor failed: %llx\n", ret);
36462306a36Sopenharmony_ci		snp_cleanup_vmsa(vmsa);
36562306a36Sopenharmony_ci		vmsa = NULL;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	cur_vmsa = per_cpu(hv_sev_vmsa, cpu);
36962306a36Sopenharmony_ci	/* Free up any previous VMSA page */
37062306a36Sopenharmony_ci	if (cur_vmsa)
37162306a36Sopenharmony_ci		snp_cleanup_vmsa(cur_vmsa);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* Record the current VMSA page */
37462306a36Sopenharmony_ci	per_cpu(hv_sev_vmsa, cpu) = vmsa;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return ret;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci#else
38062306a36Sopenharmony_cistatic inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
38162306a36Sopenharmony_cistatic inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
38262306a36Sopenharmony_ci#endif /* CONFIG_AMD_MEM_ENCRYPT */
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci#ifdef CONFIG_INTEL_TDX_GUEST
38562306a36Sopenharmony_cistatic void hv_tdx_msr_write(u64 msr, u64 val)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
38862306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
38962306a36Sopenharmony_ci		.r11 = EXIT_REASON_MSR_WRITE,
39062306a36Sopenharmony_ci		.r12 = msr,
39162306a36Sopenharmony_ci		.r13 = val,
39262306a36Sopenharmony_ci	};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	u64 ret = __tdx_hypercall(&args);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	WARN_ONCE(ret, "Failed to emulate MSR write: %lld\n", ret);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void hv_tdx_msr_read(u64 msr, u64 *val)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
40262306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
40362306a36Sopenharmony_ci		.r11 = EXIT_REASON_MSR_READ,
40462306a36Sopenharmony_ci		.r12 = msr,
40562306a36Sopenharmony_ci	};
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	u64 ret = __tdx_hypercall_ret(&args);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (WARN_ONCE(ret, "Failed to emulate MSR read: %lld\n", ret))
41062306a36Sopenharmony_ci		*val = 0;
41162306a36Sopenharmony_ci	else
41262306a36Sopenharmony_ci		*val = args.r11;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ciu64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct tdx_hypercall_args args = { };
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	args.r10 = control;
42062306a36Sopenharmony_ci	args.rdx = param1;
42162306a36Sopenharmony_ci	args.r8  = param2;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	(void)__tdx_hypercall_ret(&args);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return args.r11;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci#else
42962306a36Sopenharmony_cistatic inline void hv_tdx_msr_write(u64 msr, u64 value) {}
43062306a36Sopenharmony_cistatic inline void hv_tdx_msr_read(u64 msr, u64 *value) {}
43162306a36Sopenharmony_ci#endif /* CONFIG_INTEL_TDX_GUEST */
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci#if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)
43462306a36Sopenharmony_civoid hv_ivm_msr_write(u64 msr, u64 value)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	if (!ms_hyperv.paravisor_present)
43762306a36Sopenharmony_ci		return;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (hv_isolation_type_tdx())
44062306a36Sopenharmony_ci		hv_tdx_msr_write(msr, value);
44162306a36Sopenharmony_ci	else if (hv_isolation_type_snp())
44262306a36Sopenharmony_ci		hv_ghcb_msr_write(msr, value);
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_civoid hv_ivm_msr_read(u64 msr, u64 *value)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	if (!ms_hyperv.paravisor_present)
44862306a36Sopenharmony_ci		return;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (hv_isolation_type_tdx())
45162306a36Sopenharmony_ci		hv_tdx_msr_read(msr, value);
45262306a36Sopenharmony_ci	else if (hv_isolation_type_snp())
45362306a36Sopenharmony_ci		hv_ghcb_msr_read(msr, value);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/*
45762306a36Sopenharmony_ci * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * In Isolation VM, all guest memory is encrypted from host and guest
46062306a36Sopenharmony_ci * needs to set memory visible to host via hvcall before sharing memory
46162306a36Sopenharmony_ci * with host.
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_cistatic int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
46462306a36Sopenharmony_ci			   enum hv_mem_host_visibility visibility)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct hv_gpa_range_for_visibility *input;
46762306a36Sopenharmony_ci	u16 pages_processed;
46862306a36Sopenharmony_ci	u64 hv_status;
46962306a36Sopenharmony_ci	unsigned long flags;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* no-op if partition isolation is not enabled */
47262306a36Sopenharmony_ci	if (!hv_is_isolation_supported())
47362306a36Sopenharmony_ci		return 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (count > HV_MAX_MODIFY_GPA_REP_COUNT) {
47662306a36Sopenharmony_ci		pr_err("Hyper-V: GPA count:%d exceeds supported:%lu\n", count,
47762306a36Sopenharmony_ci			HV_MAX_MODIFY_GPA_REP_COUNT);
47862306a36Sopenharmony_ci		return -EINVAL;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	local_irq_save(flags);
48262306a36Sopenharmony_ci	input = *this_cpu_ptr(hyperv_pcpu_input_arg);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (unlikely(!input)) {
48562306a36Sopenharmony_ci		local_irq_restore(flags);
48662306a36Sopenharmony_ci		return -EINVAL;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	input->partition_id = HV_PARTITION_ID_SELF;
49062306a36Sopenharmony_ci	input->host_visibility = visibility;
49162306a36Sopenharmony_ci	input->reserved0 = 0;
49262306a36Sopenharmony_ci	input->reserved1 = 0;
49362306a36Sopenharmony_ci	memcpy((void *)input->gpa_page_list, pfn, count * sizeof(*pfn));
49462306a36Sopenharmony_ci	hv_status = hv_do_rep_hypercall(
49562306a36Sopenharmony_ci			HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY, count,
49662306a36Sopenharmony_ci			0, input, &pages_processed);
49762306a36Sopenharmony_ci	local_irq_restore(flags);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (hv_result_success(hv_status))
50062306a36Sopenharmony_ci		return 0;
50162306a36Sopenharmony_ci	else
50262306a36Sopenharmony_ci		return -EFAULT;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/*
50662306a36Sopenharmony_ci * hv_vtom_set_host_visibility - Set specified memory visible to host.
50762306a36Sopenharmony_ci *
50862306a36Sopenharmony_ci * In Isolation VM, all guest memory is encrypted from host and guest
50962306a36Sopenharmony_ci * needs to set memory visible to host via hvcall before sharing memory
51062306a36Sopenharmony_ci * with host. This function works as wrap of hv_mark_gpa_visibility()
51162306a36Sopenharmony_ci * with memory base and size.
51262306a36Sopenharmony_ci */
51362306a36Sopenharmony_cistatic bool hv_vtom_set_host_visibility(unsigned long kbuffer, int pagecount, bool enc)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	enum hv_mem_host_visibility visibility = enc ?
51662306a36Sopenharmony_ci			VMBUS_PAGE_NOT_VISIBLE : VMBUS_PAGE_VISIBLE_READ_WRITE;
51762306a36Sopenharmony_ci	u64 *pfn_array;
51862306a36Sopenharmony_ci	int ret = 0;
51962306a36Sopenharmony_ci	bool result = true;
52062306a36Sopenharmony_ci	int i, pfn;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
52362306a36Sopenharmony_ci	if (!pfn_array)
52462306a36Sopenharmony_ci		return false;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	for (i = 0, pfn = 0; i < pagecount; i++) {
52762306a36Sopenharmony_ci		pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE);
52862306a36Sopenharmony_ci		pfn++;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) {
53162306a36Sopenharmony_ci			ret = hv_mark_gpa_visibility(pfn, pfn_array,
53262306a36Sopenharmony_ci						     visibility);
53362306a36Sopenharmony_ci			if (ret) {
53462306a36Sopenharmony_ci				result = false;
53562306a36Sopenharmony_ci				goto err_free_pfn_array;
53662306a36Sopenharmony_ci			}
53762306a36Sopenharmony_ci			pfn = 0;
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci err_free_pfn_array:
54262306a36Sopenharmony_ci	kfree(pfn_array);
54362306a36Sopenharmony_ci	return result;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic bool hv_vtom_tlb_flush_required(bool private)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	return true;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic bool hv_vtom_cache_flush_required(void)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	return false;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic bool hv_is_private_mmio(u64 addr)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	/*
55962306a36Sopenharmony_ci	 * Hyper-V always provides a single IO-APIC in a guest VM.
56062306a36Sopenharmony_ci	 * When a paravisor is used, it is emulated by the paravisor
56162306a36Sopenharmony_ci	 * in the guest context and must be mapped private.
56262306a36Sopenharmony_ci	 */
56362306a36Sopenharmony_ci	if (addr >= HV_IOAPIC_BASE_ADDRESS &&
56462306a36Sopenharmony_ci	    addr < (HV_IOAPIC_BASE_ADDRESS + PAGE_SIZE))
56562306a36Sopenharmony_ci		return true;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* Same with a vTPM */
56862306a36Sopenharmony_ci	if (addr >= VTPM_BASE_ADDRESS &&
56962306a36Sopenharmony_ci	    addr < (VTPM_BASE_ADDRESS + PAGE_SIZE))
57062306a36Sopenharmony_ci		return true;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return false;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_civoid __init hv_vtom_init(void)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	enum hv_isolation_type type = hv_get_isolation_type();
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	switch (type) {
58062306a36Sopenharmony_ci	case HV_ISOLATION_TYPE_VBS:
58162306a36Sopenharmony_ci		fallthrough;
58262306a36Sopenharmony_ci	/*
58362306a36Sopenharmony_ci	 * By design, a VM using vTOM doesn't see the SEV setting,
58462306a36Sopenharmony_ci	 * so SEV initialization is bypassed and sev_status isn't set.
58562306a36Sopenharmony_ci	 * Set it here to indicate a vTOM VM.
58662306a36Sopenharmony_ci	 *
58762306a36Sopenharmony_ci	 * Note: if CONFIG_AMD_MEM_ENCRYPT is not set, sev_status is
58862306a36Sopenharmony_ci	 * defined as 0ULL, to which we can't assigned a value.
58962306a36Sopenharmony_ci	 */
59062306a36Sopenharmony_ci#ifdef CONFIG_AMD_MEM_ENCRYPT
59162306a36Sopenharmony_ci	case HV_ISOLATION_TYPE_SNP:
59262306a36Sopenharmony_ci		sev_status = MSR_AMD64_SNP_VTOM;
59362306a36Sopenharmony_ci		cc_vendor = CC_VENDOR_AMD;
59462306a36Sopenharmony_ci		break;
59562306a36Sopenharmony_ci#endif
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	case HV_ISOLATION_TYPE_TDX:
59862306a36Sopenharmony_ci		cc_vendor = CC_VENDOR_INTEL;
59962306a36Sopenharmony_ci		break;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	default:
60262306a36Sopenharmony_ci		panic("hv_vtom_init: unsupported isolation type %d\n", type);
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	cc_set_mask(ms_hyperv.shared_gpa_boundary);
60662306a36Sopenharmony_ci	physical_mask &= ms_hyperv.shared_gpa_boundary - 1;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	x86_platform.hyper.is_private_mmio = hv_is_private_mmio;
60962306a36Sopenharmony_ci	x86_platform.guest.enc_cache_flush_required = hv_vtom_cache_flush_required;
61062306a36Sopenharmony_ci	x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
61162306a36Sopenharmony_ci	x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* Set WB as the default cache mode. */
61462306a36Sopenharmony_ci	mtrr_overwrite_state(NULL, 0, MTRR_TYPE_WRBACK);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci#endif /* defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST) */
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cienum hv_isolation_type hv_get_isolation_type(void)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	if (!(ms_hyperv.priv_high & HV_ISOLATION))
62262306a36Sopenharmony_ci		return HV_ISOLATION_TYPE_NONE;
62362306a36Sopenharmony_ci	return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hv_get_isolation_type);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/*
62862306a36Sopenharmony_ci * hv_is_isolation_supported - Check system runs in the Hyper-V
62962306a36Sopenharmony_ci * isolation VM.
63062306a36Sopenharmony_ci */
63162306a36Sopenharmony_cibool hv_is_isolation_supported(void)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
63462306a36Sopenharmony_ci		return false;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
63762306a36Sopenharmony_ci		return false;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(isolation_type_snp);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/*
64562306a36Sopenharmony_ci * hv_isolation_type_snp - Check if the system runs in an AMD SEV-SNP based
64662306a36Sopenharmony_ci * isolation VM.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cibool hv_isolation_type_snp(void)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	return static_branch_unlikely(&isolation_type_snp);
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(isolation_type_tdx);
65462306a36Sopenharmony_ci/*
65562306a36Sopenharmony_ci * hv_isolation_type_tdx - Check if the system runs in an Intel TDX based
65662306a36Sopenharmony_ci * isolated VM.
65762306a36Sopenharmony_ci */
65862306a36Sopenharmony_cibool hv_isolation_type_tdx(void)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	return static_branch_unlikely(&isolation_type_tdx);
66162306a36Sopenharmony_ci}
662