162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VMware VMCI Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/vmw_vmci_defs.h> 962306a36Sopenharmony_ci#include <linux/vmw_vmci_api.h> 1062306a36Sopenharmony_ci#include <linux/miscdevice.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/highmem.h> 1362306a36Sopenharmony_ci#include <linux/atomic.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci#include <linux/cred.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/file.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/poll.h> 2362306a36Sopenharmony_ci#include <linux/pci.h> 2462306a36Sopenharmony_ci#include <linux/smp.h> 2562306a36Sopenharmony_ci#include <linux/fs.h> 2662306a36Sopenharmony_ci#include <linux/io.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "vmci_handle_array.h" 2962306a36Sopenharmony_ci#include "vmci_queue_pair.h" 3062306a36Sopenharmony_ci#include "vmci_datagram.h" 3162306a36Sopenharmony_ci#include "vmci_doorbell.h" 3262306a36Sopenharmony_ci#include "vmci_resource.h" 3362306a36Sopenharmony_ci#include "vmci_context.h" 3462306a36Sopenharmony_ci#include "vmci_driver.h" 3562306a36Sopenharmony_ci#include "vmci_event.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define VMCI_UTIL_NUM_RESOURCES 1 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cienum { 4062306a36Sopenharmony_ci VMCI_NOTIFY_RESOURCE_QUEUE_PAIR = 0, 4162306a36Sopenharmony_ci VMCI_NOTIFY_RESOURCE_DOOR_BELL = 1, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cienum { 4562306a36Sopenharmony_ci VMCI_NOTIFY_RESOURCE_ACTION_NOTIFY = 0, 4662306a36Sopenharmony_ci VMCI_NOTIFY_RESOURCE_ACTION_CREATE = 1, 4762306a36Sopenharmony_ci VMCI_NOTIFY_RESOURCE_ACTION_DESTROY = 2, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * VMCI driver initialization. This block can also be used to 5262306a36Sopenharmony_ci * pass initial group membership etc. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistruct vmci_init_blk { 5562306a36Sopenharmony_ci u32 cid; 5662306a36Sopenharmony_ci u32 flags; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* VMCIqueue_pairAllocInfo_VMToVM */ 6062306a36Sopenharmony_cistruct vmci_qp_alloc_info_vmvm { 6162306a36Sopenharmony_ci struct vmci_handle handle; 6262306a36Sopenharmony_ci u32 peer; 6362306a36Sopenharmony_ci u32 flags; 6462306a36Sopenharmony_ci u64 produce_size; 6562306a36Sopenharmony_ci u64 consume_size; 6662306a36Sopenharmony_ci u64 produce_page_file; /* User VA. */ 6762306a36Sopenharmony_ci u64 consume_page_file; /* User VA. */ 6862306a36Sopenharmony_ci u64 produce_page_file_size; /* Size of the file name array. */ 6962306a36Sopenharmony_ci u64 consume_page_file_size; /* Size of the file name array. */ 7062306a36Sopenharmony_ci s32 result; 7162306a36Sopenharmony_ci u32 _pad; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* VMCISetNotifyInfo: Used to pass notify flag's address to the host driver. */ 7562306a36Sopenharmony_cistruct vmci_set_notify_info { 7662306a36Sopenharmony_ci u64 notify_uva; 7762306a36Sopenharmony_ci s32 result; 7862306a36Sopenharmony_ci u32 _pad; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * Per-instance host state 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistruct vmci_host_dev { 8562306a36Sopenharmony_ci struct vmci_ctx *context; 8662306a36Sopenharmony_ci int user_version; 8762306a36Sopenharmony_ci enum vmci_obj_type ct_type; 8862306a36Sopenharmony_ci struct mutex lock; /* Mutex lock for vmci context access */ 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic struct vmci_ctx *host_context; 9262306a36Sopenharmony_cistatic bool vmci_host_device_initialized; 9362306a36Sopenharmony_cistatic atomic_t vmci_host_active_users = ATOMIC_INIT(0); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Determines whether the VMCI host personality is 9762306a36Sopenharmony_ci * available. Since the core functionality of the host driver is 9862306a36Sopenharmony_ci * always present, all guests could possibly use the host 9962306a36Sopenharmony_ci * personality. However, to minimize the deviation from the 10062306a36Sopenharmony_ci * pre-unified driver state of affairs, we only consider the host 10162306a36Sopenharmony_ci * device active if there is no active guest device or if there 10262306a36Sopenharmony_ci * are VMX'en with active VMCI contexts using the host device. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cibool vmci_host_code_active(void) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci return vmci_host_device_initialized && 10762306a36Sopenharmony_ci (!vmci_guest_code_active() || 10862306a36Sopenharmony_ci atomic_read(&vmci_host_active_users) > 0); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciint vmci_host_users(void) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci return atomic_read(&vmci_host_active_users); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * Called on open of /dev/vmci. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistatic int vmci_host_open(struct inode *inode, struct file *filp) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct vmci_host_dev *vmci_host_dev; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci vmci_host_dev = kzalloc(sizeof(struct vmci_host_dev), GFP_KERNEL); 12462306a36Sopenharmony_ci if (vmci_host_dev == NULL) 12562306a36Sopenharmony_ci return -ENOMEM; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci vmci_host_dev->ct_type = VMCIOBJ_NOT_SET; 12862306a36Sopenharmony_ci mutex_init(&vmci_host_dev->lock); 12962306a36Sopenharmony_ci filp->private_data = vmci_host_dev; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Called on close of /dev/vmci, most often when the process 13662306a36Sopenharmony_ci * exits. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic int vmci_host_close(struct inode *inode, struct file *filp) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct vmci_host_dev *vmci_host_dev = filp->private_data; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (vmci_host_dev->ct_type == VMCIOBJ_CONTEXT) { 14362306a36Sopenharmony_ci vmci_ctx_destroy(vmci_host_dev->context); 14462306a36Sopenharmony_ci vmci_host_dev->context = NULL; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * The number of active contexts is used to track whether any 14862306a36Sopenharmony_ci * VMX'en are using the host personality. It is incremented when 14962306a36Sopenharmony_ci * a context is created through the IOCTL_VMCI_INIT_CONTEXT 15062306a36Sopenharmony_ci * ioctl. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci atomic_dec(&vmci_host_active_users); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci vmci_host_dev->ct_type = VMCIOBJ_NOT_SET; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci kfree(vmci_host_dev); 15762306a36Sopenharmony_ci filp->private_data = NULL; 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * This is used to wake up the VMX when a VMCI call arrives, or 16362306a36Sopenharmony_ci * to wake up select() or poll() at the next clock tick. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_cistatic __poll_t vmci_host_poll(struct file *filp, poll_table *wait) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct vmci_host_dev *vmci_host_dev = filp->private_data; 16862306a36Sopenharmony_ci struct vmci_ctx *context; 16962306a36Sopenharmony_ci __poll_t mask = 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (vmci_host_dev->ct_type == VMCIOBJ_CONTEXT) { 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * Read context only if ct_type == VMCIOBJ_CONTEXT to make 17462306a36Sopenharmony_ci * sure that context is initialized 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci context = vmci_host_dev->context; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Check for VMCI calls to this VM context. */ 17962306a36Sopenharmony_ci if (wait) 18062306a36Sopenharmony_ci poll_wait(filp, &context->host_context.wait_queue, 18162306a36Sopenharmony_ci wait); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci spin_lock(&context->lock); 18462306a36Sopenharmony_ci if (context->pending_datagrams > 0 || 18562306a36Sopenharmony_ci vmci_handle_arr_get_size( 18662306a36Sopenharmony_ci context->pending_doorbell_array) > 0) { 18762306a36Sopenharmony_ci mask = EPOLLIN; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci spin_unlock(&context->lock); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci return mask; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * Copies the handles of a handle array into a user buffer, and 19662306a36Sopenharmony_ci * returns the new length in userBufferSize. If the copy to the 19762306a36Sopenharmony_ci * user buffer fails, the functions still returns VMCI_SUCCESS, 19862306a36Sopenharmony_ci * but retval != 0. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic int drv_cp_harray_to_user(void __user *user_buf_uva, 20162306a36Sopenharmony_ci u64 *user_buf_size, 20262306a36Sopenharmony_ci struct vmci_handle_arr *handle_array, 20362306a36Sopenharmony_ci int *retval) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u32 array_size = 0; 20662306a36Sopenharmony_ci struct vmci_handle *handles; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (handle_array) 20962306a36Sopenharmony_ci array_size = vmci_handle_arr_get_size(handle_array); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (array_size * sizeof(*handles) > *user_buf_size) 21262306a36Sopenharmony_ci return VMCI_ERROR_MORE_DATA; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci *user_buf_size = array_size * sizeof(*handles); 21562306a36Sopenharmony_ci if (*user_buf_size) 21662306a36Sopenharmony_ci *retval = copy_to_user(user_buf_uva, 21762306a36Sopenharmony_ci vmci_handle_arr_get_handles 21862306a36Sopenharmony_ci (handle_array), *user_buf_size); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return VMCI_SUCCESS; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/* 22462306a36Sopenharmony_ci * Sets up a given context for notify to work. Maps the notify 22562306a36Sopenharmony_ci * boolean in user VA into kernel space. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_cistatic int vmci_host_setup_notify(struct vmci_ctx *context, 22862306a36Sopenharmony_ci unsigned long uva) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int retval; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (context->notify_page) { 23362306a36Sopenharmony_ci pr_devel("%s: Notify mechanism is already set up\n", __func__); 23462306a36Sopenharmony_ci return VMCI_ERROR_DUPLICATE_ENTRY; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * We are using 'bool' internally, but let's make sure we explicit 23962306a36Sopenharmony_ci * about the size. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(bool) != sizeof(u8)); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * Lock physical page backing a given user VA. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ci retval = get_user_pages_fast(uva, 1, FOLL_WRITE, &context->notify_page); 24762306a36Sopenharmony_ci if (retval != 1) { 24862306a36Sopenharmony_ci context->notify_page = NULL; 24962306a36Sopenharmony_ci return VMCI_ERROR_GENERIC; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci if (context->notify_page == NULL) 25262306a36Sopenharmony_ci return VMCI_ERROR_UNAVAILABLE; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * Map the locked page and set up notify pointer. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci context->notify = kmap(context->notify_page) + (uva & (PAGE_SIZE - 1)); 25862306a36Sopenharmony_ci vmci_ctx_check_signal_notify(context); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return VMCI_SUCCESS; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int vmci_host_get_version(struct vmci_host_dev *vmci_host_dev, 26462306a36Sopenharmony_ci unsigned int cmd, void __user *uptr) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci if (cmd == IOCTL_VMCI_VERSION2) { 26762306a36Sopenharmony_ci int __user *vptr = uptr; 26862306a36Sopenharmony_ci if (get_user(vmci_host_dev->user_version, vptr)) 26962306a36Sopenharmony_ci return -EFAULT; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * The basic logic here is: 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * If the user sends in a version of 0 tell it our version. 27662306a36Sopenharmony_ci * If the user didn't send in a version, tell it our version. 27762306a36Sopenharmony_ci * If the user sent in an old version, tell it -its- version. 27862306a36Sopenharmony_ci * If the user sent in an newer version, tell it our version. 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * The rationale behind telling the caller its version is that 28162306a36Sopenharmony_ci * Workstation 6.5 required that VMX and VMCI kernel module were 28262306a36Sopenharmony_ci * version sync'd. All new VMX users will be programmed to 28362306a36Sopenharmony_ci * handle the VMCI kernel module version. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (vmci_host_dev->user_version > 0 && 28762306a36Sopenharmony_ci vmci_host_dev->user_version < VMCI_VERSION_HOSTQP) { 28862306a36Sopenharmony_ci return vmci_host_dev->user_version; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return VMCI_VERSION; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci#define vmci_ioctl_err(fmt, ...) \ 29562306a36Sopenharmony_ci pr_devel("%s: " fmt, ioctl_name, ##__VA_ARGS__) 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int vmci_host_do_init_context(struct vmci_host_dev *vmci_host_dev, 29862306a36Sopenharmony_ci const char *ioctl_name, 29962306a36Sopenharmony_ci void __user *uptr) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct vmci_init_blk init_block; 30262306a36Sopenharmony_ci const struct cred *cred; 30362306a36Sopenharmony_ci int retval; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (copy_from_user(&init_block, uptr, sizeof(init_block))) { 30662306a36Sopenharmony_ci vmci_ioctl_err("error reading init block\n"); 30762306a36Sopenharmony_ci return -EFAULT; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci mutex_lock(&vmci_host_dev->lock); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_NOT_SET) { 31362306a36Sopenharmony_ci vmci_ioctl_err("received VMCI init on initialized handle\n"); 31462306a36Sopenharmony_ci retval = -EINVAL; 31562306a36Sopenharmony_ci goto out; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (init_block.flags & ~VMCI_PRIVILEGE_FLAG_RESTRICTED) { 31962306a36Sopenharmony_ci vmci_ioctl_err("unsupported VMCI restriction flag\n"); 32062306a36Sopenharmony_ci retval = -EINVAL; 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci cred = get_current_cred(); 32562306a36Sopenharmony_ci vmci_host_dev->context = vmci_ctx_create(init_block.cid, 32662306a36Sopenharmony_ci init_block.flags, 0, 32762306a36Sopenharmony_ci vmci_host_dev->user_version, 32862306a36Sopenharmony_ci cred); 32962306a36Sopenharmony_ci put_cred(cred); 33062306a36Sopenharmony_ci if (IS_ERR(vmci_host_dev->context)) { 33162306a36Sopenharmony_ci retval = PTR_ERR(vmci_host_dev->context); 33262306a36Sopenharmony_ci vmci_ioctl_err("error initializing context\n"); 33362306a36Sopenharmony_ci goto out; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * Copy cid to userlevel, we do this to allow the VMX 33862306a36Sopenharmony_ci * to enforce its policy on cid generation. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci init_block.cid = vmci_ctx_get_id(vmci_host_dev->context); 34162306a36Sopenharmony_ci if (copy_to_user(uptr, &init_block, sizeof(init_block))) { 34262306a36Sopenharmony_ci vmci_ctx_destroy(vmci_host_dev->context); 34362306a36Sopenharmony_ci vmci_host_dev->context = NULL; 34462306a36Sopenharmony_ci vmci_ioctl_err("error writing init block\n"); 34562306a36Sopenharmony_ci retval = -EFAULT; 34662306a36Sopenharmony_ci goto out; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci vmci_host_dev->ct_type = VMCIOBJ_CONTEXT; 35062306a36Sopenharmony_ci atomic_inc(&vmci_host_active_users); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci vmci_call_vsock_callback(true); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci retval = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciout: 35762306a36Sopenharmony_ci mutex_unlock(&vmci_host_dev->lock); 35862306a36Sopenharmony_ci return retval; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int vmci_host_do_send_datagram(struct vmci_host_dev *vmci_host_dev, 36262306a36Sopenharmony_ci const char *ioctl_name, 36362306a36Sopenharmony_ci void __user *uptr) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct vmci_datagram_snd_rcv_info send_info; 36662306a36Sopenharmony_ci struct vmci_datagram *dg = NULL; 36762306a36Sopenharmony_ci u32 cid; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 37062306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 37162306a36Sopenharmony_ci return -EINVAL; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (copy_from_user(&send_info, uptr, sizeof(send_info))) 37562306a36Sopenharmony_ci return -EFAULT; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (send_info.len > VMCI_MAX_DG_SIZE) { 37862306a36Sopenharmony_ci vmci_ioctl_err("datagram is too big (size=%d)\n", 37962306a36Sopenharmony_ci send_info.len); 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (send_info.len < sizeof(*dg)) { 38462306a36Sopenharmony_ci vmci_ioctl_err("datagram is too small (size=%d)\n", 38562306a36Sopenharmony_ci send_info.len); 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci dg = memdup_user((void __user *)(uintptr_t)send_info.addr, 39062306a36Sopenharmony_ci send_info.len); 39162306a36Sopenharmony_ci if (IS_ERR(dg)) { 39262306a36Sopenharmony_ci vmci_ioctl_err( 39362306a36Sopenharmony_ci "cannot allocate memory to dispatch datagram\n"); 39462306a36Sopenharmony_ci return PTR_ERR(dg); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (VMCI_DG_SIZE(dg) != send_info.len) { 39862306a36Sopenharmony_ci vmci_ioctl_err("datagram size mismatch\n"); 39962306a36Sopenharmony_ci kfree(dg); 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci pr_devel("Datagram dst (handle=0x%x:0x%x) src (handle=0x%x:0x%x), payload (size=%llu bytes)\n", 40462306a36Sopenharmony_ci dg->dst.context, dg->dst.resource, 40562306a36Sopenharmony_ci dg->src.context, dg->src.resource, 40662306a36Sopenharmony_ci (unsigned long long)dg->payload_size); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Get source context id. */ 40962306a36Sopenharmony_ci cid = vmci_ctx_get_id(vmci_host_dev->context); 41062306a36Sopenharmony_ci send_info.result = vmci_datagram_dispatch(cid, dg, true); 41162306a36Sopenharmony_ci kfree(dg); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return copy_to_user(uptr, &send_info, sizeof(send_info)) ? -EFAULT : 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int vmci_host_do_receive_datagram(struct vmci_host_dev *vmci_host_dev, 41762306a36Sopenharmony_ci const char *ioctl_name, 41862306a36Sopenharmony_ci void __user *uptr) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct vmci_datagram_snd_rcv_info recv_info; 42162306a36Sopenharmony_ci struct vmci_datagram *dg = NULL; 42262306a36Sopenharmony_ci int retval; 42362306a36Sopenharmony_ci size_t size; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 42662306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (copy_from_user(&recv_info, uptr, sizeof(recv_info))) 43162306a36Sopenharmony_ci return -EFAULT; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci size = recv_info.len; 43462306a36Sopenharmony_ci recv_info.result = vmci_ctx_dequeue_datagram(vmci_host_dev->context, 43562306a36Sopenharmony_ci &size, &dg); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (recv_info.result >= VMCI_SUCCESS) { 43862306a36Sopenharmony_ci void __user *ubuf = (void __user *)(uintptr_t)recv_info.addr; 43962306a36Sopenharmony_ci retval = copy_to_user(ubuf, dg, VMCI_DG_SIZE(dg)); 44062306a36Sopenharmony_ci kfree(dg); 44162306a36Sopenharmony_ci if (retval != 0) 44262306a36Sopenharmony_ci return -EFAULT; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return copy_to_user(uptr, &recv_info, sizeof(recv_info)) ? -EFAULT : 0; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int vmci_host_do_alloc_queuepair(struct vmci_host_dev *vmci_host_dev, 44962306a36Sopenharmony_ci const char *ioctl_name, 45062306a36Sopenharmony_ci void __user *uptr) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct vmci_handle handle; 45362306a36Sopenharmony_ci int vmci_status; 45462306a36Sopenharmony_ci int __user *retptr; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 45762306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 45862306a36Sopenharmony_ci return -EINVAL; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (vmci_host_dev->user_version < VMCI_VERSION_NOVMVM) { 46262306a36Sopenharmony_ci struct vmci_qp_alloc_info_vmvm alloc_info; 46362306a36Sopenharmony_ci struct vmci_qp_alloc_info_vmvm __user *info = uptr; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (copy_from_user(&alloc_info, uptr, sizeof(alloc_info))) 46662306a36Sopenharmony_ci return -EFAULT; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci handle = alloc_info.handle; 46962306a36Sopenharmony_ci retptr = &info->result; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci vmci_status = vmci_qp_broker_alloc(alloc_info.handle, 47262306a36Sopenharmony_ci alloc_info.peer, 47362306a36Sopenharmony_ci alloc_info.flags, 47462306a36Sopenharmony_ci VMCI_NO_PRIVILEGE_FLAGS, 47562306a36Sopenharmony_ci alloc_info.produce_size, 47662306a36Sopenharmony_ci alloc_info.consume_size, 47762306a36Sopenharmony_ci NULL, 47862306a36Sopenharmony_ci vmci_host_dev->context); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (vmci_status == VMCI_SUCCESS) 48162306a36Sopenharmony_ci vmci_status = VMCI_SUCCESS_QUEUEPAIR_CREATE; 48262306a36Sopenharmony_ci } else { 48362306a36Sopenharmony_ci struct vmci_qp_alloc_info alloc_info; 48462306a36Sopenharmony_ci struct vmci_qp_alloc_info __user *info = uptr; 48562306a36Sopenharmony_ci struct vmci_qp_page_store page_store; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (copy_from_user(&alloc_info, uptr, sizeof(alloc_info))) 48862306a36Sopenharmony_ci return -EFAULT; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci handle = alloc_info.handle; 49162306a36Sopenharmony_ci retptr = &info->result; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci page_store.pages = alloc_info.ppn_va; 49462306a36Sopenharmony_ci page_store.len = alloc_info.num_ppns; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci vmci_status = vmci_qp_broker_alloc(alloc_info.handle, 49762306a36Sopenharmony_ci alloc_info.peer, 49862306a36Sopenharmony_ci alloc_info.flags, 49962306a36Sopenharmony_ci VMCI_NO_PRIVILEGE_FLAGS, 50062306a36Sopenharmony_ci alloc_info.produce_size, 50162306a36Sopenharmony_ci alloc_info.consume_size, 50262306a36Sopenharmony_ci &page_store, 50362306a36Sopenharmony_ci vmci_host_dev->context); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (put_user(vmci_status, retptr)) { 50762306a36Sopenharmony_ci if (vmci_status >= VMCI_SUCCESS) { 50862306a36Sopenharmony_ci vmci_status = vmci_qp_broker_detach(handle, 50962306a36Sopenharmony_ci vmci_host_dev->context); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci return -EFAULT; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int vmci_host_do_queuepair_setva(struct vmci_host_dev *vmci_host_dev, 51862306a36Sopenharmony_ci const char *ioctl_name, 51962306a36Sopenharmony_ci void __user *uptr) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct vmci_qp_set_va_info set_va_info; 52262306a36Sopenharmony_ci struct vmci_qp_set_va_info __user *info = uptr; 52362306a36Sopenharmony_ci s32 result; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 52662306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 52762306a36Sopenharmony_ci return -EINVAL; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (vmci_host_dev->user_version < VMCI_VERSION_NOVMVM) { 53162306a36Sopenharmony_ci vmci_ioctl_err("is not allowed\n"); 53262306a36Sopenharmony_ci return -EINVAL; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (copy_from_user(&set_va_info, uptr, sizeof(set_va_info))) 53662306a36Sopenharmony_ci return -EFAULT; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (set_va_info.va) { 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * VMX is passing down a new VA for the queue 54162306a36Sopenharmony_ci * pair mapping. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ci result = vmci_qp_broker_map(set_va_info.handle, 54462306a36Sopenharmony_ci vmci_host_dev->context, 54562306a36Sopenharmony_ci set_va_info.va); 54662306a36Sopenharmony_ci } else { 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * The queue pair is about to be unmapped by 54962306a36Sopenharmony_ci * the VMX. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci result = vmci_qp_broker_unmap(set_va_info.handle, 55262306a36Sopenharmony_ci vmci_host_dev->context, 0); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return put_user(result, &info->result) ? -EFAULT : 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int vmci_host_do_queuepair_setpf(struct vmci_host_dev *vmci_host_dev, 55962306a36Sopenharmony_ci const char *ioctl_name, 56062306a36Sopenharmony_ci void __user *uptr) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct vmci_qp_page_file_info page_file_info; 56362306a36Sopenharmony_ci struct vmci_qp_page_file_info __user *info = uptr; 56462306a36Sopenharmony_ci s32 result; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (vmci_host_dev->user_version < VMCI_VERSION_HOSTQP || 56762306a36Sopenharmony_ci vmci_host_dev->user_version >= VMCI_VERSION_NOVMVM) { 56862306a36Sopenharmony_ci vmci_ioctl_err("not supported on this VMX (version=%d)\n", 56962306a36Sopenharmony_ci vmci_host_dev->user_version); 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 57462306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 57562306a36Sopenharmony_ci return -EINVAL; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (copy_from_user(&page_file_info, uptr, sizeof(*info))) 57962306a36Sopenharmony_ci return -EFAULT; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * Communicate success pre-emptively to the caller. Note that the 58362306a36Sopenharmony_ci * basic premise is that it is incumbent upon the caller not to look at 58462306a36Sopenharmony_ci * the info.result field until after the ioctl() returns. And then, 58562306a36Sopenharmony_ci * only if the ioctl() result indicates no error. We send up the 58662306a36Sopenharmony_ci * SUCCESS status before calling SetPageStore() store because failing 58762306a36Sopenharmony_ci * to copy up the result code means unwinding the SetPageStore(). 58862306a36Sopenharmony_ci * 58962306a36Sopenharmony_ci * It turns out the logic to unwind a SetPageStore() opens a can of 59062306a36Sopenharmony_ci * worms. For example, if a host had created the queue_pair and a 59162306a36Sopenharmony_ci * guest attaches and SetPageStore() is successful but writing success 59262306a36Sopenharmony_ci * fails, then ... the host has to be stopped from writing (anymore) 59362306a36Sopenharmony_ci * data into the queue_pair. That means an additional test in the 59462306a36Sopenharmony_ci * VMCI_Enqueue() code path. Ugh. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (put_user(VMCI_SUCCESS, &info->result)) { 59862306a36Sopenharmony_ci /* 59962306a36Sopenharmony_ci * In this case, we can't write a result field of the 60062306a36Sopenharmony_ci * caller's info block. So, we don't even try to 60162306a36Sopenharmony_ci * SetPageStore(). 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_ci return -EFAULT; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci result = vmci_qp_broker_set_page_store(page_file_info.handle, 60762306a36Sopenharmony_ci page_file_info.produce_va, 60862306a36Sopenharmony_ci page_file_info.consume_va, 60962306a36Sopenharmony_ci vmci_host_dev->context); 61062306a36Sopenharmony_ci if (result < VMCI_SUCCESS) { 61162306a36Sopenharmony_ci if (put_user(result, &info->result)) { 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci * Note that in this case the SetPageStore() 61462306a36Sopenharmony_ci * call failed but we were unable to 61562306a36Sopenharmony_ci * communicate that to the caller (because the 61662306a36Sopenharmony_ci * copy_to_user() call failed). So, if we 61762306a36Sopenharmony_ci * simply return an error (in this case 61862306a36Sopenharmony_ci * -EFAULT) then the caller will know that the 61962306a36Sopenharmony_ci * SetPageStore failed even though we couldn't 62062306a36Sopenharmony_ci * put the result code in the result field and 62162306a36Sopenharmony_ci * indicate exactly why it failed. 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * That says nothing about the issue where we 62462306a36Sopenharmony_ci * were once able to write to the caller's info 62562306a36Sopenharmony_ci * memory and now can't. Something more 62662306a36Sopenharmony_ci * serious is probably going on than the fact 62762306a36Sopenharmony_ci * that SetPageStore() didn't work. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci return -EFAULT; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int vmci_host_do_qp_detach(struct vmci_host_dev *vmci_host_dev, 63762306a36Sopenharmony_ci const char *ioctl_name, 63862306a36Sopenharmony_ci void __user *uptr) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct vmci_qp_dtch_info detach_info; 64162306a36Sopenharmony_ci struct vmci_qp_dtch_info __user *info = uptr; 64262306a36Sopenharmony_ci s32 result; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 64562306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 64662306a36Sopenharmony_ci return -EINVAL; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (copy_from_user(&detach_info, uptr, sizeof(detach_info))) 65062306a36Sopenharmony_ci return -EFAULT; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci result = vmci_qp_broker_detach(detach_info.handle, 65362306a36Sopenharmony_ci vmci_host_dev->context); 65462306a36Sopenharmony_ci if (result == VMCI_SUCCESS && 65562306a36Sopenharmony_ci vmci_host_dev->user_version < VMCI_VERSION_NOVMVM) { 65662306a36Sopenharmony_ci result = VMCI_SUCCESS_LAST_DETACH; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return put_user(result, &info->result) ? -EFAULT : 0; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int vmci_host_do_ctx_add_notify(struct vmci_host_dev *vmci_host_dev, 66362306a36Sopenharmony_ci const char *ioctl_name, 66462306a36Sopenharmony_ci void __user *uptr) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct vmci_ctx_info ar_info; 66762306a36Sopenharmony_ci struct vmci_ctx_info __user *info = uptr; 66862306a36Sopenharmony_ci s32 result; 66962306a36Sopenharmony_ci u32 cid; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 67262306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 67362306a36Sopenharmony_ci return -EINVAL; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (copy_from_user(&ar_info, uptr, sizeof(ar_info))) 67762306a36Sopenharmony_ci return -EFAULT; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci cid = vmci_ctx_get_id(vmci_host_dev->context); 68062306a36Sopenharmony_ci result = vmci_ctx_add_notification(cid, ar_info.remote_cid); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return put_user(result, &info->result) ? -EFAULT : 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic int vmci_host_do_ctx_remove_notify(struct vmci_host_dev *vmci_host_dev, 68662306a36Sopenharmony_ci const char *ioctl_name, 68762306a36Sopenharmony_ci void __user *uptr) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct vmci_ctx_info ar_info; 69062306a36Sopenharmony_ci struct vmci_ctx_info __user *info = uptr; 69162306a36Sopenharmony_ci u32 cid; 69262306a36Sopenharmony_ci int result; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 69562306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 69662306a36Sopenharmony_ci return -EINVAL; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (copy_from_user(&ar_info, uptr, sizeof(ar_info))) 70062306a36Sopenharmony_ci return -EFAULT; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci cid = vmci_ctx_get_id(vmci_host_dev->context); 70362306a36Sopenharmony_ci result = vmci_ctx_remove_notification(cid, 70462306a36Sopenharmony_ci ar_info.remote_cid); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return put_user(result, &info->result) ? -EFAULT : 0; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int vmci_host_do_ctx_get_cpt_state(struct vmci_host_dev *vmci_host_dev, 71062306a36Sopenharmony_ci const char *ioctl_name, 71162306a36Sopenharmony_ci void __user *uptr) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct vmci_ctx_chkpt_buf_info get_info; 71462306a36Sopenharmony_ci u32 cid; 71562306a36Sopenharmony_ci void *cpt_buf; 71662306a36Sopenharmony_ci int retval; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 71962306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 72062306a36Sopenharmony_ci return -EINVAL; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (copy_from_user(&get_info, uptr, sizeof(get_info))) 72462306a36Sopenharmony_ci return -EFAULT; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci cid = vmci_ctx_get_id(vmci_host_dev->context); 72762306a36Sopenharmony_ci get_info.result = vmci_ctx_get_chkpt_state(cid, get_info.cpt_type, 72862306a36Sopenharmony_ci &get_info.buf_size, &cpt_buf); 72962306a36Sopenharmony_ci if (get_info.result == VMCI_SUCCESS && get_info.buf_size) { 73062306a36Sopenharmony_ci void __user *ubuf = (void __user *)(uintptr_t)get_info.cpt_buf; 73162306a36Sopenharmony_ci retval = copy_to_user(ubuf, cpt_buf, get_info.buf_size); 73262306a36Sopenharmony_ci kfree(cpt_buf); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (retval) 73562306a36Sopenharmony_ci return -EFAULT; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return copy_to_user(uptr, &get_info, sizeof(get_info)) ? -EFAULT : 0; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic int vmci_host_do_ctx_set_cpt_state(struct vmci_host_dev *vmci_host_dev, 74262306a36Sopenharmony_ci const char *ioctl_name, 74362306a36Sopenharmony_ci void __user *uptr) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct vmci_ctx_chkpt_buf_info set_info; 74662306a36Sopenharmony_ci u32 cid; 74762306a36Sopenharmony_ci void *cpt_buf; 74862306a36Sopenharmony_ci int retval; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 75162306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 75262306a36Sopenharmony_ci return -EINVAL; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (copy_from_user(&set_info, uptr, sizeof(set_info))) 75662306a36Sopenharmony_ci return -EFAULT; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci cpt_buf = memdup_user((void __user *)(uintptr_t)set_info.cpt_buf, 75962306a36Sopenharmony_ci set_info.buf_size); 76062306a36Sopenharmony_ci if (IS_ERR(cpt_buf)) 76162306a36Sopenharmony_ci return PTR_ERR(cpt_buf); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci cid = vmci_ctx_get_id(vmci_host_dev->context); 76462306a36Sopenharmony_ci set_info.result = vmci_ctx_set_chkpt_state(cid, set_info.cpt_type, 76562306a36Sopenharmony_ci set_info.buf_size, cpt_buf); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci retval = copy_to_user(uptr, &set_info, sizeof(set_info)) ? -EFAULT : 0; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci kfree(cpt_buf); 77062306a36Sopenharmony_ci return retval; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int vmci_host_do_get_context_id(struct vmci_host_dev *vmci_host_dev, 77462306a36Sopenharmony_ci const char *ioctl_name, 77562306a36Sopenharmony_ci void __user *uptr) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci u32 __user *u32ptr = uptr; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci return put_user(VMCI_HOST_CONTEXT_ID, u32ptr) ? -EFAULT : 0; 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic int vmci_host_do_set_notify(struct vmci_host_dev *vmci_host_dev, 78362306a36Sopenharmony_ci const char *ioctl_name, 78462306a36Sopenharmony_ci void __user *uptr) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct vmci_set_notify_info notify_info; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 78962306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 79062306a36Sopenharmony_ci return -EINVAL; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (copy_from_user(¬ify_info, uptr, sizeof(notify_info))) 79462306a36Sopenharmony_ci return -EFAULT; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (notify_info.notify_uva) { 79762306a36Sopenharmony_ci notify_info.result = 79862306a36Sopenharmony_ci vmci_host_setup_notify(vmci_host_dev->context, 79962306a36Sopenharmony_ci notify_info.notify_uva); 80062306a36Sopenharmony_ci } else { 80162306a36Sopenharmony_ci vmci_ctx_unset_notify(vmci_host_dev->context); 80262306a36Sopenharmony_ci notify_info.result = VMCI_SUCCESS; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return copy_to_user(uptr, ¬ify_info, sizeof(notify_info)) ? 80662306a36Sopenharmony_ci -EFAULT : 0; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic int vmci_host_do_notify_resource(struct vmci_host_dev *vmci_host_dev, 81062306a36Sopenharmony_ci const char *ioctl_name, 81162306a36Sopenharmony_ci void __user *uptr) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct vmci_dbell_notify_resource_info info; 81462306a36Sopenharmony_ci u32 cid; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (vmci_host_dev->user_version < VMCI_VERSION_NOTIFY) { 81762306a36Sopenharmony_ci vmci_ioctl_err("invalid for current VMX versions\n"); 81862306a36Sopenharmony_ci return -EINVAL; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 82262306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 82362306a36Sopenharmony_ci return -EINVAL; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (copy_from_user(&info, uptr, sizeof(info))) 82762306a36Sopenharmony_ci return -EFAULT; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci cid = vmci_ctx_get_id(vmci_host_dev->context); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci switch (info.action) { 83262306a36Sopenharmony_ci case VMCI_NOTIFY_RESOURCE_ACTION_NOTIFY: 83362306a36Sopenharmony_ci if (info.resource == VMCI_NOTIFY_RESOURCE_DOOR_BELL) { 83462306a36Sopenharmony_ci u32 flags = VMCI_NO_PRIVILEGE_FLAGS; 83562306a36Sopenharmony_ci info.result = vmci_ctx_notify_dbell(cid, info.handle, 83662306a36Sopenharmony_ci flags); 83762306a36Sopenharmony_ci } else { 83862306a36Sopenharmony_ci info.result = VMCI_ERROR_UNAVAILABLE; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci break; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci case VMCI_NOTIFY_RESOURCE_ACTION_CREATE: 84362306a36Sopenharmony_ci info.result = vmci_ctx_dbell_create(cid, info.handle); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci case VMCI_NOTIFY_RESOURCE_ACTION_DESTROY: 84762306a36Sopenharmony_ci info.result = vmci_ctx_dbell_destroy(cid, info.handle); 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci default: 85162306a36Sopenharmony_ci vmci_ioctl_err("got unknown action (action=%d)\n", 85262306a36Sopenharmony_ci info.action); 85362306a36Sopenharmony_ci info.result = VMCI_ERROR_INVALID_ARGS; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return copy_to_user(uptr, &info, sizeof(info)) ? -EFAULT : 0; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int vmci_host_do_recv_notifications(struct vmci_host_dev *vmci_host_dev, 86062306a36Sopenharmony_ci const char *ioctl_name, 86162306a36Sopenharmony_ci void __user *uptr) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct vmci_ctx_notify_recv_info info; 86462306a36Sopenharmony_ci struct vmci_handle_arr *db_handle_array; 86562306a36Sopenharmony_ci struct vmci_handle_arr *qp_handle_array; 86662306a36Sopenharmony_ci void __user *ubuf; 86762306a36Sopenharmony_ci u32 cid; 86862306a36Sopenharmony_ci int retval = 0; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { 87162306a36Sopenharmony_ci vmci_ioctl_err("only valid for contexts\n"); 87262306a36Sopenharmony_ci return -EINVAL; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (vmci_host_dev->user_version < VMCI_VERSION_NOTIFY) { 87662306a36Sopenharmony_ci vmci_ioctl_err("not supported for the current vmx version\n"); 87762306a36Sopenharmony_ci return -EINVAL; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (copy_from_user(&info, uptr, sizeof(info))) 88162306a36Sopenharmony_ci return -EFAULT; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if ((info.db_handle_buf_size && !info.db_handle_buf_uva) || 88462306a36Sopenharmony_ci (info.qp_handle_buf_size && !info.qp_handle_buf_uva)) { 88562306a36Sopenharmony_ci return -EINVAL; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci cid = vmci_ctx_get_id(vmci_host_dev->context); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci info.result = vmci_ctx_rcv_notifications_get(cid, 89162306a36Sopenharmony_ci &db_handle_array, &qp_handle_array); 89262306a36Sopenharmony_ci if (info.result != VMCI_SUCCESS) 89362306a36Sopenharmony_ci return copy_to_user(uptr, &info, sizeof(info)) ? -EFAULT : 0; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci ubuf = (void __user *)(uintptr_t)info.db_handle_buf_uva; 89662306a36Sopenharmony_ci info.result = drv_cp_harray_to_user(ubuf, &info.db_handle_buf_size, 89762306a36Sopenharmony_ci db_handle_array, &retval); 89862306a36Sopenharmony_ci if (info.result == VMCI_SUCCESS && !retval) { 89962306a36Sopenharmony_ci ubuf = (void __user *)(uintptr_t)info.qp_handle_buf_uva; 90062306a36Sopenharmony_ci info.result = drv_cp_harray_to_user(ubuf, 90162306a36Sopenharmony_ci &info.qp_handle_buf_size, 90262306a36Sopenharmony_ci qp_handle_array, &retval); 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (!retval && copy_to_user(uptr, &info, sizeof(info))) 90662306a36Sopenharmony_ci retval = -EFAULT; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci vmci_ctx_rcv_notifications_release(cid, 90962306a36Sopenharmony_ci db_handle_array, qp_handle_array, 91062306a36Sopenharmony_ci info.result == VMCI_SUCCESS && !retval); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return retval; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic long vmci_host_unlocked_ioctl(struct file *filp, 91662306a36Sopenharmony_ci unsigned int iocmd, unsigned long ioarg) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci#define VMCI_DO_IOCTL(ioctl_name, ioctl_fn) do { \ 91962306a36Sopenharmony_ci char *name = "IOCTL_VMCI_" # ioctl_name; \ 92062306a36Sopenharmony_ci return vmci_host_do_ ## ioctl_fn( \ 92162306a36Sopenharmony_ci vmci_host_dev, name, uptr); \ 92262306a36Sopenharmony_ci } while (0) 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci struct vmci_host_dev *vmci_host_dev = filp->private_data; 92562306a36Sopenharmony_ci void __user *uptr = (void __user *)ioarg; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci switch (iocmd) { 92862306a36Sopenharmony_ci case IOCTL_VMCI_INIT_CONTEXT: 92962306a36Sopenharmony_ci VMCI_DO_IOCTL(INIT_CONTEXT, init_context); 93062306a36Sopenharmony_ci case IOCTL_VMCI_DATAGRAM_SEND: 93162306a36Sopenharmony_ci VMCI_DO_IOCTL(DATAGRAM_SEND, send_datagram); 93262306a36Sopenharmony_ci case IOCTL_VMCI_DATAGRAM_RECEIVE: 93362306a36Sopenharmony_ci VMCI_DO_IOCTL(DATAGRAM_RECEIVE, receive_datagram); 93462306a36Sopenharmony_ci case IOCTL_VMCI_QUEUEPAIR_ALLOC: 93562306a36Sopenharmony_ci VMCI_DO_IOCTL(QUEUEPAIR_ALLOC, alloc_queuepair); 93662306a36Sopenharmony_ci case IOCTL_VMCI_QUEUEPAIR_SETVA: 93762306a36Sopenharmony_ci VMCI_DO_IOCTL(QUEUEPAIR_SETVA, queuepair_setva); 93862306a36Sopenharmony_ci case IOCTL_VMCI_QUEUEPAIR_SETPAGEFILE: 93962306a36Sopenharmony_ci VMCI_DO_IOCTL(QUEUEPAIR_SETPAGEFILE, queuepair_setpf); 94062306a36Sopenharmony_ci case IOCTL_VMCI_QUEUEPAIR_DETACH: 94162306a36Sopenharmony_ci VMCI_DO_IOCTL(QUEUEPAIR_DETACH, qp_detach); 94262306a36Sopenharmony_ci case IOCTL_VMCI_CTX_ADD_NOTIFICATION: 94362306a36Sopenharmony_ci VMCI_DO_IOCTL(CTX_ADD_NOTIFICATION, ctx_add_notify); 94462306a36Sopenharmony_ci case IOCTL_VMCI_CTX_REMOVE_NOTIFICATION: 94562306a36Sopenharmony_ci VMCI_DO_IOCTL(CTX_REMOVE_NOTIFICATION, ctx_remove_notify); 94662306a36Sopenharmony_ci case IOCTL_VMCI_CTX_GET_CPT_STATE: 94762306a36Sopenharmony_ci VMCI_DO_IOCTL(CTX_GET_CPT_STATE, ctx_get_cpt_state); 94862306a36Sopenharmony_ci case IOCTL_VMCI_CTX_SET_CPT_STATE: 94962306a36Sopenharmony_ci VMCI_DO_IOCTL(CTX_SET_CPT_STATE, ctx_set_cpt_state); 95062306a36Sopenharmony_ci case IOCTL_VMCI_GET_CONTEXT_ID: 95162306a36Sopenharmony_ci VMCI_DO_IOCTL(GET_CONTEXT_ID, get_context_id); 95262306a36Sopenharmony_ci case IOCTL_VMCI_SET_NOTIFY: 95362306a36Sopenharmony_ci VMCI_DO_IOCTL(SET_NOTIFY, set_notify); 95462306a36Sopenharmony_ci case IOCTL_VMCI_NOTIFY_RESOURCE: 95562306a36Sopenharmony_ci VMCI_DO_IOCTL(NOTIFY_RESOURCE, notify_resource); 95662306a36Sopenharmony_ci case IOCTL_VMCI_NOTIFICATIONS_RECEIVE: 95762306a36Sopenharmony_ci VMCI_DO_IOCTL(NOTIFICATIONS_RECEIVE, recv_notifications); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci case IOCTL_VMCI_VERSION: 96062306a36Sopenharmony_ci case IOCTL_VMCI_VERSION2: 96162306a36Sopenharmony_ci return vmci_host_get_version(vmci_host_dev, iocmd, uptr); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci default: 96462306a36Sopenharmony_ci pr_devel("%s: Unknown ioctl (iocmd=%d)\n", __func__, iocmd); 96562306a36Sopenharmony_ci return -EINVAL; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci#undef VMCI_DO_IOCTL 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic const struct file_operations vmuser_fops = { 97262306a36Sopenharmony_ci .owner = THIS_MODULE, 97362306a36Sopenharmony_ci .open = vmci_host_open, 97462306a36Sopenharmony_ci .release = vmci_host_close, 97562306a36Sopenharmony_ci .poll = vmci_host_poll, 97662306a36Sopenharmony_ci .unlocked_ioctl = vmci_host_unlocked_ioctl, 97762306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 97862306a36Sopenharmony_ci}; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic struct miscdevice vmci_host_miscdev = { 98162306a36Sopenharmony_ci .name = "vmci", 98262306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 98362306a36Sopenharmony_ci .fops = &vmuser_fops, 98462306a36Sopenharmony_ci}; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ciint __init vmci_host_init(void) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci int error; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci host_context = vmci_ctx_create(VMCI_HOST_CONTEXT_ID, 99162306a36Sopenharmony_ci VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS, 99262306a36Sopenharmony_ci -1, VMCI_VERSION, NULL); 99362306a36Sopenharmony_ci if (IS_ERR(host_context)) { 99462306a36Sopenharmony_ci error = PTR_ERR(host_context); 99562306a36Sopenharmony_ci pr_warn("Failed to initialize VMCIContext (error%d)\n", 99662306a36Sopenharmony_ci error); 99762306a36Sopenharmony_ci return error; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci error = misc_register(&vmci_host_miscdev); 100162306a36Sopenharmony_ci if (error) { 100262306a36Sopenharmony_ci pr_warn("Module registration error (name=%s, major=%d, minor=%d, err=%d)\n", 100362306a36Sopenharmony_ci vmci_host_miscdev.name, 100462306a36Sopenharmony_ci MISC_MAJOR, vmci_host_miscdev.minor, 100562306a36Sopenharmony_ci error); 100662306a36Sopenharmony_ci pr_warn("Unable to initialize host personality\n"); 100762306a36Sopenharmony_ci vmci_ctx_destroy(host_context); 100862306a36Sopenharmony_ci return error; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci pr_info("VMCI host device registered (name=%s, major=%d, minor=%d)\n", 101262306a36Sopenharmony_ci vmci_host_miscdev.name, MISC_MAJOR, vmci_host_miscdev.minor); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci vmci_host_device_initialized = true; 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_civoid __exit vmci_host_exit(void) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci vmci_host_device_initialized = false; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci misc_deregister(&vmci_host_miscdev); 102362306a36Sopenharmony_ci vmci_ctx_destroy(host_context); 102462306a36Sopenharmony_ci vmci_qp_broker_exit(); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci pr_debug("VMCI host driver module unloaded\n"); 102762306a36Sopenharmony_ci} 1028