VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/module/vboxmod.c@ 13053

Last change on this file since 13053 was 12280, checked in by vboxsync, 17 years ago

Additions/linux: added support for polling /dev/vboxadd for mouse movement in absolute mode

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.1 KB
Line 
1/** @file
2 *
3 * vboxadd -- VirtualBox Guest Additions for Linux
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "the-linux-kernel.h"
23#include "version-generated.h"
24
25/* #define IRQ_DEBUG */
26/* #define IOCTL_DEBUG */
27#ifdef IOCTL_DEBUG
28# define IOCTL_ENTRY(name, arg) \
29do { \
30 Log(("IOCTL_ENTRY: %s, 0x%x\n", (name), (arg))); \
31} while(0)
32# define IOCTL_EXIT(name, arg) \
33do { \
34 Log(("IOCTL_EXIT: %s, 0x%x\n", (name), (arg))); \
35} while(0)
36#else
37# define IOCTL_ENTRY(name, arg) do { } while(0)
38# define IOCTL_EXIT(name, arg) do { } while(0)
39#endif
40#ifdef IOCTL_LOG_DEBUG
41# define IOCTL_LOG_ENTRY(arg) \
42do { \
43 Log(("IOCTL_ENTRY: Log, 0x%x\n", (arg))); \
44} while(0)
45# define IOCTL_LOG_EXIT(arg) \
46do { \
47 Log(("IOCTL_EXIT: Log, 0x%x\n", (arg))); \
48} while(0)
49#else
50# define IOCTL_LOG_ENTRY(arg) do { } while(0)
51# define IOCTL_LOG_EXIT(arg) do { } while(0)
52#endif
53#ifdef IOCTL_VMM_DEBUG
54# define IOCTL_VMM_ENTRY(arg) \
55do { \
56 Log(("IOCTL_ENTRY: VMMDevReq, 0x%x\n", (arg))); \
57} while(0)
58# define IOCTL_VMM_EXIT(arg) \
59do { \
60 Log(("IOCTL_EXIT: VMMDevReq, 0x%x\n", (arg))); \
61} while(0)
62#else
63# define IOCTL_VMM_ENTRY(arg) do { } while(0)
64# define IOCTL_VMM_EXIT(arg) do { } while(0)
65#endif
66
67#include "vboxmod.h"
68#include "waitcompat.h"
69
70#include <VBox/log.h>
71#include <VBox/VBoxDev.h>
72#include <iprt/asm.h>
73#include <iprt/assert.h>
74#include <linux/miscdevice.h>
75#include <linux/poll.h>
76
77#define xstr(s) str(s)
78#define str(s) #s
79
80MODULE_DESCRIPTION("VirtualBox Guest Additions for Linux Module");
81MODULE_AUTHOR("Sun Microsystems, Inc.");
82MODULE_LICENSE("GPL");
83#ifdef MODULE_VERSION
84MODULE_VERSION(VBOX_VERSION_STRING " (interface " xstr(VMMDEV_VERSION) ")");
85#endif
86
87/* This is called by our assert macros to find out whether we want
88 to insert a breakpoint after the assertion. In kernel modules we
89 do not of course. */
90RTDECL(bool) RTAssertDoBreakpoint(void)
91{
92 return false;
93}
94EXPORT_SYMBOL(RTAssertDoBreakpoint);
95
96/** device extension structure (we only support one device instance) */
97static VBoxDevice *vboxDev = NULL;
98/** our file node major id (set dynamically) */
99#ifdef CONFIG_VBOXADD_MAJOR
100static unsigned int vbox_major = CONFIG_VBOXADD_MAJOR;
101#else
102static unsigned int vbox_major = 0;
103#endif
104
105DECLVBGL (void *) vboxadd_cmc_open (void)
106{
107 return vboxDev;
108}
109
110DECLVBGL (void) vboxadd_cmc_close (void *opaque)
111{
112 (void) opaque;
113}
114
115EXPORT_SYMBOL (vboxadd_cmc_open);
116EXPORT_SYMBOL (vboxadd_cmc_close);
117
118#define MAX_HGCM_CONNECTIONS 1024
119
120/**
121 * Structure for keeping track of HGCM connections owned by user space processes, so that
122 * we can close the connection if a process does not clean up properly (for example if it
123 * was terminated too abruptly).
124 */
125/* We just define a fixed number of these so far. This can be changed if it ever becomes
126 a problem. */
127static struct {
128 /** Open file structure that this connection handle is associated with */
129 struct file *filp;
130 /** HGCM connection ID */
131 uint32_t client_id;
132} hgcm_connections[MAX_HGCM_CONNECTIONS] = { { 0 } };
133
134/**
135 * Register an HGCM connection as being connected with a given file descriptor, so that it
136 * will be closed automatically when that file descriptor is.
137 *
138 * @returns 0 on success or Linux kernel error number
139 * @param clientID the client ID of the HGCM connection
140 * @param filep the file structure that the connection is to be associated with
141 */
142static int vboxadd_register_hgcm_connection(uint32_t client_id, struct file *filp)
143{
144 int i;
145 bool found = false;
146
147 for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) {
148 Assert(hgcm_connections[i].client_id != client_id);
149 }
150 for (i = 0; (i < MAX_HGCM_CONNECTIONS) && (false == found); ++i) {
151 if (ASMAtomicCmpXchgU32(&hgcm_connections[i].client_id, client_id, 0)) {
152 hgcm_connections[i].filp = filp;
153 found = true;
154 }
155 }
156 return found ? 0 : -ENFILE; /* Any ideas for a better error code? */
157}
158
159/**
160 * Unregister an HGCM connection associated with a given file descriptor without closing
161 * the connection.
162 *
163 * @returns 0 on success or Linux kernel error number
164 * @param clientID the client ID of the HGCM connection
165 */
166static int vboxadd_unregister_hgcm_connection_no_close(uint32_t client_id)
167{
168 int i;
169 bool found = false;
170
171 for (i = 0; (i < MAX_HGCM_CONNECTIONS) && (false == found); ++i) {
172 if (hgcm_connections[i].client_id == client_id) {
173 hgcm_connections[i].filp = NULL;
174 hgcm_connections[i].client_id = 0;
175 found = true;
176 }
177 }
178 for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) {
179 Assert(hgcm_connections[i].client_id != client_id);
180 }
181 return found ? 0 : -ENOENT;
182}
183
184/**
185 * Unregister all HGCM connections associated with a given file descriptor, closing
186 * the connections in the process. This should be called when a file descriptor is
187 * closed.
188 *
189 * @returns 0 on success or Linux kernel error number
190 * @param clientID the client ID of the HGCM connection
191 */
192static int vboxadd_unregister_all_hgcm_connections(struct file *filp)
193{
194 int i;
195
196 for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) {
197 if (hgcm_connections[i].filp == filp) {
198 VBoxGuestHGCMDisconnectInfo infoDisconnect;
199 infoDisconnect.u32ClientID = hgcm_connections[i].client_id;
200 vboxadd_cmc_call(vboxDev, VBOXGUEST_IOCTL_HGCM_DISCONNECT,
201 &infoDisconnect);
202 hgcm_connections[i].filp = NULL;
203 hgcm_connections[i].client_id = 0;
204 }
205 }
206 return 0;
207}
208
209/**
210 * File open handler
211 *
212 */
213static int vboxadd_open(struct inode *inode, struct file *filp)
214{
215 /* no checks required */
216 return 0;
217}
218
219static void
220vboxadd_wait_for_event (VBoxGuestWaitEventInfo *info)
221{
222 long timeleft;
223 uint32_t cInterruptions = vboxDev->u32GuestInterruptions;
224 uint32_t in_mask = info->u32EventMaskIn;
225
226 info->u32Result = VBOXGUEST_WAITEVENT_OK;
227 if (RT_INDEFINITE_WAIT != info->u32TimeoutIn) {
228 timeleft = wait_event_interruptible_timeout
229 (vboxDev->eventq,
230 (vboxDev->u32Events & in_mask)
231 || (vboxDev->u32GuestInterruptions != cInterruptions),
232 msecs_to_jiffies (info->u32TimeoutIn)
233 );
234 if (vboxDev->u32GuestInterruptions != cInterruptions) {
235 info->u32Result = VBOXGUEST_WAITEVENT_INTERRUPTED;
236 }
237 if (timeleft < 0) {
238 info->u32Result = VBOXGUEST_WAITEVENT_INTERRUPTED;
239 }
240 if (timeleft == 0) {
241 info->u32Result = VBOXGUEST_WAITEVENT_TIMEOUT;
242 }
243 }
244 else {
245 if (wait_event_interruptible(vboxDev->eventq,
246 (vboxDev->u32Events & in_mask)
247 || (vboxDev->u32GuestInterruptions != cInterruptions)
248 )
249 ) {
250 info->u32Result = VBOXGUEST_WAITEVENT_INTERRUPTED;
251 }
252 }
253 info->u32EventFlagsOut = vboxDev->u32Events & in_mask;
254 vboxDev->u32Events &= ~in_mask;
255}
256
257/**
258 * IOCtl handler - wait for an event from the host.
259 *
260 * @returns Linux kernel return code
261 * @param ptr User space pointer to a structure describing the event
262 */
263static int vboxadd_wait_event(void *ptr)
264{
265 int rc = 0;
266 VBoxGuestWaitEventInfo info;
267
268 if (copy_from_user (&info, ptr, sizeof (info))) {
269 LogRelFunc (("VBOXGUEST_IOCTL_WAITEVENT: can not get event info\n"));
270 rc = -EFAULT;
271 }
272
273 if (0 == rc) {
274 vboxadd_wait_for_event (&info);
275
276 if (copy_to_user (ptr, &info, sizeof (info))) {
277 LogRelFunc (("VBOXGUEST_IOCTL_WAITEVENT: can not put out_mask\n"));
278 rc = -EFAULT;
279 }
280 }
281 return 0;
282}
283
284/**
285 * IOCTL handler. Initiate an HGCM connection for a user space application. If the connection
286 * succeeds, it will be associated with the file structure used to open it, so that it will be
287 * automatically shut down again if the file descriptor is closed.
288 *
289 * @returns 0 on success, or a Linux kernel errno value
290 * @param filp the file structure with which the application opened the driver
291 * @param userspace_info userspace pointer to the hgcm connection information
292 * (VBoxGuestHGCMConnectInfo structure)
293 * @retval userspace_info userspace pointer to the hgcm connection information
294 */
295static int vboxadd_hgcm_connect(struct file *filp, unsigned long userspace_info)
296{
297 VBoxGuestHGCMConnectInfo info;
298 VBoxGuestHGCMDisconnectInfo infoDisconnect;
299 int rc = 0, rcVBox;
300
301 if (0 != copy_from_user ((void *)&info, (void *)userspace_info, sizeof (info))) {
302 LogRelFunc (("VBOXGUEST_IOCTL_HGCM_CONNECT: can not get connection info\n"));
303 return -EFAULT;
304 }
305 rcVBox = vboxadd_cmc_call(vboxDev, VBOXGUEST_IOCTL_HGCM_CONNECT, &info);
306 if (RT_FAILURE(rcVBox) || (RT_FAILURE(info.result))) {
307 LogRelFunc(("VBOXGUEST_IOCTL_HGCM_CONNECT: hgcm connection failed. internal ioctl result %Vrc, hgcm result %Vrc\n", rcVBox, info.result));
308 rc = RT_FAILURE(rcVBox) ? -RTErrConvertToErrno(rcVBox)
309 : -RTErrConvertToErrno(info.result);
310 } else {
311 /* Register that the connection is associated with this file pointer. */
312 LogRelFunc(("Connected, client ID %u\n", info.u32ClientID));
313 rc = vboxadd_register_hgcm_connection(info.u32ClientID, filp);
314 if (0 != rc) {
315 LogRelFunc(("VBOXGUEST_IOCTL_HGCM_CONNECT: failed to register the HGCM connection\n"));
316 } else {
317 if (copy_to_user ((void *)userspace_info, (void *)&info,
318 sizeof(info))) {
319 LogRelFunc (("VBOXGUEST_IOCTL_HGCM_CONNECT: failed to return the connection structure\n"));
320 rc = -EFAULT;
321 } else {
322 return 0;
323 }
324 /* Unregister again, as we didn't get as far as informing userspace. */
325 vboxadd_unregister_hgcm_connection_no_close(info.u32ClientID);
326 }
327 /* And disconnect the hgcm connection again, as we told userspace it failed. */
328 infoDisconnect.u32ClientID = info.u32ClientID;
329 vboxadd_cmc_call(vboxDev, VBOXGUEST_IOCTL_HGCM_DISCONNECT,
330 &infoDisconnect);
331 }
332 return rc;
333}
334
335/**
336 * IOCTL handler. Disconnect a specific HGCM connection.
337 *
338 * @returns 0 on success, or a Linux kernel errno value
339 * @param filp the file structure with which the application opened the driver
340 * @param userspace_info userspace pointer to the hgcm connection information
341 * (VBoxGuestHGCMConnectInfo structure)
342 * @retval userspace_info userspace pointer to the hgcm connection information
343 */
344static int vboxadd_hgcm_disconnect(struct file *filp, unsigned long userspace_info)
345{
346 VBoxGuestHGCMDisconnectInfo info;
347 if (0 != copy_from_user ((void *)&info, (void *)userspace_info, sizeof (info))) {
348 LogRelFunc (("VBOXGUEST_IOCTL_HGCM_DISCONNECT: can not get info\n"));
349 return -EFAULT;
350 }
351 LogRelFunc(("client ID %u\n", info.u32ClientID));
352 vboxadd_cmc_call(vboxDev, VBOXGUEST_IOCTL_HGCM_DISCONNECT, &info);
353 if (copy_to_user ((void *)userspace_info, (void *)&info, sizeof(info))) {
354 LogRelFunc (("VBOXGUEST_IOCTL_HGCM_DISCONNECT: failed to return the connection structure\n"));
355 return -EFAULT;
356 }
357 return 0;
358}
359
360/**
361 * IOCtl handler. Control the interrupt filter mask to specify which VMMDev interrupts
362 * we know how to handle.
363 *
364 * @returns iprt status code
365 * @param pInfo kernel space pointer to the filter mask change info
366 */
367static int vboxadd_control_filter_mask(VBoxGuestFilterMaskInfo *pInfo)
368{
369 VMMDevCtlGuestFilterMask *pReq = NULL;
370 int rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_CtlGuestFilterMask);
371
372 LogFlow(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: request received, u32OrMask=0x%x, u32NotMask=0x%x\n", pInfo->u32OrMask, pInfo->u32NotMask));
373 if (RT_FAILURE(rc))
374 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: failed to allocate %u (%#x) bytes to cache the request. rc=%d!!\n", sizeof(*pReq), sizeof(*pReq), rc));
375 else
376 {
377 pReq->u32OrMask = pInfo->u32OrMask;
378 pReq->u32NotMask = pInfo->u32NotMask;
379 rc = VbglGRPerform(&pReq->header);
380 }
381 if (RT_FAILURE(rc))
382 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: VbglGRPerform failed, rc=%Rrc!\n", rc));
383 else if (RT_FAILURE(pReq->header.rc))
384 {
385 Log(("VBoxGuestCommonIOCtl: CTL_FILTER_MASK: The request failed; VMMDev rc=%Rrc!\n", pReq->header.rc));
386 rc = pReq->header.rc;
387 }
388 if (pReq)
389 VbglGRFree(&pReq->header);
390 return rc;
391}
392
393/**
394 * IOCTL handler
395 *
396 */
397static int vboxadd_ioctl(struct inode *inode, struct file *filp,
398 unsigned int cmd, unsigned long arg)
399{
400 int rc = 0;
401
402 /* Deal with variable size ioctls first. */
403 if ( VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_LOG(0))
404 == VBOXGUEST_IOCTL_STRIP_SIZE(cmd)) {
405 char *pszMessage;
406
407 IOCTL_LOG_ENTRY(arg);
408 pszMessage = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
409 if (NULL == pszMessage) {
410 LogRelFunc(("VBOXGUEST_IOCTL_LOG: cannot allocate %d bytes of memory!\n",
411 _IOC_SIZE(cmd)));
412 rc = -ENOMEM;
413 }
414 if ( (0 == rc)
415 && copy_from_user(pszMessage, (void*)arg, _IOC_SIZE(cmd))) {
416 LogRelFunc(("VBOXGUEST_IOCTL_LOG: copy_from_user failed!\n"));
417 rc = -EFAULT;
418 }
419 if (0 == rc) {
420 Log(("%.*s", _IOC_SIZE(cmd), pszMessage));
421 }
422 if (NULL != pszMessage) {
423 kfree(pszMessage);
424 }
425 IOCTL_LOG_EXIT(arg);
426 }
427 else if ( VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_VMMREQUEST(0))
428 == VBOXGUEST_IOCTL_STRIP_SIZE(cmd)) {
429 VMMDevRequestHeader reqHeader;
430 VMMDevRequestHeader *reqFull = NULL;
431 size_t cbRequestSize;
432 size_t cbVanillaRequestSize;
433
434 IOCTL_VMM_ENTRY(arg);
435 if (copy_from_user(&reqHeader, (void*)arg, sizeof(reqHeader)))
436 {
437 LogRelFunc(("VBOXGUEST_IOCTL_VMMREQUEST: copy_from_user failed for vmm request!\n"));
438 rc = -EFAULT;
439 }
440 if (0 == rc)
441 {
442 /* get the request size */
443 cbVanillaRequestSize = vmmdevGetRequestSize(reqHeader.requestType);
444 if (!cbVanillaRequestSize)
445 {
446 LogRelFunc(("VBOXGUEST_IOCTL_VMMREQUEST: invalid request type: %d\n",
447 reqHeader.requestType));
448 rc = -EINVAL;
449 }
450 }
451 if (0 == rc)
452 {
453 cbRequestSize = reqHeader.size;
454 if (cbRequestSize < cbVanillaRequestSize)
455 {
456 LogRelFunc(("VBOXGUEST_IOCTL_VMMREQUEST: invalid request size: %d min: %d type: %d\n",
457 cbRequestSize,
458 cbVanillaRequestSize,
459 reqHeader.requestType));
460 rc = -EINVAL;
461 }
462 }
463 if (0 == rc)
464 {
465 /* request storage for the full request */
466 rc = VbglGRAlloc(&reqFull, cbRequestSize, reqHeader.requestType);
467 if (VBOX_FAILURE(rc))
468 {
469 LogRelFunc(("VBOXGUEST_IOCTL_VMMREQUEST: could not allocate request structure! rc = %d\n", rc));
470 rc = -EFAULT;
471 }
472 }
473 if (0 == rc)
474 {
475 /* now get the full request */
476 if (copy_from_user(reqFull, (void*)arg, cbRequestSize))
477 {
478 LogRelFunc(("VBOXGUEST_IOCTL_VMMREQUEST: failed to fetch full request from user space!\n"));
479 rc = -EFAULT;
480 }
481 }
482
483 /* now issue the request */
484 if (0 == rc)
485 {
486 int rrc = VbglGRPerform(reqFull);
487
488 /* asynchronous processing? */
489 if (rrc == VINF_HGCM_ASYNC_EXECUTE)
490 {
491 VMMDevHGCMRequestHeader *reqHGCM = (VMMDevHGCMRequestHeader*)reqFull;
492 wait_event_interruptible (vboxDev->eventq, reqHGCM->fu32Flags & VBOX_HGCM_REQ_DONE);
493 rrc = reqFull->rc;
494 }
495
496 /* failed? */
497 if (VBOX_FAILURE(rrc) || VBOX_FAILURE(reqFull->rc))
498 {
499 LogRelFunc(("VBOXGUEST_IOCTL_VMMREQUEST: request execution failed!\n"));
500 rc = VBOX_FAILURE(rrc) ? -RTErrConvertToErrno(rrc)
501 : -RTErrConvertToErrno(reqFull->rc);
502 }
503 else
504 {
505 /* success, copy the result data to user space */
506 if (copy_to_user((void*)arg, (void*)reqFull, cbRequestSize))
507 {
508 LogRelFunc(("VBOXGUEST_IOCTL_VMMREQUEST: error copying request result to user space!\n"));
509 rc = -EFAULT;
510 }
511 }
512 }
513 if (NULL != reqFull)
514 VbglGRFree(reqFull);
515 IOCTL_VMM_EXIT(arg);
516 }
517 else if ( VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_HGCM_CALL(0))
518 == VBOXGUEST_IOCTL_STRIP_SIZE(cmd))
519 {
520 /* This IOCTL allows the guest to make an HGCM call from user space. The
521 OS-independant part of the Guest Additions already contain code for making an
522 HGCM call from the guest, but this code assumes that the call is made from the
523 kernel's address space. So before calling it, we have to copy all parameters
524 to the HGCM call from user space to kernel space and reconstruct the structures
525 passed to the call (which include pointers to other memory) inside the kernel's
526 address space. */
527 IOCTL_ENTRY("VBOXGUEST_IOCTL_HGCM_CALL", arg);
528 rc = vbox_ioctl_hgcm_call(arg, vboxDev);
529 IOCTL_EXIT("VBOXGUEST_IOCTL_HGCM_CALL", arg);
530 }
531 else
532 {
533 switch (cmd) {
534 case VBOXGUEST_IOCTL_WAITEVENT:
535 IOCTL_ENTRY("VBOXGUEST_IOCTL_WAITEVENT", arg);
536 rc = vboxadd_wait_event((void *) arg);
537 IOCTL_EXIT("VBOXGUEST_IOCTL_WAITEVENT", arg);
538 break;
539 case VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS:
540 IOCTL_ENTRY("VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS", arg);
541 ++vboxDev->u32GuestInterruptions;
542 IOCTL_EXIT("VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS", arg);
543 break;
544 case VBOXGUEST_IOCTL_HGCM_CONNECT:
545 IOCTL_ENTRY("VBOXGUEST_IOCTL_HGCM_CONNECT", arg);
546 rc = vboxadd_hgcm_connect(filp, arg);
547 IOCTL_EXIT("VBOXGUEST_IOCTL_HGCM_CONNECT", arg);
548 break;
549 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
550 IOCTL_ENTRY("VBOXGUEST_IOCTL_HGCM_DISCONNECT", arg);
551 vboxadd_hgcm_disconnect(filp, arg);
552 IOCTL_EXIT("VBOXGUEST_IOCTL_HGCM_DISCONNECT", arg);
553 break;
554 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
555 {
556 VBoxGuestFilterMaskInfo info;
557 IOCTL_ENTRY("VBOXGUEST_IOCTL_CTL_FILTER_MASK", arg);
558 if (copy_from_user((void*)&info, (void*)arg, sizeof(info)))
559 {
560 LogRelFunc(("VBOXGUEST_IOCTL_CTL_FILTER_MASK: error getting parameters from user space!\n"));
561 rc = -EFAULT;
562 break;
563 }
564 rc = -RTErrConvertToErrno(vboxadd_control_filter_mask(&info));
565 IOCTL_EXIT("VBOXGUEST_IOCTL_CTL_FILTER_MASK", arg);
566 break;
567 }
568 default:
569 LogRelFunc(("unknown command: %x\n", cmd));
570 rc = -EINVAL;
571 break;
572 }
573 }
574 return rc;
575}
576
577/**
578 * Poll function. This returns "ready to read" if the guest is in absolute
579 * mouse pointer mode and the pointer position has changed since the last
580 * poll.
581 */
582unsigned int
583vboxadd_poll (struct file *file, poll_table *wait)
584{
585 int result = 0;
586 poll_wait(file, &vboxDev->eventq, wait);
587 if (vboxDev->u32Events & VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
588 result = (POLLIN | POLLRDNORM);
589 vboxDev->u32Events &= ~VMMDEV_EVENT_MOUSE_POSITION_CHANGED;
590 return result;
591}
592
593/** Asynchronous notification activation method. */
594static int
595vboxadd_fasync(int fd, struct file *file, int mode)
596{
597 return fasync_helper(fd, file, mode, &vboxDev->async_queue);
598}
599
600/**
601 * Dummy read function - we only supply this because we implement poll and
602 * fasync.
603 */
604static ssize_t
605vboxadd_read (struct file *file, char *buf, size_t count, loff_t *loff)
606{
607 if (0 == count || *loff != 0)
608 {
609 return -EINVAL;
610 }
611 buf[0] = 0;
612 return 1;
613}
614
615/**
616 * File close handler. Clean up any HGCM connections associated with the open file
617 * which might still be open.
618 */
619static int vboxadd_release(struct inode *inode, struct file * filp)
620{
621 vboxadd_unregister_all_hgcm_connections(filp);
622 /* Deactivate our asynchronous queue. */
623 vboxadd_fasync(-1, filp, 0);
624 return 0;
625}
626
627/** strategy handlers (file operations) */
628static struct file_operations vbox_fops =
629{
630 .owner = THIS_MODULE,
631 .open = vboxadd_open,
632 .ioctl = vboxadd_ioctl,
633 .poll = vboxadd_poll,
634 .fasync = vboxadd_fasync,
635 .read = vboxadd_read,
636 .release = vboxadd_release,
637 .llseek = no_llseek
638};
639
640static struct miscdevice gMiscDevice =
641{
642 minor: MISC_DYNAMIC_MINOR,
643 name: "vboxadd",
644 fops: &vbox_fops
645};
646
647#ifndef IRQ_RETVAL
648/* interrupt handlers in 2.4 kernels don't return anything */
649# define irqreturn_t void
650# define IRQ_RETVAL(n)
651#endif
652
653/**
654 * vboxadd_irq_handler
655 *
656 * Interrupt handler
657 *
658 * @returns scsi error code
659 * @param irq Irq number
660 * @param dev_id Irq handler parameter
661 * @param regs Regs
662 *
663 */
664#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
665static irqreturn_t vboxadd_irq_handler(int irq, void *dev_id)
666#else
667static irqreturn_t vboxadd_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
668#endif
669{
670 int fIRQTaken = 0;
671 int rcVBox;
672
673#ifdef IRQ_DEBUG
674 Log(("vboxadd IRQ_DEBUG: vboxDev->pVMMDevMemory=%p vboxDev->pVMMDevMemory->fHaveEvents=%d\n",
675 vboxDev->pVMMDevMemory, vboxDev->pVMMDevMemory->V.V1_04.fHaveEvents));
676#endif
677
678 /* check if IRQ was asserted by VBox */
679 if (vboxDev->pVMMDevMemory->V.V1_04.fHaveEvents != 0)
680 {
681#ifdef IRQ_DEBUG
682 Log(("vboxadd IRQ_DEBUG: got IRQ with event mask 0x%x\n",
683 vboxDev->irqAckRequest->events));
684#endif
685
686 /* make a copy of the event mask */
687 rcVBox = VbglGRPerform (&vboxDev->irqAckRequest->header);
688 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(vboxDev->irqAckRequest->header.rc))
689 {
690 if (RT_LIKELY (vboxDev->irqAckRequest->events))
691 {
692 vboxDev->u32Events |= vboxDev->irqAckRequest->events;
693 if ( vboxDev->irqAckRequest->events
694 & VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
695 kill_fasync(&vboxDev->async_queue, SIGIO, POLL_IN);
696 wake_up (&vboxDev->eventq);
697 }
698 }
699 else
700 {
701 /* impossible... */
702 LogRelFunc(("IRQ was not acknowledged! rc = %Vrc, header.rc = %Vrc\n",
703 rcVBox, vboxDev->irqAckRequest->header.rc));
704 BUG ();
705 }
706
707 /* it was ours! */
708 fIRQTaken = 1;
709 }
710#ifdef IRQ_DEBUG
711 else
712 {
713 /* we might be attached to a shared interrupt together with another device. */
714 Log(("vboxadd IRQ_DEBUG: stale IRQ mem=%p events=%d devevents=%#x\n",
715 vboxDev->pVMMDevMemory,
716 vboxDev->pVMMDevMemory->V.V1_04.fHaveEvents,
717 vboxDev->u32Events));
718 }
719#endif
720 /* it was ours */
721 return IRQ_RETVAL(fIRQTaken);
722}
723
724/**
725 * Helper function to reserve a fixed kernel address space window
726 * and tell the VMM that it can safely put its hypervisor there.
727 * This function might fail which is not a critical error.
728 */
729static int vboxadd_reserve_hypervisor(void)
730{
731 VMMDevReqHypervisorInfo *req = NULL;
732 int rcVBox;
733
734 /* allocate request structure */
735 rcVBox = VbglGRAlloc(
736 (VMMDevRequestHeader**)&req,
737 sizeof(VMMDevReqHypervisorInfo),
738 VMMDevReq_GetHypervisorInfo
739 );
740 if (VBOX_FAILURE(rcVBox))
741 {
742 LogRelFunc(("failed to allocate hypervisor info structure! rc = %Vrc\n", rcVBox));
743 goto bail_out;
744 }
745 /* query the hypervisor information */
746 rcVBox = VbglGRPerform(&req->header);
747 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(req->header.rc))
748 {
749 /* are we supposed to make a reservation? */
750 if (req->hypervisorSize)
751 {
752 /** @todo repeat this several times until we get an address the host likes */
753
754 void *hypervisorArea;
755 /* reserve another 4MB because the start needs to be 4MB aligned */
756 uint32_t hypervisorSize = req->hypervisorSize + 0x400000;
757 /* perform a fictive IO space mapping */
758 hypervisorArea = ioremap(HYPERVISOR_PHYSICAL_START, hypervisorSize);
759 if (hypervisorArea)
760 {
761 /* communicate result to VMM, align at 4MB */
762 req->hypervisorStart = (VMMDEVHYPPTR32)RT_ALIGN_P(hypervisorArea, 0x400000);
763 req->header.requestType = VMMDevReq_SetHypervisorInfo;
764 req->header.rc = VERR_GENERAL_FAILURE;
765 rcVBox = VbglGRPerform(&req->header);
766 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(req->header.rc))
767 {
768 /* store mapping for future unmapping */
769 vboxDev->hypervisorStart = hypervisorArea;
770 vboxDev->hypervisorSize = hypervisorSize;
771 }
772 else
773 {
774 LogRelFunc(("failed to set hypervisor region! rc = %Vrc, header.rc = %Vrc\n",
775 rcVBox, req->header.rc));
776 goto bail_out;
777 }
778 }
779 else
780 {
781 LogRelFunc(("failed to allocate 0x%x bytes of IO space\n", hypervisorSize));
782 goto bail_out;
783 }
784 }
785 }
786 else
787 {
788 LogRelFunc(("failed to query hypervisor info! rc = %Vrc, header.rc = %Vrc\n",
789 rcVBox, req->header.rc));
790 goto bail_out;
791 }
792 /* successful return */
793 VbglGRFree(&req->header);
794 return 0;
795bail_out:
796 /* error return */
797 if (req)
798 VbglGRFree(&req->header);
799 return 1;
800}
801
802/**
803 * Helper function to free the hypervisor address window
804 *
805 */
806static int vboxadd_free_hypervisor(void)
807{
808 VMMDevReqHypervisorInfo *req = NULL;
809 int rcVBox;
810
811 /* allocate request structure */
812 rcVBox = VbglGRAlloc(
813 (VMMDevRequestHeader**)&req,
814 sizeof(VMMDevReqHypervisorInfo),
815 VMMDevReq_SetHypervisorInfo
816 );
817 if (VBOX_FAILURE(rcVBox))
818 {
819 LogRelFunc(("failed to allocate hypervisor info structure! rc = %Vrc\n", rcVBox));
820 goto bail_out;
821 }
822 /* reset the hypervisor information */
823 req->hypervisorStart = 0;
824 req->hypervisorSize = 0;
825 rcVBox = VbglGRPerform(&req->header);
826 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(req->header.rc))
827 {
828 /* now we can free the associated IO space mapping */
829 iounmap(vboxDev->hypervisorStart);
830 vboxDev->hypervisorStart = 0;
831 }
832 else
833 {
834 LogRelFunc(("failed to reset hypervisor info! rc = %Vrc, header.rc = %Vrc\n",
835 rcVBox, req->header.rc));
836 goto bail_out;
837 }
838 return 0;
839
840 bail_out:
841 if (req)
842 VbglGRFree(&req->header);
843 return 1;
844}
845
846/**
847 * Helper to free resources
848 *
849 */
850static void free_resources(void)
851{
852 if (vboxDev)
853 {
854 {
855 /* Unregister notifications when the host absolute pointer
856 * position changes. */
857 VBoxGuestFilterMaskInfo info;
858 info.u32OrMask = 0;
859 info.u32NotMask = VMMDEV_EVENT_MOUSE_POSITION_CHANGED;
860 vboxadd_control_filter_mask(&info);
861 }
862 /* Detach from IRQ before cleaning up! */
863 if (vboxDev->irq)
864 free_irq(vboxDev->irq, vboxDev);
865 if (vboxDev->hypervisorStart)
866 vboxadd_free_hypervisor();
867 if (vboxDev->irqAckRequest)
868 {
869 VbglGRFree(&vboxDev->irqAckRequest->header);
870 VbglTerminate();
871 }
872 if (vboxDev->pVMMDevMemory)
873 iounmap(vboxDev->pVMMDevMemory);
874 if (vboxDev->vmmdevmem)
875 release_mem_region(vboxDev->vmmdevmem, vboxDev->vmmdevmem_size);
876 kfree(vboxDev);
877 vboxDev = NULL;
878 }
879}
880
881#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
882#define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
883#define PCI_DEV_PUT(x) pci_dev_put(x)
884#else
885#define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
886#define PCI_DEV_PUT(x)
887#endif
888
889/**
890 * Module initialization
891 *
892 */
893static __init int init(void)
894{
895 int err;
896 int rcVBox;
897 struct pci_dev *pcidev = NULL;
898 VMMDevReportGuestInfo *infoReq = NULL;
899
900 if (vboxadd_cmc_init ())
901 {
902 printk (KERN_ERR "vboxadd: could not init cmc.\n");
903 return -ENODEV;
904 }
905
906 /*
907 * Detect PCI device
908 */
909 pcidev = PCI_DEV_GET(VMMDEV_VENDORID, VMMDEV_DEVICEID, pcidev);
910 if (!pcidev)
911 {
912 printk(KERN_ERR "vboxadd: VirtualBox PCI device not found.\n");
913 return -ENODEV;
914 }
915
916 err = pci_enable_device (pcidev);
917 if (err)
918 {
919 Log(("vboxadd: could not enable device: %d\n", err));
920 PCI_DEV_PUT(pcidev);
921 return -ENODEV;
922 }
923
924 LogRel(("Starting VirtualBox version %s Guest Additions\n",
925 VBOX_VERSION_STRING));
926 /* register a character device */
927 if (vbox_major > 0)
928 {
929 err = register_chrdev(vbox_major, "vboxadd", &vbox_fops);
930 if (err < 0 || (vbox_major & err) || (!vbox_major && !err))
931 {
932 LogRelFunc(("register_chrdev failed: vbox_major: %d, err = %d\n",
933 vbox_major, err));
934 PCI_DEV_PUT(pcidev);
935 return -ENODEV;
936 }
937 /* if no major code was set, take the return value */
938 if (!vbox_major)
939 vbox_major = err;
940 }
941 else
942 {
943 err = misc_register(&gMiscDevice);
944 if (err)
945 {
946 LogRelFunc(("misc_register failed (rc=%d)\n", err));
947 return -ENODEV;
948 }
949 }
950
951 /* allocate and initialize device extension */
952 vboxDev = kmalloc(sizeof(*vboxDev), GFP_KERNEL);
953 if (!vboxDev)
954 {
955 LogRelFunc(("cannot allocate device!\n"));
956 err = -ENOMEM;
957 goto fail;
958 }
959 memset(vboxDev, 0, sizeof(*vboxDev));
960 snprintf(vboxDev->name, sizeof(vboxDev->name), "vboxadd");
961
962 /* get the IO port region */
963 vboxDev->io_port = pci_resource_start(pcidev, 0);
964
965 /* get the memory region */
966 vboxDev->vmmdevmem = pci_resource_start(pcidev, 1);
967 vboxDev->vmmdevmem_size = pci_resource_len(pcidev, 1);
968
969 /* all resources found? */
970 if (!vboxDev->io_port || !vboxDev->vmmdevmem || !vboxDev->vmmdevmem_size)
971 {
972 LogRelFunc(("did not find expected hardware resources!\n"));
973 err = -ENXIO;
974 goto fail;
975 }
976
977 /* request ownership of adapter memory */
978 if (request_mem_region(vboxDev->vmmdevmem, vboxDev->vmmdevmem_size, "vboxadd") == 0)
979 {
980 LogRelFunc(("failed to request adapter memory!\n"));
981 err = -ENXIO;
982 goto fail;
983 }
984
985 /* map adapter memory into kernel address space and check version */
986 vboxDev->pVMMDevMemory = (VMMDevMemory *) ioremap(vboxDev->vmmdevmem,
987 vboxDev->vmmdevmem_size);
988 if (!vboxDev->pVMMDevMemory)
989 {
990 LogRelFunc(("ioremap failed\n"));
991 err = -ENOMEM;
992 goto fail;
993 }
994
995 if (vboxDev->pVMMDevMemory->u32Version != VMMDEV_MEMORY_VERSION)
996 {
997 LogRelFunc(("invalid VMM device memory version! (got 0x%x, expected 0x%x)\n",
998 vboxDev->pVMMDevMemory->u32Version, VMMDEV_MEMORY_VERSION));
999 err = -ENXIO;
1000 goto fail;
1001 }
1002
1003 /* initialize VBGL subsystem */
1004 rcVBox = VbglInit(vboxDev->io_port, vboxDev->pVMMDevMemory);
1005 if (VBOX_FAILURE(rcVBox))
1006 {
1007 LogRelFunc(("could not initialize VBGL subsystem! rc = %Vrc\n", rcVBox));
1008 err = -ENXIO;
1009 goto fail;
1010 }
1011
1012 /* report guest information to host, this must be done as the very first request */
1013 rcVBox = VbglGRAlloc((VMMDevRequestHeader**)&infoReq,
1014 sizeof(VMMDevReportGuestInfo), VMMDevReq_ReportGuestInfo);
1015 if (VBOX_FAILURE(rcVBox))
1016 {
1017 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox));
1018 err = -ENOMEM;
1019 goto fail;
1020 }
1021
1022 /* report guest version to host, the VMMDev requires that to be done first */
1023 infoReq->guestInfo.additionsVersion = VMMDEV_VERSION;
1024#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
1025 infoReq->guestInfo.osType = VBOXOSTYPE_Linux26;
1026#else
1027 infoReq->guestInfo.osType = VBOXOSTYPE_Linux24;
1028#endif
1029 rcVBox = VbglGRPerform(&infoReq->header);
1030 if (VBOX_FAILURE(rcVBox) || VBOX_FAILURE(infoReq->header.rc))
1031 {
1032 LogRelFunc(("error reporting guest info to host! rc = %Vrc, header.rc = %Vrc\n",
1033 rcVBox, infoReq->header.rc));
1034 VbglGRFree(&infoReq->header);
1035 err = -ENXIO;
1036 goto fail;
1037 }
1038 VbglGRFree(&infoReq->header);
1039
1040 /* Unset the graphics capability until/unless X is loaded. */
1041 /** @todo check the error code once we bump the additions version.
1042 For now we ignore it for compatibility with older hosts. */
1043 {
1044 VMMDevReqGuestCapabilities2 *vmmreqGuestCaps;
1045
1046
1047 rcVBox = VbglGRAlloc((VMMDevRequestHeader**)&vmmreqGuestCaps,
1048 sizeof(VMMDevReqGuestCapabilities2),
1049 VMMDevReq_SetGuestCapabilities);
1050 if (VBOX_FAILURE(rcVBox))
1051 {
1052 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox));
1053 err = -ENOMEM;
1054 goto fail;
1055 }
1056 vmmreqGuestCaps->u32OrMask = 0;
1057 vmmreqGuestCaps->u32NotMask = VMMDEV_GUEST_SUPPORTS_GRAPHICS;
1058 rcVBox = VbglGRPerform(&vmmreqGuestCaps->header);
1059 VbglGRFree(&vmmreqGuestCaps->header);
1060 if (RT_FAILURE(rcVBox))
1061 {
1062 err = -ENXIO;
1063 goto fail;
1064 }
1065 }
1066
1067 /* perform hypervisor address space reservation */
1068 if (vboxadd_reserve_hypervisor())
1069 {
1070 /* we just ignore the error, no address window reservation, non fatal */
1071 }
1072
1073 /* allocate a VMM request structure for use in the ISR */
1074 rcVBox = VbglGRAlloc((VMMDevRequestHeader**)&vboxDev->irqAckRequest,
1075 sizeof(VMMDevEvents), VMMDevReq_AcknowledgeEvents);
1076 if (VBOX_FAILURE(rcVBox))
1077 {
1078 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox));
1079 err = -ENOMEM;
1080 goto fail;
1081 }
1082
1083 /* get ISR */
1084 err = request_irq(pcidev->irq, vboxadd_irq_handler,
1085#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
1086 IRQF_SHARED,
1087#else
1088 SA_SHIRQ,
1089#endif
1090 "vboxadd", vboxDev);
1091 if (err)
1092 {
1093 LogRelFunc(("could not request IRQ %d, err: %d\n", pcidev->irq, err));
1094 goto fail;
1095 }
1096 vboxDev->irq = pcidev->irq;
1097
1098 init_waitqueue_head (&vboxDev->eventq);
1099
1100 {
1101 /* Register for notification when the host absolute pointer position
1102 * changes. */
1103 VBoxGuestFilterMaskInfo info;
1104 info.u32OrMask = VMMDEV_EVENT_MOUSE_POSITION_CHANGED;
1105 info.u32NotMask = 0;
1106 rcVBox = vboxadd_control_filter_mask(&info);
1107 if (!RT_SUCCESS(rcVBox))
1108 {
1109 LogRelFunc(("failed to register for VMMDEV_EVENT_MOUSE_POSITION_CHANGED events\n"));
1110 err = -RTErrConvertToErrno(rcVBox);
1111 goto fail;
1112 }
1113 }
1114
1115 /* some useful information for the user but don't show this on the console */
1116 LogRel(("VirtualBox device settings: major %d, IRQ %d, "
1117 "I/O port 0x%x, MMIO at 0x%x (size 0x%x), "
1118 "hypervisor window at 0x%p (size 0x%x)\n",
1119 vbox_major, vboxDev->irq, vboxDev->io_port,
1120 vboxDev->vmmdevmem, vboxDev->vmmdevmem_size,
1121 vboxDev->hypervisorStart, vboxDev->hypervisorSize));
1122 printk(KERN_DEBUG "vboxadd: Successfully loaded version "
1123 VBOX_VERSION_STRING " (interface " xstr(VMMDEV_VERSION) ")\n");
1124
1125 /* successful return */
1126 PCI_DEV_PUT(pcidev);
1127 return 0;
1128
1129fail:
1130 PCI_DEV_PUT(pcidev);
1131 free_resources();
1132 if (vbox_major > 0)
1133 unregister_chrdev(vbox_major, "vboxadd");
1134 else
1135 misc_deregister(&gMiscDevice);
1136 return err;
1137}
1138
1139/**
1140 * Module termination
1141 *
1142 */
1143static __exit void fini(void)
1144{
1145 if (vbox_major > 0)
1146 unregister_chrdev(vbox_major, "vboxadd");
1147 else
1148 misc_deregister(&gMiscDevice);
1149 free_resources();
1150 vboxadd_cmc_fini ();
1151}
1152
1153module_init(init);
1154module_exit(fini);
1155
1156/* PCI hotplug structure */
1157static const struct pci_device_id __devinitdata vmmdev_pci_id[] =
1158{
1159 {
1160 .vendor = VMMDEV_VENDORID,
1161 .device = VMMDEV_DEVICEID
1162 },
1163 {
1164 /* empty entry */
1165 }
1166};
1167MODULE_DEVICE_TABLE(pci, vmmdev_pci_id);
1168
1169int __gxx_personality_v0 = 0xdeadbeef;
1170
1171/*
1172 * Local Variables:
1173 * c-mode: bsd
1174 * indent-tabs-mode: nil
1175 * c-plusplus: evil
1176 * End:
1177 */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette