VirtualBox

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

Last change on this file since 9663 was 8967, checked in by vboxsync, 17 years ago

Linux vboxadd kernel module: Fixed guest kernel crash during shutdown. Sometimes we received interrupts during module unloading.

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