162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device driver to expose SGX enclave memory to KVM guests. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(c) 2021 Intel Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/miscdevice.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/mman.h> 1162306a36Sopenharmony_ci#include <linux/sched/mm.h> 1262306a36Sopenharmony_ci#include <linux/sched/signal.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/xarray.h> 1562306a36Sopenharmony_ci#include <asm/sgx.h> 1662306a36Sopenharmony_ci#include <uapi/asm/sgx.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "encls.h" 1962306a36Sopenharmony_ci#include "sgx.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct sgx_vepc { 2262306a36Sopenharmony_ci struct xarray page_array; 2362306a36Sopenharmony_ci struct mutex lock; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * Temporary SECS pages that cannot be EREMOVE'd due to having child in other 2862306a36Sopenharmony_ci * virtual EPC instances, and the lock to protect it. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic struct mutex zombie_secs_pages_lock; 3162306a36Sopenharmony_cistatic struct list_head zombie_secs_pages; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int __sgx_vepc_fault(struct sgx_vepc *vepc, 3462306a36Sopenharmony_ci struct vm_area_struct *vma, unsigned long addr) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct sgx_epc_page *epc_page; 3762306a36Sopenharmony_ci unsigned long index, pfn; 3862306a36Sopenharmony_ci int ret; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci WARN_ON(!mutex_is_locked(&vepc->lock)); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Calculate index of EPC page in virtual EPC's page_array */ 4362306a36Sopenharmony_ci index = vma->vm_pgoff + PFN_DOWN(addr - vma->vm_start); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci epc_page = xa_load(&vepc->page_array, index); 4662306a36Sopenharmony_ci if (epc_page) 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci epc_page = sgx_alloc_epc_page(vepc, false); 5062306a36Sopenharmony_ci if (IS_ERR(epc_page)) 5162306a36Sopenharmony_ci return PTR_ERR(epc_page); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = xa_err(xa_store(&vepc->page_array, index, epc_page, GFP_KERNEL)); 5462306a36Sopenharmony_ci if (ret) 5562306a36Sopenharmony_ci goto err_free; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci pfn = PFN_DOWN(sgx_get_epc_phys_addr(epc_page)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ret = vmf_insert_pfn(vma, addr, pfn); 6062306a36Sopenharmony_ci if (ret != VM_FAULT_NOPAGE) { 6162306a36Sopenharmony_ci ret = -EFAULT; 6262306a36Sopenharmony_ci goto err_delete; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cierr_delete: 6862306a36Sopenharmony_ci xa_erase(&vepc->page_array, index); 6962306a36Sopenharmony_cierr_free: 7062306a36Sopenharmony_ci sgx_free_epc_page(epc_page); 7162306a36Sopenharmony_ci return ret; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic vm_fault_t sgx_vepc_fault(struct vm_fault *vmf) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 7762306a36Sopenharmony_ci struct sgx_vepc *vepc = vma->vm_private_data; 7862306a36Sopenharmony_ci int ret; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci mutex_lock(&vepc->lock); 8162306a36Sopenharmony_ci ret = __sgx_vepc_fault(vepc, vma, vmf->address); 8262306a36Sopenharmony_ci mutex_unlock(&vepc->lock); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!ret) 8562306a36Sopenharmony_ci return VM_FAULT_NOPAGE; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (ret == -EBUSY && (vmf->flags & FAULT_FLAG_ALLOW_RETRY)) { 8862306a36Sopenharmony_ci mmap_read_unlock(vma->vm_mm); 8962306a36Sopenharmony_ci return VM_FAULT_RETRY; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const struct vm_operations_struct sgx_vepc_vm_ops = { 9662306a36Sopenharmony_ci .fault = sgx_vepc_fault, 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int sgx_vepc_mmap(struct file *file, struct vm_area_struct *vma) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct sgx_vepc *vepc = file->private_data; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (!(vma->vm_flags & VM_SHARED)) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci vma->vm_ops = &sgx_vepc_vm_ops; 10762306a36Sopenharmony_ci /* Don't copy VMA in fork() */ 10862306a36Sopenharmony_ci vm_flags_set(vma, VM_PFNMAP | VM_IO | VM_DONTDUMP | VM_DONTCOPY); 10962306a36Sopenharmony_ci vma->vm_private_data = vepc; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int sgx_vepc_remove_page(struct sgx_epc_page *epc_page) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * Take a previously guest-owned EPC page and return it to the 11862306a36Sopenharmony_ci * general EPC page pool. 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * Guests can not be trusted to have left this page in a good 12162306a36Sopenharmony_ci * state, so run EREMOVE on the page unconditionally. In the 12262306a36Sopenharmony_ci * case that a guest properly EREMOVE'd this page, a superfluous 12362306a36Sopenharmony_ci * EREMOVE is harmless. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci return __eremove(sgx_get_epc_virt_addr(epc_page)); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int sgx_vepc_free_page(struct sgx_epc_page *epc_page) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci int ret = sgx_vepc_remove_page(epc_page); 13162306a36Sopenharmony_ci if (ret) { 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Only SGX_CHILD_PRESENT is expected, which is because of 13462306a36Sopenharmony_ci * EREMOVE'ing an SECS still with child, in which case it can 13562306a36Sopenharmony_ci * be handled by EREMOVE'ing the SECS again after all pages in 13662306a36Sopenharmony_ci * virtual EPC have been EREMOVE'd. See comments in below in 13762306a36Sopenharmony_ci * sgx_vepc_release(). 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * The user of virtual EPC (KVM) needs to guarantee there's no 14062306a36Sopenharmony_ci * logical processor is still running in the enclave in guest, 14162306a36Sopenharmony_ci * otherwise EREMOVE will get SGX_ENCLAVE_ACT which cannot be 14262306a36Sopenharmony_ci * handled here. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci WARN_ONCE(ret != SGX_CHILD_PRESENT, EREMOVE_ERROR_MESSAGE, 14562306a36Sopenharmony_ci ret, ret); 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci sgx_free_epc_page(epc_page); 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic long sgx_vepc_remove_all(struct sgx_vepc *vepc) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct sgx_epc_page *entry; 15662306a36Sopenharmony_ci unsigned long index; 15762306a36Sopenharmony_ci long failures = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci xa_for_each(&vepc->page_array, index, entry) { 16062306a36Sopenharmony_ci int ret = sgx_vepc_remove_page(entry); 16162306a36Sopenharmony_ci if (ret) { 16262306a36Sopenharmony_ci if (ret == SGX_CHILD_PRESENT) { 16362306a36Sopenharmony_ci /* The page is a SECS, userspace will retry. */ 16462306a36Sopenharmony_ci failures++; 16562306a36Sopenharmony_ci } else { 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * Report errors due to #GP or SGX_ENCLAVE_ACT; do not 16862306a36Sopenharmony_ci * WARN, as userspace can induce said failures by 16962306a36Sopenharmony_ci * calling the ioctl concurrently on multiple vEPCs or 17062306a36Sopenharmony_ci * while one or more CPUs is running the enclave. Only 17162306a36Sopenharmony_ci * a #PF on EREMOVE indicates a kernel/hardware issue. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci WARN_ON_ONCE(encls_faulted(ret) && 17462306a36Sopenharmony_ci ENCLS_TRAPNR(ret) != X86_TRAP_GP); 17562306a36Sopenharmony_ci return -EBUSY; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci cond_resched(); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * Return the number of SECS pages that failed to be removed, so 18362306a36Sopenharmony_ci * userspace knows that it has to retry. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci return failures; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int sgx_vepc_release(struct inode *inode, struct file *file) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct sgx_vepc *vepc = file->private_data; 19162306a36Sopenharmony_ci struct sgx_epc_page *epc_page, *tmp, *entry; 19262306a36Sopenharmony_ci unsigned long index; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci LIST_HEAD(secs_pages); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci xa_for_each(&vepc->page_array, index, entry) { 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * Remove all normal, child pages. sgx_vepc_free_page() 19962306a36Sopenharmony_ci * will fail if EREMOVE fails, but this is OK and expected on 20062306a36Sopenharmony_ci * SECS pages. Those can only be EREMOVE'd *after* all their 20162306a36Sopenharmony_ci * child pages. Retries below will clean them up. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci if (sgx_vepc_free_page(entry)) 20462306a36Sopenharmony_ci continue; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci xa_erase(&vepc->page_array, index); 20762306a36Sopenharmony_ci cond_resched(); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Retry EREMOVE'ing pages. This will clean up any SECS pages that 21262306a36Sopenharmony_ci * only had children in this 'epc' area. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci xa_for_each(&vepc->page_array, index, entry) { 21562306a36Sopenharmony_ci epc_page = entry; 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * An EREMOVE failure here means that the SECS page still 21862306a36Sopenharmony_ci * has children. But, since all children in this 'sgx_vepc' 21962306a36Sopenharmony_ci * have been removed, the SECS page must have a child on 22062306a36Sopenharmony_ci * another instance. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci if (sgx_vepc_free_page(epc_page)) 22362306a36Sopenharmony_ci list_add_tail(&epc_page->list, &secs_pages); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci xa_erase(&vepc->page_array, index); 22662306a36Sopenharmony_ci cond_resched(); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* 23062306a36Sopenharmony_ci * SECS pages are "pinned" by child pages, and "unpinned" once all 23162306a36Sopenharmony_ci * children have been EREMOVE'd. A child page in this instance 23262306a36Sopenharmony_ci * may have pinned an SECS page encountered in an earlier release(), 23362306a36Sopenharmony_ci * creating a zombie. Since some children were EREMOVE'd above, 23462306a36Sopenharmony_ci * try to EREMOVE all zombies in the hopes that one was unpinned. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci mutex_lock(&zombie_secs_pages_lock); 23762306a36Sopenharmony_ci list_for_each_entry_safe(epc_page, tmp, &zombie_secs_pages, list) { 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * Speculatively remove the page from the list of zombies, 24062306a36Sopenharmony_ci * if the page is successfully EREMOVE'd it will be added to 24162306a36Sopenharmony_ci * the list of free pages. If EREMOVE fails, throw the page 24262306a36Sopenharmony_ci * on the local list, which will be spliced on at the end. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci list_del(&epc_page->list); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (sgx_vepc_free_page(epc_page)) 24762306a36Sopenharmony_ci list_add_tail(&epc_page->list, &secs_pages); 24862306a36Sopenharmony_ci cond_resched(); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!list_empty(&secs_pages)) 25262306a36Sopenharmony_ci list_splice_tail(&secs_pages, &zombie_secs_pages); 25362306a36Sopenharmony_ci mutex_unlock(&zombie_secs_pages_lock); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci xa_destroy(&vepc->page_array); 25662306a36Sopenharmony_ci kfree(vepc); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int sgx_vepc_open(struct inode *inode, struct file *file) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct sgx_vepc *vepc; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci vepc = kzalloc(sizeof(struct sgx_vepc), GFP_KERNEL); 26662306a36Sopenharmony_ci if (!vepc) 26762306a36Sopenharmony_ci return -ENOMEM; 26862306a36Sopenharmony_ci mutex_init(&vepc->lock); 26962306a36Sopenharmony_ci xa_init(&vepc->page_array); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci file->private_data = vepc; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic long sgx_vepc_ioctl(struct file *file, 27762306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct sgx_vepc *vepc = file->private_data; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci switch (cmd) { 28262306a36Sopenharmony_ci case SGX_IOC_VEPC_REMOVE_ALL: 28362306a36Sopenharmony_ci if (arg) 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci return sgx_vepc_remove_all(vepc); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci return -ENOTTY; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic const struct file_operations sgx_vepc_fops = { 29362306a36Sopenharmony_ci .owner = THIS_MODULE, 29462306a36Sopenharmony_ci .open = sgx_vepc_open, 29562306a36Sopenharmony_ci .unlocked_ioctl = sgx_vepc_ioctl, 29662306a36Sopenharmony_ci .compat_ioctl = sgx_vepc_ioctl, 29762306a36Sopenharmony_ci .release = sgx_vepc_release, 29862306a36Sopenharmony_ci .mmap = sgx_vepc_mmap, 29962306a36Sopenharmony_ci}; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic struct miscdevice sgx_vepc_dev = { 30262306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 30362306a36Sopenharmony_ci .name = "sgx_vepc", 30462306a36Sopenharmony_ci .nodename = "sgx_vepc", 30562306a36Sopenharmony_ci .fops = &sgx_vepc_fops, 30662306a36Sopenharmony_ci}; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciint __init sgx_vepc_init(void) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci /* SGX virtualization requires KVM to work */ 31162306a36Sopenharmony_ci if (!cpu_feature_enabled(X86_FEATURE_VMX)) 31262306a36Sopenharmony_ci return -ENODEV; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci INIT_LIST_HEAD(&zombie_secs_pages); 31562306a36Sopenharmony_ci mutex_init(&zombie_secs_pages_lock); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return misc_register(&sgx_vepc_dev); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/** 32162306a36Sopenharmony_ci * sgx_virt_ecreate() - Run ECREATE on behalf of guest 32262306a36Sopenharmony_ci * @pageinfo: Pointer to PAGEINFO structure 32362306a36Sopenharmony_ci * @secs: Userspace pointer to SECS page 32462306a36Sopenharmony_ci * @trapnr: trap number injected to guest in case of ECREATE error 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * Run ECREATE on behalf of guest after KVM traps ECREATE for the purpose 32762306a36Sopenharmony_ci * of enforcing policies of guest's enclaves, and return the trap number 32862306a36Sopenharmony_ci * which should be injected to guest in case of any ECREATE error. 32962306a36Sopenharmony_ci * 33062306a36Sopenharmony_ci * Return: 33162306a36Sopenharmony_ci * - 0: ECREATE was successful. 33262306a36Sopenharmony_ci * - <0: on error. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ciint sgx_virt_ecreate(struct sgx_pageinfo *pageinfo, void __user *secs, 33562306a36Sopenharmony_ci int *trapnr) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int ret; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * @secs is an untrusted, userspace-provided address. It comes from 34162306a36Sopenharmony_ci * KVM and is assumed to be a valid pointer which points somewhere in 34262306a36Sopenharmony_ci * userspace. This can fault and call SGX or other fault handlers when 34362306a36Sopenharmony_ci * userspace mapping @secs doesn't exist. 34462306a36Sopenharmony_ci * 34562306a36Sopenharmony_ci * Add a WARN() to make sure @secs is already valid userspace pointer 34662306a36Sopenharmony_ci * from caller (KVM), who should already have handled invalid pointer 34762306a36Sopenharmony_ci * case (for instance, made by malicious guest). All other checks, 34862306a36Sopenharmony_ci * such as alignment of @secs, are deferred to ENCLS itself. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci if (WARN_ON_ONCE(!access_ok(secs, PAGE_SIZE))) 35162306a36Sopenharmony_ci return -EINVAL; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci __uaccess_begin(); 35462306a36Sopenharmony_ci ret = __ecreate(pageinfo, (void *)secs); 35562306a36Sopenharmony_ci __uaccess_end(); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (encls_faulted(ret)) { 35862306a36Sopenharmony_ci *trapnr = ENCLS_TRAPNR(ret); 35962306a36Sopenharmony_ci return -EFAULT; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* ECREATE doesn't return an error code, it faults or succeeds. */ 36362306a36Sopenharmony_ci WARN_ON_ONCE(ret); 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sgx_virt_ecreate); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic int __sgx_virt_einit(void __user *sigstruct, void __user *token, 36962306a36Sopenharmony_ci void __user *secs) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int ret; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* 37462306a36Sopenharmony_ci * Make sure all userspace pointers from caller (KVM) are valid. 37562306a36Sopenharmony_ci * All other checks deferred to ENCLS itself. Also see comment 37662306a36Sopenharmony_ci * for @secs in sgx_virt_ecreate(). 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci#define SGX_EINITTOKEN_SIZE 304 37962306a36Sopenharmony_ci if (WARN_ON_ONCE(!access_ok(sigstruct, sizeof(struct sgx_sigstruct)) || 38062306a36Sopenharmony_ci !access_ok(token, SGX_EINITTOKEN_SIZE) || 38162306a36Sopenharmony_ci !access_ok(secs, PAGE_SIZE))) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci __uaccess_begin(); 38562306a36Sopenharmony_ci ret = __einit((void *)sigstruct, (void *)token, (void *)secs); 38662306a36Sopenharmony_ci __uaccess_end(); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/** 39262306a36Sopenharmony_ci * sgx_virt_einit() - Run EINIT on behalf of guest 39362306a36Sopenharmony_ci * @sigstruct: Userspace pointer to SIGSTRUCT structure 39462306a36Sopenharmony_ci * @token: Userspace pointer to EINITTOKEN structure 39562306a36Sopenharmony_ci * @secs: Userspace pointer to SECS page 39662306a36Sopenharmony_ci * @lepubkeyhash: Pointer to guest's *virtual* SGX_LEPUBKEYHASH MSR values 39762306a36Sopenharmony_ci * @trapnr: trap number injected to guest in case of EINIT error 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Run EINIT on behalf of guest after KVM traps EINIT. If SGX_LC is available 40062306a36Sopenharmony_ci * in host, SGX driver may rewrite the hardware values at wish, therefore KVM 40162306a36Sopenharmony_ci * needs to update hardware values to guest's virtual MSR values in order to 40262306a36Sopenharmony_ci * ensure EINIT is executed with expected hardware values. 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * Return: 40562306a36Sopenharmony_ci * - 0: EINIT was successful. 40662306a36Sopenharmony_ci * - <0: on error. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ciint sgx_virt_einit(void __user *sigstruct, void __user *token, 40962306a36Sopenharmony_ci void __user *secs, u64 *lepubkeyhash, int *trapnr) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci int ret; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!cpu_feature_enabled(X86_FEATURE_SGX_LC)) { 41462306a36Sopenharmony_ci ret = __sgx_virt_einit(sigstruct, token, secs); 41562306a36Sopenharmony_ci } else { 41662306a36Sopenharmony_ci preempt_disable(); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci sgx_update_lepubkeyhash(lepubkeyhash); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ret = __sgx_virt_einit(sigstruct, token, secs); 42162306a36Sopenharmony_ci preempt_enable(); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Propagate up the error from the WARN_ON_ONCE in __sgx_virt_einit() */ 42562306a36Sopenharmony_ci if (ret == -EINVAL) 42662306a36Sopenharmony_ci return ret; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (encls_faulted(ret)) { 42962306a36Sopenharmony_ci *trapnr = ENCLS_TRAPNR(ret); 43062306a36Sopenharmony_ci return -EFAULT; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return ret; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sgx_virt_einit); 436