VirtualBox

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

Last change on this file was 100267, checked in by vboxsync, 10 months ago

Additions: Make the R0 physical heap configurable to allow for allocations >= 4GiB if supported by the VBox device (the MMIO request path is available), add support for the MMIO request path required for ARM, bugref:10457

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.8 KB
Line 
1/* $Id: VBoxGuest-netbsd.c 100267 2023-06-23 14:57:53Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for NetBSD.
4 */
5
6/*
7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/select.h>
44#include <sys/conf.h>
45#include <sys/kernel.h>
46#include <sys/kmem.h>
47#include <sys/module.h>
48#include <sys/device.h>
49#include <sys/bus.h>
50#include <sys/poll.h>
51#include <sys/proc.h>
52#include <sys/kauth.h>
53#include <sys/stat.h>
54#include <sys/selinfo.h>
55#include <sys/queue.h>
56#include <sys/lock.h>
57#include <sys/types.h>
58#include <sys/conf.h>
59#include <sys/malloc.h>
60#include <sys/uio.h>
61#include <sys/file.h>
62#include <sys/filedesc.h>
63#include <sys/vfs_syscalls.h>
64#include <dev/pci/pcivar.h>
65#include <dev/pci/pcireg.h>
66#include <dev/pci/pcidevs.h>
67
68#include <dev/wscons/wsconsio.h>
69#include <dev/wscons/wsmousevar.h>
70#include <dev/wscons/tpcalibvar.h>
71
72#ifdef PVM
73# undef PVM
74#endif
75#include "VBoxGuestInternal.h"
76#include <VBox/log.h>
77#include <iprt/err.h>
78#include <iprt/assert.h>
79#include <iprt/initterm.h>
80#include <iprt/process.h>
81#include <iprt/mem.h>
82#include <iprt/asm.h>
83
84
85/*********************************************************************************************************************************
86* Defined Constants And Macros *
87*********************************************************************************************************************************/
88/** The module name. */
89#define DEVICE_NAME "vboxguest"
90
91
92/*********************************************************************************************************************************
93* Structures and Typedefs *
94*********************************************************************************************************************************/
95typedef struct VBoxGuestDeviceState
96{
97 device_t sc_dev;
98 pci_chipset_tag_t sc_pc;
99
100 bus_space_tag_t sc_iot;
101 bus_space_handle_t sc_ioh;
102 bus_addr_t sc_iobase;
103 bus_size_t sc_iosize;
104
105 bus_space_tag_t sc_memt;
106 bus_space_handle_t sc_memh;
107
108 /** Size of the memory area. */
109 bus_size_t sc_memsize;
110
111 /** IRQ resource handle. */
112 pci_intr_handle_t ih;
113 /** Pointer to the IRQ handler. */
114 void *pfnIrqHandler;
115
116 /** Controller features, limits and status. */
117 u_int vboxguest_state;
118
119 device_t sc_wsmousedev;
120 VMMDevReqMouseStatus *sc_vmmmousereq;
121 PVBOXGUESTSESSION sc_session;
122 struct tpcalib_softc sc_tpcalib;
123} vboxguest_softc;
124
125
126struct vboxguest_fdata
127{
128 vboxguest_softc *sc;
129 PVBOXGUESTSESSION session;
130};
131
132#define VBOXGUEST_STATE_INITOK 1 << 0
133
134
135/*********************************************************************************************************************************
136* Internal Functions *
137*********************************************************************************************************************************/
138/*
139 * Driver(9) autoconf machinery.
140 */
141static int VBoxGuestNetBSDMatch(device_t parent, cfdata_t match, void *aux);
142static void VBoxGuestNetBSDAttach(device_t parent, device_t self, void *aux);
143static void VBoxGuestNetBSDWsmAttach(vboxguest_softc *sc);
144static int VBoxGuestNetBSDDetach(device_t self, int flags);
145
146/*
147 * IRQ related functions.
148 */
149static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *sc, struct pci_attach_args *pa);
150static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc);
151static int VBoxGuestNetBSDISR(void *pvState);
152
153/*
154 * Character device file handlers.
155 */
156static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *process);
157static int VBoxGuestNetBSDClose(struct file *fp);
158static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long cmd, void *addr);
159static int VBoxGuestNetBSDIOCtlSlow(struct vboxguest_fdata *fdata, u_long command, void *data);
160static int VBoxGuestNetBSDPoll(struct file *fp, int events);
161
162/*
163 * wsmouse(4) accessops
164 */
165static int VBoxGuestNetBSDWsmEnable(void *cookie);
166static void VBoxGuestNetBSDWsmDisable(void *cookie);
167static int VBoxGuestNetBSDWsmIOCtl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l);
168
169static int VBoxGuestNetBSDSetMouseStatus(vboxguest_softc *sc, uint32_t fStatus);
170
171
172/*********************************************************************************************************************************
173* Global Variables *
174*********************************************************************************************************************************/
175extern struct cfdriver vboxguest_cd; /* CFDRIVER_DECL */
176extern struct cfattach vboxguest_ca; /* CFATTACH_DECL */
177
178/*
179 * The /dev/vboxguest character device entry points.
180 */
181static struct cdevsw g_VBoxGuestNetBSDChrDevSW =
182{
183 .d_open = VBoxGuestNetBSDOpen,
184 .d_close = noclose,
185 .d_read = noread,
186 .d_write = nowrite,
187 .d_ioctl = noioctl,
188 .d_stop = nostop,
189 .d_tty = notty,
190 .d_poll = nopoll,
191 .d_mmap = nommap,
192 .d_kqfilter = nokqfilter,
193};
194
195static const struct fileops vboxguest_fileops = {
196 .fo_read = fbadop_read,
197 .fo_write = fbadop_write,
198 .fo_ioctl = VBoxGuestNetBSDIOCtl,
199 .fo_fcntl = fnullop_fcntl,
200 .fo_poll = VBoxGuestNetBSDPoll,
201 .fo_stat = fbadop_stat,
202 .fo_close = VBoxGuestNetBSDClose,
203 .fo_kqfilter = fnullop_kqfilter,
204 .fo_restart = fnullop_restart
205};
206
207
208const struct wsmouse_accessops vboxguest_wsm_accessops = {
209 VBoxGuestNetBSDWsmEnable,
210 VBoxGuestNetBSDWsmIOCtl,
211 VBoxGuestNetBSDWsmDisable,
212};
213
214
215/*
216 * XXX: wsmux(4) doesn't properly handle the case when two mice with
217 * absolute position events but different calibration data are being
218 * multiplexed. Without GAs the absolute events will be reported
219 * through the tablet ums(4) device with the range of 32k, but with
220 * GAs the absolute events will be reported through the VMM device
221 * (wsmouse at vboxguest) and VMM uses the range of 64k. Which one
222 * responds to the calibration ioctl depends on the order of
223 * attachment. On boot kernel attaches ums first and GAs later, so
224 * it's VMM (this driver) that gets the ioctl. After save/restore the
225 * ums will be detached and re-attached and after that it's ums that
226 * will get the ioctl, but the events (with a wider range) will still
227 * come via the VMM, confusing X, wsmoused, etc. Hack around that by
228 * forcing the range here to match the tablet's range.
229 *
230 * We force VMM range into the ums range and rely on the fact that no
231 * actual calibration is done and both devices are used in the raw
232 * mode. See tpcalib_trans call below.
233 *
234 * Cf. src/VBox/Devices/Input/UsbMouse.cpp
235 */
236#define USB_TABLET_RANGE_MIN 0
237#define USB_TABLET_RANGE_MAX 0x7fff
238
239static struct wsmouse_calibcoords vboxguest_wsm_default_calib = {
240 .minx = USB_TABLET_RANGE_MIN, // VMMDEV_MOUSE_RANGE_MIN,
241 .miny = USB_TABLET_RANGE_MIN, // VMMDEV_MOUSE_RANGE_MIN,
242 .maxx = USB_TABLET_RANGE_MAX, // VMMDEV_MOUSE_RANGE_MAX,
243 .maxy = USB_TABLET_RANGE_MAX, // VMMDEV_MOUSE_RANGE_MAX,
244 .samplelen = WSMOUSE_CALIBCOORDS_RESET,
245};
246
247/** Device extention & session data association structure. */
248static VBOXGUESTDEVEXT g_DevExt;
249
250static vboxguest_softc *g_SC;
251
252/** Reference counter */
253static volatile uint32_t cUsers;
254/** selinfo structure used for polling. */
255static struct selinfo g_SelInfo;
256
257
258CFATTACH_DECL_NEW(vboxguest, sizeof(vboxguest_softc),
259 VBoxGuestNetBSDMatch, VBoxGuestNetBSDAttach, VBoxGuestNetBSDDetach, NULL);
260
261
262static int VBoxGuestNetBSDMatch(device_t parent, cfdata_t match, void *aux)
263{
264 const struct pci_attach_args *pa = aux;
265
266 if (RT_UNLIKELY(g_SC != NULL)) /* should not happen */
267 return 0;
268
269 if ( PCI_VENDOR(pa->pa_id) == VMMDEV_VENDORID
270 && PCI_PRODUCT(pa->pa_id) == VMMDEV_DEVICEID)
271 {
272 return 1;
273 }
274
275 return 0;
276}
277
278
279static void VBoxGuestNetBSDAttach(device_t parent, device_t self, void *aux)
280{
281 int rc = VINF_SUCCESS;
282 int iResId = 0;
283 vboxguest_softc *sc;
284 struct pci_attach_args *pa = aux;
285 bus_space_tag_t iot, memt;
286 bus_space_handle_t ioh, memh;
287 bus_dma_segment_t seg;
288 int ioh_valid, memh_valid;
289
290 KASSERT(g_SC == NULL);
291
292 cUsers = 0;
293
294 aprint_normal(": VirtualBox Guest\n");
295
296 sc = device_private(self);
297 sc->sc_dev = self;
298
299 /*
300 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
301 */
302 rc = RTR0Init(0);
303 if (RT_FAILURE(rc))
304 {
305 LogFunc(("RTR0Init failed.\n"));
306 aprint_error_dev(sc->sc_dev, "RTR0Init failed\n");
307 return;
308 }
309
310 sc->sc_pc = pa->pa_pc;
311
312 /*
313 * Allocate I/O port resource.
314 */
315 ioh_valid = (pci_mapreg_map(pa, PCI_BAR0,
316 PCI_MAPREG_TYPE_IO, 0,
317 &sc->sc_iot, &sc->sc_ioh,
318 &sc->sc_iobase, &sc->sc_iosize) == 0);
319
320 if (ioh_valid)
321 {
322
323 /*
324 * Map the MMIO region.
325 */
326 memh_valid = (pci_mapreg_map(pa, PCI_BAR1,
327 PCI_MAPREG_TYPE_MEM, BUS_SPACE_MAP_LINEAR,
328 &sc->sc_memt, &sc->sc_memh,
329 NULL, &sc->sc_memsize) == 0);
330 if (memh_valid)
331 {
332 /*
333 * Call the common device extension initializer.
334 */
335 rc = VGDrvCommonInitDevExt(&g_DevExt, sc->sc_iobase,
336 NULL /*pvMmioReq*/,
337 bus_space_vaddr(sc->sc_memt, sc->sc_memh),
338 sc->sc_memsize,
339#if ARCH_BITS == 64
340 VBOXOSTYPE_NetBSD_x64,
341#else
342 VBOXOSTYPE_NetBSD,
343#endif
344 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
345 if (RT_SUCCESS(rc))
346 {
347 /*
348 * Add IRQ of VMMDev.
349 */
350 rc = VBoxGuestNetBSDAddIRQ(sc, pa);
351 if (RT_SUCCESS(rc))
352 {
353 sc->vboxguest_state |= VBOXGUEST_STATE_INITOK;
354
355 /*
356 * Read host configuration.
357 */
358 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
359
360 /*
361 * Attach wsmouse.
362 */
363 VBoxGuestNetBSDWsmAttach(sc);
364
365 g_SC = sc;
366 return;
367 }
368 VGDrvCommonDeleteDevExt(&g_DevExt);
369 }
370 else
371 {
372 aprint_error_dev(sc->sc_dev, "init failed\n");
373 }
374 bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize);
375 }
376 else
377 {
378 aprint_error_dev(sc->sc_dev, "MMIO mapping failed\n");
379 }
380 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
381 }
382 else
383 {
384 aprint_error_dev(sc->sc_dev, "IO mapping failed\n");
385 }
386
387 RTR0Term();
388 return;
389}
390
391
392/**
393 * Sets IRQ for VMMDev.
394 *
395 * @returns NetBSD error code.
396 * @param sc Pointer to the state info structure.
397 * @param pa Pointer to the PCI attach arguments.
398 */
399static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *sc, struct pci_attach_args *pa)
400{
401 int iResId = 0;
402 int rc = 0;
403 const char *intrstr;
404#if __NetBSD_Prereq__(6, 99, 39)
405 char intstrbuf[100];
406#endif
407
408 LogFlow((DEVICE_NAME ": %s\n", __func__));
409
410 if (pci_intr_map(pa, &sc->ih))
411 {
412 aprint_error_dev(sc->sc_dev, "couldn't map interrupt.\n");
413 return VERR_DEV_IO_ERROR;
414 }
415
416 intrstr = pci_intr_string(sc->sc_pc, sc->ih
417#if __NetBSD_Prereq__(6, 99, 39)
418 , intstrbuf, sizeof(intstrbuf)
419#endif
420 );
421 aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
422
423 sc->pfnIrqHandler = pci_intr_establish(sc->sc_pc, sc->ih, IPL_BIO, VBoxGuestNetBSDISR, sc);
424 if (sc->pfnIrqHandler == NULL)
425 {
426 aprint_error_dev(sc->sc_dev, "couldn't establish interrupt\n");
427 return VERR_DEV_IO_ERROR;
428 }
429
430 return VINF_SUCCESS;
431}
432
433
434/*
435 * Optionally attach wsmouse(4) device as a child.
436 */
437static void VBoxGuestNetBSDWsmAttach(vboxguest_softc *sc)
438{
439 struct wsmousedev_attach_args am = { &vboxguest_wsm_accessops, sc };
440
441 PVBOXGUESTSESSION session = NULL;
442 VMMDevReqMouseStatus *req = NULL;
443 int rc;
444
445 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &session);
446 if (RT_FAILURE(rc))
447 goto fail;
448
449 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&req, sizeof(*req),
450 VMMDevReq_GetMouseStatus);
451 if (RT_FAILURE(rc))
452 goto fail;
453
454#if __NetBSD_Prereq__(9,99,88)
455 sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint,
456 CFARGS(.iattr = "wsmousedev"));
457#elif __NetBSD_Prereq__(9,99,82)
458 sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint,
459 CFARG_IATTR, "wsmousedev",
460 CFARG_EOL);
461#else
462 sc->sc_wsmousedev = config_found_ia(sc->sc_dev, "wsmousedev",
463 &am, wsmousedevprint);
464#endif
465
466 if (sc->sc_wsmousedev == NULL)
467 goto fail;
468
469 sc->sc_session = session;
470 sc->sc_vmmmousereq = req;
471
472 tpcalib_init(&sc->sc_tpcalib);
473 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
474 &vboxguest_wsm_default_calib, 0, 0);
475 return;
476
477 fail:
478 if (session != NULL)
479 VGDrvCommonCloseSession(&g_DevExt, session);
480 if (req != NULL)
481 VbglR0GRFree((VMMDevRequestHeader *)req);
482}
483
484
485static int VBoxGuestNetBSDDetach(device_t self, int flags)
486{
487 vboxguest_softc *sc;
488 sc = device_private(self);
489
490 LogFlow((DEVICE_NAME ": %s\n", __func__));
491
492 if (cUsers > 0)
493 return EBUSY;
494
495 if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0)
496 return 0;
497
498 /*
499 * Reverse what we did in VBoxGuestNetBSDAttach.
500 */
501 if (sc->sc_vmmmousereq != NULL)
502 VbglR0GRFree((VMMDevRequestHeader *)sc->sc_vmmmousereq);
503
504 VBoxGuestNetBSDRemoveIRQ(sc);
505
506 VGDrvCommonDeleteDevExt(&g_DevExt);
507
508 bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize);
509 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
510
511 RTR0Term();
512
513 return config_detach_children(self, flags);
514}
515
516
517/**
518 * Removes IRQ for VMMDev.
519 *
520 * @param sc Opaque pointer to the state info structure.
521 */
522static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc)
523{
524 LogFlow((DEVICE_NAME ": %s\n", __func__));
525
526 if (sc->pfnIrqHandler)
527 {
528 pci_intr_disestablish(sc->sc_pc, sc->pfnIrqHandler);
529 }
530}
531
532
533/**
534 * Interrupt service routine.
535 *
536 * @returns Whether the interrupt was from VMMDev.
537 * @param pvState Opaque pointer to the device state.
538 */
539static int VBoxGuestNetBSDISR(void *pvState)
540{
541 LogFlow((DEVICE_NAME ": %s: pvState=%p\n", __func__, pvState));
542
543 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
544
545 return fOurIRQ ? 1 : 0;
546}
547
548
549/*
550 * Called by VGDrvCommonISR() if mouse position changed
551 */
552void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
553{
554 vboxguest_softc *sc = g_SC;
555
556 LogFlow((DEVICE_NAME ": %s\n", __func__));
557
558 /*
559 * Wake up poll waiters.
560 */
561 selnotify(&g_SelInfo, 0, 0);
562
563 if (sc->sc_vmmmousereq != NULL) {
564 int x, y;
565 int rc;
566
567 sc->sc_vmmmousereq->mouseFeatures = 0;
568 sc->sc_vmmmousereq->pointerXPos = 0;
569 sc->sc_vmmmousereq->pointerYPos = 0;
570
571 rc = VbglR0GRPerform(&sc->sc_vmmmousereq->header);
572 if (RT_FAILURE(rc))
573 return;
574
575 /* XXX: see the comment for vboxguest_wsm_default_calib */
576 int rawx = (unsigned)sc->sc_vmmmousereq->pointerXPos >> 1;
577 int rawy = (unsigned)sc->sc_vmmmousereq->pointerYPos >> 1;
578 tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
579
580 wsmouse_input(sc->sc_wsmousedev,
581 0, /* buttons */
582 x, y,
583 0, 0, /* z, w */
584 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
585 }
586}
587
588
589bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
590{
591 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
592 return false;
593}
594
595
596static int VBoxGuestNetBSDSetMouseStatus(vboxguest_softc *sc, uint32_t fStatus)
597{
598 VBGLIOCSETMOUSESTATUS Req;
599 int rc;
600
601 VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
602 Req.u.In.fStatus = fStatus;
603 rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS,
604 &g_DevExt,
605 sc->sc_session,
606 &Req.Hdr, sizeof(Req));
607 if (RT_SUCCESS(rc))
608 rc = Req.Hdr.rc;
609
610 return rc;
611}
612
613
614static int
615VBoxGuestNetBSDWsmEnable(void *cookie)
616{
617 vboxguest_softc *sc = cookie;
618 int rc;
619
620 rc = VBoxGuestNetBSDSetMouseStatus(sc, VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
621 | VMMDEV_MOUSE_NEW_PROTOCOL);
622 if (RT_FAILURE(rc))
623 return RTErrConvertToErrno(rc);
624
625 return 0;
626}
627
628
629static void
630VBoxGuestNetBSDWsmDisable(void *cookie)
631{
632 vboxguest_softc *sc = cookie;
633 VBoxGuestNetBSDSetMouseStatus(sc, 0);
634}
635
636
637static int
638VBoxGuestNetBSDWsmIOCtl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l)
639{
640 vboxguest_softc *sc = cookie;
641
642 switch (cmd) {
643 case WSMOUSEIO_GTYPE:
644 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
645 break;
646
647 case WSMOUSEIO_SCALIBCOORDS:
648 case WSMOUSEIO_GCALIBCOORDS:
649 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
650
651 default:
652 return EPASSTHROUGH;
653 }
654 return 0;
655}
656
657
658/**
659 * File open handler
660 *
661 */
662static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *pLwp)
663{
664 vboxguest_softc *sc;
665 struct vboxguest_fdata *fdata;
666 file_t *fp;
667 int fd, error;
668
669 LogFlow((DEVICE_NAME ": %s\n", __func__));
670
671 if ((sc = device_lookup_private(&vboxguest_cd, minor(device))) == NULL)
672 {
673 printf("device_lookup_private failed\n");
674 return (ENXIO);
675 }
676
677 if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0)
678 {
679 aprint_error_dev(sc->sc_dev, "device not configured\n");
680 return (ENXIO);
681 }
682
683 fdata = kmem_alloc(sizeof(*fdata), KM_SLEEP);
684 if (fdata != NULL)
685 {
686 fdata->sc = sc;
687
688 error = fd_allocfile(&fp, &fd);
689 if (error == 0)
690 {
691 /*
692 * Create a new session.
693 */
694 struct kauth_cred *pCred = pLwp->l_cred;
695 int fHaveCred = (pCred != NULL && pCred != NOCRED && pCred != FSCRED);
696 uint32_t fRequestor;
697 int fIsWheel;
698 int rc;
699
700 fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
701
702 /* uid */
703 if (fHaveCred && kauth_cred_geteuid(pCred) == (uid_t)0)
704 fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
705 else
706 fRequestor |= VMMDEV_REQUESTOR_USR_USER;
707
708 /* gid */
709 if (fHaveCred
710 && (kauth_cred_getegid(pCred) == (gid_t)0
711 || (kauth_cred_ismember_gid(pCred, 0, &fIsWheel) == 0
712 && fIsWheel)))
713 fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
714
715#if 0 /** @todo implement /dev/vboxuser */
716 if (!fUnrestricted)
717 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
718#else
719 fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE;
720#endif
721
722 /** @todo can we find out if pLwp is on the console? */
723 fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW;
724
725 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &fdata->session);
726 if (RT_SUCCESS(rc))
727 {
728 ASMAtomicIncU32(&cUsers);
729 return fd_clone(fp, fd, flags, &vboxguest_fileops, fdata);
730 }
731
732 aprint_error_dev(sc->sc_dev, "VBox session creation failed\n");
733 closef(fp); /* ??? */
734 error = RTErrConvertToErrno(rc);
735 }
736 kmem_free(fdata, sizeof(*fdata));
737 }
738 else
739 error = ENOMEM;
740 return error;
741}
742
743/**
744 * File close handler
745 *
746 */
747static int VBoxGuestNetBSDClose(struct file *fp)
748{
749 struct vboxguest_fdata *fdata = fp->f_data;
750 vboxguest_softc *sc = fdata->sc;
751
752 LogFlow((DEVICE_NAME ": %s\n", __func__));
753
754 VGDrvCommonCloseSession(&g_DevExt, fdata->session);
755 ASMAtomicDecU32(&cUsers);
756
757 kmem_free(fdata, sizeof(*fdata));
758
759 return 0;
760}
761
762/**
763 * IOCTL handler
764 *
765 */
766static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long command, void *data)
767{
768 struct vboxguest_fdata *fdata = fp->f_data;
769
770 if (VBGL_IOCTL_IS_FAST(command))
771 return VGDrvCommonIoCtlFast(command, &g_DevExt, fdata->session);
772
773 return VBoxGuestNetBSDIOCtlSlow(fdata, command, data);
774}
775
776static int VBoxGuestNetBSDIOCtlSlow(struct vboxguest_fdata *fdata, u_long command, void *data)
777{
778 vboxguest_softc *sc = fdata->sc;
779 size_t cbReq = IOCPARM_LEN(command);
780 PVBGLREQHDR pHdr = NULL;
781 void *pvUser = NULL;
782 int err, rc;
783
784 LogFlow(("%s: command=%#lx data=%p\n", __func__, command, data));
785
786 /*
787 * Buffered request?
788 */
789 if ((command & IOC_DIRMASK) == IOC_INOUT)
790 {
791 /* will be validated by VGDrvCommonIoCtl() */
792 pHdr = (PVBGLREQHDR)data;
793 }
794
795 /*
796 * Big unbuffered request? "data" is the userland pointer.
797 */
798 else if ((command & IOC_DIRMASK) == IOC_VOID && cbReq != 0)
799 {
800 /*
801 * Read the header, validate it and figure out how much that
802 * needs to be buffered.
803 */
804 VBGLREQHDR Hdr;
805
806 if (RT_UNLIKELY(cbReq < sizeof(Hdr)))
807 return ENOTTY;
808
809 pvUser = data;
810 err = copyin(pvUser, &Hdr, sizeof(Hdr));
811 if (RT_UNLIKELY(err != 0))
812 return err;
813
814 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
815 return ENOTTY;
816
817 if (cbReq > 16 * _1M)
818 return EINVAL;
819
820 if (Hdr.cbOut == 0)
821 Hdr.cbOut = Hdr.cbIn;
822
823 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbIn > cbReq
824 || Hdr.cbOut < sizeof(Hdr) || Hdr.cbOut > cbReq))
825 return EINVAL;
826
827 /*
828 * Allocate buffer and copy in the data.
829 */
830 cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
831
832 pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq);
833 if (RT_UNLIKELY(pHdr == NULL))
834 {
835 LogRel(("%s: command=%#lx data=%p: unable to allocate %zu bytes\n",
836 __func__, command, data, cbReq));
837 return ENOMEM;
838 }
839
840 err = copyin(pvUser, pHdr, Hdr.cbIn);
841 if (err != 0)
842 {
843 RTMemTmpFree(pHdr);
844 return err;
845 }
846
847 if (Hdr.cbIn < cbReq)
848 memset((uint8_t *)pHdr + Hdr.cbIn, '\0', cbReq - Hdr.cbIn);
849 }
850
851 /*
852 * Process the IOCtl.
853 */
854 rc = VGDrvCommonIoCtl(command, &g_DevExt, fdata->session, pHdr, cbReq);
855 if (RT_SUCCESS(rc))
856 {
857 err = 0;
858
859 /*
860 * If unbuffered, copy back the result before returning.
861 */
862 if (pvUser != NULL)
863 {
864 size_t cbOut = pHdr->cbOut;
865 if (cbOut > cbReq)
866 {
867 LogRel(("%s: command=%#lx data=%p: too much output: %zu > %zu\n",
868 __func__, command, data, cbOut, cbReq));
869 cbOut = cbReq;
870 }
871
872 err = copyout(pHdr, pvUser, cbOut);
873 RTMemTmpFree(pHdr);
874 }
875 }
876 else
877 {
878 LogRel(("%s: command=%#lx data=%p: error %Rrc\n",
879 __func__, command, data, rc));
880
881 if (pvUser != NULL)
882 RTMemTmpFree(pHdr);
883
884 err = RTErrConvertToErrno(rc);
885 }
886
887 return err;
888}
889
890static int VBoxGuestNetBSDPoll(struct file *fp, int events)
891{
892 struct vboxguest_fdata *fdata = fp->f_data;
893 vboxguest_softc *sc = fdata->sc;
894
895 int rc = 0;
896 int events_processed;
897
898 uint32_t u32CurSeq;
899
900 LogFlow((DEVICE_NAME ": %s\n", __func__));
901
902 u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
903 if (fdata->session->u32MousePosChangedSeq != u32CurSeq)
904 {
905 events_processed = events & (POLLIN | POLLRDNORM);
906 fdata->session->u32MousePosChangedSeq = u32CurSeq;
907 }
908 else
909 {
910 events_processed = 0;
911
912 selrecord(curlwp, &g_SelInfo);
913 }
914
915 return events_processed;
916}
917
918
919/**
920 * @note This code is duplicated on other platforms with variations, so please
921 * keep them all up to date when making changes!
922 */
923int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
924{
925 /*
926 * Simple request validation (common code does the rest).
927 */
928 int rc;
929 if ( RT_VALID_PTR(pReqHdr)
930 && cbReq >= sizeof(*pReqHdr))
931 {
932 /*
933 * All requests except the connect one requires a valid session.
934 */
935 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
936 if (pSession)
937 {
938 if ( RT_VALID_PTR(pSession)
939 && pSession->pDevExt == &g_DevExt)
940 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
941 else
942 rc = VERR_INVALID_HANDLE;
943 }
944 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
945 {
946 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
947 if (RT_SUCCESS(rc))
948 {
949 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
950 if (RT_FAILURE(rc))
951 VGDrvCommonCloseSession(&g_DevExt, pSession);
952 }
953 }
954 else
955 rc = VERR_INVALID_HANDLE;
956 }
957 else
958 rc = VERR_INVALID_POINTER;
959 return rc;
960}
961
962
963MODULE(MODULE_CLASS_DRIVER, vboxguest, "pci");
964
965/*
966 * XXX: See netbsd/vboxguest.ioconf for the details.
967*/
968#if 0
969#include "ioconf.c"
970#else
971
972static const struct cfiattrdata wsmousedevcf_iattrdata = {
973 "wsmousedev", 1, {
974 { "mux", "0", 0 },
975 }
976};
977
978/* device vboxguest: wsmousedev */
979static const struct cfiattrdata * const vboxguest_attrs[] = { &wsmousedevcf_iattrdata, NULL };
980CFDRIVER_DECL(vboxguest, DV_DULL, vboxguest_attrs);
981
982static struct cfdriver * const cfdriver_ioconf_vboxguest[] = {
983 &vboxguest_cd, NULL
984};
985
986
987static const struct cfparent vboxguest_pspec = {
988 "pci", "pci", DVUNIT_ANY
989};
990static int vboxguest_loc[] = { -1, -1 };
991
992
993static const struct cfparent wsmousedev_pspec = {
994 "wsmousedev", "vboxguest", DVUNIT_ANY
995};
996static int wsmousedev_loc[] = { 0 };
997
998
999static struct cfdata cfdata_ioconf_vboxguest[] = {
1000 /* vboxguest0 at pci? dev ? function ? */
1001 {
1002 .cf_name = "vboxguest",
1003 .cf_atname = "vboxguest",
1004 .cf_unit = 0, /* Only unit 0 is ever used */
1005 .cf_fstate = FSTATE_NOTFOUND,
1006 .cf_loc = vboxguest_loc,
1007 .cf_flags = 0,
1008 .cf_pspec = &vboxguest_pspec,
1009 },
1010
1011 /* wsmouse* at vboxguest? */
1012 { "wsmouse", "wsmouse", 0, FSTATE_STAR, wsmousedev_loc, 0, &wsmousedev_pspec },
1013
1014 { NULL, NULL, 0, 0, NULL, 0, NULL }
1015};
1016
1017static struct cfattach * const vboxguest_cfattachinit[] = {
1018 &vboxguest_ca, NULL
1019};
1020
1021static const struct cfattachinit cfattach_ioconf_vboxguest[] = {
1022 { "vboxguest", vboxguest_cfattachinit },
1023 { NULL, NULL }
1024};
1025#endif
1026
1027
1028static int
1029vboxguest_modcmd(modcmd_t cmd, void *opaque)
1030{
1031 devmajor_t bmajor, cmajor;
1032#if !__NetBSD_Prereq__(8,99,46)
1033 register_t retval;
1034#endif
1035 int error;
1036
1037 LogFlow((DEVICE_NAME ": %s\n", __func__));
1038
1039 switch (cmd)
1040 {
1041 case MODULE_CMD_INIT:
1042 error = config_init_component(cfdriver_ioconf_vboxguest,
1043 cfattach_ioconf_vboxguest,
1044 cfdata_ioconf_vboxguest);
1045 if (error)
1046 break;
1047
1048 bmajor = cmajor = NODEVMAJOR;
1049 error = devsw_attach("vboxguest",
1050 NULL, &bmajor,
1051 &g_VBoxGuestNetBSDChrDevSW, &cmajor);
1052 if (error)
1053 {
1054 if (error == EEXIST)
1055 error = 0; /* maybe built-in ... improve eventually */
1056 else
1057 break;
1058 }
1059
1060 error = do_sys_mknod(curlwp, "/dev/vboxguest",
1061 0666|S_IFCHR, makedev(cmajor, 0),
1062#if !__NetBSD_Prereq__(8,99,46)
1063 &retval,
1064#endif
1065 UIO_SYSSPACE);
1066 if (error == EEXIST) {
1067 error = 0;
1068
1069 /*
1070 * Since NetBSD doesn't yet have a major reserved for
1071 * vboxguest, the (first free) major we get will
1072 * change when new devices are added, so an existing
1073 * /dev/vboxguest may now point to some other device,
1074 * creating confusion (tripped me up a few times).
1075 */
1076 aprint_normal("vboxguest: major %d:"
1077 " check existing /dev/vboxguest\n", cmajor);
1078 }
1079 break;
1080
1081 case MODULE_CMD_FINI:
1082 error = config_fini_component(cfdriver_ioconf_vboxguest,
1083 cfattach_ioconf_vboxguest,
1084 cfdata_ioconf_vboxguest);
1085 if (error)
1086 break;
1087
1088 devsw_detach(NULL, &g_VBoxGuestNetBSDChrDevSW);
1089 break;
1090
1091 default:
1092 return ENOTTY;
1093 }
1094 return error;
1095}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use