diff options
Diffstat (limited to 'tools/dv/samples/DV-FailDriver-WDM/defect_toastmon.c')
| -rw-r--r-- | tools/dv/samples/DV-FailDriver-WDM/defect_toastmon.c | 1532 |
1 files changed, 1532 insertions, 0 deletions
diff --git a/tools/dv/samples/DV-FailDriver-WDM/defect_toastmon.c b/tools/dv/samples/DV-FailDriver-WDM/defect_toastmon.c new file mode 100644 index 00000000..f8c545d2 --- /dev/null +++ b/tools/dv/samples/DV-FailDriver-WDM/defect_toastmon.c @@ -0,0 +1,1532 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + defect_toastmon.c + +Abstract: This sample is designed to demonstrate the Driver Verifier + functionality present in Windows. A bug violating the + IrqlPsPassive rule is injected in the ToasterDrvCancelQueuedReadIrps + callback. Running the Device Fundamentals PNP Surprise Remove test + with Driver Verifier enabled on this Defect_Toastmon driver will create + a bugcheck (0xC4) describing the error. + + This driver should not be used as a sample to build a new driver from. + +Environment: + + Kernel mode + +Revision History: + + Injected defect to demonstrate DV capabilities and cleaned sample + (7/14/2015) + + Added module to demonstrate how to register and receive WMI + notification in Kernel-mode (3/3/2004) + +--*/ +#include "defect_toastmon.h" +#include <initguid.h> +#include <wdmguid.h> +#include "public.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, Defect_ToastMon_AddDevice) +#pragma alloc_text (PAGE, Defect_ToastMon_DispatchPnp) +#pragma alloc_text (PAGE, Defect_ToastMon_DispatchPower) +#pragma alloc_text (PAGE, Defect_ToastMon_Dispatch) +#pragma alloc_text (PAGE, Defect_ToastMon_DispatchRead) +#pragma alloc_text (PAGE, Defect_ToastMon_DispatchSystemControl) +#pragma alloc_text (PAGE, Defect_ToastMon_Unload) +#pragma alloc_text (PAGE, Defect_ToastMon_PnpNotifyDeviceChange) +#pragma alloc_text (PAGE, Defect_ToastMon_PnpNotifyInterfaceChange) +#pragma alloc_text (PAGE, Defect_ToastMon_GetTargetDevicePdo) +#pragma alloc_text (PAGE, Defect_ToastMon_OpenTargetDevice) +#pragma alloc_text (PAGE, Defect_ToastMon_CloseTargetDevice) +#endif + +NTSTATUS +DriverEntry( + __in PDRIVER_OBJECT DriverObject, + __in PUNICODE_STRING RegistryPath + ) + //++ +/* + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + + UNREFERENCED_PARAMETER (RegistryPath); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Entered Driver Entry\n")); + + // + // Create dispatch points for the IRPs. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = Defect_ToastMon_Dispatch; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = Defect_ToastMon_Dispatch; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Defect_ToastMon_Dispatch; + DriverObject->MajorFunction[IRP_MJ_READ] = Defect_ToastMon_DispatchRead; + DriverObject->MajorFunction[IRP_MJ_PNP] = Defect_ToastMon_DispatchPnp; + DriverObject->MajorFunction[IRP_MJ_POWER] = Defect_ToastMon_DispatchPower; + DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = + Defect_ToastMon_DispatchSystemControl; + DriverObject->DriverExtension->AddDevice = Defect_ToastMon_AddDevice; + DriverObject->DriverUnload = Defect_ToastMon_Unload; + + return STATUS_SUCCESS; +} + + +NTSTATUS +Defect_ToastMon_AddDevice( + __in PDRIVER_OBJECT DriverObject, + __in PDEVICE_OBJECT PhysicalDeviceObject + ) +/*++ + +Routine Description: + + The Plug & Play subsystem is handing us a brand new PDO, for which we + (by means of INF registration) have been asked to provide a driver. + + We need to determine if we need to be in the driver stack for the device. + Create a functional device object to attach to the stack + Initialize that device object + Return status success. + + Remember: we can NOT actually send ANY non pnp IRPS to the given driver + stack, UNTIL we have received an IRP_MN_START_DEVICE. + +Arguments: + + DeviceObject - pointer to a device object. + + PhysicalDeviceObject - pointer to a device object created by the + underlying bus driver. + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDEVICE_OBJECT deviceObject = NULL; + PDEVICE_EXTENSION deviceExtension; + + PAGED_CODE(); + + // + // Create a device object. + // + + status = IoCreateDevice (DriverObject, + sizeof (DEVICE_EXTENSION), + NULL, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &deviceObject); + + + if (!NT_SUCCESS (status)) { + // + // Either not enough memory to create a deviceobject or another + // deviceobject with the same name exits. This could happen + // if you install another instance of this device. + // + return status; + } + + deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension; + + // initialize the device wide spinlock + KeInitializeSpinLock(&deviceExtension->deviceSpinlock); + + deviceExtension->TopOfStack = IoAttachDeviceToDeviceStack ( + deviceObject, + PhysicalDeviceObject); + if (NULL == deviceExtension->TopOfStack) { + IoDeleteDevice(deviceObject); + return STATUS_DEVICE_REMOVED; + } + + IoInitializeRemoveLock (&deviceExtension->RemoveLock , + DRIVER_TAG, + 1, // MaxLockedMinutes + 5); // HighWatermark, this parameter is + // used only on checked build. + // + // Set the flag if the device is not holding a pagefile + // crashdump file or hibernate file. + // + + deviceObject->Flags |= DO_POWER_PAGABLE; + deviceObject->Flags |= DO_BUFFERED_IO; + + deviceExtension->DeviceObject = deviceObject; + INITIALIZE_PNP_STATE(deviceExtension); + + // + // We will keep list of all the toaster devices we + // interact with and protect the access to the list + // with a mutex. + // + InitializeListHead(&deviceExtension->DeviceListHead); + InitializeListHead(&deviceExtension->RecvQueueHead); + ExInitializeFastMutex (&deviceExtension->ListMutex); + + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Defect_Toastmon: AddDevice: %p to %p->%p \n", deviceObject, + deviceExtension->TopOfStack, + PhysicalDeviceObject); + + // + // Register for TOASTER device interface changes. + // + // We will get an ARRIVAL callback for every TOASTER device that is + // started and a REMOVAL callback for every TOASTER that is removed + // + + status = IoRegisterPlugPlayNotification ( + EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVINTERFACE_TOASTER, + DriverObject, + (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) + Defect_ToastMon_PnpNotifyInterfaceChange, + (PVOID)deviceExtension, +#pragma warning(suppress: 6014) // handle is released with unregister call during remove IRP processing + &deviceExtension->NotificationHandle); + + if (!NT_SUCCESS(status)) { + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL, "Defect_Toastmon: RegisterPnPNotifiction failed: %x\n", status); + } + + RegisterForWMINotification(deviceExtension); + + return STATUS_SUCCESS; + +} + +NTSTATUS +Defect_ToastMon_CompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) +/*++ + +Routine Description: + + The completion routine for plug & play irps that needs to be + processed first by the lower drivers. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + + Context - pointer to an event object. + +Return Value: + + NT status code + +--*/ +{ + PKEVENT event; + + event = (PKEVENT) Context; + + UNREFERENCED_PARAMETER(DeviceObject); + + // + // If the lower driver didn't return STATUS_PENDING, we don't need to + // set the event because we won't be waiting on it. + // This optimization avoids grabbing the dispatcher lock and improves perf. + // + if (Irp->PendingReturned == TRUE) { +#pragma warning(suppress: 28183) + KeSetEvent(event, 0, FALSE); + } + // + // Allows the caller to reuse the IRP + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +Defect_ToastMon_DispatchPnp ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + The plug and play dispatch routines. + + Most of these the driver will completely ignore. + In all cases it must pass the IRP to the next lower driver. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT status code + +--*/ +{ + PIO_STACK_LOCATION irpStack; + NTSTATUS status = STATUS_SUCCESS; + KEVENT event; + PDEVICE_EXTENSION deviceExtension; + PLIST_ENTRY thisEntry; + PDEVICE_INFO list; + + + + + + deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + + + + irpStack = IoGetCurrentIrpStackLocation(Irp); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Defect_Toastmon: %s IRP:0x%p \n", + PnPMinorFunctionString(irpStack->MinorFunction), Irp); + + + status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); + if (!NT_SUCCESS (status)) + { + Irp->IoStatus.Status = status; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } + + + + + + + + switch (irpStack->MinorFunction) { + case IRP_MN_START_DEVICE: + + // + // The device is starting. + // + // We cannot touch the device (send it any non pnp irps) until a + // start device has been passed down to the lower drivers. + // + IoCopyCurrentIrpStackLocationToNext(Irp); + KeInitializeEvent(&event, + NotificationEvent, + FALSE + ); + + IoSetCompletionRoutine(Irp, + (PIO_COMPLETION_ROUTINE)Defect_ToastMon_CompletionRoutine, + &event, + TRUE, + TRUE, + TRUE); // No need for Cancel + + status = IoCallDriver(deviceExtension->TopOfStack, Irp); + + if (STATUS_PENDING == status) { + KeWaitForSingleObject( + &event, + Executive, // Waiting for reason of a driver + KernelMode, // Waiting in kernel mode + FALSE, // No alert + NULL); // No timeout + status = Irp->IoStatus.Status; + } + + if (NT_SUCCESS(status)) { + + SET_NEW_PNP_STATE(deviceExtension, Started); + } + + // + // We must now complete the IRP, since we stopped it in the + // completion routine with STATUS_MORE_PROCESSING_REQUIRED. + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; + + case IRP_MN_REMOVE_DEVICE: + + + // + // Wait for all outstanding requests to complete + // + IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, Irp); + SET_NEW_PNP_STATE(deviceExtension, Deleted); + Irp->IoStatus.Status = STATUS_SUCCESS; + IoSkipCurrentIrpStackLocation(Irp); + status = IoCallDriver(deviceExtension->TopOfStack, Irp); + + // + // Unregister the interface notification + // + IoUnregisterPlugPlayNotification(deviceExtension->NotificationHandle); + // + // Close all the handles to the target device + // + ExAcquireFastMutex (&deviceExtension->ListMutex); + while(!IsListEmpty(&deviceExtension->DeviceListHead)) + { + thisEntry = RemoveHeadList(&deviceExtension->DeviceListHead); + list = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + Defect_ToastMon_CloseTargetDevice(list); + } + ExReleaseFastMutex (&deviceExtension->ListMutex); + + UnregisterForWMINotification(deviceExtension); + + IoDetachDevice(deviceExtension->TopOfStack); + IoDeleteDevice(DeviceObject); + + return status; + + + case IRP_MN_QUERY_STOP_DEVICE: + SET_NEW_PNP_STATE(deviceExtension, StopPending); + status = STATUS_SUCCESS; + break; + + case IRP_MN_CANCEL_STOP_DEVICE: + + // + // Check to see whether you have received cancel-stop + // without first receiving a query-stop. This could happen if someone + // above us fails a query-stop and passes down the subsequent + // cancel-stop. + // + + if (StopPending == deviceExtension->DevicePnPState) + { + // + // We did receive a query-stop, so restore. + // + RESTORE_PREVIOUS_PNP_STATE(deviceExtension); + } + status = STATUS_SUCCESS; // We must not fail this IRP. + break; + + case IRP_MN_STOP_DEVICE: + SET_NEW_PNP_STATE(deviceExtension, Stopped); + status = STATUS_SUCCESS; + break; + + case IRP_MN_QUERY_REMOVE_DEVICE: + + SET_NEW_PNP_STATE(deviceExtension, RemovePending); + status = STATUS_SUCCESS; + break; + + case IRP_MN_SURPRISE_REMOVAL: + + + SET_NEW_PNP_STATE(deviceExtension, SurpriseRemovePending); + ToasterDrvCancelQueuedReadIrps(deviceExtension); + status = STATUS_SUCCESS; + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + + // + // Check to see whether you have received cancel-remove + // without first receiving a query-remove. This could happen if + // someone above us fails a query-remove and passes down the + // subsequent cancel-remove. + // + + if (RemovePending == deviceExtension->DevicePnPState) + { + // + // We did receive a query-remove, so restore. + // + RESTORE_PREVIOUS_PNP_STATE(deviceExtension); + } + status = STATUS_SUCCESS; // We must not fail this IRP. + break; + + + default: + // + // If you don't handle any IRP you must leave the + // status as is. + // + status = Irp->IoStatus.Status; + + break; + } + + // + // Pass the IRP down and forget it. + // + Irp->IoStatus.Status = status; + IoSkipCurrentIrpStackLocation (Irp); + status = IoCallDriver (deviceExtension->TopOfStack, Irp); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; +} + +NTSTATUS +Defect_ToastMon_DispatchPower( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + This routine is the dispatch routine for power irps. + Does nothing except forwarding the IRP to the next device + in the stack. + +Arguments: + + DeviceObject - Pointer to the device object. + + Irp - Pointer to the request packet. + +Return Value: + + NT Status code +--*/ +{ + PDEVICE_EXTENSION deviceExtension; + PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); + POWER_STATE_TYPE powerType = stack->Parameters.Power.Type; + UCHAR minorFunction = stack->MinorFunction; + NTSTATUS status= STATUS_SUCCESS; + + deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + + status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); + if (!NT_SUCCESS (status)) { + Irp->IoStatus.Status = status; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } + + switch( minorFunction ) + { + case IRP_MN_QUERY_POWER: + PoStartNextPowerIrp(Irp); + IoSkipCurrentIrpStackLocation(Irp); + status=PoCallDriver(deviceExtension->TopOfStack, Irp); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; + break; + case IRP_MN_SET_POWER: + switch( powerType ) + { + case DevicePowerState: + PoStartNextPowerIrp(Irp); + IoSkipCurrentIrpStackLocation(Irp); + status=PoCallDriver(deviceExtension->TopOfStack, Irp); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; + break; + case SystemPowerState: + PoStartNextPowerIrp(Irp); + IoSkipCurrentIrpStackLocation(Irp); + status=PoCallDriver(deviceExtension->TopOfStack, Irp); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; + break; + default: + status = Irp->IoStatus.Status; + PoStartNextPowerIrp(Irp); + IoSkipCurrentIrpStackLocation(Irp); + status=PoCallDriver(deviceExtension->TopOfStack, Irp); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; + } + + break; + default: + + status = Irp->IoStatus.Status; + PoStartNextPowerIrp(Irp); + IoSkipCurrentIrpStackLocation(Irp); + status=PoCallDriver(deviceExtension->TopOfStack, Irp); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; + } +} + +NTSTATUS +Defect_ToastMon_DispatchSystemControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + This routine is the dispatch routine for WMI irps. + Does nothing except forwarding the IRP to the next device + in the stack. + +Arguments: + + DeviceObject - Pointer to the device object. + + Irp - Pointer to the request packet. + +Return Value: + + NT Status code +--*/ +{ + PDEVICE_EXTENSION deviceExtension; + NTSTATUS status= STATUS_SUCCESS; + + deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); + if (!NT_SUCCESS (status)) + { + Irp->IoStatus.Status = status; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } + IoSkipCurrentIrpStackLocation(Irp); + status=IoCallDriver(deviceExtension->TopOfStack, Irp); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; +} + + +VOID +Defect_ToastMon_Unload( + __in PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Free all the allocated resources, etc. + +Arguments: + + DriverObject - pointer to a driver object. + +Return Value: + + VOID. + +--*/ +{ + UNREFERENCED_PARAMETER(DriverObject); + + PAGED_CODE (); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Entered Defect_ToastMon_Unload\n")); + + return; +} + +NTSTATUS +Defect_ToastMon_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + This routine is the dispatch handler for the driver. It is responsible + for processing the IRPs. + +Arguments: + + pDO - Pointer to device object. + + Irp - Pointer to the current IRP. + +Return Value: + + STATUS_SUCCESS if the IRP was processed successfully, otherwise an error + indicating the reason for failure. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack; + NTSTATUS status; + + Irp->IoStatus.Information = 0; + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Entered Defect_ToastMon_Dispatch\n")); + + status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); + if (!NT_SUCCESS (status)) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } + + if (NotStarted == deviceExtension->DevicePnPState) + { + // + // We fail all the IRPs that arrive before the device is started. + // + Irp->IoStatus.Status = status = STATUS_DEVICE_NOT_READY; + IoCompleteRequest(Irp, IO_NO_INCREMENT ); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; + } + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + // Dispatch based on major fcn code. + + switch (irpStack->MajorFunction) + { + case IRP_MJ_CREATE: + case IRP_MJ_CLOSE: + // We don't need any special processing on open/close so we'll + // just return success. + status = STATUS_SUCCESS; + break; + + case IRP_MJ_DEVICE_CONTROL: + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + // + // We're done with I/O request. Record the status of the I/O action. + // + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT ); + IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); + return status; +} + + +NTSTATUS +#pragma warning(suppress: 28208) +Defect_ToastMon_PnpNotifyInterfaceChange( + PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStruct, + PVOID Context + ) +/*++ + +Routine Description: + + This routine is the PnP "interface change notification" callback routine. + + This gets called on a Toaster triggered device interface arrival or + removal. + - Interface arrival corresponds to a Toaster device being STARTED + - Interface removal corresponds to a Toaster device being REMOVED + + On arrival: + - Get the target deviceobject pointer by using the symboliclink. + - Get the PDO of the target device, in case you need to set the + device parameters of the target device. + - Regiter for EventCategoryTargetDeviceChange notification on the fileobject + so that you can cleanup whenever associated device is removed. + + On removal: + - This callback is a NO-OP for interface removal because we + for PnP EventCategoryTargetDeviceChange callbacks and + use that callback to clean up when their associated toaster device goes + away. + +Arguments: + + NotificationStruct - Structure defining the change. + + Context - pointer to the device extension. + (supplied as the "context" when we + registered for this callback) +Return Value: + + STATUS_SUCCESS - always, even if something goes wrong + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDEVICE_INFO list = NULL; + PDEVICE_EXTENSION deviceExtension = Context; + PUNICODE_STRING symbolicLinkName; + + PAGED_CODE(); + + symbolicLinkName = NotificationStruct->SymbolicLinkName; + + // + // Verify that interface class is a toaster device interface. + // + // Any other InterfaceClassGuid is an error, but let it go since + // it is not fatal to the machine. + // + + if ( !IsEqualGUID( (LPGUID)&(NotificationStruct->InterfaceClassGuid), + (LPGUID)&GUID_DEVINTERFACE_TOASTER) ) { + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL, ("Defect_Toastmon: Bad interfaceClassGuid in Defect_ToastMon_PnpNotifyInterfaceChange\n")); + return STATUS_SUCCESS; + } + + // + // Check the callback event. + // + if (IsEqualGUID( (LPGUID)&(NotificationStruct->Event), + (LPGUID)&GUID_DEVICE_INTERFACE_ARRIVAL )) { + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Arrival Notification in Defect_ToastMon_PnpNotifyInterfaceChange\n")); + + // + // Allocate memory for the deviceinfo + // + + list = ExAllocatePoolWithTag(PagedPool,sizeof(DEVICE_INFO),DRIVER_TAG); + if (list == NULL) + { + goto Error; + } + RtlZeroMemory(list, sizeof(DEVICE_INFO)); + + // + // Copy the symbolic link + // + list->SymbolicLink.MaximumLength = symbolicLinkName->Length + + sizeof(UNICODE_NULL); + list->SymbolicLink.Length = symbolicLinkName->Length; + list->SymbolicLink.Buffer = ExAllocatePoolWithTag ( + PagedPool, + list->SymbolicLink.MaximumLength, + DRIVER_TAG); + if (list->SymbolicLink.Buffer == NULL) + { + goto Error; + } + RtlCopyUnicodeString(&list->SymbolicLink,symbolicLinkName); + + list->DeviceExtension = deviceExtension; + InitializeListHead(&list->ListEntry); + + // + // Warning: It's not recommended to open the targetdevice + // from a pnp notification callback routine, because if + // the target device initiates any kind of PnP action as + // a result of this open, the PnP manager could deadlock. + // You should queue a workitem to do that. + // For example, SWENUM devices in conjunction with KS + // initiate an enumeration of a device when you do the + // open on the device interface. + // For simplicity, I'm opening the device here, because + // I know the toaster function driver doesn't trigger + // any pnp action. + // For an example on how to queue a workitem, take a look + // at ToasterQueuePassiveLevelCallback in func\featured2\ + // wake.c file. + // + + status = Defect_ToastMon_OpenTargetDevice(list); + if (!NT_SUCCESS( status)) { + goto Error; + } + + // + // Finally queue the Deviceinfo. + // + ExAcquireFastMutex (&deviceExtension->ListMutex); + InsertTailList(&deviceExtension->DeviceListHead, &list->ListEntry); + ExReleaseFastMutex (&deviceExtension->ListMutex); + } + else + { + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Removal Interface Notification\n")); + } + return STATUS_SUCCESS; + + Error: + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL, "Defect_Toastmon: PnPNotifyInterfaceChange failed: %x\n", status); + Defect_ToastMon_CloseTargetDevice(list); + return STATUS_SUCCESS; +} + +NTSTATUS +#pragma warning(suppress: 28208) +Defect_ToastMon_PnpNotifyDeviceChange( + PTARGET_DEVICE_REMOVAL_NOTIFICATION NotificationStruct, + PVOID Context + ) +/*++ + +Routine Description: + + This routine is the PnP "Device Change Notification" callback routine. + + This gets called on a when the target is query or surprise removed. + + - Interface arrival corresponds to a toaster device being STARTed + - Interface removal corresponds to a toaster device being REMOVEd + + On Query_Remove or Remove_Complete: + - Find the targetdevice from the list by matching the fileobject pointers. + - Dereference the FileObject (this generates a close to the target device) + - Free the resources. + +Arguments: + + NotificationStruct - Structure defining the change. + + Context - pointer to the device extension. + (supplied as the "context" when we + registered for this callback) +Return Value: + + STATUS_SUCCESS - always, even if something goes wrong + +--*/ +{ + NTSTATUS status; + PDEVICE_INFO list = Context; + PDEVICE_EXTENSION deviceExtension = list->DeviceExtension; + + PAGED_CODE(); + + // + // if the event is query_remove + // + if ( (IsEqualGUID( (LPGUID)&(NotificationStruct->Event), + (LPGUID)&GUID_TARGET_DEVICE_QUERY_REMOVE))){ + PTARGET_DEVICE_REMOVAL_NOTIFICATION removalNotification; + +#pragma warning(suppress: 28930) // used in assert + removalNotification = + (PTARGET_DEVICE_REMOVAL_NOTIFICATION)NotificationStruct; + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Device Removal (query remove) Notification\n")); + + ASSERT(list->FileObject == removalNotification->FileObject); + // + // Deref the fileobject so that we don't prevent + // the target device from being removed. + // + ObDereferenceObject(list->FileObject); + list->FileObject = NULL; + // + // Deref the PDO to compensate for the reference taken + // by the bus driver when it returned the PDO in response + // to the query-device-relations (target-relations). + // + ObDereferenceObject(list->Pdo); + list->Pdo = NULL; + // + // We will defer freeing other resources to remove-complete + // notification because if query-remove is vetoed, we would reopen + // the device in remove-cancelled notification. + // + + } else if (IsEqualGUID( (LPGUID)&(NotificationStruct->Event), + (LPGUID)&GUID_TARGET_DEVICE_REMOVE_COMPLETE) ) { + // + // Device is gone. Let us cleanup our resources. + // + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Device Removal (remove complete) Notification\n")); + + ExAcquireFastMutex (&deviceExtension->ListMutex); + RemoveEntryList(&list->ListEntry); + ExReleaseFastMutex (&deviceExtension->ListMutex); + + Defect_ToastMon_CloseTargetDevice(list); + + } else if ( IsEqualGUID( (LPGUID)&(NotificationStruct->Event), + (LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED) ) { + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Device Removal (remove cancelled) Notification\n")); + + // Should be null because we cleared it in query-remove + + ASSERT(!list->FileObject); + ASSERT(!list->Pdo); + + // + // Unregister the previous notification because when we reopen + // the device we will register again on the new fileobject. + // + IoUnregisterPlugPlayNotification(list->NotificationHandle); + + // + // Reopen the device + // +#pragma warning(suppress: 28183) + status = Defect_ToastMon_OpenTargetDevice(list); + if (!NT_SUCCESS (status)) { + // + // Couldn't reopen the device. Cleanup. + // + ExAcquireFastMutex (&deviceExtension->ListMutex); + RemoveEntryList(&list->ListEntry); + ExReleaseFastMutex (&deviceExtension->ListMutex); + + Defect_ToastMon_CloseTargetDevice(list); + } + + } else { + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Unknown Device Notification\n")); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +Defect_ToastMon_GetTargetDevicePdo( + __in PDEVICE_OBJECT DeviceObject, + __out PDEVICE_OBJECT *PhysicalDeviceObject + ) +/*++ + +Routine Description: + + This builds and send a pnp irp to get the PDO a device. + +Arguments: + + DeviceObject - This is the top of the device in the device stack + the irp is to be sent to. + + PhysicalDeviceObject - Address where the PDO pointer is returned + +Return Value: + + NT status code +--*/ +{ + + KEVENT event; + NTSTATUS status; + PIRP irp; + IO_STATUS_BLOCK ioStatusBlock; + PIO_STACK_LOCATION irpStack; + PDEVICE_RELATIONS deviceRelations; + + PAGED_CODE(); + + KeInitializeEvent( &event, NotificationEvent, FALSE ); + + irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, + DeviceObject, + NULL, + 0, + NULL, + &event, + &ioStatusBlock ); + + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto End; + } + + irpStack = IoGetNextIrpStackLocation( irp ); + irpStack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS; + irpStack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation; + + // + // Initialize the status to error in case the bus driver decides not to + // set it correctly. + // + + irp->IoStatus.Status = STATUS_NOT_SUPPORTED ; + + + status = IoCallDriver( DeviceObject, irp ); + + if (status == STATUS_PENDING) { + + KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); + status = ioStatusBlock.Status; + } + + if (NT_SUCCESS( status)) { + deviceRelations = (PDEVICE_RELATIONS)ioStatusBlock.Information; + ASSERT(deviceRelations); + // + // You must dereference the PDO when it's no longer + // required. + // + *PhysicalDeviceObject = deviceRelations->Objects[0]; + ExFreePool(deviceRelations); + } + +End: + return status; + +} + +NTSTATUS +Defect_ToastMon_OpenTargetDevice( + __in PDEVICE_INFO List + ) +/*++ + +Routine Description: + + Open the target device, get the PDO and register + for TargetDeviceChange notification on the fileobject. + +Arguments: + + +Return Value: + + NT status code +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // Get a pointer to and open a handle to the toaster device + // + + status = IoGetDeviceObjectPointer(&List->SymbolicLink, + STANDARD_RIGHTS_ALL, + &List->FileObject, + &List->TargetDeviceObject); + if ( !NT_SUCCESS(status) ) { + goto Error; + } + + // + // Register for TargerDeviceChange notification on the fileobject. + // + status = IoRegisterPlugPlayNotification ( + EventCategoryTargetDeviceChange, + 0, + (PVOID)List->FileObject, + List->DeviceExtension->DeviceObject->DriverObject, + (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)Defect_ToastMon_PnpNotifyDeviceChange, + (PVOID)List, + &List->NotificationHandle); + if (!NT_SUCCESS (status)) { + goto Error; + } + + // + // Get the PDO. This is required in case you need to set + // the target device's parameters using IoOpenDeviceRegistryKey + // + status = Defect_ToastMon_GetTargetDevicePdo(List->TargetDeviceObject, + &List->Pdo); + if (!NT_SUCCESS (status)) { + goto Error; + } + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Defect_Toastmon: Target device Toplevel DO:0x%p and PDO: 0x%p\n", + List->TargetDeviceObject, List->Pdo); +Error: + + return status; +} + + +__drv_arg(List->SymbolicLink.Buffer, __drv_freesMem(SymLink)) +VOID +#pragma warning(suppress: 6014) +Defect_ToastMon_CloseTargetDevice( + __in __drv_freesMem(List) PDEVICE_INFO List + ) +/*++ + +Routine Description: + + Close all the handles and free the list. + +Arguments: + + +Return Value: + VOID +--*/ +{ + PAGED_CODE(); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Defect_Toastmon: Closing handle to 0x%p\n", List->TargetDeviceObject); + if (List->FileObject){ + ObDereferenceObject(List->FileObject); + } + if (List->NotificationHandle) { + IoUnregisterPlugPlayNotification(List->NotificationHandle); + } + if (List->Pdo) { + ObDereferenceObject(List->Pdo); + } + RtlFreeUnicodeString(&List->SymbolicLink); + ExFreePool(List); +} + +PCHAR +PnPMinorFunctionString ( + __in UCHAR MinorFunction +) +{ + switch (MinorFunction) + { + case IRP_MN_START_DEVICE: + return "IRP_MN_START_DEVICE"; + case IRP_MN_QUERY_REMOVE_DEVICE: + return "IRP_MN_QUERY_REMOVE_DEVICE"; + case IRP_MN_REMOVE_DEVICE: + return "IRP_MN_REMOVE_DEVICE"; + case IRP_MN_CANCEL_REMOVE_DEVICE: + return "IRP_MN_CANCEL_REMOVE_DEVICE"; + case IRP_MN_STOP_DEVICE: + return "IRP_MN_STOP_DEVICE"; + case IRP_MN_QUERY_STOP_DEVICE: + return "IRP_MN_QUERY_STOP_DEVICE"; + case IRP_MN_CANCEL_STOP_DEVICE: + return "IRP_MN_CANCEL_STOP_DEVICE"; + case IRP_MN_QUERY_DEVICE_RELATIONS: + return "IRP_MN_QUERY_DEVICE_RELATIONS"; + case IRP_MN_QUERY_INTERFACE: + return "IRP_MN_QUERY_INTERFACE"; + case IRP_MN_QUERY_CAPABILITIES: + return "IRP_MN_QUERY_CAPABILITIES"; + case IRP_MN_QUERY_RESOURCES: + return "IRP_MN_QUERY_RESOURCES"; + case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: + return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS"; + case IRP_MN_QUERY_DEVICE_TEXT: + return "IRP_MN_QUERY_DEVICE_TEXT"; + case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: + return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS"; + case IRP_MN_READ_CONFIG: + return "IRP_MN_READ_CONFIG"; + case IRP_MN_WRITE_CONFIG: + return "IRP_MN_WRITE_CONFIG"; + case IRP_MN_EJECT: + return "IRP_MN_EJECT"; + case IRP_MN_SET_LOCK: + return "IRP_MN_SET_LOCK"; + case IRP_MN_QUERY_ID: + return "IRP_MN_QUERY_ID"; + case IRP_MN_QUERY_PNP_DEVICE_STATE: + return "IRP_MN_QUERY_PNP_DEVICE_STATE"; + case IRP_MN_QUERY_BUS_INFORMATION: + return "IRP_MN_QUERY_BUS_INFORMATION"; + case IRP_MN_DEVICE_USAGE_NOTIFICATION: + return "IRP_MN_DEVICE_USAGE_NOTIFICATION"; + case IRP_MN_SURPRISE_REMOVAL: + return "IRP_MN_SURPRISE_REMOVAL"; + case IRP_MN_QUERY_LEGACY_BUS_INFORMATION: + return "IRP_MN_QUERY_LEGACY_BUS_INFORMATION"; + default: + return "unknown_pnp_irp"; + } +} + + +NTSTATUS +Defect_ToastMon_DispatchRead ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + Performs read from the NIC. + +Arguments: + + FdoData - pointer to a FDO_DATA structure + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT status code + + +--*/ + +{ + NTSTATUS status; + KIRQL oldIrql; + BOOLEAN CancelIrp; + PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Entered Defect_ToastMon_DispatchRead \n")); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Attempting to acquire remove lock\n")); + status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); + if (!NT_SUCCESS (status)) + { + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL, ("Defect_Toastmon: Acquire remove lock failed; the device is already removed\n")); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } + + + KeAcquireSpinLock(&deviceExtension->RecvQueueLock, &oldIrql); + CancelIrp = FALSE; + // + // Since we are queueing the IRP, we should set the cancel routine. + // + IoSetCancelRoutine(Irp, Defect_ToastmonCancelRoutineForReadIrp); + + // + // Let us check to see if the IRP is cancelled at this point. + // + if(Irp->Cancel!=FALSE) + { + // + // This irp has been marked cancelled. This may have occured + // before or after we set the cancel routine. If it occured + // before, our cancel routine will not be called and we must + // cancel the irp here. If it was cancelled after we set the + // cancel routine, the thread that is cancelling the irp will + // call our cancel routine. We determine this by setting the + // cancel routine to NULL and seeing if the original cancel + // routine is still there. + // + + + status = STATUS_CANCELLED; + if (IoSetCancelRoutine(Irp, NULL) != NULL) + { + CancelIrp = TRUE; + } + } + else + { + // + // Queue the IRP and return status pending. + // + IoMarkIrpPending(Irp); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Defect_ToastMon_DispatchRead - insert the Read Irp into the Recieve Queue and mark IRP as pending \n ")); + InsertTailList(&deviceExtension->RecvQueueHead, + &Irp->Tail.Overlay.ListEntry); + status = STATUS_PENDING; + // + // The IRP shouldn't be accessed after the lock is released + // It could be grabbed by another thread or the cancel routine + // is running. + // + } + + KeReleaseSpinLock(&deviceExtension->RecvQueueLock, oldIrql); + + if (CancelIrp != FALSE) + { + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Release the removelock \n")); + IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp); + } + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Exiting Defect_ToastMon_DispatchRead \n")); + return status; +} + +VOID +Defect_ToastmonCancelRoutineForReadIrp ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + The cancel routine for IRPs waiting in the RecvQueue. + The cancel spin lock is already acquired when this routine is called. + +Arguments: + + DeviceObject - pointer to the device object. + + Irp - pointer to the IRP to be cancelled. + + +Return Value: + + VOID. + +--*/ +{ + PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + KIRQL oldIrql; + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Canceling Read Request inside Defect_ToastmonCancelRoutineForReadIrp\n")); + + // + // Release the cancel spin lock and pass Irp->CancelIrql to + // restore the original Irql prior to the call to IoAcquireCancelSpinLock + // + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Releasing the cancel spinlock\n")); + IoReleaseCancelSpinLock(Irp->CancelIrql); + + // + // Acquire the local spinlock and raise the Irql to DISPATCH_LEVEL + // + + KeAcquireSpinLock(&deviceExtension->RecvQueueLock, &oldIrql); + + // + // Remove the cancelled IRP from queue and release the queue lock. + // + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); + + // + // Release the spinlock and restore the old Irql + // + KeReleaseSpinLock(&deviceExtension->RecvQueueLock, oldIrql); + + // + // Complete the request with STATUS_CANCELLED. + // + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Releasing the removelock\n")); + IoReleaseRemoveLock (&deviceExtension->RemoveLock, Irp); + return; + +} + + +VOID +#pragma warning(suppress: 28167) +ToasterDrvCancelQueuedReadIrps( + PDEVICE_EXTENSION deviceExtension + ) +/*++ + +Routine Description: + + Cancel all the read IRPs waiting in the RecvQueue. + +Arguments: + + FdoData - Pointer to the device extension. + +Return Value: + + None + +--*/ +{ + KIRQL oldIrql; + PIRP irp; + PLIST_ENTRY listEntry; + ULONG MajorVersion; + ULONG MinorVersion; + ULONG BuildNumber; + BOOLEAN ListEmpty = FALSE; + + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Canceling queued IRPs inside ToasterDrvCancelQueuedReadIrps")); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Acquire the RecvQueueLock before checking the RecvQueue as cancellation may also run at the same time")); + KeAcquireSpinLock(&deviceExtension->RecvQueueLock, &oldIrql); + ListEmpty=IsListEmpty(&deviceExtension->RecvQueueHead); + if(ListEmpty) + { + // injected defect for IrqlPsPassive SDV and DV rule +#pragma warning(suppress: 28159) // this is the injected defect +#pragma warning(suppress: 28121) + PsGetVersion(&MajorVersion, &MinorVersion, &BuildNumber, NULL); + KeReleaseSpinLock(&deviceExtension->RecvQueueLock, oldIrql); + } + else + { + while(!ListEmpty) + { + // + // Remove a request from the queue. + // + listEntry = RemoveHeadList(&deviceExtension->RecvQueueHead); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Extracting IRPs from the list.")); + irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry); + // + // Set the cancel routine to NULL. This is an atomic operation. + // + if (IoSetCancelRoutine(irp, NULL)) + { + // injected defect for IrqlPsPassive SDV and DV rule +#pragma warning(suppress: 28159) // this is the injected defect +#pragma warning(suppress: 28121) + PsGetVersion(&MajorVersion, &MinorVersion, &BuildNumber, NULL); + irp->IoStatus.Status = STATUS_CANCELLED; + irp->IoStatus.Information = 0; + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Cancelling the queued IRPs from the list.")); + KeReleaseSpinLock(&deviceExtension->RecvQueueLock, oldIrql); + // do not complete the IRP while holding the lock + IoCompleteRequest(irp, IO_NO_INCREMENT); + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, ("Defect_Toastmon: Releasing the removelock.")); + IoReleaseRemoveLock (&deviceExtension->RemoveLock, irp); + KeAcquireSpinLock(&deviceExtension->RecvQueueLock, &oldIrql); + } + else + { + // + // Cancel rotuine is running. Leave the irp alone. + // + irp = NULL; + } + ListEmpty=IsListEmpty(&deviceExtension->RecvQueueHead); + if(ListEmpty) + { + KeReleaseSpinLock(&deviceExtension->RecvQueueLock, oldIrql); + } + } + } +}
\ No newline at end of file |
