VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c@ 6412

Last change on this file since 6412 was 4988, checked in by vboxsync, 17 years ago

did the freebsd device skeleton. the cloning doesn't seem quiet stable yet wrt unloading (may panic).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.4 KB
Line 
1/* $Id: SUPDrv-freebsd.c 4988 2007-09-24 03:47:01Z vboxsync $ */
2/** @file
3 * VBoxDrv - FreeBSD specifics.
4 */
5
6/*
7 * Copyright (c) 2007 knut st. osmundsen <[email protected]>
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35/* Deal with conflicts first. */
36#include <sys/param.h>
37#undef PVM
38#include <sys/types.h>
39#include <sys/module.h>
40#include <sys/systm.h>
41#include <sys/errno.h>
42#include <sys/kernel.h>
43#include <sys/fcntl.h>
44#include <sys/conf.h>
45#include <sys/uio.h>
46
47#include "SUPDRV.h"
48#include <VBox/version.h>
49#include <iprt/initterm.h>
50#include <iprt/string.h>
51#include <iprt/spinlock.h>
52#include <iprt/process.h>
53#include <iprt/assert.h>
54#include <iprt/log.h>
55#include <iprt/alloc.h>
56#include <iprt/err.h>
57
58
59/*******************************************************************************
60* Internal Functions *
61*******************************************************************************/
62static int VBoxDrvFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg);
63static int VBoxDrvFreeBSDLoad(void);
64static int VBoxDrvFreeBSDUnload(void);
65static void VBoxDrvFreeBSDClone(void *pvArg, struct ucred *pCred, char *pachName, int cchName, struct cdev **ppDev);
66
67static d_fdopen_t VBoxDrvFreeBSDOpen;
68static d_close_t VBoxDrvFreeBSDClose;
69static d_ioctl_t VBoxDrvFreeBSDIOCtl;
70static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd);
71
72
73/*******************************************************************************
74* Global Variables *
75*******************************************************************************/
76/**
77 * Module info structure used by the kernel.
78 */
79static moduledata_t g_VBoxDrvFreeBSDModule =
80{
81 "vboxdrv",
82 VBoxDrvFreeBSDModuleEvent,
83 NULL
84};
85
86/** Declare the module as a pseudo device. */
87DECLARE_MODULE(vboxdrv, g_VBoxDrvFreeBSDModule, SI_SUB_PSEUDO, SI_ORDER_ANY);
88
89/**
90 * The /dev/vboxdrv character device entry points.
91 */
92static struct cdevsw g_VBoxDrvFreeBSDChrDevSW =
93{
94 .d_version = D_VERSION,
95 .d_flags = D_PSEUDO | D_TRACKCLOSE,
96 .d_fdopen = VBoxDrvFreeBSDOpen,
97 .d_close = VBoxDrvFreeBSDClose,
98 .d_ioctl = VBoxDrvFreeBSDIOCtl,
99 .d_name = "vboxdrv"
100};
101
102/** List of cloned device. Managed by the kernel. */
103static struct clonedevs *g_pVBoxDrvFreeBSDClones;
104/** The dev_clone event handler tag. */
105static eventhandler_tag g_VBoxDrvFreeBSDEHTag;
106
107/** The device extention. */
108static SUPDRVDEVEXT g_VBoxDrvFreeBSDDevExt;
109
110
111
112
113/**
114 * Module event handler.
115 *
116 * @param pMod The module structure.
117 * @param enmEventType The event type (modeventtype_t).
118 * @param pvArg Module argument. NULL.
119 *
120 * @return 0 on success, errno.h status code on failure.
121 */
122static int VBoxDrvFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg)
123{
124 int rc;
125 switch (enmEventType)
126 {
127 case MOD_LOAD:
128 rc = VBoxDrvFreeBSDLoad();
129 break;
130
131 case MOD_UNLOAD:
132 rc = VBoxDrvFreeBSDUnload();
133 break;
134
135 case MOD_SHUTDOWN:
136 case MOD_QUIESCE:
137 default:
138 return EOPNOTSUPP;
139 }
140
141 if (RT_SUCCESS(rc))
142 return 0;
143 return RTErrConvertToErrno(rc);
144}
145
146
147static int VBoxDrvFreeBSDLoad(void)
148{
149 dprintf(("VBoxDrvFreeBSDLoad:\n"));
150
151 /*
152 * Initialize the runtime.
153 */
154 int rc = RTR0Init(0);
155 if (RT_SUCCESS(rc))
156 {
157 /*
158 * Initialize the device extension.
159 */
160 rc = supdrvInitDevExt(&g_VBoxDrvFreeBSDDevExt);
161 if (RT_SUCCESS(rc))
162 {
163 /*
164 * Configure device cloning.
165 */
166 clone_setup(&g_pVBoxDrvFreeBSDClones);
167 g_VBoxDrvFreeBSDEHTag = EVENTHANDLER_REGISTER(dev_clone, VBoxDrvFreeBSDClone, 0, 1000);
168 if (g_VBoxDrvFreeBSDEHTag)
169 {
170 dprintf(("VBoxDrvFreeBSDLoad: returns successfully\n"));
171 return VINF_SUCCESS;
172 }
173
174 printf("vboxdrv: EVENTHANDLER_REGISTER(dev_clone,,,) failed\n");
175 clone_cleanup(&g_pVBoxDrvFreeBSDClones);
176 rc = SUPDRV_ERR_ALREADY_LOADED;
177 supdrvDeleteDevExt(&g_VBoxDrvFreeBSDDevExt);
178 }
179 else
180 printf("vboxdrv: supdrvInitDevExt failed, rc=%d\n", rc);
181 RTR0Term();
182 }
183 else
184 printf("vboxdrv: RTR0Init failed, rc=%d\n", rc);
185 return rc;
186}
187
188static int VBoxDrvFreeBSDUnload(void)
189{
190 dprintf(("VBoxDrvFreeBSDUnload:\n"));
191
192 /** @todo verify that FreeBSD does reference counting. */
193
194 /*
195 * Reserve what we did in VBoxDrvFreeBSDInit.
196 */
197 clone_cleanup(&g_pVBoxDrvFreeBSDClones);
198
199 supdrvDeleteDevExt(&g_VBoxDrvFreeBSDDevExt);
200
201 RTR0Term();
202
203 memset(&g_VBoxDrvFreeBSDDevExt, 0, sizeof(g_VBoxDrvFreeBSDDevExt));
204
205 dprintf(("VBoxDrvFreeBSDUnload: returns\n"));
206 return VINF_SUCCESS;
207}
208
209
210/**
211 * DEVFS event handler.
212 */
213static void VBoxDrvFreeBSDClone(void *pvArg, struct ucred *pCred, char *pszName, int cchName, struct cdev **ppDev)
214{
215 int iUnit;
216 int rc;
217
218 dprintf(("VBoxDrvFreeBSDClone: pszName=%s ppDev=%p\n", pszName, ppDev));
219
220 /*
221 * One device node per user, si_drv1 points to the session.
222 * /dev/vboxdrv<N> where N = {0...255}.
223 */
224 if (!ppDev)
225 return;
226 if (dev_stdclone(pszName, NULL, "vboxdrv", &iUnit) != 1)
227 return;
228 if (iUnit >= 256 || iUnit < 0)
229 {
230 dprintf(("VBoxDrvFreeBSDClone: iUnit=%d >= 256 - rejected\n", iUnit));
231 return;
232 }
233
234 dprintf(("VBoxDrvFreeBSDClone: pszName=%s iUnit=%d\n", pszName, iUnit));
235
236 rc = clone_create(&g_pVBoxDrvFreeBSDClones, &g_VBoxDrvFreeBSDChrDevSW, &iUnit, ppDev, 0);
237 dprintf(("VBoxDrvFreeBSDClone: clone_create -> %d; iUnit=%d\n", rc, iUnit));
238 if (rc)
239 {
240 *ppDev = make_dev(&g_VBoxDrvFreeBSDChrDevSW,
241 unit2minor(iUnit),
242 UID_ROOT,
243 GID_WHEEL,
244 0666,
245 "vboxdrv%d", iUnit);
246 if (*ppDev)
247 {
248 dev_ref(*ppDev);
249 (*ppDev)->si_flags |= SI_CHEAPCLONE;
250 dprintf(("VBoxDrvFreeBSDClone: Created *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n",
251 *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2));
252 (*ppDev)->si_drv1 = (*ppDev)->si_drv2 = NULL;
253 }
254 else
255 OSDBGPRINT(("VBoxDrvFreeBSDClone: make_dev iUnit=%d failed\n", iUnit));
256 }
257 else
258 dprintf(("VBoxDrvFreeBSDClone: Existing *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n",
259 *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2));
260}
261
262
263
264/**
265 *
266 * @returns 0 on success, errno on failure.
267 * EBUSY if the device is used by someone else.
268 * @param pDev The device node.
269 * @param fOpen The open flags.
270 * @param pTd The thread.
271 * @param pFd The file descriptor. FreeBSD 7.0 and later.
272 * @param iFd The file descriptor index(?). Pre FreeBSD 7.0.
273 */
274#if FreeBSD >= 700 /* figure when and how to check properly */
275static int VBoxDrvFreeBSDOpen(struct cdev *pDev, int fOpen, struct thread *pTd, struct file *pFd)
276#else
277static int VBoxDrvFreeBSDOpen(struct cdev *pDev, int fOpen, struct thread *pTd, int iFd)
278#endif
279{
280 PSUPDRVSESSION pSession;
281 int rc;
282
283 dprintf(("VBoxDrvFreeBSDOpen: fOpen=%#x iUnit=%d\n", fOpen, minor2unit(minor(pDev))));
284
285 /*
286 * Let's be a bit picky about the flags...
287 */
288 if (fOpen != (FREAD|FWRITE /*=O_RDWR*/))
289 {
290 dprintf(("VBoxDrvFreeBSDOpen: fOpen=%#x expected %#x\n", fOpen, O_RDWR));
291 return EINVAL;
292 }
293
294 /*
295 * Try grab it (we don't grab the giant, remember).
296 */
297 if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, (void *)0x42, NULL))
298 return EBUSY;
299
300 /*
301 * Create a new session.
302 */
303 rc = supdrvCreateSession(&g_VBoxDrvFreeBSDDevExt, &pSession);
304 if (RT_SUCCESS(rc))
305 {
306 pSession->Uid = 0;
307 pSession->Gid = 0;
308 pSession->Process = RTProcSelf();
309 pSession->R0Process = RTR0ProcHandleSelf();
310 if (ASMAtomicCmpXchgPtr(&pDev->si_drv1, pSession, (void *)0x42))
311 return 0;
312
313 OSDBGPRINT(("VBoxDrvFreeBSDOpen: si_drv1=%p, expected 0x42!\n", pDev->si_drv1));
314 supdrvCloseSession(&g_VBoxDrvFreeBSDDevExt, pSession);
315 }
316
317 return RTErrConvertToErrno(rc);
318}
319
320
321/**
322 * Close a file device previously opened by VBoxDrvFreeBSDOpen
323 *
324 * @returns 0 on success.
325 * @param pDev The device.
326 * @param fFile The file descriptor flags.
327 * @param DevType The device type (CHR.
328 * @param pTd The calling thread.
329 */
330static int VBoxDrvFreeBSDClose(struct cdev *pDev, int fFile, int DevType, struct thread *pTd)
331{
332 PSUPDRVSESSION pSession = (PSUPDRVSESSION)pDev->si_drv1;
333 dprintf(("VBoxDrvFreeBSDClose: fFile=%#x iUnit=%d pSession=%p\n", fFile, minor2unit(minor(pDev)), pSession));
334
335 /*
336 * Close the session if it's still hanging on to the device...
337 */
338 if (VALID_PTR(pSession))
339 {
340 supdrvCloseSession(&g_VBoxDrvFreeBSDDevExt, pSession);
341 if (!ASMAtomicCmpXchgPtr(&pDev->si_drv1, NULL, pSession))
342 OSDBGPRINT(("VBoxDrvFreeBSDClose: si_drv1=%p expected %p!\n", pDev->si_drv1, pSession));
343 }
344 else
345 OSDBGPRINT(("VBoxDrvFreeBSDClose: si_drv1=%p!\n", pSession));
346 return 0;
347}
348
349
350/**
351 * I/O control request.
352 *
353 * @returns depends...
354 * @param pDev The device.
355 * @param ulCmd The command.
356 * @param pvData Pointer to the data.
357 * @param fFile The file descriptor flags.
358 * @param pTd The calling thread.
359 */
360static int VBoxDrvFreeBSDIOCtl(struct cdev *pDev, u_long ulCmd, caddr_t pvData, int fFile, struct thread *pTd)
361{
362 /*
363 * Validate the input.
364 */
365 PSUPDRVSESSION pSession = (PSUPDRVSESSION)pDev->si_drv1;
366 if (RT_UNLIKELY(!VALID_PTR(pSession)))
367 return EINVAL;
368
369 /*
370 * Deal with the fast ioctl path first.
371 */
372 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
373 || ulCmd == SUP_IOCTL_FAST_DO_HWACC_RUN
374 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
375 return supdrvIOCtlFast(ulCmd, &g_VBoxDrvFreeBSDDevExt, pSession);
376
377 return VBoxDrvFreeBSDIOCtlSlow(pSession, ulCmd, pvData, pTd);
378}
379
380
381/**
382 * Deal with the 'slow' I/O control requests.
383 *
384 * @returns 0 on success, appropriate errno on failure.
385 * @param pSession The session.
386 * @param ulCmd The command.
387 * @param pvData The request data.
388 * @param pTd The calling thread.
389 */
390static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd)
391{
392 PSUPREQHDR pHdr;
393 uint32_t cbReq = IOCPARM_LEN(ulCmd);
394 void *pvUser = NULL;
395
396 /*
397 * Buffered request?
398 */
399 if ((IOC_DIRMASK & ulCmd) == IOC_INOUT)
400 {
401 pHdr = (PSUPREQHDR)pvData;
402 if (RT_UNLIKELY(cbReq < sizeof(*pHdr)))
403 {
404 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: cbReq=%#x < %#x; ulCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), ulCmd));
405 return EINVAL;
406 }
407 if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
408 {
409 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", pHdr->fFlags, ulCmd));
410 return EINVAL;
411 }
412 if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq
413 || pHdr->cbIn < sizeof(*pHdr)
414 || pHdr->cbOut < sizeof(*pHdr)))
415 {
416 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x) != %#x; ulCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, ulCmd));
417 return EINVAL;
418 }
419 }
420 /*
421 * Big unbuffered request?
422 */
423 else if ((IOC_DIRMASK & ulCmd) == IOC_VOID && !cbReq)
424 {
425 /*
426 * Read the header, validate it and figure out how much that needs to be buffered.
427 */
428 SUPREQHDR Hdr;
429 pvUser = *(void **)pvData;
430 int rc = copyin(pvUser, &Hdr, sizeof(Hdr));
431 if (RT_UNLIKELY(rc))
432 {
433 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,Hdr,) -> %#x; ulCmd=%#lx\n", pvUser, rc, ulCmd));
434 return rc;
435 }
436 if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
437 {
438 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", Hdr.fFlags, ulCmd));
439 return EINVAL;
440 }
441 cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
442 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
443 || Hdr.cbOut < sizeof(Hdr)
444 || cbReq > _1M*16))
445 {
446 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x); ulCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, ulCmd));
447 return EINVAL;
448 }
449
450 /*
451 * Allocate buffer and copy in the data.
452 */
453 pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq);
454 if (RT_UNLIKELY(!pHdr))
455 {
456 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: failed to allocate buffer of %d bytes; ulCmd=%#lx\n", cbReq, ulCmd));
457 return ENOMEM;
458 }
459 rc = copyin(pvUser, pHdr, Hdr.cbIn);
460 if (RT_UNLIKELY(rc))
461 {
462 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,%p,%#x) -> %#x; ulCmd=%#lx\n",
463 pvUser, pHdr, Hdr.cbIn, rc, ulCmd));
464 RTMemTmpFree(pHdr);
465 return rc;
466 }
467 }
468 else
469 {
470 dprintf(("VBoxDrvFreeBSDIOCtlSlow: huh? cbReq=%#x ulCmd=%#lx\n", cbReq, ulCmd));
471 return EINVAL;
472 }
473
474 /*
475 * Process the IOCtl.
476 */
477 int rc = supdrvIOCtl(ulCmd, &g_VBoxDrvFreeBSDDevExt, pSession, pHdr);
478 if (RT_LIKELY(!rc))
479 {
480 /*
481 * If unbuffered, copy back the result before returning.
482 */
483 if (pvUser)
484 {
485 uint32_t cbOut = pHdr->cbOut;
486 if (cbOut > cbReq)
487 {
488 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, ulCmd));
489 cbOut = cbReq;
490 }
491 rc = copyout(pHdr, pvUser, cbOut);
492 if (RT_UNLIKELY(rc))
493 OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyout(%p,%p,%#x) -> %d; uCmd=%#lx!\n", pHdr, pvUser, cbOut, rc, ulCmd));
494
495 /* cleanup */
496 RTMemTmpFree(pHdr);
497 }
498 dprintf(("VBoxDrvFreeBSDIOCtlSlow: returns %d / %d ulCmd=%lx\n", 0, pHdr->rc, ulCmd));
499 }
500 else
501 {
502 /*
503 * The request failed, just clean up.
504 */
505 if (pvUser)
506 RTMemTmpFree(pHdr);
507
508 dprintf(("VBoxDrvFreeBSDIOCtlSlow: ulCmd=%lx pData=%p failed, rc=%d\n", ulCmd, pvData, rc));
509 rc = EINVAL;
510 }
511
512 return rc;
513}
514
515
516
517void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
518{
519 NOREF(pObj);
520 NOREF(pSession);
521}
522
523
524bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
525{
526 NOREF(pObj);
527 NOREF(pSession);
528 NOREF(pszObjName);
529 NOREF(prc);
530 return false;
531}
532
533
534SUPR0DECL(int) SUPR0Printf(const char *pszFormat, ...)
535{
536 va_list va;
537 char szMsg[256];
538 int cch;
539
540 va_start(va, pszFormat);
541 cch = RTStrPrintfV(szMsg, sizeof(szMsg), pszFormat, va);
542 va_end(va);
543
544 printf("%s", szMsg);
545
546 return cch;
547}
548
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