// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ /** * DOC: Enclave lifetime management driver for Nitro Enclaves (NE). * Nitro is a hypervisor that has been developed by Amazon. */ #include <linux/anon_inodes.h> #include <linux/capability.h> #include <linux/cpu.h> #include <linux/device.h> #include <linux/file.h> #include <linux/hugetlb.h> #include <linux/limits.h> #include <linux/list.h> #include <linux/miscdevice.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/nitro_enclaves.h> #include <linux/pci.h> #include <linux/poll.h> #include <linux/range.h> #include <linux/slab.h> #include <linux/types.h> #include <uapi/linux/vm_sockets.h> #include "ne_misc_dev.h" #include "ne_pci_dev.h" /** * NE_CPUS_SIZE - Size for max 128 CPUs, for now, in a cpu-list string, comma * separated. The NE CPU pool includes CPUs from a single NUMA * node. */ #define NE_CPUS_SIZE … /** * NE_EIF_LOAD_OFFSET - The offset where to copy the Enclave Image Format (EIF) * image in enclave memory. */ #define NE_EIF_LOAD_OFFSET … /** * NE_MIN_ENCLAVE_MEM_SIZE - The minimum memory size an enclave can be launched * with. */ #define NE_MIN_ENCLAVE_MEM_SIZE … /** * NE_MIN_MEM_REGION_SIZE - The minimum size of an enclave memory region. */ #define NE_MIN_MEM_REGION_SIZE … /** * NE_PARENT_VM_CID - The CID for the vsock device of the primary / parent VM. */ #define NE_PARENT_VM_CID … static long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static const struct file_operations ne_fops = …; static struct miscdevice ne_misc_dev = …; struct ne_devs ne_devs = …; /* * TODO: Update logic to create new sysfs entries instead of using * a kernel parameter e.g. if multiple sysfs files needed. */ static int ne_set_kernel_param(const char *val, const struct kernel_param *kp); static const struct kernel_param_ops ne_cpu_pool_ops = …; static char ne_cpus[NE_CPUS_SIZE]; static struct kparam_string ne_cpus_arg = …; module_param_cb(…); /* https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html#cpu-lists */ MODULE_PARM_DESC(…) …; /** * struct ne_cpu_pool - CPU pool used for Nitro Enclaves. * @avail_threads_per_core: Available full CPU cores to be dedicated to * enclave(s). The cpumasks from the array, indexed * by core id, contain all the threads from the * available cores, that are not set for created * enclave(s). The full CPU cores are part of the * NE CPU pool. * @mutex: Mutex for the access to the NE CPU pool. * @nr_parent_vm_cores : The size of the available threads per core array. * The total number of CPU cores available on the * primary / parent VM. * @nr_threads_per_core: The number of threads that a full CPU core has. * @numa_node: NUMA node of the CPUs in the pool. */ struct ne_cpu_pool { … }; static struct ne_cpu_pool ne_cpu_pool; /** * struct ne_phys_contig_mem_regions - Contiguous physical memory regions. * @num: The number of regions that currently has. * @regions: The array of physical memory regions. */ struct ne_phys_contig_mem_regions { … }; /** * ne_check_enclaves_created() - Verify if at least one enclave has been created. * @void: No parameters provided. * * Context: Process context. * Return: * * True if at least one enclave is created. * * False otherwise. */ static bool ne_check_enclaves_created(void) { … } /** * ne_setup_cpu_pool() - Set the NE CPU pool after handling sanity checks such * as not sharing CPU cores with the primary / parent VM * or not using CPU 0, which should remain available for * the primary / parent VM. Offline the CPUs from the * pool after the checks passed. * @ne_cpu_list: The CPU list used for setting NE CPU pool. * * Context: Process context. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_setup_cpu_pool(const char *ne_cpu_list) { … } /** * ne_teardown_cpu_pool() - Online the CPUs from the NE CPU pool and cleanup the * CPU pool. * @void: No parameters provided. * * Context: Process context. */ static void ne_teardown_cpu_pool(void) { … } /** * ne_set_kernel_param() - Set the NE CPU pool value via the NE kernel parameter. * @val: NE CPU pool string value. * @kp : NE kernel parameter associated with the NE CPU pool. * * Context: Process context. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_set_kernel_param(const char *val, const struct kernel_param *kp) { … } /** * ne_donated_cpu() - Check if the provided CPU is already used by the enclave. * @ne_enclave : Private data associated with the current enclave. * @cpu: CPU to check if already used. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * True if the provided CPU is already used by the enclave. * * False otherwise. */ static bool ne_donated_cpu(struct ne_enclave *ne_enclave, unsigned int cpu) { … } /** * ne_get_unused_core_from_cpu_pool() - Get the id of a full core from the * NE CPU pool. * @void: No parameters provided. * * Context: Process context. This function is called with the ne_enclave and * ne_cpu_pool mutexes held. * Return: * * Core id. * * -1 if no CPU core available in the pool. */ static int ne_get_unused_core_from_cpu_pool(void) { … } /** * ne_set_enclave_threads_per_core() - Set the threads of the provided core in * the enclave data structure. * @ne_enclave : Private data associated with the current enclave. * @core_id: Core id to get its threads from the NE CPU pool. * @vcpu_id: vCPU id part of the provided core. * * Context: Process context. This function is called with the ne_enclave and * ne_cpu_pool mutexes held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_set_enclave_threads_per_core(struct ne_enclave *ne_enclave, int core_id, u32 vcpu_id) { … } /** * ne_get_cpu_from_cpu_pool() - Get a CPU from the NE CPU pool, either from the * remaining sibling(s) of a CPU core or the first * sibling of a new CPU core. * @ne_enclave : Private data associated with the current enclave. * @vcpu_id: vCPU to get from the NE CPU pool. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_get_cpu_from_cpu_pool(struct ne_enclave *ne_enclave, u32 *vcpu_id) { … } /** * ne_get_vcpu_core_from_cpu_pool() - Get from the NE CPU pool the id of the * core associated with the provided vCPU. * @vcpu_id: Provided vCPU id to get its associated core id. * * Context: Process context. This function is called with the ne_enclave and * ne_cpu_pool mutexes held. * Return: * * Core id. * * -1 if the provided vCPU is not in the pool. */ static int ne_get_vcpu_core_from_cpu_pool(u32 vcpu_id) { … } /** * ne_check_cpu_in_cpu_pool() - Check if the given vCPU is in the available CPUs * from the pool. * @ne_enclave : Private data associated with the current enclave. * @vcpu_id: ID of the vCPU to check if available in the NE CPU pool. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_check_cpu_in_cpu_pool(struct ne_enclave *ne_enclave, u32 vcpu_id) { … } /** * ne_add_vcpu_ioctl() - Add a vCPU to the slot associated with the current * enclave. * @ne_enclave : Private data associated with the current enclave. * @vcpu_id: ID of the CPU to be associated with the given slot, * apic id on x86. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_add_vcpu_ioctl(struct ne_enclave *ne_enclave, u32 vcpu_id) { … } /** * ne_sanity_check_user_mem_region() - Sanity check the user space memory * region received during the set user * memory region ioctl call. * @ne_enclave : Private data associated with the current enclave. * @mem_region : User space memory region to be sanity checked. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_sanity_check_user_mem_region(struct ne_enclave *ne_enclave, struct ne_user_memory_region mem_region) { … } /** * ne_sanity_check_user_mem_region_page() - Sanity check a page from the user space * memory region received during the set * user memory region ioctl call. * @ne_enclave : Private data associated with the current enclave. * @mem_region_page: Page from the user space memory region to be sanity checked. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_sanity_check_user_mem_region_page(struct ne_enclave *ne_enclave, struct page *mem_region_page) { … } /** * ne_sanity_check_phys_mem_region() - Sanity check the start address and the size * of a physical memory region. * @phys_mem_region_paddr : Physical start address of the region to be sanity checked. * @phys_mem_region_size : Length of the region to be sanity checked. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_sanity_check_phys_mem_region(u64 phys_mem_region_paddr, u64 phys_mem_region_size) { … } /** * ne_merge_phys_contig_memory_regions() - Add a memory region and merge the adjacent * regions if they are physically contiguous. * @phys_contig_regions : Private data associated with the contiguous physical memory regions. * @page_paddr : Physical start address of the region to be added. * @page_size : Length of the region to be added. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_merge_phys_contig_memory_regions(struct ne_phys_contig_mem_regions *phys_contig_regions, u64 page_paddr, u64 page_size) { … } /** * ne_set_user_memory_region_ioctl() - Add user space memory region to the slot * associated with the current enclave. * @ne_enclave : Private data associated with the current enclave. * @mem_region : User space memory region to be associated with the given slot. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, struct ne_user_memory_region mem_region) { … } /** * ne_start_enclave_ioctl() - Trigger enclave start after the enclave resources, * such as memory and CPU, have been set. * @ne_enclave : Private data associated with the current enclave. * @enclave_start_info : Enclave info that includes enclave cid and flags. * * Context: Process context. This function is called with the ne_enclave mutex held. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_start_enclave_ioctl(struct ne_enclave *ne_enclave, struct ne_enclave_start_info *enclave_start_info) { … } /** * ne_enclave_ioctl() - Ioctl function provided by the enclave file. * @file: File associated with this ioctl function. * @cmd: The command that is set for the ioctl call. * @arg: The argument that is provided for the ioctl call. * * Context: Process context. * Return: * * 0 on success. * * Negative return value on failure. */ static long ne_enclave_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } /** * ne_enclave_remove_all_mem_region_entries() - Remove all memory region entries * from the enclave data structure. * @ne_enclave : Private data associated with the current enclave. * * Context: Process context. This function is called with the ne_enclave mutex held. */ static void ne_enclave_remove_all_mem_region_entries(struct ne_enclave *ne_enclave) { … } /** * ne_enclave_remove_all_vcpu_id_entries() - Remove all vCPU id entries from * the enclave data structure. * @ne_enclave : Private data associated with the current enclave. * * Context: Process context. This function is called with the ne_enclave mutex held. */ static void ne_enclave_remove_all_vcpu_id_entries(struct ne_enclave *ne_enclave) { … } /** * ne_pci_dev_remove_enclave_entry() - Remove the enclave entry from the data * structure that is part of the NE PCI * device private data. * @ne_enclave : Private data associated with the current enclave. * @ne_pci_dev : Private data associated with the PCI device. * * Context: Process context. This function is called with the ne_pci_dev enclave * mutex held. */ static void ne_pci_dev_remove_enclave_entry(struct ne_enclave *ne_enclave, struct ne_pci_dev *ne_pci_dev) { … } /** * ne_enclave_release() - Release function provided by the enclave file. * @inode: Inode associated with this file release function. * @file: File associated with this release function. * * Context: Process context. * Return: * * 0 on success. * * Negative return value on failure. */ static int ne_enclave_release(struct inode *inode, struct file *file) { … } /** * ne_enclave_poll() - Poll functionality used for enclave out-of-band events. * @file: File associated with this poll function. * @wait: Poll table data structure. * * Context: Process context. * Return: * * Poll mask. */ static __poll_t ne_enclave_poll(struct file *file, poll_table *wait) { … } static const struct file_operations ne_enclave_fops = …; /** * ne_create_vm_ioctl() - Alloc slot to be associated with an enclave. Create * enclave file descriptor to be further used for enclave * resources handling e.g. memory regions and CPUs. * @ne_pci_dev : Private data associated with the PCI device. * @slot_uid: User pointer to store the generated unique slot id * associated with an enclave to. * * Context: Process context. This function is called with the ne_pci_dev enclave * mutex held. * Return: * * Enclave fd on success. * * Negative return value on failure. */ static int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 __user *slot_uid) { … } /** * ne_ioctl() - Ioctl function provided by the NE misc device. * @file: File associated with this ioctl function. * @cmd: The command that is set for the ioctl call. * @arg: The argument that is provided for the ioctl call. * * Context: Process context. * Return: * * Ioctl result (e.g. enclave file descriptor) on success. * * Negative return value on failure. */ static long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } #if defined(CONFIG_NITRO_ENCLAVES_MISC_DEV_TEST) #include "ne_misc_dev_test.c" #endif static int __init ne_init(void) { … } static void __exit ne_exit(void) { … } module_init(…) …; module_exit(ne_exit); MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;