VirtualBox

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

Last change on this file since 14079 was 13837, checked in by vboxsync, 16 years ago

s/%Vr\([acfs]\)/%Rr\1/g - since I'm upsetting everyone anyway, better make the most of it...

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

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