/*++ Copyright (c) 1998-2000 Microsoft Corporation Module Name: fdo.c Abstract: This module handles IRPs for PCI FDO's. Author: Adrian J. Oney (adriao) & Andrew Thornton (andrewth) 10-20-1998 Revision History: --*/ #include "pcip.h" NTSTATUS PciFdoIrpStartDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpQueryRemoveDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpRemoveDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpCancelRemoveDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpStopDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpQueryStopDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpCancelStopDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpQueryCapabilities( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpQueryInterface( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpQueryLegacyBusInformation( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpDeviceUsageNotification( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); NTSTATUS PciFdoIrpSurpriseRemoval( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ); VOID PciGetHotPlugParameters( IN PPCI_FDO_EXTENSION Fdo ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PciAddDevice) #pragma alloc_text(PAGE, PciInitializeFdoExtensionCommonFields) #pragma alloc_text(PAGE, PciFdoIrpStartDevice) #pragma alloc_text(PAGE, PciFdoIrpQueryRemoveDevice) #pragma alloc_text(PAGE, PciFdoIrpRemoveDevice) #pragma alloc_text(PAGE, PciFdoIrpCancelRemoveDevice) #pragma alloc_text(PAGE, PciFdoIrpQueryStopDevice) #pragma alloc_text(PAGE, PciFdoIrpStopDevice) #pragma alloc_text(PAGE, PciFdoIrpCancelStopDevice) #pragma alloc_text(PAGE, PciFdoIrpQueryDeviceRelations) #pragma alloc_text(PAGE, PciFdoIrpQueryInterface) #pragma alloc_text(PAGE, PciFdoIrpQueryCapabilities) #pragma alloc_text(PAGE, PciFdoIrpDeviceUsageNotification) #pragma alloc_text(PAGE, PciFdoIrpSurpriseRemoval) #pragma alloc_text(PAGE, PciFdoIrpQueryLegacyBusInformation) #pragma alloc_text(PAGE, PciGetHotPlugParameters) #endif // // The following is used to determine if we failed to get a // reasonable configuration from the PDO (in AddDevice) more // than once. If only once, we try to guess, if twice, we're // in big trouble. // static BOOLEAN HaveGuessedConfigOnceAlready = FALSE; /*++ The majority of functions in this file are called based on their presence in Pnp and Po dispatch tables. In the interests of brevity the arguments to all those functions will be described below: NTSTATUS PciXxxPdo( IN PIRP Irp, IN PIO_STACK_LOCATION IrpStack, IN PPCI_EXTENSION DeviceExtension ) Routine Description: This function handles the Xxx requests for a given PCI FDO or PDO. Arguments: Irp - Points to the IRP associated with this request. IrpStack - Points to the current stack location for this request. DeviceExtension - Points to the device's extension. Return Value: Status code that indicates whether or not the function was successful. STATUS_NOT_SUPPORTED indicates that the IRP should be completed without changing the Irp->IoStatus.Status field otherwise it is updated with this status. --*/ #define PCI_MAX_MINOR_POWER_IRP 0x3 #define PCI_MAX_MINOR_PNP_IRP 0x18 PCI_MN_DISPATCH_TABLE PciFdoDispatchPowerTable[] = { { IRP_DISPATCH, PciFdoWaitWake }, // 0x00 - IRP_MN_WAIT_WAKE { IRP_DOWNWARD, PciIrpNotSupported }, // 0x01 - IRP_MN_POWER_SEQUENCE { IRP_DOWNWARD, PciFdoSetPowerState }, // 0x02 - IRP_MN_SET_POWER { IRP_DOWNWARD, PciFdoIrpQueryPower }, // 0x03 - IRP_MN_QUERY_POWER { IRP_DOWNWARD, PciIrpNotSupported } // - UNHANDLED Power IRP }; PCI_MN_DISPATCH_TABLE PciFdoDispatchPnpTable[] = { { IRP_UPWARD, PciFdoIrpStartDevice }, // 0x00 - IRP_MN_START_DEVICE { IRP_DOWNWARD, PciFdoIrpQueryRemoveDevice }, // 0x01 - IRP_MN_QUERY_REMOVE_DEVICE { IRP_DISPATCH, PciFdoIrpRemoveDevice }, // 0x02 - IRP_MN_REMOVE_DEVICE { IRP_DOWNWARD, PciFdoIrpCancelRemoveDevice }, // 0x03 - IRP_MN_CANCEL_REMOVE_DEVICE { IRP_DOWNWARD, PciFdoIrpStopDevice }, // 0x04 - IRP_MN_STOP_DEVICE { IRP_DOWNWARD, PciFdoIrpQueryStopDevice }, // 0x05 - IRP_MN_QUERY_STOP_DEVICE { IRP_DOWNWARD, PciFdoIrpCancelStopDevice }, // 0x06 - IRP_MN_CANCEL_STOP_DEVICE { IRP_DOWNWARD, PciFdoIrpQueryDeviceRelations }, // 0x07 - IRP_MN_QUERY_DEVICE_RELATIONS { IRP_DISPATCH, PciFdoIrpQueryInterface }, // 0x08 - IRP_MN_QUERY_INTERFACE { IRP_UPWARD, PciFdoIrpQueryCapabilities }, // 0x09 - IRP_MN_QUERY_CAPABILITIES { IRP_DOWNWARD, PciIrpNotSupported }, // 0x0A - IRP_MN_QUERY_RESOURCES { IRP_DOWNWARD, PciIrpNotSupported }, // 0x0B - IRP_MN_QUERY_RESOURCE_REQUIREMENTS { IRP_DOWNWARD, PciIrpNotSupported }, // 0x0C - IRP_MN_QUERY_DEVICE_TEXT { IRP_DOWNWARD, PciIrpNotSupported }, // 0x0D - IRP_MN_FILTER_RESOURCE_REQUIREMENTS { IRP_DOWNWARD, PciIrpNotSupported }, // 0x0E - NOT USED { IRP_DOWNWARD, PciIrpNotSupported }, // 0x0F - IRP_MN_READ_CONFIG { IRP_DOWNWARD, PciIrpNotSupported }, // 0x10 - IRP_MN_WRITE_CONFIG { IRP_DOWNWARD, PciIrpNotSupported }, // 0x11 - IRP_MN_EJECT { IRP_DOWNWARD, PciIrpNotSupported }, // 0x12 - IRP_MN_SET_LOCK { IRP_DOWNWARD, PciIrpNotSupported }, // 0x13 - IRP_MN_QUERY_ID { IRP_DOWNWARD, PciIrpNotSupported }, // 0x14 - IRP_MN_QUERY_PNP_DEVICE_STATE { IRP_DOWNWARD, PciIrpNotSupported }, // 0x15 - IRP_MN_QUERY_BUS_INFORMATION { IRP_UPWARD, PciFdoIrpDeviceUsageNotification }, // 0x16 - IRP_MN_DEVICE_USAGE_NOTIFICATION { IRP_DOWNWARD, PciFdoIrpSurpriseRemoval }, // 0x17 - IRP_MN_SURPRISE_REMOVAL { IRP_DOWNWARD, PciFdoIrpQueryLegacyBusInformation }, // 0x18 - IRP_MN_QUERY_LEGACY_BUS_INFORMATION { IRP_DOWNWARD, PciIrpNotSupported } // - UNHANDLED PNP IRP }; // // This is the major function dispatch table for Fdo's // PCI_MJ_DISPATCH_TABLE PciFdoDispatchTable = { PCI_MAX_MINOR_PNP_IRP, PciFdoDispatchPnpTable, // Pnp irps PCI_MAX_MINOR_POWER_IRP, PciFdoDispatchPowerTable, // Power irps IRP_DOWNWARD, PciIrpNotSupported, // SystemControl - just pass down! IRP_DOWNWARD, PciIrpNotSupported // DeviceControl - just pass down! }; NTSTATUS PciFdoIrpStartDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) /*++ Routine Description: Handler routine for start IRPs. This allows PDO filters to modify the allocated resources if they are filtering resource requirements. Called after completion. Arguments: DeviceObject - Supplies the device object Irp - Supplies the IRP_MN_START_DEVICE irp. FdoExtension - Supplies the FDO extension Return Value: ERROR_SUCCESS if successful NTSTATUS error code otherwise --*/ { NTSTATUS status; PPCI_FDO_EXTENSION fdoExtension; PPCI_PDO_EXTENSION pdoExtension = NULL; PCM_RESOURCE_LIST resources; UCHAR barType[PCI_TYPE1_ADDRESSES] = {0,0}; ULONG index; PCM_PARTIAL_RESOURCE_DESCRIPTOR currentResource; PIO_RESOURCE_DESCRIPTOR currentRequirement; PAGED_CODE(); if (!NT_SUCCESS(Irp->IoStatus.Status)) { return STATUS_NOT_SUPPORTED; } fdoExtension = (PPCI_FDO_EXTENSION) DeviceExtension; status = PciBeginStateTransition(DeviceExtension, PciStarted); if (!NT_SUCCESS(status)) { return status; } // // If this is a PCI-PCI bridge then check if it has any bars and if so exclude // them from the list used to initialize the arbiters. // resources = IrpSp->Parameters.StartDevice.AllocatedResources; if (resources && !PCI_IS_ROOT_FDO(fdoExtension)) { PCI_ASSERT(resources->Count == 1); pdoExtension = PCI_BRIDGE_PDO(fdoExtension); if (pdoExtension->Resources) { if (pdoExtension->HeaderType == PCI_BRIDGE_TYPE) { // // If there are any bars they are at the beginning of the list // so "begin at the beginning which is a very good place to start". // currentResource = resources->List[0].PartialResourceList.PartialDescriptors; for (index = 0; index < PCI_TYPE1_ADDRESSES; index++) { // // Extract the requirement we asked for to determine if this // bridge implements any bars (index 0 and 1 in the Limits // array) // currentRequirement = &pdoExtension->Resources->Limit[index]; // // CmResourceTypeNull indicates that we didn't request any // resources so the bar is not implemented and there is nothing // to prune out. // if (currentRequirement->Type != CmResourceTypeNull) { PCI_ASSERT(currentResource->Type == currentRequirement->Type); // // Save away the type so we can restore it later // barType[index] = currentResource->Type; // // Null out the resource so we don't configure the arbiters to // use it // currentResource->Type = CmResourceTypeNull; // // Advance the pointer into the started resources by 2 // descriptors to skip over the device private // PCI_ASSERT((currentResource+1)->Type == CmResourceTypeDevicePrivate); currentResource+=2; } } } } } // // Initialize arbiters with these resources. // status = PciInitializeArbiterRanges(fdoExtension, resources); if (!NT_SUCCESS(status)) { goto cleanup; } // // Restore the original resource list if we changed it // if (resources && !PCI_IS_ROOT_FDO(fdoExtension) && pdoExtension->Resources) { currentResource = resources->List[0].PartialResourceList.PartialDescriptors; for (index = 0; index < PCI_TYPE1_ADDRESSES; index++) { if (barType[index] != CmResourceTypeNull) { currentResource->Type = barType[index]; // // Advance the pointer into the started resources by 2 // descriptors to skip over the device private // PCI_ASSERT((currentResource+1)->Type == CmResourceTypeDevicePrivate); currentResource+=2; } } } cleanup: if (!NT_SUCCESS(status)) { PciCancelStateTransition(DeviceExtension, PciStarted); return status; } PciCommitStateTransition(DeviceExtension, PciStarted); return STATUS_SUCCESS; } NTSTATUS PciFdoIrpQueryRemoveDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PAGED_CODE(); return PciBeginStateTransition(DeviceExtension, PciDeleted); } NTSTATUS PciFdoIrpRemoveDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PPCI_FDO_EXTENSION fdoExtension; PPCI_PDO_EXTENSION pdox; PDEVICE_OBJECT attachedDevice; NTSTATUS status; PAGED_CODE(); fdoExtension = (PPCI_FDO_EXTENSION) DeviceExtension; ExAcquireFastMutex(&fdoExtension->ChildListMutex); while (fdoExtension->ChildPdoList) { pdox = (PPCI_PDO_EXTENSION) fdoExtension->ChildPdoList; #if DBG PciDebugPrint( PciDbgVerbose, "PCI Killing PDO %p PDOx %p (b=%d, d=%d, f=%d)\n", pdox->PhysicalDeviceObject, pdox, PCI_PARENT_FDOX(pdox)->BaseBus, pdox->Slot.u.bits.DeviceNumber, pdox->Slot.u.bits.FunctionNumber ); PCI_ASSERT(pdox->DeviceState == PciNotStarted); #endif PciPdoDestroy(pdox->PhysicalDeviceObject); } ExReleaseFastMutex(&fdoExtension->ChildListMutex); // // Destroy any secondary extensions associated with // this FDO. // while (fdoExtension->SecondaryExtension.Next) { PcipDestroySecondaryExtension( &fdoExtension->SecondaryExtension, NULL, fdoExtension->SecondaryExtension.Next ); } // // Destroy the FDO. // // The IRP needs to go down the device stack but we // need to remove the device from the stack so grab // the next object first, then detach, then pass it // down. // PciDebugPrint( PciDbgInformative, "PCI FDOx (%p) destroyed.", fdoExtension ); // // Note that a filter above us may have failed Start. If this is so, we get // no query because the "devnode" has never been started... // if (!PciIsInTransitionToState(DeviceExtension, PciDeleted)) { status = PciBeginStateTransition(DeviceExtension, PciDeleted); PCI_ASSERT(NT_SUCCESS(status)); } PciCommitStateTransition(DeviceExtension, PciDeleted); PciRemoveEntryFromList(&PciFdoExtensionListHead, &fdoExtension->List, &PciGlobalLock); attachedDevice = fdoExtension->AttachedDeviceObject; IoDetachDevice(attachedDevice); IoDeleteDevice(fdoExtension->FunctionalDeviceObject); Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(attachedDevice, Irp); } NTSTATUS PciFdoIrpCancelRemoveDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PAGED_CODE(); PciCancelStateTransition(DeviceExtension, PciDeleted); return STATUS_SUCCESS; } NTSTATUS PciFdoIrpStopDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PAGED_CODE(); PciCommitStateTransition(DeviceExtension, PciStopped); return STATUS_SUCCESS; } NTSTATUS PciFdoIrpQueryStopDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PAGED_CODE(); PciBeginStateTransition(DeviceExtension, PciStopped); // // We don't support multilevel rebalance so we can't stop host bridges. // return STATUS_UNSUCCESSFUL; } NTSTATUS PciFdoIrpCancelStopDevice( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PAGED_CODE(); PciCancelStateTransition(DeviceExtension, PciStopped); return STATUS_SUCCESS; } NTSTATUS PciFdoIrpQueryDeviceRelations( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PAGED_CODE(); if (IrpSp->Parameters.QueryDeviceRelations.Type == BusRelations) { return PciQueryDeviceRelations( (PPCI_FDO_EXTENSION) DeviceExtension, (PDEVICE_RELATIONS *) &Irp->IoStatus.Information ); } // // No other relation types need to be handled. // return STATUS_NOT_SUPPORTED; } NTSTATUS PciFdoIrpQueryCapabilities( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) /*++ Routine Description: Snoops the results of a QUERY CAPABILITIES IRP that got sent downwards. This saves us having to send our own for things like the device's power characteristics. Arguments: DeviceObject - Supplies the device object Irp - Supplies the IRP_MN_QUERY_CAPABILITIES irp. FdoExtension - Supplies the FDO extension Return Value: STATUS_SUCCESS --*/ { PDEVICE_CAPABILITIES capabilities; PPCI_FDO_EXTENSION fdoExtension; PAGED_CODE(); fdoExtension = (PPCI_FDO_EXTENSION) DeviceExtension; ASSERT_PCI_FDO_EXTENSION(fdoExtension); PciDebugPrint( PciDbgQueryCap, "PCI - FdoQueryCapabilitiesCompletion (fdox %08x) child status = %08x\n", fdoExtension, Irp->IoStatus.Status ); // // Grab a pointer to the capablities for easy referencing // capabilities = IrpSp->Parameters.DeviceCapabilities.Capabilities; // // Remember what the system wake and device wake level are // fdoExtension->PowerState.SystemWakeLevel = capabilities->SystemWake; fdoExtension->PowerState.DeviceWakeLevel = capabilities->DeviceWake; // // Grab the S-state to D-State mapping // RtlCopyMemory( fdoExtension->PowerState.SystemStateMapping, capabilities->DeviceState, (PowerSystemShutdown + 1) * sizeof(DEVICE_POWER_STATE) ); #if DBG if (PciDebug & PciDbgQueryCap) { PciDebugDumpQueryCapabilities(capabilities); } #endif return STATUS_SUCCESS; } NTSTATUS PciFdoIrpQueryLegacyBusInformation( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PAGED_CODE(); return PciQueryLegacyBusInformation( (PPCI_FDO_EXTENSION) DeviceExtension, (PLEGACY_BUS_INFORMATION *) &Irp->IoStatus.Information ); } NTSTATUS PciFdoIrpDeviceUsageNotification( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PPCI_FDO_EXTENSION fdoExtension; PAGED_CODE(); fdoExtension = (PPCI_FDO_EXTENSION) DeviceExtension; if (NT_SUCCESS(Irp->IoStatus.Status)) { return PciLocalDeviceUsage(&fdoExtension->PowerState, Irp); } else { return STATUS_NOT_SUPPORTED; } } NTSTATUS PciFdoIrpQueryInterface( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PPCI_FDO_EXTENSION fdoExtension; NTSTATUS status; PAGED_CODE(); fdoExtension = (PPCI_FDO_EXTENSION) DeviceExtension; ASSERT_PCI_FDO_EXTENSION(fdoExtension); // NTRAID #54671 - 4/20/2000 - andrewth // // We might want to do a synchronizing state // transition here so we don't attempt to get the interface during a // stop/remove sequence. // // We shouldn't hold interfaces when something isn't // started. But we won't boot unless we hack the below.... // //if (fdoExtension->DeviceState != PciStarted) { if (fdoExtension->DeviceState == PciDeleted) { return PciPassIrpFromFdoToPdo(DeviceExtension, Irp); } status = PciQueryInterface( fdoExtension, IrpSp->Parameters.QueryInterface.InterfaceType, IrpSp->Parameters.QueryInterface.Size, IrpSp->Parameters.QueryInterface.Version, IrpSp->Parameters.QueryInterface.InterfaceSpecificData, IrpSp->Parameters.QueryInterface.Interface, FALSE ); if (NT_SUCCESS(status)) { Irp->IoStatus.Status = status; return PciPassIrpFromFdoToPdo(DeviceExtension, Irp); } else if (status == STATUS_NOT_SUPPORTED) { // // Status == STATUS_NOT_SUPPORTED. Pass IRP down the stack // and see if anyone else is kind enough to provide this // interface. // status = PciCallDownIrpStack(DeviceExtension, Irp); if (status == STATUS_NOT_SUPPORTED) { // // If nobody provided the interface, try again at // this level. // status = PciQueryInterface( fdoExtension, IrpSp->Parameters.QueryInterface.InterfaceType, IrpSp->Parameters.QueryInterface.Size, IrpSp->Parameters.QueryInterface.Version, IrpSp->Parameters.QueryInterface.InterfaceSpecificData, IrpSp->Parameters.QueryInterface.Interface, TRUE ); } } if (status != STATUS_NOT_SUPPORTED) { Irp->IoStatus.Status = status; } else { status = Irp->IoStatus.Status; } IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } NTSTATUS PciFdoIrpSurpriseRemoval( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, IN PPCI_COMMON_EXTENSION DeviceExtension ) { PPCI_FDO_EXTENSION fdoExtension; NTSTATUS status; PAGED_CODE(); fdoExtension = (PPCI_FDO_EXTENSION) DeviceExtension; status = PciBeginStateTransition(DeviceExtension, PciSurpriseRemoved); PCI_ASSERT(NT_SUCCESS(status)); if (NT_SUCCESS(status)) { PciCommitStateTransition(DeviceExtension, PciSurpriseRemoved); status = PciBeginStateTransition(DeviceExtension, PciDeleted); } return status; } NTSTATUS PciAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: Given a physical device object, this routine creates a functional device object for it. Arguments: DriverObject - Pointer to our driver's DRIVER_OBJECT structure. PhysicalDeviceObject - Pointer to the physical device object for which we must create a functional device object. Return Value: NT status. --*/ { PDEVICE_OBJECT functionalDeviceObject = NULL; PDEVICE_OBJECT attachedTo = NULL; PPCI_FDO_EXTENSION fdoExtension = NULL; PPCI_FDO_EXTENSION pciParentFdoExtension; PPCI_PDO_EXTENSION pdoExtension = NULL; PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor; NTSTATUS status; HANDLE deviceRegistryHandle; ULONG resultLength; UNICODE_STRING hackFlagsString; UCHAR infoBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG) - 1]; PKEY_VALUE_PARTIAL_INFORMATION info = (PKEY_VALUE_PARTIAL_INFORMATION) infoBuffer; PAGED_CODE(); PciDebugPrint(PciDbgAddDevice, "PCI - AddDevice (a new bus).\n"); // // Find out if the PDO was created by the PCI driver. That is, // if it is a child or a root bus. Validate a few things before // going any further. // pciParentFdoExtension = PciFindParentPciFdoExtension(PhysicalDeviceObject, &PciGlobalLock); if (pciParentFdoExtension) { // // The PDO was created by this driver, therefore we can look at // the extension. Get it and verify it's ours. // pdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension; ASSERT_PCI_PDO_EXTENSION(pdoExtension); // // The only thing we should get an add device that is a // child device is a PCI-PCI bridge. // if ((pdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) || (pdoExtension->SubClass != PCI_SUBCLASS_BR_PCI_TO_PCI)) { PciDebugPrint( PciDbgAlways, "PCI - PciAddDevice for Non-Root/Non-PCI-PCI bridge,\n" " Class %02x, SubClass %02x, will not add.\n", pdoExtension->BaseClass, pdoExtension->SubClass ); PCI_ASSERT((pdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && (pdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI)); status = STATUS_INVALID_DEVICE_REQUEST; goto cleanup; } PciDebugPrint(PciDbgAddDevice, "PCI - AddDevice (new bus is child of bus 0x%x).\n", pciParentFdoExtension->BaseBus ); if (!PciAreBusNumbersConfigured(pdoExtension)) { // // This bridge isn't configured and if we had been able to we would // already have done so // PciDebugPrint( PciDbgAddDevice | PciDbgInformative, "PCI - Bus numbers not configured for bridge (0x%x.0x%x.0x%x)\n", pciParentFdoExtension->BaseBus, pdoExtension->Slot.u.bits.DeviceNumber, pdoExtension->Slot.u.bits.FunctionNumber, pdoExtension->Dependent.type1.PrimaryBus ); status = STATUS_INVALID_DEVICE_REQUEST; goto cleanup; } } // // We've been given the PhysicalDeviceObject for a PCI bus. Create the // functionalDeviceObject. Our FDO will be nameless. // status = IoCreateDevice( DriverObject, // our driver object sizeof(PCI_FDO_EXTENSION), // size of our extension NULL, // our name FILE_DEVICE_BUS_EXTENDER, // device type 0, // device characteristics FALSE, // not exclusive &functionalDeviceObject // store new device object here ); if (!NT_SUCCESS(status)) { goto cleanup; } fdoExtension = (PPCI_FDO_EXTENSION)functionalDeviceObject->DeviceExtension; // // We have our functionalDeviceObject, initialize it. // PciInitializeFdoExtensionCommonFields( fdoExtension, functionalDeviceObject, PhysicalDeviceObject ); // // Now attach to the PDO we were given. // attachedTo = IoAttachDeviceToDeviceStack(functionalDeviceObject, PhysicalDeviceObject); if (attachedTo == NULL) { PCI_ASSERT(attachedTo != NULL); status = STATUS_NO_SUCH_DEVICE; goto cleanup; } fdoExtension->AttachedDeviceObject = attachedTo; // // Get the access registers and base bus number for this bus. // If this bus was discovered by this driver, then the PDO was // created by this driver and will be on one of the PDO lists // under one of the FDOs owned by this driver. Otherwise it // is a new root,.... use magic. // if (pciParentFdoExtension) { // // This physical device was discovered by this driver. // Get the bus number from the PDO extension. // fdoExtension->BaseBus = pdoExtension->Dependent.type1.SecondaryBus; // // Copy the access methods from the root fdo and set // the root fdo back pointer. // fdoExtension->BusRootFdoExtension = pciParentFdoExtension->BusRootFdoExtension; // // Point the PDOextension to the new FDOextension (also indicates // the object is a bridge) and vice versa. // pdoExtension->BridgeFdoExtension = fdoExtension; fdoExtension->ParentFdoExtension = pciParentFdoExtension; } else { PVOID buffer; // // Get the boot configuration (CmResourceList) for // this PDO. This gives us the bus number and the // ranges covered by this host bridge. // status = PciGetDeviceProperty( PhysicalDeviceObject, DevicePropertyBootConfiguration, &buffer ); if (NT_SUCCESS(status)) { #if DBG PciDebugPrint(PciDbgAddDeviceRes, "PCI - CM RESOURCE LIST FROM ROOT PDO\n"); PciDebugPrintCmResList(PciDbgAddDeviceRes, buffer); #endif descriptor = PciFindDescriptorInCmResourceList( CmResourceTypeBusNumber, buffer, NULL ); } else { descriptor = NULL; } if (descriptor != NULL) { // // Sanity check, some servers are aproaching // 256 busses but as there is no way to deal with // numbering bridges under a bus > 256 and we don't // have raw and translated bus numbers yet - it had // better be < 0xFF! // PCI_ASSERT(descriptor->u.BusNumber.Start <= 0xFF); PCI_ASSERT(descriptor->u.BusNumber.Start + descriptor->u.BusNumber.Length - 1 <= 0xFF); fdoExtension->BaseBus = (UCHAR)descriptor->u.BusNumber.Start; fdoExtension->MaxSubordinateBus = (UCHAR)(descriptor->u.BusNumber.Start + descriptor->u.BusNumber.Length - 1); PciDebugPrint(PciDbgAddDevice, "PCI - Root Bus # 0x%x->0x%x.\n", fdoExtension->BaseBus, fdoExtension->MaxSubordinateBus ); } else { // // HaveGuessedConfigOnceAlready is used to tell // if have multiple roots and no config info. If // this happens we end up gussing the bus number // as zero, doing this more than once is not good. // if (HaveGuessedConfigOnceAlready) { KeBugCheckEx(PCI_BUS_DRIVER_INTERNAL, PCI_BUGCODE_TOO_MANY_CONFIG_GUESSES, (ULONG_PTR)PhysicalDeviceObject, 0, 0); } PciDebugPrint( PciDbgAlways, "PCI Will use default configuration.\n" ); HaveGuessedConfigOnceAlready = TRUE; fdoExtension->BaseBus = 0; } fdoExtension->BusRootFdoExtension = fdoExtension; } // // Organise access to config space // status = PciGetConfigHandlers(fdoExtension); if (!NT_SUCCESS(status)) { goto cleanup; } // // Initialize arbiters for this FDO. // status = PciInitializeArbiters(fdoExtension); if (!NT_SUCCESS(status)) { goto cleanup; } // // Indicate this is a REAL FDO extension that is part of a REAL // FDO. (Fake extensions exist to assist in the enumeration of // busses which PCI isn't the real controller for (eg CardBus). // fdoExtension->Fake = FALSE; // // Insert this Fdo in the list of PCI parent Fdos. // PciInsertEntryAtTail(&PciFdoExtensionListHead, &fdoExtension->List, &PciGlobalLock); #if defined(_WIN64) // // Update the legacy hardware tree that would have been build by the ARC // firmware or NTDetect which don't exist here. // status = PciUpdateLegacyHardwareDescription(fdoExtension); if (!NT_SUCCESS(status)) { goto cleanup; } #endif // // Check if there are any hacks to apply to this bus. // These are located under the device registry key in a value called HackFlags // status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &deviceRegistryHandle ); if (!NT_SUCCESS(status)) { goto cleanup; } PciConstStringToUnicodeString(&hackFlagsString, L"HackFlags"); status = ZwQueryValueKey(deviceRegistryHandle, &hackFlagsString, KeyValuePartialInformation, info, sizeof(infoBuffer), &resultLength ); ZwClose(deviceRegistryHandle); // // If we have valid data in the registry then remember it // if (NT_SUCCESS(status) && (info->Type == REG_DWORD) && (info->DataLength == sizeof(ULONG))) { fdoExtension->BusHackFlags = *((PULONG)(&info->Data)); } // // We can receive IRPs now... // functionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; // // Get any hotplug parameters (we send IRPS for this so it must be after // DO_DEVICE_INITIALIZING is cleared so we can deal with them) // PciGetHotPlugParameters(fdoExtension); return STATUS_SUCCESS; cleanup: PCI_ASSERT(!NT_SUCCESS(status)); // // Destroy any secondary extensions associated with // this FDO. // if (fdoExtension) { while (fdoExtension->SecondaryExtension.Next) { PcipDestroySecondaryExtension( &fdoExtension->SecondaryExtension, NULL, fdoExtension->SecondaryExtension.Next ); } } if (attachedTo) { IoDetachDevice(attachedTo); } if (functionalDeviceObject) { IoDeleteDevice(functionalDeviceObject); } return status; } VOID PciInitializeFdoExtensionCommonFields( IN PPCI_FDO_EXTENSION FdoExtension, IN PDEVICE_OBJECT Fdo, IN PDEVICE_OBJECT Pdo ) { RtlZeroMemory(FdoExtension, sizeof(PCI_FDO_EXTENSION)); FdoExtension->ExtensionType = PciFdoExtensionType; FdoExtension->PhysicalDeviceObject = Pdo; FdoExtension->FunctionalDeviceObject = Fdo; FdoExtension->PowerState.CurrentSystemState = PowerSystemWorking; FdoExtension->PowerState.CurrentDeviceState = PowerDeviceD0; FdoExtension->IrpDispatchTable = &PciFdoDispatchTable; ExInitializeFastMutex(&FdoExtension->SecondaryExtMutex); ExInitializeFastMutex(&FdoExtension->ChildListMutex); PciInitializeState((PPCI_COMMON_EXTENSION) FdoExtension); } VOID PciGetHotPlugParameters( IN PPCI_FDO_EXTENSION Fdo ) /*++ Description: Runs the _HPP (described below) on the device and saves the parameters if available Method (_HPP, 0) { Return (Package(){ 0x00000008, // CacheLineSize in DWORDS 0x00000040, // LatencyTimer in PCI clocks 0x00000001, // Enable SERR (Boolean) 0x00000001 // Enable PERR (Boolean) }) Arguments: Fdo - The PDO extension for the bridge Return Value: TRUE - if the parameters are available, FASLE otherwise --*/ { NTSTATUS status; ACPI_EVAL_INPUT_BUFFER input; PACPI_EVAL_OUTPUT_BUFFER output = NULL; ULONG count; ULONG outputSize = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + sizeof(ACPI_METHOD_ARGUMENT) * PCI_HPP_PACKAGE_COUNT; PAGED_CODE(); output = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, outputSize); if (!output) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } RtlZeroMemory(&input, sizeof(ACPI_EVAL_INPUT_BUFFER)); RtlZeroMemory(output, outputSize); // // Send a IOCTL to ACPI to request it to run the _HPP method on this device // if the method it is present // input.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; input.MethodNameAsUlong = (ULONG)'PPH_'; // // PciSendIoctl deals with sending this from the top of the stack. // status = PciSendIoctl(Fdo->PhysicalDeviceObject, IOCTL_ACPI_EVAL_METHOD, &input, sizeof(ACPI_EVAL_INPUT_BUFFER), output, outputSize ); if (!NT_SUCCESS(status)) { // // Inherit them from my parent (If I have one) // if (!PCI_IS_ROOT_FDO(Fdo)) { RtlCopyMemory(&Fdo->HotPlugParameters, &Fdo->ParentFdoExtension->HotPlugParameters, sizeof(Fdo->HotPlugParameters) ); } } else { if (output->Count != PCI_HPP_PACKAGE_COUNT) { goto exit; } // // Check they are all integers and in the right bounds // for (count = 0; count < PCI_HPP_PACKAGE_COUNT; count++) { ULONG current; if (output->Argument[count].Type != ACPI_METHOD_ARGUMENT_INTEGER) { goto exit; } current = output->Argument[count].Argument; switch (count) { case PCI_HPP_CACHE_LINE_SIZE_INDEX: case PCI_HPP_LATENCY_TIMER_INDEX: // // These registers are only a UCHAR in length // if (current > 0xFF) { goto exit; } break; case PCI_HPP_ENABLE_SERR_INDEX: case PCI_HPP_ENABLE_PERR_INDEX: // // These are booleans - 1 or 0 // if (current > 1) { goto exit; } break; } } // // Finally save them and remember we got them. // Fdo->HotPlugParameters.CacheLineSize = (UCHAR)output->Argument[PCI_HPP_CACHE_LINE_SIZE_INDEX].Argument; Fdo->HotPlugParameters.LatencyTimer = (UCHAR)output->Argument[PCI_HPP_LATENCY_TIMER_INDEX].Argument; Fdo->HotPlugParameters.EnableSERR = (BOOLEAN)output->Argument[PCI_HPP_ENABLE_SERR_INDEX].Argument; Fdo->HotPlugParameters.EnablePERR = (BOOLEAN)output->Argument[PCI_HPP_ENABLE_PERR_INDEX].Argument; Fdo->HotPlugParameters.Acquired = TRUE; } exit: if (output) { ExFreePool(output); } }