1 | /* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */ |
2 | /* |
3 | * vboxguest core guest-device handling code, VBoxGuest.cpp in upstream svn. |
4 | * |
5 | * Copyright (C) 2007-2016 Oracle Corporation |
6 | */ |
7 | |
8 | #include <linux/device.h> |
9 | #include <linux/io.h> |
10 | #include <linux/mm.h> |
11 | #include <linux/sched.h> |
12 | #include <linux/sizes.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/vbox_err.h> |
15 | #include <linux/vbox_utils.h> |
16 | #include <linux/vmalloc.h> |
17 | #include "vboxguest_core.h" |
18 | #include "vboxguest_version.h" |
19 | |
20 | /* Get the pointer to the first HGCM parameter. */ |
21 | #define VBG_IOCTL_HGCM_CALL_PARMS(a) \ |
22 | ((struct vmmdev_hgcm_function_parameter *)( \ |
23 | (u8 *)(a) + sizeof(struct vbg_ioctl_hgcm_call))) |
24 | /* Get the pointer to the first HGCM parameter in a 32-bit request. */ |
25 | #define VBG_IOCTL_HGCM_CALL_PARMS32(a) \ |
26 | ((struct vmmdev_hgcm_function_parameter32 *)( \ |
27 | (u8 *)(a) + sizeof(struct vbg_ioctl_hgcm_call))) |
28 | |
29 | #define GUEST_MAPPINGS_TRIES 5 |
30 | |
31 | #define VBG_KERNEL_REQUEST \ |
32 | (VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV | \ |
33 | VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN) |
34 | |
35 | /** |
36 | * vbg_guest_mappings_init - Reserves memory in which the VMM can |
37 | * relocate any guest mappings that are floating around. |
38 | * @gdev: The Guest extension device. |
39 | * |
40 | * This operation is a little bit tricky since the VMM might not accept |
41 | * just any address because of address clashes between the three contexts |
42 | * it operates in, so we try several times. |
43 | * |
44 | * Failure to reserve the guest mappings is ignored. |
45 | */ |
46 | static void vbg_guest_mappings_init(struct vbg_dev *gdev) |
47 | { |
48 | struct vmmdev_hypervisorinfo *req; |
49 | void *guest_mappings[GUEST_MAPPINGS_TRIES]; |
50 | struct page **pages = NULL; |
51 | u32 size, hypervisor_size; |
52 | int i, rc; |
53 | |
54 | /* Query the required space. */ |
55 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_GET_HYPERVISOR_INFO, |
56 | VBG_KERNEL_REQUEST); |
57 | if (!req) |
58 | return; |
59 | |
60 | req->hypervisor_start = 0; |
61 | req->hypervisor_size = 0; |
62 | rc = vbg_req_perform(gdev, req); |
63 | if (rc < 0) |
64 | goto out; |
65 | |
66 | /* |
67 | * The VMM will report back if there is nothing it wants to map, like |
68 | * for instance in VT-x and AMD-V mode. |
69 | */ |
70 | if (req->hypervisor_size == 0) |
71 | goto out; |
72 | |
73 | hypervisor_size = req->hypervisor_size; |
74 | /* Add 4M so that we can align the vmap to 4MiB as the host requires. */ |
75 | size = PAGE_ALIGN(req->hypervisor_size) + SZ_4M; |
76 | |
77 | pages = kmalloc_array(n: size >> PAGE_SHIFT, size: sizeof(*pages), GFP_KERNEL); |
78 | if (!pages) |
79 | goto out; |
80 | |
81 | gdev->guest_mappings_dummy_page = alloc_page(GFP_HIGHUSER); |
82 | if (!gdev->guest_mappings_dummy_page) |
83 | goto out; |
84 | |
85 | for (i = 0; i < (size >> PAGE_SHIFT); i++) |
86 | pages[i] = gdev->guest_mappings_dummy_page; |
87 | |
88 | /* |
89 | * Try several times, the VMM might not accept some addresses because |
90 | * of address clashes between the three contexts. |
91 | */ |
92 | for (i = 0; i < GUEST_MAPPINGS_TRIES; i++) { |
93 | guest_mappings[i] = vmap(pages, count: (size >> PAGE_SHIFT), |
94 | VM_MAP, PAGE_KERNEL_RO); |
95 | if (!guest_mappings[i]) |
96 | break; |
97 | |
98 | req->header.request_type = VMMDEVREQ_SET_HYPERVISOR_INFO; |
99 | req->header.rc = VERR_INTERNAL_ERROR; |
100 | req->hypervisor_size = hypervisor_size; |
101 | req->hypervisor_start = |
102 | (unsigned long)PTR_ALIGN(guest_mappings[i], SZ_4M); |
103 | |
104 | rc = vbg_req_perform(gdev, req); |
105 | if (rc >= 0) { |
106 | gdev->guest_mappings = guest_mappings[i]; |
107 | break; |
108 | } |
109 | } |
110 | |
111 | /* Free vmap's from failed attempts. */ |
112 | while (--i >= 0) |
113 | vunmap(addr: guest_mappings[i]); |
114 | |
115 | /* On failure free the dummy-page backing the vmap */ |
116 | if (!gdev->guest_mappings) { |
117 | __free_page(gdev->guest_mappings_dummy_page); |
118 | gdev->guest_mappings_dummy_page = NULL; |
119 | } |
120 | |
121 | out: |
122 | vbg_req_free(req, len: sizeof(*req)); |
123 | kfree(objp: pages); |
124 | } |
125 | |
126 | /** |
127 | * vbg_guest_mappings_exit - Undo what vbg_guest_mappings_init did. |
128 | * |
129 | * @gdev: The Guest extension device. |
130 | */ |
131 | static void vbg_guest_mappings_exit(struct vbg_dev *gdev) |
132 | { |
133 | struct vmmdev_hypervisorinfo *req; |
134 | int rc; |
135 | |
136 | if (!gdev->guest_mappings) |
137 | return; |
138 | |
139 | /* |
140 | * Tell the host that we're going to free the memory we reserved for |
141 | * it, the free it up. (Leak the memory if anything goes wrong here.) |
142 | */ |
143 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_SET_HYPERVISOR_INFO, |
144 | VBG_KERNEL_REQUEST); |
145 | if (!req) |
146 | return; |
147 | |
148 | req->hypervisor_start = 0; |
149 | req->hypervisor_size = 0; |
150 | |
151 | rc = vbg_req_perform(gdev, req); |
152 | |
153 | vbg_req_free(req, len: sizeof(*req)); |
154 | |
155 | if (rc < 0) { |
156 | vbg_err(fmt: "%s error: %d\n" , __func__, rc); |
157 | return; |
158 | } |
159 | |
160 | vunmap(addr: gdev->guest_mappings); |
161 | gdev->guest_mappings = NULL; |
162 | |
163 | __free_page(gdev->guest_mappings_dummy_page); |
164 | gdev->guest_mappings_dummy_page = NULL; |
165 | } |
166 | |
167 | /** |
168 | * vbg_report_guest_info - Report the guest information to the host. |
169 | * @gdev: The Guest extension device. |
170 | * |
171 | * Return: %0 or negative errno value. |
172 | */ |
173 | static int vbg_report_guest_info(struct vbg_dev *gdev) |
174 | { |
175 | /* |
176 | * Allocate and fill in the two guest info reports. |
177 | */ |
178 | struct vmmdev_guest_info *req1 = NULL; |
179 | struct vmmdev_guest_info2 *req2 = NULL; |
180 | int rc, ret = -ENOMEM; |
181 | |
182 | req1 = vbg_req_alloc(len: sizeof(*req1), req_type: VMMDEVREQ_REPORT_GUEST_INFO, |
183 | VBG_KERNEL_REQUEST); |
184 | req2 = vbg_req_alloc(len: sizeof(*req2), req_type: VMMDEVREQ_REPORT_GUEST_INFO2, |
185 | VBG_KERNEL_REQUEST); |
186 | if (!req1 || !req2) |
187 | goto out_free; |
188 | |
189 | req1->interface_version = VMMDEV_VERSION; |
190 | req1->os_type = VMMDEV_OSTYPE_LINUX26; |
191 | #if __BITS_PER_LONG == 64 |
192 | req1->os_type |= VMMDEV_OSTYPE_X64; |
193 | #endif |
194 | |
195 | req2->additions_major = VBG_VERSION_MAJOR; |
196 | req2->additions_minor = VBG_VERSION_MINOR; |
197 | req2->additions_build = VBG_VERSION_BUILD; |
198 | req2->additions_revision = VBG_SVN_REV; |
199 | req2->additions_features = |
200 | VMMDEV_GUEST_INFO2_ADDITIONS_FEATURES_REQUESTOR_INFO; |
201 | strscpy(req2->name, VBG_VERSION_STRING, |
202 | sizeof(req2->name)); |
203 | |
204 | /* |
205 | * There are two protocols here: |
206 | * 1. INFO2 + INFO1. Supported by >=3.2.51. |
207 | * 2. INFO1 and optionally INFO2. The old protocol. |
208 | * |
209 | * We try protocol 2 first. It will fail with VERR_NOT_SUPPORTED |
210 | * if not supported by the VMMDev (message ordering requirement). |
211 | */ |
212 | rc = vbg_req_perform(gdev, req: req2); |
213 | if (rc >= 0) { |
214 | rc = vbg_req_perform(gdev, req: req1); |
215 | } else if (rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED) { |
216 | rc = vbg_req_perform(gdev, req: req1); |
217 | if (rc >= 0) { |
218 | rc = vbg_req_perform(gdev, req: req2); |
219 | if (rc == VERR_NOT_IMPLEMENTED) |
220 | rc = VINF_SUCCESS; |
221 | } |
222 | } |
223 | ret = vbg_status_code_to_errno(rc); |
224 | |
225 | out_free: |
226 | vbg_req_free(req: req2, len: sizeof(*req2)); |
227 | vbg_req_free(req: req1, len: sizeof(*req1)); |
228 | return ret; |
229 | } |
230 | |
231 | /** |
232 | * vbg_report_driver_status - Report the guest driver status to the host. |
233 | * @gdev: The Guest extension device. |
234 | * @active: Flag whether the driver is now active or not. |
235 | * |
236 | * Return: 0 or negative errno value. |
237 | */ |
238 | static int vbg_report_driver_status(struct vbg_dev *gdev, bool active) |
239 | { |
240 | struct vmmdev_guest_status *req; |
241 | int rc; |
242 | |
243 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_REPORT_GUEST_STATUS, |
244 | VBG_KERNEL_REQUEST); |
245 | if (!req) |
246 | return -ENOMEM; |
247 | |
248 | req->facility = VBOXGUEST_FACILITY_TYPE_VBOXGUEST_DRIVER; |
249 | if (active) |
250 | req->status = VBOXGUEST_FACILITY_STATUS_ACTIVE; |
251 | else |
252 | req->status = VBOXGUEST_FACILITY_STATUS_INACTIVE; |
253 | req->flags = 0; |
254 | |
255 | rc = vbg_req_perform(gdev, req); |
256 | if (rc == VERR_NOT_IMPLEMENTED) /* Compatibility with older hosts. */ |
257 | rc = VINF_SUCCESS; |
258 | |
259 | vbg_req_free(req, len: sizeof(*req)); |
260 | |
261 | return vbg_status_code_to_errno(rc); |
262 | } |
263 | |
264 | /** |
265 | * vbg_balloon_inflate - Inflate the balloon by one chunk. The caller |
266 | * owns the balloon mutex. |
267 | * @gdev: The Guest extension device. |
268 | * @chunk_idx: Index of the chunk. |
269 | * |
270 | * Return: %0 or negative errno value. |
271 | */ |
272 | static int vbg_balloon_inflate(struct vbg_dev *gdev, u32 chunk_idx) |
273 | { |
274 | struct vmmdev_memballoon_change *req = gdev->mem_balloon.change_req; |
275 | struct page **pages; |
276 | int i, rc, ret; |
277 | |
278 | pages = kmalloc_array(VMMDEV_MEMORY_BALLOON_CHUNK_PAGES, |
279 | size: sizeof(*pages), |
280 | GFP_KERNEL | __GFP_NOWARN); |
281 | if (!pages) |
282 | return -ENOMEM; |
283 | |
284 | req->header.size = sizeof(*req); |
285 | req->inflate = true; |
286 | req->pages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; |
287 | |
288 | for (i = 0; i < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; i++) { |
289 | pages[i] = alloc_page(GFP_KERNEL | __GFP_NOWARN); |
290 | if (!pages[i]) { |
291 | ret = -ENOMEM; |
292 | goto out_error; |
293 | } |
294 | |
295 | req->phys_page[i] = page_to_phys(pages[i]); |
296 | } |
297 | |
298 | rc = vbg_req_perform(gdev, req); |
299 | if (rc < 0) { |
300 | vbg_err(fmt: "%s error, rc: %d\n" , __func__, rc); |
301 | ret = vbg_status_code_to_errno(rc); |
302 | goto out_error; |
303 | } |
304 | |
305 | gdev->mem_balloon.pages[chunk_idx] = pages; |
306 | |
307 | return 0; |
308 | |
309 | out_error: |
310 | while (--i >= 0) |
311 | __free_page(pages[i]); |
312 | kfree(objp: pages); |
313 | |
314 | return ret; |
315 | } |
316 | |
317 | /** |
318 | * vbg_balloon_deflate - Deflate the balloon by one chunk. The caller |
319 | * owns the balloon mutex. |
320 | * @gdev: The Guest extension device. |
321 | * @chunk_idx: Index of the chunk. |
322 | * |
323 | * Return: %0 or negative errno value. |
324 | */ |
325 | static int vbg_balloon_deflate(struct vbg_dev *gdev, u32 chunk_idx) |
326 | { |
327 | struct vmmdev_memballoon_change *req = gdev->mem_balloon.change_req; |
328 | struct page **pages = gdev->mem_balloon.pages[chunk_idx]; |
329 | int i, rc; |
330 | |
331 | req->header.size = sizeof(*req); |
332 | req->inflate = false; |
333 | req->pages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; |
334 | |
335 | for (i = 0; i < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; i++) |
336 | req->phys_page[i] = page_to_phys(pages[i]); |
337 | |
338 | rc = vbg_req_perform(gdev, req); |
339 | if (rc < 0) { |
340 | vbg_err(fmt: "%s error, rc: %d\n" , __func__, rc); |
341 | return vbg_status_code_to_errno(rc); |
342 | } |
343 | |
344 | for (i = 0; i < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; i++) |
345 | __free_page(pages[i]); |
346 | kfree(objp: pages); |
347 | gdev->mem_balloon.pages[chunk_idx] = NULL; |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | /* |
353 | * Respond to VMMDEV_EVENT_BALLOON_CHANGE_REQUEST events, query the size |
354 | * the host wants the balloon to be and adjust accordingly. |
355 | */ |
356 | static void vbg_balloon_work(struct work_struct *work) |
357 | { |
358 | struct vbg_dev *gdev = |
359 | container_of(work, struct vbg_dev, mem_balloon.work); |
360 | struct vmmdev_memballoon_info *req = gdev->mem_balloon.get_req; |
361 | u32 i, chunks; |
362 | int rc, ret; |
363 | |
364 | /* |
365 | * Setting this bit means that we request the value from the host and |
366 | * change the guest memory balloon according to the returned value. |
367 | */ |
368 | req->event_ack = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST; |
369 | rc = vbg_req_perform(gdev, req); |
370 | if (rc < 0) { |
371 | vbg_err(fmt: "%s error, rc: %d)\n" , __func__, rc); |
372 | return; |
373 | } |
374 | |
375 | /* |
376 | * The host always returns the same maximum amount of chunks, so |
377 | * we do this once. |
378 | */ |
379 | if (!gdev->mem_balloon.max_chunks) { |
380 | gdev->mem_balloon.pages = |
381 | devm_kcalloc(dev: gdev->dev, n: req->phys_mem_chunks, |
382 | size: sizeof(struct page **), GFP_KERNEL); |
383 | if (!gdev->mem_balloon.pages) |
384 | return; |
385 | |
386 | gdev->mem_balloon.max_chunks = req->phys_mem_chunks; |
387 | } |
388 | |
389 | chunks = req->balloon_chunks; |
390 | if (chunks > gdev->mem_balloon.max_chunks) { |
391 | vbg_err(fmt: "%s: illegal balloon size %u (max=%u)\n" , |
392 | __func__, chunks, gdev->mem_balloon.max_chunks); |
393 | return; |
394 | } |
395 | |
396 | if (chunks > gdev->mem_balloon.chunks) { |
397 | /* inflate */ |
398 | for (i = gdev->mem_balloon.chunks; i < chunks; i++) { |
399 | ret = vbg_balloon_inflate(gdev, chunk_idx: i); |
400 | if (ret < 0) |
401 | return; |
402 | |
403 | gdev->mem_balloon.chunks++; |
404 | } |
405 | } else { |
406 | /* deflate */ |
407 | for (i = gdev->mem_balloon.chunks; i-- > chunks;) { |
408 | ret = vbg_balloon_deflate(gdev, chunk_idx: i); |
409 | if (ret < 0) |
410 | return; |
411 | |
412 | gdev->mem_balloon.chunks--; |
413 | } |
414 | } |
415 | } |
416 | |
417 | /* |
418 | * Callback for heartbeat timer. |
419 | */ |
420 | static void vbg_heartbeat_timer(struct timer_list *t) |
421 | { |
422 | struct vbg_dev *gdev = from_timer(gdev, t, heartbeat_timer); |
423 | |
424 | vbg_req_perform(gdev, req: gdev->guest_heartbeat_req); |
425 | mod_timer(timer: &gdev->heartbeat_timer, |
426 | expires: msecs_to_jiffies(m: gdev->heartbeat_interval_ms)); |
427 | } |
428 | |
429 | /** |
430 | * vbg_heartbeat_host_config - Configure the host to check guest's heartbeat |
431 | * and get heartbeat interval from the host. |
432 | * @gdev: The Guest extension device. |
433 | * @enabled: Set true to enable guest heartbeat checks on host. |
434 | * |
435 | * Return: %0 or negative errno value. |
436 | */ |
437 | static int vbg_heartbeat_host_config(struct vbg_dev *gdev, bool enabled) |
438 | { |
439 | struct vmmdev_heartbeat *req; |
440 | int rc; |
441 | |
442 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_HEARTBEAT_CONFIGURE, |
443 | VBG_KERNEL_REQUEST); |
444 | if (!req) |
445 | return -ENOMEM; |
446 | |
447 | req->enabled = enabled; |
448 | req->interval_ns = 0; |
449 | rc = vbg_req_perform(gdev, req); |
450 | do_div(req->interval_ns, 1000000); /* ns -> ms */ |
451 | gdev->heartbeat_interval_ms = req->interval_ns; |
452 | vbg_req_free(req, len: sizeof(*req)); |
453 | |
454 | return vbg_status_code_to_errno(rc); |
455 | } |
456 | |
457 | /** |
458 | * vbg_heartbeat_init - Initializes the heartbeat timer. This feature |
459 | * may be disabled by the host. |
460 | * @gdev: The Guest extension device. |
461 | * |
462 | * Return: %0 or negative errno value. |
463 | */ |
464 | static int vbg_heartbeat_init(struct vbg_dev *gdev) |
465 | { |
466 | int ret; |
467 | |
468 | /* Make sure that heartbeat checking is disabled if we fail. */ |
469 | ret = vbg_heartbeat_host_config(gdev, enabled: false); |
470 | if (ret < 0) |
471 | return ret; |
472 | |
473 | ret = vbg_heartbeat_host_config(gdev, enabled: true); |
474 | if (ret < 0) |
475 | return ret; |
476 | |
477 | gdev->guest_heartbeat_req = vbg_req_alloc( |
478 | len: sizeof(*gdev->guest_heartbeat_req), |
479 | req_type: VMMDEVREQ_GUEST_HEARTBEAT, |
480 | VBG_KERNEL_REQUEST); |
481 | if (!gdev->guest_heartbeat_req) |
482 | return -ENOMEM; |
483 | |
484 | vbg_info(fmt: "%s: Setting up heartbeat to trigger every %d milliseconds\n" , |
485 | __func__, gdev->heartbeat_interval_ms); |
486 | mod_timer(timer: &gdev->heartbeat_timer, expires: 0); |
487 | |
488 | return 0; |
489 | } |
490 | |
491 | /** |
492 | * vbg_heartbeat_exit - Cleanup heartbeat code, stop HB timer and disable |
493 | * host heartbeat checking. |
494 | * @gdev: The Guest extension device. |
495 | */ |
496 | static void vbg_heartbeat_exit(struct vbg_dev *gdev) |
497 | { |
498 | del_timer_sync(timer: &gdev->heartbeat_timer); |
499 | vbg_heartbeat_host_config(gdev, enabled: false); |
500 | vbg_req_free(req: gdev->guest_heartbeat_req, |
501 | len: sizeof(*gdev->guest_heartbeat_req)); |
502 | } |
503 | |
504 | /** |
505 | * vbg_track_bit_usage - Applies a change to the bit usage tracker. |
506 | * @tracker: The bit usage tracker. |
507 | * @changed: The bits to change. |
508 | * @previous: The previous value of the bits. |
509 | * |
510 | * Return: %true if the mask changed, %false if not. |
511 | */ |
512 | static bool vbg_track_bit_usage(struct vbg_bit_usage_tracker *tracker, |
513 | u32 changed, u32 previous) |
514 | { |
515 | bool global_change = false; |
516 | |
517 | while (changed) { |
518 | u32 bit = ffs(changed) - 1; |
519 | u32 bitmask = BIT(bit); |
520 | |
521 | if (bitmask & previous) { |
522 | tracker->per_bit_usage[bit] -= 1; |
523 | if (tracker->per_bit_usage[bit] == 0) { |
524 | global_change = true; |
525 | tracker->mask &= ~bitmask; |
526 | } |
527 | } else { |
528 | tracker->per_bit_usage[bit] += 1; |
529 | if (tracker->per_bit_usage[bit] == 1) { |
530 | global_change = true; |
531 | tracker->mask |= bitmask; |
532 | } |
533 | } |
534 | |
535 | changed &= ~bitmask; |
536 | } |
537 | |
538 | return global_change; |
539 | } |
540 | |
541 | /** |
542 | * vbg_reset_host_event_filter - Init and termination worker for |
543 | * resetting the (host) event filter on the host |
544 | * @gdev: The Guest extension device. |
545 | * @fixed_events: Fixed events (init time). |
546 | * |
547 | * Return: %0 or negative errno value. |
548 | */ |
549 | static int vbg_reset_host_event_filter(struct vbg_dev *gdev, |
550 | u32 fixed_events) |
551 | { |
552 | struct vmmdev_mask *req; |
553 | int rc; |
554 | |
555 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_CTL_GUEST_FILTER_MASK, |
556 | VBG_KERNEL_REQUEST); |
557 | if (!req) |
558 | return -ENOMEM; |
559 | |
560 | req->not_mask = U32_MAX & ~fixed_events; |
561 | req->or_mask = fixed_events; |
562 | rc = vbg_req_perform(gdev, req); |
563 | if (rc < 0) |
564 | vbg_err(fmt: "%s error, rc: %d\n" , __func__, rc); |
565 | |
566 | vbg_req_free(req, len: sizeof(*req)); |
567 | return vbg_status_code_to_errno(rc); |
568 | } |
569 | |
570 | /** |
571 | * vbg_set_session_event_filter - Changes the event filter mask for the |
572 | * given session. |
573 | * @gdev: The Guest extension device. |
574 | * @session: The session. |
575 | * @or_mask: The events to add. |
576 | * @not_mask: The events to remove. |
577 | * @session_termination: Set if we're called by the session cleanup code. |
578 | * This tweaks the error handling so we perform |
579 | * proper session cleanup even if the host |
580 | * misbehaves. |
581 | * |
582 | * This is called in response to VBG_IOCTL_CHANGE_FILTER_MASK as well as to |
583 | * do session cleanup. Takes the session mutex. |
584 | * |
585 | * Return: 0 or negative errno value. |
586 | */ |
587 | static int vbg_set_session_event_filter(struct vbg_dev *gdev, |
588 | struct vbg_session *session, |
589 | u32 or_mask, u32 not_mask, |
590 | bool session_termination) |
591 | { |
592 | struct vmmdev_mask *req; |
593 | u32 changed, previous; |
594 | int rc, ret = 0; |
595 | |
596 | /* |
597 | * Allocate a request buffer before taking the spinlock, when |
598 | * the session is being terminated the requestor is the kernel, |
599 | * as we're cleaning up. |
600 | */ |
601 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_CTL_GUEST_FILTER_MASK, |
602 | requestor: session_termination ? VBG_KERNEL_REQUEST : |
603 | session->requestor); |
604 | if (!req) { |
605 | if (!session_termination) |
606 | return -ENOMEM; |
607 | /* Ignore allocation failure, we must do session cleanup. */ |
608 | } |
609 | |
610 | mutex_lock(&gdev->session_mutex); |
611 | |
612 | /* Apply the changes to the session mask. */ |
613 | previous = session->event_filter; |
614 | session->event_filter |= or_mask; |
615 | session->event_filter &= ~not_mask; |
616 | |
617 | /* If anything actually changed, update the global usage counters. */ |
618 | changed = previous ^ session->event_filter; |
619 | if (!changed) |
620 | goto out; |
621 | |
622 | vbg_track_bit_usage(tracker: &gdev->event_filter_tracker, changed, previous); |
623 | or_mask = gdev->fixed_events | gdev->event_filter_tracker.mask; |
624 | |
625 | if (gdev->event_filter_host == or_mask || !req) |
626 | goto out; |
627 | |
628 | gdev->event_filter_host = or_mask; |
629 | req->or_mask = or_mask; |
630 | req->not_mask = ~or_mask; |
631 | rc = vbg_req_perform(gdev, req); |
632 | if (rc < 0) { |
633 | ret = vbg_status_code_to_errno(rc); |
634 | |
635 | /* Failed, roll back (unless it's session termination time). */ |
636 | gdev->event_filter_host = U32_MAX; |
637 | if (session_termination) |
638 | goto out; |
639 | |
640 | vbg_track_bit_usage(tracker: &gdev->event_filter_tracker, changed, |
641 | previous: session->event_filter); |
642 | session->event_filter = previous; |
643 | } |
644 | |
645 | out: |
646 | mutex_unlock(lock: &gdev->session_mutex); |
647 | vbg_req_free(req, len: sizeof(*req)); |
648 | |
649 | return ret; |
650 | } |
651 | |
652 | /** |
653 | * vbg_reset_host_capabilities - Init and termination worker for set |
654 | * guest capabilities to zero on the host. |
655 | * @gdev: The Guest extension device. |
656 | * |
657 | * Return: %0 or negative errno value. |
658 | */ |
659 | static int vbg_reset_host_capabilities(struct vbg_dev *gdev) |
660 | { |
661 | struct vmmdev_mask *req; |
662 | int rc; |
663 | |
664 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_SET_GUEST_CAPABILITIES, |
665 | VBG_KERNEL_REQUEST); |
666 | if (!req) |
667 | return -ENOMEM; |
668 | |
669 | req->not_mask = U32_MAX; |
670 | req->or_mask = 0; |
671 | rc = vbg_req_perform(gdev, req); |
672 | if (rc < 0) |
673 | vbg_err(fmt: "%s error, rc: %d\n" , __func__, rc); |
674 | |
675 | vbg_req_free(req, len: sizeof(*req)); |
676 | return vbg_status_code_to_errno(rc); |
677 | } |
678 | |
679 | /** |
680 | * vbg_set_host_capabilities - Set guest capabilities on the host. |
681 | * @gdev: The Guest extension device. |
682 | * @session: The session. |
683 | * @session_termination: Set if we're called by the session cleanup code. |
684 | * |
685 | * Must be called with gdev->session_mutex hold. |
686 | * |
687 | * Return: %0 or negative errno value. |
688 | */ |
689 | static int vbg_set_host_capabilities(struct vbg_dev *gdev, |
690 | struct vbg_session *session, |
691 | bool session_termination) |
692 | { |
693 | struct vmmdev_mask *req; |
694 | u32 caps; |
695 | int rc; |
696 | |
697 | WARN_ON(!mutex_is_locked(&gdev->session_mutex)); |
698 | |
699 | caps = gdev->acquired_guest_caps | gdev->set_guest_caps_tracker.mask; |
700 | |
701 | if (gdev->guest_caps_host == caps) |
702 | return 0; |
703 | |
704 | /* On termination the requestor is the kernel, as we're cleaning up. */ |
705 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_SET_GUEST_CAPABILITIES, |
706 | requestor: session_termination ? VBG_KERNEL_REQUEST : |
707 | session->requestor); |
708 | if (!req) { |
709 | gdev->guest_caps_host = U32_MAX; |
710 | return -ENOMEM; |
711 | } |
712 | |
713 | req->or_mask = caps; |
714 | req->not_mask = ~caps; |
715 | rc = vbg_req_perform(gdev, req); |
716 | vbg_req_free(req, len: sizeof(*req)); |
717 | |
718 | gdev->guest_caps_host = (rc >= 0) ? caps : U32_MAX; |
719 | |
720 | return vbg_status_code_to_errno(rc); |
721 | } |
722 | |
723 | /** |
724 | * vbg_acquire_session_capabilities - Acquire (get exclusive access) |
725 | * guest capabilities for a session. |
726 | * @gdev: The Guest extension device. |
727 | * @session: The session. |
728 | * @flags: Flags (VBGL_IOC_AGC_FLAGS_XXX). |
729 | * @or_mask: The capabilities to add. |
730 | * @not_mask: The capabilities to remove. |
731 | * @session_termination: Set if we're called by the session cleanup code. |
732 | * This tweaks the error handling so we perform |
733 | * proper session cleanup even if the host |
734 | * misbehaves. |
735 | * |
736 | * Takes the session mutex. |
737 | * |
738 | * Return: %0 or negative errno value. |
739 | */ |
740 | static int vbg_acquire_session_capabilities(struct vbg_dev *gdev, |
741 | struct vbg_session *session, |
742 | u32 or_mask, u32 not_mask, |
743 | u32 flags, bool session_termination) |
744 | { |
745 | unsigned long irqflags; |
746 | bool wakeup = false; |
747 | int ret = 0; |
748 | |
749 | mutex_lock(&gdev->session_mutex); |
750 | |
751 | if (gdev->set_guest_caps_tracker.mask & or_mask) { |
752 | vbg_err(fmt: "%s error: cannot acquire caps which are currently set\n" , |
753 | __func__); |
754 | ret = -EINVAL; |
755 | goto out; |
756 | } |
757 | |
758 | /* |
759 | * Mark any caps in the or_mask as now being in acquire-mode. Note |
760 | * once caps are in acquire_mode they always stay in this mode. |
761 | * This impacts event handling, so we take the event-lock. |
762 | */ |
763 | spin_lock_irqsave(&gdev->event_spinlock, irqflags); |
764 | gdev->acquire_mode_guest_caps |= or_mask; |
765 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags: irqflags); |
766 | |
767 | /* If we only have to switch the caps to acquire mode, we're done. */ |
768 | if (flags & VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE) |
769 | goto out; |
770 | |
771 | not_mask &= ~or_mask; /* or_mask takes priority over not_mask */ |
772 | not_mask &= session->acquired_guest_caps; |
773 | or_mask &= ~session->acquired_guest_caps; |
774 | |
775 | if (or_mask == 0 && not_mask == 0) |
776 | goto out; |
777 | |
778 | if (gdev->acquired_guest_caps & or_mask) { |
779 | ret = -EBUSY; |
780 | goto out; |
781 | } |
782 | |
783 | gdev->acquired_guest_caps |= or_mask; |
784 | gdev->acquired_guest_caps &= ~not_mask; |
785 | /* session->acquired_guest_caps impacts event handling, take the lock */ |
786 | spin_lock_irqsave(&gdev->event_spinlock, irqflags); |
787 | session->acquired_guest_caps |= or_mask; |
788 | session->acquired_guest_caps &= ~not_mask; |
789 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags: irqflags); |
790 | |
791 | ret = vbg_set_host_capabilities(gdev, session, session_termination); |
792 | /* Roll back on failure, unless it's session termination time. */ |
793 | if (ret < 0 && !session_termination) { |
794 | gdev->acquired_guest_caps &= ~or_mask; |
795 | gdev->acquired_guest_caps |= not_mask; |
796 | spin_lock_irqsave(&gdev->event_spinlock, irqflags); |
797 | session->acquired_guest_caps &= ~or_mask; |
798 | session->acquired_guest_caps |= not_mask; |
799 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags: irqflags); |
800 | } |
801 | |
802 | /* |
803 | * If we added a capability, check if that means some other thread in |
804 | * our session should be unblocked because there are events pending |
805 | * (the result of vbg_get_allowed_event_mask_for_session() may change). |
806 | * |
807 | * HACK ALERT! When the seamless support capability is added we generate |
808 | * a seamless change event so that the ring-3 client can sync with |
809 | * the seamless state. |
810 | */ |
811 | if (ret == 0 && or_mask != 0) { |
812 | spin_lock_irqsave(&gdev->event_spinlock, irqflags); |
813 | |
814 | if (or_mask & VMMDEV_GUEST_SUPPORTS_SEAMLESS) |
815 | gdev->pending_events |= |
816 | VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; |
817 | |
818 | if (gdev->pending_events) |
819 | wakeup = true; |
820 | |
821 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags: irqflags); |
822 | |
823 | if (wakeup) |
824 | wake_up(&gdev->event_wq); |
825 | } |
826 | |
827 | out: |
828 | mutex_unlock(lock: &gdev->session_mutex); |
829 | |
830 | return ret; |
831 | } |
832 | |
833 | /** |
834 | * vbg_set_session_capabilities - Sets the guest capabilities for a |
835 | * session. Takes the session mutex. |
836 | * @gdev: The Guest extension device. |
837 | * @session: The session. |
838 | * @or_mask: The capabilities to add. |
839 | * @not_mask: The capabilities to remove. |
840 | * @session_termination: Set if we're called by the session cleanup code. |
841 | * This tweaks the error handling so we perform |
842 | * proper session cleanup even if the host |
843 | * misbehaves. |
844 | * |
845 | * Return: %0 or negative errno value. |
846 | */ |
847 | static int vbg_set_session_capabilities(struct vbg_dev *gdev, |
848 | struct vbg_session *session, |
849 | u32 or_mask, u32 not_mask, |
850 | bool session_termination) |
851 | { |
852 | u32 changed, previous; |
853 | int ret = 0; |
854 | |
855 | mutex_lock(&gdev->session_mutex); |
856 | |
857 | if (gdev->acquire_mode_guest_caps & or_mask) { |
858 | vbg_err(fmt: "%s error: cannot set caps which are in acquire_mode\n" , |
859 | __func__); |
860 | ret = -EBUSY; |
861 | goto out; |
862 | } |
863 | |
864 | /* Apply the changes to the session mask. */ |
865 | previous = session->set_guest_caps; |
866 | session->set_guest_caps |= or_mask; |
867 | session->set_guest_caps &= ~not_mask; |
868 | |
869 | /* If anything actually changed, update the global usage counters. */ |
870 | changed = previous ^ session->set_guest_caps; |
871 | if (!changed) |
872 | goto out; |
873 | |
874 | vbg_track_bit_usage(tracker: &gdev->set_guest_caps_tracker, changed, previous); |
875 | |
876 | ret = vbg_set_host_capabilities(gdev, session, session_termination); |
877 | /* Roll back on failure, unless it's session termination time. */ |
878 | if (ret < 0 && !session_termination) { |
879 | vbg_track_bit_usage(tracker: &gdev->set_guest_caps_tracker, changed, |
880 | previous: session->set_guest_caps); |
881 | session->set_guest_caps = previous; |
882 | } |
883 | |
884 | out: |
885 | mutex_unlock(lock: &gdev->session_mutex); |
886 | |
887 | return ret; |
888 | } |
889 | |
890 | /** |
891 | * vbg_query_host_version - get the host feature mask and version information. |
892 | * @gdev: The Guest extension device. |
893 | * |
894 | * Return: %0 or negative errno value. |
895 | */ |
896 | static int vbg_query_host_version(struct vbg_dev *gdev) |
897 | { |
898 | struct vmmdev_host_version *req; |
899 | int rc, ret; |
900 | |
901 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_GET_HOST_VERSION, |
902 | VBG_KERNEL_REQUEST); |
903 | if (!req) |
904 | return -ENOMEM; |
905 | |
906 | rc = vbg_req_perform(gdev, req); |
907 | ret = vbg_status_code_to_errno(rc); |
908 | if (ret) { |
909 | vbg_err(fmt: "%s error: %d\n" , __func__, rc); |
910 | goto out; |
911 | } |
912 | |
913 | snprintf(buf: gdev->host_version, size: sizeof(gdev->host_version), fmt: "%u.%u.%ur%u" , |
914 | req->major, req->minor, req->build, req->revision); |
915 | gdev->host_features = req->features; |
916 | |
917 | vbg_info(fmt: "vboxguest: host-version: %s %#x\n" , gdev->host_version, |
918 | gdev->host_features); |
919 | |
920 | if (!(req->features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST)) { |
921 | vbg_err(fmt: "vboxguest: Error host too old (does not support page-lists)\n" ); |
922 | ret = -ENODEV; |
923 | } |
924 | |
925 | out: |
926 | vbg_req_free(req, len: sizeof(*req)); |
927 | return ret; |
928 | } |
929 | |
930 | /** |
931 | * vbg_core_init - Initializes the VBoxGuest device extension when the |
932 | * device driver is loaded. |
933 | * @gdev: The Guest extension device. |
934 | * @fixed_events: Events that will be enabled upon init and no client |
935 | * will ever be allowed to mask. |
936 | * |
937 | * The native code locates the VMMDev on the PCI bus and retrieve |
938 | * the MMIO and I/O port ranges, this function will take care of |
939 | * mapping the MMIO memory (if present). Upon successful return |
940 | * the native code should set up the interrupt handler. |
941 | * |
942 | * Return: %0 or negative errno value. |
943 | */ |
944 | int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events) |
945 | { |
946 | int ret = -ENOMEM; |
947 | |
948 | gdev->fixed_events = fixed_events | VMMDEV_EVENT_HGCM; |
949 | gdev->event_filter_host = U32_MAX; /* forces a report */ |
950 | gdev->guest_caps_host = U32_MAX; /* forces a report */ |
951 | |
952 | init_waitqueue_head(&gdev->event_wq); |
953 | init_waitqueue_head(&gdev->hgcm_wq); |
954 | spin_lock_init(&gdev->event_spinlock); |
955 | mutex_init(&gdev->session_mutex); |
956 | mutex_init(&gdev->cancel_req_mutex); |
957 | timer_setup(&gdev->heartbeat_timer, vbg_heartbeat_timer, 0); |
958 | INIT_WORK(&gdev->mem_balloon.work, vbg_balloon_work); |
959 | |
960 | gdev->mem_balloon.get_req = |
961 | vbg_req_alloc(len: sizeof(*gdev->mem_balloon.get_req), |
962 | req_type: VMMDEVREQ_GET_MEMBALLOON_CHANGE_REQ, |
963 | VBG_KERNEL_REQUEST); |
964 | gdev->mem_balloon.change_req = |
965 | vbg_req_alloc(len: sizeof(*gdev->mem_balloon.change_req), |
966 | req_type: VMMDEVREQ_CHANGE_MEMBALLOON, |
967 | VBG_KERNEL_REQUEST); |
968 | gdev->cancel_req = |
969 | vbg_req_alloc(len: sizeof(*(gdev->cancel_req)), |
970 | req_type: VMMDEVREQ_HGCM_CANCEL2, |
971 | VBG_KERNEL_REQUEST); |
972 | gdev->ack_events_req = |
973 | vbg_req_alloc(len: sizeof(*gdev->ack_events_req), |
974 | req_type: VMMDEVREQ_ACKNOWLEDGE_EVENTS, |
975 | VBG_KERNEL_REQUEST); |
976 | gdev->mouse_status_req = |
977 | vbg_req_alloc(len: sizeof(*gdev->mouse_status_req), |
978 | req_type: VMMDEVREQ_GET_MOUSE_STATUS, |
979 | VBG_KERNEL_REQUEST); |
980 | |
981 | if (!gdev->mem_balloon.get_req || !gdev->mem_balloon.change_req || |
982 | !gdev->cancel_req || !gdev->ack_events_req || |
983 | !gdev->mouse_status_req) |
984 | goto err_free_reqs; |
985 | |
986 | ret = vbg_query_host_version(gdev); |
987 | if (ret) |
988 | goto err_free_reqs; |
989 | |
990 | ret = vbg_report_guest_info(gdev); |
991 | if (ret) { |
992 | vbg_err(fmt: "vboxguest: vbg_report_guest_info error: %d\n" , ret); |
993 | goto err_free_reqs; |
994 | } |
995 | |
996 | ret = vbg_reset_host_event_filter(gdev, fixed_events: gdev->fixed_events); |
997 | if (ret) { |
998 | vbg_err(fmt: "vboxguest: Error setting fixed event filter: %d\n" , |
999 | ret); |
1000 | goto err_free_reqs; |
1001 | } |
1002 | |
1003 | ret = vbg_reset_host_capabilities(gdev); |
1004 | if (ret) { |
1005 | vbg_err(fmt: "vboxguest: Error clearing guest capabilities: %d\n" , |
1006 | ret); |
1007 | goto err_free_reqs; |
1008 | } |
1009 | |
1010 | ret = vbg_core_set_mouse_status(gdev, features: 0); |
1011 | if (ret) { |
1012 | vbg_err(fmt: "vboxguest: Error clearing mouse status: %d\n" , ret); |
1013 | goto err_free_reqs; |
1014 | } |
1015 | |
1016 | /* These may fail without requiring the driver init to fail. */ |
1017 | vbg_guest_mappings_init(gdev); |
1018 | vbg_heartbeat_init(gdev); |
1019 | |
1020 | /* All Done! */ |
1021 | ret = vbg_report_driver_status(gdev, active: true); |
1022 | if (ret < 0) |
1023 | vbg_err(fmt: "vboxguest: Error reporting driver status: %d\n" , ret); |
1024 | |
1025 | return 0; |
1026 | |
1027 | err_free_reqs: |
1028 | vbg_req_free(req: gdev->mouse_status_req, |
1029 | len: sizeof(*gdev->mouse_status_req)); |
1030 | vbg_req_free(req: gdev->ack_events_req, |
1031 | len: sizeof(*gdev->ack_events_req)); |
1032 | vbg_req_free(req: gdev->cancel_req, |
1033 | len: sizeof(*gdev->cancel_req)); |
1034 | vbg_req_free(req: gdev->mem_balloon.change_req, |
1035 | len: sizeof(*gdev->mem_balloon.change_req)); |
1036 | vbg_req_free(req: gdev->mem_balloon.get_req, |
1037 | len: sizeof(*gdev->mem_balloon.get_req)); |
1038 | return ret; |
1039 | } |
1040 | |
1041 | /** |
1042 | * vbg_core_exit - Call this on exit to clean-up vboxguest-core managed |
1043 | * resources. |
1044 | * @gdev: The Guest extension device. |
1045 | * |
1046 | * The native code should call this before the driver is loaded, |
1047 | * but don't call this on shutdown. |
1048 | */ |
1049 | void vbg_core_exit(struct vbg_dev *gdev) |
1050 | { |
1051 | vbg_heartbeat_exit(gdev); |
1052 | vbg_guest_mappings_exit(gdev); |
1053 | |
1054 | /* Clear the host flags (mouse status etc). */ |
1055 | vbg_reset_host_event_filter(gdev, fixed_events: 0); |
1056 | vbg_reset_host_capabilities(gdev); |
1057 | vbg_core_set_mouse_status(gdev, features: 0); |
1058 | |
1059 | vbg_req_free(req: gdev->mouse_status_req, |
1060 | len: sizeof(*gdev->mouse_status_req)); |
1061 | vbg_req_free(req: gdev->ack_events_req, |
1062 | len: sizeof(*gdev->ack_events_req)); |
1063 | vbg_req_free(req: gdev->cancel_req, |
1064 | len: sizeof(*gdev->cancel_req)); |
1065 | vbg_req_free(req: gdev->mem_balloon.change_req, |
1066 | len: sizeof(*gdev->mem_balloon.change_req)); |
1067 | vbg_req_free(req: gdev->mem_balloon.get_req, |
1068 | len: sizeof(*gdev->mem_balloon.get_req)); |
1069 | } |
1070 | |
1071 | /** |
1072 | * vbg_core_open_session - Creates a VBoxGuest user session. |
1073 | * @gdev: The Guest extension device. |
1074 | * @requestor: VMMDEV_REQUESTOR_* flags |
1075 | * |
1076 | * vboxguest_linux.c calls this when userspace opens the char-device. |
1077 | * |
1078 | * Return: A pointer to the new session or an ERR_PTR on error. |
1079 | */ |
1080 | struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, u32 requestor) |
1081 | { |
1082 | struct vbg_session *session; |
1083 | |
1084 | session = kzalloc(size: sizeof(*session), GFP_KERNEL); |
1085 | if (!session) |
1086 | return ERR_PTR(error: -ENOMEM); |
1087 | |
1088 | session->gdev = gdev; |
1089 | session->requestor = requestor; |
1090 | |
1091 | return session; |
1092 | } |
1093 | |
1094 | /** |
1095 | * vbg_core_close_session - Closes a VBoxGuest session. |
1096 | * @session: The session to close (and free). |
1097 | */ |
1098 | void vbg_core_close_session(struct vbg_session *session) |
1099 | { |
1100 | struct vbg_dev *gdev = session->gdev; |
1101 | int i, rc; |
1102 | |
1103 | vbg_acquire_session_capabilities(gdev, session, or_mask: 0, U32_MAX, flags: 0, session_termination: true); |
1104 | vbg_set_session_capabilities(gdev, session, or_mask: 0, U32_MAX, session_termination: true); |
1105 | vbg_set_session_event_filter(gdev, session, or_mask: 0, U32_MAX, session_termination: true); |
1106 | |
1107 | for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) { |
1108 | if (!session->hgcm_client_ids[i]) |
1109 | continue; |
1110 | |
1111 | /* requestor is kernel here, as we're cleaning up. */ |
1112 | vbg_hgcm_disconnect(gdev, VBG_KERNEL_REQUEST, |
1113 | client_id: session->hgcm_client_ids[i], vbox_status: &rc); |
1114 | } |
1115 | |
1116 | kfree(objp: session); |
1117 | } |
1118 | |
1119 | static int vbg_ioctl_chk(struct vbg_ioctl_hdr *hdr, size_t in_size, |
1120 | size_t out_size) |
1121 | { |
1122 | if (hdr->size_in != (sizeof(*hdr) + in_size) || |
1123 | hdr->size_out != (sizeof(*hdr) + out_size)) |
1124 | return -EINVAL; |
1125 | |
1126 | return 0; |
1127 | } |
1128 | |
1129 | static int vbg_ioctl_driver_version_info( |
1130 | struct vbg_ioctl_driver_version_info *info) |
1131 | { |
1132 | const u16 vbg_maj_version = VBG_IOC_VERSION >> 16; |
1133 | u16 min_maj_version, req_maj_version; |
1134 | |
1135 | if (vbg_ioctl_chk(hdr: &info->hdr, in_size: sizeof(info->u.in), out_size: sizeof(info->u.out))) |
1136 | return -EINVAL; |
1137 | |
1138 | req_maj_version = info->u.in.req_version >> 16; |
1139 | min_maj_version = info->u.in.min_version >> 16; |
1140 | |
1141 | if (info->u.in.min_version > info->u.in.req_version || |
1142 | min_maj_version != req_maj_version) |
1143 | return -EINVAL; |
1144 | |
1145 | if (info->u.in.min_version <= VBG_IOC_VERSION && |
1146 | min_maj_version == vbg_maj_version) { |
1147 | info->u.out.session_version = VBG_IOC_VERSION; |
1148 | } else { |
1149 | info->u.out.session_version = U32_MAX; |
1150 | info->hdr.rc = VERR_VERSION_MISMATCH; |
1151 | } |
1152 | |
1153 | info->u.out.driver_version = VBG_IOC_VERSION; |
1154 | info->u.out.driver_revision = 0; |
1155 | info->u.out.reserved1 = 0; |
1156 | info->u.out.reserved2 = 0; |
1157 | |
1158 | return 0; |
1159 | } |
1160 | |
1161 | /* Must be called with the event_lock held */ |
1162 | static u32 vbg_get_allowed_event_mask_for_session(struct vbg_dev *gdev, |
1163 | struct vbg_session *session) |
1164 | { |
1165 | u32 acquire_mode_caps = gdev->acquire_mode_guest_caps; |
1166 | u32 session_acquired_caps = session->acquired_guest_caps; |
1167 | u32 allowed_events = VMMDEV_EVENT_VALID_EVENT_MASK; |
1168 | |
1169 | if ((acquire_mode_caps & VMMDEV_GUEST_SUPPORTS_GRAPHICS) && |
1170 | !(session_acquired_caps & VMMDEV_GUEST_SUPPORTS_GRAPHICS)) |
1171 | allowed_events &= ~VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; |
1172 | |
1173 | if ((acquire_mode_caps & VMMDEV_GUEST_SUPPORTS_SEAMLESS) && |
1174 | !(session_acquired_caps & VMMDEV_GUEST_SUPPORTS_SEAMLESS)) |
1175 | allowed_events &= ~VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; |
1176 | |
1177 | return allowed_events; |
1178 | } |
1179 | |
1180 | static bool vbg_wait_event_cond(struct vbg_dev *gdev, |
1181 | struct vbg_session *session, |
1182 | u32 event_mask) |
1183 | { |
1184 | unsigned long flags; |
1185 | bool wakeup; |
1186 | u32 events; |
1187 | |
1188 | spin_lock_irqsave(&gdev->event_spinlock, flags); |
1189 | |
1190 | events = gdev->pending_events & event_mask; |
1191 | events &= vbg_get_allowed_event_mask_for_session(gdev, session); |
1192 | wakeup = events || session->cancel_waiters; |
1193 | |
1194 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags); |
1195 | |
1196 | return wakeup; |
1197 | } |
1198 | |
1199 | /* Must be called with the event_lock held */ |
1200 | static u32 vbg_consume_events_locked(struct vbg_dev *gdev, |
1201 | struct vbg_session *session, |
1202 | u32 event_mask) |
1203 | { |
1204 | u32 events = gdev->pending_events & event_mask; |
1205 | |
1206 | events &= vbg_get_allowed_event_mask_for_session(gdev, session); |
1207 | gdev->pending_events &= ~events; |
1208 | return events; |
1209 | } |
1210 | |
1211 | static int vbg_ioctl_wait_for_events(struct vbg_dev *gdev, |
1212 | struct vbg_session *session, |
1213 | struct vbg_ioctl_wait_for_events *wait) |
1214 | { |
1215 | u32 timeout_ms = wait->u.in.timeout_ms; |
1216 | u32 event_mask = wait->u.in.events; |
1217 | unsigned long flags; |
1218 | long timeout; |
1219 | int ret = 0; |
1220 | |
1221 | if (vbg_ioctl_chk(hdr: &wait->hdr, in_size: sizeof(wait->u.in), out_size: sizeof(wait->u.out))) |
1222 | return -EINVAL; |
1223 | |
1224 | if (timeout_ms == U32_MAX) |
1225 | timeout = MAX_SCHEDULE_TIMEOUT; |
1226 | else |
1227 | timeout = msecs_to_jiffies(m: timeout_ms); |
1228 | |
1229 | wait->u.out.events = 0; |
1230 | do { |
1231 | timeout = wait_event_interruptible_timeout( |
1232 | gdev->event_wq, |
1233 | vbg_wait_event_cond(gdev, session, event_mask), |
1234 | timeout); |
1235 | |
1236 | spin_lock_irqsave(&gdev->event_spinlock, flags); |
1237 | |
1238 | if (timeout < 0 || session->cancel_waiters) { |
1239 | ret = -EINTR; |
1240 | } else if (timeout == 0) { |
1241 | ret = -ETIMEDOUT; |
1242 | } else { |
1243 | wait->u.out.events = |
1244 | vbg_consume_events_locked(gdev, session, event_mask); |
1245 | } |
1246 | |
1247 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags); |
1248 | |
1249 | /* |
1250 | * Someone else may have consumed the event(s) first, in |
1251 | * which case we go back to waiting. |
1252 | */ |
1253 | } while (ret == 0 && wait->u.out.events == 0); |
1254 | |
1255 | return ret; |
1256 | } |
1257 | |
1258 | static int vbg_ioctl_interrupt_all_wait_events(struct vbg_dev *gdev, |
1259 | struct vbg_session *session, |
1260 | struct vbg_ioctl_hdr *hdr) |
1261 | { |
1262 | unsigned long flags; |
1263 | |
1264 | if (hdr->size_in != sizeof(*hdr) || hdr->size_out != sizeof(*hdr)) |
1265 | return -EINVAL; |
1266 | |
1267 | spin_lock_irqsave(&gdev->event_spinlock, flags); |
1268 | session->cancel_waiters = true; |
1269 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags); |
1270 | |
1271 | wake_up(&gdev->event_wq); |
1272 | |
1273 | return 0; |
1274 | } |
1275 | |
1276 | /** |
1277 | * vbg_req_allowed - Checks if the VMM request is allowed in the |
1278 | * context of the given session. |
1279 | * @gdev: The Guest extension device. |
1280 | * @session: The calling session. |
1281 | * @req: The request. |
1282 | * |
1283 | * Return: %0 or negative errno value. |
1284 | */ |
1285 | static int vbg_req_allowed(struct vbg_dev *gdev, struct vbg_session *session, |
1286 | const struct vmmdev_request_header *req) |
1287 | { |
1288 | const struct vmmdev_guest_status *guest_status; |
1289 | bool trusted_apps_only; |
1290 | |
1291 | switch (req->request_type) { |
1292 | /* Trusted users apps only. */ |
1293 | case VMMDEVREQ_QUERY_CREDENTIALS: |
1294 | case VMMDEVREQ_REPORT_CREDENTIALS_JUDGEMENT: |
1295 | case VMMDEVREQ_REGISTER_SHARED_MODULE: |
1296 | case VMMDEVREQ_UNREGISTER_SHARED_MODULE: |
1297 | case VMMDEVREQ_WRITE_COREDUMP: |
1298 | case VMMDEVREQ_GET_CPU_HOTPLUG_REQ: |
1299 | case VMMDEVREQ_SET_CPU_HOTPLUG_STATUS: |
1300 | case VMMDEVREQ_CHECK_SHARED_MODULES: |
1301 | case VMMDEVREQ_GET_PAGE_SHARING_STATUS: |
1302 | case VMMDEVREQ_DEBUG_IS_PAGE_SHARED: |
1303 | case VMMDEVREQ_REPORT_GUEST_STATS: |
1304 | case VMMDEVREQ_REPORT_GUEST_USER_STATE: |
1305 | case VMMDEVREQ_GET_STATISTICS_CHANGE_REQ: |
1306 | trusted_apps_only = true; |
1307 | break; |
1308 | |
1309 | /* Anyone. */ |
1310 | case VMMDEVREQ_GET_MOUSE_STATUS: |
1311 | case VMMDEVREQ_SET_MOUSE_STATUS: |
1312 | case VMMDEVREQ_SET_POINTER_SHAPE: |
1313 | case VMMDEVREQ_GET_HOST_VERSION: |
1314 | case VMMDEVREQ_IDLE: |
1315 | case VMMDEVREQ_GET_HOST_TIME: |
1316 | case VMMDEVREQ_SET_POWER_STATUS: |
1317 | case VMMDEVREQ_ACKNOWLEDGE_EVENTS: |
1318 | case VMMDEVREQ_CTL_GUEST_FILTER_MASK: |
1319 | case VMMDEVREQ_REPORT_GUEST_STATUS: |
1320 | case VMMDEVREQ_GET_DISPLAY_CHANGE_REQ: |
1321 | case VMMDEVREQ_VIDEMODE_SUPPORTED: |
1322 | case VMMDEVREQ_GET_HEIGHT_REDUCTION: |
1323 | case VMMDEVREQ_GET_DISPLAY_CHANGE_REQ2: |
1324 | case VMMDEVREQ_VIDEMODE_SUPPORTED2: |
1325 | case VMMDEVREQ_VIDEO_ACCEL_ENABLE: |
1326 | case VMMDEVREQ_VIDEO_ACCEL_FLUSH: |
1327 | case VMMDEVREQ_VIDEO_SET_VISIBLE_REGION: |
1328 | case VMMDEVREQ_VIDEO_UPDATE_MONITOR_POSITIONS: |
1329 | case VMMDEVREQ_GET_DISPLAY_CHANGE_REQEX: |
1330 | case VMMDEVREQ_GET_DISPLAY_CHANGE_REQ_MULTI: |
1331 | case VMMDEVREQ_GET_SEAMLESS_CHANGE_REQ: |
1332 | case VMMDEVREQ_GET_VRDPCHANGE_REQ: |
1333 | case VMMDEVREQ_LOG_STRING: |
1334 | case VMMDEVREQ_GET_SESSION_ID: |
1335 | trusted_apps_only = false; |
1336 | break; |
1337 | |
1338 | /* Depends on the request parameters... */ |
1339 | case VMMDEVREQ_REPORT_GUEST_CAPABILITIES: |
1340 | guest_status = (const struct vmmdev_guest_status *)req; |
1341 | switch (guest_status->facility) { |
1342 | case VBOXGUEST_FACILITY_TYPE_ALL: |
1343 | case VBOXGUEST_FACILITY_TYPE_VBOXGUEST_DRIVER: |
1344 | vbg_err(fmt: "Denying userspace vmm report guest cap. call facility %#08x\n" , |
1345 | guest_status->facility); |
1346 | return -EPERM; |
1347 | case VBOXGUEST_FACILITY_TYPE_VBOX_SERVICE: |
1348 | trusted_apps_only = true; |
1349 | break; |
1350 | case VBOXGUEST_FACILITY_TYPE_VBOX_TRAY_CLIENT: |
1351 | case VBOXGUEST_FACILITY_TYPE_SEAMLESS: |
1352 | case VBOXGUEST_FACILITY_TYPE_GRAPHICS: |
1353 | default: |
1354 | trusted_apps_only = false; |
1355 | break; |
1356 | } |
1357 | break; |
1358 | |
1359 | /* Anything else is not allowed. */ |
1360 | default: |
1361 | vbg_err(fmt: "Denying userspace vmm call type %#08x\n" , |
1362 | req->request_type); |
1363 | return -EPERM; |
1364 | } |
1365 | |
1366 | if (trusted_apps_only && |
1367 | (session->requestor & VMMDEV_REQUESTOR_USER_DEVICE)) { |
1368 | vbg_err(fmt: "Denying userspace vmm call type %#08x through vboxuser device node\n" , |
1369 | req->request_type); |
1370 | return -EPERM; |
1371 | } |
1372 | |
1373 | return 0; |
1374 | } |
1375 | |
1376 | static int vbg_ioctl_vmmrequest(struct vbg_dev *gdev, |
1377 | struct vbg_session *session, void *data) |
1378 | { |
1379 | struct vbg_ioctl_hdr *hdr = data; |
1380 | int ret; |
1381 | |
1382 | if (hdr->size_in != hdr->size_out) |
1383 | return -EINVAL; |
1384 | |
1385 | if (hdr->size_in > VMMDEV_MAX_VMMDEVREQ_SIZE) |
1386 | return -E2BIG; |
1387 | |
1388 | if (hdr->type == VBG_IOCTL_HDR_TYPE_DEFAULT) |
1389 | return -EINVAL; |
1390 | |
1391 | ret = vbg_req_allowed(gdev, session, req: data); |
1392 | if (ret < 0) |
1393 | return ret; |
1394 | |
1395 | vbg_req_perform(gdev, req: data); |
1396 | WARN_ON(hdr->rc == VINF_HGCM_ASYNC_EXECUTE); |
1397 | |
1398 | return 0; |
1399 | } |
1400 | |
1401 | static int vbg_ioctl_hgcm_connect(struct vbg_dev *gdev, |
1402 | struct vbg_session *session, |
1403 | struct vbg_ioctl_hgcm_connect *conn) |
1404 | { |
1405 | u32 client_id; |
1406 | int i, ret; |
1407 | |
1408 | if (vbg_ioctl_chk(hdr: &conn->hdr, in_size: sizeof(conn->u.in), out_size: sizeof(conn->u.out))) |
1409 | return -EINVAL; |
1410 | |
1411 | /* Find a free place in the sessions clients array and claim it */ |
1412 | mutex_lock(&gdev->session_mutex); |
1413 | for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) { |
1414 | if (!session->hgcm_client_ids[i]) { |
1415 | session->hgcm_client_ids[i] = U32_MAX; |
1416 | break; |
1417 | } |
1418 | } |
1419 | mutex_unlock(lock: &gdev->session_mutex); |
1420 | |
1421 | if (i >= ARRAY_SIZE(session->hgcm_client_ids)) |
1422 | return -EMFILE; |
1423 | |
1424 | ret = vbg_hgcm_connect(gdev, requestor: session->requestor, loc: &conn->u.in.loc, |
1425 | client_id: &client_id, vbox_status: &conn->hdr.rc); |
1426 | |
1427 | mutex_lock(&gdev->session_mutex); |
1428 | if (ret == 0 && conn->hdr.rc >= 0) { |
1429 | conn->u.out.client_id = client_id; |
1430 | session->hgcm_client_ids[i] = client_id; |
1431 | } else { |
1432 | conn->u.out.client_id = 0; |
1433 | session->hgcm_client_ids[i] = 0; |
1434 | } |
1435 | mutex_unlock(lock: &gdev->session_mutex); |
1436 | |
1437 | return ret; |
1438 | } |
1439 | |
1440 | static int vbg_ioctl_hgcm_disconnect(struct vbg_dev *gdev, |
1441 | struct vbg_session *session, |
1442 | struct vbg_ioctl_hgcm_disconnect *disconn) |
1443 | { |
1444 | u32 client_id; |
1445 | int i, ret; |
1446 | |
1447 | if (vbg_ioctl_chk(hdr: &disconn->hdr, in_size: sizeof(disconn->u.in), out_size: 0)) |
1448 | return -EINVAL; |
1449 | |
1450 | client_id = disconn->u.in.client_id; |
1451 | if (client_id == 0 || client_id == U32_MAX) |
1452 | return -EINVAL; |
1453 | |
1454 | mutex_lock(&gdev->session_mutex); |
1455 | for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) { |
1456 | if (session->hgcm_client_ids[i] == client_id) { |
1457 | session->hgcm_client_ids[i] = U32_MAX; |
1458 | break; |
1459 | } |
1460 | } |
1461 | mutex_unlock(lock: &gdev->session_mutex); |
1462 | |
1463 | if (i >= ARRAY_SIZE(session->hgcm_client_ids)) |
1464 | return -EINVAL; |
1465 | |
1466 | ret = vbg_hgcm_disconnect(gdev, requestor: session->requestor, client_id, |
1467 | vbox_status: &disconn->hdr.rc); |
1468 | |
1469 | mutex_lock(&gdev->session_mutex); |
1470 | if (ret == 0 && disconn->hdr.rc >= 0) |
1471 | session->hgcm_client_ids[i] = 0; |
1472 | else |
1473 | session->hgcm_client_ids[i] = client_id; |
1474 | mutex_unlock(lock: &gdev->session_mutex); |
1475 | |
1476 | return ret; |
1477 | } |
1478 | |
1479 | static bool vbg_param_valid(enum vmmdev_hgcm_function_parameter_type type) |
1480 | { |
1481 | switch (type) { |
1482 | case VMMDEV_HGCM_PARM_TYPE_32BIT: |
1483 | case VMMDEV_HGCM_PARM_TYPE_64BIT: |
1484 | case VMMDEV_HGCM_PARM_TYPE_LINADDR: |
1485 | case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN: |
1486 | case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT: |
1487 | return true; |
1488 | default: |
1489 | return false; |
1490 | } |
1491 | } |
1492 | |
1493 | static int vbg_ioctl_hgcm_call(struct vbg_dev *gdev, |
1494 | struct vbg_session *session, bool f32bit, |
1495 | struct vbg_ioctl_hgcm_call *call) |
1496 | { |
1497 | size_t actual_size; |
1498 | u32 client_id; |
1499 | int i, ret; |
1500 | |
1501 | if (call->hdr.size_in < sizeof(*call)) |
1502 | return -EINVAL; |
1503 | |
1504 | if (call->hdr.size_in != call->hdr.size_out) |
1505 | return -EINVAL; |
1506 | |
1507 | if (call->parm_count > VMMDEV_HGCM_MAX_PARMS) |
1508 | return -E2BIG; |
1509 | |
1510 | client_id = call->client_id; |
1511 | if (client_id == 0 || client_id == U32_MAX) |
1512 | return -EINVAL; |
1513 | |
1514 | actual_size = sizeof(*call); |
1515 | if (f32bit) |
1516 | actual_size += call->parm_count * |
1517 | sizeof(struct vmmdev_hgcm_function_parameter32); |
1518 | else |
1519 | actual_size += call->parm_count * |
1520 | sizeof(struct vmmdev_hgcm_function_parameter); |
1521 | if (call->hdr.size_in < actual_size) { |
1522 | vbg_debug("VBG_IOCTL_HGCM_CALL: hdr.size_in %d required size is %zd\n" , |
1523 | call->hdr.size_in, actual_size); |
1524 | return -EINVAL; |
1525 | } |
1526 | call->hdr.size_out = actual_size; |
1527 | |
1528 | /* Validate parameter types */ |
1529 | if (f32bit) { |
1530 | struct vmmdev_hgcm_function_parameter32 *parm = |
1531 | VBG_IOCTL_HGCM_CALL_PARMS32(call); |
1532 | |
1533 | for (i = 0; i < call->parm_count; i++) |
1534 | if (!vbg_param_valid(type: parm[i].type)) |
1535 | return -EINVAL; |
1536 | } else { |
1537 | struct vmmdev_hgcm_function_parameter *parm = |
1538 | VBG_IOCTL_HGCM_CALL_PARMS(call); |
1539 | |
1540 | for (i = 0; i < call->parm_count; i++) |
1541 | if (!vbg_param_valid(type: parm[i].type)) |
1542 | return -EINVAL; |
1543 | } |
1544 | |
1545 | /* |
1546 | * Validate the client id. |
1547 | */ |
1548 | mutex_lock(&gdev->session_mutex); |
1549 | for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) |
1550 | if (session->hgcm_client_ids[i] == client_id) |
1551 | break; |
1552 | mutex_unlock(lock: &gdev->session_mutex); |
1553 | if (i >= ARRAY_SIZE(session->hgcm_client_ids)) { |
1554 | vbg_debug("VBG_IOCTL_HGCM_CALL: INVALID handle. u32Client=%#08x\n" , |
1555 | client_id); |
1556 | return -EINVAL; |
1557 | } |
1558 | |
1559 | if (IS_ENABLED(CONFIG_COMPAT) && f32bit) |
1560 | ret = vbg_hgcm_call32(gdev, requestor: session->requestor, client_id, |
1561 | function: call->function, timeout_ms: call->timeout_ms, |
1562 | VBG_IOCTL_HGCM_CALL_PARMS32(call), |
1563 | parm_count: call->parm_count, vbox_status: &call->hdr.rc); |
1564 | else |
1565 | ret = vbg_hgcm_call(gdev, requestor: session->requestor, client_id, |
1566 | function: call->function, timeout_ms: call->timeout_ms, |
1567 | VBG_IOCTL_HGCM_CALL_PARMS(call), |
1568 | parm_count: call->parm_count, vbox_status: &call->hdr.rc); |
1569 | |
1570 | if (ret == -E2BIG) { |
1571 | /* E2BIG needs to be reported through the hdr.rc field. */ |
1572 | call->hdr.rc = VERR_OUT_OF_RANGE; |
1573 | ret = 0; |
1574 | } |
1575 | |
1576 | if (ret && ret != -EINTR && ret != -ETIMEDOUT) |
1577 | vbg_err(fmt: "VBG_IOCTL_HGCM_CALL error: %d\n" , ret); |
1578 | |
1579 | return ret; |
1580 | } |
1581 | |
1582 | static int vbg_ioctl_log(struct vbg_ioctl_log *log) |
1583 | { |
1584 | if (log->hdr.size_out != sizeof(log->hdr)) |
1585 | return -EINVAL; |
1586 | |
1587 | vbg_info(fmt: "%.*s" , (int)(log->hdr.size_in - sizeof(log->hdr)), |
1588 | log->u.in.msg); |
1589 | |
1590 | return 0; |
1591 | } |
1592 | |
1593 | static int vbg_ioctl_change_filter_mask(struct vbg_dev *gdev, |
1594 | struct vbg_session *session, |
1595 | struct vbg_ioctl_change_filter *filter) |
1596 | { |
1597 | u32 or_mask, not_mask; |
1598 | |
1599 | if (vbg_ioctl_chk(hdr: &filter->hdr, in_size: sizeof(filter->u.in), out_size: 0)) |
1600 | return -EINVAL; |
1601 | |
1602 | or_mask = filter->u.in.or_mask; |
1603 | not_mask = filter->u.in.not_mask; |
1604 | |
1605 | if ((or_mask | not_mask) & ~VMMDEV_EVENT_VALID_EVENT_MASK) |
1606 | return -EINVAL; |
1607 | |
1608 | return vbg_set_session_event_filter(gdev, session, or_mask, not_mask, |
1609 | session_termination: false); |
1610 | } |
1611 | |
1612 | static int vbg_ioctl_acquire_guest_capabilities(struct vbg_dev *gdev, |
1613 | struct vbg_session *session, |
1614 | struct vbg_ioctl_acquire_guest_caps *caps) |
1615 | { |
1616 | u32 flags, or_mask, not_mask; |
1617 | |
1618 | if (vbg_ioctl_chk(hdr: &caps->hdr, in_size: sizeof(caps->u.in), out_size: 0)) |
1619 | return -EINVAL; |
1620 | |
1621 | flags = caps->u.in.flags; |
1622 | or_mask = caps->u.in.or_mask; |
1623 | not_mask = caps->u.in.not_mask; |
1624 | |
1625 | if (flags & ~VBGL_IOC_AGC_FLAGS_VALID_MASK) |
1626 | return -EINVAL; |
1627 | |
1628 | if ((or_mask | not_mask) & ~VMMDEV_GUEST_CAPABILITIES_MASK) |
1629 | return -EINVAL; |
1630 | |
1631 | return vbg_acquire_session_capabilities(gdev, session, or_mask, |
1632 | not_mask, flags, session_termination: false); |
1633 | } |
1634 | |
1635 | static int vbg_ioctl_change_guest_capabilities(struct vbg_dev *gdev, |
1636 | struct vbg_session *session, struct vbg_ioctl_set_guest_caps *caps) |
1637 | { |
1638 | u32 or_mask, not_mask; |
1639 | int ret; |
1640 | |
1641 | if (vbg_ioctl_chk(hdr: &caps->hdr, in_size: sizeof(caps->u.in), out_size: sizeof(caps->u.out))) |
1642 | return -EINVAL; |
1643 | |
1644 | or_mask = caps->u.in.or_mask; |
1645 | not_mask = caps->u.in.not_mask; |
1646 | |
1647 | if ((or_mask | not_mask) & ~VMMDEV_GUEST_CAPABILITIES_MASK) |
1648 | return -EINVAL; |
1649 | |
1650 | ret = vbg_set_session_capabilities(gdev, session, or_mask, not_mask, |
1651 | session_termination: false); |
1652 | if (ret) |
1653 | return ret; |
1654 | |
1655 | caps->u.out.session_caps = session->set_guest_caps; |
1656 | caps->u.out.global_caps = gdev->guest_caps_host; |
1657 | |
1658 | return 0; |
1659 | } |
1660 | |
1661 | static int vbg_ioctl_check_balloon(struct vbg_dev *gdev, |
1662 | struct vbg_ioctl_check_balloon *balloon_info) |
1663 | { |
1664 | if (vbg_ioctl_chk(hdr: &balloon_info->hdr, in_size: 0, out_size: sizeof(balloon_info->u.out))) |
1665 | return -EINVAL; |
1666 | |
1667 | balloon_info->u.out.balloon_chunks = gdev->mem_balloon.chunks; |
1668 | /* |
1669 | * Under Linux we handle VMMDEV_EVENT_BALLOON_CHANGE_REQUEST |
1670 | * events entirely in the kernel, see vbg_core_isr(). |
1671 | */ |
1672 | balloon_info->u.out.handle_in_r3 = false; |
1673 | |
1674 | return 0; |
1675 | } |
1676 | |
1677 | static int vbg_ioctl_write_core_dump(struct vbg_dev *gdev, |
1678 | struct vbg_session *session, |
1679 | struct vbg_ioctl_write_coredump *dump) |
1680 | { |
1681 | struct vmmdev_write_core_dump *req; |
1682 | |
1683 | if (vbg_ioctl_chk(hdr: &dump->hdr, in_size: sizeof(dump->u.in), out_size: 0)) |
1684 | return -EINVAL; |
1685 | |
1686 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_WRITE_COREDUMP, |
1687 | requestor: session->requestor); |
1688 | if (!req) |
1689 | return -ENOMEM; |
1690 | |
1691 | req->flags = dump->u.in.flags; |
1692 | dump->hdr.rc = vbg_req_perform(gdev, req); |
1693 | |
1694 | vbg_req_free(req, len: sizeof(*req)); |
1695 | return 0; |
1696 | } |
1697 | |
1698 | /** |
1699 | * vbg_core_ioctl - Common IOCtl for user to kernel communication. |
1700 | * @session: The client session. |
1701 | * @req: The requested function. |
1702 | * @data: The i/o data buffer, minimum size sizeof(struct vbg_ioctl_hdr). |
1703 | * |
1704 | * Return: %0 or negative errno value. |
1705 | */ |
1706 | int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data) |
1707 | { |
1708 | unsigned int req_no_size = req & ~IOCSIZE_MASK; |
1709 | struct vbg_dev *gdev = session->gdev; |
1710 | struct vbg_ioctl_hdr *hdr = data; |
1711 | bool f32bit = false; |
1712 | |
1713 | hdr->rc = VINF_SUCCESS; |
1714 | if (!hdr->size_out) |
1715 | hdr->size_out = hdr->size_in; |
1716 | |
1717 | /* |
1718 | * hdr->version and hdr->size_in / hdr->size_out minimum size are |
1719 | * already checked by vbg_misc_device_ioctl(). |
1720 | */ |
1721 | |
1722 | /* For VMMDEV_REQUEST hdr->type != VBG_IOCTL_HDR_TYPE_DEFAULT */ |
1723 | if (req_no_size == VBG_IOCTL_VMMDEV_REQUEST(0) || |
1724 | req == VBG_IOCTL_VMMDEV_REQUEST_BIG || |
1725 | req == VBG_IOCTL_VMMDEV_REQUEST_BIG_ALT) |
1726 | return vbg_ioctl_vmmrequest(gdev, session, data); |
1727 | |
1728 | if (hdr->type != VBG_IOCTL_HDR_TYPE_DEFAULT) |
1729 | return -EINVAL; |
1730 | |
1731 | /* Fixed size requests. */ |
1732 | switch (req) { |
1733 | case VBG_IOCTL_DRIVER_VERSION_INFO: |
1734 | return vbg_ioctl_driver_version_info(info: data); |
1735 | case VBG_IOCTL_HGCM_CONNECT: |
1736 | return vbg_ioctl_hgcm_connect(gdev, session, conn: data); |
1737 | case VBG_IOCTL_HGCM_DISCONNECT: |
1738 | return vbg_ioctl_hgcm_disconnect(gdev, session, disconn: data); |
1739 | case VBG_IOCTL_WAIT_FOR_EVENTS: |
1740 | return vbg_ioctl_wait_for_events(gdev, session, wait: data); |
1741 | case VBG_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS: |
1742 | return vbg_ioctl_interrupt_all_wait_events(gdev, session, hdr: data); |
1743 | case VBG_IOCTL_CHANGE_FILTER_MASK: |
1744 | return vbg_ioctl_change_filter_mask(gdev, session, filter: data); |
1745 | case VBG_IOCTL_ACQUIRE_GUEST_CAPABILITIES: |
1746 | return vbg_ioctl_acquire_guest_capabilities(gdev, session, caps: data); |
1747 | case VBG_IOCTL_CHANGE_GUEST_CAPABILITIES: |
1748 | return vbg_ioctl_change_guest_capabilities(gdev, session, caps: data); |
1749 | case VBG_IOCTL_CHECK_BALLOON: |
1750 | return vbg_ioctl_check_balloon(gdev, balloon_info: data); |
1751 | case VBG_IOCTL_WRITE_CORE_DUMP: |
1752 | return vbg_ioctl_write_core_dump(gdev, session, dump: data); |
1753 | } |
1754 | |
1755 | /* Variable sized requests. */ |
1756 | switch (req_no_size) { |
1757 | #ifdef CONFIG_COMPAT |
1758 | case VBG_IOCTL_HGCM_CALL_32(0): |
1759 | f32bit = true; |
1760 | fallthrough; |
1761 | #endif |
1762 | case VBG_IOCTL_HGCM_CALL(0): |
1763 | return vbg_ioctl_hgcm_call(gdev, session, f32bit, call: data); |
1764 | case VBG_IOCTL_LOG(0): |
1765 | case VBG_IOCTL_LOG_ALT(0): |
1766 | return vbg_ioctl_log(log: data); |
1767 | } |
1768 | |
1769 | vbg_err_ratelimited(fmt: "Userspace made an unknown ioctl req %#08x\n" , req); |
1770 | return -ENOTTY; |
1771 | } |
1772 | |
1773 | /** |
1774 | * vbg_core_set_mouse_status - Report guest supported mouse-features to the host. |
1775 | * |
1776 | * @gdev: The Guest extension device. |
1777 | * @features: The set of features to report to the host. |
1778 | * |
1779 | * Return: %0 or negative errno value. |
1780 | */ |
1781 | int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features) |
1782 | { |
1783 | struct vmmdev_mouse_status *req; |
1784 | int rc; |
1785 | |
1786 | req = vbg_req_alloc(len: sizeof(*req), req_type: VMMDEVREQ_SET_MOUSE_STATUS, |
1787 | VBG_KERNEL_REQUEST); |
1788 | if (!req) |
1789 | return -ENOMEM; |
1790 | |
1791 | req->mouse_features = features; |
1792 | req->pointer_pos_x = 0; |
1793 | req->pointer_pos_y = 0; |
1794 | |
1795 | rc = vbg_req_perform(gdev, req); |
1796 | if (rc < 0) |
1797 | vbg_err(fmt: "%s error, rc: %d\n" , __func__, rc); |
1798 | |
1799 | vbg_req_free(req, len: sizeof(*req)); |
1800 | return vbg_status_code_to_errno(rc); |
1801 | } |
1802 | |
1803 | /* Core interrupt service routine. */ |
1804 | irqreturn_t vbg_core_isr(int irq, void *dev_id) |
1805 | { |
1806 | struct vbg_dev *gdev = dev_id; |
1807 | struct vmmdev_events *req = gdev->ack_events_req; |
1808 | bool mouse_position_changed = false; |
1809 | unsigned long flags; |
1810 | u32 events = 0; |
1811 | int rc; |
1812 | |
1813 | if (!gdev->mmio->V.V1_04.have_events) |
1814 | return IRQ_NONE; |
1815 | |
1816 | /* Get and acknowlegde events. */ |
1817 | req->header.rc = VERR_INTERNAL_ERROR; |
1818 | req->events = 0; |
1819 | rc = vbg_req_perform(gdev, req); |
1820 | if (rc < 0) { |
1821 | vbg_err(fmt: "Error performing events req, rc: %d\n" , rc); |
1822 | return IRQ_NONE; |
1823 | } |
1824 | |
1825 | events = req->events; |
1826 | |
1827 | if (events & VMMDEV_EVENT_MOUSE_POSITION_CHANGED) { |
1828 | mouse_position_changed = true; |
1829 | events &= ~VMMDEV_EVENT_MOUSE_POSITION_CHANGED; |
1830 | } |
1831 | |
1832 | if (events & VMMDEV_EVENT_HGCM) { |
1833 | wake_up(&gdev->hgcm_wq); |
1834 | events &= ~VMMDEV_EVENT_HGCM; |
1835 | } |
1836 | |
1837 | if (events & VMMDEV_EVENT_BALLOON_CHANGE_REQUEST) { |
1838 | schedule_work(work: &gdev->mem_balloon.work); |
1839 | events &= ~VMMDEV_EVENT_BALLOON_CHANGE_REQUEST; |
1840 | } |
1841 | |
1842 | if (events) { |
1843 | spin_lock_irqsave(&gdev->event_spinlock, flags); |
1844 | gdev->pending_events |= events; |
1845 | spin_unlock_irqrestore(lock: &gdev->event_spinlock, flags); |
1846 | |
1847 | wake_up(&gdev->event_wq); |
1848 | } |
1849 | |
1850 | if (mouse_position_changed) |
1851 | vbg_linux_mouse_event(gdev); |
1852 | |
1853 | return IRQ_HANDLED; |
1854 | } |
1855 | |