From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/HostDrivers/Support/win/SUPLib-win.cpp | 1002 +++++++++++++++++++++++ 1 file changed, 1002 insertions(+) create mode 100644 src/VBox/HostDrivers/Support/win/SUPLib-win.cpp (limited to 'src/VBox/HostDrivers/Support/win/SUPLib-win.cpp') diff --git a/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp new file mode 100644 index 00000000..dfa590a9 --- /dev/null +++ b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp @@ -0,0 +1,1002 @@ +/* $Id: SUPLib-win.cpp $ */ +/** @file + * VirtualBox Support Library - Windows NT specific parts. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# undef LOG_DISABLED +# define LOG_DISABLED + /** @todo RTLOGREL_DISABLED */ +# include +# undef LogRelIt +# define LogRelIt(pvInst, fFlags, iGroup, fmtargs) do { } while (0) +#endif + +#define USE_NT_DEVICE_IO_CONTROL_FILE +#include + +#include +#include +#include +#include +#include +#include +#ifndef IN_SUP_HARDENED_R3 +# include +# include +# include +#endif +#include +#include +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" +#ifdef VBOX_WITH_HARDENING +# include "win/SUPHardenedVerify-win.h" +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The support service name. */ +#define SERVICE_NAME "VBoxSup" + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +#ifndef IN_SUP_HARDENED_R3 +static int suplibOsCreateService(void); +//unused: static int suplibOsUpdateService(void); +static int suplibOsDeleteService(void); +static int suplibOsStartService(void); +static int suplibOsStopService(void); +#endif +#ifdef USE_NT_DEVICE_IO_CONTROL_FILE +static int suplibConvertNtStatus(NTSTATUS rcNt); +#else +static int suplibConvertWin32Err(int); +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static bool g_fHardenedVerifyInited = false; + + +DECLHIDDEN(int) suplibOsHardenedVerifyInit(void) +{ + if (!g_fHardenedVerifyInited) + { +#if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_HARDENED_R3) && !defined(IN_SUP_R3_STATIC) + supR3HardenedWinInitVersion(false /*fEarly*/); + int rc = supHardenedWinInitImageVerifier(NULL); + if (RT_FAILURE(rc)) + return rc; + supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(NULL); +#endif + g_fHardenedVerifyInited = true; + } + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) suplibOsHardenedVerifyTerm(void) +{ + /** @todo free resources... */ + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, uint32_t fFlags, SUPINITOP *penmWhat, PRTERRINFO pErrInfo) +{ + /* + * Make sure the image verifier is fully initialized. + */ + int rc = suplibOsHardenedVerifyInit(); + if (RT_FAILURE(rc)) + return RTErrInfoSetF(pErrInfo, rc, "suplibOsHardenedVerifyInit failed: %Rrc", rc); + + /* + * Done if of pre-inited. + */ + if (fPreInited) + { +#if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_HARDENED_R3) +# ifdef IN_SUP_R3_STATIC + return VERR_NOT_SUPPORTED; +# else + return VINF_SUCCESS; +# endif +#else + return VINF_SUCCESS; +#endif + } + +#if !defined(IN_SUP_HARDENED_R3) + /* + * Driverless? + */ + if (fFlags & SUPR3INIT_F_DRIVERLESS) + { + pThis->fDriverless = true; + return VINF_SUCCESS; + } +#endif + + /* + * Try open the device. + */ +#ifndef IN_SUP_HARDENED_R3 + uint32_t cTry = 0; +#endif + HANDLE hDevice; + for (;;) + { + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + + static const WCHAR s_wszName[] = L"\\Device\\VBoxDrvU"; + UNICODE_STRING NtName; + NtName.Buffer = (PWSTR)s_wszName; + NtName.Length = sizeof(s_wszName) - sizeof(WCHAR) * (fFlags & SUPR3INIT_F_UNRESTRICTED ? 2 : 1); + NtName.MaximumLength = NtName.Length; + + OBJECT_ATTRIBUTES ObjAttr; + InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/); + + hDevice = RTNT_INVALID_HANDLE_VALUE; + + NTSTATUS rcNt = NtCreateFile(&hDevice, + GENERIC_READ | GENERIC_WRITE, /* No SYNCHRONIZE. */ + &ObjAttr, + &Ios, + NULL /* Allocation Size*/, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT! */ + NULL /*EaBuffer*/, + 0 /*EaLength*/); + if (NT_SUCCESS(rcNt)) + rcNt = Ios.Status; + if (NT_SUCCESS(rcNt)) + { + /* + * We're good. + */ + pThis->hDevice = hDevice; + pThis->fUnrestricted = RT_BOOL(fFlags & SUPR3INIT_F_UNRESTRICTED); + return VINF_SUCCESS; + } + +#ifndef IN_SUP_HARDENED_R3 + /* + * Failed to open, try starting the service and reopen the device + * exactly once. + */ + if (cTry == 0 && !NT_SUCCESS(rcNt)) + { + cTry++; + suplibOsStartService(); + continue; + } +#endif + + /* + * Translate the error code. + */ + switch (rcNt) + { + /** @todo someone must test what is actually returned. */ + case STATUS_DEVICE_DOES_NOT_EXIST: + case STATUS_DEVICE_NOT_CONNECTED: + //case ERROR_BAD_DEVICE: + case STATUS_DEVICE_REMOVED: + //case ERROR_DEVICE_NOT_AVAILABLE: + rc = VERR_VM_DRIVER_LOAD_ERROR; + break; + case STATUS_OBJECT_PATH_NOT_FOUND: + case STATUS_NO_SUCH_DEVICE: + case STATUS_NO_SUCH_FILE: + case STATUS_OBJECT_NAME_NOT_FOUND: + rc = VERR_VM_DRIVER_NOT_INSTALLED; + break; + case STATUS_ACCESS_DENIED: + case STATUS_SHARING_VIOLATION: + rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; + break; + case STATUS_UNSUCCESSFUL: + rc = VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0; + break; + case STATUS_TRUST_FAILURE: + rc = VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1; + break; + case STATUS_TOO_LATE: + rc = VERR_SUPDRV_HARDENING_EVIL_HANDLE; + break; + default: + if (SUP_NT_STATUS_IS_VBOX(rcNt)) /* See VBoxDrvNtErr2NtStatus. */ + rc = SUP_NT_STATUS_TO_VBOX(rcNt); + else + rc = VERR_VM_DRIVER_OPEN_ERROR; + break; + } + +#ifdef IN_SUP_HARDENED_R3 + /* + * Get more details from VBoxDrvErrorInfo if present. + */ + if (pErrInfo && pErrInfo->cbMsg > 32) + { + /* Prefix. */ + size_t cchPrefix; + if (RTErrIsKnown(rc)) + cchPrefix = RTStrPrintf(pErrInfo->pszMsg, pErrInfo->cbMsg / 2, "Integrity error (%#x/%Rrc): ", rcNt, rc); + else + cchPrefix = RTStrPrintf(pErrInfo->pszMsg, pErrInfo->cbMsg / 2, "Integrity error (%#x/%d): ", rcNt, rc); + + /* Get error info. */ + supR3HardenedWinReadErrorInfoDevice(pErrInfo->pszMsg + cchPrefix, pErrInfo->cbMsg - cchPrefix, ""); + if (pErrInfo->pszMsg[cchPrefix] != '\0') + { + pErrInfo->fFlags |= RTERRINFO_FLAGS_SET; + pErrInfo->rc = rc; + *penmWhat = kSupInitOp_Integrity; + } + else + pErrInfo->pszMsg[0] = '\0'; + } + +#else + RT_NOREF1(penmWhat); + + /* + * Do fallback. + */ + if ( (fFlags & SUPR3INIT_F_DRIVERLESS_MASK) + && rcNt != -1 /** @todo */ ) + { + LogRel(("Failed to open '%.*ls' rc=%Rrc rcNt=%#x - Switching to driverless mode.\n", + NtName.Length / sizeof(WCHAR), NtName.Buffer, rc, rcNt)); + pThis->fDriverless = true; + return VINF_SUCCESS; + } +#endif + return rc; + } +} + + +#ifndef IN_SUP_HARDENED_R3 + +DECLHIDDEN(int) suplibOsInstall(void) +{ + int rc = suplibOsCreateService(); + if (RT_SUCCESS(rc)) + { + int rc2 = suplibOsStartService(); + if (rc2 != VINF_SUCCESS) + rc = rc2; + } + return rc; +} + + +DECLHIDDEN(int) suplibOsUninstall(void) +{ + int rc = suplibOsStopService(); + if (RT_SUCCESS(rc)) + rc = suplibOsDeleteService(); + return rc; +} + + +/** + * Creates the service. + * + * @returns VBox status code. + * @retval VWRN_ALREADY_EXISTS if it already exists. + */ +static int suplibOsCreateService(void) +{ + /* + * Assume it didn't exist, so we'll create the service. + */ + int rc; + SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG); + DWORD dwErr = GetLastError(); + AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed dwErr=%d\n", dwErr)); + if (hSMgrCreate != NULL) + { + char szDriver[RTPATH_MAX]; + rc = RTPathExecDir(szDriver, sizeof(szDriver) - sizeof("\\VBoxSup.sys")); + if (RT_SUCCESS(rc)) + { + strcat(szDriver, "\\VBoxSup.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); + dwErr = GetLastError(); + if (hService) + { + CloseServiceHandle(hService); + rc = VINF_SUCCESS; + } + else if (dwErr == ERROR_SERVICE_EXISTS) + rc = VWRN_ALREADY_EXISTS; + else + { + AssertMsgFailed(("CreateService failed! dwErr=%Rwa szDriver=%s\n", dwErr, szDriver)); + rc = RTErrConvertFromWin32(dwErr); + } + } + CloseServiceHandle(hSMgrCreate); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + return rc; +} + + +/** + * Stops a possibly running service. + * + * @returns VBox status code. + */ +static int suplibOsStopService(void) +{ + /* + * Assume it didn't exist, so we'll create the service. + */ + int rc; + SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS); + DWORD dwErr = GetLastError(); + AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed dwErr=%d\n", dwErr)); + 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 = VINF_SUCCESS; + 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 = VINF_SUCCESS; + else + { + AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState)); + rc = VERR_GENERAL_FAILURE; + } + } + else + { + dwErr = GetLastError(); + if ( Status.dwCurrentState == SERVICE_STOP_PENDING + && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) + rc = VERR_RESOURCE_BUSY; /* better than VERR_GENERAL_FAILURE */ + else + { + AssertMsgFailed(("ControlService failed with dwErr=%Rwa. status=%d\n", dwErr, Status.dwCurrentState)); + rc = RTErrConvertFromWin32(dwErr); + } + } + CloseServiceHandle(hService); + } + else + { + dwErr = GetLastError(); + if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) + rc = VINF_SUCCESS; + else + { + AssertMsgFailed(("OpenService failed dwErr=%Rwa\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + } + CloseServiceHandle(hSMgr); + } + else + rc = RTErrConvertFromWin32(dwErr); + return rc; +} + + +/** + * Deletes the service. + * + * @returns VBox status code. + */ +int suplibOsDeleteService(void) +{ + int rcRet = VINF_SUCCESS; + SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG); + DWORD dwErr = GetLastError(); + AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", dwErr)); + if (hSMgr) + { + /* + * Old service name. + */ + SC_HANDLE hService = OpenService(hSMgr, "VBoxDrv", DELETE); + if (hService) + { + if (!DeleteService(hService)) + { + dwErr = GetLastError(); + AssertMsgFailed(("DeleteService failed for VBoxDrv dwErr=%Rwa\n", dwErr)); + rcRet = RTErrConvertFromWin32(dwErr); + } + CloseServiceHandle(hService); + } + else + { + dwErr = GetLastError(); + if (dwErr != ERROR_SERVICE_DOES_NOT_EXIST) + { + AssertMsgFailed(("OpenService failed for VBoxDrv dwErr=%Rwa\n", dwErr)); + rcRet = RTErrConvertFromWin32(dwErr); + } + } + + /* + * The new service. + */ + hService = OpenService(hSMgr, SERVICE_NAME, DELETE); + if (hService) + { + if (!DeleteService(hService)) + { + dwErr = GetLastError(); + AssertMsgFailed(("DeleteService for " SERVICE_NAME " failed dwErr=%Rwa\n", dwErr)); + rcRet = RTErrConvertFromWin32(dwErr); + } + CloseServiceHandle(hService); + } + else + { + dwErr = GetLastError(); + if (dwErr != ERROR_SERVICE_DOES_NOT_EXIST) + { + AssertMsgFailed(("OpenService failed for " SERVICE_NAME " dwErr=%Rwa\n", dwErr)); + rcRet = RTErrConvertFromWin32(dwErr); + } + } + CloseServiceHandle(hSMgr); + } + else + rcRet = RTErrConvertFromWin32(dwErr); + return rcRet; +} + +#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("\\VBoxSup.sys")); + if (RT_SUCCESS(rc)) + { + strcat(szDriver, "\\VBoxSup.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 VBox status code. + */ +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) + { + DWORD dwErr = GetLastError(); + AssertMsgFailed(("couldn't open service manager in SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS mode! (dwErr=%d)\n", dwErr)); + return RTErrConvertFromWin32(dwErr); + } + + /* + * 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 (RT_FAILURE(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; + 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); NOREF(fRc); + if (Status.dwCurrentState == SERVICE_RUNNING) + rc = VINF_ALREADY_INITIALIZED; + else + { + if (Status.dwCurrentState == SERVICE_START_PENDING) + rc = VINF_SUCCESS; + else + { + /* + * Start it. + */ + if (StartService(hService, 0, NULL)) + rc = VINF_SUCCESS; + else + { + DWORD dwErr = GetLastError(); + AssertMsg(fRc, ("StartService failed with dwErr=%Rwa\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + } + + /* + * 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 dwErr = GetLastError(); NOREF(dwErr); + AssertMsg(Status.dwCurrentState != SERVICE_RUNNING, + ("Failed to start. dwErr=%Rwa iWait=%d status=%d\n", dwErr, iWait, Status.dwCurrentState)); + } + + if (Status.dwCurrentState == SERVICE_RUNNING) + rc = VINF_SUCCESS; + else if (RT_SUCCESS_NP(rc)) + rc = VERR_GENERAL_FAILURE; + } + + /* + * Close open handles. + */ + CloseServiceHandle(hService); + } + else + { + DWORD dwErr = GetLastError(); + AssertMsgFailed(("OpenService failed! LastError=%Rwa\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + if (!CloseServiceHandle(hSMgr)) + AssertFailed(); + + return rc; +} + +#endif /* !IN_SUP_HARDENED_R3 */ + +DECLHIDDEN(int) suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Check if we're inited at all. + */ + if (pThis->hDevice != NULL) + { + NTSTATUS rcNt = NtClose((HANDLE)pThis->hDevice); + Assert(NT_SUCCESS(rcNt)); RT_NOREF(rcNt); + pThis->hDevice = NIL_RTFILE; /* yes, that's right */ + } + + return VINF_SUCCESS; +} + +#ifndef IN_SUP_HARDENED_R3 + +DECLHIDDEN(int) suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + RT_NOREF1(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 = RTNT_IO_STATUS_BLOCK_INITIALIZER; + 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 +} + + +DECLHIDDEN(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 = RTNT_IO_STATUS_BLOCK_INITIALIZER; + NTSTATUS rcNt = NtDeviceIoControlFile((HANDLE)pThis->hDevice, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios, + (ULONG)uFunction, + NULL /*pvInput */, 0 /* cbInput */, + (PVOID)idCpu /*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 +} + + +DECLHIDDEN(int) suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, uint32_t fFlags, void **ppvPages) +{ + RT_NOREF(pThis, fFlags); + + /* + * Do some one-time init here wrt large pages. + * + * Large pages requires the SeLockMemoryPrivilege, which by default (Win10, + * Win11) isn't even enabled and must be gpedit'ed to be adjustable here. + */ + if (!(cPages & 511) && (fFlags & SUP_PAGE_ALLOC_F_LARGE_PAGES)) + { + static int volatile s_fCanDoLargePages = -1; + int fCanDoLargePages = s_fCanDoLargePages; + if (s_fCanDoLargePages != -1) + { /* likely */ } + else if (RTEnvExistsUtf8("VBOX_DO_NOT_USE_LARGE_PAGES")) /** @todo add flag for this instead? */ + s_fCanDoLargePages = fCanDoLargePages = 0; + else + { + HANDLE hToken = NULL; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + union + { + TOKEN_PRIVILEGES s; + uint8_t abPadding[RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES)]; + } Privileges; + RT_ZERO(Privileges); + Privileges.s.PrivilegeCount = 1; + Privileges.s.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (LookupPrivilegeValueW(NULL, L"SeLockMemoryPrivilege", &Privileges.s.Privileges[0].Luid)) + AdjustTokenPrivileges(hToken, FALSE, &Privileges.s, 0, NULL, NULL); + else + AssertFailed(); + CloseHandle(hToken); + } + else + AssertFailed(); + s_fCanDoLargePages = fCanDoLargePages = -2; + } + + /* + * Try allocate with large pages. + */ + if (fCanDoLargePages != 0) + { + *ppvPages = VirtualAlloc(NULL, (size_t)cPages << PAGE_SHIFT, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_EXECUTE_READWRITE); + if (*ppvPages) + { + if (fCanDoLargePages == -2) + { + s_fCanDoLargePages = 1; + LogRel(("SUPLib: MEM_LARGE_PAGES works!\n")); + } + LogRel2(("SUPLib: MEM_LARGE_PAGES for %p LB %p\n", *ppvPages, (size_t)cPages << PAGE_SHIFT)); + return VINF_SUCCESS; + } + + /* This can happen if the above AdjustTokenPrivileges failed (non-admin + user), or if the privilege isn't present in the token (need gpedit). */ + if (GetLastError() == ERROR_PRIVILEGE_NOT_HELD) + { + LogRel(("SUPLib: MEM_LARGE_PAGES privilege not held.\n")); + s_fCanDoLargePages = 0; + } + else + LogRel2(("SUPLib: MEM_LARGE_PAGES allocation failed with odd status: %u\n", GetLastError())); + } + } + + /* + * Do a regular allocation w/o large pages. + */ + *ppvPages = VirtualAlloc(NULL, (size_t)cPages << PAGE_SHIFT, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (*ppvPages) + return VINF_SUCCESS; + return RTErrConvertFromWin32(GetLastError()); +} + + +DECLHIDDEN(int) suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */) +{ + NOREF(pThis); + if (VirtualFree(pvPages, 0, MEM_RELEASE)) + return VINF_SUCCESS; + return RTErrConvertFromWin32(GetLastError()); +} + + +DECLHIDDEN(bool) suplibOsIsNemSupportedWhenNoVtxOrAmdV(void) +{ +# if ARCH_BITS == 64 + /* + * Check that we're in a VM. + */ + if (!ASMHasCpuId()) + return false; + if (!RTX86IsValidStdRange(ASMCpuId_EAX(0))) + return false; + if (!(ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_HVP)) + return false; + + /* + * Try load WinHvPlatform and resolve API for checking. + * Note! The two size_t arguments and the ssize_t one are all too big, but who cares. + */ + RTLDRMOD hLdrMod = NIL_RTLDRMOD; + int rc = RTLdrLoadSystem("WinHvPlatform.dll", false, &hLdrMod); + if (RT_FAILURE(rc)) + return false; + + bool fRet = false; + typedef HRESULT (WINAPI *PFNWHVGETCAPABILITY)(ssize_t, void *, size_t, size_t *); + PFNWHVGETCAPABILITY pfnWHvGetCapability = (PFNWHVGETCAPABILITY)RTLdrGetFunction(hLdrMod, "WHvGetCapability"); + if (pfnWHvGetCapability) + { + /* + * Query the API. + */ + union + { + BOOL fHypervisorPresent; + uint64_t u64Padding; + } Caps; + RT_ZERO(Caps); + size_t cbRetIgn = 0; + HRESULT hrc = pfnWHvGetCapability(0 /*WHvCapabilityCodeHypervisorPresent*/, &Caps, sizeof(Caps), &cbRetIgn); + if (SUCCEEDED(hrc) && Caps.fHypervisorPresent) + fRet = true; + } + + RTLdrClose(hLdrMod); + return fRet; +# else + return false; +#endif +} + + +# 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 + #include + #include + #include + + 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; + } + + /* See VBoxDrvNtErr2NtStatus. */ + if (SUP_NT_STATUS_IS_VBOX(rcNt)) + return SUP_NT_STATUS_TO_VBOX(rcNt); + + /* Fall back on IPRT for the rest. */ + return RTErrConvertFromNtStatus(rcNt); +} +# endif + +#endif /* !IN_SUP_HARDENED_R3 */ + -- cgit v1.2.3