[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
This commit is contained in:
Victor Perevertkin 2020-11-29 16:41:58 +03:00
parent e3198fb644
commit 582ca68696
No known key found for this signature in database
GPG key ID: C750B7222E9C7830
7 changed files with 442 additions and 317 deletions

View file

@ -609,20 +609,6 @@ IopInitDriverImplementation(
VOID 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 NTSTATUS
IopGetSystemPowerDeviceObject( IopGetSystemPowerDeviceObject(
IN PDEVICE_OBJECT *DeviceObject IN PDEVICE_OBJECT *DeviceObject
@ -828,6 +814,10 @@ IoInitializeCrashDump(
IN HANDLE PageFileHandle IN HANDLE PageFileHandle
); );
VOID
PiInitializeNotifications(
VOID);
// //
// Device/Volume Routines // Device/Volume Routines
// //
@ -926,6 +916,12 @@ IopDereferenceDeviceObject(
IN BOOLEAN ForceUnload IN BOOLEAN ForceUnload
); );
NTSTATUS
NTAPI
IopGetRelatedTargetDevice(
IN PFILE_OBJECT FileObject,
OUT PDEVICE_NODE *DeviceNode);
NTSTATUS NTSTATUS
NTAPI NTAPI
IoGetRelatedTargetDevice( IoGetRelatedTargetDevice(
@ -1412,6 +1408,25 @@ PiPerformSyncDeviceAction(
_In_ PDEVICE_OBJECT DeviceObject, _In_ PDEVICE_OBJECT DeviceObject,
_In_ DEVICE_ACTION Action); _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 // Global I/O Data
// //

View file

@ -1463,12 +1463,8 @@ IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
ExFreePoolWithTag(DeviceInstance.Buffer, TAG_IO); ExFreePoolWithTag(DeviceInstance.Buffer, TAG_IO);
EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL; EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
IopNotifyPlugPlayNotification(
PhysicalDeviceObject, PiNotifyDeviceInterfaceChange(EventGuid, &DeviceGuid, SymbolicLinkName);
EventCategoryDeviceInterfaceChange,
EventGuid,
&DeviceGuid,
(PVOID)SymbolicLinkName);
ObDereferenceObject(PhysicalDeviceObject); ObDereferenceObject(PhysicalDeviceObject);
DPRINT("Status %x\n", Status); DPRINT("Status %x\n", Status);

View file

@ -59,14 +59,12 @@ extern POBJECT_TYPE IoAdapterObjectType;
extern ERESOURCE IopDatabaseResource; extern ERESOURCE IopDatabaseResource;
ERESOURCE IopSecurityResource; ERESOURCE IopSecurityResource;
extern ERESOURCE IopDriverLoadResource; extern ERESOURCE IopDriverLoadResource;
extern KGUARDED_MUTEX PnpNotifyListLock;
extern LIST_ENTRY IopDiskFileSystemQueueHead; extern LIST_ENTRY IopDiskFileSystemQueueHead;
extern LIST_ENTRY IopCdRomFileSystemQueueHead; extern LIST_ENTRY IopCdRomFileSystemQueueHead;
extern LIST_ENTRY IopTapeFileSystemQueueHead; extern LIST_ENTRY IopTapeFileSystemQueueHead;
extern LIST_ENTRY IopNetworkFileSystemQueueHead; extern LIST_ENTRY IopNetworkFileSystemQueueHead;
extern LIST_ENTRY DriverBootReinitListHead; extern LIST_ENTRY DriverBootReinitListHead;
extern LIST_ENTRY DriverReinitListHead; extern LIST_ENTRY DriverReinitListHead;
extern LIST_ENTRY PnpNotifyListHead;
extern LIST_ENTRY IopFsNotifyChangeQueueHead; extern LIST_ENTRY IopFsNotifyChangeQueueHead;
extern LIST_ENTRY IopErrorLogListHead; extern LIST_ENTRY IopErrorLogListHead;
extern LIST_ENTRY IopTimerQueueHead; extern LIST_ENTRY IopTimerQueueHead;
@ -480,14 +478,12 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
ExInitializeResourceLite(&IopDatabaseResource); ExInitializeResourceLite(&IopDatabaseResource);
ExInitializeResourceLite(&IopSecurityResource); ExInitializeResourceLite(&IopSecurityResource);
ExInitializeResourceLite(&IopDriverLoadResource); ExInitializeResourceLite(&IopDriverLoadResource);
KeInitializeGuardedMutex(&PnpNotifyListLock);
InitializeListHead(&IopDiskFileSystemQueueHead); InitializeListHead(&IopDiskFileSystemQueueHead);
InitializeListHead(&IopCdRomFileSystemQueueHead); InitializeListHead(&IopCdRomFileSystemQueueHead);
InitializeListHead(&IopTapeFileSystemQueueHead); InitializeListHead(&IopTapeFileSystemQueueHead);
InitializeListHead(&IopNetworkFileSystemQueueHead); InitializeListHead(&IopNetworkFileSystemQueueHead);
InitializeListHead(&DriverBootReinitListHead); InitializeListHead(&DriverBootReinitListHead);
InitializeListHead(&DriverReinitListHead); InitializeListHead(&DriverReinitListHead);
InitializeListHead(&PnpNotifyListHead);
InitializeListHead(&ShutdownListHead); InitializeListHead(&ShutdownListHead);
InitializeListHead(&LastChanceShutdownListHead); InitializeListHead(&LastChanceShutdownListHead);
InitializeListHead(&IopFsNotifyChangeQueueHead); InitializeListHead(&IopFsNotifyChangeQueueHead);
@ -498,6 +494,9 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
KeInitializeSpinLock(&ShutdownListLock); KeInitializeSpinLock(&ShutdownListLock);
KeInitializeSpinLock(&IopLogListLock); KeInitializeSpinLock(&IopLogListLock);
/* Initialize PnP notifications */
PiInitializeNotifications();
/* Initialize the reserve IRP */ /* Initialize the reserve IRP */
if (!IopInitializeReserveIrp(&IopReserveIrpAllocator)) if (!IopInitializeReserveIrp(&IopReserveIrpAllocator))
{ {

View file

@ -1513,11 +1513,7 @@ IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
/* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */ /* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */
IopSynchronousCall(DeviceObject, &Stack, &Dummy); IopSynchronousCall(DeviceObject, &Stack, &Dummy);
IopNotifyPlugPlayNotification(DeviceObject, PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_COMPLETE, DeviceObject, NULL);
EventCategoryTargetDeviceChange,
&GUID_TARGET_DEVICE_REMOVE_COMPLETE,
NULL,
NULL);
ObDereferenceObject(DeviceObject); ObDereferenceObject(DeviceObject);
} }
@ -1592,11 +1588,7 @@ IopCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
/* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */ /* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */
IopSynchronousCall(DeviceObject, &Stack, &Dummy); IopSynchronousCall(DeviceObject, &Stack, &Dummy);
IopNotifyPlugPlayNotification(DeviceObject, PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_CANCELLED, DeviceObject, NULL);
EventCategoryTargetDeviceChange,
&GUID_TARGET_DEVICE_REMOVE_CANCELLED,
NULL,
NULL);
} }
static static
@ -1692,11 +1684,7 @@ IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy); Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
IopNotifyPlugPlayNotification(DeviceObject, PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_QUERY_REMOVE, DeviceObject, NULL);
EventCategoryTargetDeviceChange,
&GUID_TARGET_DEVICE_QUERY_REMOVE,
NULL,
NULL);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {

View file

@ -114,6 +114,7 @@ IopCreateDeviceNode(
} }
RtlZeroMemory(Node, sizeof(DEVICE_NODE)); RtlZeroMemory(Node, sizeof(DEVICE_NODE));
InitializeListHead(&Node->TargetDeviceNotify);
if (!ServiceName) if (!ServiceName)
ServiceName1 = &UnknownDeviceName; ServiceName1 = &UnknownDeviceName;
@ -259,6 +260,8 @@ IopFreeDeviceNode(
/* All children must be deleted before a parent is deleted */ /* All children must be deleted before a parent is deleted */
ASSERT(!DeviceNode->Child); ASSERT(!DeviceNode->Child);
ASSERT(DeviceNode->PhysicalDeviceObject); ASSERT(DeviceNode->PhysicalDeviceObject);
/* No notifications should be registered for this device */
ASSERT(IsListEmpty(&DeviceNode->TargetDeviceNotify));
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql); KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);

View file

@ -1,11 +1,11 @@
/* /*
* PROJECT: ReactOS Kernel * PROJECT: ReactOS Kernel
* COPYRIGHT: GPL - See COPYING in the top level directory * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* FILE: ntoskrnl/io/pnpmgr/pnpnotify.c * PURPOSE: Plug & Play notification functions
* PURPOSE: Plug & Play notification functions * COPYRIGHT: Copyright 2003 Filip Navara <xnavara@volny.cz>
* PROGRAMMERS: Filip Navara (xnavara@volny.cz) * Copyright 2005-2006 Hervé Poussineau <hpoussin@reactos.org>
* Hervé Poussineau (hpoussin@reactos.org) * Copyright 2010 Pierre Schweitzer <pierre@reactos.org>
* Pierre Schweitzer * Copyright 2020 Victor Perevertkin <victor.perevertkin@reactos.org>
*/ */
/* INCLUDES ******************************************************************/ /* INCLUDES ******************************************************************/
@ -14,215 +14,309 @@
#define NDEBUG #define NDEBUG
#include <debug.h> #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 typedef struct _PNP_NOTIFY_ENTRY
{ {
LIST_ENTRY PnpNotifyList; LIST_ENTRY PnpNotifyList;
IO_NOTIFICATION_EVENT_CATEGORY EventCategory;
PVOID Context; PVOID Context;
UNICODE_STRING Guid;
PFILE_OBJECT FileObject;
PDRIVER_OBJECT DriverObject; PDRIVER_OBJECT DriverObject;
PDRIVER_NOTIFICATION_CALLBACK_ROUTINE PnpNotificationProc; 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; } PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY;
KGUARDED_MUTEX PnpNotifyListLock;
LIST_ENTRY PnpNotifyListHead;
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
CODE_SEG("INIT")
VOID VOID
IopNotifyPlugPlayNotification( PiInitializeNotifications(VOID)
IN PDEVICE_OBJECT DeviceObject,
IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
IN LPCGUID Event,
IN PVOID EventCategoryData1,
IN PVOID EventCategoryData2)
{ {
PPNP_NOTIFY_ENTRY ChangeEntry; KeInitializeGuardedMutex(&PiNotifyTargetDeviceLock);
PLIST_ENTRY ListEntry; KeInitializeGuardedMutex(&PiNotifyHwProfileLock);
PVOID NotificationStructure; KeInitializeGuardedMutex(&PiNotifyDeviceInterfaceLock);
BOOLEAN CallCurrentEntry; InitializeListHead(&PiNotifyHwProfileListHead);
UNICODE_STRING GuidString; InitializeListHead(&PiNotifyDeviceInterfaceListHead);
NTSTATUS Status; }
PDEVICE_OBJECT EntryDeviceObject = NULL;
ASSERT(DeviceObject); static
CODE_SEG("PAGE")
VOID
PiDereferencePnpNotifyEntry(
_In_ PPNP_NOTIFY_ENTRY Entry)
{
PAGED_CODE();
ASSERT(Entry->RefCount > 0);
KeAcquireGuardedMutex(&PnpNotifyListLock); ObDereferenceObject(Entry->DriverObject);
if (IsListEmpty(&PnpNotifyListHead)) Entry->RefCount--;
if (Entry->RefCount == 0)
{
ASSERT(Entry->Deleted);
RemoveEntryList(&Entry->PnpNotifyList);
if (Entry->EventCategory == EventCategoryTargetDeviceChange)
{
// 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)
{ {
KeReleaseGuardedMutex(&PnpNotifyListLock);
return; return;
} }
switch (EventCategory) *notifyStruct = (DEVICE_INTERFACE_CHANGE_NOTIFICATION) {
{ .Version = 1,
case EventCategoryDeviceInterfaceChange: .Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION),
{ .Event = *Event,
PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos; .InterfaceClassGuid = *InterfaceClassGuid,
NotificationStructure = NotificationInfos = ExAllocatePoolWithTag( .SymbolicLinkName = SymbolicLinkName
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:
{
DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
KeReleaseGuardedMutex(&PnpNotifyListLock);
return;
}
}
/* Loop through procedures registred in PnpNotifyListHead DPRINT("Delivering a DeviceInterfaceChange PnP event\n");
* list to find those that meet some criteria.
*/
ListEntry = PnpNotifyListHead.Flink;
while (ListEntry != &PnpNotifyListHead)
{
ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY, PnpNotifyList);
CallCurrentEntry = FALSE;
if (ChangeEntry->EventCategory != EventCategory) KeAcquireGuardedMutex(&PiNotifyDeviceInterfaceLock);
PLIST_ENTRY entry = PiNotifyDeviceInterfaceListHead.Flink;
while (entry != &PiNotifyDeviceInterfaceListHead)
{
PPNP_NOTIFY_ENTRY nEntry = CONTAINING_RECORD(entry, PNP_NOTIFY_ENTRY, PnpNotifyList);
if (!IsEqualGUID(&notifyStruct->InterfaceClassGuid, &nEntry->Guid))
{ {
ListEntry = ListEntry->Flink; entry = entry->Flink;
continue; continue;
} }
switch (EventCategory) PiProcessSingleNotification(nEntry, notifyStruct, &PiNotifyDeviceInterfaceLock, &entry);
{
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;
}
}
/* 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... */
if (CallCurrentEntry)
{
/* Call entry into new allocated memory */
DPRINT("IopNotifyPlugPlayNotification(): found suitable callback %p\n",
ChangeEntry);
KeReleaseGuardedMutex(&PnpNotifyListLock);
(ChangeEntry->PnpNotificationProc)(NotificationStructure,
ChangeEntry->Context);
KeAcquireGuardedMutex(&PnpNotifyListLock);
}
} }
KeReleaseGuardedMutex(&PnpNotifyListLock);
ExFreePoolWithTag(NotificationStructure, TAG_PNP_NOTIFY); KeReleaseGuardedMutex(&PiNotifyDeviceInterfaceLock);
if (EventCategory == EventCategoryDeviceInterfaceChange) ExFreePoolWithTag(notifyStruct, TAG_PNP_NOTIFY);
RtlFreeUnicodeString(&GuidString); }
/**
* @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();
PVOID notificationStruct;
// just in case our device is removed during the operation
ObReferenceObject(DeviceObject);
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)
{
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(&PiNotifyTargetDeviceLock);
ExFreePoolWithTag(notificationStruct, TAG_PNP_NOTIFY);
ObDereferenceObject(DeviceObject);
} }
/* PUBLIC FUNCTIONS **********************************************************/ /* PUBLIC FUNCTIONS **********************************************************/
@ -232,10 +326,11 @@ IopNotifyPlugPlayNotification(
*/ */
ULONG ULONG
NTAPI NTAPI
IoPnPDeliverServicePowerNotification(ULONG VetoedPowerOperation OPTIONAL, IoPnPDeliverServicePowerNotification(
ULONG PowerNotification, _In_ ULONG VetoedPowerOperation,
ULONG Unknown OPTIONAL, _In_ ULONG PowerNotificationCode,
BOOLEAN Synchronous) _In_ ULONG PowerNotificationData,
_In_ BOOLEAN Synchronous)
{ {
UNIMPLEMENTED; UNIMPLEMENTED;
return 0; return 0;
@ -244,15 +339,17 @@ IoPnPDeliverServicePowerNotification(ULONG VetoedPowerOperation OPTIONAL,
/* /*
* @implemented * @implemented
*/ */
CODE_SEG("PAGE")
NTSTATUS NTSTATUS
NTAPI NTAPI
IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, IoRegisterPlugPlayNotification(
IN ULONG EventCategoryFlags, _In_ IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
IN PVOID EventCategoryData OPTIONAL, _In_ ULONG EventCategoryFlags,
IN PDRIVER_OBJECT DriverObject, _In_opt_ PVOID EventCategoryData,
IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine, _In_ PDRIVER_OBJECT DriverObject,
IN PVOID Context, _In_ PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
OUT PVOID *NotificationEntry) _Inout_opt_ PVOID Context,
_Out_ PVOID *NotificationEntry)
{ {
PPNP_NOTIFY_ENTRY Entry; PPNP_NOTIFY_ENTRY Entry;
PWSTR SymbolicLinkList; PWSTR SymbolicLinkList;
@ -260,18 +357,12 @@ IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
PAGED_CODE(); PAGED_CODE();
DPRINT("%s(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n", DPRINT("%s(EventCategory 0x%x, EventCategoryFlags 0x%lx, DriverObject %p) called.\n",
__FUNCTION__, __FUNCTION__, EventCategory, EventCategoryFlags, DriverObject);
EventCategory,
EventCategoryFlags,
DriverObject);
ObReferenceObject(DriverObject); ObReferenceObject(DriverObject);
/* Try to allocate entry for notification before sending any notification */ /* Try to allocate entry for notification before sending any notification */
Entry = ExAllocatePoolWithTag(NonPagedPool, Entry = ExAllocatePoolWithTag(PagedPool, sizeof(PNP_NOTIFY_ENTRY), TAG_PNP_NOTIFY);
sizeof(PNP_NOTIFY_ENTRY),
TAG_PNP_NOTIFY);
if (!Entry) if (!Entry)
{ {
DPRINT("ExAllocatePool() failed\n"); DPRINT("ExAllocatePool() failed\n");
@ -279,83 +370,99 @@ IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
} }
if (EventCategory == EventCategoryDeviceInterfaceChange && *Entry = (PNP_NOTIFY_ENTRY) {
EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES) .PnpNotificationProc = CallbackRoutine,
{ .Context = Context,
DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos; .DriverObject = DriverObject,
UNICODE_STRING SymbolicLinkU; .EventCategory = EventCategory,
PWSTR SymbolicLink; .RefCount = 1
};
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->PnpNotificationProc = CallbackRoutine;
Entry->EventCategory = EventCategory;
Entry->Context = Context;
Entry->DriverObject = DriverObject;
switch (EventCategory) switch (EventCategory)
{ {
case EventCategoryDeviceInterfaceChange: case EventCategoryDeviceInterfaceChange:
{ {
Status = RtlStringFromGUID(EventCategoryData, &Entry->Guid); Entry->Guid = *(LPGUID)EventCategoryData;
if (!NT_SUCCESS(Status))
// 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); DEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationInfos;
ObDereferenceObject(DriverObject); UNICODE_STRING SymbolicLinkU;
return Status; 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; break;
} }
case EventCategoryHardwareProfileChange: case EventCategoryHardwareProfileChange:
{ {
/* nothing to do */ KeAcquireGuardedMutex(&PiNotifyHwProfileLock);
break; InsertTailList(&PiNotifyHwProfileListHead, &Entry->PnpNotifyList);
KeReleaseGuardedMutex(&PiNotifyHwProfileLock);
break;
} }
case EventCategoryTargetDeviceChange: case EventCategoryTargetDeviceChange:
{ {
PDEVICE_NODE deviceNode;
Entry->FileObject = (PFILE_OBJECT)EventCategoryData; 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; break;
} }
default: default:
{ {
DPRINT1("%s: unknown EventCategory 0x%x UNIMPLEMENTED\n", DPRINT1("%s: unknown EventCategory 0x%x UNIMPLEMENTED\n",
__FUNCTION__, EventCategory); __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); DPRINT("%s returns NotificationEntry %p\n", __FUNCTION__, Entry);
*NotificationEntry = Entry; *NotificationEntry = Entry;
@ -366,25 +473,46 @@ IoRegisterPlugPlayNotification(IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
/* /*
* @implemented * @implemented
*/ */
CODE_SEG("PAGE")
NTSTATUS NTSTATUS
NTAPI NTAPI
IoUnregisterPlugPlayNotification(IN PVOID NotificationEntry) IoUnregisterPlugPlayNotification(
_In_ PVOID NotificationEntry)
{ {
PPNP_NOTIFY_ENTRY Entry; PPNP_NOTIFY_ENTRY Entry = NotificationEntry;
PKGUARDED_MUTEX Lock;
PAGED_CODE(); PAGED_CODE();
Entry = (PPNP_NOTIFY_ENTRY)NotificationEntry;
DPRINT("%s(NotificationEntry %p) called\n", __FUNCTION__, Entry); DPRINT("%s(NotificationEntry %p) called\n", __FUNCTION__, Entry);
KeAcquireGuardedMutex(&PnpNotifyListLock); switch (Entry->EventCategory)
RemoveEntryList(&Entry->PnpNotifyList); {
KeReleaseGuardedMutex(&PnpNotifyListLock); case EventCategoryDeviceInterfaceChange:
Lock = &PiNotifyDeviceInterfaceLock;
break;
case EventCategoryHardwareProfileChange:
Lock = &PiNotifyHwProfileLock;
break;
case EventCategoryTargetDeviceChange:
Lock = &PiNotifyTargetDeviceLock;
break;
default:
UNREACHABLE;
return STATUS_NOT_SUPPORTED;
}
RtlFreeUnicodeString(&Entry->Guid); KeAcquireGuardedMutex(Lock);
if (!Entry->Deleted)
ObDereferenceObject(Entry->DriverObject); {
Entry->Deleted = TRUE; // so it can't be unregistered two times
ExFreePoolWithTag(Entry, TAG_PNP_NOTIFY); PiDereferencePnpNotifyEntry(Entry);
}
else
{
DPRINT1("IoUnregisterPlugPlayNotification called two times for 0x%p\n", NotificationEntry);
}
KeReleaseGuardedMutex(Lock);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }

View file

@ -131,11 +131,7 @@ PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject,
} }
/* That call is totally wrong but notifications handler must be fixed first */ /* That call is totally wrong but notifications handler must be fixed first */
IopNotifyPlugPlayNotification(DeviceObject, PiNotifyTargetDeviceChange(&GUID_PNP_CUSTOM_NOTIFICATION, DeviceObject, NotificationStructure);
EventCategoryTargetDeviceChange,
&GUID_PNP_CUSTOM_NOTIFICATION,
NotificationStructure,
NULL);
if (SyncEvent) if (SyncEvent)
{ {