162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * DOC: Enclave lifetime management driver for Nitro Enclaves (NE). 862306a36Sopenharmony_ci * Nitro is a hypervisor that has been developed by Amazon. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/anon_inodes.h> 1262306a36Sopenharmony_ci#include <linux/capability.h> 1362306a36Sopenharmony_ci#include <linux/cpu.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/file.h> 1662306a36Sopenharmony_ci#include <linux/hugetlb.h> 1762306a36Sopenharmony_ci#include <linux/limits.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <linux/miscdevice.h> 2062306a36Sopenharmony_ci#include <linux/mm.h> 2162306a36Sopenharmony_ci#include <linux/mman.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/mutex.h> 2462306a36Sopenharmony_ci#include <linux/nitro_enclaves.h> 2562306a36Sopenharmony_ci#include <linux/pci.h> 2662306a36Sopenharmony_ci#include <linux/poll.h> 2762306a36Sopenharmony_ci#include <linux/range.h> 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/types.h> 3062306a36Sopenharmony_ci#include <uapi/linux/vm_sockets.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "ne_misc_dev.h" 3362306a36Sopenharmony_ci#include "ne_pci_dev.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/** 3662306a36Sopenharmony_ci * NE_CPUS_SIZE - Size for max 128 CPUs, for now, in a cpu-list string, comma 3762306a36Sopenharmony_ci * separated. The NE CPU pool includes CPUs from a single NUMA 3862306a36Sopenharmony_ci * node. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci#define NE_CPUS_SIZE (512) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/** 4362306a36Sopenharmony_ci * NE_EIF_LOAD_OFFSET - The offset where to copy the Enclave Image Format (EIF) 4462306a36Sopenharmony_ci * image in enclave memory. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci#define NE_EIF_LOAD_OFFSET (8 * 1024UL * 1024UL) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/** 4962306a36Sopenharmony_ci * NE_MIN_ENCLAVE_MEM_SIZE - The minimum memory size an enclave can be launched 5062306a36Sopenharmony_ci * with. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci#define NE_MIN_ENCLAVE_MEM_SIZE (64 * 1024UL * 1024UL) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/** 5562306a36Sopenharmony_ci * NE_MIN_MEM_REGION_SIZE - The minimum size of an enclave memory region. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define NE_MIN_MEM_REGION_SIZE (2 * 1024UL * 1024UL) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/** 6062306a36Sopenharmony_ci * NE_PARENT_VM_CID - The CID for the vsock device of the primary / parent VM. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci#define NE_PARENT_VM_CID (3) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct file_operations ne_fops = { 6762306a36Sopenharmony_ci .owner = THIS_MODULE, 6862306a36Sopenharmony_ci .llseek = noop_llseek, 6962306a36Sopenharmony_ci .unlocked_ioctl = ne_ioctl, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic struct miscdevice ne_misc_dev = { 7362306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 7462306a36Sopenharmony_ci .name = "nitro_enclaves", 7562306a36Sopenharmony_ci .fops = &ne_fops, 7662306a36Sopenharmony_ci .mode = 0660, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct ne_devs ne_devs = { 8062306a36Sopenharmony_ci .ne_misc_dev = &ne_misc_dev, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * TODO: Update logic to create new sysfs entries instead of using 8562306a36Sopenharmony_ci * a kernel parameter e.g. if multiple sysfs files needed. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistatic int ne_set_kernel_param(const char *val, const struct kernel_param *kp); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const struct kernel_param_ops ne_cpu_pool_ops = { 9062306a36Sopenharmony_ci .get = param_get_string, 9162306a36Sopenharmony_ci .set = ne_set_kernel_param, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic char ne_cpus[NE_CPUS_SIZE]; 9562306a36Sopenharmony_cistatic struct kparam_string ne_cpus_arg = { 9662306a36Sopenharmony_ci .maxlen = sizeof(ne_cpus), 9762306a36Sopenharmony_ci .string = ne_cpus, 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cimodule_param_cb(ne_cpus, &ne_cpu_pool_ops, &ne_cpus_arg, 0644); 10162306a36Sopenharmony_ci/* https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html#cpu-lists */ 10262306a36Sopenharmony_ciMODULE_PARM_DESC(ne_cpus, "<cpu-list> - CPU pool used for Nitro Enclaves"); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * struct ne_cpu_pool - CPU pool used for Nitro Enclaves. 10662306a36Sopenharmony_ci * @avail_threads_per_core: Available full CPU cores to be dedicated to 10762306a36Sopenharmony_ci * enclave(s). The cpumasks from the array, indexed 10862306a36Sopenharmony_ci * by core id, contain all the threads from the 10962306a36Sopenharmony_ci * available cores, that are not set for created 11062306a36Sopenharmony_ci * enclave(s). The full CPU cores are part of the 11162306a36Sopenharmony_ci * NE CPU pool. 11262306a36Sopenharmony_ci * @mutex: Mutex for the access to the NE CPU pool. 11362306a36Sopenharmony_ci * @nr_parent_vm_cores : The size of the available threads per core array. 11462306a36Sopenharmony_ci * The total number of CPU cores available on the 11562306a36Sopenharmony_ci * primary / parent VM. 11662306a36Sopenharmony_ci * @nr_threads_per_core: The number of threads that a full CPU core has. 11762306a36Sopenharmony_ci * @numa_node: NUMA node of the CPUs in the pool. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistruct ne_cpu_pool { 12062306a36Sopenharmony_ci cpumask_var_t *avail_threads_per_core; 12162306a36Sopenharmony_ci struct mutex mutex; 12262306a36Sopenharmony_ci unsigned int nr_parent_vm_cores; 12362306a36Sopenharmony_ci unsigned int nr_threads_per_core; 12462306a36Sopenharmony_ci int numa_node; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct ne_cpu_pool ne_cpu_pool; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * struct ne_phys_contig_mem_regions - Contiguous physical memory regions. 13162306a36Sopenharmony_ci * @num: The number of regions that currently has. 13262306a36Sopenharmony_ci * @regions: The array of physical memory regions. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistruct ne_phys_contig_mem_regions { 13562306a36Sopenharmony_ci unsigned long num; 13662306a36Sopenharmony_ci struct range *regions; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/** 14062306a36Sopenharmony_ci * ne_check_enclaves_created() - Verify if at least one enclave has been created. 14162306a36Sopenharmony_ci * @void: No parameters provided. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Context: Process context. 14462306a36Sopenharmony_ci * Return: 14562306a36Sopenharmony_ci * * True if at least one enclave is created. 14662306a36Sopenharmony_ci * * False otherwise. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cistatic bool ne_check_enclaves_created(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = ne_devs.ne_pci_dev; 15162306a36Sopenharmony_ci bool ret = false; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (!ne_pci_dev) 15462306a36Sopenharmony_ci return ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci mutex_lock(&ne_pci_dev->enclaves_list_mutex); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!list_empty(&ne_pci_dev->enclaves_list)) 15962306a36Sopenharmony_ci ret = true; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci mutex_unlock(&ne_pci_dev->enclaves_list_mutex); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/** 16762306a36Sopenharmony_ci * ne_setup_cpu_pool() - Set the NE CPU pool after handling sanity checks such 16862306a36Sopenharmony_ci * as not sharing CPU cores with the primary / parent VM 16962306a36Sopenharmony_ci * or not using CPU 0, which should remain available for 17062306a36Sopenharmony_ci * the primary / parent VM. Offline the CPUs from the 17162306a36Sopenharmony_ci * pool after the checks passed. 17262306a36Sopenharmony_ci * @ne_cpu_list: The CPU list used for setting NE CPU pool. 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * Context: Process context. 17562306a36Sopenharmony_ci * Return: 17662306a36Sopenharmony_ci * * 0 on success. 17762306a36Sopenharmony_ci * * Negative return value on failure. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic int ne_setup_cpu_pool(const char *ne_cpu_list) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int core_id = -1; 18262306a36Sopenharmony_ci unsigned int cpu = 0; 18362306a36Sopenharmony_ci cpumask_var_t cpu_pool; 18462306a36Sopenharmony_ci unsigned int cpu_sibling = 0; 18562306a36Sopenharmony_ci unsigned int i = 0; 18662306a36Sopenharmony_ci int numa_node = -1; 18762306a36Sopenharmony_ci int rc = -EINVAL; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (!zalloc_cpumask_var(&cpu_pool, GFP_KERNEL)) 19062306a36Sopenharmony_ci return -ENOMEM; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci mutex_lock(&ne_cpu_pool.mutex); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci rc = cpulist_parse(ne_cpu_list, cpu_pool); 19562306a36Sopenharmony_ci if (rc < 0) { 19662306a36Sopenharmony_ci pr_err("%s: Error in cpulist parse [rc=%d]\n", ne_misc_dev.name, rc); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci goto free_pool_cpumask; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci cpu = cpumask_any(cpu_pool); 20262306a36Sopenharmony_ci if (cpu >= nr_cpu_ids) { 20362306a36Sopenharmony_ci pr_err("%s: No CPUs available in CPU pool\n", ne_misc_dev.name); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci rc = -EINVAL; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci goto free_pool_cpumask; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Check if the CPUs are online, to further get info about them 21262306a36Sopenharmony_ci * e.g. numa node, core id, siblings. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci for_each_cpu(cpu, cpu_pool) 21562306a36Sopenharmony_ci if (cpu_is_offline(cpu)) { 21662306a36Sopenharmony_ci pr_err("%s: CPU %d is offline, has to be online to get its metadata\n", 21762306a36Sopenharmony_ci ne_misc_dev.name, cpu); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci rc = -EINVAL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci goto free_pool_cpumask; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Check if the CPUs from the NE CPU pool are from the same NUMA node. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci for_each_cpu(cpu, cpu_pool) 22862306a36Sopenharmony_ci if (numa_node < 0) { 22962306a36Sopenharmony_ci numa_node = cpu_to_node(cpu); 23062306a36Sopenharmony_ci if (numa_node < 0) { 23162306a36Sopenharmony_ci pr_err("%s: Invalid NUMA node %d\n", 23262306a36Sopenharmony_ci ne_misc_dev.name, numa_node); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci rc = -EINVAL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci goto free_pool_cpumask; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci if (numa_node != cpu_to_node(cpu)) { 24062306a36Sopenharmony_ci pr_err("%s: CPUs with different NUMA nodes\n", 24162306a36Sopenharmony_ci ne_misc_dev.name); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci rc = -EINVAL; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci goto free_pool_cpumask; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * Check if CPU 0 and its siblings are included in the provided CPU pool 25162306a36Sopenharmony_ci * They should remain available for the primary / parent VM. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci if (cpumask_test_cpu(0, cpu_pool)) { 25462306a36Sopenharmony_ci pr_err("%s: CPU 0 has to remain available\n", ne_misc_dev.name); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci rc = -EINVAL; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci goto free_pool_cpumask; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for_each_cpu(cpu_sibling, topology_sibling_cpumask(0)) { 26262306a36Sopenharmony_ci if (cpumask_test_cpu(cpu_sibling, cpu_pool)) { 26362306a36Sopenharmony_ci pr_err("%s: CPU sibling %d for CPU 0 is in CPU pool\n", 26462306a36Sopenharmony_ci ne_misc_dev.name, cpu_sibling); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci rc = -EINVAL; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci goto free_pool_cpumask; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * Check if CPU siblings are included in the provided CPU pool. The 27462306a36Sopenharmony_ci * expectation is that full CPU cores are made available in the CPU pool 27562306a36Sopenharmony_ci * for enclaves. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci for_each_cpu(cpu, cpu_pool) { 27862306a36Sopenharmony_ci for_each_cpu(cpu_sibling, topology_sibling_cpumask(cpu)) { 27962306a36Sopenharmony_ci if (!cpumask_test_cpu(cpu_sibling, cpu_pool)) { 28062306a36Sopenharmony_ci pr_err("%s: CPU %d is not in CPU pool\n", 28162306a36Sopenharmony_ci ne_misc_dev.name, cpu_sibling); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci rc = -EINVAL; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci goto free_pool_cpumask; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Calculate the number of threads from a full CPU core. */ 29162306a36Sopenharmony_ci cpu = cpumask_any(cpu_pool); 29262306a36Sopenharmony_ci for_each_cpu(cpu_sibling, topology_sibling_cpumask(cpu)) 29362306a36Sopenharmony_ci ne_cpu_pool.nr_threads_per_core++; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ne_cpu_pool.nr_parent_vm_cores = nr_cpu_ids / ne_cpu_pool.nr_threads_per_core; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ne_cpu_pool.avail_threads_per_core = kcalloc(ne_cpu_pool.nr_parent_vm_cores, 29862306a36Sopenharmony_ci sizeof(*ne_cpu_pool.avail_threads_per_core), 29962306a36Sopenharmony_ci GFP_KERNEL); 30062306a36Sopenharmony_ci if (!ne_cpu_pool.avail_threads_per_core) { 30162306a36Sopenharmony_ci rc = -ENOMEM; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci goto free_pool_cpumask; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) 30762306a36Sopenharmony_ci if (!zalloc_cpumask_var(&ne_cpu_pool.avail_threads_per_core[i], GFP_KERNEL)) { 30862306a36Sopenharmony_ci rc = -ENOMEM; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci goto free_cores_cpumask; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* 31462306a36Sopenharmony_ci * Split the NE CPU pool in threads per core to keep the CPU topology 31562306a36Sopenharmony_ci * after offlining the CPUs. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci for_each_cpu(cpu, cpu_pool) { 31862306a36Sopenharmony_ci core_id = topology_core_id(cpu); 31962306a36Sopenharmony_ci if (core_id < 0 || core_id >= ne_cpu_pool.nr_parent_vm_cores) { 32062306a36Sopenharmony_ci pr_err("%s: Invalid core id %d for CPU %d\n", 32162306a36Sopenharmony_ci ne_misc_dev.name, core_id, cpu); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci rc = -EINVAL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci goto clear_cpumask; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci cpumask_set_cpu(cpu, ne_cpu_pool.avail_threads_per_core[core_id]); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * CPUs that are given to enclave(s) should not be considered online 33362306a36Sopenharmony_ci * by Linux anymore, as the hypervisor will degrade them to floating. 33462306a36Sopenharmony_ci * The physical CPUs (full cores) are carved out of the primary / parent 33562306a36Sopenharmony_ci * VM and given to the enclave VM. The same number of vCPUs would run 33662306a36Sopenharmony_ci * on less pCPUs for the primary / parent VM. 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * We offline them here, to not degrade performance and expose correct 33962306a36Sopenharmony_ci * topology to Linux and user space. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci for_each_cpu(cpu, cpu_pool) { 34262306a36Sopenharmony_ci rc = remove_cpu(cpu); 34362306a36Sopenharmony_ci if (rc != 0) { 34462306a36Sopenharmony_ci pr_err("%s: CPU %d is not offlined [rc=%d]\n", 34562306a36Sopenharmony_ci ne_misc_dev.name, cpu, rc); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci goto online_cpus; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci free_cpumask_var(cpu_pool); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ne_cpu_pool.numa_node = numa_node; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cionline_cpus: 36062306a36Sopenharmony_ci for_each_cpu(cpu, cpu_pool) 36162306a36Sopenharmony_ci add_cpu(cpu); 36262306a36Sopenharmony_ciclear_cpumask: 36362306a36Sopenharmony_ci for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) 36462306a36Sopenharmony_ci cpumask_clear(ne_cpu_pool.avail_threads_per_core[i]); 36562306a36Sopenharmony_cifree_cores_cpumask: 36662306a36Sopenharmony_ci for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) 36762306a36Sopenharmony_ci free_cpumask_var(ne_cpu_pool.avail_threads_per_core[i]); 36862306a36Sopenharmony_ci kfree(ne_cpu_pool.avail_threads_per_core); 36962306a36Sopenharmony_cifree_pool_cpumask: 37062306a36Sopenharmony_ci free_cpumask_var(cpu_pool); 37162306a36Sopenharmony_ci ne_cpu_pool.nr_parent_vm_cores = 0; 37262306a36Sopenharmony_ci ne_cpu_pool.nr_threads_per_core = 0; 37362306a36Sopenharmony_ci ne_cpu_pool.numa_node = -1; 37462306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return rc; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * ne_teardown_cpu_pool() - Online the CPUs from the NE CPU pool and cleanup the 38162306a36Sopenharmony_ci * CPU pool. 38262306a36Sopenharmony_ci * @void: No parameters provided. 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * Context: Process context. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_cistatic void ne_teardown_cpu_pool(void) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci unsigned int cpu = 0; 38962306a36Sopenharmony_ci unsigned int i = 0; 39062306a36Sopenharmony_ci int rc = -EINVAL; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci mutex_lock(&ne_cpu_pool.mutex); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!ne_cpu_pool.nr_parent_vm_cores) { 39562306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) { 40162306a36Sopenharmony_ci for_each_cpu(cpu, ne_cpu_pool.avail_threads_per_core[i]) { 40262306a36Sopenharmony_ci rc = add_cpu(cpu); 40362306a36Sopenharmony_ci if (rc != 0) 40462306a36Sopenharmony_ci pr_err("%s: CPU %d is not onlined [rc=%d]\n", 40562306a36Sopenharmony_ci ne_misc_dev.name, cpu, rc); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci cpumask_clear(ne_cpu_pool.avail_threads_per_core[i]); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci free_cpumask_var(ne_cpu_pool.avail_threads_per_core[i]); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci kfree(ne_cpu_pool.avail_threads_per_core); 41462306a36Sopenharmony_ci ne_cpu_pool.nr_parent_vm_cores = 0; 41562306a36Sopenharmony_ci ne_cpu_pool.nr_threads_per_core = 0; 41662306a36Sopenharmony_ci ne_cpu_pool.numa_node = -1; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/** 42262306a36Sopenharmony_ci * ne_set_kernel_param() - Set the NE CPU pool value via the NE kernel parameter. 42362306a36Sopenharmony_ci * @val: NE CPU pool string value. 42462306a36Sopenharmony_ci * @kp : NE kernel parameter associated with the NE CPU pool. 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Context: Process context. 42762306a36Sopenharmony_ci * Return: 42862306a36Sopenharmony_ci * * 0 on success. 42962306a36Sopenharmony_ci * * Negative return value on failure. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_cistatic int ne_set_kernel_param(const char *val, const struct kernel_param *kp) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci char error_val[] = ""; 43462306a36Sopenharmony_ci int rc = -EINVAL; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 43762306a36Sopenharmony_ci return -EPERM; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (ne_check_enclaves_created()) { 44062306a36Sopenharmony_ci pr_err("%s: The CPU pool is used by enclave(s)\n", ne_misc_dev.name); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return -EPERM; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ne_teardown_cpu_pool(); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci rc = ne_setup_cpu_pool(val); 44862306a36Sopenharmony_ci if (rc < 0) { 44962306a36Sopenharmony_ci pr_err("%s: Error in setup CPU pool [rc=%d]\n", ne_misc_dev.name, rc); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci param_set_copystring(error_val, kp); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return rc; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci rc = param_set_copystring(val, kp); 45762306a36Sopenharmony_ci if (rc < 0) { 45862306a36Sopenharmony_ci pr_err("%s: Error in param set copystring [rc=%d]\n", ne_misc_dev.name, rc); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ne_teardown_cpu_pool(); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci param_set_copystring(error_val, kp); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return rc; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/** 47162306a36Sopenharmony_ci * ne_donated_cpu() - Check if the provided CPU is already used by the enclave. 47262306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 47362306a36Sopenharmony_ci * @cpu: CPU to check if already used. 47462306a36Sopenharmony_ci * 47562306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 47662306a36Sopenharmony_ci * Return: 47762306a36Sopenharmony_ci * * True if the provided CPU is already used by the enclave. 47862306a36Sopenharmony_ci * * False otherwise. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_cistatic bool ne_donated_cpu(struct ne_enclave *ne_enclave, unsigned int cpu) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci if (cpumask_test_cpu(cpu, ne_enclave->vcpu_ids)) 48362306a36Sopenharmony_ci return true; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return false; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/** 48962306a36Sopenharmony_ci * ne_get_unused_core_from_cpu_pool() - Get the id of a full core from the 49062306a36Sopenharmony_ci * NE CPU pool. 49162306a36Sopenharmony_ci * @void: No parameters provided. 49262306a36Sopenharmony_ci * 49362306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave and 49462306a36Sopenharmony_ci * ne_cpu_pool mutexes held. 49562306a36Sopenharmony_ci * Return: 49662306a36Sopenharmony_ci * * Core id. 49762306a36Sopenharmony_ci * * -1 if no CPU core available in the pool. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic int ne_get_unused_core_from_cpu_pool(void) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int core_id = -1; 50262306a36Sopenharmony_ci unsigned int i = 0; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) 50562306a36Sopenharmony_ci if (!cpumask_empty(ne_cpu_pool.avail_threads_per_core[i])) { 50662306a36Sopenharmony_ci core_id = i; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return core_id; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/** 51562306a36Sopenharmony_ci * ne_set_enclave_threads_per_core() - Set the threads of the provided core in 51662306a36Sopenharmony_ci * the enclave data structure. 51762306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 51862306a36Sopenharmony_ci * @core_id: Core id to get its threads from the NE CPU pool. 51962306a36Sopenharmony_ci * @vcpu_id: vCPU id part of the provided core. 52062306a36Sopenharmony_ci * 52162306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave and 52262306a36Sopenharmony_ci * ne_cpu_pool mutexes held. 52362306a36Sopenharmony_ci * Return: 52462306a36Sopenharmony_ci * * 0 on success. 52562306a36Sopenharmony_ci * * Negative return value on failure. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_cistatic int ne_set_enclave_threads_per_core(struct ne_enclave *ne_enclave, 52862306a36Sopenharmony_ci int core_id, u32 vcpu_id) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci unsigned int cpu = 0; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (core_id < 0 && vcpu_id == 0) { 53362306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 53462306a36Sopenharmony_ci "No CPUs available in NE CPU pool\n"); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return -NE_ERR_NO_CPUS_AVAIL_IN_POOL; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (core_id < 0) { 54062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 54162306a36Sopenharmony_ci "CPU %d is not in NE CPU pool\n", vcpu_id); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return -NE_ERR_VCPU_NOT_IN_CPU_POOL; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (core_id >= ne_enclave->nr_parent_vm_cores) { 54762306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 54862306a36Sopenharmony_ci "Invalid core id %d - ne_enclave\n", core_id); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return -NE_ERR_VCPU_INVALID_CPU_CORE; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for_each_cpu(cpu, ne_cpu_pool.avail_threads_per_core[core_id]) 55462306a36Sopenharmony_ci cpumask_set_cpu(cpu, ne_enclave->threads_per_core[core_id]); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci cpumask_clear(ne_cpu_pool.avail_threads_per_core[core_id]); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/** 56262306a36Sopenharmony_ci * ne_get_cpu_from_cpu_pool() - Get a CPU from the NE CPU pool, either from the 56362306a36Sopenharmony_ci * remaining sibling(s) of a CPU core or the first 56462306a36Sopenharmony_ci * sibling of a new CPU core. 56562306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 56662306a36Sopenharmony_ci * @vcpu_id: vCPU to get from the NE CPU pool. 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 56962306a36Sopenharmony_ci * Return: 57062306a36Sopenharmony_ci * * 0 on success. 57162306a36Sopenharmony_ci * * Negative return value on failure. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_cistatic int ne_get_cpu_from_cpu_pool(struct ne_enclave *ne_enclave, u32 *vcpu_id) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci int core_id = -1; 57662306a36Sopenharmony_ci unsigned int cpu = 0; 57762306a36Sopenharmony_ci unsigned int i = 0; 57862306a36Sopenharmony_ci int rc = -EINVAL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * If previously allocated a thread of a core to this enclave, first 58262306a36Sopenharmony_ci * check remaining sibling(s) for new CPU allocations, so that full 58362306a36Sopenharmony_ci * CPU cores are used for the enclave. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci for (i = 0; i < ne_enclave->nr_parent_vm_cores; i++) 58662306a36Sopenharmony_ci for_each_cpu(cpu, ne_enclave->threads_per_core[i]) 58762306a36Sopenharmony_ci if (!ne_donated_cpu(ne_enclave, cpu)) { 58862306a36Sopenharmony_ci *vcpu_id = cpu; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci mutex_lock(&ne_cpu_pool.mutex); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * If no remaining siblings, get a core from the NE CPU pool and keep 59762306a36Sopenharmony_ci * track of all the threads in the enclave threads per core data structure. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci core_id = ne_get_unused_core_from_cpu_pool(); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci rc = ne_set_enclave_threads_per_core(ne_enclave, core_id, *vcpu_id); 60262306a36Sopenharmony_ci if (rc < 0) 60362306a36Sopenharmony_ci goto unlock_mutex; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci *vcpu_id = cpumask_any(ne_enclave->threads_per_core[core_id]); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci rc = 0; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ciunlock_mutex: 61062306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return rc; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/** 61662306a36Sopenharmony_ci * ne_get_vcpu_core_from_cpu_pool() - Get from the NE CPU pool the id of the 61762306a36Sopenharmony_ci * core associated with the provided vCPU. 61862306a36Sopenharmony_ci * @vcpu_id: Provided vCPU id to get its associated core id. 61962306a36Sopenharmony_ci * 62062306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave and 62162306a36Sopenharmony_ci * ne_cpu_pool mutexes held. 62262306a36Sopenharmony_ci * Return: 62362306a36Sopenharmony_ci * * Core id. 62462306a36Sopenharmony_ci * * -1 if the provided vCPU is not in the pool. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_cistatic int ne_get_vcpu_core_from_cpu_pool(u32 vcpu_id) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci int core_id = -1; 62962306a36Sopenharmony_ci unsigned int i = 0; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) 63262306a36Sopenharmony_ci if (cpumask_test_cpu(vcpu_id, ne_cpu_pool.avail_threads_per_core[i])) { 63362306a36Sopenharmony_ci core_id = i; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return core_id; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci/** 64262306a36Sopenharmony_ci * ne_check_cpu_in_cpu_pool() - Check if the given vCPU is in the available CPUs 64362306a36Sopenharmony_ci * from the pool. 64462306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 64562306a36Sopenharmony_ci * @vcpu_id: ID of the vCPU to check if available in the NE CPU pool. 64662306a36Sopenharmony_ci * 64762306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 64862306a36Sopenharmony_ci * Return: 64962306a36Sopenharmony_ci * * 0 on success. 65062306a36Sopenharmony_ci * * Negative return value on failure. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_cistatic int ne_check_cpu_in_cpu_pool(struct ne_enclave *ne_enclave, u32 vcpu_id) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci int core_id = -1; 65562306a36Sopenharmony_ci unsigned int i = 0; 65662306a36Sopenharmony_ci int rc = -EINVAL; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (ne_donated_cpu(ne_enclave, vcpu_id)) { 65962306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 66062306a36Sopenharmony_ci "CPU %d already used\n", vcpu_id); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return -NE_ERR_VCPU_ALREADY_USED; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * If previously allocated a thread of a core to this enclave, but not 66762306a36Sopenharmony_ci * the full core, first check remaining sibling(s). 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci for (i = 0; i < ne_enclave->nr_parent_vm_cores; i++) 67062306a36Sopenharmony_ci if (cpumask_test_cpu(vcpu_id, ne_enclave->threads_per_core[i])) 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci mutex_lock(&ne_cpu_pool.mutex); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* 67662306a36Sopenharmony_ci * If no remaining siblings, get from the NE CPU pool the core 67762306a36Sopenharmony_ci * associated with the vCPU and keep track of all the threads in the 67862306a36Sopenharmony_ci * enclave threads per core data structure. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci core_id = ne_get_vcpu_core_from_cpu_pool(vcpu_id); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci rc = ne_set_enclave_threads_per_core(ne_enclave, core_id, vcpu_id); 68362306a36Sopenharmony_ci if (rc < 0) 68462306a36Sopenharmony_ci goto unlock_mutex; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci rc = 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ciunlock_mutex: 68962306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return rc; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci/** 69562306a36Sopenharmony_ci * ne_add_vcpu_ioctl() - Add a vCPU to the slot associated with the current 69662306a36Sopenharmony_ci * enclave. 69762306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 69862306a36Sopenharmony_ci * @vcpu_id: ID of the CPU to be associated with the given slot, 69962306a36Sopenharmony_ci * apic id on x86. 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 70262306a36Sopenharmony_ci * Return: 70362306a36Sopenharmony_ci * * 0 on success. 70462306a36Sopenharmony_ci * * Negative return value on failure. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_cistatic int ne_add_vcpu_ioctl(struct ne_enclave *ne_enclave, u32 vcpu_id) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct ne_pci_dev_cmd_reply cmd_reply = {}; 70962306a36Sopenharmony_ci struct pci_dev *pdev = ne_devs.ne_pci_dev->pdev; 71062306a36Sopenharmony_ci int rc = -EINVAL; 71162306a36Sopenharmony_ci struct slot_add_vcpu_req slot_add_vcpu_req = {}; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (ne_enclave->mm != current->mm) 71462306a36Sopenharmony_ci return -EIO; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci slot_add_vcpu_req.slot_uid = ne_enclave->slot_uid; 71762306a36Sopenharmony_ci slot_add_vcpu_req.vcpu_id = vcpu_id; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci rc = ne_do_request(pdev, SLOT_ADD_VCPU, 72062306a36Sopenharmony_ci &slot_add_vcpu_req, sizeof(slot_add_vcpu_req), 72162306a36Sopenharmony_ci &cmd_reply, sizeof(cmd_reply)); 72262306a36Sopenharmony_ci if (rc < 0) { 72362306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 72462306a36Sopenharmony_ci "Error in slot add vCPU [rc=%d]\n", rc); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return rc; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci cpumask_set_cpu(vcpu_id, ne_enclave->vcpu_ids); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci ne_enclave->nr_vcpus++; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/** 73762306a36Sopenharmony_ci * ne_sanity_check_user_mem_region() - Sanity check the user space memory 73862306a36Sopenharmony_ci * region received during the set user 73962306a36Sopenharmony_ci * memory region ioctl call. 74062306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 74162306a36Sopenharmony_ci * @mem_region : User space memory region to be sanity checked. 74262306a36Sopenharmony_ci * 74362306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 74462306a36Sopenharmony_ci * Return: 74562306a36Sopenharmony_ci * * 0 on success. 74662306a36Sopenharmony_ci * * Negative return value on failure. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_cistatic int ne_sanity_check_user_mem_region(struct ne_enclave *ne_enclave, 74962306a36Sopenharmony_ci struct ne_user_memory_region mem_region) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct ne_mem_region *ne_mem_region = NULL; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (ne_enclave->mm != current->mm) 75462306a36Sopenharmony_ci return -EIO; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (mem_region.memory_size & (NE_MIN_MEM_REGION_SIZE - 1)) { 75762306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 75862306a36Sopenharmony_ci "User space memory size is not multiple of 2 MiB\n"); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci return -NE_ERR_INVALID_MEM_REGION_SIZE; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (!IS_ALIGNED(mem_region.userspace_addr, NE_MIN_MEM_REGION_SIZE)) { 76462306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 76562306a36Sopenharmony_ci "User space address is not 2 MiB aligned\n"); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return -NE_ERR_UNALIGNED_MEM_REGION_ADDR; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if ((mem_region.userspace_addr & (NE_MIN_MEM_REGION_SIZE - 1)) || 77162306a36Sopenharmony_ci !access_ok((void __user *)(unsigned long)mem_region.userspace_addr, 77262306a36Sopenharmony_ci mem_region.memory_size)) { 77362306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 77462306a36Sopenharmony_ci "Invalid user space address range\n"); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return -NE_ERR_INVALID_MEM_REGION_ADDR; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci list_for_each_entry(ne_mem_region, &ne_enclave->mem_regions_list, 78062306a36Sopenharmony_ci mem_region_list_entry) { 78162306a36Sopenharmony_ci u64 memory_size = ne_mem_region->memory_size; 78262306a36Sopenharmony_ci u64 userspace_addr = ne_mem_region->userspace_addr; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if ((userspace_addr <= mem_region.userspace_addr && 78562306a36Sopenharmony_ci mem_region.userspace_addr < (userspace_addr + memory_size)) || 78662306a36Sopenharmony_ci (mem_region.userspace_addr <= userspace_addr && 78762306a36Sopenharmony_ci (mem_region.userspace_addr + mem_region.memory_size) > userspace_addr)) { 78862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 78962306a36Sopenharmony_ci "User space memory region already used\n"); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return -NE_ERR_MEM_REGION_ALREADY_USED; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return 0; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci/** 79962306a36Sopenharmony_ci * ne_sanity_check_user_mem_region_page() - Sanity check a page from the user space 80062306a36Sopenharmony_ci * memory region received during the set 80162306a36Sopenharmony_ci * user memory region ioctl call. 80262306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 80362306a36Sopenharmony_ci * @mem_region_page: Page from the user space memory region to be sanity checked. 80462306a36Sopenharmony_ci * 80562306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 80662306a36Sopenharmony_ci * Return: 80762306a36Sopenharmony_ci * * 0 on success. 80862306a36Sopenharmony_ci * * Negative return value on failure. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_cistatic int ne_sanity_check_user_mem_region_page(struct ne_enclave *ne_enclave, 81162306a36Sopenharmony_ci struct page *mem_region_page) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci if (!PageHuge(mem_region_page)) { 81462306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 81562306a36Sopenharmony_ci "Not a hugetlbfs page\n"); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return -NE_ERR_MEM_NOT_HUGE_PAGE; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (page_size(mem_region_page) & (NE_MIN_MEM_REGION_SIZE - 1)) { 82162306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 82262306a36Sopenharmony_ci "Page size not multiple of 2 MiB\n"); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci return -NE_ERR_INVALID_PAGE_SIZE; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (ne_enclave->numa_node != page_to_nid(mem_region_page)) { 82862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 82962306a36Sopenharmony_ci "Page is not from NUMA node %d\n", 83062306a36Sopenharmony_ci ne_enclave->numa_node); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return -NE_ERR_MEM_DIFFERENT_NUMA_NODE; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci/** 83962306a36Sopenharmony_ci * ne_sanity_check_phys_mem_region() - Sanity check the start address and the size 84062306a36Sopenharmony_ci * of a physical memory region. 84162306a36Sopenharmony_ci * @phys_mem_region_paddr : Physical start address of the region to be sanity checked. 84262306a36Sopenharmony_ci * @phys_mem_region_size : Length of the region to be sanity checked. 84362306a36Sopenharmony_ci * 84462306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 84562306a36Sopenharmony_ci * Return: 84662306a36Sopenharmony_ci * * 0 on success. 84762306a36Sopenharmony_ci * * Negative return value on failure. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_cistatic int ne_sanity_check_phys_mem_region(u64 phys_mem_region_paddr, 85062306a36Sopenharmony_ci u64 phys_mem_region_size) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci if (phys_mem_region_size & (NE_MIN_MEM_REGION_SIZE - 1)) { 85362306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 85462306a36Sopenharmony_ci "Physical mem region size is not multiple of 2 MiB\n"); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return -EINVAL; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (!IS_ALIGNED(phys_mem_region_paddr, NE_MIN_MEM_REGION_SIZE)) { 86062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 86162306a36Sopenharmony_ci "Physical mem region address is not 2 MiB aligned\n"); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return -EINVAL; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return 0; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/** 87062306a36Sopenharmony_ci * ne_merge_phys_contig_memory_regions() - Add a memory region and merge the adjacent 87162306a36Sopenharmony_ci * regions if they are physically contiguous. 87262306a36Sopenharmony_ci * @phys_contig_regions : Private data associated with the contiguous physical memory regions. 87362306a36Sopenharmony_ci * @page_paddr : Physical start address of the region to be added. 87462306a36Sopenharmony_ci * @page_size : Length of the region to be added. 87562306a36Sopenharmony_ci * 87662306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 87762306a36Sopenharmony_ci * Return: 87862306a36Sopenharmony_ci * * 0 on success. 87962306a36Sopenharmony_ci * * Negative return value on failure. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_cistatic int 88262306a36Sopenharmony_cine_merge_phys_contig_memory_regions(struct ne_phys_contig_mem_regions *phys_contig_regions, 88362306a36Sopenharmony_ci u64 page_paddr, u64 page_size) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci unsigned long num = phys_contig_regions->num; 88662306a36Sopenharmony_ci int rc = 0; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci rc = ne_sanity_check_phys_mem_region(page_paddr, page_size); 88962306a36Sopenharmony_ci if (rc < 0) 89062306a36Sopenharmony_ci return rc; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Physically contiguous, just merge */ 89362306a36Sopenharmony_ci if (num && (phys_contig_regions->regions[num - 1].end + 1) == page_paddr) { 89462306a36Sopenharmony_ci phys_contig_regions->regions[num - 1].end += page_size; 89562306a36Sopenharmony_ci } else { 89662306a36Sopenharmony_ci phys_contig_regions->regions[num].start = page_paddr; 89762306a36Sopenharmony_ci phys_contig_regions->regions[num].end = page_paddr + page_size - 1; 89862306a36Sopenharmony_ci phys_contig_regions->num++; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return 0; 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/** 90562306a36Sopenharmony_ci * ne_set_user_memory_region_ioctl() - Add user space memory region to the slot 90662306a36Sopenharmony_ci * associated with the current enclave. 90762306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 90862306a36Sopenharmony_ci * @mem_region : User space memory region to be associated with the given slot. 90962306a36Sopenharmony_ci * 91062306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 91162306a36Sopenharmony_ci * Return: 91262306a36Sopenharmony_ci * * 0 on success. 91362306a36Sopenharmony_ci * * Negative return value on failure. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_cistatic int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, 91662306a36Sopenharmony_ci struct ne_user_memory_region mem_region) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci long gup_rc = 0; 91962306a36Sopenharmony_ci unsigned long i = 0; 92062306a36Sopenharmony_ci unsigned long max_nr_pages = 0; 92162306a36Sopenharmony_ci unsigned long memory_size = 0; 92262306a36Sopenharmony_ci struct ne_mem_region *ne_mem_region = NULL; 92362306a36Sopenharmony_ci struct pci_dev *pdev = ne_devs.ne_pci_dev->pdev; 92462306a36Sopenharmony_ci struct ne_phys_contig_mem_regions phys_contig_mem_regions = {}; 92562306a36Sopenharmony_ci int rc = -EINVAL; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci rc = ne_sanity_check_user_mem_region(ne_enclave, mem_region); 92862306a36Sopenharmony_ci if (rc < 0) 92962306a36Sopenharmony_ci return rc; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci ne_mem_region = kzalloc(sizeof(*ne_mem_region), GFP_KERNEL); 93262306a36Sopenharmony_ci if (!ne_mem_region) 93362306a36Sopenharmony_ci return -ENOMEM; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci max_nr_pages = mem_region.memory_size / NE_MIN_MEM_REGION_SIZE; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci ne_mem_region->pages = kcalloc(max_nr_pages, sizeof(*ne_mem_region->pages), 93862306a36Sopenharmony_ci GFP_KERNEL); 93962306a36Sopenharmony_ci if (!ne_mem_region->pages) { 94062306a36Sopenharmony_ci rc = -ENOMEM; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci goto free_mem_region; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci phys_contig_mem_regions.regions = kcalloc(max_nr_pages, 94662306a36Sopenharmony_ci sizeof(*phys_contig_mem_regions.regions), 94762306a36Sopenharmony_ci GFP_KERNEL); 94862306a36Sopenharmony_ci if (!phys_contig_mem_regions.regions) { 94962306a36Sopenharmony_ci rc = -ENOMEM; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci goto free_mem_region; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci do { 95562306a36Sopenharmony_ci i = ne_mem_region->nr_pages; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (i == max_nr_pages) { 95862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 95962306a36Sopenharmony_ci "Reached max nr of pages in the pages data struct\n"); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci rc = -ENOMEM; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci goto put_pages; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci gup_rc = get_user_pages_unlocked(mem_region.userspace_addr + memory_size, 1, 96762306a36Sopenharmony_ci ne_mem_region->pages + i, FOLL_GET); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (gup_rc < 0) { 97062306a36Sopenharmony_ci rc = gup_rc; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 97362306a36Sopenharmony_ci "Error in get user pages [rc=%d]\n", rc); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci goto put_pages; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci rc = ne_sanity_check_user_mem_region_page(ne_enclave, ne_mem_region->pages[i]); 97962306a36Sopenharmony_ci if (rc < 0) 98062306a36Sopenharmony_ci goto put_pages; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci rc = ne_merge_phys_contig_memory_regions(&phys_contig_mem_regions, 98362306a36Sopenharmony_ci page_to_phys(ne_mem_region->pages[i]), 98462306a36Sopenharmony_ci page_size(ne_mem_region->pages[i])); 98562306a36Sopenharmony_ci if (rc < 0) 98662306a36Sopenharmony_ci goto put_pages; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci memory_size += page_size(ne_mem_region->pages[i]); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci ne_mem_region->nr_pages++; 99162306a36Sopenharmony_ci } while (memory_size < mem_region.memory_size); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if ((ne_enclave->nr_mem_regions + phys_contig_mem_regions.num) > 99462306a36Sopenharmony_ci ne_enclave->max_mem_regions) { 99562306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 99662306a36Sopenharmony_ci "Reached max memory regions %lld\n", 99762306a36Sopenharmony_ci ne_enclave->max_mem_regions); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci rc = -NE_ERR_MEM_MAX_REGIONS; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci goto put_pages; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci for (i = 0; i < phys_contig_mem_regions.num; i++) { 100562306a36Sopenharmony_ci u64 phys_region_addr = phys_contig_mem_regions.regions[i].start; 100662306a36Sopenharmony_ci u64 phys_region_size = range_len(&phys_contig_mem_regions.regions[i]); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci rc = ne_sanity_check_phys_mem_region(phys_region_addr, phys_region_size); 100962306a36Sopenharmony_ci if (rc < 0) 101062306a36Sopenharmony_ci goto put_pages; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci ne_mem_region->memory_size = mem_region.memory_size; 101462306a36Sopenharmony_ci ne_mem_region->userspace_addr = mem_region.userspace_addr; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci list_add(&ne_mem_region->mem_region_list_entry, &ne_enclave->mem_regions_list); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci for (i = 0; i < phys_contig_mem_regions.num; i++) { 101962306a36Sopenharmony_ci struct ne_pci_dev_cmd_reply cmd_reply = {}; 102062306a36Sopenharmony_ci struct slot_add_mem_req slot_add_mem_req = {}; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci slot_add_mem_req.slot_uid = ne_enclave->slot_uid; 102362306a36Sopenharmony_ci slot_add_mem_req.paddr = phys_contig_mem_regions.regions[i].start; 102462306a36Sopenharmony_ci slot_add_mem_req.size = range_len(&phys_contig_mem_regions.regions[i]); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci rc = ne_do_request(pdev, SLOT_ADD_MEM, 102762306a36Sopenharmony_ci &slot_add_mem_req, sizeof(slot_add_mem_req), 102862306a36Sopenharmony_ci &cmd_reply, sizeof(cmd_reply)); 102962306a36Sopenharmony_ci if (rc < 0) { 103062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 103162306a36Sopenharmony_ci "Error in slot add mem [rc=%d]\n", rc); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci kfree(phys_contig_mem_regions.regions); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* 103662306a36Sopenharmony_ci * Exit here without put pages as memory regions may 103762306a36Sopenharmony_ci * already been added. 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci return rc; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci ne_enclave->mem_size += slot_add_mem_req.size; 104362306a36Sopenharmony_ci ne_enclave->nr_mem_regions++; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci kfree(phys_contig_mem_regions.regions); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return 0; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ciput_pages: 105162306a36Sopenharmony_ci for (i = 0; i < ne_mem_region->nr_pages; i++) 105262306a36Sopenharmony_ci put_page(ne_mem_region->pages[i]); 105362306a36Sopenharmony_cifree_mem_region: 105462306a36Sopenharmony_ci kfree(phys_contig_mem_regions.regions); 105562306a36Sopenharmony_ci kfree(ne_mem_region->pages); 105662306a36Sopenharmony_ci kfree(ne_mem_region); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return rc; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci/** 106262306a36Sopenharmony_ci * ne_start_enclave_ioctl() - Trigger enclave start after the enclave resources, 106362306a36Sopenharmony_ci * such as memory and CPU, have been set. 106462306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 106562306a36Sopenharmony_ci * @enclave_start_info : Enclave info that includes enclave cid and flags. 106662306a36Sopenharmony_ci * 106762306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 106862306a36Sopenharmony_ci * Return: 106962306a36Sopenharmony_ci * * 0 on success. 107062306a36Sopenharmony_ci * * Negative return value on failure. 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_cistatic int ne_start_enclave_ioctl(struct ne_enclave *ne_enclave, 107362306a36Sopenharmony_ci struct ne_enclave_start_info *enclave_start_info) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct ne_pci_dev_cmd_reply cmd_reply = {}; 107662306a36Sopenharmony_ci unsigned int cpu = 0; 107762306a36Sopenharmony_ci struct enclave_start_req enclave_start_req = {}; 107862306a36Sopenharmony_ci unsigned int i = 0; 107962306a36Sopenharmony_ci struct pci_dev *pdev = ne_devs.ne_pci_dev->pdev; 108062306a36Sopenharmony_ci int rc = -EINVAL; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (!ne_enclave->nr_mem_regions) { 108362306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 108462306a36Sopenharmony_ci "Enclave has no mem regions\n"); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci return -NE_ERR_NO_MEM_REGIONS_ADDED; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (ne_enclave->mem_size < NE_MIN_ENCLAVE_MEM_SIZE) { 109062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 109162306a36Sopenharmony_ci "Enclave memory is less than %ld\n", 109262306a36Sopenharmony_ci NE_MIN_ENCLAVE_MEM_SIZE); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci return -NE_ERR_ENCLAVE_MEM_MIN_SIZE; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (!ne_enclave->nr_vcpus) { 109862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 109962306a36Sopenharmony_ci "Enclave has no vCPUs\n"); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return -NE_ERR_NO_VCPUS_ADDED; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci for (i = 0; i < ne_enclave->nr_parent_vm_cores; i++) 110562306a36Sopenharmony_ci for_each_cpu(cpu, ne_enclave->threads_per_core[i]) 110662306a36Sopenharmony_ci if (!cpumask_test_cpu(cpu, ne_enclave->vcpu_ids)) { 110762306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 110862306a36Sopenharmony_ci "Full CPU cores not used\n"); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci return -NE_ERR_FULL_CORES_NOT_USED; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci enclave_start_req.enclave_cid = enclave_start_info->enclave_cid; 111462306a36Sopenharmony_ci enclave_start_req.flags = enclave_start_info->flags; 111562306a36Sopenharmony_ci enclave_start_req.slot_uid = ne_enclave->slot_uid; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci rc = ne_do_request(pdev, ENCLAVE_START, 111862306a36Sopenharmony_ci &enclave_start_req, sizeof(enclave_start_req), 111962306a36Sopenharmony_ci &cmd_reply, sizeof(cmd_reply)); 112062306a36Sopenharmony_ci if (rc < 0) { 112162306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 112262306a36Sopenharmony_ci "Error in enclave start [rc=%d]\n", rc); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return rc; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci ne_enclave->state = NE_STATE_RUNNING; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci enclave_start_info->enclave_cid = cmd_reply.enclave_cid; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci return 0; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci/** 113562306a36Sopenharmony_ci * ne_enclave_ioctl() - Ioctl function provided by the enclave file. 113662306a36Sopenharmony_ci * @file: File associated with this ioctl function. 113762306a36Sopenharmony_ci * @cmd: The command that is set for the ioctl call. 113862306a36Sopenharmony_ci * @arg: The argument that is provided for the ioctl call. 113962306a36Sopenharmony_ci * 114062306a36Sopenharmony_ci * Context: Process context. 114162306a36Sopenharmony_ci * Return: 114262306a36Sopenharmony_ci * * 0 on success. 114362306a36Sopenharmony_ci * * Negative return value on failure. 114462306a36Sopenharmony_ci */ 114562306a36Sopenharmony_cistatic long ne_enclave_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci struct ne_enclave *ne_enclave = file->private_data; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci switch (cmd) { 115062306a36Sopenharmony_ci case NE_ADD_VCPU: { 115162306a36Sopenharmony_ci int rc = -EINVAL; 115262306a36Sopenharmony_ci u32 vcpu_id = 0; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (copy_from_user(&vcpu_id, (void __user *)arg, sizeof(vcpu_id))) 115562306a36Sopenharmony_ci return -EFAULT; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci mutex_lock(&ne_enclave->enclave_info_mutex); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (ne_enclave->state != NE_STATE_INIT) { 116062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 116162306a36Sopenharmony_ci "Enclave is not in init state\n"); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci return -NE_ERR_NOT_IN_INIT_STATE; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (vcpu_id >= (ne_enclave->nr_parent_vm_cores * 116962306a36Sopenharmony_ci ne_enclave->nr_threads_per_core)) { 117062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 117162306a36Sopenharmony_ci "vCPU id higher than max CPU id\n"); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci return -NE_ERR_INVALID_VCPU; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (!vcpu_id) { 117962306a36Sopenharmony_ci /* Use the CPU pool for choosing a CPU for the enclave. */ 118062306a36Sopenharmony_ci rc = ne_get_cpu_from_cpu_pool(ne_enclave, &vcpu_id); 118162306a36Sopenharmony_ci if (rc < 0) { 118262306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 118362306a36Sopenharmony_ci "Error in get CPU from pool [rc=%d]\n", 118462306a36Sopenharmony_ci rc); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci return rc; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci } else { 119162306a36Sopenharmony_ci /* Check if the provided vCPU is available in the NE CPU pool. */ 119262306a36Sopenharmony_ci rc = ne_check_cpu_in_cpu_pool(ne_enclave, vcpu_id); 119362306a36Sopenharmony_ci if (rc < 0) { 119462306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 119562306a36Sopenharmony_ci "Error in check CPU %d in pool [rc=%d]\n", 119662306a36Sopenharmony_ci vcpu_id, rc); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci return rc; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci rc = ne_add_vcpu_ioctl(ne_enclave, vcpu_id); 120562306a36Sopenharmony_ci if (rc < 0) { 120662306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return rc; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &vcpu_id, sizeof(vcpu_id))) 121462306a36Sopenharmony_ci return -EFAULT; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci return 0; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci case NE_GET_IMAGE_LOAD_INFO: { 122062306a36Sopenharmony_ci struct ne_image_load_info image_load_info = {}; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (copy_from_user(&image_load_info, (void __user *)arg, sizeof(image_load_info))) 122362306a36Sopenharmony_ci return -EFAULT; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci mutex_lock(&ne_enclave->enclave_info_mutex); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (ne_enclave->state != NE_STATE_INIT) { 122862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 122962306a36Sopenharmony_ci "Enclave is not in init state\n"); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci return -NE_ERR_NOT_IN_INIT_STATE; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if (!image_load_info.flags || 123962306a36Sopenharmony_ci image_load_info.flags >= NE_IMAGE_LOAD_MAX_FLAG_VAL) { 124062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 124162306a36Sopenharmony_ci "Incorrect flag in enclave image load info\n"); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return -NE_ERR_INVALID_FLAG_VALUE; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (image_load_info.flags == NE_EIF_IMAGE) 124762306a36Sopenharmony_ci image_load_info.memory_offset = NE_EIF_LOAD_OFFSET; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &image_load_info, sizeof(image_load_info))) 125062306a36Sopenharmony_ci return -EFAULT; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci return 0; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci case NE_SET_USER_MEMORY_REGION: { 125662306a36Sopenharmony_ci struct ne_user_memory_region mem_region = {}; 125762306a36Sopenharmony_ci int rc = -EINVAL; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (copy_from_user(&mem_region, (void __user *)arg, sizeof(mem_region))) 126062306a36Sopenharmony_ci return -EFAULT; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (mem_region.flags >= NE_MEMORY_REGION_MAX_FLAG_VAL) { 126362306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 126462306a36Sopenharmony_ci "Incorrect flag for user memory region\n"); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return -NE_ERR_INVALID_FLAG_VALUE; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci mutex_lock(&ne_enclave->enclave_info_mutex); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (ne_enclave->state != NE_STATE_INIT) { 127262306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 127362306a36Sopenharmony_ci "Enclave is not in init state\n"); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci return -NE_ERR_NOT_IN_INIT_STATE; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci rc = ne_set_user_memory_region_ioctl(ne_enclave, mem_region); 128162306a36Sopenharmony_ci if (rc < 0) { 128262306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci return rc; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci return 0; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci case NE_START_ENCLAVE: { 129362306a36Sopenharmony_ci struct ne_enclave_start_info enclave_start_info = {}; 129462306a36Sopenharmony_ci int rc = -EINVAL; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci if (copy_from_user(&enclave_start_info, (void __user *)arg, 129762306a36Sopenharmony_ci sizeof(enclave_start_info))) 129862306a36Sopenharmony_ci return -EFAULT; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (enclave_start_info.flags >= NE_ENCLAVE_START_MAX_FLAG_VAL) { 130162306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 130262306a36Sopenharmony_ci "Incorrect flag in enclave start info\n"); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci return -NE_ERR_INVALID_FLAG_VALUE; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* 130862306a36Sopenharmony_ci * Do not use well-known CIDs - 0, 1, 2 - for enclaves. 130962306a36Sopenharmony_ci * VMADDR_CID_ANY = -1U 131062306a36Sopenharmony_ci * VMADDR_CID_HYPERVISOR = 0 131162306a36Sopenharmony_ci * VMADDR_CID_LOCAL = 1 131262306a36Sopenharmony_ci * VMADDR_CID_HOST = 2 131362306a36Sopenharmony_ci * Note: 0 is used as a placeholder to auto-generate an enclave CID. 131462306a36Sopenharmony_ci * http://man7.org/linux/man-pages/man7/vsock.7.html 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_ci if (enclave_start_info.enclave_cid > 0 && 131762306a36Sopenharmony_ci enclave_start_info.enclave_cid <= VMADDR_CID_HOST) { 131862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 131962306a36Sopenharmony_ci "Well-known CID value, not to be used for enclaves\n"); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci return -NE_ERR_INVALID_ENCLAVE_CID; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (enclave_start_info.enclave_cid == U32_MAX) { 132562306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 132662306a36Sopenharmony_ci "Well-known CID value, not to be used for enclaves\n"); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci return -NE_ERR_INVALID_ENCLAVE_CID; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci /* 133262306a36Sopenharmony_ci * Do not use the CID of the primary / parent VM for enclaves. 133362306a36Sopenharmony_ci */ 133462306a36Sopenharmony_ci if (enclave_start_info.enclave_cid == NE_PARENT_VM_CID) { 133562306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 133662306a36Sopenharmony_ci "CID of the parent VM, not to be used for enclaves\n"); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci return -NE_ERR_INVALID_ENCLAVE_CID; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* 64-bit CIDs are not yet supported for the vsock device. */ 134262306a36Sopenharmony_ci if (enclave_start_info.enclave_cid > U32_MAX) { 134362306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 134462306a36Sopenharmony_ci "64-bit CIDs not yet supported for the vsock device\n"); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci return -NE_ERR_INVALID_ENCLAVE_CID; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci mutex_lock(&ne_enclave->enclave_info_mutex); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci if (ne_enclave->state != NE_STATE_INIT) { 135262306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 135362306a36Sopenharmony_ci "Enclave is not in init state\n"); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return -NE_ERR_NOT_IN_INIT_STATE; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci rc = ne_start_enclave_ioctl(ne_enclave, &enclave_start_info); 136162306a36Sopenharmony_ci if (rc < 0) { 136262306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci return rc; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &enclave_start_info, 137062306a36Sopenharmony_ci sizeof(enclave_start_info))) 137162306a36Sopenharmony_ci return -EFAULT; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci return 0; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci default: 137762306a36Sopenharmony_ci return -ENOTTY; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci return 0; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci/** 138462306a36Sopenharmony_ci * ne_enclave_remove_all_mem_region_entries() - Remove all memory region entries 138562306a36Sopenharmony_ci * from the enclave data structure. 138662306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 138762306a36Sopenharmony_ci * 138862306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 138962306a36Sopenharmony_ci */ 139062306a36Sopenharmony_cistatic void ne_enclave_remove_all_mem_region_entries(struct ne_enclave *ne_enclave) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci unsigned long i = 0; 139362306a36Sopenharmony_ci struct ne_mem_region *ne_mem_region = NULL; 139462306a36Sopenharmony_ci struct ne_mem_region *ne_mem_region_tmp = NULL; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci list_for_each_entry_safe(ne_mem_region, ne_mem_region_tmp, 139762306a36Sopenharmony_ci &ne_enclave->mem_regions_list, 139862306a36Sopenharmony_ci mem_region_list_entry) { 139962306a36Sopenharmony_ci list_del(&ne_mem_region->mem_region_list_entry); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci for (i = 0; i < ne_mem_region->nr_pages; i++) 140262306a36Sopenharmony_ci put_page(ne_mem_region->pages[i]); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci kfree(ne_mem_region->pages); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci kfree(ne_mem_region); 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci/** 141162306a36Sopenharmony_ci * ne_enclave_remove_all_vcpu_id_entries() - Remove all vCPU id entries from 141262306a36Sopenharmony_ci * the enclave data structure. 141362306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 141462306a36Sopenharmony_ci * 141562306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_enclave mutex held. 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_cistatic void ne_enclave_remove_all_vcpu_id_entries(struct ne_enclave *ne_enclave) 141862306a36Sopenharmony_ci{ 141962306a36Sopenharmony_ci unsigned int cpu = 0; 142062306a36Sopenharmony_ci unsigned int i = 0; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci mutex_lock(&ne_cpu_pool.mutex); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci for (i = 0; i < ne_enclave->nr_parent_vm_cores; i++) { 142562306a36Sopenharmony_ci for_each_cpu(cpu, ne_enclave->threads_per_core[i]) 142662306a36Sopenharmony_ci /* Update the available NE CPU pool. */ 142762306a36Sopenharmony_ci cpumask_set_cpu(cpu, ne_cpu_pool.avail_threads_per_core[i]); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci free_cpumask_var(ne_enclave->threads_per_core[i]); 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci kfree(ne_enclave->threads_per_core); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci free_cpumask_var(ne_enclave->vcpu_ids); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci/** 144062306a36Sopenharmony_ci * ne_pci_dev_remove_enclave_entry() - Remove the enclave entry from the data 144162306a36Sopenharmony_ci * structure that is part of the NE PCI 144262306a36Sopenharmony_ci * device private data. 144362306a36Sopenharmony_ci * @ne_enclave : Private data associated with the current enclave. 144462306a36Sopenharmony_ci * @ne_pci_dev : Private data associated with the PCI device. 144562306a36Sopenharmony_ci * 144662306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev enclave 144762306a36Sopenharmony_ci * mutex held. 144862306a36Sopenharmony_ci */ 144962306a36Sopenharmony_cistatic void ne_pci_dev_remove_enclave_entry(struct ne_enclave *ne_enclave, 145062306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci struct ne_enclave *ne_enclave_entry = NULL; 145362306a36Sopenharmony_ci struct ne_enclave *ne_enclave_entry_tmp = NULL; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci list_for_each_entry_safe(ne_enclave_entry, ne_enclave_entry_tmp, 145662306a36Sopenharmony_ci &ne_pci_dev->enclaves_list, enclave_list_entry) { 145762306a36Sopenharmony_ci if (ne_enclave_entry->slot_uid == ne_enclave->slot_uid) { 145862306a36Sopenharmony_ci list_del(&ne_enclave_entry->enclave_list_entry); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci break; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci/** 146662306a36Sopenharmony_ci * ne_enclave_release() - Release function provided by the enclave file. 146762306a36Sopenharmony_ci * @inode: Inode associated with this file release function. 146862306a36Sopenharmony_ci * @file: File associated with this release function. 146962306a36Sopenharmony_ci * 147062306a36Sopenharmony_ci * Context: Process context. 147162306a36Sopenharmony_ci * Return: 147262306a36Sopenharmony_ci * * 0 on success. 147362306a36Sopenharmony_ci * * Negative return value on failure. 147462306a36Sopenharmony_ci */ 147562306a36Sopenharmony_cistatic int ne_enclave_release(struct inode *inode, struct file *file) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci struct ne_pci_dev_cmd_reply cmd_reply = {}; 147862306a36Sopenharmony_ci struct enclave_stop_req enclave_stop_request = {}; 147962306a36Sopenharmony_ci struct ne_enclave *ne_enclave = file->private_data; 148062306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = ne_devs.ne_pci_dev; 148162306a36Sopenharmony_ci struct pci_dev *pdev = ne_pci_dev->pdev; 148262306a36Sopenharmony_ci int rc = -EINVAL; 148362306a36Sopenharmony_ci struct slot_free_req slot_free_req = {}; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (!ne_enclave) 148662306a36Sopenharmony_ci return 0; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci /* 148962306a36Sopenharmony_ci * Early exit in case there is an error in the enclave creation logic 149062306a36Sopenharmony_ci * and fput() is called on the cleanup path. 149162306a36Sopenharmony_ci */ 149262306a36Sopenharmony_ci if (!ne_enclave->slot_uid) 149362306a36Sopenharmony_ci return 0; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci /* 149662306a36Sopenharmony_ci * Acquire the enclave list mutex before the enclave mutex 149762306a36Sopenharmony_ci * in order to avoid deadlocks with @ref ne_event_work_handler. 149862306a36Sopenharmony_ci */ 149962306a36Sopenharmony_ci mutex_lock(&ne_pci_dev->enclaves_list_mutex); 150062306a36Sopenharmony_ci mutex_lock(&ne_enclave->enclave_info_mutex); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (ne_enclave->state != NE_STATE_INIT && ne_enclave->state != NE_STATE_STOPPED) { 150362306a36Sopenharmony_ci enclave_stop_request.slot_uid = ne_enclave->slot_uid; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci rc = ne_do_request(pdev, ENCLAVE_STOP, 150662306a36Sopenharmony_ci &enclave_stop_request, sizeof(enclave_stop_request), 150762306a36Sopenharmony_ci &cmd_reply, sizeof(cmd_reply)); 150862306a36Sopenharmony_ci if (rc < 0) { 150962306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 151062306a36Sopenharmony_ci "Error in enclave stop [rc=%d]\n", rc); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci goto unlock_mutex; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci memset(&cmd_reply, 0, sizeof(cmd_reply)); 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci slot_free_req.slot_uid = ne_enclave->slot_uid; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci rc = ne_do_request(pdev, SLOT_FREE, 152162306a36Sopenharmony_ci &slot_free_req, sizeof(slot_free_req), 152262306a36Sopenharmony_ci &cmd_reply, sizeof(cmd_reply)); 152362306a36Sopenharmony_ci if (rc < 0) { 152462306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 152562306a36Sopenharmony_ci "Error in slot free [rc=%d]\n", rc); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci goto unlock_mutex; 152862306a36Sopenharmony_ci } 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci ne_pci_dev_remove_enclave_entry(ne_enclave, ne_pci_dev); 153162306a36Sopenharmony_ci ne_enclave_remove_all_mem_region_entries(ne_enclave); 153262306a36Sopenharmony_ci ne_enclave_remove_all_vcpu_id_entries(ne_enclave); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 153562306a36Sopenharmony_ci mutex_unlock(&ne_pci_dev->enclaves_list_mutex); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci kfree(ne_enclave); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci return 0; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ciunlock_mutex: 154262306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 154362306a36Sopenharmony_ci mutex_unlock(&ne_pci_dev->enclaves_list_mutex); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci return rc; 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci/** 154962306a36Sopenharmony_ci * ne_enclave_poll() - Poll functionality used for enclave out-of-band events. 155062306a36Sopenharmony_ci * @file: File associated with this poll function. 155162306a36Sopenharmony_ci * @wait: Poll table data structure. 155262306a36Sopenharmony_ci * 155362306a36Sopenharmony_ci * Context: Process context. 155462306a36Sopenharmony_ci * Return: 155562306a36Sopenharmony_ci * * Poll mask. 155662306a36Sopenharmony_ci */ 155762306a36Sopenharmony_cistatic __poll_t ne_enclave_poll(struct file *file, poll_table *wait) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci __poll_t mask = 0; 156062306a36Sopenharmony_ci struct ne_enclave *ne_enclave = file->private_data; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci poll_wait(file, &ne_enclave->eventq, wait); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (ne_enclave->has_event) 156562306a36Sopenharmony_ci mask |= EPOLLHUP; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci return mask; 156862306a36Sopenharmony_ci} 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_cistatic const struct file_operations ne_enclave_fops = { 157162306a36Sopenharmony_ci .owner = THIS_MODULE, 157262306a36Sopenharmony_ci .llseek = noop_llseek, 157362306a36Sopenharmony_ci .poll = ne_enclave_poll, 157462306a36Sopenharmony_ci .unlocked_ioctl = ne_enclave_ioctl, 157562306a36Sopenharmony_ci .release = ne_enclave_release, 157662306a36Sopenharmony_ci}; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci/** 157962306a36Sopenharmony_ci * ne_create_vm_ioctl() - Alloc slot to be associated with an enclave. Create 158062306a36Sopenharmony_ci * enclave file descriptor to be further used for enclave 158162306a36Sopenharmony_ci * resources handling e.g. memory regions and CPUs. 158262306a36Sopenharmony_ci * @ne_pci_dev : Private data associated with the PCI device. 158362306a36Sopenharmony_ci * @slot_uid: User pointer to store the generated unique slot id 158462306a36Sopenharmony_ci * associated with an enclave to. 158562306a36Sopenharmony_ci * 158662306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev enclave 158762306a36Sopenharmony_ci * mutex held. 158862306a36Sopenharmony_ci * Return: 158962306a36Sopenharmony_ci * * Enclave fd on success. 159062306a36Sopenharmony_ci * * Negative return value on failure. 159162306a36Sopenharmony_ci */ 159262306a36Sopenharmony_cistatic int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 __user *slot_uid) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci struct ne_pci_dev_cmd_reply cmd_reply = {}; 159562306a36Sopenharmony_ci int enclave_fd = -1; 159662306a36Sopenharmony_ci struct file *enclave_file = NULL; 159762306a36Sopenharmony_ci unsigned int i = 0; 159862306a36Sopenharmony_ci struct ne_enclave *ne_enclave = NULL; 159962306a36Sopenharmony_ci struct pci_dev *pdev = ne_pci_dev->pdev; 160062306a36Sopenharmony_ci int rc = -EINVAL; 160162306a36Sopenharmony_ci struct slot_alloc_req slot_alloc_req = {}; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci mutex_lock(&ne_cpu_pool.mutex); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) 160662306a36Sopenharmony_ci if (!cpumask_empty(ne_cpu_pool.avail_threads_per_core[i])) 160762306a36Sopenharmony_ci break; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (i == ne_cpu_pool.nr_parent_vm_cores) { 161062306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 161162306a36Sopenharmony_ci "No CPUs available in CPU pool\n"); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci return -NE_ERR_NO_CPUS_AVAIL_IN_POOL; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci ne_enclave = kzalloc(sizeof(*ne_enclave), GFP_KERNEL); 162162306a36Sopenharmony_ci if (!ne_enclave) 162262306a36Sopenharmony_ci return -ENOMEM; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci mutex_lock(&ne_cpu_pool.mutex); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci ne_enclave->nr_parent_vm_cores = ne_cpu_pool.nr_parent_vm_cores; 162762306a36Sopenharmony_ci ne_enclave->nr_threads_per_core = ne_cpu_pool.nr_threads_per_core; 162862306a36Sopenharmony_ci ne_enclave->numa_node = ne_cpu_pool.numa_node; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci mutex_unlock(&ne_cpu_pool.mutex); 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci ne_enclave->threads_per_core = kcalloc(ne_enclave->nr_parent_vm_cores, 163362306a36Sopenharmony_ci sizeof(*ne_enclave->threads_per_core), 163462306a36Sopenharmony_ci GFP_KERNEL); 163562306a36Sopenharmony_ci if (!ne_enclave->threads_per_core) { 163662306a36Sopenharmony_ci rc = -ENOMEM; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci goto free_ne_enclave; 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci for (i = 0; i < ne_enclave->nr_parent_vm_cores; i++) 164262306a36Sopenharmony_ci if (!zalloc_cpumask_var(&ne_enclave->threads_per_core[i], GFP_KERNEL)) { 164362306a36Sopenharmony_ci rc = -ENOMEM; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci goto free_cpumask; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (!zalloc_cpumask_var(&ne_enclave->vcpu_ids, GFP_KERNEL)) { 164962306a36Sopenharmony_ci rc = -ENOMEM; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci goto free_cpumask; 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci enclave_fd = get_unused_fd_flags(O_CLOEXEC); 165562306a36Sopenharmony_ci if (enclave_fd < 0) { 165662306a36Sopenharmony_ci rc = enclave_fd; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 165962306a36Sopenharmony_ci "Error in getting unused fd [rc=%d]\n", rc); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci goto free_cpumask; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci enclave_file = anon_inode_getfile("ne-vm", &ne_enclave_fops, ne_enclave, O_RDWR); 166562306a36Sopenharmony_ci if (IS_ERR(enclave_file)) { 166662306a36Sopenharmony_ci rc = PTR_ERR(enclave_file); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 166962306a36Sopenharmony_ci "Error in anon inode get file [rc=%d]\n", rc); 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci goto put_fd; 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci rc = ne_do_request(pdev, SLOT_ALLOC, 167562306a36Sopenharmony_ci &slot_alloc_req, sizeof(slot_alloc_req), 167662306a36Sopenharmony_ci &cmd_reply, sizeof(cmd_reply)); 167762306a36Sopenharmony_ci if (rc < 0) { 167862306a36Sopenharmony_ci dev_err_ratelimited(ne_misc_dev.this_device, 167962306a36Sopenharmony_ci "Error in slot alloc [rc=%d]\n", rc); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci goto put_file; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci init_waitqueue_head(&ne_enclave->eventq); 168562306a36Sopenharmony_ci ne_enclave->has_event = false; 168662306a36Sopenharmony_ci mutex_init(&ne_enclave->enclave_info_mutex); 168762306a36Sopenharmony_ci ne_enclave->max_mem_regions = cmd_reply.mem_regions; 168862306a36Sopenharmony_ci INIT_LIST_HEAD(&ne_enclave->mem_regions_list); 168962306a36Sopenharmony_ci ne_enclave->mm = current->mm; 169062306a36Sopenharmony_ci ne_enclave->slot_uid = cmd_reply.slot_uid; 169162306a36Sopenharmony_ci ne_enclave->state = NE_STATE_INIT; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci list_add(&ne_enclave->enclave_list_entry, &ne_pci_dev->enclaves_list); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (copy_to_user(slot_uid, &ne_enclave->slot_uid, sizeof(ne_enclave->slot_uid))) { 169662306a36Sopenharmony_ci /* 169762306a36Sopenharmony_ci * As we're holding the only reference to 'enclave_file', fput() 169862306a36Sopenharmony_ci * will call ne_enclave_release() which will do a proper cleanup 169962306a36Sopenharmony_ci * of all so far allocated resources, leaving only the unused fd 170062306a36Sopenharmony_ci * for us to free. 170162306a36Sopenharmony_ci */ 170262306a36Sopenharmony_ci fput(enclave_file); 170362306a36Sopenharmony_ci put_unused_fd(enclave_fd); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci return -EFAULT; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci fd_install(enclave_fd, enclave_file); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci return enclave_fd; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ciput_file: 171362306a36Sopenharmony_ci fput(enclave_file); 171462306a36Sopenharmony_ciput_fd: 171562306a36Sopenharmony_ci put_unused_fd(enclave_fd); 171662306a36Sopenharmony_cifree_cpumask: 171762306a36Sopenharmony_ci free_cpumask_var(ne_enclave->vcpu_ids); 171862306a36Sopenharmony_ci for (i = 0; i < ne_enclave->nr_parent_vm_cores; i++) 171962306a36Sopenharmony_ci free_cpumask_var(ne_enclave->threads_per_core[i]); 172062306a36Sopenharmony_ci kfree(ne_enclave->threads_per_core); 172162306a36Sopenharmony_cifree_ne_enclave: 172262306a36Sopenharmony_ci kfree(ne_enclave); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci return rc; 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci/** 172862306a36Sopenharmony_ci * ne_ioctl() - Ioctl function provided by the NE misc device. 172962306a36Sopenharmony_ci * @file: File associated with this ioctl function. 173062306a36Sopenharmony_ci * @cmd: The command that is set for the ioctl call. 173162306a36Sopenharmony_ci * @arg: The argument that is provided for the ioctl call. 173262306a36Sopenharmony_ci * 173362306a36Sopenharmony_ci * Context: Process context. 173462306a36Sopenharmony_ci * Return: 173562306a36Sopenharmony_ci * * Ioctl result (e.g. enclave file descriptor) on success. 173662306a36Sopenharmony_ci * * Negative return value on failure. 173762306a36Sopenharmony_ci */ 173862306a36Sopenharmony_cistatic long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci switch (cmd) { 174162306a36Sopenharmony_ci case NE_CREATE_VM: { 174262306a36Sopenharmony_ci int enclave_fd = -1; 174362306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = ne_devs.ne_pci_dev; 174462306a36Sopenharmony_ci u64 __user *slot_uid = (void __user *)arg; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci mutex_lock(&ne_pci_dev->enclaves_list_mutex); 174762306a36Sopenharmony_ci enclave_fd = ne_create_vm_ioctl(ne_pci_dev, slot_uid); 174862306a36Sopenharmony_ci mutex_unlock(&ne_pci_dev->enclaves_list_mutex); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return enclave_fd; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci default: 175462306a36Sopenharmony_ci return -ENOTTY; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci return 0; 175862306a36Sopenharmony_ci} 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci#if defined(CONFIG_NITRO_ENCLAVES_MISC_DEV_TEST) 176162306a36Sopenharmony_ci#include "ne_misc_dev_test.c" 176262306a36Sopenharmony_ci#endif 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic int __init ne_init(void) 176562306a36Sopenharmony_ci{ 176662306a36Sopenharmony_ci mutex_init(&ne_cpu_pool.mutex); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci return pci_register_driver(&ne_pci_driver); 176962306a36Sopenharmony_ci} 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_cistatic void __exit ne_exit(void) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci pci_unregister_driver(&ne_pci_driver); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci ne_teardown_cpu_pool(); 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_cimodule_init(ne_init); 177962306a36Sopenharmony_cimodule_exit(ne_exit); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ciMODULE_AUTHOR("Amazon.com, Inc. or its affiliates"); 178262306a36Sopenharmony_ciMODULE_DESCRIPTION("Nitro Enclaves Driver"); 178362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1784