in drivers/virt/nitro_enclaves/ne_misc_dev.c [1229:1465]
static long ne_enclave_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct ne_enclave *ne_enclave = file->private_data;
switch (cmd) {
case NE_ADD_VCPU: {
int rc = -EINVAL;
u32 vcpu_id = 0;
if (copy_from_user(&vcpu_id, (void __user *)arg, sizeof(vcpu_id)))
return -EFAULT;
mutex_lock(&ne_enclave->enclave_info_mutex);
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Enclave is not in init state\n");
mutex_unlock(&ne_enclave->enclave_info_mutex);
return -NE_ERR_NOT_IN_INIT_STATE;
}
if (vcpu_id >= (ne_enclave->nr_parent_vm_cores *
ne_enclave->nr_threads_per_core)) {
dev_err_ratelimited(ne_misc_dev.this_device,
"vCPU id higher than max CPU id\n");
mutex_unlock(&ne_enclave->enclave_info_mutex);
return -NE_ERR_INVALID_VCPU;
}
if (!vcpu_id) {
/* Use the CPU pool for choosing a CPU for the enclave. */
rc = ne_get_cpu_from_cpu_pool(ne_enclave, &vcpu_id);
if (rc < 0) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Error in get CPU from pool [rc=%d]\n",
rc);
mutex_unlock(&ne_enclave->enclave_info_mutex);
return rc;
}
} else {
/* Check if the provided vCPU is available in the NE CPU pool. */
rc = ne_check_cpu_in_cpu_pool(ne_enclave, vcpu_id);
if (rc < 0) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Error in check CPU %d in pool [rc=%d]\n",
vcpu_id, rc);
mutex_unlock(&ne_enclave->enclave_info_mutex);
return rc;
}
}
rc = ne_add_vcpu_ioctl(ne_enclave, vcpu_id);
if (rc < 0) {
mutex_unlock(&ne_enclave->enclave_info_mutex);
return rc;
}
mutex_unlock(&ne_enclave->enclave_info_mutex);
if (copy_to_user((void __user *)arg, &vcpu_id, sizeof(vcpu_id)))
return -EFAULT;
return 0;
}
case NE_GET_IMAGE_LOAD_INFO: {
struct ne_image_load_info image_load_info = {};
if (copy_from_user(&image_load_info, (void __user *)arg, sizeof(image_load_info)))
return -EFAULT;
mutex_lock(&ne_enclave->enclave_info_mutex);
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Enclave is not in init state\n");
mutex_unlock(&ne_enclave->enclave_info_mutex);
return -NE_ERR_NOT_IN_INIT_STATE;
}
mutex_unlock(&ne_enclave->enclave_info_mutex);
if (!image_load_info.flags ||
image_load_info.flags >= NE_IMAGE_LOAD_MAX_FLAG_VAL) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Incorrect flag in enclave image load info\n");
return -NE_ERR_INVALID_FLAG_VALUE;
}
if (image_load_info.flags == NE_EIF_IMAGE)
image_load_info.memory_offset = NE_EIF_LOAD_OFFSET;
if (copy_to_user((void __user *)arg, &image_load_info, sizeof(image_load_info)))
return -EFAULT;
return 0;
}
case NE_SET_USER_MEMORY_REGION: {
struct ne_user_memory_region mem_region = {};
int rc = -EINVAL;
if (copy_from_user(&mem_region, (void __user *)arg, sizeof(mem_region)))
return -EFAULT;
if (mem_region.flags >= NE_MEMORY_REGION_MAX_FLAG_VAL) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Incorrect flag for user memory region\n");
return -NE_ERR_INVALID_FLAG_VALUE;
}
mutex_lock(&ne_enclave->enclave_info_mutex);
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Enclave is not in init state\n");
mutex_unlock(&ne_enclave->enclave_info_mutex);
return -NE_ERR_NOT_IN_INIT_STATE;
}
rc = ne_set_user_memory_region_ioctl(ne_enclave, mem_region);
if (rc < 0) {
mutex_unlock(&ne_enclave->enclave_info_mutex);
return rc;
}
mutex_unlock(&ne_enclave->enclave_info_mutex);
return 0;
}
case NE_START_ENCLAVE: {
struct ne_enclave_start_info enclave_start_info = {};
int rc = -EINVAL;
if (copy_from_user(&enclave_start_info, (void __user *)arg,
sizeof(enclave_start_info)))
return -EFAULT;
if (enclave_start_info.flags >= NE_ENCLAVE_START_MAX_FLAG_VAL) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Incorrect flag in enclave start info\n");
return -NE_ERR_INVALID_FLAG_VALUE;
}
/*
* Do not use well-known CIDs - 0, 1, 2 - for enclaves.
* VMADDR_CID_ANY = -1U
* VMADDR_CID_HYPERVISOR = 0
* VMADDR_CID_LOCAL = 1
* VMADDR_CID_HOST = 2
* Note: 0 is used as a placeholder to auto-generate an enclave CID.
* http://man7.org/linux/man-pages/man7/vsock.7.html
*/
if (enclave_start_info.enclave_cid > 0 &&
enclave_start_info.enclave_cid <= VMADDR_CID_HOST) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Well-known CID value, not to be used for enclaves\n");
return -NE_ERR_INVALID_ENCLAVE_CID;
}
if (enclave_start_info.enclave_cid == U32_MAX) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Well-known CID value, not to be used for enclaves\n");
return -NE_ERR_INVALID_ENCLAVE_CID;
}
/*
* Do not use the CID of the primary / parent VM for enclaves.
*/
if (enclave_start_info.enclave_cid == NE_PARENT_VM_CID) {
dev_err_ratelimited(ne_misc_dev.this_device,
"CID of the parent VM, not to be used for enclaves\n");
return -NE_ERR_INVALID_ENCLAVE_CID;
}
/* 64-bit CIDs are not yet supported for the vsock device. */
if (enclave_start_info.enclave_cid > U32_MAX) {
dev_err_ratelimited(ne_misc_dev.this_device,
"64-bit CIDs not yet supported for the vsock device\n");
return -NE_ERR_INVALID_ENCLAVE_CID;
}
mutex_lock(&ne_enclave->enclave_info_mutex);
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
"Enclave is not in init state\n");
mutex_unlock(&ne_enclave->enclave_info_mutex);
return -NE_ERR_NOT_IN_INIT_STATE;
}
rc = ne_start_enclave_ioctl(ne_enclave, &enclave_start_info);
if (rc < 0) {
mutex_unlock(&ne_enclave->enclave_info_mutex);
return rc;
}
mutex_unlock(&ne_enclave->enclave_info_mutex);
if (copy_to_user((void __user *)arg, &enclave_start_info,
sizeof(enclave_start_info)))
return -EFAULT;
return 0;
}
default:
return -ENOTTY;
}
return 0;
}