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 Corporation |
7 | */ |
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 0x504 |
33 | |
34 | /* This protects vbg_log_buf and serializes VBG_DEBUG_PORT accesses */ |
35 | static DEFINE_SPINLOCK(vbg_log_lock); |
36 | static char vbg_log_buf[128]; |
37 | |
38 | #define VBG_LOG(name, pr_func) \ |
39 | void 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 | } \ |
57 | EXPORT_SYMBOL(name) |
58 | |
59 | VBG_LOG(vbg_info, pr_info); |
60 | VBG_LOG(vbg_warn, pr_warn); |
61 | VBG_LOG(vbg_err, pr_err); |
62 | VBG_LOG(vbg_err_ratelimited, pr_err_ratelimited); |
63 | #if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) |
64 | VBG_LOG(vbg_debug, pr_debug); |
65 | #endif |
66 | |
67 | void *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 | |
89 | void vbg_req_free(void *req, size_t len) |
90 | { |
91 | if (!req) |
92 | return; |
93 | |
94 | free_pages(addr: (unsigned long)req, order: get_order(PAGE_ALIGN(len))); |
95 | } |
96 | |
97 | /* Note this function returns a VBox status code, not a negative errno!! */ |
98 | int vbg_req_perform(struct vbg_dev *gdev, void *req) |
99 | { |
100 | unsigned long phys_req = virt_to_phys(address: req); |
101 | |
102 | outl(value: phys_req, port: gdev->io_port + VMMDEV_PORT_OFF_REQUEST); |
103 | /* |
104 | * The host changes the request as a result of the outl, make sure |
105 | * 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 | |
112 | static bool hgcm_req_done(struct vbg_dev *gdev, |
113 | struct vmmdev_hgcmreq_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(lock: &gdev->event_spinlock, flags); |
121 | |
122 | return done; |
123 | } |
124 | |
125 | int 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(len: sizeof(*hgcm_connect), |
133 | req_type: 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, req: 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(req: hgcm_connect, len: sizeof(*hgcm_connect)); |
153 | |
154 | *vbox_status = rc; |
155 | return 0; |
156 | } |
157 | EXPORT_SYMBOL(vbg_hgcm_connect); |
158 | |
159 | int 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(len: sizeof(*hgcm_disconnect), |
166 | req_type: 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, req: 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(req: hgcm_disconnect, len: sizeof(*hgcm_disconnect)); |
184 | |
185 | *vbox_status = rc; |
186 | return 0; |
187 | } |
188 | EXPORT_SYMBOL(vbg_hgcm_disconnect); |
189 | |
190 | static 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 | |
197 | static void hgcm_call_add_pagelist_size(void *buf, u32 len, size_t *) |
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 | |
205 | static int hgcm_call_preprocess_linaddr( |
206 | const struct vmmdev_hgcm_function_parameter *src_parm, |
207 | void **bounce_buf_ret, size_t *) |
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(size: 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(to: bounce_buf, from: (void __user *)buf, n: len); |
229 | if (ret) |
230 | return -EFAULT; |
231 | } else { |
232 | memset(bounce_buf, 0, len); |
233 | } |
234 | |
235 | hgcm_call_add_pagelist_size(buf: 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 for |
242 | * page lists. |
243 | * @src_parm: Pointer to source function call parameters |
244 | * @parm_count: Number of function call parameters. |
245 | * @bounce_bufs_ret: Where to return the allocated bouncebuffer array |
246 | * @extra: Where to return the extra request space needed for |
247 | * physical page lists. |
248 | * |
249 | * Return: %0 or negative errno value. |
250 | */ |
251 | static int hgcm_call_preprocess( |
252 | const struct vmmdev_hgcm_function_parameter *src_parm, |
253 | u32 parm_count, void ***bounce_bufs_ret, size_t *) |
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(n: parm_count, |
270 | size: 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_buf_ret: &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 address |
307 | * types to page list direction flags. |
308 | * @type: The type. |
309 | * |
310 | * Return: page list flags. |
311 | */ |
312 | static 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 | |
333 | static 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 *) |
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(x: 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(addr: buf); |
364 | else |
365 | 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 sending |
376 | * 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 | */ |
384 | static 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, = (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, buf: bounce_bufs[i], |
411 | len: src_parm->u.pointer.size, |
412 | type: src_parm->type, off_extra: &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 | len: src_parm->u.pointer.size, |
421 | type: src_parm->type, off_extra: &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 code |
437 | */ |
438 | static 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 is |
444 | * protected by cancel_req_mutex. This means that all cancellations |
445 | * 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(address: call); |
449 | rc = vbg_req_perform(gdev, req: gdev->cancel_req); |
450 | mutex_unlock(lock: &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, req: 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 interruptible |
473 | * @leak_it: Where to return the leak it / free it, indicator. |
474 | * Cancellation fun. |
475 | * |
476 | * Return: %0 or negative errno value. |
477 | */ |
478 | static 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, req: call); |
487 | |
488 | /* |
489 | * If the call failed, then pretend success. Upper layers will |
490 | * 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 | else |
504 | timeout = msecs_to_jiffies(m: 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 | else |
523 | 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 the |
532 | * 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(m: 500); |
536 | else |
537 | timeout = msecs_to_jiffies(m: 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 to |
557 | * 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 | */ |
565 | static 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(to: p, from: 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 | |
616 | int 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 increases |
631 | * call_size with the amount of extra space needed for page lists. |
632 | */ |
633 | ret = hgcm_call_preprocess(src_parm: parms, parm_count, bounce_bufs_ret: &bounce_bufs, extra: &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(len: 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, src_parm: parms, parm_count, |
646 | bounce_bufs); |
647 | |
648 | ret = vbg_hgcm_do_call(gdev, call, timeout_ms, |
649 | interruptible: requestor & VMMDEV_REQUESTOR_USERMODE, leak_it: &leak_it); |
650 | if (ret == 0) { |
651 | *vbox_status = call->header.result; |
652 | ret = hgcm_call_copy_back_result(call, dst_parm: parms, parm_count, |
653 | bounce_bufs); |
654 | } |
655 | |
656 | if (!leak_it) |
657 | vbg_req_free(req: call, len: size); |
658 | |
659 | free_bounce_bufs: |
660 | if (bounce_bufs) { |
661 | for (i = 0; i < parm_count; i++) |
662 | kvfree(addr: bounce_bufs[i]); |
663 | kfree(objp: bounce_bufs); |
664 | } |
665 | |
666 | return ret; |
667 | } |
668 | EXPORT_SYMBOL(vbg_hgcm_call); |
669 | |
670 | #ifdef CONFIG_COMPAT |
671 | int 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 | |
742 | out_free: |
743 | kfree(objp: parm64); |
744 | return ret; |
745 | } |
746 | #endif |
747 | |
748 | static 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 | |
820 | int 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 | } |
834 | EXPORT_SYMBOL(vbg_status_code_to_errno); |
835 | |