[NTOS:PNP] Improve and refactor PnP notifications 3094/head
authorVictor Perevertkin <victor.perevertkin@reactos.org>
Sun, 29 Nov 2020 13:41:58 +0000 (16:41 +0300)
committerVictor Perevertkin <victor.perevertkin@reactos.org>
Sat, 5 Dec 2020 19:25:40 +0000 (22:25 +0300)
- 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
ntoskrnl/io/iomgr/deviface.c
ntoskrnl/io/iomgr/iomgr.c
ntoskrnl/io/pnpmgr/devaction.c
ntoskrnl/io/pnpmgr/devnode.c
ntoskrnl/io/pnpmgr/pnpnotify.c
ntoskrnl/io/pnpmgr/pnpreport.c

index d0e0bb5..628929f 100644 (file)
@@ -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
 //
index 3424502..f909d53 100644 (file)
@@ -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);
index 0173cf4..d7dc1bc 100644 (file)
@@ -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))
     {
index e647907..4f5fb6b 100644 (file)
@@ -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))
     {
index a068cd7..230fcce 100644 (file)
@@ -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);
 
index 0892150..cc169ab 100644 (file)
@@ -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 ******************************************************************/
 #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(&notifyStruct->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;
 }
index cb60a13..ae0ac95 100644 (file)
@@ -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)
     {