VirtualBox

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

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

IOCTL fix for Linux. Hope nothing broke ...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.3 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)
528 == VBOXGUEST_IOCTL_STRIP_SIZE(cmd))
529 || (cmd == VBOXGUEST_IOCTL_HGCM_CALL))
530 {
531 /* This IOCTL allows the guest to make an HGCM call from user space. The
532 OS-independant part of the Guest Additions already contain code for making an
533 HGCM call from the guest, but this code assumes that the call is made from the
534 kernel's address space. So before calling it, we have to copy all parameters
535 to the HGCM call from user space to kernel space and reconstruct the structures
536 passed to the call (which include pointers to other memory) inside the kernel's
537 address space. */
538 IOCTL_ENTRY("VBOXGUEST_IOCTL_HGCM_CALL", arg);
539 rc = vbox_ioctl_hgcm_call(arg, vboxDev);
540 IOCTL_EXIT("VBOXGUEST_IOCTL_HGCM_CALL", arg);
541 }
542 else
543 {
544 switch (cmd) {
545 case VBOXGUEST_IOCTL_WAITEVENT:
546 IOCTL_ENTRY("VBOXGUEST_IOCTL_WAITEVENT", arg);
547 rc = vboxadd_wait_event((void *) arg);
548 IOCTL_EXIT("VBOXGUEST_IOCTL_WAITEVENT", arg);
549 break;
550 case VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS:
551 IOCTL_ENTRY("VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS", arg);
552 ++vboxDev->u32GuestInterruptions;
553 IOCTL_EXIT("VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS", arg);
554 break;
555 case VBOXGUEST_IOCTL_HGCM_CONNECT:
556 IOCTL_ENTRY("VBOXGUEST_IOCTL_HGCM_CONNECT", arg);
557 rc = vboxadd_hgcm_connect(filp, arg);
558 IOCTL_EXIT("VBOXGUEST_IOCTL_HGCM_CONNECT", arg);
559 break;
560 case VBOXGUEST_IOCTL_HGCM_DISCONNECT:
561 IOCTL_ENTRY("VBOXGUEST_IOCTL_HGCM_DISCONNECT", arg);
562 vboxadd_hgcm_disconnect(filp, arg);
563 IOCTL_EXIT("VBOXGUEST_IOCTL_HGCM_DISCONNECT", arg);
564 break;
565 case VBOXGUEST_IOCTL_CTL_FILTER_MASK:
566 {
567 VBoxGuestFilterMaskInfo info;
568 IOCTL_ENTRY("VBOXGUEST_IOCTL_CTL_FILTER_MASK", arg);
569 if (copy_from_user((void*)&info, (void*)arg, sizeof(info)))
570 {
571 LogRelFunc(("VBOXGUEST_IOCTL_CTL_FILTER_MASK: error getting parameters from user space!\n"));
572 rc = -EFAULT;
573 break;
574 }
575 rc = -RTErrConvertToErrno(vboxadd_control_filter_mask(&info));
576 IOCTL_EXIT("VBOXGUEST_IOCTL_CTL_FILTER_MASK", arg);
577 break;
578 }
579 default:
580 LogRelFunc(("unknown command: %x\n", cmd));
581 rc = -EINVAL;
582 break;
583 }
584 }
585 return rc;
586}
587
588#ifdef DEBUG
589static ssize_t
590vboxadd_read (struct file *file, char *buf, size_t count, loff_t *loff)
591{
592 if (count != 8 || *loff != 0)
593 {
594 return -EINVAL;
595 }
596 *(uint32_t *) buf = vboxDev->pVMMDevMemory->V.V1_04.fHaveEvents;
597 *(uint32_t *) (buf + 4) = vboxDev->u32Events;
598 *loff += 8;
599 return 8;
600}
601#endif
602
603/** strategy handlers (file operations) */
604static struct file_operations vbox_fops =
605{
606 .owner = THIS_MODULE,
607 .open = vboxadd_open,
608 .release = vboxadd_release,
609 .ioctl = vboxadd_ioctl,
610#ifdef DEBUG
611 .read = vboxadd_read,
612#endif
613 .llseek = no_llseek
614};
615
616static struct miscdevice gMiscDevice =
617{
618 minor: MISC_DYNAMIC_MINOR,
619 name: "vboxadd",
620 fops: &vbox_fops
621};
622
623#ifndef IRQ_RETVAL
624/* interrupt handlers in 2.4 kernels don't return anything */
625# define irqreturn_t void
626# define IRQ_RETVAL(n)
627#endif
628
629/**
630 * vboxadd_irq_handler
631 *
632 * Interrupt handler
633 *
634 * @returns scsi error code
635 * @param irq Irq number
636 * @param dev_id Irq handler parameter
637 * @param regs Regs
638 *
639 */
640#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
641static irqreturn_t vboxadd_irq_handler(int irq, void *dev_id)
642#else
643static irqreturn_t vboxadd_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
644#endif
645{
646 int fIRQTaken = 0;
647 int rcVBox;
648
649#ifdef IRQ_DEBUG
650 Log(("vboxadd IRQ_DEBUG: vboxDev->pVMMDevMemory=%p vboxDev->pVMMDevMemory->fHaveEvents=%d\n",
651 vboxDev->pVMMDevMemory, vboxDev->pVMMDevMemory->V.V1_04.fHaveEvents));
652#endif
653
654 /* check if IRQ was asserted by VBox */
655 if (vboxDev->pVMMDevMemory->V.V1_04.fHaveEvents != 0)
656 {
657#ifdef IRQ_DEBUG
658 Log(("vboxadd IRQ_DEBUG: got IRQ with event mask 0x%x\n",
659 vboxDev->irqAckRequest->events));
660#endif
661
662 /* make a copy of the event mask */
663 rcVBox = VbglGRPerform (&vboxDev->irqAckRequest->header);
664 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(vboxDev->irqAckRequest->header.rc))
665 {
666 if (RT_LIKELY (vboxDev->irqAckRequest->events))
667 {
668 vboxDev->u32Events |= vboxDev->irqAckRequest->events;
669 wake_up (&vboxDev->eventq);
670 }
671 }
672 else
673 {
674 /* impossible... */
675 LogRelFunc(("IRQ was not acknowledged! rc = %Vrc, header.rc = %Vrc\n",
676 rcVBox, vboxDev->irqAckRequest->header.rc));
677 BUG ();
678 }
679
680 /* it was ours! */
681 fIRQTaken = 1;
682 }
683#ifdef IRQ_DEBUG
684 else
685 {
686 /* we might be attached to a shared interrupt together with another device. */
687 Log(("vboxadd IRQ_DEBUG: stale IRQ mem=%p events=%d devevents=%#x\n",
688 vboxDev->pVMMDevMemory,
689 vboxDev->pVMMDevMemory->V.V1_04.fHaveEvents,
690 vboxDev->u32Events));
691 }
692#endif
693 /* it was ours */
694 return IRQ_RETVAL(fIRQTaken);
695}
696
697/**
698 * Helper function to reserve a fixed kernel address space window
699 * and tell the VMM that it can safely put its hypervisor there.
700 * This function might fail which is not a critical error.
701 */
702static int vboxadd_reserve_hypervisor(void)
703{
704 VMMDevReqHypervisorInfo *req = NULL;
705 int rcVBox;
706
707 /* allocate request structure */
708 rcVBox = VbglGRAlloc(
709 (VMMDevRequestHeader**)&req,
710 sizeof(VMMDevReqHypervisorInfo),
711 VMMDevReq_GetHypervisorInfo
712 );
713 if (VBOX_FAILURE(rcVBox))
714 {
715 LogRelFunc(("failed to allocate hypervisor info structure! rc = %Vrc\n", rcVBox));
716 goto bail_out;
717 }
718 /* query the hypervisor information */
719 rcVBox = VbglGRPerform(&req->header);
720 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(req->header.rc))
721 {
722 /* are we supposed to make a reservation? */
723 if (req->hypervisorSize)
724 {
725 /** @todo repeat this several times until we get an address the host likes */
726
727 void *hypervisorArea;
728 /* reserve another 4MB because the start needs to be 4MB aligned */
729 uint32_t hypervisorSize = req->hypervisorSize + 0x400000;
730 /* perform a fictive IO space mapping */
731 hypervisorArea = ioremap(HYPERVISOR_PHYSICAL_START, hypervisorSize);
732 if (hypervisorArea)
733 {
734 /* communicate result to VMM, align at 4MB */
735 req->hypervisorStart = (vmmDevHypPtr)RT_ALIGN_P(hypervisorArea, 0x400000);
736 req->header.requestType = VMMDevReq_SetHypervisorInfo;
737 req->header.rc = VERR_GENERAL_FAILURE;
738 rcVBox = VbglGRPerform(&req->header);
739 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(req->header.rc))
740 {
741 /* store mapping for future unmapping */
742 vboxDev->hypervisorStart = hypervisorArea;
743 vboxDev->hypervisorSize = hypervisorSize;
744 }
745 else
746 {
747 LogRelFunc(("failed to set hypervisor region! rc = %Vrc, header.rc = %Vrc\n",
748 rcVBox, req->header.rc));
749 goto bail_out;
750 }
751 }
752 else
753 {
754 LogRelFunc(("failed to allocate 0x%x bytes of IO space\n", hypervisorSize));
755 goto bail_out;
756 }
757 }
758 }
759 else
760 {
761 LogRelFunc(("failed to query hypervisor info! rc = %Vrc, header.rc = %Vrc\n",
762 rcVBox, req->header.rc));
763 goto bail_out;
764 }
765 /* successful return */
766 VbglGRFree(&req->header);
767 return 0;
768bail_out:
769 /* error return */
770 if (req)
771 VbglGRFree(&req->header);
772 return 1;
773}
774
775/**
776 * Helper function to free the hypervisor address window
777 *
778 */
779static int vboxadd_free_hypervisor(void)
780{
781 VMMDevReqHypervisorInfo *req = NULL;
782 int rcVBox;
783
784 /* allocate request structure */
785 rcVBox = VbglGRAlloc(
786 (VMMDevRequestHeader**)&req,
787 sizeof(VMMDevReqHypervisorInfo),
788 VMMDevReq_SetHypervisorInfo
789 );
790 if (VBOX_FAILURE(rcVBox))
791 {
792 LogRelFunc(("failed to allocate hypervisor info structure! rc = %Vrc\n", rcVBox));
793 goto bail_out;
794 }
795 /* reset the hypervisor information */
796 req->hypervisorStart = 0;
797 req->hypervisorSize = 0;
798 rcVBox = VbglGRPerform(&req->header);
799 if (VBOX_SUCCESS(rcVBox) && VBOX_SUCCESS(req->header.rc))
800 {
801 /* now we can free the associated IO space mapping */
802 iounmap(vboxDev->hypervisorStart);
803 vboxDev->hypervisorStart = 0;
804 }
805 else
806 {
807 LogRelFunc(("failed to reset hypervisor info! rc = %Vrc, header.rc = %Vrc\n",
808 rcVBox, req->header.rc));
809 goto bail_out;
810 }
811 return 0;
812
813 bail_out:
814 if (req)
815 VbglGRFree(&req->header);
816 return 1;
817}
818
819/**
820 * Helper to free resources
821 *
822 */
823static void free_resources(void)
824{
825 if (vboxDev)
826 {
827 /* at first detach from IRQ! */
828 if (vboxDev->irq)
829 free_irq(vboxDev->irq, vboxDev);
830 if (vboxDev->hypervisorStart)
831 vboxadd_free_hypervisor();
832 if (vboxDev->irqAckRequest)
833 {
834 VbglGRFree(&vboxDev->irqAckRequest->header);
835 VbglTerminate();
836 }
837 if (vboxDev->pVMMDevMemory)
838 iounmap(vboxDev->pVMMDevMemory);
839 if (vboxDev->vmmdevmem)
840 release_mem_region(vboxDev->vmmdevmem, vboxDev->vmmdevmem_size);
841 kfree(vboxDev);
842 vboxDev = NULL;
843 }
844}
845
846#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
847#define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
848#define PCI_DEV_PUT(x) pci_dev_put(x)
849#else
850#define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
851#define PCI_DEV_PUT(x)
852#endif
853
854/**
855 * Module initialization
856 *
857 */
858static __init int init(void)
859{
860 int err;
861 int rcVBox;
862 struct pci_dev *pcidev = NULL;
863 VMMDevReportGuestInfo *infoReq = NULL;
864
865 if (vboxadd_cmc_init ())
866 {
867 printk (KERN_ERR "vboxadd: could not init cmc.\n");
868 return -ENODEV;
869 }
870
871 /*
872 * Detect PCI device
873 */
874 pcidev = PCI_DEV_GET(VMMDEV_VENDORID, VMMDEV_DEVICEID, pcidev);
875 if (!pcidev)
876 {
877 printk(KERN_ERR "vboxadd: VirtualBox PCI device not found.\n");
878 return -ENODEV;
879 }
880
881 err = pci_enable_device (pcidev);
882 if (err)
883 {
884 Log(("vboxadd: could not enable device: %d\n", err));
885 PCI_DEV_PUT(pcidev);
886 return -ENODEV;
887 }
888
889 LogRel(("Starting VirtualBox version %s Guest Additions\n",
890 VBOX_VERSION_STRING));
891 /* register a character device */
892 if (vbox_major > 0)
893 {
894 err = register_chrdev(vbox_major, "vboxadd", &vbox_fops);
895 if (err < 0 || (vbox_major & err) || (!vbox_major && !err))
896 {
897 LogRelFunc(("register_chrdev failed: vbox_major: %d, err = %d\n",
898 vbox_major, err));
899 PCI_DEV_PUT(pcidev);
900 return -ENODEV;
901 }
902 /* if no major code was set, take the return value */
903 if (!vbox_major)
904 vbox_major = err;
905 }
906 else
907 {
908 err = misc_register(&gMiscDevice);
909 if (err)
910 {
911 LogRelFunc(("misc_register failed (rc=%d)\n", err));
912 return -ENODEV;
913 }
914 }
915
916 /* allocate and initialize device extension */
917 vboxDev = kmalloc(sizeof(*vboxDev), GFP_KERNEL);
918 if (!vboxDev)
919 {
920 LogRelFunc(("cannot allocate device!\n"));
921 err = -ENOMEM;
922 goto fail;
923 }
924 memset(vboxDev, 0, sizeof(*vboxDev));
925 snprintf(vboxDev->name, sizeof(vboxDev->name), "vboxadd");
926
927 /* get the IO port region */
928 vboxDev->io_port = pci_resource_start(pcidev, 0);
929
930 /* get the memory region */
931 vboxDev->vmmdevmem = pci_resource_start(pcidev, 1);
932 vboxDev->vmmdevmem_size = pci_resource_len(pcidev, 1);
933
934 /* all resources found? */
935 if (!vboxDev->io_port || !vboxDev->vmmdevmem || !vboxDev->vmmdevmem_size)
936 {
937 LogRelFunc(("did not find expected hardware resources!\n"));
938 err = -ENXIO;
939 goto fail;
940 }
941
942 /* request ownership of adapter memory */
943 if (request_mem_region(vboxDev->vmmdevmem, vboxDev->vmmdevmem_size, "vboxadd") == 0)
944 {
945 LogRelFunc(("failed to request adapter memory!\n"));
946 err = -ENXIO;
947 goto fail;
948 }
949
950 /* map adapter memory into kernel address space and check version */
951 vboxDev->pVMMDevMemory = (VMMDevMemory *) ioremap(vboxDev->vmmdevmem,
952 vboxDev->vmmdevmem_size);
953 if (!vboxDev->pVMMDevMemory)
954 {
955 LogRelFunc(("ioremap failed\n"));
956 err = -ENOMEM;
957 goto fail;
958 }
959
960 if (vboxDev->pVMMDevMemory->u32Version != VMMDEV_MEMORY_VERSION)
961 {
962 LogRelFunc(("invalid VMM device memory version! (got 0x%x, expected 0x%x)\n",
963 vboxDev->pVMMDevMemory->u32Version, VMMDEV_MEMORY_VERSION));
964 err = -ENXIO;
965 goto fail;
966 }
967
968 /* initialize VBGL subsystem */
969 rcVBox = VbglInit(vboxDev->io_port, vboxDev->pVMMDevMemory);
970 if (VBOX_FAILURE(rcVBox))
971 {
972 LogRelFunc(("could not initialize VBGL subsystem! rc = %Vrc\n", rcVBox));
973 err = -ENXIO;
974 goto fail;
975 }
976
977 /* report guest information to host, this must be done as the very first request */
978 rcVBox = VbglGRAlloc((VMMDevRequestHeader**)&infoReq,
979 sizeof(VMMDevReportGuestInfo), VMMDevReq_ReportGuestInfo);
980 if (VBOX_FAILURE(rcVBox))
981 {
982 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox));
983 err = -ENOMEM;
984 goto fail;
985 }
986
987 /* report guest version to host, the VMMDev requires that to be done first */
988 infoReq->guestInfo.additionsVersion = VMMDEV_VERSION;
989#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
990 infoReq->guestInfo.osType = VBOXOSTYPE_Linux26;
991#else
992 infoReq->guestInfo.osType = VBOXOSTYPE_Linux24;
993#endif
994 rcVBox = VbglGRPerform(&infoReq->header);
995 if (VBOX_FAILURE(rcVBox) || VBOX_FAILURE(infoReq->header.rc))
996 {
997 LogRelFunc(("error reporting guest info to host! rc = %Vrc, header.rc = %Vrc\n",
998 rcVBox, infoReq->header.rc));
999 VbglGRFree(&infoReq->header);
1000 err = -ENXIO;
1001 goto fail;
1002 }
1003 VbglGRFree(&infoReq->header);
1004
1005 /* Unset the graphics capability until/unless X is loaded. */
1006 /** @todo check the error code once we bump the additions version.
1007 For now we ignore it for compatibility with older hosts. */
1008 {
1009 VMMDevReqGuestCapabilities2 *vmmreqGuestCaps;
1010
1011
1012 rcVBox = VbglGRAlloc((VMMDevRequestHeader**)&vmmreqGuestCaps,
1013 sizeof(VMMDevReqGuestCapabilities2),
1014 VMMDevReq_SetGuestCapabilities);
1015 if (VBOX_FAILURE(rcVBox))
1016 {
1017 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox));
1018 err = -ENOMEM;
1019 goto fail;
1020 }
1021 vmmreqGuestCaps->u32OrMask = 0;
1022 vmmreqGuestCaps->u32NotMask = VMMDEV_GUEST_SUPPORTS_GRAPHICS;
1023 rcVBox = VbglGRPerform(&vmmreqGuestCaps->header);
1024 VbglGRFree(&vmmreqGuestCaps->header);
1025 if (RT_FAILURE(rcVBox))
1026 {
1027 err = -ENXIO;
1028 goto fail;
1029 }
1030 }
1031
1032 /* perform hypervisor address space reservation */
1033 if (vboxadd_reserve_hypervisor())
1034 {
1035 /* we just ignore the error, no address window reservation, non fatal */
1036 }
1037
1038 /* allocate a VMM request structure for use in the ISR */
1039 rcVBox = VbglGRAlloc((VMMDevRequestHeader**)&vboxDev->irqAckRequest,
1040 sizeof(VMMDevEvents), VMMDevReq_AcknowledgeEvents);
1041 if (VBOX_FAILURE(rcVBox))
1042 {
1043 LogRelFunc(("could not allocate request structure! rc = %Vrc\n", rcVBox));
1044 err = -ENOMEM;
1045 goto fail;
1046 }
1047
1048 /* get ISR */
1049 err = request_irq(pcidev->irq, vboxadd_irq_handler,
1050#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
1051 IRQF_SHARED,
1052#else
1053 SA_SHIRQ,
1054#endif
1055 "vboxadd", vboxDev);
1056 if (err)
1057 {
1058 LogRelFunc(("could not request IRQ %d, err: %d\n", pcidev->irq, err));
1059 goto fail;
1060 }
1061 vboxDev->irq = pcidev->irq;
1062
1063 init_waitqueue_head (&vboxDev->eventq);
1064
1065 /* some useful information for the user but don't show this on the console */
1066 LogRel(("VirtualBox device settings: major %d, IRQ %d, "
1067 "I/O port 0x%x, MMIO at 0x%x (size 0x%x), "
1068 "hypervisor window at 0x%p (size 0x%x)\n",
1069 vbox_major, vboxDev->irq, vboxDev->io_port,
1070 vboxDev->vmmdevmem, vboxDev->vmmdevmem_size,
1071 vboxDev->hypervisorStart, vboxDev->hypervisorSize));
1072 Log(("Successfully loaded VirtualBox device version "
1073 VBOX_VERSION_STRING " (interface " xstr(VMMDEV_VERSION) ")\n"));
1074
1075 /* successful return */
1076 PCI_DEV_PUT(pcidev);
1077 return 0;
1078
1079fail:
1080 PCI_DEV_PUT(pcidev);
1081 free_resources();
1082 unregister_chrdev(vbox_major, "vboxadd");
1083 return err;
1084}
1085
1086/**
1087 * Module termination
1088 *
1089 */
1090static __exit void fini(void)
1091{
1092 unregister_chrdev(vbox_major, "vboxadd");
1093 free_resources();
1094 vboxadd_cmc_fini ();
1095}
1096
1097module_init(init);
1098module_exit(fini);
1099
1100/* PCI hotplug structure */
1101static const struct pci_device_id __devinitdata vmmdev_pci_id[] =
1102{
1103 {
1104 .vendor = VMMDEV_VENDORID,
1105 .device = VMMDEV_DEVICEID
1106 },
1107 {
1108 /* empty entry */
1109 }
1110};
1111MODULE_DEVICE_TABLE(pci, vmmdev_pci_id);
1112
1113int __gxx_personality_v0 = 0xdeadbeef;
1114
1115/*
1116 * Local Variables:
1117 * c-mode: bsd
1118 * indent-tabs-mode: nil
1119 * c-plusplus: evil
1120 * End:
1121 */
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