VirtualBox

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

Last change on this file since 97436 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 130.3 KB
Line 
1/* $Id: VBoxGuest-win.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2022 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_GUEST;
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 else
2662 LogRel(("vgdrvNtCalcRequestorFlags: NtOpenProcessToken query failed: %#x\n", rcNt));
2663
2664 Log5(("vgdrvNtCalcRequestorFlags: returns %#x\n", fRequestor));
2665 return fRequestor;
2666}
2667
2668
2669/**
2670 * Create (i.e. Open) file entry point.
2671 *
2672 * @param pDevObj Device object.
2673 * @param pIrp Request packet.
2674 */
2675static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2676{
2677 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
2678 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2679 PFILE_OBJECT pFileObj = pStack->FileObject;
2680 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2681
2682 Assert(pFileObj->FsContext == NULL);
2683
2684 /*
2685 * We are not remotely similar to a directory...
2686 */
2687 NTSTATUS rcNt;
2688 if (!(pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE))
2689 {
2690 /*
2691 * Check the device state. We enter the critsect in shared mode to
2692 * prevent race with PnP system requests checking whether we're idle.
2693 */
2694 RTCritSectRwEnterShared(&pDevExt->SessionCreateCritSect);
2695 VGDRVNTDEVSTATE const enmDevState = pDevExt->enmDevState;
2696 if (enmDevState == VGDRVNTDEVSTATE_OPERATIONAL)
2697 {
2698 /*
2699 * Create a client session.
2700 */
2701 int rc;
2702 PVBOXGUESTSESSION pSession;
2703 if (pIrp->RequestorMode == KernelMode)
2704 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
2705 else
2706 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, vgdrvNtCalcRequestorFlags(), &pSession);
2707 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2708 if (RT_SUCCESS(rc))
2709 {
2710 pFileObj->FsContext = pSession;
2711 Log(("vgdrvNtCreate: Successfully created %s session %p (fRequestor=%#x)\n",
2712 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession, pSession->fRequestor));
2713
2714 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
2715 }
2716
2717 /* Note. the IoStatus is completely ignored on error. */
2718 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
2719 if (rc == VERR_NO_MEMORY)
2720 rcNt = STATUS_NO_MEMORY;
2721 else
2722 rcNt = STATUS_UNSUCCESSFUL;
2723 }
2724 else
2725 {
2726 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2727 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", enmDevState));
2728 rcNt = STATUS_DEVICE_NOT_READY;
2729 }
2730 }
2731 else
2732 {
2733 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
2734 rcNt = STATUS_NOT_A_DIRECTORY;
2735 }
2736 return vgdrvNtCompleteRequest(rcNt, pIrp);
2737}
2738
2739
2740/**
2741 * Close file entry point.
2742 *
2743 * @param pDevObj Device object.
2744 * @param pIrp Request packet.
2745 */
2746static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2747{
2748 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2749 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2750 PFILE_OBJECT pFileObj = pStack->FileObject;
2751
2752 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
2753
2754#ifdef VBOX_WITH_HGCM
2755 /* Close both, R0 and R3 sessions. */
2756 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
2757 if (pSession)
2758 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
2759#endif
2760
2761 pFileObj->FsContext = NULL;
2762 pIrp->IoStatus.Information = 0;
2763 pIrp->IoStatus.Status = STATUS_SUCCESS;
2764 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2765
2766 return STATUS_SUCCESS;
2767}
2768
2769
2770/**
2771 * Device I/O Control entry point.
2772 *
2773 * @param pDevObj Device object.
2774 * @param pIrp Request packet.
2775 */
2776NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2777{
2778 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2779 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2780 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
2781
2782 if (!RT_VALID_PTR(pSession))
2783 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
2784
2785#if 0 /* No fast I/O controls defined yet. */
2786 /*
2787 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
2788 * the session and iCmd, and does not return anything.
2789 */
2790 if (pSession->fUnrestricted)
2791 {
2792 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
2793 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
2794 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
2795 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
2796 {
2797 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
2798
2799 /* Complete the I/O request. */
2800 supdrvSessionRelease(pSession);
2801 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
2802 }
2803 }
2804#endif
2805
2806 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
2807}
2808
2809
2810/**
2811 * Device I/O Control entry point.
2812 *
2813 * @param pDevExt The device extension.
2814 * @param pSession The session.
2815 * @param pIrp Request packet.
2816 * @param pStack The request stack pointer.
2817 */
2818static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2819 PIRP pIrp, PIO_STACK_LOCATION pStack)
2820{
2821 NTSTATUS rcNt;
2822 uint32_t cbOut = 0;
2823 int rc = 0;
2824 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
2825 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
2826 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
2827 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
2828
2829#if 0 /*def RT_ARCH_AMD64*/
2830 /* Don't allow 32-bit processes to do any I/O controls. */
2831 if (!IoIs32bitProcess(pIrp))
2832#endif
2833 {
2834 /* Verify that it's a buffered CTL. */
2835 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
2836 {
2837 /* Verify that the sizes in the request header are correct. */
2838 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
2839 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
2840 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
2841 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
2842 {
2843 /* Zero extra output bytes to make sure we don't leak anything. */
2844 if (pHdr->cbIn < pHdr->cbOut)
2845 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
2846
2847 /*
2848 * Do the job.
2849 */
2850 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
2851 RT_MAX(pHdr->cbIn, pHdr->cbOut));
2852 if (RT_SUCCESS(rc))
2853 {
2854 rcNt = STATUS_SUCCESS;
2855 cbOut = pHdr->cbOut;
2856 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
2857 {
2858 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
2859 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
2860 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
2861 }
2862
2863 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
2864 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
2865 && RT_SUCCESS(pHdr->rc))
2866 pStack->FileObject->FsContext = NULL;
2867 }
2868 else if (rc == VERR_NOT_SUPPORTED)
2869 rcNt = STATUS_NOT_SUPPORTED;
2870 else
2871 rcNt = STATUS_INVALID_PARAMETER;
2872 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
2873 }
2874 else
2875 {
2876 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
2877 pStack->Parameters.DeviceIoControl.IoControlCode,
2878 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
2879 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
2880 pStack->Parameters.DeviceIoControl.InputBufferLength,
2881 pStack->Parameters.DeviceIoControl.OutputBufferLength));
2882 rcNt = STATUS_INVALID_PARAMETER;
2883 }
2884 }
2885 else
2886 {
2887 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
2888 pStack->Parameters.DeviceIoControl.IoControlCode));
2889 rcNt = STATUS_NOT_SUPPORTED;
2890 }
2891 }
2892#if 0 /*def RT_ARCH_AMD64*/
2893 else
2894 {
2895 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
2896 rcNt = STATUS_NOT_SUPPORTED;
2897 }
2898#endif
2899
2900 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
2901}
2902
2903
2904/**
2905 * Internal Device I/O Control entry point (for IDC).
2906 *
2907 * @param pDevObj Device object.
2908 * @param pIrp Request packet.
2909 */
2910static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2911{
2912 /* Currently no special code here. */
2913 return vgdrvNtDeviceControl(pDevObj, pIrp);
2914}
2915
2916
2917/**
2918 * IRP_MJ_SHUTDOWN handler.
2919 *
2920 * @returns NT status code
2921 * @param pDevObj Device object.
2922 * @param pIrp IRP.
2923 */
2924static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2925{
2926 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2927 LogFlowFuncEnter();
2928
2929 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
2930 if (pReq)
2931 {
2932 pReq->header.requestType = VMMDevReq_SetPowerStatus;
2933 pReq->powerState = VMMDevPowerState_PowerOff;
2934
2935 int rc = VbglR0GRPerform(&pReq->header);
2936 if (RT_FAILURE(rc))
2937 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
2938 }
2939
2940 /* just in case, since we shouldn't normally get here. */
2941 pIrp->IoStatus.Information = 0;
2942 pIrp->IoStatus.Status = STATUS_SUCCESS;
2943 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2944 return STATUS_SUCCESS;
2945}
2946
2947
2948/**
2949 * Stub function for functions we don't implemented.
2950 *
2951 * @returns STATUS_NOT_SUPPORTED
2952 * @param pDevObj Device object.
2953 * @param pIrp IRP.
2954 */
2955static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2956{
2957 RT_NOREF1(pDevObj);
2958 LogFlowFuncEnter();
2959
2960 pIrp->IoStatus.Information = 0;
2961 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
2962 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2963
2964 return STATUS_NOT_SUPPORTED;
2965}
2966
2967
2968/**
2969 * Bug check callback (KBUGCHECK_CALLBACK_ROUTINE).
2970 *
2971 * This adds a log entry on the host, in case Hyper-V isn't active or the guest
2972 * is too old for reporting it itself via the crash MSRs.
2973 *
2974 * @param pvBuffer Not used.
2975 * @param cbBuffer Not used.
2976 */
2977static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer)
2978{
2979 if (g_pauKiBugCheckData)
2980 {
2981 RTLogBackdoorPrintf("VBoxGuest: BugCheck! P0=%#zx P1=%#zx P2=%#zx P3=%#zx P4=%#zx\n", g_pauKiBugCheckData[0],
2982 g_pauKiBugCheckData[1], g_pauKiBugCheckData[2], g_pauKiBugCheckData[3], g_pauKiBugCheckData[4]);
2983
2984 VMMDevReqNtBugCheck *pReq = NULL;
2985 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_NtBugCheck);
2986 if (RT_SUCCESS(rc))
2987 {
2988 pReq->uBugCheck = g_pauKiBugCheckData[0];
2989 pReq->auParameters[0] = g_pauKiBugCheckData[1];
2990 pReq->auParameters[1] = g_pauKiBugCheckData[2];
2991 pReq->auParameters[2] = g_pauKiBugCheckData[3];
2992 pReq->auParameters[3] = g_pauKiBugCheckData[4];
2993 VbglR0GRPerform(&pReq->header);
2994 VbglR0GRFree(&pReq->header);
2995 }
2996 }
2997 else
2998 {
2999 RTLogBackdoorPrintf("VBoxGuest: BugCheck!\n");
3000
3001 VMMDevRequestHeader *pReqHdr = NULL;
3002 int rc = VbglR0GRAlloc(&pReqHdr, sizeof(*pReqHdr), VMMDevReq_NtBugCheck);
3003 if (RT_SUCCESS(rc))
3004 {
3005 VbglR0GRPerform(pReqHdr);
3006 VbglR0GRFree(pReqHdr);
3007 }
3008 }
3009
3010 RT_NOREF(pvBuffer, cbBuffer);
3011}
3012
3013
3014/**
3015 * Sets the mouse notification callback.
3016 *
3017 * @returns VBox status code.
3018 * @param pDevExt Pointer to the device extension.
3019 * @param pNotify Pointer to the mouse notify struct.
3020 */
3021int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
3022{
3023 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
3024 /* we need a lock here to avoid concurrency with the set event functionality */
3025 KIRQL OldIrql;
3026 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql);
3027 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
3028 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
3029 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql);
3030 return VINF_SUCCESS;
3031}
3032
3033
3034/**
3035 * DPC handler.
3036 *
3037 * @param pDPC DPC descriptor.
3038 * @param pDevObj Device object.
3039 * @param pIrp Interrupt request packet.
3040 * @param pContext Context specific pointer.
3041 */
3042static void NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
3043{
3044 RT_NOREF3(pDPC, pIrp, pContext);
3045 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
3046 Log3Func(("pDevExt=0x%p\n", pDevExt));
3047
3048 /* Test & reset the counter. */
3049 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
3050 {
3051 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
3052 * i.e. to prevent the event from destroyed while we're using it */
3053 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
3054 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3055
3056 if (pDevExt->Core.pfnMouseNotifyCallback)
3057 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
3058
3059 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3060 }
3061
3062 /* Process the wake-up list we were asked by the scheduling a DPC
3063 * in vgdrvNtIsrHandler(). */
3064 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
3065}
3066
3067
3068/**
3069 * ISR handler.
3070 *
3071 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
3072 * @param pInterrupt Interrupt that was triggered.
3073 * @param pServiceContext Context specific pointer.
3074 */
3075static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
3076{
3077 RT_NOREF1(pInterrupt);
3078 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
3079 if (pDevExt == NULL)
3080 return FALSE;
3081
3082 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
3083
3084 /* Enter the common ISR routine and do the actual work. */
3085 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
3086
3087 /* If we need to wake up some events we do that in a DPC to make
3088 * sure we're called at the right IRQL. */
3089 if (fIRQTaken)
3090 {
3091 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
3092 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
3093 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
3094 {
3095 Log3Func(("Requesting DPC...\n"));
3096 IoRequestDpc(pDevExt->pDeviceObject, NULL /*pIrp*/, NULL /*pvContext*/);
3097 }
3098 }
3099 return fIRQTaken;
3100}
3101
3102
3103void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
3104{
3105 NOREF(pDevExt);
3106 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
3107 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
3108 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
3109}
3110
3111
3112/**
3113 * Hook for handling OS specfic options from the host.
3114 *
3115 * @returns true if handled, false if not.
3116 * @param pDevExt The device extension.
3117 * @param pszName The option name.
3118 * @param pszValue The option value.
3119 */
3120bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
3121{
3122 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
3123 return false;
3124}
3125
3126
3127/**
3128 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
3129 */
3130static NTSTATUS NTAPI vgdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
3131 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
3132{
3133 Log4(("vgdrvNtRegistryEnumCallback: pwszValueName=%ls uValueType=%#x Value=%.*Rhxs\n", pwszValueName, uValueType, cbValue, pvValue));
3134
3135 /*
3136 * Filter out general service config values.
3137 */
3138 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
3139 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
3140 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
3141 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
3142 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
3143 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
3144 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
3145 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
3146 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
3147 )
3148 {
3149 return STATUS_SUCCESS;
3150 }
3151
3152 /*
3153 * Convert the value name.
3154 */
3155 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
3156 if (cch < 64 && cch > 0)
3157 {
3158 char szValueName[72];
3159 char *pszTmp = szValueName;
3160 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
3161 if (RT_SUCCESS(rc))
3162 {
3163 /*
3164 * Convert the value.
3165 */
3166 char szValue[72];
3167 char *pszFree = NULL;
3168 char *pszValue = NULL;
3169 szValue[0] = '\0';
3170 switch (uValueType)
3171 {
3172 case REG_SZ:
3173 case REG_EXPAND_SZ:
3174 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
3175 if (RT_SUCCESS(rc) && cch < _1K)
3176 {
3177 if (cch < sizeof(szValue))
3178 {
3179 pszValue = szValue;
3180 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3181 }
3182 else
3183 {
3184 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3185 if (RT_SUCCESS(rc))
3186 pszFree = pszValue;
3187 }
3188 if (RT_FAILURE(rc))
3189 {
3190 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
3191 pwszValueName, rc));
3192 pszValue = NULL;
3193 }
3194 }
3195 else if (RT_SUCCESS(rc))
3196 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
3197 pwszValueName, cbValue, uValueType));
3198 else
3199 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
3200 pwszValueName, cbValue, uValueType));
3201 break;
3202
3203 case REG_DWORD:
3204 if (cbValue == sizeof(uint32_t))
3205 {
3206 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3207 pszValue = szValue;
3208 }
3209 else
3210 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3211 break;
3212
3213 case REG_QWORD:
3214 if (cbValue == sizeof(uint64_t))
3215 {
3216 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3217 pszValue = szValue;
3218 }
3219 else
3220 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3221 break;
3222
3223 default:
3224 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
3225 break;
3226 }
3227 if (pszValue)
3228 {
3229 /*
3230 * Process it.
3231 */
3232 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
3233 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
3234 if (pszFree)
3235 RTStrFree(pszFree);
3236 }
3237 }
3238 }
3239 else if (cch > 0)
3240 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
3241 else
3242 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
3243 NOREF(pvEntryCtx);
3244 return STATUS_SUCCESS;
3245}
3246
3247
3248/**
3249 * Reads configuration from the registry and guest properties.
3250 *
3251 * We ignore failures and instead preserve existing configuration values.
3252 *
3253 * Thie routine will block.
3254 *
3255 * @param pDevExt The device extension.
3256 */
3257static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
3258{
3259 /*
3260 * First the registry.
3261 *
3262 * Note! RTL_QUERY_REGISTRY_NOEXPAND is sensible (no environment) and also necessary to
3263 * avoid crash on NT 3.1 because RtlExpandEnvironmentStrings_U thinks its in ring-3
3264 * and tries to get the default heap from the PEB via the TEB. No TEB in ring-0.
3265 */
3266 RTL_QUERY_REGISTRY_TABLE aQuery[2];
3267 RT_ZERO(aQuery);
3268 aQuery[0].QueryRoutine = vgdrvNtRegistryEnumCallback;
3269 aQuery[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
3270 aQuery[0].Name = NULL;
3271 aQuery[0].EntryContext = NULL;
3272 aQuery[0].DefaultType = REG_NONE;
3273 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /*pwszzEnv*/);
3274 if (!NT_SUCCESS(rcNt))
3275 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
3276
3277 /*
3278 * Read configuration from the host.
3279 */
3280 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
3281}
3282
3283#ifdef VBOX_STRICT
3284
3285/**
3286 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
3287 */
3288static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
3289{
3290 AssertPtrReturn(pu32Bits, 0);
3291 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
3292 uint32_t u32Result = 0;
3293 uint32_t u32WorkingMask = u32Mask;
3294 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3295
3296 while (iBitOffset > 0)
3297 {
3298 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
3299 if (fSet)
3300 u32Result |= 1 << (iBitOffset - 1);
3301 u32WorkingMask &= ~(1 << (iBitOffset - 1));
3302 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3303 }
3304 LogFlowFunc(("Returning %#x\n", u32Result));
3305 return u32Result;
3306}
3307
3308
3309static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
3310{
3311 ULONG u32Bits2 = u32Bits;
3312 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
3313 if ( u32Result != u32Exp
3314 || (u32Bits2 & u32Mask)
3315 || (u32Bits2 & u32Result)
3316 || ((u32Bits2 | u32Result) != u32Bits)
3317 )
3318 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
3319 u32Mask, u32Bits, u32Bits2, u32Result));
3320}
3321
3322
3323static void vgdrvNtDoTests(void)
3324{
3325 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
3326 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
3327 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
3328 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
3329 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
3330 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
3331}
3332
3333#endif /* VBOX_STRICT */
3334
3335#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
3336
3337/*
3338 * DPC latency checker.
3339 */
3340
3341/**
3342 * One DPC latency sample.
3343 */
3344typedef struct DPCSAMPLE
3345{
3346 LARGE_INTEGER PerfDelta;
3347 LARGE_INTEGER PerfCounter;
3348 LARGE_INTEGER PerfFrequency;
3349 uint64_t u64TSC;
3350} DPCSAMPLE;
3351AssertCompileSize(DPCSAMPLE, 4*8);
3352
3353/**
3354 * The DPC latency measurement workset.
3355 */
3356typedef struct DPCDATA
3357{
3358 KDPC Dpc;
3359 KTIMER Timer;
3360 KSPIN_LOCK SpinLock;
3361
3362 ULONG ulTimerRes;
3363
3364 bool volatile fFinished;
3365
3366 /** The timer interval (relative). */
3367 LARGE_INTEGER DueTime;
3368
3369 LARGE_INTEGER PerfCounterPrev;
3370
3371 /** Align the sample array on a 64 byte boundrary just for the off chance
3372 * that we'll get cache line aligned memory backing this structure. */
3373 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
3374
3375 int cSamples;
3376 DPCSAMPLE aSamples[8192];
3377} DPCDATA;
3378
3379AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
3380
3381/**
3382 * DPC callback routine for the DPC latency measurement code.
3383 *
3384 * @param pDpc The DPC, not used.
3385 * @param pvDeferredContext Pointer to the DPCDATA.
3386 * @param SystemArgument1 System use, ignored.
3387 * @param SystemArgument2 System use, ignored.
3388 */
3389static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
3390{
3391 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
3392 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
3393
3394 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
3395
3396 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
3397 pData->fFinished = true;
3398 else
3399 {
3400 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
3401
3402 pSample->u64TSC = ASMReadTSC();
3403 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
3404 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
3405
3406 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
3407
3408 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3409 }
3410
3411 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
3412}
3413
3414
3415/**
3416 * Handles the DPC latency checker request.
3417 *
3418 * @returns VBox status code.
3419 */
3420int VGDrvNtIOCtl_DpcLatencyChecker(void)
3421{
3422 /*
3423 * Allocate a block of non paged memory for samples and related data.
3424 */
3425 DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
3426 if (!pData)
3427 {
3428 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
3429 return VERR_NO_MEMORY;
3430 }
3431
3432 /*
3433 * Initialize the data.
3434 */
3435 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
3436 KeInitializeTimer(&pData->Timer);
3437 KeInitializeSpinLock(&pData->SpinLock);
3438
3439 pData->fFinished = false;
3440 pData->cSamples = 0;
3441 pData->PerfCounterPrev.QuadPart = 0;
3442
3443 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
3444 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
3445
3446 /*
3447 * Start the DPC measurements and wait for a full set.
3448 */
3449 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3450
3451 while (!pData->fFinished)
3452 {
3453 LARGE_INTEGER Interval;
3454 Interval.QuadPart = -100 * 1000 * 10;
3455 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
3456 }
3457
3458 ExSetTimerResolution(0, 0);
3459
3460 /*
3461 * Log everything to the host.
3462 */
3463 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
3464 for (int i = 0; i < pData->cSamples; i++)
3465 {
3466 DPCSAMPLE *pSample = &pData->aSamples[i];
3467
3468 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
3469 i,
3470 pSample->PerfDelta.QuadPart,
3471 pSample->PerfCounter.QuadPart,
3472 pSample->PerfFrequency.QuadPart,
3473 pSample->u64TSC);
3474 }
3475
3476 RTMemFree(pData);
3477 return VINF_SUCCESS;
3478}
3479
3480#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
3481
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette