VirtualBox

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

Last change on this file since 53349 was 53008, checked in by vboxsync, 10 years ago

include,Additions: Windows 10 tweaks.

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

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