162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Kernel-based Virtual Machine driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * AMD SVM-SEV support 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2010 Red Hat, Inc. and/or its affiliates. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kvm_types.h> 1262306a36Sopenharmony_ci#include <linux/kvm_host.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/highmem.h> 1562306a36Sopenharmony_ci#include <linux/psp.h> 1662306a36Sopenharmony_ci#include <linux/psp-sev.h> 1762306a36Sopenharmony_ci#include <linux/pagemap.h> 1862306a36Sopenharmony_ci#include <linux/swap.h> 1962306a36Sopenharmony_ci#include <linux/misc_cgroup.h> 2062306a36Sopenharmony_ci#include <linux/processor.h> 2162306a36Sopenharmony_ci#include <linux/trace_events.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/pkru.h> 2462306a36Sopenharmony_ci#include <asm/trapnr.h> 2562306a36Sopenharmony_ci#include <asm/fpu/xcr.h> 2662306a36Sopenharmony_ci#include <asm/debugreg.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "mmu.h" 2962306a36Sopenharmony_ci#include "x86.h" 3062306a36Sopenharmony_ci#include "svm.h" 3162306a36Sopenharmony_ci#include "svm_ops.h" 3262306a36Sopenharmony_ci#include "cpuid.h" 3362306a36Sopenharmony_ci#include "trace.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#ifndef CONFIG_KVM_AMD_SEV 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * When this config is not defined, SEV feature is not supported and APIs in 3862306a36Sopenharmony_ci * this file are not used but this file still gets compiled into the KVM AMD 3962306a36Sopenharmony_ci * module. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * We will not have MISC_CG_RES_SEV and MISC_CG_RES_SEV_ES entries in the enum 4262306a36Sopenharmony_ci * misc_res_type {} defined in linux/misc_cgroup.h. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Below macros allow compilation to succeed. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci#define MISC_CG_RES_SEV MISC_CG_RES_TYPES 4762306a36Sopenharmony_ci#define MISC_CG_RES_SEV_ES MISC_CG_RES_TYPES 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#ifdef CONFIG_KVM_AMD_SEV 5162306a36Sopenharmony_ci/* enable/disable SEV support */ 5262306a36Sopenharmony_cistatic bool sev_enabled = true; 5362306a36Sopenharmony_cimodule_param_named(sev, sev_enabled, bool, 0444); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* enable/disable SEV-ES support */ 5662306a36Sopenharmony_cistatic bool sev_es_enabled = true; 5762306a36Sopenharmony_cimodule_param_named(sev_es, sev_es_enabled, bool, 0444); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* enable/disable SEV-ES DebugSwap support */ 6062306a36Sopenharmony_cistatic bool sev_es_debug_swap_enabled = true; 6162306a36Sopenharmony_cimodule_param_named(debug_swap, sev_es_debug_swap_enabled, bool, 0444); 6262306a36Sopenharmony_ci#else 6362306a36Sopenharmony_ci#define sev_enabled false 6462306a36Sopenharmony_ci#define sev_es_enabled false 6562306a36Sopenharmony_ci#define sev_es_debug_swap_enabled false 6662306a36Sopenharmony_ci#endif /* CONFIG_KVM_AMD_SEV */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic u8 sev_enc_bit; 6962306a36Sopenharmony_cistatic DECLARE_RWSEM(sev_deactivate_lock); 7062306a36Sopenharmony_cistatic DEFINE_MUTEX(sev_bitmap_lock); 7162306a36Sopenharmony_ciunsigned int max_sev_asid; 7262306a36Sopenharmony_cistatic unsigned int min_sev_asid; 7362306a36Sopenharmony_cistatic unsigned long sev_me_mask; 7462306a36Sopenharmony_cistatic unsigned int nr_asids; 7562306a36Sopenharmony_cistatic unsigned long *sev_asid_bitmap; 7662306a36Sopenharmony_cistatic unsigned long *sev_reclaim_asid_bitmap; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistruct enc_region { 7962306a36Sopenharmony_ci struct list_head list; 8062306a36Sopenharmony_ci unsigned long npages; 8162306a36Sopenharmony_ci struct page **pages; 8262306a36Sopenharmony_ci unsigned long uaddr; 8362306a36Sopenharmony_ci unsigned long size; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Called with the sev_bitmap_lock held, or on shutdown */ 8762306a36Sopenharmony_cistatic int sev_flush_asids(int min_asid, int max_asid) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int ret, asid, error = 0; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Check if there are any ASIDs to reclaim before performing a flush */ 9262306a36Sopenharmony_ci asid = find_next_bit(sev_reclaim_asid_bitmap, nr_asids, min_asid); 9362306a36Sopenharmony_ci if (asid > max_asid) 9462306a36Sopenharmony_ci return -EBUSY; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * DEACTIVATE will clear the WBINVD indicator causing DF_FLUSH to fail, 9862306a36Sopenharmony_ci * so it must be guarded. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci down_write(&sev_deactivate_lock); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci wbinvd_on_all_cpus(); 10362306a36Sopenharmony_ci ret = sev_guest_df_flush(&error); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci up_write(&sev_deactivate_lock); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (ret) 10862306a36Sopenharmony_ci pr_err("SEV: DF_FLUSH failed, ret=%d, error=%#x\n", ret, error); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return ret; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline bool is_mirroring_enc_context(struct kvm *kvm) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return !!to_kvm_svm(kvm)->sev_info.enc_context_owner; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Must be called with the sev_bitmap_lock held */ 11962306a36Sopenharmony_cistatic bool __sev_recycle_asids(int min_asid, int max_asid) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci if (sev_flush_asids(min_asid, max_asid)) 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* The flush process will flush all reclaimable SEV and SEV-ES ASIDs */ 12562306a36Sopenharmony_ci bitmap_xor(sev_asid_bitmap, sev_asid_bitmap, sev_reclaim_asid_bitmap, 12662306a36Sopenharmony_ci nr_asids); 12762306a36Sopenharmony_ci bitmap_zero(sev_reclaim_asid_bitmap, nr_asids); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return true; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int sev_misc_cg_try_charge(struct kvm_sev_info *sev) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci enum misc_res_type type = sev->es_active ? MISC_CG_RES_SEV_ES : MISC_CG_RES_SEV; 13562306a36Sopenharmony_ci return misc_cg_try_charge(type, sev->misc_cg, 1); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void sev_misc_cg_uncharge(struct kvm_sev_info *sev) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci enum misc_res_type type = sev->es_active ? MISC_CG_RES_SEV_ES : MISC_CG_RES_SEV; 14162306a36Sopenharmony_ci misc_cg_uncharge(type, sev->misc_cg, 1); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int sev_asid_new(struct kvm_sev_info *sev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int asid, min_asid, max_asid, ret; 14762306a36Sopenharmony_ci bool retry = true; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci WARN_ON(sev->misc_cg); 15062306a36Sopenharmony_ci sev->misc_cg = get_current_misc_cg(); 15162306a36Sopenharmony_ci ret = sev_misc_cg_try_charge(sev); 15262306a36Sopenharmony_ci if (ret) { 15362306a36Sopenharmony_ci put_misc_cg(sev->misc_cg); 15462306a36Sopenharmony_ci sev->misc_cg = NULL; 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci mutex_lock(&sev_bitmap_lock); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * SEV-enabled guests must use asid from min_sev_asid to max_sev_asid. 16262306a36Sopenharmony_ci * SEV-ES-enabled guest can use from 1 to min_sev_asid - 1. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci min_asid = sev->es_active ? 1 : min_sev_asid; 16562306a36Sopenharmony_ci max_asid = sev->es_active ? min_sev_asid - 1 : max_sev_asid; 16662306a36Sopenharmony_ciagain: 16762306a36Sopenharmony_ci asid = find_next_zero_bit(sev_asid_bitmap, max_asid + 1, min_asid); 16862306a36Sopenharmony_ci if (asid > max_asid) { 16962306a36Sopenharmony_ci if (retry && __sev_recycle_asids(min_asid, max_asid)) { 17062306a36Sopenharmony_ci retry = false; 17162306a36Sopenharmony_ci goto again; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci mutex_unlock(&sev_bitmap_lock); 17462306a36Sopenharmony_ci ret = -EBUSY; 17562306a36Sopenharmony_ci goto e_uncharge; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci __set_bit(asid, sev_asid_bitmap); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mutex_unlock(&sev_bitmap_lock); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return asid; 18362306a36Sopenharmony_cie_uncharge: 18462306a36Sopenharmony_ci sev_misc_cg_uncharge(sev); 18562306a36Sopenharmony_ci put_misc_cg(sev->misc_cg); 18662306a36Sopenharmony_ci sev->misc_cg = NULL; 18762306a36Sopenharmony_ci return ret; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int sev_get_asid(struct kvm *kvm) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return sev->asid; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void sev_asid_free(struct kvm_sev_info *sev) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct svm_cpu_data *sd; 20062306a36Sopenharmony_ci int cpu; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci mutex_lock(&sev_bitmap_lock); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci __set_bit(sev->asid, sev_reclaim_asid_bitmap); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 20762306a36Sopenharmony_ci sd = per_cpu_ptr(&svm_data, cpu); 20862306a36Sopenharmony_ci sd->sev_vmcbs[sev->asid] = NULL; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci mutex_unlock(&sev_bitmap_lock); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci sev_misc_cg_uncharge(sev); 21462306a36Sopenharmony_ci put_misc_cg(sev->misc_cg); 21562306a36Sopenharmony_ci sev->misc_cg = NULL; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void sev_decommission(unsigned int handle) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct sev_data_decommission decommission; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!handle) 22362306a36Sopenharmony_ci return; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci decommission.handle = handle; 22662306a36Sopenharmony_ci sev_guest_decommission(&decommission, NULL); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void sev_unbind_asid(struct kvm *kvm, unsigned int handle) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct sev_data_deactivate deactivate; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (!handle) 23462306a36Sopenharmony_ci return; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci deactivate.handle = handle; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Guard DEACTIVATE against WBINVD/DF_FLUSH used in ASID recycling */ 23962306a36Sopenharmony_ci down_read(&sev_deactivate_lock); 24062306a36Sopenharmony_ci sev_guest_deactivate(&deactivate, NULL); 24162306a36Sopenharmony_ci up_read(&sev_deactivate_lock); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci sev_decommission(handle); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 24962306a36Sopenharmony_ci int asid, ret; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (kvm->created_vcpus) 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ret = -EBUSY; 25562306a36Sopenharmony_ci if (unlikely(sev->active)) 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci sev->active = true; 25962306a36Sopenharmony_ci sev->es_active = argp->id == KVM_SEV_ES_INIT; 26062306a36Sopenharmony_ci asid = sev_asid_new(sev); 26162306a36Sopenharmony_ci if (asid < 0) 26262306a36Sopenharmony_ci goto e_no_asid; 26362306a36Sopenharmony_ci sev->asid = asid; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = sev_platform_init(&argp->error); 26662306a36Sopenharmony_ci if (ret) 26762306a36Sopenharmony_ci goto e_free; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci INIT_LIST_HEAD(&sev->regions_list); 27062306a36Sopenharmony_ci INIT_LIST_HEAD(&sev->mirror_vms); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_SEV); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cie_free: 27762306a36Sopenharmony_ci sev_asid_free(sev); 27862306a36Sopenharmony_ci sev->asid = 0; 27962306a36Sopenharmony_cie_no_asid: 28062306a36Sopenharmony_ci sev->es_active = false; 28162306a36Sopenharmony_ci sev->active = false; 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int sev_bind_asid(struct kvm *kvm, unsigned int handle, int *error) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct sev_data_activate activate; 28862306a36Sopenharmony_ci int asid = sev_get_asid(kvm); 28962306a36Sopenharmony_ci int ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* activate ASID on the given handle */ 29262306a36Sopenharmony_ci activate.handle = handle; 29362306a36Sopenharmony_ci activate.asid = asid; 29462306a36Sopenharmony_ci ret = sev_guest_activate(&activate, error); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int __sev_issue_cmd(int fd, int id, void *data, int *error) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct fd f; 30262306a36Sopenharmony_ci int ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci f = fdget(fd); 30562306a36Sopenharmony_ci if (!f.file) 30662306a36Sopenharmony_ci return -EBADF; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ret = sev_issue_cmd_external_user(f.file, id, data, error); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci fdput(f); 31162306a36Sopenharmony_ci return ret; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int sev_issue_cmd(struct kvm *kvm, int id, void *data, int *error) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return __sev_issue_cmd(sev->fd, id, data, error); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 32462306a36Sopenharmony_ci struct sev_data_launch_start start; 32562306a36Sopenharmony_ci struct kvm_sev_launch_start params; 32662306a36Sopenharmony_ci void *dh_blob, *session_blob; 32762306a36Sopenharmony_ci int *error = &argp->error; 32862306a36Sopenharmony_ci int ret; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!sev_guest(kvm)) 33162306a36Sopenharmony_ci return -ENOTTY; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) 33462306a36Sopenharmony_ci return -EFAULT; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci memset(&start, 0, sizeof(start)); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci dh_blob = NULL; 33962306a36Sopenharmony_ci if (params.dh_uaddr) { 34062306a36Sopenharmony_ci dh_blob = psp_copy_user_blob(params.dh_uaddr, params.dh_len); 34162306a36Sopenharmony_ci if (IS_ERR(dh_blob)) 34262306a36Sopenharmony_ci return PTR_ERR(dh_blob); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci start.dh_cert_address = __sme_set(__pa(dh_blob)); 34562306a36Sopenharmony_ci start.dh_cert_len = params.dh_len; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci session_blob = NULL; 34962306a36Sopenharmony_ci if (params.session_uaddr) { 35062306a36Sopenharmony_ci session_blob = psp_copy_user_blob(params.session_uaddr, params.session_len); 35162306a36Sopenharmony_ci if (IS_ERR(session_blob)) { 35262306a36Sopenharmony_ci ret = PTR_ERR(session_blob); 35362306a36Sopenharmony_ci goto e_free_dh; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci start.session_address = __sme_set(__pa(session_blob)); 35762306a36Sopenharmony_ci start.session_len = params.session_len; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci start.handle = params.handle; 36162306a36Sopenharmony_ci start.policy = params.policy; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* create memory encryption context */ 36462306a36Sopenharmony_ci ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_LAUNCH_START, &start, error); 36562306a36Sopenharmony_ci if (ret) 36662306a36Sopenharmony_ci goto e_free_session; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Bind ASID to this guest */ 36962306a36Sopenharmony_ci ret = sev_bind_asid(kvm, start.handle, error); 37062306a36Sopenharmony_ci if (ret) { 37162306a36Sopenharmony_ci sev_decommission(start.handle); 37262306a36Sopenharmony_ci goto e_free_session; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* return handle to userspace */ 37662306a36Sopenharmony_ci params.handle = start.handle; 37762306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params))) { 37862306a36Sopenharmony_ci sev_unbind_asid(kvm, start.handle); 37962306a36Sopenharmony_ci ret = -EFAULT; 38062306a36Sopenharmony_ci goto e_free_session; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci sev->handle = start.handle; 38462306a36Sopenharmony_ci sev->fd = argp->sev_fd; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cie_free_session: 38762306a36Sopenharmony_ci kfree(session_blob); 38862306a36Sopenharmony_cie_free_dh: 38962306a36Sopenharmony_ci kfree(dh_blob); 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr, 39462306a36Sopenharmony_ci unsigned long ulen, unsigned long *n, 39562306a36Sopenharmony_ci int write) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 39862306a36Sopenharmony_ci unsigned long npages, size; 39962306a36Sopenharmony_ci int npinned; 40062306a36Sopenharmony_ci unsigned long locked, lock_limit; 40162306a36Sopenharmony_ci struct page **pages; 40262306a36Sopenharmony_ci unsigned long first, last; 40362306a36Sopenharmony_ci int ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci lockdep_assert_held(&kvm->lock); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (ulen == 0 || uaddr + ulen < uaddr) 40862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Calculate number of pages. */ 41162306a36Sopenharmony_ci first = (uaddr & PAGE_MASK) >> PAGE_SHIFT; 41262306a36Sopenharmony_ci last = ((uaddr + ulen - 1) & PAGE_MASK) >> PAGE_SHIFT; 41362306a36Sopenharmony_ci npages = (last - first + 1); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci locked = sev->pages_locked + npages; 41662306a36Sopenharmony_ci lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; 41762306a36Sopenharmony_ci if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { 41862306a36Sopenharmony_ci pr_err("SEV: %lu locked pages exceed the lock limit of %lu.\n", locked, lock_limit); 41962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (WARN_ON_ONCE(npages > INT_MAX)) 42362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Avoid using vmalloc for smaller buffers. */ 42662306a36Sopenharmony_ci size = npages * sizeof(struct page *); 42762306a36Sopenharmony_ci if (size > PAGE_SIZE) 42862306a36Sopenharmony_ci pages = __vmalloc(size, GFP_KERNEL_ACCOUNT | __GFP_ZERO); 42962306a36Sopenharmony_ci else 43062306a36Sopenharmony_ci pages = kmalloc(size, GFP_KERNEL_ACCOUNT); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!pages) 43362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Pin the user virtual address. */ 43662306a36Sopenharmony_ci npinned = pin_user_pages_fast(uaddr, npages, write ? FOLL_WRITE : 0, pages); 43762306a36Sopenharmony_ci if (npinned != npages) { 43862306a36Sopenharmony_ci pr_err("SEV: Failure locking %lu pages.\n", npages); 43962306a36Sopenharmony_ci ret = -ENOMEM; 44062306a36Sopenharmony_ci goto err; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci *n = npages; 44462306a36Sopenharmony_ci sev->pages_locked = locked; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return pages; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cierr: 44962306a36Sopenharmony_ci if (npinned > 0) 45062306a36Sopenharmony_ci unpin_user_pages(pages, npinned); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci kvfree(pages); 45362306a36Sopenharmony_ci return ERR_PTR(ret); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic void sev_unpin_memory(struct kvm *kvm, struct page **pages, 45762306a36Sopenharmony_ci unsigned long npages) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci unpin_user_pages(pages, npages); 46262306a36Sopenharmony_ci kvfree(pages); 46362306a36Sopenharmony_ci sev->pages_locked -= npages; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic void sev_clflush_pages(struct page *pages[], unsigned long npages) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci uint8_t *page_virtual; 46962306a36Sopenharmony_ci unsigned long i; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (this_cpu_has(X86_FEATURE_SME_COHERENT) || npages == 0 || 47262306a36Sopenharmony_ci pages == NULL) 47362306a36Sopenharmony_ci return; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci for (i = 0; i < npages; i++) { 47662306a36Sopenharmony_ci page_virtual = kmap_local_page(pages[i]); 47762306a36Sopenharmony_ci clflush_cache_range(page_virtual, PAGE_SIZE); 47862306a36Sopenharmony_ci kunmap_local(page_virtual); 47962306a36Sopenharmony_ci cond_resched(); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic unsigned long get_num_contig_pages(unsigned long idx, 48462306a36Sopenharmony_ci struct page **inpages, unsigned long npages) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci unsigned long paddr, next_paddr; 48762306a36Sopenharmony_ci unsigned long i = idx + 1, pages = 1; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* find the number of contiguous pages starting from idx */ 49062306a36Sopenharmony_ci paddr = __sme_page_pa(inpages[idx]); 49162306a36Sopenharmony_ci while (i < npages) { 49262306a36Sopenharmony_ci next_paddr = __sme_page_pa(inpages[i++]); 49362306a36Sopenharmony_ci if ((paddr + PAGE_SIZE) == next_paddr) { 49462306a36Sopenharmony_ci pages++; 49562306a36Sopenharmony_ci paddr = next_paddr; 49662306a36Sopenharmony_ci continue; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return pages; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci unsigned long vaddr, vaddr_end, next_vaddr, npages, pages, size, i; 50762306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 50862306a36Sopenharmony_ci struct kvm_sev_launch_update_data params; 50962306a36Sopenharmony_ci struct sev_data_launch_update_data data; 51062306a36Sopenharmony_ci struct page **inpages; 51162306a36Sopenharmony_ci int ret; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!sev_guest(kvm)) 51462306a36Sopenharmony_ci return -ENOTTY; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) 51762306a36Sopenharmony_ci return -EFAULT; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci vaddr = params.uaddr; 52062306a36Sopenharmony_ci size = params.len; 52162306a36Sopenharmony_ci vaddr_end = vaddr + size; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Lock the user memory. */ 52462306a36Sopenharmony_ci inpages = sev_pin_memory(kvm, vaddr, size, &npages, 1); 52562306a36Sopenharmony_ci if (IS_ERR(inpages)) 52662306a36Sopenharmony_ci return PTR_ERR(inpages); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* 52962306a36Sopenharmony_ci * Flush (on non-coherent CPUs) before LAUNCH_UPDATE encrypts pages in 53062306a36Sopenharmony_ci * place; the cache may contain the data that was written unencrypted. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ci sev_clflush_pages(inpages, npages); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci data.reserved = 0; 53562306a36Sopenharmony_ci data.handle = sev->handle; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci for (i = 0; vaddr < vaddr_end; vaddr = next_vaddr, i += pages) { 53862306a36Sopenharmony_ci int offset, len; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* 54162306a36Sopenharmony_ci * If the user buffer is not page-aligned, calculate the offset 54262306a36Sopenharmony_ci * within the page. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci offset = vaddr & (PAGE_SIZE - 1); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Calculate the number of pages that can be encrypted in one go. */ 54762306a36Sopenharmony_ci pages = get_num_contig_pages(i, inpages, npages); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci len = min_t(size_t, ((pages * PAGE_SIZE) - offset), size); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci data.len = len; 55262306a36Sopenharmony_ci data.address = __sme_page_pa(inpages[i]) + offset; 55362306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_DATA, &data, &argp->error); 55462306a36Sopenharmony_ci if (ret) 55562306a36Sopenharmony_ci goto e_unpin; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci size -= len; 55862306a36Sopenharmony_ci next_vaddr = vaddr + len; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cie_unpin: 56262306a36Sopenharmony_ci /* content of memory is updated, mark pages dirty */ 56362306a36Sopenharmony_ci for (i = 0; i < npages; i++) { 56462306a36Sopenharmony_ci set_page_dirty_lock(inpages[i]); 56562306a36Sopenharmony_ci mark_page_accessed(inpages[i]); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci /* unlock the user pages */ 56862306a36Sopenharmony_ci sev_unpin_memory(kvm, inpages, npages); 56962306a36Sopenharmony_ci return ret; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int sev_es_sync_vmsa(struct vcpu_svm *svm) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct sev_es_save_area *save = svm->sev_es.vmsa; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Check some debug related fields before encrypting the VMSA */ 57762306a36Sopenharmony_ci if (svm->vcpu.guest_debug || (svm->vmcb->save.dr7 & ~DR7_FIXED_1)) 57862306a36Sopenharmony_ci return -EINVAL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * SEV-ES will use a VMSA that is pointed to by the VMCB, not 58262306a36Sopenharmony_ci * the traditional VMSA that is part of the VMCB. Copy the 58362306a36Sopenharmony_ci * traditional VMSA as it has been built so far (in prep 58462306a36Sopenharmony_ci * for LAUNCH_UPDATE_VMSA) to be the initial SEV-ES state. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci memcpy(save, &svm->vmcb->save, sizeof(svm->vmcb->save)); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Sync registgers */ 58962306a36Sopenharmony_ci save->rax = svm->vcpu.arch.regs[VCPU_REGS_RAX]; 59062306a36Sopenharmony_ci save->rbx = svm->vcpu.arch.regs[VCPU_REGS_RBX]; 59162306a36Sopenharmony_ci save->rcx = svm->vcpu.arch.regs[VCPU_REGS_RCX]; 59262306a36Sopenharmony_ci save->rdx = svm->vcpu.arch.regs[VCPU_REGS_RDX]; 59362306a36Sopenharmony_ci save->rsp = svm->vcpu.arch.regs[VCPU_REGS_RSP]; 59462306a36Sopenharmony_ci save->rbp = svm->vcpu.arch.regs[VCPU_REGS_RBP]; 59562306a36Sopenharmony_ci save->rsi = svm->vcpu.arch.regs[VCPU_REGS_RSI]; 59662306a36Sopenharmony_ci save->rdi = svm->vcpu.arch.regs[VCPU_REGS_RDI]; 59762306a36Sopenharmony_ci#ifdef CONFIG_X86_64 59862306a36Sopenharmony_ci save->r8 = svm->vcpu.arch.regs[VCPU_REGS_R8]; 59962306a36Sopenharmony_ci save->r9 = svm->vcpu.arch.regs[VCPU_REGS_R9]; 60062306a36Sopenharmony_ci save->r10 = svm->vcpu.arch.regs[VCPU_REGS_R10]; 60162306a36Sopenharmony_ci save->r11 = svm->vcpu.arch.regs[VCPU_REGS_R11]; 60262306a36Sopenharmony_ci save->r12 = svm->vcpu.arch.regs[VCPU_REGS_R12]; 60362306a36Sopenharmony_ci save->r13 = svm->vcpu.arch.regs[VCPU_REGS_R13]; 60462306a36Sopenharmony_ci save->r14 = svm->vcpu.arch.regs[VCPU_REGS_R14]; 60562306a36Sopenharmony_ci save->r15 = svm->vcpu.arch.regs[VCPU_REGS_R15]; 60662306a36Sopenharmony_ci#endif 60762306a36Sopenharmony_ci save->rip = svm->vcpu.arch.regs[VCPU_REGS_RIP]; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Sync some non-GPR registers before encrypting */ 61062306a36Sopenharmony_ci save->xcr0 = svm->vcpu.arch.xcr0; 61162306a36Sopenharmony_ci save->pkru = svm->vcpu.arch.pkru; 61262306a36Sopenharmony_ci save->xss = svm->vcpu.arch.ia32_xss; 61362306a36Sopenharmony_ci save->dr6 = svm->vcpu.arch.dr6; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (sev_es_debug_swap_enabled) 61662306a36Sopenharmony_ci save->sev_features |= SVM_SEV_FEAT_DEBUG_SWAP; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci pr_debug("Virtual Machine Save Area (VMSA):\n"); 61962306a36Sopenharmony_ci print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, save, sizeof(*save), false); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu, 62562306a36Sopenharmony_ci int *error) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct sev_data_launch_update_vmsa vmsa; 62862306a36Sopenharmony_ci struct vcpu_svm *svm = to_svm(vcpu); 62962306a36Sopenharmony_ci int ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (vcpu->guest_debug) { 63262306a36Sopenharmony_ci pr_warn_once("KVM_SET_GUEST_DEBUG for SEV-ES guest is not supported"); 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Perform some pre-encryption checks against the VMSA */ 63762306a36Sopenharmony_ci ret = sev_es_sync_vmsa(svm); 63862306a36Sopenharmony_ci if (ret) 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* 64262306a36Sopenharmony_ci * The LAUNCH_UPDATE_VMSA command will perform in-place encryption of 64362306a36Sopenharmony_ci * the VMSA memory content (i.e it will write the same memory region 64462306a36Sopenharmony_ci * with the guest's key), so invalidate it first. 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ci clflush_cache_range(svm->sev_es.vmsa, PAGE_SIZE); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci vmsa.reserved = 0; 64962306a36Sopenharmony_ci vmsa.handle = to_kvm_svm(kvm)->sev_info.handle; 65062306a36Sopenharmony_ci vmsa.address = __sme_pa(svm->sev_es.vmsa); 65162306a36Sopenharmony_ci vmsa.len = PAGE_SIZE; 65262306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_VMSA, &vmsa, error); 65362306a36Sopenharmony_ci if (ret) 65462306a36Sopenharmony_ci return ret; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci vcpu->arch.guest_state_protected = true; 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int sev_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 66362306a36Sopenharmony_ci unsigned long i; 66462306a36Sopenharmony_ci int ret; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (!sev_es_guest(kvm)) 66762306a36Sopenharmony_ci return -ENOTTY; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci kvm_for_each_vcpu(i, vcpu, kvm) { 67062306a36Sopenharmony_ci ret = mutex_lock_killable(&vcpu->mutex); 67162306a36Sopenharmony_ci if (ret) 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = __sev_launch_update_vmsa(kvm, vcpu, &argp->error); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci mutex_unlock(&vcpu->mutex); 67762306a36Sopenharmony_ci if (ret) 67862306a36Sopenharmony_ci return ret; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci void __user *measure = (void __user *)(uintptr_t)argp->data; 68762306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 68862306a36Sopenharmony_ci struct sev_data_launch_measure data; 68962306a36Sopenharmony_ci struct kvm_sev_launch_measure params; 69062306a36Sopenharmony_ci void __user *p = NULL; 69162306a36Sopenharmony_ci void *blob = NULL; 69262306a36Sopenharmony_ci int ret; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (!sev_guest(kvm)) 69562306a36Sopenharmony_ci return -ENOTTY; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (copy_from_user(¶ms, measure, sizeof(params))) 69862306a36Sopenharmony_ci return -EFAULT; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* User wants to query the blob length */ 70362306a36Sopenharmony_ci if (!params.len) 70462306a36Sopenharmony_ci goto cmd; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci p = (void __user *)(uintptr_t)params.uaddr; 70762306a36Sopenharmony_ci if (p) { 70862306a36Sopenharmony_ci if (params.len > SEV_FW_BLOB_MAX_SIZE) 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci blob = kzalloc(params.len, GFP_KERNEL_ACCOUNT); 71262306a36Sopenharmony_ci if (!blob) 71362306a36Sopenharmony_ci return -ENOMEM; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci data.address = __psp_pa(blob); 71662306a36Sopenharmony_ci data.len = params.len; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cicmd: 72062306a36Sopenharmony_ci data.handle = sev->handle; 72162306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_MEASURE, &data, &argp->error); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * If we query the session length, FW responded with expected data. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci if (!params.len) 72762306a36Sopenharmony_ci goto done; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (ret) 73062306a36Sopenharmony_ci goto e_free_blob; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (blob) { 73362306a36Sopenharmony_ci if (copy_to_user(p, blob, params.len)) 73462306a36Sopenharmony_ci ret = -EFAULT; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cidone: 73862306a36Sopenharmony_ci params.len = data.len; 73962306a36Sopenharmony_ci if (copy_to_user(measure, ¶ms, sizeof(params))) 74062306a36Sopenharmony_ci ret = -EFAULT; 74162306a36Sopenharmony_cie_free_blob: 74262306a36Sopenharmony_ci kfree(blob); 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int sev_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 74962306a36Sopenharmony_ci struct sev_data_launch_finish data; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (!sev_guest(kvm)) 75262306a36Sopenharmony_ci return -ENOTTY; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci data.handle = sev->handle; 75562306a36Sopenharmony_ci return sev_issue_cmd(kvm, SEV_CMD_LAUNCH_FINISH, &data, &argp->error); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int sev_guest_status(struct kvm *kvm, struct kvm_sev_cmd *argp) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 76162306a36Sopenharmony_ci struct kvm_sev_guest_status params; 76262306a36Sopenharmony_ci struct sev_data_guest_status data; 76362306a36Sopenharmony_ci int ret; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (!sev_guest(kvm)) 76662306a36Sopenharmony_ci return -ENOTTY; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci data.handle = sev->handle; 77162306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_GUEST_STATUS, &data, &argp->error); 77262306a36Sopenharmony_ci if (ret) 77362306a36Sopenharmony_ci return ret; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci params.policy = data.policy; 77662306a36Sopenharmony_ci params.state = data.state; 77762306a36Sopenharmony_ci params.handle = data.handle; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params))) 78062306a36Sopenharmony_ci ret = -EFAULT; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci return ret; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic int __sev_issue_dbg_cmd(struct kvm *kvm, unsigned long src, 78662306a36Sopenharmony_ci unsigned long dst, int size, 78762306a36Sopenharmony_ci int *error, bool enc) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 79062306a36Sopenharmony_ci struct sev_data_dbg data; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci data.reserved = 0; 79362306a36Sopenharmony_ci data.handle = sev->handle; 79462306a36Sopenharmony_ci data.dst_addr = dst; 79562306a36Sopenharmony_ci data.src_addr = src; 79662306a36Sopenharmony_ci data.len = size; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return sev_issue_cmd(kvm, 79962306a36Sopenharmony_ci enc ? SEV_CMD_DBG_ENCRYPT : SEV_CMD_DBG_DECRYPT, 80062306a36Sopenharmony_ci &data, error); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int __sev_dbg_decrypt(struct kvm *kvm, unsigned long src_paddr, 80462306a36Sopenharmony_ci unsigned long dst_paddr, int sz, int *err) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci int offset; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* 80962306a36Sopenharmony_ci * Its safe to read more than we are asked, caller should ensure that 81062306a36Sopenharmony_ci * destination has enough space. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci offset = src_paddr & 15; 81362306a36Sopenharmony_ci src_paddr = round_down(src_paddr, 16); 81462306a36Sopenharmony_ci sz = round_up(sz + offset, 16); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return __sev_issue_dbg_cmd(kvm, src_paddr, dst_paddr, sz, err, false); 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int __sev_dbg_decrypt_user(struct kvm *kvm, unsigned long paddr, 82062306a36Sopenharmony_ci void __user *dst_uaddr, 82162306a36Sopenharmony_ci unsigned long dst_paddr, 82262306a36Sopenharmony_ci int size, int *err) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct page *tpage = NULL; 82562306a36Sopenharmony_ci int ret, offset; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* if inputs are not 16-byte then use intermediate buffer */ 82862306a36Sopenharmony_ci if (!IS_ALIGNED(dst_paddr, 16) || 82962306a36Sopenharmony_ci !IS_ALIGNED(paddr, 16) || 83062306a36Sopenharmony_ci !IS_ALIGNED(size, 16)) { 83162306a36Sopenharmony_ci tpage = (void *)alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); 83262306a36Sopenharmony_ci if (!tpage) 83362306a36Sopenharmony_ci return -ENOMEM; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci dst_paddr = __sme_page_pa(tpage); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci ret = __sev_dbg_decrypt(kvm, paddr, dst_paddr, size, err); 83962306a36Sopenharmony_ci if (ret) 84062306a36Sopenharmony_ci goto e_free; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (tpage) { 84362306a36Sopenharmony_ci offset = paddr & 15; 84462306a36Sopenharmony_ci if (copy_to_user(dst_uaddr, page_address(tpage) + offset, size)) 84562306a36Sopenharmony_ci ret = -EFAULT; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cie_free: 84962306a36Sopenharmony_ci if (tpage) 85062306a36Sopenharmony_ci __free_page(tpage); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return ret; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic int __sev_dbg_encrypt_user(struct kvm *kvm, unsigned long paddr, 85662306a36Sopenharmony_ci void __user *vaddr, 85762306a36Sopenharmony_ci unsigned long dst_paddr, 85862306a36Sopenharmony_ci void __user *dst_vaddr, 85962306a36Sopenharmony_ci int size, int *error) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct page *src_tpage = NULL; 86262306a36Sopenharmony_ci struct page *dst_tpage = NULL; 86362306a36Sopenharmony_ci int ret, len = size; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* If source buffer is not aligned then use an intermediate buffer */ 86662306a36Sopenharmony_ci if (!IS_ALIGNED((unsigned long)vaddr, 16)) { 86762306a36Sopenharmony_ci src_tpage = alloc_page(GFP_KERNEL_ACCOUNT); 86862306a36Sopenharmony_ci if (!src_tpage) 86962306a36Sopenharmony_ci return -ENOMEM; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (copy_from_user(page_address(src_tpage), vaddr, size)) { 87262306a36Sopenharmony_ci __free_page(src_tpage); 87362306a36Sopenharmony_ci return -EFAULT; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci paddr = __sme_page_pa(src_tpage); 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * If destination buffer or length is not aligned then do read-modify-write: 88162306a36Sopenharmony_ci * - decrypt destination in an intermediate buffer 88262306a36Sopenharmony_ci * - copy the source buffer in an intermediate buffer 88362306a36Sopenharmony_ci * - use the intermediate buffer as source buffer 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci if (!IS_ALIGNED((unsigned long)dst_vaddr, 16) || !IS_ALIGNED(size, 16)) { 88662306a36Sopenharmony_ci int dst_offset; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci dst_tpage = alloc_page(GFP_KERNEL_ACCOUNT); 88962306a36Sopenharmony_ci if (!dst_tpage) { 89062306a36Sopenharmony_ci ret = -ENOMEM; 89162306a36Sopenharmony_ci goto e_free; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ret = __sev_dbg_decrypt(kvm, dst_paddr, 89562306a36Sopenharmony_ci __sme_page_pa(dst_tpage), size, error); 89662306a36Sopenharmony_ci if (ret) 89762306a36Sopenharmony_ci goto e_free; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* 90062306a36Sopenharmony_ci * If source is kernel buffer then use memcpy() otherwise 90162306a36Sopenharmony_ci * copy_from_user(). 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_ci dst_offset = dst_paddr & 15; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (src_tpage) 90662306a36Sopenharmony_ci memcpy(page_address(dst_tpage) + dst_offset, 90762306a36Sopenharmony_ci page_address(src_tpage), size); 90862306a36Sopenharmony_ci else { 90962306a36Sopenharmony_ci if (copy_from_user(page_address(dst_tpage) + dst_offset, 91062306a36Sopenharmony_ci vaddr, size)) { 91162306a36Sopenharmony_ci ret = -EFAULT; 91262306a36Sopenharmony_ci goto e_free; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci paddr = __sme_page_pa(dst_tpage); 91762306a36Sopenharmony_ci dst_paddr = round_down(dst_paddr, 16); 91862306a36Sopenharmony_ci len = round_up(size, 16); 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ret = __sev_issue_dbg_cmd(kvm, paddr, dst_paddr, len, error, true); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cie_free: 92462306a36Sopenharmony_ci if (src_tpage) 92562306a36Sopenharmony_ci __free_page(src_tpage); 92662306a36Sopenharmony_ci if (dst_tpage) 92762306a36Sopenharmony_ci __free_page(dst_tpage); 92862306a36Sopenharmony_ci return ret; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci unsigned long vaddr, vaddr_end, next_vaddr; 93462306a36Sopenharmony_ci unsigned long dst_vaddr; 93562306a36Sopenharmony_ci struct page **src_p, **dst_p; 93662306a36Sopenharmony_ci struct kvm_sev_dbg debug; 93762306a36Sopenharmony_ci unsigned long n; 93862306a36Sopenharmony_ci unsigned int size; 93962306a36Sopenharmony_ci int ret; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (!sev_guest(kvm)) 94262306a36Sopenharmony_ci return -ENOTTY; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (copy_from_user(&debug, (void __user *)(uintptr_t)argp->data, sizeof(debug))) 94562306a36Sopenharmony_ci return -EFAULT; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (!debug.len || debug.src_uaddr + debug.len < debug.src_uaddr) 94862306a36Sopenharmony_ci return -EINVAL; 94962306a36Sopenharmony_ci if (!debug.dst_uaddr) 95062306a36Sopenharmony_ci return -EINVAL; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci vaddr = debug.src_uaddr; 95362306a36Sopenharmony_ci size = debug.len; 95462306a36Sopenharmony_ci vaddr_end = vaddr + size; 95562306a36Sopenharmony_ci dst_vaddr = debug.dst_uaddr; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci for (; vaddr < vaddr_end; vaddr = next_vaddr) { 95862306a36Sopenharmony_ci int len, s_off, d_off; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* lock userspace source and destination page */ 96162306a36Sopenharmony_ci src_p = sev_pin_memory(kvm, vaddr & PAGE_MASK, PAGE_SIZE, &n, 0); 96262306a36Sopenharmony_ci if (IS_ERR(src_p)) 96362306a36Sopenharmony_ci return PTR_ERR(src_p); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci dst_p = sev_pin_memory(kvm, dst_vaddr & PAGE_MASK, PAGE_SIZE, &n, 1); 96662306a36Sopenharmony_ci if (IS_ERR(dst_p)) { 96762306a36Sopenharmony_ci sev_unpin_memory(kvm, src_p, n); 96862306a36Sopenharmony_ci return PTR_ERR(dst_p); 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * Flush (on non-coherent CPUs) before DBG_{DE,EN}CRYPT read or modify 97362306a36Sopenharmony_ci * the pages; flush the destination too so that future accesses do not 97462306a36Sopenharmony_ci * see stale data. 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_ci sev_clflush_pages(src_p, 1); 97762306a36Sopenharmony_ci sev_clflush_pages(dst_p, 1); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* 98062306a36Sopenharmony_ci * Since user buffer may not be page aligned, calculate the 98162306a36Sopenharmony_ci * offset within the page. 98262306a36Sopenharmony_ci */ 98362306a36Sopenharmony_ci s_off = vaddr & ~PAGE_MASK; 98462306a36Sopenharmony_ci d_off = dst_vaddr & ~PAGE_MASK; 98562306a36Sopenharmony_ci len = min_t(size_t, (PAGE_SIZE - s_off), size); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (dec) 98862306a36Sopenharmony_ci ret = __sev_dbg_decrypt_user(kvm, 98962306a36Sopenharmony_ci __sme_page_pa(src_p[0]) + s_off, 99062306a36Sopenharmony_ci (void __user *)dst_vaddr, 99162306a36Sopenharmony_ci __sme_page_pa(dst_p[0]) + d_off, 99262306a36Sopenharmony_ci len, &argp->error); 99362306a36Sopenharmony_ci else 99462306a36Sopenharmony_ci ret = __sev_dbg_encrypt_user(kvm, 99562306a36Sopenharmony_ci __sme_page_pa(src_p[0]) + s_off, 99662306a36Sopenharmony_ci (void __user *)vaddr, 99762306a36Sopenharmony_ci __sme_page_pa(dst_p[0]) + d_off, 99862306a36Sopenharmony_ci (void __user *)dst_vaddr, 99962306a36Sopenharmony_ci len, &argp->error); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci sev_unpin_memory(kvm, src_p, n); 100262306a36Sopenharmony_ci sev_unpin_memory(kvm, dst_p, n); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (ret) 100562306a36Sopenharmony_ci goto err; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci next_vaddr = vaddr + len; 100862306a36Sopenharmony_ci dst_vaddr = dst_vaddr + len; 100962306a36Sopenharmony_ci size -= len; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_cierr: 101262306a36Sopenharmony_ci return ret; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 101862306a36Sopenharmony_ci struct sev_data_launch_secret data; 101962306a36Sopenharmony_ci struct kvm_sev_launch_secret params; 102062306a36Sopenharmony_ci struct page **pages; 102162306a36Sopenharmony_ci void *blob, *hdr; 102262306a36Sopenharmony_ci unsigned long n, i; 102362306a36Sopenharmony_ci int ret, offset; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (!sev_guest(kvm)) 102662306a36Sopenharmony_ci return -ENOTTY; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) 102962306a36Sopenharmony_ci return -EFAULT; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci pages = sev_pin_memory(kvm, params.guest_uaddr, params.guest_len, &n, 1); 103262306a36Sopenharmony_ci if (IS_ERR(pages)) 103362306a36Sopenharmony_ci return PTR_ERR(pages); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* 103662306a36Sopenharmony_ci * Flush (on non-coherent CPUs) before LAUNCH_SECRET encrypts pages in 103762306a36Sopenharmony_ci * place; the cache may contain the data that was written unencrypted. 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci sev_clflush_pages(pages, n); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* 104262306a36Sopenharmony_ci * The secret must be copied into contiguous memory region, lets verify 104362306a36Sopenharmony_ci * that userspace memory pages are contiguous before we issue command. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci if (get_num_contig_pages(0, pages, n) != n) { 104662306a36Sopenharmony_ci ret = -EINVAL; 104762306a36Sopenharmony_ci goto e_unpin_memory; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci offset = params.guest_uaddr & (PAGE_SIZE - 1); 105362306a36Sopenharmony_ci data.guest_address = __sme_page_pa(pages[0]) + offset; 105462306a36Sopenharmony_ci data.guest_len = params.guest_len; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci blob = psp_copy_user_blob(params.trans_uaddr, params.trans_len); 105762306a36Sopenharmony_ci if (IS_ERR(blob)) { 105862306a36Sopenharmony_ci ret = PTR_ERR(blob); 105962306a36Sopenharmony_ci goto e_unpin_memory; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci data.trans_address = __psp_pa(blob); 106362306a36Sopenharmony_ci data.trans_len = params.trans_len; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci hdr = psp_copy_user_blob(params.hdr_uaddr, params.hdr_len); 106662306a36Sopenharmony_ci if (IS_ERR(hdr)) { 106762306a36Sopenharmony_ci ret = PTR_ERR(hdr); 106862306a36Sopenharmony_ci goto e_free_blob; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci data.hdr_address = __psp_pa(hdr); 107162306a36Sopenharmony_ci data.hdr_len = params.hdr_len; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci data.handle = sev->handle; 107462306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_SECRET, &data, &argp->error); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci kfree(hdr); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cie_free_blob: 107962306a36Sopenharmony_ci kfree(blob); 108062306a36Sopenharmony_cie_unpin_memory: 108162306a36Sopenharmony_ci /* content of memory is updated, mark pages dirty */ 108262306a36Sopenharmony_ci for (i = 0; i < n; i++) { 108362306a36Sopenharmony_ci set_page_dirty_lock(pages[i]); 108462306a36Sopenharmony_ci mark_page_accessed(pages[i]); 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci sev_unpin_memory(kvm, pages, n); 108762306a36Sopenharmony_ci return ret; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic int sev_get_attestation_report(struct kvm *kvm, struct kvm_sev_cmd *argp) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci void __user *report = (void __user *)(uintptr_t)argp->data; 109362306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 109462306a36Sopenharmony_ci struct sev_data_attestation_report data; 109562306a36Sopenharmony_ci struct kvm_sev_attestation_report params; 109662306a36Sopenharmony_ci void __user *p; 109762306a36Sopenharmony_ci void *blob = NULL; 109862306a36Sopenharmony_ci int ret; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (!sev_guest(kvm)) 110162306a36Sopenharmony_ci return -ENOTTY; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) 110462306a36Sopenharmony_ci return -EFAULT; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci /* User wants to query the blob length */ 110962306a36Sopenharmony_ci if (!params.len) 111062306a36Sopenharmony_ci goto cmd; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci p = (void __user *)(uintptr_t)params.uaddr; 111362306a36Sopenharmony_ci if (p) { 111462306a36Sopenharmony_ci if (params.len > SEV_FW_BLOB_MAX_SIZE) 111562306a36Sopenharmony_ci return -EINVAL; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci blob = kzalloc(params.len, GFP_KERNEL_ACCOUNT); 111862306a36Sopenharmony_ci if (!blob) 111962306a36Sopenharmony_ci return -ENOMEM; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci data.address = __psp_pa(blob); 112262306a36Sopenharmony_ci data.len = params.len; 112362306a36Sopenharmony_ci memcpy(data.mnonce, params.mnonce, sizeof(params.mnonce)); 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_cicmd: 112662306a36Sopenharmony_ci data.handle = sev->handle; 112762306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_ATTESTATION_REPORT, &data, &argp->error); 112862306a36Sopenharmony_ci /* 112962306a36Sopenharmony_ci * If we query the session length, FW responded with expected data. 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_ci if (!params.len) 113262306a36Sopenharmony_ci goto done; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (ret) 113562306a36Sopenharmony_ci goto e_free_blob; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (blob) { 113862306a36Sopenharmony_ci if (copy_to_user(p, blob, params.len)) 113962306a36Sopenharmony_ci ret = -EFAULT; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cidone: 114362306a36Sopenharmony_ci params.len = data.len; 114462306a36Sopenharmony_ci if (copy_to_user(report, ¶ms, sizeof(params))) 114562306a36Sopenharmony_ci ret = -EFAULT; 114662306a36Sopenharmony_cie_free_blob: 114762306a36Sopenharmony_ci kfree(blob); 114862306a36Sopenharmony_ci return ret; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/* Userspace wants to query session length. */ 115262306a36Sopenharmony_cistatic int 115362306a36Sopenharmony_ci__sev_send_start_query_session_length(struct kvm *kvm, struct kvm_sev_cmd *argp, 115462306a36Sopenharmony_ci struct kvm_sev_send_start *params) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 115762306a36Sopenharmony_ci struct sev_data_send_start data; 115862306a36Sopenharmony_ci int ret; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 116162306a36Sopenharmony_ci data.handle = sev->handle; 116262306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, &data, &argp->error); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci params->session_len = data.session_len; 116562306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)argp->data, params, 116662306a36Sopenharmony_ci sizeof(struct kvm_sev_send_start))) 116762306a36Sopenharmony_ci ret = -EFAULT; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci return ret; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic int sev_send_start(struct kvm *kvm, struct kvm_sev_cmd *argp) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 117562306a36Sopenharmony_ci struct sev_data_send_start data; 117662306a36Sopenharmony_ci struct kvm_sev_send_start params; 117762306a36Sopenharmony_ci void *amd_certs, *session_data; 117862306a36Sopenharmony_ci void *pdh_cert, *plat_certs; 117962306a36Sopenharmony_ci int ret; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (!sev_guest(kvm)) 118262306a36Sopenharmony_ci return -ENOTTY; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, 118562306a36Sopenharmony_ci sizeof(struct kvm_sev_send_start))) 118662306a36Sopenharmony_ci return -EFAULT; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci /* if session_len is zero, userspace wants to query the session length */ 118962306a36Sopenharmony_ci if (!params.session_len) 119062306a36Sopenharmony_ci return __sev_send_start_query_session_length(kvm, argp, 119162306a36Sopenharmony_ci ¶ms); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* some sanity checks */ 119462306a36Sopenharmony_ci if (!params.pdh_cert_uaddr || !params.pdh_cert_len || 119562306a36Sopenharmony_ci !params.session_uaddr || params.session_len > SEV_FW_BLOB_MAX_SIZE) 119662306a36Sopenharmony_ci return -EINVAL; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* allocate the memory to hold the session data blob */ 119962306a36Sopenharmony_ci session_data = kzalloc(params.session_len, GFP_KERNEL_ACCOUNT); 120062306a36Sopenharmony_ci if (!session_data) 120162306a36Sopenharmony_ci return -ENOMEM; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci /* copy the certificate blobs from userspace */ 120462306a36Sopenharmony_ci pdh_cert = psp_copy_user_blob(params.pdh_cert_uaddr, 120562306a36Sopenharmony_ci params.pdh_cert_len); 120662306a36Sopenharmony_ci if (IS_ERR(pdh_cert)) { 120762306a36Sopenharmony_ci ret = PTR_ERR(pdh_cert); 120862306a36Sopenharmony_ci goto e_free_session; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci plat_certs = psp_copy_user_blob(params.plat_certs_uaddr, 121262306a36Sopenharmony_ci params.plat_certs_len); 121362306a36Sopenharmony_ci if (IS_ERR(plat_certs)) { 121462306a36Sopenharmony_ci ret = PTR_ERR(plat_certs); 121562306a36Sopenharmony_ci goto e_free_pdh; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci amd_certs = psp_copy_user_blob(params.amd_certs_uaddr, 121962306a36Sopenharmony_ci params.amd_certs_len); 122062306a36Sopenharmony_ci if (IS_ERR(amd_certs)) { 122162306a36Sopenharmony_ci ret = PTR_ERR(amd_certs); 122262306a36Sopenharmony_ci goto e_free_plat_cert; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* populate the FW SEND_START field with system physical address */ 122662306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 122762306a36Sopenharmony_ci data.pdh_cert_address = __psp_pa(pdh_cert); 122862306a36Sopenharmony_ci data.pdh_cert_len = params.pdh_cert_len; 122962306a36Sopenharmony_ci data.plat_certs_address = __psp_pa(plat_certs); 123062306a36Sopenharmony_ci data.plat_certs_len = params.plat_certs_len; 123162306a36Sopenharmony_ci data.amd_certs_address = __psp_pa(amd_certs); 123262306a36Sopenharmony_ci data.amd_certs_len = params.amd_certs_len; 123362306a36Sopenharmony_ci data.session_address = __psp_pa(session_data); 123462306a36Sopenharmony_ci data.session_len = params.session_len; 123562306a36Sopenharmony_ci data.handle = sev->handle; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, &data, &argp->error); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (!ret && copy_to_user((void __user *)(uintptr_t)params.session_uaddr, 124062306a36Sopenharmony_ci session_data, params.session_len)) { 124162306a36Sopenharmony_ci ret = -EFAULT; 124262306a36Sopenharmony_ci goto e_free_amd_cert; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci params.policy = data.policy; 124662306a36Sopenharmony_ci params.session_len = data.session_len; 124762306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, 124862306a36Sopenharmony_ci sizeof(struct kvm_sev_send_start))) 124962306a36Sopenharmony_ci ret = -EFAULT; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cie_free_amd_cert: 125262306a36Sopenharmony_ci kfree(amd_certs); 125362306a36Sopenharmony_cie_free_plat_cert: 125462306a36Sopenharmony_ci kfree(plat_certs); 125562306a36Sopenharmony_cie_free_pdh: 125662306a36Sopenharmony_ci kfree(pdh_cert); 125762306a36Sopenharmony_cie_free_session: 125862306a36Sopenharmony_ci kfree(session_data); 125962306a36Sopenharmony_ci return ret; 126062306a36Sopenharmony_ci} 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci/* Userspace wants to query either header or trans length. */ 126362306a36Sopenharmony_cistatic int 126462306a36Sopenharmony_ci__sev_send_update_data_query_lengths(struct kvm *kvm, struct kvm_sev_cmd *argp, 126562306a36Sopenharmony_ci struct kvm_sev_send_update_data *params) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 126862306a36Sopenharmony_ci struct sev_data_send_update_data data; 126962306a36Sopenharmony_ci int ret; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 127262306a36Sopenharmony_ci data.handle = sev->handle; 127362306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_SEND_UPDATE_DATA, &data, &argp->error); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci params->hdr_len = data.hdr_len; 127662306a36Sopenharmony_ci params->trans_len = data.trans_len; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)argp->data, params, 127962306a36Sopenharmony_ci sizeof(struct kvm_sev_send_update_data))) 128062306a36Sopenharmony_ci ret = -EFAULT; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci return ret; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_cistatic int sev_send_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 128862306a36Sopenharmony_ci struct sev_data_send_update_data data; 128962306a36Sopenharmony_ci struct kvm_sev_send_update_data params; 129062306a36Sopenharmony_ci void *hdr, *trans_data; 129162306a36Sopenharmony_ci struct page **guest_page; 129262306a36Sopenharmony_ci unsigned long n; 129362306a36Sopenharmony_ci int ret, offset; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (!sev_guest(kvm)) 129662306a36Sopenharmony_ci return -ENOTTY; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, 129962306a36Sopenharmony_ci sizeof(struct kvm_sev_send_update_data))) 130062306a36Sopenharmony_ci return -EFAULT; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* userspace wants to query either header or trans length */ 130362306a36Sopenharmony_ci if (!params.trans_len || !params.hdr_len) 130462306a36Sopenharmony_ci return __sev_send_update_data_query_lengths(kvm, argp, ¶ms); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (!params.trans_uaddr || !params.guest_uaddr || 130762306a36Sopenharmony_ci !params.guest_len || !params.hdr_uaddr) 130862306a36Sopenharmony_ci return -EINVAL; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* Check if we are crossing the page boundary */ 131162306a36Sopenharmony_ci offset = params.guest_uaddr & (PAGE_SIZE - 1); 131262306a36Sopenharmony_ci if (params.guest_len > PAGE_SIZE || (params.guest_len + offset) > PAGE_SIZE) 131362306a36Sopenharmony_ci return -EINVAL; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci /* Pin guest memory */ 131662306a36Sopenharmony_ci guest_page = sev_pin_memory(kvm, params.guest_uaddr & PAGE_MASK, 131762306a36Sopenharmony_ci PAGE_SIZE, &n, 0); 131862306a36Sopenharmony_ci if (IS_ERR(guest_page)) 131962306a36Sopenharmony_ci return PTR_ERR(guest_page); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* allocate memory for header and transport buffer */ 132262306a36Sopenharmony_ci ret = -ENOMEM; 132362306a36Sopenharmony_ci hdr = kzalloc(params.hdr_len, GFP_KERNEL_ACCOUNT); 132462306a36Sopenharmony_ci if (!hdr) 132562306a36Sopenharmony_ci goto e_unpin; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci trans_data = kzalloc(params.trans_len, GFP_KERNEL_ACCOUNT); 132862306a36Sopenharmony_ci if (!trans_data) 132962306a36Sopenharmony_ci goto e_free_hdr; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 133262306a36Sopenharmony_ci data.hdr_address = __psp_pa(hdr); 133362306a36Sopenharmony_ci data.hdr_len = params.hdr_len; 133462306a36Sopenharmony_ci data.trans_address = __psp_pa(trans_data); 133562306a36Sopenharmony_ci data.trans_len = params.trans_len; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* The SEND_UPDATE_DATA command requires C-bit to be always set. */ 133862306a36Sopenharmony_ci data.guest_address = (page_to_pfn(guest_page[0]) << PAGE_SHIFT) + offset; 133962306a36Sopenharmony_ci data.guest_address |= sev_me_mask; 134062306a36Sopenharmony_ci data.guest_len = params.guest_len; 134162306a36Sopenharmony_ci data.handle = sev->handle; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_SEND_UPDATE_DATA, &data, &argp->error); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci if (ret) 134662306a36Sopenharmony_ci goto e_free_trans_data; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* copy transport buffer to user space */ 134962306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)params.trans_uaddr, 135062306a36Sopenharmony_ci trans_data, params.trans_len)) { 135162306a36Sopenharmony_ci ret = -EFAULT; 135262306a36Sopenharmony_ci goto e_free_trans_data; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* Copy packet header to userspace. */ 135662306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)params.hdr_uaddr, hdr, 135762306a36Sopenharmony_ci params.hdr_len)) 135862306a36Sopenharmony_ci ret = -EFAULT; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cie_free_trans_data: 136162306a36Sopenharmony_ci kfree(trans_data); 136262306a36Sopenharmony_cie_free_hdr: 136362306a36Sopenharmony_ci kfree(hdr); 136462306a36Sopenharmony_cie_unpin: 136562306a36Sopenharmony_ci sev_unpin_memory(kvm, guest_page, n); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci return ret; 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 137362306a36Sopenharmony_ci struct sev_data_send_finish data; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (!sev_guest(kvm)) 137662306a36Sopenharmony_ci return -ENOTTY; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci data.handle = sev->handle; 137962306a36Sopenharmony_ci return sev_issue_cmd(kvm, SEV_CMD_SEND_FINISH, &data, &argp->error); 138062306a36Sopenharmony_ci} 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cistatic int sev_send_cancel(struct kvm *kvm, struct kvm_sev_cmd *argp) 138362306a36Sopenharmony_ci{ 138462306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 138562306a36Sopenharmony_ci struct sev_data_send_cancel data; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci if (!sev_guest(kvm)) 138862306a36Sopenharmony_ci return -ENOTTY; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci data.handle = sev->handle; 139162306a36Sopenharmony_ci return sev_issue_cmd(kvm, SEV_CMD_SEND_CANCEL, &data, &argp->error); 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic int sev_receive_start(struct kvm *kvm, struct kvm_sev_cmd *argp) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 139762306a36Sopenharmony_ci struct sev_data_receive_start start; 139862306a36Sopenharmony_ci struct kvm_sev_receive_start params; 139962306a36Sopenharmony_ci int *error = &argp->error; 140062306a36Sopenharmony_ci void *session_data; 140162306a36Sopenharmony_ci void *pdh_data; 140262306a36Sopenharmony_ci int ret; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (!sev_guest(kvm)) 140562306a36Sopenharmony_ci return -ENOTTY; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci /* Get parameter from the userspace */ 140862306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, 140962306a36Sopenharmony_ci sizeof(struct kvm_sev_receive_start))) 141062306a36Sopenharmony_ci return -EFAULT; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci /* some sanity checks */ 141362306a36Sopenharmony_ci if (!params.pdh_uaddr || !params.pdh_len || 141462306a36Sopenharmony_ci !params.session_uaddr || !params.session_len) 141562306a36Sopenharmony_ci return -EINVAL; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci pdh_data = psp_copy_user_blob(params.pdh_uaddr, params.pdh_len); 141862306a36Sopenharmony_ci if (IS_ERR(pdh_data)) 141962306a36Sopenharmony_ci return PTR_ERR(pdh_data); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci session_data = psp_copy_user_blob(params.session_uaddr, 142262306a36Sopenharmony_ci params.session_len); 142362306a36Sopenharmony_ci if (IS_ERR(session_data)) { 142462306a36Sopenharmony_ci ret = PTR_ERR(session_data); 142562306a36Sopenharmony_ci goto e_free_pdh; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci memset(&start, 0, sizeof(start)); 142962306a36Sopenharmony_ci start.handle = params.handle; 143062306a36Sopenharmony_ci start.policy = params.policy; 143162306a36Sopenharmony_ci start.pdh_cert_address = __psp_pa(pdh_data); 143262306a36Sopenharmony_ci start.pdh_cert_len = params.pdh_len; 143362306a36Sopenharmony_ci start.session_address = __psp_pa(session_data); 143462306a36Sopenharmony_ci start.session_len = params.session_len; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci /* create memory encryption context */ 143762306a36Sopenharmony_ci ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_RECEIVE_START, &start, 143862306a36Sopenharmony_ci error); 143962306a36Sopenharmony_ci if (ret) 144062306a36Sopenharmony_ci goto e_free_session; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* Bind ASID to this guest */ 144362306a36Sopenharmony_ci ret = sev_bind_asid(kvm, start.handle, error); 144462306a36Sopenharmony_ci if (ret) { 144562306a36Sopenharmony_ci sev_decommission(start.handle); 144662306a36Sopenharmony_ci goto e_free_session; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci params.handle = start.handle; 145062306a36Sopenharmony_ci if (copy_to_user((void __user *)(uintptr_t)argp->data, 145162306a36Sopenharmony_ci ¶ms, sizeof(struct kvm_sev_receive_start))) { 145262306a36Sopenharmony_ci ret = -EFAULT; 145362306a36Sopenharmony_ci sev_unbind_asid(kvm, start.handle); 145462306a36Sopenharmony_ci goto e_free_session; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci sev->handle = start.handle; 145862306a36Sopenharmony_ci sev->fd = argp->sev_fd; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cie_free_session: 146162306a36Sopenharmony_ci kfree(session_data); 146262306a36Sopenharmony_cie_free_pdh: 146362306a36Sopenharmony_ci kfree(pdh_data); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci return ret; 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_cistatic int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) 146962306a36Sopenharmony_ci{ 147062306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 147162306a36Sopenharmony_ci struct kvm_sev_receive_update_data params; 147262306a36Sopenharmony_ci struct sev_data_receive_update_data data; 147362306a36Sopenharmony_ci void *hdr = NULL, *trans = NULL; 147462306a36Sopenharmony_ci struct page **guest_page; 147562306a36Sopenharmony_ci unsigned long n; 147662306a36Sopenharmony_ci int ret, offset; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci if (!sev_guest(kvm)) 147962306a36Sopenharmony_ci return -EINVAL; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, 148262306a36Sopenharmony_ci sizeof(struct kvm_sev_receive_update_data))) 148362306a36Sopenharmony_ci return -EFAULT; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (!params.hdr_uaddr || !params.hdr_len || 148662306a36Sopenharmony_ci !params.guest_uaddr || !params.guest_len || 148762306a36Sopenharmony_ci !params.trans_uaddr || !params.trans_len) 148862306a36Sopenharmony_ci return -EINVAL; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* Check if we are crossing the page boundary */ 149162306a36Sopenharmony_ci offset = params.guest_uaddr & (PAGE_SIZE - 1); 149262306a36Sopenharmony_ci if (params.guest_len > PAGE_SIZE || (params.guest_len + offset) > PAGE_SIZE) 149362306a36Sopenharmony_ci return -EINVAL; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci hdr = psp_copy_user_blob(params.hdr_uaddr, params.hdr_len); 149662306a36Sopenharmony_ci if (IS_ERR(hdr)) 149762306a36Sopenharmony_ci return PTR_ERR(hdr); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci trans = psp_copy_user_blob(params.trans_uaddr, params.trans_len); 150062306a36Sopenharmony_ci if (IS_ERR(trans)) { 150162306a36Sopenharmony_ci ret = PTR_ERR(trans); 150262306a36Sopenharmony_ci goto e_free_hdr; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 150662306a36Sopenharmony_ci data.hdr_address = __psp_pa(hdr); 150762306a36Sopenharmony_ci data.hdr_len = params.hdr_len; 150862306a36Sopenharmony_ci data.trans_address = __psp_pa(trans); 150962306a36Sopenharmony_ci data.trans_len = params.trans_len; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* Pin guest memory */ 151262306a36Sopenharmony_ci guest_page = sev_pin_memory(kvm, params.guest_uaddr & PAGE_MASK, 151362306a36Sopenharmony_ci PAGE_SIZE, &n, 1); 151462306a36Sopenharmony_ci if (IS_ERR(guest_page)) { 151562306a36Sopenharmony_ci ret = PTR_ERR(guest_page); 151662306a36Sopenharmony_ci goto e_free_trans; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* 152062306a36Sopenharmony_ci * Flush (on non-coherent CPUs) before RECEIVE_UPDATE_DATA, the PSP 152162306a36Sopenharmony_ci * encrypts the written data with the guest's key, and the cache may 152262306a36Sopenharmony_ci * contain dirty, unencrypted data. 152362306a36Sopenharmony_ci */ 152462306a36Sopenharmony_ci sev_clflush_pages(guest_page, n); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci /* The RECEIVE_UPDATE_DATA command requires C-bit to be always set. */ 152762306a36Sopenharmony_ci data.guest_address = (page_to_pfn(guest_page[0]) << PAGE_SHIFT) + offset; 152862306a36Sopenharmony_ci data.guest_address |= sev_me_mask; 152962306a36Sopenharmony_ci data.guest_len = params.guest_len; 153062306a36Sopenharmony_ci data.handle = sev->handle; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci ret = sev_issue_cmd(kvm, SEV_CMD_RECEIVE_UPDATE_DATA, &data, 153362306a36Sopenharmony_ci &argp->error); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci sev_unpin_memory(kvm, guest_page, n); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_cie_free_trans: 153862306a36Sopenharmony_ci kfree(trans); 153962306a36Sopenharmony_cie_free_hdr: 154062306a36Sopenharmony_ci kfree(hdr); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci return ret; 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 154862306a36Sopenharmony_ci struct sev_data_receive_finish data; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if (!sev_guest(kvm)) 155162306a36Sopenharmony_ci return -ENOTTY; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci data.handle = sev->handle; 155462306a36Sopenharmony_ci return sev_issue_cmd(kvm, SEV_CMD_RECEIVE_FINISH, &data, &argp->error); 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic bool is_cmd_allowed_from_mirror(u32 cmd_id) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci /* 156062306a36Sopenharmony_ci * Allow mirrors VM to call KVM_SEV_LAUNCH_UPDATE_VMSA to enable SEV-ES 156162306a36Sopenharmony_ci * active mirror VMs. Also allow the debugging and status commands. 156262306a36Sopenharmony_ci */ 156362306a36Sopenharmony_ci if (cmd_id == KVM_SEV_LAUNCH_UPDATE_VMSA || 156462306a36Sopenharmony_ci cmd_id == KVM_SEV_GUEST_STATUS || cmd_id == KVM_SEV_DBG_DECRYPT || 156562306a36Sopenharmony_ci cmd_id == KVM_SEV_DBG_ENCRYPT) 156662306a36Sopenharmony_ci return true; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci return false; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic int sev_lock_two_vms(struct kvm *dst_kvm, struct kvm *src_kvm) 157262306a36Sopenharmony_ci{ 157362306a36Sopenharmony_ci struct kvm_sev_info *dst_sev = &to_kvm_svm(dst_kvm)->sev_info; 157462306a36Sopenharmony_ci struct kvm_sev_info *src_sev = &to_kvm_svm(src_kvm)->sev_info; 157562306a36Sopenharmony_ci int r = -EBUSY; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci if (dst_kvm == src_kvm) 157862306a36Sopenharmony_ci return -EINVAL; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci /* 158162306a36Sopenharmony_ci * Bail if these VMs are already involved in a migration to avoid 158262306a36Sopenharmony_ci * deadlock between two VMs trying to migrate to/from each other. 158362306a36Sopenharmony_ci */ 158462306a36Sopenharmony_ci if (atomic_cmpxchg_acquire(&dst_sev->migration_in_progress, 0, 1)) 158562306a36Sopenharmony_ci return -EBUSY; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (atomic_cmpxchg_acquire(&src_sev->migration_in_progress, 0, 1)) 158862306a36Sopenharmony_ci goto release_dst; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci r = -EINTR; 159162306a36Sopenharmony_ci if (mutex_lock_killable(&dst_kvm->lock)) 159262306a36Sopenharmony_ci goto release_src; 159362306a36Sopenharmony_ci if (mutex_lock_killable_nested(&src_kvm->lock, SINGLE_DEPTH_NESTING)) 159462306a36Sopenharmony_ci goto unlock_dst; 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ciunlock_dst: 159862306a36Sopenharmony_ci mutex_unlock(&dst_kvm->lock); 159962306a36Sopenharmony_cirelease_src: 160062306a36Sopenharmony_ci atomic_set_release(&src_sev->migration_in_progress, 0); 160162306a36Sopenharmony_cirelease_dst: 160262306a36Sopenharmony_ci atomic_set_release(&dst_sev->migration_in_progress, 0); 160362306a36Sopenharmony_ci return r; 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic void sev_unlock_two_vms(struct kvm *dst_kvm, struct kvm *src_kvm) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct kvm_sev_info *dst_sev = &to_kvm_svm(dst_kvm)->sev_info; 160962306a36Sopenharmony_ci struct kvm_sev_info *src_sev = &to_kvm_svm(src_kvm)->sev_info; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci mutex_unlock(&dst_kvm->lock); 161262306a36Sopenharmony_ci mutex_unlock(&src_kvm->lock); 161362306a36Sopenharmony_ci atomic_set_release(&dst_sev->migration_in_progress, 0); 161462306a36Sopenharmony_ci atomic_set_release(&src_sev->migration_in_progress, 0); 161562306a36Sopenharmony_ci} 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci/* vCPU mutex subclasses. */ 161862306a36Sopenharmony_cienum sev_migration_role { 161962306a36Sopenharmony_ci SEV_MIGRATION_SOURCE = 0, 162062306a36Sopenharmony_ci SEV_MIGRATION_TARGET, 162162306a36Sopenharmony_ci SEV_NR_MIGRATION_ROLES, 162262306a36Sopenharmony_ci}; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cistatic int sev_lock_vcpus_for_migration(struct kvm *kvm, 162562306a36Sopenharmony_ci enum sev_migration_role role) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 162862306a36Sopenharmony_ci unsigned long i, j; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci kvm_for_each_vcpu(i, vcpu, kvm) { 163162306a36Sopenharmony_ci if (mutex_lock_killable_nested(&vcpu->mutex, role)) 163262306a36Sopenharmony_ci goto out_unlock; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci#ifdef CONFIG_PROVE_LOCKING 163562306a36Sopenharmony_ci if (!i) 163662306a36Sopenharmony_ci /* 163762306a36Sopenharmony_ci * Reset the role to one that avoids colliding with 163862306a36Sopenharmony_ci * the role used for the first vcpu mutex. 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci role = SEV_NR_MIGRATION_ROLES; 164162306a36Sopenharmony_ci else 164262306a36Sopenharmony_ci mutex_release(&vcpu->mutex.dep_map, _THIS_IP_); 164362306a36Sopenharmony_ci#endif 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci return 0; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ciout_unlock: 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci kvm_for_each_vcpu(j, vcpu, kvm) { 165162306a36Sopenharmony_ci if (i == j) 165262306a36Sopenharmony_ci break; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci#ifdef CONFIG_PROVE_LOCKING 165562306a36Sopenharmony_ci if (j) 165662306a36Sopenharmony_ci mutex_acquire(&vcpu->mutex.dep_map, role, 0, _THIS_IP_); 165762306a36Sopenharmony_ci#endif 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci mutex_unlock(&vcpu->mutex); 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci return -EINTR; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic void sev_unlock_vcpus_for_migration(struct kvm *kvm) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 166762306a36Sopenharmony_ci unsigned long i; 166862306a36Sopenharmony_ci bool first = true; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci kvm_for_each_vcpu(i, vcpu, kvm) { 167162306a36Sopenharmony_ci if (first) 167262306a36Sopenharmony_ci first = false; 167362306a36Sopenharmony_ci else 167462306a36Sopenharmony_ci mutex_acquire(&vcpu->mutex.dep_map, 167562306a36Sopenharmony_ci SEV_NR_MIGRATION_ROLES, 0, _THIS_IP_); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci mutex_unlock(&vcpu->mutex); 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci struct kvm_sev_info *dst = &to_kvm_svm(dst_kvm)->sev_info; 168462306a36Sopenharmony_ci struct kvm_sev_info *src = &to_kvm_svm(src_kvm)->sev_info; 168562306a36Sopenharmony_ci struct kvm_vcpu *dst_vcpu, *src_vcpu; 168662306a36Sopenharmony_ci struct vcpu_svm *dst_svm, *src_svm; 168762306a36Sopenharmony_ci struct kvm_sev_info *mirror; 168862306a36Sopenharmony_ci unsigned long i; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci dst->active = true; 169162306a36Sopenharmony_ci dst->asid = src->asid; 169262306a36Sopenharmony_ci dst->handle = src->handle; 169362306a36Sopenharmony_ci dst->pages_locked = src->pages_locked; 169462306a36Sopenharmony_ci dst->enc_context_owner = src->enc_context_owner; 169562306a36Sopenharmony_ci dst->es_active = src->es_active; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci src->asid = 0; 169862306a36Sopenharmony_ci src->active = false; 169962306a36Sopenharmony_ci src->handle = 0; 170062306a36Sopenharmony_ci src->pages_locked = 0; 170162306a36Sopenharmony_ci src->enc_context_owner = NULL; 170262306a36Sopenharmony_ci src->es_active = false; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list); 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci /* 170762306a36Sopenharmony_ci * If this VM has mirrors, "transfer" each mirror's refcount of the 170862306a36Sopenharmony_ci * source to the destination (this KVM). The caller holds a reference 170962306a36Sopenharmony_ci * to the source, so there's no danger of use-after-free. 171062306a36Sopenharmony_ci */ 171162306a36Sopenharmony_ci list_cut_before(&dst->mirror_vms, &src->mirror_vms, &src->mirror_vms); 171262306a36Sopenharmony_ci list_for_each_entry(mirror, &dst->mirror_vms, mirror_entry) { 171362306a36Sopenharmony_ci kvm_get_kvm(dst_kvm); 171462306a36Sopenharmony_ci kvm_put_kvm(src_kvm); 171562306a36Sopenharmony_ci mirror->enc_context_owner = dst_kvm; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci /* 171962306a36Sopenharmony_ci * If this VM is a mirror, remove the old mirror from the owners list 172062306a36Sopenharmony_ci * and add the new mirror to the list. 172162306a36Sopenharmony_ci */ 172262306a36Sopenharmony_ci if (is_mirroring_enc_context(dst_kvm)) { 172362306a36Sopenharmony_ci struct kvm_sev_info *owner_sev_info = 172462306a36Sopenharmony_ci &to_kvm_svm(dst->enc_context_owner)->sev_info; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci list_del(&src->mirror_entry); 172762306a36Sopenharmony_ci list_add_tail(&dst->mirror_entry, &owner_sev_info->mirror_vms); 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci kvm_for_each_vcpu(i, dst_vcpu, dst_kvm) { 173162306a36Sopenharmony_ci dst_svm = to_svm(dst_vcpu); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci sev_init_vmcb(dst_svm); 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (!dst->es_active) 173662306a36Sopenharmony_ci continue; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci /* 173962306a36Sopenharmony_ci * Note, the source is not required to have the same number of 174062306a36Sopenharmony_ci * vCPUs as the destination when migrating a vanilla SEV VM. 174162306a36Sopenharmony_ci */ 174262306a36Sopenharmony_ci src_vcpu = kvm_get_vcpu(src_kvm, i); 174362306a36Sopenharmony_ci src_svm = to_svm(src_vcpu); 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci /* 174662306a36Sopenharmony_ci * Transfer VMSA and GHCB state to the destination. Nullify and 174762306a36Sopenharmony_ci * clear source fields as appropriate, the state now belongs to 174862306a36Sopenharmony_ci * the destination. 174962306a36Sopenharmony_ci */ 175062306a36Sopenharmony_ci memcpy(&dst_svm->sev_es, &src_svm->sev_es, sizeof(src_svm->sev_es)); 175162306a36Sopenharmony_ci dst_svm->vmcb->control.ghcb_gpa = src_svm->vmcb->control.ghcb_gpa; 175262306a36Sopenharmony_ci dst_svm->vmcb->control.vmsa_pa = src_svm->vmcb->control.vmsa_pa; 175362306a36Sopenharmony_ci dst_vcpu->arch.guest_state_protected = true; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci memset(&src_svm->sev_es, 0, sizeof(src_svm->sev_es)); 175662306a36Sopenharmony_ci src_svm->vmcb->control.ghcb_gpa = INVALID_PAGE; 175762306a36Sopenharmony_ci src_svm->vmcb->control.vmsa_pa = INVALID_PAGE; 175862306a36Sopenharmony_ci src_vcpu->arch.guest_state_protected = false; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic int sev_check_source_vcpus(struct kvm *dst, struct kvm *src) 176362306a36Sopenharmony_ci{ 176462306a36Sopenharmony_ci struct kvm_vcpu *src_vcpu; 176562306a36Sopenharmony_ci unsigned long i; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (!sev_es_guest(src)) 176862306a36Sopenharmony_ci return 0; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (atomic_read(&src->online_vcpus) != atomic_read(&dst->online_vcpus)) 177162306a36Sopenharmony_ci return -EINVAL; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci kvm_for_each_vcpu(i, src_vcpu, src) { 177462306a36Sopenharmony_ci if (!src_vcpu->arch.guest_state_protected) 177562306a36Sopenharmony_ci return -EINVAL; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci return 0; 177962306a36Sopenharmony_ci} 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ciint sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) 178262306a36Sopenharmony_ci{ 178362306a36Sopenharmony_ci struct kvm_sev_info *dst_sev = &to_kvm_svm(kvm)->sev_info; 178462306a36Sopenharmony_ci struct kvm_sev_info *src_sev, *cg_cleanup_sev; 178562306a36Sopenharmony_ci struct fd f = fdget(source_fd); 178662306a36Sopenharmony_ci struct kvm *source_kvm; 178762306a36Sopenharmony_ci bool charged = false; 178862306a36Sopenharmony_ci int ret; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (!f.file) 179162306a36Sopenharmony_ci return -EBADF; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci if (!file_is_kvm(f.file)) { 179462306a36Sopenharmony_ci ret = -EBADF; 179562306a36Sopenharmony_ci goto out_fput; 179662306a36Sopenharmony_ci } 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci source_kvm = f.file->private_data; 179962306a36Sopenharmony_ci ret = sev_lock_two_vms(kvm, source_kvm); 180062306a36Sopenharmony_ci if (ret) 180162306a36Sopenharmony_ci goto out_fput; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (sev_guest(kvm) || !sev_guest(source_kvm)) { 180462306a36Sopenharmony_ci ret = -EINVAL; 180562306a36Sopenharmony_ci goto out_unlock; 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci src_sev = &to_kvm_svm(source_kvm)->sev_info; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci dst_sev->misc_cg = get_current_misc_cg(); 181162306a36Sopenharmony_ci cg_cleanup_sev = dst_sev; 181262306a36Sopenharmony_ci if (dst_sev->misc_cg != src_sev->misc_cg) { 181362306a36Sopenharmony_ci ret = sev_misc_cg_try_charge(dst_sev); 181462306a36Sopenharmony_ci if (ret) 181562306a36Sopenharmony_ci goto out_dst_cgroup; 181662306a36Sopenharmony_ci charged = true; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci ret = sev_lock_vcpus_for_migration(kvm, SEV_MIGRATION_SOURCE); 182062306a36Sopenharmony_ci if (ret) 182162306a36Sopenharmony_ci goto out_dst_cgroup; 182262306a36Sopenharmony_ci ret = sev_lock_vcpus_for_migration(source_kvm, SEV_MIGRATION_TARGET); 182362306a36Sopenharmony_ci if (ret) 182462306a36Sopenharmony_ci goto out_dst_vcpu; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci ret = sev_check_source_vcpus(kvm, source_kvm); 182762306a36Sopenharmony_ci if (ret) 182862306a36Sopenharmony_ci goto out_source_vcpu; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci sev_migrate_from(kvm, source_kvm); 183162306a36Sopenharmony_ci kvm_vm_dead(source_kvm); 183262306a36Sopenharmony_ci cg_cleanup_sev = src_sev; 183362306a36Sopenharmony_ci ret = 0; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ciout_source_vcpu: 183662306a36Sopenharmony_ci sev_unlock_vcpus_for_migration(source_kvm); 183762306a36Sopenharmony_ciout_dst_vcpu: 183862306a36Sopenharmony_ci sev_unlock_vcpus_for_migration(kvm); 183962306a36Sopenharmony_ciout_dst_cgroup: 184062306a36Sopenharmony_ci /* Operates on the source on success, on the destination on failure. */ 184162306a36Sopenharmony_ci if (charged) 184262306a36Sopenharmony_ci sev_misc_cg_uncharge(cg_cleanup_sev); 184362306a36Sopenharmony_ci put_misc_cg(cg_cleanup_sev->misc_cg); 184462306a36Sopenharmony_ci cg_cleanup_sev->misc_cg = NULL; 184562306a36Sopenharmony_ciout_unlock: 184662306a36Sopenharmony_ci sev_unlock_two_vms(kvm, source_kvm); 184762306a36Sopenharmony_ciout_fput: 184862306a36Sopenharmony_ci fdput(f); 184962306a36Sopenharmony_ci return ret; 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ciint sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci struct kvm_sev_cmd sev_cmd; 185562306a36Sopenharmony_ci int r; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci if (!sev_enabled) 185862306a36Sopenharmony_ci return -ENOTTY; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci if (!argp) 186162306a36Sopenharmony_ci return 0; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci if (copy_from_user(&sev_cmd, argp, sizeof(struct kvm_sev_cmd))) 186462306a36Sopenharmony_ci return -EFAULT; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci mutex_lock(&kvm->lock); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci /* Only the enc_context_owner handles some memory enc operations. */ 186962306a36Sopenharmony_ci if (is_mirroring_enc_context(kvm) && 187062306a36Sopenharmony_ci !is_cmd_allowed_from_mirror(sev_cmd.id)) { 187162306a36Sopenharmony_ci r = -EINVAL; 187262306a36Sopenharmony_ci goto out; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci switch (sev_cmd.id) { 187662306a36Sopenharmony_ci case KVM_SEV_ES_INIT: 187762306a36Sopenharmony_ci if (!sev_es_enabled) { 187862306a36Sopenharmony_ci r = -ENOTTY; 187962306a36Sopenharmony_ci goto out; 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci fallthrough; 188262306a36Sopenharmony_ci case KVM_SEV_INIT: 188362306a36Sopenharmony_ci r = sev_guest_init(kvm, &sev_cmd); 188462306a36Sopenharmony_ci break; 188562306a36Sopenharmony_ci case KVM_SEV_LAUNCH_START: 188662306a36Sopenharmony_ci r = sev_launch_start(kvm, &sev_cmd); 188762306a36Sopenharmony_ci break; 188862306a36Sopenharmony_ci case KVM_SEV_LAUNCH_UPDATE_DATA: 188962306a36Sopenharmony_ci r = sev_launch_update_data(kvm, &sev_cmd); 189062306a36Sopenharmony_ci break; 189162306a36Sopenharmony_ci case KVM_SEV_LAUNCH_UPDATE_VMSA: 189262306a36Sopenharmony_ci r = sev_launch_update_vmsa(kvm, &sev_cmd); 189362306a36Sopenharmony_ci break; 189462306a36Sopenharmony_ci case KVM_SEV_LAUNCH_MEASURE: 189562306a36Sopenharmony_ci r = sev_launch_measure(kvm, &sev_cmd); 189662306a36Sopenharmony_ci break; 189762306a36Sopenharmony_ci case KVM_SEV_LAUNCH_FINISH: 189862306a36Sopenharmony_ci r = sev_launch_finish(kvm, &sev_cmd); 189962306a36Sopenharmony_ci break; 190062306a36Sopenharmony_ci case KVM_SEV_GUEST_STATUS: 190162306a36Sopenharmony_ci r = sev_guest_status(kvm, &sev_cmd); 190262306a36Sopenharmony_ci break; 190362306a36Sopenharmony_ci case KVM_SEV_DBG_DECRYPT: 190462306a36Sopenharmony_ci r = sev_dbg_crypt(kvm, &sev_cmd, true); 190562306a36Sopenharmony_ci break; 190662306a36Sopenharmony_ci case KVM_SEV_DBG_ENCRYPT: 190762306a36Sopenharmony_ci r = sev_dbg_crypt(kvm, &sev_cmd, false); 190862306a36Sopenharmony_ci break; 190962306a36Sopenharmony_ci case KVM_SEV_LAUNCH_SECRET: 191062306a36Sopenharmony_ci r = sev_launch_secret(kvm, &sev_cmd); 191162306a36Sopenharmony_ci break; 191262306a36Sopenharmony_ci case KVM_SEV_GET_ATTESTATION_REPORT: 191362306a36Sopenharmony_ci r = sev_get_attestation_report(kvm, &sev_cmd); 191462306a36Sopenharmony_ci break; 191562306a36Sopenharmony_ci case KVM_SEV_SEND_START: 191662306a36Sopenharmony_ci r = sev_send_start(kvm, &sev_cmd); 191762306a36Sopenharmony_ci break; 191862306a36Sopenharmony_ci case KVM_SEV_SEND_UPDATE_DATA: 191962306a36Sopenharmony_ci r = sev_send_update_data(kvm, &sev_cmd); 192062306a36Sopenharmony_ci break; 192162306a36Sopenharmony_ci case KVM_SEV_SEND_FINISH: 192262306a36Sopenharmony_ci r = sev_send_finish(kvm, &sev_cmd); 192362306a36Sopenharmony_ci break; 192462306a36Sopenharmony_ci case KVM_SEV_SEND_CANCEL: 192562306a36Sopenharmony_ci r = sev_send_cancel(kvm, &sev_cmd); 192662306a36Sopenharmony_ci break; 192762306a36Sopenharmony_ci case KVM_SEV_RECEIVE_START: 192862306a36Sopenharmony_ci r = sev_receive_start(kvm, &sev_cmd); 192962306a36Sopenharmony_ci break; 193062306a36Sopenharmony_ci case KVM_SEV_RECEIVE_UPDATE_DATA: 193162306a36Sopenharmony_ci r = sev_receive_update_data(kvm, &sev_cmd); 193262306a36Sopenharmony_ci break; 193362306a36Sopenharmony_ci case KVM_SEV_RECEIVE_FINISH: 193462306a36Sopenharmony_ci r = sev_receive_finish(kvm, &sev_cmd); 193562306a36Sopenharmony_ci break; 193662306a36Sopenharmony_ci default: 193762306a36Sopenharmony_ci r = -EINVAL; 193862306a36Sopenharmony_ci goto out; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci if (copy_to_user(argp, &sev_cmd, sizeof(struct kvm_sev_cmd))) 194262306a36Sopenharmony_ci r = -EFAULT; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ciout: 194562306a36Sopenharmony_ci mutex_unlock(&kvm->lock); 194662306a36Sopenharmony_ci return r; 194762306a36Sopenharmony_ci} 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ciint sev_mem_enc_register_region(struct kvm *kvm, 195062306a36Sopenharmony_ci struct kvm_enc_region *range) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 195362306a36Sopenharmony_ci struct enc_region *region; 195462306a36Sopenharmony_ci int ret = 0; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci if (!sev_guest(kvm)) 195762306a36Sopenharmony_ci return -ENOTTY; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci /* If kvm is mirroring encryption context it isn't responsible for it */ 196062306a36Sopenharmony_ci if (is_mirroring_enc_context(kvm)) 196162306a36Sopenharmony_ci return -EINVAL; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci if (range->addr > ULONG_MAX || range->size > ULONG_MAX) 196462306a36Sopenharmony_ci return -EINVAL; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci region = kzalloc(sizeof(*region), GFP_KERNEL_ACCOUNT); 196762306a36Sopenharmony_ci if (!region) 196862306a36Sopenharmony_ci return -ENOMEM; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci mutex_lock(&kvm->lock); 197162306a36Sopenharmony_ci region->pages = sev_pin_memory(kvm, range->addr, range->size, ®ion->npages, 1); 197262306a36Sopenharmony_ci if (IS_ERR(region->pages)) { 197362306a36Sopenharmony_ci ret = PTR_ERR(region->pages); 197462306a36Sopenharmony_ci mutex_unlock(&kvm->lock); 197562306a36Sopenharmony_ci goto e_free; 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci region->uaddr = range->addr; 197962306a36Sopenharmony_ci region->size = range->size; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci list_add_tail(®ion->list, &sev->regions_list); 198262306a36Sopenharmony_ci mutex_unlock(&kvm->lock); 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci /* 198562306a36Sopenharmony_ci * The guest may change the memory encryption attribute from C=0 -> C=1 198662306a36Sopenharmony_ci * or vice versa for this memory range. Lets make sure caches are 198762306a36Sopenharmony_ci * flushed to ensure that guest data gets written into memory with 198862306a36Sopenharmony_ci * correct C-bit. 198962306a36Sopenharmony_ci */ 199062306a36Sopenharmony_ci sev_clflush_pages(region->pages, region->npages); 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci return ret; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_cie_free: 199562306a36Sopenharmony_ci kfree(region); 199662306a36Sopenharmony_ci return ret; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_cistatic struct enc_region * 200062306a36Sopenharmony_cifind_enc_region(struct kvm *kvm, struct kvm_enc_region *range) 200162306a36Sopenharmony_ci{ 200262306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 200362306a36Sopenharmony_ci struct list_head *head = &sev->regions_list; 200462306a36Sopenharmony_ci struct enc_region *i; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci list_for_each_entry(i, head, list) { 200762306a36Sopenharmony_ci if (i->uaddr == range->addr && 200862306a36Sopenharmony_ci i->size == range->size) 200962306a36Sopenharmony_ci return i; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci return NULL; 201362306a36Sopenharmony_ci} 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_cistatic void __unregister_enc_region_locked(struct kvm *kvm, 201662306a36Sopenharmony_ci struct enc_region *region) 201762306a36Sopenharmony_ci{ 201862306a36Sopenharmony_ci sev_unpin_memory(kvm, region->pages, region->npages); 201962306a36Sopenharmony_ci list_del(®ion->list); 202062306a36Sopenharmony_ci kfree(region); 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ciint sev_mem_enc_unregister_region(struct kvm *kvm, 202462306a36Sopenharmony_ci struct kvm_enc_region *range) 202562306a36Sopenharmony_ci{ 202662306a36Sopenharmony_ci struct enc_region *region; 202762306a36Sopenharmony_ci int ret; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci /* If kvm is mirroring encryption context it isn't responsible for it */ 203062306a36Sopenharmony_ci if (is_mirroring_enc_context(kvm)) 203162306a36Sopenharmony_ci return -EINVAL; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci mutex_lock(&kvm->lock); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (!sev_guest(kvm)) { 203662306a36Sopenharmony_ci ret = -ENOTTY; 203762306a36Sopenharmony_ci goto failed; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci region = find_enc_region(kvm, range); 204162306a36Sopenharmony_ci if (!region) { 204262306a36Sopenharmony_ci ret = -EINVAL; 204362306a36Sopenharmony_ci goto failed; 204462306a36Sopenharmony_ci } 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci /* 204762306a36Sopenharmony_ci * Ensure that all guest tagged cache entries are flushed before 204862306a36Sopenharmony_ci * releasing the pages back to the system for use. CLFLUSH will 204962306a36Sopenharmony_ci * not do this, so issue a WBINVD. 205062306a36Sopenharmony_ci */ 205162306a36Sopenharmony_ci wbinvd_on_all_cpus(); 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci __unregister_enc_region_locked(kvm, region); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci mutex_unlock(&kvm->lock); 205662306a36Sopenharmony_ci return 0; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_cifailed: 205962306a36Sopenharmony_ci mutex_unlock(&kvm->lock); 206062306a36Sopenharmony_ci return ret; 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ciint sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) 206462306a36Sopenharmony_ci{ 206562306a36Sopenharmony_ci struct fd f = fdget(source_fd); 206662306a36Sopenharmony_ci struct kvm *source_kvm; 206762306a36Sopenharmony_ci struct kvm_sev_info *source_sev, *mirror_sev; 206862306a36Sopenharmony_ci int ret; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci if (!f.file) 207162306a36Sopenharmony_ci return -EBADF; 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci if (!file_is_kvm(f.file)) { 207462306a36Sopenharmony_ci ret = -EBADF; 207562306a36Sopenharmony_ci goto e_source_fput; 207662306a36Sopenharmony_ci } 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci source_kvm = f.file->private_data; 207962306a36Sopenharmony_ci ret = sev_lock_two_vms(kvm, source_kvm); 208062306a36Sopenharmony_ci if (ret) 208162306a36Sopenharmony_ci goto e_source_fput; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* 208462306a36Sopenharmony_ci * Mirrors of mirrors should work, but let's not get silly. Also 208562306a36Sopenharmony_ci * disallow out-of-band SEV/SEV-ES init if the target is already an 208662306a36Sopenharmony_ci * SEV guest, or if vCPUs have been created. KVM relies on vCPUs being 208762306a36Sopenharmony_ci * created after SEV/SEV-ES initialization, e.g. to init intercepts. 208862306a36Sopenharmony_ci */ 208962306a36Sopenharmony_ci if (sev_guest(kvm) || !sev_guest(source_kvm) || 209062306a36Sopenharmony_ci is_mirroring_enc_context(source_kvm) || kvm->created_vcpus) { 209162306a36Sopenharmony_ci ret = -EINVAL; 209262306a36Sopenharmony_ci goto e_unlock; 209362306a36Sopenharmony_ci } 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci /* 209662306a36Sopenharmony_ci * The mirror kvm holds an enc_context_owner ref so its asid can't 209762306a36Sopenharmony_ci * disappear until we're done with it 209862306a36Sopenharmony_ci */ 209962306a36Sopenharmony_ci source_sev = &to_kvm_svm(source_kvm)->sev_info; 210062306a36Sopenharmony_ci kvm_get_kvm(source_kvm); 210162306a36Sopenharmony_ci mirror_sev = &to_kvm_svm(kvm)->sev_info; 210262306a36Sopenharmony_ci list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* Set enc_context_owner and copy its encryption context over */ 210562306a36Sopenharmony_ci mirror_sev->enc_context_owner = source_kvm; 210662306a36Sopenharmony_ci mirror_sev->active = true; 210762306a36Sopenharmony_ci mirror_sev->asid = source_sev->asid; 210862306a36Sopenharmony_ci mirror_sev->fd = source_sev->fd; 210962306a36Sopenharmony_ci mirror_sev->es_active = source_sev->es_active; 211062306a36Sopenharmony_ci mirror_sev->handle = source_sev->handle; 211162306a36Sopenharmony_ci INIT_LIST_HEAD(&mirror_sev->regions_list); 211262306a36Sopenharmony_ci INIT_LIST_HEAD(&mirror_sev->mirror_vms); 211362306a36Sopenharmony_ci ret = 0; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci /* 211662306a36Sopenharmony_ci * Do not copy ap_jump_table. Since the mirror does not share the same 211762306a36Sopenharmony_ci * KVM contexts as the original, and they may have different 211862306a36Sopenharmony_ci * memory-views. 211962306a36Sopenharmony_ci */ 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_cie_unlock: 212262306a36Sopenharmony_ci sev_unlock_two_vms(kvm, source_kvm); 212362306a36Sopenharmony_cie_source_fput: 212462306a36Sopenharmony_ci fdput(f); 212562306a36Sopenharmony_ci return ret; 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_civoid sev_vm_destroy(struct kvm *kvm) 212962306a36Sopenharmony_ci{ 213062306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; 213162306a36Sopenharmony_ci struct list_head *head = &sev->regions_list; 213262306a36Sopenharmony_ci struct list_head *pos, *q; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci if (!sev_guest(kvm)) 213562306a36Sopenharmony_ci return; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci WARN_ON(!list_empty(&sev->mirror_vms)); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci /* If this is a mirror_kvm release the enc_context_owner and skip sev cleanup */ 214062306a36Sopenharmony_ci if (is_mirroring_enc_context(kvm)) { 214162306a36Sopenharmony_ci struct kvm *owner_kvm = sev->enc_context_owner; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci mutex_lock(&owner_kvm->lock); 214462306a36Sopenharmony_ci list_del(&sev->mirror_entry); 214562306a36Sopenharmony_ci mutex_unlock(&owner_kvm->lock); 214662306a36Sopenharmony_ci kvm_put_kvm(owner_kvm); 214762306a36Sopenharmony_ci return; 214862306a36Sopenharmony_ci } 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci /* 215162306a36Sopenharmony_ci * Ensure that all guest tagged cache entries are flushed before 215262306a36Sopenharmony_ci * releasing the pages back to the system for use. CLFLUSH will 215362306a36Sopenharmony_ci * not do this, so issue a WBINVD. 215462306a36Sopenharmony_ci */ 215562306a36Sopenharmony_ci wbinvd_on_all_cpus(); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci /* 215862306a36Sopenharmony_ci * if userspace was terminated before unregistering the memory regions 215962306a36Sopenharmony_ci * then lets unpin all the registered memory. 216062306a36Sopenharmony_ci */ 216162306a36Sopenharmony_ci if (!list_empty(head)) { 216262306a36Sopenharmony_ci list_for_each_safe(pos, q, head) { 216362306a36Sopenharmony_ci __unregister_enc_region_locked(kvm, 216462306a36Sopenharmony_ci list_entry(pos, struct enc_region, list)); 216562306a36Sopenharmony_ci cond_resched(); 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci sev_unbind_asid(kvm, sev->handle); 217062306a36Sopenharmony_ci sev_asid_free(sev); 217162306a36Sopenharmony_ci} 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_civoid __init sev_set_cpu_caps(void) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci if (!sev_enabled) 217662306a36Sopenharmony_ci kvm_cpu_cap_clear(X86_FEATURE_SEV); 217762306a36Sopenharmony_ci if (!sev_es_enabled) 217862306a36Sopenharmony_ci kvm_cpu_cap_clear(X86_FEATURE_SEV_ES); 217962306a36Sopenharmony_ci} 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_civoid __init sev_hardware_setup(void) 218262306a36Sopenharmony_ci{ 218362306a36Sopenharmony_ci#ifdef CONFIG_KVM_AMD_SEV 218462306a36Sopenharmony_ci unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count; 218562306a36Sopenharmony_ci bool sev_es_supported = false; 218662306a36Sopenharmony_ci bool sev_supported = false; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci if (!sev_enabled || !npt_enabled || !nrips) 218962306a36Sopenharmony_ci goto out; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci /* 219262306a36Sopenharmony_ci * SEV must obviously be supported in hardware. Sanity check that the 219362306a36Sopenharmony_ci * CPU supports decode assists, which is mandatory for SEV guests to 219462306a36Sopenharmony_ci * support instruction emulation. 219562306a36Sopenharmony_ci */ 219662306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_SEV) || 219762306a36Sopenharmony_ci WARN_ON_ONCE(!boot_cpu_has(X86_FEATURE_DECODEASSISTS))) 219862306a36Sopenharmony_ci goto out; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci /* Retrieve SEV CPUID information */ 220162306a36Sopenharmony_ci cpuid(0x8000001f, &eax, &ebx, &ecx, &edx); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci /* Set encryption bit location for SEV-ES guests */ 220462306a36Sopenharmony_ci sev_enc_bit = ebx & 0x3f; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci /* Maximum number of encrypted guests supported simultaneously */ 220762306a36Sopenharmony_ci max_sev_asid = ecx; 220862306a36Sopenharmony_ci if (!max_sev_asid) 220962306a36Sopenharmony_ci goto out; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci /* Minimum ASID value that should be used for SEV guest */ 221262306a36Sopenharmony_ci min_sev_asid = edx; 221362306a36Sopenharmony_ci sev_me_mask = 1UL << (ebx & 0x3f); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci /* 221662306a36Sopenharmony_ci * Initialize SEV ASID bitmaps. Allocate space for ASID 0 in the bitmap, 221762306a36Sopenharmony_ci * even though it's never used, so that the bitmap is indexed by the 221862306a36Sopenharmony_ci * actual ASID. 221962306a36Sopenharmony_ci */ 222062306a36Sopenharmony_ci nr_asids = max_sev_asid + 1; 222162306a36Sopenharmony_ci sev_asid_bitmap = bitmap_zalloc(nr_asids, GFP_KERNEL); 222262306a36Sopenharmony_ci if (!sev_asid_bitmap) 222362306a36Sopenharmony_ci goto out; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci sev_reclaim_asid_bitmap = bitmap_zalloc(nr_asids, GFP_KERNEL); 222662306a36Sopenharmony_ci if (!sev_reclaim_asid_bitmap) { 222762306a36Sopenharmony_ci bitmap_free(sev_asid_bitmap); 222862306a36Sopenharmony_ci sev_asid_bitmap = NULL; 222962306a36Sopenharmony_ci goto out; 223062306a36Sopenharmony_ci } 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci sev_asid_count = max_sev_asid - min_sev_asid + 1; 223362306a36Sopenharmony_ci WARN_ON_ONCE(misc_cg_set_capacity(MISC_CG_RES_SEV, sev_asid_count)); 223462306a36Sopenharmony_ci sev_supported = true; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci /* SEV-ES support requested? */ 223762306a36Sopenharmony_ci if (!sev_es_enabled) 223862306a36Sopenharmony_ci goto out; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci /* 224162306a36Sopenharmony_ci * SEV-ES requires MMIO caching as KVM doesn't have access to the guest 224262306a36Sopenharmony_ci * instruction stream, i.e. can't emulate in response to a #NPF and 224362306a36Sopenharmony_ci * instead relies on #NPF(RSVD) being reflected into the guest as #VC 224462306a36Sopenharmony_ci * (the guest can then do a #VMGEXIT to request MMIO emulation). 224562306a36Sopenharmony_ci */ 224662306a36Sopenharmony_ci if (!enable_mmio_caching) 224762306a36Sopenharmony_ci goto out; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci /* Does the CPU support SEV-ES? */ 225062306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_SEV_ES)) 225162306a36Sopenharmony_ci goto out; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci /* Has the system been allocated ASIDs for SEV-ES? */ 225462306a36Sopenharmony_ci if (min_sev_asid == 1) 225562306a36Sopenharmony_ci goto out; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci sev_es_asid_count = min_sev_asid - 1; 225862306a36Sopenharmony_ci WARN_ON_ONCE(misc_cg_set_capacity(MISC_CG_RES_SEV_ES, sev_es_asid_count)); 225962306a36Sopenharmony_ci sev_es_supported = true; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ciout: 226262306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_SEV)) 226362306a36Sopenharmony_ci pr_info("SEV %s (ASIDs %u - %u)\n", 226462306a36Sopenharmony_ci sev_supported ? "enabled" : "disabled", 226562306a36Sopenharmony_ci min_sev_asid, max_sev_asid); 226662306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_SEV_ES)) 226762306a36Sopenharmony_ci pr_info("SEV-ES %s (ASIDs %u - %u)\n", 226862306a36Sopenharmony_ci sev_es_supported ? "enabled" : "disabled", 226962306a36Sopenharmony_ci min_sev_asid > 1 ? 1 : 0, min_sev_asid - 1); 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci sev_enabled = sev_supported; 227262306a36Sopenharmony_ci sev_es_enabled = sev_es_supported; 227362306a36Sopenharmony_ci if (!sev_es_enabled || !cpu_feature_enabled(X86_FEATURE_DEBUG_SWAP) || 227462306a36Sopenharmony_ci !cpu_feature_enabled(X86_FEATURE_NO_NESTED_DATA_BP)) 227562306a36Sopenharmony_ci sev_es_debug_swap_enabled = false; 227662306a36Sopenharmony_ci#endif 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_civoid sev_hardware_unsetup(void) 228062306a36Sopenharmony_ci{ 228162306a36Sopenharmony_ci if (!sev_enabled) 228262306a36Sopenharmony_ci return; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci /* No need to take sev_bitmap_lock, all VMs have been destroyed. */ 228562306a36Sopenharmony_ci sev_flush_asids(1, max_sev_asid); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci bitmap_free(sev_asid_bitmap); 228862306a36Sopenharmony_ci bitmap_free(sev_reclaim_asid_bitmap); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci misc_cg_set_capacity(MISC_CG_RES_SEV, 0); 229162306a36Sopenharmony_ci misc_cg_set_capacity(MISC_CG_RES_SEV_ES, 0); 229262306a36Sopenharmony_ci} 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ciint sev_cpu_init(struct svm_cpu_data *sd) 229562306a36Sopenharmony_ci{ 229662306a36Sopenharmony_ci if (!sev_enabled) 229762306a36Sopenharmony_ci return 0; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci sd->sev_vmcbs = kcalloc(nr_asids, sizeof(void *), GFP_KERNEL); 230062306a36Sopenharmony_ci if (!sd->sev_vmcbs) 230162306a36Sopenharmony_ci return -ENOMEM; 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci return 0; 230462306a36Sopenharmony_ci} 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci/* 230762306a36Sopenharmony_ci * Pages used by hardware to hold guest encrypted state must be flushed before 230862306a36Sopenharmony_ci * returning them to the system. 230962306a36Sopenharmony_ci */ 231062306a36Sopenharmony_cistatic void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va) 231162306a36Sopenharmony_ci{ 231262306a36Sopenharmony_ci int asid = to_kvm_svm(vcpu->kvm)->sev_info.asid; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci /* 231562306a36Sopenharmony_ci * Note! The address must be a kernel address, as regular page walk 231662306a36Sopenharmony_ci * checks are performed by VM_PAGE_FLUSH, i.e. operating on a user 231762306a36Sopenharmony_ci * address is non-deterministic and unsafe. This function deliberately 231862306a36Sopenharmony_ci * takes a pointer to deter passing in a user address. 231962306a36Sopenharmony_ci */ 232062306a36Sopenharmony_ci unsigned long addr = (unsigned long)va; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci /* 232362306a36Sopenharmony_ci * If CPU enforced cache coherency for encrypted mappings of the 232462306a36Sopenharmony_ci * same physical page is supported, use CLFLUSHOPT instead. NOTE: cache 232562306a36Sopenharmony_ci * flush is still needed in order to work properly with DMA devices. 232662306a36Sopenharmony_ci */ 232762306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_SME_COHERENT)) { 232862306a36Sopenharmony_ci clflush_cache_range(va, PAGE_SIZE); 232962306a36Sopenharmony_ci return; 233062306a36Sopenharmony_ci } 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci /* 233362306a36Sopenharmony_ci * VM Page Flush takes a host virtual address and a guest ASID. Fall 233462306a36Sopenharmony_ci * back to WBINVD if this faults so as not to make any problems worse 233562306a36Sopenharmony_ci * by leaving stale encrypted data in the cache. 233662306a36Sopenharmony_ci */ 233762306a36Sopenharmony_ci if (WARN_ON_ONCE(wrmsrl_safe(MSR_AMD64_VM_PAGE_FLUSH, addr | asid))) 233862306a36Sopenharmony_ci goto do_wbinvd; 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci return; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_cido_wbinvd: 234362306a36Sopenharmony_ci wbinvd_on_all_cpus(); 234462306a36Sopenharmony_ci} 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_civoid sev_guest_memory_reclaimed(struct kvm *kvm) 234762306a36Sopenharmony_ci{ 234862306a36Sopenharmony_ci if (!sev_guest(kvm)) 234962306a36Sopenharmony_ci return; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci wbinvd_on_all_cpus(); 235262306a36Sopenharmony_ci} 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_civoid sev_free_vcpu(struct kvm_vcpu *vcpu) 235562306a36Sopenharmony_ci{ 235662306a36Sopenharmony_ci struct vcpu_svm *svm; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci if (!sev_es_guest(vcpu->kvm)) 235962306a36Sopenharmony_ci return; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci svm = to_svm(vcpu); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci if (vcpu->arch.guest_state_protected) 236462306a36Sopenharmony_ci sev_flush_encrypted_page(vcpu, svm->sev_es.vmsa); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci __free_page(virt_to_page(svm->sev_es.vmsa)); 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci if (svm->sev_es.ghcb_sa_free) 236962306a36Sopenharmony_ci kvfree(svm->sev_es.ghcb_sa); 237062306a36Sopenharmony_ci} 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_cistatic void dump_ghcb(struct vcpu_svm *svm) 237362306a36Sopenharmony_ci{ 237462306a36Sopenharmony_ci struct ghcb *ghcb = svm->sev_es.ghcb; 237562306a36Sopenharmony_ci unsigned int nbits; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci /* Re-use the dump_invalid_vmcb module parameter */ 237862306a36Sopenharmony_ci if (!dump_invalid_vmcb) { 237962306a36Sopenharmony_ci pr_warn_ratelimited("set kvm_amd.dump_invalid_vmcb=1 to dump internal KVM state.\n"); 238062306a36Sopenharmony_ci return; 238162306a36Sopenharmony_ci } 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci nbits = sizeof(ghcb->save.valid_bitmap) * 8; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci pr_err("GHCB (GPA=%016llx):\n", svm->vmcb->control.ghcb_gpa); 238662306a36Sopenharmony_ci pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_code", 238762306a36Sopenharmony_ci ghcb->save.sw_exit_code, ghcb_sw_exit_code_is_valid(ghcb)); 238862306a36Sopenharmony_ci pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_info_1", 238962306a36Sopenharmony_ci ghcb->save.sw_exit_info_1, ghcb_sw_exit_info_1_is_valid(ghcb)); 239062306a36Sopenharmony_ci pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_info_2", 239162306a36Sopenharmony_ci ghcb->save.sw_exit_info_2, ghcb_sw_exit_info_2_is_valid(ghcb)); 239262306a36Sopenharmony_ci pr_err("%-20s%016llx is_valid: %u\n", "sw_scratch", 239362306a36Sopenharmony_ci ghcb->save.sw_scratch, ghcb_sw_scratch_is_valid(ghcb)); 239462306a36Sopenharmony_ci pr_err("%-20s%*pb\n", "valid_bitmap", nbits, ghcb->save.valid_bitmap); 239562306a36Sopenharmony_ci} 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_cistatic void sev_es_sync_to_ghcb(struct vcpu_svm *svm) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci struct kvm_vcpu *vcpu = &svm->vcpu; 240062306a36Sopenharmony_ci struct ghcb *ghcb = svm->sev_es.ghcb; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci /* 240362306a36Sopenharmony_ci * The GHCB protocol so far allows for the following data 240462306a36Sopenharmony_ci * to be returned: 240562306a36Sopenharmony_ci * GPRs RAX, RBX, RCX, RDX 240662306a36Sopenharmony_ci * 240762306a36Sopenharmony_ci * Copy their values, even if they may not have been written during the 240862306a36Sopenharmony_ci * VM-Exit. It's the guest's responsibility to not consume random data. 240962306a36Sopenharmony_ci */ 241062306a36Sopenharmony_ci ghcb_set_rax(ghcb, vcpu->arch.regs[VCPU_REGS_RAX]); 241162306a36Sopenharmony_ci ghcb_set_rbx(ghcb, vcpu->arch.regs[VCPU_REGS_RBX]); 241262306a36Sopenharmony_ci ghcb_set_rcx(ghcb, vcpu->arch.regs[VCPU_REGS_RCX]); 241362306a36Sopenharmony_ci ghcb_set_rdx(ghcb, vcpu->arch.regs[VCPU_REGS_RDX]); 241462306a36Sopenharmony_ci} 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistatic void sev_es_sync_from_ghcb(struct vcpu_svm *svm) 241762306a36Sopenharmony_ci{ 241862306a36Sopenharmony_ci struct vmcb_control_area *control = &svm->vmcb->control; 241962306a36Sopenharmony_ci struct kvm_vcpu *vcpu = &svm->vcpu; 242062306a36Sopenharmony_ci struct ghcb *ghcb = svm->sev_es.ghcb; 242162306a36Sopenharmony_ci u64 exit_code; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci /* 242462306a36Sopenharmony_ci * The GHCB protocol so far allows for the following data 242562306a36Sopenharmony_ci * to be supplied: 242662306a36Sopenharmony_ci * GPRs RAX, RBX, RCX, RDX 242762306a36Sopenharmony_ci * XCR0 242862306a36Sopenharmony_ci * CPL 242962306a36Sopenharmony_ci * 243062306a36Sopenharmony_ci * VMMCALL allows the guest to provide extra registers. KVM also 243162306a36Sopenharmony_ci * expects RSI for hypercalls, so include that, too. 243262306a36Sopenharmony_ci * 243362306a36Sopenharmony_ci * Copy their values to the appropriate location if supplied. 243462306a36Sopenharmony_ci */ 243562306a36Sopenharmony_ci memset(vcpu->arch.regs, 0, sizeof(vcpu->arch.regs)); 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(svm->sev_es.valid_bitmap) != sizeof(ghcb->save.valid_bitmap)); 243862306a36Sopenharmony_ci memcpy(&svm->sev_es.valid_bitmap, &ghcb->save.valid_bitmap, sizeof(ghcb->save.valid_bitmap)); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci vcpu->arch.regs[VCPU_REGS_RAX] = kvm_ghcb_get_rax_if_valid(svm, ghcb); 244162306a36Sopenharmony_ci vcpu->arch.regs[VCPU_REGS_RBX] = kvm_ghcb_get_rbx_if_valid(svm, ghcb); 244262306a36Sopenharmony_ci vcpu->arch.regs[VCPU_REGS_RCX] = kvm_ghcb_get_rcx_if_valid(svm, ghcb); 244362306a36Sopenharmony_ci vcpu->arch.regs[VCPU_REGS_RDX] = kvm_ghcb_get_rdx_if_valid(svm, ghcb); 244462306a36Sopenharmony_ci vcpu->arch.regs[VCPU_REGS_RSI] = kvm_ghcb_get_rsi_if_valid(svm, ghcb); 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci svm->vmcb->save.cpl = kvm_ghcb_get_cpl_if_valid(svm, ghcb); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci if (kvm_ghcb_xcr0_is_valid(svm)) { 244962306a36Sopenharmony_ci vcpu->arch.xcr0 = ghcb_get_xcr0(ghcb); 245062306a36Sopenharmony_ci kvm_update_cpuid_runtime(vcpu); 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci /* Copy the GHCB exit information into the VMCB fields */ 245462306a36Sopenharmony_ci exit_code = ghcb_get_sw_exit_code(ghcb); 245562306a36Sopenharmony_ci control->exit_code = lower_32_bits(exit_code); 245662306a36Sopenharmony_ci control->exit_code_hi = upper_32_bits(exit_code); 245762306a36Sopenharmony_ci control->exit_info_1 = ghcb_get_sw_exit_info_1(ghcb); 245862306a36Sopenharmony_ci control->exit_info_2 = ghcb_get_sw_exit_info_2(ghcb); 245962306a36Sopenharmony_ci svm->sev_es.sw_scratch = kvm_ghcb_get_sw_scratch_if_valid(svm, ghcb); 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci /* Clear the valid entries fields */ 246262306a36Sopenharmony_ci memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap)); 246362306a36Sopenharmony_ci} 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_cistatic u64 kvm_ghcb_get_sw_exit_code(struct vmcb_control_area *control) 246662306a36Sopenharmony_ci{ 246762306a36Sopenharmony_ci return (((u64)control->exit_code_hi) << 32) | control->exit_code; 246862306a36Sopenharmony_ci} 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_cistatic int sev_es_validate_vmgexit(struct vcpu_svm *svm) 247162306a36Sopenharmony_ci{ 247262306a36Sopenharmony_ci struct vmcb_control_area *control = &svm->vmcb->control; 247362306a36Sopenharmony_ci struct kvm_vcpu *vcpu = &svm->vcpu; 247462306a36Sopenharmony_ci u64 exit_code; 247562306a36Sopenharmony_ci u64 reason; 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci /* 247862306a36Sopenharmony_ci * Retrieve the exit code now even though it may not be marked valid 247962306a36Sopenharmony_ci * as it could help with debugging. 248062306a36Sopenharmony_ci */ 248162306a36Sopenharmony_ci exit_code = kvm_ghcb_get_sw_exit_code(control); 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci /* Only GHCB Usage code 0 is supported */ 248462306a36Sopenharmony_ci if (svm->sev_es.ghcb->ghcb_usage) { 248562306a36Sopenharmony_ci reason = GHCB_ERR_INVALID_USAGE; 248662306a36Sopenharmony_ci goto vmgexit_err; 248762306a36Sopenharmony_ci } 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci reason = GHCB_ERR_MISSING_INPUT; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci if (!kvm_ghcb_sw_exit_code_is_valid(svm) || 249262306a36Sopenharmony_ci !kvm_ghcb_sw_exit_info_1_is_valid(svm) || 249362306a36Sopenharmony_ci !kvm_ghcb_sw_exit_info_2_is_valid(svm)) 249462306a36Sopenharmony_ci goto vmgexit_err; 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci switch (exit_code) { 249762306a36Sopenharmony_ci case SVM_EXIT_READ_DR7: 249862306a36Sopenharmony_ci break; 249962306a36Sopenharmony_ci case SVM_EXIT_WRITE_DR7: 250062306a36Sopenharmony_ci if (!kvm_ghcb_rax_is_valid(svm)) 250162306a36Sopenharmony_ci goto vmgexit_err; 250262306a36Sopenharmony_ci break; 250362306a36Sopenharmony_ci case SVM_EXIT_RDTSC: 250462306a36Sopenharmony_ci break; 250562306a36Sopenharmony_ci case SVM_EXIT_RDPMC: 250662306a36Sopenharmony_ci if (!kvm_ghcb_rcx_is_valid(svm)) 250762306a36Sopenharmony_ci goto vmgexit_err; 250862306a36Sopenharmony_ci break; 250962306a36Sopenharmony_ci case SVM_EXIT_CPUID: 251062306a36Sopenharmony_ci if (!kvm_ghcb_rax_is_valid(svm) || 251162306a36Sopenharmony_ci !kvm_ghcb_rcx_is_valid(svm)) 251262306a36Sopenharmony_ci goto vmgexit_err; 251362306a36Sopenharmony_ci if (vcpu->arch.regs[VCPU_REGS_RAX] == 0xd) 251462306a36Sopenharmony_ci if (!kvm_ghcb_xcr0_is_valid(svm)) 251562306a36Sopenharmony_ci goto vmgexit_err; 251662306a36Sopenharmony_ci break; 251762306a36Sopenharmony_ci case SVM_EXIT_INVD: 251862306a36Sopenharmony_ci break; 251962306a36Sopenharmony_ci case SVM_EXIT_IOIO: 252062306a36Sopenharmony_ci if (control->exit_info_1 & SVM_IOIO_STR_MASK) { 252162306a36Sopenharmony_ci if (!kvm_ghcb_sw_scratch_is_valid(svm)) 252262306a36Sopenharmony_ci goto vmgexit_err; 252362306a36Sopenharmony_ci } else { 252462306a36Sopenharmony_ci if (!(control->exit_info_1 & SVM_IOIO_TYPE_MASK)) 252562306a36Sopenharmony_ci if (!kvm_ghcb_rax_is_valid(svm)) 252662306a36Sopenharmony_ci goto vmgexit_err; 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci break; 252962306a36Sopenharmony_ci case SVM_EXIT_MSR: 253062306a36Sopenharmony_ci if (!kvm_ghcb_rcx_is_valid(svm)) 253162306a36Sopenharmony_ci goto vmgexit_err; 253262306a36Sopenharmony_ci if (control->exit_info_1) { 253362306a36Sopenharmony_ci if (!kvm_ghcb_rax_is_valid(svm) || 253462306a36Sopenharmony_ci !kvm_ghcb_rdx_is_valid(svm)) 253562306a36Sopenharmony_ci goto vmgexit_err; 253662306a36Sopenharmony_ci } 253762306a36Sopenharmony_ci break; 253862306a36Sopenharmony_ci case SVM_EXIT_VMMCALL: 253962306a36Sopenharmony_ci if (!kvm_ghcb_rax_is_valid(svm) || 254062306a36Sopenharmony_ci !kvm_ghcb_cpl_is_valid(svm)) 254162306a36Sopenharmony_ci goto vmgexit_err; 254262306a36Sopenharmony_ci break; 254362306a36Sopenharmony_ci case SVM_EXIT_RDTSCP: 254462306a36Sopenharmony_ci break; 254562306a36Sopenharmony_ci case SVM_EXIT_WBINVD: 254662306a36Sopenharmony_ci break; 254762306a36Sopenharmony_ci case SVM_EXIT_MONITOR: 254862306a36Sopenharmony_ci if (!kvm_ghcb_rax_is_valid(svm) || 254962306a36Sopenharmony_ci !kvm_ghcb_rcx_is_valid(svm) || 255062306a36Sopenharmony_ci !kvm_ghcb_rdx_is_valid(svm)) 255162306a36Sopenharmony_ci goto vmgexit_err; 255262306a36Sopenharmony_ci break; 255362306a36Sopenharmony_ci case SVM_EXIT_MWAIT: 255462306a36Sopenharmony_ci if (!kvm_ghcb_rax_is_valid(svm) || 255562306a36Sopenharmony_ci !kvm_ghcb_rcx_is_valid(svm)) 255662306a36Sopenharmony_ci goto vmgexit_err; 255762306a36Sopenharmony_ci break; 255862306a36Sopenharmony_ci case SVM_VMGEXIT_MMIO_READ: 255962306a36Sopenharmony_ci case SVM_VMGEXIT_MMIO_WRITE: 256062306a36Sopenharmony_ci if (!kvm_ghcb_sw_scratch_is_valid(svm)) 256162306a36Sopenharmony_ci goto vmgexit_err; 256262306a36Sopenharmony_ci break; 256362306a36Sopenharmony_ci case SVM_VMGEXIT_NMI_COMPLETE: 256462306a36Sopenharmony_ci case SVM_VMGEXIT_AP_HLT_LOOP: 256562306a36Sopenharmony_ci case SVM_VMGEXIT_AP_JUMP_TABLE: 256662306a36Sopenharmony_ci case SVM_VMGEXIT_UNSUPPORTED_EVENT: 256762306a36Sopenharmony_ci break; 256862306a36Sopenharmony_ci default: 256962306a36Sopenharmony_ci reason = GHCB_ERR_INVALID_EVENT; 257062306a36Sopenharmony_ci goto vmgexit_err; 257162306a36Sopenharmony_ci } 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci return 0; 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_civmgexit_err: 257662306a36Sopenharmony_ci if (reason == GHCB_ERR_INVALID_USAGE) { 257762306a36Sopenharmony_ci vcpu_unimpl(vcpu, "vmgexit: ghcb usage %#x is not valid\n", 257862306a36Sopenharmony_ci svm->sev_es.ghcb->ghcb_usage); 257962306a36Sopenharmony_ci } else if (reason == GHCB_ERR_INVALID_EVENT) { 258062306a36Sopenharmony_ci vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n", 258162306a36Sopenharmony_ci exit_code); 258262306a36Sopenharmony_ci } else { 258362306a36Sopenharmony_ci vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n", 258462306a36Sopenharmony_ci exit_code); 258562306a36Sopenharmony_ci dump_ghcb(svm); 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); 258962306a36Sopenharmony_ci ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, reason); 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci /* Resume the guest to "return" the error code. */ 259262306a36Sopenharmony_ci return 1; 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_civoid sev_es_unmap_ghcb(struct vcpu_svm *svm) 259662306a36Sopenharmony_ci{ 259762306a36Sopenharmony_ci if (!svm->sev_es.ghcb) 259862306a36Sopenharmony_ci return; 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci if (svm->sev_es.ghcb_sa_free) { 260162306a36Sopenharmony_ci /* 260262306a36Sopenharmony_ci * The scratch area lives outside the GHCB, so there is a 260362306a36Sopenharmony_ci * buffer that, depending on the operation performed, may 260462306a36Sopenharmony_ci * need to be synced, then freed. 260562306a36Sopenharmony_ci */ 260662306a36Sopenharmony_ci if (svm->sev_es.ghcb_sa_sync) { 260762306a36Sopenharmony_ci kvm_write_guest(svm->vcpu.kvm, 260862306a36Sopenharmony_ci svm->sev_es.sw_scratch, 260962306a36Sopenharmony_ci svm->sev_es.ghcb_sa, 261062306a36Sopenharmony_ci svm->sev_es.ghcb_sa_len); 261162306a36Sopenharmony_ci svm->sev_es.ghcb_sa_sync = false; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci kvfree(svm->sev_es.ghcb_sa); 261562306a36Sopenharmony_ci svm->sev_es.ghcb_sa = NULL; 261662306a36Sopenharmony_ci svm->sev_es.ghcb_sa_free = false; 261762306a36Sopenharmony_ci } 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci trace_kvm_vmgexit_exit(svm->vcpu.vcpu_id, svm->sev_es.ghcb); 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci sev_es_sync_to_ghcb(svm); 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci kvm_vcpu_unmap(&svm->vcpu, &svm->sev_es.ghcb_map, true); 262462306a36Sopenharmony_ci svm->sev_es.ghcb = NULL; 262562306a36Sopenharmony_ci} 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_civoid pre_sev_run(struct vcpu_svm *svm, int cpu) 262862306a36Sopenharmony_ci{ 262962306a36Sopenharmony_ci struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, cpu); 263062306a36Sopenharmony_ci int asid = sev_get_asid(svm->vcpu.kvm); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci /* Assign the asid allocated with this SEV guest */ 263362306a36Sopenharmony_ci svm->asid = asid; 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci /* 263662306a36Sopenharmony_ci * Flush guest TLB: 263762306a36Sopenharmony_ci * 263862306a36Sopenharmony_ci * 1) when different VMCB for the same ASID is to be run on the same host CPU. 263962306a36Sopenharmony_ci * 2) or this VMCB was executed on different host CPU in previous VMRUNs. 264062306a36Sopenharmony_ci */ 264162306a36Sopenharmony_ci if (sd->sev_vmcbs[asid] == svm->vmcb && 264262306a36Sopenharmony_ci svm->vcpu.arch.last_vmentry_cpu == cpu) 264362306a36Sopenharmony_ci return; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci sd->sev_vmcbs[asid] = svm->vmcb; 264662306a36Sopenharmony_ci svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ASID; 264762306a36Sopenharmony_ci vmcb_mark_dirty(svm->vmcb, VMCB_ASID); 264862306a36Sopenharmony_ci} 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci#define GHCB_SCRATCH_AREA_LIMIT (16ULL * PAGE_SIZE) 265162306a36Sopenharmony_cistatic int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) 265262306a36Sopenharmony_ci{ 265362306a36Sopenharmony_ci struct vmcb_control_area *control = &svm->vmcb->control; 265462306a36Sopenharmony_ci u64 ghcb_scratch_beg, ghcb_scratch_end; 265562306a36Sopenharmony_ci u64 scratch_gpa_beg, scratch_gpa_end; 265662306a36Sopenharmony_ci void *scratch_va; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci scratch_gpa_beg = svm->sev_es.sw_scratch; 265962306a36Sopenharmony_ci if (!scratch_gpa_beg) { 266062306a36Sopenharmony_ci pr_err("vmgexit: scratch gpa not provided\n"); 266162306a36Sopenharmony_ci goto e_scratch; 266262306a36Sopenharmony_ci } 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci scratch_gpa_end = scratch_gpa_beg + len; 266562306a36Sopenharmony_ci if (scratch_gpa_end < scratch_gpa_beg) { 266662306a36Sopenharmony_ci pr_err("vmgexit: scratch length (%#llx) not valid for scratch address (%#llx)\n", 266762306a36Sopenharmony_ci len, scratch_gpa_beg); 266862306a36Sopenharmony_ci goto e_scratch; 266962306a36Sopenharmony_ci } 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci if ((scratch_gpa_beg & PAGE_MASK) == control->ghcb_gpa) { 267262306a36Sopenharmony_ci /* Scratch area begins within GHCB */ 267362306a36Sopenharmony_ci ghcb_scratch_beg = control->ghcb_gpa + 267462306a36Sopenharmony_ci offsetof(struct ghcb, shared_buffer); 267562306a36Sopenharmony_ci ghcb_scratch_end = control->ghcb_gpa + 267662306a36Sopenharmony_ci offsetof(struct ghcb, reserved_0xff0); 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci /* 267962306a36Sopenharmony_ci * If the scratch area begins within the GHCB, it must be 268062306a36Sopenharmony_ci * completely contained in the GHCB shared buffer area. 268162306a36Sopenharmony_ci */ 268262306a36Sopenharmony_ci if (scratch_gpa_beg < ghcb_scratch_beg || 268362306a36Sopenharmony_ci scratch_gpa_end > ghcb_scratch_end) { 268462306a36Sopenharmony_ci pr_err("vmgexit: scratch area is outside of GHCB shared buffer area (%#llx - %#llx)\n", 268562306a36Sopenharmony_ci scratch_gpa_beg, scratch_gpa_end); 268662306a36Sopenharmony_ci goto e_scratch; 268762306a36Sopenharmony_ci } 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci scratch_va = (void *)svm->sev_es.ghcb; 269062306a36Sopenharmony_ci scratch_va += (scratch_gpa_beg - control->ghcb_gpa); 269162306a36Sopenharmony_ci } else { 269262306a36Sopenharmony_ci /* 269362306a36Sopenharmony_ci * The guest memory must be read into a kernel buffer, so 269462306a36Sopenharmony_ci * limit the size 269562306a36Sopenharmony_ci */ 269662306a36Sopenharmony_ci if (len > GHCB_SCRATCH_AREA_LIMIT) { 269762306a36Sopenharmony_ci pr_err("vmgexit: scratch area exceeds KVM limits (%#llx requested, %#llx limit)\n", 269862306a36Sopenharmony_ci len, GHCB_SCRATCH_AREA_LIMIT); 269962306a36Sopenharmony_ci goto e_scratch; 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci scratch_va = kvzalloc(len, GFP_KERNEL_ACCOUNT); 270262306a36Sopenharmony_ci if (!scratch_va) 270362306a36Sopenharmony_ci return -ENOMEM; 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, len)) { 270662306a36Sopenharmony_ci /* Unable to copy scratch area from guest */ 270762306a36Sopenharmony_ci pr_err("vmgexit: kvm_read_guest for scratch area failed\n"); 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci kvfree(scratch_va); 271062306a36Sopenharmony_ci return -EFAULT; 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci /* 271462306a36Sopenharmony_ci * The scratch area is outside the GHCB. The operation will 271562306a36Sopenharmony_ci * dictate whether the buffer needs to be synced before running 271662306a36Sopenharmony_ci * the vCPU next time (i.e. a read was requested so the data 271762306a36Sopenharmony_ci * must be written back to the guest memory). 271862306a36Sopenharmony_ci */ 271962306a36Sopenharmony_ci svm->sev_es.ghcb_sa_sync = sync; 272062306a36Sopenharmony_ci svm->sev_es.ghcb_sa_free = true; 272162306a36Sopenharmony_ci } 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci svm->sev_es.ghcb_sa = scratch_va; 272462306a36Sopenharmony_ci svm->sev_es.ghcb_sa_len = len; 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci return 0; 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_cie_scratch: 272962306a36Sopenharmony_ci ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); 273062306a36Sopenharmony_ci ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, GHCB_ERR_INVALID_SCRATCH_AREA); 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci return 1; 273362306a36Sopenharmony_ci} 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_cistatic void set_ghcb_msr_bits(struct vcpu_svm *svm, u64 value, u64 mask, 273662306a36Sopenharmony_ci unsigned int pos) 273762306a36Sopenharmony_ci{ 273862306a36Sopenharmony_ci svm->vmcb->control.ghcb_gpa &= ~(mask << pos); 273962306a36Sopenharmony_ci svm->vmcb->control.ghcb_gpa |= (value & mask) << pos; 274062306a36Sopenharmony_ci} 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_cistatic u64 get_ghcb_msr_bits(struct vcpu_svm *svm, u64 mask, unsigned int pos) 274362306a36Sopenharmony_ci{ 274462306a36Sopenharmony_ci return (svm->vmcb->control.ghcb_gpa >> pos) & mask; 274562306a36Sopenharmony_ci} 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_cistatic void set_ghcb_msr(struct vcpu_svm *svm, u64 value) 274862306a36Sopenharmony_ci{ 274962306a36Sopenharmony_ci svm->vmcb->control.ghcb_gpa = value; 275062306a36Sopenharmony_ci} 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_cistatic int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) 275362306a36Sopenharmony_ci{ 275462306a36Sopenharmony_ci struct vmcb_control_area *control = &svm->vmcb->control; 275562306a36Sopenharmony_ci struct kvm_vcpu *vcpu = &svm->vcpu; 275662306a36Sopenharmony_ci u64 ghcb_info; 275762306a36Sopenharmony_ci int ret = 1; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci ghcb_info = control->ghcb_gpa & GHCB_MSR_INFO_MASK; 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci trace_kvm_vmgexit_msr_protocol_enter(svm->vcpu.vcpu_id, 276262306a36Sopenharmony_ci control->ghcb_gpa); 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci switch (ghcb_info) { 276562306a36Sopenharmony_ci case GHCB_MSR_SEV_INFO_REQ: 276662306a36Sopenharmony_ci set_ghcb_msr(svm, GHCB_MSR_SEV_INFO(GHCB_VERSION_MAX, 276762306a36Sopenharmony_ci GHCB_VERSION_MIN, 276862306a36Sopenharmony_ci sev_enc_bit)); 276962306a36Sopenharmony_ci break; 277062306a36Sopenharmony_ci case GHCB_MSR_CPUID_REQ: { 277162306a36Sopenharmony_ci u64 cpuid_fn, cpuid_reg, cpuid_value; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci cpuid_fn = get_ghcb_msr_bits(svm, 277462306a36Sopenharmony_ci GHCB_MSR_CPUID_FUNC_MASK, 277562306a36Sopenharmony_ci GHCB_MSR_CPUID_FUNC_POS); 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci /* Initialize the registers needed by the CPUID intercept */ 277862306a36Sopenharmony_ci vcpu->arch.regs[VCPU_REGS_RAX] = cpuid_fn; 277962306a36Sopenharmony_ci vcpu->arch.regs[VCPU_REGS_RCX] = 0; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci ret = svm_invoke_exit_handler(vcpu, SVM_EXIT_CPUID); 278262306a36Sopenharmony_ci if (!ret) { 278362306a36Sopenharmony_ci /* Error, keep GHCB MSR value as-is */ 278462306a36Sopenharmony_ci break; 278562306a36Sopenharmony_ci } 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci cpuid_reg = get_ghcb_msr_bits(svm, 278862306a36Sopenharmony_ci GHCB_MSR_CPUID_REG_MASK, 278962306a36Sopenharmony_ci GHCB_MSR_CPUID_REG_POS); 279062306a36Sopenharmony_ci if (cpuid_reg == 0) 279162306a36Sopenharmony_ci cpuid_value = vcpu->arch.regs[VCPU_REGS_RAX]; 279262306a36Sopenharmony_ci else if (cpuid_reg == 1) 279362306a36Sopenharmony_ci cpuid_value = vcpu->arch.regs[VCPU_REGS_RBX]; 279462306a36Sopenharmony_ci else if (cpuid_reg == 2) 279562306a36Sopenharmony_ci cpuid_value = vcpu->arch.regs[VCPU_REGS_RCX]; 279662306a36Sopenharmony_ci else 279762306a36Sopenharmony_ci cpuid_value = vcpu->arch.regs[VCPU_REGS_RDX]; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci set_ghcb_msr_bits(svm, cpuid_value, 280062306a36Sopenharmony_ci GHCB_MSR_CPUID_VALUE_MASK, 280162306a36Sopenharmony_ci GHCB_MSR_CPUID_VALUE_POS); 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci set_ghcb_msr_bits(svm, GHCB_MSR_CPUID_RESP, 280462306a36Sopenharmony_ci GHCB_MSR_INFO_MASK, 280562306a36Sopenharmony_ci GHCB_MSR_INFO_POS); 280662306a36Sopenharmony_ci break; 280762306a36Sopenharmony_ci } 280862306a36Sopenharmony_ci case GHCB_MSR_TERM_REQ: { 280962306a36Sopenharmony_ci u64 reason_set, reason_code; 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci reason_set = get_ghcb_msr_bits(svm, 281262306a36Sopenharmony_ci GHCB_MSR_TERM_REASON_SET_MASK, 281362306a36Sopenharmony_ci GHCB_MSR_TERM_REASON_SET_POS); 281462306a36Sopenharmony_ci reason_code = get_ghcb_msr_bits(svm, 281562306a36Sopenharmony_ci GHCB_MSR_TERM_REASON_MASK, 281662306a36Sopenharmony_ci GHCB_MSR_TERM_REASON_POS); 281762306a36Sopenharmony_ci pr_info("SEV-ES guest requested termination: %#llx:%#llx\n", 281862306a36Sopenharmony_ci reason_set, reason_code); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT; 282162306a36Sopenharmony_ci vcpu->run->system_event.type = KVM_SYSTEM_EVENT_SEV_TERM; 282262306a36Sopenharmony_ci vcpu->run->system_event.ndata = 1; 282362306a36Sopenharmony_ci vcpu->run->system_event.data[0] = control->ghcb_gpa; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci return 0; 282662306a36Sopenharmony_ci } 282762306a36Sopenharmony_ci default: 282862306a36Sopenharmony_ci /* Error, keep GHCB MSR value as-is */ 282962306a36Sopenharmony_ci break; 283062306a36Sopenharmony_ci } 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci trace_kvm_vmgexit_msr_protocol_exit(svm->vcpu.vcpu_id, 283362306a36Sopenharmony_ci control->ghcb_gpa, ret); 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci return ret; 283662306a36Sopenharmony_ci} 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ciint sev_handle_vmgexit(struct kvm_vcpu *vcpu) 283962306a36Sopenharmony_ci{ 284062306a36Sopenharmony_ci struct vcpu_svm *svm = to_svm(vcpu); 284162306a36Sopenharmony_ci struct vmcb_control_area *control = &svm->vmcb->control; 284262306a36Sopenharmony_ci u64 ghcb_gpa, exit_code; 284362306a36Sopenharmony_ci int ret; 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci /* Validate the GHCB */ 284662306a36Sopenharmony_ci ghcb_gpa = control->ghcb_gpa; 284762306a36Sopenharmony_ci if (ghcb_gpa & GHCB_MSR_INFO_MASK) 284862306a36Sopenharmony_ci return sev_handle_vmgexit_msr_protocol(svm); 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci if (!ghcb_gpa) { 285162306a36Sopenharmony_ci vcpu_unimpl(vcpu, "vmgexit: GHCB gpa is not set\n"); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci /* Without a GHCB, just return right back to the guest */ 285462306a36Sopenharmony_ci return 1; 285562306a36Sopenharmony_ci } 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci if (kvm_vcpu_map(vcpu, ghcb_gpa >> PAGE_SHIFT, &svm->sev_es.ghcb_map)) { 285862306a36Sopenharmony_ci /* Unable to map GHCB from guest */ 285962306a36Sopenharmony_ci vcpu_unimpl(vcpu, "vmgexit: error mapping GHCB [%#llx] from guest\n", 286062306a36Sopenharmony_ci ghcb_gpa); 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci /* Without a GHCB, just return right back to the guest */ 286362306a36Sopenharmony_ci return 1; 286462306a36Sopenharmony_ci } 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci svm->sev_es.ghcb = svm->sev_es.ghcb_map.hva; 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci trace_kvm_vmgexit_enter(vcpu->vcpu_id, svm->sev_es.ghcb); 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci sev_es_sync_from_ghcb(svm); 287162306a36Sopenharmony_ci ret = sev_es_validate_vmgexit(svm); 287262306a36Sopenharmony_ci if (ret) 287362306a36Sopenharmony_ci return ret; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 0); 287662306a36Sopenharmony_ci ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, 0); 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci exit_code = kvm_ghcb_get_sw_exit_code(control); 287962306a36Sopenharmony_ci switch (exit_code) { 288062306a36Sopenharmony_ci case SVM_VMGEXIT_MMIO_READ: 288162306a36Sopenharmony_ci ret = setup_vmgexit_scratch(svm, true, control->exit_info_2); 288262306a36Sopenharmony_ci if (ret) 288362306a36Sopenharmony_ci break; 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci ret = kvm_sev_es_mmio_read(vcpu, 288662306a36Sopenharmony_ci control->exit_info_1, 288762306a36Sopenharmony_ci control->exit_info_2, 288862306a36Sopenharmony_ci svm->sev_es.ghcb_sa); 288962306a36Sopenharmony_ci break; 289062306a36Sopenharmony_ci case SVM_VMGEXIT_MMIO_WRITE: 289162306a36Sopenharmony_ci ret = setup_vmgexit_scratch(svm, false, control->exit_info_2); 289262306a36Sopenharmony_ci if (ret) 289362306a36Sopenharmony_ci break; 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_ci ret = kvm_sev_es_mmio_write(vcpu, 289662306a36Sopenharmony_ci control->exit_info_1, 289762306a36Sopenharmony_ci control->exit_info_2, 289862306a36Sopenharmony_ci svm->sev_es.ghcb_sa); 289962306a36Sopenharmony_ci break; 290062306a36Sopenharmony_ci case SVM_VMGEXIT_NMI_COMPLETE: 290162306a36Sopenharmony_ci ++vcpu->stat.nmi_window_exits; 290262306a36Sopenharmony_ci svm->nmi_masked = false; 290362306a36Sopenharmony_ci kvm_make_request(KVM_REQ_EVENT, vcpu); 290462306a36Sopenharmony_ci ret = 1; 290562306a36Sopenharmony_ci break; 290662306a36Sopenharmony_ci case SVM_VMGEXIT_AP_HLT_LOOP: 290762306a36Sopenharmony_ci ret = kvm_emulate_ap_reset_hold(vcpu); 290862306a36Sopenharmony_ci break; 290962306a36Sopenharmony_ci case SVM_VMGEXIT_AP_JUMP_TABLE: { 291062306a36Sopenharmony_ci struct kvm_sev_info *sev = &to_kvm_svm(vcpu->kvm)->sev_info; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci switch (control->exit_info_1) { 291362306a36Sopenharmony_ci case 0: 291462306a36Sopenharmony_ci /* Set AP jump table address */ 291562306a36Sopenharmony_ci sev->ap_jump_table = control->exit_info_2; 291662306a36Sopenharmony_ci break; 291762306a36Sopenharmony_ci case 1: 291862306a36Sopenharmony_ci /* Get AP jump table address */ 291962306a36Sopenharmony_ci ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, sev->ap_jump_table); 292062306a36Sopenharmony_ci break; 292162306a36Sopenharmony_ci default: 292262306a36Sopenharmony_ci pr_err("svm: vmgexit: unsupported AP jump table request - exit_info_1=%#llx\n", 292362306a36Sopenharmony_ci control->exit_info_1); 292462306a36Sopenharmony_ci ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); 292562306a36Sopenharmony_ci ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, GHCB_ERR_INVALID_INPUT); 292662306a36Sopenharmony_ci } 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci ret = 1; 292962306a36Sopenharmony_ci break; 293062306a36Sopenharmony_ci } 293162306a36Sopenharmony_ci case SVM_VMGEXIT_UNSUPPORTED_EVENT: 293262306a36Sopenharmony_ci vcpu_unimpl(vcpu, 293362306a36Sopenharmony_ci "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n", 293462306a36Sopenharmony_ci control->exit_info_1, control->exit_info_2); 293562306a36Sopenharmony_ci ret = -EINVAL; 293662306a36Sopenharmony_ci break; 293762306a36Sopenharmony_ci default: 293862306a36Sopenharmony_ci ret = svm_invoke_exit_handler(vcpu, exit_code); 293962306a36Sopenharmony_ci } 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci return ret; 294262306a36Sopenharmony_ci} 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ciint sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in) 294562306a36Sopenharmony_ci{ 294662306a36Sopenharmony_ci int count; 294762306a36Sopenharmony_ci int bytes; 294862306a36Sopenharmony_ci int r; 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci if (svm->vmcb->control.exit_info_2 > INT_MAX) 295162306a36Sopenharmony_ci return -EINVAL; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci count = svm->vmcb->control.exit_info_2; 295462306a36Sopenharmony_ci if (unlikely(check_mul_overflow(count, size, &bytes))) 295562306a36Sopenharmony_ci return -EINVAL; 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci r = setup_vmgexit_scratch(svm, in, bytes); 295862306a36Sopenharmony_ci if (r) 295962306a36Sopenharmony_ci return r; 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci return kvm_sev_es_string_io(&svm->vcpu, size, port, svm->sev_es.ghcb_sa, 296262306a36Sopenharmony_ci count, in); 296362306a36Sopenharmony_ci} 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_cistatic void sev_es_vcpu_after_set_cpuid(struct vcpu_svm *svm) 296662306a36Sopenharmony_ci{ 296762306a36Sopenharmony_ci struct kvm_vcpu *vcpu = &svm->vcpu; 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_V_TSC_AUX)) { 297062306a36Sopenharmony_ci bool v_tsc_aux = guest_cpuid_has(vcpu, X86_FEATURE_RDTSCP) || 297162306a36Sopenharmony_ci guest_cpuid_has(vcpu, X86_FEATURE_RDPID); 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci set_msr_interception(vcpu, svm->msrpm, MSR_TSC_AUX, v_tsc_aux, v_tsc_aux); 297462306a36Sopenharmony_ci } 297562306a36Sopenharmony_ci} 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_civoid sev_vcpu_after_set_cpuid(struct vcpu_svm *svm) 297862306a36Sopenharmony_ci{ 297962306a36Sopenharmony_ci struct kvm_vcpu *vcpu = &svm->vcpu; 298062306a36Sopenharmony_ci struct kvm_cpuid_entry2 *best; 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci /* For sev guests, the memory encryption bit is not reserved in CR3. */ 298362306a36Sopenharmony_ci best = kvm_find_cpuid_entry(vcpu, 0x8000001F); 298462306a36Sopenharmony_ci if (best) 298562306a36Sopenharmony_ci vcpu->arch.reserved_gpa_bits &= ~(1UL << (best->ebx & 0x3f)); 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci if (sev_es_guest(svm->vcpu.kvm)) 298862306a36Sopenharmony_ci sev_es_vcpu_after_set_cpuid(svm); 298962306a36Sopenharmony_ci} 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_cistatic void sev_es_init_vmcb(struct vcpu_svm *svm) 299262306a36Sopenharmony_ci{ 299362306a36Sopenharmony_ci struct vmcb *vmcb = svm->vmcb01.ptr; 299462306a36Sopenharmony_ci struct kvm_vcpu *vcpu = &svm->vcpu; 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ES_ENABLE; 299762306a36Sopenharmony_ci svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK; 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci /* 300062306a36Sopenharmony_ci * An SEV-ES guest requires a VMSA area that is a separate from the 300162306a36Sopenharmony_ci * VMCB page. Do not include the encryption mask on the VMSA physical 300262306a36Sopenharmony_ci * address since hardware will access it using the guest key. Note, 300362306a36Sopenharmony_ci * the VMSA will be NULL if this vCPU is the destination for intrahost 300462306a36Sopenharmony_ci * migration, and will be copied later. 300562306a36Sopenharmony_ci */ 300662306a36Sopenharmony_ci if (svm->sev_es.vmsa) 300762306a36Sopenharmony_ci svm->vmcb->control.vmsa_pa = __pa(svm->sev_es.vmsa); 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci /* Can't intercept CR register access, HV can't modify CR registers */ 301062306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_CR0_READ); 301162306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_CR4_READ); 301262306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_CR8_READ); 301362306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_CR0_WRITE); 301462306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_CR4_WRITE); 301562306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_CR8_WRITE); 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_SELECTIVE_CR0); 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci /* Track EFER/CR register changes */ 302062306a36Sopenharmony_ci svm_set_intercept(svm, TRAP_EFER_WRITE); 302162306a36Sopenharmony_ci svm_set_intercept(svm, TRAP_CR0_WRITE); 302262306a36Sopenharmony_ci svm_set_intercept(svm, TRAP_CR4_WRITE); 302362306a36Sopenharmony_ci svm_set_intercept(svm, TRAP_CR8_WRITE); 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci vmcb->control.intercepts[INTERCEPT_DR] = 0; 302662306a36Sopenharmony_ci if (!sev_es_debug_swap_enabled) { 302762306a36Sopenharmony_ci vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ); 302862306a36Sopenharmony_ci vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE); 302962306a36Sopenharmony_ci recalc_intercepts(svm); 303062306a36Sopenharmony_ci } else { 303162306a36Sopenharmony_ci /* 303262306a36Sopenharmony_ci * Disable #DB intercept iff DebugSwap is enabled. KVM doesn't 303362306a36Sopenharmony_ci * allow debugging SEV-ES guests, and enables DebugSwap iff 303462306a36Sopenharmony_ci * NO_NESTED_DATA_BP is supported, so there's no reason to 303562306a36Sopenharmony_ci * intercept #DB when DebugSwap is enabled. For simplicity 303662306a36Sopenharmony_ci * with respect to guest debug, intercept #DB for other VMs 303762306a36Sopenharmony_ci * even if NO_NESTED_DATA_BP is supported, i.e. even if the 303862306a36Sopenharmony_ci * guest can't DoS the CPU with infinite #DB vectoring. 303962306a36Sopenharmony_ci */ 304062306a36Sopenharmony_ci clr_exception_intercept(svm, DB_VECTOR); 304162306a36Sopenharmony_ci } 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci /* Can't intercept XSETBV, HV can't modify XCR0 directly */ 304462306a36Sopenharmony_ci svm_clr_intercept(svm, INTERCEPT_XSETBV); 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci /* Clear intercepts on selected MSRs */ 304762306a36Sopenharmony_ci set_msr_interception(vcpu, svm->msrpm, MSR_EFER, 1, 1); 304862306a36Sopenharmony_ci set_msr_interception(vcpu, svm->msrpm, MSR_IA32_CR_PAT, 1, 1); 304962306a36Sopenharmony_ci set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 1, 1); 305062306a36Sopenharmony_ci set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1); 305162306a36Sopenharmony_ci set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); 305262306a36Sopenharmony_ci set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); 305362306a36Sopenharmony_ci} 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_civoid sev_init_vmcb(struct vcpu_svm *svm) 305662306a36Sopenharmony_ci{ 305762306a36Sopenharmony_ci svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE; 305862306a36Sopenharmony_ci clr_exception_intercept(svm, UD_VECTOR); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci /* 306162306a36Sopenharmony_ci * Don't intercept #GP for SEV guests, e.g. for the VMware backdoor, as 306262306a36Sopenharmony_ci * KVM can't decrypt guest memory to decode the faulting instruction. 306362306a36Sopenharmony_ci */ 306462306a36Sopenharmony_ci clr_exception_intercept(svm, GP_VECTOR); 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci if (sev_es_guest(svm->vcpu.kvm)) 306762306a36Sopenharmony_ci sev_es_init_vmcb(svm); 306862306a36Sopenharmony_ci} 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_civoid sev_es_vcpu_reset(struct vcpu_svm *svm) 307162306a36Sopenharmony_ci{ 307262306a36Sopenharmony_ci /* 307362306a36Sopenharmony_ci * Set the GHCB MSR value as per the GHCB specification when emulating 307462306a36Sopenharmony_ci * vCPU RESET for an SEV-ES guest. 307562306a36Sopenharmony_ci */ 307662306a36Sopenharmony_ci set_ghcb_msr(svm, GHCB_MSR_SEV_INFO(GHCB_VERSION_MAX, 307762306a36Sopenharmony_ci GHCB_VERSION_MIN, 307862306a36Sopenharmony_ci sev_enc_bit)); 307962306a36Sopenharmony_ci} 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_civoid sev_es_prepare_switch_to_guest(struct sev_es_save_area *hostsa) 308262306a36Sopenharmony_ci{ 308362306a36Sopenharmony_ci /* 308462306a36Sopenharmony_ci * All host state for SEV-ES guests is categorized into three swap types 308562306a36Sopenharmony_ci * based on how it is handled by hardware during a world switch: 308662306a36Sopenharmony_ci * 308762306a36Sopenharmony_ci * A: VMRUN: Host state saved in host save area 308862306a36Sopenharmony_ci * VMEXIT: Host state loaded from host save area 308962306a36Sopenharmony_ci * 309062306a36Sopenharmony_ci * B: VMRUN: Host state _NOT_ saved in host save area 309162306a36Sopenharmony_ci * VMEXIT: Host state loaded from host save area 309262306a36Sopenharmony_ci * 309362306a36Sopenharmony_ci * C: VMRUN: Host state _NOT_ saved in host save area 309462306a36Sopenharmony_ci * VMEXIT: Host state initialized to default(reset) values 309562306a36Sopenharmony_ci * 309662306a36Sopenharmony_ci * Manually save type-B state, i.e. state that is loaded by VMEXIT but 309762306a36Sopenharmony_ci * isn't saved by VMRUN, that isn't already saved by VMSAVE (performed 309862306a36Sopenharmony_ci * by common SVM code). 309962306a36Sopenharmony_ci */ 310062306a36Sopenharmony_ci hostsa->xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); 310162306a36Sopenharmony_ci hostsa->pkru = read_pkru(); 310262306a36Sopenharmony_ci hostsa->xss = host_xss; 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ci /* 310562306a36Sopenharmony_ci * If DebugSwap is enabled, debug registers are loaded but NOT saved by 310662306a36Sopenharmony_ci * the CPU (Type-B). If DebugSwap is disabled/unsupported, the CPU both 310762306a36Sopenharmony_ci * saves and loads debug registers (Type-A). 310862306a36Sopenharmony_ci */ 310962306a36Sopenharmony_ci if (sev_es_debug_swap_enabled) { 311062306a36Sopenharmony_ci hostsa->dr0 = native_get_debugreg(0); 311162306a36Sopenharmony_ci hostsa->dr1 = native_get_debugreg(1); 311262306a36Sopenharmony_ci hostsa->dr2 = native_get_debugreg(2); 311362306a36Sopenharmony_ci hostsa->dr3 = native_get_debugreg(3); 311462306a36Sopenharmony_ci hostsa->dr0_addr_mask = amd_get_dr_addr_mask(0); 311562306a36Sopenharmony_ci hostsa->dr1_addr_mask = amd_get_dr_addr_mask(1); 311662306a36Sopenharmony_ci hostsa->dr2_addr_mask = amd_get_dr_addr_mask(2); 311762306a36Sopenharmony_ci hostsa->dr3_addr_mask = amd_get_dr_addr_mask(3); 311862306a36Sopenharmony_ci } 311962306a36Sopenharmony_ci} 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_civoid sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector) 312262306a36Sopenharmony_ci{ 312362306a36Sopenharmony_ci struct vcpu_svm *svm = to_svm(vcpu); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci /* First SIPI: Use the values as initially set by the VMM */ 312662306a36Sopenharmony_ci if (!svm->sev_es.received_first_sipi) { 312762306a36Sopenharmony_ci svm->sev_es.received_first_sipi = true; 312862306a36Sopenharmony_ci return; 312962306a36Sopenharmony_ci } 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci /* 313262306a36Sopenharmony_ci * Subsequent SIPI: Return from an AP Reset Hold VMGEXIT, where 313362306a36Sopenharmony_ci * the guest will set the CS and RIP. Set SW_EXIT_INFO_2 to a 313462306a36Sopenharmony_ci * non-zero value. 313562306a36Sopenharmony_ci */ 313662306a36Sopenharmony_ci if (!svm->sev_es.ghcb) 313762306a36Sopenharmony_ci return; 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, 1); 314062306a36Sopenharmony_ci} 3141