SUPLib-win.cpp revision 304e26e9a9c1422dbc299ed139eceacca271a9dd
/* $Id$ */
/** @file
* VirtualBox Support Library - Windows NT specific parts.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP
#ifdef IN_SUP_HARDENED_R3
# undef DEBUG /* Warning: disables RT_STRICT */
# define LOG_DISABLED
/** @todo RTLOGREL_DISABLED */
# include <iprt/log.h>
# undef LogRelIt
# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0)
#endif
#define USE_NT_DEVICE_IO_CONTROL_FILE
#ifdef USE_NT_DEVICE_IO_CONTROL_FILE
# include <iprt/nt/nt-and-windows.h>
#else
# include <Windows.h>
#endif
#include <VBox/sup.h>
#include <VBox/types.h>
#include <VBox/err.h>
#include <VBox/param.h>
#include <VBox/log.h>
#include <iprt/assert.h>
#include <iprt/path.h>
#include <iprt/string.h>
#include "../SUPLibInternal.h"
#include "../SUPDrvIOC.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The support service name. */
#define SERVICE_NAME "VBoxDrv"
/** Win32 Device name - system. */
#define DEVICE_NAME_SYS "\\\\.\\VBoxDrv"
/** Win32 Device name - user. */
#define DEVICE_NAME_USR "\\\\.\\VBoxDrvU"
/** NT Device name. */
#define DEVICE_NAME_NT L"\\Device\\VBoxDrv"
/** Win32 Symlink name. */
#define DEVICE_NAME_DOS L"\\DosDevices\\VBoxDrv"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int suplibOsCreateService(void);
//unused: static int suplibOsUpdateService(void);
static int suplibOsDeleteService(void);
static int suplibOsStartService(void);
static int suplibOsStopService(void);
#ifdef USE_NT_DEVICE_IO_CONTROL_FILE
static int suplibConvertNtStatus(NTSTATUS rcNt);
#else
static int suplibConvertWin32Err(int);
#endif
int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, bool fUnrestricted)
{
/*
* Nothing to do if pre-inited.
*/
if (fPreInited)
return VINF_SUCCESS;
/*
* Try open the device.
*/
HANDLE hDevice = CreateFile(fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
#ifndef IN_SUP_HARDENED_R3
/*
* Try start the service and retry opening it.
*/
suplibOsStartService();
hDevice = CreateFile(fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (hDevice == INVALID_HANDLE_VALUE)
#endif /* !IN_SUP_HARDENED_R3 */
{
int rc = GetLastError();
switch (rc)
{
/** @todo someone must test what is actually returned. */
case ERROR_DEV_NOT_EXIST:
case ERROR_DEVICE_NOT_CONNECTED:
case ERROR_BAD_DEVICE:
case ERROR_DEVICE_REMOVED:
case ERROR_DEVICE_NOT_AVAILABLE:
return VERR_VM_DRIVER_LOAD_ERROR;
case ERROR_PATH_NOT_FOUND:
case ERROR_FILE_NOT_FOUND:
return VERR_VM_DRIVER_NOT_INSTALLED;
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
return VERR_VM_DRIVER_NOT_ACCESSIBLE;
default:
return VERR_VM_DRIVER_OPEN_ERROR;
}
return -1 /** @todo define proper error codes for suplibOsInit failure. */;
}
}
/*
* We're done.
*/
pThis->hDevice = hDevice;
pThis->fUnrestricted = fUnrestricted;
return VINF_SUCCESS;
}
#ifndef IN_SUP_HARDENED_R3
int suplibOsInstall(void)
{
return suplibOsCreateService();
}
int suplibOsUninstall(void)
{
int rc = suplibOsStopService();
if (!rc)
rc = suplibOsDeleteService();
return rc;
}
/**
* Creates the service.
*
* @returns 0 on success.
* @returns -1 on failure.
*/
static int suplibOsCreateService(void)
{
/*
* Assume it didn't exist, so we'll create the service.
*/
SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed rc=%d\n", LastError));
if (hSMgrCreate)
{
char szDriver[RTPATH_MAX];
int rc = RTPathExecDir(szDriver, sizeof(szDriver) - sizeof("\\VBoxDrv.sys"));
if (RT_SUCCESS(rc))
{
strcat(szDriver, "\\VBoxDrv.sys");
SC_HANDLE hService = CreateService(hSMgrCreate,
SERVICE_NAME,
"VBox Support Driver",
SERVICE_QUERY_STATUS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
szDriver,
NULL, NULL, NULL, NULL, NULL);
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsg(hService, ("CreateService failed! LastError=%Rwa szDriver=%s\n", LastError, szDriver));
CloseServiceHandle(hService);
CloseServiceHandle(hSMgrCreate);
return hService ? 0 : -1;
}
CloseServiceHandle(hSMgrCreate);
return rc;
}
return -1;
}
/**
* Stops a possibly running service.
*
* @returns 0 on success.
* @returns -1 on failure.
*/
static int suplibOsStopService(void)
{
/*
* Assume it didn't exist, so we'll create the service.
*/
int rc = -1;
SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS);
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", LastError));
if (hSMgr)
{
SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
if (hService)
{
/*
* Stop the service.
*/
SERVICE_STATUS Status;
QueryServiceStatus(hService, &Status);
if (Status.dwCurrentState == SERVICE_STOPPED)
rc = 0;
else if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
{
int iWait = 100;
while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
{
Sleep(100);
QueryServiceStatus(hService, &Status);
}
if (Status.dwCurrentState == SERVICE_STOPPED)
rc = 0;
else
AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState));
}
else
{
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsgFailed(("ControlService failed with LastError=%Rwa. status=%d\n", LastError, Status.dwCurrentState));
}
CloseServiceHandle(hService);
}
else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
rc = 0;
else
{
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsgFailed(("OpenService failed LastError=%Rwa\n", LastError));
}
CloseServiceHandle(hSMgr);
}
return rc;
}
/**
* Deletes the service.
*
* @returns 0 on success.
* @returns -1 on failure.
*/
int suplibOsDeleteService(void)
{
/*
* Assume it didn't exist, so we'll create the service.
*/
int rc = -1;
SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", LastError));
if (hSMgr)
{
SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, DELETE);
if (hService)
{
/*
* Delete the service.
*/
if (DeleteService(hService))
rc = 0;
else
{
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsgFailed(("DeleteService failed LastError=%Rwa\n", LastError));
}
CloseServiceHandle(hService);
}
else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
rc = 0;
else
{
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsgFailed(("OpenService failed LastError=%Rwa\n", LastError));
}
CloseServiceHandle(hSMgr);
}
return rc;
}
#if 0
/**
* Creates the service.
*
* @returns 0 on success.
* @returns -1 on failure.
*/
static int suplibOsUpdateService(void)
{
/*
* Assume it didn't exist, so we'll create the service.
*/
SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed LastError=%Rwa\n", LastError));
if (hSMgr)
{
SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_CHANGE_CONFIG);
if (hService)
{
char szDriver[RTPATH_MAX];
int rc = RTPathExecDir(szDriver, sizeof(szDriver) - sizeof("\\VBoxDrv.sys"));
if (RT_SUCCESS(rc))
{
strcat(szDriver, "\\VBoxDrv.sys");
SC_LOCK hLock = LockServiceDatabase(hSMgr);
if (ChangeServiceConfig(hService,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
szDriver,
NULL, NULL, NULL, NULL, NULL, NULL))
{
UnlockServiceDatabase(hLock);
CloseServiceHandle(hService);
CloseServiceHandle(hSMgr);
return 0;
}
else
{
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsgFailed(("ChangeServiceConfig failed LastError=%Rwa\n", LastError));
}
}
UnlockServiceDatabase(hLock);
CloseServiceHandle(hService);
}
else
{
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsgFailed(("OpenService failed LastError=%Rwa\n", LastError));
}
CloseServiceHandle(hSMgr);
}
return -1;
}
#endif
/**
* Attempts to start the service, creating it if necessary.
*
* @returns 0 on success.
* @returns -1 on failure.
* @param fRetry Indicates retry call.
*/
static int suplibOsStartService(void)
{
/*
* Check if the driver service is there.
*/
SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_START);
if (hSMgr == NULL)
{
AssertMsgFailed(("couldn't open service manager in SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS mode!\n"));
return -1;
}
/*
* Try open our service to check it's status.
*/
SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START);
if (!hService)
{
/*
* Create the service.
*/
int rc = suplibOsCreateService();
if (rc)
return rc;
/*
* Try open the service.
*/
hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START);
}
/*
* Check if open and on demand create succeeded.
*/
int rc = -1;
if (hService)
{
/*
* Query service status to see if we need to start it or not.
*/
SERVICE_STATUS Status;
BOOL fRc = QueryServiceStatus(hService, &Status);
Assert(fRc);
if ( Status.dwCurrentState != SERVICE_RUNNING
&& Status.dwCurrentState != SERVICE_START_PENDING)
{
/*
* Start it.
*/
fRc = StartService(hService, 0, NULL);
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsg(fRc, ("StartService failed with LastError=%Rwa\n", LastError));
}
/*
* Wait for the service to finish starting.
* We'll wait for 10 seconds then we'll give up.
*/
QueryServiceStatus(hService, &Status);
if (Status.dwCurrentState == SERVICE_START_PENDING)
{
int iWait;
for (iWait = 100; iWait > 0 && Status.dwCurrentState == SERVICE_START_PENDING; iWait--)
{
Sleep(100);
QueryServiceStatus(hService, &Status);
}
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsg(Status.dwCurrentState != SERVICE_RUNNING,
("Failed to start. LastError=%Rwa iWait=%d status=%d\n",
LastError, iWait, Status.dwCurrentState));
}
if (Status.dwCurrentState == SERVICE_RUNNING)
rc = 0;
/*
* Close open handles.
*/
CloseServiceHandle(hService);
}
else
{
DWORD LastError = GetLastError(); NOREF(LastError);
AssertMsgFailed(("OpenService failed! LastError=%Rwa\n", LastError));
}
if (!CloseServiceHandle(hSMgr))
AssertFailed();
return rc;
}
int suplibOsTerm(PSUPLIBDATA pThis)
{
/*
* Check if we're inited at all.
*/
if (pThis->hDevice != NULL)
{
if (!CloseHandle((HANDLE)pThis->hDevice))
AssertFailed();
pThis->hDevice = NIL_RTFILE; /* yes, that's right */
}
return VINF_SUCCESS;
}
int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq)
{
/*
* Issue the device I/O control.
*/
PSUPREQHDR pHdr = (PSUPREQHDR)pvReq;
Assert(cbReq == RT_MAX(pHdr->cbIn, pHdr->cbOut));
# ifdef USE_NT_DEVICE_IO_CONTROL_FILE
IO_STATUS_BLOCK Ios;
Ios.Status = -1;
Ios.Information = 0;
NTSTATUS rcNt = NtDeviceIoControlFile((HANDLE)pThis->hDevice, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios,
(ULONG)uFunction,
pvReq /*pvInput */, pHdr->cbIn /* cbInput */,
pvReq /*pvOutput*/, pHdr->cbOut /* cbOutput */);
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(Ios.Status))
return VINF_SUCCESS;
rcNt = Ios.Status;
}
return suplibConvertNtStatus(rcNt);
# else
DWORD cbReturned = (ULONG)pHdr->cbOut;
if (DeviceIoControl((HANDLE)pThis->hDevice, uFunction, pvReq, pHdr->cbIn, pvReq, cbReturned, &cbReturned, NULL))
return 0;
return suplibConvertWin32Err(GetLastError());
# endif
}
int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu)
{
/*
* Issue device I/O control.
*/
# ifdef USE_NT_DEVICE_IO_CONTROL_FILE
IO_STATUS_BLOCK Ios;
Ios.Status = -1;
Ios.Information = 0;
NTSTATUS rcNt = NtDeviceIoControlFile((HANDLE)pThis->hDevice, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios,
(ULONG)uFunction,
(PVOID)idCpu /*pvInput */, 0 /* cbInput */,
NULL /*pvOutput*/, 0 /* cbOutput */);
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(Ios.Status))
return VINF_SUCCESS;
rcNt = Ios.Status;
}
return suplibConvertNtStatus(rcNt);
# else
DWORD cbReturned = 0;
if (DeviceIoControl((HANDLE)pThis->hDevice, uFunction, NULL, 0, (LPVOID)idCpu, 0, &cbReturned, NULL))
return VINF_SUCCESS;
return suplibConvertWin32Err(GetLastError());
# endif
}
int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages)
{
NOREF(pThis);
*ppvPages = VirtualAlloc(NULL, (size_t)cPages << PAGE_SHIFT, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (*ppvPages)
return VINF_SUCCESS;
return RTErrConvertFromWin32(GetLastError());
}
int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */)
{
NOREF(pThis);
if (VirtualFree(pvPages, 0, MEM_RELEASE))
return VINF_SUCCESS;
return RTErrConvertFromWin32(GetLastError());
}
# ifndef USE_NT_DEVICE_IO_CONTROL_FILE
/**
* Converts a supdrv win32 error code to an IPRT status code.
*
* @returns corresponding IPRT error code.
* @param rc Win32 error code.
*/
static int suplibConvertWin32Err(int rc)
{
/* Conversion program (link with ntdll.lib from ddk):
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <ntstatus.h>
#include <winternl.h>
#include <stdio.h>
int main()
{
#define CONVERT(a) printf(#a " %#x -> %d\n", a, RtlNtStatusToDosError((a)))
CONVERT(STATUS_SUCCESS);
CONVERT(STATUS_NOT_SUPPORTED);
CONVERT(STATUS_INVALID_PARAMETER);
CONVERT(STATUS_UNKNOWN_REVISION);
CONVERT(STATUS_INVALID_HANDLE);
CONVERT(STATUS_INVALID_ADDRESS);
CONVERT(STATUS_NOT_LOCKED);
CONVERT(STATUS_IMAGE_ALREADY_LOADED);
CONVERT(STATUS_ACCESS_DENIED);
CONVERT(STATUS_REVISION_MISMATCH);
return 0;
}
*/
switch (rc)
{
//case 0: return STATUS_SUCCESS;
case 0: return VINF_SUCCESS;
case ERROR_NOT_SUPPORTED: return VERR_GENERAL_FAILURE;
case ERROR_INVALID_PARAMETER: return VERR_INVALID_PARAMETER;
case ERROR_UNKNOWN_REVISION: return VERR_INVALID_MAGIC;
case ERROR_INVALID_HANDLE: return VERR_INVALID_HANDLE;
case ERROR_UNEXP_NET_ERR: return VERR_INVALID_POINTER;
case ERROR_NOT_LOCKED: return VERR_LOCK_FAILED;
case ERROR_SERVICE_ALREADY_RUNNING: return VERR_ALREADY_LOADED;
case ERROR_ACCESS_DENIED: return VERR_PERMISSION_DENIED;
case ERROR_REVISION_MISMATCH: return VERR_VERSION_MISMATCH;
}
/* fall back on the default conversion. */
return RTErrConvertFromWin32(rc);
}
# else
/**
* Reverse of VBoxDrvNtErr2NtStatus
* returns VBox status code.
* @param rcNt NT status code.
*/
static int suplibConvertNtStatus(NTSTATUS rcNt)
{
switch (rcNt)
{
case STATUS_SUCCESS: return VINF_SUCCESS;
case STATUS_NOT_SUPPORTED: return VERR_GENERAL_FAILURE;
case STATUS_INVALID_PARAMETER: return VERR_INVALID_PARAMETER;
case STATUS_UNKNOWN_REVISION: return VERR_INVALID_MAGIC;
case STATUS_INVALID_HANDLE: return VERR_INVALID_HANDLE;
case STATUS_INVALID_ADDRESS: return VERR_INVALID_POINTER;
case STATUS_NOT_LOCKED: return VERR_LOCK_FAILED;
case STATUS_IMAGE_ALREADY_LOADED: return VERR_ALREADY_LOADED;
case STATUS_ACCESS_DENIED: return VERR_PERMISSION_DENIED;
case STATUS_REVISION_MISMATCH: return VERR_VERSION_MISMATCH;
}
/* Fall back on IPRT for the rest. */
return RTErrConvertFromNtStatus(rcNt);
}
# endif
#endif /* !IN_SUP_HARDENED_R3 */