/*++ Copyright (c) 1999-2001 Microsoft Corporation Module Name: pnp.c Abstract: This module implements the IRP_MJ_PNP IRP processing routines for the Plug and Play Memory driver. Dispatch routines are invoked through tables located at the bottom of the module. Author: Dave Richards (daveri) 16-Aug-1999 Environment: Kernel mode only. Revision History: --*/ #include "pnpmem.h" NTSTATUS PmPassIrp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmPnpDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmDeferProcessing( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ); NTSTATUS PmStartDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmQueryRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmCancelRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmQueryStopDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmCancelStopDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmQueryCapabilities( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS PmSurpriseRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PmPassIrp) #pragma alloc_text(PAGE, PmPnpDispatch) #pragma alloc_text(PAGE, PmDeferProcessing) #pragma alloc_text(PAGE, PmStartDevice) #pragma alloc_text(PAGE, PmQueryRemoveDevice) #pragma alloc_text(PAGE, PmRemoveDevice) #pragma alloc_text(PAGE, PmCancelRemoveDevice) #pragma alloc_text(PAGE, PmQueryStopDevice) #pragma alloc_text(PAGE, PmCancelStopDevice) #pragma alloc_text(PAGE, PmSurpriseRemoveDevice) #pragma alloc_text(PAGE, PmQueryCapabilities) #endif PDRIVER_DISPATCH PmPnpDispatchTable[]; extern ULONG PmPnpDispatchTableSize; NTSTATUS PmPassIrp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PPM_DEVICE_EXTENSION deviceExtension; PAGED_CODE(); deviceExtension = DeviceObject->DeviceExtension; IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(deviceExtension->AttachedDevice, Irp); } NTSTATUS PmPnpDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine handles IRP_MJ_PNP IRPs for FDOs. Arguments: DeviceObject - Pointer to the FDO for which this IRP applies. Irp - Pointer to the IRP_MJ_PNP IRP to dispatch. Return Value: NT status. --*/ { NTSTATUS status; BOOLEAN isRemoveDevice; PIO_STACK_LOCATION irpSp; PPM_DEVICE_EXTENSION deviceExtension; PAGED_CODE(); // // Get a pointer to our stack location and take appropriate action based // on the minor function. // irpSp = IoGetCurrentIrpStackLocation(Irp); deviceExtension = DeviceObject->DeviceExtension; IoAcquireRemoveLock(&deviceExtension->RemoveLock, (PVOID) Irp); isRemoveDevice = irpSp->MinorFunction == IRP_MN_REMOVE_DEVICE; if ((irpSp->MinorFunction < PmPnpDispatchTableSize) && PmPnpDispatchTable[irpSp->MinorFunction]) { status = PmPnpDispatchTable[irpSp->MinorFunction](DeviceObject, Irp ); } else { status = PmPassIrp(DeviceObject, Irp); } if (!isRemoveDevice) { IoReleaseRemoveLock(&deviceExtension->RemoveLock, (PVOID) Irp); } return status; } NTSTATUS PmPnpCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: This routine is used to defer processing of an IRP until drivers lower in the stack including the bus driver have done their processing. This routine triggers the event to indicate that processing of the irp can now continue. Arguments: DeviceObject - Pointer to the FDO for which this IRP applies. Irp - Pointer to the IRP_MJ_PNP IRP to dispatch. Return Value: NT status. --*/ { KeSetEvent((PKEVENT) Context, EVENT_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS PmDeferProcessing( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) /*++ Routine Description: This routine is used to defer processing of an IRP until drivers lower in the stack including the bus driver have done their processing. This routine uses an IoCompletion routine along with an event to wait until the lower level drivers have completed processing of the irp. Arguments: Parent - FDO extension for the FDO devobj in question Irp - Pointer to the IRP_MJ_PNP IRP to defer Return Value: NT status. --*/ { PPM_DEVICE_EXTENSION deviceExtension; KEVENT event; NTSTATUS status; PAGED_CODE(); deviceExtension = DeviceObject->DeviceExtension; KeInitializeEvent(&event, NotificationEvent, FALSE); // // Set our completion routine // IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, PmPnpCompletion, &event, TRUE, TRUE, TRUE ); status = IoCallDriver(deviceExtension->AttachedDevice, Irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } return status; } NTSTATUS PmStartDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function handles IRP_MN_START_DEVICE IRPs. Arguments: DeviceObject - The functional device object. Irp - The I/O request packet. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp; PPM_RANGE_LIST RangeList; PPM_DEVICE_EXTENSION deviceExtension; POWER_STATE power; NTSTATUS status; PAGED_CODE(); status = PmDeferProcessing(DeviceObject, Irp); if (!NT_SUCCESS(status)) { IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } PmDumpOsMemoryRanges(L"Before Start"); irpSp = IoGetCurrentIrpStackLocation(Irp); deviceExtension = DeviceObject->DeviceExtension; ASSERT(deviceExtension->RangeList == NULL); if (irpSp->Parameters.StartDevice.AllocatedResources) { RangeList = PmCreateRangeListFromCmResourceList( irpSp->Parameters.StartDevice.AllocatedResources ); if (RangeList == NULL) { // // The memory allocation failure here is more serious than // is intially obvious. If we fail this allocation, we're // going to get removed from the stack before we find out // if the OS knows about this memory. If the OS knows // about this memory already, then ejecting the PDO would // cause the memory underneath the OS to disappear. // Better to be on the stack, but not have added any // memory then to be off the stack and leave a dangerous // situation. // // Only solution is to arbitrarily fail // IRP_MN_QUERY_REMOVE. // deviceExtension->FailQueryRemoves = TRUE; } } else { RangeList = NULL; } PmTrimReservedMemory(deviceExtension, &RangeList); PmDebugDumpRangeList(PNPMEM_MEMORY, "Memory Ranges to be added:\n", RangeList); if (deviceExtension->FailQueryRemoves) { PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY, "PNPMEM device can't be removed\n")); } if (RangeList != NULL) { (VOID) PmAddPhysicalMemory(DeviceObject, RangeList); deviceExtension->RangeList = RangeList; } power.DeviceState = PowerDeviceD0; PoSetPowerState(DeviceObject, DevicePowerState, power); deviceExtension->PowerState = PowerDeviceD0; PmDumpOsMemoryRanges(L"After Start "); Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS PmQueryRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function handles IRP_MN_QUERY_REMOVE_DEVICE IRPs. Arguments: DeviceObject - The functional device object. Irp - The I/O request packet. Return Value: NTSTATUS --*/ { PPM_DEVICE_EXTENSION deviceExtension; NTSTATUS status; PAGED_CODE(); deviceExtension = DeviceObject->DeviceExtension; if (!MemoryRemovalSupported) { PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY, "QueryRemove vetoed because memory removal is not supported\n")); status = STATUS_UNSUCCESSFUL; } else if (deviceExtension->FailQueryRemoves) { PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY, "QueryRemove vetoed because removing this memory device may contain special memory\n")); status = STATUS_UNSUCCESSFUL; } else if (deviceExtension->RangeList != NULL) { status = PmRemovePhysicalMemory(deviceExtension->RangeList); if (!NT_SUCCESS(status)) { // // Some ranges may have been removed, before failure. Add // them back. Should be low-cost due to optimizations in // PmAddPhysicalMemory. // (VOID) PmAddPhysicalMemory(DeviceObject, deviceExtension->RangeList); } } else { status = STATUS_SUCCESS; } Irp->IoStatus.Status = status; if (NT_SUCCESS(status)) { return PmPassIrp(DeviceObject, Irp); } else { IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } } NTSTATUS PmRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function handles IRP_MN_REMOVE_DEVICE IRPs. Arguments: DeviceObject - The functional device object. Irp - The I/O request packet. Return Value: NTSTATUS --*/ { PPM_DEVICE_EXTENSION deviceExtension; PPM_RANGE_LIST RangeList; POWER_STATE power; NTSTATUS status; PAGED_CODE(); deviceExtension = DeviceObject->DeviceExtension; if ((deviceExtension->Flags & DF_SURPRISE_REMOVED) == 0) { power.DeviceState = PowerDeviceD3; PoSetPowerState(DeviceObject, DevicePowerState, power); deviceExtension->PowerState = PowerDeviceD0; if (deviceExtension->RangeList != NULL) { status = PmRemovePhysicalMemory(deviceExtension->RangeList); ASSERT(status == STATUS_SUCCESS); DbgBreakPoint(); } } Irp->IoStatus.Status = STATUS_SUCCESS; status = PmPassIrp(DeviceObject, Irp); IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, (PVOID) Irp); IoDetachDevice(deviceExtension->AttachedDevice); deviceExtension->AttachedDevice = NULL; if (deviceExtension->RangeList != NULL) { PmFreeRangeList(deviceExtension->RangeList); deviceExtension->RangeList = NULL; } IoDeleteDevice(DeviceObject); return status; } NTSTATUS PmCancelRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function handles IRP_MN_CANCEL_REMOVE_DEVICE IRPs. Arguments: DeviceObject - The functional device object. Irp - The I/O request packet. Return Value: NTSTATUS --*/ { PPM_DEVICE_EXTENSION deviceExtension; PAGED_CODE(); (VOID) PmDeferProcessing(DeviceObject, Irp); deviceExtension = DeviceObject->DeviceExtension; if (deviceExtension->RangeList != NULL) { (VOID) PmAddPhysicalMemory(DeviceObject, deviceExtension->RangeList); } Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS PmQueryStopDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function handles IRP_MN_QUERY_STOP_DEVICE IRPs. Arguments: DeviceObject - The functional device object. Irp - The I/O request packet. Return Value: NTSTATUS --*/ { PAGED_CODE(); Irp->IoStatus.Status = STATUS_DEVICE_BUSY; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_DEVICE_BUSY; } NTSTATUS PmCancelStopDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function handles IRP_MN_CANCEL_STOP_DEVICE IRPs. Arguments: DeviceObject - The functional device object. Irp - The I/O request packet. Return Value: NTSTATUS --*/ { NTSTATUS status; PAGED_CODE(); (VOID) PmDeferProcessing(DeviceObject, Irp); Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS PmQueryCapabilities( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PPM_DEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpSp; NTSTATUS status; ULONG i; PAGED_CODE(); irpSp = IoGetCurrentIrpStackLocation(Irp); deviceExtension = DeviceObject->DeviceExtension; status = PmDeferProcessing(DeviceObject, Irp); if (!NT_SUCCESS(status)) { IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } if (irpSp->Parameters.DeviceCapabilities.Capabilities->Version != 1) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_PARAMETER; } for (i = 0; i < PowerSystemMaximum; i++) { deviceExtension->DeviceStateMapping[i] = irpSp->Parameters.DeviceCapabilities.Capabilities->DeviceState[i]; } // // Would *LIKE* to smash the eject supported, and removable bits // here but this isn't really supported. The hot plug applet pops // up (because the device is marked removable or ejectable) and // then goes away a few seconds later when the driver is installed // (and the capabilities requeried). // Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS PmSurpriseRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PPM_DEVICE_EXTENSION deviceExtension; PAGED_CODE(); deviceExtension = DeviceObject->DeviceExtension; if (deviceExtension->RangeList != NULL) { PVOID parameterArray[4] = {0}; UCHAR buffer[] = "Memory module can not be surprise removed safely\n"; // // Memory cannot be gracefully yanked from a running // system. // KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR, 0x10001, (ULONG_PTR) parameterArray, (ULONG_PTR) buffer, (ULONG_PTR) NULL); } deviceExtension->Flags |= DF_SURPRISE_REMOVED; if (deviceExtension->RangeList != NULL) { PmFreeRangeList(deviceExtension->RangeList); deviceExtension->RangeList = NULL; } Irp->IoStatus.Status = STATUS_SUCCESS; return PmPassIrp(DeviceObject, Irp); } PDRIVER_DISPATCH PmPnpDispatchTable[] = { PmStartDevice, // IRP_MN_START_DEVICE PmQueryRemoveDevice, // IRP_MN_QUERY_REMOVE_DEVICE PmRemoveDevice, // IRP_MN_REMOVE_DEVICE PmCancelRemoveDevice, // IRP_MN_CANCEL_REMOVE_DEVICE NULL, // IRP_MN_STOP_DEVICE (never get, fails query-stop) PmQueryStopDevice, // IRP_MN_QUERY_STOP_DEVICE PmCancelStopDevice, // IRP_MN_CANCEL_STOP_DEVICE NULL, // IRP_MN_QUERY_DEVICE_RELATIONS NULL, // IRP_MN_QUERY_INTERFACE PmQueryCapabilities, // IRP_MN_QUERY_CAPABILITIES NULL, // IRP_MN_QUERY_RESOURCES NULL, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS NULL, // IRP_MN_QUERY_DEVICE_TEXT NULL, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS NULL, // unused NULL, // IRP_MN_READ_CONFIG NULL, // IRP_MN_WRITE_CONFIG NULL, // IRP_MN_EJECT NULL, // IRP_MN_SET_LOCK NULL, // IRP_MN_QUERY_ID NULL, // IRP_MN_QUERY_PNP_DEVICE_STATE NULL, // IRP_MN_QUERY_BUS_INFORMATION NULL, // IRP_MN_DEVICE_USAGE_NOTIFICATION PmSurpriseRemoveDevice, // IRP_MN_SURPRISE_REMOVAL }; ULONG PmPnpDispatchTableSize = sizeof (PmPnpDispatchTable) / sizeof (PmPnpDispatchTable[0]);