/* $Id: VBoxDrvTool.cpp $ */ /** @file * Windows Driver R0 Tooling. */ /* * Copyright (C) 2011-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 */ #include "VBoxDrvTool.h" #include #include #include "../../../win/VBoxDbgLog.h" #define VBOXDRVTOOL_MEMTAG 'TDBV' static PVOID vboxDrvToolMemAlloc(SIZE_T cbBytes) { PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXDRVTOOL_MEMTAG); Assert(pvMem); return pvMem; } static PVOID vboxDrvToolMemAllocZ(SIZE_T cbBytes) { PVOID pvMem = vboxDrvToolMemAlloc(cbBytes); if (pvMem) { RtlZeroMemory(pvMem, cbBytes); } return pvMem; } static VOID vboxDrvToolMemFree(PVOID pvMem) { ExFreePoolWithTag(pvMem, VBOXDRVTOOL_MEMTAG); } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKeyU(OUT PHANDLE phKey, IN PUNICODE_STRING pName, IN ACCESS_MASK fAccess) { OBJECT_ATTRIBUTES ObjAttr; InitializeObjectAttributes(&ObjAttr, pName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); return ZwOpenKey(phKey, fAccess, &ObjAttr); } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKey(OUT PHANDLE phKey, IN PWCHAR pName, IN ACCESS_MASK fAccess) { UNICODE_STRING RtlStr; RtlInitUnicodeString(&RtlStr, pName); return VBoxDrvToolRegOpenKeyU(phKey, &RtlStr, fAccess); } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegCloseKey(IN HANDLE hKey) { return ZwClose(hKey); } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegQueryValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT PULONG pDword) { struct { KEY_VALUE_PARTIAL_INFORMATION Info; UCHAR Buf[32]; /* should be enough */ } Buf; ULONG cbBuf; UNICODE_STRING RtlStr; RtlInitUnicodeString(&RtlStr, pName); NTSTATUS Status = ZwQueryValueKey(hKey, &RtlStr, KeyValuePartialInformation, &Buf.Info, sizeof(Buf), &cbBuf); if (Status == STATUS_SUCCESS) { if (Buf.Info.Type == REG_DWORD) { Assert(Buf.Info.DataLength == 4); *pDword = *((PULONG)Buf.Info.Data); return STATUS_SUCCESS; } } return STATUS_INVALID_PARAMETER; } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegSetValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT ULONG val) { UNICODE_STRING RtlStr; RtlInitUnicodeString(&RtlStr, pName); return ZwSetValueKey(hKey, &RtlStr, NULL, /* IN ULONG TitleIndex OPTIONAL, reserved */ REG_DWORD, &val, sizeof(val)); } static NTSTATUS vboxDrvToolIoCompletionSetEvent(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pvContext) { RT_NOREF2(pDevObj, pIrp); PKEVENT pEvent = (PKEVENT)pvContext; KeSetEvent(pEvent, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostAsync(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent) { IoSetCompletionRoutine(pIrp, vboxDrvToolIoCompletionSetEvent, pEvent, TRUE, TRUE, TRUE); return IoCallDriver(pDevObj, pIrp); } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSync(PDEVICE_OBJECT pDevObj, PIRP pIrp) { KEVENT Event; KeInitializeEvent(&Event, NotificationEvent, FALSE); NTSTATUS Status = VBoxDrvToolIoPostAsync(pDevObj, pIrp, &Event); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = pIrp->IoStatus.Status; } return Status; } /* !!!NOTE: the caller MUST be the IRP owner!!! * * !! one can not post threaded IRPs this way!! */ VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSyncWithTimeout(PDEVICE_OBJECT pDevObj, PIRP pIrp, ULONG dwTimeoutMs) { KEVENT Event; LOG(("post irp (0x%p) to DevObj(0x%p) with timeout (%u)", pIrp, pDevObj, dwTimeoutMs)); KeInitializeEvent(&Event, NotificationEvent, FALSE); NTSTATUS Status = VBoxDrvToolIoPostAsync(pDevObj, pIrp, &Event); if (Status == STATUS_PENDING) { LARGE_INTEGER Interval; PLARGE_INTEGER pInterval = NULL; if (dwTimeoutMs != RT_INDEFINITE_WAIT) { Interval.QuadPart = -(int64_t) dwTimeoutMs /* ms */ * 10000; pInterval = &Interval; } Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, pInterval); if (Status == STATUS_TIMEOUT) { WARN(("irp (0x%p) to DevObj(0x%p) was not completed within timeout (%u), cancelling", pIrp, pDevObj, dwTimeoutMs)); if (!IoCancelIrp(pIrp)) { /* this may happen, but this is something the caller with timeout is not expecting */ WARN(("IoCancelIrp failed")); } /* wait for the IRP to complete */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); } else { ASSERT_WARN(Status == STATUS_SUCCESS, ("uunexpected Status (0x%x)", Status)); } /* by this time the IRP is completed */ Status = pIrp->IoStatus.Status; LOG(("Pending IRP(0x%p) completed with status(0x%x)", pIrp, Status)); } else { LOG(("IRP(0x%p) completed with status(0x%x)", pIrp, Status)); } return Status; } VBOXDRVTOOL_DECL(VOID) VBoxDrvToolRefWaitEqual(PVBOXDRVTOOL_REF pRef, uint32_t u32Val) { LARGE_INTEGER Interval; Interval.QuadPart = -(int64_t) 2 /* ms */ * 10000; uint32_t cRefs; size_t loops = 0; KTIMER kTimer; NTSTATUS status = STATUS_SUCCESS; KeInitializeTimer(&kTimer); while ((cRefs = ASMAtomicReadU32(&pRef->cRefs)) > u32Val && loops < 256) { Assert(cRefs >= u32Val); Assert(cRefs < UINT32_MAX/2); KeSetTimer(&kTimer, Interval, NULL); status = KeWaitForSingleObject(&kTimer, Executive, KernelMode, false, NULL); Assert(NT_SUCCESS(status)); loops++; } } VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolStrCopy(PUNICODE_STRING pDst, CONST PUNICODE_STRING pSrc) { USHORT cbLength = pSrc->Length + sizeof (pDst->Buffer[0]); pDst->Buffer = (PWCHAR)vboxDrvToolMemAlloc(cbLength); Assert(pDst->Buffer); if (pDst->Buffer) { RtlMoveMemory(pDst->Buffer, pSrc->Buffer, pSrc->Length); pDst->Buffer[pSrc->Length / sizeof (pDst->Buffer[0])] = L'\0'; pDst->Length = pSrc->Length; pDst->MaximumLength = cbLength; return STATUS_SUCCESS; } return STATUS_NO_MEMORY; } VBOXDRVTOOL_DECL(VOID) VBoxDrvToolStrFree(PUNICODE_STRING pStr) { vboxDrvToolMemFree(pStr->Buffer); }