18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vboxguest vmm-req and hgcm-call code, VBoxGuestR0LibHGCMInternal.cpp, 48c2ecf20Sopenharmony_ci * VBoxGuestR0LibGenericRequest.cpp and RTErrConvertToErrno.cpp in vbox svn. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2006-2016 Oracle Corporation 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/sizes.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci#include <linux/vbox_err.h> 198c2ecf20Sopenharmony_ci#include <linux/vbox_utils.h> 208c2ecf20Sopenharmony_ci#include "vboxguest_core.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Get the pointer to the first parameter of a HGCM call request. */ 238c2ecf20Sopenharmony_ci#define VMMDEV_HGCM_CALL_PARMS(a) \ 248c2ecf20Sopenharmony_ci ((struct vmmdev_hgcm_function_parameter *)( \ 258c2ecf20Sopenharmony_ci (u8 *)(a) + sizeof(struct vmmdev_hgcm_call))) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* The max parameter buffer size for a user request. */ 288c2ecf20Sopenharmony_ci#define VBG_MAX_HGCM_USER_PARM (24 * SZ_1M) 298c2ecf20Sopenharmony_ci/* The max parameter buffer size for a kernel request. */ 308c2ecf20Sopenharmony_ci#define VBG_MAX_HGCM_KERNEL_PARM (16 * SZ_1M) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define VBG_DEBUG_PORT 0x504 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* This protects vbg_log_buf and serializes VBG_DEBUG_PORT accesses */ 358c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(vbg_log_lock); 368c2ecf20Sopenharmony_cistatic char vbg_log_buf[128]; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define VBG_LOG(name, pr_func) \ 398c2ecf20Sopenharmony_civoid name(const char *fmt, ...) \ 408c2ecf20Sopenharmony_ci{ \ 418c2ecf20Sopenharmony_ci unsigned long flags; \ 428c2ecf20Sopenharmony_ci va_list args; \ 438c2ecf20Sopenharmony_ci int i, count; \ 448c2ecf20Sopenharmony_ci \ 458c2ecf20Sopenharmony_ci va_start(args, fmt); \ 468c2ecf20Sopenharmony_ci spin_lock_irqsave(&vbg_log_lock, flags); \ 478c2ecf20Sopenharmony_ci \ 488c2ecf20Sopenharmony_ci count = vscnprintf(vbg_log_buf, sizeof(vbg_log_buf), fmt, args);\ 498c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) \ 508c2ecf20Sopenharmony_ci outb(vbg_log_buf[i], VBG_DEBUG_PORT); \ 518c2ecf20Sopenharmony_ci \ 528c2ecf20Sopenharmony_ci pr_func("%s", vbg_log_buf); \ 538c2ecf20Sopenharmony_ci \ 548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vbg_log_lock, flags); \ 558c2ecf20Sopenharmony_ci va_end(args); \ 568c2ecf20Sopenharmony_ci} \ 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(name) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciVBG_LOG(vbg_info, pr_info); 608c2ecf20Sopenharmony_ciVBG_LOG(vbg_warn, pr_warn); 618c2ecf20Sopenharmony_ciVBG_LOG(vbg_err, pr_err); 628c2ecf20Sopenharmony_ciVBG_LOG(vbg_err_ratelimited, pr_err_ratelimited); 638c2ecf20Sopenharmony_ci#if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) 648c2ecf20Sopenharmony_ciVBG_LOG(vbg_debug, pr_debug); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type, 688c2ecf20Sopenharmony_ci u32 requestor) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct vmmdev_request_header *req; 718c2ecf20Sopenharmony_ci int order = get_order(PAGE_ALIGN(len)); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci req = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order); 748c2ecf20Sopenharmony_ci if (!req) 758c2ecf20Sopenharmony_ci return NULL; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci memset(req, 0xaa, len); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci req->size = len; 808c2ecf20Sopenharmony_ci req->version = VMMDEV_REQUEST_HEADER_VERSION; 818c2ecf20Sopenharmony_ci req->request_type = req_type; 828c2ecf20Sopenharmony_ci req->rc = VERR_GENERAL_FAILURE; 838c2ecf20Sopenharmony_ci req->reserved1 = 0; 848c2ecf20Sopenharmony_ci req->requestor = requestor; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return req; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid vbg_req_free(void *req, size_t len) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci if (!req) 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci free_pages((unsigned long)req, get_order(PAGE_ALIGN(len))); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* Note this function returns a VBox status code, not a negative errno!! */ 988c2ecf20Sopenharmony_ciint vbg_req_perform(struct vbg_dev *gdev, void *req) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci unsigned long phys_req = virt_to_phys(req); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci outl(phys_req, gdev->io_port + VMMDEV_PORT_OFF_REQUEST); 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * The host changes the request as a result of the outl, make sure 1058c2ecf20Sopenharmony_ci * the outl and any reads of the req happen in the correct order. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci mb(); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return ((struct vmmdev_request_header *)req)->rc; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic bool hgcm_req_done(struct vbg_dev *gdev, 1138c2ecf20Sopenharmony_ci struct vmmdev_hgcmreq_header *header) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci unsigned long flags; 1168c2ecf20Sopenharmony_ci bool done; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci spin_lock_irqsave(&gdev->event_spinlock, flags); 1198c2ecf20Sopenharmony_ci done = header->flags & VMMDEV_HGCM_REQ_DONE; 1208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gdev->event_spinlock, flags); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return done; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciint vbg_hgcm_connect(struct vbg_dev *gdev, u32 requestor, 1268c2ecf20Sopenharmony_ci struct vmmdev_hgcm_service_location *loc, 1278c2ecf20Sopenharmony_ci u32 *client_id, int *vbox_status) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct vmmdev_hgcm_connect *hgcm_connect = NULL; 1308c2ecf20Sopenharmony_ci int rc; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci hgcm_connect = vbg_req_alloc(sizeof(*hgcm_connect), 1338c2ecf20Sopenharmony_ci VMMDEVREQ_HGCM_CONNECT, requestor); 1348c2ecf20Sopenharmony_ci if (!hgcm_connect) 1358c2ecf20Sopenharmony_ci return -ENOMEM; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci hgcm_connect->header.flags = 0; 1388c2ecf20Sopenharmony_ci memcpy(&hgcm_connect->loc, loc, sizeof(*loc)); 1398c2ecf20Sopenharmony_ci hgcm_connect->client_id = 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci rc = vbg_req_perform(gdev, hgcm_connect); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (rc == VINF_HGCM_ASYNC_EXECUTE) 1448c2ecf20Sopenharmony_ci wait_event(gdev->hgcm_wq, 1458c2ecf20Sopenharmony_ci hgcm_req_done(gdev, &hgcm_connect->header)); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (rc >= 0) { 1488c2ecf20Sopenharmony_ci *client_id = hgcm_connect->client_id; 1498c2ecf20Sopenharmony_ci rc = hgcm_connect->header.result; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci vbg_req_free(hgcm_connect, sizeof(*hgcm_connect)); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci *vbox_status = rc; 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vbg_hgcm_connect); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciint vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 requestor, 1608c2ecf20Sopenharmony_ci u32 client_id, int *vbox_status) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct vmmdev_hgcm_disconnect *hgcm_disconnect = NULL; 1638c2ecf20Sopenharmony_ci int rc; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci hgcm_disconnect = vbg_req_alloc(sizeof(*hgcm_disconnect), 1668c2ecf20Sopenharmony_ci VMMDEVREQ_HGCM_DISCONNECT, 1678c2ecf20Sopenharmony_ci requestor); 1688c2ecf20Sopenharmony_ci if (!hgcm_disconnect) 1698c2ecf20Sopenharmony_ci return -ENOMEM; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci hgcm_disconnect->header.flags = 0; 1728c2ecf20Sopenharmony_ci hgcm_disconnect->client_id = client_id; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci rc = vbg_req_perform(gdev, hgcm_disconnect); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (rc == VINF_HGCM_ASYNC_EXECUTE) 1778c2ecf20Sopenharmony_ci wait_event(gdev->hgcm_wq, 1788c2ecf20Sopenharmony_ci hgcm_req_done(gdev, &hgcm_disconnect->header)); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (rc >= 0) 1818c2ecf20Sopenharmony_ci rc = hgcm_disconnect->header.result; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci vbg_req_free(hgcm_disconnect, sizeof(*hgcm_disconnect)); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci *vbox_status = rc; 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vbg_hgcm_disconnect); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic u32 hgcm_call_buf_size_in_pages(void *buf, u32 len) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci u32 size = PAGE_ALIGN(len + ((unsigned long)buf & ~PAGE_MASK)); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return size >> PAGE_SHIFT; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void hgcm_call_add_pagelist_size(void *buf, u32 len, size_t *extra) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci u32 page_count; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci page_count = hgcm_call_buf_size_in_pages(buf, len); 2028c2ecf20Sopenharmony_ci *extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int hgcm_call_preprocess_linaddr( 2068c2ecf20Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm, 2078c2ecf20Sopenharmony_ci void **bounce_buf_ret, size_t *extra) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci void *buf, *bounce_buf; 2108c2ecf20Sopenharmony_ci bool copy_in; 2118c2ecf20Sopenharmony_ci u32 len; 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci buf = (void *)src_parm->u.pointer.u.linear_addr; 2158c2ecf20Sopenharmony_ci len = src_parm->u.pointer.size; 2168c2ecf20Sopenharmony_ci copy_in = src_parm->type != VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (len > VBG_MAX_HGCM_USER_PARM) 2198c2ecf20Sopenharmony_ci return -E2BIG; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci bounce_buf = kvmalloc(len, GFP_KERNEL); 2228c2ecf20Sopenharmony_ci if (!bounce_buf) 2238c2ecf20Sopenharmony_ci return -ENOMEM; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci *bounce_buf_ret = bounce_buf; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (copy_in) { 2288c2ecf20Sopenharmony_ci ret = copy_from_user(bounce_buf, (void __user *)buf, len); 2298c2ecf20Sopenharmony_ci if (ret) 2308c2ecf20Sopenharmony_ci return -EFAULT; 2318c2ecf20Sopenharmony_ci } else { 2328c2ecf20Sopenharmony_ci memset(bounce_buf, 0, len); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci hgcm_call_add_pagelist_size(bounce_buf, len, extra); 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/** 2408c2ecf20Sopenharmony_ci * Preprocesses the HGCM call, validate parameters, alloc bounce buffers and 2418c2ecf20Sopenharmony_ci * figure out how much extra storage we need for page lists. 2428c2ecf20Sopenharmony_ci * Return: 0 or negative errno value. 2438c2ecf20Sopenharmony_ci * @src_parm: Pointer to source function call parameters 2448c2ecf20Sopenharmony_ci * @parm_count: Number of function call parameters. 2458c2ecf20Sopenharmony_ci * @bounce_bufs_ret: Where to return the allocated bouncebuffer array 2468c2ecf20Sopenharmony_ci * @extra: Where to return the extra request space needed for 2478c2ecf20Sopenharmony_ci * physical page lists. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_cistatic int hgcm_call_preprocess( 2508c2ecf20Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm, 2518c2ecf20Sopenharmony_ci u32 parm_count, void ***bounce_bufs_ret, size_t *extra) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci void *buf, **bounce_bufs = NULL; 2548c2ecf20Sopenharmony_ci u32 i, len; 2558c2ecf20Sopenharmony_ci int ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (i = 0; i < parm_count; i++, src_parm++) { 2588c2ecf20Sopenharmony_ci switch (src_parm->type) { 2598c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 2608c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 2648c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 2658c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 2668c2ecf20Sopenharmony_ci if (!bounce_bufs) { 2678c2ecf20Sopenharmony_ci bounce_bufs = kcalloc(parm_count, 2688c2ecf20Sopenharmony_ci sizeof(void *), 2698c2ecf20Sopenharmony_ci GFP_KERNEL); 2708c2ecf20Sopenharmony_ci if (!bounce_bufs) 2718c2ecf20Sopenharmony_ci return -ENOMEM; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci *bounce_bufs_ret = bounce_bufs; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ret = hgcm_call_preprocess_linaddr(src_parm, 2778c2ecf20Sopenharmony_ci &bounce_bufs[i], 2788c2ecf20Sopenharmony_ci extra); 2798c2ecf20Sopenharmony_ci if (ret) 2808c2ecf20Sopenharmony_ci return ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 2858c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 2868c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 2878c2ecf20Sopenharmony_ci buf = (void *)src_parm->u.pointer.u.linear_addr; 2888c2ecf20Sopenharmony_ci len = src_parm->u.pointer.size; 2898c2ecf20Sopenharmony_ci if (WARN_ON(len > VBG_MAX_HGCM_KERNEL_PARM)) 2908c2ecf20Sopenharmony_ci return -E2BIG; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci hgcm_call_add_pagelist_size(buf, len, extra); 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci default: 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/** 3048c2ecf20Sopenharmony_ci * Translates linear address types to page list direction flags. 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * Return: page list flags. 3078c2ecf20Sopenharmony_ci * @type: The type. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_cistatic u32 hgcm_call_linear_addr_type_to_pagelist_flags( 3108c2ecf20Sopenharmony_ci enum vmmdev_hgcm_function_parameter_type type) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci switch (type) { 3138c2ecf20Sopenharmony_ci default: 3148c2ecf20Sopenharmony_ci WARN_ON(1); 3158c2ecf20Sopenharmony_ci fallthrough; 3168c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 3178c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 3188c2ecf20Sopenharmony_ci return VMMDEV_HGCM_F_PARM_DIRECTION_BOTH; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 3218c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 3228c2ecf20Sopenharmony_ci return VMMDEV_HGCM_F_PARM_DIRECTION_TO_HOST; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 3258c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 3268c2ecf20Sopenharmony_ci return VMMDEV_HGCM_F_PARM_DIRECTION_FROM_HOST; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void hgcm_call_init_linaddr(struct vmmdev_hgcm_call *call, 3318c2ecf20Sopenharmony_ci struct vmmdev_hgcm_function_parameter *dst_parm, void *buf, u32 len, 3328c2ecf20Sopenharmony_ci enum vmmdev_hgcm_function_parameter_type type, u32 *off_extra) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct vmmdev_hgcm_pagelist *dst_pg_lst; 3358c2ecf20Sopenharmony_ci struct page *page; 3368c2ecf20Sopenharmony_ci bool is_vmalloc; 3378c2ecf20Sopenharmony_ci u32 i, page_count; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci dst_parm->type = type; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (len == 0) { 3428c2ecf20Sopenharmony_ci dst_parm->u.pointer.size = 0; 3438c2ecf20Sopenharmony_ci dst_parm->u.pointer.u.linear_addr = 0; 3448c2ecf20Sopenharmony_ci return; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci dst_pg_lst = (void *)call + *off_extra; 3488c2ecf20Sopenharmony_ci page_count = hgcm_call_buf_size_in_pages(buf, len); 3498c2ecf20Sopenharmony_ci is_vmalloc = is_vmalloc_addr(buf); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci dst_parm->type = VMMDEV_HGCM_PARM_TYPE_PAGELIST; 3528c2ecf20Sopenharmony_ci dst_parm->u.page_list.size = len; 3538c2ecf20Sopenharmony_ci dst_parm->u.page_list.offset = *off_extra; 3548c2ecf20Sopenharmony_ci dst_pg_lst->flags = hgcm_call_linear_addr_type_to_pagelist_flags(type); 3558c2ecf20Sopenharmony_ci dst_pg_lst->offset_first_page = (unsigned long)buf & ~PAGE_MASK; 3568c2ecf20Sopenharmony_ci dst_pg_lst->page_count = page_count; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 0; i < page_count; i++) { 3598c2ecf20Sopenharmony_ci if (is_vmalloc) 3608c2ecf20Sopenharmony_ci page = vmalloc_to_page(buf); 3618c2ecf20Sopenharmony_ci else 3628c2ecf20Sopenharmony_ci page = virt_to_page(buf); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci dst_pg_lst->pages[i] = page_to_phys(page); 3658c2ecf20Sopenharmony_ci buf += PAGE_SIZE; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci *off_extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/** 3728c2ecf20Sopenharmony_ci * Initializes the call request that we're sending to the host. 3738c2ecf20Sopenharmony_ci * @call: The call to initialize. 3748c2ecf20Sopenharmony_ci * @client_id: The client ID of the caller. 3758c2ecf20Sopenharmony_ci * @function: The function number of the function to call. 3768c2ecf20Sopenharmony_ci * @src_parm: Pointer to source function call parameters. 3778c2ecf20Sopenharmony_ci * @parm_count: Number of function call parameters. 3788c2ecf20Sopenharmony_ci * @bounce_bufs: The bouncebuffer array. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic void hgcm_call_init_call( 3818c2ecf20Sopenharmony_ci struct vmmdev_hgcm_call *call, u32 client_id, u32 function, 3828c2ecf20Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm, 3838c2ecf20Sopenharmony_ci u32 parm_count, void **bounce_bufs) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct vmmdev_hgcm_function_parameter *dst_parm = 3868c2ecf20Sopenharmony_ci VMMDEV_HGCM_CALL_PARMS(call); 3878c2ecf20Sopenharmony_ci u32 i, off_extra = (uintptr_t)(dst_parm + parm_count) - (uintptr_t)call; 3888c2ecf20Sopenharmony_ci void *buf; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci call->header.flags = 0; 3918c2ecf20Sopenharmony_ci call->header.result = VINF_SUCCESS; 3928c2ecf20Sopenharmony_ci call->client_id = client_id; 3938c2ecf20Sopenharmony_ci call->function = function; 3948c2ecf20Sopenharmony_ci call->parm_count = parm_count; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) { 3978c2ecf20Sopenharmony_ci switch (src_parm->type) { 3988c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 3998c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 4008c2ecf20Sopenharmony_ci *dst_parm = *src_parm; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 4048c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 4058c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 4068c2ecf20Sopenharmony_ci hgcm_call_init_linaddr(call, dst_parm, bounce_bufs[i], 4078c2ecf20Sopenharmony_ci src_parm->u.pointer.size, 4088c2ecf20Sopenharmony_ci src_parm->type, &off_extra); 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 4128c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 4138c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 4148c2ecf20Sopenharmony_ci buf = (void *)src_parm->u.pointer.u.linear_addr; 4158c2ecf20Sopenharmony_ci hgcm_call_init_linaddr(call, dst_parm, buf, 4168c2ecf20Sopenharmony_ci src_parm->u.pointer.size, 4178c2ecf20Sopenharmony_ci src_parm->type, &off_extra); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci default: 4218c2ecf20Sopenharmony_ci WARN_ON(1); 4228c2ecf20Sopenharmony_ci dst_parm->type = VMMDEV_HGCM_PARM_TYPE_INVALID; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * Tries to cancel a pending HGCM call. 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * Return: VBox status code 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_cistatic int hgcm_cancel_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci int rc; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * We use a pre-allocated request for cancellations, which is 4388c2ecf20Sopenharmony_ci * protected by cancel_req_mutex. This means that all cancellations 4398c2ecf20Sopenharmony_ci * get serialized, this should be fine since they should be rare. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci mutex_lock(&gdev->cancel_req_mutex); 4428c2ecf20Sopenharmony_ci gdev->cancel_req->phys_req_to_cancel = virt_to_phys(call); 4438c2ecf20Sopenharmony_ci rc = vbg_req_perform(gdev, gdev->cancel_req); 4448c2ecf20Sopenharmony_ci mutex_unlock(&gdev->cancel_req_mutex); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (rc == VERR_NOT_IMPLEMENTED) { 4478c2ecf20Sopenharmony_ci call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED; 4488c2ecf20Sopenharmony_ci call->header.header.request_type = VMMDEVREQ_HGCM_CANCEL; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci rc = vbg_req_perform(gdev, call); 4518c2ecf20Sopenharmony_ci if (rc == VERR_INVALID_PARAMETER) 4528c2ecf20Sopenharmony_ci rc = VERR_NOT_FOUND; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (rc >= 0) 4568c2ecf20Sopenharmony_ci call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return rc; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/** 4628c2ecf20Sopenharmony_ci * Performs the call and completion wait. 4638c2ecf20Sopenharmony_ci * Return: 0 or negative errno value. 4648c2ecf20Sopenharmony_ci * @gdev: The VBoxGuest device extension. 4658c2ecf20Sopenharmony_ci * @call: The call to execute. 4668c2ecf20Sopenharmony_ci * @timeout_ms: Timeout in ms. 4678c2ecf20Sopenharmony_ci * @leak_it: Where to return the leak it / free it, indicator. 4688c2ecf20Sopenharmony_ci * Cancellation fun. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_cistatic int vbg_hgcm_do_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call, 4718c2ecf20Sopenharmony_ci u32 timeout_ms, bool interruptible, bool *leak_it) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci int rc, cancel_rc, ret; 4748c2ecf20Sopenharmony_ci long timeout; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci *leak_it = false; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci rc = vbg_req_perform(gdev, call); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * If the call failed, then pretend success. Upper layers will 4828c2ecf20Sopenharmony_ci * interpret the result code in the packet. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci if (rc < 0) { 4858c2ecf20Sopenharmony_ci call->header.result = rc; 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (rc != VINF_HGCM_ASYNC_EXECUTE) 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* Host decided to process the request asynchronously, wait for it */ 4938c2ecf20Sopenharmony_ci if (timeout_ms == U32_MAX) 4948c2ecf20Sopenharmony_ci timeout = MAX_SCHEDULE_TIMEOUT; 4958c2ecf20Sopenharmony_ci else 4968c2ecf20Sopenharmony_ci timeout = msecs_to_jiffies(timeout_ms); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (interruptible) { 4998c2ecf20Sopenharmony_ci timeout = wait_event_interruptible_timeout(gdev->hgcm_wq, 5008c2ecf20Sopenharmony_ci hgcm_req_done(gdev, &call->header), 5018c2ecf20Sopenharmony_ci timeout); 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci timeout = wait_event_timeout(gdev->hgcm_wq, 5048c2ecf20Sopenharmony_ci hgcm_req_done(gdev, &call->header), 5058c2ecf20Sopenharmony_ci timeout); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* timeout > 0 means hgcm_req_done has returned true, so success */ 5098c2ecf20Sopenharmony_ci if (timeout > 0) 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (timeout == 0) 5138c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 5148c2ecf20Sopenharmony_ci else 5158c2ecf20Sopenharmony_ci ret = -EINTR; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* Cancel the request */ 5188c2ecf20Sopenharmony_ci cancel_rc = hgcm_cancel_call(gdev, call); 5198c2ecf20Sopenharmony_ci if (cancel_rc >= 0) 5208c2ecf20Sopenharmony_ci return ret; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * Failed to cancel, this should mean that the cancel has lost the 5248c2ecf20Sopenharmony_ci * race with normal completion, wait while the host completes it. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci if (cancel_rc == VERR_NOT_FOUND || cancel_rc == VERR_SEM_DESTROYED) 5278c2ecf20Sopenharmony_ci timeout = msecs_to_jiffies(500); 5288c2ecf20Sopenharmony_ci else 5298c2ecf20Sopenharmony_ci timeout = msecs_to_jiffies(2000); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci timeout = wait_event_timeout(gdev->hgcm_wq, 5328c2ecf20Sopenharmony_ci hgcm_req_done(gdev, &call->header), 5338c2ecf20Sopenharmony_ci timeout); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (WARN_ON(timeout == 0)) { 5368c2ecf20Sopenharmony_ci /* We really should never get here */ 5378c2ecf20Sopenharmony_ci vbg_err("%s: Call timedout and cancellation failed, leaking the request\n", 5388c2ecf20Sopenharmony_ci __func__); 5398c2ecf20Sopenharmony_ci *leak_it = true; 5408c2ecf20Sopenharmony_ci return ret; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* The call has completed normally after all */ 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/** 5488c2ecf20Sopenharmony_ci * Copies the result of the call back to the caller info structure and user 5498c2ecf20Sopenharmony_ci * buffers. 5508c2ecf20Sopenharmony_ci * Return: 0 or negative errno value. 5518c2ecf20Sopenharmony_ci * @call: HGCM call request. 5528c2ecf20Sopenharmony_ci * @dst_parm: Pointer to function call parameters destination. 5538c2ecf20Sopenharmony_ci * @parm_count: Number of function call parameters. 5548c2ecf20Sopenharmony_ci * @bounce_bufs: The bouncebuffer array. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_cistatic int hgcm_call_copy_back_result( 5578c2ecf20Sopenharmony_ci const struct vmmdev_hgcm_call *call, 5588c2ecf20Sopenharmony_ci struct vmmdev_hgcm_function_parameter *dst_parm, 5598c2ecf20Sopenharmony_ci u32 parm_count, void **bounce_bufs) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci const struct vmmdev_hgcm_function_parameter *src_parm = 5628c2ecf20Sopenharmony_ci VMMDEV_HGCM_CALL_PARMS(call); 5638c2ecf20Sopenharmony_ci void __user *p; 5648c2ecf20Sopenharmony_ci int ret; 5658c2ecf20Sopenharmony_ci u32 i; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Copy back parameters. */ 5688c2ecf20Sopenharmony_ci for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) { 5698c2ecf20Sopenharmony_ci switch (dst_parm->type) { 5708c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 5718c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 5728c2ecf20Sopenharmony_ci *dst_parm = *src_parm; 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_PAGELIST: 5768c2ecf20Sopenharmony_ci dst_parm->u.page_list.size = src_parm->u.page_list.size; 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 5808c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL: 5818c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN: 5828c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT: 5838c2ecf20Sopenharmony_ci dst_parm->u.pointer.size = src_parm->u.pointer.size; 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 5878c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 5888c2ecf20Sopenharmony_ci dst_parm->u.pointer.size = src_parm->u.pointer.size; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci p = (void __user *)dst_parm->u.pointer.u.linear_addr; 5918c2ecf20Sopenharmony_ci ret = copy_to_user(p, bounce_bufs[i], 5928c2ecf20Sopenharmony_ci min(src_parm->u.pointer.size, 5938c2ecf20Sopenharmony_ci dst_parm->u.pointer.size)); 5948c2ecf20Sopenharmony_ci if (ret) 5958c2ecf20Sopenharmony_ci return -EFAULT; 5968c2ecf20Sopenharmony_ci break; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci default: 5998c2ecf20Sopenharmony_ci WARN_ON(1); 6008c2ecf20Sopenharmony_ci return -EINVAL; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciint vbg_hgcm_call(struct vbg_dev *gdev, u32 requestor, u32 client_id, 6088c2ecf20Sopenharmony_ci u32 function, u32 timeout_ms, 6098c2ecf20Sopenharmony_ci struct vmmdev_hgcm_function_parameter *parms, u32 parm_count, 6108c2ecf20Sopenharmony_ci int *vbox_status) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct vmmdev_hgcm_call *call; 6138c2ecf20Sopenharmony_ci void **bounce_bufs = NULL; 6148c2ecf20Sopenharmony_ci bool leak_it; 6158c2ecf20Sopenharmony_ci size_t size; 6168c2ecf20Sopenharmony_ci int i, ret; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci size = sizeof(struct vmmdev_hgcm_call) + 6198c2ecf20Sopenharmony_ci parm_count * sizeof(struct vmmdev_hgcm_function_parameter); 6208c2ecf20Sopenharmony_ci /* 6218c2ecf20Sopenharmony_ci * Validate and buffer the parameters for the call. This also increases 6228c2ecf20Sopenharmony_ci * call_size with the amount of extra space needed for page lists. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci ret = hgcm_call_preprocess(parms, parm_count, &bounce_bufs, &size); 6258c2ecf20Sopenharmony_ci if (ret) { 6268c2ecf20Sopenharmony_ci /* Even on error bounce bufs may still have been allocated */ 6278c2ecf20Sopenharmony_ci goto free_bounce_bufs; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci call = vbg_req_alloc(size, VMMDEVREQ_HGCM_CALL, requestor); 6318c2ecf20Sopenharmony_ci if (!call) { 6328c2ecf20Sopenharmony_ci ret = -ENOMEM; 6338c2ecf20Sopenharmony_ci goto free_bounce_bufs; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci hgcm_call_init_call(call, client_id, function, parms, parm_count, 6378c2ecf20Sopenharmony_ci bounce_bufs); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci ret = vbg_hgcm_do_call(gdev, call, timeout_ms, 6408c2ecf20Sopenharmony_ci requestor & VMMDEV_REQUESTOR_USERMODE, &leak_it); 6418c2ecf20Sopenharmony_ci if (ret == 0) { 6428c2ecf20Sopenharmony_ci *vbox_status = call->header.result; 6438c2ecf20Sopenharmony_ci ret = hgcm_call_copy_back_result(call, parms, parm_count, 6448c2ecf20Sopenharmony_ci bounce_bufs); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (!leak_it) 6488c2ecf20Sopenharmony_ci vbg_req_free(call, size); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cifree_bounce_bufs: 6518c2ecf20Sopenharmony_ci if (bounce_bufs) { 6528c2ecf20Sopenharmony_ci for (i = 0; i < parm_count; i++) 6538c2ecf20Sopenharmony_ci kvfree(bounce_bufs[i]); 6548c2ecf20Sopenharmony_ci kfree(bounce_bufs); 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return ret; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vbg_hgcm_call); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 6628c2ecf20Sopenharmony_ciint vbg_hgcm_call32( 6638c2ecf20Sopenharmony_ci struct vbg_dev *gdev, u32 requestor, u32 client_id, u32 function, 6648c2ecf20Sopenharmony_ci u32 timeout_ms, struct vmmdev_hgcm_function_parameter32 *parm32, 6658c2ecf20Sopenharmony_ci u32 parm_count, int *vbox_status) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct vmmdev_hgcm_function_parameter *parm64 = NULL; 6688c2ecf20Sopenharmony_ci u32 i, size; 6698c2ecf20Sopenharmony_ci int ret = 0; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* KISS allocate a temporary request and convert the parameters. */ 6728c2ecf20Sopenharmony_ci size = parm_count * sizeof(struct vmmdev_hgcm_function_parameter); 6738c2ecf20Sopenharmony_ci parm64 = kzalloc(size, GFP_KERNEL); 6748c2ecf20Sopenharmony_ci if (!parm64) 6758c2ecf20Sopenharmony_ci return -ENOMEM; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci for (i = 0; i < parm_count; i++) { 6788c2ecf20Sopenharmony_ci switch (parm32[i].type) { 6798c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 6808c2ecf20Sopenharmony_ci parm64[i].type = VMMDEV_HGCM_PARM_TYPE_32BIT; 6818c2ecf20Sopenharmony_ci parm64[i].u.value32 = parm32[i].u.value32; 6828c2ecf20Sopenharmony_ci break; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 6858c2ecf20Sopenharmony_ci parm64[i].type = VMMDEV_HGCM_PARM_TYPE_64BIT; 6868c2ecf20Sopenharmony_ci parm64[i].u.value64 = parm32[i].u.value64; 6878c2ecf20Sopenharmony_ci break; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 6908c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 6918c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 6928c2ecf20Sopenharmony_ci parm64[i].type = parm32[i].type; 6938c2ecf20Sopenharmony_ci parm64[i].u.pointer.size = parm32[i].u.pointer.size; 6948c2ecf20Sopenharmony_ci parm64[i].u.pointer.u.linear_addr = 6958c2ecf20Sopenharmony_ci parm32[i].u.pointer.u.linear_addr; 6968c2ecf20Sopenharmony_ci break; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci default: 6998c2ecf20Sopenharmony_ci ret = -EINVAL; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci if (ret < 0) 7028c2ecf20Sopenharmony_ci goto out_free; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci ret = vbg_hgcm_call(gdev, requestor, client_id, function, timeout_ms, 7068c2ecf20Sopenharmony_ci parm64, parm_count, vbox_status); 7078c2ecf20Sopenharmony_ci if (ret < 0) 7088c2ecf20Sopenharmony_ci goto out_free; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* Copy back. */ 7118c2ecf20Sopenharmony_ci for (i = 0; i < parm_count; i++, parm32++, parm64++) { 7128c2ecf20Sopenharmony_ci switch (parm64[i].type) { 7138c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_32BIT: 7148c2ecf20Sopenharmony_ci parm32[i].u.value32 = parm64[i].u.value32; 7158c2ecf20Sopenharmony_ci break; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_64BIT: 7188c2ecf20Sopenharmony_ci parm32[i].u.value64 = parm64[i].u.value64; 7198c2ecf20Sopenharmony_ci break; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: 7228c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR: 7238c2ecf20Sopenharmony_ci case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: 7248c2ecf20Sopenharmony_ci parm32[i].u.pointer.size = parm64[i].u.pointer.size; 7258c2ecf20Sopenharmony_ci break; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci default: 7288c2ecf20Sopenharmony_ci WARN_ON(1); 7298c2ecf20Sopenharmony_ci ret = -EINVAL; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ciout_free: 7348c2ecf20Sopenharmony_ci kfree(parm64); 7358c2ecf20Sopenharmony_ci return ret; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci#endif 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic const int vbg_status_code_to_errno_table[] = { 7408c2ecf20Sopenharmony_ci [-VERR_ACCESS_DENIED] = -EPERM, 7418c2ecf20Sopenharmony_ci [-VERR_FILE_NOT_FOUND] = -ENOENT, 7428c2ecf20Sopenharmony_ci [-VERR_PROCESS_NOT_FOUND] = -ESRCH, 7438c2ecf20Sopenharmony_ci [-VERR_INTERRUPTED] = -EINTR, 7448c2ecf20Sopenharmony_ci [-VERR_DEV_IO_ERROR] = -EIO, 7458c2ecf20Sopenharmony_ci [-VERR_TOO_MUCH_DATA] = -E2BIG, 7468c2ecf20Sopenharmony_ci [-VERR_BAD_EXE_FORMAT] = -ENOEXEC, 7478c2ecf20Sopenharmony_ci [-VERR_INVALID_HANDLE] = -EBADF, 7488c2ecf20Sopenharmony_ci [-VERR_TRY_AGAIN] = -EAGAIN, 7498c2ecf20Sopenharmony_ci [-VERR_NO_MEMORY] = -ENOMEM, 7508c2ecf20Sopenharmony_ci [-VERR_INVALID_POINTER] = -EFAULT, 7518c2ecf20Sopenharmony_ci [-VERR_RESOURCE_BUSY] = -EBUSY, 7528c2ecf20Sopenharmony_ci [-VERR_ALREADY_EXISTS] = -EEXIST, 7538c2ecf20Sopenharmony_ci [-VERR_NOT_SAME_DEVICE] = -EXDEV, 7548c2ecf20Sopenharmony_ci [-VERR_NOT_A_DIRECTORY] = -ENOTDIR, 7558c2ecf20Sopenharmony_ci [-VERR_PATH_NOT_FOUND] = -ENOTDIR, 7568c2ecf20Sopenharmony_ci [-VERR_INVALID_NAME] = -ENOENT, 7578c2ecf20Sopenharmony_ci [-VERR_IS_A_DIRECTORY] = -EISDIR, 7588c2ecf20Sopenharmony_ci [-VERR_INVALID_PARAMETER] = -EINVAL, 7598c2ecf20Sopenharmony_ci [-VERR_TOO_MANY_OPEN_FILES] = -ENFILE, 7608c2ecf20Sopenharmony_ci [-VERR_INVALID_FUNCTION] = -ENOTTY, 7618c2ecf20Sopenharmony_ci [-VERR_SHARING_VIOLATION] = -ETXTBSY, 7628c2ecf20Sopenharmony_ci [-VERR_FILE_TOO_BIG] = -EFBIG, 7638c2ecf20Sopenharmony_ci [-VERR_DISK_FULL] = -ENOSPC, 7648c2ecf20Sopenharmony_ci [-VERR_SEEK_ON_DEVICE] = -ESPIPE, 7658c2ecf20Sopenharmony_ci [-VERR_WRITE_PROTECT] = -EROFS, 7668c2ecf20Sopenharmony_ci [-VERR_BROKEN_PIPE] = -EPIPE, 7678c2ecf20Sopenharmony_ci [-VERR_DEADLOCK] = -EDEADLK, 7688c2ecf20Sopenharmony_ci [-VERR_FILENAME_TOO_LONG] = -ENAMETOOLONG, 7698c2ecf20Sopenharmony_ci [-VERR_FILE_LOCK_FAILED] = -ENOLCK, 7708c2ecf20Sopenharmony_ci [-VERR_NOT_IMPLEMENTED] = -ENOSYS, 7718c2ecf20Sopenharmony_ci [-VERR_NOT_SUPPORTED] = -ENOSYS, 7728c2ecf20Sopenharmony_ci [-VERR_DIR_NOT_EMPTY] = -ENOTEMPTY, 7738c2ecf20Sopenharmony_ci [-VERR_TOO_MANY_SYMLINKS] = -ELOOP, 7748c2ecf20Sopenharmony_ci [-VERR_NO_MORE_FILES] = -ENODATA, 7758c2ecf20Sopenharmony_ci [-VERR_NO_DATA] = -ENODATA, 7768c2ecf20Sopenharmony_ci [-VERR_NET_NO_NETWORK] = -ENONET, 7778c2ecf20Sopenharmony_ci [-VERR_NET_NOT_UNIQUE_NAME] = -ENOTUNIQ, 7788c2ecf20Sopenharmony_ci [-VERR_NO_TRANSLATION] = -EILSEQ, 7798c2ecf20Sopenharmony_ci [-VERR_NET_NOT_SOCKET] = -ENOTSOCK, 7808c2ecf20Sopenharmony_ci [-VERR_NET_DEST_ADDRESS_REQUIRED] = -EDESTADDRREQ, 7818c2ecf20Sopenharmony_ci [-VERR_NET_MSG_SIZE] = -EMSGSIZE, 7828c2ecf20Sopenharmony_ci [-VERR_NET_PROTOCOL_TYPE] = -EPROTOTYPE, 7838c2ecf20Sopenharmony_ci [-VERR_NET_PROTOCOL_NOT_AVAILABLE] = -ENOPROTOOPT, 7848c2ecf20Sopenharmony_ci [-VERR_NET_PROTOCOL_NOT_SUPPORTED] = -EPROTONOSUPPORT, 7858c2ecf20Sopenharmony_ci [-VERR_NET_SOCKET_TYPE_NOT_SUPPORTED] = -ESOCKTNOSUPPORT, 7868c2ecf20Sopenharmony_ci [-VERR_NET_OPERATION_NOT_SUPPORTED] = -EOPNOTSUPP, 7878c2ecf20Sopenharmony_ci [-VERR_NET_PROTOCOL_FAMILY_NOT_SUPPORTED] = -EPFNOSUPPORT, 7888c2ecf20Sopenharmony_ci [-VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED] = -EAFNOSUPPORT, 7898c2ecf20Sopenharmony_ci [-VERR_NET_ADDRESS_IN_USE] = -EADDRINUSE, 7908c2ecf20Sopenharmony_ci [-VERR_NET_ADDRESS_NOT_AVAILABLE] = -EADDRNOTAVAIL, 7918c2ecf20Sopenharmony_ci [-VERR_NET_DOWN] = -ENETDOWN, 7928c2ecf20Sopenharmony_ci [-VERR_NET_UNREACHABLE] = -ENETUNREACH, 7938c2ecf20Sopenharmony_ci [-VERR_NET_CONNECTION_RESET] = -ENETRESET, 7948c2ecf20Sopenharmony_ci [-VERR_NET_CONNECTION_ABORTED] = -ECONNABORTED, 7958c2ecf20Sopenharmony_ci [-VERR_NET_CONNECTION_RESET_BY_PEER] = -ECONNRESET, 7968c2ecf20Sopenharmony_ci [-VERR_NET_NO_BUFFER_SPACE] = -ENOBUFS, 7978c2ecf20Sopenharmony_ci [-VERR_NET_ALREADY_CONNECTED] = -EISCONN, 7988c2ecf20Sopenharmony_ci [-VERR_NET_NOT_CONNECTED] = -ENOTCONN, 7998c2ecf20Sopenharmony_ci [-VERR_NET_SHUTDOWN] = -ESHUTDOWN, 8008c2ecf20Sopenharmony_ci [-VERR_NET_TOO_MANY_REFERENCES] = -ETOOMANYREFS, 8018c2ecf20Sopenharmony_ci [-VERR_TIMEOUT] = -ETIMEDOUT, 8028c2ecf20Sopenharmony_ci [-VERR_NET_CONNECTION_REFUSED] = -ECONNREFUSED, 8038c2ecf20Sopenharmony_ci [-VERR_NET_HOST_DOWN] = -EHOSTDOWN, 8048c2ecf20Sopenharmony_ci [-VERR_NET_HOST_UNREACHABLE] = -EHOSTUNREACH, 8058c2ecf20Sopenharmony_ci [-VERR_NET_ALREADY_IN_PROGRESS] = -EALREADY, 8068c2ecf20Sopenharmony_ci [-VERR_NET_IN_PROGRESS] = -EINPROGRESS, 8078c2ecf20Sopenharmony_ci [-VERR_MEDIA_NOT_PRESENT] = -ENOMEDIUM, 8088c2ecf20Sopenharmony_ci [-VERR_MEDIA_NOT_RECOGNIZED] = -EMEDIUMTYPE, 8098c2ecf20Sopenharmony_ci}; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ciint vbg_status_code_to_errno(int rc) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci if (rc >= 0) 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci rc = -rc; 8178c2ecf20Sopenharmony_ci if (rc >= ARRAY_SIZE(vbg_status_code_to_errno_table) || 8188c2ecf20Sopenharmony_ci vbg_status_code_to_errno_table[rc] == 0) { 8198c2ecf20Sopenharmony_ci vbg_warn("%s: Unhandled err %d\n", __func__, -rc); 8208c2ecf20Sopenharmony_ci return -EPROTO; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return vbg_status_code_to_errno_table[rc]; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vbg_status_code_to_errno); 826