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(&notify_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, &notify_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