static void command(unsigned long io, char *com) { mpcwrite((char *) &io, sizeof io); if (io & IOC_IN) mpcwrite(com, IOCPARM_LEN(io)); if (io & IOC_OUT) mpcread(com, IOCPARM_LEN(io)); }
int _ioctl(int fd, unsigned long com, ...) #endif { unsigned int size; va_list ap; void *data; size = IOCPARM_LEN(com); if (size > 0) { va_start(ap, com); if (com & IOC_VOID) { /* * In the (size > 0 && com & IOC_VOID) case, the * kernel assigns the value of data to an int * and passes a pointer to that int down. */ /* * XXX-BD: there is no telling what real * applications are passing here. We may want an * __np_va_space_remaining() or the like to peak * at the passed argument rather than crashing * deep in a library. */ data = (void *)(intptr_t)(va_arg(ap, int)); } else {
int ioctl(int fd, int request, ...) { void *data; va_list args; va_start(args, request); data = (request & IOC_INOUT) ? va_arg(args, void *) : NULL; va_end(args); if(data == NULL && (request & IOC_INOUT) && IOCPARM_LEN(request) != 0) { errno = EFAULT; return -1; } fd = request != FIONBIO ? _socketGetFd(fd) : fd; if(fd == -1) return -1; switch(request) { case FIONBIO: { // See note in fcntl (below) int flags = fcntl(fd, F_GETFL, 0); if(flags == -1) return -1; flags = *(int *)data != 0 ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK); return fcntl(fd, F_SETFL, flags); } case BIOCSETF: case BIOCSETWF: case BIOCSETFNR: { int ret; struct bpf_program *prog = (struct bpf_program *)data; if(prog->bf_len > BPF_MAXBUFSIZE) { errno = EINVAL; return -1; } struct bpf_program_serialized *prog_ser = (struct bpf_program_serialized *)malloc(sizeof(struct bpf_program_serialized)); if(prog_ser == NULL) { errno = ENOMEM; return -1; } prog_ser->bf_len = prog->bf_len; memcpy(prog_ser->bf_insns, prog->bf_insns, prog->bf_len); request = _IOC(request & IOC_DIRMASK, IOCGROUP(request), IOCBASECMD(request), sizeof(struct bpf_program_serialized)); ret = bsdIoctl(fd, request, prog_ser); free(prog_ser); return _socketParseBsdResult(NULL, ret); } default: return _socketParseBsdResult(NULL, bsdIoctl(fd, request, data)); } }
int uts_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *l) { int error = 0; struct uts_softc *sc = v; struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; DPRINTF(("uts_ioctl(%d, '%c', %d)\n", IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd & 0xff)); switch (cmd) { case WSMOUSEIO_SCALIBCOORDS: if (!(wsmc->minx >= 0 && wsmc->maxx >= 0 && wsmc->miny >= 0 && wsmc->maxy >= 0 && wsmc->resx >= 0 && wsmc->resy >= 0 && wsmc->minx < 32768 && wsmc->maxx < 32768 && wsmc->miny < 32768 && wsmc->maxy < 32768 && (wsmc->maxx - wsmc->minx) != 0 && (wsmc->maxy - wsmc->miny) != 0 && wsmc->resx < 32768 && wsmc->resy < 32768 && wsmc->swapxy >= 0 && wsmc->swapxy <= 1 && wsmc->samplelen >= 0 && wsmc->samplelen <= 1)) return (EINVAL); sc->sc_tsscale.minx = wsmc->minx; sc->sc_tsscale.maxx = wsmc->maxx; sc->sc_tsscale.miny = wsmc->miny; sc->sc_tsscale.maxy = wsmc->maxy; sc->sc_tsscale.swapxy = wsmc->swapxy; sc->sc_tsscale.resx = wsmc->resx; sc->sc_tsscale.resy = wsmc->resy; sc->sc_rawmode = wsmc->samplelen; break; case WSMOUSEIO_GCALIBCOORDS: wsmc->minx = sc->sc_tsscale.minx; wsmc->maxx = sc->sc_tsscale.maxx; wsmc->miny = sc->sc_tsscale.miny; wsmc->maxy = sc->sc_tsscale.maxy; wsmc->swapxy = sc->sc_tsscale.swapxy; wsmc->resx = sc->sc_tsscale.resx; wsmc->resy = sc->sc_tsscale.resy; wsmc->samplelen = sc->sc_rawmode; break; case WSMOUSEIO_GTYPE: *(u_int *)data = WSMOUSE_TYPE_TPANEL; break; default: error = ENOTTY; break; } return (error); }
/** * Device I/O Control entry point. * * @returns Darwin for slow IOCtls and VBox status code for the fast ones. * @param Dev The device number (major+minor). * @param iCmd The IOCtl command. * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)). * @param fFlags Flag saying we're a character device (like we didn't know already). * @param pProcess The process issuing this request. */ static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) { uint32_t cbReq = IOCPARM_LEN(iCmd); PVBOXNETADPREQ pReq = (PVBOXNETADPREQ)pData; int rc; Log(("VBoxNetAdpDarwinIOCtl: param len %#x; iCmd=%#lx\n", cbReq, iCmd)); switch (IOCBASECMD(iCmd)) { case IOCBASECMD(VBOXNETADP_CTL_ADD): { if ( (IOC_DIRMASK & iCmd) != IOC_INOUT || cbReq < sizeof(VBOXNETADPREQ)) return EINVAL; PVBOXNETADP pNew; Log(("VBoxNetAdpDarwinIOCtl: szName=%s\n", pReq->szName)); rc = vboxNetAdpCreate(&pNew, pReq->szName[0] && RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName))) ? pReq->szName : NULL); if (RT_FAILURE(rc)) return rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL; Assert(strlen(pReq->szName) < sizeof(pReq->szName)); strncpy(pReq->szName, pNew->szName, sizeof(pReq->szName) - 1); pReq->szName[sizeof(pReq->szName) - 1] = '\0'; Log(("VBoxNetAdpDarwinIOCtl: Added '%s'\n", pReq->szName)); break; } case IOCBASECMD(VBOXNETADP_CTL_REMOVE): { if (!RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName)))) return EINVAL; PVBOXNETADP pAdp = vboxNetAdpFindByName(pReq->szName); if (!pAdp) return EINVAL; rc = vboxNetAdpDestroy(pAdp); if (RT_FAILURE(rc)) return EINVAL; Log(("VBoxNetAdpDarwinIOCtl: Removed %s\n", pReq->szName)); break; } default: printf("VBoxNetAdpDarwinIOCtl: unknown command %lx.\n", IOCBASECMD(iCmd)); return EINVAL; } return 0; }
/** * Driver ioctl, an alternate entry point for this character driver. * * @param Dev Device number * @param iCmd Operation identifier * @param iArgs Arguments from user to driver * @param Mode Information bitfield (read/write, address space etc.) * @param pCred User credentials * @param pVal Return value for calling process. * * @return corresponding solaris error code. */ static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t iArgs, int Mode, cred_t *pCred, int *pVal) { /* * Get the session from the soft state item. */ vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev)); if (!pState) { LogRel(("vgdrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev))); return EINVAL; } PVBOXGUESTSESSION pSession = pState->pSession; if (!pSession) { LogRel(("vgdrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev))); return DDI_SUCCESS; } /* * Deal with fast requests. */ if (VBGL_IOCTL_IS_FAST(iCmd)) { *pVal = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession); return 0; } /* * It's kind of simple if this is a kernel session, take slow path if user land. */ if (pSession->R0Process == NIL_RTR0PROCESS) { if (IOCPARM_LEN(iCmd) == sizeof(VBGLREQHDR)) { PVBGLREQHDR pHdr = (PVBGLREQHDR)iArgs; int rc; if (iCmd != VBGL_IOCTL_IDC_DISCONNECT) rc =VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut)); else { pState->pSession = NULL; rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut)); if (RT_FAILURE(rc)) pState->pSession = pSession; } return rc; } } return vgdrvSolarisIOCtlSlow(pSession, iCmd, Mode, iArgs); }
int main(int ac, char **av) { unsigned long cmd; const char *name; char *cp; int group, i; if (ac < 2) usage(av); printf(" command : dir group num len name\n"); for (i = 1; i < ac; i++) { errno = 0; cmd = strtoul(av[i], &cp, 0); if (*cp != '\0' || errno != 0) { fprintf(stderr, "Invalid integer: %s\n", av[i]); usage(av); } printf("0x%08lx: ", cmd); switch (cmd & IOC_DIRMASK) { case IOC_VOID: printf("VOID "); break; case IOC_OUT: printf("OUT "); break; case IOC_IN: printf("IN "); break; case IOC_INOUT: printf("INOUT"); break; default: printf("%01lx ???", (cmd & IOC_DIRMASK) >> 29); break; } printf(" "); group = IOCGROUP(cmd); if (isprint(group)) printf(" '%c' ", group); else printf(" 0x%02x", group); printf(" %3lu %4lu", cmd & 0xff, IOCPARM_LEN(cmd)); name = sysdecode_ioctlname(cmd); if (name != NULL) printf(" %s", name); printf("\n"); } return (0); }
static int sgx_linux_ioctl(struct thread *td, struct linux_ioctl_args *args) { uint8_t data[SGX_IOCTL_MAX_DATA_LEN]; cap_rights_t rights; struct file *fp; u_long cmd; int error; int len; error = fget(td, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); if (error != 0) return (error); cmd = args->cmd; args->cmd &= ~(LINUX_IOC_IN | LINUX_IOC_OUT); if (cmd & LINUX_IOC_IN) args->cmd |= IOC_IN; if (cmd & LINUX_IOC_OUT) args->cmd |= IOC_OUT; len = IOCPARM_LEN(cmd); if (len > SGX_IOCTL_MAX_DATA_LEN) { printf("%s: Can't copy data: cmd len is too big %d\n", __func__, len); return (EINVAL); } if (cmd & LINUX_IOC_IN) { error = copyin((void *)args->arg, data, len); if (error) { printf("%s: Can't copy data, error %d\n", __func__, error); return (EINVAL); } } error = (fo_ioctl(fp, args->cmd, (caddr_t)data, td->td_ucred, td)); fdrop(fp, td); return (error); }
/** * Driver ioctl, an alternate entry point for this character driver. * * @param Dev Device number * @param Cmd Operation identifier * @param pArg Arguments from user to driver * @param Mode Information bitfield (read/write, address space etc.) * @param pCred User credentials * @param pVal Return value for calling process. * * @return corresponding solaris error code. */ static int VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal) { LogFlow((DEVICE_NAME ":VBoxGuestSolarisIOCtl\n")); /* * Get the session from the soft state item. */ vboxguest_state_t *pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, getminor(Dev)); if (!pState) { LogRel((DEVICE_NAME "::IOCtl: no state data for %d\n", getminor(Dev))); return EINVAL; } PVBOXGUESTSESSION pSession = pState->pSession; if (!pSession) { LogRel((DEVICE_NAME "::IOCtl: no session data for %d\n", getminor(Dev))); return EINVAL; } /* * Read and validate the request wrapper. */ VBGLBIGREQ ReqWrap; if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap)) { LogRel((DEVICE_NAME "::IOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd), sizeof(ReqWrap))); return ENOTTY; } int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode); if (RT_UNLIKELY(rc)) { LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%#x.\n", pArg, Cmd, rc)); return EINVAL; } if (ReqWrap.u32Magic != VBGLBIGREQ_MAGIC) { LogRel((DEVICE_NAME "::IOCtl: bad magic %#x; pArg=%p Cmd=%#x.\n", ReqWrap.u32Magic, pArg, Cmd)); return EINVAL; } if (RT_UNLIKELY(ReqWrap.cbData > _1M*16)) { LogRel((DEVICE_NAME "::IOCtl: bad size %#x; pArg=%p Cmd=%#x.\n", ReqWrap.cbData, pArg, Cmd)); return EINVAL; } /* * Read the request payload if any; requests like VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS have no data payload. */ void *pvBuf = NULL; if (RT_LIKELY(ReqWrap.cbData > 0)) { pvBuf = RTMemTmpAlloc(ReqWrap.cbData); if (RT_UNLIKELY(!pvBuf)) { LogRel((DEVICE_NAME "::IOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap.cbData)); return ENOMEM; } rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode); if (RT_UNLIKELY(rc)) { RTMemTmpFree(pvBuf); LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc)); return EFAULT; } if (RT_UNLIKELY(!VALID_PTR(pvBuf))) { RTMemTmpFree(pvBuf); LogRel((DEVICE_NAME "::IOCtl: pvBuf invalid pointer %p\n", pvBuf)); return EINVAL; } } Log((DEVICE_NAME "::IOCtl: pSession=%p pid=%d.\n", pSession, (int)RTProcSelf())); /* * Process the IOCtl. */ size_t cbDataReturned = 0; rc = VBoxGuestCommonIOCtl(Cmd, &g_DevExt, pSession, pvBuf, ReqWrap.cbData, &cbDataReturned); if (RT_SUCCESS(rc)) { rc = 0; if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData)) { LogRel((DEVICE_NAME "::IOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData)); cbDataReturned = ReqWrap.cbData; } if (cbDataReturned > 0) { rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode); if (RT_UNLIKELY(rc)) { LogRel((DEVICE_NAME "::IOCtl: ddi_copyout failed; pvBuf=%p pArg=%p cbDataReturned=%u Cmd=%d. rc=%d\n", pvBuf, pArg, cbDataReturned, Cmd, rc)); rc = EFAULT; } } } else { /* * We Log() instead of LogRel() here because VBOXGUEST_IOCTL_WAITEVENT can return VERR_TIMEOUT, * VBOXGUEST_IOCTL_CANCEL_ALL_EVENTS can return VERR_INTERRUPTED and possibly more in the future; * which are not really failures that require logging. */ Log((DEVICE_NAME "::IOCtl: VBoxGuestCommonIOCtl failed. Cmd=%#x rc=%d\n", Cmd, rc)); rc = RTErrConvertToErrno(rc); } *pVal = rc; if (pvBuf) RTMemTmpFree(pvBuf); return rc; }
/** * IOCTL handler * */ static int VBoxGuestFreeBSDIOCtl(struct cdev *pDev, u_long ulCmd, caddr_t pvData, int fFile, struct thread *pTd) { LogFlow((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl\n")); int rc = 0; /* * Validate the input. */ PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pDev->si_drv1; if (RT_UNLIKELY(!VALID_PTR(pSession))) return EINVAL; /* * Validate the request wrapper. */ if (IOCPARM_LEN(ulCmd) != sizeof(VBGLBIGREQ)) { Log((DEVICE_NAME ": VBoxGuestFreeBSDIOCtl: bad request %lu size=%lu expected=%d\n", ulCmd, IOCPARM_LEN(ulCmd), sizeof(VBGLBIGREQ))); return ENOTTY; } PVBGLBIGREQ ReqWrap = (PVBGLBIGREQ)pvData; if (ReqWrap->u32Magic != VBGLBIGREQ_MAGIC) { Log((DEVICE_NAME ": VBoxGuestFreeBSDIOCtl: bad magic %#x; pArg=%p Cmd=%lu.\n", ReqWrap->u32Magic, pvData, ulCmd)); return EINVAL; } if (RT_UNLIKELY( ReqWrap->cbData == 0 || ReqWrap->cbData > _1M*16)) { printf(DEVICE_NAME ": VBoxGuestFreeBSDIOCtl: bad size %#x; pArg=%p Cmd=%lu.\n", ReqWrap->cbData, pvData, ulCmd); return EINVAL; } /* * Read the request. */ void *pvBuf = RTMemTmpAlloc(ReqWrap->cbData); if (RT_UNLIKELY(!pvBuf)) { Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap->cbData)); return ENOMEM; } rc = copyin((void *)(uintptr_t)ReqWrap->pvDataR3, pvBuf, ReqWrap->cbData); if (RT_UNLIKELY(rc)) { RTMemTmpFree(pvBuf); Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: copyin failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, pvData, ulCmd, rc)); return EFAULT; } if (RT_UNLIKELY( ReqWrap->cbData != 0 && !VALID_PTR(pvBuf))) { RTMemTmpFree(pvBuf); Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: pvBuf invalid pointer %p\n", pvBuf)); return EINVAL; } Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: pSession=%p pid=%d.\n", pSession, (int)RTProcSelf())); /* * Process the IOCtl. */ size_t cbDataReturned; rc = VbgdCommonIoCtl(ulCmd, &g_DevExt, pSession, pvBuf, ReqWrap->cbData, &cbDataReturned); if (RT_SUCCESS(rc)) { rc = 0; if (RT_UNLIKELY(cbDataReturned > ReqWrap->cbData)) { Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap->cbData)); cbDataReturned = ReqWrap->cbData; } if (cbDataReturned > 0) { rc = copyout(pvBuf, (void *)(uintptr_t)ReqWrap->pvDataR3, cbDataReturned); if (RT_UNLIKELY(rc)) { Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: copyout failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, pvData, ulCmd, rc)); rc = EFAULT; } } } else { Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: VbgdCommonIoCtl failed. rc=%d\n", rc)); rc = EFAULT; } RTMemTmpFree(pvBuf); return rc; }
/** * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions. * * @returns Solaris errno. * * @param pSession The session. * @param Cmd The IOCtl command. * @param Mode Information bitfield (for specifying ownership of data) * @param iArg User space address of the request buffer. */ static int VBoxDrvSolarisIOCtlSlow(PSUPDRVSESSION pSession, int iCmd, int Mode, intptr_t iArg) { int rc; uint32_t cbBuf = 0; union { SUPREQHDR Hdr; uint8_t abBuf[64]; } StackBuf; PSUPREQHDR pHdr; /* * Read the header. */ if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr))) { LogRel(("VBoxDrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr))); return EINVAL; } rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode); if (RT_UNLIKELY(rc)) { LogRel(("VBoxDrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc)); return EFAULT; } if (RT_UNLIKELY((StackBuf.Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { LogRel(("VBoxDrvSolarisIOCtlSlow: bad header magic %#x; iCmd=%#x\n", StackBuf.Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, iCmd)); return EINVAL; } cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut); if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr) || StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) || cbBuf > _1M*16)) { LogRel(("VBoxDrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd)); return EINVAL; } /* * Buffer the request. */ if (cbBuf <= sizeof(StackBuf)) pHdr = &StackBuf.Hdr; else { pHdr = RTMemTmpAlloc(cbBuf); if (RT_UNLIKELY(!pHdr)) { LogRel(("VBoxDrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd)); return ENOMEM; } } rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode); if (RT_UNLIKELY(rc)) { LogRel(("VBoxDrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc)); if (pHdr != &StackBuf.Hdr) RTMemFree(pHdr); return EFAULT; } /* * Process the IOCtl. */ rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf); /* * Copy ioctl data and output buffer back to user space. */ if (RT_LIKELY(!rc)) { uint32_t cbOut = pHdr->cbOut; if (RT_UNLIKELY(cbOut > cbBuf)) { LogRel(("VBoxDrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd)); cbOut = cbBuf; } rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode); if (RT_UNLIKELY(rc != 0)) { /* this is really bad */ LogRel(("VBoxDrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc)); rc = EFAULT; } } else rc = EINVAL; if (pHdr != &StackBuf.Hdr) RTMemTmpFree(pHdr); return rc; }
/** * Worker for VbgdDarwinIOCtl that takes the slow IOCtl functions. * * @returns Darwin errno. * * @param pSession The session. * @param iCmd The IOCtl command. * @param pData Pointer to the kernel copy of the data buffer. * @param pProcess The calling process. */ static int VbgdDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess) { LogFlow(("VbgdDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess)); /* * Buffered or unbuffered? */ void *pvReqData; user_addr_t pUser = 0; void *pvPageBuf = NULL; uint32_t cbReq = IOCPARM_LEN(iCmd); if ((IOC_DIRMASK & iCmd) == IOC_INOUT) { /* * Raw buffered request data, common code validates it. */ pvReqData = pData; } else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq) { /* * Get the header and figure out how much we're gonna have to read. */ VBGLBIGREQ Hdr; pUser = (user_addr_t)*(void **)pData; int rc = copyin(pUser, &Hdr, sizeof(Hdr)); if (RT_UNLIKELY(rc)) { Log(("VbgdDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd)); return rc; } if (RT_UNLIKELY(Hdr.u32Magic != VBGLBIGREQ_MAGIC)) { Log(("VbgdDarwinIOCtlSlow: bad magic u32Magic=%#x; iCmd=%#lx\n", Hdr.u32Magic, iCmd)); return EINVAL; } cbReq = Hdr.cbData; if (RT_UNLIKELY(cbReq > _1M*16)) { Log(("VbgdDarwinIOCtlSlow: %#x; iCmd=%#lx\n", Hdr.cbData, iCmd)); return EINVAL; } pUser = Hdr.pvDataR3; /* * Allocate buffer and copy in the data. */ pvReqData = RTMemTmpAlloc(cbReq); if (!pvReqData) pvPageBuf = pvReqData = IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8); if (RT_UNLIKELY(!pvReqData)) { Log(("VbgdDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd)); return ENOMEM; } rc = copyin(pUser, pvReqData, Hdr.cbData); if (RT_UNLIKELY(rc)) { Log(("VbgdDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, pvReqData, Hdr.cbData, rc, iCmd)); if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pvReqData); return rc; } } else { Log(("VbgdDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd)); return EINVAL; } /* * Process the IOCtl. */ size_t cbReqRet = 0; int rc = VBoxGuestCommonIOCtl(iCmd, &g_DevExt, pSession, pvReqData, cbReq, &cbReqRet); if (RT_SUCCESS(rc)) { /* * If not buffered, copy back the buffer before returning. */ if (pUser) { if (cbReqRet > cbReq) { Log(("VbgdDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbReqRet, cbReq, iCmd)); cbReqRet = cbReq; } rc = copyout(pvReqData, pUser, cbReqRet); if (RT_UNLIKELY(rc)) Log(("VbgdDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n", pvReqData, (unsigned long long)pUser, cbReqRet, rc, iCmd)); /* cleanup */ if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pvReqData); } else rc = 0; } else { /* * The request failed, just clean up. */ if (pUser) { if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pvReqData); } Log(("VbgdDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc)); rc = EINVAL; } Log2(("VbgdDarwinIOCtlSlow: returns %d\n", rc)); return rc; }
/** * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions. * * @returns Darwin errno. * * @param pSession The session. * @param iCmd The IOCtl command. * @param pData Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer. * @param pProcess The calling process. */ static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess) { LogFlow(("VBoxDrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess)); /* * Buffered or unbuffered? */ PSUPREQHDR pHdr; user_addr_t pUser = 0; void *pvPageBuf = NULL; uint32_t cbReq = IOCPARM_LEN(iCmd); if ((IOC_DIRMASK & iCmd) == IOC_INOUT) { pHdr = (PSUPREQHDR)pData; if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd)); return EINVAL; } if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", pHdr->fFlags, iCmd)); return EINVAL; } if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq || pHdr->cbIn < sizeof(*pHdr) || pHdr->cbOut < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd)); return EINVAL; } } else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq) { /* * Get the header and figure out how much we're gonna have to read. */ SUPREQHDR Hdr; pUser = (user_addr_t)*(void **)pData; int rc = copyin(pUser, &Hdr, sizeof(Hdr)); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd)); return rc; } if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", Hdr.fFlags, iCmd)); return EINVAL; } cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbOut < sizeof(Hdr) || cbReq > _1M*16)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd)); return EINVAL; } /* * Allocate buffer and copy in the data. */ pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq); if (!pHdr) pvPageBuf = pHdr = (PSUPREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8); if (RT_UNLIKELY(!pHdr)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd)); return ENOMEM; } rc = copyin(pUser, pHdr, Hdr.cbIn); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd)); if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pHdr); return rc; } } else { Log(("VBoxDrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd)); return EINVAL; } /* * Process the IOCtl. */ int rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr); if (RT_LIKELY(!rc)) { /* * If not buffered, copy back the buffer before returning. */ if (pUser) { uint32_t cbOut = pHdr->cbOut; if (cbOut > cbReq) { OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd)); cbOut = cbReq; } rc = copyout(pHdr, pUser, cbOut); if (RT_UNLIKELY(rc)) OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n", pHdr, (unsigned long long)pUser, cbOut, rc, iCmd)); /* cleanup */ if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pHdr); } } else { /* * The request failed, just clean up. */ if (pUser) { if (pvPageBuf) IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); else RTMemTmpFree(pHdr); } Log(("VBoxDrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc)); rc = EINVAL; } Log2(("VBoxDrvDarwinIOCtlSlow: returns %d\n", rc)); return rc; }
/* * Ioctl system call */ int anp_ioctl(int fd,int ucom,caddr_t udata) { struct socket *sock; register int com, error; register u_int size; caddr_t data, memp; int tmp; #define STK_PARAMS 128 char stkbuf[STK_PARAMS]; MU_LOCK(kern_lock_p); sock=anp_fdfind(fd); com=ucom; /* AC: since we don't support exec() in sim. environment, we don't support * these ioctl's either... */ #ifdef NOPE switch (com = ucom) { case FIONCLEX: fdp->fd_ofileflags[uap->fd] &= ~UF_EXCLOSE; MU_UNLOCK(kern_lock_p); return (0); case FIOCLEX: fdp->fd_ofileflags[uap->fd] |= UF_EXCLOSE; MU_UNLOCK(kern_lock_p); return (0); } #endif /* * Interpret high order word to find amount of data to be * copied to/from the user's address space. */ size = IOCPARM_LEN(com); if (size > IOCPARM_MAX) { anp_errno=ENOTTY; MU_UNLOCK(kern_lock_p); return -1; } memp = NULL; if (size > sizeof (stkbuf)) { memp = (caddr_t)anp_sys_malloc((u_long)size, M_IOCTLOPS, M_WAITOK); data = memp; } else data = stkbuf; if (com&IOC_IN) { if (size) { error = copyin(udata, data, (u_int)size); if (error) { if (memp) anp_sys_free(memp, M_IOCTLOPS); anp_errno=error; MU_UNLOCK(kern_lock_p); return -1; } } else *(caddr_t *)data = udata; } else if ((com&IOC_OUT) && size) /* * Zero the buffer so the user always * gets back something deterministic. */ bzero(data, size); else if (com&IOC_VOID) *(caddr_t *)data = udata; switch (com) { case FIONBIO: case FIOASYNC: tmp = *((int *) data); error = (soo_ioctl)(sock, com, (caddr_t)&tmp); break; #ifdef NOPE case FIOSETOWN: tmp = *((int *)data); if (fp->f_type == DTYPE_SOCKET) { ((struct socket *)fp->f_data)->so_pgid = tmp; error = 0; break; } if (tmp <= 0) { tmp = -tmp; } else { struct proc *p1 = pfind(tmp); if (p1 == 0) { error = ESRCH; break; } tmp = p1->p_pgrp->pg_id; } error = (*fp->f_ops->fo_ioctl) (fp, (int)TIOCSPGRP, (caddr_t)&tmp, p); break; case FIOGETOWN: if (fp->f_type == DTYPE_SOCKET) { error = 0; *(int *)data = ((struct socket *)fp->f_data)->so_pgid; break; } error = (*fp->f_ops->fo_ioctl)(fp, (int)TIOCGPGRP, data, p); *(int *)data = -*(int *)data; break; #endif default: error = soo_ioctl(sock, com, data); /* * Copy any data to user, size was * already set and checked above. */ if (error == 0 && (com&IOC_OUT) && size) error = copyout(data, udata, (u_int)size); break; } if (memp) anp_sys_free(memp, M_IOCTLOPS); anp_errno=error; MU_UNLOCK(kern_lock_p); if (anp_errno!=0) { return -1; } else { return 0; } }
char * print_arg(struct syscall_args *sc, unsigned long *args, long retval, struct trussinfo *trussinfo) { char *tmp; pid_t pid; tmp = NULL; pid = trussinfo->pid; switch (sc->type & ARG_MASK) { case Hex: asprintf(&tmp, "0x%x", (int)args[sc->offset]); break; case Octal: asprintf(&tmp, "0%o", (int)args[sc->offset]); break; case Int: asprintf(&tmp, "%d", (int)args[sc->offset]); break; case Name: { /* NULL-terminated string. */ char *tmp2; tmp2 = get_string(pid, (void*)args[sc->offset], 0); asprintf(&tmp, "\"%s\"", tmp2); free(tmp2); break; } case BinString: { /* Binary block of data that might have printable characters. XXX If type|OUT, assume that the length is the syscall's return value. Otherwise, assume that the length of the block is in the next syscall argument. */ int max_string = trussinfo->strsize; char tmp2[max_string+1], *tmp3; int len; int truncated = 0; if (sc->type & OUT) len = retval; else len = args[sc->offset + 1]; /* Don't print more than max_string characters, to avoid word wrap. If we have to truncate put some ... after the string. */ if (len > max_string) { len = max_string; truncated = 1; } if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) != -1) { tmp3 = malloc(len * 4 + 1); while (len) { if (strvisx(tmp3, tmp2, len, VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) break; len--; truncated = 1; }; asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? "..." : ""); free(tmp3); } else { asprintf(&tmp, "0x%lx", args[sc->offset]); } break; } case StringArray: { int num, size, i; char *tmp2; char *string; char *strarray[100]; /* XXX This is ugly. */ if (get_struct(pid, (void *)args[sc->offset], (void *)&strarray, sizeof(strarray)) == -1) err(1, "get_struct %p", (void *)args[sc->offset]); num = 0; size = 0; /* Find out how large of a buffer we'll need. */ while (strarray[num] != NULL) { string = get_string(pid, (void*)strarray[num], 0); size += strlen(string); free(string); num++; } size += 4 + (num * 4); tmp = (char *)malloc(size); tmp2 = tmp; tmp2 += sprintf(tmp2, " ["); for (i = 0; i < num; i++) { string = get_string(pid, (void*)strarray[i], 0); tmp2 += sprintf(tmp2, " \"%s\"%c", string, (i + 1 == num) ? ' ' : ','); free(string); } tmp2 += sprintf(tmp2, "]"); break; } #ifdef __LP64__ case Quad: asprintf(&tmp, "0x%lx", args[sc->offset]); break; #else case Quad: { unsigned long long ll; ll = *(unsigned long long *)(args + sc->offset); asprintf(&tmp, "0x%llx", ll); break; } #endif case Ptr: asprintf(&tmp, "0x%lx", args[sc->offset]); break; case Readlinkres: { char *tmp2; if (retval == -1) { tmp = strdup(""); break; } tmp2 = get_string(pid, (void*)args[sc->offset], retval); asprintf(&tmp, "\"%s\"", tmp2); free(tmp2); break; } case Ioctl: { const char *temp = ioctlname(args[sc->offset]); if (temp) tmp = strdup(temp); else { unsigned long arg = args[sc->offset]; asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", arg, arg & IOC_OUT ? "R" : "", arg & IOC_IN ? "W" : "", IOCGROUP(arg), isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?', arg & 0xFF, IOCPARM_LEN(arg)); } break; } case Umtx: { struct umtx umtx; if (get_struct(pid, (void *)args[sc->offset], &umtx, sizeof(umtx)) != -1) asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner); else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case Timespec: { struct timespec ts; if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) != -1) asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec, ts.tv_nsec); else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case Timeval: { struct timeval tv; if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec, tv.tv_usec); else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case Timeval2: { struct timeval tv[2]; if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", (long)tv[0].tv_sec, tv[0].tv_usec, (long)tv[1].tv_sec, tv[1].tv_usec); else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case Itimerval: { struct itimerval itv; if (get_struct(pid, (void *)args[sc->offset], &itv, sizeof(itv)) != -1) asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", (long)itv.it_interval.tv_sec, itv.it_interval.tv_usec, (long)itv.it_value.tv_sec, itv.it_value.tv_usec); else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case LinuxSockArgs: { struct linux_socketcall_args largs; if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, sizeof(largs)) == -1) { err(1, "get_struct %p", (void *)args[sc->offset]); } const char *what; char buf[30]; switch (largs.what) { case LINUX_SOCKET: what = "LINUX_SOCKET"; break; case LINUX_BIND: what = "LINUX_BIND"; break; case LINUX_CONNECT: what = "LINUX_CONNECT"; break; case LINUX_LISTEN: what = "LINUX_LISTEN"; break; case LINUX_ACCEPT: what = "LINUX_ACCEPT"; break; case LINUX_GETSOCKNAME: what = "LINUX_GETSOCKNAME"; break; case LINUX_GETPEERNAME: what = "LINUX_GETPEERNAME"; break; case LINUX_SOCKETPAIR: what = "LINUX_SOCKETPAIR"; break; case LINUX_SEND: what = "LINUX_SEND"; break; case LINUX_RECV: what = "LINUX_RECV"; break; case LINUX_SENDTO: what = "LINUX_SENDTO"; break; case LINUX_RECVFROM: what = "LINUX_RECVFROM"; break; case LINUX_SHUTDOWN: what = "LINUX_SHUTDOWN"; break; case LINUX_SETSOCKOPT: what = "LINUX_SETSOCKOPT"; break; case LINUX_GETSOCKOPT: what = "LINUX_GETSOCKOPT"; break; case LINUX_SENDMSG: what = "LINUX_SENDMSG"; break; case LINUX_RECVMSG: what = "LINUX_RECVMSG"; break; default: sprintf(buf, "%d", largs.what); what = buf; break; } asprintf(&tmp, "(0x%lx)%s, 0x%lx", args[sc->offset], what, (long unsigned int)largs.args); break; } case Pollfd: { /* * XXX: A Pollfd argument expects the /next/ syscall argument * to be the number of fds in the array. This matches the poll * syscall. */ struct pollfd *pfd; int numfds = args[sc->offset+1]; int bytes = sizeof(struct pollfd) * numfds; int i, tmpsize, u, used; const int per_fd = 100; if ((pfd = malloc(bytes)) == NULL) err(1, "Cannot malloc %d bytes for pollfd array", bytes); if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) != -1) { used = 0; tmpsize = 1 + per_fd * numfds + 2; if ((tmp = malloc(tmpsize)) == NULL) err(1, "Cannot alloc %d bytes for poll output", tmpsize); tmp[used++] = '{'; for (i = 0; i < numfds; i++) { u = snprintf(tmp + used, per_fd, "%s%d/%s", i > 0 ? " " : "", pfd[i].fd, xlookup_bits(poll_flags, pfd[i].events)); if (u > 0) used += u < per_fd ? u : per_fd; } tmp[used++] = '}'; tmp[used++] = '\0'; } else { asprintf(&tmp, "0x%lx", args[sc->offset]); } free(pfd); break; } case Fd_set: { /* * XXX: A Fd_set argument expects the /first/ syscall argument * to be the number of fds in the array. This matches the * select syscall. */ fd_set *fds; int numfds = args[0]; int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; int i, tmpsize, u, used; const int per_fd = 20; if ((fds = malloc(bytes)) == NULL) err(1, "Cannot malloc %d bytes for fd_set array", bytes); if (get_struct(pid, (void *)args[sc->offset], fds, bytes) != -1) { used = 0; tmpsize = 1 + numfds * per_fd + 2; if ((tmp = malloc(tmpsize)) == NULL) err(1, "Cannot alloc %d bytes for fd_set " "output", tmpsize); tmp[used++] = '{'; for (i = 0; i < numfds; i++) { if (FD_ISSET(i, fds)) { u = snprintf(tmp + used, per_fd, "%d ", i); if (u > 0) used += u < per_fd ? u : per_fd; } } if (tmp[used-1] == ' ') used--; tmp[used++] = '}'; tmp[used++] = '\0'; } else asprintf(&tmp, "0x%lx", args[sc->offset]); free(fds); break; } case Signal: tmp = strsig2(args[sc->offset]); break; case Sigset: { long sig; sigset_t ss; int i, used; char *signame; sig = args[sc->offset]; if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, sizeof(ss)) == -1) { asprintf(&tmp, "0x%lx", args[sc->offset]); break; } tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ used = 0; for (i = 1; i < sys_nsig; i++) { if (sigismember(&ss, i)) { signame = strsig(i); used += sprintf(tmp + used, "%s|", signame); free(signame); } } if (used) tmp[used-1] = 0; else strcpy(tmp, "0x0"); break; } case Sigprocmask: { switch (args[sc->offset]) { #define S(a) case a: tmp = strdup(#a); break; S(SIG_BLOCK); S(SIG_UNBLOCK); S(SIG_SETMASK); #undef S } if (tmp == NULL) asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case Fcntlflag: { /* XXX output depends on the value of the previous argument */ switch (args[sc->offset-1]) { case F_SETFD: tmp = strdup(xlookup_bits(fcntlfd_arg, args[sc->offset])); break; case F_SETFL: tmp = strdup(xlookup_bits(fcntlfl_arg, args[sc->offset])); break; case F_GETFD: case F_GETFL: case F_GETOWN: tmp = strdup(""); break; default: asprintf(&tmp, "0x%lx", args[sc->offset]); break; } break; } case Open: tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); break; case Fcntl: tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); break; case Mprot: tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); break; case Mmapflags: { char *base, *alignstr; int align, flags; /* * MAP_ALIGNED can't be handled by xlookup_bits(), so * generate that string manually and prepend it to the * string from xlookup_bits(). Have to be careful to * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is * the only flag. */ flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; align = args[sc->offset] & MAP_ALIGNMENT_MASK; if (align != 0) { if (align == MAP_ALIGNED_SUPER) alignstr = strdup("MAP_ALIGNED_SUPER"); else asprintf(&alignstr, "MAP_ALIGNED(%d)", align >> MAP_ALIGNMENT_SHIFT); if (flags == 0) { tmp = alignstr; break; } } else alignstr = NULL; base = strdup(xlookup_bits(mmap_flags, flags)); if (alignstr == NULL) { tmp = base; break; } asprintf(&tmp, "%s|%s", alignstr, base); free(alignstr); free(base); break; } case Whence: tmp = strdup(xlookup(whence_arg, args[sc->offset])); break; case Sockdomain: tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); break; case Socktype: tmp = strdup(xlookup(socktype_arg, args[sc->offset])); break; case Shutdown: tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); break; case Resource: tmp = strdup(xlookup(resource_arg, args[sc->offset])); break; case Pathconf: tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); break; case Rforkflags: tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset])); break; case Sockaddr: { struct sockaddr_storage ss; char addr[64]; struct sockaddr_in *lsin; struct sockaddr_in6 *lsin6; struct sockaddr_un *sun; struct sockaddr *sa; char *p; u_char *q; int i; if (args[sc->offset] == 0) { asprintf(&tmp, "NULL"); break; } /* yuck: get ss_len */ if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) err(1, "get_struct %p", (void *)args[sc->offset]); /* * If ss_len is 0, then try to guess from the sockaddr type. * AF_UNIX may be initialized incorrectly, so always frob * it by using the "right" size. */ if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { switch (ss.ss_family) { case AF_INET: ss.ss_len = sizeof(*lsin); break; case AF_UNIX: ss.ss_len = sizeof(*sun); break; default: /* hurrrr */ break; } } if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, ss.ss_len) == -1) { err(2, "get_struct %p", (void *)args[sc->offset]); } switch (ss.ss_family) { case AF_INET: lsin = (struct sockaddr_in *)&ss; inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); asprintf(&tmp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port)); break; case AF_INET6: lsin6 = (struct sockaddr_in6 *)&ss; inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr); asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port)); break; case AF_UNIX: sun = (struct sockaddr_un *)&ss; asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); break; default: sa = (struct sockaddr *)&ss; asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data " "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family, &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - (char *)sa)), ""); if (tmp != NULL) { p = tmp + i; for (q = (u_char *)&sa->sa_data; q < (u_char *)sa + sa->sa_len; q++) p += sprintf(p, " %#02x,", *q); } } break; } case Sigaction: { struct sigaction sa; char *hand; const char *h; if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) != -1) { asprintf(&hand, "%p", sa.sa_handler); if (sa.sa_handler == SIG_DFL) h = "SIG_DFL"; else if (sa.sa_handler == SIG_IGN) h = "SIG_IGN"; else h = hand; asprintf(&tmp, "{ %s %s ss_t }", h, xlookup_bits(sigaction_flags, sa.sa_flags)); free(hand); } else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case Kevent: { /* * XXX XXX: the size of the array is determined by either the * next syscall argument, or by the syscall returnvalue, * depending on which argument number we are. This matches the * kevent syscall, but luckily that's the only syscall that uses * them. */ struct kevent *ke; int numevents = -1; int bytes = 0; int i, tmpsize, u, used; const int per_ke = 100; if (sc->offset == 1) numevents = args[sc->offset+1]; else if (sc->offset == 3 && retval != -1) numevents = retval; if (numevents >= 0) bytes = sizeof(struct kevent) * numevents; if ((ke = malloc(bytes)) == NULL) err(1, "Cannot malloc %d bytes for kevent array", bytes); if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], ke, bytes) != -1) { used = 0; tmpsize = 1 + per_ke * numevents + 2; if ((tmp = malloc(tmpsize)) == NULL) err(1, "Cannot alloc %d bytes for kevent " "output", tmpsize); tmp[used++] = '{'; for (i = 0; i < numevents; i++) { u = snprintf(tmp + used, per_ke, "%s%p,%s,%s,%d,%p,%p", i > 0 ? " " : "", (void *)ke[i].ident, xlookup(kevent_filters, ke[i].filter), xlookup_bits(kevent_flags, ke[i].flags), ke[i].fflags, (void *)ke[i].data, (void *)ke[i].udata); if (u > 0) used += u < per_ke ? u : per_ke; } tmp[used++] = '}'; tmp[used++] = '\0'; } else { asprintf(&tmp, "0x%lx", args[sc->offset]); } free(ke); break; } case Stat: { struct stat st; if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) != -1) { char mode[12]; strmode(st.st_mode, mode); asprintf(&tmp, "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, (intmax_t)st.st_ino, (intmax_t)st.st_size, (long)st.st_blksize); } else { asprintf(&tmp, "0x%lx", args[sc->offset]); } break; } case Rusage: { struct rusage ru; if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) != -1) { asprintf(&tmp, "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }", (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, ru.ru_inblock, ru.ru_oublock); } else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case Rlimit: { struct rlimit rl; if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) != -1) { asprintf(&tmp, "{ cur=%ju,max=%ju }", rl.rlim_cur, rl.rlim_max); } else asprintf(&tmp, "0x%lx", args[sc->offset]); break; } case ExitStatus: { char *signame; int status; signame = NULL; if (get_struct(pid, (void *)args[sc->offset], &status, sizeof(status)) != -1) { if (WIFCONTINUED(status)) tmp = strdup("{ CONTINUED }"); else if (WIFEXITED(status)) asprintf(&tmp, "{ EXITED,val=%d }", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) asprintf(&tmp, "{ SIGNALED,sig=%s%s }", signame = strsig2(WTERMSIG(status)), WCOREDUMP(status) ? ",cored" : ""); else asprintf(&tmp, "{ STOPPED,sig=%s }", signame = strsig2(WTERMSIG(status))); } else asprintf(&tmp, "0x%lx", args[sc->offset]); free(signame); break; } case Waitoptions: tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); break; case Idtype: tmp = strdup(xlookup(idtype_arg, args[sc->offset])); break; case Procctl: tmp = strdup(xlookup(procctl_arg, args[sc->offset])); break; default: errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); }
int videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) { struct video_softc *sc; int unit, error; unit = VIDEOUNIT(dev); if (unit >= video_cd.cd_ndevs || (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL) return (ENXIO); DPRINTF(("video_ioctl(%d, '%c', %d)\n", IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd & 0xff)); error = EOPNOTSUPP; switch (cmd) { case VIDIOC_QUERYCAP: if (sc->hw_if->querycap) error = (sc->hw_if->querycap)(sc->hw_hdl, (struct v4l2_capability *)data); break; case VIDIOC_ENUM_FMT: if (sc->hw_if->enum_fmt) error = (sc->hw_if->enum_fmt)(sc->hw_hdl, (struct v4l2_fmtdesc *)data); break; case VIDIOC_ENUM_FRAMESIZES: if (sc->hw_if->enum_fsizes) error = (sc->hw_if->enum_fsizes)(sc->hw_hdl, (struct v4l2_frmsizeenum *)data); break; case VIDIOC_ENUM_FRAMEINTERVALS: if (sc->hw_if->enum_fivals) error = (sc->hw_if->enum_fivals)(sc->hw_hdl, (struct v4l2_frmivalenum *)data); break; case VIDIOC_S_FMT: if (!(flags & FWRITE)) return (EACCES); if (sc->hw_if->s_fmt) error = (sc->hw_if->s_fmt)(sc->hw_hdl, (struct v4l2_format *)data); break; case VIDIOC_G_FMT: if (sc->hw_if->g_fmt) error = (sc->hw_if->g_fmt)(sc->hw_hdl, (struct v4l2_format *)data); break; case VIDIOC_S_PARM: if (sc->hw_if->s_parm) error = (sc->hw_if->s_parm)(sc->hw_hdl, (struct v4l2_streamparm *)data); break; case VIDIOC_G_PARM: if (sc->hw_if->g_parm) error = (sc->hw_if->g_parm)(sc->hw_hdl, (struct v4l2_streamparm *)data); break; case VIDIOC_ENUMINPUT: if (sc->hw_if->enum_input) error = (sc->hw_if->enum_input)(sc->hw_hdl, (struct v4l2_input *)data); break; case VIDIOC_S_INPUT: if (sc->hw_if->s_input) error = (sc->hw_if->s_input)(sc->hw_hdl, (int)*data); break; case VIDIOC_REQBUFS: if (sc->hw_if->reqbufs) error = (sc->hw_if->reqbufs)(sc->hw_hdl, (struct v4l2_requestbuffers *)data); break; case VIDIOC_QUERYBUF: if (sc->hw_if->querybuf) error = (sc->hw_if->querybuf)(sc->hw_hdl, (struct v4l2_buffer *)data); break; case VIDIOC_QBUF: if (sc->hw_if->qbuf) error = (sc->hw_if->qbuf)(sc->hw_hdl, (struct v4l2_buffer *)data); break; case VIDIOC_DQBUF: if (!sc->hw_if->dqbuf) break; /* should have called mmap() before now */ if (sc->sc_vidmode != VIDMODE_MMAP) { error = EINVAL; break; } error = (sc->hw_if->dqbuf)(sc->hw_hdl, (struct v4l2_buffer *)data); sc->sc_frames_ready--; break; case VIDIOC_STREAMON: if (sc->hw_if->streamon) error = (sc->hw_if->streamon)(sc->hw_hdl, (int)*data); break; case VIDIOC_STREAMOFF: if (sc->hw_if->streamoff) error = (sc->hw_if->streamoff)(sc->hw_hdl, (int)*data); break; case VIDIOC_TRY_FMT: if (sc->hw_if->try_fmt) error = (sc->hw_if->try_fmt)(sc->hw_hdl, (struct v4l2_format *)data); break; case VIDIOC_QUERYCTRL: if (sc->hw_if->queryctrl) error = (sc->hw_if->queryctrl)(sc->hw_hdl, (struct v4l2_queryctrl *)data); break; case VIDIOC_G_CTRL: if (sc->hw_if->g_ctrl) error = (sc->hw_if->g_ctrl)(sc->hw_hdl, (struct v4l2_control *)data); break; case VIDIOC_S_CTRL: if (sc->hw_if->s_ctrl) error = (sc->hw_if->s_ctrl)(sc->hw_hdl, (struct v4l2_control *)data); break; default: error = (ENOTTY); } return (error); }
static status_t ds_ioctl(void *cookie, uint32 cmd, void *arg, size_t len) { /*socket_info_t *s = (socket_info_t *) cookie; u_int size = IOCPARM_LEN(cmd); status_t ret, err; ds_ioctl_arg_t buf; if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL; err = ret = 0; if (cmd & IOC_IN) copy_from_user((char *)&buf, (char *)arg, size); switch (cmd) { case DS_ADJUST_RESOURCE_INFO: ret = CardServices(AdjustResourceInfo, s->handle, &buf.adjust); break; case DS_GET_CARD_SERVICES_INFO: ret = CardServices(GetCardServicesInfo, &buf.servinfo); break; case DS_GET_CONFIGURATION_INFO: ret = CardServices(GetConfigurationInfo, s->handle, &buf.config); break; case DS_GET_FIRST_TUPLE: ret = CardServices(GetFirstTuple, s->handle, &buf.tuple); break; case DS_GET_NEXT_TUPLE: ret = CardServices(GetNextTuple, s->handle, &buf.tuple); break; case DS_GET_TUPLE_DATA: buf.tuple.TupleData = buf.tuple_parse.data; buf.tuple.TupleDataMax = sizeof(buf.tuple_parse.data); ret = CardServices(GetTupleData, s->handle, &buf.tuple); break; case DS_PARSE_TUPLE: buf.tuple.TupleData = buf.tuple_parse.data; ret = CardServices(ParseTuple, s->handle, &buf.tuple, &buf.tuple_parse.parse); break; case DS_RESET_CARD: ret = CardServices(ResetCard, s->handle, NULL); break; case DS_GET_STATUS: ret = CardServices(GetStatus, s->handle, &buf.status); break; case DS_VALIDATE_CIS: ret = CardServices(ValidateCIS, s->handle, &buf.cisinfo); break; case DS_SUSPEND_CARD: ret = CardServices(SuspendCard, s->handle, NULL); break; case DS_RESUME_CARD: ret = CardServices(ResumeCard, s->handle, NULL); break; case DS_EJECT_CARD: ret = CardServices(EjectCard, s->handle, NULL); break; case DS_INSERT_CARD: ret = CardServices(InsertCard, s->handle, NULL); break; case DS_ACCESS_CONFIGURATION_REGISTER: if ((buf.conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) return -EPERM; ret = CardServices(AccessConfigurationRegister, s->handle, &buf.conf_reg); break; case DS_GET_FIRST_REGION: ret = CardServices(GetFirstRegion, s->handle, &buf.region); break; case DS_GET_NEXT_REGION: ret = CardServices(GetNextRegion, s->handle, &buf.region); break; case DS_GET_FIRST_WINDOW: buf.win_info.handle = (window_handle_t)s->handle; ret = CardServices(GetFirstWindow, &buf.win_info.handle, &buf.win_info.window); break; case DS_GET_NEXT_WINDOW: ret = CardServices(GetNextWindow, &buf.win_info.handle, &buf.win_info.window); break; case DS_GET_MEM_PAGE: ret = CardServices(GetMemPage, buf.win_info.handle, &buf.win_info.map); break; case DS_REPLACE_CIS: ret = CardServices(ReplaceCIS, s->handle, &buf.cisdump); break; */ client_handle_t h = (client_handle_t) cookie; u_int size = IOCPARM_LEN(cmd); status_t ret, err; ds_ioctl_arg_t buf; if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL; err = ret = 0; if (cmd & IOC_IN) copy_from_user((char *)&buf, (char *)arg, size); switch (cmd) { case DS_ADJUST_RESOURCE_INFO: ret = CardServices(AdjustResourceInfo, h, &buf.adjust); break; case DS_GET_CARD_SERVICES_INFO: ret = CardServices(GetCardServicesInfo, &buf.servinfo); break; case DS_GET_CONFIGURATION_INFO: ret = CardServices(GetConfigurationInfo, h, &buf.config); break; case DS_GET_FIRST_TUPLE: ret = CardServices(GetFirstTuple, h, &buf.tuple); break; case DS_GET_NEXT_TUPLE: ret = CardServices(GetNextTuple, h, &buf.tuple); break; case DS_GET_TUPLE_DATA: buf.tuple.TupleData = buf.tuple_parse.data; buf.tuple.TupleDataMax = sizeof(buf.tuple_parse.data); ret = CardServices(GetTupleData, h, &buf.tuple); break; case DS_PARSE_TUPLE: buf.tuple.TupleData = buf.tuple_parse.data; ret = CardServices(ParseTuple, h, &buf.tuple, &buf.tuple_parse.parse); break; case DS_RESET_CARD: ret = CardServices(ResetCard, h, NULL); break; case DS_GET_STATUS: ret = CardServices(GetStatus, h, &buf.status); break; case DS_VALIDATE_CIS: ret = CardServices(ValidateCIS, h, &buf.cisinfo); break; case DS_SUSPEND_CARD: ret = CardServices(SuspendCard, h, NULL); break; case DS_RESUME_CARD: ret = CardServices(ResumeCard, h, NULL); break; case DS_EJECT_CARD: ret = CardServices(EjectCard, h, NULL); break; case DS_INSERT_CARD: ret = CardServices(InsertCard, h, NULL); break; /* case DS_ACCESS_CONFIGURATION_REGISTER: if ((buf.conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) return -EPERM; ret = CardServices(AccessConfigurationRegister, h, &buf.conf_reg); break; case DS_GET_FIRST_REGION: ret = CardServices(GetFirstRegion, h, &buf.region); break; case DS_GET_NEXT_REGION: ret = CardServices(GetNextRegion, h, &buf.region); break; case DS_GET_FIRST_WINDOW: buf.win_info.handle = (window_handle_t)h; ret = CardServices(GetFirstWindow, &buf.win_info.handle, &buf.win_info.window); break; case DS_GET_NEXT_WINDOW: ret = CardServices(GetNextWindow, &buf.win_info.handle, &buf.win_info.window); break; case DS_GET_MEM_PAGE: ret = CardServices(GetMemPage, buf.win_info.handle, &buf.win_info.map); break;*/ case DS_REPLACE_CIS: ret = CardServices(ReplaceCIS, h, &buf.cisdump); break; /* case DS_BIND_REQUEST: if (!capable(CAP_SYS_ADMIN)) return -EPERM; err = bind_request(i, &buf.bind_info); break; case DS_GET_DEVICE_INFO: err = get_device_info(i, &buf.bind_info, 1); break; case DS_GET_NEXT_DEVICE: err = get_device_info(i, &buf.bind_info, 0); break; case DS_UNBIND_REQUEST: err = unbind_request(i, &buf.bind_info); break; case DS_BIND_MTD: if (!capable(CAP_SYS_ADMIN)) return -EPERM; err = bind_mtd(i, &buf.mtd_info); break;*/ default: err = -EINVAL; } if ((err == 0) && (ret != CS_SUCCESS)) { switch (ret) { case CS_BAD_SOCKET: case CS_NO_CARD: err = ENODEV; break; case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ: case CS_BAD_TUPLE: err = EINVAL; break; case CS_IN_USE: err = EBUSY; break; case CS_OUT_OF_RESOURCE: err = ENOSPC; break; case CS_NO_MORE_ITEMS: err = ENODATA; break; case CS_UNSUPPORTED_FUNCTION: err = ENOSYS; break; default: err = EIO; break; } } if (cmd & IOC_OUT) copy_to_user((char *)arg, (char *)&buf, size); return err; } /* ds_ioctl */
/** * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions. * * @returns Solaris errno. * * @param pSession The session. * @param iCmd The IOCtl command. * @param Mode Information bitfield (for specifying ownership of data) * @param iArg User space address of the request buffer. */ static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArg) { int rc; uint32_t cbBuf = 0; union { VBGLREQHDR Hdr; uint8_t abBuf[64]; } StackBuf; PVBGLREQHDR pHdr; /* * Read the header. */ if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr))) { LogRel(("vgdrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr))); return EINVAL; } rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode); if (RT_UNLIKELY(rc)) { LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc)); return EFAULT; } if (RT_UNLIKELY(StackBuf.Hdr.uVersion != VBGLREQHDR_VERSION)) { LogRel(("vgdrvSolarisIOCtlSlow: bad header version %#x; iCmd=%#x\n", StackBuf.Hdr.uVersion, iCmd)); return EINVAL; } cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut); if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr) || (StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) && StackBuf.Hdr.cbOut != 0) || cbBuf > _1M*16)) { LogRel(("vgdrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd)); return EINVAL; } /* * Buffer the request. * * Note! Common code revalidates the header sizes and version. So it's * fine to read it once more. */ if (cbBuf <= sizeof(StackBuf)) pHdr = &StackBuf.Hdr; else { pHdr = RTMemTmpAlloc(cbBuf); if (RT_UNLIKELY(!pHdr)) { LogRel(("vgdrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd)); return ENOMEM; } } rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode); if (RT_UNLIKELY(rc)) { LogRel(("vgdrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc)); if (pHdr != &StackBuf.Hdr) RTMemFree(pHdr); return EFAULT; } /* * Process the IOCtl. */ rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf); /* * Copy ioctl data and output buffer back to user space. */ if (RT_SUCCESS(rc)) { uint32_t cbOut = pHdr->cbOut; if (RT_UNLIKELY(cbOut > cbBuf)) { LogRel(("vgdrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd)); cbOut = cbBuf; } rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode); if (RT_UNLIKELY(rc != 0)) { /* this is really bad */ LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc)); rc = EFAULT; } } else rc = EINVAL; if (pHdr != &StackBuf.Hdr) RTMemTmpFree(pHdr); return rc; }
int ioctl(int fd, int cmd, ...) { va_list vl; void *data; struct termios ltermios; #ifdef NOT_USED struct termios *ptermios; struct termio ltermio; #endif struct termio *ptermio; int match, tempint; //Pull out the command and the data va_start(vl, cmd); data = va_arg(vl, void *); va_end(vl); /* These calls have significantly different inputs or outputs that we have to manipulate (or re-direct entirely) */ switch ((unsigned)cmd) { /**** These calls map to fcntl's ****/ //Set/Clear FD_CLOEXEC state case FIOCLEX: return(fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)); case FIONCLEX: return(fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC)); //These are undoced uses for fcntl case SIOCGPGRP: case FIOGETOWN: if ( (tempint = fcntl (fd, F_GETOWN)) != -1) *(int *)data = tempint; return(tempint); case SIOCSPGRP: case FIOSETOWN: return(fcntl(fd, F_SETOWN, *(int *)data)); //Set the non-blocking state for reads on and off case FIONBIO: if (*(int *)data) return(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)); else return(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK)); //Set the async state on and off case FIOASYNC: if (*(int *)data) return(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC)); else return(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_ASYNC)); //Set/Clear exclusive mode case TIOCEXCL: { struct flock lflock; memset(&lflock, 0, sizeof(lflock)); lflock.l_type = F_WRLCK; //Fails if file opened read only? lflock.l_whence = SEEK_SET; lflock.l_start = 0; lflock.l_len = 0; lflock.l_pid = getpid(); return(__fcntl2ioctl(fcntl(fd, F_SETLK, &lflock))); } case TIOCNXCL: { struct flock lflock; memset(&lflock, 0, sizeof(lflock)); lflock.l_type = F_UNLCK; lflock.l_whence = SEEK_SET; lflock.l_start = 0; lflock.l_len = 0; lflock.l_pid = getpid(); (void)fcntl(fd, F_GETLK, &lflock); return(__fcntl2ioctl(fcntl(fd, F_SETLK, &lflock))); } //Make the terminal the controlling terminal for the process case TIOCSCTTY: return(tcsetsid(fd, getpid())); //Dis-associate this terminal as the controlling terminal case TIOCNOTTY: return(tcsetsid(-1, getpid())); /**** These calls translate from one type to another using tc[s|g]etattr ****/ //Terminal properties w/ termio case TCGETA: { ptermio = (struct termio *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); termios2termio(<ermios, ptermio); return(EOK); } case TCSETA: { ptermio = (struct termio *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); termio2termios(ptermio, <ermios); return(tcsetattr(fd, TCSANOW, <ermios)); } case TCSETAW: { ptermio = (struct termio *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); termio2termios(ptermio, <ermios); return(tcsetattr(fd, TCSADRAIN, <ermios)); } case TCSETAF: { ptermio = (struct termio *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); termio2termios(ptermio, <ermios); return(tcsetattr(fd, TCSAFLUSH, <ermios)); } //Set terminal state in an sgttyb structure (incomplete) case TIOCGETP: { struct sgttyb *psgttyb = (struct sgttyb *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); termios2sgttyb(<ermios, psgttyb); return(EOK); } case TIOCSETP: { struct sgttyb *psgttyb = (struct sgttyb *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); sgttyb2termios(psgttyb, <ermios); return(tcsetattr(fd, TCSAFLUSH, <ermios)); } case TIOCSETN: { struct sgttyb *psgttyb = (struct sgttyb *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); sgttyb2termios(psgttyb, <ermios); return(tcsetattr(fd, TCSANOW, <ermios)); } //Terminal state in tchars structure case TIOCGETC: { struct tchars *ptchars = (struct tchars *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); termios2tchars(<ermios, ptchars); return(EOK); } case TIOCSETC: { struct tchars *ptchars = (struct tchars *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); tchars2termios(ptchars, <ermios); return(tcsetattr(fd, TCSANOW, <ermios)); } //Terminal state in an ltchars structure case TIOCGLTC: { struct ltchars *pltchars = (struct ltchars *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); termios2ltchars(<ermios, pltchars); return(EOK); } case TIOCSLTC: { struct ltchars *pltchars = (struct ltchars *)data; if (tcgetattr(fd, <ermios) == -1) return(-1); ltchars2termios(pltchars, <ermios); return(tcsetattr(fd, TCSANOW, <ermios)); } //Set/Get the local flags structure case TIOCLGET: { if (tcgetattr(fd, <ermios) == -1) return(-1); *((int *)data) = (int)ltermios.c_lflag; return(EOK); } case TIOCLSET: { if (tcgetattr(fd, <ermios) == -1) return(-1); ltermios.c_lflag &= ~0xffff; ltermios.c_lflag |= *((int*)data); return(tcsetattr(fd, TCSANOW, <ermios)); } //Clear the break flag (implement TIOCSBRK?) case TIOCCBRK: { if (tcgetattr(fd, <ermios) == -1) return(-1); ltermios.c_iflag &= ~(BRKINT); ltermios.c_iflag |= IGNBRK; return(tcsetattr(fd, TCSANOW, <ermios)); } //Set the HUPCL flag case TIOCHPCL: { if (tcgetattr(fd, <ermios) != -1) { ltermios.c_cflag |= HUPCL; return(tcsetattr(fd, TCSANOW, <ermios)); } } /**** These calls map to devctl's, but need serious data munging ****/ //Inject a character into the stream case TIOCSTI: { return(tcinject(fd, (char*)data, sizeof(char))); } //Send a break for a period of time case TCSBRK: { int *duration = (int *) data; //Duration is measured in ms return(tcsendbreak(fd, *duration)); //tempint = (((*duration) ? *duration : 300) << 16) | _SERCTL_BRK_CHG | _SERCTL_BRK; //return(devctl(fd, DCMD_CHR_SERCTL, &tempint, sizeof(tempint), &ret)); } /**** Modem control operations (via devctl) ****/ //Clear the state of the modem by ~AND'ing in the int argument case TIOCMBIC: { int tmpmodem; if (_devctl(fd, DCMD_CHR_LINESTATUS, &tmpmodem, sizeof(tmpmodem), _DEVCTL_FLAG_NOTTY) == -1) return(-1); tmpmodem &= ~(*((int*)data)); tempint = 0; modem2serctl(&tmpmodem, &tempint); return(_devctl(fd, DCMD_CHR_SERCTL, &tempint, sizeof(tempint), _DEVCTL_FLAG_NOTTY)); } //Set the state of the modem by OR'ing in the int argument case TIOCMBIS: { int tmpmodem; if (_devctl(fd, DCMD_CHR_LINESTATUS, &tmpmodem, sizeof(tmpmodem), _DEVCTL_FLAG_NOTTY) == -1) return(-1); tmpmodem |= *((int*)data); tempint = 0; modem2serctl(&tmpmodem, &tempint); return(_devctl(fd, DCMD_CHR_SERCTL, &tempint, sizeof(tempint), _DEVCTL_FLAG_NOTTY)); } //Set the state of the modem lines case TIOCMSET: { tempint = 0; modem2serctl((int*)data, &tempint); return(_devctl(fd, DCMD_CHR_SERCTL, &tempint, sizeof(tempint), _DEVCTL_FLAG_NOTTY)); } //Set/Clear DTR lines case TIOCCDTR: { int status; if (_devctl(fd, DCMD_CHR_LINESTATUS, &status, sizeof(status), _DEVCTL_FLAG_NOTTY) == -1) return(-1); if (status & _LINESTATUS_SER_DTR) { status = _SERCTL_DTR_CHG; return(_devctl(fd, DCMD_CHR_SERCTL, &status, sizeof(status), _DEVCTL_FLAG_NOTTY)); } return(EOK); } case TIOCSDTR: { int status; if (_devctl(fd, DCMD_CHR_LINESTATUS, &status, sizeof(status), _DEVCTL_FLAG_NOTTY) == -1) return(-1); if (!(status & _LINESTATUS_SER_DTR)) { status = _SERCTL_DTR | _SERCTL_DTR_CHG; return(_devctl(fd, DCMD_CHR_SERCTL, &status, sizeof(status), _DEVCTL_FLAG_NOTTY)); } return(EOK); } default: break; } /* * The following block can go away when 6.4 is released * and <sys/sockio.h> from the networking project is * generally available. */ #ifndef NOSIOCGIFCONF /* Define these as in the latest <sys/sockio.h> */ #define NOSIOCGIFCONF _IOWR('i', 36, struct ifconf) #undef SIOCGIFCONF #define SIOCGIFCONF _IOWR('i', 38, struct ifconf) #endif /* Generic handling for all but SIOCGIFCONF networking IOCTL's */ if ((unsigned)cmd == SIOCGIFCONF || (unsigned)cmd == NOSIOCGIFCONF) { io_devctl_t msg; iov_t wiov[2], riov[3]; struct ifconf *ifconfp = (struct ifconf *)data; msg.i.type = _IO_DEVCTL; msg.i.combine_len = sizeof(msg.i); msg.i.dcmd = cmd; msg.i.zero = 0; msg.i.nbytes = ifconfp->ifc_len; SETIOV (wiov + 0, &msg, sizeof (msg.i)); SETIOV (wiov + 1, data, IOCPARM_LEN((unsigned)cmd)); SETIOV (riov + 0, &msg, sizeof (msg.o)); SETIOV (riov + 1, ifconfp, sizeof (ifconfp->ifc_len)); SETIOV (riov + 2, ifconfp->ifc_buf, ifconfp->ifc_len); return MsgSendv(fd, wiov, 2, riov, 3); } /* These calls require their command types to be translated */ switch ((unsigned)cmd) { case TCGETS: //Not on NetBSD SETNEWNUM(cmd, TIOCGETA); match = 1; break; case TCSETS: //Not on NetBSD SETNEWNUM(cmd, TIOCSETA); match = 1; break; case TCSETSW: //Not on NetBSD SETNEWNUM(cmd, TIOCSETAW); match = 1; break; case TCSETSF: //Not on NetBSD SETNEWNUM(cmd, TIOCSETAF); match = 1; break; case TIOCSETPGRP: //Not on Sun SETNEWNUM(cmd, TIOCSPGRP); match = 1; break; case TIOCGETPGRP: //Not on Sun SETNEWNUM(cmd, TIOCGPGRP); match = 1; break; case TIOCSTOP: case TIOCSTART: //These functions pass in void but we need to pass an int so ... data = &tempint; tempint = (cmd == TIOCSTOP) ? TCOOFF : TIOCSTART; cmd = _IOW(_DCMD_CHR,TCXONC,int); //Create new command case TCXONC: //Not on NetBSD //Assume incoming data already looks like: //data = 0= suspend output = TCOOFF, 1= restart output = TCOON // 2= suspend input = TCIOFF, 3= restart input = TCION SETNEWNUM(cmd, TCXONC); match = 1; break; case TIOCFLUSH: //Need to re-map 0 -> 2, FREAD -> 0, FWRITE -> 1 switch (*((int*)data)) { case 0: *((int*)data) = TCIOFLUSH; break; case FREAD: *((int*)data) = TCIFLUSH; break; case FWRITE: *((int*)data) = TCOFLUSH; break; default: break; } /* Fall Through */ case TCFLSH: //Not on NetBSD //Assume input data looks like: //data = 0 = flush in, 1 = flush output, 2 = flush both SETNEWNUM(cmd, TIOCFLUSH); match = 1; break; default: break; } //If you got this far then out you go as a generic devctl return(_devctl(fd, cmd, data, IOCPARM_LEN((unsigned)cmd), _DEVCTL_FLAG_NOTTY)); /* Returns different things for different commands: - GETPRGRP returns pid_t - FIOREAD returns number of chars in input queue - TIOCOUTQ returns number of chars in output queue - 0 for all other cases */ }
/** * Driver IOCtl entry. * @param cookie The session. * @param op The operation to perform. * @param data The data associated with the operation. * @param len Size of the data in bytes. * * @return Haiku status code. */ static status_t vgdrvHaikuIOCtl(void *cookie, uint32 op, void *data, size_t len) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; int rc; Log(("vgdrvHaikuIOCtl: cookie=%p op=0x%08x data=%p len=%lu)\n", cookie, op, data, len)); /* * Validate the input. */ if (RT_UNLIKELY(!VALID_PTR(pSession))) return EINVAL; /* * Validate the request wrapper. */ #if 0 if (IOCPARM_LEN(ulCmd) != sizeof(VBGLBIGREQ)) { Log((DRIVER_NAME ": vgdrvHaikuIOCtl: bad request %lu size=%lu expected=%d\n", ulCmd, IOCPARM_LEN(ulCmd), sizeof(VBGLBIGREQ))); return ENOTTY; } #endif if (RT_UNLIKELY(len > _1M * 16)) { dprintf(DRIVER_NAME ": vgdrvHaikuIOCtl: bad size %#x; pArg=%p Cmd=%lu.\n", (unsigned)len, data, op); return EINVAL; } /* * Read the request. */ void *pvBuf = NULL; if (RT_LIKELY(len > 0)) { pvBuf = RTMemTmpAlloc(len); if (RT_UNLIKELY(!pvBuf)) { LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", len)); return ENOMEM; } /** @todo r=ramshankar: replace with RTR0MemUserCopyFrom() */ rc = user_memcpy(pvBuf, data, len); if (RT_UNLIKELY(rc < 0)) { RTMemTmpFree(pvBuf); LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: user_memcpy failed; pvBuf=%p data=%p op=%d. rc=%d\n", pvBuf, data, op, rc)); return EFAULT; } if (RT_UNLIKELY(!VALID_PTR(pvBuf))) { RTMemTmpFree(pvBuf); LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: pvBuf invalid pointer %p\n", pvBuf)); return EINVAL; } } Log(("vgdrvHaikuIOCtl: pSession=%p pid=%d.\n", pSession,(int)RTProcSelf())); /* * Process the IOCtl. */ size_t cbDataReturned; rc = VGDrvCommonIoCtl(op, &g_DevExt, pSession, pvBuf, len, &cbDataReturned); if (RT_SUCCESS(rc)) { rc = 0; if (RT_UNLIKELY(cbDataReturned > len)) { Log(("vgdrvHaikuIOCtl: too much output data %d expected %d\n", cbDataReturned, len)); cbDataReturned = len; } if (cbDataReturned > 0) { rc = user_memcpy(data, pvBuf, cbDataReturned); if (RT_UNLIKELY(rc < 0)) { Log(("vgdrvHaikuIOCtl: user_memcpy failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, op, rc)); rc = EFAULT; } } } else { Log(("vgdrvHaikuIOCtl: VGDrvCommonIoCtl failed. rc=%d\n", rc)); rc = EFAULT; } RTMemTmpFree(pvBuf); return rc; }
/** * Deal with the 'slow' I/O control requests. * * @returns 0 on success, appropriate errno on failure. * @param pSession The session. * @param ulCmd The command. * @param pvData The request data. * @param pTd The calling thread. */ static int VBoxDrvFreeBSDIOCtlSlow(PSUPDRVSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd) { PSUPREQHDR pHdr; uint32_t cbReq = IOCPARM_LEN(ulCmd); void *pvUser = NULL; /* * Buffered request? */ if ((IOC_DIRMASK & ulCmd) == IOC_INOUT) { pHdr = (PSUPREQHDR)pvData; if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: cbReq=%#x < %#x; ulCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), ulCmd)); return EINVAL; } if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", pHdr->fFlags, ulCmd)); return EINVAL; } if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq || pHdr->cbIn < sizeof(*pHdr) || pHdr->cbOut < sizeof(*pHdr))) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x) != %#x; ulCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, ulCmd)); return EINVAL; } } /* * Big unbuffered request? */ else if ((IOC_DIRMASK & ulCmd) == IOC_VOID && !cbReq) { /* * Read the header, validate it and figure out how much that needs to be buffered. */ SUPREQHDR Hdr; pvUser = *(void **)pvData; int rc = copyin(pvUser, &Hdr, sizeof(Hdr)); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,Hdr,) -> %#x; ulCmd=%#lx\n", pvUser, rc, ulCmd)); return rc; } if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: bad magic fFlags=%#x; ulCmd=%#lx\n", Hdr.fFlags, ulCmd)); return EINVAL; } cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbOut < sizeof(Hdr) || cbReq > _1M*16)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: max(%#x,%#x); ulCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, ulCmd)); return EINVAL; } /* * Allocate buffer and copy in the data. */ pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq); if (RT_UNLIKELY(!pHdr)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: failed to allocate buffer of %d bytes; ulCmd=%#lx\n", cbReq, ulCmd)); return ENOMEM; } rc = copyin(pvUser, pHdr, Hdr.cbIn); if (RT_UNLIKELY(rc)) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyin(%p,%p,%#x) -> %#x; ulCmd=%#lx\n", pvUser, pHdr, Hdr.cbIn, rc, ulCmd)); RTMemTmpFree(pHdr); return rc; } } else { Log(("VBoxDrvFreeBSDIOCtlSlow: huh? cbReq=%#x ulCmd=%#lx\n", cbReq, ulCmd)); return EINVAL; } /* * Process the IOCtl. */ int rc = supdrvIOCtl(ulCmd, &g_VBoxDrvFreeBSDDevExt, pSession, pHdr); if (RT_LIKELY(!rc)) { /* * If unbuffered, copy back the result before returning. */ if (pvUser) { uint32_t cbOut = pHdr->cbOut; if (cbOut > cbReq) { OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, ulCmd)); cbOut = cbReq; } rc = copyout(pHdr, pvUser, cbOut); if (RT_UNLIKELY(rc)) OSDBGPRINT(("VBoxDrvFreeBSDIOCtlSlow: copyout(%p,%p,%#x) -> %d; uCmd=%#lx!\n", pHdr, pvUser, cbOut, rc, ulCmd)); Log(("VBoxDrvFreeBSDIOCtlSlow: returns %d / %d ulCmd=%lx\n", 0, pHdr->rc, ulCmd)); /* cleanup */ RTMemTmpFree(pHdr); } } else { /* * The request failed, just clean up. */ if (pvUser) RTMemTmpFree(pHdr); Log(("VBoxDrvFreeBSDIOCtlSlow: ulCmd=%lx pData=%p failed, rc=%d\n", ulCmd, pvData, rc)); rc = EINVAL; } return rc; }
static int acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg) { union acpi_battery_ioctl_arg *ioctl_arg; int error, unit; device_t dev; /* For commands that use the ioctl_arg struct, validate it first. */ error = ENXIO; unit = 0; dev = NULL; ioctl_arg = NULL; if (IOCPARM_LEN(cmd) == sizeof(*ioctl_arg)) { ioctl_arg = (union acpi_battery_ioctl_arg *)addr; unit = ioctl_arg->unit; if (unit != ACPI_BATTERY_ALL_UNITS) dev = acpi_battery_find_dev(unit); } /* * No security check required: information retrieval only. If * new functions are added here, a check might be required. */ switch (cmd) { case ACPIIO_BATT_GET_UNITS: *(int *)addr = acpi_battery_get_units(); error = 0; break; case ACPIIO_BATT_GET_BATTINFO: if (dev != NULL || unit == ACPI_BATTERY_ALL_UNITS) { bzero(&ioctl_arg->battinfo, sizeof(ioctl_arg->battinfo)); error = acpi_battery_get_battinfo(dev, &ioctl_arg->battinfo); } break; case ACPIIO_BATT_GET_BIF: if (dev != NULL) { bzero(&ioctl_arg->bif, sizeof(ioctl_arg->bif)); error = ACPI_BATT_GET_INFO(dev, &ioctl_arg->bif); /* * Remove invalid characters. Perhaps this should be done * within a convenience function so all callers get the * benefit. */ acpi_battery_clean_str(ioctl_arg->bif.model, sizeof(ioctl_arg->bif.model)); acpi_battery_clean_str(ioctl_arg->bif.serial, sizeof(ioctl_arg->bif.serial)); acpi_battery_clean_str(ioctl_arg->bif.type, sizeof(ioctl_arg->bif.type)); acpi_battery_clean_str(ioctl_arg->bif.oeminfo, sizeof(ioctl_arg->bif.oeminfo)); } break; case ACPIIO_BATT_GET_BST: if (dev != NULL) { bzero(&ioctl_arg->bst, sizeof(ioctl_arg->bst)); error = ACPI_BATT_GET_STATUS(dev, &ioctl_arg->bst); } break; default: error = EINVAL; } return (error); }
/** * Deal with the 'slow' I/O control requests. * * @returns 0 on success, appropriate errno on failure. * @param pSession The session. * @param ulCmd The command. * @param pvData The request data. * @param pTd The calling thread. */ static int vgdrvFreeBSDIOCtlSlow(PVBOXGUESTSESSION pSession, u_long ulCmd, caddr_t pvData, struct thread *pTd) { PVBGLREQHDR pHdr; uint32_t cbReq = IOCPARM_LEN(ulCmd); void *pvUser = NULL; /* * Buffered request? */ if ((IOC_DIRMASK & ulCmd) == IOC_INOUT) { pHdr = (PVBGLREQHDR)pvData; if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) { LogRel(("vgdrvFreeBSDIOCtlSlow: cbReq=%#x < %#x; ulCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), ulCmd)); return EINVAL; } if (RT_UNLIKELY(pHdr->uVersion != VBGLREQHDR_VERSION)) { LogRel(("vgdrvFreeBSDIOCtlSlow: bad uVersion=%#x; ulCmd=%#lx\n", pHdr->uVersion, ulCmd)); return EINVAL; } if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq || pHdr->cbIn < sizeof(*pHdr) || (pHdr->cbOut < sizeof(*pHdr) && pHdr->cbOut != 0))) { LogRel(("vgdrvFreeBSDIOCtlSlow: max(%#x,%#x) != %#x; ulCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, ulCmd)); return EINVAL; } } /* * Big unbuffered request? */ else if ((IOC_DIRMASK & ulCmd) == IOC_VOID && !cbReq) { /* * Read the header, validate it and figure out how much that needs to be buffered. */ VBGLREQHDR Hdr; pvUser = *(void **)pvData; int rc = copyin(pvUser, &Hdr, sizeof(Hdr)); if (RT_UNLIKELY(rc)) { LogRel(("vgdrvFreeBSDIOCtlSlow: copyin(%p,Hdr,) -> %#x; ulCmd=%#lx\n", pvUser, rc, ulCmd)); return rc; } if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION)) { LogRel(("vgdrvFreeBSDIOCtlSlow: bad uVersion=%#x; ulCmd=%#lx\n", Hdr.uVersion, ulCmd)); return EINVAL; } cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || (Hdr.cbOut < sizeof(Hdr) && Hdr.cbOut != 0) || cbReq > _1M*16)) { LogRel(("vgdrvFreeBSDIOCtlSlow: max(%#x,%#x); ulCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, ulCmd)); return EINVAL; } /* * Allocate buffer and copy in the data. */ pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq); if (RT_UNLIKELY(!pHdr)) { LogRel(("vgdrvFreeBSDIOCtlSlow: failed to allocate buffer of %d bytes; ulCmd=%#lx\n", cbReq, ulCmd)); return ENOMEM; } rc = copyin(pvUser, pHdr, Hdr.cbIn); if (RT_UNLIKELY(rc)) { LogRel(("vgdrvFreeBSDIOCtlSlow: copyin(%p,%p,%#x) -> %#x; ulCmd=%#lx\n", pvUser, pHdr, Hdr.cbIn, rc, ulCmd)); RTMemTmpFree(pHdr); return rc; } if (Hdr.cbIn < cbReq) RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn); } else { Log(("vgdrvFreeBSDIOCtlSlow: huh? cbReq=%#x ulCmd=%#lx\n", cbReq, ulCmd)); return EINVAL; } /* * Process the IOCtl. */ int rc = VGDrvCommonIoCtl(ulCmd, &g_DevExt, pSession, pHdr, cbReq); if (RT_LIKELY(!rc)) { /* * If unbuffered, copy back the result before returning. */ if (pvUser) { uint32_t cbOut = pHdr->cbOut; if (cbOut > cbReq) { LogRel(("vgdrvFreeBSDIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, ulCmd)); cbOut = cbReq; } rc = copyout(pHdr, pvUser, cbOut); if (RT_UNLIKELY(rc)) LogRel(("vgdrvFreeBSDIOCtlSlow: copyout(%p,%p,%#x) -> %d; uCmd=%#lx!\n", pHdr, pvUser, cbOut, rc, ulCmd)); Log(("vgdrvFreeBSDIOCtlSlow: returns %d / %d ulCmd=%lx\n", 0, pHdr->rc, ulCmd)); /* cleanup */ RTMemTmpFree(pHdr); } } else { /* * The request failed, just clean up. */ if (pvUser) RTMemTmpFree(pHdr); Log(("vgdrvFreeBSDIOCtlSlow: ulCmd=%lx pData=%p failed, rc=%d\n", ulCmd, pvData, rc)); rc = EINVAL; } return rc; }
/* * main ioctl syscall. * * ok, here we are in the biggy. we have to do fix ups depending * on the ioctl command before and afterwards. */ int netbsd32_ioctl(struct lwp *l, const struct netbsd32_ioctl_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(netbsd32_u_long) com; syscallarg(netbsd32_voidp) data; } */ struct proc *p = l->l_proc; struct file *fp; struct filedesc *fdp; u_long com; int error = 0; u_int size, size32; void *data, *memp = NULL; void *data32, *memp32 = NULL; unsigned fd; fdfile_t *ff; int tmp; #define STK_PARAMS 128 u_long stkbuf[STK_PARAMS/sizeof(u_long)]; u_long stkbuf32[STK_PARAMS/sizeof(u_long)]; /* * we need to translate some commands (_IOW) before calling sys_ioctl, * some after (_IOR), and some both (_IOWR). */ #if 0 { char *dirs[8] = { "NONE!", "VOID", "OUT", "VOID|OUT!", "IN", "VOID|IN!", "INOUT", "VOID|IN|OUT!" }; printf("netbsd32_ioctl(%d, %x, %x): %s group %c base %d len %d\n", SCARG(uap, fd), SCARG(uap, com), SCARG(uap, data), dirs[((SCARG(uap, com) & IOC_DIRMASK)>>29)], IOCGROUP(SCARG(uap, com)), IOCBASECMD(SCARG(uap, com)), IOCPARM_LEN(SCARG(uap, com))); } #endif fdp = p->p_fd; fd = SCARG(uap, fd); if ((fp = fd_getfile(fd)) == NULL) return (EBADF); if ((fp->f_flag & (FREAD | FWRITE)) == 0) { error = EBADF; goto out; } ff = fdp->fd_ofiles[SCARG(uap, fd)]; switch (com = SCARG(uap, com)) { case FIOCLEX: ff->ff_exclose = true; fdp->fd_exclose = true; goto out; case FIONCLEX: ff->ff_exclose = false; goto out; } /* * Interpret high order word to find amount of data to be * copied to/from the user's address space. */ size = 0; size32 = IOCPARM_LEN(com); if (size32 > IOCPARM_MAX) { error = ENOTTY; goto out; } if (size32 > sizeof(stkbuf)) { memp32 = kmem_alloc((size_t)size32, KM_SLEEP); data32 = memp32; } else data32 = (void *)stkbuf32; if (com&IOC_IN) { if (size32) { error = copyin(SCARG_P32(uap, data), data32, size32); if (error) { if (memp32) kmem_free(memp32, (size_t)size32); goto out; } ktrgenio(fd, UIO_WRITE, SCARG_P32(uap, data), size32, 0); } else *(void **)data32 = SCARG_P32(uap, data); } else if ((com&IOC_OUT) && size32) /* * Zero the buffer so the user always * gets back something deterministic. */ memset(data32, 0, size32); else if (com&IOC_VOID) *(void **)data32 = SCARG_P32(uap, data); /* * convert various structures, pointers, and other objects that * change size from 32 bit -> 64 bit, for all ioctl commands. */ switch (SCARG(uap, com)) { case FIONBIO: mutex_enter(&fp->f_lock); if ((tmp = *(int *)data32) != 0) fp->f_flag |= FNONBLOCK; else fp->f_flag &= ~FNONBLOCK; mutex_exit(&fp->f_lock); error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (void *)&tmp); break; case FIOASYNC: mutex_enter(&fp->f_lock); if ((tmp = *(int *)data32) != 0) fp->f_flag |= FASYNC; else fp->f_flag &= ~FASYNC; mutex_exit(&fp->f_lock); error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (void *)&tmp); break; case DIOCGPART32: IOCTL_STRUCT_CONV_TO(DIOCGPART, partinfo); #if 0 /* not implemented by anything */ case DIOCRFORMAT32: IOCTL_STRUCT_CONV_TO(DIOCRFORMAT, format_op); case DIOCWFORMAT32: IOCTL_STRUCT_CONV_TO(DIOCWFORMAT, format_op); #endif /* * only a few ifreq syscalls need conversion and those are * all driver specific... XXX */ #if 0 case SIOCGADDRROM3232: IOCTL_STRUCT_CONV_TO(SIOCGADDRROM32, ifreq); case SIOCGCHIPID32: IOCTL_STRUCT_CONV_TO(SIOCGCHIPID, ifreq); case SIOCSIFADDR32: IOCTL_STRUCT_CONV_TO(SIOCSIFADDR, ifreq); case OSIOCGIFADDR32: IOCTL_STRUCT_CONV_TO(OSIOCGIFADDR, ifreq); case SIOCGIFADDR32: IOCTL_STRUCT_CONV_TO(SIOCGIFADDR, ifreq); case SIOCSIFDSTADDR32: IOCTL_STRUCT_CONV_TO(SIOCSIFDSTADDR, ifreq); case OSIOCGIFDSTADDR32: IOCTL_STRUCT_CONV_TO(OSIOCGIFDSTADDR, ifreq); case SIOCGIFDSTADDR32: IOCTL_STRUCT_CONV_TO(SIOCGIFDSTADDR, ifreq); case OSIOCGIFBRDADDR32: IOCTL_STRUCT_CONV_TO(OSIOCGIFBRDADDR, ifreq); case SIOCGIFBRDADDR32: IOCTL_STRUCT_CONV_TO(SIOCGIFBRDADDR, ifreq); case SIOCSIFBRDADDR32: IOCTL_STRUCT_CONV_TO(SIOCSIFBRDADDR, ifreq); case OSIOCGIFNETMASK32: IOCTL_STRUCT_CONV_TO(OSIOCGIFNETMASK, ifreq); case SIOCGIFNETMASK32: IOCTL_STRUCT_CONV_TO(SIOCGIFNETMASK, ifreq); case SIOCSIFNETMASK32: IOCTL_STRUCT_CONV_TO(SIOCSIFNETMASK, ifreq); case SIOCGIFMETRIC32: IOCTL_STRUCT_CONV_TO(SIOCGIFMETRIC, ifreq); case SIOCSIFMETRIC32: IOCTL_STRUCT_CONV_TO(SIOCSIFMETRIC, ifreq); case SIOCDIFADDR32: IOCTL_STRUCT_CONV_TO(SIOCDIFADDR, ifreq); case SIOCADDMULTI32: IOCTL_STRUCT_CONV_TO(SIOCADDMULTI, ifreq); case SIOCDELMULTI32: IOCTL_STRUCT_CONV_TO(SIOCDELMULTI, ifreq); case SIOCSIFMEDIA32: IOCTL_STRUCT_CONV_TO(SIOCSIFMEDIA, ifreq); case SIOCSIFMTU32: IOCTL_STRUCT_CONV_TO(SIOCSIFMTU, ifreq); case SIOCGIFMTU32: IOCTL_STRUCT_CONV_TO(SIOCGIFMTU, ifreq); case BIOCGETIF32: IOCTL_STRUCT_CONV_TO(BIOCGETIF, ifreq); case BIOCSETIF32: IOCTL_STRUCT_CONV_TO(BIOCSETIF, ifreq); case SIOCPHASE132: IOCTL_STRUCT_CONV_TO(SIOCPHASE1, ifreq); case SIOCPHASE232: IOCTL_STRUCT_CONV_TO(SIOCPHASE2, ifreq); #endif case OOSIOCGIFCONF32: IOCTL_STRUCT_CONV_TO(OOSIOCGIFCONF, ifconf); case OSIOCGIFCONF32: IOCTL_STRUCT_CONV_TO(OSIOCGIFCONF, ifconf); case SIOCGIFCONF32: IOCTL_STRUCT_CONV_TO(SIOCGIFCONF, ifconf); case SIOCGIFFLAGS32: IOCTL_STRUCT_CONV_TO(SIOCGIFFLAGS, ifreq); case SIOCSIFFLAGS32: IOCTL_STRUCT_CONV_TO(SIOCSIFFLAGS, ifreq); case SIOCGIFMEDIA32: IOCTL_STRUCT_CONV_TO(SIOCGIFMEDIA, ifmediareq); case SIOCSDRVSPEC32: IOCTL_STRUCT_CONV_TO(SIOCSDRVSPEC, ifdrv); case SIOCGETVIFCNT32: IOCTL_STRUCT_CONV_TO(SIOCGETVIFCNT, sioc_vif_req); case SIOCGETSGCNT32: IOCTL_STRUCT_CONV_TO(SIOCGETSGCNT, sioc_sg_req); default: #ifdef NETBSD32_MD_IOCTL error = netbsd32_md_ioctl(fp, com, data32, l); #else error = (*fp->f_ops->fo_ioctl)(fp, com, data32); #endif break; } if (error == EPASSTHROUGH) error = ENOTTY; /* * Copy any data to user, size was * already set and checked above. */ if (error == 0 && (com&IOC_OUT) && size32) { error = copyout(data32, SCARG_P32(uap, data), size32); ktrgenio(fd, UIO_READ, SCARG_P32(uap, data), size32, error); } /* if we malloced data, free it here */ if (memp32) kmem_free(memp32, (size_t)size32); if (memp) kmem_free(memp, (size_t)size); out: fd_putfile(fd); return (error); }
static int VBoxUSBMonSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal) { LogFunc((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg)); /* * Get the session from the soft state item. */ vboxusbmon_state_t *pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, getminor(Dev)); if (!pState) { LogRel((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: no state data for %d\n", getminor(Dev))); return EINVAL; } /* * Read the request wrapper. Though We don't really need wrapper struct. now * it's room for the future as Solaris isn't generous regarding the size. */ VBOXUSBREQ ReqWrap; if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap)) { LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd), sizeof(ReqWrap))); return ENOTTY; } int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode); if (RT_UNLIKELY(rc)) { LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d.\n", pArg, Cmd, rc)); return EINVAL; } if (ReqWrap.u32Magic != VBOXUSBMON_MAGIC) { LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: bad magic %#x; pArg=%p Cmd=%d.\n", ReqWrap.u32Magic, pArg, Cmd)); return EINVAL; } if (RT_UNLIKELY( ReqWrap.cbData == 0 || ReqWrap.cbData > _1M*16)) { LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: bad size %#x; pArg=%p Cmd=%d.\n", ReqWrap.cbData, pArg, Cmd)); return EINVAL; } /* * Read the request. */ void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData); if (RT_UNLIKELY(!pvBuf)) { LogRel((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap.cbData)); return ENOMEM; } rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode); if (RT_UNLIKELY(rc)) { RTMemTmpFree(pvBuf); LogRel((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc)); return EFAULT; } if (RT_UNLIKELY( ReqWrap.cbData != 0 && !VALID_PTR(pvBuf))) { RTMemTmpFree(pvBuf); LogRel((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: pvBuf invalid pointer %p\n", pvBuf)); return EINVAL; } Log((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: pid=%d.\n", (int)RTProcSelf())); /* * Process the IOCtl. */ size_t cbDataReturned; rc = vboxUSBMonSolarisProcessIOCtl(Cmd, pState, pvBuf, ReqWrap.cbData, &cbDataReturned); ReqWrap.rc = rc; rc = 0; if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData)) { LogRel((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData)); cbDataReturned = ReqWrap.cbData; } ReqWrap.cbData = cbDataReturned; /* * Copy the request back to user space. */ rc = ddi_copyout(&ReqWrap, (void *)pArg, sizeof(ReqWrap), Mode); if (RT_LIKELY(!rc)) { /* * Copy the payload (if any) back to user space. */ if (cbDataReturned > 0) { rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode); if (RT_UNLIKELY(rc)) { LogRel((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: ddi_copyout failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc)); rc = EFAULT; } } } else { LogRel((DEVICE_NAME ":VBoxUSBMonSolarisIOCtl: ddi_copyout(1) failed pArg=%p Cmd=%d\n", pArg, Cmd)); rc = EFAULT; } *pVal = rc; RTMemTmpFree(pvBuf); return rc; }
static int acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg) { union acpi_battery_ioctl_arg *ioctl_arg; int error, unit; device_t dev; /* * Giant is acquired to work around a reference counting bug in ACPICA * versions prior to 20130328. If not for that bug this function could * be executed concurrently without any problems. * The bug is in acpi_BatteryIsPresent -> AcpiGetObjectInfo call tree, * where AcpiUtExecute_HID, AcpiUtExecute_UID, etc are executed without * protection of any ACPICA lock and may concurrently call * AcpiUtRemoveReference on a battery object. */ mtx_lock(&Giant); /* For commands that use the ioctl_arg struct, validate it first. */ error = ENXIO; unit = 0; dev = NULL; ioctl_arg = NULL; if (IOCPARM_LEN(cmd) == sizeof(*ioctl_arg)) { ioctl_arg = (union acpi_battery_ioctl_arg *)addr; unit = ioctl_arg->unit; if (unit != ACPI_BATTERY_ALL_UNITS) dev = acpi_battery_find_dev(unit); } /* * No security check required: information retrieval only. If * new functions are added here, a check might be required. */ switch (cmd) { case ACPIIO_BATT_GET_UNITS: *(int *)addr = acpi_battery_get_units(); error = 0; break; case ACPIIO_BATT_GET_BATTINFO: if (dev != NULL || unit == ACPI_BATTERY_ALL_UNITS) { bzero(&ioctl_arg->battinfo, sizeof(ioctl_arg->battinfo)); error = acpi_battery_get_battinfo(dev, &ioctl_arg->battinfo); } break; case ACPIIO_BATT_GET_BIF: if (dev != NULL) { bzero(&ioctl_arg->bif, sizeof(ioctl_arg->bif)); error = ACPI_BATT_GET_INFO(dev, &ioctl_arg->bif); /* * Remove invalid characters. Perhaps this should be done * within a convenience function so all callers get the * benefit. */ acpi_battery_clean_str(ioctl_arg->bif.model, sizeof(ioctl_arg->bif.model)); acpi_battery_clean_str(ioctl_arg->bif.serial, sizeof(ioctl_arg->bif.serial)); acpi_battery_clean_str(ioctl_arg->bif.type, sizeof(ioctl_arg->bif.type)); acpi_battery_clean_str(ioctl_arg->bif.oeminfo, sizeof(ioctl_arg->bif.oeminfo)); } break; case ACPIIO_BATT_GET_BST: if (dev != NULL) { bzero(&ioctl_arg->bst, sizeof(ioctl_arg->bst)); error = ACPI_BATT_GET_STATUS(dev, &ioctl_arg->bst); } break; default: error = EINVAL; } mtx_unlock(&Giant); return (error); }