windows-nt/Source/XPSP1/NT/base/busdrv/pci/busno.c
2020-09-26 16:20:57 +08:00

1006 lines
24 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
busno.c
Abstract:
This module implements routines pertaining to PCI bus numbers.
Author:
Andy Thornton (andrewth) 9/5/98
Revision History:
--*/
#include "pcip.h"
VOID
PciSpreadBridges(
IN PPCI_FDO_EXTENSION Parent,
IN UCHAR BridgeCount
);
UCHAR
PciFindBridgeNumberLimit(
IN PPCI_FDO_EXTENSION BridgeParent,
IN UCHAR Base
);
VOID
PciFitBridge(
IN PPCI_FDO_EXTENSION ParentFdoExtension,
IN PPCI_PDO_EXTENSION BridgePdoExtension
);
VOID
PciSetBusNumbers(
IN PPCI_PDO_EXTENSION PdoExtension,
IN UCHAR Primary,
IN UCHAR Secondary,
IN UCHAR Subordinate
);
VOID
PciUpdateAncestorSubordinateBuses(
IN PPCI_FDO_EXTENSION FdoExtension,
IN UCHAR Subordinate
);
VOID
PciDisableBridge(
IN PPCI_PDO_EXTENSION Bridge
);
UCHAR
PciFindBridgeNumberLimitWorker(
IN PPCI_FDO_EXTENSION BridgeParent,
IN PPCI_FDO_EXTENSION CurrentParent,
IN UCHAR Base,
OUT PBOOLEAN RootConstrained
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PciConfigureBusNumbers)
#pragma alloc_text(PAGE, PciAreBusNumbersConfigured)
#pragma alloc_text(PAGE, PciSpreadBridges)
#pragma alloc_text(PAGE, PciFindBridgeNumberLimit)
#pragma alloc_text(PAGE, PciFitBridge)
#pragma alloc_text(PAGE, PciSetBusNumbers)
#pragma alloc_text(PAGE, PciUpdateAncestorSubordinateBuses)
#pragma alloc_text(PAGE, PciDisableBridge)
#pragma alloc_text(PAGE, PciFindBridgeNumberLimitWorker)
#endif
VOID
PciConfigureBusNumbers(
PPCI_FDO_EXTENSION Parent
)
/*++
Routine Description:
This routine is called after scanning a PCI bus (root or bridge) and
configures the bus numbers for any newly encountered bridges if possible.
Any unconfigurable bridges will be set to Primary = Secondary = Subordinate = 0
and their IO, Memory and BusMaster bits will be disabled. When PCI is later
asked to Add to them it will fail.
The Parent->Mutex lock should be held before calling this function
Arguments:
Parent - The bridge we have just enumerated.
Return Value:
Status.
--*/
{
PPCI_PDO_EXTENSION current, parentPdo = NULL;
UCHAR bridgeCount = 0, configuredBridgeCount = 0;
PAGED_CODE();
if (!PCI_IS_ROOT_FDO(Parent)) {
parentPdo = (PPCI_PDO_EXTENSION)Parent->PhysicalDeviceObject->DeviceExtension;
}
//
// Walk the list of child PDO's for this bus and count the number of
// bridges and configured bridges
//
ExAcquireFastMutex(&Parent->ChildListMutex);
for (current = Parent->ChildBridgePdoList;
current;
current = current->NextBridge) {
if (current->NotPresent) {
PciDebugPrint(PciDbgBusNumbers,
"Skipping not present bridge PDOX @ %p\n",
current
);
continue;
}
bridgeCount++;
//
// If we configured the parent then all the children are considered
// to be unconfigured. Root buses are always configured
//
if ((parentPdo &&
parentPdo->Dependent.type1.WeChangedBusNumbers &&
(current->DeviceState == PciNotStarted))
|| (!PciAreBusNumbersConfigured(current))) {
//
// Disable this bridge and we will fix it later
//
PciDisableBridge(current);
} else {
//
// The bios must have configured this bridge and it looks valid so
// leave it alone!
//
configuredBridgeCount++;
}
}
ExReleaseFastMutex(&Parent->ChildListMutex);
//
// Now there are four posibilities...
//
if (bridgeCount == 0) {
//
// There are no bridges so not a lot to do...
//
PciDebugPrint(PciDbgBusNumbers,
"PCI - No bridges found on bus 0x%x\n",
Parent->BaseBus
);
} else if (bridgeCount == configuredBridgeCount) {
//
// All the bridges are configured - still not a lot to do...
//
PciDebugPrint(PciDbgBusNumbers,
"PCI - 0x%x bridges found on bus 0x%x - all already configured\n",
bridgeCount,
Parent->BaseBus
);
} else if (configuredBridgeCount == 0) {
PciDebugPrint(PciDbgBusNumbers,
"PCI - 0x%x bridges found on bus 0x%x - all need configuration\n",
bridgeCount,
Parent->BaseBus
);
//
// All the bridges require configuration so we should use a spreading
// out algorithm
//
PciSpreadBridges(Parent, bridgeCount);
} else {
//
// Some of the bridges are configured and some are not - we should try
// to fit the unconfigured ones into the holes left by the configured
// ones
//
ASSERT(configuredBridgeCount < bridgeCount);
PciDebugPrint(PciDbgBusNumbers,
"PCI - 0x%x bridges found on bus 0x%x - 0x%x need configuration\n",
bridgeCount,
Parent->BaseBus,
bridgeCount - configuredBridgeCount
);
//
// Walk the list of PDO's again and configure each one seperatly
//
for (current = Parent->ChildBridgePdoList;
current;
current = current->NextBridge) {
if (current->NotPresent) {
PciDebugPrint(PciDbgBusNumbers,
"Skipping not present bridge PDOX @ %p\n",
current
);
continue;
}
//
// Fit the bridge if we disabled it.
//
if ((parentPdo &&
parentPdo->Dependent.type1.WeChangedBusNumbers &&
(current->DeviceState == PciNotStarted))
|| (!PciAreBusNumbersConfigured(current))) {
ASSERT(current->Dependent.type1.PrimaryBus == 0
&& current->Dependent.type1.SecondaryBus == 0
&& current->Dependent.type1.SubordinateBus == 0
);
PciFitBridge(Parent, current);
}
}
}
}
BOOLEAN
PciAreBusNumbersConfigured(
IN PPCI_PDO_EXTENSION Bridge
)
/*++
Routine Description:
This checks if the bus numbers assigned to the bridge are valid
Arguments:
Bridge - the bridge to check
Return Value:
TRUE if numbers are valid FALSE otherwise.
--*/
{
PAGED_CODE();
//
// Check this bridge is configured to run on the bus we found it.
//
if (Bridge->Dependent.type1.PrimaryBus != Bridge->ParentFdoExtension->BaseBus) {
return FALSE;
}
//
// Ensure the child bus number is greater than the parent bus.
// (HP Omnibooks actually break this rule when not plugged into
// their docking stations).
//
if (Bridge->Dependent.type1.SecondaryBus <= Bridge->Dependent.type1.PrimaryBus) {
return FALSE;
}
//
// And finally, make sure the secondary bus is in the range
// of busses the bridge is programmed for. Paranoia.
//
if (Bridge->Dependent.type1.SubordinateBus < Bridge->Dependent.type1.SecondaryBus) {
return FALSE;
}
return TRUE;
}
VOID
PciSpreadBridges(
IN PPCI_FDO_EXTENSION Parent,
IN UCHAR BridgeCount
)
/*++
Routine Description:
This routine attemps to spread out the available bus numbers between the
unconfigured bridges. It is only called if ALL the bridges on a particular
bus are not configured - eg we just hot docked!
If a particular brigde can not be configured it is disabled (Decodes OFF and
bus number 0->0-0) and the subsequent AddDevice will fail.
Arguments:
Parent - The FDO extension for the bridge we are enumerating.
BridgeCount - The number of bridges at this level
Return Value:
None
--*/
{
UCHAR base, limit, numberCount, currentNumber, spread, maxAssigned = 0;
PPCI_PDO_EXTENSION current;
BOOLEAN outOfNumbers = FALSE;
PAGED_CODE();
ASSERT(Parent->BaseBus < PCI_MAX_BRIDGE_NUMBER);
//
// Seeing as we only get here if all the bridges arn't configured the base
// is the lowest bus out parent passes
//
base = (UCHAR)Parent->BaseBus;
//
// The limit is constrained by the siblings of the parent bridge or in the
// case that there are none, by the siblings of the parent's parent and so on
// until we find a sibling or run out of buses in which case the constraint
// is the maximum bus number passed by this root.
//
limit = PciFindBridgeNumberLimit(Parent, base);
if (limit < base) {
//
// This normally means the BIOS or HAL messed up and got the subordinate
// bus number for the root bus wrong. There's not much we can do..
//
ASSERT(limit >= base);
return;
}
//
// Now see if we have enough numbers available to number all the busses
//
numberCount = limit - base;
if (numberCount == 0) {
//
// We don't have any bus numbers available - bail now
//
return;
} else if (BridgeCount >= numberCount) {
//
// We have just/not enough - don't spread things out!
//
spread = 1;
} else {
//
// Try and spread things out a bit so we can accomodate subordinate
// bridges of the one we are configuring. Also leave some space on the
// parent bus for any bridges that appear here (the + 1). As we have no idea
// what is behind each bridge treat them equally...
//
spread = numberCount / (BridgeCount + 1);
}
//
// Now assign the bus numbers - we have already disabled all the unconfigured
// bridges
//
currentNumber = base + 1;
for (current = Parent->ChildBridgePdoList;
current;
current = current->NextBridge) {
if (current->NotPresent) {
PciDebugPrint(PciDbgBusNumbers,
"Skipping not present bridge PDOX @ %p\n",
current
);
continue;
}
//
// Now go and write it out to the hardware
//
ASSERT(!PciAreBusNumbersConfigured(current));
//
// Primary is the bus we are on, secondary is our bus number.
// We don't know if there are any bridges there - we have left space
// just in case - therefore we don't pass any bus numbers. If we
// need to, the subordinate number can be updated later.
//
PciSetBusNumbers(current,
base,
currentNumber,
currentNumber
);
//
// Remember the max number we assigned
//
maxAssigned = currentNumber;
//
// Check if we have run out of numbers
//
if ((currentNumber + spread) < currentNumber // wrapped
|| (currentNumber + spread) > limit) {
break;
} else {
//
// Move onto the next number
//
currentNumber += spread;
}
}
//
// Now we have programmed the bridges - we need to go back and update the
// subordinate bus numbers for all ancestor bridges.
//
ASSERT(maxAssigned > 0);
PciUpdateAncestorSubordinateBuses(Parent, maxAssigned);
}
UCHAR
PciFindBridgeNumberLimitWorker(
IN PPCI_FDO_EXTENSION BridgeParent,
IN PPCI_FDO_EXTENSION CurrentParent,
IN UCHAR Base,
OUT PBOOLEAN RootConstrained
)
/*++
Routine Description:
This determines the subordinate bus number a bridge on the bus BridgeParent
with secondary number Base can have given the constraints of the configured
busses in the system.
Arguments:
BridgeParent - The bus on which the bridge resides
CurrentParent - The current bridge we are looking at (used for synchronization)
Base - The primary bus number of this bridge (ie the parent's secondary bus number)
Constraint - The number of the bus that constrains us
RootConstrained - Set to TRUE if we were constrained by a root appeture, FALSE
if constrained by another bridge
Return Value:
None
--*/
{
PPCI_PDO_EXTENSION current;
UCHAR currentNumber, closest = 0;
PAGED_CODE();
if (BridgeParent != CurrentParent) {
//
// We're going to mess with the child pdo list - lock the state...
//
ExAcquireFastMutex(&CurrentParent->ChildListMutex);
}
//
// Look for any bridge that will constrain us
//
for (current = CurrentParent->ChildBridgePdoList;
current;
current = current->NextBridge) {
if (current->NotPresent) {
PciDebugPrint(PciDbgBusNumbers,
"Skipping not present bridge PDOX @ %p\n",
current
);
continue;
}
//
// Unconfigured bridges can't constrain us
//
if (!PciAreBusNumbersConfigured(current)) {
continue;
}
currentNumber = current->Dependent.type1.SecondaryBus;
if (currentNumber > Base
&& (currentNumber < closest || closest == 0)) {
closest = currentNumber;
}
}
//
// If we haven't found a closest bridge then move up one level - yes this
// is recursive but is bounded by the depth of the pci tree is the best way
// of dealing with the hierarchial locking.
//
if (closest == 0) {
if (CurrentParent->ParentFdoExtension == NULL) {
//
// We have reached the root without finding a sibling
//
*RootConstrained = TRUE;
closest = CurrentParent->MaxSubordinateBus;
} else {
closest = PciFindBridgeNumberLimitWorker(BridgeParent,
CurrentParent->ParentFdoExtension,
Base,
RootConstrained
);
}
} else {
//
// We are constrained by a bridge so by definition not by a root.
//
*RootConstrained = FALSE;
}
if (BridgeParent != CurrentParent) {
ExReleaseFastMutex(&CurrentParent->ChildListMutex);
}
return closest;
}
UCHAR
PciFindBridgeNumberLimit(
IN PPCI_FDO_EXTENSION Bridge,
IN UCHAR Base
)
/*++
Routine Description:
This determines the subordinate bus number a bridge on the bus BridgeParent
with secondary number Base can have given the constraints of the configured
busses in the system.
Arguments:
BridgeParent - The bus on which the bridge resides
Base - The primary bus number of this bridge (ie the parent's secondary bus number)
Return Value:
The max subordinate value.
--*/
{
BOOLEAN rootConstrained;
UCHAR constraint;
PAGED_CODE();
constraint = PciFindBridgeNumberLimitWorker(Bridge,
Bridge,
Base,
&rootConstrained
);
if (rootConstrained) {
//
// We are constrained by the maximum bus number that this root bus passes
// - this is therefore the max subordinate bus.
//
return constraint;
} else {
//
// If we are not constrained by a root bus we must be constrained by a
// bridge and thus the max subordinate value we can assign to the bus is
// one less that the bridge that constrained us. (A bridge must have a
// bus number greater that 1 so we can't wrap)
//
ASSERT(constraint > 0);
return constraint - 1;
}
}
VOID
PciFitBridge(
IN PPCI_FDO_EXTENSION Parent,
IN PPCI_PDO_EXTENSION Bridge
)
/*++
Routine Description:
This routine attemps to find a range of bus numbers for Bridge given the
constraints of the already configured bridges.
If a particular brigde can not be configured it is disabled (Decodes OFF and
bus number 0->0-0) and the subsequent AddDevice will fail.
Arguments:
Parent - The FDO extension for the bridge we are enumerating.
Bridge - The brige we want to configure
Return Value:
None
--*/
{
PPCI_PDO_EXTENSION current;
UCHAR base, limit, gap, bestBase = 0, biggestGap = 0, lowest = 0xFF;
PAGED_CODE();
for (current = Parent->ChildBridgePdoList;
current;
current = current->NextBridge) {
if (current->NotPresent) {
PciDebugPrint(PciDbgBusNumbers,
"Skipping not present bridge PDOX @ %p\n",
current
);
continue;
}
//
// Only look at configured bridges - buses we disabled have
// bus numbers 0->0-0 which is helpfully invalid
//
if (PciAreBusNumbersConfigured(current)) {
//
// Get the base and limit for each bridge and calculate which bridge
// has the biggest gap.
//
base = (UCHAR) current->Dependent.type1.SubordinateBus;
limit = PciFindBridgeNumberLimit(Parent, base);
//
// This ASSERT might fail if a BIOS or HAL misreported the limits
// of a root bridge. For example, an ACPI BIOS might have a _CRS
// for the root bridge that specifies bus-numbers 0 to 0 (length 1)
// are passed down, even though the real range is 0 to 255.
//
ASSERT(limit >= base);
gap = limit - base;
if (gap > biggestGap) {
ASSERT(gap > 0);
biggestGap = gap;
bestBase = base + 1;
}
if (current->Dependent.type1.SecondaryBus < lowest) {
lowest = current->Dependent.type1.SecondaryBus;
}
}
}
//
// Now make sure the gap between the bus we are on and the first bridge
// is not the biggest - lowest must always be greater that the parents bus
// number or it is miss configured and would have failed the
// BusNumbersConfigured test above.
//
ASSERT(lowest > Parent->BaseBus);
gap = lowest - (Parent->BaseBus + 1);
if (gap > biggestGap) {
ASSERT(gap > 0);
biggestGap = gap;
bestBase = Parent->BaseBus + 1;
}
//
// Did we find anywhere to put the bridge?
//
if (biggestGap >= 1) {
//
// Ok - we have some space to play with so we can configure out bridge
// right in the middle of the gap, if the bestGap is 1 (ie the bridge
// just fits) then this still works.
//
base = bestBase + (biggestGap / 2);
//
// Set subordinate equal to secondary as we are just leaving room for
// any bridges.
//
PciSetBusNumbers(Bridge, Parent->BaseBus, base, base);
//
// Update the ancestor subordinates if we configured the bridge
//
PciUpdateAncestorSubordinateBuses(Parent,
Bridge->Dependent.type1.SecondaryBus
);
}
}
VOID
PciSetBusNumbers(
IN PPCI_PDO_EXTENSION PdoExtension,
IN UCHAR Primary,
IN UCHAR Secondary,
IN UCHAR Subordinate
)
/*++
Routine Description:
This routine sets the bus numbers for a bridge and tracks if we have changed
bus numbers.
Arguments:
PdoExtension - The PDO for the bridge
Primary - The primary bus number to assign
Secondary - The secondary bus number to assign
Subordinate - The subordinate bus number to assign
Return Value:
None
--*/
{
PCI_COMMON_HEADER commonHeader;
PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader;
PAGED_CODE();
ASSERT(Primary < Secondary || (Primary == 0 && Secondary == 0));
ASSERT(Secondary <= Subordinate);
//
// Fill in in the config. Note that the Primary/Secondary/Subordinate bus
// numbers are in the same place for type1 and type2 headers.
//
commonConfig->u.type1.PrimaryBus = Primary;
commonConfig->u.type1.SecondaryBus = Secondary;
commonConfig->u.type1.SubordinateBus = Subordinate;
//
// Grab the PCI Bus lock - this will let hwverifier reliably check the
// config space against our extension.
//
ExAcquireFastMutex(&PciBusLock);
//
// Remember in the PDO
//
PdoExtension->Dependent.type1.PrimaryBus = Primary;
PdoExtension->Dependent.type1.SecondaryBus = Secondary;
PdoExtension->Dependent.type1.SubordinateBus = Subordinate;
PdoExtension->Dependent.type1.WeChangedBusNumbers = TRUE;
PciWriteDeviceConfig(
PdoExtension,
&commonConfig->u.type1.PrimaryBus,
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type1.PrimaryBus),
sizeof(Primary) + sizeof(Secondary) + sizeof(Subordinate)
);
ExReleaseFastMutex(&PciBusLock);
}
VOID
PciUpdateAncestorSubordinateBuses(
IN PPCI_FDO_EXTENSION FdoExtension,
IN UCHAR Subordinate
)
/*++
Routine Description:
This routine walks the bridge hierarchy updating the subordinate bus numbers
of each ancestor to ensure that numbers up to Subordinate are passed.
Arguments:
FdoExtension - The Fdo for the parent of the bridge(s) we have just configured
Subordinate - The maximum (subordinate) bus number to pass
Return Value:
None
--*/
{
PPCI_FDO_EXTENSION current;
PPCI_PDO_EXTENSION currentPdo;
PAGED_CODE();
//
// For all ancestors except the root update the subordinate bus number
//
for (current = FdoExtension;
current->ParentFdoExtension; // Root has no parent
current = current->ParentFdoExtension) {
currentPdo = (PPCI_PDO_EXTENSION)current->PhysicalDeviceObject->DeviceExtension;
ASSERT(!currentPdo->NotPresent);
if (currentPdo->Dependent.type1.SubordinateBus < Subordinate) {
currentPdo->Dependent.type1.SubordinateBus = Subordinate;
PciWriteDeviceConfig(currentPdo,
&Subordinate,
FIELD_OFFSET(PCI_COMMON_CONFIG,
u.type1.SubordinateBus),
sizeof(Subordinate)
);
}
}
//
// Ok so now we're at the root - can't be too careful on a checked build
// so lets make sure the subordinate value we came up with actually gets
// down this root...
//
ASSERT(PCI_IS_ROOT_FDO(current));
ASSERT(Subordinate <= current->MaxSubordinateBus);
}
VOID
PciDisableBridge(
IN PPCI_PDO_EXTENSION Bridge
)
/*++
Routine Description:
This routine disables a bridge by turing of its decodes and zeroing its
bus numbers.
Arguments:
PdoExtension - The PDO for the bridge
Return Value:
node
--*/
{
PAGED_CODE();
ASSERT(Bridge->DeviceState == PciNotStarted);
//
// Zero all the bus numbers so we shouldn't pass any config cycles
//
PciSetBusNumbers(Bridge, 0, 0, 0);
// NTRAID #62594 - 04/03/2000 - andrewth
// Close the windows in case this is the VGA bridge which we must
// leave decoding...
//
// Turn off the decodes so we don't pass IO or Memory cycles and bus
// master so we don't generate any
//
PciDecodeEnable(Bridge, FALSE, NULL);
}