VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp

Last change on this file was 100267, checked in by vboxsync, 10 months ago

Additions: Make the R0 physical heap configurable to allow for allocations >= 4GiB if supported by the VBox device (the MMIO request path is available), add support for the MMIO request path required for ARM, bugref:10457

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.8 KB
Line 
1/* $Id: VBoxGuest-win.cpp 100267 2023-06-23 14:57:53Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP LOG_GROUP_SUP_DRV
42#include <iprt/nt/nt.h>
43
44#include "VBoxGuestInternal.h"
45#include <VBox/VBoxGuestLib.h>
46#include <VBox/log.h>
47
48#include <iprt/asm.h>
49#include <iprt/asm-amd64-x86.h>
50#include <iprt/critsect.h>
51#include <iprt/dbg.h>
52#include <iprt/err.h>
53#include <iprt/initterm.h>
54#include <iprt/memobj.h>
55#include <iprt/mem.h>
56#include <iprt/mp.h>
57#include <iprt/spinlock.h>
58#include <iprt/string.h>
59#include <iprt/utf16.h>
60
61#ifdef TARGET_NT4
62# include <VBox/pci.h>
63# define PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS32_IPRT
64# include <iprt/formats/mz.h>
65# include <iprt/formats/pecoff.h>
66extern "C" IMAGE_DOS_HEADER __ImageBase;
67#endif
68
69
70/*********************************************************************************************************************************
71* Defined Constants And Macros *
72*********************************************************************************************************************************/
73#undef ExFreePool
74
75#ifndef PCI_MAX_BUSES
76# define PCI_MAX_BUSES 256
77#endif
78
79/** CM_RESOURCE_MEMORY_* flags which were used on XP or earlier. */
80#define VBOX_CM_PRE_VISTA_MASK (0x3f)
81
82
83/*********************************************************************************************************************************
84* Structures and Typedefs *
85*********************************************************************************************************************************/
86/**
87 * Possible device states for our state machine.
88 */
89typedef enum VGDRVNTDEVSTATE
90{
91 /** @name Stable states
92 * @{ */
93 VGDRVNTDEVSTATE_REMOVED = 0,
94 VGDRVNTDEVSTATE_STOPPED,
95 VGDRVNTDEVSTATE_OPERATIONAL,
96 /** @} */
97
98 /** @name Transitional states
99 * @{ */
100 VGDRVNTDEVSTATE_PENDINGSTOP,
101 VGDRVNTDEVSTATE_PENDINGREMOVE,
102 VGDRVNTDEVSTATE_SURPRISEREMOVED
103 /** @} */
104} VGDRVNTDEVSTATE;
105
106
107/**
108 * Subclassing the device extension for adding windows-specific bits.
109 */
110typedef struct VBOXGUESTDEVEXTWIN
111{
112 /** The common device extension core. */
113 VBOXGUESTDEVEXT Core;
114
115 /** Our functional driver object. */
116 PDEVICE_OBJECT pDeviceObject;
117 /** Top of the stack. */
118 PDEVICE_OBJECT pNextLowerDriver;
119
120 /** @name PCI bus and slot (device+function) set by for legacy NT only.
121 * @{ */
122 /** Bus number where the device is located. */
123 ULONG uBus;
124 /** Slot number where the device is located (PCI_SLOT_NUMBER). */
125 ULONG uSlot;
126 /** @} */
127
128 /** @name Interrupt stuff.
129 * @{ */
130 /** Interrupt object pointer. */
131 PKINTERRUPT pInterruptObject;
132 /** Device interrupt level. */
133 ULONG uInterruptLevel;
134 /** Device interrupt vector. */
135 ULONG uInterruptVector;
136 /** Affinity mask. */
137 KAFFINITY fInterruptAffinity;
138 /** LevelSensitive or Latched. */
139 KINTERRUPT_MODE enmInterruptMode;
140 /** @} */
141
142 /** Physical address and length of VMMDev memory. */
143 PHYSICAL_ADDRESS uVmmDevMemoryPhysAddr;
144 /** Length of VMMDev memory. */
145 ULONG cbVmmDevMemory;
146
147 /** Device state. */
148 VGDRVNTDEVSTATE volatile enmDevState;
149 /** The previous stable device state. */
150 VGDRVNTDEVSTATE enmPrevDevState;
151
152 /** Last system power action set (see VBoxGuestPower). */
153 POWER_ACTION enmLastSystemPowerAction;
154 /** Preallocated generic request for shutdown. */
155 VMMDevPowerStateRequest *pPowerStateRequest;
156
157 /** Spinlock protecting MouseNotifyCallback. Required since the consumer is
158 * in a DPC callback and not the ISR. */
159 KSPIN_LOCK MouseEventAccessSpinLock;
160
161 /** Read/write critical section for handling race between checking for idle
162 * driver (in IRP_MN_QUERY_REMOVE_DEVICE & IRP_MN_QUERY_STOP_DEVICE) and
163 * creating new sessions. The session creation code enteres the critical
164 * section in read (shared) access mode, whereas the idle checking code
165 * enteres is in write (exclusive) access mode. */
166 RTCRITSECTRW SessionCreateCritSect;
167} VBOXGUESTDEVEXTWIN;
168typedef VBOXGUESTDEVEXTWIN *PVBOXGUESTDEVEXTWIN;
169
170
171/** NT (windows) version identifier. */
172typedef enum VGDRVNTVER
173{
174 VGDRVNTVER_INVALID = 0,
175 VGDRVNTVER_WINNT310,
176 VGDRVNTVER_WINNT350,
177 VGDRVNTVER_WINNT351,
178 VGDRVNTVER_WINNT4,
179 VGDRVNTVER_WIN2K,
180 VGDRVNTVER_WINXP,
181 VGDRVNTVER_WIN2K3,
182 VGDRVNTVER_WINVISTA,
183 VGDRVNTVER_WIN7,
184 VGDRVNTVER_WIN8,
185 VGDRVNTVER_WIN81,
186 VGDRVNTVER_WIN10,
187 VGDRVNTVER_WIN11
188} VGDRVNTVER;
189
190
191/*********************************************************************************************************************************
192* Internal Functions *
193*********************************************************************************************************************************/
194RT_C_DECLS_BEGIN
195static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
196static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp);
197static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp);
198static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
199static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj);
200static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
201static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
202static NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
203static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
204 PIRP pIrp, PIO_STACK_LOCATION pStack);
205static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
206static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt);
207static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
208static NTSTATUS NTAPI vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
209static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer);
210static VOID NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
211static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
212#ifdef VBOX_STRICT
213static void vgdrvNtDoTests(void);
214#endif
215#ifdef TARGET_NT4
216static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
217 void *pvData, ULONG offData, ULONG cbData);
218static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
219 void *pvData, ULONG offData, ULONG cbData);
220#endif
221
222/*
223 * We only do INIT allocations. PAGE is too much work and risk for little gain.
224 */
225#ifdef ALLOC_PRAGMA
226NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
227# pragma alloc_text(INIT, DriverEntry)
228# ifdef TARGET_NT4
229static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
230# pragma alloc_text(INIT, vgdrvNt4CreateDevice)
231static NTSTATUS vgdrvNt4FindPciDevice(PULONG puluBusNumber, PPCI_SLOT_NUMBER puSlotNumber);
232# pragma alloc_text(INIT, vgdrvNt4FindPciDevice)
233# endif
234#endif
235RT_C_DECLS_END
236
237
238/*********************************************************************************************************************************
239* Global Variables *
240*********************************************************************************************************************************/
241/** The detected NT (windows) version. */
242static VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
243/** Pointer to the PoStartNextPowerIrp routine (in the NT kernel).
244 * Introduced in Windows 2000. */
245static decltype(PoStartNextPowerIrp) *g_pfnPoStartNextPowerIrp = NULL;
246/** Pointer to the PoCallDriver routine (in the NT kernel).
247 * Introduced in Windows 2000. */
248static decltype(PoCallDriver) *g_pfnPoCallDriver = NULL;
249#ifdef TARGET_NT4
250/** Pointer to the HalAssignSlotResources routine (in the HAL).
251 * Introduced in NT 3.50. */
252static decltype(HalAssignSlotResources) *g_pfnHalAssignSlotResources= NULL;
253/** Pointer to the HalGetBusDataByOffset routine (in the HAL).
254 * Introduced in NT 3.50. */
255static decltype(HalGetBusDataByOffset) *g_pfnHalGetBusDataByOffset = NULL;
256/** Pointer to the HalSetBusDataByOffset routine (in the HAL).
257 * Introduced in NT 3.50 (we provide fallback and use it only for NT 3.1). */
258static decltype(HalSetBusDataByOffset) *g_pfnHalSetBusDataByOffset = NULL;
259#endif
260/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel).
261 * Introduced in Windows 3.50. */
262static decltype(KeRegisterBugCheckCallback) *g_pfnKeRegisterBugCheckCallback = NULL;
263/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel).
264 * Introduced in Windows 3.50. */
265static decltype(KeDeregisterBugCheckCallback) *g_pfnKeDeregisterBugCheckCallback = NULL;
266/** Pointer to the KiBugCheckData array (in the NT kernel).
267 * Introduced in Windows 4. */
268static uintptr_t const *g_pauKiBugCheckData = NULL;
269/** Set if the callback was successfully registered and needs deregistering. */
270static bool g_fBugCheckCallbackRegistered = false;
271/** The bugcheck callback record. */
272static KBUGCHECK_CALLBACK_RECORD g_BugCheckCallbackRec;
273
274
275
276/**
277 * Driver entry point.
278 *
279 * @returns appropriate status code.
280 * @param pDrvObj Pointer to driver object.
281 * @param pRegPath Registry base path.
282 */
283NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
284{
285 RT_NOREF1(pRegPath);
286#ifdef TARGET_NT4
287 /*
288 * Looks like NT 3.1 doesn't necessarily zero our uninitialized data segments
289 * (like ".bss"), at least not when loading at runtime, so do that.
290 */
291 PIMAGE_DOS_HEADER pMzHdr = &__ImageBase;
292 PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)((uint8_t *)pMzHdr + pMzHdr->e_lfanew);
293 if ( pNtHdrs->Signature == IMAGE_NT_SIGNATURE
294 && pNtHdrs->FileHeader.NumberOfSections > 2
295 && pNtHdrs->FileHeader.NumberOfSections < 64)
296 {
297 uint32_t iShdr = pNtHdrs->FileHeader.NumberOfSections;
298 uint32_t uRvaEnd = pNtHdrs->OptionalHeader.SizeOfImage; /* (may be changed to exclude tail sections) */
299 PIMAGE_SECTION_HEADER paShdrs;
300 paShdrs = (PIMAGE_SECTION_HEADER)&pNtHdrs->OptionalHeader.DataDirectory[pNtHdrs->OptionalHeader.NumberOfRvaAndSizes];
301 while (iShdr-- > 0)
302 {
303 if ( !(paShdrs[iShdr].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
304 && paShdrs[iShdr].VirtualAddress < uRvaEnd)
305 {
306 uint32_t const cbSection = uRvaEnd - paShdrs[iShdr].VirtualAddress;
307 uint32_t const offUninitialized = paShdrs[iShdr].SizeOfRawData;
308 //RTLogBackdoorPrintf("section #%u: rva=%#x size=%#x calcsize=%#x) rawsize=%#x\n", iShdr,
309 // paShdrs[iShdr].VirtualAddress, paShdrs[iShdr].Misc.VirtualSize, cbSection, offUninitialized);
310 if ( offUninitialized < cbSection
311 && (paShdrs[iShdr].Characteristics & IMAGE_SCN_MEM_WRITE))
312 memset((uint8_t *)pMzHdr + paShdrs[iShdr].VirtualAddress + offUninitialized, 0, cbSection - offUninitialized);
313 uRvaEnd = paShdrs[iShdr].VirtualAddress;
314 }
315 }
316 }
317 else
318 RTLogBackdoorPrintf("VBoxGuest: Bad pNtHdrs=%p: %#x\n", pNtHdrs, pNtHdrs->Signature);
319#endif
320
321 /*
322 * Start by initializing IPRT.
323 */
324 int rc = RTR0Init(0);
325 if (RT_FAILURE(rc))
326 {
327 RTLogBackdoorPrintf("VBoxGuest: RTR0Init failed: %Rrc!\n", rc);
328 return STATUS_UNSUCCESSFUL;
329 }
330 VGDrvCommonInitLoggers();
331
332 LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
333
334 /*
335 * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
336 */
337 ULONG ulMajorVer;
338 ULONG ulMinorVer;
339 ULONG ulBuildNo;
340 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
341
342 /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log on the host. */
343 RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo);
344 if (fCheckedBuild)
345 RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n");
346
347#ifdef VBOX_STRICT
348 vgdrvNtDoTests();
349#endif
350 NTSTATUS rcNt = STATUS_SUCCESS;
351 switch (ulMajorVer)
352 {
353 case 10:
354 /* Windows 10 Preview builds starting with 9926. */
355 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
356 /* Windows 11 Preview builds starting with 22000. */
357 if (ulBuildNo >= 22000)
358 g_enmVGDrvNtVer = VGDRVNTVER_WIN11;
359 break;
360 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
361 switch (ulMinorVer)
362 {
363 case 0: /* Note: Also could be Windows 2008 Server! */
364 g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA;
365 break;
366 case 1: /* Note: Also could be Windows 2008 Server R2! */
367 g_enmVGDrvNtVer = VGDRVNTVER_WIN7;
368 break;
369 case 2:
370 g_enmVGDrvNtVer = VGDRVNTVER_WIN8;
371 break;
372 case 3:
373 g_enmVGDrvNtVer = VGDRVNTVER_WIN81;
374 break;
375 case 4: /* Windows 10 Preview builds. */
376 default:
377 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
378 break;
379 }
380 break;
381 case 5:
382 switch (ulMinorVer)
383 {
384 default:
385 case 2:
386 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3;
387 break;
388 case 1:
389 g_enmVGDrvNtVer = VGDRVNTVER_WINXP;
390 break;
391 case 0:
392 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K;
393 break;
394 }
395 break;
396 case 4:
397 g_enmVGDrvNtVer = VGDRVNTVER_WINNT4;
398 break;
399 case 3:
400 if (ulMinorVer > 50)
401 g_enmVGDrvNtVer = VGDRVNTVER_WINNT351;
402 else if (ulMinorVer >= 50)
403 g_enmVGDrvNtVer = VGDRVNTVER_WINNT350;
404 else
405 g_enmVGDrvNtVer = VGDRVNTVER_WINNT310;
406 break;
407 default:
408 /* Major versions above 6 gets classified as windows 10. */
409 if (ulMajorVer > 6)
410 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
411 else
412 {
413 RTLogBackdoorPrintf("At least Windows NT 3.10 required! Found %u.%u!\n", ulMajorVer, ulMinorVer);
414 rcNt = STATUS_DRIVER_UNABLE_TO_LOAD;
415 }
416 break;
417 }
418 if (NT_SUCCESS(rcNt))
419 {
420 /*
421 * Dynamically resolve symbols not present in NT4.
422 */
423 RTDBGKRNLINFO hKrnlInfo;
424 rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0 /*fFlags*/);
425 if (RT_SUCCESS(rc))
426 {
427 g_pfnKeRegisterBugCheckCallback = (decltype(KeRegisterBugCheckCallback) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeRegisterBugCheckCallback");
428 g_pfnKeDeregisterBugCheckCallback = (decltype(KeDeregisterBugCheckCallback) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeDeregisterBugCheckCallback");
429 g_pauKiBugCheckData = (uintptr_t const *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KiBugCheckData");
430 g_pfnPoCallDriver = (decltype(PoCallDriver) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoCallDriver");
431 g_pfnPoStartNextPowerIrp = (decltype(PoStartNextPowerIrp) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoStartNextPowerIrp");
432#ifdef TARGET_NT4
433 if (g_enmVGDrvNtVer > VGDRVNTVER_WINNT4)
434#endif
435 {
436 if (!g_pfnPoCallDriver) { LogRelFunc(("Missing PoCallDriver!\n")); rc = VERR_SYMBOL_NOT_FOUND; }
437 if (!g_pfnPoStartNextPowerIrp) { LogRelFunc(("Missing PoStartNextPowerIrp!\n")); rc = VERR_SYMBOL_NOT_FOUND; }
438 }
439
440#ifdef TARGET_NT4
441 g_pfnHalAssignSlotResources = (decltype(HalAssignSlotResources) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalAssignSlotResources");
442 if (!g_pfnHalAssignSlotResources && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
443 {
444 RTLogBackdoorPrintf("VBoxGuest: Missing HalAssignSlotResources!\n");
445 rc = VERR_SYMBOL_NOT_FOUND;
446 }
447
448 g_pfnHalGetBusDataByOffset = (decltype(HalGetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalGetBusDataByOffset");
449 if (!g_pfnHalGetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
450 {
451 RTLogBackdoorPrintf("VBoxGuest: Missing HalGetBusDataByOffset!\n");
452 rc = VERR_SYMBOL_NOT_FOUND;
453 }
454 if (!g_pfnHalGetBusDataByOffset)
455 g_pfnHalGetBusDataByOffset = vgdrvNt31GetBusDataByOffset;
456
457 g_pfnHalSetBusDataByOffset = (decltype(HalSetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalSetBusDataByOffset");
458 if (!g_pfnHalSetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
459 {
460 RTLogBackdoorPrintf("VBoxGuest: Missing HalSetBusDataByOffset!\n");
461 rc = VERR_SYMBOL_NOT_FOUND;
462 }
463 if (!g_pfnHalSetBusDataByOffset)
464 g_pfnHalSetBusDataByOffset = vgdrvNt31SetBusDataByOffset;
465#endif
466 RTR0DbgKrnlInfoRelease(hKrnlInfo);
467 }
468 if (RT_SUCCESS(rc))
469 {
470 /*
471 * Setup the driver entry points in pDrvObj.
472 */
473 pDrvObj->DriverUnload = vgdrvNtUnload;
474 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate;
475 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose;
476 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl;
477 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl;
478 /** @todo Need to call IoRegisterShutdownNotification or
479 * IoRegisterLastChanceShutdownNotification, possibly hooking the
480 * HalReturnToFirmware import in NTOSKRNL on older systems (<= ~NT4) and
481 * check for power off requests. */
482 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown;
483 pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub;
484 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub;
485#ifdef TARGET_NT4
486 if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
487 rcNt = vgdrvNt4CreateDevice(pDrvObj, pRegPath);
488 else
489#endif
490 {
491 pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtNt5PlusPnP;
492 pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtNt5PlusPower;
493 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtNt5PlusSystemControl;
494 pDrvObj->DriverExtension->AddDevice = vgdrvNtNt5PlusAddDevice;
495 }
496 if (NT_SUCCESS(rcNt))
497 {
498 /*
499 * Try register the bugcheck callback (non-fatal).
500 */
501 if ( g_pfnKeRegisterBugCheckCallback
502 && g_pfnKeDeregisterBugCheckCallback)
503 {
504 AssertCompile(BufferEmpty == 0);
505 KeInitializeCallbackRecord(&g_BugCheckCallbackRec);
506 if (g_pfnKeRegisterBugCheckCallback(&g_BugCheckCallbackRec, vgdrvNtBugCheckCallback,
507 NULL, 0, (PUCHAR)"VBoxGuest"))
508 g_fBugCheckCallbackRegistered = true;
509 else
510 g_fBugCheckCallbackRegistered = false;
511 }
512 else
513 Assert(g_pfnKeRegisterBugCheckCallback == NULL && g_pfnKeDeregisterBugCheckCallback == NULL);
514
515 LogFlowFunc(("Returning %#x\n", rcNt));
516 return rcNt;
517 }
518 }
519 else
520 rcNt = STATUS_PROCEDURE_NOT_FOUND;
521 }
522
523 /*
524 * Failed.
525 */
526 LogRelFunc(("Failed! rcNt=%#x\n", rcNt));
527 VGDrvCommonDestroyLoggers();
528 RTR0Term();
529 return rcNt;
530}
531
532
533/**
534 * Translates our internal NT version enum to VBox OS.
535 *
536 * @returns VBox OS type.
537 * @param enmNtVer The NT version.
538 */
539static VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
540{
541 VBOXOSTYPE enmOsType;
542 switch (enmNtVer)
543 {
544 case VGDRVNTVER_WINNT310: enmOsType = VBOXOSTYPE_WinNT3x; break;
545 case VGDRVNTVER_WINNT350: enmOsType = VBOXOSTYPE_WinNT3x; break;
546 case VGDRVNTVER_WINNT351: enmOsType = VBOXOSTYPE_WinNT3x; break;
547 case VGDRVNTVER_WINNT4: enmOsType = VBOXOSTYPE_WinNT4; break;
548 case VGDRVNTVER_WIN2K: enmOsType = VBOXOSTYPE_Win2k; break;
549 case VGDRVNTVER_WINXP: enmOsType = VBOXOSTYPE_WinXP; break;
550 case VGDRVNTVER_WIN2K3: enmOsType = VBOXOSTYPE_Win2k3; break;
551 case VGDRVNTVER_WINVISTA: enmOsType = VBOXOSTYPE_WinVista; break;
552 case VGDRVNTVER_WIN7: enmOsType = VBOXOSTYPE_Win7; break;
553 case VGDRVNTVER_WIN8: enmOsType = VBOXOSTYPE_Win8; break;
554 case VGDRVNTVER_WIN81: enmOsType = VBOXOSTYPE_Win81; break;
555 case VGDRVNTVER_WIN10: enmOsType = VBOXOSTYPE_Win10; break;
556 case VGDRVNTVER_WIN11: enmOsType = VBOXOSTYPE_Win11_x64; break;
557
558 default:
559 /* We don't know, therefore NT family. */
560 enmOsType = VBOXOSTYPE_WinNT;
561 break;
562 }
563#if ARCH_BITS == 64
564 enmOsType = (VBOXOSTYPE)((int)enmOsType | VBOXOSTYPE_x64);
565#endif
566 return enmOsType;
567}
568
569
570/**
571 * Does the fundamental device extension initialization.
572 *
573 * @returns NT status.
574 * @param pDevExt The device extension.
575 * @param pDevObj The device object.
576 */
577static NTSTATUS vgdrvNtInitDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj)
578{
579 RT_ZERO(*pDevExt);
580
581 KeInitializeSpinLock(&pDevExt->MouseEventAccessSpinLock);
582 pDevExt->pDeviceObject = pDevObj;
583 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED;
584 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
585
586 int rc = RTCritSectRwInit(&pDevExt->SessionCreateCritSect);
587 if (RT_SUCCESS(rc))
588 {
589 rc = VGDrvCommonInitDevExtFundament(&pDevExt->Core);
590 if (RT_SUCCESS(rc))
591 {
592 LogFlow(("vgdrvNtInitDevExtFundament: returning success\n"));
593 return STATUS_SUCCESS;
594 }
595
596 RTCritSectRwDelete(&pDevExt->SessionCreateCritSect);
597 }
598 Log(("vgdrvNtInitDevExtFundament: failed: rc=%Rrc\n", rc));
599 return STATUS_UNSUCCESSFUL;
600}
601
602
603/**
604 * Counter part to vgdrvNtInitDevExtFundament.
605 *
606 * @param pDevExt The device extension.
607 */
608static void vgdrvNtDeleteDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt)
609{
610 LogFlow(("vgdrvNtDeleteDevExtFundament:\n"));
611 VGDrvCommonDeleteDevExtFundament(&pDevExt->Core);
612 RTCritSectRwDelete(&pDevExt->SessionCreateCritSect);
613}
614
615
616#ifdef LOG_ENABLED
617/**
618 * Debug helper to dump a device resource list.
619 *
620 * @param pResourceList list of device resources.
621 */
622static void vgdrvNtShowDeviceResources(PCM_RESOURCE_LIST pRsrcList)
623{
624 for (uint32_t iList = 0; iList < pRsrcList->Count; iList++)
625 {
626 PCM_FULL_RESOURCE_DESCRIPTOR pList = &pRsrcList->List[iList];
627 LogFunc(("List #%u: InterfaceType=%#x BusNumber=%#x ListCount=%u ListRev=%#x ListVer=%#x\n",
628 iList, pList->InterfaceType, pList->BusNumber, pList->PartialResourceList.Count,
629 pList->PartialResourceList.Revision, pList->PartialResourceList.Version ));
630
631 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pList->PartialResourceList.PartialDescriptors;
632 for (ULONG i = 0; i < pList->PartialResourceList.Count; ++i, ++pResource)
633 {
634 ULONG uType = pResource->Type;
635 static char const * const s_apszName[] =
636 {
637 "CmResourceTypeNull",
638 "CmResourceTypePort",
639 "CmResourceTypeInterrupt",
640 "CmResourceTypeMemory",
641 "CmResourceTypeDma",
642 "CmResourceTypeDeviceSpecific",
643 "CmResourceTypeuBusNumber",
644 "CmResourceTypeDevicePrivate",
645 "CmResourceTypeAssignedResource",
646 "CmResourceTypeSubAllocateFrom",
647 };
648
649 if (uType < RT_ELEMENTS(s_apszName))
650 LogFunc((" %.30s Flags=%#x Share=%#x", s_apszName[uType], pResource->Flags, pResource->ShareDisposition));
651 else
652 LogFunc((" Type=%#x Flags=%#x Share=%#x", uType, pResource->Flags, pResource->ShareDisposition));
653 switch (uType)
654 {
655 case CmResourceTypePort:
656 case CmResourceTypeMemory:
657 Log((" Start %#RX64, length=%#x\n", pResource->u.Port.Start.QuadPart, pResource->u.Port.Length));
658 break;
659
660 case CmResourceTypeInterrupt:
661 Log((" Level=%X, vector=%#x, affinity=%#x\n",
662 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity));
663 break;
664
665 case CmResourceTypeDma:
666 Log((" Channel %d, Port %#x\n", pResource->u.Dma.Channel, pResource->u.Dma.Port));
667 break;
668
669 default:
670 Log(("\n"));
671 break;
672 }
673 }
674 }
675}
676#endif /* LOG_ENABLED */
677
678
679/**
680 * Helper to scan the PCI resource list and remember stuff.
681 *
682 * @param pDevExt The device extension.
683 * @param pResList Resource list
684 * @param fTranslated Whether the addresses are translated or not.
685 */
686static NTSTATUS vgdrvNtScanPCIResourceList(PVBOXGUESTDEVEXTWIN pDevExt, PCM_RESOURCE_LIST pResList, bool fTranslated)
687{
688 LogFlowFunc(("Found %d resources\n", pResList->List->PartialResourceList.Count));
689 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
690 bool fGotIrq = false;
691 bool fGotMmio = false;
692 bool fGotIoPorts = false;
693 NTSTATUS rc = STATUS_SUCCESS;
694 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
695 {
696 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
697 switch (pPartialData->Type)
698 {
699 case CmResourceTypePort:
700 LogFlowFunc(("I/O range: Base=%#RX64, length=%08x\n",
701 pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length));
702 /* Save the first I/O port base. */
703 if (!fGotIoPorts)
704 {
705 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
706 fGotIoPorts = true;
707 LogFunc(("I/O range for VMMDev found! Base=%#RX64, length=%08x\n",
708 pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length));
709 }
710 else
711 LogRelFunc(("More than one I/O port range?!?\n"));
712 break;
713
714 case CmResourceTypeInterrupt:
715 LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
716 pPartialData->u.Interrupt.Level, pPartialData->u.Interrupt.Vector, pPartialData->Flags));
717 if (!fGotIrq)
718 {
719 /* Save information. */
720 pDevExt->uInterruptLevel = pPartialData->u.Interrupt.Level;
721 pDevExt->uInterruptVector = pPartialData->u.Interrupt.Vector;
722 pDevExt->fInterruptAffinity = pPartialData->u.Interrupt.Affinity;
723
724 /* Check interrupt mode. */
725 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
726 pDevExt->enmInterruptMode = Latched;
727 else
728 pDevExt->enmInterruptMode = LevelSensitive;
729 fGotIrq = true;
730 LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n", pDevExt->uInterruptVector,
731 pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode));
732 }
733 else
734 LogFunc(("More than one IRQ resource!\n"));
735 break;
736
737 case CmResourceTypeMemory:
738 LogFlowFunc(("Memory range: Base=%#RX64, length=%08x\n",
739 pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length));
740 /* We only care about the first read/write memory range. */
741 if ( !fGotMmio
742 && (pPartialData->Flags & CM_RESOURCE_MEMORY_WRITEABILITY_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
743 {
744 /* Save physical MMIO base + length for VMMDev. */
745 pDevExt->uVmmDevMemoryPhysAddr = pPartialData->u.Memory.Start;
746 pDevExt->cbVmmDevMemory = (ULONG)pPartialData->u.Memory.Length;
747
748 if (!fTranslated)
749 {
750 /* Technically we need to make the HAL translate the address. since we
751 didn't used to do this and it probably just returns the input address,
752 we allow ourselves to ignore failures. */
753 ULONG uAddressSpace = 0;
754 PHYSICAL_ADDRESS PhysAddr = pPartialData->u.Memory.Start;
755 if (HalTranslateBusAddress(pResList->List->InterfaceType, pResList->List->BusNumber, PhysAddr,
756 &uAddressSpace, &PhysAddr))
757 {
758 Log(("HalTranslateBusAddress(%#RX64) -> %RX64, type %#x\n",
759 pPartialData->u.Memory.Start.QuadPart, PhysAddr.QuadPart, uAddressSpace));
760 if (pPartialData->u.Memory.Start.QuadPart != PhysAddr.QuadPart)
761 pDevExt->uVmmDevMemoryPhysAddr = PhysAddr;
762 }
763 else
764 Log(("HalTranslateBusAddress(%#RX64) -> failed!\n", pPartialData->u.Memory.Start.QuadPart));
765 }
766
767 fGotMmio = true;
768 LogFunc(("Found memory range for VMMDev! Base = %#RX64, Length = %08x\n",
769 pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length));
770 }
771 else
772 LogFunc(("Ignoring memory: Flags=%08x Base=%#RX64\n",
773 pPartialData->Flags, pPartialData->u.Memory.Start.QuadPart));
774 break;
775
776 default:
777 LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
778 break;
779 }
780 }
781 return rc;
782}
783
784
785#ifdef TARGET_NT4
786
787/**
788 * Scans the PCI resources on NT 3.1.
789 *
790 * @returns STATUS_SUCCESS or STATUS_DEVICE_CONFIGURATION_ERROR.
791 * @param pDevExt The device extension.
792 * @param uBus The bus number.
793 * @param uSlot The PCI slot to scan.
794 */
795static NTSTATUS vgdrvNt31ScanSlotResources(PVBOXGUESTDEVEXTWIN pDevExt, ULONG uBus, ULONG uSlot)
796{
797 /*
798 * Disable memory mappings so we can determin the BAR lengths
799 * without upsetting other mappings.
800 */
801 uint16_t fCmd = 0;
802 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd));
803 if (fCmd & VBOX_PCI_COMMAND_MEMORY)
804 {
805 uint16_t fCmdTmp = fCmd & ~VBOX_PCI_COMMAND_MEMORY;
806 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdTmp, VBOX_PCI_COMMAND, sizeof(fCmdTmp));
807 }
808
809 /*
810 * Scan the address resources first.
811 */
812 uint32_t aBars[6] = { UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX };
813 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &aBars, VBOX_PCI_BASE_ADDRESS_0, sizeof(aBars));
814
815 bool fGotMmio = false;
816 bool fGotIoPorts = false;
817 for (uint32_t i = 0; i < RT_ELEMENTS(aBars); i++)
818 {
819 uint32_t uBar = aBars[i];
820 if (uBar == UINT32_MAX)
821 continue;
822 if ((uBar & 1) == PCI_ADDRESS_SPACE_IO)
823 {
824 uint32_t uAddr = uBar & UINT32_C(0xfffffffc);
825 if (!uAddr)
826 continue;
827 if (!fGotIoPorts)
828 {
829 pDevExt->Core.IOPortBase = (uint16_t)uAddr & UINT16_C(0xfffc);
830 fGotIoPorts = true;
831 LogFunc(("I/O range for VMMDev found in BAR%u! %#x\n", i, pDevExt->Core.IOPortBase));
832 }
833 else
834 LogRelFunc(("More than one I/O port range?!? BAR%u=%#x\n", i, uBar));
835 }
836 else
837 {
838 uint32_t uAddr = uBar & UINT32_C(0xfffffff0);
839 if (!uAddr)
840 continue;
841
842 if (!fGotMmio)
843 {
844 /* Figure the length by trying to set all address bits and seeing
845 how many we're allowed to set. */
846 uint32_t iBit = 4;
847 while (!(uAddr & RT_BIT_32(iBit)))
848 iBit++;
849
850 uint32_t const offPciBar = VBOX_PCI_BASE_ADDRESS_0 + i * 4;
851 uint32_t uTmpBar = uBar | ((RT_BIT_32(iBit) - 1) & UINT32_C(0xfffffff0));
852 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar));
853 uTmpBar = uBar;
854 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar));
855 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uBar, offPciBar, sizeof(uBar));
856
857 while (iBit > 4 && (uTmpBar & RT_BIT_32(iBit - 1)))
858 iBit--;
859
860 /* got it */
861 pDevExt->cbVmmDevMemory = RT_BIT_32(iBit);
862 pDevExt->uVmmDevMemoryPhysAddr.QuadPart = uAddr;
863 fGotMmio = true;
864 LogFunc(("Found memory range for VMMDev in BAR%u! %#RX64 LB %#x (raw %#x)\n",
865 i, pDevExt->uVmmDevMemoryPhysAddr.QuadPart, pDevExt->cbVmmDevMemory, uBar));
866 }
867 else
868 LogFunc(("Ignoring memory: BAR%u=%#x\n", i, uBar));
869 }
870 }
871
872 /*
873 * Get the IRQ
874 */
875 struct
876 {
877 uint8_t bInterruptLine;
878 uint8_t bInterruptPin;
879 } Buf = { 0, 0 };
880 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &Buf, VBOX_PCI_INTERRUPT_LINE, sizeof(Buf));
881 if (Buf.bInterruptPin != 0)
882 {
883 pDevExt->uInterruptVector = Buf.bInterruptLine;
884 pDevExt->uInterruptLevel = Buf.bInterruptLine;
885 pDevExt->enmInterruptMode = LevelSensitive;
886 pDevExt->fInterruptAffinity = RT_BIT_32(RTMpGetCount()) - 1;
887 LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n",
888 pDevExt->uInterruptVector, pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode));
889 }
890
891 /*
892 * Got what we need?
893 */
894 if (fGotIoPorts && (!fGotMmio || Buf.bInterruptPin != 0))
895 {
896 /*
897 * Enable both MMIO, I/O space and busmastering so we can use the device.
898 */
899 uint16_t fCmdNew = fCmd | VBOX_PCI_COMMAND_IO | VBOX_PCI_COMMAND_MEMORY | VBOX_PCI_COMMAND_MASTER;
900 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdNew, VBOX_PCI_COMMAND, sizeof(fCmdNew));
901
902 return STATUS_SUCCESS;
903 }
904
905 /* No. Complain, restore device command value and return failure. */
906 if (!fGotIoPorts)
907 LogRel(("VBoxGuest: Did not find I/O port range: %#x %#x %#x %#x %#x %#x\n",
908 aBars[0], aBars[1], aBars[2], aBars[3], aBars[4], aBars[5]));
909 if (!fGotMmio || Buf.bInterruptPin != 0)
910 LogRel(("VBoxGuest: Got MMIO but no interrupts!\n"));
911
912 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd));
913 return STATUS_DEVICE_CONFIGURATION_ERROR;
914}
915
916#endif /* TARGET_NT4 */
917
918/**
919 * Unmaps the VMMDev I/O range from kernel space.
920 *
921 * @param pDevExt The device extension.
922 */
923static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
924{
925 LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
926 if (pDevExt->Core.pVMMDevMemory)
927 {
928 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->cbVmmDevMemory);
929 pDevExt->Core.pVMMDevMemory = NULL;
930 }
931
932 pDevExt->uVmmDevMemoryPhysAddr.QuadPart = 0;
933 pDevExt->cbVmmDevMemory = 0;
934}
935
936
937/**
938 * Maps the I/O space from VMMDev to virtual kernel address space.
939 *
940 * @return NTSTATUS
941 *
942 * @param pDevExt The device extension.
943 * @param PhysAddr Physical address to map.
944 * @param cbToMap Number of bytes to map.
945 * @param ppvMMIOBase Pointer of mapped I/O base.
946 * @param pcbMMIO Length of mapped I/O base.
947 */
948static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
949 void **ppvMMIOBase, uint32_t *pcbMMIO)
950{
951 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
952 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
953 /* pcbMMIO is optional. */
954
955 NTSTATUS rc = STATUS_SUCCESS;
956 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
957 {
958 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
959 LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
960 if (pVMMDevMemory)
961 {
962 LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
963
964 /* Check version of the structure; do we have the right memory version? */
965 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
966 {
967 /* Save results. */
968 *ppvMMIOBase = pVMMDevMemory;
969 if (pcbMMIO) /* Optional. */
970 *pcbMMIO = pVMMDevMemory->u32Size;
971
972 LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
973 }
974 else
975 {
976 /* Not our version, refuse operation and unmap the memory. */
977 LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
978
979 vgdrvNtUnmapVMMDevMemory(pDevExt);
980 rc = STATUS_UNSUCCESSFUL;
981 }
982 }
983 else
984 rc = STATUS_UNSUCCESSFUL;
985 }
986 return rc;
987}
988
989
990/**
991 * Sets up the device and its resources.
992 *
993 * @param pDevExt Our device extension data.
994 * @param pDevObj The device object.
995 * @param pIrp The request packet if NT5+, NULL for NT4 and earlier.
996 * @param pDrvObj The driver object for NT4, NULL for NT5+.
997 * @param pRegPath The registry path for NT4, NULL for NT5+.
998 */
999static NTSTATUS vgdrvNtSetupDevice(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj,
1000 PIRP pIrp, PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
1001{
1002 LogFlowFunc(("ENTER: pDevExt=%p pDevObj=%p pIrq=%p pDrvObj=%p pRegPath=%p\n", pDevExt, pDevObj, pIrp, pDrvObj, pRegPath));
1003
1004 NTSTATUS rcNt;
1005 if (!pIrp)
1006 {
1007#ifdef TARGET_NT4
1008 /*
1009 * NT4, NT3.x: Let's have a look at what our PCI adapter offers.
1010 */
1011 LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n"));
1012
1013 /* Assign the PCI resources. */
1014 UNICODE_STRING ClassName;
1015 RtlInitUnicodeString(&ClassName, L"VBoxGuestAdapter");
1016 PCM_RESOURCE_LIST pResourceList = NULL;
1017 if (g_pfnHalAssignSlotResources)
1018 {
1019 rcNt = g_pfnHalAssignSlotResources(pRegPath, &ClassName, pDrvObj, pDevObj, PCIBus, pDevExt->uBus, pDevExt->uSlot,
1020 &pResourceList);
1021# ifdef LOG_ENABLED
1022 if (pResourceList)
1023 vgdrvNtShowDeviceResources(pResourceList);
1024# endif
1025 if (NT_SUCCESS(rcNt))
1026 {
1027 rcNt = vgdrvNtScanPCIResourceList(pDevExt, pResourceList, false /*fTranslated*/);
1028 ExFreePool(pResourceList);
1029 }
1030 }
1031 else
1032 rcNt = vgdrvNt31ScanSlotResources(pDevExt, pDevExt->uBus, pDevExt->uSlot);
1033
1034# else /* !TARGET_NT4 */
1035 AssertFailed();
1036 RT_NOREF(pDevObj, pDrvObj, pRegPath);
1037 rcNt = STATUS_INTERNAL_ERROR;
1038# endif /* !TARGET_NT4 */
1039 }
1040 else
1041 {
1042 /*
1043 * NT5+: Scan the PCI resource list from the IRP.
1044 */
1045 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1046# ifdef LOG_ENABLED
1047 vgdrvNtShowDeviceResources(pStack->Parameters.StartDevice.AllocatedResourcesTranslated);
1048# endif
1049 rcNt = vgdrvNtScanPCIResourceList(pDevExt, pStack->Parameters.StartDevice.AllocatedResourcesTranslated,
1050 true /*fTranslated*/);
1051 }
1052 if (NT_SUCCESS(rcNt))
1053 {
1054 /*
1055 * Map physical address of VMMDev memory into MMIO region
1056 * and init the common device extension bits.
1057 */
1058 void *pvMMIOBase = NULL;
1059 uint32_t cbMMIO = 0;
1060 rcNt = vgdrvNtMapVMMDevMemory(pDevExt,
1061 pDevExt->uVmmDevMemoryPhysAddr,
1062 pDevExt->cbVmmDevMemory,
1063 &pvMMIOBase,
1064 &cbMMIO);
1065 if (NT_SUCCESS(rcNt))
1066 {
1067 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
1068
1069 LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n",
1070 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
1071
1072 int vrc = VGDrvCommonInitDevExtResources(&pDevExt->Core,
1073 pDevExt->Core.IOPortBase,
1074 NULL /*pvMmioReq*/,
1075 pvMMIOBase, cbMMIO,
1076 vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
1077 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
1078 if (RT_SUCCESS(vrc))
1079 {
1080
1081 vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
1082 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
1083 if (RT_SUCCESS(vrc))
1084 {
1085 /*
1086 * Register DPC and ISR.
1087 */
1088 LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
1089 IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
1090
1091 ULONG uInterruptVector = pDevExt->uInterruptVector;
1092 KIRQL uHandlerIrql = (KIRQL)pDevExt->uInterruptLevel;
1093#ifdef TARGET_NT4
1094 if (!pIrp)
1095 {
1096 /* NT4: Get an interrupt vector. Only proceed if the device provides an interrupt. */
1097 if ( uInterruptVector
1098 || pDevExt->uInterruptLevel)
1099 {
1100 LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
1101 pDevExt->uBus, pDevExt->uInterruptLevel, pDevExt->uInterruptVector));
1102 uInterruptVector = HalGetInterruptVector(g_enmVGDrvNtVer == VGDRVNTVER_WINNT310 ? Isa : PCIBus,
1103 pDevExt->uBus,
1104 pDevExt->uInterruptLevel,
1105 pDevExt->uInterruptVector,
1106 &uHandlerIrql,
1107 &pDevExt->fInterruptAffinity);
1108 LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
1109 }
1110 else
1111 LogFunc(("Device does not provide an interrupt!\n"));
1112 }
1113#endif
1114 if (uInterruptVector)
1115 {
1116 LogFlowFunc(("Connecting interrupt (IntVector=%#u), uHandlerIrql=%u) ...\n",
1117 uInterruptVector, uHandlerIrql));
1118
1119 rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
1120 vgdrvNtIsrHandler, /* Our ISR handler. */
1121 pDevExt, /* Device context. */
1122 NULL, /* Optional spinlock. */
1123 uInterruptVector, /* Interrupt vector. */
1124 uHandlerIrql, /* Irql. */
1125 uHandlerIrql, /* SynchronizeIrql. */
1126 pDevExt->enmInterruptMode, /* LevelSensitive or Latched. */
1127 TRUE, /* Shareable interrupt. */
1128 pDevExt->fInterruptAffinity, /* CPU affinity. */
1129 FALSE); /* Don't save FPU stack. */
1130 if (NT_ERROR(rcNt))
1131 LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
1132 }
1133 else
1134 LogFunc(("No interrupt vector found!\n"));
1135 if (NT_SUCCESS(rcNt))
1136 {
1137 /*
1138 * Once we've read configuration from register and host, we're finally read.
1139 */
1140 /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
1141 pDevExt->Core.fLoggingEnabled = true;
1142 vgdrvNtReadConfiguration(pDevExt);
1143
1144 /* Ready to rumble! */
1145 LogRelFunc(("Device is ready!\n"));
1146 pDevExt->enmDevState = VGDRVNTDEVSTATE_OPERATIONAL;
1147 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_OPERATIONAL;
1148 return STATUS_SUCCESS;
1149 }
1150
1151 pDevExt->pInterruptObject = NULL;
1152
1153 VbglR0GRFree(&pDevExt->pPowerStateRequest->header);
1154 pDevExt->pPowerStateRequest = NULL;
1155 }
1156 else
1157 {
1158 LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
1159 rcNt = STATUS_UNSUCCESSFUL;
1160 }
1161
1162 VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
1163 }
1164 else
1165 {
1166 LogFunc(("Could not init device extension resources: vrc=%Rrc\n", vrc));
1167 rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
1168 }
1169 vgdrvNtUnmapVMMDevMemory(pDevExt);
1170 }
1171 else
1172 LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
1173 }
1174
1175 LogFunc(("Returned with rcNt=%#x\n", rcNt));
1176 return rcNt;
1177}
1178
1179
1180
1181
1182#ifdef TARGET_NT4
1183# define PCI_CFG_ADDR 0xcf8
1184# define PCI_CFG_DATA 0xcfc
1185
1186/**
1187 * NT 3.1 doesn't do PCI nor HalSetBusDataByOffset, this is our fallback.
1188 */
1189static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
1190 void *pvData, ULONG offData, ULONG cbData)
1191{
1192 /*
1193 * Validate input a little bit.
1194 */
1195 RT_NOREF(enmBusDataType);
1196 Assert(idxBus <= 255);
1197 Assert(uSlot <= 255);
1198 Assert(offData <= 255);
1199 Assert(cbData > 0);
1200
1201 PCI_SLOT_NUMBER PciSlot;
1202 PciSlot.u.AsULONG = uSlot;
1203 uint32_t const idxAddrTop = UINT32_C(0x80000000)
1204 | (idxBus << 16)
1205 | (PciSlot.u.bits.DeviceNumber << 11)
1206 | (PciSlot.u.bits.FunctionNumber << 8);
1207
1208 /*
1209 * Write the given bytes.
1210 */
1211 uint8_t const *pbData = (uint8_t const *)pvData;
1212 uint32_t off = offData;
1213 uint32_t cbRet = 0;
1214
1215 /* Unaligned start. */
1216 if (off & 3)
1217 {
1218 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
1219 switch (off & 3)
1220 {
1221 case 1:
1222 ASMOutU8(PCI_CFG_DATA + 1, pbData[cbRet++]);
1223 if (cbRet >= cbData)
1224 break;
1225 RT_FALL_THRU();
1226 case 2:
1227 ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet++]);
1228 if (cbRet >= cbData)
1229 break;
1230 RT_FALL_THRU();
1231 case 3:
1232 ASMOutU8(PCI_CFG_DATA + 3, pbData[cbRet++]);
1233 break;
1234 }
1235 off = (off | 3) + 1;
1236 }
1237
1238 /* Bulk. */
1239 while (off < 256 && cbRet < cbData)
1240 {
1241 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
1242 switch (cbData - cbRet)
1243 {
1244 case 1:
1245 ASMOutU8(PCI_CFG_DATA, pbData[cbRet]);
1246 cbRet += 1;
1247 break;
1248 case 2:
1249 ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
1250 cbRet += 2;
1251 break;
1252 case 3:
1253 ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
1254 ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet + 2]);
1255 cbRet += 3;
1256 break;
1257 default:
1258 ASMOutU32(PCI_CFG_DATA, RT_MAKE_U32_FROM_U8(pbData[cbRet], pbData[cbRet + 1],
1259 pbData[cbRet + 2], pbData[cbRet + 3]));
1260 cbRet += 4;
1261 break;
1262 }
1263 off += 4;
1264 }
1265
1266 return cbRet;
1267}
1268
1269
1270/**
1271 * NT 3.1 doesn't do PCI nor HalGetBusDataByOffset, this is our fallback.
1272 */
1273static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
1274 void *pvData, ULONG offData, ULONG cbData)
1275{
1276 /*
1277 * Validate input a little bit.
1278 */
1279 RT_NOREF(enmBusDataType);
1280 Assert(idxBus <= 255);
1281 Assert(uSlot <= 255);
1282 Assert(offData <= 255);
1283 Assert(cbData > 0);
1284
1285 PCI_SLOT_NUMBER PciSlot;
1286 PciSlot.u.AsULONG = uSlot;
1287 uint32_t const idxAddrTop = UINT32_C(0x80000000)
1288 | (idxBus << 16)
1289 | (PciSlot.u.bits.DeviceNumber << 11)
1290 | (PciSlot.u.bits.FunctionNumber << 8);
1291
1292 /*
1293 * Read the header type.
1294 */
1295 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (VBOX_PCI_HEADER_TYPE & ~3));
1296 uint8_t bHdrType = ASMInU8(PCI_CFG_DATA + (VBOX_PCI_HEADER_TYPE & 3));
1297 if (bHdrType == 0xff)
1298 return idxBus < 8 ? 2 : 0; /* No device here */
1299 if ( offData == VBOX_PCI_HEADER_TYPE
1300 && cbData == 1)
1301 {
1302 *(uint8_t *)pvData = bHdrType;
1303 /*Log("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %02x\n", idxAddrTop, offData, bHdrType);*/
1304 return 1;
1305 }
1306
1307 /*
1308 * Read the requested bytes.
1309 */
1310 uint8_t *pbData = (uint8_t *)pvData;
1311 uint32_t off = offData;
1312 uint32_t cbRet = 0;
1313
1314 /* Unaligned start. */
1315 if (off & 3)
1316 {
1317 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
1318 uint32_t uValue = ASMInU32(PCI_CFG_DATA);
1319 switch (off & 3)
1320 {
1321 case 1:
1322 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1323 if (cbRet >= cbData)
1324 break;
1325 RT_FALL_THRU();
1326 case 2:
1327 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1328 if (cbRet >= cbData)
1329 break;
1330 RT_FALL_THRU();
1331 case 3:
1332 pbData[cbRet++] = (uint8_t)(uValue >> 24);
1333 break;
1334 }
1335 off = (off | 3) + 1;
1336 }
1337
1338 /* Bulk. */
1339 while (off < 256 && cbRet < cbData)
1340 {
1341 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
1342 uint32_t uValue = ASMInU32(PCI_CFG_DATA);
1343 switch (cbData - cbRet)
1344 {
1345 case 1:
1346 pbData[cbRet++] = (uint8_t)uValue;
1347 break;
1348 case 2:
1349 pbData[cbRet++] = (uint8_t)uValue;
1350 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1351 break;
1352 case 3:
1353 pbData[cbRet++] = (uint8_t)uValue;
1354 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1355 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1356 break;
1357 default:
1358 pbData[cbRet++] = (uint8_t)uValue;
1359 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1360 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1361 pbData[cbRet++] = (uint8_t)(uValue >> 24);
1362 break;
1363 }
1364 off += 4;
1365 }
1366
1367 Log(("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %.*Rhxs\n", idxAddrTop, offData, cbRet, pvData));
1368 return cbRet;
1369}
1370
1371
1372/**
1373 * Helper function to handle the PCI device lookup.
1374 *
1375 * @returns NT status code.
1376 *
1377 * @param puBus Where to return the bus number on success.
1378 * @param pSlot Where to return the slot number on success.
1379 */
1380static NTSTATUS vgdrvNt4FindPciDevice(PULONG puBus, PPCI_SLOT_NUMBER pSlot)
1381{
1382 Log(("vgdrvNt4FindPciDevice\n"));
1383
1384 PCI_SLOT_NUMBER Slot;
1385 Slot.u.AsULONG = 0;
1386
1387 /* Scan each bus. */
1388 for (ULONG uBus = 0; uBus < PCI_MAX_BUSES; uBus++)
1389 {
1390 /* Scan each device. */
1391 for (ULONG idxDevice = 0; idxDevice < PCI_MAX_DEVICES; idxDevice++)
1392 {
1393 Slot.u.bits.DeviceNumber = idxDevice;
1394 Slot.u.bits.FunctionNumber = 0;
1395
1396 /* Check the device header. */
1397 uint8_t bHeaderType = 0xff;
1398 ULONG cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG,
1399 &bHeaderType, VBOX_PCI_HEADER_TYPE, sizeof(bHeaderType));
1400 if (cbRet == 0)
1401 break;
1402 if (cbRet == 2 || bHeaderType == 0xff)
1403 continue;
1404
1405 /* Scan functions. */
1406 uint32_t const cFunctionStep = bHeaderType & 0x80 ? 1 : 8;
1407 Log(("vgdrvNt4FindPciDevice: %#x:%#x cFunctionStep=%d bHeaderType=%#x\n", uBus, idxDevice, cFunctionStep, bHeaderType));
1408 for (ULONG idxFunction = 0; idxFunction < PCI_MAX_FUNCTION; idxFunction += cFunctionStep)
1409 {
1410 Slot.u.bits.FunctionNumber = idxFunction;
1411
1412 /* Read the vendor and device IDs of this device and compare with the VMMDev. */
1413 struct
1414 {
1415 uint16_t idVendor;
1416 uint16_t idDevice;
1417 } Buf = { PCI_INVALID_VENDORID, PCI_INVALID_VENDORID };
1418 cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG, &Buf, VBOX_PCI_VENDOR_ID, sizeof(Buf));
1419 if ( cbRet == sizeof(Buf)
1420 && Buf.idVendor == VMMDEV_VENDORID
1421 && Buf.idDevice == VMMDEV_DEVICEID)
1422 {
1423 /* Hooray, we've found it! */
1424 Log(("vgdrvNt4FindPciDevice: Device found! Bus=%#x Slot=%#u (dev %#x, fun %#x, rvd %#x)\n",
1425 uBus, Slot.u.AsULONG, Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber, Slot.u.bits.Reserved));
1426
1427 *puBus = uBus;
1428 *pSlot = Slot;
1429 return STATUS_SUCCESS;
1430 }
1431 }
1432 }
1433 }
1434
1435 return STATUS_DEVICE_DOES_NOT_EXIST;
1436}
1437
1438
1439/**
1440 * Legacy helper function to create the device object.
1441 *
1442 * @returns NT status code.
1443 *
1444 * @param pDrvObj The driver object.
1445 * @param pRegPath The driver registry path.
1446 */
1447static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
1448{
1449 Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath));
1450
1451 /*
1452 * Find our virtual PCI device
1453 */
1454 ULONG uBus;
1455 PCI_SLOT_NUMBER uSlot;
1456 NTSTATUS rc = vgdrvNt4FindPciDevice(&uBus, &uSlot);
1457 if (NT_ERROR(rc))
1458 {
1459 Log(("vgdrvNt4CreateDevice: Device not found!\n"));
1460 return rc;
1461 }
1462
1463 /*
1464 * Create device.
1465 */
1466 UNICODE_STRING DevName;
1467 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
1468 PDEVICE_OBJECT pDeviceObject = NULL;
1469 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
1470 if (NT_SUCCESS(rc))
1471 {
1472 Log(("vgdrvNt4CreateDevice: Device created\n"));
1473
1474 UNICODE_STRING DosName;
1475 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1476 rc = IoCreateSymbolicLink(&DosName, &DevName);
1477 if (NT_SUCCESS(rc))
1478 {
1479 Log(("vgdrvNt4CreateDevice: Symlink created\n"));
1480
1481 /*
1482 * Setup the device extension.
1483 */
1484 Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n"));
1485 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
1486 int vrc = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
1487 if (RT_SUCCESS(vrc))
1488 {
1489 /* Store bus and slot number we've queried before. */
1490 pDevExt->uBus = uBus;
1491 pDevExt->uSlot = uSlot.u.AsULONG;
1492
1493 Log(("vgdrvNt4CreateDevice: Device extension created\n"));
1494
1495 /* Do the actual VBox init ... */
1496 rc = vgdrvNtSetupDevice(pDevExt, pDeviceObject, NULL /*pIrp*/, pDrvObj, pRegPath);
1497 if (NT_SUCCESS(rc))
1498 {
1499 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (success)\n", rc));
1500 return rc;
1501 }
1502
1503 /* bail out */
1504 vgdrvNtDeleteDevExtFundament(pDevExt);
1505 }
1506 IoDeleteSymbolicLink(&DosName);
1507 }
1508 else
1509 Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc));
1510 IoDeleteDevice(pDeviceObject);
1511 }
1512 else
1513 Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc));
1514 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc));
1515 return rc;
1516}
1517
1518#endif /* TARGET_NT4 */
1519
1520/**
1521 * Handle request from the Plug & Play subsystem.
1522 *
1523 * @returns NT status code
1524 * @param pDrvObj Driver object
1525 * @param pDevObj Device object
1526 *
1527 * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
1528 */
1529static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
1530{
1531 LogFlowFuncEnter();
1532
1533 /*
1534 * Create device.
1535 */
1536 UNICODE_STRING DevName;
1537 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
1538 PDEVICE_OBJECT pDeviceObject = NULL;
1539 NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
1540 if (NT_SUCCESS(rcNt))
1541 {
1542 /*
1543 * Create symbolic link (DOS devices).
1544 */
1545 UNICODE_STRING DosName;
1546 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1547 rcNt = IoCreateSymbolicLink(&DosName, &DevName);
1548 if (NT_SUCCESS(rcNt))
1549 {
1550 /*
1551 * Setup the device extension.
1552 */
1553 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
1554 rcNt = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
1555 if (NT_SUCCESS(rcNt))
1556 {
1557 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
1558 if (pDevExt->pNextLowerDriver != NULL)
1559 {
1560 /* Ensure we are not called at elevated IRQL, even if our code isn't pagable any more. */
1561 pDeviceObject->Flags |= DO_POWER_PAGABLE;
1562
1563 /* Driver is ready now. */
1564 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1565 LogFlowFunc(("Returning with rcNt=%#x (success)\n", rcNt));
1566 return rcNt;
1567 }
1568 LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
1569 rcNt = STATUS_DEVICE_NOT_CONNECTED;
1570 vgdrvNtDeleteDevExtFundament(pDevExt);
1571 }
1572
1573 IoDeleteSymbolicLink(&DosName);
1574 }
1575 else
1576 LogFunc(("IoCreateSymbolicLink failed with rcNt=%#x!\n", rcNt));
1577 IoDeleteDevice(pDeviceObject);
1578 }
1579 else
1580 LogFunc(("IoCreateDevice failed with rcNt=%#x!\n", rcNt));
1581
1582 LogFunc(("Returning with rcNt=%#x\n", rcNt));
1583 return rcNt;
1584}
1585
1586
1587/**
1588 * Irp completion routine for PnP Irps we send.
1589 *
1590 * @returns NT status code.
1591 * @param pDevObj Device object.
1592 * @param pIrp Request packet.
1593 * @param pEvent Semaphore.
1594 */
1595static NTSTATUS vgdrvNt5PlusPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
1596{
1597 RT_NOREF2(pDevObj, pIrp);
1598 KeSetEvent(pEvent, 0, FALSE);
1599 return STATUS_MORE_PROCESSING_REQUIRED;
1600}
1601
1602
1603/**
1604 * Helper to send a PnP IRP and wait until it's done.
1605 *
1606 * @returns NT status code.
1607 * @param pDevObj Device object.
1608 * @param pIrp Request packet.
1609 * @param fStrict When set, returns an error if the IRP gives an error.
1610 */
1611static NTSTATUS vgdrvNt5PlusPnPSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict)
1612{
1613 KEVENT Event;
1614
1615 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1616
1617 IoCopyCurrentIrpStackLocationToNext(pIrp);
1618 IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vgdrvNt5PlusPnpIrpComplete, &Event, TRUE, TRUE, TRUE);
1619
1620 NTSTATUS rcNt = IoCallDriver(pDevObj, pIrp);
1621 if (rcNt == STATUS_PENDING)
1622 {
1623 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1624 rcNt = pIrp->IoStatus.Status;
1625 }
1626
1627 if ( !fStrict
1628 && (rcNt == STATUS_NOT_SUPPORTED || rcNt == STATUS_INVALID_DEVICE_REQUEST))
1629 {
1630 rcNt = STATUS_SUCCESS;
1631 }
1632
1633 Log(("vgdrvNt5PlusPnPSendIrpSynchronously: Returning %#x\n", rcNt));
1634 return rcNt;
1635}
1636
1637
1638/**
1639 * Deletes the device hardware resources.
1640 *
1641 * Used during removal, stopping and legacy module unloading.
1642 *
1643 * @param pDevExt The device extension.
1644 */
1645static void vgdrvNtDeleteDeviceResources(PVBOXGUESTDEVEXTWIN pDevExt)
1646{
1647 if (pDevExt->pInterruptObject)
1648 {
1649 IoDisconnectInterrupt(pDevExt->pInterruptObject);
1650 pDevExt->pInterruptObject = NULL;
1651 }
1652 pDevExt->pPowerStateRequest = NULL; /* Will be deleted by the following call. */
1653 if (pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES)
1654 VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
1655 vgdrvNtUnmapVMMDevMemory(pDevExt);
1656}
1657
1658
1659/**
1660 * Deletes the device extension fundament and unlinks the device
1661 *
1662 * Used during removal and legacy module unloading. Must have called
1663 * vgdrvNtDeleteDeviceResources.
1664 *
1665 * @param pDevObj Device object.
1666 * @param pDevExt The device extension.
1667 */
1668static void vgdrvNtDeleteDeviceFundamentAndUnlink(PDEVICE_OBJECT pDevObj, PVBOXGUESTDEVEXTWIN pDevExt)
1669{
1670 /*
1671 * Delete the remainder of the device extension.
1672 */
1673 vgdrvNtDeleteDevExtFundament(pDevExt);
1674
1675 /*
1676 * Delete the DOS symlink to the device and finally the device itself.
1677 */
1678 UNICODE_STRING DosName;
1679 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1680 IoDeleteSymbolicLink(&DosName);
1681
1682 Log(("vgdrvNtDeleteDeviceFundamentAndUnlink: Deleting device ...\n"));
1683 IoDeleteDevice(pDevObj);
1684}
1685
1686
1687/**
1688 * Checks if the device is idle.
1689 * @returns STATUS_SUCCESS if idle, STATUS_UNSUCCESSFUL if busy.
1690 * @param pDevExt The device extension.
1691 * @param pszQueryNm The query name.
1692 */
1693static NTSTATUS vgdrvNtCheckIdle(PVBOXGUESTDEVEXTWIN pDevExt, const char *pszQueryNm)
1694{
1695 uint32_t cSessions = pDevExt->Core.cSessions;
1696 if (cSessions == 0)
1697 return STATUS_SUCCESS;
1698 LogRel(("vgdrvNtCheckIdle/%s: cSessions=%d\n", pszQueryNm, cSessions));
1699 return STATUS_UNSUCCESSFUL;
1700}
1701
1702
1703/**
1704 * PnP Request handler.
1705 *
1706 * @param pDevObj Device object.
1707 * @param pIrp Request packet.
1708 */
1709static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1710{
1711 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1712 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1713
1714#ifdef LOG_ENABLED
1715 static char const * const s_apszFnctName[] =
1716 {
1717 "IRP_MN_START_DEVICE",
1718 "IRP_MN_QUERY_REMOVE_DEVICE",
1719 "IRP_MN_REMOVE_DEVICE",
1720 "IRP_MN_CANCEL_REMOVE_DEVICE",
1721 "IRP_MN_STOP_DEVICE",
1722 "IRP_MN_QUERY_STOP_DEVICE",
1723 "IRP_MN_CANCEL_STOP_DEVICE",
1724 "IRP_MN_QUERY_DEVICE_RELATIONS",
1725 "IRP_MN_QUERY_INTERFACE",
1726 "IRP_MN_QUERY_CAPABILITIES",
1727 "IRP_MN_QUERY_RESOURCES",
1728 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
1729 "IRP_MN_QUERY_DEVICE_TEXT",
1730 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
1731 "IRP_MN_0xE",
1732 "IRP_MN_READ_CONFIG",
1733 "IRP_MN_WRITE_CONFIG",
1734 "IRP_MN_EJECT",
1735 "IRP_MN_SET_LOCK",
1736 "IRP_MN_QUERY_ID",
1737 "IRP_MN_QUERY_PNP_DEVICE_STATE",
1738 "IRP_MN_QUERY_BUS_INFORMATION",
1739 "IRP_MN_DEVICE_USAGE_NOTIFICATION",
1740 "IRP_MN_SURPRISE_REMOVAL",
1741 };
1742 Log(("vgdrvNtNt5PlusPnP: MinorFunction: %s\n",
1743 pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown"));
1744#endif
1745
1746 NTSTATUS rc = STATUS_SUCCESS;
1747 uint8_t bMinorFunction = pStack->MinorFunction;
1748 switch (bMinorFunction)
1749 {
1750 case IRP_MN_START_DEVICE:
1751 {
1752 Log(("vgdrvNtNt5PlusPnP: START_DEVICE\n"));
1753
1754 /* This must be handled first by the lower driver. */
1755 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1756 if ( NT_SUCCESS(rc)
1757 && NT_SUCCESS(pIrp->IoStatus.Status))
1758 {
1759 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n",
1760 pStack->Parameters.StartDevice.AllocatedResources));
1761 if (pStack->Parameters.StartDevice.AllocatedResources)
1762 {
1763 rc = vgdrvNtSetupDevice(pDevExt, pDevObj, pIrp, NULL, NULL);
1764 if (NT_SUCCESS(rc))
1765 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: success\n"));
1766 else
1767 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNtSetupDevice failed: %#x\n", rc));
1768 }
1769 else
1770 {
1771 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n",
1772 pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL));
1773 rc = STATUS_UNSUCCESSFUL;
1774 }
1775 }
1776 else
1777 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNt5PlusPnPSendIrpSynchronously failed: %#x + %#x\n",
1778 rc, pIrp->IoStatus.Status));
1779
1780 pIrp->IoStatus.Status = rc;
1781 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1782 return rc;
1783 }
1784
1785
1786 /*
1787 * Sent before removing the device and/or driver.
1788 */
1789 case IRP_MN_QUERY_REMOVE_DEVICE:
1790 {
1791 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE\n"));
1792
1793 RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
1794#ifdef VBOX_REBOOT_ON_UNINSTALL
1795 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n"));
1796 rc = STATUS_UNSUCCESSFUL;
1797#endif
1798 if (NT_SUCCESS(rc))
1799 rc = vgdrvNtCheckIdle(pDevExt, "QUERY_REMOVE_DEVICE");
1800 if (NT_SUCCESS(rc))
1801 {
1802 pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGREMOVE;
1803 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1804
1805 /* This IRP passed down to lower driver. */
1806 pIrp->IoStatus.Status = STATUS_SUCCESS;
1807
1808 IoSkipCurrentIrpStackLocation(pIrp);
1809 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1810 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1811
1812 /* We must not do anything the IRP after doing IoSkip & CallDriver
1813 since the driver below us will complete (or already have completed) the IRP.
1814 I.e. just return the status we got from IoCallDriver */
1815 }
1816 else
1817 {
1818 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1819 pIrp->IoStatus.Status = rc;
1820 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1821 }
1822
1823 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Returning with rc = 0x%x\n", rc));
1824 return rc;
1825 }
1826
1827 /*
1828 * Cancels a pending remove, IRP_MN_QUERY_REMOVE_DEVICE.
1829 * We only have to revert the state.
1830 */
1831 case IRP_MN_CANCEL_REMOVE_DEVICE:
1832 {
1833 Log(("vgdrvNtNt5PlusPnP: CANCEL_REMOVE_DEVICE\n"));
1834
1835 /* This must be handled first by the lower driver. */
1836 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1837 if ( NT_SUCCESS(rc)
1838 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGREMOVE)
1839 {
1840 /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */
1841 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1842 }
1843
1844 /* Complete the IRP. */
1845 pIrp->IoStatus.Status = rc;
1846 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1847 return rc;
1848 }
1849
1850 /*
1851 * We do nothing here actually, esp. since this request is not expected for VBoxGuest.
1852 * The cleanup will be done in IRP_MN_REMOVE_DEVICE, which follows this call.
1853 */
1854 case IRP_MN_SURPRISE_REMOVAL:
1855 {
1856 Log(("vgdrvNtNt5PlusPnP: IRP_MN_SURPRISE_REMOVAL\n"));
1857 pDevExt->enmDevState = VGDRVNTDEVSTATE_SURPRISEREMOVED;
1858 LogRel(("VBoxGuest: unexpected device removal\n"));
1859
1860 /* Pass to the lower driver. */
1861 pIrp->IoStatus.Status = STATUS_SUCCESS;
1862
1863 IoSkipCurrentIrpStackLocation(pIrp);
1864 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1865
1866 /* Do not complete the IRP. */
1867 return rc;
1868 }
1869
1870 /*
1871 * Device and/or driver removal. Destroy everything.
1872 */
1873 case IRP_MN_REMOVE_DEVICE:
1874 {
1875 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE\n"));
1876 pDevExt->enmDevState = VGDRVNTDEVSTATE_REMOVED;
1877
1878 /*
1879 * Disconnect interrupts and delete all hardware resources.
1880 * Note! This may already have been done if we're STOPPED already, if that's a possibility.
1881 */
1882 vgdrvNtDeleteDeviceResources(pDevExt);
1883
1884 /*
1885 * We need to send the remove down the stack before we detach, but we don't need
1886 * to wait for the completion of this operation (nor register a completion routine).
1887 */
1888 pIrp->IoStatus.Status = STATUS_SUCCESS;
1889
1890 IoSkipCurrentIrpStackLocation(pIrp);
1891 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1892 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1893
1894 IoDetachDevice(pDevExt->pNextLowerDriver);
1895 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Removing device ...\n"));
1896
1897 /*
1898 * Delete the remainder of the device extension data, unlink it from the namespace and delete it.
1899 */
1900 vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
1901
1902 pDevObj = NULL; /* invalid */
1903 pDevExt = NULL; /* invalid */
1904
1905 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Device removed!\n"));
1906 return rc; /* Propagating rc from IoCallDriver. */
1907 }
1908
1909
1910 /*
1911 * Sent before stopping the device/driver to check whether it is okay to do so.
1912 */
1913 case IRP_MN_QUERY_STOP_DEVICE:
1914 {
1915 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE\n"));
1916 RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
1917 rc = vgdrvNtCheckIdle(pDevExt, "QUERY_STOP_DEVICE");
1918 if (NT_SUCCESS(rc))
1919 {
1920 pDevExt->enmPrevDevState = pDevExt->enmDevState;
1921 pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGSTOP;
1922 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1923
1924 /* This IRP passed down to lower driver. */
1925 pIrp->IoStatus.Status = STATUS_SUCCESS;
1926
1927 IoSkipCurrentIrpStackLocation(pIrp);
1928
1929 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1930 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1931
1932 /* we must not do anything with the IRP after doing IoSkip & CallDriver since the
1933 driver below us will complete (or already have completed) the IRP. I.e. just
1934 return the status we got from IoCallDriver. */
1935 }
1936 else
1937 {
1938 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1939 pIrp->IoStatus.Status = rc;
1940 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1941 }
1942
1943 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Returning with rc = 0x%x\n", rc));
1944 return rc;
1945 }
1946
1947 /*
1948 * Cancels a pending remove, IRP_MN_QUERY_STOP_DEVICE.
1949 * We only have to revert the state.
1950 */
1951 case IRP_MN_CANCEL_STOP_DEVICE:
1952 {
1953 Log(("vgdrvNtNt5PlusPnP: CANCEL_STOP_DEVICE\n"));
1954
1955 /* This must be handled first by the lower driver. */
1956 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1957 if ( NT_SUCCESS(rc)
1958 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGSTOP)
1959 {
1960 /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */
1961 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1962 }
1963
1964 /* Complete the IRP. */
1965 pIrp->IoStatus.Status = rc;
1966 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1967 return rc;
1968 }
1969
1970 /*
1971 * Stop the device.
1972 */
1973 case IRP_MN_STOP_DEVICE:
1974 {
1975 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE\n"));
1976 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
1977
1978 /*
1979 * Release the hardware resources.
1980 */
1981 vgdrvNtDeleteDeviceResources(pDevExt);
1982
1983 /*
1984 * Pass the request to the lower driver.
1985 */
1986 pIrp->IoStatus.Status = STATUS_SUCCESS;
1987 IoSkipCurrentIrpStackLocation(pIrp);
1988 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1989 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1990 return rc;
1991 }
1992
1993 default:
1994 {
1995 IoSkipCurrentIrpStackLocation(pIrp);
1996 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1997 Log(("vgdrvNtNt5PlusPnP: Unknown request %#x: Lower driver replied: %x\n", bMinorFunction, rc));
1998 return rc;
1999 }
2000 }
2001}
2002
2003
2004/**
2005 * Handle the power completion event.
2006 *
2007 * @returns NT status code.
2008 * @param pDevObj Targetted device object.
2009 * @param pIrp IO request packet.
2010 * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower.
2011 */
2012static NTSTATUS vgdrvNtNt5PlusPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext)
2013{
2014#ifdef VBOX_STRICT
2015 RT_NOREF1(pDevObj);
2016 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext;
2017 PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
2018
2019 Assert(pDevExt);
2020
2021 if (pIrpSp)
2022 {
2023 Assert(pIrpSp->MajorFunction == IRP_MJ_POWER);
2024 if (NT_SUCCESS(pIrp->IoStatus.Status))
2025 {
2026 switch (pIrpSp->MinorFunction)
2027 {
2028 case IRP_MN_SET_POWER:
2029 switch (pIrpSp->Parameters.Power.Type)
2030 {
2031 case DevicePowerState:
2032 switch (pIrpSp->Parameters.Power.State.DeviceState)
2033 {
2034 case PowerDeviceD0:
2035 break;
2036 default: /* Shut up MSC */
2037 break;
2038 }
2039 break;
2040 default: /* Shut up MSC */
2041 break;
2042 }
2043 break;
2044 }
2045 }
2046 }
2047#else
2048 RT_NOREF3(pDevObj, pIrp, pContext);
2049#endif
2050
2051 return STATUS_SUCCESS;
2052}
2053
2054
2055/**
2056 * Handle the Power requests.
2057 *
2058 * @returns NT status code
2059 * @param pDevObj device object
2060 * @param pIrp IRP
2061 */
2062static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2063{
2064 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2065 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2066 POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type;
2067 POWER_STATE PowerState = pStack->Parameters.Power.State;
2068 POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType;
2069
2070 Log(("vgdrvNtNt5PlusPower:\n"));
2071
2072 switch (pStack->MinorFunction)
2073 {
2074 case IRP_MN_SET_POWER:
2075 {
2076 Log(("vgdrvNtNt5PlusPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType));
2077 switch (enmPowerType)
2078 {
2079 case SystemPowerState:
2080 {
2081 Log(("vgdrvNtNt5PlusPower: SystemPowerState, action = %d, state = %d/%d\n",
2082 enmPowerAction, PowerState.SystemState, PowerState.DeviceState));
2083
2084 switch (enmPowerAction)
2085 {
2086 case PowerActionSleep:
2087
2088 /* System now is in a working state. */
2089 if (PowerState.SystemState == PowerSystemWorking)
2090 {
2091 if ( pDevExt
2092 && pDevExt->enmLastSystemPowerAction == PowerActionHibernate)
2093 {
2094 Log(("vgdrvNtNt5PlusPower: Returning from hibernation!\n"));
2095 int rc = VGDrvCommonReinitDevExtAfterHibernation(&pDevExt->Core,
2096 vgdrvNtVersionToOSType(g_enmVGDrvNtVer));
2097 if (RT_FAILURE(rc))
2098 Log(("vgdrvNtNt5PlusPower: Cannot re-init VMMDev chain, rc = %d!\n", rc));
2099 }
2100 }
2101 break;
2102
2103 case PowerActionShutdownReset:
2104 {
2105 Log(("vgdrvNtNt5PlusPower: Power action reset!\n"));
2106
2107 /* Tell the VMM that we no longer support mouse pointer integration. */
2108 VMMDevReqMouseStatus *pReq = NULL;
2109 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus),
2110 VMMDevReq_SetMouseStatus);
2111 if (RT_SUCCESS(vrc))
2112 {
2113 pReq->mouseFeatures = 0;
2114 pReq->pointerXPos = 0;
2115 pReq->pointerYPos = 0;
2116
2117 vrc = VbglR0GRPerform(&pReq->header);
2118 if (RT_FAILURE(vrc))
2119 {
2120 Log(("vgdrvNtNt5PlusPower: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
2121 }
2122
2123 VbglR0GRFree(&pReq->header);
2124 }
2125
2126 /* Don't do any cleanup here; there might be still coming in some IOCtls after we got this
2127 * power action and would assert/crash when we already cleaned up all the stuff! */
2128 break;
2129 }
2130
2131 case PowerActionShutdown:
2132 case PowerActionShutdownOff:
2133 {
2134 Log(("vgdrvNtNt5PlusPower: Power action shutdown!\n"));
2135 if (PowerState.SystemState >= PowerSystemShutdown)
2136 {
2137 Log(("vgdrvNtNt5PlusPower: Telling the VMMDev to close the VM ...\n"));
2138
2139 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
2140 int vrc = VERR_NOT_IMPLEMENTED;
2141 if (pReq)
2142 {
2143 pReq->header.requestType = VMMDevReq_SetPowerStatus;
2144 pReq->powerState = VMMDevPowerState_PowerOff;
2145
2146 vrc = VbglR0GRPerform(&pReq->header);
2147 }
2148 if (RT_FAILURE(vrc))
2149 Log(("vgdrvNtNt5PlusPower: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
2150
2151 /* No need to do cleanup here; at this point we should've been
2152 * turned off by VMMDev already! */
2153 }
2154 break;
2155 }
2156
2157 case PowerActionHibernate:
2158 Log(("vgdrvNtNt5PlusPower: Power action hibernate!\n"));
2159 break;
2160
2161 case PowerActionWarmEject:
2162 Log(("vgdrvNtNt5PlusPower: PowerActionWarmEject!\n"));
2163 break;
2164
2165 default:
2166 Log(("vgdrvNtNt5PlusPower: %d\n", enmPowerAction));
2167 break;
2168 }
2169
2170 /*
2171 * Save the current system power action for later use.
2172 * This becomes handy when we return from hibernation for example.
2173 */
2174 if (pDevExt)
2175 pDevExt->enmLastSystemPowerAction = enmPowerAction;
2176
2177 break;
2178 }
2179 default:
2180 break;
2181 }
2182 break;
2183 }
2184 default:
2185 break;
2186 }
2187
2188 /*
2189 * Whether we are completing or relaying this power IRP,
2190 * we must call PoStartNextPowerIrp.
2191 */
2192 g_pfnPoStartNextPowerIrp(pIrp);
2193
2194 /*
2195 * Send the IRP down the driver stack, using PoCallDriver
2196 * (not IoCallDriver, as for non-power irps).
2197 */
2198 IoCopyCurrentIrpStackLocationToNext(pIrp);
2199 IoSetCompletionRoutine(pIrp,
2200 vgdrvNtNt5PlusPowerComplete,
2201 (PVOID)pDevExt,
2202 TRUE,
2203 TRUE,
2204 TRUE);
2205 return g_pfnPoCallDriver(pDevExt->pNextLowerDriver, pIrp);
2206}
2207
2208
2209/**
2210 * IRP_MJ_SYSTEM_CONTROL handler.
2211 *
2212 * @returns NT status code
2213 * @param pDevObj Device object.
2214 * @param pIrp IRP.
2215 */
2216static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2217{
2218 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2219
2220 LogFlowFuncEnter();
2221
2222 /* Always pass it on to the next driver. */
2223 IoSkipCurrentIrpStackLocation(pIrp);
2224
2225 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
2226}
2227
2228
2229/**
2230 * Unload the driver.
2231 *
2232 * @param pDrvObj Driver object.
2233 */
2234static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
2235{
2236 LogFlowFuncEnter();
2237
2238#ifdef TARGET_NT4
2239 /*
2240 * We need to destroy the device object here on NT4 and earlier.
2241 */
2242 PDEVICE_OBJECT pDevObj = pDrvObj->DeviceObject;
2243 if (pDevObj)
2244 {
2245 if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
2246 {
2247 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2248 AssertPtr(pDevExt);
2249 AssertMsg(pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES,
2250 ("uInitState=%#x\n", pDevExt->Core.uInitState));
2251
2252 vgdrvNtDeleteDeviceResources(pDevExt);
2253 vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
2254 }
2255 }
2256#else /* !TARGET_NT4 */
2257 /*
2258 * On a PnP driver this routine will be called after IRP_MN_REMOVE_DEVICE
2259 * where we already did the cleanup, so don't do anything here (yet).
2260 */
2261 RT_NOREF1(pDrvObj);
2262#endif /* !TARGET_NT4 */
2263
2264 VGDrvCommonDestroyLoggers();
2265 RTR0Term();
2266
2267 /*
2268 * Finally deregister the bugcheck callback. Do it late to catch trouble in RTR0Term.
2269 */
2270 if (g_fBugCheckCallbackRegistered)
2271 {
2272 g_pfnKeDeregisterBugCheckCallback(&g_BugCheckCallbackRec);
2273 g_fBugCheckCallbackRegistered = false;
2274 }
2275}
2276
2277
2278/**
2279 * For simplifying request completion into a simple return statement, extended
2280 * version.
2281 *
2282 * @returns rcNt
2283 * @param rcNt The status code.
2284 * @param uInfo Extra info value.
2285 * @param pIrp The IRP.
2286 */
2287DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
2288{
2289 pIrp->IoStatus.Status = rcNt;
2290 pIrp->IoStatus.Information = uInfo;
2291 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2292 return rcNt;
2293}
2294
2295
2296/**
2297 * For simplifying request completion into a simple return statement.
2298 *
2299 * @returns rcNt
2300 * @param rcNt The status code.
2301 * @param pIrp The IRP.
2302 */
2303DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
2304{
2305 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
2306}
2307
2308
2309/**
2310 * Checks if NT authority rev 1 SID (SECURITY_NT_AUTHORITY).
2311 *
2312 * @returns true / false.
2313 * @param pSid The SID to check.
2314 */
2315DECLINLINE(bool) vgdrvNtIsSidNtAuth(struct _SID const *pSid)
2316{
2317 return pSid != NULL
2318 && pSid->Revision == 1
2319 && pSid->IdentifierAuthority.Value[5] == 5
2320 && pSid->IdentifierAuthority.Value[4] == 0
2321 && pSid->IdentifierAuthority.Value[3] == 0
2322 && pSid->IdentifierAuthority.Value[2] == 0
2323 && pSid->IdentifierAuthority.Value[1] == 0
2324 && pSid->IdentifierAuthority.Value[0] == 0;
2325}
2326
2327
2328/**
2329 * Matches SID with local system user (S-1-5-18 / SECURITY_LOCAL_SYSTEM_RID).
2330 */
2331DECLINLINE(bool) vgdrvNtIsSidLocalSystemUser(SID const *pSid)
2332{
2333 return vgdrvNtIsSidNtAuth(pSid)
2334 && pSid->SubAuthorityCount == 1
2335 && pSid->SubAuthority[0] == SECURITY_LOCAL_SYSTEM_RID;
2336}
2337
2338
2339/**
2340 * Matches SID with NT system admin user (S-1-5-*-500 / DOMAIN_USER_RID_ADMIN).
2341 */
2342DECLINLINE(bool) vgdrvNtIsSidAdminUser(SID const *pSid)
2343{
2344 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2345 return vgdrvNtIsSidNtAuth(pSid)
2346 && pSid->SubAuthorityCount >= 2
2347 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2348 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_ADMIN;
2349}
2350
2351
2352/**
2353 * Matches SID with NT system guest user (S-1-5-*-501 / DOMAIN_USER_RID_GUEST).
2354 */
2355DECLINLINE(bool) vgdrvNtIsSidGuestUser(SID const *pSid)
2356{
2357 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2358 return vgdrvNtIsSidNtAuth(pSid)
2359 && pSid->SubAuthorityCount >= 2
2360 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2361 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_GUEST;
2362}
2363
2364
2365/**
2366 * Matches SID with NT system admins group (S-1-5-32-544, S-1-5-*-512).
2367 */
2368DECLINLINE(bool) vgdrvNtIsSidAdminsGroup(SID const *pSid)
2369{
2370 return vgdrvNtIsSidNtAuth(pSid)
2371 && ( ( pSid->SubAuthorityCount == 2
2372 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2373 && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_ADMINS)
2374#if 0
2375 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2376 || ( pSid->SubAuthorityCount >= 2
2377 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2378 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_ADMINS)
2379#endif
2380 );
2381}
2382
2383
2384/**
2385 * Matches SID with NT system users group (S-1-5-32-545, S-1-5-32-547, S-1-5-*-512).
2386 */
2387DECLINLINE(bool) vgdrvNtIsSidUsersGroup(SID const *pSid)
2388{
2389 return vgdrvNtIsSidNtAuth(pSid)
2390 && ( ( pSid->SubAuthorityCount == 2
2391 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2392 && ( pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_USERS
2393 || pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_POWER_USERS) )
2394#if 0
2395 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2396 || ( pSid->SubAuthorityCount >= 2
2397 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2398 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_USERS)
2399#endif
2400 );
2401}
2402
2403
2404/**
2405 * Matches SID with NT system guests group (S-1-5-32-546, S-1-5-*-512).
2406 */
2407DECLINLINE(bool) vgdrvNtIsSidGuestsGroup(SID const *pSid)
2408{
2409 return vgdrvNtIsSidNtAuth(pSid)
2410 && ( ( pSid->SubAuthorityCount == 2
2411 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2412 && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_GUESTS)
2413#if 0
2414 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2415 || ( pSid->SubAuthorityCount >= 2
2416 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2417 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_GUESTS)
2418#endif
2419 );
2420}
2421
2422
2423/**
2424 * Checks if local authority rev 1 SID (SECURITY_LOCAL_SID_AUTHORITY).
2425 *
2426 * @returns true / false.
2427 * @param pSid The SID to check.
2428 */
2429DECLINLINE(bool) vgdrvNtIsSidLocalAuth(struct _SID const *pSid)
2430{
2431 return pSid != NULL
2432 && pSid->Revision == 1
2433 && pSid->IdentifierAuthority.Value[5] == 2
2434 && pSid->IdentifierAuthority.Value[4] == 0
2435 && pSid->IdentifierAuthority.Value[3] == 0
2436 && pSid->IdentifierAuthority.Value[2] == 0
2437 && pSid->IdentifierAuthority.Value[1] == 0
2438 && pSid->IdentifierAuthority.Value[0] == 0;
2439}
2440
2441
2442/**
2443 * Matches SID with console logon group (S-1-2-1 / SECURITY_LOCAL_LOGON_RID).
2444 */
2445DECLINLINE(bool) vgdrvNtIsSidConsoleLogonGroup(SID const *pSid)
2446{
2447 return vgdrvNtIsSidLocalAuth(pSid)
2448 && pSid->SubAuthorityCount == 1
2449 && pSid->SubAuthority[0] == SECURITY_LOCAL_LOGON_RID;
2450}
2451
2452
2453/**
2454 * Checks if mandatory label authority rev 1 SID (SECURITY_MANDATORY_LABEL_AUTHORITY).
2455 *
2456 * @returns true / false.
2457 * @param pSid The SID to check.
2458 */
2459DECLINLINE(bool) vgdrvNtIsSidMandatoryLabelAuth(struct _SID const *pSid)
2460{
2461 return pSid != NULL
2462 && pSid->Revision == 1
2463 && pSid->IdentifierAuthority.Value[5] == 16
2464 && pSid->IdentifierAuthority.Value[4] == 0
2465 && pSid->IdentifierAuthority.Value[3] == 0
2466 && pSid->IdentifierAuthority.Value[2] == 0
2467 && pSid->IdentifierAuthority.Value[1] == 0
2468 && pSid->IdentifierAuthority.Value[0] == 0;
2469}
2470
2471
2472#ifdef LOG_ENABLED
2473/** Format an SID for logging. */
2474static const char *vgdrvNtFormatSid(char *pszBuf, size_t cbBuf, struct _SID const *pSid)
2475{
2476 uint64_t uAuth = RT_MAKE_U64_FROM_U8(pSid->IdentifierAuthority.Value[5], pSid->IdentifierAuthority.Value[4],
2477 pSid->IdentifierAuthority.Value[3], pSid->IdentifierAuthority.Value[2],
2478 pSid->IdentifierAuthority.Value[1], pSid->IdentifierAuthority.Value[0],
2479 0, 0);
2480 ssize_t offCur = RTStrPrintf2(pszBuf, cbBuf, "S-%u-%RU64", pSid->Revision, uAuth);
2481 ULONG const *puSubAuth = &pSid->SubAuthority[0];
2482 unsigned cSubAuths = pSid->SubAuthorityCount;
2483 while (cSubAuths > 0 && (size_t)offCur < cbBuf)
2484 {
2485 ssize_t cchThis = RTStrPrintf2(&pszBuf[offCur], cbBuf - (size_t)offCur, "-%u", *puSubAuth);
2486 if (cchThis > 0)
2487 {
2488 offCur += cchThis;
2489 puSubAuth++;
2490 cSubAuths--;
2491 }
2492 else
2493 {
2494 Assert(cbBuf >= 5);
2495 pszBuf[cbBuf - 4] = '.';
2496 pszBuf[cbBuf - 3] = '.';
2497 pszBuf[cbBuf - 2] = '.';
2498 pszBuf[cbBuf - 1] = '\0';
2499 break;
2500 }
2501 }
2502 return pszBuf;
2503}
2504#endif
2505
2506
2507/**
2508 * Calculate requestor flags for the current process.
2509 *
2510 * ASSUMES vgdrvNtCreate is executed in the context of the process and thread
2511 * doing the NtOpenFile call.
2512 *
2513 * @returns VMMDEV_REQUESTOR_XXX
2514 */
2515static uint32_t vgdrvNtCalcRequestorFlags(void)
2516{
2517 uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE
2518 | VMMDEV_REQUESTOR_USR_NOT_GIVEN
2519 | VMMDEV_REQUESTOR_CON_DONT_KNOW
2520 | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN
2521 | VMMDEV_REQUESTOR_NO_USER_DEVICE;
2522 HANDLE hToken = NULL;
2523 NTSTATUS rcNt = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken);
2524 if (NT_SUCCESS(rcNt))
2525 {
2526 union
2527 {
2528 TOKEN_USER CurUser;
2529 TOKEN_GROUPS CurGroups;
2530 uint8_t abPadding[256];
2531 } Buf;
2532#ifdef LOG_ENABLED
2533 char szSid[200];
2534#endif
2535
2536 /*
2537 * Get the user SID and see if it's a standard one.
2538 */
2539 RT_ZERO(Buf.CurUser);
2540 ULONG cbReturned = 0;
2541 rcNt = ZwQueryInformationToken(hToken, TokenUser, &Buf.CurUser, sizeof(Buf), &cbReturned);
2542 if (NT_SUCCESS(rcNt))
2543 {
2544 struct _SID const *pSid = (struct _SID const *)Buf.CurUser.User.Sid;
2545 Log5(("vgdrvNtCalcRequestorFlags: TokenUser: %#010x %s\n",
2546 Buf.CurUser.User.Attributes, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
2547
2548 if (vgdrvNtIsSidLocalSystemUser(pSid))
2549 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_SYSTEM;
2550 else if (vgdrvNtIsSidAdminUser(pSid))
2551 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_ROOT;
2552 else if (vgdrvNtIsSidGuestUser(pSid))
2553 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
2554 }
2555 else
2556 LogRel(("vgdrvNtCalcRequestorFlags: TokenUser query failed: %#x\n", rcNt));
2557
2558 /*
2559 * Get the groups.
2560 */
2561 TOKEN_GROUPS *pCurGroupsFree = NULL;
2562 TOKEN_GROUPS *pCurGroups = &Buf.CurGroups;
2563 uint32_t cbCurGroups = sizeof(Buf);
2564 cbReturned = 0;
2565 RT_ZERO(Buf);
2566 rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
2567 if (rcNt == STATUS_BUFFER_TOO_SMALL)
2568 {
2569 uint32_t cTries = 8;
2570 do
2571 {
2572 RTMemTmpFree(pCurGroupsFree);
2573 if (cbCurGroups < cbReturned)
2574 cbCurGroups = RT_ALIGN_32(cbCurGroups + 32, 64);
2575 else
2576 cbCurGroups += 64;
2577 pCurGroupsFree = pCurGroups = (TOKEN_GROUPS *)RTMemTmpAllocZ(cbCurGroups);
2578 if (pCurGroupsFree)
2579 rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
2580 else
2581 rcNt = STATUS_NO_MEMORY;
2582 } while (rcNt == STATUS_BUFFER_TOO_SMALL && cTries-- > 0);
2583 }
2584 if (NT_SUCCESS(rcNt))
2585 {
2586 bool fGuestsMember = false;
2587 bool fUsersMember = false;
2588 if (g_enmVGDrvNtVer >= VGDRVNTVER_WIN7)
2589 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_NO;
2590
2591 for (uint32_t iGrp = 0; iGrp < pCurGroups->GroupCount; iGrp++)
2592 {
2593 uint32_t const fAttribs = pCurGroups->Groups[iGrp].Attributes;
2594 struct _SID const *pSid = (struct _SID const *)pCurGroups->Groups[iGrp].Sid;
2595 Log5(("vgdrvNtCalcRequestorFlags: TokenGroups[%u]: %#10x %s\n",
2596 iGrp, fAttribs, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
2597
2598 if ( (fAttribs & SE_GROUP_INTEGRITY_ENABLED)
2599 && vgdrvNtIsSidMandatoryLabelAuth(pSid)
2600 && pSid->SubAuthorityCount == 1
2601 && (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)
2602 {
2603 fRequestor &= ~VMMDEV_REQUESTOR_TRUST_MASK;
2604 if (pSid->SubAuthority[0] < SECURITY_MANDATORY_LOW_RID)
2605 fRequestor |= VMMDEV_REQUESTOR_TRUST_UNTRUSTED;
2606 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_RID)
2607 fRequestor |= VMMDEV_REQUESTOR_TRUST_LOW;
2608 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_PLUS_RID)
2609 fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM;
2610 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_HIGH_RID)
2611 fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM_PLUS;
2612 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_SYSTEM_RID)
2613 fRequestor |= VMMDEV_REQUESTOR_TRUST_HIGH;
2614 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
2615 fRequestor |= VMMDEV_REQUESTOR_TRUST_SYSTEM;
2616 else
2617 fRequestor |= VMMDEV_REQUESTOR_TRUST_PROTECTED;
2618 Log5(("vgdrvNtCalcRequestorFlags: mandatory label %u: => %#x\n", pSid->SubAuthority[0], fRequestor));
2619 }
2620 else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
2621 == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
2622 && vgdrvNtIsSidConsoleLogonGroup(pSid))
2623 {
2624 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_YES;
2625 Log5(("vgdrvNtCalcRequestorFlags: console: => %#x\n", fRequestor));
2626 }
2627 else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
2628 == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
2629 && vgdrvNtIsSidNtAuth(pSid))
2630 {
2631 if (vgdrvNtIsSidAdminsGroup(pSid))
2632 {
2633 fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
2634 Log5(("vgdrvNtCalcRequestorFlags: admins group: => %#x\n", fRequestor));
2635 }
2636 else if (vgdrvNtIsSidUsersGroup(pSid))
2637 {
2638 Log5(("vgdrvNtCalcRequestorFlags: users group\n"));
2639 fUsersMember = true;
2640 }
2641 else if (vgdrvNtIsSidGuestsGroup(pSid))
2642 {
2643 Log5(("vgdrvNtCalcRequestorFlags: guests group\n"));
2644 fGuestsMember = true;
2645 }
2646 }
2647 }
2648 if ((fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_NOT_GIVEN)
2649 {
2650 if (fUsersMember)
2651 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_USER;
2652 else if (fGuestsMember)
2653 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
2654 }
2655 }
2656 else
2657 LogRel(("vgdrvNtCalcRequestorFlags: TokenGroups query failed: %#x\n", rcNt));
2658
2659 RTMemTmpFree(pCurGroupsFree);
2660 ZwClose(hToken);
2661
2662 /*
2663 * Determine whether we should set VMMDEV_REQUESTOR_USER_DEVICE or not.
2664 *
2665 * The purpose here is to differentiate VBoxService accesses
2666 * from VBoxTray and VBoxControl, as VBoxService should be allowed to
2667 * do more than the latter two. VBoxService normally runs under the
2668 * system account which is easily detected, but for debugging and
2669 * similar purposes we also allow an elevated admin to run it as well.
2670 */
2671 if ( (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_UNTRUSTED /* general paranoia wrt system account */
2672 || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_LOW /* ditto */
2673 || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_MEDIUM /* ditto */
2674 || !( (fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_SYSTEM
2675 || ( ( (fRequestor & VMMDEV_REQUESTOR_GRP_WHEEL)
2676 || (fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_ROOT)
2677 && ( (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) >= VMMDEV_REQUESTOR_TRUST_HIGH
2678 || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)) ))
2679 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
2680 }
2681 else
2682 {
2683 LogRel(("vgdrvNtCalcRequestorFlags: NtOpenProcessToken query failed: %#x\n", rcNt));
2684 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
2685 }
2686
2687 Log5(("vgdrvNtCalcRequestorFlags: returns %#x\n", fRequestor));
2688 return fRequestor;
2689}
2690
2691
2692/**
2693 * Create (i.e. Open) file entry point.
2694 *
2695 * @param pDevObj Device object.
2696 * @param pIrp Request packet.
2697 */
2698static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2699{
2700 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
2701 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2702 PFILE_OBJECT pFileObj = pStack->FileObject;
2703 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2704
2705 Assert(pFileObj->FsContext == NULL);
2706
2707 /*
2708 * We are not remotely similar to a directory...
2709 */
2710 NTSTATUS rcNt;
2711 if (!(pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE))
2712 {
2713 /*
2714 * Check the device state. We enter the critsect in shared mode to
2715 * prevent race with PnP system requests checking whether we're idle.
2716 */
2717 RTCritSectRwEnterShared(&pDevExt->SessionCreateCritSect);
2718 VGDRVNTDEVSTATE const enmDevState = pDevExt->enmDevState;
2719 if (enmDevState == VGDRVNTDEVSTATE_OPERATIONAL)
2720 {
2721 /*
2722 * Create a client session.
2723 */
2724 int rc;
2725 PVBOXGUESTSESSION pSession;
2726 if (pIrp->RequestorMode == KernelMode)
2727 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
2728 else
2729 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, vgdrvNtCalcRequestorFlags(), &pSession);
2730 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2731 if (RT_SUCCESS(rc))
2732 {
2733 pFileObj->FsContext = pSession;
2734 Log(("vgdrvNtCreate: Successfully created %s session %p (fRequestor=%#x)\n",
2735 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession, pSession->fRequestor));
2736
2737 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
2738 }
2739
2740 /* Note. the IoStatus is completely ignored on error. */
2741 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
2742 if (rc == VERR_NO_MEMORY)
2743 rcNt = STATUS_NO_MEMORY;
2744 else
2745 rcNt = STATUS_UNSUCCESSFUL;
2746 }
2747 else
2748 {
2749 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2750 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", enmDevState));
2751 rcNt = STATUS_DEVICE_NOT_READY;
2752 }
2753 }
2754 else
2755 {
2756 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
2757 rcNt = STATUS_NOT_A_DIRECTORY;
2758 }
2759 return vgdrvNtCompleteRequest(rcNt, pIrp);
2760}
2761
2762
2763/**
2764 * Close file entry point.
2765 *
2766 * @param pDevObj Device object.
2767 * @param pIrp Request packet.
2768 */
2769static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2770{
2771 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2772 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2773 PFILE_OBJECT pFileObj = pStack->FileObject;
2774
2775 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
2776
2777#ifdef VBOX_WITH_HGCM
2778 /* Close both, R0 and R3 sessions. */
2779 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
2780 if (pSession)
2781 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
2782#endif
2783
2784 pFileObj->FsContext = NULL;
2785 pIrp->IoStatus.Information = 0;
2786 pIrp->IoStatus.Status = STATUS_SUCCESS;
2787 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2788
2789 return STATUS_SUCCESS;
2790}
2791
2792
2793/**
2794 * Device I/O Control entry point.
2795 *
2796 * @param pDevObj Device object.
2797 * @param pIrp Request packet.
2798 */
2799NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2800{
2801 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2802 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2803 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
2804
2805 if (!RT_VALID_PTR(pSession))
2806 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
2807
2808#if 0 /* No fast I/O controls defined yet. */
2809 /*
2810 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
2811 * the session and iCmd, and does not return anything.
2812 */
2813 if (pSession->fUnrestricted)
2814 {
2815 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
2816 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
2817 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
2818 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
2819 {
2820 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
2821
2822 /* Complete the I/O request. */
2823 supdrvSessionRelease(pSession);
2824 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
2825 }
2826 }
2827#endif
2828
2829 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
2830}
2831
2832
2833/**
2834 * Device I/O Control entry point.
2835 *
2836 * @param pDevExt The device extension.
2837 * @param pSession The session.
2838 * @param pIrp Request packet.
2839 * @param pStack The request stack pointer.
2840 */
2841static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2842 PIRP pIrp, PIO_STACK_LOCATION pStack)
2843{
2844 NTSTATUS rcNt;
2845 uint32_t cbOut = 0;
2846 int rc = 0;
2847 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
2848 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
2849 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
2850 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
2851
2852#if 0 /*def RT_ARCH_AMD64*/
2853 /* Don't allow 32-bit processes to do any I/O controls. */
2854 if (!IoIs32bitProcess(pIrp))
2855#endif
2856 {
2857 /* Verify that it's a buffered CTL. */
2858 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
2859 {
2860 /* Verify that the sizes in the request header are correct. */
2861 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
2862 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
2863 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
2864 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
2865 {
2866 /* Zero extra output bytes to make sure we don't leak anything. */
2867 if (pHdr->cbIn < pHdr->cbOut)
2868 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
2869
2870 /*
2871 * Do the job.
2872 */
2873 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
2874 RT_MAX(pHdr->cbIn, pHdr->cbOut));
2875 if (RT_SUCCESS(rc))
2876 {
2877 rcNt = STATUS_SUCCESS;
2878 cbOut = pHdr->cbOut;
2879 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
2880 {
2881 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
2882 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
2883 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
2884 }
2885
2886 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
2887 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
2888 && RT_SUCCESS(pHdr->rc))
2889 pStack->FileObject->FsContext = NULL;
2890 }
2891 else if (rc == VERR_NOT_SUPPORTED)
2892 rcNt = STATUS_NOT_SUPPORTED;
2893 else
2894 rcNt = STATUS_INVALID_PARAMETER;
2895 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
2896 }
2897 else
2898 {
2899 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
2900 pStack->Parameters.DeviceIoControl.IoControlCode,
2901 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
2902 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
2903 pStack->Parameters.DeviceIoControl.InputBufferLength,
2904 pStack->Parameters.DeviceIoControl.OutputBufferLength));
2905 rcNt = STATUS_INVALID_PARAMETER;
2906 }
2907 }
2908 else
2909 {
2910 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
2911 pStack->Parameters.DeviceIoControl.IoControlCode));
2912 rcNt = STATUS_NOT_SUPPORTED;
2913 }
2914 }
2915#if 0 /*def RT_ARCH_AMD64*/
2916 else
2917 {
2918 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
2919 rcNt = STATUS_NOT_SUPPORTED;
2920 }
2921#endif
2922
2923 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
2924}
2925
2926
2927/**
2928 * Internal Device I/O Control entry point (for IDC).
2929 *
2930 * @param pDevObj Device object.
2931 * @param pIrp Request packet.
2932 */
2933static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2934{
2935 /* Currently no special code here. */
2936 return vgdrvNtDeviceControl(pDevObj, pIrp);
2937}
2938
2939
2940/**
2941 * IRP_MJ_SHUTDOWN handler.
2942 *
2943 * @returns NT status code
2944 * @param pDevObj Device object.
2945 * @param pIrp IRP.
2946 */
2947static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2948{
2949 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2950 LogFlowFuncEnter();
2951
2952 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
2953 if (pReq)
2954 {
2955 pReq->header.requestType = VMMDevReq_SetPowerStatus;
2956 pReq->powerState = VMMDevPowerState_PowerOff;
2957
2958 int rc = VbglR0GRPerform(&pReq->header);
2959 if (RT_FAILURE(rc))
2960 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
2961 }
2962
2963 /* just in case, since we shouldn't normally get here. */
2964 pIrp->IoStatus.Information = 0;
2965 pIrp->IoStatus.Status = STATUS_SUCCESS;
2966 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2967 return STATUS_SUCCESS;
2968}
2969
2970
2971/**
2972 * Stub function for functions we don't implemented.
2973 *
2974 * @returns STATUS_NOT_SUPPORTED
2975 * @param pDevObj Device object.
2976 * @param pIrp IRP.
2977 */
2978static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2979{
2980 RT_NOREF1(pDevObj);
2981 LogFlowFuncEnter();
2982
2983 pIrp->IoStatus.Information = 0;
2984 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
2985 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2986
2987 return STATUS_NOT_SUPPORTED;
2988}
2989
2990
2991/**
2992 * Bug check callback (KBUGCHECK_CALLBACK_ROUTINE).
2993 *
2994 * This adds a log entry on the host, in case Hyper-V isn't active or the guest
2995 * is too old for reporting it itself via the crash MSRs.
2996 *
2997 * @param pvBuffer Not used.
2998 * @param cbBuffer Not used.
2999 */
3000static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer)
3001{
3002 if (g_pauKiBugCheckData)
3003 {
3004 RTLogBackdoorPrintf("VBoxGuest: BugCheck! P0=%#zx P1=%#zx P2=%#zx P3=%#zx P4=%#zx\n", g_pauKiBugCheckData[0],
3005 g_pauKiBugCheckData[1], g_pauKiBugCheckData[2], g_pauKiBugCheckData[3], g_pauKiBugCheckData[4]);
3006
3007 VMMDevReqNtBugCheck *pReq = NULL;
3008 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_NtBugCheck);
3009 if (RT_SUCCESS(rc))
3010 {
3011 pReq->uBugCheck = g_pauKiBugCheckData[0];
3012 pReq->auParameters[0] = g_pauKiBugCheckData[1];
3013 pReq->auParameters[1] = g_pauKiBugCheckData[2];
3014 pReq->auParameters[2] = g_pauKiBugCheckData[3];
3015 pReq->auParameters[3] = g_pauKiBugCheckData[4];
3016 VbglR0GRPerform(&pReq->header);
3017 VbglR0GRFree(&pReq->header);
3018 }
3019 }
3020 else
3021 {
3022 RTLogBackdoorPrintf("VBoxGuest: BugCheck!\n");
3023
3024 VMMDevRequestHeader *pReqHdr = NULL;
3025 int rc = VbglR0GRAlloc(&pReqHdr, sizeof(*pReqHdr), VMMDevReq_NtBugCheck);
3026 if (RT_SUCCESS(rc))
3027 {
3028 VbglR0GRPerform(pReqHdr);
3029 VbglR0GRFree(pReqHdr);
3030 }
3031 }
3032
3033 RT_NOREF(pvBuffer, cbBuffer);
3034}
3035
3036
3037/**
3038 * Sets the mouse notification callback.
3039 *
3040 * @returns VBox status code.
3041 * @param pDevExt Pointer to the device extension.
3042 * @param pNotify Pointer to the mouse notify struct.
3043 */
3044int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
3045{
3046 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
3047 /* we need a lock here to avoid concurrency with the set event functionality */
3048 KIRQL OldIrql;
3049 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql);
3050 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
3051 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
3052 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql);
3053 return VINF_SUCCESS;
3054}
3055
3056
3057/**
3058 * DPC handler.
3059 *
3060 * @param pDPC DPC descriptor.
3061 * @param pDevObj Device object.
3062 * @param pIrp Interrupt request packet.
3063 * @param pContext Context specific pointer.
3064 */
3065static void NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
3066{
3067 RT_NOREF3(pDPC, pIrp, pContext);
3068 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
3069 Log3Func(("pDevExt=0x%p\n", pDevExt));
3070
3071 /* Test & reset the counter. */
3072 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
3073 {
3074 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
3075 * i.e. to prevent the event from destroyed while we're using it */
3076 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
3077 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3078
3079 if (pDevExt->Core.pfnMouseNotifyCallback)
3080 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
3081
3082 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3083 }
3084
3085 /* Process the wake-up list we were asked by the scheduling a DPC
3086 * in vgdrvNtIsrHandler(). */
3087 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
3088}
3089
3090
3091/**
3092 * ISR handler.
3093 *
3094 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
3095 * @param pInterrupt Interrupt that was triggered.
3096 * @param pServiceContext Context specific pointer.
3097 */
3098static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
3099{
3100 RT_NOREF1(pInterrupt);
3101 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
3102 if (pDevExt == NULL)
3103 return FALSE;
3104
3105 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
3106
3107 /* Enter the common ISR routine and do the actual work. */
3108 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
3109
3110 /* If we need to wake up some events we do that in a DPC to make
3111 * sure we're called at the right IRQL. */
3112 if (fIRQTaken)
3113 {
3114 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
3115 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
3116 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
3117 {
3118 Log3Func(("Requesting DPC...\n"));
3119 IoRequestDpc(pDevExt->pDeviceObject, NULL /*pIrp*/, NULL /*pvContext*/);
3120 }
3121 }
3122 return fIRQTaken;
3123}
3124
3125
3126void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
3127{
3128 NOREF(pDevExt);
3129 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
3130 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
3131 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
3132}
3133
3134
3135/**
3136 * Hook for handling OS specfic options from the host.
3137 *
3138 * @returns true if handled, false if not.
3139 * @param pDevExt The device extension.
3140 * @param pszName The option name.
3141 * @param pszValue The option value.
3142 */
3143bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
3144{
3145 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
3146 return false;
3147}
3148
3149
3150/**
3151 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
3152 */
3153static NTSTATUS NTAPI vgdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
3154 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
3155{
3156 Log4(("vgdrvNtRegistryEnumCallback: pwszValueName=%ls uValueType=%#x Value=%.*Rhxs\n", pwszValueName, uValueType, cbValue, pvValue));
3157
3158 /*
3159 * Filter out general service config values.
3160 */
3161 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
3162 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
3163 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
3164 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
3165 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
3166 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
3167 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
3168 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
3169 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
3170 )
3171 {
3172 return STATUS_SUCCESS;
3173 }
3174
3175 /*
3176 * Convert the value name.
3177 */
3178 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
3179 if (cch < 64 && cch > 0)
3180 {
3181 char szValueName[72];
3182 char *pszTmp = szValueName;
3183 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
3184 if (RT_SUCCESS(rc))
3185 {
3186 /*
3187 * Convert the value.
3188 */
3189 char szValue[72];
3190 char *pszFree = NULL;
3191 char *pszValue = NULL;
3192 szValue[0] = '\0';
3193 switch (uValueType)
3194 {
3195 case REG_SZ:
3196 case REG_EXPAND_SZ:
3197 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
3198 if (RT_SUCCESS(rc) && cch < _1K)
3199 {
3200 if (cch < sizeof(szValue))
3201 {
3202 pszValue = szValue;
3203 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3204 }
3205 else
3206 {
3207 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3208 if (RT_SUCCESS(rc))
3209 pszFree = pszValue;
3210 }
3211 if (RT_FAILURE(rc))
3212 {
3213 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
3214 pwszValueName, rc));
3215 pszValue = NULL;
3216 }
3217 }
3218 else if (RT_SUCCESS(rc))
3219 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
3220 pwszValueName, cbValue, uValueType));
3221 else
3222 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
3223 pwszValueName, cbValue, uValueType));
3224 break;
3225
3226 case REG_DWORD:
3227 if (cbValue == sizeof(uint32_t))
3228 {
3229 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3230 pszValue = szValue;
3231 }
3232 else
3233 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3234 break;
3235
3236 case REG_QWORD:
3237 if (cbValue == sizeof(uint64_t))
3238 {
3239 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3240 pszValue = szValue;
3241 }
3242 else
3243 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3244 break;
3245
3246 default:
3247 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
3248 break;
3249 }
3250 if (pszValue)
3251 {
3252 /*
3253 * Process it.
3254 */
3255 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
3256 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
3257 if (pszFree)
3258 RTStrFree(pszFree);
3259 }
3260 }
3261 }
3262 else if (cch > 0)
3263 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
3264 else
3265 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
3266 NOREF(pvEntryCtx);
3267 return STATUS_SUCCESS;
3268}
3269
3270
3271/**
3272 * Reads configuration from the registry and guest properties.
3273 *
3274 * We ignore failures and instead preserve existing configuration values.
3275 *
3276 * Thie routine will block.
3277 *
3278 * @param pDevExt The device extension.
3279 */
3280static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
3281{
3282 /*
3283 * First the registry.
3284 *
3285 * Note! RTL_QUERY_REGISTRY_NOEXPAND is sensible (no environment) and also necessary to
3286 * avoid crash on NT 3.1 because RtlExpandEnvironmentStrings_U thinks its in ring-3
3287 * and tries to get the default heap from the PEB via the TEB. No TEB in ring-0.
3288 */
3289 RTL_QUERY_REGISTRY_TABLE aQuery[2];
3290 RT_ZERO(aQuery);
3291 aQuery[0].QueryRoutine = vgdrvNtRegistryEnumCallback;
3292 aQuery[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
3293 aQuery[0].Name = NULL;
3294 aQuery[0].EntryContext = NULL;
3295 aQuery[0].DefaultType = REG_NONE;
3296 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /*pwszzEnv*/);
3297 if (!NT_SUCCESS(rcNt))
3298 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
3299
3300 /*
3301 * Read configuration from the host.
3302 */
3303 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
3304}
3305
3306#ifdef VBOX_STRICT
3307
3308/**
3309 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
3310 */
3311static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
3312{
3313 AssertPtrReturn(pu32Bits, 0);
3314 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
3315 uint32_t u32Result = 0;
3316 uint32_t u32WorkingMask = u32Mask;
3317 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3318
3319 while (iBitOffset > 0)
3320 {
3321 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
3322 if (fSet)
3323 u32Result |= 1 << (iBitOffset - 1);
3324 u32WorkingMask &= ~(1 << (iBitOffset - 1));
3325 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3326 }
3327 LogFlowFunc(("Returning %#x\n", u32Result));
3328 return u32Result;
3329}
3330
3331
3332static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
3333{
3334 ULONG u32Bits2 = u32Bits;
3335 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
3336 if ( u32Result != u32Exp
3337 || (u32Bits2 & u32Mask)
3338 || (u32Bits2 & u32Result)
3339 || ((u32Bits2 | u32Result) != u32Bits)
3340 )
3341 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
3342 u32Mask, u32Bits, u32Bits2, u32Result));
3343}
3344
3345
3346static void vgdrvNtDoTests(void)
3347{
3348 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
3349 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
3350 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
3351 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
3352 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
3353 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
3354}
3355
3356#endif /* VBOX_STRICT */
3357
3358#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
3359
3360/*
3361 * DPC latency checker.
3362 */
3363
3364/**
3365 * One DPC latency sample.
3366 */
3367typedef struct DPCSAMPLE
3368{
3369 LARGE_INTEGER PerfDelta;
3370 LARGE_INTEGER PerfCounter;
3371 LARGE_INTEGER PerfFrequency;
3372 uint64_t u64TSC;
3373} DPCSAMPLE;
3374AssertCompileSize(DPCSAMPLE, 4*8);
3375
3376/**
3377 * The DPC latency measurement workset.
3378 */
3379typedef struct DPCDATA
3380{
3381 KDPC Dpc;
3382 KTIMER Timer;
3383 KSPIN_LOCK SpinLock;
3384
3385 ULONG ulTimerRes;
3386
3387 bool volatile fFinished;
3388
3389 /** The timer interval (relative). */
3390 LARGE_INTEGER DueTime;
3391
3392 LARGE_INTEGER PerfCounterPrev;
3393
3394 /** Align the sample array on a 64 byte boundrary just for the off chance
3395 * that we'll get cache line aligned memory backing this structure. */
3396 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
3397
3398 int cSamples;
3399 DPCSAMPLE aSamples[8192];
3400} DPCDATA;
3401
3402AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
3403
3404/**
3405 * DPC callback routine for the DPC latency measurement code.
3406 *
3407 * @param pDpc The DPC, not used.
3408 * @param pvDeferredContext Pointer to the DPCDATA.
3409 * @param SystemArgument1 System use, ignored.
3410 * @param SystemArgument2 System use, ignored.
3411 */
3412static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
3413{
3414 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
3415 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
3416
3417 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
3418
3419 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
3420 pData->fFinished = true;
3421 else
3422 {
3423 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
3424
3425 pSample->u64TSC = ASMReadTSC();
3426 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
3427 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
3428
3429 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
3430
3431 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3432 }
3433
3434 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
3435}
3436
3437
3438/**
3439 * Handles the DPC latency checker request.
3440 *
3441 * @returns VBox status code.
3442 */
3443int VGDrvNtIOCtl_DpcLatencyChecker(void)
3444{
3445 /*
3446 * Allocate a block of non paged memory for samples and related data.
3447 */
3448 DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
3449 if (!pData)
3450 {
3451 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
3452 return VERR_NO_MEMORY;
3453 }
3454
3455 /*
3456 * Initialize the data.
3457 */
3458 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
3459 KeInitializeTimer(&pData->Timer);
3460 KeInitializeSpinLock(&pData->SpinLock);
3461
3462 pData->fFinished = false;
3463 pData->cSamples = 0;
3464 pData->PerfCounterPrev.QuadPart = 0;
3465
3466 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
3467 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
3468
3469 /*
3470 * Start the DPC measurements and wait for a full set.
3471 */
3472 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3473
3474 while (!pData->fFinished)
3475 {
3476 LARGE_INTEGER Interval;
3477 Interval.QuadPart = -100 * 1000 * 10;
3478 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
3479 }
3480
3481 ExSetTimerResolution(0, 0);
3482
3483 /*
3484 * Log everything to the host.
3485 */
3486 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
3487 for (int i = 0; i < pData->cSamples; i++)
3488 {
3489 DPCSAMPLE *pSample = &pData->aSamples[i];
3490
3491 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
3492 i,
3493 pSample->PerfDelta.QuadPart,
3494 pSample->PerfCounter.QuadPart,
3495 pSample->PerfFrequency.QuadPart,
3496 pSample->u64TSC);
3497 }
3498
3499 RTMemFree(pData);
3500 return VINF_SUCCESS;
3501}
3502
3503#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
3504
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use