[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [win-pv-devel] [PATCH 06/14] Enumerate ~/device/console for non-default consoles
From: Owen Smith <owen.smith@xxxxxxxxxx> Signed-off-by: Owen Smith <owen.smith@xxxxxxxxxx> --- src/xencons/driver.c | 6 + src/xencons/driver.h | 12 +- src/xencons/fdo.c | 721 +++++++++++++++- src/xencons/fdo.h | 43 + src/xencons/mutex.h | 82 ++ src/xencons/pdo.c | 1786 ++++++++++++++++++++++++++++++++++++++++ src/xencons/pdo.h | 114 +++ vs2015/xencons/xencons.vcxproj | 1 + 8 files changed, 2755 insertions(+), 10 deletions(-) create mode 100644 src/xencons/mutex.h create mode 100755 src/xencons/pdo.c create mode 100755 src/xencons/pdo.h diff --git a/src/xencons/driver.c b/src/xencons/driver.c index 3121b99..beac030 100644 --- a/src/xencons/driver.c +++ b/src/xencons/driver.c @@ -202,6 +202,12 @@ Dispatch( status = STATUS_NOT_SUPPORTED; switch (Dx->Type) { + case PHYSICAL_DEVICE_OBJECT: { + PXENCONS_PDO Pdo = Dx->Pdo; + + status = PdoDispatch(Pdo, Irp); + break; + } case FUNCTION_DEVICE_OBJECT: { PXENCONS_FDO Fdo = Dx->Fdo; diff --git a/src/xencons/driver.h b/src/xencons/driver.h index 4a2eb61..fb5ece0 100644 --- a/src/xencons/driver.h +++ b/src/xencons/driver.h @@ -50,12 +50,17 @@ DriverGetParametersKey( ); typedef struct _XENCONS_FDO XENCONS_FDO, *PXENCONS_FDO; +typedef struct _XENCONS_PDO XENCONS_PDO, *PXENCONS_PDO; #include "fdo.h" +#include "pdo.h" #define MAX_DEVICE_ID_LEN 200 #define MAX_GUID_STRING_LEN 39 +#pragma warning(push) +#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union + typedef struct _XENCONS_DX { PDEVICE_OBJECT DeviceObject; DEVICE_OBJECT_TYPE Type; @@ -72,7 +77,12 @@ typedef struct _XENCONS_DX { LIST_ENTRY ListEntry; - PXENCONS_FDO Fdo; + union { + PXENCONS_FDO Fdo; + PXENCONS_PDO Pdo; + }; } XENCONS_DX, *PXENCONS_DX; +#pragma warning(pop) + #endif // _XENCONS_DRIVER_H diff --git a/src/xencons/fdo.c b/src/xencons/fdo.c index 8e72dc6..5296c7f 100644 --- a/src/xencons/fdo.c +++ b/src/xencons/fdo.c @@ -49,6 +49,7 @@ #include "fdo.h" #include "console.h" #include "thread.h" +#include "mutex.h" #include "names.h" #include "dbg_print.h" #include "assert.h" @@ -85,6 +86,12 @@ struct _XENCONS_FDO { CHAR VendorName[MAXNAMELEN]; + PXENCONS_THREAD ScanThread; + KEVENT ScanEvent; + PXENBUS_STORE_WATCH ScanWatch; + MUTEX Mutex; + ULONG References; + FDO_RESOURCE Resource[RESOURCE_COUNT]; PXENCONS_CONSOLE Console; @@ -210,6 +217,14 @@ __FdoGetPhysicalDeviceObject( return Fdo->PhysicalDeviceObject; } +PDEVICE_OBJECT +FdoGetPhysicalDeviceObject( + IN PXENCONS_FDO Fdo + ) +{ + return __FdoGetPhysicalDeviceObject(Fdo); +} + __drv_requiresIRQL(PASSIVE_LEVEL) static FORCEINLINE NTSTATUS __FdoAcquireLowerBusInterface( @@ -358,6 +373,14 @@ __FdoGetVendorName( return Fdo->VendorName; } +PCHAR +FdoGetVendorName( + IN PXENCONS_FDO Fdo + ) +{ + return __FdoGetVendorName(Fdo); +} + static FORCEINLINE VOID __FdoSetName( IN PXENCONS_FDO Fdo @@ -383,6 +406,99 @@ __FdoGetName( return Dx->Name; } +PCHAR +FdoGetName( + IN PXENCONS_FDO Fdo + ) +{ + return __FdoGetName(Fdo); +} + +__drv_functionClass(IO_COMPLETION_ROUTINE) +__drv_sameIRQL +static NTSTATUS +__FdoDelegateIrp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PKEVENT Event = Context; + + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Irp); + + KeSetEvent(Event, IO_NO_INCREMENT, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +FdoDelegateIrp( + IN PXENCONS_FDO Fdo, + IN PIRP Irp + ) +{ + PDEVICE_OBJECT DeviceObject; + PIO_STACK_LOCATION StackLocation; + PIRP SubIrp; + KEVENT Event; + PIO_STACK_LOCATION SubStackLocation; + NTSTATUS status; + + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + // Find the top of the FDO stack and hold a reference + DeviceObject = IoGetAttachedDeviceReference(Fdo->Dx->DeviceObject); + + // Get a new IRP for the FDO stack + SubIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + + status = STATUS_NO_MEMORY; + if (SubIrp == NULL) + goto done; + + // Copy in the information from the original IRP + SubStackLocation = IoGetNextIrpStackLocation(SubIrp); + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + RtlCopyMemory(SubStackLocation, StackLocation, + FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); + SubStackLocation->Control = 0; + + IoSetCompletionRoutine(SubIrp, + __FdoDelegateIrp, + &Event, + TRUE, + TRUE, + TRUE); + + // Default completion status + SubIrp->IoStatus.Status = Irp->IoStatus.Status; + + status = IoCallDriver(DeviceObject, SubIrp); + if (status == STATUS_PENDING) { + (VOID)KeWaitForSingleObject(&Event, + Executive, + KernelMode, + FALSE, + NULL); + status = SubIrp->IoStatus.Status; + } else { + ASSERT3U(status, == , SubIrp->IoStatus.Status); + } + + IoFreeIrp(SubIrp); + +done: + ObDereferenceObject(DeviceObject); + + return status; +} + __drv_functionClass(IO_COMPLETION_ROUTINE) __drv_sameIRQL static NTSTATUS @@ -487,6 +603,204 @@ FdoParseResources( } } + +NTSTATUS +FdoAddPhysicalDeviceObject( + IN PXENCONS_FDO Fdo, + IN PXENCONS_PDO Pdo + ) +{ + PDEVICE_OBJECT DeviceObject; + PXENCONS_DX Dx; + NTSTATUS status; + + DeviceObject = PdoGetDeviceObject(Pdo); + Dx = (PXENCONS_DX)DeviceObject->DeviceExtension; + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD3) + goto done; + + status = PdoResume(Pdo); + if (!NT_SUCCESS(status)) + goto fail1; + +done: + InsertTailList(&Fdo->Dx->ListEntry, &Dx->ListEntry); + ASSERT3U(Fdo->References, != , 0); + Fdo->References++; + + return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +VOID +FdoRemovePhysicalDeviceObject( + IN PXENCONS_FDO Fdo, + IN PXENCONS_PDO Pdo + ) +{ + PDEVICE_OBJECT DeviceObject; + PXENCONS_DX Dx; + + DeviceObject = PdoGetDeviceObject(Pdo); + Dx = (PXENCONS_DX)DeviceObject->DeviceExtension; + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD3) + goto done; + + PdoSuspend(Pdo); + +done: + RemoveEntryList(&Dx->ListEntry); + ASSERT3U(Fdo->References, != , 0); + --Fdo->References; + + if (Fdo->ScanThread) + ThreadWake(Fdo->ScanThread); +} + +static FORCEINLINE VOID +__FdoAcquireMutex( + IN PXENCONS_FDO Fdo + ) +{ + AcquireMutex(&Fdo->Mutex); +} + +VOID +FdoAcquireMutex( + IN PXENCONS_FDO Fdo + ) +{ + __FdoAcquireMutex(Fdo); +} + +static FORCEINLINE VOID +__FdoReleaseMutex( + IN PXENCONS_FDO Fdo + ) +{ + ReleaseMutex(&Fdo->Mutex); +} + +VOID +FdoReleaseMutex( + IN PXENCONS_FDO Fdo + ) +{ + __FdoReleaseMutex(Fdo); + + if (Fdo->References == 0) + FdoDestroy(Fdo); +} + +static FORCEINLINE BOOLEAN +__FdoEnumerate( + IN PXENCONS_FDO Fdo, + IN PANSI_STRING Devices + ) +{ + BOOLEAN NeedInvalidate; + HANDLE ParametersKey; + ULONG Enumerate; + PLIST_ENTRY ListEntry; + ULONG Index; + NTSTATUS status; + + Trace("====>\n"); + + NeedInvalidate = FALSE; + + ParametersKey = DriverGetParametersKey(); + + status = RegistryQueryDwordValue(ParametersKey, + "Enumerate", + &Enumerate); + if (!NT_SUCCESS(status)) + Enumerate = 1; + + if (Enumerate == 0) + goto done; + + __FdoAcquireMutex(Fdo); + + ListEntry = Fdo->Dx->ListEntry.Flink; + while (ListEntry != &Fdo->Dx->ListEntry) { + PLIST_ENTRY Next = ListEntry->Flink; + PXENCONS_DX Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry); + PXENCONS_PDO Pdo = Dx->Pdo; + + if (PdoGetDevicePnpState(Pdo) != Deleted) { + PCHAR Name; + BOOLEAN Missing; + + Name = PdoGetName(Pdo); + Missing = TRUE; + + // If the PDO already exists and its name is in the device list + // then we don't want to remove it. + for (Index = 0; Devices[Index].Buffer != NULL; Index++) { + PANSI_STRING Device = &Devices[Index]; + + if (Device->Length == 0) + continue; + + if (strcmp(Name, Device->Buffer) == 0) { + Missing = FALSE; + Device->Length = 0; // avoid duplication + break; + } + } + + if (!PdoIsMissing(Pdo)) { + if (PdoIsEjectRequested(Pdo)) { + IoRequestDeviceEject(PdoGetDeviceObject(Pdo)); + } else if (Missing) { + PdoSetMissing(Pdo, "device disappeared"); + + // If the PDO has not yet been enumerated then we can + // go ahead and mark it as deleted, otherwise we need + // to notify PnP manager and wait for the REMOVE_DEVICE + // IRP. + if (PdoGetDevicePnpState(Pdo) == Present) { + PdoSetDevicePnpState(Pdo, Deleted); + PdoDestroy(Pdo); + } else { + NeedInvalidate = TRUE; + } + } + } + } + + ListEntry = Next; + } + + // Walk the class list and create PDOs for any new device + for (Index = 0; Devices[Index].Buffer != NULL; Index++) { + PANSI_STRING Device = &Devices[Index]; + + if (Device->Length == 0) + continue; + + status = PdoCreate(Fdo, Device); + if (NT_SUCCESS(status)) + NeedInvalidate = TRUE; + } + + __FdoReleaseMutex(Fdo); + +done: + Trace("<====\n"); + + return NeedInvalidate; +} + static FORCEINLINE PANSI_STRING __FdoMultiSzToUpcaseAnsi( IN PCHAR Buffer @@ -565,6 +879,126 @@ __FdoFreeAnsi( __FdoFree(Ansi); } + +static NTSTATUS +FdoScan( + PXENCONS_THREAD Self, + PVOID Context + ) +{ + PXENCONS_FDO Fdo = Context; + PKEVENT Event; + HANDLE ParametersKey; + NTSTATUS status; + + Trace("====>\n"); + + Event = ThreadGetEvent(Self); + + ParametersKey = DriverGetParametersKey(); + + for (;;) { + PCHAR Buffer; + PANSI_STRING Devices; + PANSI_STRING UnsupportedDevices; + ULONG Index; + BOOLEAN NeedInvalidate; + + Trace("waiting...\n"); + + (VOID)KeWaitForSingleObject(Event, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(Event); + + if (ThreadIsAlerted(Self)) + break; + + // It is not safe to use interfaces before this point + if (__FdoGetDevicePnpState(Fdo) != Started) { + KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE); + continue; + } + + status = XENBUS_STORE(Directory, + &Fdo->StoreInterface, + NULL, + "device", + "console", + &Buffer); + if (NT_SUCCESS(status)) { + Devices = __FdoMultiSzToUpcaseAnsi(Buffer); + + XENBUS_STORE(Free, + &Fdo->StoreInterface, + Buffer); + } else { + Devices = NULL; + } + + if (Devices == NULL) + goto loop; + + if (ParametersKey != NULL) { + status = RegistryQuerySzValue(ParametersKey, + "UnsupportedDevices", + NULL, + &UnsupportedDevices); + if (!NT_SUCCESS(status)) + UnsupportedDevices = NULL; + } else { + UnsupportedDevices = NULL; + } + + // NULL out anything in the Devices list that is in the + // UnsupportedDevices list + for (Index = 0; Devices[Index].Buffer != NULL; Index++) { + PANSI_STRING Device = &Devices[Index]; + ULONG Entry; + BOOLEAN Supported; + + Supported = TRUE; + + for (Entry = 0; + UnsupportedDevices != NULL && UnsupportedDevices[Entry].Buffer != NULL; + Entry++) { + if (strncmp(Device->Buffer, + UnsupportedDevices[Entry].Buffer, + Device->Length) == 0) { + Supported = FALSE; + break; + } + } + + if (!Supported) + Device->Length = 0; + } + + if (UnsupportedDevices != NULL) + RegistryFreeSzValue(UnsupportedDevices); + + NeedInvalidate = __FdoEnumerate(Fdo, Devices); + + __FdoFreeAnsi(Devices); + + if (NeedInvalidate) { + NeedInvalidate = FALSE; + IoInvalidateDeviceRelations(__FdoGetPhysicalDeviceObject(Fdo), + BusRelations); + } + + loop: + KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE); + } + + KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE); + + Trace("<====\n"); + return STATUS_SUCCESS; +} + static FORCEINLINE BOOLEAN __FdoMatchDistribution( IN PXENCONS_FDO Fdo, @@ -796,12 +1230,34 @@ __FdoD3ToD0( if (!NT_SUCCESS(status)) goto fail1; + status = XENBUS_STORE(WatchAdd, + &Fdo->StoreInterface, + "device", + "console", + ThreadGetEvent(Fdo->ScanThread), + &Fdo->ScanWatch); + if (!NT_SUCCESS(status)) + goto fail2; + + (VOID)XENBUS_STORE(Printf, + &Fdo->StoreInterface, + NULL, + "feature/hotplug", + "console", + "%u", + TRUE); + (VOID) FdoSetDistribution(Fdo); Trace("<====\n"); return STATUS_SUCCESS; +fail2: + Error("fail2\n"); + + ConsoleDisable(Fdo->Console); + fail1: Error("fail1 (%08x)\n", status); @@ -819,6 +1275,17 @@ __FdoD0ToD3( FdoClearDistribution(Fdo); + (VOID)XENBUS_STORE(Remove, + &Fdo->StoreInterface, + NULL, + "feature/hotplug", + "console"); + + (VOID)XENBUS_STORE(WatchRemove, + &Fdo->StoreInterface, + Fdo->ScanWatch); + Fdo->ScanWatch = NULL; + ConsoleDisable(Fdo->Console); Trace("<====\n"); @@ -844,9 +1311,9 @@ FdoD3ToD0( IN PXENCONS_FDO Fdo ) { - PXENCONS_DX Dx = Fdo->Dx; POWER_STATE PowerState; KIRQL Irql; + PLIST_ENTRY ListEntry; NTSTATUS status; ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL); @@ -886,8 +1353,24 @@ FdoD3ToD0( DevicePowerState, PowerState); + __FdoAcquireMutex(Fdo); + + for (ListEntry = Fdo->Dx->ListEntry.Flink; + ListEntry != &Fdo->Dx->ListEntry; + ListEntry = ListEntry->Flink) { + PXENCONS_DX Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry); + PXENCONS_PDO Pdo = Dx->Pdo; + + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + status = PdoResume(Pdo); + ASSERT(NT_SUCCESS(status)); + } + + __FdoReleaseMutex(Fdo); + #pragma prefast(suppress:28123) - (VOID) IoSetDeviceInterfaceState(&Dx->Link, TRUE); + (VOID) IoSetDeviceInterfaceState(&Fdo->Dx->Link, TRUE); Trace("<====\n"); @@ -924,8 +1407,8 @@ FdoD0ToD3( IN PXENCONS_FDO Fdo ) { - PXENCONS_DX Dx = Fdo->Dx; POWER_STATE PowerState; + PLIST_ENTRY ListEntry; KIRQL Irql; ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL); @@ -934,7 +1417,26 @@ FdoD0ToD3( Trace("====>\n"); #pragma prefast(suppress:28123) - (VOID) IoSetDeviceInterfaceState(&Dx->Link, FALSE); + (VOID) IoSetDeviceInterfaceState(&Fdo->Dx->Link, FALSE); + + __FdoAcquireMutex(Fdo); + + for (ListEntry = Fdo->Dx->ListEntry.Flink; + ListEntry != &Fdo->Dx->ListEntry; + ListEntry = ListEntry->Flink) { + PXENCONS_DX Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry); + PXENCONS_PDO Pdo = Dx->Pdo; + + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + if (PdoGetDevicePnpState(Pdo) == Deleted || + PdoIsMissing(Pdo)) + continue; + + PdoSuspend(Pdo); + } + + __FdoReleaseMutex(Fdo); PowerState.DeviceState = PowerDeviceD3; PoSetPowerState(Fdo->Dx->DeviceObject, @@ -1004,20 +1506,36 @@ FdoStartDevice( StackLocation->Parameters.StartDevice.AllocatedResources, StackLocation->Parameters.StartDevice.AllocatedResourcesTranslated); - status = FdoD3ToD0(Fdo); + KeInitializeEvent(&Fdo->ScanEvent, NotificationEvent, FALSE); + + status = ThreadCreate(FdoScan, Fdo, &Fdo->ScanThread); if (!NT_SUCCESS(status)) goto fail2; + status = FdoD3ToD0(Fdo); + if (!NT_SUCCESS(status)) + goto fail3; + __FdoSetDevicePnpState(Fdo, Started); + ThreadWake(Fdo->ScanThread); status = Irp->IoStatus.Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; +fail3: + Error("fail3\n"); + + ThreadAlert(Fdo->ScanThread); + ThreadJoin(Fdo->ScanThread); + Fdo->ScanThread = NULL; + fail2: Error("fail2\n"); + RtlZeroMemory(&Fdo->ScanEvent, sizeof(KEVENT)); + RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT); fail1: @@ -1075,6 +1593,12 @@ FdoStopDevice( if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0) FdoD0ToD3(Fdo); + ThreadAlert(Fdo->ScanThread); + ThreadJoin(Fdo->ScanThread); + Fdo->ScanThread = NULL; + + RtlZeroMemory(&Fdo->ScanEvent, sizeof(KEVENT)); + RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT); __FdoSetDevicePnpState(Fdo, Stopped); @@ -1127,10 +1651,27 @@ FdoSurpriseRemoval( IN PIRP Irp ) { + PLIST_ENTRY ListEntry; NTSTATUS status; __FdoSetDevicePnpState(Fdo, SurpriseRemovePending); + __FdoAcquireMutex(Fdo); + + for (ListEntry = Fdo->Dx->ListEntry.Flink; + ListEntry != &Fdo->Dx->ListEntry; + ListEntry = ListEntry->Flink) { + PXENCONS_DX Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry); + PXENCONS_PDO Pdo = Dx->Pdo; + + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + if (!PdoIsMissing(Pdo)) + PdoSetMissing(Pdo, "FDO surprise removed"); + } + + __FdoReleaseMutex(Fdo); + Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); @@ -1145,6 +1686,7 @@ FdoRemoveDevice( IN PIRP Irp ) { + PLIST_ENTRY ListEntry; NTSTATUS status; ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL); @@ -1152,9 +1694,50 @@ FdoRemoveDevice( if (__FdoGetPreviousDevicePnpState(Fdo) != Started) goto done; + KeClearEvent(&Fdo->ScanEvent); + ThreadWake(Fdo->ScanThread); + + Trace("waiting for scan thread\n"); + + (VOID)KeWaitForSingleObject(&Fdo->ScanEvent, + Executive, + KernelMode, + FALSE, + NULL); + + __FdoAcquireMutex(Fdo); + + ListEntry = Fdo->Dx->ListEntry.Flink; + while (ListEntry != &Fdo->Dx->ListEntry) { + PLIST_ENTRY Flink = ListEntry->Flink; + PXENCONS_DX Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry); + PXENCONS_PDO Pdo = Dx->Pdo; + + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + if (!PdoIsMissing(Pdo)) + PdoSetMissing(Pdo, "FDO removed"); + + if (PdoGetDevicePnpState(Pdo) != SurpriseRemovePending) + PdoSetDevicePnpState(Pdo, Deleted); + + if (PdoGetDevicePnpState(Pdo) == Deleted) + PdoDestroy(Pdo); + + ListEntry = Flink; + } + + __FdoReleaseMutex(Fdo); + if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0) FdoD0ToD3(Fdo); + ThreadAlert(Fdo->ScanThread); + ThreadJoin(Fdo->ScanThread); + Fdo->ScanThread = NULL; + + RtlZeroMemory(&Fdo->ScanEvent, sizeof(KEVENT)); + RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT); done: @@ -1167,7 +1750,13 @@ done: IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(Fdo->LowerDeviceObject, Irp); - FdoDestroy(Fdo); + __FdoAcquireMutex(Fdo); + ASSERT3U(Fdo->References, != , 0); + --Fdo->References; + __FdoReleaseMutex(Fdo); + + if (Fdo->References == 0) + FdoDestroy(Fdo); return status; } @@ -1179,16 +1768,121 @@ FdoQueryDeviceRelations( ) { PIO_STACK_LOCATION StackLocation; + ULONG Size; + PDEVICE_RELATIONS Relations; + ULONG Count; + PLIST_ENTRY ListEntry; NTSTATUS status; - ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL); + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); StackLocation = IoGetCurrentIrpStackLocation(Irp); status = Irp->IoStatus.Status; - IoSkipCurrentIrpStackLocation(Irp); - status = IoCallDriver(Fdo->LowerDeviceObject, Irp); + if (StackLocation->Parameters.QueryDeviceRelations.Type != BusRelations) { + IoSkipCurrentIrpStackLocation(Irp); + status = IoCallDriver(Fdo->LowerDeviceObject, Irp); + + goto done; + } + + KeClearEvent(&Fdo->ScanEvent); + ThreadWake(Fdo->ScanThread); + + Trace("waiting for scan thread\n"); + + (VOID)KeWaitForSingleObject(&Fdo->ScanEvent, + Executive, + KernelMode, + FALSE, + NULL); + + __FdoAcquireMutex(Fdo); + + Count = 0; + for (ListEntry = Fdo->Dx->ListEntry.Flink; + ListEntry != &Fdo->Dx->ListEntry; + ListEntry = ListEntry->Flink) + Count++; + + Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + (sizeof(PDEVICE_OBJECT) * __max(Count, 1)); + + Relations = ExAllocatePoolWithTag(PagedPool, Size, FDO_POOL); + + status = STATUS_NO_MEMORY; + if (Relations == NULL) + goto fail1; + + RtlZeroMemory(Relations, Size); + + for (ListEntry = Fdo->Dx->ListEntry.Flink; + ListEntry != &Fdo->Dx->ListEntry; + ListEntry = ListEntry->Flink) { + PXENCONS_DX Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry); + PXENCONS_PDO Pdo = Dx->Pdo; + + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + if (PdoIsMissing(Pdo)) + continue; + + if (PdoGetDevicePnpState(Pdo) == Present) + PdoSetDevicePnpState(Pdo, Enumerated); + + ObReferenceObject(Dx->DeviceObject); + Relations->Objects[Relations->Count++] = Dx->DeviceObject; + } + + ASSERT3U(Relations->Count, <= , Count); + + Trace("%d PDO(s)\n", Relations->Count); + + __FdoReleaseMutex(Fdo); + + Irp->IoStatus.Information = (ULONG_PTR)Relations; + Irp->IoStatus.Status = STATUS_SUCCESS; + + status = FdoForwardIrpSynchronously(Fdo, Irp); + if (!NT_SUCCESS(status)) + goto fail2; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + __FdoAcquireMutex(Fdo); + + ListEntry = Fdo->Dx->ListEntry.Flink; + while (ListEntry != &Fdo->Dx->ListEntry) { + PXENCONS_DX Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry); + PXENCONS_PDO Pdo = Dx->Pdo; + PLIST_ENTRY Next = ListEntry->Flink; + + ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT); + + if (PdoGetDevicePnpState(Pdo) == Deleted && + PdoIsMissing(Pdo)) + PdoDestroy(Pdo); + + ListEntry = Next; + } + + __FdoReleaseMutex(Fdo); + +done: + return status; + +fail2: + Error("fail2\n"); + + __FdoAcquireMutex(Fdo); + +fail1: + Error("fail1 (%08x)\n", status); + + __FdoReleaseMutex(Fdo); + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } @@ -2566,6 +3260,11 @@ FdoCreate( Dx->Fdo = Fdo; + InitializeMutex(&Fdo->Mutex); + InitializeListHead(&Dx->ListEntry); + + Fdo->References = 1; + Info("%p (%s)\n", FunctionDeviceObject, __FdoGetName(Fdo)); @@ -2658,6 +3357,8 @@ FdoDestroy( PXENCONS_DX Dx = Fdo->Dx; PDEVICE_OBJECT FunctionDeviceObject = Dx->DeviceObject; + ASSERT(IsListEmpty(&Dx->ListEntry)); + ASSERT3U(Fdo->References, == , 0); ASSERT3U(__FdoGetDevicePnpState(Fdo), ==, Deleted); Fdo->NotDisableable = FALSE; @@ -2666,6 +3367,8 @@ FdoDestroy( FunctionDeviceObject, __FdoGetName(Fdo)); + RtlZeroMemory(&Fdo->Mutex, sizeof(MUTEX)); + Dx->Fdo = NULL; ConsoleDestroy(Fdo->Console); diff --git a/src/xencons/fdo.h b/src/xencons/fdo.h index 19eaa42..189c70f 100644 --- a/src/xencons/fdo.h +++ b/src/xencons/fdo.h @@ -40,6 +40,49 @@ #include "driver.h" +extern PCHAR +FdoGetVendorName( + IN PXENCONS_FDO Fdo + ); + +extern PCHAR +FdoGetName( + IN PXENCONS_FDO Fdo + ); + +extern NTSTATUS +FdoAddPhysicalDeviceObject( + IN PXENCONS_FDO Fdo, + IN PXENCONS_PDO Pdo + ); + +extern VOID +FdoRemovePhysicalDeviceObject( + IN PXENCONS_FDO Fdo, + IN PXENCONS_PDO Pdo + ); + +extern VOID +FdoAcquireMutex( + IN PXENCONS_FDO Fdo + ); + +extern VOID +FdoReleaseMutex( + IN PXENCONS_FDO Fdo + ); + +extern PDEVICE_OBJECT +FdoGetPhysicalDeviceObject( + IN PXENCONS_FDO Fdo + ); + +extern NTSTATUS +FdoDelegateIrp( + IN PXENCONS_FDO Fdo, + IN PIRP Irp + ); + extern NTSTATUS FdoDispatch( IN PXENCONS_FDO Fdo, diff --git a/src/xencons/mutex.h b/src/xencons/mutex.h new file mode 100644 index 0000000..faf1a98 --- /dev/null +++ b/src/xencons/mutex.h @@ -0,0 +1,82 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _XENCONS_MUTEX_H +#define _XENCONS_MUTEX_H + +#include <ntddk.h> + +#include "assert.h" + +typedef struct _MUTEX { + PKTHREAD Owner; + KEVENT Event; +} MUTEX, *PMUTEX; + +static FORCEINLINE VOID +InitializeMutex( + IN PMUTEX Mutex + ) +{ + RtlZeroMemory(Mutex, sizeof (MUTEX)); + + KeInitializeEvent(&Mutex->Event, SynchronizationEvent, TRUE); +} + +static FORCEINLINE VOID +__drv_maxIRQL(PASSIVE_LEVEL) +AcquireMutex( + IN PMUTEX Mutex + ) +{ + (VOID) KeWaitForSingleObject(&Mutex->Event, + Executive, + KernelMode, + FALSE, + NULL); + + ASSERT3P(Mutex->Owner, ==, NULL); + Mutex->Owner = KeGetCurrentThread(); +} + +static FORCEINLINE VOID +__drv_maxIRQL(PASSIVE_LEVEL) +ReleaseMutex( + IN PMUTEX Mutex + ) +{ + ASSERT3P(Mutex->Owner, ==, KeGetCurrentThread()); + Mutex->Owner = NULL; + + KeSetEvent(&Mutex->Event, IO_NO_INCREMENT, FALSE); +} + +#endif // _XENCONS_MUTEX_H diff --git a/src/xencons/pdo.c b/src/xencons/pdo.c new file mode 100755 index 0000000..7a9c1e6 --- /dev/null +++ b/src/xencons/pdo.c @@ -0,0 +1,1786 @@ +/* Copyright (c) Citrix Systems Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, +* with or without modification, are permitted provided +* that the following conditions are met: +* +* * Redistributions of source code must retain the above +* copyright notice, this list of conditions and the +* following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +*/ + +#define INITGUID 1 + +#include <ntddk.h> +#include <wdmguid.h> +#include <ntstrsafe.h> +#include <stdlib.h> + +#include "names.h" +#include "fdo.h" +#include "pdo.h" +#include "driver.h" +#include "registry.h" +#include "thread.h" +#include "dbg_print.h" +#include "assert.h" +#include "util.h" +#include "version.h" + +#define PDO_POOL 'ODP' + +#define MAXNAMELEN 128 + +struct _XENCONS_PDO { + PXENCONS_DX Dx; + + PXENCONS_THREAD SystemPowerThread; + PIRP SystemPowerIrp; + PXENCONS_THREAD DevicePowerThread; + PIRP DevicePowerIrp; + + PXENCONS_FDO Fdo; + BOOLEAN Missing; + const CHAR *Reason; + LONG Eject; + + XENBUS_SUSPEND_INTERFACE SuspendInterface; + PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate; +}; + +static FORCEINLINE PVOID +__PdoAllocate( + IN ULONG Length + ) +{ + return __AllocatePoolWithTag(NonPagedPool, Length, PDO_POOL); +} + +static FORCEINLINE VOID +__PdoFree( + IN PVOID Buffer + ) +{ + __FreePoolWithTag(Buffer, PDO_POOL); +} + +static FORCEINLINE VOID +__PdoSetDevicePnpState( + IN PXENCONS_PDO Pdo, + IN DEVICE_PNP_STATE State + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + // We can never transition out of the deleted state + ASSERT(Dx->DevicePnpState != Deleted || State == Deleted); + + Dx->PreviousDevicePnpState = Dx->DevicePnpState; + Dx->DevicePnpState = State; +} + +VOID +PdoSetDevicePnpState( + IN PXENCONS_PDO Pdo, + IN DEVICE_PNP_STATE State + ) +{ + __PdoSetDevicePnpState(Pdo, State); +} + +static FORCEINLINE VOID +__PdoRestoreDevicePnpState( + IN PXENCONS_PDO Pdo, + IN DEVICE_PNP_STATE State + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + if (Dx->DevicePnpState == State) + Dx->DevicePnpState = Dx->PreviousDevicePnpState; +} + +static FORCEINLINE DEVICE_PNP_STATE +__PdoGetDevicePnpState( + IN PXENCONS_PDO Pdo + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + return Dx->DevicePnpState; +} + +DEVICE_PNP_STATE +PdoGetDevicePnpState( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoGetDevicePnpState(Pdo); +} + +static FORCEINLINE VOID +__PdoSetSystemPowerState( + IN PXENCONS_PDO Pdo, + IN SYSTEM_POWER_STATE State + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + Dx->SystemPowerState = State; +} + +static FORCEINLINE SYSTEM_POWER_STATE +__PdoGetSystemPowerState( + IN PXENCONS_PDO Pdo + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + return Dx->SystemPowerState; +} + +static FORCEINLINE VOID +__PdoSetDevicePowerState( + IN PXENCONS_PDO Pdo, + IN DEVICE_POWER_STATE State + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + Dx->DevicePowerState = State; +} + +static FORCEINLINE DEVICE_POWER_STATE +__PdoGetDevicePowerState( + IN PXENCONS_PDO Pdo + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + return Dx->DevicePowerState; +} + +static FORCEINLINE VOID +__PdoSetMissing( + IN PXENCONS_PDO Pdo, + IN const CHAR *Reason + ) +{ + Pdo->Reason = Reason; + Pdo->Missing = TRUE; +} + +VOID +PdoSetMissing( + IN PXENCONS_PDO Pdo, + IN const CHAR *Reason + ) +{ + __PdoSetMissing(Pdo, Reason); +} + +static FORCEINLINE BOOLEAN +__PdoIsMissing( + IN PXENCONS_PDO Pdo + ) +{ + return Pdo->Missing; +} + +BOOLEAN +PdoIsMissing( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoIsMissing(Pdo); +} + +static FORCEINLINE PXENCONS_FDO +__PdoGetFdo( + IN PXENCONS_PDO Pdo + ) +{ + return Pdo->Fdo; +} + +PXENCONS_FDO +PdoGetFdo( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoGetFdo(Pdo); +} + +static FORCEINLINE VOID +__PdoSetName( + IN PXENCONS_PDO Pdo, + IN PANSI_STRING Name + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + NTSTATUS status; + + status = RtlStringCbPrintfA(Dx->Name, + MAX_DEVICE_ID_LEN, + "%Z", + Name); + ASSERT(NT_SUCCESS(status)); +} + +static FORCEINLINE PCHAR +__PdoGetName( + IN PXENCONS_PDO Pdo + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + return Dx->Name; +} + +PCHAR +PdoGetName( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoGetName(Pdo); +} + +static FORCEINLINE BOOLEAN +__PdoSetEjectRequested( + IN PXENCONS_PDO Pdo + ) +{ + return (InterlockedBitTestAndSet(&Pdo->Eject, 0) == 0) ? TRUE : FALSE; +} + +VOID +PdoRequestEject( + IN PXENCONS_PDO Pdo + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + PDEVICE_OBJECT PhysicalDeviceObject = Dx->DeviceObject; + PXENCONS_FDO Fdo = __PdoGetFdo(Pdo); + + if (!__PdoSetEjectRequested(Pdo)) + return; + + Info("%p (%s)\n", + PhysicalDeviceObject, + __PdoGetName(Pdo)); + + IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo), + BusRelations); +} + +static FORCEINLINE BOOLEAN +__PdoClearEjectRequested( + IN PXENCONS_PDO Pdo + ) +{ + return (InterlockedBitTestAndReset(&Pdo->Eject, 0) != 0) ? TRUE : FALSE; +} + +static FORCEINLINE BOOLEAN +__PdoIsEjectRequested( + IN PXENCONS_PDO Pdo + ) +{ + KeMemoryBarrier(); + return (Pdo->Eject & 1) ? TRUE : FALSE; +} + +BOOLEAN +PdoIsEjectRequested( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoIsEjectRequested(Pdo); +} + +static FORCEINLINE PDEVICE_OBJECT +__PdoGetDeviceObject( + IN PXENCONS_PDO Pdo + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + + return (Dx->DeviceObject); +} + +PDEVICE_OBJECT +PdoGetDeviceObject( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoGetDeviceObject(Pdo); +} + +static FORCEINLINE PCHAR +__PdoGetVendorName( + IN PXENCONS_PDO Pdo + ) +{ + return FdoGetVendorName(__PdoGetFdo(Pdo)); +} + +static FORCEINLINE NTSTATUS +__PdoD3ToD0( + IN PXENCONS_PDO Pdo + ) +{ + POWER_STATE PowerState; + + Trace("(%s) ====>\n", __PdoGetName(Pdo)); + + ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL); + ASSERT3U(__PdoGetDevicePowerState(Pdo), == , PowerDeviceD3); + + __PdoSetDevicePowerState(Pdo, PowerDeviceD0); + + PowerState.DeviceState = PowerDeviceD0; + PoSetPowerState(__PdoGetDeviceObject(Pdo), + DevicePowerState, + PowerState); + + Trace("(%s) <====\n", __PdoGetName(Pdo)); + + return STATUS_SUCCESS; +} + +static FORCEINLINE VOID +__PdoD0ToD3( + IN PXENCONS_PDO Pdo + ) +{ + POWER_STATE PowerState; + + Trace("(%s) ====>\n", __PdoGetName(Pdo)); + + ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL); + ASSERT3U(__PdoGetDevicePowerState(Pdo), == , PowerDeviceD0); + + PowerState.DeviceState = PowerDeviceD3; + PoSetPowerState(__PdoGetDeviceObject(Pdo), + DevicePowerState, + PowerState); + + __PdoSetDevicePowerState(Pdo, PowerDeviceD3); + + Trace("(%s) <====\n", __PdoGetName(Pdo)); +} + +static DECLSPEC_NOINLINE VOID +PdoSuspendCallbackLate( + IN PVOID Argument + ) +{ + PXENCONS_PDO Pdo = Argument; + NTSTATUS status; + + __PdoD0ToD3(Pdo); + + status = __PdoD3ToD0(Pdo); + ASSERT(NT_SUCCESS(status)); +} + +// This function must not touch pageable code or data +static DECLSPEC_NOINLINE NTSTATUS +PdoD3ToD0( + IN PXENCONS_PDO Pdo + ) +{ + KIRQL Irql; + NTSTATUS status; + + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + + KeRaiseIrql(DISPATCH_LEVEL, &Irql); + + status = XENBUS_SUSPEND(Acquire, &Pdo->SuspendInterface); + if (!NT_SUCCESS(status)) + goto fail1; + + status = __PdoD3ToD0(Pdo); + if (!NT_SUCCESS(status)) + goto fail2; + + status = XENBUS_SUSPEND(Register, + &Pdo->SuspendInterface, + SUSPEND_CALLBACK_LATE, + PdoSuspendCallbackLate, + Pdo, + &Pdo->SuspendCallbackLate); + if (!NT_SUCCESS(status)) + goto fail3; + + KeLowerIrql(Irql); + + return STATUS_SUCCESS; + +fail3: + Error("fail3\n"); + + __PdoD0ToD3(Pdo); + +fail2: + Error("fail2\n"); + + XENBUS_SUSPEND(Release, &Pdo->SuspendInterface); + +fail1: + Error("fail1 (%08x)\n", status); + + KeLowerIrql(Irql); + + return status; +} + +// This function must not touch pageable code or data +static DECLSPEC_NOINLINE VOID +PdoD0ToD3( + IN PXENCONS_PDO Pdo + ) +{ + KIRQL Irql; + + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + + KeRaiseIrql(DISPATCH_LEVEL, &Irql); + + XENBUS_SUSPEND(Deregister, + &Pdo->SuspendInterface, + Pdo->SuspendCallbackLate); + Pdo->SuspendCallbackLate = NULL; + + __PdoD0ToD3(Pdo); + + XENBUS_SUSPEND(Release, &Pdo->SuspendInterface); + + KeLowerIrql(Irql); +} + +// This function must not touch pageable code or data +static DECLSPEC_NOINLINE VOID +PdoS4ToS3( + IN PXENCONS_PDO Pdo + ) +{ + Trace("(%s) ====>\n", __PdoGetName(Pdo)); + + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + ASSERT3U(__PdoGetSystemPowerState(Pdo), == , PowerSystemHibernate); + + __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3); + + Trace("(%s) <====\n", __PdoGetName(Pdo)); +} + +// This function must not touch pageable code or data +static DECLSPEC_NOINLINE VOID +PdoS3ToS4( + IN PXENCONS_PDO Pdo + ) +{ + Trace("(%s) ====>\n", __PdoGetName(Pdo)); + + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + ASSERT3U(__PdoGetSystemPowerState(Pdo), == , PowerSystemSleeping3); + + __PdoSetSystemPowerState(Pdo, PowerSystemHibernate); + + Trace("(%s) <====\n", __PdoGetName(Pdo)); +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoStartDevice( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + status = PdoD3ToD0(Pdo); + if (!NT_SUCCESS(status)) + goto fail1; + + __PdoSetDevicePnpState(Pdo, Started); + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryStopDevice( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + __PdoSetDevicePnpState(Pdo, StopPending); + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoCancelStopDevice( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + __PdoRestoreDevicePnpState(Pdo, StopPending); + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoStopDevice( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0) + goto done; + + PdoD0ToD3(Pdo); + +done: + __PdoSetDevicePnpState(Pdo, Stopped); + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryRemoveDevice( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + __PdoSetDevicePnpState(Pdo, RemovePending); + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoCancelRemoveDevice( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + //if (__PdoClearEjectRequested(Pdo)) + // FrontendEjectFailed(__PdoGetFrontend(Pdo)); + + __PdoRestoreDevicePnpState(Pdo, RemovePending); + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoSurpriseRemoval( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + Warning("%s\n", __PdoGetName(Pdo)); + + __PdoSetDevicePnpState(Pdo, SurpriseRemovePending); + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoRemoveDevice( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PXENCONS_FDO Fdo = __PdoGetFdo(Pdo); + BOOLEAN NeedInvalidate; + NTSTATUS status; + + if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0) + goto done; + + PdoD0ToD3(Pdo); + +done: + NeedInvalidate = FALSE; + + FdoAcquireMutex(Fdo); + + if (__PdoIsMissing(Pdo)) { + DEVICE_PNP_STATE State = __PdoGetDevicePnpState(Pdo); + + __PdoSetDevicePnpState(Pdo, Deleted); + + if (State == SurpriseRemovePending) + PdoDestroy(Pdo); + else + NeedInvalidate = TRUE; + } else { + __PdoSetDevicePnpState(Pdo, Enumerated); + } + + FdoReleaseMutex(Fdo); + + if (NeedInvalidate) + IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo), + BusRelations); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryDeviceRelations( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + PDEVICE_RELATIONS Relations; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + status = Irp->IoStatus.Status; + + if (StackLocation->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) + goto done; + + Relations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), PDO_POOL); + + status = STATUS_NO_MEMORY; + if (Relations == NULL) + goto done; + + RtlZeroMemory(Relations, sizeof(DEVICE_RELATIONS)); + + Relations->Count = 1; + ObReferenceObject(__PdoGetDeviceObject(Pdo)); + Relations->Objects[0] = __PdoGetDeviceObject(Pdo); + + Irp->IoStatus.Information = (ULONG_PTR)Relations; + status = STATUS_SUCCESS; + +done: + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryCapabilities( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + PDEVICE_CAPABILITIES Capabilities; + SYSTEM_POWER_STATE SystemPowerState; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Pdo); + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + Capabilities = StackLocation->Parameters.DeviceCapabilities.Capabilities; + + status = STATUS_INVALID_PARAMETER; + if (Capabilities->Version != 1) + goto done; + + Capabilities->DeviceD1 = 0; + Capabilities->DeviceD2 = 0; + Capabilities->LockSupported = 0; + Capabilities->EjectSupported = 1; + Capabilities->Removable = 1; + Capabilities->DockDevice = 0; + Capabilities->UniqueID = 1; + Capabilities->SilentInstall = 1; + Capabilities->RawDeviceOK = 1; + Capabilities->SurpriseRemovalOK = 1; + Capabilities->HardwareDisabled = 0; + Capabilities->NoDisplayInUI = 0; + + Capabilities->Address = 0xffffffff; + Capabilities->UINumber = 0xffffffff; + + for (SystemPowerState = 0; SystemPowerState < PowerSystemMaximum; SystemPowerState++) { + switch (SystemPowerState) { + case PowerSystemUnspecified: + case PowerSystemSleeping1: + case PowerSystemSleeping2: + break; + + case PowerSystemWorking: + Capabilities->DeviceState[SystemPowerState] = PowerDeviceD0; + break; + + default: + Capabilities->DeviceState[SystemPowerState] = PowerDeviceD3; + break; + } + } + + Capabilities->SystemWake = PowerSystemUnspecified; + Capabilities->DeviceWake = PowerDeviceUnspecified; + Capabilities->D1Latency = 0; + Capabilities->D2Latency = 0; + Capabilities->D3Latency = 0; + + status = STATUS_SUCCESS; + +done: + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +#define MAXTEXTLEN 1024 + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryDeviceText( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + PWCHAR Buffer; + UNICODE_STRING Text; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) { + case DeviceTextDescription: + Trace("DeviceTextDescription\n"); + break; + + case DeviceTextLocationInformation: + Trace("DeviceTextLocationInformation\n"); + break; + + default: + Irp->IoStatus.Information = 0; + status = STATUS_NOT_SUPPORTED; + goto done; + } + + Buffer = ExAllocatePoolWithTag(PagedPool, MAXTEXTLEN, PDO_POOL); + + status = STATUS_NO_MEMORY; + if (Buffer == NULL) + goto done; + + RtlZeroMemory(Buffer, MAXTEXTLEN); + + Text.Buffer = Buffer; + Text.MaximumLength = MAXTEXTLEN; + Text.Length = 0; + + switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) { + case DeviceTextDescription: + status = RtlStringCbPrintfW(Buffer, + MAXTEXTLEN, + L"%hs PV Console #%hs", + FdoGetName(__PdoGetFdo(Pdo)), + __PdoGetName(Pdo)); + ASSERT(NT_SUCCESS(status)); + + Buffer += wcslen(Buffer); + + break; + + case DeviceTextLocationInformation: + status = RtlStringCbPrintfW(Buffer, + MAXTEXTLEN, + L"%hs", + __PdoGetName(Pdo)); + ASSERT(NT_SUCCESS(status)); + + Buffer += wcslen(Buffer); + + break; + + default: + ASSERT(FALSE); + break; + } + + Text.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Text.Buffer); + + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + + Trace("%s: %wZ\n", __PdoGetName(Pdo), &Text); + + Irp->IoStatus.Information = (ULONG_PTR)Text.Buffer; + status = STATUS_SUCCESS; + +done: + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoReadConfig( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(Pdo); + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_NOT_SUPPORTED; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoWriteConfig( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(Pdo); + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_NOT_SUPPORTED; +} + +#define REGSTR_VAL_MAX_HCID_LEN 1024 + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryId( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + PWCHAR Buffer; + UNICODE_STRING Id; + ULONG Type; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + switch (StackLocation->Parameters.QueryId.IdType) { + case BusQueryInstanceID: + Trace("BusQueryInstanceID\n"); + Id.MaximumLength = (USHORT)(strlen(__PdoGetName(Pdo)) + 1) * sizeof(WCHAR); + break; + + case BusQueryDeviceID: + Trace("BusQueryDeviceID\n"); + Id.MaximumLength = (MAX_DEVICE_ID_LEN - 2) * sizeof(WCHAR); + break; + + case BusQueryHardwareIDs: + Trace("BusQueryHardwareIDs\n"); + Id.MaximumLength = (USHORT)(MAX_DEVICE_ID_LEN * sizeof(WCHAR)); + break; + + case BusQueryCompatibleIDs: + Trace("BusQueryCompatibleIDs\n"); + Id.MaximumLength = (USHORT)(MAX_DEVICE_ID_LEN * sizeof(WCHAR)); + break; + + default: + Irp->IoStatus.Information = 0; + status = STATUS_NOT_SUPPORTED; + goto done; + } + + Buffer = ExAllocatePoolWithTag(PagedPool, Id.MaximumLength, PDO_POOL); + + status = STATUS_NO_MEMORY; + if (Buffer == NULL) + goto done; + + RtlZeroMemory(Buffer, Id.MaximumLength); + + Id.Buffer = Buffer; + Id.Length = 0; + + switch (StackLocation->Parameters.QueryId.IdType) { + case BusQueryInstanceID: + Type = REG_SZ; + + status = RtlStringCbPrintfW(Buffer, + Id.MaximumLength, + L"%hs", + __PdoGetName(Pdo)); + ASSERT(NT_SUCCESS(status)); + + Buffer += wcslen(Buffer); + + break; + + case BusQueryDeviceID: + Type = REG_SZ; + + status = RtlStringCbPrintfW(Buffer, + Id.MaximumLength, + L"XENCONS\\VEN_%hs&DEV_CONS", + __PdoGetVendorName(Pdo)); + ASSERT(NT_SUCCESS(status)); + + Buffer += wcslen(Buffer); + + break; + + case BusQueryHardwareIDs: + case BusQueryCompatibleIDs: + { + ULONG Length; + + Type = REG_MULTI_SZ; + + Length = Id.MaximumLength; + + status = RtlStringCbPrintfW(Buffer, + Length, + L"XENCONS\\VEN_%hs&DEV_CONS", + __PdoGetVendorName(Pdo)); + ASSERT(NT_SUCCESS(status)); + + Buffer += wcslen(Buffer); + Length -= (ULONG)(wcslen(Buffer) * sizeof(WCHAR)); + + Buffer++; + Length -= sizeof(WCHAR); + + status = RtlStringCbPrintfW(Buffer, + Length, + L"XENDEVICE"); + ASSERT(NT_SUCCESS(status)); + + Buffer += wcslen(Buffer); + Buffer++; + + ASSERT3U((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer, <, + REGSTR_VAL_MAX_HCID_LEN); + break; + } + default: + Type = REG_NONE; + + ASSERT(FALSE); + break; + } + + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + + Id.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer); + Buffer = Id.Buffer; + + switch (Type) { + case REG_SZ: + Trace("- %ws\n", Buffer); + break; + + case REG_MULTI_SZ: + do { + Trace("- %ws\n", Buffer); + Buffer += wcslen(Buffer); + Buffer++; + } while (*Buffer != L'\0'); + break; + + default: + ASSERT(FALSE); + break; + } + + Irp->IoStatus.Information = (ULONG_PTR)Id.Buffer; + status = STATUS_SUCCESS; + +done: + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryBusInformation( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PPNP_BUS_INFORMATION Info; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Pdo); + + Info = ExAllocatePoolWithTag(PagedPool, sizeof(PNP_BUS_INFORMATION), PDO_POOL); + + status = STATUS_NO_MEMORY; + if (Info == NULL) + goto done; + + RtlZeroMemory(Info, sizeof(PNP_BUS_INFORMATION)); + + Info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL; + Info->LegacyBusType = PNPBus; + Info->BusNumber = 0; + + Irp->IoStatus.Information = (ULONG_PTR)Info; + status = STATUS_SUCCESS; + +done: + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + + +static NTSTATUS +PdoDelegateIrp( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + return FdoDelegateIrp(Pdo->Fdo, Irp); +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoDeviceUsageNotification( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + status = PdoDelegateIrp(Pdo, Irp); + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoEject( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PXENCONS_FDO Fdo = __PdoGetFdo(Pdo); + NTSTATUS status; + + Trace("%s\n", __PdoGetName(Pdo)); + + FdoAcquireMutex(Fdo); + + __PdoSetDevicePnpState(Pdo, Deleted); + __PdoSetMissing(Pdo, "device ejected"); + + FdoReleaseMutex(Fdo); + + IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo), + BusRelations); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoDispatchPnp( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + UCHAR MinorFunction; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + MinorFunction = StackLocation->MinorFunction; + + Trace("====> (%s) (%02x:%s)\n", + __PdoGetName(Pdo), + MinorFunction, + PnpMinorFunctionName(MinorFunction)); + + switch (StackLocation->MinorFunction) { + case IRP_MN_START_DEVICE: + status = PdoStartDevice(Pdo, Irp); + break; + + case IRP_MN_QUERY_STOP_DEVICE: + status = PdoQueryStopDevice(Pdo, Irp); + break; + + case IRP_MN_CANCEL_STOP_DEVICE: + status = PdoCancelStopDevice(Pdo, Irp); + break; + + case IRP_MN_STOP_DEVICE: + status = PdoStopDevice(Pdo, Irp); + break; + + case IRP_MN_QUERY_REMOVE_DEVICE: + status = PdoQueryRemoveDevice(Pdo, Irp); + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + status = PdoCancelRemoveDevice(Pdo, Irp); + break; + + case IRP_MN_SURPRISE_REMOVAL: + status = PdoSurpriseRemoval(Pdo, Irp); + break; + + case IRP_MN_REMOVE_DEVICE: + status = PdoRemoveDevice(Pdo, Irp); + break; + + case IRP_MN_QUERY_DEVICE_RELATIONS: + status = PdoQueryDeviceRelations(Pdo, Irp); + break; + + case IRP_MN_QUERY_CAPABILITIES: + status = PdoQueryCapabilities(Pdo, Irp); + break; + + case IRP_MN_QUERY_DEVICE_TEXT: + status = PdoQueryDeviceText(Pdo, Irp); + break; + + case IRP_MN_READ_CONFIG: + status = PdoReadConfig(Pdo, Irp); + break; + + case IRP_MN_WRITE_CONFIG: + status = PdoWriteConfig(Pdo, Irp); + break; + + case IRP_MN_QUERY_ID: + status = PdoQueryId(Pdo, Irp); + break; + + case IRP_MN_QUERY_BUS_INFORMATION: + status = PdoQueryBusInformation(Pdo, Irp); + break; + + case IRP_MN_DEVICE_USAGE_NOTIFICATION: + status = PdoDeviceUsageNotification(Pdo, Irp); + break; + + case IRP_MN_EJECT: + status = PdoEject(Pdo, Irp); + break; + + default: + status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + break; + } + + Trace("<==== (%02x:%s)(%08x)\n", + MinorFunction, + PnpMinorFunctionName(MinorFunction), + status); + + return status; +} + +static FORCEINLINE NTSTATUS +__PdoSetDevicePower( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + DEVICE_POWER_STATE DeviceState; + POWER_ACTION PowerAction; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + DeviceState = StackLocation->Parameters.Power.State.DeviceState; + PowerAction = StackLocation->Parameters.Power.ShutdownType; + + Trace("====> (%s) (%s:%s)\n", + __PdoGetName(Pdo), + PowerDeviceStateName(DeviceState), + PowerActionName(PowerAction)); + + ASSERT3U(PowerAction, <, PowerActionShutdown); + + if (__PdoGetDevicePowerState(Pdo) > DeviceState) { + Trace("%s: POWERING UP: %s -> %s\n", + __PdoGetName(Pdo), + PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)), + PowerDeviceStateName(DeviceState)); + + ASSERT3U(DeviceState, == , PowerDeviceD0); + status = PdoD3ToD0(Pdo); + ASSERT(NT_SUCCESS(status)); + } else if (__PdoGetDevicePowerState(Pdo) < DeviceState) { + Trace("%s: POWERING DOWN: %s -> %s\n", + __PdoGetName(Pdo), + PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)), + PowerDeviceStateName(DeviceState)); + + ASSERT3U(DeviceState, == , PowerDeviceD3); + PdoD0ToD3(Pdo); + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + Trace("<==== (%s:%s)\n", + PowerDeviceStateName(DeviceState), + PowerActionName(PowerAction)); + + return STATUS_SUCCESS; +} + +static NTSTATUS +PdoDevicePower( + IN PXENCONS_THREAD Self, + IN PVOID Context + ) +{ + PXENCONS_PDO Pdo = Context; + PKEVENT Event; + + Event = ThreadGetEvent(Self); + + for (;;) { + PIRP Irp; + + if (Pdo->DevicePowerIrp == NULL) { + (VOID)KeWaitForSingleObject(Event, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(Event); + } + + if (ThreadIsAlerted(Self)) + break; + + Irp = Pdo->DevicePowerIrp; + + if (Irp == NULL) + continue; + + Pdo->DevicePowerIrp = NULL; + KeMemoryBarrier(); + + (VOID)__PdoSetDevicePower(Pdo, Irp); + } + + return STATUS_SUCCESS; +} + +static FORCEINLINE NTSTATUS +__PdoSetSystemPower( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + SYSTEM_POWER_STATE SystemState; + POWER_ACTION PowerAction; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + SystemState = StackLocation->Parameters.Power.State.SystemState; + PowerAction = StackLocation->Parameters.Power.ShutdownType; + + Trace("====> (%s) (%s:%s)\n", + __PdoGetName(Pdo), + PowerSystemStateName(SystemState), + PowerActionName(PowerAction)); + + ASSERT3U(PowerAction, <, PowerActionShutdown); + + if (__PdoGetSystemPowerState(Pdo) > SystemState) { + if (SystemState < PowerSystemHibernate && + __PdoGetSystemPowerState(Pdo) >= PowerSystemHibernate) { + __PdoSetSystemPowerState(Pdo, PowerSystemHibernate); + PdoS4ToS3(Pdo); + } + + Trace("%s: POWERING UP: %s -> %s\n", + __PdoGetName(Pdo), + PowerSystemStateName(__PdoGetSystemPowerState(Pdo)), + PowerSystemStateName(SystemState)); + } else if (__PdoGetSystemPowerState(Pdo) < SystemState) { + Trace("%s: POWERING DOWN: %s -> %s\n", + __PdoGetName(Pdo), + PowerSystemStateName(__PdoGetSystemPowerState(Pdo)), + PowerSystemStateName(SystemState)); + + if (SystemState >= PowerSystemHibernate && + __PdoGetSystemPowerState(Pdo) < PowerSystemHibernate) { + __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3); + PdoS3ToS4(Pdo); + } + } + + __PdoSetSystemPowerState(Pdo, SystemState); + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + Trace("<==== (%s:%s)\n", + PowerSystemStateName(SystemState), + PowerActionName(PowerAction)); + + return STATUS_SUCCESS; +} + +static NTSTATUS +PdoSystemPower( + IN PXENCONS_THREAD Self, + IN PVOID Context + ) +{ + PXENCONS_PDO Pdo = Context; + PKEVENT Event; + + Event = ThreadGetEvent(Self); + + for (;;) { + PIRP Irp; + + if (Pdo->SystemPowerIrp == NULL) { + (VOID)KeWaitForSingleObject(Event, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(Event); + } + + if (ThreadIsAlerted(Self)) + break; + + Irp = Pdo->SystemPowerIrp; + + if (Irp == NULL) + continue; + + Pdo->SystemPowerIrp = NULL; + KeMemoryBarrier(); + + (VOID)__PdoSetSystemPower(Pdo, Irp); + } + + return STATUS_SUCCESS; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoSetPower( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + POWER_STATE_TYPE PowerType; + POWER_ACTION PowerAction; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + PowerType = StackLocation->Parameters.Power.Type; + PowerAction = StackLocation->Parameters.Power.ShutdownType; + + if (PowerAction >= PowerActionShutdown) { + Irp->IoStatus.Status = STATUS_SUCCESS; + + status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + goto done; + } + + switch (PowerType) { + case DevicePowerState: + IoMarkIrpPending(Irp); + + ASSERT3P(Pdo->DevicePowerIrp, == , NULL); + Pdo->DevicePowerIrp = Irp; + KeMemoryBarrier(); + + ThreadWake(Pdo->DevicePowerThread); + + status = STATUS_PENDING; + break; + + case SystemPowerState: + IoMarkIrpPending(Irp); + + ASSERT3P(Pdo->SystemPowerIrp, == , NULL); + Pdo->SystemPowerIrp = Irp; + KeMemoryBarrier(); + + ThreadWake(Pdo->SystemPowerThread); + + status = STATUS_PENDING; + break; + + default: + status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + break; + } + +done: + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoQueryPower( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Pdo); + + Irp->IoStatus.Status = STATUS_SUCCESS; + + status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoDispatchPower( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + UCHAR MinorFunction; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + MinorFunction = StackLocation->MinorFunction; + + switch (StackLocation->MinorFunction) { + case IRP_MN_SET_POWER: + status = PdoSetPower(Pdo, Irp); + break; + + case IRP_MN_QUERY_POWER: + status = PdoQueryPower(Pdo, Irp); + break; + + default: + status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + break; + } + + return status; +} + +static DECLSPEC_NOINLINE NTSTATUS +PdoDispatchDefault( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Pdo); + + status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +PdoDispatch( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + switch (StackLocation->MajorFunction) { + case IRP_MJ_PNP: + status = PdoDispatchPnp(Pdo, Irp); + break; + + case IRP_MJ_POWER: + status = PdoDispatchPower(Pdo, Irp); + break; + + default: + status = PdoDispatchDefault(Pdo, Irp); + break; + } + + return status; +} + +NTSTATUS +PdoResume( + IN PXENCONS_PDO Pdo + ) +{ + UNREFERENCED_PARAMETER(Pdo); + return STATUS_SUCCESS; +} + +VOID +PdoSuspend( + IN PXENCONS_PDO Pdo + ) +{ + UNREFERENCED_PARAMETER(Pdo); +} + +NTSTATUS +PdoCreate( + IN PXENCONS_FDO Fdo, + IN PANSI_STRING Name + ) +{ + PDEVICE_OBJECT PhysicalDeviceObject; + PXENCONS_DX Dx; + PXENCONS_PDO Pdo; + NTSTATUS status; + +#pragma prefast(suppress:28197) // Possibly leaking memory 'PhysicalDeviceObject' + status = IoCreateDevice(DriverGetDriverObject(), + sizeof(XENCONS_DX), + NULL, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN | FILE_AUTOGENERATED_DEVICE_NAME, + FALSE, + &PhysicalDeviceObject); + if (!NT_SUCCESS(status)) + goto fail1; + + Dx = (PXENCONS_DX)PhysicalDeviceObject->DeviceExtension; + RtlZeroMemory(Dx, sizeof(XENCONS_DX)); + + Dx->Type = PHYSICAL_DEVICE_OBJECT; + Dx->DeviceObject = PhysicalDeviceObject; + Dx->DevicePnpState = Present; + Dx->SystemPowerState = PowerSystemWorking; + Dx->DevicePowerState = PowerDeviceD3; + + Pdo = __PdoAllocate(sizeof(XENCONS_PDO)); + + status = STATUS_NO_MEMORY; + if (Pdo == NULL) + goto fail2; + + Pdo->Dx = Dx; + Pdo->Fdo = Fdo; + + status = ThreadCreate(PdoSystemPower, Pdo, &Pdo->SystemPowerThread); + if (!NT_SUCCESS(status)) + goto fail3; + + status = ThreadCreate(PdoDevicePower, Pdo, &Pdo->DevicePowerThread); + if (!NT_SUCCESS(status)) + goto fail4; + + __PdoSetName(Pdo, Name); + + FdoGetSuspendInterface(Fdo, &Pdo->SuspendInterface); + + Dx->Pdo = Pdo; + + status = FdoAddPhysicalDeviceObject(Fdo, Pdo); + if (!NT_SUCCESS(status)) + goto fail5; + + status = STATUS_UNSUCCESSFUL; + if (__PdoIsEjectRequested(Pdo)) + goto fail6; + + Info("%p (%s)\n", + PhysicalDeviceObject, + __PdoGetName(Pdo)); + + PhysicalDeviceObject->Flags |= DO_BUFFERED_IO; + PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + return STATUS_SUCCESS; + +fail6: + Error("fail6\n"); + + FdoRemovePhysicalDeviceObject(Fdo, Pdo); + +fail5: + Error("fail5\n"); + + (VOID)__PdoClearEjectRequested(Pdo); + + Dx->Pdo = NULL; + + RtlZeroMemory(&Pdo->SuspendInterface, + sizeof(XENBUS_SUSPEND_INTERFACE)); + + ThreadAlert(Pdo->DevicePowerThread); + ThreadJoin(Pdo->DevicePowerThread); + Pdo->DevicePowerThread = NULL; + +fail4: + Error("fail4\n"); + + ThreadAlert(Pdo->SystemPowerThread); + ThreadJoin(Pdo->SystemPowerThread); + Pdo->SystemPowerThread = NULL; + +fail3: + Error("fail3\n"); + + Pdo->Fdo = NULL; + Pdo->Dx = NULL; + + ASSERT(IsZeroMemory(Pdo, sizeof(XENCONS_PDO))); + __PdoFree(Pdo); + +fail2: + Error("fail2\n"); + + IoDeleteDevice(PhysicalDeviceObject); + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +VOID +PdoDestroy( + IN PXENCONS_PDO Pdo + ) +{ + PXENCONS_DX Dx = Pdo->Dx; + PDEVICE_OBJECT PhysicalDeviceObject = Dx->DeviceObject; + PXENCONS_FDO Fdo = __PdoGetFdo(Pdo); + + ASSERT3U(__PdoGetDevicePnpState(Pdo), == , Deleted); + + ASSERT(__PdoIsMissing(Pdo)); + Pdo->Missing = FALSE; + + Info("%p (%s) (%s)\n", + PhysicalDeviceObject, + __PdoGetName(Pdo), + Pdo->Reason); + + Pdo->Reason = NULL; + + FdoRemovePhysicalDeviceObject(Fdo, Pdo); + + (VOID)__PdoClearEjectRequested(Pdo); + + Dx->Pdo = NULL; + + RtlZeroMemory(&Pdo->SuspendInterface, + sizeof(XENBUS_SUSPEND_INTERFACE)); + + ThreadAlert(Pdo->DevicePowerThread); + ThreadJoin(Pdo->DevicePowerThread); + Pdo->DevicePowerThread = NULL; + + ThreadAlert(Pdo->SystemPowerThread); + ThreadJoin(Pdo->SystemPowerThread); + Pdo->SystemPowerThread = NULL; + + Pdo->Fdo = NULL; + Pdo->Dx = NULL; + + ASSERT(IsZeroMemory(Pdo, sizeof(XENCONS_PDO))); + __PdoFree(Pdo); + + IoDeleteDevice(PhysicalDeviceObject); +} diff --git a/src/xencons/pdo.h b/src/xencons/pdo.h new file mode 100755 index 0000000..2164542 --- /dev/null +++ b/src/xencons/pdo.h @@ -0,0 +1,114 @@ +/* Copyright (c) Citrix Systems Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, +* with or without modification, are permitted provided +* that the following conditions are met: +* +* * Redistributions of source code must retain the above +* copyright notice, this list of conditions and the +* following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +*/ + +#ifndef _XENCONS_PDO_H +#define _XENCONS_PDO_H + +#include <ntddk.h> + +#include "driver.h" +#include "types.h" + +extern VOID +PdoSetDevicePnpState( + IN PXENCONS_PDO Pdo, + IN DEVICE_PNP_STATE State + ); + +extern DEVICE_PNP_STATE +PdoGetDevicePnpState( + IN PXENCONS_PDO Pdo + ); + +extern VOID +PdoSetMissing( + IN PXENCONS_PDO Pdo, + IN const CHAR *Reason + ); + +extern BOOLEAN +PdoIsMissing( + IN PXENCONS_PDO Pdo + ); + +extern VOID +PdoRequestEject( + IN PXENCONS_PDO Pdo + ); + +extern BOOLEAN +PdoIsEjectRequested( + IN PXENCONS_PDO Pdo + ); + +extern PCHAR +PdoGetName( + IN PXENCONS_PDO Pdo + ); + +extern PXENCONS_FDO +PdoGetFdo( + IN PXENCONS_PDO Pdo + ); + +extern PDEVICE_OBJECT +PdoGetDeviceObject( + IN PXENCONS_PDO Pdo + ); + +extern NTSTATUS +PdoCreate( + IN PXENCONS_FDO Fdo, + IN PANSI_STRING Name + ); + +extern NTSTATUS +PdoResume( + IN PXENCONS_PDO Pdo + ); + +extern VOID +PdoSuspend( + IN PXENCONS_PDO Pdo + ); + +extern VOID +PdoDestroy( + IN PXENCONS_PDO Pdo + ); + +extern NTSTATUS +PdoDispatch( + IN PXENCONS_PDO Pdo, + IN PIRP Irp + ); + +#endif // _XENCONS_PDO_H diff --git a/vs2015/xencons/xencons.vcxproj b/vs2015/xencons/xencons.vcxproj index 12f5b70..2e9d208 100644 --- a/vs2015/xencons/xencons.vcxproj +++ b/vs2015/xencons/xencons.vcxproj @@ -66,6 +66,7 @@ <ItemGroup> <ClCompile Include="../../src/xencons/driver.c" /> <ClCompile Include="../../src/xencons/fdo.c" /> + <ClCompile Include="../../src/xencons/pdo.c" /> <ClCompile Include="../../src/xencons/registry.c" /> <ClCompile Include="../../src/xencons/console.c" /> <ClCompile Include="../../src/xencons/stream.c" /> -- 2.8.3 _______________________________________________ win-pv-devel mailing list win-pv-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/win-pv-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |