static int vboxguestLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg) #endif { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data; uint32_t cbData = _IOC_SIZE(uCmd); void *pvBufFree; void *pvBuf; int rc; uint64_t au64Buf[32/sizeof(uint64_t)]; Log6(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid)); /* * Buffer the request. */ if (cbData <= sizeof(au64Buf)) { pvBufFree = NULL; pvBuf = &au64Buf[0]; } else { pvBufFree = pvBuf = RTMemTmpAlloc(cbData); if (RT_UNLIKELY(!pvBuf)) { LogRel((DEVICE_NAME "::IOCtl: RTMemTmpAlloc failed to alloc %u bytes.\n", cbData)); return -ENOMEM; } } if (RT_LIKELY(copy_from_user(pvBuf, (void *)ulArg, cbData) == 0)) { /* * Process the IOCtl. */ size_t cbDataReturned; rc = VBoxGuestCommonIOCtl(uCmd, &g_DevExt, pSession, pvBuf, cbData, &cbDataReturned); /* * Copy ioctl data and output buffer back to user space. */ if (RT_SUCCESS(rc)) { rc = 0; if (RT_UNLIKELY(cbDataReturned > cbData)) { LogRel((DEVICE_NAME "::IOCtl: too much output data %u expected %u\n", cbDataReturned, cbData)); cbDataReturned = cbData; } if (cbDataReturned > 0) { if (RT_UNLIKELY(copy_to_user((void *)ulArg, pvBuf, cbDataReturned) != 0)) { LogRel((DEVICE_NAME "::IOCtl: copy_to_user failed; pvBuf=%p ulArg=%p cbDataReturned=%u uCmd=%d\n", pvBuf, (void *)ulArg, cbDataReturned, uCmd, rc)); rc = -EFAULT; } } } else { Log(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc)); rc = -rc; Assert(rc > 0); /* Positive returns == negated VBox error status codes. */ } } else { Log((DEVICE_NAME "::IOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x.\n", ulArg, cbData, uCmd)); rc = -EFAULT; } if (pvBufFree) RTMemFree(pvBufFree); Log6(("vboxguestLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid)); return rc; }
/** Calls the kernel IOCtl to report mouse status to the host on behalf of * our kernel session. */ static int vboxguestLinuxSetMouseStatus(uint32_t fStatus) { return VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &g_DevExt, g_pKernelSession, &fStatus, sizeof(fStatus), NULL); }
/** * 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; }
/** * 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; }
/** * Device I/O Control entry point. * * @param pDevObj Device object. * @param pIrp Request packet. */ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS Status = STATUS_SUCCESS; PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode; char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* All requests are buffered. */ size_t cbData = pStack->Parameters.DeviceIoControl.InputBufferLength; unsigned cbOut = 0; /* Do we have a file object associated?*/ PFILE_OBJECT pFileObj = pStack->FileObject; PVBOXGUESTSESSION pSession = NULL; if (pFileObj) /* ... then we might have a session object as well! */ pSession = (PVBOXGUESTSESSION)pFileObj->FsContext; Log(("VBoxGuest::vboxguestwinIOCtl: uCmd=%u, pDevExt=0x%p, pSession=0x%p\n", uCmd, pDevExt, pSession)); /* We don't have a session associated with the file object? So this seems * to be a kernel call then. */ /** @todo r=bird: What on earth is this supposed to be? Each kernel session * shall have its own context of course, no hacks, pleeease. */ if (pSession == NULL) { Log(("VBoxGuest::vboxguestwinIOCtl: Using kernel session data ...\n")); pSession = pDevExt->win.s.pKernelSession; } /* * First process Windows specific stuff which cannot be handled * by the common code used on all other platforms. In the default case * we then finally handle the common cases. */ switch (uCmd) { #ifdef VBOX_WITH_VRDP_SESSION_HANDLING case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION: { LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis")); if (!pDevExt->fVRDPEnabled) { KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA; pDevExt->fVRDPEnabled = TRUE; LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Current active console ID: 0x%08X\n", pSharedUserData->ActiveConsoleId)); pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId; pSharedUserData->ActiveConsoleId = 2; } break; } case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION: { LogRel(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis")); if (pDevExt->fVRDPEnabled) { KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA; pDevExt->fVRDPEnabled = FALSE; Log(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Current active console ID: 0x%08X\n", pSharedUserData->ActiveConsoleId)); pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId; pDevExt->ulOldActiveConsoleId = 0; } break; } #else /* Add at least one (bogus) fall through case to shut up MSVC! */ case 0: #endif default: { /* * Process the common IOCtls. */ size_t cbDataReturned; int vrc = VBoxGuestCommonIOCtl(uCmd, pDevExt, pSession, pBuf, cbData, &cbDataReturned); Log(("VBoxGuest::vboxguestwinGuestDeviceControl: rc=%Rrc, pBuf=0x%p, cbData=%u, cbDataReturned=%u\n", vrc, pBuf, cbData, cbDataReturned)); if (RT_SUCCESS(vrc)) { if (RT_UNLIKELY(cbDataReturned > cbData)) { Log(("VBoxGuest::vboxguestwinGuestDeviceControl: Too much output data %u - expected %u!\n", cbDataReturned, cbData)); cbDataReturned = cbData; Status = STATUS_BUFFER_TOO_SMALL; } if (cbDataReturned > 0) cbOut = cbDataReturned; } else { if ( vrc == VERR_NOT_SUPPORTED || vrc == VERR_INVALID_PARAMETER) { Status = STATUS_INVALID_PARAMETER; } else Status = STATUS_UNSUCCESSFUL; } break; } } pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = cbOut; IoCompleteRequest(pIrp, IO_NO_INCREMENT); //Log(("VBoxGuest::vboxguestwinGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status)); return Status; }
/** * 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 VBoxGuestHaikuIOCtl(void *cookie, uint32 op, void *data, size_t len) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl cookie=%p op=0x%08x data=%p len=%lu)\n", cookie, op, data, len)); int rc = B_OK; /* * 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 ": VBoxGuestHaikuIOCtl: 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 ": VBoxGuestHaikuIOCtl: 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 ":VBoxGuestHaikuIOCtl: 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 ":VBoxGuestHaikuIOCtl: 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 ":VBoxGuestHaikuIOCtl: pvBuf invalid pointer %p\n", pvBuf)); return EINVAL; } } Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl: pSession=%p pid=%d.\n", pSession,(int)RTProcSelf())); /* * Process the IOCtl. */ size_t cbDataReturned; rc = VBoxGuestCommonIOCtl(op, &g_DevExt, pSession, pvBuf, len, &cbDataReturned); if (RT_SUCCESS(rc)) { rc = 0; if (RT_UNLIKELY(cbDataReturned > len)) { Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl: 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((DRIVER_NAME ":VBoxGuestHaikuIOCtl: user_memcpy failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, op, rc)); rc = EFAULT; } } } else { Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl: VBoxGuestCommonIOCtl failed. rc=%d\n", rc)); rc = EFAULT; } RTMemTmpFree(pvBuf); return rc; }