VirtualBox

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

Last change on this file since 99365 was 99365, checked in by vboxsync, 13 months ago

Additions/Win: Distinguish between normal/privileged users in VBoxGuest.sys (see bugref:10363).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.7 KB
Line 
1/* $Id: VBoxGuest-win.cpp 99365 2023-04-10 17:28:33Z 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 pvMMIOBase, cbMMIO,
1075 vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
1076 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
1077 if (RT_SUCCESS(vrc))
1078 {
1079
1080 vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
1081 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
1082 if (RT_SUCCESS(vrc))
1083 {
1084 /*
1085 * Register DPC and ISR.
1086 */
1087 LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
1088 IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
1089
1090 ULONG uInterruptVector = pDevExt->uInterruptVector;
1091 KIRQL uHandlerIrql = (KIRQL)pDevExt->uInterruptLevel;
1092#ifdef TARGET_NT4
1093 if (!pIrp)
1094 {
1095 /* NT4: Get an interrupt vector. Only proceed if the device provides an interrupt. */
1096 if ( uInterruptVector
1097 || pDevExt->uInterruptLevel)
1098 {
1099 LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
1100 pDevExt->uBus, pDevExt->uInterruptLevel, pDevExt->uInterruptVector));
1101 uInterruptVector = HalGetInterruptVector(g_enmVGDrvNtVer == VGDRVNTVER_WINNT310 ? Isa : PCIBus,
1102 pDevExt->uBus,
1103 pDevExt->uInterruptLevel,
1104 pDevExt->uInterruptVector,
1105 &uHandlerIrql,
1106 &pDevExt->fInterruptAffinity);
1107 LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
1108 }
1109 else
1110 LogFunc(("Device does not provide an interrupt!\n"));
1111 }
1112#endif
1113 if (uInterruptVector)
1114 {
1115 LogFlowFunc(("Connecting interrupt (IntVector=%#u), uHandlerIrql=%u) ...\n",
1116 uInterruptVector, uHandlerIrql));
1117
1118 rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
1119 vgdrvNtIsrHandler, /* Our ISR handler. */
1120 pDevExt, /* Device context. */
1121 NULL, /* Optional spinlock. */
1122 uInterruptVector, /* Interrupt vector. */
1123 uHandlerIrql, /* Irql. */
1124 uHandlerIrql, /* SynchronizeIrql. */
1125 pDevExt->enmInterruptMode, /* LevelSensitive or Latched. */
1126 TRUE, /* Shareable interrupt. */
1127 pDevExt->fInterruptAffinity, /* CPU affinity. */
1128 FALSE); /* Don't save FPU stack. */
1129 if (NT_ERROR(rcNt))
1130 LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
1131 }
1132 else
1133 LogFunc(("No interrupt vector found!\n"));
1134 if (NT_SUCCESS(rcNt))
1135 {
1136 /*
1137 * Once we've read configuration from register and host, we're finally read.
1138 */
1139 /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
1140 pDevExt->Core.fLoggingEnabled = true;
1141 vgdrvNtReadConfiguration(pDevExt);
1142
1143 /* Ready to rumble! */
1144 LogRelFunc(("Device is ready!\n"));
1145 pDevExt->enmDevState = VGDRVNTDEVSTATE_OPERATIONAL;
1146 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_OPERATIONAL;
1147 return STATUS_SUCCESS;
1148 }
1149
1150 pDevExt->pInterruptObject = NULL;
1151
1152 VbglR0GRFree(&pDevExt->pPowerStateRequest->header);
1153 pDevExt->pPowerStateRequest = NULL;
1154 }
1155 else
1156 {
1157 LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
1158 rcNt = STATUS_UNSUCCESSFUL;
1159 }
1160
1161 VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
1162 }
1163 else
1164 {
1165 LogFunc(("Could not init device extension resources: vrc=%Rrc\n", vrc));
1166 rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
1167 }
1168 vgdrvNtUnmapVMMDevMemory(pDevExt);
1169 }
1170 else
1171 LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
1172 }
1173
1174 LogFunc(("Returned with rcNt=%#x\n", rcNt));
1175 return rcNt;
1176}
1177
1178
1179
1180
1181#ifdef TARGET_NT4
1182# define PCI_CFG_ADDR 0xcf8
1183# define PCI_CFG_DATA 0xcfc
1184
1185/**
1186 * NT 3.1 doesn't do PCI nor HalSetBusDataByOffset, this is our fallback.
1187 */
1188static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
1189 void *pvData, ULONG offData, ULONG cbData)
1190{
1191 /*
1192 * Validate input a little bit.
1193 */
1194 RT_NOREF(enmBusDataType);
1195 Assert(idxBus <= 255);
1196 Assert(uSlot <= 255);
1197 Assert(offData <= 255);
1198 Assert(cbData > 0);
1199
1200 PCI_SLOT_NUMBER PciSlot;
1201 PciSlot.u.AsULONG = uSlot;
1202 uint32_t const idxAddrTop = UINT32_C(0x80000000)
1203 | (idxBus << 16)
1204 | (PciSlot.u.bits.DeviceNumber << 11)
1205 | (PciSlot.u.bits.FunctionNumber << 8);
1206
1207 /*
1208 * Write the given bytes.
1209 */
1210 uint8_t const *pbData = (uint8_t const *)pvData;
1211 uint32_t off = offData;
1212 uint32_t cbRet = 0;
1213
1214 /* Unaligned start. */
1215 if (off & 3)
1216 {
1217 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
1218 switch (off & 3)
1219 {
1220 case 1:
1221 ASMOutU8(PCI_CFG_DATA + 1, pbData[cbRet++]);
1222 if (cbRet >= cbData)
1223 break;
1224 RT_FALL_THRU();
1225 case 2:
1226 ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet++]);
1227 if (cbRet >= cbData)
1228 break;
1229 RT_FALL_THRU();
1230 case 3:
1231 ASMOutU8(PCI_CFG_DATA + 3, pbData[cbRet++]);
1232 break;
1233 }
1234 off = (off | 3) + 1;
1235 }
1236
1237 /* Bulk. */
1238 while (off < 256 && cbRet < cbData)
1239 {
1240 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
1241 switch (cbData - cbRet)
1242 {
1243 case 1:
1244 ASMOutU8(PCI_CFG_DATA, pbData[cbRet]);
1245 cbRet += 1;
1246 break;
1247 case 2:
1248 ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
1249 cbRet += 2;
1250 break;
1251 case 3:
1252 ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
1253 ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet + 2]);
1254 cbRet += 3;
1255 break;
1256 default:
1257 ASMOutU32(PCI_CFG_DATA, RT_MAKE_U32_FROM_U8(pbData[cbRet], pbData[cbRet + 1],
1258 pbData[cbRet + 2], pbData[cbRet + 3]));
1259 cbRet += 4;
1260 break;
1261 }
1262 off += 4;
1263 }
1264
1265 return cbRet;
1266}
1267
1268
1269/**
1270 * NT 3.1 doesn't do PCI nor HalGetBusDataByOffset, this is our fallback.
1271 */
1272static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
1273 void *pvData, ULONG offData, ULONG cbData)
1274{
1275 /*
1276 * Validate input a little bit.
1277 */
1278 RT_NOREF(enmBusDataType);
1279 Assert(idxBus <= 255);
1280 Assert(uSlot <= 255);
1281 Assert(offData <= 255);
1282 Assert(cbData > 0);
1283
1284 PCI_SLOT_NUMBER PciSlot;
1285 PciSlot.u.AsULONG = uSlot;
1286 uint32_t const idxAddrTop = UINT32_C(0x80000000)
1287 | (idxBus << 16)
1288 | (PciSlot.u.bits.DeviceNumber << 11)
1289 | (PciSlot.u.bits.FunctionNumber << 8);
1290
1291 /*
1292 * Read the header type.
1293 */
1294 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (VBOX_PCI_HEADER_TYPE & ~3));
1295 uint8_t bHdrType = ASMInU8(PCI_CFG_DATA + (VBOX_PCI_HEADER_TYPE & 3));
1296 if (bHdrType == 0xff)
1297 return idxBus < 8 ? 2 : 0; /* No device here */
1298 if ( offData == VBOX_PCI_HEADER_TYPE
1299 && cbData == 1)
1300 {
1301 *(uint8_t *)pvData = bHdrType;
1302 /*Log("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %02x\n", idxAddrTop, offData, bHdrType);*/
1303 return 1;
1304 }
1305
1306 /*
1307 * Read the requested bytes.
1308 */
1309 uint8_t *pbData = (uint8_t *)pvData;
1310 uint32_t off = offData;
1311 uint32_t cbRet = 0;
1312
1313 /* Unaligned start. */
1314 if (off & 3)
1315 {
1316 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
1317 uint32_t uValue = ASMInU32(PCI_CFG_DATA);
1318 switch (off & 3)
1319 {
1320 case 1:
1321 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1322 if (cbRet >= cbData)
1323 break;
1324 RT_FALL_THRU();
1325 case 2:
1326 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1327 if (cbRet >= cbData)
1328 break;
1329 RT_FALL_THRU();
1330 case 3:
1331 pbData[cbRet++] = (uint8_t)(uValue >> 24);
1332 break;
1333 }
1334 off = (off | 3) + 1;
1335 }
1336
1337 /* Bulk. */
1338 while (off < 256 && cbRet < cbData)
1339 {
1340 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
1341 uint32_t uValue = ASMInU32(PCI_CFG_DATA);
1342 switch (cbData - cbRet)
1343 {
1344 case 1:
1345 pbData[cbRet++] = (uint8_t)uValue;
1346 break;
1347 case 2:
1348 pbData[cbRet++] = (uint8_t)uValue;
1349 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1350 break;
1351 case 3:
1352 pbData[cbRet++] = (uint8_t)uValue;
1353 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1354 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1355 break;
1356 default:
1357 pbData[cbRet++] = (uint8_t)uValue;
1358 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1359 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1360 pbData[cbRet++] = (uint8_t)(uValue >> 24);
1361 break;
1362 }
1363 off += 4;
1364 }
1365
1366 Log(("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %.*Rhxs\n", idxAddrTop, offData, cbRet, pvData));
1367 return cbRet;
1368}
1369
1370
1371/**
1372 * Helper function to handle the PCI device lookup.
1373 *
1374 * @returns NT status code.
1375 *
1376 * @param puBus Where to return the bus number on success.
1377 * @param pSlot Where to return the slot number on success.
1378 */
1379static NTSTATUS vgdrvNt4FindPciDevice(PULONG puBus, PPCI_SLOT_NUMBER pSlot)
1380{
1381 Log(("vgdrvNt4FindPciDevice\n"));
1382
1383 PCI_SLOT_NUMBER Slot;
1384 Slot.u.AsULONG = 0;
1385
1386 /* Scan each bus. */
1387 for (ULONG uBus = 0; uBus < PCI_MAX_BUSES; uBus++)
1388 {
1389 /* Scan each device. */
1390 for (ULONG idxDevice = 0; idxDevice < PCI_MAX_DEVICES; idxDevice++)
1391 {
1392 Slot.u.bits.DeviceNumber = idxDevice;
1393 Slot.u.bits.FunctionNumber = 0;
1394
1395 /* Check the device header. */
1396 uint8_t bHeaderType = 0xff;
1397 ULONG cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG,
1398 &bHeaderType, VBOX_PCI_HEADER_TYPE, sizeof(bHeaderType));
1399 if (cbRet == 0)
1400 break;
1401 if (cbRet == 2 || bHeaderType == 0xff)
1402 continue;
1403
1404 /* Scan functions. */
1405 uint32_t const cFunctionStep = bHeaderType & 0x80 ? 1 : 8;
1406 Log(("vgdrvNt4FindPciDevice: %#x:%#x cFunctionStep=%d bHeaderType=%#x\n", uBus, idxDevice, cFunctionStep, bHeaderType));
1407 for (ULONG idxFunction = 0; idxFunction < PCI_MAX_FUNCTION; idxFunction += cFunctionStep)
1408 {
1409 Slot.u.bits.FunctionNumber = idxFunction;
1410
1411 /* Read the vendor and device IDs of this device and compare with the VMMDev. */
1412 struct
1413 {
1414 uint16_t idVendor;
1415 uint16_t idDevice;
1416 } Buf = { PCI_INVALID_VENDORID, PCI_INVALID_VENDORID };
1417 cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG, &Buf, VBOX_PCI_VENDOR_ID, sizeof(Buf));
1418 if ( cbRet == sizeof(Buf)
1419 && Buf.idVendor == VMMDEV_VENDORID
1420 && Buf.idDevice == VMMDEV_DEVICEID)
1421 {
1422 /* Hooray, we've found it! */
1423 Log(("vgdrvNt4FindPciDevice: Device found! Bus=%#x Slot=%#u (dev %#x, fun %#x, rvd %#x)\n",
1424 uBus, Slot.u.AsULONG, Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber, Slot.u.bits.Reserved));
1425
1426 *puBus = uBus;
1427 *pSlot = Slot;
1428 return STATUS_SUCCESS;
1429 }
1430 }
1431 }
1432 }
1433
1434 return STATUS_DEVICE_DOES_NOT_EXIST;
1435}
1436
1437
1438/**
1439 * Legacy helper function to create the device object.
1440 *
1441 * @returns NT status code.
1442 *
1443 * @param pDrvObj The driver object.
1444 * @param pRegPath The driver registry path.
1445 */
1446static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
1447{
1448 Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath));
1449
1450 /*
1451 * Find our virtual PCI device
1452 */
1453 ULONG uBus;
1454 PCI_SLOT_NUMBER uSlot;
1455 NTSTATUS rc = vgdrvNt4FindPciDevice(&uBus, &uSlot);
1456 if (NT_ERROR(rc))
1457 {
1458 Log(("vgdrvNt4CreateDevice: Device not found!\n"));
1459 return rc;
1460 }
1461
1462 /*
1463 * Create device.
1464 */
1465 UNICODE_STRING DevName;
1466 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
1467 PDEVICE_OBJECT pDeviceObject = NULL;
1468 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
1469 if (NT_SUCCESS(rc))
1470 {
1471 Log(("vgdrvNt4CreateDevice: Device created\n"));
1472
1473 UNICODE_STRING DosName;
1474 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1475 rc = IoCreateSymbolicLink(&DosName, &DevName);
1476 if (NT_SUCCESS(rc))
1477 {
1478 Log(("vgdrvNt4CreateDevice: Symlink created\n"));
1479
1480 /*
1481 * Setup the device extension.
1482 */
1483 Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n"));
1484 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
1485 int vrc = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
1486 if (RT_SUCCESS(vrc))
1487 {
1488 /* Store bus and slot number we've queried before. */
1489 pDevExt->uBus = uBus;
1490 pDevExt->uSlot = uSlot.u.AsULONG;
1491
1492 Log(("vgdrvNt4CreateDevice: Device extension created\n"));
1493
1494 /* Do the actual VBox init ... */
1495 rc = vgdrvNtSetupDevice(pDevExt, pDeviceObject, NULL /*pIrp*/, pDrvObj, pRegPath);
1496 if (NT_SUCCESS(rc))
1497 {
1498 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (success)\n", rc));
1499 return rc;
1500 }
1501
1502 /* bail out */
1503 vgdrvNtDeleteDevExtFundament(pDevExt);
1504 }
1505 IoDeleteSymbolicLink(&DosName);
1506 }
1507 else
1508 Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc));
1509 IoDeleteDevice(pDeviceObject);
1510 }
1511 else
1512 Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc));
1513 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc));
1514 return rc;
1515}
1516
1517#endif /* TARGET_NT4 */
1518
1519/**
1520 * Handle request from the Plug & Play subsystem.
1521 *
1522 * @returns NT status code
1523 * @param pDrvObj Driver object
1524 * @param pDevObj Device object
1525 *
1526 * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
1527 */
1528static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
1529{
1530 LogFlowFuncEnter();
1531
1532 /*
1533 * Create device.
1534 */
1535 UNICODE_STRING DevName;
1536 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
1537 PDEVICE_OBJECT pDeviceObject = NULL;
1538 NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
1539 if (NT_SUCCESS(rcNt))
1540 {
1541 /*
1542 * Create symbolic link (DOS devices).
1543 */
1544 UNICODE_STRING DosName;
1545 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1546 rcNt = IoCreateSymbolicLink(&DosName, &DevName);
1547 if (NT_SUCCESS(rcNt))
1548 {
1549 /*
1550 * Setup the device extension.
1551 */
1552 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
1553 rcNt = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
1554 if (NT_SUCCESS(rcNt))
1555 {
1556 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
1557 if (pDevExt->pNextLowerDriver != NULL)
1558 {
1559 /* Ensure we are not called at elevated IRQL, even if our code isn't pagable any more. */
1560 pDeviceObject->Flags |= DO_POWER_PAGABLE;
1561
1562 /* Driver is ready now. */
1563 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1564 LogFlowFunc(("Returning with rcNt=%#x (success)\n", rcNt));
1565 return rcNt;
1566 }
1567 LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
1568 rcNt = STATUS_DEVICE_NOT_CONNECTED;
1569 vgdrvNtDeleteDevExtFundament(pDevExt);
1570 }
1571
1572 IoDeleteSymbolicLink(&DosName);
1573 }
1574 else
1575 LogFunc(("IoCreateSymbolicLink failed with rcNt=%#x!\n", rcNt));
1576 IoDeleteDevice(pDeviceObject);
1577 }
1578 else
1579 LogFunc(("IoCreateDevice failed with rcNt=%#x!\n", rcNt));
1580
1581 LogFunc(("Returning with rcNt=%#x\n", rcNt));
1582 return rcNt;
1583}
1584
1585
1586/**
1587 * Irp completion routine for PnP Irps we send.
1588 *
1589 * @returns NT status code.
1590 * @param pDevObj Device object.
1591 * @param pIrp Request packet.
1592 * @param pEvent Semaphore.
1593 */
1594static NTSTATUS vgdrvNt5PlusPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
1595{
1596 RT_NOREF2(pDevObj, pIrp);
1597 KeSetEvent(pEvent, 0, FALSE);
1598 return STATUS_MORE_PROCESSING_REQUIRED;
1599}
1600
1601
1602/**
1603 * Helper to send a PnP IRP and wait until it's done.
1604 *
1605 * @returns NT status code.
1606 * @param pDevObj Device object.
1607 * @param pIrp Request packet.
1608 * @param fStrict When set, returns an error if the IRP gives an error.
1609 */
1610static NTSTATUS vgdrvNt5PlusPnPSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict)
1611{
1612 KEVENT Event;
1613
1614 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1615
1616 IoCopyCurrentIrpStackLocationToNext(pIrp);
1617 IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vgdrvNt5PlusPnpIrpComplete, &Event, TRUE, TRUE, TRUE);
1618
1619 NTSTATUS rcNt = IoCallDriver(pDevObj, pIrp);
1620 if (rcNt == STATUS_PENDING)
1621 {
1622 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1623 rcNt = pIrp->IoStatus.Status;
1624 }
1625
1626 if ( !fStrict
1627 && (rcNt == STATUS_NOT_SUPPORTED || rcNt == STATUS_INVALID_DEVICE_REQUEST))
1628 {
1629 rcNt = STATUS_SUCCESS;
1630 }
1631
1632 Log(("vgdrvNt5PlusPnPSendIrpSynchronously: Returning %#x\n", rcNt));
1633 return rcNt;
1634}
1635
1636
1637/**
1638 * Deletes the device hardware resources.
1639 *
1640 * Used during removal, stopping and legacy module unloading.
1641 *
1642 * @param pDevExt The device extension.
1643 */
1644static void vgdrvNtDeleteDeviceResources(PVBOXGUESTDEVEXTWIN pDevExt)
1645{
1646 if (pDevExt->pInterruptObject)
1647 {
1648 IoDisconnectInterrupt(pDevExt->pInterruptObject);
1649 pDevExt->pInterruptObject = NULL;
1650 }
1651 pDevExt->pPowerStateRequest = NULL; /* Will be deleted by the following call. */
1652 if (pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES)
1653 VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
1654 vgdrvNtUnmapVMMDevMemory(pDevExt);
1655}
1656
1657
1658/**
1659 * Deletes the device extension fundament and unlinks the device
1660 *
1661 * Used during removal and legacy module unloading. Must have called
1662 * vgdrvNtDeleteDeviceResources.
1663 *
1664 * @param pDevObj Device object.
1665 * @param pDevExt The device extension.
1666 */
1667static void vgdrvNtDeleteDeviceFundamentAndUnlink(PDEVICE_OBJECT pDevObj, PVBOXGUESTDEVEXTWIN pDevExt)
1668{
1669 /*
1670 * Delete the remainder of the device extension.
1671 */
1672 vgdrvNtDeleteDevExtFundament(pDevExt);
1673
1674 /*
1675 * Delete the DOS symlink to the device and finally the device itself.
1676 */
1677 UNICODE_STRING DosName;
1678 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1679 IoDeleteSymbolicLink(&DosName);
1680
1681 Log(("vgdrvNtDeleteDeviceFundamentAndUnlink: Deleting device ...\n"));
1682 IoDeleteDevice(pDevObj);
1683}
1684
1685
1686/**
1687 * Checks if the device is idle.
1688 * @returns STATUS_SUCCESS if idle, STATUS_UNSUCCESSFUL if busy.
1689 * @param pDevExt The device extension.
1690 * @param pszQueryNm The query name.
1691 */
1692static NTSTATUS vgdrvNtCheckIdle(PVBOXGUESTDEVEXTWIN pDevExt, const char *pszQueryNm)
1693{
1694 uint32_t cSessions = pDevExt->Core.cSessions;
1695 if (cSessions == 0)
1696 return STATUS_SUCCESS;
1697 LogRel(("vgdrvNtCheckIdle/%s: cSessions=%d\n", pszQueryNm, cSessions));
1698 return STATUS_UNSUCCESSFUL;
1699}
1700
1701
1702/**
1703 * PnP Request handler.
1704 *
1705 * @param pDevObj Device object.
1706 * @param pIrp Request packet.
1707 */
1708static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1709{
1710 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1711 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1712
1713#ifdef LOG_ENABLED
1714 static char const * const s_apszFnctName[] =
1715 {
1716 "IRP_MN_START_DEVICE",
1717 "IRP_MN_QUERY_REMOVE_DEVICE",
1718 "IRP_MN_REMOVE_DEVICE",
1719 "IRP_MN_CANCEL_REMOVE_DEVICE",
1720 "IRP_MN_STOP_DEVICE",
1721 "IRP_MN_QUERY_STOP_DEVICE",
1722 "IRP_MN_CANCEL_STOP_DEVICE",
1723 "IRP_MN_QUERY_DEVICE_RELATIONS",
1724 "IRP_MN_QUERY_INTERFACE",
1725 "IRP_MN_QUERY_CAPABILITIES",
1726 "IRP_MN_QUERY_RESOURCES",
1727 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
1728 "IRP_MN_QUERY_DEVICE_TEXT",
1729 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
1730 "IRP_MN_0xE",
1731 "IRP_MN_READ_CONFIG",
1732 "IRP_MN_WRITE_CONFIG",
1733 "IRP_MN_EJECT",
1734 "IRP_MN_SET_LOCK",
1735 "IRP_MN_QUERY_ID",
1736 "IRP_MN_QUERY_PNP_DEVICE_STATE",
1737 "IRP_MN_QUERY_BUS_INFORMATION",
1738 "IRP_MN_DEVICE_USAGE_NOTIFICATION",
1739 "IRP_MN_SURPRISE_REMOVAL",
1740 };
1741 Log(("vgdrvNtNt5PlusPnP: MinorFunction: %s\n",
1742 pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown"));
1743#endif
1744
1745 NTSTATUS rc = STATUS_SUCCESS;
1746 uint8_t bMinorFunction = pStack->MinorFunction;
1747 switch (bMinorFunction)
1748 {
1749 case IRP_MN_START_DEVICE:
1750 {
1751 Log(("vgdrvNtNt5PlusPnP: START_DEVICE\n"));
1752
1753 /* This must be handled first by the lower driver. */
1754 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1755 if ( NT_SUCCESS(rc)
1756 && NT_SUCCESS(pIrp->IoStatus.Status))
1757 {
1758 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n",
1759 pStack->Parameters.StartDevice.AllocatedResources));
1760 if (pStack->Parameters.StartDevice.AllocatedResources)
1761 {
1762 rc = vgdrvNtSetupDevice(pDevExt, pDevObj, pIrp, NULL, NULL);
1763 if (NT_SUCCESS(rc))
1764 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: success\n"));
1765 else
1766 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNtSetupDevice failed: %#x\n", rc));
1767 }
1768 else
1769 {
1770 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n",
1771 pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL));
1772 rc = STATUS_UNSUCCESSFUL;
1773 }
1774 }
1775 else
1776 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNt5PlusPnPSendIrpSynchronously failed: %#x + %#x\n",
1777 rc, pIrp->IoStatus.Status));
1778
1779 pIrp->IoStatus.Status = rc;
1780 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1781 return rc;
1782 }
1783
1784
1785 /*
1786 * Sent before removing the device and/or driver.
1787 */
1788 case IRP_MN_QUERY_REMOVE_DEVICE:
1789 {
1790 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE\n"));
1791
1792 RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
1793#ifdef VBOX_REBOOT_ON_UNINSTALL
1794 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n"));
1795 rc = STATUS_UNSUCCESSFUL;
1796#endif
1797 if (NT_SUCCESS(rc))
1798 rc = vgdrvNtCheckIdle(pDevExt, "QUERY_REMOVE_DEVICE");
1799 if (NT_SUCCESS(rc))
1800 {
1801 pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGREMOVE;
1802 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1803
1804 /* This IRP passed down to lower driver. */
1805 pIrp->IoStatus.Status = STATUS_SUCCESS;
1806
1807 IoSkipCurrentIrpStackLocation(pIrp);
1808 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1809 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1810
1811 /* We must not do anything the IRP after doing IoSkip & CallDriver
1812 since the driver below us will complete (or already have completed) the IRP.
1813 I.e. just return the status we got from IoCallDriver */
1814 }
1815 else
1816 {
1817 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1818 pIrp->IoStatus.Status = rc;
1819 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1820 }
1821
1822 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Returning with rc = 0x%x\n", rc));
1823 return rc;
1824 }
1825
1826 /*
1827 * Cancels a pending remove, IRP_MN_QUERY_REMOVE_DEVICE.
1828 * We only have to revert the state.
1829 */
1830 case IRP_MN_CANCEL_REMOVE_DEVICE:
1831 {
1832 Log(("vgdrvNtNt5PlusPnP: CANCEL_REMOVE_DEVICE\n"));
1833
1834 /* This must be handled first by the lower driver. */
1835 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1836 if ( NT_SUCCESS(rc)
1837 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGREMOVE)
1838 {
1839 /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */
1840 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1841 }
1842
1843 /* Complete the IRP. */
1844 pIrp->IoStatus.Status = rc;
1845 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1846 return rc;
1847 }
1848
1849 /*
1850 * We do nothing here actually, esp. since this request is not expected for VBoxGuest.
1851 * The cleanup will be done in IRP_MN_REMOVE_DEVICE, which follows this call.
1852 */
1853 case IRP_MN_SURPRISE_REMOVAL:
1854 {
1855 Log(("vgdrvNtNt5PlusPnP: IRP_MN_SURPRISE_REMOVAL\n"));
1856 pDevExt->enmDevState = VGDRVNTDEVSTATE_SURPRISEREMOVED;
1857 LogRel(("VBoxGuest: unexpected device removal\n"));
1858
1859 /* Pass to the lower driver. */
1860 pIrp->IoStatus.Status = STATUS_SUCCESS;
1861
1862 IoSkipCurrentIrpStackLocation(pIrp);
1863 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1864
1865 /* Do not complete the IRP. */
1866 return rc;
1867 }
1868
1869 /*
1870 * Device and/or driver removal. Destroy everything.
1871 */
1872 case IRP_MN_REMOVE_DEVICE:
1873 {
1874 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE\n"));
1875 pDevExt->enmDevState = VGDRVNTDEVSTATE_REMOVED;
1876
1877 /*
1878 * Disconnect interrupts and delete all hardware resources.
1879 * Note! This may already have been done if we're STOPPED already, if that's a possibility.
1880 */
1881 vgdrvNtDeleteDeviceResources(pDevExt);
1882
1883 /*
1884 * We need to send the remove down the stack before we detach, but we don't need
1885 * to wait for the completion of this operation (nor register a completion routine).
1886 */
1887 pIrp->IoStatus.Status = STATUS_SUCCESS;
1888
1889 IoSkipCurrentIrpStackLocation(pIrp);
1890 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1891 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1892
1893 IoDetachDevice(pDevExt->pNextLowerDriver);
1894 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Removing device ...\n"));
1895
1896 /*
1897 * Delete the remainder of the device extension data, unlink it from the namespace and delete it.
1898 */
1899 vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
1900
1901 pDevObj = NULL; /* invalid */
1902 pDevExt = NULL; /* invalid */
1903
1904 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Device removed!\n"));
1905 return rc; /* Propagating rc from IoCallDriver. */
1906 }
1907
1908
1909 /*
1910 * Sent before stopping the device/driver to check whether it is okay to do so.
1911 */
1912 case IRP_MN_QUERY_STOP_DEVICE:
1913 {
1914 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE\n"));
1915 RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
1916 rc = vgdrvNtCheckIdle(pDevExt, "QUERY_STOP_DEVICE");
1917 if (NT_SUCCESS(rc))
1918 {
1919 pDevExt->enmPrevDevState = pDevExt->enmDevState;
1920 pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGSTOP;
1921 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1922
1923 /* This IRP passed down to lower driver. */
1924 pIrp->IoStatus.Status = STATUS_SUCCESS;
1925
1926 IoSkipCurrentIrpStackLocation(pIrp);
1927
1928 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1929 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1930
1931 /* we must not do anything with the IRP after doing IoSkip & CallDriver since the
1932 driver below us will complete (or already have completed) the IRP. I.e. just
1933 return the status we got from IoCallDriver. */
1934 }
1935 else
1936 {
1937 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1938 pIrp->IoStatus.Status = rc;
1939 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1940 }
1941
1942 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Returning with rc = 0x%x\n", rc));
1943 return rc;
1944 }
1945
1946 /*
1947 * Cancels a pending remove, IRP_MN_QUERY_STOP_DEVICE.
1948 * We only have to revert the state.
1949 */
1950 case IRP_MN_CANCEL_STOP_DEVICE:
1951 {
1952 Log(("vgdrvNtNt5PlusPnP: CANCEL_STOP_DEVICE\n"));
1953
1954 /* This must be handled first by the lower driver. */
1955 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1956 if ( NT_SUCCESS(rc)
1957 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGSTOP)
1958 {
1959 /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */
1960 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1961 }
1962
1963 /* Complete the IRP. */
1964 pIrp->IoStatus.Status = rc;
1965 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1966 return rc;
1967 }
1968
1969 /*
1970 * Stop the device.
1971 */
1972 case IRP_MN_STOP_DEVICE:
1973 {
1974 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE\n"));
1975 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
1976
1977 /*
1978 * Release the hardware resources.
1979 */
1980 vgdrvNtDeleteDeviceResources(pDevExt);
1981
1982 /*
1983 * Pass the request to the lower driver.
1984 */
1985 pIrp->IoStatus.Status = STATUS_SUCCESS;
1986 IoSkipCurrentIrpStackLocation(pIrp);
1987 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1988 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1989 return rc;
1990 }
1991
1992 default:
1993 {
1994 IoSkipCurrentIrpStackLocation(pIrp);
1995 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1996 Log(("vgdrvNtNt5PlusPnP: Unknown request %#x: Lower driver replied: %x\n", bMinorFunction, rc));
1997 return rc;
1998 }
1999 }
2000}
2001
2002
2003/**
2004 * Handle the power completion event.
2005 *
2006 * @returns NT status code.
2007 * @param pDevObj Targetted device object.
2008 * @param pIrp IO request packet.
2009 * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower.
2010 */
2011static NTSTATUS vgdrvNtNt5PlusPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext)
2012{
2013#ifdef VBOX_STRICT
2014 RT_NOREF1(pDevObj);
2015 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext;
2016 PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
2017
2018 Assert(pDevExt);
2019
2020 if (pIrpSp)
2021 {
2022 Assert(pIrpSp->MajorFunction == IRP_MJ_POWER);
2023 if (NT_SUCCESS(pIrp->IoStatus.Status))
2024 {
2025 switch (pIrpSp->MinorFunction)
2026 {
2027 case IRP_MN_SET_POWER:
2028 switch (pIrpSp->Parameters.Power.Type)
2029 {
2030 case DevicePowerState:
2031 switch (pIrpSp->Parameters.Power.State.DeviceState)
2032 {
2033 case PowerDeviceD0:
2034 break;
2035 default: /* Shut up MSC */
2036 break;
2037 }
2038 break;
2039 default: /* Shut up MSC */
2040 break;
2041 }
2042 break;
2043 }
2044 }
2045 }
2046#else
2047 RT_NOREF3(pDevObj, pIrp, pContext);
2048#endif
2049
2050 return STATUS_SUCCESS;
2051}
2052
2053
2054/**
2055 * Handle the Power requests.
2056 *
2057 * @returns NT status code
2058 * @param pDevObj device object
2059 * @param pIrp IRP
2060 */
2061static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2062{
2063 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2064 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2065 POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type;
2066 POWER_STATE PowerState = pStack->Parameters.Power.State;
2067 POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType;
2068
2069 Log(("vgdrvNtNt5PlusPower:\n"));
2070
2071 switch (pStack->MinorFunction)
2072 {
2073 case IRP_MN_SET_POWER:
2074 {
2075 Log(("vgdrvNtNt5PlusPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType));
2076 switch (enmPowerType)
2077 {
2078 case SystemPowerState:
2079 {
2080 Log(("vgdrvNtNt5PlusPower: SystemPowerState, action = %d, state = %d/%d\n",
2081 enmPowerAction, PowerState.SystemState, PowerState.DeviceState));
2082
2083 switch (enmPowerAction)
2084 {
2085 case PowerActionSleep:
2086
2087 /* System now is in a working state. */
2088 if (PowerState.SystemState == PowerSystemWorking)
2089 {
2090 if ( pDevExt
2091 && pDevExt->enmLastSystemPowerAction == PowerActionHibernate)
2092 {
2093 Log(("vgdrvNtNt5PlusPower: Returning from hibernation!\n"));
2094 int rc = VGDrvCommonReinitDevExtAfterHibernation(&pDevExt->Core,
2095 vgdrvNtVersionToOSType(g_enmVGDrvNtVer));
2096 if (RT_FAILURE(rc))
2097 Log(("vgdrvNtNt5PlusPower: Cannot re-init VMMDev chain, rc = %d!\n", rc));
2098 }
2099 }
2100 break;
2101
2102 case PowerActionShutdownReset:
2103 {
2104 Log(("vgdrvNtNt5PlusPower: Power action reset!\n"));
2105
2106 /* Tell the VMM that we no longer support mouse pointer integration. */
2107 VMMDevReqMouseStatus *pReq = NULL;
2108 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus),
2109 VMMDevReq_SetMouseStatus);
2110 if (RT_SUCCESS(vrc))
2111 {
2112 pReq->mouseFeatures = 0;
2113 pReq->pointerXPos = 0;
2114 pReq->pointerYPos = 0;
2115
2116 vrc = VbglR0GRPerform(&pReq->header);
2117 if (RT_FAILURE(vrc))
2118 {
2119 Log(("vgdrvNtNt5PlusPower: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
2120 }
2121
2122 VbglR0GRFree(&pReq->header);
2123 }
2124
2125 /* Don't do any cleanup here; there might be still coming in some IOCtls after we got this
2126 * power action and would assert/crash when we already cleaned up all the stuff! */
2127 break;
2128 }
2129
2130 case PowerActionShutdown:
2131 case PowerActionShutdownOff:
2132 {
2133 Log(("vgdrvNtNt5PlusPower: Power action shutdown!\n"));
2134 if (PowerState.SystemState >= PowerSystemShutdown)
2135 {
2136 Log(("vgdrvNtNt5PlusPower: Telling the VMMDev to close the VM ...\n"));
2137
2138 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
2139 int vrc = VERR_NOT_IMPLEMENTED;
2140 if (pReq)
2141 {
2142 pReq->header.requestType = VMMDevReq_SetPowerStatus;
2143 pReq->powerState = VMMDevPowerState_PowerOff;
2144
2145 vrc = VbglR0GRPerform(&pReq->header);
2146 }
2147 if (RT_FAILURE(vrc))
2148 Log(("vgdrvNtNt5PlusPower: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
2149
2150 /* No need to do cleanup here; at this point we should've been
2151 * turned off by VMMDev already! */
2152 }
2153 break;
2154 }
2155
2156 case PowerActionHibernate:
2157 Log(("vgdrvNtNt5PlusPower: Power action hibernate!\n"));
2158 break;
2159
2160 case PowerActionWarmEject:
2161 Log(("vgdrvNtNt5PlusPower: PowerActionWarmEject!\n"));
2162 break;
2163
2164 default:
2165 Log(("vgdrvNtNt5PlusPower: %d\n", enmPowerAction));
2166 break;
2167 }
2168
2169 /*
2170 * Save the current system power action for later use.
2171 * This becomes handy when we return from hibernation for example.
2172 */
2173 if (pDevExt)
2174 pDevExt->enmLastSystemPowerAction = enmPowerAction;
2175
2176 break;
2177 }
2178 default:
2179 break;
2180 }
2181 break;
2182 }
2183 default:
2184 break;
2185 }
2186
2187 /*
2188 * Whether we are completing or relaying this power IRP,
2189 * we must call PoStartNextPowerIrp.
2190 */
2191 g_pfnPoStartNextPowerIrp(pIrp);
2192
2193 /*
2194 * Send the IRP down the driver stack, using PoCallDriver
2195 * (not IoCallDriver, as for non-power irps).
2196 */
2197 IoCopyCurrentIrpStackLocationToNext(pIrp);
2198 IoSetCompletionRoutine(pIrp,
2199 vgdrvNtNt5PlusPowerComplete,
2200 (PVOID)pDevExt,
2201 TRUE,
2202 TRUE,
2203 TRUE);
2204 return g_pfnPoCallDriver(pDevExt->pNextLowerDriver, pIrp);
2205}
2206
2207
2208/**
2209 * IRP_MJ_SYSTEM_CONTROL handler.
2210 *
2211 * @returns NT status code
2212 * @param pDevObj Device object.
2213 * @param pIrp IRP.
2214 */
2215static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2216{
2217 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2218
2219 LogFlowFuncEnter();
2220
2221 /* Always pass it on to the next driver. */
2222 IoSkipCurrentIrpStackLocation(pIrp);
2223
2224 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
2225}
2226
2227
2228/**
2229 * Unload the driver.
2230 *
2231 * @param pDrvObj Driver object.
2232 */
2233static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
2234{
2235 LogFlowFuncEnter();
2236
2237#ifdef TARGET_NT4
2238 /*
2239 * We need to destroy the device object here on NT4 and earlier.
2240 */
2241 PDEVICE_OBJECT pDevObj = pDrvObj->DeviceObject;
2242 if (pDevObj)
2243 {
2244 if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
2245 {
2246 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2247 AssertPtr(pDevExt);
2248 AssertMsg(pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES,
2249 ("uInitState=%#x\n", pDevExt->Core.uInitState));
2250
2251 vgdrvNtDeleteDeviceResources(pDevExt);
2252 vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
2253 }
2254 }
2255#else /* !TARGET_NT4 */
2256 /*
2257 * On a PnP driver this routine will be called after IRP_MN_REMOVE_DEVICE
2258 * where we already did the cleanup, so don't do anything here (yet).
2259 */
2260 RT_NOREF1(pDrvObj);
2261#endif /* !TARGET_NT4 */
2262
2263 VGDrvCommonDestroyLoggers();
2264 RTR0Term();
2265
2266 /*
2267 * Finally deregister the bugcheck callback. Do it late to catch trouble in RTR0Term.
2268 */
2269 if (g_fBugCheckCallbackRegistered)
2270 {
2271 g_pfnKeDeregisterBugCheckCallback(&g_BugCheckCallbackRec);
2272 g_fBugCheckCallbackRegistered = false;
2273 }
2274}
2275
2276
2277/**
2278 * For simplifying request completion into a simple return statement, extended
2279 * version.
2280 *
2281 * @returns rcNt
2282 * @param rcNt The status code.
2283 * @param uInfo Extra info value.
2284 * @param pIrp The IRP.
2285 */
2286DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
2287{
2288 pIrp->IoStatus.Status = rcNt;
2289 pIrp->IoStatus.Information = uInfo;
2290 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2291 return rcNt;
2292}
2293
2294
2295/**
2296 * For simplifying request completion into a simple return statement.
2297 *
2298 * @returns rcNt
2299 * @param rcNt The status code.
2300 * @param pIrp The IRP.
2301 */
2302DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
2303{
2304 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
2305}
2306
2307
2308/**
2309 * Checks if NT authority rev 1 SID (SECURITY_NT_AUTHORITY).
2310 *
2311 * @returns true / false.
2312 * @param pSid The SID to check.
2313 */
2314DECLINLINE(bool) vgdrvNtIsSidNtAuth(struct _SID const *pSid)
2315{
2316 return pSid != NULL
2317 && pSid->Revision == 1
2318 && pSid->IdentifierAuthority.Value[5] == 5
2319 && pSid->IdentifierAuthority.Value[4] == 0
2320 && pSid->IdentifierAuthority.Value[3] == 0
2321 && pSid->IdentifierAuthority.Value[2] == 0
2322 && pSid->IdentifierAuthority.Value[1] == 0
2323 && pSid->IdentifierAuthority.Value[0] == 0;
2324}
2325
2326
2327/**
2328 * Matches SID with local system user (S-1-5-18 / SECURITY_LOCAL_SYSTEM_RID).
2329 */
2330DECLINLINE(bool) vgdrvNtIsSidLocalSystemUser(SID const *pSid)
2331{
2332 return vgdrvNtIsSidNtAuth(pSid)
2333 && pSid->SubAuthorityCount == 1
2334 && pSid->SubAuthority[0] == SECURITY_LOCAL_SYSTEM_RID;
2335}
2336
2337
2338/**
2339 * Matches SID with NT system admin user (S-1-5-*-500 / DOMAIN_USER_RID_ADMIN).
2340 */
2341DECLINLINE(bool) vgdrvNtIsSidAdminUser(SID const *pSid)
2342{
2343 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2344 return vgdrvNtIsSidNtAuth(pSid)
2345 && pSid->SubAuthorityCount >= 2
2346 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2347 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_ADMIN;
2348}
2349
2350
2351/**
2352 * Matches SID with NT system guest user (S-1-5-*-501 / DOMAIN_USER_RID_GUEST).
2353 */
2354DECLINLINE(bool) vgdrvNtIsSidGuestUser(SID const *pSid)
2355{
2356 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2357 return vgdrvNtIsSidNtAuth(pSid)
2358 && pSid->SubAuthorityCount >= 2
2359 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2360 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_GUEST;
2361}
2362
2363
2364/**
2365 * Matches SID with NT system admins group (S-1-5-32-544, S-1-5-*-512).
2366 */
2367DECLINLINE(bool) vgdrvNtIsSidAdminsGroup(SID const *pSid)
2368{
2369 return vgdrvNtIsSidNtAuth(pSid)
2370 && ( ( pSid->SubAuthorityCount == 2
2371 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2372 && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_ADMINS)
2373#if 0
2374 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2375 || ( pSid->SubAuthorityCount >= 2
2376 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2377 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_ADMINS)
2378#endif
2379 );
2380}
2381
2382
2383/**
2384 * Matches SID with NT system users group (S-1-5-32-545, S-1-5-32-547, S-1-5-*-512).
2385 */
2386DECLINLINE(bool) vgdrvNtIsSidUsersGroup(SID const *pSid)
2387{
2388 return vgdrvNtIsSidNtAuth(pSid)
2389 && ( ( pSid->SubAuthorityCount == 2
2390 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2391 && ( pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_USERS
2392 || pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_POWER_USERS) )
2393#if 0
2394 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2395 || ( pSid->SubAuthorityCount >= 2
2396 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2397 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_USERS)
2398#endif
2399 );
2400}
2401
2402
2403/**
2404 * Matches SID with NT system guests group (S-1-5-32-546, S-1-5-*-512).
2405 */
2406DECLINLINE(bool) vgdrvNtIsSidGuestsGroup(SID const *pSid)
2407{
2408 return vgdrvNtIsSidNtAuth(pSid)
2409 && ( ( pSid->SubAuthorityCount == 2
2410 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2411 && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_GUESTS)
2412#if 0
2413 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2414 || ( pSid->SubAuthorityCount >= 2
2415 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2416 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_GUESTS)
2417#endif
2418 );
2419}
2420
2421
2422/**
2423 * Checks if local authority rev 1 SID (SECURITY_LOCAL_SID_AUTHORITY).
2424 *
2425 * @returns true / false.
2426 * @param pSid The SID to check.
2427 */
2428DECLINLINE(bool) vgdrvNtIsSidLocalAuth(struct _SID const *pSid)
2429{
2430 return pSid != NULL
2431 && pSid->Revision == 1
2432 && pSid->IdentifierAuthority.Value[5] == 2
2433 && pSid->IdentifierAuthority.Value[4] == 0
2434 && pSid->IdentifierAuthority.Value[3] == 0
2435 && pSid->IdentifierAuthority.Value[2] == 0
2436 && pSid->IdentifierAuthority.Value[1] == 0
2437 && pSid->IdentifierAuthority.Value[0] == 0;
2438}
2439
2440
2441/**
2442 * Matches SID with console logon group (S-1-2-1 / SECURITY_LOCAL_LOGON_RID).
2443 */
2444DECLINLINE(bool) vgdrvNtIsSidConsoleLogonGroup(SID const *pSid)
2445{
2446 return vgdrvNtIsSidLocalAuth(pSid)
2447 && pSid->SubAuthorityCount == 1
2448 && pSid->SubAuthority[0] == SECURITY_LOCAL_LOGON_RID;
2449}
2450
2451
2452/**
2453 * Checks if mandatory label authority rev 1 SID (SECURITY_MANDATORY_LABEL_AUTHORITY).
2454 *
2455 * @returns true / false.
2456 * @param pSid The SID to check.
2457 */
2458DECLINLINE(bool) vgdrvNtIsSidMandatoryLabelAuth(struct _SID const *pSid)
2459{
2460 return pSid != NULL
2461 && pSid->Revision == 1
2462 && pSid->IdentifierAuthority.Value[5] == 16
2463 && pSid->IdentifierAuthority.Value[4] == 0
2464 && pSid->IdentifierAuthority.Value[3] == 0
2465 && pSid->IdentifierAuthority.Value[2] == 0
2466 && pSid->IdentifierAuthority.Value[1] == 0
2467 && pSid->IdentifierAuthority.Value[0] == 0;
2468}
2469
2470
2471#ifdef LOG_ENABLED
2472/** Format an SID for logging. */
2473static const char *vgdrvNtFormatSid(char *pszBuf, size_t cbBuf, struct _SID const *pSid)
2474{
2475 uint64_t uAuth = RT_MAKE_U64_FROM_U8(pSid->IdentifierAuthority.Value[5], pSid->IdentifierAuthority.Value[4],
2476 pSid->IdentifierAuthority.Value[3], pSid->IdentifierAuthority.Value[2],
2477 pSid->IdentifierAuthority.Value[1], pSid->IdentifierAuthority.Value[0],
2478 0, 0);
2479 ssize_t offCur = RTStrPrintf2(pszBuf, cbBuf, "S-%u-%RU64", pSid->Revision, uAuth);
2480 ULONG const *puSubAuth = &pSid->SubAuthority[0];
2481 unsigned cSubAuths = pSid->SubAuthorityCount;
2482 while (cSubAuths > 0 && (size_t)offCur < cbBuf)
2483 {
2484 ssize_t cchThis = RTStrPrintf2(&pszBuf[offCur], cbBuf - (size_t)offCur, "-%u", *puSubAuth);
2485 if (cchThis > 0)
2486 {
2487 offCur += cchThis;
2488 puSubAuth++;
2489 cSubAuths--;
2490 }
2491 else
2492 {
2493 Assert(cbBuf >= 5);
2494 pszBuf[cbBuf - 4] = '.';
2495 pszBuf[cbBuf - 3] = '.';
2496 pszBuf[cbBuf - 2] = '.';
2497 pszBuf[cbBuf - 1] = '\0';
2498 break;
2499 }
2500 }
2501 return pszBuf;
2502}
2503#endif
2504
2505
2506/**
2507 * Calculate requestor flags for the current process.
2508 *
2509 * ASSUMES vgdrvNtCreate is executed in the context of the process and thread
2510 * doing the NtOpenFile call.
2511 *
2512 * @returns VMMDEV_REQUESTOR_XXX
2513 */
2514static uint32_t vgdrvNtCalcRequestorFlags(void)
2515{
2516 uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE
2517 | VMMDEV_REQUESTOR_USR_NOT_GIVEN
2518 | VMMDEV_REQUESTOR_CON_DONT_KNOW
2519 | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN
2520 | VMMDEV_REQUESTOR_NO_USER_DEVICE;
2521 HANDLE hToken = NULL;
2522 NTSTATUS rcNt = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken);
2523 if (NT_SUCCESS(rcNt))
2524 {
2525 union
2526 {
2527 TOKEN_USER CurUser;
2528 TOKEN_GROUPS CurGroups;
2529 uint8_t abPadding[256];
2530 } Buf;
2531#ifdef LOG_ENABLED
2532 char szSid[200];
2533#endif
2534
2535 /*
2536 * Get the user SID and see if it's a standard one.
2537 */
2538 RT_ZERO(Buf.CurUser);
2539 ULONG cbReturned = 0;
2540 rcNt = ZwQueryInformationToken(hToken, TokenUser, &Buf.CurUser, sizeof(Buf), &cbReturned);
2541 if (NT_SUCCESS(rcNt))
2542 {
2543 struct _SID const *pSid = (struct _SID const *)Buf.CurUser.User.Sid;
2544 Log5(("vgdrvNtCalcRequestorFlags: TokenUser: %#010x %s\n",
2545 Buf.CurUser.User.Attributes, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
2546
2547 if (vgdrvNtIsSidLocalSystemUser(pSid))
2548 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_SYSTEM;
2549 else if (vgdrvNtIsSidAdminUser(pSid))
2550 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_ROOT;
2551 else if (vgdrvNtIsSidGuestUser(pSid))
2552 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
2553 }
2554 else
2555 LogRel(("vgdrvNtCalcRequestorFlags: TokenUser query failed: %#x\n", rcNt));
2556
2557 /*
2558 * Get the groups.
2559 */
2560 TOKEN_GROUPS *pCurGroupsFree = NULL;
2561 TOKEN_GROUPS *pCurGroups = &Buf.CurGroups;
2562 uint32_t cbCurGroups = sizeof(Buf);
2563 cbReturned = 0;
2564 RT_ZERO(Buf);
2565 rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
2566 if (rcNt == STATUS_BUFFER_TOO_SMALL)
2567 {
2568 uint32_t cTries = 8;
2569 do
2570 {
2571 RTMemTmpFree(pCurGroupsFree);
2572 if (cbCurGroups < cbReturned)
2573 cbCurGroups = RT_ALIGN_32(cbCurGroups + 32, 64);
2574 else
2575 cbCurGroups += 64;
2576 pCurGroupsFree = pCurGroups = (TOKEN_GROUPS *)RTMemTmpAllocZ(cbCurGroups);
2577 if (pCurGroupsFree)
2578 rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
2579 else
2580 rcNt = STATUS_NO_MEMORY;
2581 } while (rcNt == STATUS_BUFFER_TOO_SMALL && cTries-- > 0);
2582 }
2583 if (NT_SUCCESS(rcNt))
2584 {
2585 bool fGuestsMember = false;
2586 bool fUsersMember = false;
2587 if (g_enmVGDrvNtVer >= VGDRVNTVER_WIN7)
2588 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_NO;
2589
2590 for (uint32_t iGrp = 0; iGrp < pCurGroups->GroupCount; iGrp++)
2591 {
2592 uint32_t const fAttribs = pCurGroups->Groups[iGrp].Attributes;
2593 struct _SID const *pSid = (struct _SID const *)pCurGroups->Groups[iGrp].Sid;
2594 Log5(("vgdrvNtCalcRequestorFlags: TokenGroups[%u]: %#10x %s\n",
2595 iGrp, fAttribs, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
2596
2597 if ( (fAttribs & SE_GROUP_INTEGRITY_ENABLED)
2598 && vgdrvNtIsSidMandatoryLabelAuth(pSid)
2599 && pSid->SubAuthorityCount == 1
2600 && (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)
2601 {
2602 fRequestor &= ~VMMDEV_REQUESTOR_TRUST_MASK;
2603 if (pSid->SubAuthority[0] < SECURITY_MANDATORY_LOW_RID)
2604 fRequestor |= VMMDEV_REQUESTOR_TRUST_UNTRUSTED;
2605 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_RID)
2606 fRequestor |= VMMDEV_REQUESTOR_TRUST_LOW;
2607 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_PLUS_RID)
2608 fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM;
2609 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_HIGH_RID)
2610 fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM_PLUS;
2611 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_SYSTEM_RID)
2612 fRequestor |= VMMDEV_REQUESTOR_TRUST_HIGH;
2613 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
2614 fRequestor |= VMMDEV_REQUESTOR_TRUST_SYSTEM;
2615 else
2616 fRequestor |= VMMDEV_REQUESTOR_TRUST_PROTECTED;
2617 Log5(("vgdrvNtCalcRequestorFlags: mandatory label %u: => %#x\n", pSid->SubAuthority[0], fRequestor));
2618 }
2619 else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
2620 == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
2621 && vgdrvNtIsSidConsoleLogonGroup(pSid))
2622 {
2623 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_YES;
2624 Log5(("vgdrvNtCalcRequestorFlags: console: => %#x\n", fRequestor));
2625 }
2626 else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
2627 == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
2628 && vgdrvNtIsSidNtAuth(pSid))
2629 {
2630 if (vgdrvNtIsSidAdminsGroup(pSid))
2631 {
2632 fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
2633 Log5(("vgdrvNtCalcRequestorFlags: admins group: => %#x\n", fRequestor));
2634 }
2635 else if (vgdrvNtIsSidUsersGroup(pSid))
2636 {
2637 Log5(("vgdrvNtCalcRequestorFlags: users group\n"));
2638 fUsersMember = true;
2639 }
2640 else if (vgdrvNtIsSidGuestsGroup(pSid))
2641 {
2642 Log5(("vgdrvNtCalcRequestorFlags: guests group\n"));
2643 fGuestsMember = true;
2644 }
2645 }
2646 }
2647 if ((fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_NOT_GIVEN)
2648 {
2649 if (fUsersMember)
2650 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_USER;
2651 else if (fGuestsMember)
2652 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
2653 }
2654 }
2655 else
2656 LogRel(("vgdrvNtCalcRequestorFlags: TokenGroups query failed: %#x\n", rcNt));
2657
2658 RTMemTmpFree(pCurGroupsFree);
2659 ZwClose(hToken);
2660
2661 /*
2662 * Determine whether we should set VMMDEV_REQUESTOR_USER_DEVICE or not.
2663 *
2664 * The purpose here is to differentiate VBoxService accesses
2665 * from VBoxTray and VBoxControl, as VBoxService should be allowed to
2666 * do more than the latter two. VBoxService normally runs under the
2667 * system account which is easily detected, but for debugging and
2668 * similar purposes we also allow an elevated admin to run it as well.
2669 */
2670 if ( (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_UNTRUSTED /* general paranoia wrt system account */
2671 || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_LOW /* ditto */
2672 || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_MEDIUM /* ditto */
2673 || !( (fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_SYSTEM
2674 || ( ( (fRequestor & VMMDEV_REQUESTOR_GRP_WHEEL)
2675 || (fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_ROOT)
2676 && ( (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) >= VMMDEV_REQUESTOR_TRUST_HIGH
2677 || (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)) ))
2678 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
2679 }
2680 else
2681 {
2682 LogRel(("vgdrvNtCalcRequestorFlags: NtOpenProcessToken query failed: %#x\n", rcNt));
2683 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
2684 }
2685
2686 Log5(("vgdrvNtCalcRequestorFlags: returns %#x\n", fRequestor));
2687 return fRequestor;
2688}
2689
2690
2691/**
2692 * Create (i.e. Open) file entry point.
2693 *
2694 * @param pDevObj Device object.
2695 * @param pIrp Request packet.
2696 */
2697static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2698{
2699 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
2700 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2701 PFILE_OBJECT pFileObj = pStack->FileObject;
2702 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2703
2704 Assert(pFileObj->FsContext == NULL);
2705
2706 /*
2707 * We are not remotely similar to a directory...
2708 */
2709 NTSTATUS rcNt;
2710 if (!(pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE))
2711 {
2712 /*
2713 * Check the device state. We enter the critsect in shared mode to
2714 * prevent race with PnP system requests checking whether we're idle.
2715 */
2716 RTCritSectRwEnterShared(&pDevExt->SessionCreateCritSect);
2717 VGDRVNTDEVSTATE const enmDevState = pDevExt->enmDevState;
2718 if (enmDevState == VGDRVNTDEVSTATE_OPERATIONAL)
2719 {
2720 /*
2721 * Create a client session.
2722 */
2723 int rc;
2724 PVBOXGUESTSESSION pSession;
2725 if (pIrp->RequestorMode == KernelMode)
2726 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
2727 else
2728 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, vgdrvNtCalcRequestorFlags(), &pSession);
2729 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2730 if (RT_SUCCESS(rc))
2731 {
2732 pFileObj->FsContext = pSession;
2733 Log(("vgdrvNtCreate: Successfully created %s session %p (fRequestor=%#x)\n",
2734 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession, pSession->fRequestor));
2735
2736 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
2737 }
2738
2739 /* Note. the IoStatus is completely ignored on error. */
2740 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
2741 if (rc == VERR_NO_MEMORY)
2742 rcNt = STATUS_NO_MEMORY;
2743 else
2744 rcNt = STATUS_UNSUCCESSFUL;
2745 }
2746 else
2747 {
2748 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2749 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", enmDevState));
2750 rcNt = STATUS_DEVICE_NOT_READY;
2751 }
2752 }
2753 else
2754 {
2755 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
2756 rcNt = STATUS_NOT_A_DIRECTORY;
2757 }
2758 return vgdrvNtCompleteRequest(rcNt, pIrp);
2759}
2760
2761
2762/**
2763 * Close file entry point.
2764 *
2765 * @param pDevObj Device object.
2766 * @param pIrp Request packet.
2767 */
2768static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2769{
2770 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2771 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2772 PFILE_OBJECT pFileObj = pStack->FileObject;
2773
2774 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
2775
2776#ifdef VBOX_WITH_HGCM
2777 /* Close both, R0 and R3 sessions. */
2778 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
2779 if (pSession)
2780 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
2781#endif
2782
2783 pFileObj->FsContext = NULL;
2784 pIrp->IoStatus.Information = 0;
2785 pIrp->IoStatus.Status = STATUS_SUCCESS;
2786 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2787
2788 return STATUS_SUCCESS;
2789}
2790
2791
2792/**
2793 * Device I/O Control entry point.
2794 *
2795 * @param pDevObj Device object.
2796 * @param pIrp Request packet.
2797 */
2798NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2799{
2800 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2801 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2802 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
2803
2804 if (!RT_VALID_PTR(pSession))
2805 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
2806
2807#if 0 /* No fast I/O controls defined yet. */
2808 /*
2809 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
2810 * the session and iCmd, and does not return anything.
2811 */
2812 if (pSession->fUnrestricted)
2813 {
2814 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
2815 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
2816 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
2817 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
2818 {
2819 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
2820
2821 /* Complete the I/O request. */
2822 supdrvSessionRelease(pSession);
2823 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
2824 }
2825 }
2826#endif
2827
2828 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
2829}
2830
2831
2832/**
2833 * Device I/O Control entry point.
2834 *
2835 * @param pDevExt The device extension.
2836 * @param pSession The session.
2837 * @param pIrp Request packet.
2838 * @param pStack The request stack pointer.
2839 */
2840static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2841 PIRP pIrp, PIO_STACK_LOCATION pStack)
2842{
2843 NTSTATUS rcNt;
2844 uint32_t cbOut = 0;
2845 int rc = 0;
2846 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
2847 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
2848 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
2849 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
2850
2851#if 0 /*def RT_ARCH_AMD64*/
2852 /* Don't allow 32-bit processes to do any I/O controls. */
2853 if (!IoIs32bitProcess(pIrp))
2854#endif
2855 {
2856 /* Verify that it's a buffered CTL. */
2857 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
2858 {
2859 /* Verify that the sizes in the request header are correct. */
2860 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
2861 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
2862 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
2863 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
2864 {
2865 /* Zero extra output bytes to make sure we don't leak anything. */
2866 if (pHdr->cbIn < pHdr->cbOut)
2867 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
2868
2869 /*
2870 * Do the job.
2871 */
2872 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
2873 RT_MAX(pHdr->cbIn, pHdr->cbOut));
2874 if (RT_SUCCESS(rc))
2875 {
2876 rcNt = STATUS_SUCCESS;
2877 cbOut = pHdr->cbOut;
2878 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
2879 {
2880 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
2881 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
2882 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
2883 }
2884
2885 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
2886 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
2887 && RT_SUCCESS(pHdr->rc))
2888 pStack->FileObject->FsContext = NULL;
2889 }
2890 else if (rc == VERR_NOT_SUPPORTED)
2891 rcNt = STATUS_NOT_SUPPORTED;
2892 else
2893 rcNt = STATUS_INVALID_PARAMETER;
2894 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
2895 }
2896 else
2897 {
2898 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
2899 pStack->Parameters.DeviceIoControl.IoControlCode,
2900 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
2901 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
2902 pStack->Parameters.DeviceIoControl.InputBufferLength,
2903 pStack->Parameters.DeviceIoControl.OutputBufferLength));
2904 rcNt = STATUS_INVALID_PARAMETER;
2905 }
2906 }
2907 else
2908 {
2909 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
2910 pStack->Parameters.DeviceIoControl.IoControlCode));
2911 rcNt = STATUS_NOT_SUPPORTED;
2912 }
2913 }
2914#if 0 /*def RT_ARCH_AMD64*/
2915 else
2916 {
2917 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
2918 rcNt = STATUS_NOT_SUPPORTED;
2919 }
2920#endif
2921
2922 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
2923}
2924
2925
2926/**
2927 * Internal Device I/O Control entry point (for IDC).
2928 *
2929 * @param pDevObj Device object.
2930 * @param pIrp Request packet.
2931 */
2932static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2933{
2934 /* Currently no special code here. */
2935 return vgdrvNtDeviceControl(pDevObj, pIrp);
2936}
2937
2938
2939/**
2940 * IRP_MJ_SHUTDOWN handler.
2941 *
2942 * @returns NT status code
2943 * @param pDevObj Device object.
2944 * @param pIrp IRP.
2945 */
2946static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2947{
2948 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2949 LogFlowFuncEnter();
2950
2951 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
2952 if (pReq)
2953 {
2954 pReq->header.requestType = VMMDevReq_SetPowerStatus;
2955 pReq->powerState = VMMDevPowerState_PowerOff;
2956
2957 int rc = VbglR0GRPerform(&pReq->header);
2958 if (RT_FAILURE(rc))
2959 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
2960 }
2961
2962 /* just in case, since we shouldn't normally get here. */
2963 pIrp->IoStatus.Information = 0;
2964 pIrp->IoStatus.Status = STATUS_SUCCESS;
2965 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2966 return STATUS_SUCCESS;
2967}
2968
2969
2970/**
2971 * Stub function for functions we don't implemented.
2972 *
2973 * @returns STATUS_NOT_SUPPORTED
2974 * @param pDevObj Device object.
2975 * @param pIrp IRP.
2976 */
2977static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2978{
2979 RT_NOREF1(pDevObj);
2980 LogFlowFuncEnter();
2981
2982 pIrp->IoStatus.Information = 0;
2983 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
2984 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2985
2986 return STATUS_NOT_SUPPORTED;
2987}
2988
2989
2990/**
2991 * Bug check callback (KBUGCHECK_CALLBACK_ROUTINE).
2992 *
2993 * This adds a log entry on the host, in case Hyper-V isn't active or the guest
2994 * is too old for reporting it itself via the crash MSRs.
2995 *
2996 * @param pvBuffer Not used.
2997 * @param cbBuffer Not used.
2998 */
2999static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer)
3000{
3001 if (g_pauKiBugCheckData)
3002 {
3003 RTLogBackdoorPrintf("VBoxGuest: BugCheck! P0=%#zx P1=%#zx P2=%#zx P3=%#zx P4=%#zx\n", g_pauKiBugCheckData[0],
3004 g_pauKiBugCheckData[1], g_pauKiBugCheckData[2], g_pauKiBugCheckData[3], g_pauKiBugCheckData[4]);
3005
3006 VMMDevReqNtBugCheck *pReq = NULL;
3007 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_NtBugCheck);
3008 if (RT_SUCCESS(rc))
3009 {
3010 pReq->uBugCheck = g_pauKiBugCheckData[0];
3011 pReq->auParameters[0] = g_pauKiBugCheckData[1];
3012 pReq->auParameters[1] = g_pauKiBugCheckData[2];
3013 pReq->auParameters[2] = g_pauKiBugCheckData[3];
3014 pReq->auParameters[3] = g_pauKiBugCheckData[4];
3015 VbglR0GRPerform(&pReq->header);
3016 VbglR0GRFree(&pReq->header);
3017 }
3018 }
3019 else
3020 {
3021 RTLogBackdoorPrintf("VBoxGuest: BugCheck!\n");
3022
3023 VMMDevRequestHeader *pReqHdr = NULL;
3024 int rc = VbglR0GRAlloc(&pReqHdr, sizeof(*pReqHdr), VMMDevReq_NtBugCheck);
3025 if (RT_SUCCESS(rc))
3026 {
3027 VbglR0GRPerform(pReqHdr);
3028 VbglR0GRFree(pReqHdr);
3029 }
3030 }
3031
3032 RT_NOREF(pvBuffer, cbBuffer);
3033}
3034
3035
3036/**
3037 * Sets the mouse notification callback.
3038 *
3039 * @returns VBox status code.
3040 * @param pDevExt Pointer to the device extension.
3041 * @param pNotify Pointer to the mouse notify struct.
3042 */
3043int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
3044{
3045 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
3046 /* we need a lock here to avoid concurrency with the set event functionality */
3047 KIRQL OldIrql;
3048 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql);
3049 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
3050 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
3051 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql);
3052 return VINF_SUCCESS;
3053}
3054
3055
3056/**
3057 * DPC handler.
3058 *
3059 * @param pDPC DPC descriptor.
3060 * @param pDevObj Device object.
3061 * @param pIrp Interrupt request packet.
3062 * @param pContext Context specific pointer.
3063 */
3064static void NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
3065{
3066 RT_NOREF3(pDPC, pIrp, pContext);
3067 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
3068 Log3Func(("pDevExt=0x%p\n", pDevExt));
3069
3070 /* Test & reset the counter. */
3071 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
3072 {
3073 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
3074 * i.e. to prevent the event from destroyed while we're using it */
3075 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
3076 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3077
3078 if (pDevExt->Core.pfnMouseNotifyCallback)
3079 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
3080
3081 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3082 }
3083
3084 /* Process the wake-up list we were asked by the scheduling a DPC
3085 * in vgdrvNtIsrHandler(). */
3086 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
3087}
3088
3089
3090/**
3091 * ISR handler.
3092 *
3093 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
3094 * @param pInterrupt Interrupt that was triggered.
3095 * @param pServiceContext Context specific pointer.
3096 */
3097static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
3098{
3099 RT_NOREF1(pInterrupt);
3100 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
3101 if (pDevExt == NULL)
3102 return FALSE;
3103
3104 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
3105
3106 /* Enter the common ISR routine and do the actual work. */
3107 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
3108
3109 /* If we need to wake up some events we do that in a DPC to make
3110 * sure we're called at the right IRQL. */
3111 if (fIRQTaken)
3112 {
3113 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
3114 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
3115 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
3116 {
3117 Log3Func(("Requesting DPC...\n"));
3118 IoRequestDpc(pDevExt->pDeviceObject, NULL /*pIrp*/, NULL /*pvContext*/);
3119 }
3120 }
3121 return fIRQTaken;
3122}
3123
3124
3125void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
3126{
3127 NOREF(pDevExt);
3128 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
3129 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
3130 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
3131}
3132
3133
3134/**
3135 * Hook for handling OS specfic options from the host.
3136 *
3137 * @returns true if handled, false if not.
3138 * @param pDevExt The device extension.
3139 * @param pszName The option name.
3140 * @param pszValue The option value.
3141 */
3142bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
3143{
3144 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
3145 return false;
3146}
3147
3148
3149/**
3150 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
3151 */
3152static NTSTATUS NTAPI vgdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
3153 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
3154{
3155 Log4(("vgdrvNtRegistryEnumCallback: pwszValueName=%ls uValueType=%#x Value=%.*Rhxs\n", pwszValueName, uValueType, cbValue, pvValue));
3156
3157 /*
3158 * Filter out general service config values.
3159 */
3160 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
3161 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
3162 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
3163 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
3164 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
3165 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
3166 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
3167 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
3168 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
3169 )
3170 {
3171 return STATUS_SUCCESS;
3172 }
3173
3174 /*
3175 * Convert the value name.
3176 */
3177 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
3178 if (cch < 64 && cch > 0)
3179 {
3180 char szValueName[72];
3181 char *pszTmp = szValueName;
3182 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
3183 if (RT_SUCCESS(rc))
3184 {
3185 /*
3186 * Convert the value.
3187 */
3188 char szValue[72];
3189 char *pszFree = NULL;
3190 char *pszValue = NULL;
3191 szValue[0] = '\0';
3192 switch (uValueType)
3193 {
3194 case REG_SZ:
3195 case REG_EXPAND_SZ:
3196 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
3197 if (RT_SUCCESS(rc) && cch < _1K)
3198 {
3199 if (cch < sizeof(szValue))
3200 {
3201 pszValue = szValue;
3202 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3203 }
3204 else
3205 {
3206 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3207 if (RT_SUCCESS(rc))
3208 pszFree = pszValue;
3209 }
3210 if (RT_FAILURE(rc))
3211 {
3212 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
3213 pwszValueName, rc));
3214 pszValue = NULL;
3215 }
3216 }
3217 else if (RT_SUCCESS(rc))
3218 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
3219 pwszValueName, cbValue, uValueType));
3220 else
3221 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
3222 pwszValueName, cbValue, uValueType));
3223 break;
3224
3225 case REG_DWORD:
3226 if (cbValue == sizeof(uint32_t))
3227 {
3228 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3229 pszValue = szValue;
3230 }
3231 else
3232 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3233 break;
3234
3235 case REG_QWORD:
3236 if (cbValue == sizeof(uint64_t))
3237 {
3238 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3239 pszValue = szValue;
3240 }
3241 else
3242 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3243 break;
3244
3245 default:
3246 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
3247 break;
3248 }
3249 if (pszValue)
3250 {
3251 /*
3252 * Process it.
3253 */
3254 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
3255 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
3256 if (pszFree)
3257 RTStrFree(pszFree);
3258 }
3259 }
3260 }
3261 else if (cch > 0)
3262 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
3263 else
3264 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
3265 NOREF(pvEntryCtx);
3266 return STATUS_SUCCESS;
3267}
3268
3269
3270/**
3271 * Reads configuration from the registry and guest properties.
3272 *
3273 * We ignore failures and instead preserve existing configuration values.
3274 *
3275 * Thie routine will block.
3276 *
3277 * @param pDevExt The device extension.
3278 */
3279static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
3280{
3281 /*
3282 * First the registry.
3283 *
3284 * Note! RTL_QUERY_REGISTRY_NOEXPAND is sensible (no environment) and also necessary to
3285 * avoid crash on NT 3.1 because RtlExpandEnvironmentStrings_U thinks its in ring-3
3286 * and tries to get the default heap from the PEB via the TEB. No TEB in ring-0.
3287 */
3288 RTL_QUERY_REGISTRY_TABLE aQuery[2];
3289 RT_ZERO(aQuery);
3290 aQuery[0].QueryRoutine = vgdrvNtRegistryEnumCallback;
3291 aQuery[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
3292 aQuery[0].Name = NULL;
3293 aQuery[0].EntryContext = NULL;
3294 aQuery[0].DefaultType = REG_NONE;
3295 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /*pwszzEnv*/);
3296 if (!NT_SUCCESS(rcNt))
3297 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
3298
3299 /*
3300 * Read configuration from the host.
3301 */
3302 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
3303}
3304
3305#ifdef VBOX_STRICT
3306
3307/**
3308 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
3309 */
3310static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
3311{
3312 AssertPtrReturn(pu32Bits, 0);
3313 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
3314 uint32_t u32Result = 0;
3315 uint32_t u32WorkingMask = u32Mask;
3316 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3317
3318 while (iBitOffset > 0)
3319 {
3320 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
3321 if (fSet)
3322 u32Result |= 1 << (iBitOffset - 1);
3323 u32WorkingMask &= ~(1 << (iBitOffset - 1));
3324 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3325 }
3326 LogFlowFunc(("Returning %#x\n", u32Result));
3327 return u32Result;
3328}
3329
3330
3331static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
3332{
3333 ULONG u32Bits2 = u32Bits;
3334 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
3335 if ( u32Result != u32Exp
3336 || (u32Bits2 & u32Mask)
3337 || (u32Bits2 & u32Result)
3338 || ((u32Bits2 | u32Result) != u32Bits)
3339 )
3340 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
3341 u32Mask, u32Bits, u32Bits2, u32Result));
3342}
3343
3344
3345static void vgdrvNtDoTests(void)
3346{
3347 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
3348 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
3349 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
3350 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
3351 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
3352 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
3353}
3354
3355#endif /* VBOX_STRICT */
3356
3357#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
3358
3359/*
3360 * DPC latency checker.
3361 */
3362
3363/**
3364 * One DPC latency sample.
3365 */
3366typedef struct DPCSAMPLE
3367{
3368 LARGE_INTEGER PerfDelta;
3369 LARGE_INTEGER PerfCounter;
3370 LARGE_INTEGER PerfFrequency;
3371 uint64_t u64TSC;
3372} DPCSAMPLE;
3373AssertCompileSize(DPCSAMPLE, 4*8);
3374
3375/**
3376 * The DPC latency measurement workset.
3377 */
3378typedef struct DPCDATA
3379{
3380 KDPC Dpc;
3381 KTIMER Timer;
3382 KSPIN_LOCK SpinLock;
3383
3384 ULONG ulTimerRes;
3385
3386 bool volatile fFinished;
3387
3388 /** The timer interval (relative). */
3389 LARGE_INTEGER DueTime;
3390
3391 LARGE_INTEGER PerfCounterPrev;
3392
3393 /** Align the sample array on a 64 byte boundrary just for the off chance
3394 * that we'll get cache line aligned memory backing this structure. */
3395 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
3396
3397 int cSamples;
3398 DPCSAMPLE aSamples[8192];
3399} DPCDATA;
3400
3401AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
3402
3403/**
3404 * DPC callback routine for the DPC latency measurement code.
3405 *
3406 * @param pDpc The DPC, not used.
3407 * @param pvDeferredContext Pointer to the DPCDATA.
3408 * @param SystemArgument1 System use, ignored.
3409 * @param SystemArgument2 System use, ignored.
3410 */
3411static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
3412{
3413 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
3414 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
3415
3416 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
3417
3418 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
3419 pData->fFinished = true;
3420 else
3421 {
3422 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
3423
3424 pSample->u64TSC = ASMReadTSC();
3425 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
3426 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
3427
3428 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
3429
3430 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3431 }
3432
3433 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
3434}
3435
3436
3437/**
3438 * Handles the DPC latency checker request.
3439 *
3440 * @returns VBox status code.
3441 */
3442int VGDrvNtIOCtl_DpcLatencyChecker(void)
3443{
3444 /*
3445 * Allocate a block of non paged memory for samples and related data.
3446 */
3447 DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
3448 if (!pData)
3449 {
3450 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
3451 return VERR_NO_MEMORY;
3452 }
3453
3454 /*
3455 * Initialize the data.
3456 */
3457 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
3458 KeInitializeTimer(&pData->Timer);
3459 KeInitializeSpinLock(&pData->SpinLock);
3460
3461 pData->fFinished = false;
3462 pData->cSamples = 0;
3463 pData->PerfCounterPrev.QuadPart = 0;
3464
3465 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
3466 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
3467
3468 /*
3469 * Start the DPC measurements and wait for a full set.
3470 */
3471 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3472
3473 while (!pData->fFinished)
3474 {
3475 LARGE_INTEGER Interval;
3476 Interval.QuadPart = -100 * 1000 * 10;
3477 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
3478 }
3479
3480 ExSetTimerResolution(0, 0);
3481
3482 /*
3483 * Log everything to the host.
3484 */
3485 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
3486 for (int i = 0; i < pData->cSamples; i++)
3487 {
3488 DPCSAMPLE *pSample = &pData->aSamples[i];
3489
3490 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
3491 i,
3492 pSample->PerfDelta.QuadPart,
3493 pSample->PerfCounter.QuadPart,
3494 pSample->PerfFrequency.QuadPart,
3495 pSample->u64TSC);
3496 }
3497
3498 RTMemFree(pData);
3499 return VINF_SUCCESS;
3500}
3501
3502#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
3503
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use