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