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);
}
Пример #3
0
/**
 * 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;
}
Пример #4
0
/**
 * 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;
}
Пример #6
0
/**
 * 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;
}