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