X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=reactos%2Fdrivers%2Fbus%2Fpcix%2Fenum.c;h=3caa988223213d041ff4b09918f31326f526ab2e;hp=d67db2862ae63036e6d99ad6f6432e4c87c0f50e;hb=af01479fbfc43d41eb62ef48643a4451d861f240;hpb=3ab04b5faa39ff5418e8afceb1521be33a9964fb diff --git a/reactos/drivers/bus/pcix/enum.c b/reactos/drivers/bus/pcix/enum.c index d67db2862ae..3caa9882232 100644 --- a/reactos/drivers/bus/pcix/enum.c +++ b/reactos/drivers/bus/pcix/enum.c @@ -14,6 +14,1596 @@ /* GLOBALS ********************************************************************/ +PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements; + +PCI_CONFIGURATOR PciConfigurators[] = +{ + { + Device_MassageHeaderForLimitsDetermination, + Device_RestoreCurrent, + Device_SaveLimits, + Device_SaveCurrentSettings, + Device_ChangeResourceSettings, + Device_GetAdditionalResourceDescriptors, + Device_ResetDevice + }, + { + PPBridge_MassageHeaderForLimitsDetermination, + PPBridge_RestoreCurrent, + PPBridge_SaveLimits, + PPBridge_SaveCurrentSettings, + PPBridge_ChangeResourceSettings, + PPBridge_GetAdditionalResourceDescriptors, + PPBridge_ResetDevice + }, + { + Cardbus_MassageHeaderForLimitsDetermination, + Cardbus_RestoreCurrent, + Cardbus_SaveLimits, + Cardbus_SaveCurrentSettings, + Cardbus_ChangeResourceSettings, + Cardbus_GetAdditionalResourceDescriptors, + Cardbus_ResetDevice + } +}; + /* FUNCTIONS ******************************************************************/ +NTSTATUS +NTAPI +PciQueryResources(IN PPCI_PDO_EXTENSION PdoExtension, + OUT PCM_RESOURCE_LIST *Buffer) +{ + /* Not yet implemented */ + UNIMPLEMENTED; + while (TRUE); + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +PciQueryTargetDeviceRelations(IN PPCI_PDO_EXTENSION PdoExtension, + IN OUT PDEVICE_RELATIONS *pDeviceRelations) +{ + PDEVICE_RELATIONS DeviceRelations; + PAGED_CODE(); + + /* If there were existing relations, free them */ + if (*pDeviceRelations) ExFreePoolWithTag(*pDeviceRelations, 0); + + /* Allocate a new structure for the relations */ + DeviceRelations = ExAllocatePoolWithTag(NonPagedPool, + sizeof(DEVICE_RELATIONS), + 'BicP'); + if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES; + + /* Only one relation: the PDO */ + DeviceRelations->Count = 1; + DeviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject; + ObReferenceObject(DeviceRelations->Objects[0]); + + /* Return the new relations */ + *pDeviceRelations = DeviceRelations; + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +PciQueryEjectionRelations(IN PPCI_PDO_EXTENSION PdoExtension, + IN OUT PDEVICE_RELATIONS *pDeviceRelations) +{ + /* Not yet implemented */ + UNIMPLEMENTED; + while (TRUE); +} + +NTSTATUS +NTAPI +PciQueryRequirements(IN PPCI_PDO_EXTENSION PdoExtension, + IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList) +{ + /* Not yet implemented */ + UNIMPLEMENTED; + while (TRUE); + return STATUS_SUCCESS; +} + +/* + * 7. The IO/MEM/Busmaster decodes are disabled for the device. + * 8. The PCI bus driver sets the operating mode bits of the Programming + * Interface byte to switch the controller to native mode. + * + * Important: When the controller is set to native mode, it must quiet itself + * and must not decode I/O resources or generate interrupts until the operating + * system has enabled the ports in the PCI configuration header. + * The IO/MEM/BusMaster bits will be disabled before the mode change, but it + * is not possible to disable interrupts on the device. The device must not + * generate interrupts (either legacy or native mode) while the decodes are + * disabled in the command register. + * + * This operation is expected to be instantaneous and the operating system does + * not stall afterward. It is also expected that the interrupt pin register in + * the PCI Configuration space for this device is accurate. The operating system + * re-reads this data after previously ignoring it. + */ +BOOLEAN +NTAPI +PciConfigureIdeController(IN PPCI_PDO_EXTENSION PdoExtension, + IN PPCI_COMMON_HEADER PciData, + IN BOOLEAN Initial) +{ + UCHAR MasterMode, SlaveMode, MasterFixed, SlaveFixed, ProgIf, NewProgIf; + BOOLEAN Switched; + USHORT Command; + + /* Assume it won't work */ + Switched = FALSE; + + /* Get master and slave current settings, and programmability flag */ + ProgIf = PciData->ProgIf; + MasterMode = (ProgIf & 1) == 1; + MasterFixed = (ProgIf & 2) == 0; + SlaveMode = (ProgIf & 4) == 4; + SlaveFixed = (ProgIf & 8) == 0; + + /* + * [..] In order for Windows XP SP1 and Windows Server 2003 to switch an ATA + * ATA controller from compatible mode to native mode, the following must be + * true: + * + * - The controller must indicate in its programming interface that both channels + * can be switched to native mode. Windows XP SP1 and Windows Server 2003 do + * not support switching only one IDE channel to native mode. See the PCI IDE + * Controller Specification Revision 1.0 for details. + */ + if ((MasterMode != SlaveMode) || (MasterFixed != SlaveFixed)) + { + /* Windows does not support this configuration, fail */ + DPRINT1("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!", + PdoExtension->VendorId, + PdoExtension->DeviceId); + return Switched; + } + + /* Check if the controller is already in native mode */ + if ((MasterMode) && (SlaveMode)) + { + /* Check if I/O decodes should be disabled */ + if ((Initial) || (PdoExtension->IoSpaceUnderNativeIdeControl)) + { + /* Read the current command */ + PciReadDeviceConfig(PdoExtension, + &Command, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(USHORT)); + + /* Disable I/O space decode */ + Command &= ~PCI_ENABLE_IO_SPACE; + + /* Update new command in PCI IDE controller */ + PciWriteDeviceConfig(PdoExtension, + &Command, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(USHORT)); + + /* Save updated command value */ + PciData->Command = Command; + } + + /* The controller is now in native mode */ + Switched = TRUE; + } + else if (!(MasterFixed) && + !(SlaveFixed) && + (PdoExtension->BIOSAllowsIDESwitchToNativeMode) && + !(PdoExtension->HackFlags & PCI_HACK_DISABLE_IDE_NATIVE_MODE)) + { + /* Turn off decodes */ + PciDecodeEnable(PdoExtension, FALSE, NULL); + + /* Update the current command */ + PciReadDeviceConfig(PdoExtension, + &PciData->Command, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(USHORT)); + + /* Enable native mode */ + ProgIf = PciData->ProgIf | 5; + PciWriteDeviceConfig(PdoExtension, + &ProgIf, + FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf), + sizeof(UCHAR)); + + /* Verify the setting "stuck" */ + PciReadDeviceConfig(PdoExtension, + &NewProgIf, + FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf), + sizeof(UCHAR)); + if (NewProgIf == ProgIf) + { + /* Update the header and PDO data with the new programming mode */ + PciData->ProgIf = ProgIf; + PdoExtension->ProgIf = NewProgIf; + + /* Clear the first four BARs to reset current BAR setttings */ + PciData->u.type0.BaseAddresses[0] = 0; + PciData->u.type0.BaseAddresses[1] = 0; + PciData->u.type0.BaseAddresses[2] = 0; + PciData->u.type0.BaseAddresses[3] = 0; + PciWriteDeviceConfig(PdoExtension, + PciData->u.type0.BaseAddresses, + FIELD_OFFSET(PCI_COMMON_HEADER, + u.type0.BaseAddresses), + 4 * sizeof(ULONG)); + + /* Re-read the BARs to have the latest data for native mode IDE */ + PciReadDeviceConfig(PdoExtension, + PciData->u.type0.BaseAddresses, + FIELD_OFFSET(PCI_COMMON_HEADER, + u.type0.BaseAddresses), + 4 * sizeof(ULONG)); + + /* Re-read the interrupt pin used for native mode IDE */ + PciReadDeviceConfig(PdoExtension, + &PciData->u.type0.InterruptPin, + FIELD_OFFSET(PCI_COMMON_HEADER, + u.type0.InterruptPin), + sizeof(UCHAR)); + + /* The IDE Controller is now in native mode */ + Switched = TRUE; + } + else + { + /* Settings did not work, fail */ + DPRINT1("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!", + PciData->VendorID, + PciData->DeviceID); + } + } + + /* Return whether or not native mode was enabled on the IDE controller */ + return Switched; +} + +VOID +NTAPI +PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension, + IN PPCI_COMMON_HEADER PciData, + IN PCI_SLOT_NUMBER SlotNumber, + IN ULONG OperationType, + PPCI_PDO_EXTENSION PdoExtension) +{ + ULONG LegacyBaseAddress; + USHORT Command; + UCHAR RegValue; + + /* Check what kind of hack operation this is */ + switch (OperationType) + { + /* + * This is mostly concerned with fixing up incorrect class data that can + * exist on certain PCI hardware before the 2.0 spec was ratified. + */ + case PCI_HACK_FIXUP_BEFORE_CONFIGURATION: + + /* Note that the i82375 PCI/EISA and the i82378 PCI/ISA bridges that + * are present on certain DEC/NT Alpha machines are pre-PCI 2.0 devices + * and appear as non-classified, so their correct class/subclass data + * is written here instead. + */ + if ((PciData->VendorID == 0x8086) && + ((PciData->DeviceID == 0x482) || (PciData->DeviceID == 0x484))) + { + /* Note that 0x482 is the i82375 (EISA), 0x484 is the i82378 (ISA) */ + PciData->SubClass = PciData->DeviceID == 0x482 ? + PCI_SUBCLASS_BR_EISA : PCI_SUBCLASS_BR_ISA; + PciData->BaseClass = PCI_CLASS_BRIDGE_DEV; + + /* + * Because the software is modifying the actual header data from + * the BIOS, this flag tells the driver to ignore failures when + * comparing the original BIOS data with the PCI data. + */ + if (PdoExtension) PdoExtension->ExpectedWritebackFailure = TRUE; + } + + /* Note that in this case, an immediate return is issued */ + return; + + /* + * This is concerned with setting up interrupts correctly for native IDE + * mode, but will also handle broken VGA decoding on older bridges as + * well as a PAE-specific hack for certain Compaq Hot-Plug Controllers. + */ + case PCI_HACK_FIXUP_AFTER_CONFIGURATION: + + /* There should always be a PDO extension passed in */ + ASSERT(PdoExtension); + + /* + * On the OPTi Viper-M IDE controller, Linux doesn't support IDE-DMA + * and FreeBSD bug reports indicate that the system crashes when the + * feature is enabled (so it's disabled on that OS as well). In the + * NT PCI Bus Driver, it seems Microsoft too, completely disables + * Native IDE functionality on this controller, so it would seem OPTi + * simply frelled up this controller. + */ + if ((PciData->VendorID == 0x1045) && (PciData->DeviceID != 0xC621)) + { + /* Disable native mode */ + PciData->ProgIf &= ~5; + PciData->u.type0.InterruptPin = 0; + + /* + * Because the software is modifying the actual header data from + * the BIOS, this flag tells the driver to ignore failures when + * comparing the original BIOS data with the PCI data. + */ + PdoExtension->ExpectedWritebackFailure = TRUE; + } + else if ((PciData->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && + (PciData->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) + { + /* For other IDE controllers, start out in compatible mode */ + PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE; + + /* + * Registry must have enabled native mode (typically as a result + * of an INF file directive part of the IDE controller's driver) + * and the system must not be booted in Safe Mode. If that checks + * out, then evaluate the ACPI NATA method to see if the platform + * supports this. See the section "BIOS and Platform Prerequisites + * for Switching a Native-Mode-Capable Controller" in the Storage + * section of the Windows Driver Kit for more details: + * + * 5. For each ATA controller enumerated, the PCI bus driver checks + * the Programming Interface register of the IDE controller to + * see if it supports switching both channels to native mode. + * 6. The PCI bus driver checks whether the BIOS/platform supports + * switching the controller by checking the NATA method described + * earlier in this article. + * + * If an ATA controller does not indicate that it is native + * mode-capable, or if the BIOS NATA control method is missing + * or does not list that device, the PCI bus driver does not + * switch the controller and it is assigned legacy resources. + * + * If both the controller and the BIOS indicate that the controller + * can be switched, the process of switching the controller begins + * with the next step. + */ + if ((PciEnableNativeModeATA) && + !(InitSafeBootMode) && + (PciIsSlotPresentInParentMethod(PdoExtension, 'ATAN'))) + { + /* The platform supports it, remember that */ + PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE; + + /* + * Now switch the controller into native mode if both channels + * support native IDE mode. See "How Windows Switches an ATA + * Controller to Native Mode" in the Storage section of the + * Windows Driver Kit for more details. + */ + PdoExtension->SwitchedIDEToNativeMode = + PciConfigureIdeController(PdoExtension, PciData, 1); + } + + /* Is native mode enabled after all? */ + if ((PciData->ProgIf & 5) != 5) + { + /* Compatible mode, so force ISA-style IRQ14 and IRQ 15 */ + PciData->u.type0.InterruptPin = 0; + } + } + + /* Is this a PCI device with legacy VGA card decodes on the root bus? */ + if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE) && + (PCI_IS_ROOT_FDO(DeviceExtension)) && + !(DeviceExtension->BrokenVideoHackApplied)) + { + /* Tell the arbiter to apply a hack for these older devices */ + ario_ApplyBrokenVideoHack(DeviceExtension); + } + + /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */ + if ((PciData->VendorID == 0xE11) && + (PciData->DeviceID == 0xA0F7) && + (PciData->RevisionID == 17) && + (ExIsProcessorFeaturePresent(PF_PAE_ENABLED))) + { + /* Turn off the decodes immediately */ + PciData->Command &= ~(PCI_ENABLE_IO_SPACE | + PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_BUS_MASTER); + PciWriteDeviceConfig(PdoExtension, + &PciData->Command, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(USHORT)); + + /* Do not EVER turn them on again, this will blow up the system */ + PdoExtension->CommandEnables &= ~(PCI_ENABLE_IO_SPACE | + PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_BUS_MASTER); + PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND; + } + break; + + /* + * This is called whenever resources are changed and hardware needs to be + * updated. It is concerned with two highly specific erratas on an IBM + * hot-plug docking bridge used on the Thinkpad 600 Series and on Intel's + * ICH PCI Bridges. + */ + case PCI_HACK_FIXUP_BEFORE_UPDATE: + + /* There should always be a PDO extension passed in */ + ASSERT(PdoExtension); + + /* Is this an IBM 20H2999 PCI Docking Bridge, used on Thinkpads? */ + if ((PdoExtension->VendorId == 0x1014) && + (PdoExtension->DeviceId == 0x95)) + { + /* Read the current command */ + PciReadDeviceConfig(PdoExtension, + &Command, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(USHORT)); + + /* Turn off the decodes */ + PciDecodeEnable(PdoExtension, FALSE, &Command); + + /* Apply the required IBM workaround */ + PciReadDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR)); + RegValue &= ~2; + RegValue |= 1; + PciWriteDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR)); + + /* Restore the command to its original value */ + PciWriteDeviceConfig(PdoExtension, + &Command, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(USHORT)); + + } + + /* + * Check for Intel ICH PCI-to-PCI (i82801) bridges (used on the i810, + * i820, i840, i845 Chipsets) that have subtractive decode enabled, + * and whose hack flags do not specifiy that this support is broken. + */ + if ((PdoExtension->HeaderType == PCI_BRIDGE_TYPE) && + (PdoExtension->Dependent.type1.SubtractiveDecode) && + ((PdoExtension->VendorId == 0x8086) && + ((PdoExtension->DeviceId == 0x2418) || + (PdoExtension->DeviceId == 0x2428) || + (PdoExtension->DeviceId == 0x244E) || + (PdoExtension->DeviceId == 0x2448))) && + !(PdoExtension->HackFlags & PCI_HACK_BROKEN_SUBTRACTIVE_DECODE)) + { + /* + * The positive decode window shouldn't be used, these values are + * normally all read-only or initialized to 0 by the BIOS, but + * it appears Intel doesn't do this, so the PCI Bus Driver will + * do it in software instead. Note that this is used to prevent + * certain non-compliant PCI devices from breaking down due to the + * fact that these ICH bridges have a known "quirk" (which Intel + * documents as a known "erratum", although it's not not really + * an ICH bug since the PCI specification does allow for it) in + * that they will sometimes send non-zero addresses during special + * cycles (ie: non-zero data during the address phase). These + * broken PCI cards will mistakenly attempt to claim the special + * cycle and corrupt their I/O and RAM ranges. Again, in Intel's + * defense, the PCI specification only requires stable data, not + * necessarily zero data, during the address phase. + */ + PciData->u.type1.MemoryBase = 0xFFFF; + PciData->u.type1.PrefetchBase = 0xFFFF; + PciData->u.type1.IOBase = 0xFF; + PciData->u.type1.IOLimit = 0; + PciData->u.type1.MemoryLimit = 0; + PciData->u.type1.PrefetchLimit = 0; + PciData->u.type1.PrefetchBaseUpper32 = 0; + PciData->u.type1.PrefetchLimitUpper32 = 0; + PciData->u.type1.IOBaseUpper16 = 0; + PciData->u.type1.IOLimitUpper16 = 0; + } + break; + + default: + return; + } + + /* Finally, also check if this is this a CardBUS device? */ + if (PCI_CONFIGURATION_TYPE(PciData) == PCI_CARDBUS_BRIDGE_TYPE) + { + /* + * At offset 44h the LegacyBaseAddress is stored, which is cleared by + * ACPI-aware versions of Windows, to disable legacy-mode I/O access to + * CardBus controllers. For more information, see "Supporting CardBus + * Controllers under ACPI" in the "CardBus Controllers and Windows" + * Whitepaper on WHDC. + */ + LegacyBaseAddress = 0; + PciWriteDeviceConfig(PdoExtension, + &LegacyBaseAddress, + sizeof(PCI_COMMON_HEADER) + sizeof(ULONG), + sizeof(ULONG)); + } +} + +BOOLEAN +NTAPI +PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension, + IN PPCI_COMMON_HEADER PciData) +{ + BOOLEAN IdMatch, RevMatch, SubsysMatch; + ULONGLONG HackFlags = DeviceExtension->HackFlags; + + /* Check if the IDs match */ + IdMatch = (PciData->VendorID == DeviceExtension->VendorId) && + (PciData->DeviceID == DeviceExtension->DeviceId); + if (!IdMatch) return FALSE; + + /* If the device has a valid revision, check if it matches */ + RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) || + (PciData->RevisionID == DeviceExtension->RevisionId); + if (!RevMatch) return FALSE; + + /* For multifunction devices, this is enough to assume they're the same */ + if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE; + + /* For bridge devices, there's also nothing else that can be checked */ + if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE; + + /* Devices, on the other hand, have subsystem data that can be compared */ + SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM | + PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) || + ((DeviceExtension->SubsystemVendorId == + PciData->u.type0.SubVendorID) && + (DeviceExtension->SubsystemId == + PciData->u.type0.SubSystemID)); + return SubsysMatch; +} + +BOOLEAN +NTAPI +PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData, + IN PCI_SLOT_NUMBER Slot, + IN UCHAR OperationType, + IN ULONGLONG HackFlags) +{ + do + { + /* Check if this is device enumeration */ + if (OperationType == PCI_SKIP_DEVICE_ENUMERATION) + { + /* Check if there's a hackflag saying not to enumerate this device */ + if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break; + + /* Check if this is the high end of a double decker device */ + if ((HackFlags & PCI_HACK_DOUBLE_DECKER) && + (Slot.u.bits.DeviceNumber >= 16)) + { + /* It belongs to the same device, so skip it */ + DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n", + PciData->VendorID, + PciData->DeviceID, + Slot.u.bits.DeviceNumber, + Slot.u.bits.FunctionNumber); + break; + } + } + else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION) + { + /* Resource enumeration, check for a hackflag saying not to do it */ + if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break; + } + else + { + /* Logic error in the driver */ + ASSERTMSG(FALSE, "PCI Skip Function - Operation type unknown."); + } + + /* Check for legacy bridges during resource enumeration */ + if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) && + (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) && + (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)) + { + /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */ + break; + } + else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED) + { + /* Undefined base class (usually a PCI BIOS/ROM bug) */ + DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n", + PciData->VendorID, + PciData->DeviceID); + + /* + * The Alder has an Intel Extended Express System Support Controller + * which presents apparently spurious BARs. When the PCI resource + * code tries to reassign these BARs, the second IO-APIC gets + * disabled (with disastrous consequences). The first BAR is the + * actual IO-APIC, the remaining five bars seem to be spurious + * resources, so ignore this device completely. + */ + if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break; + } + + /* Other normal PCI cards and bridges are enumerated */ + if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE; + } while (FALSE); + + /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */ + DPRINT1(" Device skipped (not enumerated).\n"); + return TRUE; +} + +VOID +NTAPI +PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension, + IN PPCI_COMMON_HEADER PciData) +{ + ULONG HeaderType, CapPtr, TargetAgpCapabilityId; + DEVICE_POWER_STATE WakeLevel; + PCI_CAPABILITIES_HEADER AgpCapability; + PCI_PM_CAPABILITY PowerCapabilities; + PAGED_CODE(); + + /* Assume no known wake level */ + PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified; + + /* Make sure the device has capabilities */ + if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST)) + { + /* If it doesn't, there will be no power management */ + PdoExtension->CapabilitiesPtr = 0; + PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; + } + else + { + /* There's capabilities, need to figure out where to get the offset */ + HeaderType = PCI_CONFIGURATION_TYPE(PciData); + if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE) + { + /* Use the bridge's header */ + CapPtr = PciData->u.type2.CapabilitiesPtr; + } + else + { + /* Use the device header */ + ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE); + CapPtr = PciData->u.type0.CapabilitiesPtr; + } + + /* Make sure the pointer is spec-aligned and located, and save it */ + DPRINT1("Device has capabilities at: %lx\n", CapPtr); + ASSERT(((CapPtr & 0x3) == 0) && (CapPtr >= PCI_COMMON_HDR_LENGTH)); + PdoExtension->CapabilitiesPtr = CapPtr; + + /* Check for PCI-to-PCI Bridges and AGP bridges */ + if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && + ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) || + (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI))) + { + /* Query either the raw AGP capabilitity, or the Target AGP one */ + TargetAgpCapabilityId = (PdoExtension->SubClass == + PCI_SUBCLASS_BR_PCI_TO_PCI) ? + PCI_CAPABILITY_ID_AGP_TARGET : + PCI_CAPABILITY_ID_AGP; + if (PciReadDeviceCapability(PdoExtension, + PdoExtension->CapabilitiesPtr, + TargetAgpCapabilityId, + &AgpCapability, + sizeof(PCI_CAPABILITIES_HEADER))) + { + /* AGP target ID was found, store it */ + DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId); + PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId; + } + } + + /* Check for devices that are known not to have proper power management */ + if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) + { + /* Query if this device supports power management */ + if (!PciReadDeviceCapability(PdoExtension, + PdoExtension->CapabilitiesPtr, + PCI_CAPABILITY_ID_POWER_MANAGEMENT, + &PowerCapabilities.Header, + sizeof(PCI_PM_CAPABILITY))) + { + /* No power management, so act as if it had the hackflag set */ + DPRINT1("No PM caps, disabling PM\n"); + PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; + } + else + { + /* Otherwise, pick the highest wake level that is supported */ + WakeLevel = PowerDeviceUnspecified; + if (PowerCapabilities.PMC.Capabilities.Support.PMED0) + WakeLevel = PowerDeviceD0; + if (PowerCapabilities.PMC.Capabilities.Support.PMED1) + WakeLevel = PowerDeviceD1; + if (PowerCapabilities.PMC.Capabilities.Support.PMED2) + WakeLevel = PowerDeviceD2; + if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot) + WakeLevel = PowerDeviceD3; + if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold) + WakeLevel = PowerDeviceD3; + PdoExtension->PowerState.DeviceWakeLevel = WakeLevel; + + /* Convert the PCI power state to the NT power state */ + PdoExtension->PowerState.CurrentDeviceState = + PowerCapabilities.PMCSR.ControlStatus.PowerState + 1; + + /* Save all the power capabilities */ + PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities; + DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n", + WakeLevel, PdoExtension->PowerState.CurrentDeviceState); + } + } + } + + /* At the very end of all this, does this device not have power management? */ + if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS) + { + /* Then guess the current state based on whether the decodes are on */ + PdoExtension->PowerState.CurrentDeviceState = + PciData->Command & (PCI_ENABLE_IO_SPACE | + PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_BUS_MASTER) ? + PowerDeviceD0: PowerDeviceD3; + DPRINT1("PM is off, so assumed device is: %d based on enables\n", + PdoExtension->PowerState.CurrentDeviceState); + } +} + +VOID +NTAPI +PciWriteLimitsAndRestoreCurrent(IN PVOID Reserved, + IN PPCI_CONFIGURATOR_CONTEXT Context) +{ + PPCI_COMMON_HEADER PciData, Current; + PPCI_PDO_EXTENSION PdoExtension; + + /* Grab all parameters from the context */ + PdoExtension = Context->PdoExtension; + Current = Context->Current; + PciData = Context->PciData; + + /* Write the limit discovery header */ + PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); + + /* Now read what the device indicated the limits are */ + PciReadDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); + + /* Then write back the original configuration header */ + PciWriteDeviceConfig(PdoExtension, Current, 0, PCI_COMMON_HDR_LENGTH); + + /* Copy back the original command that was saved in the context */ + Current->Command = Context->Command; + if (Context->Command) + { + /* Program it back into the device */ + PciWriteDeviceConfig(PdoExtension, + &Context->Command, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(USHORT)); + } + + /* Copy back the original status that was saved as well */ + Current->Status = Context->Status; + + /* Call the configurator to restore any other data that might've changed */ + Context->Configurator->RestoreCurrent(Context); +} + +NTSTATUS +NTAPI +PcipGetFunctionLimits(IN PPCI_CONFIGURATOR_CONTEXT Context) +{ + PPCI_CONFIGURATOR Configurator; + PPCI_COMMON_HEADER PciData, Current; + PPCI_PDO_EXTENSION PdoExtension; + PCI_IPI_CONTEXT IpiContext; + PIO_RESOURCE_DESCRIPTOR IoDescriptor; + ULONG Offset; + PAGED_CODE(); + + /* Grab all parameters from the context */ + PdoExtension = Context->PdoExtension; + Current = Context->Current; + PciData = Context->PciData; + + /* Save the current PCI Command and Status word */ + Context->Status = Current->Status; + Context->Command = Current->Command; + + /* Now that they're saved, clear the status, and disable all decodes */ + Current->Status = 0; + Current->Command &= ~(PCI_ENABLE_IO_SPACE | + PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_BUS_MASTER); + + /* Make a copy of the current PCI configuration header (with decodes off) */ + RtlCopyMemory(PciData, Current, PCI_COMMON_HDR_LENGTH); + + /* Locate the correct resource configurator for this type of device */ + Configurator = &PciConfigurators[PdoExtension->HeaderType]; + Context->Configurator = Configurator; + + /* Initialize it, which will typically setup the BARs for limit discovery */ + Configurator->Initialize(Context); + + /* Check for critical devices and PCI Debugging devices */ + if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) || + (PdoExtension->OnDebugPath)) + { + /* Specifically check for a PCI Debugging device */ + if (PdoExtension->OnDebugPath) + { + /* Was it enabled for bus mastering? */ + if (Context->Command & PCI_ENABLE_BUS_MASTER) + { + /* This decode needs to be re-enabled so debugging can work */ + PciData->Command |= PCI_ENABLE_BUS_MASTER; + Current->Command |= PCI_ENABLE_BUS_MASTER; + } + + /* Disable the debugger while the discovery is happening */ + KdDisableDebugger(); + } + + /* For these devices, an IPI must be sent to force high-IRQL discovery */ + IpiContext.Barrier = 1; + IpiContext.RunCount = 1; + IpiContext.PdoExtension = PdoExtension; + IpiContext.Function = PciWriteLimitsAndRestoreCurrent; + IpiContext.Context = Context; + KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&IpiContext); + + /* Re-enable the debugger if this was a PCI Debugging Device */ + if (PdoExtension->OnDebugPath) KdEnableDebugger(); + } + else + { + /* Otherwise, it's safe to do this in-line at low IRQL */ + PciWriteLimitsAndRestoreCurrent(PdoExtension, Context); + } + + /* + * Check if it's valid to compare the headers to see if limit discovery mode + * has properly exited (the expected case is that the PCI header would now + * be equal to what it was before). In some cases, it is known that this will + * fail, because during PciApplyHacks (among other places), software hacks + * had to be applied to the header, which the hardware-side will not see, and + * thus the headers would appear "different". + */ + if (!PdoExtension->ExpectedWritebackFailure) + { + /* Read the current PCI header now, after discovery has completed */ + PciReadDeviceConfig(PdoExtension, PciData + 1, 0, PCI_COMMON_HDR_LENGTH); + + /* Check if the current header at entry, is equal to the header now */ + Offset = RtlCompareMemory(PciData + 1, Current, PCI_COMMON_HDR_LENGTH); + if (Offset != PCI_COMMON_HDR_LENGTH) + { + /* It's not, which means configuration somehow changed, dump this */ + DPRINT1("PCI - CFG space write verify failed at offset 0x%x\n", Offset); + PciDebugDumpCommonConfig(PciData + 1); + DPRINT1("----------\n"); + PciDebugDumpCommonConfig(Current); + } + } + + /* This PDO should not already have resources, since this is only done once */ + ASSERT(PdoExtension->Resources == NULL); + + /* Allocate the structure that will hold the discovered resources and limits */ + PdoExtension->Resources = ExAllocatePoolWithTag(NonPagedPool, + sizeof(PCI_FUNCTION_RESOURCES), + 'BicP'); + if (!PdoExtension->Resources) return STATUS_INSUFFICIENT_RESOURCES; + + /* Clear it out for now */ + RtlZeroMemory(PdoExtension->Resources, sizeof(PCI_FUNCTION_RESOURCES)); + + /* Now call the configurator, which will first store the limits... */ + Configurator->SaveLimits(Context); + + /* ...and then store the current resources being used */ + Configurator->SaveCurrentSettings(Context); + + /* Loop all the limit descriptors backwards */ + IoDescriptor = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1]; + while (TRUE) + { + /* Keep going until a non-null descriptor is found */ + IoDescriptor--; + if (IoDescriptor->Type != CmResourceTypeNull) break; + + /* This is a null descriptor, is it the last one? */ + if (IoDescriptor == &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1]) + { + /* This means the descriptor is NULL, which means discovery failed */ + DPRINT1("PCI Resources fail!\n"); + + /* No resources will be assigned for the device */ + ExFreePoolWithTag(PdoExtension->Resources, 0); + PdoExtension->Resources = NULL; + break; + } + } + + /* Return success here, even if the device has no assigned resources */ + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +PciGetFunctionLimits(IN PPCI_PDO_EXTENSION PdoExtension, + IN PPCI_COMMON_HEADER Current, + IN ULONGLONG HackFlags) +{ + NTSTATUS Status; + PPCI_COMMON_HEADER PciData; + PCI_CONFIGURATOR_CONTEXT Context; + PAGED_CODE(); + + /* Do the hackflags indicate this device should be skipped? */ + if (PciSkipThisFunction(Current, + PdoExtension->Slot, + PCI_SKIP_RESOURCE_ENUMERATION, + HackFlags)) + { + /* Do not process its resources */ + return STATUS_SUCCESS; + } + + /* Allocate a buffer to hold two PCI configuration headers */ + PciData = ExAllocatePoolWithTag(0, 2 * PCI_COMMON_HDR_LENGTH, 'BicP'); + if (!PciData) return STATUS_INSUFFICIENT_RESOURCES; + + /* Set up the context for the resource enumeration, and do it */ + Context.Current = Current; + Context.PciData = PciData; + Context.PdoExtension = PdoExtension; + Status = PcipGetFunctionLimits(&Context); + + /* Enumeration is completed, free the PCI headers and return the status */ + ExFreePoolWithTag(PciData, 0); + return Status; +} + +VOID +NTAPI +PciProcessBus(IN PPCI_FDO_EXTENSION DeviceExtension) +{ + PPCI_PDO_EXTENSION PdoExtension; + PDEVICE_OBJECT PhysicalDeviceObject; + PAGED_CODE(); + + /* Get the PDO Extension */ + PhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject; + PdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension; + + /* Cheeck if this is the root bus */ + if (!PCI_IS_ROOT_FDO(DeviceExtension)) + { + /* Not really handling this year */ + UNIMPLEMENTED; + while (TRUE); + + /* Check for PCI bridges with the ISA bit set, or required */ + if ((PdoExtension) && + (PciClassifyDeviceType(PdoExtension) == PciTypePciBridge) && + ((PdoExtension->Dependent.type1.IsaBitRequired) || + (PdoExtension->Dependent.type1.IsaBitSet))) + { + /* We'll need to do some legacy support */ + UNIMPLEMENTED; + while (TRUE); + } + } + else + { + /* Scan all of the root bus' children bridges */ + for (PdoExtension = DeviceExtension->ChildBridgePdoList; + PdoExtension; + PdoExtension = PdoExtension->NextBridge) + { + /* Find any that have the VGA decode bit on */ + if (PdoExtension->Dependent.type1.VgaBitSet) + { + /* Again, some more legacy support we'll have to do */ + UNIMPLEMENTED; + while (TRUE); + } + } + } + + /* Check for ACPI systems where the OS assigns bus numbers */ + if (PciAssignBusNumbers) + { + /* Not yet supported */ + UNIMPLEMENTED; + while (TRUE); + } +} + +NTSTATUS +NTAPI +PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) +{ + ULONG MaxDevice = PCI_MAX_DEVICES; + BOOLEAN ProcessFlag = FALSE; + ULONG i, j, k, Size; + USHORT CapOffset, TempOffset; + LONGLONG HackFlags; + PDEVICE_OBJECT DeviceObject; + UCHAR Buffer[PCI_COMMON_HDR_LENGTH]; + UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH]; + PPCI_COMMON_HEADER PciData = (PVOID)Buffer; + PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer; + PCI_SLOT_NUMBER PciSlot; + PCHAR Name; + NTSTATUS Status; + PPCI_PDO_EXTENSION PdoExtension, NewExtension; + PPCI_PDO_EXTENSION* BridgeExtension; + PWCHAR DescriptionText; + USHORT SubVendorId, SubSystemId; + PCI_CAPABILITIES_HEADER CapHeader, PcixCapHeader; + DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n", + DeviceExtension, DeviceExtension->BaseBus); + + /* Is this the root FDO? */ + if (!PCI_IS_ROOT_FDO(DeviceExtension)) + { + /* Other FDOs are not currently supported */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Loop every device on the bus */ + PciSlot.u.bits.Reserved = 0; + i = DeviceExtension->BaseBus; + for (j = 0; j < MaxDevice; j++) + { + /* Loop every function of each device */ + PciSlot.u.bits.DeviceNumber = j; + for (k = 0; k < PCI_MAX_FUNCTION; k++) + { + /* Build the final slot structure */ + PciSlot.u.bits.FunctionNumber = k; + + /* Read the vendor for this slot */ + PciReadSlotConfig(DeviceExtension, + PciSlot, + PciData, + 0, + sizeof(USHORT)); + + /* Skip invalid device */ + if (PciData->VendorID == PCI_INVALID_VENDORID) continue; + + /* Now read the whole header */ + PciReadSlotConfig(DeviceExtension, + PciSlot, + &PciData->DeviceID, + sizeof(USHORT), + PCI_COMMON_HDR_LENGTH - sizeof(USHORT)); + + /* Apply any hacks before even analyzing the configuration header */ + PciApplyHacks(DeviceExtension, + PciData, + PciSlot, + PCI_HACK_FIXUP_BEFORE_CONFIGURATION, + NULL); + + /* Dump device that was found */ + DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n", + PciSlot.u.AsULONG, + i, + j, + k); + + /* Dump the device's header */ + PciDebugDumpCommonConfig(PciData); + + /* Find description for this device for the debugger's sake */ + DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass, + PciData->SubClass); + DPRINT1("Device Description \"%S\".\n", + DescriptionText ? DescriptionText : L"(NULL)"); + if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0); + + /* Check if there is an ACPI Watchdog Table */ + if (WdTable) + { + /* Check if this PCI device is the ACPI Watchdog Device... */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check for non-simple devices */ + if ((PCI_MULTIFUNCTION_DEVICE(PciData)) || + (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV)) + { + /* No subsystem data defined for these kinds of bridges */ + SubVendorId = 0; + SubSystemId = 0; + } + else + { + /* Read the subsystem information from the PCI header */ + SubVendorId = PciData->u.type0.SubVendorID; + SubSystemId = PciData->u.type0.SubSystemID; + } + + /* Get any hack flags for this device */ + HackFlags = PciGetHackFlags(PciData->VendorID, + PciData->DeviceID, + SubVendorId, + SubSystemId, + PciData->RevisionID); + + /* Check if this device is considered critical by the OS */ + if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass)) + { + /* Check if normally the decodes would be disabled */ + if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES)) + { + /* Because this device is critical, don't disable them */ + DPRINT1("Not allowing PM Because device is critical\n"); + HackFlags |= PCI_HACK_CRITICAL_DEVICE; + } + } + + /* PCI bridges with a VGA card are also considered critical */ + if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) && + (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) && + (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) && + !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES)) + { + /* Do not disable their decodes either */ + DPRINT1("Not allowing PM because device is VGA\n"); + HackFlags |= PCI_HACK_CRITICAL_DEVICE; + } + + /* Check if the device should be skipped for whatever reason */ + if (PciSkipThisFunction(PciData, + PciSlot, + PCI_SKIP_DEVICE_ENUMERATION, + HackFlags)) + { + /* Skip this device */ + continue; + } + + /* Check if a PDO has already been created for this device */ + PdoExtension = PciFindPdoByFunction(DeviceExtension, + PciSlot.u.AsULONG, + PciData); + if (PdoExtension) + { + /* Rescan scenarios are not yet implemented */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Bus processing will need to happen */ + ProcessFlag = TRUE; + + /* Create the PDO for this device */ + Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject); + ASSERT(NT_SUCCESS(Status)); + NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension; + + /* Check for broken devices with wrong/no class codes */ + if (HackFlags & PCI_HACK_FAKE_CLASS_CODE) + { + /* Setup a default one */ + PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV; + PciData->SubClass = PCI_SUBCLASS_SYS_OTHER; + + /* Device will behave erratically when reading back data */ + NewExtension->ExpectedWritebackFailure = TRUE; + } + + /* Clone all the information from the header */ + NewExtension->VendorId = PciData->VendorID; + NewExtension->DeviceId = PciData->DeviceID; + NewExtension->RevisionId = PciData->RevisionID; + NewExtension->ProgIf = PciData->ProgIf; + NewExtension->SubClass = PciData->SubClass; + NewExtension->BaseClass = PciData->BaseClass; + NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData); + + /* Check for modern bridge types, which are managed by the driver */ + if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && + ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) || + (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS))) + { + /* Acquire this device's lock */ + KeEnterCriticalRegion(); + KeWaitForSingleObject(&DeviceExtension->ChildListLock, + Executive, + KernelMode, + FALSE, + NULL); + + /* Scan the bridge list until the first free entry */ + for (BridgeExtension = &DeviceExtension->ChildBridgePdoList; + *BridgeExtension; + BridgeExtension = &(*BridgeExtension)->NextBridge); + + /* Add this PDO as a bridge */ + *BridgeExtension = NewExtension; + ASSERT(NewExtension->NextBridge == NULL); + + /* Release this device's lock */ + KeSetEvent(&DeviceExtension->ChildListLock, + IO_NO_INCREMENT, + FALSE); + KeLeaveCriticalRegion(); + } + + /* Get the PCI BIOS configuration saved in the registry */ + Status = PciGetBiosConfig(NewExtension, BiosData); + if (NT_SUCCESS(Status)) + { + /* This path has not yet been fully tested by eVb */ + DPRINT1("Have BIOS configuration!\n"); + UNIMPLEMENTED; + + /* Check if the PCI BIOS configuration has changed */ + if (!PcipIsSameDevice(NewExtension, BiosData)) + { + /* This is considered failure, and new data will be saved */ + Status = STATUS_UNSUCCESSFUL; + } + else + { + /* Data is still correct, check for interrupt line change */ + if (BiosData->u.type0.InterruptLine != + PciData->u.type0.InterruptLine) + { + /* Update the current BIOS with the saved interrupt line */ + PciWriteDeviceConfig(NewExtension, + &BiosData->u.type0.InterruptLine, + FIELD_OFFSET(PCI_COMMON_HEADER, + u.type0.InterruptLine), + sizeof(UCHAR)); + } + + /* Save the BIOS interrupt line and the initial command */ + NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine; + NewExtension->InitialCommand = BiosData->Command; + } + } + + /* Check if no saved data was present or if it was a mismatch */ + if (!NT_SUCCESS(Status)) + { + /* Save the new data */ + Status = PciSaveBiosConfig(NewExtension, PciData); + ASSERT(NT_SUCCESS(Status)); + + /* Save the interrupt line and command from the device */ + NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine; + NewExtension->InitialCommand = PciData->Command; + } + + /* Save original command from the device and hack flags */ + NewExtension->CommandEnables = PciData->Command; + NewExtension->HackFlags = HackFlags; + + /* Get power, AGP, and other capability data */ + PciGetEnhancedCapabilities(NewExtension, PciData); + + /* Now configure the BARs */ + Status = PciGetFunctionLimits(NewExtension, PciData, HackFlags); + + /* Power up the device */ + PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE); + + /* Apply any device hacks required for enumeration */ + PciApplyHacks(DeviceExtension, + PciData, + PciSlot, + PCI_HACK_FIXUP_AFTER_CONFIGURATION, + NewExtension); + + /* Save interrupt pin */ + NewExtension->InterruptPin = PciData->u.type0.InterruptPin; + + /* + * Use either this device's actual IRQ line or, if it's connected on + * a master bus whose IRQ line is actually connected to the host, use + * the HAL to query the bus' IRQ line and store that as the adjusted + * interrupt line instead + */ + NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension); + + /* Check if this device is used for PCI debugger cards */ + NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension); + + /* Check for devices with invalid/bogus subsystem data */ + if (HackFlags & PCI_HACK_NO_SUBSYSTEM) + { + /* Set the subsystem information to zero instead */ + NewExtension->SubsystemVendorId = 0; + NewExtension->SubsystemId = 0; + } + + /* Scan all capabilities */ + CapOffset = NewExtension->CapabilitiesPtr; + while (CapOffset) + { + /* Read this header */ + TempOffset = PciReadDeviceCapability(NewExtension, + CapOffset, + 0, + &CapHeader, + sizeof(PCI_CAPABILITIES_HEADER)); + if (TempOffset != CapOffset) + { + /* This is a strange issue that shouldn't happen normally */ + DPRINT1("PCI - Failed to read PCI capability at offset 0x%02x\n", + CapOffset); + ASSERT(TempOffset == CapOffset); + } + + /* Check for capabilities that this driver cares about */ + switch (CapHeader.CapabilityID) + { + /* Power management capability is heavily used by the bus */ + case PCI_CAPABILITY_ID_POWER_MANAGEMENT: + + /* Dump the capability */ + Name = "POWER"; + Size = sizeof(PCI_PM_CAPABILITY); + break; + + /* AGP capability is required for AGP bus functionality */ + case PCI_CAPABILITY_ID_AGP: + + /* Dump the capability */ + Name = "AGP"; + Size = sizeof(PCI_AGP_CAPABILITY); + break; + + /* This driver doesn't really use anything other than that */ + default: + + /* Windows prints this, we could do a translation later */ + Name = "UNKNOWN CAPABILITY"; + Size = 0; + break; + } + + /* Check if this is a capability that should be dumped */ + if (Size) + { + /* Read the whole capability data */ + TempOffset = PciReadDeviceCapability(NewExtension, + CapOffset, + CapHeader.CapabilityID, + &CapHeader, + Size); + + if (TempOffset != CapOffset) + { + /* Again, a strange issue that shouldn't be seen */ + DPRINT1("- Failed to read capability data. ***\n"); + ASSERT(TempOffset == CapOffset); + } + } + + /* Dump this capability */ + DPRINT1("CAP @%02x ID %02x (%s)\n", + CapOffset, CapHeader.CapabilityID, Name); + for (i = 0; i < Size; i += 2) + DPRINT1(" %04x\n", *(PUSHORT)((ULONG_PTR)&CapHeader + i)); + DPRINT1("\n"); + + /* Check the next capability */ + CapOffset = CapHeader.Next; + } + + /* Check for IDE controllers */ + if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && + (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) + { + /* Do not allow them to power down completely */ + NewExtension->DisablePowerDown = TRUE; + } + + /* + * Check if this is a legacy bridge. Note that the i82375 PCI/EISA + * bridge that is present on certain NT Alpha machines appears as + * non-classified so detect it manually by scanning for its VID/PID. + */ + if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && + ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) || + (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) || + (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) || + ((NewExtension->VendorId == 0x8086) && + (NewExtension->DeviceId == 0x482))) + { + /* Do not allow these legacy bridges to be powered down */ + NewExtension->DisablePowerDown = TRUE; + } + + /* Check if the BIOS did not configure a cache line size */ + if (!PciData->CacheLineSize) + { + /* Check if the device is disabled */ + if (!(NewExtension->CommandEnables & (PCI_ENABLE_IO_SPACE | + PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_BUS_MASTER))) + { + /* Check if this is a PCI-X device*/ + TempOffset = PciReadDeviceCapability(NewExtension, + NewExtension->CapabilitiesPtr, + PCI_CAPABILITY_ID_PCIX, + &PcixCapHeader, + sizeof(PCI_CAPABILITIES_HEADER)); + + /* + * A device with default cache line size and latency timer + * settings is considered to be unconfigured. Note that on + * PCI-X, the reset value of the latency timer field in the + * header is 64, not 0, hence why the check for PCI-X caps + * was required, and the value used here below. + */ + if (!(PciData->LatencyTimer) || + ((TempOffset) && (PciData->LatencyTimer == 64))) + { + /* Keep track of the fact that it needs configuration */ + DPRINT1("PCI - ScanBus, PDOx %x found unconfigured\n", + NewExtension); + NewExtension->NeedsHotPlugConfiguration = TRUE; + } + } + } + + /* Save latency and cache size information */ + NewExtension->SavedLatencyTimer = PciData->LatencyTimer; + NewExtension->SavedCacheLineSize = PciData->CacheLineSize; + + /* The PDO is now ready to go */ + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + } + } + + /* Enumeration completed, do a final pass now that all devices are found */ + if (ProcessFlag) PciProcessBus(DeviceExtension); + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension, + IN OUT PDEVICE_RELATIONS *pDeviceRelations) +{ + NTSTATUS Status; + PPCI_PDO_EXTENSION PdoExtension; + ULONG PdoCount = 0; + PDEVICE_RELATIONS DeviceRelations, NewRelations; + SIZE_T Size; + PDEVICE_OBJECT DeviceObject, *ObjectArray; + PAGED_CODE(); + + /* Make sure the FDO is started */ + ASSERT(DeviceExtension->DeviceState == PciStarted); + + /* Synchronize while we enumerate the bus */ + Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation); + if (!NT_SUCCESS(Status)) return Status; + + /* Scan all children PDO */ + for (PdoExtension = DeviceExtension->ChildPdoList; + PdoExtension; + PdoExtension = PdoExtension->Next) + { + /* Invalidate them */ + PdoExtension->NotPresent = TRUE; + } + + /* Scan the PCI Bus */ + Status = PciScanBus(DeviceExtension); + ASSERT(NT_SUCCESS(Status)); + + /* Enumerate all children PDO again */ + for (PdoExtension = DeviceExtension->ChildPdoList; + PdoExtension; + PdoExtension = PdoExtension->Next) + { + /* Check for PDOs that are still invalidated */ + if (PdoExtension->NotPresent) + { + /* This means this PDO existed before, but not anymore */ + PdoExtension->ReportedMissing = TRUE; + DPRINT1("PCI - Old device (pdox) %08x not found on rescan.\n", + PdoExtension); + } + else + { + /* Increase count of detected PDOs */ + PdoCount++; + } + } + + /* Read the current relations and add the newly discovered relations */ + DeviceRelations = *pDeviceRelations; + Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + + PdoCount * sizeof(PDEVICE_OBJECT); + if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count; + + /* Allocate the device relations */ + NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP'); + if (!NewRelations) + { + /* Out of space, cancel the operation */ + PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Check if there were any older relations */ + NewRelations->Count = 0; + if (DeviceRelations) + { + /* Copy the old relations into the new buffer, then free the old one */ + RtlCopyMemory(NewRelations, + DeviceRelations, + FIELD_OFFSET(DEVICE_RELATIONS, Objects) + + DeviceRelations->Count * sizeof(PDEVICE_OBJECT)); + ExFreePoolWithTag(DeviceRelations, 0); + } + + /* Print out that we're ready to dump relations */ + DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n", + DeviceExtension, + DeviceExtension->BaseBus); + + /* Loop the current PDO children and the device relation object array */ + PdoExtension = DeviceExtension->ChildPdoList; + ObjectArray = &NewRelations->Objects[NewRelations->Count]; + while (PdoExtension) + { + /* Dump this relation */ + DPRINT1(" QDR PDO %08x (x %08x)%s\n", + PdoExtension->PhysicalDeviceObject, + PdoExtension, + PdoExtension->NotPresent ? + "" : ""); + + /* Is this PDO present? */ + if (!PdoExtension->NotPresent) + { + /* Reference it and add it to the array */ + DeviceObject = PdoExtension->PhysicalDeviceObject; + ObfReferenceObject(DeviceObject); + *ObjectArray++ = DeviceObject; + } + + /* Go to the next PDO */ + PdoExtension = PdoExtension->Next; + } + + /* Terminate dumping the relations */ + DPRINT1(" QDR Total PDO count = %d (%d already in list)\n", + NewRelations->Count + PdoCount, + NewRelations->Count); + + /* Return the final count and the new buffer */ + NewRelations->Count += PdoCount; + *pDeviceRelations = NewRelations; + return STATUS_SUCCESS; +} + /* EOF */