VirtualBox

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

Last change on this file since 45221 was 45013, checked in by vboxsync, 12 years ago

VBoxGuest: Build fix (untested).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.3 KB
Line 
1/* $Id: VBoxGuest-win.cpp 45013 2013-03-13 08:42:43Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_SUP_DRV
22#include "VBoxGuest-win.h"
23#include "VBoxGuestInternal.h"
24
25#include <iprt/asm.h>
26#include <iprt/asm-amd64-x86.h>
27
28#include <VBox/log.h>
29#include <VBox/VBoxGuestLib.h>
30#include <iprt/string.h>
31
32/*
33 * XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist
34 * on NT4, so... The same for ExAllocatePool.
35 */
36#ifdef TARGET_NT4
37# undef ExAllocatePool
38# undef ExFreePool
39#endif
40
41
42/*******************************************************************************
43* Internal Functions *
44*******************************************************************************/
45RT_C_DECLS_BEGIN
46static NTSTATUS vbgdNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
47static void vbgdNtUnload(PDRIVER_OBJECT pDrvObj);
48static NTSTATUS vbgdNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
49static NTSTATUS vbgdNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
50static NTSTATUS vbgdNtIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
51static NTSTATUS vbgdNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
52static NTSTATUS vbgdNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue);
53static NTSTATUS vbgdNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
54static NTSTATUS vbgdNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
55static NTSTATUS vbgdNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
56#ifdef DEBUG
57static void vbgdNtDoTests(void);
58#endif
59RT_C_DECLS_END
60
61
62/*******************************************************************************
63* Exported Functions *
64*******************************************************************************/
65RT_C_DECLS_BEGIN
66ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
67RT_C_DECLS_END
68
69#ifdef ALLOC_PRAGMA
70# pragma alloc_text(INIT, DriverEntry)
71# pragma alloc_text(PAGE, vbgdNtAddDevice)
72# pragma alloc_text(PAGE, vbgdNtUnload)
73# pragma alloc_text(PAGE, vbgdNtCreate)
74# pragma alloc_text(PAGE, vbgdNtClose)
75# pragma alloc_text(PAGE, vbgdNtShutdown)
76# pragma alloc_text(PAGE, vbgdNtNotSupportedStub)
77# pragma alloc_text(PAGE, vbgdNtScanPCIResourceList)
78#endif
79
80
81/*******************************************************************************
82* Global Variables *
83*******************************************************************************/
84/** The detected NT (windows) version. */
85VBGDNTVER g_enmVbgdNtVer = VBGDNTVER_INVALID;
86
87
88
89/**
90 * Driver entry point.
91 *
92 * @returns appropriate status code.
93 * @param pDrvObj Pointer to driver object.
94 * @param pRegPath Registry base path.
95 */
96ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
97{
98 NTSTATUS rc = STATUS_SUCCESS;
99
100 Log(("VBoxGuest::DriverEntry. Driver built: %s %s\n", __DATE__, __TIME__));
101
102 /*
103 * Check if the the NT version is supported and initializing
104 * g_enmVbgdNtVer in the process.
105 */
106 ULONG ulMajorVer;
107 ULONG ulMinorVer;
108 ULONG ulBuildNo;
109 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
110 Log(("VBoxGuest::DriverEntry: Running on Windows NT version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo));
111 if (fCheckedBuild)
112 Log(("VBoxGuest::DriverEntry: Running on a Windows checked build (debug)!\n"));
113#ifdef DEBUG
114 vbgdNtDoTests();
115#endif
116 switch (ulMajorVer)
117 {
118 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
119 switch (ulMinorVer)
120 {
121 case 0: /* Note: Also could be Windows 2008 Server! */
122 g_enmVbgdNtVer = VBGDNTVER_WINVISTA;
123 break;
124 case 1: /* Note: Also could be Windows 2008 Server R2! */
125 g_enmVbgdNtVer = VBGDNTVER_WIN7;
126 break;
127 case 2:
128 g_enmVbgdNtVer = VBGDNTVER_WIN8;
129 break;
130 default:
131 Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", ulMajorVer, ulMinorVer));
132 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
133 break;
134 }
135 break;
136 case 5:
137 switch (ulMinorVer)
138 {
139 case 2:
140 g_enmVbgdNtVer = VBGDNTVER_WIN2K3;
141 break;
142 case 1:
143 g_enmVbgdNtVer = VBGDNTVER_WINXP;
144 break;
145 case 0:
146 g_enmVbgdNtVer = VBGDNTVER_WIN2K;
147 break;
148 default:
149 Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", ulMajorVer, ulMinorVer));
150 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
151 }
152 break;
153 case 4:
154 g_enmVbgdNtVer = VBGDNTVER_WINNT4;
155 break;
156 default:
157 if (ulMajorVer < 4)
158 Log(("VBoxGuest::DriverEntry: At least Windows NT4 required! (%u.%u)\n", ulMajorVer, ulMinorVer));
159 else
160 Log(("VBoxGuest::DriverEntry: Too new version %u.%u!\n", ulMajorVer, ulMinorVer));
161 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
162 break;
163 }
164
165 if (NT_SUCCESS(rc))
166 {
167 /*
168 * Setup the driver entry points in pDrvObj.
169 */
170 pDrvObj->DriverUnload = vbgdNtUnload;
171 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vbgdNtCreate;
172 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vbgdNtClose;
173 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vbgdNtIOCtl;
174 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vbgdNtInternalIOCtl;
175 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vbgdNtShutdown;
176 pDrvObj->MajorFunction[IRP_MJ_READ] = vbgdNtNotSupportedStub;
177 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vbgdNtNotSupportedStub;
178#ifdef TARGET_NT4
179 rc = vbgdNt4CreateDevice(pDrvObj, NULL /* pDevObj */, pRegPath);
180#else
181 pDrvObj->MajorFunction[IRP_MJ_PNP] = vbgdNtPnP;
182 pDrvObj->MajorFunction[IRP_MJ_POWER] = vbgdNtPower;
183 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vbgdNtSystemControl;
184 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vbgdNtAddDevice;
185#endif
186 }
187
188 Log(("VBoxGuest::DriverEntry returning %#x\n", rc));
189 return rc;
190}
191
192
193#ifndef TARGET_NT4
194/**
195 * Handle request from the Plug & Play subsystem.
196 *
197 * @returns NT status code
198 * @param pDrvObj Driver object
199 * @param pDevObj Device object
200 */
201static NTSTATUS vbgdNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
202{
203 NTSTATUS rc;
204 Log(("VBoxGuest::vbgdNtGuestAddDevice\n"));
205
206 /*
207 * Create device.
208 */
209 UNICODE_STRING DevName;
210 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
211 PDEVICE_OBJECT pDeviceObject = NULL;
212 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
213 if (NT_SUCCESS(rc))
214 {
215 /*
216 * Create symbolic link (DOS devices).
217 */
218 UNICODE_STRING DosName;
219 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
220 rc = IoCreateSymbolicLink(&DosName, &DevName);
221 if (NT_SUCCESS(rc))
222 {
223 /*
224 * Setup the device extension.
225 */
226 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
227 RT_ZERO(*pDevExt);
228
229 KeInitializeSpinLock(&pDevExt->MouseEventAccessLock);
230
231 pDevExt->pDeviceObject = pDeviceObject;
232 pDevExt->prevDevState = STOPPED;
233 pDevExt->devState = STOPPED;
234
235 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
236 if (pDevExt->pNextLowerDriver != NULL)
237 {
238 /*
239 * If we reached this point we're fine with the basic driver setup,
240 * so continue to init our own things.
241 */
242#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
243 vbgdNtBugCheckCallback(pDevExt); /* Ignore failure! */
244#endif
245 if (NT_SUCCESS(rc))
246 {
247 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
248 pDeviceObject->Flags |= DO_POWER_PAGABLE;
249
250 /* Driver is ready now. */
251 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
252 Log(("VBoxGuest::vbgdNtGuestAddDevice: returning with rc = 0x%x (success)\n", rc));
253 return rc;
254 }
255
256 IoDetachDevice(pDevExt->pNextLowerDriver);
257 }
258 else
259 {
260 Log(("VBoxGuest::vbgdNtGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
261 rc = STATUS_DEVICE_NOT_CONNECTED;
262 }
263
264 /* bail out */
265 IoDeleteSymbolicLink(&DosName);
266 }
267 else
268 Log(("VBoxGuest::vbgdNtGuestAddDevice: IoCreateSymbolicLink failed with rc=%#x!\n", rc));
269 IoDeleteDevice(pDeviceObject);
270 }
271 else
272 Log(("VBoxGuest::vbgdNtGuestAddDevice: IoCreateDevice failed with rc=%#x!\n", rc));
273 Log(("VBoxGuest::vbgdNtGuestAddDevice: returning with rc = 0x%x\n", rc));
274 return rc;
275}
276#endif
277
278
279/**
280 * Debug helper to dump a device resource list.
281 *
282 * @param pResourceList list of device resources.
283 */
284static void vbgdNtShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList)
285{
286#ifdef LOG_ENABLED
287 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pResourceList->PartialDescriptors;
288 ULONG cResources = pResourceList->Count;
289
290 for (ULONG i = 0; i < cResources; ++i, ++pResource)
291 {
292 ULONG uType = pResource->Type;
293 static char const * const s_apszName[] =
294 {
295 "CmResourceTypeNull",
296 "CmResourceTypePort",
297 "CmResourceTypeInterrupt",
298 "CmResourceTypeMemory",
299 "CmResourceTypeDma",
300 "CmResourceTypeDeviceSpecific",
301 "CmResourceTypeBusNumber",
302 "CmResourceTypeDevicePrivate",
303 "CmResourceTypeAssignedResource",
304 "CmResourceTypeSubAllocateFrom",
305 };
306
307 Log(("VBoxGuest::vbgdNtShowDeviceResources: Type %s",
308 uType < RT_ELEMENTS(s_apszName) ? s_apszName[uType] : "Unknown"));
309
310 switch (uType)
311 {
312 case CmResourceTypePort:
313 case CmResourceTypeMemory:
314 Log(("VBoxGuest::vbgdNtShowDeviceResources: Start %8X%8.8lX length %X\n",
315 pResource->u.Port.Start.HighPart, pResource->u.Port.Start.LowPart,
316 pResource->u.Port.Length));
317 break;
318
319 case CmResourceTypeInterrupt:
320 Log(("VBoxGuest::vbgdNtShowDeviceResources: Level %X, Vector %X, Affinity %X\n",
321 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector,
322 pResource->u.Interrupt.Affinity));
323 break;
324
325 case CmResourceTypeDma:
326 Log(("VBoxGuest::vbgdNtShowDeviceResources: Channel %d, Port %X\n",
327 pResource->u.Dma.Channel, pResource->u.Dma.Port));
328 break;
329
330 default:
331 Log(("\n"));
332 break;
333 }
334 }
335#endif
336}
337
338
339/**
340 * Global initialisation stuff (PnP + NT4 legacy).
341 *
342 * @param pDevObj Device object.
343 * @param pIrp Request packet.
344 */
345#ifndef TARGET_NT4
346NTSTATUS vbgdNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp)
347#else
348NTSTATUS vbgdNtInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath)
349#endif
350{
351 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
352#ifndef TARGET_NT4
353 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
354#endif
355
356 Log(("VBoxGuest::vbgdNtInit\n"));
357
358 int rc = STATUS_SUCCESS;
359#ifdef TARGET_NT4
360 /*
361 * Let's have a look at what our PCI adapter offers.
362 */
363 Log(("VBoxGuest::vbgdNtInit: Starting to scan PCI resources of VBoxGuest ...\n"));
364
365 /* Assign the PCI resources. */
366 PCM_RESOURCE_LIST pResourceList = NULL;
367 UNICODE_STRING classNameString;
368 RtlInitUnicodeString(&classNameString, L"VBoxGuestAdapter");
369 rc = HalAssignSlotResources(pRegPath, &classNameString,
370 pDrvObj, pDevObj,
371 PCIBus, pDevExt->busNumber, pDevExt->slotNumber,
372 &pResourceList);
373 if (pResourceList && pResourceList->Count > 0)
374 vbgdNtShowDeviceResources(&pResourceList->List[0].PartialResourceList);
375 if (NT_SUCCESS(rc))
376 rc = vbgdNtScanPCIResourceList(pResourceList, pDevExt);
377#else
378 if (pStack->Parameters.StartDevice.AllocatedResources->Count > 0)
379 vbgdNtShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
380 if (NT_SUCCESS(rc))
381 rc = vbgdNtScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, pDevExt);
382#endif
383 if (NT_SUCCESS(rc))
384 {
385 /*
386 * Map physical address of VMMDev memory into MMIO region
387 * and init the common device extension bits.
388 */
389 void *pvMMIOBase = NULL;
390 uint32_t cbMMIO = 0;
391 rc = vbgdNtMapVMMDevMemory(pDevExt,
392 pDevExt->vmmDevPhysMemoryAddress,
393 pDevExt->vmmDevPhysMemoryLength,
394 &pvMMIOBase,
395 &cbMMIO);
396 if (NT_SUCCESS(rc))
397 {
398 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
399
400 Log(("VBoxGuest::vbgdNtInit: pvMMIOBase = 0x%p, pDevExt = 0x%p, pDevExt->Core.pVMMDevMemory = 0x%p\n",
401 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
402
403 int vrc = VBoxGuestInitDevExt(&pDevExt->Core,
404 pDevExt->Core.IOPortBase,
405 pvMMIOBase, cbMMIO,
406 vbgdNtVersionToOSType(g_enmVbgdNtVer),
407 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
408 if (RT_FAILURE(vrc))
409 {
410 Log(("VBoxGuest::vbgdNtInit: Could not init device extension, rc = %Rrc!\n", vrc));
411 rc = STATUS_DEVICE_CONFIGURATION_ERROR;
412 }
413 }
414 else
415 Log(("VBoxGuest::vbgdNtInit: Could not map physical address of VMMDev, rc = 0x%x!\n", rc));
416 }
417
418 if (NT_SUCCESS(rc))
419 {
420 int vrc = VbglGRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
421 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
422 if (RT_FAILURE(vrc))
423 {
424 Log(("VBoxGuest::vbgdNtInit: Alloc for pPowerStateRequest failed, rc = %Rrc\n", vrc));
425 rc = STATUS_UNSUCCESSFUL;
426 }
427 }
428
429 if (NT_SUCCESS(rc))
430 {
431 /*
432 * Register DPC and ISR.
433 */
434 Log(("VBoxGuest::vbgdNtInit: Initializing DPC/ISR ...\n"));
435
436 IoInitializeDpcRequest(pDevExt->pDeviceObject, vbgdNtDpcHandler);
437#ifdef TARGET_NT4
438 ULONG uInterruptVector;
439 KIRQL irqLevel;
440 /* Get an interrupt vector. */
441 /* Only proceed if the device provides an interrupt. */
442 if ( pDevExt->interruptLevel
443 || pDevExt->interruptVector)
444 {
445 Log(("VBoxGuest::vbgdNtInit: Getting interrupt vector (HAL): Bus: %u, IRQL: %u, Vector: %u\n",
446 pDevExt->busNumber, pDevExt->interruptLevel, pDevExt->interruptVector));
447
448 uInterruptVector = HalGetInterruptVector(PCIBus,
449 pDevExt->busNumber,
450 pDevExt->interruptLevel,
451 pDevExt->interruptVector,
452 &irqLevel,
453 &pDevExt->interruptAffinity);
454 Log(("VBoxGuest::vbgdNtInit: HalGetInterruptVector returns vector %u\n", uInterruptVector));
455 if (uInterruptVector == 0)
456 Log(("VBoxGuest::vbgdNtInit: No interrupt vector found!\n"));
457 }
458 else
459 Log(("VBoxGuest::vbgdNtInit: Device does not provide an interrupt!\n"));
460#endif
461 if (pDevExt->interruptVector)
462 {
463 Log(("VBoxGuest::vbgdNtInit: Connecting interrupt ...\n"));
464
465 rc = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
466 (PKSERVICE_ROUTINE)vbgdNtIsrHandler, /* Our ISR handler. */
467 pDevExt, /* Device context. */
468 NULL, /* Optional spinlock. */
469#ifdef TARGET_NT4
470 uInterruptVector, /* Interrupt vector. */
471 irqLevel, /* Interrupt level. */
472 irqLevel, /* Interrupt level. */
473#else
474 pDevExt->interruptVector, /* Interrupt vector. */
475 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
476 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
477#endif
478 pDevExt->interruptMode, /* LevelSensitive or Latched. */
479 TRUE, /* Shareable interrupt. */
480 pDevExt->interruptAffinity, /* CPU affinity. */
481 FALSE); /* Don't save FPU stack. */
482 if (NT_ERROR(rc))
483 Log(("VBoxGuest::vbgdNtInit: Could not connect interrupt, rc = 0x%x\n", rc));
484 }
485 else
486 Log(("VBoxGuest::vbgdNtInit: No interrupt vector found!\n"));
487 }
488
489
490#ifdef VBOX_WITH_HGCM
491 Log(("VBoxGuest::vbgdNtInit: Allocating kernel session data ...\n"));
492 int vrc = VBoxGuestCreateKernelSession(&pDevExt->Core, &pDevExt->pKernelSession);
493 if (RT_FAILURE(vrc))
494 {
495 Log(("VBoxGuest::vbgdNtInit: Failed to allocated kernel session data! rc = %Rrc\n", rc));
496 rc = STATUS_UNSUCCESSFUL;
497 }
498#endif
499
500 if (RT_SUCCESS(rc))
501 {
502 ULONG ulValue = 0;
503 NTSTATUS rcNt = vbgdNtRegistryReadDWORD(RTL_REGISTRY_SERVICES, L"VBoxGuest", L"LoggingEnabled", &ulValue);
504 if (NT_SUCCESS(rcNt))
505 {
506 pDevExt->Core.fLoggingEnabled = ulValue >= 0xFF;
507 if (pDevExt->Core.fLoggingEnabled)
508 Log(("Logging to release log enabled (0x%x)", ulValue));
509 }
510
511 /* Ready to rumble! */
512 Log(("VBoxGuest::vbgdNtInit: Device is ready!\n"));
513 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, WORKING);
514 }
515 else
516 pDevExt->pInterruptObject = NULL;
517
518 /** @todo r=bird: The error cleanup here is completely missing. We'll leak a
519 * whole bunch of things... */
520
521 Log(("VBoxGuest::vbgdNtInit: Returned with rc = 0x%x\n", rc));
522 return rc;
523}
524
525
526/**
527 * Cleans up hardware resources.
528 * Do not delete DevExt here.
529 *
530 * @param pDrvObj Driver object.
531 */
532NTSTATUS vbgdNtCleanup(PDEVICE_OBJECT pDevObj)
533{
534 Log(("VBoxGuest::vbgdNtCleanup\n"));
535
536 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
537 if (pDevExt)
538 {
539
540#if 0 /* @todo: test & enable cleaning global session data */
541#ifdef VBOX_WITH_HGCM
542 if (pDevExt->pKernelSession)
543 {
544 VBoxGuestCloseSession(pDevExt, pDevExt->pKernelSession);
545 pDevExt->pKernelSession = NULL;
546 }
547#endif
548#endif
549
550 if (pDevExt->pInterruptObject)
551 {
552 IoDisconnectInterrupt(pDevExt->pInterruptObject);
553 pDevExt->pInterruptObject = NULL;
554 }
555
556 /** @todo: cleanup the rest stuff */
557
558
559#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
560 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
561#endif
562 /* According to MSDN we have to unmap previously mapped memory. */
563 vbgdNtUnmapVMMDevMemory(pDevExt);
564 }
565 return STATUS_SUCCESS;
566}
567
568
569/**
570 * Unload the driver.
571 *
572 * @param pDrvObj Driver object.
573 */
574static void vbgdNtUnload(PDRIVER_OBJECT pDrvObj)
575{
576 Log(("VBoxGuest::vbgdNtGuestUnload\n"));
577#ifdef TARGET_NT4
578 vbgdNtCleanup(pDrvObj->DeviceObject);
579
580 /* Destroy device extension and clean up everything else. */
581 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
582 VBoxGuestDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
583
584 /*
585 * I don't think it's possible to unload a driver which processes have
586 * opened, at least we'll blindly assume that here.
587 */
588 UNICODE_STRING DosName;
589 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
590 NTSTATUS rc = IoDeleteSymbolicLink(&DosName);
591
592 IoDeleteDevice(pDrvObj->DeviceObject);
593#else /* !TARGET_NT4 */
594 /* On a PnP driver this routine will be called after
595 * IRP_MN_REMOVE_DEVICE (where we already did the cleanup),
596 * so don't do anything here (yet). */
597#endif /* !TARGET_NT4 */
598
599 Log(("VBoxGuest::vbgdNtGuestUnload: returning\n"));
600}
601
602
603/**
604 * Create (i.e. Open) file entry point.
605 *
606 * @param pDevObj Device object.
607 * @param pIrp Request packet.
608 */
609static NTSTATUS vbgdNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
610{
611 /** @todo AssertPtrReturn(pIrp); */
612 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
613 /** @todo AssertPtrReturn(pStack); */
614 PFILE_OBJECT pFileObj = pStack->FileObject;
615 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
616 NTSTATUS rc = STATUS_SUCCESS;
617
618 if (pDevExt->devState != WORKING)
619 {
620 Log(("VBoxGuest::vbgdNtGuestCreate: device is not working currently: %d!\n", pDevExt->devState));
621 rc = STATUS_UNSUCCESSFUL;
622 }
623 else if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
624 {
625 /*
626 * We are not remotely similar to a directory...
627 * (But this is possible.)
628 */
629 Log(("VBoxGuest::vbgdNtGuestCreate: Uhm, we're not a directory!\n"));
630 rc = STATUS_NOT_A_DIRECTORY;
631 }
632 else
633 {
634#ifdef VBOX_WITH_HGCM
635 if (pFileObj)
636 {
637 Log(("VBoxGuest::vbgdNtGuestCreate: File object type = %d\n",
638 pFileObj->Type));
639
640 int vrc;
641 PVBOXGUESTSESSION pSession;
642 if (pFileObj->Type == 5 /* File Object */)
643 {
644 /*
645 * Create a session object if we have a valid file object. This session object
646 * exists for every R3 process.
647 */
648 vrc = VBoxGuestCreateUserSession(&pDevExt->Core, &pSession);
649 }
650 else
651 {
652 /* ... otherwise we've been called from R0! */
653 vrc = VBoxGuestCreateKernelSession(&pDevExt->Core, &pSession);
654 }
655 if (RT_SUCCESS(vrc))
656 pFileObj->FsContext = pSession;
657 }
658#endif
659 }
660
661 /* Complete the request! */
662 pIrp->IoStatus.Information = 0;
663 pIrp->IoStatus.Status = rc;
664 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
665
666 Log(("VBoxGuest::vbgdNtGuestCreate: Returning 0x%x\n", rc));
667 return rc;
668}
669
670
671/**
672 * Close file entry point.
673 *
674 * @param pDevObj Device object.
675 * @param pIrp Request packet.
676 */
677static NTSTATUS vbgdNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
678{
679 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
680 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
681 PFILE_OBJECT pFileObj = pStack->FileObject;
682
683 Log(("VBoxGuest::vbgdNtGuestClose: pDevExt=0x%p pFileObj=0x%p FsContext=0x%p\n",
684 pDevExt, pFileObj, pFileObj->FsContext));
685
686#ifdef VBOX_WITH_HGCM
687 /* Close both, R0 and R3 sessions. */
688 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
689 if (pSession)
690 VBoxGuestCloseSession(&pDevExt->Core, pSession);
691#endif
692
693 pFileObj->FsContext = NULL;
694 pIrp->IoStatus.Information = 0;
695 pIrp->IoStatus.Status = STATUS_SUCCESS;
696 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
697
698 return STATUS_SUCCESS;
699}
700
701
702/**
703 * Device I/O Control entry point.
704 *
705 * @param pDevObj Device object.
706 * @param pIrp Request packet.
707 */
708static NTSTATUS vbgdNtIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
709{
710 NTSTATUS Status = STATUS_SUCCESS;
711 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
712 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
713 unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode;
714
715 char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* All requests are buffered. */
716 size_t cbData = pStack->Parameters.DeviceIoControl.InputBufferLength;
717 size_t cbOut = 0;
718
719 /* Do we have a file object associated?*/
720 PFILE_OBJECT pFileObj = pStack->FileObject;
721 PVBOXGUESTSESSION pSession = NULL;
722 if (pFileObj) /* ... then we might have a session object as well! */
723 pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
724
725 Log(("VBoxGuest::vbgdNtIOCtl: uCmd=%u, pDevExt=0x%p, pSession=0x%p\n",
726 uCmd, pDevExt, pSession));
727
728 /* We don't have a session associated with the file object? So this seems
729 * to be a kernel call then. */
730 /** @todo r=bird: What on earth is this supposed to be? Each kernel session
731 * shall have its own context of course, no hacks, pleeease. */
732 if (pSession == NULL)
733 {
734 Log(("VBoxGuest::vbgdNtIOCtl: Using kernel session data ...\n"));
735 pSession = pDevExt->pKernelSession;
736 }
737
738 /*
739 * First process Windows specific stuff which cannot be handled
740 * by the common code used on all other platforms. In the default case
741 * we then finally handle the common cases.
742 */
743 switch (uCmd)
744 {
745#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
746 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
747 {
748 LogRel(("VBoxGuest::vbgdNtIOCtl: ENABLE_VRDP_SESSION: Currently: %sabled\n",
749 pDevExt->fVRDPEnabled? "en": "dis"));
750 if (!pDevExt->fVRDPEnabled)
751 {
752 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
753
754 pDevExt->fVRDPEnabled = true;
755 LogRel(("VBoxGuest::vbgdNtIOCtl: ENABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
756 pSharedUserData->ActiveConsoleId));
757 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
758 pSharedUserData->ActiveConsoleId = 2;
759 }
760 break;
761 }
762
763 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
764 {
765 LogRel(("VBoxGuest::vbgdNtIOCtl: DISABLE_VRDP_SESSION: Currently: %sabled\n",
766 pDevExt->fVRDPEnabled? "en": "dis"));
767 if (pDevExt->fVRDPEnabled)
768 {
769 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
770
771 pDevExt->fVRDPEnabled = false;
772 Log(("VBoxGuest::vbgdNtIOCtl: DISABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
773 pSharedUserData->ActiveConsoleId));
774 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
775 pDevExt->ulOldActiveConsoleId = 0;
776 }
777 break;
778 }
779#else
780 /* Add at least one (bogus) fall through case to shut up MSVC! */
781 case 0:
782#endif
783 default:
784 {
785 /*
786 * Process the common IOCtls.
787 */
788 size_t cbDataReturned;
789 int vrc = VBoxGuestCommonIOCtl(uCmd, &pDevExt->Core, pSession, pBuf, cbData, &cbDataReturned);
790
791 Log(("VBoxGuest::vbgdNtGuestDeviceControl: rc=%Rrc, pBuf=0x%p, cbData=%u, cbDataReturned=%u\n",
792 vrc, pBuf, cbData, cbDataReturned));
793
794 if (RT_SUCCESS(vrc))
795 {
796 if (RT_UNLIKELY(cbDataReturned > cbData))
797 {
798 Log(("VBoxGuest::vbgdNtGuestDeviceControl: Too much output data %u - expected %u!\n", cbDataReturned, cbData));
799 cbDataReturned = cbData;
800 Status = STATUS_BUFFER_TOO_SMALL;
801 }
802 if (cbDataReturned > 0)
803 cbOut = cbDataReturned;
804 }
805 else
806 {
807 if ( vrc == VERR_NOT_SUPPORTED
808 || vrc == VERR_INVALID_PARAMETER)
809 Status = STATUS_INVALID_PARAMETER;
810 else if (vrc == VERR_OUT_OF_RANGE)
811 Status = STATUS_INVALID_BUFFER_SIZE;
812 else
813 Status = STATUS_UNSUCCESSFUL;
814 }
815 break;
816 }
817 }
818
819 pIrp->IoStatus.Status = Status;
820 pIrp->IoStatus.Information = cbOut;
821
822 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
823
824 //Log(("VBoxGuest::vbgdNtGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
825 return Status;
826}
827
828/**
829 * Internal Device I/O Control entry point.
830 *
831 * @param pDevObj Device object.
832 * @param pIrp Request packet.
833 */
834static NTSTATUS vbgdNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
835{
836 NTSTATUS Status = STATUS_SUCCESS;
837 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
838 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
839 unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode;
840 bool fProcessed = false;
841 unsigned Info = 0;
842
843 /*
844 * Override common behavior of some operations.
845 */
846 /** @todo r=bird: Better to add dedicated worker functions for this! */
847 switch (uCmd)
848 {
849 case VBOXGUEST_IOCTL_SET_MOUSE_NOTIFY_CALLBACK:
850 {
851 PVOID pvBuf = pStack->Parameters.Others.Argument1;
852 size_t cbData = (size_t)pStack->Parameters.Others.Argument2;
853 fProcessed = true;
854 if (cbData != sizeof(VBoxGuestMouseSetNotifyCallback))
855 {
856 AssertFailed();
857 Status = STATUS_INVALID_PARAMETER;
858 break;
859 }
860
861 VBoxGuestMouseSetNotifyCallback *pInfo = (VBoxGuestMouseSetNotifyCallback*)pvBuf;
862
863 /* we need a lock here to avoid concurrency with the set event functionality */
864 KIRQL OldIrql;
865 KeAcquireSpinLock(&pDevExt->MouseEventAccessLock, &OldIrql);
866 pDevExt->Core.MouseNotifyCallback = *pInfo;
867 KeReleaseSpinLock(&pDevExt->MouseEventAccessLock, OldIrql);
868
869 Status = STATUS_SUCCESS;
870 break;
871 }
872
873 default:
874 break;
875 }
876 if (fProcessed)
877 {
878 pIrp->IoStatus.Status = Status;
879 pIrp->IoStatus.Information = Info;
880
881 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
882 return Status;
883 }
884
885 /*
886 * No override, go to common code.
887 */
888 return vbgdNtIOCtl(pDevObj, pIrp);
889}
890
891
892/**
893 * IRP_MJ_SYSTEM_CONTROL handler.
894 *
895 * @returns NT status code
896 * @param pDevObj Device object.
897 * @param pIrp IRP.
898 */
899NTSTATUS vbgdNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
900{
901 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
902
903 Log(("VBoxGuest::vbgdNtGuestSystemControl\n"));
904
905 /* Always pass it on to the next driver. */
906 IoSkipCurrentIrpStackLocation(pIrp);
907
908 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
909}
910
911
912/**
913 * IRP_MJ_SHUTDOWN handler.
914 *
915 * @returns NT status code
916 * @param pDevObj Device object.
917 * @param pIrp IRP.
918 */
919NTSTATUS vbgdNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
920{
921 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
922
923 Log(("VBoxGuest::vbgdNtGuestShutdown\n"));
924
925 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
926 if (pReq)
927 {
928 pReq->header.requestType = VMMDevReq_SetPowerStatus;
929 pReq->powerState = VMMDevPowerState_PowerOff;
930
931 int rc = VbglGRPerform(&pReq->header);
932 if (RT_FAILURE(rc))
933 {
934 Log(("VBoxGuest::vbgdNtGuestShutdown: Error performing request to VMMDev! "
935 "rc = %Rrc\n", rc));
936 }
937 }
938 return STATUS_SUCCESS;
939}
940
941
942/**
943 * Stub function for functions we don't implemented.
944 *
945 * @returns STATUS_NOT_SUPPORTED
946 * @param pDevObj Device object.
947 * @param pIrp IRP.
948 */
949NTSTATUS vbgdNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
950{
951 Log(("VBoxGuest::vbgdNtGuestNotSupportedStub\n"));
952
953 pIrp->IoStatus.Information = 0;
954 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
955 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
956
957 return STATUS_NOT_SUPPORTED;
958}
959
960
961/**
962 * DPC handler.
963 *
964 * @param pDPC DPC descriptor.
965 * @param pDevObj Device object.
966 * @param pIrp Interrupt request packet.
967 * @param pContext Context specific pointer.
968 */
969void vbgdNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
970{
971 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
972 Log(("VBoxGuest::vbgdNtGuestDpcHandler: pDevExt=0x%p\n", pDevExt));
973
974 /* test & reset the counter */
975 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
976 {
977 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
978 * i.e. to prevent the event from destroyed while we're using it */
979 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
980 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessLock);
981
982 if (pDevExt->Core.MouseNotifyCallback.pfnNotify)
983 pDevExt->Core.MouseNotifyCallback.pfnNotify(pDevExt->Core.MouseNotifyCallback.pvUser);
984
985 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessLock);
986 }
987
988 /* Process the wake-up list we were asked by the scheduling a DPC
989 * in vbgdNtIsrHandler(). */
990 VBoxGuestWaitDoWakeUps(&pDevExt->Core);
991}
992
993
994/**
995 * ISR handler.
996 *
997 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
998 * @param pInterrupt Interrupt that was triggered.
999 * @param pServiceContext Context specific pointer.
1000 */
1001BOOLEAN vbgdNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
1002{
1003 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
1004 if (pDevExt == NULL)
1005 return FALSE;
1006
1007 /*Log(("VBoxGuest::vbgdNtGuestIsrHandler: pDevExt = 0x%p, pVMMDevMemory = 0x%p\n",
1008 pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
1009
1010 /* Enter the common ISR routine and do the actual work. */
1011 BOOLEAN fIRQTaken = VBoxGuestCommonISR(&pDevExt->Core);
1012
1013 /* If we need to wake up some events we do that in a DPC to make
1014 * sure we're called at the right IRQL. */
1015 if (fIRQTaken)
1016 {
1017 Log(("VBoxGuest::vbgdNtGuestIsrHandler: IRQ was taken! pInterrupt = 0x%p, pDevExt = 0x%p\n",
1018 pInterrupt, pDevExt));
1019 if (ASMAtomicUoReadU32(&pDevExt->Core.u32MousePosChangedSeq) || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
1020 {
1021 Log(("VBoxGuest::vbgdNtGuestIsrHandler: Requesting DPC ...\n"));
1022 IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL);
1023 }
1024 }
1025 return fIRQTaken;
1026}
1027
1028
1029/**
1030 * Overridden routine for mouse polling events.
1031 *
1032 * @param pDevExt Device extension structure.
1033 */
1034void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1035{
1036 NOREF(pDevExt);
1037 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
1038 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
1039 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
1040}
1041
1042
1043/**
1044 * Queries (gets) a DWORD value from the registry.
1045 *
1046 * @return NTSTATUS
1047 * @param ulRoot Relative path root. See RTL_REGISTRY_SERVICES or RTL_REGISTRY_ABSOLUTE.
1048 * @param pwszPath Path inside path root.
1049 * @param pwszName Actual value name to look up.
1050 * @param puValue On input this can specify the default value (if RTL_REGISTRY_OPTIONAL is
1051 * not specified in ulRoot), on output this will retrieve the looked up
1052 * registry value if found.
1053 */
1054NTSTATUS vbgdNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue)
1055{
1056 if (!pwszPath || !pwszName || !puValue)
1057 return STATUS_INVALID_PARAMETER;
1058
1059 ULONG ulDefault = *puValue;
1060
1061 RTL_QUERY_REGISTRY_TABLE tblQuery[2];
1062 RtlZeroMemory(tblQuery, sizeof(tblQuery));
1063 /** @todo Add RTL_QUERY_REGISTRY_TYPECHECK! */
1064 tblQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1065 tblQuery[0].Name = pwszName;
1066 tblQuery[0].EntryContext = puValue;
1067 tblQuery[0].DefaultType = REG_DWORD;
1068 tblQuery[0].DefaultData = &ulDefault;
1069 tblQuery[0].DefaultLength = sizeof(ULONG);
1070
1071 return RtlQueryRegistryValues(ulRoot,
1072 pwszPath,
1073 &tblQuery[0],
1074 NULL /* Context */,
1075 NULL /* Environment */);
1076}
1077
1078
1079/**
1080 * Helper to scan the PCI resource list and remember stuff.
1081 *
1082 * @param pResList Resource list
1083 * @param pDevExt Device extension
1084 */
1085NTSTATUS vbgdNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt)
1086{
1087 /* Enumerate the resource list. */
1088 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Found %d resources\n",
1089 pResList->List->PartialResourceList.Count));
1090
1091 NTSTATUS rc = STATUS_SUCCESS;
1092 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
1093 ULONG rangeCount = 0;
1094 ULONG cMMIORange = 0;
1095 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->pciBaseAddress;
1096 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
1097 {
1098 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
1099 switch (pPartialData->Type)
1100 {
1101 case CmResourceTypePort:
1102 {
1103 /* Overflow protection. */
1104 if (rangeCount < PCI_TYPE0_ADDRESSES)
1105 {
1106 Log(("VBoxGuest::vbgdNtScanPCIResourceList: I/O range: Base = %08x:%08x, Length = %08x\n",
1107 pPartialData->u.Port.Start.HighPart,
1108 pPartialData->u.Port.Start.LowPart,
1109 pPartialData->u.Port.Length));
1110
1111 /* Save the IO port base. */
1112 /** @todo Not so good.
1113 * Update/bird: What is not so good? That we just consider the last range? */
1114 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
1115
1116 /* Save resource information. */
1117 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
1118 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
1119 pBaseAddress->RangeInMemory = FALSE;
1120 pBaseAddress->ResourceMapped = FALSE;
1121
1122 Log(("VBoxGuest::vbgdNtScanPCIResourceList: I/O range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1123 pPartialData->u.Port.Start.HighPart,
1124 pPartialData->u.Port.Start.LowPart,
1125 pPartialData->u.Port.Length));
1126
1127 /* Next item ... */
1128 rangeCount++; pBaseAddress++;
1129 }
1130 break;
1131 }
1132
1133 case CmResourceTypeInterrupt:
1134 {
1135 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Interrupt: Level = %x, Vector = %x, Mode = %x\n",
1136 pPartialData->u.Interrupt.Level,
1137 pPartialData->u.Interrupt.Vector,
1138 pPartialData->Flags));
1139
1140 /* Save information. */
1141 pDevExt->interruptLevel = pPartialData->u.Interrupt.Level;
1142 pDevExt->interruptVector = pPartialData->u.Interrupt.Vector;
1143 pDevExt->interruptAffinity = pPartialData->u.Interrupt.Affinity;
1144
1145 /* Check interrupt mode. */
1146 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
1147 pDevExt->interruptMode = Latched;
1148 else
1149 pDevExt->interruptMode = LevelSensitive;
1150 break;
1151 }
1152
1153 case CmResourceTypeMemory:
1154 {
1155 /* Overflow protection. */
1156 if (rangeCount < PCI_TYPE0_ADDRESSES)
1157 {
1158 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Memory range: Base = %08x:%08x, Length = %08x\n",
1159 pPartialData->u.Memory.Start.HighPart,
1160 pPartialData->u.Memory.Start.LowPart,
1161 pPartialData->u.Memory.Length));
1162
1163 /* We only care about read/write memory. */
1164 /** @todo Reconsider memory type. */
1165 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
1166 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
1167 {
1168 /* Save physical MMIO base + length for VMMDev. */
1169 pDevExt->vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start;
1170 pDevExt->vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length;
1171
1172 /* Save resource information. */
1173 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
1174 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
1175 pBaseAddress->RangeInMemory = TRUE;
1176 pBaseAddress->ResourceMapped = FALSE;
1177
1178 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1179 pPartialData->u.Memory.Start.HighPart,
1180 pPartialData->u.Memory.Start.LowPart,
1181 pPartialData->u.Memory.Length));
1182
1183 /* Next item ... */
1184 rangeCount++; pBaseAddress++; cMMIORange++;
1185 }
1186 else
1187 {
1188 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Ignoring memory: Flags = %08x\n",
1189 pPartialData->Flags));
1190 }
1191 }
1192 break;
1193 }
1194
1195 default:
1196 {
1197 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Unhandled resource found, type = %d\n", pPartialData->Type));
1198 break;
1199 }
1200 }
1201 }
1202
1203 /* Memorize the number of resources found. */
1204 pDevExt->pciAddressCount = rangeCount;
1205 return rc;
1206}
1207
1208
1209/**
1210 * Maps the I/O space from VMMDev to virtual kernel address space.
1211 *
1212 * @return NTSTATUS
1213 *
1214 * @param pDevExt The device extension.
1215 * @param PhysAddr Physical address to map.
1216 * @param cbToMap Number of bytes to map.
1217 * @param ppvMMIOBase Pointer of mapped I/O base.
1218 * @param pcbMMIO Length of mapped I/O base.
1219 */
1220NTSTATUS vbgdNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
1221 void **ppvMMIOBase, uint32_t *pcbMMIO)
1222{
1223 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
1224 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
1225 /* pcbMMIO is optional. */
1226
1227 NTSTATUS rc = STATUS_SUCCESS;
1228 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
1229 {
1230 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
1231 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: pVMMDevMemory = 0x%x\n", pVMMDevMemory));
1232 if (pVMMDevMemory)
1233 {
1234 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: VMMDevMemory: Version = 0x%x, Size = %d\n",
1235 pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
1236
1237 /* Check version of the structure; do we have the right memory version? */
1238 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
1239 {
1240 /* Save results. */
1241 *ppvMMIOBase = pVMMDevMemory;
1242 if (pcbMMIO) /* Optional. */
1243 *pcbMMIO = pVMMDevMemory->u32Size;
1244
1245 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n",
1246 *ppvMMIOBase));
1247 }
1248 else
1249 {
1250 /* Not our version, refuse operation and unmap the memory. */
1251 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
1252 vbgdNtUnmapVMMDevMemory(pDevExt);
1253 rc = STATUS_UNSUCCESSFUL;
1254 }
1255 }
1256 else
1257 rc = STATUS_UNSUCCESSFUL;
1258 }
1259 return rc;
1260}
1261
1262
1263/**
1264 * Unmaps the VMMDev I/O range from kernel space.
1265 *
1266 * @param pDevExt The device extension.
1267 */
1268void vbgdNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
1269{
1270 Log(("VBoxGuest::vbgdNtUnmapVMMDevMemory: pVMMDevMemory = 0x%x\n", pDevExt->Core.pVMMDevMemory));
1271 if (pDevExt->Core.pVMMDevMemory)
1272 {
1273 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->vmmDevPhysMemoryLength);
1274 pDevExt->Core.pVMMDevMemory = NULL;
1275 }
1276
1277 pDevExt->vmmDevPhysMemoryAddress.QuadPart = 0;
1278 pDevExt->vmmDevPhysMemoryLength = 0;
1279}
1280
1281
1282VBOXOSTYPE vbgdNtVersionToOSType(VBGDNTVER enmNtVer)
1283{
1284 VBOXOSTYPE enmOsType;
1285 switch (enmNtVer)
1286 {
1287 case VBGDNTVER_WINNT4:
1288 enmOsType = VBOXOSTYPE_WinNT4;
1289 break;
1290
1291 case VBGDNTVER_WIN2K:
1292 enmOsType = VBOXOSTYPE_Win2k;
1293 break;
1294
1295 case VBGDNTVER_WINXP:
1296#if ARCH_BITS == 64
1297 enmOsType = VBOXOSTYPE_WinXP_x64;
1298#else
1299 enmOsType = VBOXOSTYPE_WinXP;
1300#endif
1301 break;
1302
1303 case VBGDNTVER_WIN2K3:
1304#if ARCH_BITS == 64
1305 enmOsType = VBOXOSTYPE_Win2k3_x64;
1306#else
1307 enmOsType = VBOXOSTYPE_Win2k3;
1308#endif
1309 break;
1310
1311 case VBGDNTVER_WINVISTA:
1312#if ARCH_BITS == 64
1313 enmOsType = VBOXOSTYPE_WinVista_x64;
1314#else
1315 enmOsType = VBOXOSTYPE_WinVista;
1316#endif
1317 break;
1318
1319 case VBGDNTVER_WIN7:
1320#if ARCH_BITS == 64
1321 enmOsType = VBOXOSTYPE_Win7_x64;
1322#else
1323 enmOsType = VBOXOSTYPE_Win7;
1324#endif
1325 break;
1326
1327 case VBGDNTVER_WIN8:
1328#if ARCH_BITS == 64
1329 enmOsType = VBOXOSTYPE_Win8_x64;
1330#else
1331 enmOsType = VBOXOSTYPE_Win8;
1332#endif
1333 break;
1334
1335 default:
1336 /* We don't know, therefore NT family. */
1337 enmOsType = VBOXOSTYPE_WinNT;
1338 break;
1339 }
1340 return enmOsType;
1341}
1342
1343#ifdef DEBUG
1344
1345/**
1346 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
1347 */
1348static uint32_t vboxugestwinAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
1349{
1350 AssertPtrReturn(pu32Bits, 0);
1351 LogFlowFunc(("*pu32Bits=0x%x, u32Mask=0x%x\n", *(uint32_t *)pu32Bits, u32Mask));
1352 uint32_t u32Result = 0;
1353 uint32_t u32WorkingMask = u32Mask;
1354 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1355
1356 while (iBitOffset > 0)
1357 {
1358 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
1359 if (fSet)
1360 u32Result |= 1 << (iBitOffset - 1);
1361 u32WorkingMask &= ~(1 << (iBitOffset - 1));
1362 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1363 }
1364 LogFlowFunc(("Returning 0x%x\n", u32Result));
1365 return u32Result;
1366}
1367
1368
1369static void vbgdNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
1370{
1371 ULONG u32Bits2 = u32Bits;
1372 uint32_t u32Result = vboxugestwinAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1373 if ( u32Result != u32Exp
1374 || (u32Bits2 & u32Mask)
1375 || (u32Bits2 & u32Result)
1376 || ((u32Bits2 | u32Result) != u32Bits)
1377 )
1378 AssertLogRelMsgFailed(("%s: TEST FAILED: u32Mask=0x%x, u32Bits (before)=0x%x, u32Bits (after)=0x%x, u32Result=0x%x, u32Exp=ox%x\n",
1379 __PRETTY_FUNCTION__, u32Mask, u32Bits, u32Bits2,
1380 u32Result));
1381}
1382
1383
1384static void vbgdNtDoTests(void)
1385{
1386 vbgdNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1387 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
1388 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1389 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1390 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1391 vbgdNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1392}
1393
1394#endif /* DEBUG */
1395
1396#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1397
1398/*
1399 * DPC latency checker.
1400 */
1401
1402/**
1403 * One DPC latency sample.
1404 */
1405typedef struct DPCSAMPLE
1406{
1407 LARGE_INTEGER PerfDelta;
1408 LARGE_INTEGER PerfCounter;
1409 LARGE_INTEGER PerfFrequency;
1410 uint64_t u64TSC;
1411} DPCSAMPLE;
1412AssertCompileSize(DPCSAMPLE, 4*8);
1413
1414/**
1415 * The DPC latency measurement workset.
1416 */
1417typedef struct DPCDATA
1418{
1419 KDPC Dpc;
1420 KTIMER Timer;
1421 KSPIN_LOCK SpinLock;
1422
1423 ULONG ulTimerRes;
1424
1425 bool volatile fFinished;
1426
1427 /** The timer interval (relative). */
1428 LARGE_INTEGER DueTime;
1429
1430 LARGE_INTEGER PerfCounterPrev;
1431
1432 /** Align the sample array on a 64 byte boundrary just for the off chance
1433 * that we'll get cache line aligned memory backing this structure. */
1434 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
1435
1436 int cSamples;
1437 DPCSAMPLE aSamples[8192];
1438} DPCDATA;
1439
1440AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
1441
1442# define VBOXGUEST_DPC_TAG 'DPCS'
1443
1444
1445/**
1446 * DPC callback routine for the DPC latency measurement code.
1447 *
1448 * @param pDpc The DPC, not used.
1449 * @param pvDeferredContext Pointer to the DPCDATA.
1450 * @param SystemArgument1 System use, ignored.
1451 * @param SystemArgument2 System use, ignored.
1452 */
1453static VOID vbgdNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
1454{
1455 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
1456
1457 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
1458
1459 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
1460 pData->fFinished = true;
1461 else
1462 {
1463 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
1464
1465 pSample->u64TSC = ASMReadTSC();
1466 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
1467 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
1468
1469 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
1470
1471 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1472 }
1473
1474 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
1475}
1476
1477
1478/**
1479 * Handles the DPC latency checker request.
1480 *
1481 * @returns VBox status code.
1482 */
1483int VbgdNtIOCtl_DpcLatencyChecker(void)
1484{
1485 /*
1486 * Allocate a block of non paged memory for samples and related data.
1487 */
1488 DPCDATA *pData = (DPCDATA *)ExAllocatePoolWithTag(NonPagedPool, sizeof(DPCDATA), VBOXGUEST_DPC_TAG);
1489 if (!pData)
1490 {
1491 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
1492 return VERR_NO_MEMORY;
1493 }
1494
1495 /*
1496 * Initialize the data.
1497 */
1498 KeInitializeDpc(&pData->Dpc, vbgdNtDpcLatencyCallback, pData);
1499 KeInitializeTimer(&pData->Timer);
1500 KeInitializeSpinLock(&pData->SpinLock);
1501
1502 pData->fFinished = false;
1503 pData->cSamples = 0;
1504 pData->PerfCounterPrev.QuadPart = 0;
1505
1506 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
1507 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
1508
1509 /*
1510 * Start the DPC measurements and wait for a full set.
1511 */
1512 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1513
1514 while (!pData->fFinished)
1515 {
1516 LARGE_INTEGER Interval;
1517 Interval.QuadPart = -100 * 1000 * 10;
1518 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
1519 }
1520
1521 ExSetTimerResolution(0, 0);
1522
1523 /*
1524 * Log everything to the host.
1525 */
1526 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
1527 for (int i = 0; i < pData->cSamples; i++)
1528 {
1529 DPCSAMPLE *pSample = &pData->aSamples[i];
1530
1531 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
1532 i,
1533 pSample->PerfDelta.QuadPart,
1534 pSample->PerfCounter.QuadPart,
1535 pSample->PerfFrequency.QuadPart,
1536 pSample->u64TSC);
1537 }
1538
1539 ExFreePoolWithTag(pData, VBOXGUEST_DPC_TAG);
1540 return VINF_SUCCESS;
1541}
1542
1543#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
1544
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