162306a36Sopenharmony_ci/* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vboxguest vmm-req and hgcm-call code, VBoxGuestR0LibHGCMInternal.cpp, 462306a36Sopenharmony_ci * VBoxGuestR0LibGenericRequest.cpp and RTErrConvertToErrno.cpp in vbox svn. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2006-2016 Oracle Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/sizes.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/uaccess.h> 1762306a36Sopenharmony_ci#include <linux/vmalloc.h> 1862306a36Sopenharmony_ci#include <linux/vbox_err.h> 1962306a36Sopenharmony_ci#include <linux/vbox_utils.h> 2062306a36Sopenharmony_ci#include "vboxguest_core.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Get the pointer to the first parameter of a HGCM call request. */ 2362306a36Sopenharmony_ci#define VMMDEV_HGCM_CALL_PARMS(a) \ 2462306a36Sopenharmony_ci ((struct vmmdev_hgcm_function_parameter *)( \ 2562306a36Sopenharmony_ci (u8 *)(a) + sizeof(struct vmmdev_hgcm_call))) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* The max parameter buffer size for a user request. */ 2862306a36Sopenharmony_ci#define VBG_MAX_HGCM_USER_PARM (24 * SZ_1M) 2962306a36Sopenharmony_ci/* The max parameter buffer size for a kernel request. */ 3062306a36Sopenharmony_ci#define VBG_MAX_HGCM_KERNEL_PARM (16 * SZ_1M) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define VBG_DEBUG_PORT 0x504 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* This protects vbg_log_buf and serializes VBG_DEBUG_PORT accesses */ 3562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(vbg_log_lock); 3662306a36Sopenharmony_cistatic char vbg_log_buf[128]; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define VBG_LOG(name, pr_func) \ 3962306a36Sopenharmony_civoid name(const char *fmt, ...) \ 4062306a36Sopenharmony_ci{ \ 4162306a36Sopenharmony_ci unsigned long flags; \ 4262306a36Sopenharmony_ci va_list args; \ 4362306a36Sopenharmony_ci int i, count; \ 4462306a36Sopenharmony_ci \ 4562306a36Sopenharmony_ci va_start(args, fmt); \ 4662306a36Sopenharmony_ci spin_lock_irqsave(&vbg_log_lock, flags); \ 4762306a36Sopenharmony_ci \ 4862306a36Sopenharmony_ci count = vscnprintf(vbg_log_buf, sizeof(vbg_log_buf), fmt, args);\ 4962306a36Sopenharmony_ci for (i = 0; i < count; i++) \ 5062306a36Sopenharmony_ci outb(vbg_log_buf[i], VBG_DEBUG_PORT); \ 5162306a36Sopenharmony_ci \ 5262306a36Sopenharmony_ci pr_func("%s", vbg_log_buf); \ 5362306a36Sopenharmony_ci \ 5462306a36Sopenharmony_ci spin_unlock_irqrestore(&vbg_log_lock, flags); \ 5562306a36Sopenharmony_ci va_end(args); \ 5662306a36Sopenharmony_ci} \ 5762306a36Sopenharmony_ciEXPORT_SYMBOL(name) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciVBG_LOG(vbg_info, pr_info); 6062306a36Sopenharmony_ciVBG_LOG(vbg_warn, pr_warn); 6162306a36Sopenharmony_ciVBG_LOG(vbg_err, pr_err); 6262306a36Sopenharmony_ciVBG_LOG(vbg_err_ratelimited, pr_err_ratelimited); 6362306a36Sopenharmony_ci#if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) 6462306a36Sopenharmony_ciVBG_LOG(vbg_debug, pr_debug); 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_civoid *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type, 6862306a36Sopenharmony_ci u32 requestor) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct vmmdev_request_header *req; 7162306a36Sopenharmony_ci int order = get_order(PAGE_ALIGN(len)); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci req = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order); 7462306a36Sopenharmony_ci if (!req) 7562306a36Sopenharmony_ci return NULL; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci memset(req, 0xaa, len); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci req->size = len; 8062306a36Sopenharmony_ci req->version = VMMDEV_REQUEST_HEADER_VERSION; 8162306a36Sopenharmony_ci req->request_type = req_type; 8262306a36Sopenharmony_ci req->rc = VERR_GENERAL_FAILURE; 8362306a36Sopenharmony_ci req->reserved1 = 0; 8462306a36Sopenharmony_ci req->requestor = requestor; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return req; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_civoid vbg_req_free(void *req, size_t len) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci if (!req) 9262306a36Sopenharmony_ci return; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci free_pages((unsigned long)req, get_order(PAGE_ALIGN(len))); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Note this function returns a VBox status code, not a negative errno!! */ 9862306a36Sopenharmony_ciint vbg_req_perform(struct vbg_dev *gdev, void *req) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned long phys_req = virt_to_phys(req); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci outl(phys_req, gdev->io_port + VMMDEV_PORT_OFF_REQUEST); 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * The host changes the request as a result of the outl, make sure 10562306a36Sopenharmony_ci * the outl and any reads of the req happen in the correct order. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci mb(); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return ((struct vmmdev_request_header *)req)->rc; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic bool hgcm_req_done(struct vbg_dev *gdev, 11362306a36Sopenharmony_ci struct vmmdev_hgcmreq_header *header) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci unsigned long flags; 11662306a36Sopenharmony_ci bool done; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci spin_lock_irqsave(&gdev->event_spinlock, flags); 11962306a36Sopenharmony_ci done = header->flags & VMMDEV_HGCM_REQ_DONE; 12062306a36Sopenharmony_ci spin_unlock_irqrestore(&gdev->event_spinlock, flags); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return done; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciint vbg_hgcm_connect(struct vbg_dev *gdev, u32 requestor, 12662306a36Sopenharmony_ci struct vmmdev_hgcm_service_location *loc, 12762306a36Sopenharmony_ci u32 *client_id, int *vbox_status) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct vmmdev_hgcm_connect *hgcm_connect = NULL; 13062306a36Sopenharmony_ci int rc; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci hgcm_connect = vbg_req_alloc(sizeof(*hgcm_connect), 13362306a36Sopenharmony_ci VMMDEVREQ_HGCM_CONNECT, requestor); 13462306a36Sopenharmony_ci if (!hgcm_connect) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci hgcm_connect->header.flags = 0; 13862306a36Sopenharmony_ci memcpy(&hgcm_connect->loc, loc, sizeof(*loc)); 13962306a36Sopenharmony_ci hgcm_connect->client_id = 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci rc = vbg_req_perform(gdev, hgcm_connect); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (rc == VINF_HGCM_ASYNC_EXECUTE) 14462306a36Sopenharmony_ci wait_event(gdev->hgcm_wq, 14562306a36Sopenharmony_ci hgcm_req_done(gdev, &hgcm_connect->header)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (rc >= 0) { 14862306a36Sopenharmony_ci *client_id = hgcm_connect->client_id; 14962306a36Sopenharmony_ci rc = hgcm_connect->header.result; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci vbg_req_free(hgcm_connect, sizeof(*hgcm_connect)); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci *vbox_status = rc; 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ciEXPORT_SYMBOL(vbg_hgcm_connect); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciint vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 requestor, 16062306a36Sopenharmony_ci u32 client_id, int *vbox_status) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct vmmdev_hgcm_disconnect *hgcm_disconnect = NULL; 16362306a36Sopenharmony_ci int rc; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci hgcm_disconnect = vbg_req_alloc(sizeof(*hgcm_disconnect), 16662306a36Sopenharmony_ci VMMDEVREQ_HGCM_DISCONNECT, 16762306a36Sopenharmony_ci requestor); 16862306a36Sopenharmony_ci if (!hgcm_disconnect) 16962306a36Sopenharmony_ci return -ENOMEM; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci hgcm_disconnect->header.flags = 0; 17262306a36Sopenharmony_ci hgcm_disconnect->client_id = client_id; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci rc = vbg_req_perform(gdev, hgcm_disconnect); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (rc == VINF_HGCM_ASYNC_EXECUTE) 17762306a36Sopenharmony_ci wait_event(gdev->hgcm_wq, 17862306a36Sopenharmony_ci hgcm_req_done(gdev, &hgcm_disconnect->header)); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (rc >= 0) 18162306a36Sopenharmony_ci rc = hgcm_disconnect->header.result; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci vbg_req_free(hgcm_disconnect, sizeof(*hgcm_disconnect)); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci *vbox_status = rc; 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ciEXPORT_SYMBOL(vbg_hgcm_disconnect); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic u32 hgcm_call_buf_size_in_pages(void *buf, u32 len) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 size = PAGE_ALIGN(len + ((unsigned long)buf & ~PAGE_MASK)); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return size >> PAGE_SHIFT; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void hgcm_call_add_pagelist_size(void *buf, u32 len, size_t *extra) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci u32 page_count; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci page_count = hgcm_call_buf_size_in_pages(buf, len); 20262306a36Sopenharmony_ci *extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int hgcm_call_preprocess_linaddr( 20662306a36Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm, 20762306a36Sopenharmony_ci void **bounce_buf_ret, size_t *extra) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci void *buf, *bounce_buf; 21062306a36Sopenharmony_ci bool copy_in; 21162306a36Sopenharmony_ci u32 len; 21262306a36Sopenharmony_ci int ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci buf = (void *)src_parm->u.pointer.u.linear_addr; 21562306a36Sopenharmony_ci len = src_parm->u.pointer.size; 21662306a36Sopenharmony_ci copy_in = src_parm->type != VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (len > VBG_MAX_HGCM_USER_PARM) 21962306a36Sopenharmony_ci return -E2BIG; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci bounce_buf = kvmalloc(len, GFP_KERNEL); 22262306a36Sopenharmony_ci if (!bounce_buf) 22362306a36Sopenharmony_ci return -ENOMEM; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci *bounce_buf_ret = bounce_buf; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (copy_in) { 22862306a36Sopenharmony_ci ret = copy_from_user(bounce_buf, (void __user *)buf, len); 22962306a36Sopenharmony_ci if (ret) 23062306a36Sopenharmony_ci return -EFAULT; 23162306a36Sopenharmony_ci } else { 23262306a36Sopenharmony_ci memset(bounce_buf, 0, len); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci hgcm_call_add_pagelist_size(bounce_buf, len, extra); 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/** 24062306a36Sopenharmony_ci * Preprocesses the HGCM call, validate parameters, alloc bounce buffers and 24162306a36Sopenharmony_ci * figure out how much extra storage we need for page lists. 24262306a36Sopenharmony_ci * Return: 0 or negative errno value. 24362306a36Sopenharmony_ci * @src_parm: Pointer to source function call parameters 24462306a36Sopenharmony_ci * @parm_count: Number of function call parameters. 24562306a36Sopenharmony_ci * @bounce_bufs_ret: Where to return the allocated bouncebuffer array 24662306a36Sopenharmony_ci * @extra: Where to return the extra request space needed for 24762306a36Sopenharmony_ci * physical page lists. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_cistatic int hgcm_call_preprocess( 25062306a36Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm, 25162306a36Sopenharmony_ci u32 parm_count, void ***bounce_bufs_ret, size_t *extra) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci void *buf, **bounce_bufs = NULL; 25462306a36Sopenharmony_ci u32 i, len; 25562306a36Sopenharmony_ci int ret; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci for (i = 0; i < parm_count; i++, src_parm++) { 25862306a36Sopenharmony_ci switch (src_parm->type) { 25962306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 26062306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 26462306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 26562306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 26662306a36Sopenharmony_ci if (!bounce_bufs) { 26762306a36Sopenharmony_ci bounce_bufs = kcalloc(parm_count, 26862306a36Sopenharmony_ci sizeof(void *), 26962306a36Sopenharmony_ci GFP_KERNEL); 27062306a36Sopenharmony_ci if (!bounce_bufs) 27162306a36Sopenharmony_ci return -ENOMEM; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci *bounce_bufs_ret = bounce_bufs; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ret = hgcm_call_preprocess_linaddr(src_parm, 27762306a36Sopenharmony_ci &bounce_bufs[i], 27862306a36Sopenharmony_ci extra); 27962306a36Sopenharmony_ci if (ret) 28062306a36Sopenharmony_ci return ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 28562306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 28662306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 28762306a36Sopenharmony_ci buf = (void *)src_parm->u.pointer.u.linear_addr; 28862306a36Sopenharmony_ci len = src_parm->u.pointer.size; 28962306a36Sopenharmony_ci if (WARN_ON(len > VBG_MAX_HGCM_KERNEL_PARM)) 29062306a36Sopenharmony_ci return -E2BIG; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci hgcm_call_add_pagelist_size(buf, len, extra); 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci default: 29662306a36Sopenharmony_ci return -EINVAL; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/** 30462306a36Sopenharmony_ci * Translates linear address types to page list direction flags. 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * Return: page list flags. 30762306a36Sopenharmony_ci * @type: The type. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic u32 hgcm_call_linear_addr_type_to_pagelist_flags( 31062306a36Sopenharmony_ci enum vmmdev_hgcm_function_parameter_type type) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci switch (type) { 31362306a36Sopenharmony_ci default: 31462306a36Sopenharmony_ci WARN_ON(1); 31562306a36Sopenharmony_ci fallthrough; 31662306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 31762306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 31862306a36Sopenharmony_ci return VMMDEV_HGCM_F_PARM_DIRECTION_BOTH; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 32162306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 32262306a36Sopenharmony_ci return VMMDEV_HGCM_F_PARM_DIRECTION_TO_HOST; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 32562306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 32662306a36Sopenharmony_ci return VMMDEV_HGCM_F_PARM_DIRECTION_FROM_HOST; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void hgcm_call_init_linaddr(struct vmmdev_hgcm_call *call, 33162306a36Sopenharmony_ci struct vmmdev_hgcm_function_parameter *dst_parm, void *buf, u32 len, 33262306a36Sopenharmony_ci enum vmmdev_hgcm_function_parameter_type type, u32 *off_extra) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct vmmdev_hgcm_pagelist *dst_pg_lst; 33562306a36Sopenharmony_ci struct page *page; 33662306a36Sopenharmony_ci bool is_vmalloc; 33762306a36Sopenharmony_ci u32 i, page_count; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci dst_parm->type = type; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (len == 0) { 34262306a36Sopenharmony_ci dst_parm->u.pointer.size = 0; 34362306a36Sopenharmony_ci dst_parm->u.pointer.u.linear_addr = 0; 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci dst_pg_lst = (void *)call + *off_extra; 34862306a36Sopenharmony_ci page_count = hgcm_call_buf_size_in_pages(buf, len); 34962306a36Sopenharmony_ci is_vmalloc = is_vmalloc_addr(buf); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci dst_parm->type = VMMDEV_HGCM_PARM_TYPE_PAGELIST; 35262306a36Sopenharmony_ci dst_parm->u.page_list.size = len; 35362306a36Sopenharmony_ci dst_parm->u.page_list.offset = *off_extra; 35462306a36Sopenharmony_ci dst_pg_lst->flags = hgcm_call_linear_addr_type_to_pagelist_flags(type); 35562306a36Sopenharmony_ci dst_pg_lst->offset_first_page = (unsigned long)buf & ~PAGE_MASK; 35662306a36Sopenharmony_ci dst_pg_lst->page_count = page_count; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci for (i = 0; i < page_count; i++) { 35962306a36Sopenharmony_ci if (is_vmalloc) 36062306a36Sopenharmony_ci page = vmalloc_to_page(buf); 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci page = virt_to_page(buf); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci dst_pg_lst->pages[i] = page_to_phys(page); 36562306a36Sopenharmony_ci buf += PAGE_SIZE; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci *off_extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/** 37262306a36Sopenharmony_ci * Initializes the call request that we're sending to the host. 37362306a36Sopenharmony_ci * @call: The call to initialize. 37462306a36Sopenharmony_ci * @client_id: The client ID of the caller. 37562306a36Sopenharmony_ci * @function: The function number of the function to call. 37662306a36Sopenharmony_ci * @src_parm: Pointer to source function call parameters. 37762306a36Sopenharmony_ci * @parm_count: Number of function call parameters. 37862306a36Sopenharmony_ci * @bounce_bufs: The bouncebuffer array. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic void hgcm_call_init_call( 38162306a36Sopenharmony_ci struct vmmdev_hgcm_call *call, u32 client_id, u32 function, 38262306a36Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm, 38362306a36Sopenharmony_ci u32 parm_count, void **bounce_bufs) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct vmmdev_hgcm_function_parameter *dst_parm = 38662306a36Sopenharmony_ci VMMDEV_HGCM_CALL_PARMS(call); 38762306a36Sopenharmony_ci u32 i, off_extra = (uintptr_t)(dst_parm + parm_count) - (uintptr_t)call; 38862306a36Sopenharmony_ci void *buf; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci call->header.flags = 0; 39162306a36Sopenharmony_ci call->header.result = VINF_SUCCESS; 39262306a36Sopenharmony_ci call->client_id = client_id; 39362306a36Sopenharmony_ci call->function = function; 39462306a36Sopenharmony_ci call->parm_count = parm_count; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) { 39762306a36Sopenharmony_ci switch (src_parm->type) { 39862306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 39962306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 40062306a36Sopenharmony_ci *dst_parm = *src_parm; 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 40462306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 40562306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 40662306a36Sopenharmony_ci hgcm_call_init_linaddr(call, dst_parm, bounce_bufs[i], 40762306a36Sopenharmony_ci src_parm->u.pointer.size, 40862306a36Sopenharmony_ci src_parm->type, &off_extra); 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 41262306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 41362306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 41462306a36Sopenharmony_ci buf = (void *)src_parm->u.pointer.u.linear_addr; 41562306a36Sopenharmony_ci hgcm_call_init_linaddr(call, dst_parm, buf, 41662306a36Sopenharmony_ci src_parm->u.pointer.size, 41762306a36Sopenharmony_ci src_parm->type, &off_extra); 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci default: 42162306a36Sopenharmony_ci WARN_ON(1); 42262306a36Sopenharmony_ci dst_parm->type = VMMDEV_HGCM_PARM_TYPE_INVALID; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/** 42862306a36Sopenharmony_ci * Tries to cancel a pending HGCM call. 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * Return: VBox status code 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_cistatic int hgcm_cancel_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci int rc; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * We use a pre-allocated request for cancellations, which is 43862306a36Sopenharmony_ci * protected by cancel_req_mutex. This means that all cancellations 43962306a36Sopenharmony_ci * get serialized, this should be fine since they should be rare. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci mutex_lock(&gdev->cancel_req_mutex); 44262306a36Sopenharmony_ci gdev->cancel_req->phys_req_to_cancel = virt_to_phys(call); 44362306a36Sopenharmony_ci rc = vbg_req_perform(gdev, gdev->cancel_req); 44462306a36Sopenharmony_ci mutex_unlock(&gdev->cancel_req_mutex); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (rc == VERR_NOT_IMPLEMENTED) { 44762306a36Sopenharmony_ci call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED; 44862306a36Sopenharmony_ci call->header.header.request_type = VMMDEVREQ_HGCM_CANCEL; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci rc = vbg_req_perform(gdev, call); 45162306a36Sopenharmony_ci if (rc == VERR_INVALID_PARAMETER) 45262306a36Sopenharmony_ci rc = VERR_NOT_FOUND; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (rc >= 0) 45662306a36Sopenharmony_ci call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return rc; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/** 46262306a36Sopenharmony_ci * Performs the call and completion wait. 46362306a36Sopenharmony_ci * Return: 0 or negative errno value. 46462306a36Sopenharmony_ci * @gdev: The VBoxGuest device extension. 46562306a36Sopenharmony_ci * @call: The call to execute. 46662306a36Sopenharmony_ci * @timeout_ms: Timeout in ms. 46762306a36Sopenharmony_ci * @leak_it: Where to return the leak it / free it, indicator. 46862306a36Sopenharmony_ci * Cancellation fun. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_cistatic int vbg_hgcm_do_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call, 47162306a36Sopenharmony_ci u32 timeout_ms, bool interruptible, bool *leak_it) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int rc, cancel_rc, ret; 47462306a36Sopenharmony_ci long timeout; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci *leak_it = false; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci rc = vbg_req_perform(gdev, call); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * If the call failed, then pretend success. Upper layers will 48262306a36Sopenharmony_ci * interpret the result code in the packet. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci if (rc < 0) { 48562306a36Sopenharmony_ci call->header.result = rc; 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (rc != VINF_HGCM_ASYNC_EXECUTE) 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Host decided to process the request asynchronously, wait for it */ 49362306a36Sopenharmony_ci if (timeout_ms == U32_MAX) 49462306a36Sopenharmony_ci timeout = MAX_SCHEDULE_TIMEOUT; 49562306a36Sopenharmony_ci else 49662306a36Sopenharmony_ci timeout = msecs_to_jiffies(timeout_ms); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (interruptible) { 49962306a36Sopenharmony_ci timeout = wait_event_interruptible_timeout(gdev->hgcm_wq, 50062306a36Sopenharmony_ci hgcm_req_done(gdev, &call->header), 50162306a36Sopenharmony_ci timeout); 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci timeout = wait_event_timeout(gdev->hgcm_wq, 50462306a36Sopenharmony_ci hgcm_req_done(gdev, &call->header), 50562306a36Sopenharmony_ci timeout); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* timeout > 0 means hgcm_req_done has returned true, so success */ 50962306a36Sopenharmony_ci if (timeout > 0) 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (timeout == 0) 51362306a36Sopenharmony_ci ret = -ETIMEDOUT; 51462306a36Sopenharmony_ci else 51562306a36Sopenharmony_ci ret = -EINTR; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Cancel the request */ 51862306a36Sopenharmony_ci cancel_rc = hgcm_cancel_call(gdev, call); 51962306a36Sopenharmony_ci if (cancel_rc >= 0) 52062306a36Sopenharmony_ci return ret; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * Failed to cancel, this should mean that the cancel has lost the 52462306a36Sopenharmony_ci * race with normal completion, wait while the host completes it. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci if (cancel_rc == VERR_NOT_FOUND || cancel_rc == VERR_SEM_DESTROYED) 52762306a36Sopenharmony_ci timeout = msecs_to_jiffies(500); 52862306a36Sopenharmony_ci else 52962306a36Sopenharmony_ci timeout = msecs_to_jiffies(2000); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci timeout = wait_event_timeout(gdev->hgcm_wq, 53262306a36Sopenharmony_ci hgcm_req_done(gdev, &call->header), 53362306a36Sopenharmony_ci timeout); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (WARN_ON(timeout == 0)) { 53662306a36Sopenharmony_ci /* We really should never get here */ 53762306a36Sopenharmony_ci vbg_err("%s: Call timedout and cancellation failed, leaking the request\n", 53862306a36Sopenharmony_ci __func__); 53962306a36Sopenharmony_ci *leak_it = true; 54062306a36Sopenharmony_ci return ret; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* The call has completed normally after all */ 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/** 54862306a36Sopenharmony_ci * Copies the result of the call back to the caller info structure and user 54962306a36Sopenharmony_ci * buffers. 55062306a36Sopenharmony_ci * Return: 0 or negative errno value. 55162306a36Sopenharmony_ci * @call: HGCM call request. 55262306a36Sopenharmony_ci * @dst_parm: Pointer to function call parameters destination. 55362306a36Sopenharmony_ci * @parm_count: Number of function call parameters. 55462306a36Sopenharmony_ci * @bounce_bufs: The bouncebuffer array. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_cistatic int hgcm_call_copy_back_result( 55762306a36Sopenharmony_ci const struct vmmdev_hgcm_call *call, 55862306a36Sopenharmony_ci struct vmmdev_hgcm_function_parameter *dst_parm, 55962306a36Sopenharmony_ci u32 parm_count, void **bounce_bufs) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm = 56262306a36Sopenharmony_ci VMMDEV_HGCM_CALL_PARMS(call); 56362306a36Sopenharmony_ci void __user *p; 56462306a36Sopenharmony_ci int ret; 56562306a36Sopenharmony_ci u32 i; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Copy back parameters. */ 56862306a36Sopenharmony_ci for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) { 56962306a36Sopenharmony_ci switch (dst_parm->type) { 57062306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 57162306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 57262306a36Sopenharmony_ci *dst_parm = *src_parm; 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_PAGELIST: 57662306a36Sopenharmony_ci dst_parm->u.page_list.size = src_parm->u.page_list.size; 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 58062306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 58162306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 58262306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 58362306a36Sopenharmony_ci dst_parm->u.pointer.size = src_parm->u.pointer.size; 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 58762306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 58862306a36Sopenharmony_ci dst_parm->u.pointer.size = src_parm->u.pointer.size; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci p = (void __user *)dst_parm->u.pointer.u.linear_addr; 59162306a36Sopenharmony_ci ret = copy_to_user(p, bounce_bufs[i], 59262306a36Sopenharmony_ci min(src_parm->u.pointer.size, 59362306a36Sopenharmony_ci dst_parm->u.pointer.size)); 59462306a36Sopenharmony_ci if (ret) 59562306a36Sopenharmony_ci return -EFAULT; 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci default: 59962306a36Sopenharmony_ci WARN_ON(1); 60062306a36Sopenharmony_ci return -EINVAL; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciint vbg_hgcm_call(struct vbg_dev *gdev, u32 requestor, u32 client_id, 60862306a36Sopenharmony_ci u32 function, u32 timeout_ms, 60962306a36Sopenharmony_ci struct vmmdev_hgcm_function_parameter *parms, u32 parm_count, 61062306a36Sopenharmony_ci int *vbox_status) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct vmmdev_hgcm_call *call; 61362306a36Sopenharmony_ci void **bounce_bufs = NULL; 61462306a36Sopenharmony_ci bool leak_it; 61562306a36Sopenharmony_ci size_t size; 61662306a36Sopenharmony_ci int i, ret; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci size = sizeof(struct vmmdev_hgcm_call) + 61962306a36Sopenharmony_ci parm_count * sizeof(struct vmmdev_hgcm_function_parameter); 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * Validate and buffer the parameters for the call. This also increases 62262306a36Sopenharmony_ci * call_size with the amount of extra space needed for page lists. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci ret = hgcm_call_preprocess(parms, parm_count, &bounce_bufs, &size); 62562306a36Sopenharmony_ci if (ret) { 62662306a36Sopenharmony_ci /* Even on error bounce bufs may still have been allocated */ 62762306a36Sopenharmony_ci goto free_bounce_bufs; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci call = vbg_req_alloc(size, VMMDEVREQ_HGCM_CALL, requestor); 63162306a36Sopenharmony_ci if (!call) { 63262306a36Sopenharmony_ci ret = -ENOMEM; 63362306a36Sopenharmony_ci goto free_bounce_bufs; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci hgcm_call_init_call(call, client_id, function, parms, parm_count, 63762306a36Sopenharmony_ci bounce_bufs); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci ret = vbg_hgcm_do_call(gdev, call, timeout_ms, 64062306a36Sopenharmony_ci requestor & VMMDEV_REQUESTOR_USERMODE, &leak_it); 64162306a36Sopenharmony_ci if (ret == 0) { 64262306a36Sopenharmony_ci *vbox_status = call->header.result; 64362306a36Sopenharmony_ci ret = hgcm_call_copy_back_result(call, parms, parm_count, 64462306a36Sopenharmony_ci bounce_bufs); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (!leak_it) 64862306a36Sopenharmony_ci vbg_req_free(call, size); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cifree_bounce_bufs: 65162306a36Sopenharmony_ci if (bounce_bufs) { 65262306a36Sopenharmony_ci for (i = 0; i < parm_count; i++) 65362306a36Sopenharmony_ci kvfree(bounce_bufs[i]); 65462306a36Sopenharmony_ci kfree(bounce_bufs); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ciEXPORT_SYMBOL(vbg_hgcm_call); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 66262306a36Sopenharmony_ciint vbg_hgcm_call32( 66362306a36Sopenharmony_ci struct vbg_dev *gdev, u32 requestor, u32 client_id, u32 function, 66462306a36Sopenharmony_ci u32 timeout_ms, struct vmmdev_hgcm_function_parameter32 *parm32, 66562306a36Sopenharmony_ci u32 parm_count, int *vbox_status) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct vmmdev_hgcm_function_parameter *parm64 = NULL; 66862306a36Sopenharmony_ci u32 i, size; 66962306a36Sopenharmony_ci int ret = 0; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* KISS allocate a temporary request and convert the parameters. */ 67262306a36Sopenharmony_ci size = parm_count * sizeof(struct vmmdev_hgcm_function_parameter); 67362306a36Sopenharmony_ci parm64 = kzalloc(size, GFP_KERNEL); 67462306a36Sopenharmony_ci if (!parm64) 67562306a36Sopenharmony_ci return -ENOMEM; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci for (i = 0; i < parm_count; i++) { 67862306a36Sopenharmony_ci switch (parm32[i].type) { 67962306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 68062306a36Sopenharmony_ci parm64[i].type = VMMDEV_HGCM_PARM_TYPE_32BIT; 68162306a36Sopenharmony_ci parm64[i].u.value32 = parm32[i].u.value32; 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 68562306a36Sopenharmony_ci parm64[i].type = VMMDEV_HGCM_PARM_TYPE_64BIT; 68662306a36Sopenharmony_ci parm64[i].u.value64 = parm32[i].u.value64; 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 69062306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 69162306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 69262306a36Sopenharmony_ci parm64[i].type = parm32[i].type; 69362306a36Sopenharmony_ci parm64[i].u.pointer.size = parm32[i].u.pointer.size; 69462306a36Sopenharmony_ci parm64[i].u.pointer.u.linear_addr = 69562306a36Sopenharmony_ci parm32[i].u.pointer.u.linear_addr; 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci default: 69962306a36Sopenharmony_ci ret = -EINVAL; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci if (ret < 0) 70262306a36Sopenharmony_ci goto out_free; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ret = vbg_hgcm_call(gdev, requestor, client_id, function, timeout_ms, 70662306a36Sopenharmony_ci parm64, parm_count, vbox_status); 70762306a36Sopenharmony_ci if (ret < 0) 70862306a36Sopenharmony_ci goto out_free; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Copy back. */ 71162306a36Sopenharmony_ci for (i = 0; i < parm_count; i++, parm32++, parm64++) { 71262306a36Sopenharmony_ci switch (parm64[i].type) { 71362306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 71462306a36Sopenharmony_ci parm32[i].u.value32 = parm64[i].u.value32; 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 71862306a36Sopenharmony_ci parm32[i].u.value64 = parm64[i].u.value64; 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 72262306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 72362306a36Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 72462306a36Sopenharmony_ci parm32[i].u.pointer.size = parm64[i].u.pointer.size; 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci default: 72862306a36Sopenharmony_ci WARN_ON(1); 72962306a36Sopenharmony_ci ret = -EINVAL; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ciout_free: 73462306a36Sopenharmony_ci kfree(parm64); 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci#endif 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic const int vbg_status_code_to_errno_table[] = { 74062306a36Sopenharmony_ci [-VERR_ACCESS_DENIED] = -EPERM, 74162306a36Sopenharmony_ci [-VERR_FILE_NOT_FOUND] = -ENOENT, 74262306a36Sopenharmony_ci [-VERR_PROCESS_NOT_FOUND] = -ESRCH, 74362306a36Sopenharmony_ci [-VERR_INTERRUPTED] = -EINTR, 74462306a36Sopenharmony_ci [-VERR_DEV_IO_ERROR] = -EIO, 74562306a36Sopenharmony_ci [-VERR_TOO_MUCH_DATA] = -E2BIG, 74662306a36Sopenharmony_ci [-VERR_BAD_EXE_FORMAT] = -ENOEXEC, 74762306a36Sopenharmony_ci [-VERR_INVALID_HANDLE] = -EBADF, 74862306a36Sopenharmony_ci [-VERR_TRY_AGAIN] = -EAGAIN, 74962306a36Sopenharmony_ci [-VERR_NO_MEMORY] = -ENOMEM, 75062306a36Sopenharmony_ci [-VERR_INVALID_POINTER] = -EFAULT, 75162306a36Sopenharmony_ci [-VERR_RESOURCE_BUSY] = -EBUSY, 75262306a36Sopenharmony_ci [-VERR_ALREADY_EXISTS] = -EEXIST, 75362306a36Sopenharmony_ci [-VERR_NOT_SAME_DEVICE] = -EXDEV, 75462306a36Sopenharmony_ci [-VERR_NOT_A_DIRECTORY] = -ENOTDIR, 75562306a36Sopenharmony_ci [-VERR_PATH_NOT_FOUND] = -ENOTDIR, 75662306a36Sopenharmony_ci [-VERR_INVALID_NAME] = -ENOENT, 75762306a36Sopenharmony_ci [-VERR_IS_A_DIRECTORY] = -EISDIR, 75862306a36Sopenharmony_ci [-VERR_INVALID_PARAMETER] = -EINVAL, 75962306a36Sopenharmony_ci [-VERR_TOO_MANY_OPEN_FILES] = -ENFILE, 76062306a36Sopenharmony_ci [-VERR_INVALID_FUNCTION] = -ENOTTY, 76162306a36Sopenharmony_ci [-VERR_SHARING_VIOLATION] = -ETXTBSY, 76262306a36Sopenharmony_ci [-VERR_FILE_TOO_BIG] = -EFBIG, 76362306a36Sopenharmony_ci [-VERR_DISK_FULL] = -ENOSPC, 76462306a36Sopenharmony_ci [-VERR_SEEK_ON_DEVICE] = -ESPIPE, 76562306a36Sopenharmony_ci [-VERR_WRITE_PROTECT] = -EROFS, 76662306a36Sopenharmony_ci [-VERR_BROKEN_PIPE] = -EPIPE, 76762306a36Sopenharmony_ci [-VERR_DEADLOCK] = -EDEADLK, 76862306a36Sopenharmony_ci [-VERR_FILENAME_TOO_LONG] = -ENAMETOOLONG, 76962306a36Sopenharmony_ci [-VERR_FILE_LOCK_FAILED] = -ENOLCK, 77062306a36Sopenharmony_ci [-VERR_NOT_IMPLEMENTED] = -ENOSYS, 77162306a36Sopenharmony_ci [-VERR_NOT_SUPPORTED] = -ENOSYS, 77262306a36Sopenharmony_ci [-VERR_DIR_NOT_EMPTY] = -ENOTEMPTY, 77362306a36Sopenharmony_ci [-VERR_TOO_MANY_SYMLINKS] = -ELOOP, 77462306a36Sopenharmony_ci [-VERR_NO_MORE_FILES] = -ENODATA, 77562306a36Sopenharmony_ci [-VERR_NO_DATA] = -ENODATA, 77662306a36Sopenharmony_ci [-VERR_NET_NO_NETWORK] = -ENONET, 77762306a36Sopenharmony_ci [-VERR_NET_NOT_UNIQUE_NAME] = -ENOTUNIQ, 77862306a36Sopenharmony_ci [-VERR_NO_TRANSLATION] = -EILSEQ, 77962306a36Sopenharmony_ci [-VERR_NET_NOT_SOCKET] = -ENOTSOCK, 78062306a36Sopenharmony_ci [-VERR_NET_DEST_ADDRESS_REQUIRED] = -EDESTADDRREQ, 78162306a36Sopenharmony_ci [-VERR_NET_MSG_SIZE] = -EMSGSIZE, 78262306a36Sopenharmony_ci [-VERR_NET_PROTOCOL_TYPE] = -EPROTOTYPE, 78362306a36Sopenharmony_ci [-VERR_NET_PROTOCOL_NOT_AVAILABLE] = -ENOPROTOOPT, 78462306a36Sopenharmony_ci [-VERR_NET_PROTOCOL_NOT_SUPPORTED] = -EPROTONOSUPPORT, 78562306a36Sopenharmony_ci [-VERR_NET_SOCKET_TYPE_NOT_SUPPORTED] = -ESOCKTNOSUPPORT, 78662306a36Sopenharmony_ci [-VERR_NET_OPERATION_NOT_SUPPORTED] = -EOPNOTSUPP, 78762306a36Sopenharmony_ci [-VERR_NET_PROTOCOL_FAMILY_NOT_SUPPORTED] = -EPFNOSUPPORT, 78862306a36Sopenharmony_ci [-VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED] = -EAFNOSUPPORT, 78962306a36Sopenharmony_ci [-VERR_NET_ADDRESS_IN_USE] = -EADDRINUSE, 79062306a36Sopenharmony_ci [-VERR_NET_ADDRESS_NOT_AVAILABLE] = -EADDRNOTAVAIL, 79162306a36Sopenharmony_ci [-VERR_NET_DOWN] = -ENETDOWN, 79262306a36Sopenharmony_ci [-VERR_NET_UNREACHABLE] = -ENETUNREACH, 79362306a36Sopenharmony_ci [-VERR_NET_CONNECTION_RESET] = -ENETRESET, 79462306a36Sopenharmony_ci [-VERR_NET_CONNECTION_ABORTED] = -ECONNABORTED, 79562306a36Sopenharmony_ci [-VERR_NET_CONNECTION_RESET_BY_PEER] = -ECONNRESET, 79662306a36Sopenharmony_ci [-VERR_NET_NO_BUFFER_SPACE] = -ENOBUFS, 79762306a36Sopenharmony_ci [-VERR_NET_ALREADY_CONNECTED] = -EISCONN, 79862306a36Sopenharmony_ci [-VERR_NET_NOT_CONNECTED] = -ENOTCONN, 79962306a36Sopenharmony_ci [-VERR_NET_SHUTDOWN] = -ESHUTDOWN, 80062306a36Sopenharmony_ci [-VERR_NET_TOO_MANY_REFERENCES] = -ETOOMANYREFS, 80162306a36Sopenharmony_ci [-VERR_TIMEOUT] = -ETIMEDOUT, 80262306a36Sopenharmony_ci [-VERR_NET_CONNECTION_REFUSED] = -ECONNREFUSED, 80362306a36Sopenharmony_ci [-VERR_NET_HOST_DOWN] = -EHOSTDOWN, 80462306a36Sopenharmony_ci [-VERR_NET_HOST_UNREACHABLE] = -EHOSTUNREACH, 80562306a36Sopenharmony_ci [-VERR_NET_ALREADY_IN_PROGRESS] = -EALREADY, 80662306a36Sopenharmony_ci [-VERR_NET_IN_PROGRESS] = -EINPROGRESS, 80762306a36Sopenharmony_ci [-VERR_MEDIA_NOT_PRESENT] = -ENOMEDIUM, 80862306a36Sopenharmony_ci [-VERR_MEDIA_NOT_RECOGNIZED] = -EMEDIUMTYPE, 80962306a36Sopenharmony_ci}; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ciint vbg_status_code_to_errno(int rc) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci if (rc >= 0) 81462306a36Sopenharmony_ci return 0; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci rc = -rc; 81762306a36Sopenharmony_ci if (rc >= ARRAY_SIZE(vbg_status_code_to_errno_table) || 81862306a36Sopenharmony_ci vbg_status_code_to_errno_table[rc] == 0) { 81962306a36Sopenharmony_ci vbg_warn("%s: Unhandled err %d\n", __func__, -rc); 82062306a36Sopenharmony_ci return -EPROTO; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return vbg_status_code_to_errno_table[rc]; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ciEXPORT_SYMBOL(vbg_status_code_to_errno); 826