VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c@ 63408

Last change on this file since 63408 was 63408, checked in by vboxsync, 9 years ago

VBoxGuest-netbsd.c: Sprinkle LogFlow.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.2 KB
Line 
1/* $Id: VBoxGuest-netbsd.c 63408 2016-08-13 01:37:19Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for NetBSD.
4 */
5
6/*
7 * Copyright (C) 2007-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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
18/** @todo r=bird: This must merge with SUPDrv-netbsd.c before long. The two
19 * source files should only differ on prefixes and the extra bits wrt to the
20 * pci device. I.e. it should be diffable so that fixes to one can easily be
21 * applied to the other. */
22
23
24/*********************************************************************************************************************************
25* Header Files *
26*********************************************************************************************************************************/
27#include <sys/param.h>
28#include <sys/systm.h>
29#include <sys/select.h>
30#include <sys/conf.h>
31#include <sys/kernel.h>
32#include <sys/kmem.h>
33#include <sys/module.h>
34#include <sys/device.h>
35#include <sys/bus.h>
36#include <sys/poll.h>
37#include <sys/proc.h>
38#include <sys/stat.h>
39#include <sys/selinfo.h>
40#include <sys/queue.h>
41#include <sys/lock.h>
42#include <sys/types.h>
43#include <sys/conf.h>
44#include <sys/malloc.h>
45#include <sys/uio.h>
46#include <sys/file.h>
47#include <sys/filedesc.h>
48#include <sys/vfs_syscalls.h>
49#include <dev/pci/pcivar.h>
50#include <dev/pci/pcireg.h>
51#include <dev/pci/pcidevs.h>
52
53#ifdef PVM
54# undef PVM
55#endif
56#include "VBoxGuestInternal.h"
57#include <VBox/log.h>
58#include <iprt/assert.h>
59#include <iprt/initterm.h>
60#include <iprt/process.h>
61#include <iprt/mem.h>
62#include <iprt/asm.h>
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** The module name. */
69#define DEVICE_NAME "vboxguest"
70
71
72/*********************************************************************************************************************************
73* Structures and Typedefs *
74*********************************************************************************************************************************/
75typedef struct VBoxGuestDeviceState
76{
77 device_t sc_dev;
78 struct pci_attach_args *pa;
79 bus_space_tag_t io_tag;
80 bus_space_handle_t io_handle;
81 bus_addr_t uIOPortBase;
82 bus_size_t io_regsize;
83 bus_space_tag_t iVMMDevMemResId;
84 bus_space_handle_t VMMDevMemHandle;
85
86 /** Size of the memory area. */
87 bus_size_t VMMDevMemSize;
88 /** Mapping of the register space */
89 bus_addr_t pMMIOBase;
90
91 /** IRQ resource handle. */
92 pci_intr_handle_t ih;
93 /** Pointer to the IRQ handler. */
94 void *pfnIrqHandler;
95
96 /** Controller features, limits and status. */
97 u_int vboxguest_state;
98} vboxguest_softc;
99
100
101struct vboxguest_session
102{
103 vboxguest_softc *sc;
104 PVBOXGUESTSESSION session;
105};
106
107#define VBOXGUEST_STATE_INITOK 1 << 0
108
109/*********************************************************************************************************************************
110* Internal Functions *
111*********************************************************************************************************************************/
112/*
113 * Character device file handlers.
114 */
115static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *process);
116static int VBoxGuestNetBSDClose(struct file *fp);
117static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long cmd, void *addr);
118static int VBoxGuestNetBSDPoll(struct file *fp, int events);
119static void VBoxGuestNetBSDAttach(device_t, device_t, void*);
120static int VBoxGuestNetBSDDetach(device_t pDevice, int flags);
121
122/*
123 * IRQ related functions.
124 */
125static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc);
126static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *pvState);
127static int VBoxGuestNetBSDISR(void *pvState);
128
129
130/*********************************************************************************************************************************
131* Global Variables *
132*********************************************************************************************************************************/
133/*
134 * The /dev/vboxguest character device entry points.
135 */
136static struct cdevsw g_VBoxGuestNetBSDChrDevSW =
137{
138 VBoxGuestNetBSDOpen,
139 noclose,
140 noread,
141 nowrite,
142 noioctl,
143 nostop,
144 notty,
145 nopoll,
146 nommap,
147 nokqfilter,
148};
149
150static const struct fileops vboxguest_fileops = {
151 .fo_read = fbadop_read,
152 .fo_write = fbadop_write,
153 .fo_ioctl = VBoxGuestNetBSDIOCtl,
154 .fo_fcntl = fnullop_fcntl,
155 .fo_poll = VBoxGuestNetBSDPoll,
156 .fo_stat = fbadop_stat,
157 .fo_close = VBoxGuestNetBSDClose,
158 .fo_kqfilter = fnullop_kqfilter,
159 .fo_restart = fnullop_restart
160};
161
162/** Device extention & session data association structure. */
163static VBOXGUESTDEVEXT g_DevExt;
164/** Reference counter */
165static volatile uint32_t cUsers;
166/** selinfo structure used for polling. */
167static struct selinfo g_SelInfo;
168
169CFDRIVER_DECL(vboxguest, DV_DULL, NULL);
170extern struct cfdriver vboxguest_cd;
171
172/**
173 * File open handler
174 *
175 */
176static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *process)
177{
178 int rc;
179 vboxguest_softc *vboxguest;
180 struct vboxguest_session *session;
181 file_t *fp;
182 int fd, error;
183
184 LogFlow((DEVICE_NAME ": %s\n", __func__));
185
186 if ((vboxguest = device_lookup_private(&vboxguest_cd, minor(device))) == NULL) {
187 printf("device_lookup_private failed\n");
188 return (ENXIO);
189 }
190 if ((vboxguest->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0) {
191 aprint_error_dev(vboxguest->sc_dev, "device not configured\n");
192 return (ENXIO);
193 }
194
195 session = kmem_alloc(sizeof(*session), KM_SLEEP);
196 if (session == NULL) {
197 return (ENOMEM);
198 }
199
200 session->sc = vboxguest;
201
202 if ((error = fd_allocfile(&fp, &fd)) != 0) {
203 kmem_free(session, sizeof(*session));
204 return error;
205 }
206
207 /*
208 * Create a new session.
209 */
210 rc = VGDrvCommonCreateUserSession(&g_DevExt, &session->session);
211 if (! RT_SUCCESS(rc))
212 {
213 aprint_error_dev(vboxguest->sc_dev, "VBox session creation failed\n");
214 closef(fp); /* ??? */
215 kmem_free(session, sizeof(*session));
216 return RTErrConvertToErrno(rc);
217 }
218 ASMAtomicIncU32(&cUsers);
219 return fd_clone(fp, fd, flags, &vboxguest_fileops, session);
220
221}
222
223/**
224 * File close handler
225 *
226 */
227static int VBoxGuestNetBSDClose(struct file *fp)
228{
229 struct vboxguest_session *session = fp->f_data;
230 vboxguest_softc *vboxguest = session->sc;
231
232 LogFlow((DEVICE_NAME ": %s\n", __func__));
233
234 VGDrvCommonCloseSession(&g_DevExt, session->session);
235 ASMAtomicDecU32(&cUsers);
236
237 kmem_free(session, sizeof(*session));
238
239 return 0;
240}
241
242/**
243 * IOCTL handler
244 *
245 */
246static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long command, void *data)
247{
248 struct vboxguest_session *session = fp->f_data;
249 vboxguest_softc *vboxguest = session->sc;
250
251 int rc = 0;
252
253 LogFlow((DEVICE_NAME ": %s\n", __func__));
254
255 /*
256 * Validate the request wrapper.
257 */
258 if (IOCPARM_LEN(command) != sizeof(VBGLBIGREQ)) {
259 Log((DEVICE_NAME ": VBoxGuestFreeBSDIOCtl: bad request %lu size=%lu expected=%d\n", command, IOCPARM_LEN(command), sizeof(VBGLBIGREQ)));
260 return ENOTTY;
261 }
262
263 PVBGLBIGREQ ReqWrap = (PVBGLBIGREQ)data;
264 if (ReqWrap->u32Magic != VBGLBIGREQ_MAGIC)
265 {
266 Log((DEVICE_NAME ": VBoxGuestNetBSDIOCtl: bad magic %#x; pArg=%p Cmd=%lu.\n", ReqWrap->u32Magic, data, command));
267 return EINVAL;
268 }
269 if (RT_UNLIKELY( ReqWrap->cbData == 0
270 || ReqWrap->cbData > _1M*16))
271 {
272 printf(DEVICE_NAME ": VBoxGuestNetBSDIOCtl: bad size %#x; pArg=%p Cmd=%lu.\n", ReqWrap->cbData, data, command);
273 return EINVAL;
274 }
275
276 /*
277 * Read the request.
278 */
279 void *pvBuf = RTMemTmpAlloc(ReqWrap->cbData);
280 if (RT_UNLIKELY(!pvBuf))
281 {
282 Log((DEVICE_NAME ":VBoxGuestNetBSDIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap->cbData));
283 return ENOMEM;
284 }
285
286 rc = copyin((void *)(uintptr_t)ReqWrap->pvDataR3, pvBuf, ReqWrap->cbData);
287 if (RT_UNLIKELY(rc))
288 {
289 RTMemTmpFree(pvBuf);
290 Log((DEVICE_NAME ":VBoxGuestNetBSDIOCtl: copyin failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, command, rc));
291aprint_error_dev(vboxguest->sc_dev, "copyin failed\n");
292 return EFAULT;
293 }
294 if (RT_UNLIKELY( ReqWrap->cbData != 0
295 && !VALID_PTR(pvBuf)))
296 {
297 RTMemTmpFree(pvBuf);
298 Log((DEVICE_NAME ":VBoxGuestNetBSDIOCtl: pvBuf invalid pointer %p\n", pvBuf));
299 return EINVAL;
300 }
301
302 /*
303 * Process the IOCtl.
304 */
305 size_t cbDataReturned;
306 rc = VGDrvCommonIoCtl(command, &g_DevExt, session->session, pvBuf, ReqWrap->cbData, &cbDataReturned);
307 if (RT_SUCCESS(rc)) {
308 rc = 0;
309 if (RT_UNLIKELY(cbDataReturned > ReqWrap->cbData))
310 {
311 Log((DEVICE_NAME ":VBoxGuestNetBSDIOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap->cbData));
312 cbDataReturned = ReqWrap->cbData;
313 }
314 if (cbDataReturned > 0)
315 {
316 rc = copyout(pvBuf, (void *)(uintptr_t)ReqWrap->pvDataR3, cbDataReturned);
317 if (RT_UNLIKELY(rc))
318 {
319 Log((DEVICE_NAME ":VBoxGuestNetBSDIOCtl: copyout failed; pvBuf=%p pArg=%p. rc=%d\n", pvBuf, data, rc));
320 }
321 }
322 } else {
323 Log((DEVICE_NAME ":VBoxGuestNetBSDIOCtl: VGDrvCommonIoCtl failed. rc=%d\n", rc));
324 rc = -rc;
325 }
326 RTMemTmpFree(pvBuf);
327 return rc;
328}
329
330static int VBoxGuestNetBSDPoll(struct file *fp, int events)
331{
332 struct vboxguest_session *session = fp->f_data;
333 vboxguest_softc *vboxguest = session->sc;
334
335 int rc = 0;
336 int events_processed;
337
338 uint32_t u32CurSeq;
339
340 LogFlow((DEVICE_NAME ": %s\n", __func__));
341
342 u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
343 if (session->session->u32MousePosChangedSeq != u32CurSeq) {
344 events_processed = events & (POLLIN | POLLRDNORM);
345 session->session->u32MousePosChangedSeq = u32CurSeq;
346 } else {
347 events_processed = 0;
348
349 selrecord(curlwp, &g_SelInfo);
350 }
351
352 return events_processed;
353}
354
355static int VBoxGuestNetBSDDetach(device_t self, int flags)
356{
357 vboxguest_softc *vboxguest;
358 vboxguest = device_private(self);
359
360 LogFlow((DEVICE_NAME ": %s\n", __func__));
361
362 if (cUsers > 0)
363 return EBUSY;
364
365 if ((vboxguest->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0) {
366 return 0;
367 }
368
369 /*
370 * Reverse what we did in VBoxGuestNetBSDAttach.
371 */
372
373 VBoxGuestNetBSDRemoveIRQ(vboxguest);
374
375 VGDrvCommonDeleteDevExt(&g_DevExt);
376
377 RTR0Term();
378
379 return 0;
380}
381
382/**
383 * Interrupt service routine.
384 *
385 * @returns Whether the interrupt was from VMMDev.
386 * @param pvState Opaque pointer to the device state.
387 */
388static int VBoxGuestNetBSDISR(void *pvState)
389{
390 LogFlow((DEVICE_NAME ": %s: pvState=%p\n", __func__, pvState));
391
392 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
393
394 return fOurIRQ ? 0 : 1;
395}
396
397void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
398{
399 LogFlow((DEVICE_NAME ": %s\n", __func__));
400
401 /*
402 * Wake up poll waiters.
403 */
404 selnotify(&g_SelInfo, 0, 0);
405}
406
407/**
408 * Sets IRQ for VMMDev.
409 *
410 * @returns NetBSD error code.
411 * @param vboxguest Pointer to the state info structure.
412 */
413static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *vboxguest)
414{
415 int iResId = 0;
416 int rc = 0;
417 struct pci_attach_args *pa = vboxguest->pa;
418 const char *intrstr;
419#if __NetBSD_Prereq__(6, 99, 39)
420 char intstrbuf[100];
421#endif
422
423 LogFlow((DEVICE_NAME ": %s\n", __func__));
424
425 if (pci_intr_map(pa, &vboxguest->ih)) {
426 aprint_error_dev(vboxguest->sc_dev, "couldn't map interrupt.\n");
427 return VERR_DEV_IO_ERROR;
428 }
429 intrstr = pci_intr_string(vboxguest->pa->pa_pc, vboxguest->ih
430#if __NetBSD_Prereq__(6, 99, 39)
431 , intstrbuf, sizeof(intstrbuf)
432#endif
433 );
434 aprint_normal_dev(vboxguest->sc_dev, "interrupting at %s\n", intrstr);
435 vboxguest->pfnIrqHandler = pci_intr_establish(vboxguest->pa->pa_pc, vboxguest->ih, IPL_BIO, VBoxGuestNetBSDISR, vboxguest);
436 if (vboxguest->pfnIrqHandler == NULL) {
437 aprint_error_dev(vboxguest->sc_dev, "couldn't establish interrupt\n");
438 return VERR_DEV_IO_ERROR;
439 }
440
441 return VINF_SUCCESS;
442}
443
444/**
445 * Removes IRQ for VMMDev.
446 *
447 * @param vboxguest Opaque pointer to the state info structure.
448 */
449static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *vboxguest)
450{
451 LogFlow((DEVICE_NAME ": %s\n", __func__));
452
453 if (vboxguest->pfnIrqHandler)
454 {
455 pci_intr_disestablish(vboxguest->pa->pa_pc, vboxguest->pfnIrqHandler);
456 }
457}
458
459static void VBoxGuestNetBSDAttach(device_t parent, device_t self, void *aux)
460{
461 int rc = VINF_SUCCESS;
462 int iResId = 0;
463 vboxguest_softc *vboxguest;
464 struct pci_attach_args *pa = aux;
465 bus_space_tag_t iot, memt;
466 bus_space_handle_t ioh, memh;
467 bus_dma_segment_t seg;
468 int ioh_valid, memh_valid;
469
470 cUsers = 0;
471
472 aprint_normal(": VirtualBox Guest\n");
473
474 vboxguest = device_private(self);
475 vboxguest->sc_dev = self;
476
477 /*
478 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
479 */
480 rc = RTR0Init(0);
481 if (RT_FAILURE(rc))
482 {
483 LogFunc(("RTR0Init failed.\n"));
484 aprint_error_dev(vboxguest->sc_dev, "RTR0Init failed\n");
485 return ;
486 }
487
488 vboxguest->pa = pa;
489
490 /*
491 * Allocate I/O port resource.
492 */
493 ioh_valid = (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0, &vboxguest->io_tag, &vboxguest->io_handle, &vboxguest->uIOPortBase, &vboxguest->io_regsize) == 0);
494
495 if (ioh_valid)
496 {
497
498 /*
499 * Map the MMIO region.
500 */
501 memh_valid = (pci_mapreg_map(pa, PCI_MAPREG_START+4, PCI_MAPREG_TYPE_MEM, BUS_SPACE_MAP_LINEAR, &vboxguest->iVMMDevMemResId, &vboxguest->VMMDevMemHandle, &vboxguest->pMMIOBase, &vboxguest->VMMDevMemSize) == 0);
502 if (memh_valid)
503 {
504 /*
505 * Call the common device extension initializer.
506 */
507 rc = VGDrvCommonInitDevExt(&g_DevExt, vboxguest->uIOPortBase,
508 bus_space_vaddr(vboxguest->iVMMDevMemResId,
509 vboxguest->VMMDevMemHandle),
510 vboxguest->VMMDevMemSize,
511#if ARCH_BITS == 64
512 VBOXOSTYPE_NetBSD_x64,
513#else
514 VBOXOSTYPE_NetBSD,
515#endif
516 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
517 if (RT_SUCCESS(rc))
518 {
519 /*
520 * Add IRQ of VMMDev.
521 */
522 rc = VBoxGuestNetBSDAddIRQ(vboxguest);
523 if (RT_SUCCESS(rc))
524 {
525 vboxguest->vboxguest_state |= VBOXGUEST_STATE_INITOK;
526 return ;
527 }
528 VGDrvCommonDeleteDevExt(&g_DevExt);
529 } else {
530 aprint_error_dev(vboxguest->sc_dev, "init failed\n");
531 }
532 } else {
533 aprint_error_dev(vboxguest->sc_dev, "MMIO mapping failed\n");
534 }
535 } else {
536 aprint_error_dev(vboxguest->sc_dev, "IO mapping failed\n");
537 }
538
539 RTR0Term();
540 return ;
541}
542
543static int
544VBoxGuestNetBSDMatch(device_t parent, cfdata_t match, void *aux)
545{
546 const struct pci_attach_args *pa = aux;
547 if ((PCI_VENDOR(pa->pa_id) == VMMDEV_VENDORID)
548 && (PCI_PRODUCT(pa->pa_id) == VMMDEV_DEVICEID)) {
549 return 1;
550 } else {
551 return 2;
552 }
553}
554
555/* Common code that depend on g_DevExt. */
556#include "VBoxGuestIDC-unix.c.h"
557
558CFATTACH_DECL_NEW(vboxguest, sizeof(vboxguest_softc),
559 VBoxGuestNetBSDMatch, VBoxGuestNetBSDAttach, VBoxGuestNetBSDDetach, NULL);
560
561MODULE(MODULE_CLASS_DRIVER, vboxguest, "pci");
562
563static int loc[2] = {-1, -1};
564
565static const struct cfparent pspec = {
566 "pci", "pci", DVUNIT_ANY
567};
568
569static struct cfdata vboxguest_cfdata[] = {
570 {
571 .cf_name = "vboxguest",
572 .cf_atname = "vboxguest",
573 .cf_unit = 0, /* Only unit 0 is ever used */
574 .cf_fstate = FSTATE_STAR,
575 .cf_loc = loc,
576 .cf_flags = 0,
577 .cf_pspec = &pspec,
578 },
579 { NULL, NULL, 0, 0, NULL, 0, NULL }
580};
581
582static int
583vboxguest_modcmd(modcmd_t cmd, void *opaque)
584{
585 devmajor_t bmajor, cmajor;
586 int error;
587 register_t retval;
588
589 LogFlow((DEVICE_NAME ": %s\n", __func__));
590
591 bmajor = cmajor = NODEVMAJOR;
592 switch (cmd) {
593 case MODULE_CMD_INIT:
594 error = config_cfdriver_attach(&vboxguest_cd);
595 if (error) {
596 printf("config_cfdriver_attach failed: %d", error);
597 break;
598 }
599 error = config_cfattach_attach(vboxguest_cd.cd_name, &vboxguest_ca);
600 if (error) {
601 config_cfdriver_detach(&vboxguest_cd);
602 printf("%s: unable to register cfattach\n", vboxguest_cd.cd_name);
603 break;
604 }
605
606 error = config_cfdata_attach(vboxguest_cfdata, 1);
607 if (error) {
608 printf("%s: unable to attach cfdata\n", vboxguest_cd.cd_name);
609 config_cfattach_detach(vboxguest_cd.cd_name, &vboxguest_ca);
610 config_cfdriver_detach(&vboxguest_cd);
611 break;
612 }
613
614 error = devsw_attach("vboxguest", NULL, &bmajor, &g_VBoxGuestNetBSDChrDevSW, &cmajor);
615
616 if (error == EEXIST)
617 error = 0; /* maybe built-in ... improve eventually */
618 if (error)
619 break;
620
621 error = do_sys_mknod(curlwp, "/dev/vboxguest", 0666|S_IFCHR, makedev(cmajor, 0), &retval, UIO_SYSSPACE);
622 if (error == EEXIST)
623 error = 0;
624 break;
625 case MODULE_CMD_FINI:
626 error = config_cfdata_detach(vboxguest_cfdata);
627 if (error)
628 break;
629 error = config_cfattach_detach(vboxguest_cd.cd_name, &vboxguest_ca);
630 if (error)
631 break;
632 config_cfdriver_detach(&vboxguest_cd);
633 error = devsw_detach(NULL, &g_VBoxGuestNetBSDChrDevSW);
634 break;
635 default:
636 return ENOTTY;
637 }
638 return error;
639}
Note: See TracBrowser for help on using the repository browser.

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