|
|
/*++
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
//
PCI_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))) {
PCI_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;
PAGED_CODE();
PCI_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..
//
PCI_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
//
PCI_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;
currentNumber = currentNumber + spread; } }
//
// Now we have programmed the bridges - we need to go back and update the
// subordinate bus numbers for all ancestor bridges.
//
PCI_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)
//
PCI_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.
//
PCI_ASSERT(limit >= base);
gap = limit - base;
if (gap > biggestGap) {
PCI_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.
//
PCI_ASSERT(lowest > Parent->BaseBus);
gap = lowest - (Parent->BaseBus + 1);
if (gap > biggestGap) {
PCI_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();
PCI_ASSERT(Primary < Secondary || (Primary == 0 && Secondary == 0)); PCI_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;
PCI_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...
//
PCI_ASSERT(PCI_IS_ROOT_FDO(current)); PCI_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();
PCI_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);
}
|