From 2d71daf03483dbe5e074f18e546e942116234984 Mon Sep 17 00:00:00 2001 From: Owen Smith Date: Thu, 1 Mar 2018 13:57:48 +0000 Subject: [PATCH] Enumerate non-default consoles * Add enumeration thread and watch * Non-default consoles will fail any read/write/iocontrol IRPs Signed-off-by: Owen Smith Whitespace fix, and introduce __PdoSetDefault() rather than setting flag directly in PdoCreate() Signed-off-by: Paul Durrant --- src/xencons/fdo.c | 318 +++++++++++++++++++++++++++++++++++++++++++++- src/xencons/pdo.c | 79 ++++++++++-- src/xencons/pdo.h | 5 + 3 files changed, 393 insertions(+), 9 deletions(-) diff --git a/src/xencons/fdo.c b/src/xencons/fdo.c index 45a1fdd..e7feaee 100644 --- a/src/xencons/fdo.c +++ b/src/xencons/fdo.c @@ -85,6 +85,9 @@ struct _XENCONS_FDO { CHAR VendorName[MAXNAMELEN]; + PXENCONS_THREAD ScanThread; + KEVENT ScanEvent; + PXENBUS_STORE_WATCH ScanWatch; MUTEX Mutex; ULONG References; @@ -653,6 +656,9 @@ done: RemoveEntryList(&Dx->ListEntry); ASSERT3U(Fdo->References, != , 0); --Fdo->References; + + if (Fdo->ScanThread) + ThreadWake(Fdo->ScanThread); } static FORCEINLINE VOID @@ -690,6 +696,114 @@ FdoReleaseMutex( 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 the PDO is the default console, it wont exist in the + // the device list, as its statically created + if (PdoIsDefault(Pdo)) { + ListEntry = Next; + continue; + } + + 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 @@ -768,6 +882,125 @@ __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, @@ -989,15 +1222,39 @@ __FdoD3ToD0( IN PXENCONS_FDO Fdo ) { + NTSTATUS status; + Trace("====>\n"); ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL); (VOID) FdoSetDistribution(Fdo); + status = XENBUS_STORE(WatchAdd, + &Fdo->StoreInterface, + "device", + "console", + ThreadGetEvent(Fdo->ScanThread), + &Fdo->ScanWatch); + if (!NT_SUCCESS(status)) + goto fail1; + + (VOID)XENBUS_STORE(Printf, + &Fdo->StoreInterface, + NULL, + "feature/hotplug", + "console", + "%u", + TRUE); + Trace("<====\n"); return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; } static FORCEINLINE VOID @@ -1009,6 +1266,17 @@ __FdoD0ToD3( ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL); + (VOID)XENBUS_STORE(Remove, + &Fdo->StoreInterface, + NULL, + "feature/hotplug", + "console"); + + (VOID)XENBUS_STORE(WatchRemove, + &Fdo->StoreInterface, + Fdo->ScanWatch); + Fdo->ScanWatch = NULL; + FdoClearDistribution(Fdo); Trace("<====\n"); @@ -1221,17 +1489,31 @@ 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"); @@ -1292,6 +1574,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); @@ -1387,6 +1675,17 @@ 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; @@ -1414,6 +1713,12 @@ FdoRemoveDevice( 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: @@ -1463,6 +1768,17 @@ FdoQueryDeviceRelations( 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; diff --git a/src/xencons/pdo.c b/src/xencons/pdo.c index a3fabdf..1783c89 100644 --- a/src/xencons/pdo.c +++ b/src/xencons/pdo.c @@ -71,6 +71,7 @@ struct _XENCONS_PDO { XENBUS_SUSPEND_INTERFACE SuspendInterface; PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate; + BOOLEAN IsDefault; PXENCONS_CONSOLE Console; }; @@ -286,6 +287,14 @@ __PdoGetName( return Dx->Name; } +PCHAR +PdoGetName( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoGetName(Pdo); +} + static FORCEINLINE PCHAR __PdoGetVendorName( IN PXENCONS_PDO Pdo @@ -294,6 +303,32 @@ __PdoGetVendorName( return FdoGetVendorName(__PdoGetFdo(Pdo)); } +static FORCEINLINE BOOLEAN +__PdoIsDefault( + IN PXENCONS_PDO Pdo + ) +{ + return Pdo->IsDefault; +} + +BOOLEAN +PdoIsDefault( + IN PXENCONS_PDO Pdo + ) +{ + return __PdoIsDefault(Pdo); +} + +static FORCEINLINE VOID +__PdoSetDefault( + IN PXENCONS_PDO Pdo, + IN PANSI_STRING Device + ) +{ + Pdo->IsDefault = (Device == NULL) ? TRUE : FALSE; +} + + static FORCEINLINE BOOLEAN __PdoSetEjectRequested( IN PXENCONS_PDO Pdo @@ -441,7 +476,11 @@ PdoD3ToD0( KeLowerIrql(Irql); - status = ConsoleD3ToD0(Pdo->Console); + if (__PdoIsDefault(Pdo)) + status = ConsoleD3ToD0(Pdo->Console); + else + status = STATUS_SUCCESS; + if (!NT_SUCCESS(status)) goto fail4; @@ -495,7 +534,8 @@ PdoD0ToD3( #pragma prefast(suppress:28123) (VOID) IoSetDeviceInterfaceState(&Pdo->Dx->Link, FALSE); - ConsoleD0ToD3(Pdo->Console); + if (__PdoIsDefault(Pdo)) + ConsoleD0ToD3(Pdo->Console); KeRaiseIrql(DISPATCH_LEVEL, &Irql); @@ -1689,7 +1729,10 @@ PdoDispatchCreate( StackLocation = IoGetCurrentIrpStackLocation(Irp); - status = ConsoleOpen(Pdo->Console, StackLocation->FileObject); + if (__PdoIsDefault(Pdo)) + status = ConsoleOpen(Pdo->Console, StackLocation->FileObject); + else + status = STATUS_SUCCESS; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); @@ -1708,7 +1751,10 @@ PdoDispatchCleanup( StackLocation = IoGetCurrentIrpStackLocation(Irp); - status = ConsoleClose(Pdo->Console, StackLocation->FileObject); + if (__PdoIsDefault(Pdo)) + status = ConsoleClose(Pdo->Console, StackLocation->FileObject); + else + status = STATUS_SUCCESS; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); @@ -1742,7 +1788,10 @@ PdoDispatchReadWriteControl( { NTSTATUS status; - status = ConsolePutQueue(Pdo->Console, Irp); + if (__PdoIsDefault(Pdo)) + status = ConsolePutQueue(Pdo->Console, Irp); + else + status = STATUS_DEVICE_NOT_READY; if (status == STATUS_PENDING) { IoMarkIrpPending(Irp); @@ -1891,7 +1940,13 @@ PdoCreate( Dx->Pdo = Pdo; - status = ConsoleCreate(Fdo, &Pdo->Console); + __PdoSetDefault(Pdo, Device); + + if (__PdoIsDefault(Pdo)) + status = ConsoleCreate(Fdo, &Pdo->Console); + else + status = STATUS_SUCCESS; + if (!NT_SUCCESS(status)) goto fail5; @@ -1921,9 +1976,13 @@ fail6: (VOID)__PdoClearEjectRequested(Pdo); - ConsoleDestroy(Pdo->Console); + if (__PdoIsDefault(Pdo)) + ConsoleDestroy(Pdo->Console); + Pdo->Console = NULL; + Pdo->IsDefault = FALSE; + fail5: Error("fail5\n"); @@ -1990,9 +2049,13 @@ PdoDestroy( Dx->Pdo = NULL; - ConsoleDestroy(Pdo->Console); + if (__PdoIsDefault(Pdo)) + ConsoleDestroy(Pdo->Console); + Pdo->Console = NULL; + Pdo->IsDefault = FALSE; + RtlFreeUnicodeString(&Pdo->Dx->Link); RtlZeroMemory(&Pdo->SuspendInterface, diff --git a/src/xencons/pdo.h b/src/xencons/pdo.h index 0605483..85f73a3 100644 --- a/src/xencons/pdo.h +++ b/src/xencons/pdo.h @@ -83,6 +83,11 @@ PdoGetDeviceObject( IN PXENCONS_PDO Pdo ); +extern BOOLEAN +PdoIsDefault( + IN PXENCONS_PDO Pdo + ); + extern NTSTATUS PdoCreate( IN PXENCONS_FDO Fdo, -- 2.39.5