VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $Id: VBoxGuest-netbsd.c 98103 2023-01-17 14:15:46Z 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 bus_space_vaddr(sc->sc_memt, sc->sc_memh),
337 sc->sc_memsize,
338#if ARCH_BITS == 64
339 VBOXOSTYPE_NetBSD_x64,
340#else
341 VBOXOSTYPE_NetBSD,
342#endif
343 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
344 if (RT_SUCCESS(rc))
345 {
346 /*
347 * Add IRQ of VMMDev.
348 */
349 rc = VBoxGuestNetBSDAddIRQ(sc, pa);
350 if (RT_SUCCESS(rc))
351 {
352 sc->vboxguest_state |= VBOXGUEST_STATE_INITOK;
353
354 /*
355 * Read host configuration.
356 */
357 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
358
359 /*
360 * Attach wsmouse.
361 */
362 VBoxGuestNetBSDWsmAttach(sc);
363
364 g_SC = sc;
365 return;
366 }
367 VGDrvCommonDeleteDevExt(&g_DevExt);
368 }
369 else
370 {
371 aprint_error_dev(sc->sc_dev, "init failed\n");
372 }
373 bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize);
374 }
375 else
376 {
377 aprint_error_dev(sc->sc_dev, "MMIO mapping failed\n");
378 }
379 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
380 }
381 else
382 {
383 aprint_error_dev(sc->sc_dev, "IO mapping failed\n");
384 }
385
386 RTR0Term();
387 return;
388}
389
390
391/**
392 * Sets IRQ for VMMDev.
393 *
394 * @returns NetBSD error code.
395 * @param sc Pointer to the state info structure.
396 * @param pa Pointer to the PCI attach arguments.
397 */
398static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *sc, struct pci_attach_args *pa)
399{
400 int iResId = 0;
401 int rc = 0;
402 const char *intrstr;
403#if __NetBSD_Prereq__(6, 99, 39)
404 char intstrbuf[100];
405#endif
406
407 LogFlow((DEVICE_NAME ": %s\n", __func__));
408
409 if (pci_intr_map(pa, &sc->ih))
410 {
411 aprint_error_dev(sc->sc_dev, "couldn't map interrupt.\n");
412 return VERR_DEV_IO_ERROR;
413 }
414
415 intrstr = pci_intr_string(sc->sc_pc, sc->ih
416#if __NetBSD_Prereq__(6, 99, 39)
417 , intstrbuf, sizeof(intstrbuf)
418#endif
419 );
420 aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
421
422 sc->pfnIrqHandler = pci_intr_establish(sc->sc_pc, sc->ih, IPL_BIO, VBoxGuestNetBSDISR, sc);
423 if (sc->pfnIrqHandler == NULL)
424 {
425 aprint_error_dev(sc->sc_dev, "couldn't establish interrupt\n");
426 return VERR_DEV_IO_ERROR;
427 }
428
429 return VINF_SUCCESS;
430}
431
432
433/*
434 * Optionally attach wsmouse(4) device as a child.
435 */
436static void VBoxGuestNetBSDWsmAttach(vboxguest_softc *sc)
437{
438 struct wsmousedev_attach_args am = { &vboxguest_wsm_accessops, sc };
439
440 PVBOXGUESTSESSION session = NULL;
441 VMMDevReqMouseStatus *req = NULL;
442 int rc;
443
444 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &session);
445 if (RT_FAILURE(rc))
446 goto fail;
447
448 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&req, sizeof(*req),
449 VMMDevReq_GetMouseStatus);
450 if (RT_FAILURE(rc))
451 goto fail;
452
453#if __NetBSD_Prereq__(9,99,88)
454 sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint,
455 CFARGS(.iattr = "wsmousedev"));
456#elif __NetBSD_Prereq__(9,99,82)
457 sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint,
458 CFARG_IATTR, "wsmousedev",
459 CFARG_EOL);
460#else
461 sc->sc_wsmousedev = config_found_ia(sc->sc_dev, "wsmousedev",
462 &am, wsmousedevprint);
463#endif
464
465 if (sc->sc_wsmousedev == NULL)
466 goto fail;
467
468 sc->sc_session = session;
469 sc->sc_vmmmousereq = req;
470
471 tpcalib_init(&sc->sc_tpcalib);
472 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
473 &vboxguest_wsm_default_calib, 0, 0);
474 return;
475
476 fail:
477 if (session != NULL)
478 VGDrvCommonCloseSession(&g_DevExt, session);
479 if (req != NULL)
480 VbglR0GRFree((VMMDevRequestHeader *)req);
481}
482
483
484static int VBoxGuestNetBSDDetach(device_t self, int flags)
485{
486 vboxguest_softc *sc;
487 sc = device_private(self);
488
489 LogFlow((DEVICE_NAME ": %s\n", __func__));
490
491 if (cUsers > 0)
492 return EBUSY;
493
494 if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0)
495 return 0;
496
497 /*
498 * Reverse what we did in VBoxGuestNetBSDAttach.
499 */
500 if (sc->sc_vmmmousereq != NULL)
501 VbglR0GRFree((VMMDevRequestHeader *)sc->sc_vmmmousereq);
502
503 VBoxGuestNetBSDRemoveIRQ(sc);
504
505 VGDrvCommonDeleteDevExt(&g_DevExt);
506
507 bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize);
508 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
509
510 RTR0Term();
511
512 return config_detach_children(self, flags);
513}
514
515
516/**
517 * Removes IRQ for VMMDev.
518 *
519 * @param sc Opaque pointer to the state info structure.
520 */
521static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc)
522{
523 LogFlow((DEVICE_NAME ": %s\n", __func__));
524
525 if (sc->pfnIrqHandler)
526 {
527 pci_intr_disestablish(sc->sc_pc, sc->pfnIrqHandler);
528 }
529}
530
531
532/**
533 * Interrupt service routine.
534 *
535 * @returns Whether the interrupt was from VMMDev.
536 * @param pvState Opaque pointer to the device state.
537 */
538static int VBoxGuestNetBSDISR(void *pvState)
539{
540 LogFlow((DEVICE_NAME ": %s: pvState=%p\n", __func__, pvState));
541
542 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
543
544 return fOurIRQ ? 1 : 0;
545}
546
547
548/*
549 * Called by VGDrvCommonISR() if mouse position changed
550 */
551void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
552{
553 vboxguest_softc *sc = g_SC;
554
555 LogFlow((DEVICE_NAME ": %s\n", __func__));
556
557 /*
558 * Wake up poll waiters.
559 */
560 selnotify(&g_SelInfo, 0, 0);
561
562 if (sc->sc_vmmmousereq != NULL) {
563 int x, y;
564 int rc;
565
566 sc->sc_vmmmousereq->mouseFeatures = 0;
567 sc->sc_vmmmousereq->pointerXPos = 0;
568 sc->sc_vmmmousereq->pointerYPos = 0;
569
570 rc = VbglR0GRPerform(&sc->sc_vmmmousereq->header);
571 if (RT_FAILURE(rc))
572 return;
573
574 /* XXX: see the comment for vboxguest_wsm_default_calib */
575 int rawx = (unsigned)sc->sc_vmmmousereq->pointerXPos >> 1;
576 int rawy = (unsigned)sc->sc_vmmmousereq->pointerYPos >> 1;
577 tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
578
579 wsmouse_input(sc->sc_wsmousedev,
580 0, /* buttons */
581 x, y,
582 0, 0, /* z, w */
583 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
584 }
585}
586
587
588bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
589{
590 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
591 return false;
592}
593
594
595static int VBoxGuestNetBSDSetMouseStatus(vboxguest_softc *sc, uint32_t fStatus)
596{
597 VBGLIOCSETMOUSESTATUS Req;
598 int rc;
599
600 VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
601 Req.u.In.fStatus = fStatus;
602 rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS,
603 &g_DevExt,
604 sc->sc_session,
605 &Req.Hdr, sizeof(Req));
606 if (RT_SUCCESS(rc))
607 rc = Req.Hdr.rc;
608
609 return rc;
610}
611
612
613static int
614VBoxGuestNetBSDWsmEnable(void *cookie)
615{
616 vboxguest_softc *sc = cookie;
617 int rc;
618
619 rc = VBoxGuestNetBSDSetMouseStatus(sc, VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
620 | VMMDEV_MOUSE_NEW_PROTOCOL);
621 if (RT_FAILURE(rc))
622 return RTErrConvertToErrno(rc);
623
624 return 0;
625}
626
627
628static void
629VBoxGuestNetBSDWsmDisable(void *cookie)
630{
631 vboxguest_softc *sc = cookie;
632 VBoxGuestNetBSDSetMouseStatus(sc, 0);
633}
634
635
636static int
637VBoxGuestNetBSDWsmIOCtl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l)
638{
639 vboxguest_softc *sc = cookie;
640
641 switch (cmd) {
642 case WSMOUSEIO_GTYPE:
643 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
644 break;
645
646 case WSMOUSEIO_SCALIBCOORDS:
647 case WSMOUSEIO_GCALIBCOORDS:
648 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
649
650 default:
651 return EPASSTHROUGH;
652 }
653 return 0;
654}
655
656
657/**
658 * File open handler
659 *
660 */
661static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *pLwp)
662{
663 vboxguest_softc *sc;
664 struct vboxguest_fdata *fdata;
665 file_t *fp;
666 int fd, error;
667
668 LogFlow((DEVICE_NAME ": %s\n", __func__));
669
670 if ((sc = device_lookup_private(&vboxguest_cd, minor(device))) == NULL)
671 {
672 printf("device_lookup_private failed\n");
673 return (ENXIO);
674 }
675
676 if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0)
677 {
678 aprint_error_dev(sc->sc_dev, "device not configured\n");
679 return (ENXIO);
680 }
681
682 fdata = kmem_alloc(sizeof(*fdata), KM_SLEEP);
683 if (fdata != NULL)
684 {
685 fdata->sc = sc;
686
687 error = fd_allocfile(&fp, &fd);
688 if (error == 0)
689 {
690 /*
691 * Create a new session.
692 */
693 struct kauth_cred *pCred = pLwp->l_cred;
694 int fHaveCred = (pCred != NULL && pCred != NOCRED && pCred != FSCRED);
695 uint32_t fRequestor;
696 int fIsWheel;
697 int rc;
698
699 fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
700
701 /* uid */
702 if (fHaveCred && kauth_cred_geteuid(pCred) == (uid_t)0)
703 fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
704 else
705 fRequestor |= VMMDEV_REQUESTOR_USR_USER;
706
707 /* gid */
708 if (fHaveCred
709 && (kauth_cred_getegid(pCred) == (gid_t)0
710 || (kauth_cred_ismember_gid(pCred, 0, &fIsWheel) == 0
711 && fIsWheel)))
712 fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
713
714#if 0 /** @todo implement /dev/vboxuser */
715 if (!fUnrestricted)
716 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
717#else
718 fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE;
719#endif
720
721 /** @todo can we find out if pLwp is on the console? */
722 fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW;
723
724 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &fdata->session);
725 if (RT_SUCCESS(rc))
726 {
727 ASMAtomicIncU32(&cUsers);
728 return fd_clone(fp, fd, flags, &vboxguest_fileops, fdata);
729 }
730
731 aprint_error_dev(sc->sc_dev, "VBox session creation failed\n");
732 closef(fp); /* ??? */
733 error = RTErrConvertToErrno(rc);
734 }
735 kmem_free(fdata, sizeof(*fdata));
736 }
737 else
738 error = ENOMEM;
739 return error;
740}
741
742/**
743 * File close handler
744 *
745 */
746static int VBoxGuestNetBSDClose(struct file *fp)
747{
748 struct vboxguest_fdata *fdata = fp->f_data;
749 vboxguest_softc *sc = fdata->sc;
750
751 LogFlow((DEVICE_NAME ": %s\n", __func__));
752
753 VGDrvCommonCloseSession(&g_DevExt, fdata->session);
754 ASMAtomicDecU32(&cUsers);
755
756 kmem_free(fdata, sizeof(*fdata));
757
758 return 0;
759}
760
761/**
762 * IOCTL handler
763 *
764 */
765static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long command, void *data)
766{
767 struct vboxguest_fdata *fdata = fp->f_data;
768
769 if (VBGL_IOCTL_IS_FAST(command))
770 return VGDrvCommonIoCtlFast(command, &g_DevExt, fdata->session);
771
772 return VBoxGuestNetBSDIOCtlSlow(fdata, command, data);
773}
774
775static int VBoxGuestNetBSDIOCtlSlow(struct vboxguest_fdata *fdata, u_long command, void *data)
776{
777 vboxguest_softc *sc = fdata->sc;
778 size_t cbReq = IOCPARM_LEN(command);
779 PVBGLREQHDR pHdr = NULL;
780 void *pvUser = NULL;
781 int err, rc;
782
783 LogFlow(("%s: command=%#lx data=%p\n", __func__, command, data));
784
785 /*
786 * Buffered request?
787 */
788 if ((command & IOC_DIRMASK) == IOC_INOUT)
789 {
790 /* will be validated by VGDrvCommonIoCtl() */
791 pHdr = (PVBGLREQHDR)data;
792 }
793
794 /*
795 * Big unbuffered request? "data" is the userland pointer.
796 */
797 else if ((command & IOC_DIRMASK) == IOC_VOID && cbReq != 0)
798 {
799 /*
800 * Read the header, validate it and figure out how much that
801 * needs to be buffered.
802 */
803 VBGLREQHDR Hdr;
804
805 if (RT_UNLIKELY(cbReq < sizeof(Hdr)))
806 return ENOTTY;
807
808 pvUser = data;
809 err = copyin(pvUser, &Hdr, sizeof(Hdr));
810 if (RT_UNLIKELY(err != 0))
811 return err;
812
813 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
814 return ENOTTY;
815
816 if (cbReq > 16 * _1M)
817 return EINVAL;
818
819 if (Hdr.cbOut == 0)
820 Hdr.cbOut = Hdr.cbIn;
821
822 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbIn > cbReq
823 || Hdr.cbOut < sizeof(Hdr) || Hdr.cbOut > cbReq))
824 return EINVAL;
825
826 /*
827 * Allocate buffer and copy in the data.
828 */
829 cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
830
831 pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq);
832 if (RT_UNLIKELY(pHdr == NULL))
833 {
834 LogRel(("%s: command=%#lx data=%p: unable to allocate %zu bytes\n",
835 __func__, command, data, cbReq));
836 return ENOMEM;
837 }
838
839 err = copyin(pvUser, pHdr, Hdr.cbIn);
840 if (err != 0)
841 {
842 RTMemTmpFree(pHdr);
843 return err;
844 }
845
846 if (Hdr.cbIn < cbReq)
847 memset((uint8_t *)pHdr + Hdr.cbIn, '\0', cbReq - Hdr.cbIn);
848 }
849
850 /*
851 * Process the IOCtl.
852 */
853 rc = VGDrvCommonIoCtl(command, &g_DevExt, fdata->session, pHdr, cbReq);
854 if (RT_SUCCESS(rc))
855 {
856 err = 0;
857
858 /*
859 * If unbuffered, copy back the result before returning.
860 */
861 if (pvUser != NULL)
862 {
863 size_t cbOut = pHdr->cbOut;
864 if (cbOut > cbReq)
865 {
866 LogRel(("%s: command=%#lx data=%p: too much output: %zu > %zu\n",
867 __func__, command, data, cbOut, cbReq));
868 cbOut = cbReq;
869 }
870
871 err = copyout(pHdr, pvUser, cbOut);
872 RTMemTmpFree(pHdr);
873 }
874 }
875 else
876 {
877 LogRel(("%s: command=%#lx data=%p: error %Rrc\n",
878 __func__, command, data, rc));
879
880 if (pvUser != NULL)
881 RTMemTmpFree(pHdr);
882
883 err = RTErrConvertToErrno(rc);
884 }
885
886 return err;
887}
888
889static int VBoxGuestNetBSDPoll(struct file *fp, int events)
890{
891 struct vboxguest_fdata *fdata = fp->f_data;
892 vboxguest_softc *sc = fdata->sc;
893
894 int rc = 0;
895 int events_processed;
896
897 uint32_t u32CurSeq;
898
899 LogFlow((DEVICE_NAME ": %s\n", __func__));
900
901 u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
902 if (fdata->session->u32MousePosChangedSeq != u32CurSeq)
903 {
904 events_processed = events & (POLLIN | POLLRDNORM);
905 fdata->session->u32MousePosChangedSeq = u32CurSeq;
906 }
907 else
908 {
909 events_processed = 0;
910
911 selrecord(curlwp, &g_SelInfo);
912 }
913
914 return events_processed;
915}
916
917
918/**
919 * @note This code is duplicated on other platforms with variations, so please
920 * keep them all up to date when making changes!
921 */
922int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
923{
924 /*
925 * Simple request validation (common code does the rest).
926 */
927 int rc;
928 if ( RT_VALID_PTR(pReqHdr)
929 && cbReq >= sizeof(*pReqHdr))
930 {
931 /*
932 * All requests except the connect one requires a valid session.
933 */
934 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
935 if (pSession)
936 {
937 if ( RT_VALID_PTR(pSession)
938 && pSession->pDevExt == &g_DevExt)
939 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
940 else
941 rc = VERR_INVALID_HANDLE;
942 }
943 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
944 {
945 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
946 if (RT_SUCCESS(rc))
947 {
948 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
949 if (RT_FAILURE(rc))
950 VGDrvCommonCloseSession(&g_DevExt, pSession);
951 }
952 }
953 else
954 rc = VERR_INVALID_HANDLE;
955 }
956 else
957 rc = VERR_INVALID_POINTER;
958 return rc;
959}
960
961
962MODULE(MODULE_CLASS_DRIVER, vboxguest, "pci");
963
964/*
965 * XXX: See netbsd/vboxguest.ioconf for the details.
966*/
967#if 0
968#include "ioconf.c"
969#else
970
971static const struct cfiattrdata wsmousedevcf_iattrdata = {
972 "wsmousedev", 1, {
973 { "mux", "0", 0 },
974 }
975};
976
977/* device vboxguest: wsmousedev */
978static const struct cfiattrdata * const vboxguest_attrs[] = { &wsmousedevcf_iattrdata, NULL };
979CFDRIVER_DECL(vboxguest, DV_DULL, vboxguest_attrs);
980
981static struct cfdriver * const cfdriver_ioconf_vboxguest[] = {
982 &vboxguest_cd, NULL
983};
984
985
986static const struct cfparent vboxguest_pspec = {
987 "pci", "pci", DVUNIT_ANY
988};
989static int vboxguest_loc[] = { -1, -1 };
990
991
992static const struct cfparent wsmousedev_pspec = {
993 "wsmousedev", "vboxguest", DVUNIT_ANY
994};
995static int wsmousedev_loc[] = { 0 };
996
997
998static struct cfdata cfdata_ioconf_vboxguest[] = {
999 /* vboxguest0 at pci? dev ? function ? */
1000 {
1001 .cf_name = "vboxguest",
1002 .cf_atname = "vboxguest",
1003 .cf_unit = 0, /* Only unit 0 is ever used */
1004 .cf_fstate = FSTATE_NOTFOUND,
1005 .cf_loc = vboxguest_loc,
1006 .cf_flags = 0,
1007 .cf_pspec = &vboxguest_pspec,
1008 },
1009
1010 /* wsmouse* at vboxguest? */
1011 { "wsmouse", "wsmouse", 0, FSTATE_STAR, wsmousedev_loc, 0, &wsmousedev_pspec },
1012
1013 { NULL, NULL, 0, 0, NULL, 0, NULL }
1014};
1015
1016static struct cfattach * const vboxguest_cfattachinit[] = {
1017 &vboxguest_ca, NULL
1018};
1019
1020static const struct cfattachinit cfattach_ioconf_vboxguest[] = {
1021 { "vboxguest", vboxguest_cfattachinit },
1022 { NULL, NULL }
1023};
1024#endif
1025
1026
1027static int
1028vboxguest_modcmd(modcmd_t cmd, void *opaque)
1029{
1030 devmajor_t bmajor, cmajor;
1031#if !__NetBSD_Prereq__(8,99,46)
1032 register_t retval;
1033#endif
1034 int error;
1035
1036 LogFlow((DEVICE_NAME ": %s\n", __func__));
1037
1038 switch (cmd)
1039 {
1040 case MODULE_CMD_INIT:
1041 error = config_init_component(cfdriver_ioconf_vboxguest,
1042 cfattach_ioconf_vboxguest,
1043 cfdata_ioconf_vboxguest);
1044 if (error)
1045 break;
1046
1047 bmajor = cmajor = NODEVMAJOR;
1048 error = devsw_attach("vboxguest",
1049 NULL, &bmajor,
1050 &g_VBoxGuestNetBSDChrDevSW, &cmajor);
1051 if (error)
1052 {
1053 if (error == EEXIST)
1054 error = 0; /* maybe built-in ... improve eventually */
1055 else
1056 break;
1057 }
1058
1059 error = do_sys_mknod(curlwp, "/dev/vboxguest",
1060 0666|S_IFCHR, makedev(cmajor, 0),
1061#if !__NetBSD_Prereq__(8,99,46)
1062 &retval,
1063#endif
1064 UIO_SYSSPACE);
1065 if (error == EEXIST) {
1066 error = 0;
1067
1068 /*
1069 * Since NetBSD doesn't yet have a major reserved for
1070 * vboxguest, the (first free) major we get will
1071 * change when new devices are added, so an existing
1072 * /dev/vboxguest may now point to some other device,
1073 * creating confusion (tripped me up a few times).
1074 */
1075 aprint_normal("vboxguest: major %d:"
1076 " check existing /dev/vboxguest\n", cmajor);
1077 }
1078 break;
1079
1080 case MODULE_CMD_FINI:
1081 error = config_fini_component(cfdriver_ioconf_vboxguest,
1082 cfattach_ioconf_vboxguest,
1083 cfdata_ioconf_vboxguest);
1084 if (error)
1085 break;
1086
1087 devsw_detach(NULL, &g_VBoxGuestNetBSDChrDevSW);
1088 break;
1089
1090 default:
1091 return ENOTTY;
1092 }
1093 return error;
1094}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use