VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxGuest/VBoxGuestPnP.cpp@ 4071

Last change on this file since 4071 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.1 KB
Line 
1/** @file
2 *
3 * VBoxGuest -- VirtualBox Win32 guest support driver PnP code
4 *
5 * Copyright (C) 2006-2007 innotek GmbH
6 *
7 * This file is part of VirtualBox Open Source Edition (OSE), as
8 * available from http://www.virtualbox.org. This file is free software;
9 * you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License as published by the Free Software Foundation,
11 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
12 * distribution. VirtualBox OSE is distributed in the hope that it will
13 * be useful, but WITHOUT ANY WARRANTY of any kind.
14 */
15
16// enable backdoor logging
17//#define LOG_ENABLED
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "VBoxGuestPnP.h"
23#include "Helper.h"
24#include <VBox/err.h>
25
26#include <VBox/VBoxGuestLib.h>
27
28/*******************************************************************************
29* Defined Constants And Macros *
30*******************************************************************************/
31
32
33extern "C"
34{
35static NTSTATUS sendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict);
36static NTSTATUS pnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT event);
37static VOID showDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList);
38}
39
40#ifdef ALLOC_PRAGMA
41#pragma alloc_text (PAGE, VBoxGuestPnP)
42#pragma alloc_text (PAGE, VBoxGuestPower)
43#pragma alloc_text (PAGE, sendIrpSynchronously)
44#pragma alloc_text (PAGE, showDeviceResources)
45#endif
46
47/* reenable logging, this was #undef'ed on iprt/log.h for RING0 */
48#define LOG_ENABLED
49
50/*******************************************************************************
51* Internal Functions *
52*******************************************************************************/
53
54/**
55 * Irp completion routine for PnP Irps we send.
56 *
57 * @param pDevObj Device object.
58 * @param pIrp Request packet.
59 * @param event Semaphore.
60 * @return NT status code
61 */
62static NTSTATUS pnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT event)
63{
64 KeSetEvent(event, 0, FALSE);
65 return STATUS_MORE_PROCESSING_REQUIRED;
66}
67
68/**
69 * Helper to send a PnP IRP and wait until it's done.
70 *
71 * @param pDevObj Device object.
72 * @param pIrp Request packet.
73 * @param fStrict When set, returns an error if the IRP gives an error.
74 * @return NT status code
75 */
76static NTSTATUS sendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict)
77{
78 KEVENT event;
79
80 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
81
82 IoCopyCurrentIrpStackLocationToNext(pIrp);
83 IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)pnpIrpComplete, &event, TRUE, TRUE, TRUE);
84
85 NTSTATUS rc = IoCallDriver(pDevObj, pIrp);
86
87 if (rc == STATUS_PENDING)
88 {
89 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
90 rc = pIrp->IoStatus.Status;
91 }
92
93 if (!fStrict
94 && (rc == STATUS_NOT_SUPPORTED || rc == STATUS_INVALID_DEVICE_REQUEST))
95 {
96 rc = STATUS_SUCCESS;
97 }
98
99 dprintf(("VBoxGuest::sendIrpSynchronously: returning 0x%x\n", rc));
100
101 return rc;
102}
103
104
105/**
106 * PnP Request handler.
107 *
108 * @param pDevObj Device object.
109 * @param pIrp Request packet.
110 */
111NTSTATUS VBoxGuestPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp)
112{
113 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
114 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
115 NTSTATUS rc = STATUS_SUCCESS;
116
117#ifdef LOG_ENABLED
118 static char* fcnname[] =
119 {
120 "IRP_MN_START_DEVICE",
121 "IRP_MN_QUERY_REMOVE_DEVICE",
122 "IRP_MN_REMOVE_DEVICE",
123 "IRP_MN_CANCEL_REMOVE_DEVICE",
124 "IRP_MN_STOP_DEVICE",
125 "IRP_MN_QUERY_STOP_DEVICE",
126 "IRP_MN_CANCEL_STOP_DEVICE",
127 "IRP_MN_QUERY_DEVICE_RELATIONS",
128 "IRP_MN_QUERY_INTERFACE",
129 "IRP_MN_QUERY_CAPABILITIES",
130 "IRP_MN_QUERY_RESOURCES",
131 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
132 "IRP_MN_QUERY_DEVICE_TEXT",
133 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
134 "",
135 "IRP_MN_READ_CONFIG",
136 "IRP_MN_WRITE_CONFIG",
137 "IRP_MN_EJECT",
138 "IRP_MN_SET_LOCK",
139 "IRP_MN_QUERY_ID",
140 "IRP_MN_QUERY_PNP_DEVICE_STATE",
141 "IRP_MN_QUERY_BUS_INFORMATION",
142 "IRP_MN_DEVICE_USAGE_NOTIFICATION",
143 "IRP_MN_SURPRISE_REMOVAL",
144 };
145 dprintf(("VBoxGuest::VBoxGuestPnp: MinorFunction: %s\n", pStack->MinorFunction < (sizeof(fcnname)/sizeof(fcnname[0])) ? fcnname[pStack->MinorFunction] : "unknown"));
146#endif
147 switch (pStack->MinorFunction)
148 {
149 case IRP_MN_START_DEVICE:
150 {
151 rc = sendIrpSynchronously(pDevExt->nextLowerDriver, pIrp, TRUE);
152
153 if (NT_SUCCESS(rc) && NT_SUCCESS(pIrp->IoStatus.Status))
154 {
155 dprintf(("VBoxGuest::START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n", pStack->Parameters.StartDevice.AllocatedResources));
156
157 if (!pStack->Parameters.StartDevice.AllocatedResources)
158 {
159 dprintf(("VBoxGuest::START_DEVICE: no resources, pDevExt = %p, nextLowerDriver = %p!!!\n", pDevExt, pDevExt? pDevExt->nextLowerDriver: NULL));
160 rc = STATUS_UNSUCCESSFUL;
161 }
162 else
163 {
164 showDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
165
166 VBoxScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated,
167 pDevExt);
168
169 /** @todo cleanup and merging codepath with NT */
170 int rcVBox;
171 rcVBox = VbglInit (pDevExt->startPortAddress, pDevExt->pVMMDevMemory);
172 if (!VBOX_SUCCESS(rcVBox))
173 {
174 dprintf(("VBoxGuest::START_DEVICE: VbglInit failed. rcVBox = %d\n", rcVBox));
175 rc = STATUS_UNSUCCESSFUL;
176 }
177
178 if (NT_SUCCESS(rc))
179 {
180 rcVBox = VbglGRAlloc ((VMMDevRequestHeader **)&pDevExt->irqAckEvents, sizeof (VMMDevEvents), VMMDevReq_AcknowledgeEvents);
181 if (!VBOX_SUCCESS(rc))
182 {
183 dprintf(("VBoxGuest::START_DEVICE: VbglAlloc failed. rcVBox = %d\n", rcVBox));
184 rc = STATUS_UNSUCCESSFUL;
185 }
186 }
187
188 if (NT_SUCCESS(rc))
189 {
190 // Map physical address of VMMDev memory
191 rc = hlpVBoxMapVMMDevMemory(pDevExt);
192 if (!NT_SUCCESS(rc))
193 {
194 dprintf(("VBoxGuest::START_DEVICE: can't map physical memory, rc = %d\n", rc));
195 }
196 }
197
198 if (NT_SUCCESS(rc))
199 {
200 rc = hlpVBoxReportGuestInfo (pDevExt);
201 if (!NT_SUCCESS(rc))
202 {
203 dprintf(("VBoxGuest::START_DEVICE: could not report information to host, rc = %d\n", rc));
204 }
205 }
206
207 if (NT_SUCCESS(rc))
208 {
209 // register DPC and ISR
210 dprintf(("VBoxGuest::VBoxGuestPnp: initializing DPC...\n"));
211 IoInitializeDpcRequest(pDevExt->deviceObject, VBoxGuestDpcHandler);
212
213 rc = IoConnectInterrupt(&pDevExt->interruptObject, // out: interrupt object
214 (PKSERVICE_ROUTINE)VBoxGuestIsrHandler, // ISR
215 pDevExt, // context
216 NULL, // optional spinlock
217 pDevExt->interruptVector, // interrupt vector
218 (KIRQL)pDevExt->interruptLevel, // interrupt level
219 (KIRQL)pDevExt->interruptLevel, // interrupt level
220 pDevExt->interruptMode, // LevelSensitive or Latched
221 TRUE, // shareable interrupt
222 pDevExt->interruptAffinity, // CPU affinity
223 FALSE); // don't save FPU stack
224 if (!NT_SUCCESS(rc))
225 {
226 dprintf(("VBoxGuest::VBoxGuestPnp: could not connect interrupt: rc = 0x%x\n", rc));
227 }
228 }
229 }
230 }
231 if (NT_SUCCESS(rc))
232 {
233 createThreads(pDevExt);
234
235 // initialize the event notification semaphore
236 KeInitializeEvent(&pDevExt->keventNotification, NotificationEvent, FALSE);
237
238 // ready to rumble!
239 dprintf(("VBoxGuest::VBoxGuestPnp: device is ready!\n"));
240 pDevExt->devState = WORKING;
241 } else
242 {
243 dprintf(("VBoxGuest::VBoxGuestPnp: error: rc = 0x%x\n", rc));
244
245 // need to unmap memory in case of errors
246 hlpVBoxUnmapVMMDevMemory (pDevExt);
247 }
248 pIrp->IoStatus.Status = rc;
249 pIrp->IoStatus.Information = 0;
250 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
251 break;
252 }
253
254 case IRP_MN_QUERY_REMOVE_DEVICE:
255 {
256#ifdef VBOX_REBOOT_ON_UNINSTALL
257 /* The device can not be removed without a reboot. */
258 if (pDevExt->devState == WORKING)
259 {
260 pDevExt->devState = PENDINGREMOVE;
261 }
262 pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
263 pIrp->IoStatus.Information = 0;
264 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
265 rc = STATUS_UNSUCCESSFUL;
266
267 dprintf(("VBoxGuest::VBoxGuestPnp: refuse with rc = %p\n", pIrp->IoStatus.Status));
268#else
269 pIrp->IoStatus.Status = STATUS_SUCCESS;
270 if (pDevExt->devState == WORKING)
271 {
272 pDevExt->devState = PENDINGREMOVE;
273 }
274 IoSkipCurrentIrpStackLocation(pIrp);
275 rc = IoCallDriver(pDevExt->nextLowerDriver, pIrp);
276#endif /* VBOX_REBOOT_ON_UNINSTALL */
277 break;
278 }
279
280 case IRP_MN_REMOVE_DEVICE:
281 {
282 /* @todo merge Remove and Stop, make a helper for common actions */
283 pIrp->IoStatus.Status = STATUS_SUCCESS;
284
285 unreserveHypervisorMemory(pDevExt);
286
287 if (pDevExt->workerThread)
288 {
289 dprintf(("VBoxGuest::VBoxGuestPnp: waiting for the worker thread to terminate...\n"));
290 pDevExt->stopThread = TRUE;
291 KeSetEvent(&pDevExt->workerThreadRequest, 0, FALSE);
292 KeWaitForSingleObject(pDevExt->workerThread,
293 Executive, KernelMode, FALSE, NULL);
294 dprintf(("VBoxGuest::VBoxGuestPnp: returned from KeWaitForSingleObject for worker thread\n"));
295 }
296
297 if (pDevExt->idleThread)
298 {
299 dprintf(("VBoxGuest::VBoxGuestPnp: waiting for the idle thread to terminate...\n"));
300 pDevExt->stopThread = TRUE;
301 KeWaitForSingleObject(pDevExt->idleThread,
302 Executive, KernelMode, FALSE, NULL);
303 dprintf(("VBoxGuest::VBoxGuestPnp: returned from KeWaitForSingleObject for idle thread\n"));
304 }
305
306 VbglTerminate ();
307
308 // according to MSDN we have to unmap previously mapped memory
309 hlpVBoxUnmapVMMDevMemory (pDevExt);
310
311 if (pDevExt->nextLowerDriver != NULL)
312 {
313 IoDetachDevice(pDevExt->nextLowerDriver);
314 }
315 UNICODE_STRING win32Name;
316 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
317 IoDeleteSymbolicLink(&win32Name);
318 IoDeleteDevice(pDevObj);
319 pDevExt->devState = REMOVED;
320 IoSkipCurrentIrpStackLocation(pIrp);
321 rc = IoCallDriver(pDevExt->nextLowerDriver, pIrp);
322 break;
323 }
324
325 case IRP_MN_QUERY_STOP_DEVICE:
326 {
327#ifdef VBOX_REBOOT_ON_UNINSTALL
328 dprintf(("VBoxGuest::VBoxGuestPnp: refuse\n"));
329 /* The device can not be stopped without a reboot. */
330 if (pDevExt->devState == WORKING)
331 {
332 pDevExt->devState = PENDINGSTOP;
333 }
334 pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
335 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
336 rc = STATUS_UNSUCCESSFUL;
337#else
338 pIrp->IoStatus.Status = STATUS_SUCCESS;
339 if (pDevExt->devState == WORKING)
340 {
341 pDevExt->devState = PENDINGSTOP;
342 }
343 IoSkipCurrentIrpStackLocation(pIrp);
344 rc = IoCallDriver(pDevExt->nextLowerDriver, pIrp);
345#endif /* VBOX_REBOOT_ON_UNINSTALL */
346 break;
347 }
348
349 case IRP_MN_STOP_DEVICE:
350 {
351 pIrp->IoStatus.Status = STATUS_SUCCESS;
352 if (pDevExt->devState == PENDINGSTOP)
353 {
354 VbglTerminate ();
355
356 // according to MSDN we have to unmap previously mapped memory
357 hlpVBoxUnmapVMMDevMemory (pDevExt);
358
359 pDevExt->devState = STOPPED;
360 dprintf(("VBoxGuest::VBoxGuestPnp: device has been disabled\n"));
361 } else
362 {
363 dprintf(("VBoxGuest::VBoxGuestPnp: devState not PENDINGSTOP but %d\n", pDevExt->devState));
364 }
365 IoSkipCurrentIrpStackLocation(pIrp);
366 rc = IoCallDriver(pDevExt->nextLowerDriver, pIrp);
367 break;
368 }
369
370 default:
371 {
372 IoSkipCurrentIrpStackLocation(pIrp);
373 rc = IoCallDriver(pDevExt->nextLowerDriver, pIrp);
374 }
375
376 }
377 return rc;
378}
379
380
381/**
382 * Debug helper to dump a device resource list.
383 *
384 * @param pResourceList list of device resources.
385 */
386static VOID showDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList)
387{
388#ifdef LOG_ENABLED
389 PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = pResourceList->PartialDescriptors;
390 ULONG nres = pResourceList->Count;
391 ULONG i;
392
393 for (i = 0; i < nres; ++i, ++resource)
394 {
395 ULONG type = resource->Type;
396
397 static char* name[] =
398 {
399 "CmResourceTypeNull",
400 "CmResourceTypePort",
401 "CmResourceTypeInterrupt",
402 "CmResourceTypeMemory",
403 "CmResourceTypeDma",
404 "CmResourceTypeDeviceSpecific",
405 "CmResourceTypeBusNumber",
406 "CmResourceTypeDevicePrivate",
407 "CmResourceTypeAssignedResource",
408 "CmResourceTypeSubAllocateFrom",
409 };
410
411 dprintf(("VBoxGuest::showDeviceResources: type %s", type < (sizeof(name)/sizeof(name[0])) ? name[type] : "unknown"));
412
413 switch (type)
414 {
415 case CmResourceTypePort:
416 case CmResourceTypeMemory:
417 dprintf(("VBoxGuest::showDeviceResources: start %8X%8.8lX length %X\n",
418 resource->u.Port.Start.HighPart, resource->u.Port.Start.LowPart,
419 resource->u.Port.Length));
420 break;
421
422 case CmResourceTypeInterrupt:
423 dprintf(("VBoxGuest::showDeviceResources: level %X, vector %X, affinity %X\n",
424 resource->u.Interrupt.Level, resource->u.Interrupt.Vector,
425 resource->u.Interrupt.Affinity));
426 break;
427
428 case CmResourceTypeDma:
429 dprintf(("VBoxGuest::showDeviceResources: channel %d, port %X\n",
430 resource->u.Dma.Channel, resource->u.Dma.Port));
431 break;
432
433 default:
434 dprintf(("\n"));
435 break;
436 }
437 }
438#endif
439}
440
441/**
442 * Handle the power completion event.
443 *
444 * @returns NT status code
445 * @param devObj targetted device object
446 * @param irp IO request packet
447 * @param context context value passed to IoSetCompletionRoutine in VBoxGuestPower
448 */
449NTSTATUS VBoxGuestPowerComplete(IN PDEVICE_OBJECT devObj,
450 IN PIRP irp, IN PVOID context)
451{
452 PIO_STACK_LOCATION irpSp;
453 PVBOXGUESTDEVEXT devExt = (PVBOXGUESTDEVEXT)context;
454
455 ASSERT(devExt);
456 ASSERT(devExt->signature == DEVICE_EXTENSION_SIGNATURE);
457
458 irpSp = IoGetCurrentIrpStackLocation(irp);
459 ASSERT(irpSp->MajorFunction == IRP_MJ_POWER);
460
461 if (NT_SUCCESS(irp->IoStatus.Status))
462 {
463 switch (irpSp->MinorFunction)
464 {
465 case IRP_MN_SET_POWER:
466
467 switch (irpSp->Parameters.Power.Type)
468 {
469 case DevicePowerState:
470 switch (irpSp->Parameters.Power.State.DeviceState)
471 {
472 case PowerDeviceD0:
473 break;
474 }
475 break;
476 }
477 break;
478 }
479 }
480
481 return STATUS_SUCCESS;
482}
483
484
485/**
486 * Handle the Power requests.
487 *
488 * @returns NT status code
489 * @param pDevObj device object
490 * @param pIrp IRP
491 */
492NTSTATUS VBoxGuestPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
493{
494 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
495 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
496 POWER_STATE_TYPE powerType;
497 POWER_STATE powerState;
498 POWER_ACTION powerAction;
499
500 dprintf(("VBoxGuest::VBoxGuestPower\n"));
501
502 powerType = pStack->Parameters.Power.Type;
503 powerAction = pStack->Parameters.Power.ShutdownType;
504 powerState = pStack->Parameters.Power.State;
505
506 switch (pStack->MinorFunction)
507 {
508 case IRP_MN_SET_POWER:
509 {
510 dprintf(("VBoxGuest::VBoxGuestPower: IRP_MN_SET_POWER\n"));
511 switch (powerType)
512 {
513 case SystemPowerState:
514 {
515 dprintf(("VBoxGuest::VBoxGuestPower: SystemPowerState\n"));
516 switch (powerAction)
517 {
518 case PowerActionShutdownReset:
519 {
520 dprintf(("VBoxGuest::VBoxGuestPower: power action reset!\n"));
521 /* tell the VMM that we no longer support mouse pointer integration */
522
523 VMMDevReqMouseStatus *req = NULL;
524
525 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
526
527 if (VBOX_SUCCESS(rc))
528 {
529 req->mouseFeatures = 0;
530 req->pointerXPos = 0;
531 req->pointerYPos = 0;
532
533 rc = VbglGRPerform (&req->header);
534
535 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
536 {
537 dprintf(("VBoxGuest::PowerStateRequest: error communicating new power status to VMMDev."
538 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
539 }
540
541 VbglGRFree (&req->header);
542 }
543 break;
544 }
545
546 case PowerActionShutdown:
547 case PowerActionShutdownOff:
548 {
549 dprintf(("VBoxGuest::VBoxGuestPower: power action shutdown!\n"));
550 if (powerState.SystemState >= PowerSystemShutdown)
551 {
552 dprintf(("VBoxGuest::VBoxGuestPower: Telling the VMMDev to close the VM...\n"));
553 VMMDevPowerStateRequest *req = NULL;
554
555 int rc = VbglGRAlloc ((VMMDevRequestHeader **)&req, sizeof (VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
556
557 if (VBOX_SUCCESS(rc))
558 {
559 req->powerState = VMMDevPowerState_PowerOff;
560
561 rc = VbglGRPerform (&req->header);
562
563 if (VBOX_FAILURE(rc) || VBOX_FAILURE(req->header.rc))
564 {
565 dprintf(("VBoxGuest::PowerStateRequest: error communicating new power status to VMMDev."
566 "rc = %d, VMMDev rc = %Vrc\n", rc, req->header.rc));
567 }
568
569 VbglGRFree (&req->header);
570 }
571 }
572 break;
573 }
574 }
575 break;
576 }
577 default:
578 break;
579 }
580 break;
581 }
582 default:
583 break;
584 }
585
586 /*
587 * Whether we are completing or relaying this power IRP,
588 * we must call PoStartNextPowerIrp.
589 */
590 PoStartNextPowerIrp(pIrp);
591
592 /*
593 * Send the IRP down the driver stack,
594 * using PoCallDriver (not IoCallDriver, as for non-power irps).
595 */
596 IoCopyCurrentIrpStackLocationToNext(pIrp);
597 IoSetCompletionRoutine(pIrp,
598 VBoxGuestPowerComplete,
599 (PVOID)pDevExt,
600 TRUE,
601 TRUE,
602 TRUE);
603 return PoCallDriver(pDevExt->nextLowerDriver, pIrp);
604}
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