brintos

brintos / linux-shallow public Read only

0
0
Text · 23.8 KiB · 1c02b3c Raw
835 lines · c
1/* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */2/*3 * vboxguest vmm-req and hgcm-call code, VBoxGuestR0LibHGCMInternal.cpp,4 * VBoxGuestR0LibGenericRequest.cpp and RTErrConvertToErrno.cpp in vbox svn.5 *6 * Copyright (C) 2006-2016 Oracle Corporation7 */8 9#include <linux/errno.h>10#include <linux/io.h>11#include <linux/kernel.h>12#include <linux/mm.h>13#include <linux/module.h>14#include <linux/sizes.h>15#include <linux/slab.h>16#include <linux/uaccess.h>17#include <linux/vmalloc.h>18#include <linux/vbox_err.h>19#include <linux/vbox_utils.h>20#include "vboxguest_core.h"21 22/* Get the pointer to the first parameter of a HGCM call request. */23#define VMMDEV_HGCM_CALL_PARMS(a) \24	((struct vmmdev_hgcm_function_parameter *)( \25		(u8 *)(a) + sizeof(struct vmmdev_hgcm_call)))26 27/* The max parameter buffer size for a user request. */28#define VBG_MAX_HGCM_USER_PARM		(24 * SZ_1M)29/* The max parameter buffer size for a kernel request. */30#define VBG_MAX_HGCM_KERNEL_PARM	(16 * SZ_1M)31 32#define VBG_DEBUG_PORT			0x50433 34/* This protects vbg_log_buf and serializes VBG_DEBUG_PORT accesses */35static DEFINE_SPINLOCK(vbg_log_lock);36static char vbg_log_buf[128];37 38#define VBG_LOG(name, pr_func) \39void name(const char *fmt, ...)						\40{									\41	unsigned long flags;						\42	va_list args;							\43	int i, count;							\44									\45	va_start(args, fmt);						\46	spin_lock_irqsave(&vbg_log_lock, flags);			\47									\48	count = vscnprintf(vbg_log_buf, sizeof(vbg_log_buf), fmt, args);\49	for (i = 0; i < count; i++)					\50		outb(vbg_log_buf[i], VBG_DEBUG_PORT);			\51									\52	pr_func("%s", vbg_log_buf);					\53									\54	spin_unlock_irqrestore(&vbg_log_lock, flags);			\55	va_end(args);							\56}									\57EXPORT_SYMBOL(name)58 59VBG_LOG(vbg_info, pr_info);60VBG_LOG(vbg_warn, pr_warn);61VBG_LOG(vbg_err, pr_err);62VBG_LOG(vbg_err_ratelimited, pr_err_ratelimited);63#if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG)64VBG_LOG(vbg_debug, pr_debug);65#endif66 67void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type,68		    u32 requestor)69{70	struct vmmdev_request_header *req;71	int order = get_order(PAGE_ALIGN(len));72 73	req = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order);74	if (!req)75		return NULL;76 77	memset(req, 0xaa, len);78 79	req->size = len;80	req->version = VMMDEV_REQUEST_HEADER_VERSION;81	req->request_type = req_type;82	req->rc = VERR_GENERAL_FAILURE;83	req->reserved1 = 0;84	req->requestor = requestor;85 86	return req;87}88 89void vbg_req_free(void *req, size_t len)90{91	if (!req)92		return;93 94	free_pages((unsigned long)req, get_order(PAGE_ALIGN(len)));95}96 97/* Note this function returns a VBox status code, not a negative errno!! */98int vbg_req_perform(struct vbg_dev *gdev, void *req)99{100	unsigned long phys_req = virt_to_phys(req);101 102	outl(phys_req, gdev->io_port + VMMDEV_PORT_OFF_REQUEST);103	/*104	 * The host changes the request as a result of the outl, make sure105	 * the outl and any reads of the req happen in the correct order.106	 */107	mb();108 109	return ((struct vmmdev_request_header *)req)->rc;110}111 112static bool hgcm_req_done(struct vbg_dev *gdev,113			  struct vmmdev_hgcmreq_header *header)114{115	unsigned long flags;116	bool done;117 118	spin_lock_irqsave(&gdev->event_spinlock, flags);119	done = header->flags & VMMDEV_HGCM_REQ_DONE;120	spin_unlock_irqrestore(&gdev->event_spinlock, flags);121 122	return done;123}124 125int vbg_hgcm_connect(struct vbg_dev *gdev, u32 requestor,126		     struct vmmdev_hgcm_service_location *loc,127		     u32 *client_id, int *vbox_status)128{129	struct vmmdev_hgcm_connect *hgcm_connect = NULL;130	int rc;131 132	hgcm_connect = vbg_req_alloc(sizeof(*hgcm_connect),133				     VMMDEVREQ_HGCM_CONNECT, requestor);134	if (!hgcm_connect)135		return -ENOMEM;136 137	hgcm_connect->header.flags = 0;138	memcpy(&hgcm_connect->loc, loc, sizeof(*loc));139	hgcm_connect->client_id = 0;140 141	rc = vbg_req_perform(gdev, hgcm_connect);142 143	if (rc == VINF_HGCM_ASYNC_EXECUTE)144		wait_event(gdev->hgcm_wq,145			   hgcm_req_done(gdev, &hgcm_connect->header));146 147	if (rc >= 0) {148		*client_id = hgcm_connect->client_id;149		rc = hgcm_connect->header.result;150	}151 152	vbg_req_free(hgcm_connect, sizeof(*hgcm_connect));153 154	*vbox_status = rc;155	return 0;156}157EXPORT_SYMBOL(vbg_hgcm_connect);158 159int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 requestor,160			u32 client_id, int *vbox_status)161{162	struct vmmdev_hgcm_disconnect *hgcm_disconnect = NULL;163	int rc;164 165	hgcm_disconnect = vbg_req_alloc(sizeof(*hgcm_disconnect),166					VMMDEVREQ_HGCM_DISCONNECT,167					requestor);168	if (!hgcm_disconnect)169		return -ENOMEM;170 171	hgcm_disconnect->header.flags = 0;172	hgcm_disconnect->client_id = client_id;173 174	rc = vbg_req_perform(gdev, hgcm_disconnect);175 176	if (rc == VINF_HGCM_ASYNC_EXECUTE)177		wait_event(gdev->hgcm_wq,178			   hgcm_req_done(gdev, &hgcm_disconnect->header));179 180	if (rc >= 0)181		rc = hgcm_disconnect->header.result;182 183	vbg_req_free(hgcm_disconnect, sizeof(*hgcm_disconnect));184 185	*vbox_status = rc;186	return 0;187}188EXPORT_SYMBOL(vbg_hgcm_disconnect);189 190static u32 hgcm_call_buf_size_in_pages(void *buf, u32 len)191{192	u32 size = PAGE_ALIGN(len + ((unsigned long)buf & ~PAGE_MASK));193 194	return size >> PAGE_SHIFT;195}196 197static void hgcm_call_add_pagelist_size(void *buf, u32 len, size_t *extra)198{199	u32 page_count;200 201	page_count = hgcm_call_buf_size_in_pages(buf, len);202	*extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]);203}204 205static int hgcm_call_preprocess_linaddr(206	const struct vmmdev_hgcm_function_parameter *src_parm,207	void **bounce_buf_ret, size_t *extra)208{209	void *buf, *bounce_buf;210	bool copy_in;211	u32 len;212	int ret;213 214	buf = (void *)src_parm->u.pointer.u.linear_addr;215	len = src_parm->u.pointer.size;216	copy_in = src_parm->type != VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT;217 218	if (len > VBG_MAX_HGCM_USER_PARM)219		return -E2BIG;220 221	bounce_buf = kvmalloc(len, GFP_KERNEL);222	if (!bounce_buf)223		return -ENOMEM;224 225	*bounce_buf_ret = bounce_buf;226 227	if (copy_in) {228		ret = copy_from_user(bounce_buf, (void __user *)buf, len);229		if (ret)230			return -EFAULT;231	} else {232		memset(bounce_buf, 0, len);233	}234 235	hgcm_call_add_pagelist_size(bounce_buf, len, extra);236	return 0;237}238 239/**240 * hgcm_call_preprocess - Preprocesses the HGCM call, validate parameters,241 *	alloc bounce buffers and figure out how much extra storage we need for242 *	page lists.243 * @src_parm:         Pointer to source function call parameters244 * @parm_count:       Number of function call parameters.245 * @bounce_bufs_ret:  Where to return the allocated bouncebuffer array246 * @extra:            Where to return the extra request space needed for247 *                    physical page lists.248 *249 * Return: %0 or negative errno value.250 */251static int hgcm_call_preprocess(252	const struct vmmdev_hgcm_function_parameter *src_parm,253	u32 parm_count, void ***bounce_bufs_ret, size_t *extra)254{255	void *buf, **bounce_bufs = NULL;256	u32 i, len;257	int ret;258 259	for (i = 0; i < parm_count; i++, src_parm++) {260		switch (src_parm->type) {261		case VMMDEV_HGCM_PARM_TYPE_32BIT:262		case VMMDEV_HGCM_PARM_TYPE_64BIT:263			break;264 265		case VMMDEV_HGCM_PARM_TYPE_LINADDR:266		case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:267		case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:268			if (!bounce_bufs) {269				bounce_bufs = kcalloc(parm_count,270						      sizeof(void *),271						      GFP_KERNEL);272				if (!bounce_bufs)273					return -ENOMEM;274 275				*bounce_bufs_ret = bounce_bufs;276			}277 278			ret = hgcm_call_preprocess_linaddr(src_parm,279							   &bounce_bufs[i],280							   extra);281			if (ret)282				return ret;283 284			break;285 286		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:287		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:288		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:289			buf = (void *)src_parm->u.pointer.u.linear_addr;290			len = src_parm->u.pointer.size;291			if (WARN_ON(len > VBG_MAX_HGCM_KERNEL_PARM))292				return -E2BIG;293 294			hgcm_call_add_pagelist_size(buf, len, extra);295			break;296 297		default:298			return -EINVAL;299		}300	}301 302	return 0;303}304 305/**306 * hgcm_call_linear_addr_type_to_pagelist_flags - Translates linear address307 *	types to page list direction flags.308 * @type:  The type.309 *310 * Return: page list flags.311 */312static u32 hgcm_call_linear_addr_type_to_pagelist_flags(313	enum vmmdev_hgcm_function_parameter_type type)314{315	switch (type) {316	default:317		WARN_ON(1);318		fallthrough;319	case VMMDEV_HGCM_PARM_TYPE_LINADDR:320	case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:321		return VMMDEV_HGCM_F_PARM_DIRECTION_BOTH;322 323	case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:324	case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:325		return VMMDEV_HGCM_F_PARM_DIRECTION_TO_HOST;326 327	case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:328	case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:329		return VMMDEV_HGCM_F_PARM_DIRECTION_FROM_HOST;330	}331}332 333static void hgcm_call_init_linaddr(struct vmmdev_hgcm_call *call,334	struct vmmdev_hgcm_function_parameter *dst_parm, void *buf, u32 len,335	enum vmmdev_hgcm_function_parameter_type type, u32 *off_extra)336{337	struct vmmdev_hgcm_pagelist *dst_pg_lst;338	struct page *page;339	bool is_vmalloc;340	u32 i, page_count;341 342	dst_parm->type = type;343 344	if (len == 0) {345		dst_parm->u.pointer.size = 0;346		dst_parm->u.pointer.u.linear_addr = 0;347		return;348	}349 350	dst_pg_lst = (void *)call + *off_extra;351	page_count = hgcm_call_buf_size_in_pages(buf, len);352	is_vmalloc = is_vmalloc_addr(buf);353 354	dst_parm->type = VMMDEV_HGCM_PARM_TYPE_PAGELIST;355	dst_parm->u.page_list.size = len;356	dst_parm->u.page_list.offset = *off_extra;357	dst_pg_lst->flags = hgcm_call_linear_addr_type_to_pagelist_flags(type);358	dst_pg_lst->offset_first_page = (unsigned long)buf & ~PAGE_MASK;359	dst_pg_lst->page_count = page_count;360 361	for (i = 0; i < page_count; i++) {362		if (is_vmalloc)363			page = vmalloc_to_page(buf);364		else365			page = virt_to_page(buf);366 367		dst_pg_lst->pages[i] = page_to_phys(page);368		buf += PAGE_SIZE;369	}370 371	*off_extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]);372}373 374/**375 * hgcm_call_init_call - Initializes the call request that we're sending376 *	to the host.377 * @call:            The call to initialize.378 * @client_id:       The client ID of the caller.379 * @function:        The function number of the function to call.380 * @src_parm:        Pointer to source function call parameters.381 * @parm_count:      Number of function call parameters.382 * @bounce_bufs:     The bouncebuffer array.383 */384static void hgcm_call_init_call(385	struct vmmdev_hgcm_call *call, u32 client_id, u32 function,386	const struct vmmdev_hgcm_function_parameter *src_parm,387	u32 parm_count, void **bounce_bufs)388{389	struct vmmdev_hgcm_function_parameter *dst_parm =390		VMMDEV_HGCM_CALL_PARMS(call);391	u32 i, off_extra = (uintptr_t)(dst_parm + parm_count) - (uintptr_t)call;392	void *buf;393 394	call->header.flags = 0;395	call->header.result = VINF_SUCCESS;396	call->client_id = client_id;397	call->function = function;398	call->parm_count = parm_count;399 400	for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) {401		switch (src_parm->type) {402		case VMMDEV_HGCM_PARM_TYPE_32BIT:403		case VMMDEV_HGCM_PARM_TYPE_64BIT:404			*dst_parm = *src_parm;405			break;406 407		case VMMDEV_HGCM_PARM_TYPE_LINADDR:408		case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:409		case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:410			hgcm_call_init_linaddr(call, dst_parm, bounce_bufs[i],411					       src_parm->u.pointer.size,412					       src_parm->type, &off_extra);413			break;414 415		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:416		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:417		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:418			buf = (void *)src_parm->u.pointer.u.linear_addr;419			hgcm_call_init_linaddr(call, dst_parm, buf,420					       src_parm->u.pointer.size,421					       src_parm->type, &off_extra);422			break;423 424		default:425			WARN_ON(1);426			dst_parm->type = VMMDEV_HGCM_PARM_TYPE_INVALID;427		}428	}429}430 431/**432 * hgcm_cancel_call - Tries to cancel a pending HGCM call.433 * @gdev:        The VBoxGuest device extension.434 * @call:        The call to cancel.435 *436 * Return: VBox status code437 */438static int hgcm_cancel_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call)439{440	int rc;441 442	/*443	 * We use a pre-allocated request for cancellations, which is444	 * protected by cancel_req_mutex. This means that all cancellations445	 * get serialized, this should be fine since they should be rare.446	 */447	mutex_lock(&gdev->cancel_req_mutex);448	gdev->cancel_req->phys_req_to_cancel = virt_to_phys(call);449	rc = vbg_req_perform(gdev, gdev->cancel_req);450	mutex_unlock(&gdev->cancel_req_mutex);451 452	if (rc == VERR_NOT_IMPLEMENTED) {453		call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED;454		call->header.header.request_type = VMMDEVREQ_HGCM_CANCEL;455 456		rc = vbg_req_perform(gdev, call);457		if (rc == VERR_INVALID_PARAMETER)458			rc = VERR_NOT_FOUND;459	}460 461	if (rc >= 0)462		call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED;463 464	return rc;465}466 467/**468 * vbg_hgcm_do_call - Performs the call and completion wait.469 * @gdev:        The VBoxGuest device extension.470 * @call:        The call to execute.471 * @timeout_ms:  Timeout in ms.472 * @interruptible: whether this call is interruptible473 * @leak_it:     Where to return the leak it / free it, indicator.474 *               Cancellation fun.475 *476 * Return: %0 or negative errno value.477 */478static int vbg_hgcm_do_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call,479			    u32 timeout_ms, bool interruptible, bool *leak_it)480{481	int rc, cancel_rc, ret;482	long timeout;483 484	*leak_it = false;485 486	rc = vbg_req_perform(gdev, call);487 488	/*489	 * If the call failed, then pretend success. Upper layers will490	 * interpret the result code in the packet.491	 */492	if (rc < 0) {493		call->header.result = rc;494		return 0;495	}496 497	if (rc != VINF_HGCM_ASYNC_EXECUTE)498		return 0;499 500	/* Host decided to process the request asynchronously, wait for it */501	if (timeout_ms == U32_MAX)502		timeout = MAX_SCHEDULE_TIMEOUT;503	else504		timeout = msecs_to_jiffies(timeout_ms);505 506	if (interruptible) {507		timeout = wait_event_interruptible_timeout(gdev->hgcm_wq,508							   hgcm_req_done(gdev, &call->header),509							   timeout);510	} else {511		timeout = wait_event_timeout(gdev->hgcm_wq,512					     hgcm_req_done(gdev, &call->header),513					     timeout);514	}515 516	/* timeout > 0 means hgcm_req_done has returned true, so success */517	if (timeout > 0)518		return 0;519 520	if (timeout == 0)521		ret = -ETIMEDOUT;522	else523		ret = -EINTR;524 525	/* Cancel the request */526	cancel_rc = hgcm_cancel_call(gdev, call);527	if (cancel_rc >= 0)528		return ret;529 530	/*531	 * Failed to cancel, this should mean that the cancel has lost the532	 * race with normal completion, wait while the host completes it.533	 */534	if (cancel_rc == VERR_NOT_FOUND || cancel_rc == VERR_SEM_DESTROYED)535		timeout = msecs_to_jiffies(500);536	else537		timeout = msecs_to_jiffies(2000);538 539	timeout = wait_event_timeout(gdev->hgcm_wq,540				     hgcm_req_done(gdev, &call->header),541				     timeout);542 543	if (WARN_ON(timeout == 0)) {544		/* We really should never get here */545		vbg_err("%s: Call timedout and cancellation failed, leaking the request\n",546			__func__);547		*leak_it = true;548		return ret;549	}550 551	/* The call has completed normally after all */552	return 0;553}554 555/**556 * hgcm_call_copy_back_result - Copies the result of the call back to557 *	the caller info structure and user buffers.558 * @call:            HGCM call request.559 * @dst_parm:        Pointer to function call parameters destination.560 * @parm_count:      Number of function call parameters.561 * @bounce_bufs:     The bouncebuffer array.562 *563 * Return: %0 or negative errno value.564 */565static int hgcm_call_copy_back_result(566	const struct vmmdev_hgcm_call *call,567	struct vmmdev_hgcm_function_parameter *dst_parm,568	u32 parm_count, void **bounce_bufs)569{570	const struct vmmdev_hgcm_function_parameter *src_parm =571		VMMDEV_HGCM_CALL_PARMS(call);572	void __user *p;573	int ret;574	u32 i;575 576	/* Copy back parameters. */577	for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) {578		switch (dst_parm->type) {579		case VMMDEV_HGCM_PARM_TYPE_32BIT:580		case VMMDEV_HGCM_PARM_TYPE_64BIT:581			*dst_parm = *src_parm;582			break;583 584		case VMMDEV_HGCM_PARM_TYPE_PAGELIST:585			dst_parm->u.page_list.size = src_parm->u.page_list.size;586			break;587 588		case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:589		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:590		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:591		case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:592			dst_parm->u.pointer.size = src_parm->u.pointer.size;593			break;594 595		case VMMDEV_HGCM_PARM_TYPE_LINADDR:596		case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:597			dst_parm->u.pointer.size = src_parm->u.pointer.size;598 599			p = (void __user *)dst_parm->u.pointer.u.linear_addr;600			ret = copy_to_user(p, bounce_bufs[i],601					   min(src_parm->u.pointer.size,602					       dst_parm->u.pointer.size));603			if (ret)604				return -EFAULT;605			break;606 607		default:608			WARN_ON(1);609			return -EINVAL;610		}611	}612 613	return 0;614}615 616int vbg_hgcm_call(struct vbg_dev *gdev, u32 requestor, u32 client_id,617		  u32 function, u32 timeout_ms,618		  struct vmmdev_hgcm_function_parameter *parms, u32 parm_count,619		  int *vbox_status)620{621	struct vmmdev_hgcm_call *call;622	void **bounce_bufs = NULL;623	bool leak_it;624	size_t size;625	int i, ret;626 627	size = sizeof(struct vmmdev_hgcm_call) +628		   parm_count * sizeof(struct vmmdev_hgcm_function_parameter);629	/*630	 * Validate and buffer the parameters for the call. This also increases631	 * call_size with the amount of extra space needed for page lists.632	 */633	ret = hgcm_call_preprocess(parms, parm_count, &bounce_bufs, &size);634	if (ret) {635		/* Even on error bounce bufs may still have been allocated */636		goto free_bounce_bufs;637	}638 639	call = vbg_req_alloc(size, VMMDEVREQ_HGCM_CALL, requestor);640	if (!call) {641		ret = -ENOMEM;642		goto free_bounce_bufs;643	}644 645	hgcm_call_init_call(call, client_id, function, parms, parm_count,646			    bounce_bufs);647 648	ret = vbg_hgcm_do_call(gdev, call, timeout_ms,649			       requestor & VMMDEV_REQUESTOR_USERMODE, &leak_it);650	if (ret == 0) {651		*vbox_status = call->header.result;652		ret = hgcm_call_copy_back_result(call, parms, parm_count,653						 bounce_bufs);654	}655 656	if (!leak_it)657		vbg_req_free(call, size);658 659free_bounce_bufs:660	if (bounce_bufs) {661		for (i = 0; i < parm_count; i++)662			kvfree(bounce_bufs[i]);663		kfree(bounce_bufs);664	}665 666	return ret;667}668EXPORT_SYMBOL(vbg_hgcm_call);669 670#ifdef CONFIG_COMPAT671int vbg_hgcm_call32(672	struct vbg_dev *gdev, u32 requestor, u32 client_id, u32 function,673	u32 timeout_ms, struct vmmdev_hgcm_function_parameter32 *parm32,674	u32 parm_count, int *vbox_status)675{676	struct vmmdev_hgcm_function_parameter *parm64 = NULL;677	u32 i, size;678	int ret = 0;679 680	/* KISS allocate a temporary request and convert the parameters. */681	size = parm_count * sizeof(struct vmmdev_hgcm_function_parameter);682	parm64 = kzalloc(size, GFP_KERNEL);683	if (!parm64)684		return -ENOMEM;685 686	for (i = 0; i < parm_count; i++) {687		switch (parm32[i].type) {688		case VMMDEV_HGCM_PARM_TYPE_32BIT:689			parm64[i].type = VMMDEV_HGCM_PARM_TYPE_32BIT;690			parm64[i].u.value32 = parm32[i].u.value32;691			break;692 693		case VMMDEV_HGCM_PARM_TYPE_64BIT:694			parm64[i].type = VMMDEV_HGCM_PARM_TYPE_64BIT;695			parm64[i].u.value64 = parm32[i].u.value64;696			break;697 698		case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:699		case VMMDEV_HGCM_PARM_TYPE_LINADDR:700		case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:701			parm64[i].type = parm32[i].type;702			parm64[i].u.pointer.size = parm32[i].u.pointer.size;703			parm64[i].u.pointer.u.linear_addr =704			    parm32[i].u.pointer.u.linear_addr;705			break;706 707		default:708			ret = -EINVAL;709		}710		if (ret < 0)711			goto out_free;712	}713 714	ret = vbg_hgcm_call(gdev, requestor, client_id, function, timeout_ms,715			    parm64, parm_count, vbox_status);716	if (ret < 0)717		goto out_free;718 719	/* Copy back. */720	for (i = 0; i < parm_count; i++, parm32++, parm64++) {721		switch (parm64[i].type) {722		case VMMDEV_HGCM_PARM_TYPE_32BIT:723			parm32[i].u.value32 = parm64[i].u.value32;724			break;725 726		case VMMDEV_HGCM_PARM_TYPE_64BIT:727			parm32[i].u.value64 = parm64[i].u.value64;728			break;729 730		case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:731		case VMMDEV_HGCM_PARM_TYPE_LINADDR:732		case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:733			parm32[i].u.pointer.size = parm64[i].u.pointer.size;734			break;735 736		default:737			WARN_ON(1);738			ret = -EINVAL;739		}740	}741 742out_free:743	kfree(parm64);744	return ret;745}746#endif747 748static const int vbg_status_code_to_errno_table[] = {749	[-VERR_ACCESS_DENIED]                            = -EPERM,750	[-VERR_FILE_NOT_FOUND]                           = -ENOENT,751	[-VERR_PROCESS_NOT_FOUND]                        = -ESRCH,752	[-VERR_INTERRUPTED]                              = -EINTR,753	[-VERR_DEV_IO_ERROR]                             = -EIO,754	[-VERR_TOO_MUCH_DATA]                            = -E2BIG,755	[-VERR_BAD_EXE_FORMAT]                           = -ENOEXEC,756	[-VERR_INVALID_HANDLE]                           = -EBADF,757	[-VERR_TRY_AGAIN]                                = -EAGAIN,758	[-VERR_NO_MEMORY]                                = -ENOMEM,759	[-VERR_INVALID_POINTER]                          = -EFAULT,760	[-VERR_RESOURCE_BUSY]                            = -EBUSY,761	[-VERR_ALREADY_EXISTS]                           = -EEXIST,762	[-VERR_NOT_SAME_DEVICE]                          = -EXDEV,763	[-VERR_NOT_A_DIRECTORY]                          = -ENOTDIR,764	[-VERR_PATH_NOT_FOUND]                           = -ENOTDIR,765	[-VERR_INVALID_NAME]                             = -ENOENT,766	[-VERR_IS_A_DIRECTORY]                           = -EISDIR,767	[-VERR_INVALID_PARAMETER]                        = -EINVAL,768	[-VERR_TOO_MANY_OPEN_FILES]                      = -ENFILE,769	[-VERR_INVALID_FUNCTION]                         = -ENOTTY,770	[-VERR_SHARING_VIOLATION]                        = -ETXTBSY,771	[-VERR_FILE_TOO_BIG]                             = -EFBIG,772	[-VERR_DISK_FULL]                                = -ENOSPC,773	[-VERR_SEEK_ON_DEVICE]                           = -ESPIPE,774	[-VERR_WRITE_PROTECT]                            = -EROFS,775	[-VERR_BROKEN_PIPE]                              = -EPIPE,776	[-VERR_DEADLOCK]                                 = -EDEADLK,777	[-VERR_FILENAME_TOO_LONG]                        = -ENAMETOOLONG,778	[-VERR_FILE_LOCK_FAILED]                         = -ENOLCK,779	[-VERR_NOT_IMPLEMENTED]                          = -ENOSYS,780	[-VERR_NOT_SUPPORTED]                            = -ENOSYS,781	[-VERR_DIR_NOT_EMPTY]                            = -ENOTEMPTY,782	[-VERR_TOO_MANY_SYMLINKS]                        = -ELOOP,783	[-VERR_NO_MORE_FILES]				 = -ENODATA,784	[-VERR_NO_DATA]                                  = -ENODATA,785	[-VERR_NET_NO_NETWORK]                           = -ENONET,786	[-VERR_NET_NOT_UNIQUE_NAME]                      = -ENOTUNIQ,787	[-VERR_NO_TRANSLATION]                           = -EILSEQ,788	[-VERR_NET_NOT_SOCKET]                           = -ENOTSOCK,789	[-VERR_NET_DEST_ADDRESS_REQUIRED]                = -EDESTADDRREQ,790	[-VERR_NET_MSG_SIZE]                             = -EMSGSIZE,791	[-VERR_NET_PROTOCOL_TYPE]                        = -EPROTOTYPE,792	[-VERR_NET_PROTOCOL_NOT_AVAILABLE]               = -ENOPROTOOPT,793	[-VERR_NET_PROTOCOL_NOT_SUPPORTED]               = -EPROTONOSUPPORT,794	[-VERR_NET_SOCKET_TYPE_NOT_SUPPORTED]            = -ESOCKTNOSUPPORT,795	[-VERR_NET_OPERATION_NOT_SUPPORTED]              = -EOPNOTSUPP,796	[-VERR_NET_PROTOCOL_FAMILY_NOT_SUPPORTED]        = -EPFNOSUPPORT,797	[-VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED]         = -EAFNOSUPPORT,798	[-VERR_NET_ADDRESS_IN_USE]                       = -EADDRINUSE,799	[-VERR_NET_ADDRESS_NOT_AVAILABLE]                = -EADDRNOTAVAIL,800	[-VERR_NET_DOWN]                                 = -ENETDOWN,801	[-VERR_NET_UNREACHABLE]                          = -ENETUNREACH,802	[-VERR_NET_CONNECTION_RESET]                     = -ENETRESET,803	[-VERR_NET_CONNECTION_ABORTED]                   = -ECONNABORTED,804	[-VERR_NET_CONNECTION_RESET_BY_PEER]             = -ECONNRESET,805	[-VERR_NET_NO_BUFFER_SPACE]                      = -ENOBUFS,806	[-VERR_NET_ALREADY_CONNECTED]                    = -EISCONN,807	[-VERR_NET_NOT_CONNECTED]                        = -ENOTCONN,808	[-VERR_NET_SHUTDOWN]                             = -ESHUTDOWN,809	[-VERR_NET_TOO_MANY_REFERENCES]                  = -ETOOMANYREFS,810	[-VERR_TIMEOUT]                                  = -ETIMEDOUT,811	[-VERR_NET_CONNECTION_REFUSED]                   = -ECONNREFUSED,812	[-VERR_NET_HOST_DOWN]                            = -EHOSTDOWN,813	[-VERR_NET_HOST_UNREACHABLE]                     = -EHOSTUNREACH,814	[-VERR_NET_ALREADY_IN_PROGRESS]                  = -EALREADY,815	[-VERR_NET_IN_PROGRESS]                          = -EINPROGRESS,816	[-VERR_MEDIA_NOT_PRESENT]                        = -ENOMEDIUM,817	[-VERR_MEDIA_NOT_RECOGNIZED]                     = -EMEDIUMTYPE,818};819 820int vbg_status_code_to_errno(int rc)821{822	if (rc >= 0)823		return 0;824 825	rc = -rc;826	if (rc >= ARRAY_SIZE(vbg_status_code_to_errno_table) ||827	    vbg_status_code_to_errno_table[rc] == 0) {828		vbg_warn("%s: Unhandled err %d\n", __func__, -rc);829		return -EPROTO;830	}831 832	return vbg_status_code_to_errno_table[rc];833}834EXPORT_SYMBOL(vbg_status_code_to_errno);835