https://git.reactos.org/?p=reactos.git;a=commitdiff;h=582ca686965e51629df88c...
commit 582ca686965e51629df88cdb340a2480060cd638 Author: Victor Perevertkin victor.perevertkin@reactos.org AuthorDate: Sun Nov 29 16:41:58 2020 +0300 Commit: Victor Perevertkin victor.perevertkin@reactos.org CommitDate: Sat Dec 5 22:25:40 2020 +0300
[NTOS:PNP] Improve and refactor PnP notifications
- BUGFIX: do not call IoGetRelatedTargetDevice while guarded mutex is acquired (the function issues an APC, but they are disabled inside a critical section) - BUGFIX: only the beginning of a structure for GUID_PNP_CUSTOM_NOTIFICATION was copied and queued. Just pass it as-is to a subscriber, without copying - Don't convert event GUID to string, store and compare GUID struct itself - Split IopNotifyPlugPlayNotification into 3 functions for each type of notification (less stack usage and for future changes) - Move initialization code for notifications into a separate routine - Use separate lists and locks for every type of notification - Put "TargetDeviceChange" notifications into their place inside DEVICE_NODE --- ntoskrnl/include/internal/io.h | 43 ++- ntoskrnl/io/iomgr/deviface.c | 8 +- ntoskrnl/io/iomgr/iomgr.c | 7 +- ntoskrnl/io/pnpmgr/devaction.c | 18 +- ntoskrnl/io/pnpmgr/devnode.c | 3 + ntoskrnl/io/pnpmgr/pnpnotify.c | 660 ++++++++++++++++++++++++----------------- ntoskrnl/io/pnpmgr/pnpreport.c | 6 +- 7 files changed, 435 insertions(+), 310 deletions(-)
diff --git a/ntoskrnl/include/internal/io.h b/ntoskrnl/include/internal/io.h index d0e0bb5e94e..628929f1cff 100644 --- a/ntoskrnl/include/internal/io.h +++ b/ntoskrnl/include/internal/io.h @@ -609,20 +609,6 @@ IopInitDriverImplementation( VOID );
-VOID -IopInitPnpNotificationImplementation( - VOID -); - -VOID -IopNotifyPlugPlayNotification( - IN PDEVICE_OBJECT DeviceObject, - IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, - IN LPCGUID Event, - IN PVOID EventCategoryData1, - IN PVOID EventCategoryData2 -); - NTSTATUS IopGetSystemPowerDeviceObject( IN PDEVICE_OBJECT *DeviceObject @@ -828,6 +814,10 @@ IoInitializeCrashDump( IN HANDLE PageFileHandle );
+VOID +PiInitializeNotifications( + VOID); + // // Device/Volume Routines // @@ -926,6 +916,12 @@ IopDereferenceDeviceObject( IN BOOLEAN ForceUnload );
+NTSTATUS +NTAPI +IopGetRelatedTargetDevice( + IN PFILE_OBJECT FileObject, + OUT PDEVICE_NODE *DeviceNode); + NTSTATUS NTAPI IoGetRelatedTargetDevice( @@ -1412,6 +1408,25 @@ PiPerformSyncDeviceAction( _In_ PDEVICE_OBJECT DeviceObject, _In_ DEVICE_ACTION Action);
+// +// PnP notifications +// +VOID +PiNotifyDeviceInterfaceChange( + _In_ LPCGUID Event, + _In_ LPCGUID InterfaceClassGuid, + _In_ PUNICODE_STRING SymbolicLinkName); + +VOID +PiNotifyHardwareProfileChange( + _In_ LPCGUID Event); + +VOID +PiNotifyTargetDeviceChange( + _In_ LPCGUID Event, + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PTARGET_DEVICE_CUSTOM_NOTIFICATION CustomNotification); + // // Global I/O Data // diff --git a/ntoskrnl/io/iomgr/deviface.c b/ntoskrnl/io/iomgr/deviface.c index 34245027627..f909d536461 100644 --- a/ntoskrnl/io/iomgr/deviface.c +++ b/ntoskrnl/io/iomgr/deviface.c @@ -1463,12 +1463,8 @@ IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName, ExFreePoolWithTag(DeviceInstance.Buffer, TAG_IO);
EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL; - IopNotifyPlugPlayNotification( - PhysicalDeviceObject, - EventCategoryDeviceInterfaceChange, - EventGuid, - &DeviceGuid, - (PVOID)SymbolicLinkName); + + PiNotifyDeviceInterfaceChange(EventGuid, &DeviceGuid, SymbolicLinkName);
ObDereferenceObject(PhysicalDeviceObject); DPRINT("Status %x\n", Status); diff --git a/ntoskrnl/io/iomgr/iomgr.c b/ntoskrnl/io/iomgr/iomgr.c index 0173cf4911d..d7dc1bcb8c4 100644 --- a/ntoskrnl/io/iomgr/iomgr.c +++ b/ntoskrnl/io/iomgr/iomgr.c @@ -59,14 +59,12 @@ extern POBJECT_TYPE IoAdapterObjectType; extern ERESOURCE IopDatabaseResource; ERESOURCE IopSecurityResource; extern ERESOURCE IopDriverLoadResource; -extern KGUARDED_MUTEX PnpNotifyListLock; extern LIST_ENTRY IopDiskFileSystemQueueHead; extern LIST_ENTRY IopCdRomFileSystemQueueHead; extern LIST_ENTRY IopTapeFileSystemQueueHead; extern LIST_ENTRY IopNetworkFileSystemQueueHead; extern LIST_ENTRY DriverBootReinitListHead; extern LIST_ENTRY DriverReinitListHead; -extern LIST_ENTRY PnpNotifyListHead; extern LIST_ENTRY IopFsNotifyChangeQueueHead; extern LIST_ENTRY IopErrorLogListHead; extern LIST_ENTRY IopTimerQueueHead; @@ -480,14 +478,12 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock) ExInitializeResourceLite(&IopDatabaseResource); ExInitializeResourceLite(&IopSecurityResource); ExInitializeResourceLite(&IopDriverLoadResource); - KeInitializeGuardedMutex(&PnpNotifyListLock); InitializeListHead(&IopDiskFileSystemQueueHead); InitializeListHead(&IopCdRomFileSystemQueueHead); InitializeListHead(&IopTapeFileSystemQueueHead); InitializeListHead(&IopNetworkFileSystemQueueHead); InitializeListHead(&DriverBootReinitListHead); InitializeListHead(&DriverReinitListHead); - InitializeListHead(&PnpNotifyListHead); InitializeListHead(&ShutdownListHead); InitializeListHead(&LastChanceShutdownListHead); InitializeListHead(&IopFsNotifyChangeQueueHead); @@ -498,6 +494,9 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock) KeInitializeSpinLock(&ShutdownListLock); KeInitializeSpinLock(&IopLogListLock);
+ /* Initialize PnP notifications */ + PiInitializeNotifications(); + /* Initialize the reserve IRP */ if (!IopInitializeReserveIrp(&IopReserveIrpAllocator)) { diff --git a/ntoskrnl/io/pnpmgr/devaction.c b/ntoskrnl/io/pnpmgr/devaction.c index e6479074f32..4f5fb6b3884 100644 --- a/ntoskrnl/io/pnpmgr/devaction.c +++ b/ntoskrnl/io/pnpmgr/devaction.c @@ -1513,11 +1513,7 @@ IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject) /* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */ IopSynchronousCall(DeviceObject, &Stack, &Dummy);
- IopNotifyPlugPlayNotification(DeviceObject, - EventCategoryTargetDeviceChange, - &GUID_TARGET_DEVICE_REMOVE_COMPLETE, - NULL, - NULL); + PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_COMPLETE, DeviceObject, NULL); ObDereferenceObject(DeviceObject); }
@@ -1592,11 +1588,7 @@ IopCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject) /* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */ IopSynchronousCall(DeviceObject, &Stack, &Dummy);
- IopNotifyPlugPlayNotification(DeviceObject, - EventCategoryTargetDeviceChange, - &GUID_TARGET_DEVICE_REMOVE_CANCELLED, - NULL, - NULL); + PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_CANCELLED, DeviceObject, NULL); }
static @@ -1692,11 +1684,7 @@ IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
- IopNotifyPlugPlayNotification(DeviceObject, - EventCategoryTargetDeviceChange, - &GUID_TARGET_DEVICE_QUERY_REMOVE, - NULL, - NULL); + PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_QUERY_REMOVE, DeviceObject, NULL);
if (!NT_SUCCESS(Status)) { diff --git a/ntoskrnl/io/pnpmgr/devnode.c b/ntoskrnl/io/pnpmgr/devnode.c index a068cd71ddb..230fcce100b 100644 --- a/ntoskrnl/io/pnpmgr/devnode.c +++ b/ntoskrnl/io/pnpmgr/devnode.c @@ -114,6 +114,7 @@ IopCreateDeviceNode( }
RtlZeroMemory(Node, sizeof(DEVICE_NODE)); + InitializeListHead(&Node->TargetDeviceNotify);
if (!ServiceName) ServiceName1 = &UnknownDeviceName; @@ -259,6 +260,8 @@ IopFreeDeviceNode( /* All children must be deleted before a parent is deleted */ ASSERT(!DeviceNode->Child); ASSERT(DeviceNode->PhysicalDeviceObject); + /* No notifications should be registered for this device */ + ASSERT(IsListEmpty(&DeviceNode->TargetDeviceNotify));
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
diff --git a/ntoskrnl/io/pnpmgr/pnpnotify.c b/ntoskrnl/io/pnpmgr/pnpnotify.c index 089215071fd..cc169aba171 100644 --- a/ntoskrnl/io/pnpmgr/pnpnotify.c +++ b/ntoskrnl/io/pnpmgr/pnpnotify.c @@ -1,11 +1,11 @@ /* - * PROJECT: ReactOS Kernel - * COPYRIGHT: GPL - See COPYING in the top level directory - * FILE: ntoskrnl/io/pnpmgr/pnpnotify.c - * PURPOSE: Plug & Play notification functions - * PROGRAMMERS: Filip Navara (xnavara@volny.cz) - * Herv� Poussineau (hpoussin@reactos.org) - * Pierre Schweitzer + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Plug & Play notification functions + * COPYRIGHT: Copyright 2003 Filip Navara xnavara@volny.cz + * Copyright 2005-2006 Hervé Poussineau hpoussin@reactos.org + * Copyright 2010 Pierre Schweitzer pierre@reactos.org + * Copyright 2020 Victor Perevertkin victor.perevertkin@reactos.org */
/* INCLUDES ******************************************************************/ @@ -14,215 +14,309 @@ #define NDEBUG #include <debug.h>
-/* TYPES *******************************************************************/ +/* DATA **********************************************************************/ + +KGUARDED_MUTEX PiNotifyTargetDeviceLock; +KGUARDED_MUTEX PiNotifyHwProfileLock; +KGUARDED_MUTEX PiNotifyDeviceInterfaceLock; + +_Guarded_by_(PiNotifyHwProfileLock) +LIST_ENTRY PiNotifyHwProfileListHead; + +_Guarded_by_(PiNotifyDeviceInterfaceLock) +LIST_ENTRY PiNotifyDeviceInterfaceListHead; + +/* TYPES *********************************************************************/
typedef struct _PNP_NOTIFY_ENTRY { LIST_ENTRY PnpNotifyList; - IO_NOTIFICATION_EVENT_CATEGORY EventCategory; PVOID Context; - UNICODE_STRING Guid; - PFILE_OBJECT FileObject; PDRIVER_OBJECT DriverObject; PDRIVER_NOTIFICATION_CALLBACK_ROUTINE PnpNotificationProc; + union + { + GUID Guid; // for EventCategoryDeviceInterfaceChange + struct + { + PFILE_OBJECT FileObject; // for EventCategoryTargetDeviceChange + PDEVICE_OBJECT DeviceObject; + }; + }; + IO_NOTIFICATION_EVENT_CATEGORY EventCategory; + UINT8 RefCount; + BOOLEAN Deleted; } PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY;
-KGUARDED_MUTEX PnpNotifyListLock; -LIST_ENTRY PnpNotifyListHead; - /* FUNCTIONS *****************************************************************/
+CODE_SEG("INIT") VOID -IopNotifyPlugPlayNotification( - IN PDEVICE_OBJECT DeviceObject, - IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, - IN LPCGUID Event, - IN PVOID EventCategoryData1, - IN PVOID EventCategoryData2) +PiInitializeNotifications(VOID) { - PPNP_NOTIFY_ENTRY ChangeEntry; - PLIST_ENTRY ListEntry; - PVOID NotificationStructure; - BOOLEAN CallCurrentEntry; - UNICODE_STRING GuidString; - NTSTATUS Status; - PDEVICE_OBJECT EntryDeviceObject = NULL; + KeInitializeGuardedMutex(&PiNotifyTargetDeviceLock); + KeInitializeGuardedMutex(&PiNotifyHwProfileLock); + KeInitializeGuardedMutex(&PiNotifyDeviceInterfaceLock); + InitializeListHead(&PiNotifyHwProfileListHead); + InitializeListHead(&PiNotifyDeviceInterfaceListHead); +}
- ASSERT(DeviceObject); +static +CODE_SEG("PAGE") +VOID +PiDereferencePnpNotifyEntry( + _In_ PPNP_NOTIFY_ENTRY Entry) +{ + PAGED_CODE(); + ASSERT(Entry->RefCount > 0);
- KeAcquireGuardedMutex(&PnpNotifyListLock); - if (IsListEmpty(&PnpNotifyListHead)) + ObDereferenceObject(Entry->DriverObject); + Entry->RefCount--; + if (Entry->RefCount == 0) { - KeReleaseGuardedMutex(&PnpNotifyListLock); - return; - } + ASSERT(Entry->Deleted);
- switch (EventCategory) - { - case EventCategoryDeviceInterfaceChange: - { - PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos; - NotificationStructure = NotificationInfos = ExAllocatePoolWithTag( - PagedPool, - sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION), - TAG_PNP_NOTIFY); - if (!NotificationInfos) - { - KeReleaseGuardedMutex(&PnpNotifyListLock); - return; - } - NotificationInfos->Version = 1; - NotificationInfos->Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION); - RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID)); - RtlCopyMemory(&NotificationInfos->InterfaceClassGuid, EventCategoryData1, sizeof(GUID)); - NotificationInfos->SymbolicLinkName = (PUNICODE_STRING)EventCategoryData2; - Status = RtlStringFromGUID(&NotificationInfos->InterfaceClassGuid, &GuidString); - if (!NT_SUCCESS(Status)) - { - KeReleaseGuardedMutex(&PnpNotifyListLock); - ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY); - return; - } - break; - } - case EventCategoryHardwareProfileChange: - { - PHWPROFILE_CHANGE_NOTIFICATION NotificationInfos; - NotificationStructure = NotificationInfos = ExAllocatePoolWithTag( - PagedPool, - sizeof(HWPROFILE_CHANGE_NOTIFICATION), - TAG_PNP_NOTIFY); - if (!NotificationInfos) - { - KeReleaseGuardedMutex(&PnpNotifyListLock); - return; - } - NotificationInfos->Version = 1; - NotificationInfos->Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION); - RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID)); - break; - } - case EventCategoryTargetDeviceChange: - { - if (Event != &GUID_PNP_CUSTOM_NOTIFICATION) - { - PTARGET_DEVICE_REMOVAL_NOTIFICATION NotificationInfos; - NotificationStructure = NotificationInfos = ExAllocatePoolWithTag( - PagedPool, - sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION), - TAG_PNP_NOTIFY); - if (!NotificationInfos) - { - KeReleaseGuardedMutex(&PnpNotifyListLock); - return; - } - NotificationInfos->Version = 1; - NotificationInfos->Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION); - RtlCopyMemory(&NotificationInfos->Event, Event, sizeof(GUID)); - } - else - { - PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationInfos; - NotificationStructure = NotificationInfos = ExAllocatePoolWithTag( - PagedPool, - sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION), - TAG_PNP_NOTIFY); - if (!NotificationInfos) - { - KeReleaseGuardedMutex(&PnpNotifyListLock); - return; - } - RtlCopyMemory(NotificationInfos, EventCategoryData1, sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION)); - } - break; - } - default: + RemoveEntryList(&Entry->PnpNotifyList); + if (Entry->EventCategory == EventCategoryTargetDeviceChange) { - DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory); - KeReleaseGuardedMutex(&PnpNotifyListLock); - return; + // IopGetRelatedTargetDevice referenced the device upon the notification registration + ObDereferenceObject(Entry->DeviceObject); } + ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY); } +} + +static +CODE_SEG("PAGE") +VOID +PiReferencePnpNotifyEntry( + _In_ PPNP_NOTIFY_ENTRY Entry) +{ + PAGED_CODE(); + ObReferenceObject(Entry->DriverObject); + Entry->RefCount++; +} + +/** + * @brief Calls PnP notification routine and makes some checks to detect faulty drivers + */ +static +CODE_SEG("PAGE") +VOID +PiCallNotifyProc( + _In_ PDRIVER_NOTIFICATION_CALLBACK_ROUTINE Proc, + _In_ PVOID NotificationStructure, + _In_ PVOID Context) +{ + PAGED_CODE(); +#if DBG + KIRQL oldIrql = KeGetCurrentIrql(); + ULONG oldApcDisable = KeGetCurrentThread()->CombinedApcDisable; +#endif + + Proc(NotificationStructure, Context); + + ASSERT(oldIrql == KeGetCurrentIrql() && + oldApcDisable == KeGetCurrentThread()->CombinedApcDisable); +} + +static +CODE_SEG("PAGE") +_Requires_lock_held_(Lock) +VOID +PiProcessSingleNotification( + _In_ PPNP_NOTIFY_ENTRY Entry, + _In_ PVOID NotificationStructure, + _In_ PKGUARDED_MUTEX Lock, + _Out_ PLIST_ENTRY *NextEntry) +{ + PAGED_CODE(); + + // the notification may be unregistered inside the procedure + // thus reference the entry so we may proceed + PiReferencePnpNotifyEntry(Entry); + + // release the lock because the notification routine has to be called without any + // limitations regarding APCs + KeReleaseGuardedMutex(Lock); + PiCallNotifyProc(Entry->PnpNotificationProc, NotificationStructure, Entry->Context); + KeAcquireGuardedMutex(Lock); + + // take the next entry link only after the callback finishes + // the lock is not held there, so Entry may have changed at this point + *NextEntry = Entry->PnpNotifyList.Flink; + PiDereferencePnpNotifyEntry(Entry); +} + +/** + * @brief Delivers the event to all drivers subscribed to + * EventCategoryDeviceInterfaceChange + * + * @param[in] Event The PnP event GUID + * @param[in] InterfaceClassGuid The GUID of an interface class + * @param[in] SymbolicLinkName Pointer to a string identifying the device interface name + */ +CODE_SEG("PAGE") +VOID +PiNotifyDeviceInterfaceChange( + _In_ LPCGUID Event, + _In_ LPCGUID InterfaceClassGuid, + _In_ PUNICODE_STRING SymbolicLinkName) +{ + PAGED_CODE(); + + PDEVICE_INTERFACE_CHANGE_NOTIFICATION notifyStruct; + notifyStruct = ExAllocatePoolWithTag(PagedPool, sizeof(*notifyStruct), TAG_PNP_NOTIFY); + if (!notifyStruct) + { + return; + } + + *notifyStruct = (DEVICE_INTERFACE_CHANGE_NOTIFICATION) { + .Version = 1, + .Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION), + .Event = *Event, + .InterfaceClassGuid = *InterfaceClassGuid, + .SymbolicLinkName = SymbolicLinkName + }; + + DPRINT("Delivering a DeviceInterfaceChange PnP event\n");
- /* Loop through procedures registred in PnpNotifyListHead - * list to find those that meet some criteria. - */ - ListEntry = PnpNotifyListHead.Flink; - while (ListEntry != &PnpNotifyListHead) + KeAcquireGuardedMutex(&PiNotifyDeviceInterfaceLock); + + PLIST_ENTRY entry = PiNotifyDeviceInterfaceListHead.Flink; + while (entry != &PiNotifyDeviceInterfaceListHead) { - ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY, PnpNotifyList); - CallCurrentEntry = FALSE; + PPNP_NOTIFY_ENTRY nEntry = CONTAINING_RECORD(entry, PNP_NOTIFY_ENTRY, PnpNotifyList);
- if (ChangeEntry->EventCategory != EventCategory) + if (!IsEqualGUID(¬ifyStruct->InterfaceClassGuid, &nEntry->Guid)) { - ListEntry = ListEntry->Flink; + entry = entry->Flink; continue; }
- switch (EventCategory) - { - case EventCategoryDeviceInterfaceChange: - { - if (RtlCompareUnicodeString(&ChangeEntry->Guid, &GuidString, FALSE) == 0) - { - CallCurrentEntry = TRUE; - } - break; - } - case EventCategoryHardwareProfileChange: - { - CallCurrentEntry = TRUE; - break; - } - case EventCategoryTargetDeviceChange: - { - Status = IoGetRelatedTargetDevice(ChangeEntry->FileObject, &EntryDeviceObject); - if (NT_SUCCESS(Status)) - { - if (DeviceObject == EntryDeviceObject) - { - if (Event == &GUID_PNP_CUSTOM_NOTIFICATION) - { - ((PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure)->FileObject = ChangeEntry->FileObject; - } - else - { - ((PTARGET_DEVICE_REMOVAL_NOTIFICATION)NotificationStructure)->FileObject = ChangeEntry->FileObject; - } - CallCurrentEntry = TRUE; - } - } - break; - } - default: - { - DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory); - break; - } - } + PiProcessSingleNotification(nEntry, notifyStruct, &PiNotifyDeviceInterfaceLock, &entry); + } + + KeReleaseGuardedMutex(&PiNotifyDeviceInterfaceLock); + ExFreePoolWithTag(notifyStruct, TAG_PNP_NOTIFY); +} + + +/** + * @brief Delivers the event to all drivers subscribed to + * EventCategoryHardwareProfileChange PnP event + * + * @param[in] Event The PnP event GUID + */ +CODE_SEG("PAGE") +VOID +PiNotifyHardwareProfileChange( + _In_ LPCGUID Event) +{ + PAGED_CODE(); + + PHWPROFILE_CHANGE_NOTIFICATION notifyStruct; + notifyStruct = ExAllocatePoolWithTag(PagedPool, sizeof(*notifyStruct), TAG_PNP_NOTIFY); + if (!notifyStruct) + { + return; + } + + *notifyStruct = (HWPROFILE_CHANGE_NOTIFICATION) { + .Version = 1, + .Size = sizeof(HWPROFILE_CHANGE_NOTIFICATION), + .Event = *Event + }; + + DPRINT("Delivering a HardwareProfileChange PnP event\n"); + + KeAcquireGuardedMutex(&PiNotifyHwProfileLock); + + PLIST_ENTRY entry = PiNotifyHwProfileListHead.Flink; + while (entry != &PiNotifyHwProfileListHead) + { + PPNP_NOTIFY_ENTRY nEntry = CONTAINING_RECORD(entry, PNP_NOTIFY_ENTRY, PnpNotifyList); + + PiProcessSingleNotification(nEntry, notifyStruct, &PiNotifyHwProfileLock, &entry); + } + + KeReleaseGuardedMutex(&PiNotifyHwProfileLock); + ExFreePoolWithTag(notifyStruct, TAG_PNP_NOTIFY); +} + +/** + * @brief Delivers the event to all drivers subscribed to + * EventCategoryTargetDeviceChange PnP event + * + * @param[in] Event The PnP event GUID + * @param[in] DeviceObject The (target) device object + * @param[in] CustomNotification Pointer to a custom notification for GUID_PNP_CUSTOM_NOTIFICATION + */ +CODE_SEG("PAGE") +VOID +PiNotifyTargetDeviceChange( + _In_ LPCGUID Event, + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PTARGET_DEVICE_CUSTOM_NOTIFICATION CustomNotification) +{ + PAGED_CODE();
- /* Move to the next element now, as callback may unregister itself */ - ListEntry = ListEntry->Flink; - /* FIXME: If ListEntry was the last element and that callback registers - * new notifications, those won't be checked... */ + PVOID notificationStruct; + // just in case our device is removed during the operation + ObReferenceObject(DeviceObject);
- if (CallCurrentEntry) + PDEVICE_NODE deviceNode = IopGetDeviceNode(DeviceObject); + ASSERT(deviceNode); + + if (!IsEqualGUID(Event, &GUID_PNP_CUSTOM_NOTIFICATION)) + { + PTARGET_DEVICE_REMOVAL_NOTIFICATION notifStruct; + notifStruct = ExAllocatePoolWithTag(PagedPool, sizeof(*notifStruct), TAG_PNP_NOTIFY); + if (!notifStruct) { - /* Call entry into new allocated memory */ - DPRINT("IopNotifyPlugPlayNotification(): found suitable callback %p\n", - ChangeEntry); - - KeReleaseGuardedMutex(&PnpNotifyListLock); - (ChangeEntry->PnpNotificationProc)(NotificationStructure, - ChangeEntry->Context); - KeAcquireGuardedMutex(&PnpNotifyListLock); + return; }
+ *notifStruct = (TARGET_DEVICE_REMOVAL_NOTIFICATION) { + .Version = 1, + .Size = sizeof(TARGET_DEVICE_REMOVAL_NOTIFICATION), + .Event = *Event + }; + + notificationStruct = notifStruct; + + DPRINT("Delivering a (non-custom) TargetDeviceChange PnP event\n"); + } + else + { + ASSERT(CustomNotification); + // assuming everythng else is correct + + notificationStruct = CustomNotification; + + DPRINT("Delivering a (custom) TargetDeviceChange PnP event\n"); + } + + KeAcquireGuardedMutex(&PiNotifyTargetDeviceLock); + + PLIST_ENTRY entry = deviceNode->TargetDeviceNotify.Flink; + while (entry != &deviceNode->TargetDeviceNotify) + { + PPNP_NOTIFY_ENTRY nEntry = CONTAINING_RECORD(entry, PNP_NOTIFY_ENTRY, PnpNotifyList); + + // put the file object from our saved entry to this particular notification's struct + ((PTARGET_DEVICE_REMOVAL_NOTIFICATION)notificationStruct)->FileObject = nEntry->FileObject; + // so you don't need to look at the definition ;) + C_ASSERT(FIELD_OFFSET(TARGET_DEVICE_REMOVAL_NOTIFICATION, FileObject) + == FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, FileObject)); + + PiProcessSingleNotification(nEntry, notificationStruct, &PiNotifyTargetDeviceLock, &entry); } - KeReleaseGuardedMutex(&PnpNotifyListLock); - ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY); - if (EventCategory == EventCategoryDeviceInterfaceChange) - RtlFreeUnicodeString(&GuidString); + + KeReleaseGuardedMutex(&PiNotifyTargetDeviceLock); + ExFreePoolWithTag(notificationStruct, TAG_PNP_NOTIFY); + ObDereferenceObject(DeviceObject); }
/* PUBLIC FUNCTIONS **********************************************************/ @@ -232,10 +326,11 @@ IopNotifyPlugPlayNotification( */ ULONG NTAPI -IoPnPDeliverServicePowerNotification(ULONG VetoedPowerOperation OPTIONAL, - ULONG PowerNotification, - ULONG Unknown OPTIONAL, - BOOLEAN Synchronous) +IoPnPDeliverServicePowerNotification( + _In_ ULONG VetoedPowerOperation, + _In_ ULONG PowerNotificationCode, + _In_ ULONG PowerNotificationData, + _In_ BOOLEAN Synchronous) { UNIMPLEMENTED; return 0; @@ -244,15 +339,17 @@ IoPnPDeliverServicePowerNotification(ULONG VetoedPowerOperation OPTIONAL, /* * @implemented */ +CODE_SEG("PAGE") NTSTATUS NTAPI -IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, - IN ULONG EventCategoryFlags, - IN PVOID EventCategoryData OPTIONAL, - IN PDRIVER_OBJECT DriverObject, - IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine, - IN PVOID Context, - OUT PVOID *NotificationEntry) +IoRegisterPlugPlayNotification( + _In_ IO_NOTIFICATION_EVENT_CATEGORY EventCategory, + _In_ ULONG EventCategoryFlags, + _In_opt_ PVOID EventCategoryData, + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine, + _Inout_opt_ PVOID Context, + _Out_ PVOID *NotificationEntry) { PPNP_NOTIFY_ENTRY Entry; PWSTR SymbolicLinkList; @@ -260,18 +357,12 @@ IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, PAGED_CODE();
DPRINT("%s(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n", - __FUNCTION__, - EventCategory, - EventCategoryFlags, - DriverObject); + __FUNCTION__, EventCategory, EventCategoryFlags, DriverObject);
ObReferenceObject(DriverObject);
/* Try to allocate entry for notification before sending any notification */ - Entry = ExAllocatePoolWithTag(NonPagedPool, - sizeof(PNP_NOTIFY_ENTRY), - TAG_PNP_NOTIFY); - + Entry = ExAllocatePoolWithTag(PagedPool, sizeof(PNP_NOTIFY_ENTRY), TAG_PNP_NOTIFY); if (!Entry) { DPRINT("ExAllocatePool() failed\n"); @@ -279,83 +370,99 @@ IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, return STATUS_INSUFFICIENT_RESOURCES; }
- if (EventCategory == EventCategoryDeviceInterfaceChange && - EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES) - { - DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos; - UNICODE_STRING SymbolicLinkU; - PWSTR SymbolicLink; - - Status = IoGetDeviceInterfaces((LPGUID)EventCategoryData, - NULL, /* PhysicalDeviceObject OPTIONAL */ - 0, /* Flags */ - &SymbolicLinkList); - if (NT_SUCCESS(Status)) - { - /* Enumerate SymbolicLinkList */ - NotificationInfos.Version = 1; - NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION); - RtlCopyMemory(&NotificationInfos.Event, - &GUID_DEVICE_INTERFACE_ARRIVAL, - sizeof(GUID)); - RtlCopyMemory(&NotificationInfos.InterfaceClassGuid, - EventCategoryData, - sizeof(GUID)); - NotificationInfos.SymbolicLinkName = &SymbolicLinkU; - - for (SymbolicLink = SymbolicLinkList; - *SymbolicLink; - SymbolicLink += wcslen(SymbolicLink) + 1) - { - RtlInitUnicodeString(&SymbolicLinkU, SymbolicLink); - DPRINT("Calling callback routine for %S\n", SymbolicLink); - (*CallbackRoutine)(&NotificationInfos, Context); - } - - ExFreePool(SymbolicLinkList); - } - } + *Entry = (PNP_NOTIFY_ENTRY) { + .PnpNotificationProc = CallbackRoutine, + .Context = Context, + .DriverObject = DriverObject, + .EventCategory = EventCategory, + .RefCount = 1 + };
- Entry->PnpNotificationProc = CallbackRoutine; - Entry->EventCategory = EventCategory; - Entry->Context = Context; - Entry->DriverObject = DriverObject; switch (EventCategory) { case EventCategoryDeviceInterfaceChange: { - Status = RtlStringFromGUID(EventCategoryData, &Entry->Guid); - if (!NT_SUCCESS(Status)) + Entry->Guid = *(LPGUID)EventCategoryData; + + // first register the notification + KeAcquireGuardedMutex(&PiNotifyDeviceInterfaceLock); + InsertTailList(&PiNotifyDeviceInterfaceListHead, &Entry->PnpNotifyList); + KeReleaseGuardedMutex(&PiNotifyDeviceInterfaceLock); + + // then process existing interfaces if asked + if (EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES) { - ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY); - ObDereferenceObject(DriverObject); - return Status; + DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos; + UNICODE_STRING SymbolicLinkU; + PWSTR SymbolicLink; + + Status = IoGetDeviceInterfaces((LPGUID)EventCategoryData, + NULL, /* PhysicalDeviceObject OPTIONAL */ + 0, /* Flags */ + &SymbolicLinkList); + if (NT_SUCCESS(Status)) + { + /* Enumerate SymbolicLinkList */ + NotificationInfos.Version = 1; + NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION); + NotificationInfos.Event = GUID_DEVICE_INTERFACE_ARRIVAL; + NotificationInfos.InterfaceClassGuid = *(LPGUID)EventCategoryData; + NotificationInfos.SymbolicLinkName = &SymbolicLinkU; + + for (SymbolicLink = SymbolicLinkList; + *SymbolicLink; + SymbolicLink += (SymbolicLinkU.Length / sizeof(WCHAR)) + 1) + { + RtlInitUnicodeString(&SymbolicLinkU, SymbolicLink); + DPRINT("Calling callback routine for %S\n", SymbolicLink); + PiCallNotifyProc(CallbackRoutine, &NotificationInfos, Context); + } + + ExFreePool(SymbolicLinkList); + } } break; } case EventCategoryHardwareProfileChange: { - /* nothing to do */ - break; + KeAcquireGuardedMutex(&PiNotifyHwProfileLock); + InsertTailList(&PiNotifyHwProfileListHead, &Entry->PnpNotifyList); + KeReleaseGuardedMutex(&PiNotifyHwProfileLock); + break; } case EventCategoryTargetDeviceChange: { + PDEVICE_NODE deviceNode; Entry->FileObject = (PFILE_OBJECT)EventCategoryData; + + // NOTE: the device node's PDO is referenced here + Status = IopGetRelatedTargetDevice(Entry->FileObject, &deviceNode); + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(DriverObject); + ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY); + return Status; + } + // save it so we can dereference it later + Entry->DeviceObject = deviceNode->PhysicalDeviceObject; + + // each DEVICE_NODE has its own registered notifications list + KeAcquireGuardedMutex(&PiNotifyTargetDeviceLock); + InsertTailList(&deviceNode->TargetDeviceNotify, &Entry->PnpNotifyList); + KeReleaseGuardedMutex(&PiNotifyTargetDeviceLock); break; } default: { DPRINT1("%s: unknown EventCategory 0x%x UNIMPLEMENTED\n", __FUNCTION__, EventCategory); - break; + + ObDereferenceObject(DriverObject); + ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY); + return STATUS_NOT_SUPPORTED; } }
- KeAcquireGuardedMutex(&PnpNotifyListLock); - InsertHeadList(&PnpNotifyListHead, - &Entry->PnpNotifyList); - KeReleaseGuardedMutex(&PnpNotifyListLock); - DPRINT("%s returns NotificationEntry %p\n", __FUNCTION__, Entry);
*NotificationEntry = Entry; @@ -366,25 +473,46 @@ IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, /* * @implemented */ +CODE_SEG("PAGE") NTSTATUS NTAPI -IoUnregisterPlugPlayNotification(IN PVOID NotificationEntry) +IoUnregisterPlugPlayNotification( + _In_ PVOID NotificationEntry) { - PPNP_NOTIFY_ENTRY Entry; + PPNP_NOTIFY_ENTRY Entry = NotificationEntry; + PKGUARDED_MUTEX Lock; + PAGED_CODE();
- Entry = (PPNP_NOTIFY_ENTRY)NotificationEntry; DPRINT("%s(NotificationEntry %p) called\n", __FUNCTION__, Entry);
- KeAcquireGuardedMutex(&PnpNotifyListLock); - RemoveEntryList(&Entry->PnpNotifyList); - KeReleaseGuardedMutex(&PnpNotifyListLock); - - RtlFreeUnicodeString(&Entry->Guid); - - ObDereferenceObject(Entry->DriverObject); + switch (Entry->EventCategory) + { + case EventCategoryDeviceInterfaceChange: + Lock = &PiNotifyDeviceInterfaceLock; + break; + case EventCategoryHardwareProfileChange: + Lock = &PiNotifyHwProfileLock; + break; + case EventCategoryTargetDeviceChange: + Lock = &PiNotifyTargetDeviceLock; + break; + default: + UNREACHABLE; + return STATUS_NOT_SUPPORTED; + }
- ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY); + KeAcquireGuardedMutex(Lock); + if (!Entry->Deleted) + { + Entry->Deleted = TRUE; // so it can't be unregistered two times + PiDereferencePnpNotifyEntry(Entry); + } + else + { + DPRINT1("IoUnregisterPlugPlayNotification called two times for 0x%p\n", NotificationEntry); + } + KeReleaseGuardedMutex(Lock);
return STATUS_SUCCESS; } diff --git a/ntoskrnl/io/pnpmgr/pnpreport.c b/ntoskrnl/io/pnpmgr/pnpreport.c index cb60a133329..ae0ac9566d3 100644 --- a/ntoskrnl/io/pnpmgr/pnpreport.c +++ b/ntoskrnl/io/pnpmgr/pnpreport.c @@ -131,11 +131,7 @@ PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject, }
/* That call is totally wrong but notifications handler must be fixed first */ - IopNotifyPlugPlayNotification(DeviceObject, - EventCategoryTargetDeviceChange, - &GUID_PNP_CUSTOM_NOTIFICATION, - NotificationStructure, - NULL); + PiNotifyTargetDeviceChange(&GUID_PNP_CUSTOM_NOTIFICATION, DeviceObject, NotificationStructure);
if (SyncEvent) {