VirtualBox

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

Last change on this file since 11519 was 11174, checked in by vboxsync, 16 years ago

Linux guest additions: vboxadd: properly unregister the misc device

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