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