5121 lines
146 KiB
C
5121 lines
146 KiB
C
/*++
|
||
|
||
Copyright (c) 1996-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
enum.c
|
||
|
||
Abstract:
|
||
|
||
This module contains functions associated with enumerating the
|
||
PCI buses.
|
||
|
||
Author:
|
||
|
||
Peter Johnston (peterj) 20-Nov-1996
|
||
|
||
Revision History:
|
||
|
||
Elliot Shmukler (t-ellios) 7-15-1998 Added support for MSI-capable devices.
|
||
|
||
--*/
|
||
|
||
#include "pcip.h"
|
||
|
||
NTSTATUS
|
||
PciScanBus(
|
||
IN PPCI_FDO_EXTENSION FdoExtension
|
||
);
|
||
|
||
VOID
|
||
PciFreeIoRequirementsList(
|
||
IN PIO_RESOURCE_REQUIREMENTS_LIST List
|
||
);
|
||
|
||
PCM_RESOURCE_LIST
|
||
PciAllocateCmResourceList(
|
||
IN ULONG ResourceCount,
|
||
IN ULONG BusNumber
|
||
);
|
||
|
||
|
||
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
|
||
},
|
||
};
|
||
|
||
//
|
||
// When dealing with devices whose configuration is totally
|
||
// unknown to us, we may want to emit the device but not its
|
||
// resources,... or, we may not want to see the device at all.
|
||
//
|
||
|
||
typedef enum {
|
||
EnumHackConfigSpace,
|
||
EnumBusScan,
|
||
EnumResourceDetermination,
|
||
EnumStartDevice
|
||
} ENUM_OPERATION_TYPE;
|
||
|
||
PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements;
|
||
|
||
extern PULONG InitSafeBootMode;
|
||
|
||
//
|
||
// Prototypes for functions contained and only used in this module.
|
||
//
|
||
|
||
|
||
NTSTATUS
|
||
PciGetFunctionLimits(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG CurrentConfig,
|
||
IN ULONGLONG DeviceFlags
|
||
);
|
||
|
||
NTSTATUS
|
||
PcipGetFunctionLimits(
|
||
IN PPCI_CONFIGURABLE_OBJECT This
|
||
);
|
||
|
||
BOOLEAN
|
||
PciSkipThisFunction(
|
||
IN PPCI_COMMON_CONFIG Config,
|
||
IN PCI_SLOT_NUMBER Slot,
|
||
IN ENUM_OPERATION_TYPE Operation,
|
||
IN ULONGLONG DeviceFlags
|
||
);
|
||
|
||
VOID
|
||
PciPrivateResourceInitialize(
|
||
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
|
||
IN PCI_PRIVATE_RESOURCE_TYPES PrivateResourceType,
|
||
IN ULONG Data
|
||
);
|
||
|
||
VOID
|
||
PciGetInUseRanges(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG CurrentConfig,
|
||
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR InUse
|
||
);
|
||
|
||
VOID
|
||
PciWriteLimitsAndRestoreCurrent(
|
||
IN PPCI_CONFIGURABLE_OBJECT This
|
||
);
|
||
|
||
VOID
|
||
PciGetEnhancedCapabilities(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG Config
|
||
);
|
||
|
||
BOOLEAN
|
||
PcipIsSameDevice(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG CommonConfig
|
||
);
|
||
|
||
BOOLEAN
|
||
PciConfigureIdeController(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN OUT PPCI_COMMON_CONFIG Config,
|
||
IN BOOLEAN TurnOffAllNative
|
||
);
|
||
VOID
|
||
PciBuildGraduatedWindow(
|
||
IN PIO_RESOURCE_DESCRIPTOR PrototypeDescriptor,
|
||
IN ULONG WindowMax,
|
||
IN ULONG WindowCount,
|
||
OUT PIO_RESOURCE_DESCRIPTOR OutputDescriptor
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(PAGE, PciAllocateCmResourceList)
|
||
#pragma alloc_text(PAGE, PciComputeNewCurrentSettings)
|
||
#pragma alloc_text(PAGE, PciFreeIoRequirementsList)
|
||
#pragma alloc_text(PAGE, PciGetInUseRanges)
|
||
#pragma alloc_text(PAGE, PciQueryDeviceRelations)
|
||
#pragma alloc_text(PAGE, PciQueryTargetDeviceRelations)
|
||
#pragma alloc_text(PAGE, PciQueryRequirements)
|
||
#pragma alloc_text(PAGE, PciQueryResources)
|
||
#pragma alloc_text(PAGE, PciScanBus)
|
||
#pragma alloc_text(PAGE, PciBuildRequirementsList)
|
||
#pragma alloc_text(PAGE, PciGetFunctionLimits)
|
||
#pragma alloc_text(PAGE, PcipGetFunctionLimits)
|
||
#pragma alloc_text(PAGE, PciPrivateResourceInitialize)
|
||
#pragma alloc_text(PAGE, PciWriteLimitsAndRestoreCurrent)
|
||
#pragma alloc_text(PAGE, PciGetEnhancedCapabilities)
|
||
#pragma alloc_text(PAGE, PciBuildGraduatedWindow)
|
||
|
||
#endif
|
||
|
||
BOOLEAN
|
||
PciSkipThisFunction(
|
||
IN PPCI_COMMON_CONFIG Config,
|
||
IN PCI_SLOT_NUMBER Slot,
|
||
IN ENUM_OPERATION_TYPE Operation,
|
||
IN ULONGLONG RegistryFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Check for known defective parts and return TRUE if this driver
|
||
should do no further processing on this function.
|
||
|
||
Arguments:
|
||
|
||
Config Pointer to a copy of the common configuration header as
|
||
read from the function's configuration space.
|
||
|
||
Return Value:
|
||
|
||
TRUE is this function is not known to cause problems, FALSE
|
||
if the function should be skipped altogether.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONGLONG flags = RegistryFlags;
|
||
|
||
#define SKIP_IF_FLAG(f, skip) if (flags & (f)) goto skip
|
||
#define FLAG_SET(f) (flags & (f))
|
||
|
||
switch (Operation) {
|
||
case EnumBusScan:
|
||
|
||
//
|
||
// Test the flags we care about during a bus scan.
|
||
// Eg: the ones that say "pretend we never saw this device".
|
||
//
|
||
|
||
SKIP_IF_FLAG(PCI_HACK_NO_ENUM_AT_ALL, skipFunction);
|
||
|
||
if (FLAG_SET(PCI_HACK_DOUBLE_DECKER) &&
|
||
(Slot.u.bits.DeviceNumber >= 16)) {
|
||
|
||
//
|
||
// This device seems to look at only the lower 4 bits of
|
||
// its DEVSEL lines, ie it is mirrored in the upper half
|
||
// if the buss's device domain.
|
||
//
|
||
|
||
PciDebugPrint(
|
||
PciDbgInformative,
|
||
" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
|
||
Config->VendorID,
|
||
Config->DeviceID,
|
||
Slot.u.bits.DeviceNumber,
|
||
Slot.u.bits.FunctionNumber
|
||
);
|
||
goto skipFunction;
|
||
}
|
||
|
||
break;
|
||
|
||
case EnumResourceDetermination:
|
||
|
||
//
|
||
// Limit the flags to those applicable to resource determination.
|
||
//
|
||
|
||
SKIP_IF_FLAG(PCI_HACK_ENUM_NO_RESOURCE, skipFunction);
|
||
break;
|
||
|
||
default:
|
||
ASSERTMSG("PCI Skip Function - Operation type unknown.", 0);
|
||
|
||
//
|
||
// Don't know how to apply flags here.
|
||
//
|
||
}
|
||
|
||
switch (Config->BaseClass) {
|
||
case PCI_CLASS_NOT_DEFINED:
|
||
|
||
//
|
||
// Currently we get this from VendorID = 8086, DeviceID = 0008,
|
||
// which reports a bunch of bogus resources.
|
||
//
|
||
// We have no idea what it really is either.
|
||
//
|
||
|
||
PciDebugPrint(
|
||
PciDbgInformative,
|
||
" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
|
||
Config->VendorID,
|
||
Config->DeviceID
|
||
);
|
||
|
||
// This case should be added to the registry.
|
||
|
||
if ((Config->VendorID == 0x8086) &&
|
||
(Config->DeviceID == 0x0008)) {
|
||
goto skipFunction;
|
||
}
|
||
break;
|
||
|
||
case PCI_CLASS_BRIDGE_DEV:
|
||
|
||
switch (Config->SubClass) {
|
||
case PCI_SUBCLASS_BR_HOST:
|
||
|
||
//
|
||
// It's a host bridge, emit the PDO in case there is
|
||
// a (miniport) driver for it, but under no circumstances
|
||
// should we attempt to figure out what resources it
|
||
// consumes (we don't know the format of its configuration
|
||
// space).
|
||
//
|
||
|
||
case PCI_SUBCLASS_BR_ISA:
|
||
case PCI_SUBCLASS_BR_EISA:
|
||
case PCI_SUBCLASS_BR_MCA:
|
||
|
||
//
|
||
// Microchannel bridges report their resource usage
|
||
// like good citizens. Unfortunately we really want
|
||
// them to behave like ISA bridges and consume no
|
||
// resources themselves. Their children are subtractive
|
||
// from the parent bus. Enumerate the device but not
|
||
// its resources.
|
||
//
|
||
|
||
if (Operation == EnumResourceDetermination) {
|
||
goto skipFunction;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Verify we understand the header type.
|
||
//
|
||
|
||
if (PciGetConfigurationType(Config) > PCI_MAX_CONFIG_TYPE) {
|
||
goto skipFunction;
|
||
}
|
||
|
||
//
|
||
// Nothing interesting,
|
||
//
|
||
|
||
return FALSE;
|
||
|
||
skipFunction:
|
||
|
||
PciDebugPrint(PciDbgPrattling, " Device skipped (not enumerated).\n");
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
PciApplyHacks(
|
||
IN PPCI_FDO_EXTENSION FdoExtension,
|
||
IN PPCI_COMMON_CONFIG Config,
|
||
IN PCI_SLOT_NUMBER Slot,
|
||
IN ENUM_OPERATION_TYPE Operation,
|
||
IN PPCI_PDO_EXTENSION PdoExtension OPTIONAL
|
||
)
|
||
{
|
||
|
||
switch (Operation) {
|
||
case EnumHackConfigSpace:
|
||
|
||
ASSERT(PdoExtension == NULL);
|
||
|
||
//
|
||
// Some devices (e.g. pre-2.0 devices) do not report reasonable class
|
||
// codes. Update the class code for a given set of devices so that we
|
||
// don't have to special-case these devices throughout the driver.
|
||
//
|
||
|
||
switch (Config->VendorID) {
|
||
|
||
//
|
||
// Intel
|
||
//
|
||
|
||
case 0x8086:
|
||
|
||
switch (Config->DeviceID) {
|
||
|
||
//
|
||
// PCEB - PCI/EISA Bridge (pre 2.0)
|
||
//
|
||
|
||
case 0x0482:
|
||
Config->BaseClass = PCI_CLASS_BRIDGE_DEV;
|
||
Config->SubClass = PCI_SUBCLASS_BR_EISA;
|
||
#if DBG
|
||
if (PdoExtension != NULL) {
|
||
PdoExtension->ExpectedWritebackFailure = TRUE;
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
//
|
||
// SIO - PCI/ISA Bridge (pre 2.0)
|
||
//
|
||
|
||
case 0x0484:
|
||
Config->BaseClass = PCI_CLASS_BRIDGE_DEV;
|
||
Config->SubClass = PCI_SUBCLASS_BR_ISA;
|
||
#if DBG
|
||
if (PdoExtension != NULL) {
|
||
PdoExtension->ExpectedWritebackFailure = TRUE;
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
|
||
case EnumBusScan:
|
||
|
||
ASSERT(PdoExtension);
|
||
|
||
if ((Config->VendorID == 0x1045) &&
|
||
(Config->DeviceID == 0xc621)) {
|
||
|
||
//
|
||
// Bug 131482. Force this device into legacy mode for
|
||
// the purposes of detection anyway.
|
||
//
|
||
|
||
Config->ProgIf &= ~(PCI_IDE_PRIMARY_NATIVE_MODE
|
||
| PCI_IDE_SECONDARY_NATIVE_MODE);
|
||
|
||
#if DBG
|
||
//
|
||
// This field is not actually writeable so don't tell
|
||
// people the writeback failed (we don't care).
|
||
//
|
||
|
||
PdoExtension->ExpectedWritebackFailure = TRUE;
|
||
#endif
|
||
|
||
} else if (Config->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR
|
||
&& Config->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR) {
|
||
|
||
//
|
||
// Check with the BIOS to ensure that it can deal with the mode change
|
||
// This is indicated by the *parent* of the device having a method
|
||
// called NATA which returns a package of integers which are slots
|
||
// in _ADR format that can be switched to native mode.
|
||
//
|
||
// Enabling native mode might expose BIOS bugs like interrupt routing problems
|
||
// that could prevent the machine from booting, so don't do this for
|
||
// safe mode, so that the user has some way to boot.
|
||
//
|
||
// For XP SP1, also check for a system wide hack flag that will not be set
|
||
// by a service pack installed through update.exe. This way we
|
||
// know we will only be enabling this feature on slipstreamed builds.
|
||
//
|
||
if ((PciEnableNativeModeATA != 0) &&
|
||
(*InitSafeBootMode == 0) &&
|
||
PciIsSlotPresentInParentMethod(PdoExtension, (ULONG)'ATAN')) {
|
||
|
||
PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE;
|
||
|
||
} else {
|
||
|
||
PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE;
|
||
}
|
||
|
||
//
|
||
// Config is updated to reflect the results of the switch
|
||
// if any. Relied upon below.
|
||
//
|
||
|
||
PdoExtension->SwitchedIDEToNativeMode =
|
||
PciConfigureIdeController(PdoExtension, Config, TRUE);
|
||
}
|
||
|
||
//
|
||
// If the controller is (still) in legacy mode then it
|
||
// consume 2 ISA interrupts whatever its interrupt pin says
|
||
// force the driver to figure out interrupts on its own.
|
||
//
|
||
// Examine Base Class, Sub Class and Programming Interface,
|
||
// if legacy mode, pretend PIN == 0.
|
||
//
|
||
|
||
if (PCI_IS_LEGACY_IDE_CONTROLLER(Config)) {
|
||
|
||
//
|
||
// Legacy mode. Pretend there is no PCI interrupt.
|
||
//
|
||
|
||
Config->u.type0.InterruptPin = 0;
|
||
}
|
||
|
||
//
|
||
// This hack doesn't change the config space for this device but enables
|
||
// a hack in the PCI arbiters to reserve a large number IO ranges for
|
||
// broken S3 and ATI cards. These legacy cards don't function behind a
|
||
// bridge so we only perform the check on a root bus and only perform it
|
||
// once
|
||
//
|
||
|
||
if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE)
|
||
&& PCI_IS_ROOT_FDO(FdoExtension)
|
||
&& !FdoExtension->BrokenVideoHackApplied) {
|
||
|
||
ario_ApplyBrokenVideoHack(FdoExtension);
|
||
|
||
}
|
||
|
||
//
|
||
// Check if this is the broken Compaq hot-plug controller that
|
||
// is integrated into the Profusion chipset. It only does a 32bit
|
||
// decode in a 64bit address space... Does this seem familiar to anyone...
|
||
// can you say ISA aliasing!
|
||
//
|
||
// The solution is to disable the memory decode. But so that the user
|
||
// gets to keep the hot-plug functionality we need to still enumerate
|
||
// but prune out the memory requirement and rely on the fact that the
|
||
// registers can be accessed through config space. This is done later
|
||
// in PciGetRequirements.
|
||
//
|
||
// Only do this on machines with PAE enabled as they can have > 4GB.
|
||
// Note that this will only work on x86 machines but this is an x86 only
|
||
// chipset. Only revision 0x11 was broken.
|
||
//
|
||
|
||
if (Config->VendorID == 0x0e11
|
||
&& Config->DeviceID == 0xa0f7
|
||
&& Config->RevisionID == 0x11
|
||
&& ExIsProcessorFeaturePresent(PF_PAE_ENABLED)) {
|
||
|
||
Config->Command &= ~(PCI_ENABLE_MEMORY_SPACE
|
||
| PCI_ENABLE_BUS_MASTER
|
||
| PCI_ENABLE_IO_SPACE);
|
||
|
||
PciSetCommandRegister(PdoExtension, Config->Command);
|
||
PdoExtension->CommandEnables &= ~(PCI_ENABLE_MEMORY_SPACE
|
||
| PCI_ENABLE_BUS_MASTER
|
||
| PCI_ENABLE_IO_SPACE);
|
||
PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND;
|
||
}
|
||
|
||
//
|
||
// If this is a cardbus controller force it into Cardbus mode by writing 0
|
||
// to the LegacyModeBaseAddressRegister.
|
||
//
|
||
|
||
if (PCI_CONFIGURATION_TYPE(Config) == PCI_CARDBUS_BRIDGE_TYPE) {
|
||
|
||
ULONG zeroLMBA = 0;
|
||
|
||
PciWriteDeviceConfig(PdoExtension,
|
||
&zeroLMBA,
|
||
CARDBUS_LMBA_OFFSET,
|
||
sizeof(zeroLMBA)
|
||
);
|
||
}
|
||
|
||
break;
|
||
|
||
case EnumStartDevice:
|
||
|
||
ASSERT(PdoExtension);
|
||
|
||
//
|
||
// IBM built a bridge (Kirin) that does both positive and subtractive decode
|
||
// - we don't do that so set it to totally subtractive mode (info is from
|
||
// NT bug 267076)
|
||
//
|
||
// NB - this relies on the fact that Kirin has a ProgIf of 1.
|
||
//
|
||
|
||
if (PdoExtension->VendorId == 0x1014 && PdoExtension->DeviceId == 0x0095) {
|
||
|
||
UCHAR regE0;
|
||
USHORT cmd;
|
||
|
||
//
|
||
// Turn off the hardware as we are going to mess with it
|
||
//
|
||
|
||
PciGetCommandRegister(PdoExtension, &cmd);
|
||
PciDecodeEnable(PdoExtension, FALSE, &cmd);
|
||
|
||
//
|
||
// This is a Kirin
|
||
//
|
||
// Offset E0h - bit 0 : Subtractive Decode enable/disable
|
||
// = 1 .. Enable
|
||
// = 0 .. Disable
|
||
// bit 1 : Subtractive Decode Timing
|
||
// = 0 : Subtractive Timing
|
||
// = 1 : Slow Timing
|
||
//
|
||
|
||
PciReadDeviceConfig(PdoExtension, ®E0, 0xE0, 1);
|
||
|
||
//
|
||
// Set subtractive with subtractive timing.
|
||
//
|
||
|
||
regE0 |= 0x1;
|
||
regE0 &= ~0x2;
|
||
|
||
PciWriteDeviceConfig(PdoExtension, ®E0, 0xE0, 1);
|
||
|
||
//
|
||
// Put the command register back as we found it
|
||
//
|
||
|
||
PciSetCommandRegister(PdoExtension, cmd);
|
||
|
||
}
|
||
|
||
//
|
||
// Subtractive decode bridges are not meant to have writeable window
|
||
// register - some do so if this is a subtractive bridge then close
|
||
// these windows by setting base > limit.
|
||
//
|
||
|
||
if (PdoExtension->HeaderType == PCI_BRIDGE_TYPE
|
||
&& PdoExtension->Dependent.type1.SubtractiveDecode
|
||
&& !PCI_IS_INTEL_ICH(PdoExtension)) {
|
||
|
||
//
|
||
// Now close all the windows on this bridge - if the registers are read only
|
||
// this is a NOP.
|
||
//
|
||
|
||
Config->u.type1.IOBase = 0xFF;
|
||
Config->u.type1.IOLimit = 0;
|
||
Config->u.type1.MemoryBase = 0xFFFF;
|
||
Config->u.type1.MemoryLimit = 0;
|
||
Config->u.type1.PrefetchBase = 0xFFFF;
|
||
Config->u.type1.PrefetchLimit = 0;
|
||
Config->u.type1.PrefetchBaseUpper32 = 0;
|
||
Config->u.type1.PrefetchLimitUpper32 = 0;
|
||
Config->u.type1.IOBaseUpper16 = 0;
|
||
Config->u.type1.IOLimitUpper16 = 0;
|
||
|
||
}
|
||
|
||
//
|
||
// If this is a cardbus controller force it into Cardbus mode by writing 0
|
||
// to the LegacyModeBaseAddressRegister.
|
||
//
|
||
|
||
if (Config->HeaderType == PCI_CARDBUS_BRIDGE_TYPE) {
|
||
|
||
ULONG zeroLMBA = 0;
|
||
|
||
PciWriteDeviceConfig(PdoExtension,
|
||
&zeroLMBA,
|
||
CARDBUS_LMBA_OFFSET,
|
||
sizeof(zeroLMBA)
|
||
);
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
PIO_RESOURCE_REQUIREMENTS_LIST
|
||
PciAllocateIoRequirementsList(
|
||
IN ULONG ResourceCount,
|
||
IN ULONG BusNumber,
|
||
IN ULONG SlotNumber
|
||
)
|
||
{
|
||
PIO_RESOURCE_REQUIREMENTS_LIST list;
|
||
ULONG size;
|
||
|
||
//
|
||
// Allocate space for (and zero) the resource requirements list.
|
||
//
|
||
|
||
size = ((ResourceCount - 1) * sizeof(IO_RESOURCE_DESCRIPTOR)) +
|
||
sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
|
||
|
||
if (ResourceCount == 0) {
|
||
|
||
//
|
||
// We should not be called for a resource count of zero, except
|
||
// once for the empty list. In any case, it should work.
|
||
//
|
||
|
||
size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
|
||
}
|
||
|
||
list = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, size);
|
||
|
||
if (list != NULL) {
|
||
|
||
RtlZeroMemory(list, size);
|
||
|
||
//
|
||
// Initialize the list structure header.
|
||
//
|
||
// Driver constant-
|
||
//
|
||
|
||
list->InterfaceType = PCIBus;
|
||
list->AlternativeLists = 1;
|
||
list->List[0].Version = PCI_CM_RESOURCE_VERSION;
|
||
list->List[0].Revision = PCI_CM_RESOURCE_REVISION;
|
||
|
||
//
|
||
// Call dependent.
|
||
//
|
||
|
||
list->BusNumber = BusNumber;
|
||
list->SlotNumber = SlotNumber;
|
||
list->ListSize = size;
|
||
list->List[0].Count = ResourceCount;
|
||
}
|
||
return list;
|
||
}
|
||
|
||
VOID
|
||
PciFreeIoRequirementsList(
|
||
IN PIO_RESOURCE_REQUIREMENTS_LIST List
|
||
)
|
||
{
|
||
//
|
||
// Don't free the empty list and don't free NULL which is
|
||
// also allowed.
|
||
//
|
||
|
||
if ((List == NULL) || (List == PciZeroIoResourceRequirements)) {
|
||
return;
|
||
}
|
||
|
||
ExFreePool(List);
|
||
}
|
||
|
||
PCM_RESOURCE_LIST
|
||
PciAllocateCmResourceList(
|
||
IN ULONG ResourceCount,
|
||
IN ULONG BusNumber
|
||
)
|
||
{
|
||
PCM_RESOURCE_LIST list;
|
||
ULONG size;
|
||
PCM_PARTIAL_RESOURCE_LIST partial;
|
||
|
||
//
|
||
// CM_RESOURCE_LIST includes space for one descriptor. If there's
|
||
// more than one (in the resource list) increase the allocation by
|
||
// that amount.
|
||
//
|
||
|
||
size = sizeof(CM_RESOURCE_LIST);
|
||
|
||
if (ResourceCount > 1) {
|
||
size += (ResourceCount - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
|
||
}
|
||
|
||
//
|
||
// Get memory for the resource list.
|
||
//
|
||
|
||
list = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, size);
|
||
if (list != NULL) {
|
||
|
||
//
|
||
// Initialize the resource list.
|
||
//
|
||
|
||
list->Count = 1;
|
||
list->List[0].InterfaceType = PCIBus;
|
||
list->List[0].BusNumber = BusNumber;
|
||
|
||
partial = &list->List[0].PartialResourceList;
|
||
|
||
partial->Version = PCI_CM_RESOURCE_VERSION;
|
||
partial->Revision = PCI_CM_RESOURCE_REVISION;
|
||
partial->Count = ResourceCount;
|
||
|
||
RtlZeroMemory(
|
||
partial->PartialDescriptors,
|
||
size - ((ULONG_PTR)partial->PartialDescriptors - (ULONG_PTR)list)
|
||
);
|
||
}
|
||
return list;
|
||
}
|
||
|
||
VOID
|
||
PciPrivateResourceInitialize(
|
||
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
|
||
IN PCI_PRIVATE_RESOURCE_TYPES PrivateResourceType,
|
||
IN ULONG Data
|
||
)
|
||
{
|
||
Descriptor->Type = CmResourceTypeDevicePrivate;
|
||
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
||
Descriptor->Option = 0;
|
||
Descriptor->Flags = 0;
|
||
|
||
Descriptor->u.DevicePrivate.Data[0] = PrivateResourceType;
|
||
Descriptor->u.DevicePrivate.Data[1] = Data;
|
||
}
|
||
|
||
VOID
|
||
PciGetInUseRanges(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG CurrentConfig,
|
||
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR InUse
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Builds an array of CM Partial Resource descriptors containing
|
||
valid entries only where the corresponding PCI address range
|
||
is in use, NULL otherwise.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pointer to the Physical Device Object Extension for
|
||
the device whose requirements list is needed.
|
||
CurrentConfig - Existing contents of configuration space.
|
||
Partial - Pointer to an array of CM_PARTIAL_RESOURCE_DESCRIPTORs.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
|
||
PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
|
||
BOOLEAN enabledPciIo;
|
||
BOOLEAN enabledPciMem;
|
||
BOOLEAN passing = FALSE;
|
||
ULONG index;
|
||
|
||
partial = PdoExtension->Resources->Current;
|
||
ioResourceDescriptor = PdoExtension->Resources->Limit;
|
||
|
||
enabledPciIo = BITS_SET(CurrentConfig->Command, PCI_ENABLE_IO_SPACE)
|
||
|| BITS_SET(PdoExtension->InitialCommand, PCI_ENABLE_IO_SPACE);
|
||
enabledPciMem = BITS_SET(CurrentConfig->Command, PCI_ENABLE_MEMORY_SPACE)
|
||
|| BITS_SET(PdoExtension->InitialCommand, PCI_ENABLE_MEMORY_SPACE);
|
||
|
||
for (index = 0;
|
||
index < PCI_MAX_RANGE_COUNT;
|
||
index++, InUse++, partial++, ioResourceDescriptor++) {
|
||
|
||
//
|
||
// Default to not in use.
|
||
//
|
||
|
||
InUse->Type = CmResourceTypeNull;
|
||
|
||
//
|
||
// If the resource type in the limits array is
|
||
// CmResourceTypeNull, this resource is not implemented.
|
||
//
|
||
|
||
if (ioResourceDescriptor->Type != CmResourceTypeNull) {
|
||
|
||
//
|
||
// Not NULL, only options are Port or Memory, we will
|
||
// consider this entry if the approptiate resource
|
||
// (Port or Memory) is currently enabled.
|
||
//
|
||
|
||
if (((partial->Type == CmResourceTypePort) && enabledPciIo) ||
|
||
((partial->Type == CmResourceTypeMemory) && enabledPciMem)) {
|
||
|
||
if (partial->u.Generic.Length != 0) {
|
||
|
||
//
|
||
// Length is non-zero, if base is also non-zero, OR
|
||
// if this is a bridge and the resource type is IO,
|
||
// allow it.
|
||
//
|
||
|
||
if ((partial->u.Generic.Start.QuadPart != 0) ||
|
||
((PciGetConfigurationType(CurrentConfig) == PCI_BRIDGE_TYPE) &&
|
||
(partial->Type == CmResourceTypePort))) {
|
||
|
||
//
|
||
// This resource describes a valid range that is
|
||
// currently enabled by hardware.
|
||
//
|
||
|
||
*InUse = *partial;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
PciBuildGraduatedWindow(
|
||
IN PIO_RESOURCE_DESCRIPTOR PrototypeDescriptor,
|
||
IN ULONG WindowMax,
|
||
IN ULONG WindowCount,
|
||
OUT PIO_RESOURCE_DESCRIPTOR OutputDescriptor
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Builds an array of IO Resource descriptors containing
|
||
graduated requirements from WindowMax for WindowCount
|
||
Descriptors dividing the length required in half each time.
|
||
|
||
eg If WindowMax is 64Mb and WindowCount is 7 we end up with
|
||
the progression 64Mb, 32Mb, 16Mb, 8Mb, 4Mb, 2Mb, 1Mb
|
||
|
||
This only works for IO and Memory descriptors.
|
||
|
||
Arguments:
|
||
|
||
PrototypeDescriptor - this is used to initialize each
|
||
requirement descriptor then the lenght is modified
|
||
|
||
WindowMax - the maximum size of the window (where we build
|
||
the progression from)
|
||
|
||
WindowCount - the number of descriptors in the progression
|
||
|
||
OutputDescriptor - pointer to the first of WindowCount
|
||
descriptors to be populated by this function.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
ULONG window, count;
|
||
PIO_RESOURCE_DESCRIPTOR current;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(PrototypeDescriptor->Type == CmResourceTypePort
|
||
|| PrototypeDescriptor->Type == CmResourceTypeMemory);
|
||
|
||
|
||
window = WindowMax;
|
||
current = OutputDescriptor;
|
||
|
||
for (count = 0; count < WindowCount; count++) {
|
||
|
||
RtlCopyMemory(current, PrototypeDescriptor, sizeof(IO_RESOURCE_DESCRIPTOR));
|
||
//
|
||
// Update the length
|
||
//
|
||
current->u.Generic.Length = window;
|
||
//
|
||
// If this is an alternative then mark it so
|
||
//
|
||
if (count > 0) {
|
||
current->Option = IO_RESOURCE_ALTERNATIVE;
|
||
}
|
||
current++;
|
||
|
||
//
|
||
// Divide window and repeat
|
||
//
|
||
|
||
window /= 2;
|
||
ASSERT(window > 1);
|
||
}
|
||
|
||
//
|
||
// Return the number of descriptors filled in
|
||
//
|
||
|
||
ASSERT((current - OutputDescriptor) == WindowCount);
|
||
}
|
||
|
||
NTSTATUS
|
||
PciBuildRequirementsList(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG CurrentConfig,
|
||
OUT PIO_RESOURCE_REQUIREMENTS_LIST *FinalReqList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Build the IO_RESOURCE_REQUIREMENTS_LIST structure for this device.
|
||
|
||
This structure contains the devices limits and requirements, for
|
||
example, IO space in the range 0x100 to 0x1ff, Length 10.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pointer to the Physical Device Object Extension for
|
||
the device whose requirements list is needed.
|
||
CurrentConfig - Existing contents of configuration space.
|
||
|
||
Return Value:
|
||
|
||
Returns a pointer to the IO_RESOURCE_REQUIREMENTS_LIST for this
|
||
device/function if all went well. NULL otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Each base resource requires three extended resource descriptors.
|
||
// the total RESOURCES_PER_BAR are
|
||
//
|
||
// 1. Base Resource Descriptor. eg PCI Memory or I/O space.
|
||
// 2. Ext Resource Descriptor, DevicePrivate. This is used to
|
||
// keep track of which BAR this resource is derived from.
|
||
|
||
#define RESOURCES_PER_BAR 2
|
||
#define PCI_GRADUATED_WINDOW_COUNT 7 // 64, 32, 16, 8, 4, 2, 1
|
||
#define PCI_GRADUATED_WINDOW_MAX (64 * 1024 * 1024) // 64Mb
|
||
|
||
ULONG index;
|
||
ULONG baseResourceCount = 0;
|
||
ULONG configType;
|
||
ULONG interruptMin, interruptMax;
|
||
ULONG iterationCount;
|
||
BOOLEAN generatesInterrupt = FALSE;
|
||
NTSTATUS status;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
|
||
PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
|
||
PIO_RESOURCE_DESCRIPTOR resource;
|
||
PIO_RESOURCE_REQUIREMENTS_LIST reqList;
|
||
CM_PARTIAL_RESOURCE_DESCRIPTOR inUse[PCI_MAX_RANGE_COUNT];
|
||
PPCI_CONFIGURATOR configurator;
|
||
|
||
PciDebugPrint(PciDbgInformative,
|
||
"PciBuildRequirementsList: Bus 0x%x, Dev 0x%x, Func 0x%x.\n",
|
||
PCI_PARENT_FDOX(PdoExtension)->BaseBus,
|
||
PdoExtension->Slot.u.bits.DeviceNumber,
|
||
PdoExtension->Slot.u.bits.FunctionNumber);
|
||
|
||
if (PdoExtension->Resources == NULL) {
|
||
|
||
//
|
||
// If this function implements no BARs, we won't have a
|
||
// resource structure for it.
|
||
//
|
||
|
||
iterationCount = 0;
|
||
|
||
} else {
|
||
|
||
iterationCount = PCI_MAX_RANGE_COUNT;
|
||
partial = inUse;
|
||
ioResourceDescriptor = PdoExtension->Resources->Limit;
|
||
PciGetInUseRanges(PdoExtension, CurrentConfig, partial);
|
||
}
|
||
|
||
configurator =
|
||
&PciConfigurators[PciGetConfigurationType(CurrentConfig)];
|
||
|
||
//
|
||
// First pass, figure out how large the resource requirements
|
||
// list needs to be.
|
||
//
|
||
|
||
for (index = 0;
|
||
index < iterationCount;
|
||
index++, partial++, ioResourceDescriptor++) {
|
||
|
||
//
|
||
// If the resource type in the limits array is
|
||
// CmResourceTypeNull, this resource is not implemented.
|
||
//
|
||
|
||
if (ioResourceDescriptor->Type != CmResourceTypeNull) {
|
||
|
||
if (partial->Type != CmResourceTypeNull) {
|
||
|
||
if ((ioResourceDescriptor->u.Generic.Length == 0) // bridge
|
||
|
||
#if PCI_BOOT_CONFIG_PREFERRED
|
||
|
||
|| (1) // always
|
||
|
||
#endif
|
||
)
|
||
{
|
||
|
||
//
|
||
// Count one for the preferred setting.
|
||
//
|
||
|
||
baseResourceCount++;
|
||
|
||
PciDebugPrint(PciDbgObnoxious,
|
||
" Index %d, Preferred = TRUE\n",
|
||
index
|
||
);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// This range is not being passed so we will not
|
||
// generate a preferred setting for it.
|
||
//
|
||
// Bridges have variable length ranges so there is
|
||
// no meaningful value that we could have stored in
|
||
// the base descriptor other than 0. We use this
|
||
// fact to determine if it's a bridged range.
|
||
//
|
||
// If this range is a bridge range, we do not want
|
||
// to generate a base descriptor for it either.
|
||
//
|
||
// Unless we are providing default minimum settings.
|
||
// (Only do this for PCI-PCI bridges, CardBus gets
|
||
// to figure out what it wants).
|
||
//
|
||
|
||
if (ioResourceDescriptor->u.Generic.Length == 0) {
|
||
|
||
|
||
//
|
||
// Generating prefered settings,... unless,...
|
||
// If the bridge IO is enabled and VGA is enabled,
|
||
// (and the IO range isn't programmed,... which is
|
||
// how we got here), then the VGA ranges are enough,
|
||
// don't try to add a range.
|
||
//
|
||
|
||
if ((ioResourceDescriptor->Type == CmResourceTypePort) &&
|
||
PdoExtension->Dependent.type1.VgaBitSet) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If this is a memory window then make space for a graduated
|
||
// requirement and a device private to follow it
|
||
//
|
||
|
||
if (ioResourceDescriptor->Type == CmResourceTypeMemory) {
|
||
baseResourceCount += PCI_GRADUATED_WINDOW_COUNT + 1;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this resource is a ROM which is not currently
|
||
// enabled, don't report it at all.
|
||
//
|
||
// Note: The ROM requirement exists in the io resource
|
||
// descriptors so we know what to do if we get a read
|
||
// config for the ROM.
|
||
//
|
||
|
||
if ((ioResourceDescriptor->Type == CmResourceTypeMemory) &&
|
||
(ioResourceDescriptor->Flags == CM_RESOURCE_MEMORY_READ_ONLY)) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Count one for the base resource, and any per resource
|
||
// special ones (eg Device Private).
|
||
//
|
||
|
||
baseResourceCount += RESOURCES_PER_BAR;
|
||
|
||
PciDebugPrint(PciDbgObnoxious,
|
||
" Index %d, Base Resource = TRUE\n",
|
||
index
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// One base type for Interrupts if enabled.
|
||
//
|
||
|
||
status = PciGetInterruptAssignment(PdoExtension,
|
||
&interruptMin,
|
||
&interruptMax);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
generatesInterrupt = TRUE;
|
||
baseResourceCount += RESOURCES_PER_BAR - 1;
|
||
}
|
||
|
||
//
|
||
// If the header type dependent resource routines indicated
|
||
// additional resources are required, add them in here.
|
||
//
|
||
|
||
baseResourceCount += PdoExtension->AdditionalResourceCount;
|
||
|
||
PciDebugPrint(PciDbgPrattling,
|
||
"PCI - build resource reqs - baseResourceCount = %d\n",
|
||
baseResourceCount);
|
||
|
||
if (baseResourceCount == 0) {
|
||
|
||
//
|
||
// This device consumes no resources. Succeed the request but
|
||
// return a pointer to our private empty list. This will never
|
||
// actually be given to anyone else but having an empty list
|
||
// removes a bunch of special case code for handling a NULL
|
||
// pointer.
|
||
//
|
||
|
||
if (PciZeroIoResourceRequirements == NULL) {
|
||
PciZeroIoResourceRequirements = PciAllocateIoRequirementsList(
|
||
0, // resource count
|
||
0, // bus
|
||
0 // slot
|
||
);
|
||
}
|
||
*FinalReqList = PciZeroIoResourceRequirements;
|
||
|
||
PciDebugPrint(PciDbgPrattling,
|
||
"PCI - build resource reqs - early out, 0 resources\n");
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Allocate and (bulk) initialize the IO resource requirements list.
|
||
//
|
||
|
||
reqList = PciAllocateIoRequirementsList(
|
||
baseResourceCount,
|
||
PCI_PARENT_FDOX(PdoExtension)->BaseBus,
|
||
PdoExtension->Slot.u.AsULONG);
|
||
|
||
if (reqList == NULL) {
|
||
|
||
//
|
||
// Not much we can do about this, bail.
|
||
//
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Second pass, build the resource list.
|
||
//
|
||
|
||
if (iterationCount != 0) {
|
||
partial = inUse;
|
||
ioResourceDescriptor = PdoExtension->Resources->Limit;
|
||
}
|
||
resource = reqList->List[0].Descriptors;
|
||
|
||
for (index = 0;
|
||
index < iterationCount;
|
||
index++, partial++, ioResourceDescriptor++) {
|
||
|
||
BOOLEAN passing;
|
||
ULONG genericLength;
|
||
ULONG genericAlignment;
|
||
|
||
if (ioResourceDescriptor->Type == CmResourceTypeNull) {
|
||
|
||
//
|
||
// Nothing here.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Try to determine if the current setting for this resource
|
||
// is (a) active (eg is's an IO resource and IO is enabled
|
||
// for this device), and (b) valid.
|
||
//
|
||
|
||
passing = FALSE;
|
||
genericLength = ioResourceDescriptor->u.Generic.Length;
|
||
genericAlignment = ioResourceDescriptor->u.Generic.Alignment;
|
||
|
||
if (partial->Type == CmResourceTypeNull) {
|
||
|
||
//
|
||
// Current setting is either not enabled or it's invalid
|
||
// (h/w invalid ie the h/w will not see it as enabled).
|
||
//
|
||
// The base resource is for a bridged range, skip it
|
||
// altogether.
|
||
//
|
||
|
||
if (genericLength == 0) {
|
||
|
||
//
|
||
// There is no boot setting for this bridge resource,
|
||
// If the VGA bit is set, no need for a normal range.
|
||
//
|
||
|
||
if ((ioResourceDescriptor->Type == CmResourceTypeMemory) ||
|
||
((ioResourceDescriptor->Type == CmResourceTypePort) &&
|
||
(PdoExtension->Dependent.type1.VgaBitSet == FALSE))) {
|
||
|
||
switch (PciClassifyDeviceType(PdoExtension)) {
|
||
case PciTypePciBridge:
|
||
|
||
if (ioResourceDescriptor->Type == CmResourceTypeMemory) {
|
||
PciBuildGraduatedWindow(ioResourceDescriptor,
|
||
PCI_GRADUATED_WINDOW_MAX,
|
||
PCI_GRADUATED_WINDOW_COUNT,
|
||
resource);
|
||
|
||
resource += PCI_GRADUATED_WINDOW_COUNT;
|
||
|
||
PciPrivateResourceInitialize(resource,
|
||
PciPrivateBar,
|
||
index);
|
||
|
||
resource++;
|
||
|
||
continue;
|
||
|
||
} else {
|
||
//
|
||
// Do the minium for IO space which is 4kb
|
||
//
|
||
|
||
genericLength = 0x1000;
|
||
genericAlignment = 0x1000;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case PciTypeCardbusBridge:
|
||
|
||
if (ioResourceDescriptor->Type == CmResourceTypeMemory) {
|
||
PciBuildGraduatedWindow(ioResourceDescriptor,
|
||
PCI_GRADUATED_WINDOW_MAX,
|
||
PCI_GRADUATED_WINDOW_COUNT,
|
||
resource);
|
||
|
||
resource += PCI_GRADUATED_WINDOW_COUNT;
|
||
|
||
PciPrivateResourceInitialize(resource,
|
||
PciPrivateBar,
|
||
index);
|
||
|
||
resource++;
|
||
|
||
continue;
|
||
|
||
} else {
|
||
//
|
||
// Do the minium for IO space which is 256 bytes
|
||
//
|
||
|
||
genericLength = 0x100;
|
||
genericAlignment = 0x100;
|
||
}
|
||
|
||
break;
|
||
default:
|
||
|
||
//
|
||
// I don't know what this is.
|
||
// N.B. Can't actually get here.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Not IO or memory? - Skip it altogether.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Could be that it's a ROM that we don't actually want
|
||
// to report.
|
||
//
|
||
|
||
if ((ioResourceDescriptor->Type == CmResourceTypeMemory) &&
|
||
(ioResourceDescriptor->Flags == CM_RESOURCE_MEMORY_READ_ONLY)) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Current setting IS being passed. If it is a bridge,
|
||
// we will provide the current setting as preferred
|
||
// regardless. This may change one day, if it does
|
||
// we MUST get the length into the generic setting
|
||
// before we pass it into IO in the resource descriptor.
|
||
//
|
||
|
||
if ((genericLength == 0) // bridge
|
||
|
||
#if PCI_BOOT_CONFIG_PREFERRED
|
||
|
||
|| (1) // always
|
||
|
||
#endif
|
||
)
|
||
{
|
||
passing = TRUE;
|
||
genericLength = partial->u.Generic.Length;
|
||
}
|
||
}
|
||
|
||
PciDebugPrint(PciDbgObnoxious,
|
||
" Index %d, Setting Base Resource,%s setting preferred.\n",
|
||
index,
|
||
passing ? "": " not"
|
||
);
|
||
|
||
ASSERT((resource + RESOURCES_PER_BAR + (passing ? 1 : 0) -
|
||
reqList->List[0].Descriptors) <= (LONG)baseResourceCount);
|
||
|
||
//
|
||
// Fill in the base resource descriptor.
|
||
//
|
||
|
||
*resource = *ioResourceDescriptor;
|
||
resource->ShareDisposition = CmResourceShareDeviceExclusive;
|
||
resource->u.Generic.Length = genericLength;
|
||
resource->u.Generic.Alignment = genericAlignment;
|
||
|
||
//
|
||
// Set the positive decode bit and 16 bit decode for all IO requirements
|
||
//
|
||
|
||
if (ioResourceDescriptor->Type == CmResourceTypePort) {
|
||
resource->Flags |= (CM_RESOURCE_PORT_POSITIVE_DECODE
|
||
| CM_RESOURCE_PORT_16_BIT_DECODE);
|
||
}
|
||
|
||
//
|
||
// If this device is decoding IO or Memory, and this resource
|
||
// is of that type, include the prefered settings in the list.
|
||
//
|
||
|
||
if (passing) {
|
||
|
||
extern BOOLEAN PciLockDeviceResources;
|
||
|
||
//
|
||
// Copy the set of descriptors we just created.
|
||
//
|
||
|
||
PciDebugPrint(PciDbgVerbose, " Duplicating for preferred locn.\n");
|
||
|
||
*(resource + 1) = *resource;
|
||
|
||
//
|
||
// Change the original to indicate it is the preferred
|
||
// setting and put the current settings into minimum
|
||
// address field, current setting + length into the max.
|
||
//
|
||
|
||
resource->Option = IO_RESOURCE_PREFERRED;
|
||
resource->u.Generic.MinimumAddress = partial->u.Generic.Start;
|
||
resource->u.Generic.MaximumAddress.QuadPart =
|
||
resource->u.Generic.MinimumAddress.QuadPart +
|
||
(resource->u.Generic.Length - 1);
|
||
|
||
//
|
||
// The preferred setting is fixed (Start + Length - 1 == End) and
|
||
// so alignment is not a restricting factor.
|
||
//
|
||
resource->u.Generic.Alignment = 1;
|
||
|
||
if (PciLockDeviceResources == TRUE ||
|
||
PdoExtension->LegacyDriver == TRUE ||
|
||
PdoExtension->OnDebugPath ||
|
||
(PCI_PARENT_FDOX(PdoExtension)->BusHackFlags & PCI_BUS_HACK_LOCK_RESOURCES) ||
|
||
|
||
//
|
||
// This is a work around for a pnp bug which affects Toshiba
|
||
// Satellite machines. We end up moving the PCI modem off its
|
||
// boot config because it is reserved (on 2f8 or 3f8) and then
|
||
// put a PCMCIA modem on top of it before it has been turned off.
|
||
// Stops before Starts would fix this but the driver folks say
|
||
// they can't deal with that so we need to fix this as part
|
||
// of the rebalance cleanup in 5.1
|
||
//
|
||
|
||
#if PCI_NO_MOVE_MODEM_IN_TOSHIBA
|
||
(PdoExtension->VendorId == 0x11c1
|
||
&& PdoExtension->DeviceId == 0x0441
|
||
&& PdoExtension->SubsystemVendorId == 0x1179
|
||
&& (PdoExtension->SubsystemId == 0x0001 || PdoExtension->SubsystemId == 0x0002))
|
||
#endif
|
||
|
||
) {
|
||
|
||
|
||
//
|
||
// Restrict the alternatives to the current settings.
|
||
//
|
||
|
||
*(resource + 1) = *resource;
|
||
}
|
||
|
||
(resource + 1)->Option = IO_RESOURCE_ALTERNATIVE;
|
||
|
||
//
|
||
// bump resource by one to allow for the one we just added.
|
||
//
|
||
|
||
resource++;
|
||
}
|
||
|
||
//
|
||
// A devicePrivateResource is used to keep track of which
|
||
// BAR this resource is derived from. Record it now because
|
||
// index may get bumped if this is a 64 bit memory BAR.
|
||
//
|
||
|
||
PciPrivateResourceInitialize(resource + 1,
|
||
PciPrivateBar,
|
||
index);
|
||
|
||
resource += RESOURCES_PER_BAR;
|
||
}
|
||
|
||
//
|
||
// Assign descriptors for interrupts.
|
||
//
|
||
|
||
if (generatesInterrupt) {
|
||
|
||
PciDebugPrint(PciDbgVerbose, " Assigning INT descriptor\n");
|
||
|
||
//
|
||
// Finally, fill in the base resource descriptor.
|
||
//
|
||
|
||
resource->Type = CmResourceTypeInterrupt;
|
||
resource->ShareDisposition = CmResourceShareShared;
|
||
resource->Option = 0;
|
||
resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
||
resource->u.Interrupt.MinimumVector = interruptMin;
|
||
resource->u.Interrupt.MaximumVector = interruptMax;
|
||
|
||
resource += (RESOURCES_PER_BAR - 1);
|
||
}
|
||
|
||
if (PdoExtension->AdditionalResourceCount != 0) {
|
||
|
||
//
|
||
// The header type dependent code indicated that it has
|
||
// resources to add. Call it back and allow it do add
|
||
// them now.
|
||
//
|
||
|
||
configurator->GetAdditionalResourceDescriptors(
|
||
PdoExtension,
|
||
CurrentConfig,
|
||
resource
|
||
);
|
||
|
||
resource += PdoExtension->AdditionalResourceCount;
|
||
}
|
||
|
||
//
|
||
// Done.
|
||
//
|
||
|
||
ASSERT(reqList->ListSize == (ULONG_PTR)resource - (ULONG_PTR)reqList);
|
||
|
||
#if DBG
|
||
|
||
PciDebugPrint(PciDbgPrattling,
|
||
"PCI build resource req - final resource count == %d\n",
|
||
resource - reqList->List[0].Descriptors);
|
||
|
||
ASSERT((resource - reqList->List[0].Descriptors) != 0);
|
||
|
||
#endif
|
||
|
||
//
|
||
// Return the address of the resource list and a successful status
|
||
// back to the caller.
|
||
//
|
||
|
||
*FinalReqList = reqList;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
PciWriteLimitsAndRestoreCurrent(
|
||
IN PPCI_CONFIGURABLE_OBJECT This
|
||
)
|
||
//
|
||
// Overwrite the device's configuration space with the adjusted
|
||
// version then read it back to see what the device did with it.
|
||
//
|
||
{
|
||
|
||
if (This->PdoExtension->OnDebugPath) {
|
||
|
||
//
|
||
// If our debugger is bus mastering then dont clear this bit as it
|
||
// will blow away the DMA engine on the card and we dont currently
|
||
// re-program the card when we call KdEnableDebugger.
|
||
//
|
||
if (This->Command & PCI_ENABLE_BUS_MASTER){
|
||
|
||
This->Working->Command |= PCI_ENABLE_BUS_MASTER;
|
||
This->Current->Command |= PCI_ENABLE_BUS_MASTER;
|
||
}
|
||
|
||
KdDisableDebugger();
|
||
}
|
||
|
||
//
|
||
// Write out all those F's to work out which bits are sticky
|
||
//
|
||
|
||
PciSetConfigData(This->PdoExtension, This->Working);
|
||
|
||
//
|
||
// Read in which bits stuck
|
||
//
|
||
|
||
PciGetConfigData(This->PdoExtension, This->Working);
|
||
|
||
//
|
||
// Return the device to it's previous state by writing the
|
||
// original values back into it.
|
||
//
|
||
// Note: Don't enable anything up front (ie, Command = 0)
|
||
// because the Command register will get wriiten before
|
||
// the BARs are updated which might enable translations
|
||
// at undesirable locations.
|
||
//
|
||
|
||
PciSetConfigData(This->PdoExtension, This->Current);
|
||
|
||
//
|
||
// Now, return the command register to it's previous state.
|
||
//
|
||
|
||
This->Current->Command = This->Command;
|
||
|
||
//
|
||
// Only do the write if we are actually going to change the
|
||
// value of the command field.
|
||
//
|
||
|
||
if (This->Command != 0) {
|
||
|
||
PciSetCommandRegister(This->PdoExtension, This->Command);
|
||
}
|
||
|
||
//
|
||
// Restore the status field in the caller's buffer.
|
||
//
|
||
|
||
This->Current->Status = This->Status;
|
||
|
||
//
|
||
// Restore any type specific fields.
|
||
//
|
||
|
||
This->Configurator->RestoreCurrent(This);
|
||
|
||
if (This->PdoExtension->OnDebugPath) {
|
||
KdEnableDebugger();
|
||
}
|
||
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// Check that what was written back stuck.
|
||
//
|
||
|
||
if (This->PdoExtension->ExpectedWritebackFailure == FALSE) {
|
||
|
||
PPCI_COMMON_CONFIG verifyConfig;
|
||
ULONG len;
|
||
|
||
verifyConfig = (PPCI_COMMON_CONFIG)
|
||
((ULONG_PTR)This->Working + PCI_COMMON_HDR_LENGTH);
|
||
PciGetConfigData(This->PdoExtension, verifyConfig);
|
||
|
||
if ((len = (ULONG)RtlCompareMemory(
|
||
verifyConfig,
|
||
This->Current,
|
||
PCI_COMMON_HDR_LENGTH)) != PCI_COMMON_HDR_LENGTH) {
|
||
|
||
//
|
||
// Compare failed.
|
||
//
|
||
|
||
PciDebugPrint(PciDbgInformative,
|
||
"PCI - CFG space write verify failed at offset 0x%x\n",
|
||
len);
|
||
PciDebugDumpCommonConfig(verifyConfig);
|
||
}
|
||
}
|
||
|
||
#endif
|
||
}
|
||
|
||
NTSTATUS
|
||
PcipGetFunctionLimits(
|
||
IN PPCI_CONFIGURABLE_OBJECT This
|
||
)
|
||
|
||
/*++
|
||
|
||
Description:
|
||
|
||
Determine the limits for the Base Address Registers (BARs) for a
|
||
given Bus/Device/Function.
|
||
|
||
This is done by writing all ones the the BARs, reading them
|
||
back again. The hardware will adjust the values to it's limits
|
||
so we just store these new values away.
|
||
|
||
Arguments:
|
||
|
||
This - Pointer to the configuration object.
|
||
|
||
Return Value:
|
||
|
||
Returns status indicating the success or failure of this routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG configType;
|
||
PPCI_COMMON_CONFIG current = This->Current;
|
||
PPCI_COMMON_CONFIG working = This->Working;
|
||
ULONG count;
|
||
PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// The first 16 bytes of configuration space are required by the
|
||
// PCI specification to be of the following format.
|
||
//
|
||
// 3 2 1 0
|
||
// +------------+------------+------------+------------+
|
||
// | Device ID | Vendor ID |
|
||
// +------------+------------+------------+------------+
|
||
// | Status | Command |
|
||
// +------------+------------+------------+------------+
|
||
// | Base Class | Sub Class | Progr. I/F | Revision ID|
|
||
// +------------+------------+------------+------------+
|
||
// | BIST | Header Type| Latency | Cache Ln Sz|
|
||
// +------------+------------+------------+------------+
|
||
//
|
||
// The status field in PCI Configuration space has its bits cleared
|
||
// by writing a one to each bit to be cleared. Zero the status
|
||
// field in the image of the current configuration space so writing
|
||
// to the hardware will not change it.
|
||
//
|
||
|
||
This->Status = current->Status;
|
||
current->Status = 0;
|
||
|
||
//
|
||
// Disable the device while it's configuration is being messed
|
||
// with.
|
||
//
|
||
|
||
This->Command = current->Command;
|
||
current->Command &= ~(PCI_ENABLE_IO_SPACE |
|
||
PCI_ENABLE_MEMORY_SPACE |
|
||
PCI_ENABLE_BUS_MASTER);
|
||
|
||
|
||
//
|
||
// Make a copy of the configuration space that was handed in.
|
||
// This copy will be modified and written to/read back from the
|
||
// device's configuration space to allow us to determine the
|
||
// limits for the device.
|
||
//
|
||
|
||
RtlCopyMemory(working, current, PCI_COMMON_HDR_LENGTH);
|
||
|
||
//
|
||
// Get the configuration type from the function's header.
|
||
// NOTE: We have already checked that it is valid so no
|
||
// further checking is needed here.
|
||
//
|
||
|
||
configType = PciGetConfigurationType(current);
|
||
|
||
//
|
||
// Set the configuration type dispatch table.
|
||
//
|
||
|
||
This->Configurator = &PciConfigurators[configType];
|
||
|
||
//
|
||
// Modify the "Working" copy of config space such that writing
|
||
// it to the hardware and reading it back again will enable us
|
||
// to determine the "limits" of this hardware's configurability.
|
||
//
|
||
|
||
This->Configurator->MassageHeaderForLimitsDetermination(This);
|
||
|
||
//
|
||
// Overwrite the device's configuration space with the adjusted
|
||
// version then read it back to see what the device did with it.
|
||
//
|
||
|
||
PciWriteLimitsAndRestoreCurrent(This);
|
||
|
||
//
|
||
// Allocate memory for limits and current usage.
|
||
//
|
||
// Note: This should NOT have been done already.
|
||
//
|
||
|
||
ASSERT(This->PdoExtension->Resources == NULL);
|
||
|
||
This->PdoExtension->Resources = ExAllocatePool(
|
||
NonPagedPool,
|
||
sizeof(PCI_FUNCTION_RESOURCES)
|
||
);
|
||
|
||
if (This->PdoExtension->Resources == NULL) {
|
||
|
||
//
|
||
// Couldn't get memory for this???
|
||
//
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Clear these structures.
|
||
//
|
||
// CmResourceTypeNull == 0, otherwise we need to init the Limits
|
||
// and current settings structures seperately.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
This->PdoExtension->Resources,
|
||
sizeof(PCI_FUNCTION_RESOURCES)
|
||
);
|
||
|
||
#if CmResourceTypeNull
|
||
|
||
for (count = 0; count < PCI_MAX_RANGE_COUNT; count++) {
|
||
This->PdoExtension->Resources->Limit[count].Type = CmResourceTypeNull;
|
||
This->PdoExtension->Resources->Current[count].Type = CmResourceTypeNull;
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Copy the limits and current settings into our device extension.
|
||
//
|
||
|
||
This->Configurator->SaveLimits(This);
|
||
This->Configurator->SaveCurrentSettings(This);
|
||
|
||
//
|
||
// If SaveLimits didn't find any resources, we can free the
|
||
// memory allocated to both limits and current settings. Note
|
||
// that we still must call SaveCurrentSettings because that
|
||
// routine is responsible for saving type specific data.
|
||
//
|
||
|
||
count = 0;
|
||
ioResourceDescriptor = This->PdoExtension->Resources->Limit +
|
||
PCI_MAX_RANGE_COUNT;
|
||
|
||
do {
|
||
ioResourceDescriptor--;
|
||
|
||
if (ioResourceDescriptor->Type != CmResourceTypeNull) {
|
||
|
||
//
|
||
// Some resource exists, get out.
|
||
//
|
||
|
||
count++;
|
||
break;
|
||
}
|
||
} while (ioResourceDescriptor != This->PdoExtension->Resources->Limit);
|
||
|
||
if (count == 0) {
|
||
|
||
//
|
||
// No resources.
|
||
//
|
||
|
||
ExFreePool(This->PdoExtension->Resources);
|
||
This->PdoExtension->Resources = NULL;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PciGetFunctionLimits(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG CurrentConfig,
|
||
IN ULONGLONG Flags
|
||
)
|
||
|
||
/*++
|
||
|
||
Description:
|
||
|
||
Determine the limits for the Base Address Registers (BARs) for a
|
||
given Bus/Device/Function.
|
||
|
||
The work is really done by PcipGetFunctionLimits. This function is
|
||
a wrapper to handle the allocation/deallocation of working memory.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - PDO Extension for the device object to obtain the
|
||
limits for.
|
||
CurrentConfig - Existing contents of the PCI Common Configuration
|
||
Space for this function.
|
||
|
||
Return Value:
|
||
|
||
Returns status indicating the success or failure of this routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPCI_COMMON_CONFIG workingConfig;
|
||
NTSTATUS status;
|
||
ULONG size;
|
||
PCI_CONFIGURABLE_OBJECT this;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check for anything the registry says we should not try
|
||
// to figure out the resources on. Examples are devices
|
||
// that don't consume resources but return garbage in their
|
||
// base address registers.
|
||
//
|
||
|
||
if (PciSkipThisFunction(CurrentConfig,
|
||
PdoExtension->Slot,
|
||
EnumResourceDetermination,
|
||
Flags) == TRUE) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
size = PCI_COMMON_HDR_LENGTH;
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// If a checked build, we will verify the writeback of the
|
||
// orginal contents of configuration space. Allow enough
|
||
// space for a verification copy.
|
||
//
|
||
|
||
size *= 2;
|
||
|
||
#endif
|
||
|
||
workingConfig = ExAllocatePool(NonPagedPool, size);
|
||
|
||
if (workingConfig == NULL) {
|
||
|
||
//
|
||
// Failed to get memory to work with, bail.
|
||
//
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
this.Current = CurrentConfig;
|
||
this.Working = workingConfig;
|
||
this.PdoExtension = PdoExtension;
|
||
|
||
status = PcipGetFunctionLimits(&this);
|
||
|
||
ExFreePool(workingConfig);
|
||
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
PciProcessBus(
|
||
IN PPCI_FDO_EXTENSION ParentFdo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Walk the child devices enumerated by PciScanBus and perform any processing
|
||
the needs to be done once all the children have been enumerated
|
||
|
||
Arguments:
|
||
|
||
ParentFdo - Our extension for the PCI bus functional device object.
|
||
|
||
Return Value:
|
||
|
||
NT status.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPCI_PDO_EXTENSION current, vgaBridge = NULL, parentBridge = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!PCI_IS_ROOT_FDO(ParentFdo)) {
|
||
parentBridge = PCI_BRIDGE_PDO(ParentFdo);
|
||
}
|
||
|
||
//
|
||
// If our parent is a bridge with the ISA bit set, then set the ISA bit on
|
||
// all child bridges unless they are subtractive in which case we set the
|
||
// IsaRequired bit
|
||
//
|
||
|
||
if (parentBridge
|
||
&& PciClassifyDeviceType(parentBridge) == PciTypePciBridge
|
||
&& (parentBridge->Dependent.type1.IsaBitSet || parentBridge->Dependent.type1.IsaBitRequired)) {
|
||
|
||
for (current = ParentFdo->ChildBridgePdoList;
|
||
current;
|
||
current = current->NextBridge) {
|
||
|
||
//
|
||
// For now we only set the ISA bit on PCI-PCI bridges
|
||
//
|
||
|
||
if (PciClassifyDeviceType(current) == PciTypePciBridge) {
|
||
if (current->Dependent.type1.SubtractiveDecode) {
|
||
current->Dependent.type1.IsaBitRequired = TRUE;
|
||
} else {
|
||
current->Dependent.type1.IsaBitSet = TRUE;
|
||
current->UpdateHardware = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Scan the bridges enumerated to see if we need to set the ISA bit
|
||
//
|
||
|
||
for (current = ParentFdo->ChildBridgePdoList;
|
||
current;
|
||
current = current->NextBridge) {
|
||
|
||
if (current->Dependent.type1.VgaBitSet) {
|
||
vgaBridge = current;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we have a bridge with the VGA bit set - set the ISA bit on all other
|
||
// bridges on this bus and force this to be written out to the hardware on
|
||
// the start.
|
||
//
|
||
|
||
if (vgaBridge) {
|
||
|
||
for (current = ParentFdo->ChildBridgePdoList;
|
||
current;
|
||
current = current->NextBridge) {
|
||
|
||
if (current != vgaBridge
|
||
&& PciClassifyDeviceType(current) == PciTypePciBridge) {
|
||
|
||
//
|
||
// If this device is already started then we had better have already set the ISA bit
|
||
//
|
||
|
||
if (current->DeviceState == PciStarted) {
|
||
ASSERT(current->Dependent.type1.IsaBitRequired || current->Dependent.type1.IsaBitSet);
|
||
}
|
||
|
||
//
|
||
// If its a subtrative decode bridge remember we would have
|
||
// set the ISA bit so any children can inhereit it, otherwise
|
||
// set it and force it out to the hardware.
|
||
//
|
||
|
||
if (current->Dependent.type1.SubtractiveDecode) {
|
||
current->Dependent.type1.IsaBitRequired = TRUE;
|
||
} else {
|
||
current->Dependent.type1.IsaBitSet = TRUE;
|
||
current->UpdateHardware = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check to see if there are any bridges in need of bus numbers and assign
|
||
// them if we are running on a machine where this is a good idea.
|
||
//
|
||
if (PciAssignBusNumbers) {
|
||
PciConfigureBusNumbers(ParentFdo);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PciScanBus(
|
||
IN PPCI_FDO_EXTENSION FdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Scan the bus (detailed in FdoExtension) for any PCI devices/functions
|
||
elligible for control via a WDM driver.
|
||
|
||
Arguments:
|
||
|
||
FdoExtension - Our extension for the PCI bus functional device object.
|
||
|
||
Return Value:
|
||
|
||
NT status.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PCI_COMMON_HEADER commonHeader[2];
|
||
PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader[0];
|
||
PPCI_COMMON_CONFIG biosConfig = (PPCI_COMMON_CONFIG)&commonHeader[1];
|
||
PDEVICE_OBJECT physicalDeviceObject;
|
||
PPCI_PDO_EXTENSION pdoExtension;
|
||
PCI_SLOT_NUMBER slot;
|
||
ULONG deviceNumber;
|
||
ULONG functionNumber;
|
||
ULONG pass;
|
||
USHORT SubVendorID, SubSystemID;
|
||
BOOLEAN isRoot;
|
||
ULONGLONG hackFlags;
|
||
ULONG maximumDevices;
|
||
PIO_RESOURCE_REQUIREMENTS_LIST tempRequirementsList;
|
||
BOOLEAN newDevices = FALSE;
|
||
UCHAR secondary;
|
||
|
||
PciDebugPrint(PciDbgPrattling,
|
||
"PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
|
||
FdoExtension,
|
||
FdoExtension->BaseBus);
|
||
|
||
isRoot = PCI_IS_ROOT_FDO(FdoExtension);
|
||
|
||
//
|
||
// Examine each possible device on this bus.
|
||
//
|
||
|
||
maximumDevices = PCI_MAX_DEVICES;
|
||
if (!isRoot) {
|
||
|
||
//
|
||
// Examine the PDO extension for the bridge device and see
|
||
// if it's broken.
|
||
//
|
||
|
||
pdoExtension = (PPCI_PDO_EXTENSION)
|
||
FdoExtension->PhysicalDeviceObject->DeviceExtension;
|
||
|
||
ASSERT_PCI_PDO_EXTENSION(pdoExtension);
|
||
|
||
if (pdoExtension->HackFlags & PCI_HACK_ONE_CHILD) {
|
||
maximumDevices = 1;
|
||
}
|
||
|
||
//
|
||
// NEC program the bus number in their _DCK method, unfortunatley we have already
|
||
// done it! So detect someone else reprogramming the bus number and restore
|
||
// the correct one!
|
||
//
|
||
|
||
PciReadDeviceConfig(pdoExtension,
|
||
&secondary,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type1.SecondaryBus),
|
||
sizeof(UCHAR)
|
||
);
|
||
|
||
if (secondary != pdoExtension->Dependent.type1.SecondaryBus) {
|
||
DbgPrint("PCI: Bus numbers have been changed! Restoring originals.\n");
|
||
PciSetBusNumbers(pdoExtension,
|
||
pdoExtension->Dependent.type1.PrimaryBus,
|
||
pdoExtension->Dependent.type1.SecondaryBus,
|
||
pdoExtension->Dependent.type1.SubordinateBus
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
slot.u.AsULONG = 0;
|
||
|
||
for (deviceNumber = 0;
|
||
deviceNumber < maximumDevices;
|
||
deviceNumber++) {
|
||
|
||
slot.u.bits.DeviceNumber = deviceNumber;
|
||
|
||
//
|
||
// Examine each possible function on this device.
|
||
// N.B. Early out if function 0 not present.
|
||
//
|
||
|
||
for (functionNumber = 0;
|
||
functionNumber < PCI_MAX_FUNCTION;
|
||
functionNumber++) {
|
||
|
||
#if defined(_AMD64_SIMULATOR_)
|
||
if (deviceNumber == 7 && functionNumber == 2) {
|
||
|
||
//
|
||
// The simulator is reporting an IDE controller here, but
|
||
// it's really a USB controller. Ignore this one.
|
||
//
|
||
|
||
break;
|
||
}
|
||
#endif
|
||
|
||
slot.u.bits.FunctionNumber = functionNumber;
|
||
|
||
PciReadSlotConfig(FdoExtension,
|
||
slot,
|
||
commonConfig,
|
||
0,
|
||
sizeof(commonConfig->VendorID)
|
||
);
|
||
|
||
|
||
if (commonConfig->VendorID == 0xFFFF ||
|
||
commonConfig->VendorID == 0) {
|
||
|
||
if (functionNumber == 0) {
|
||
|
||
//
|
||
// Didn't get any data on function zero of this
|
||
// device, no point in checking other functions.
|
||
//
|
||
|
||
break;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Check next function.
|
||
//
|
||
|
||
continue;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// We have a device so get the rest of its config space
|
||
//
|
||
|
||
PciReadSlotConfig(FdoExtension,
|
||
slot,
|
||
&commonConfig->DeviceID,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceID),
|
||
sizeof(PCI_COMMON_HEADER)
|
||
- sizeof(commonConfig->VendorID)
|
||
);
|
||
|
||
//
|
||
// Munge the config space if necessary
|
||
//
|
||
|
||
PciApplyHacks(FdoExtension,
|
||
commonConfig,
|
||
slot,
|
||
EnumHackConfigSpace,
|
||
NULL
|
||
);
|
||
|
||
#if DBG
|
||
|
||
{
|
||
ULONG i;
|
||
PWSTR descr;
|
||
|
||
i = 0x8000000 |
|
||
(FdoExtension->BaseBus << 16) |
|
||
(deviceNumber << 11) |
|
||
(functionNumber << 8);
|
||
|
||
PciDebugPrint(PciDbgPrattling,
|
||
"Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
|
||
i,
|
||
FdoExtension->BaseBus,
|
||
deviceNumber,
|
||
functionNumber);
|
||
|
||
PciDebugDumpCommonConfig(commonConfig);
|
||
|
||
descr = PciGetDeviceDescriptionMessage(
|
||
commonConfig->BaseClass,
|
||
commonConfig->SubClass);
|
||
|
||
PciDebugPrint(PciDbgPrattling,
|
||
"Device Description \"%S\".\n",
|
||
descr ? descr : L"(NULL)");
|
||
|
||
if (descr) {
|
||
ExFreePool(descr);
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
if ((PciGetConfigurationType(commonConfig) == PCI_DEVICE_TYPE) &&
|
||
(commonConfig->BaseClass != PCI_CLASS_BRIDGE_DEV)) {
|
||
SubVendorID = commonConfig->u.type0.SubVendorID;
|
||
SubSystemID = commonConfig->u.type0.SubSystemID;
|
||
} else {
|
||
SubVendorID = 0;
|
||
SubSystemID = 0;
|
||
}
|
||
|
||
hackFlags = PciGetHackFlags(commonConfig->VendorID,
|
||
commonConfig->DeviceID,
|
||
SubVendorID,
|
||
SubSystemID,
|
||
commonConfig->RevisionID
|
||
);
|
||
|
||
if (PciSkipThisFunction(commonConfig,
|
||
slot,
|
||
EnumBusScan,
|
||
hackFlags)) {
|
||
//
|
||
// Skip this function
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
|
||
//
|
||
// In case we are rescanning the bus, check to see if
|
||
// a PDO for this device already exists as a child of
|
||
// the FDO.
|
||
//
|
||
|
||
pdoExtension = PciFindPdoByFunction(
|
||
FdoExtension,
|
||
slot,
|
||
commonConfig);
|
||
|
||
if (pdoExtension == NULL) {
|
||
|
||
//
|
||
// Create a PDO for this new device.
|
||
//
|
||
|
||
newDevices = TRUE;
|
||
|
||
status = PciPdoCreate(FdoExtension,
|
||
slot,
|
||
&physicalDeviceObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ASSERT(NT_SUCCESS(status));
|
||
return status;
|
||
}
|
||
|
||
pdoExtension = (PPCI_PDO_EXTENSION)
|
||
physicalDeviceObject->DeviceExtension;
|
||
|
||
if (hackFlags & PCI_HACK_FAKE_CLASS_CODE) {
|
||
commonConfig->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
|
||
commonConfig->SubClass = PCI_SUBCLASS_SYS_OTHER;
|
||
#if DBG
|
||
pdoExtension->ExpectedWritebackFailure = TRUE;
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// Record device identification and type info.
|
||
//
|
||
|
||
pdoExtension->VendorId = commonConfig->VendorID;
|
||
pdoExtension->DeviceId = commonConfig->DeviceID;
|
||
pdoExtension->RevisionId = commonConfig->RevisionID;
|
||
pdoExtension->ProgIf = commonConfig->ProgIf;
|
||
pdoExtension->SubClass = commonConfig->SubClass;
|
||
pdoExtension->BaseClass = commonConfig->BaseClass;
|
||
pdoExtension->HeaderType =
|
||
PciGetConfigurationType(commonConfig);
|
||
|
||
//
|
||
// If this is a bridge (PCI-PCI or Cardbus) then insert into
|
||
// the list of child bridges for this bus
|
||
//
|
||
|
||
if (pdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV
|
||
&& (pdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI
|
||
|| pdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS)) {
|
||
|
||
PPCI_PDO_EXTENSION *current;
|
||
|
||
//
|
||
// Insert at the end of the list
|
||
//
|
||
|
||
ExAcquireFastMutex(&FdoExtension->ChildListMutex);
|
||
|
||
current = &FdoExtension->ChildBridgePdoList;
|
||
|
||
while (*current) {
|
||
current = &((*current)->NextBridge);
|
||
}
|
||
|
||
*current = pdoExtension;
|
||
ASSERT(pdoExtension->NextBridge == NULL);
|
||
|
||
ExReleaseFastMutex(&FdoExtension->ChildListMutex);
|
||
}
|
||
|
||
|
||
//
|
||
// See if we have already cached info for this device
|
||
//
|
||
|
||
status = PciGetBiosConfig(pdoExtension,
|
||
biosConfig
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Check if its the same device
|
||
//
|
||
|
||
if (PcipIsSameDevice(pdoExtension, biosConfig)) {
|
||
|
||
//
|
||
// Write the BiosConfig InterruptLine out to the hardware
|
||
// now and don't wait until start as many HALs will fail in
|
||
// PciGetAdjustedInterruptLine if we don't
|
||
//
|
||
|
||
if (biosConfig->u.type1.InterruptLine
|
||
!= commonConfig->u.type1.InterruptLine) {
|
||
|
||
PciWriteDeviceConfig(pdoExtension,
|
||
&biosConfig->u.type1.InterruptLine,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG,
|
||
u.type1.InterruptLine),
|
||
sizeof(UCHAR)
|
||
);
|
||
}
|
||
|
||
pdoExtension->RawInterruptLine
|
||
= biosConfig->u.type0.InterruptLine;
|
||
|
||
pdoExtension->InitialCommand = biosConfig->Command;
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Its a different device so blow away the old bios
|
||
// config
|
||
//
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Write out the BiosConfig from the config space we just
|
||
// read from the hardware
|
||
//
|
||
|
||
status = PciSaveBiosConfig(pdoExtension,
|
||
commonConfig
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
pdoExtension->RawInterruptLine
|
||
= commonConfig->u.type0.InterruptLine;
|
||
|
||
pdoExtension->InitialCommand = commonConfig->Command;
|
||
|
||
}
|
||
|
||
//
|
||
// Save the command register so we can restore the appropriate bits
|
||
//
|
||
pdoExtension->CommandEnables = commonConfig->Command;
|
||
|
||
//
|
||
// Save the device flags so we don't need to go to
|
||
// the registry all the time.
|
||
//
|
||
|
||
pdoExtension->HackFlags = hackFlags;
|
||
|
||
//
|
||
// See if we have any capabilities for this device
|
||
//
|
||
|
||
PciGetEnhancedCapabilities(pdoExtension, commonConfig);
|
||
|
||
//
|
||
// Before we calculate the Bar length, or get the capabilities
|
||
// we may need to set the device to D0. N.B. This does *not*
|
||
// update the power state stored in the pdoExtension.
|
||
//
|
||
PciSetPowerManagedDevicePowerState(
|
||
pdoExtension,
|
||
PowerDeviceD0,
|
||
FALSE
|
||
);
|
||
|
||
//
|
||
// Apply any hacks we know about for this device
|
||
//
|
||
|
||
PciApplyHacks(FdoExtension,
|
||
commonConfig,
|
||
slot,
|
||
EnumBusScan,
|
||
pdoExtension
|
||
);
|
||
|
||
//
|
||
// The interrupt number we report in the config data is obtained
|
||
// from the HAL rather than the hardware's config space.
|
||
//
|
||
pdoExtension->InterruptPin = commonConfig->u.type0.InterruptPin;
|
||
pdoExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(pdoExtension);
|
||
|
||
//
|
||
// Work out if we are on the debug path
|
||
//
|
||
|
||
pdoExtension->OnDebugPath = PciIsDeviceOnDebugPath(pdoExtension);
|
||
|
||
//
|
||
// Get the IO and MEMORY limits for this device. This
|
||
// is a hardware thing and will never change so we keep
|
||
// it in the PDO extension for future reference.
|
||
//
|
||
|
||
status = PciGetFunctionLimits(pdoExtension,
|
||
commonConfig,
|
||
hackFlags);
|
||
|
||
|
||
//
|
||
// NTRAID #62636 - 4/20/2000 - andrewth
|
||
// We are going to expose a PDO. Why not just let the OS put
|
||
// into whatever Dstate it feels?
|
||
//
|
||
PciSetPowerManagedDevicePowerState(
|
||
pdoExtension,
|
||
pdoExtension->PowerState.CurrentDeviceState,
|
||
FALSE
|
||
);
|
||
|
||
//
|
||
// Currently, this only returns errors on memory allocation.
|
||
//
|
||
if (!NT_SUCCESS(status)) {
|
||
ASSERT(NT_SUCCESS(status));
|
||
PciPdoDestroy(physicalDeviceObject);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If the device's SubSystem ID fields are not
|
||
// guaranteed to be the same when we enumerate
|
||
// the device after reapplying power to it (ie
|
||
// they depend on the BIOS to initialize it),
|
||
// then pretend it doesn't have a SubSystem ID
|
||
// at all.
|
||
//
|
||
|
||
if (hackFlags & PCI_HACK_NO_SUBSYSTEM) {
|
||
pdoExtension->SubsystemVendorId = 0;
|
||
pdoExtension->SubsystemId = 0;
|
||
}
|
||
|
||
#if DBG
|
||
//
|
||
// Dump the capabilities list.
|
||
//
|
||
|
||
{
|
||
union _cap_buffer {
|
||
PCI_CAPABILITIES_HEADER header;
|
||
PCI_PM_CAPABILITY pm;
|
||
PCI_AGP_CAPABILITY agp;
|
||
} cap;
|
||
|
||
UCHAR capOffset = pdoExtension->CapabilitiesPtr;
|
||
PUCHAR capStr;
|
||
ULONG nshort;
|
||
PUSHORT capData;
|
||
|
||
//
|
||
// Run the list.
|
||
//
|
||
|
||
while (capOffset != 0) {
|
||
|
||
UCHAR tmpOffset;
|
||
tmpOffset = PciReadDeviceCapability(
|
||
pdoExtension,
|
||
capOffset,
|
||
0, // match ANY ID
|
||
&cap,
|
||
sizeof(cap.header)
|
||
);
|
||
|
||
if (tmpOffset != capOffset) {
|
||
|
||
//
|
||
// Sanity check only, this can't happen.
|
||
//
|
||
|
||
PciDebugPrint(
|
||
PciDbgAlways,
|
||
"PCI - Failed to read PCI capability at offset 0x%02x\n",
|
||
capOffset
|
||
);
|
||
|
||
ASSERT(tmpOffset == capOffset);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Depending on the Capability ID, the amount
|
||
// of data varies.
|
||
//
|
||
|
||
switch (cap.header.CapabilityID) {
|
||
case PCI_CAPABILITY_ID_POWER_MANAGEMENT:
|
||
|
||
capStr = "POWER";
|
||
nshort = 3;
|
||
tmpOffset = PciReadDeviceCapability(
|
||
pdoExtension,
|
||
capOffset,
|
||
cap.header.CapabilityID,
|
||
&cap,
|
||
sizeof(cap.pm)
|
||
);
|
||
break;
|
||
|
||
case PCI_CAPABILITY_ID_AGP:
|
||
|
||
capStr = "AGP";
|
||
nshort = 5;
|
||
tmpOffset = PciReadDeviceCapability(
|
||
pdoExtension,
|
||
capOffset,
|
||
cap.header.CapabilityID,
|
||
&cap,
|
||
sizeof(cap.agp)
|
||
);
|
||
break;
|
||
|
||
default:
|
||
|
||
capStr = "UNKNOWN CAPABILITY";
|
||
nshort = 0;
|
||
break;
|
||
}
|
||
|
||
PciDebugPrint(
|
||
PciDbgPrattling,
|
||
"CAP @%02x ID %02x (%s)",
|
||
capOffset,
|
||
cap.header.CapabilityID,
|
||
capStr
|
||
);
|
||
|
||
if (tmpOffset != capOffset) {
|
||
|
||
//
|
||
// Sanity check only, this can't happen.
|
||
//
|
||
|
||
PciDebugPrint(
|
||
PciDbgAlways,
|
||
"- Failed to read capability data. ***\n"
|
||
);
|
||
|
||
ASSERT(tmpOffset == capOffset);
|
||
break;
|
||
}
|
||
|
||
capData = ((PUSHORT)&cap) + 1;
|
||
|
||
while (nshort--) {
|
||
|
||
PciDebugPrint(
|
||
PciDbgPrattling,
|
||
" %04x",
|
||
*capData++
|
||
);
|
||
}
|
||
PciDebugPrint(PciDbgPrattling, "\n");
|
||
|
||
//
|
||
// Advance to the next entry in the list.
|
||
//
|
||
|
||
capOffset = cap.header.Next;
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Don't allow Power Down to legacy type busses (ISA/EISA/
|
||
// MCA). Who knows what sort of unenumerated devices
|
||
// might be out there that the system is dependent on.
|
||
//
|
||
|
||
#ifdef PCIIDE_HACKS
|
||
|
||
//
|
||
// NTRAID #103766 - 4/20/2000 - andrewth
|
||
// This needs to be removed
|
||
// Also, don't allow an IDE device to power itself off.
|
||
//
|
||
|
||
if (pdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR &&
|
||
pdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR) {
|
||
pdoExtension->DisablePowerDown = TRUE;
|
||
}
|
||
#endif
|
||
|
||
if ((pdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV &&
|
||
(pdoExtension->SubClass == PCI_SUBCLASS_BR_ISA ||
|
||
pdoExtension->SubClass == PCI_SUBCLASS_BR_EISA ||
|
||
pdoExtension->SubClass == PCI_SUBCLASS_BR_MCA)) ||
|
||
|
||
(pdoExtension->VendorId == 0x8086 &&
|
||
pdoExtension->DeviceId == 0x0482)) {
|
||
|
||
pdoExtension->DisablePowerDown = TRUE;
|
||
}
|
||
|
||
//
|
||
// Try to determine if this device looks like it was hot plugged
|
||
// we assume that if IO, Mem and BusMaster bits are off and no
|
||
// one has initialized either the latency timer or the cache line
|
||
// size they should be initialized.
|
||
//
|
||
|
||
if (((pdoExtension->CommandEnables & (PCI_ENABLE_IO_SPACE
|
||
| PCI_ENABLE_MEMORY_SPACE
|
||
| PCI_ENABLE_BUS_MASTER)) == 0)
|
||
&& commonConfig->LatencyTimer == 0
|
||
&& commonConfig->CacheLineSize == 0) {
|
||
|
||
PciDebugPrint(
|
||
PciDbgConfigParam,
|
||
"PCI - ScanBus, PDOx %x found unconfigured\n",
|
||
pdoExtension
|
||
);
|
||
|
||
//
|
||
// Remember we need to configure this in PciSetResources
|
||
//
|
||
|
||
pdoExtension->NeedsHotPlugConfiguration = TRUE;
|
||
}
|
||
//
|
||
// Save the Latency Timer and Cache Line Size
|
||
// registers. These were set by the BIOS on
|
||
// power up but might need to be reset by the
|
||
// OS if the device is powered down/up by the
|
||
// OS without a reboot.
|
||
//
|
||
|
||
pdoExtension->SavedLatencyTimer =
|
||
commonConfig->LatencyTimer;
|
||
pdoExtension->SavedCacheLineSize =
|
||
commonConfig->CacheLineSize;
|
||
|
||
//
|
||
// We are able to receive IRPs for this device now.
|
||
//
|
||
|
||
physicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
||
} else {
|
||
|
||
//
|
||
// A PDO already exists for this device.
|
||
//
|
||
|
||
pdoExtension->NotPresent = FALSE;
|
||
ASSERT(pdoExtension->DeviceState != PciDeleted);
|
||
}
|
||
|
||
if ( (functionNumber == 0) &&
|
||
!PCI_MULTIFUNCTION_DEVICE(commonConfig) ) {
|
||
|
||
//
|
||
// Not a multifunction adapter, skip other functions on
|
||
// this device.
|
||
//
|
||
|
||
break;
|
||
}
|
||
} // function loop
|
||
} // device loop
|
||
|
||
//
|
||
// Perform any post processing on the devices for this bridge if we found any
|
||
// new devices
|
||
//
|
||
|
||
if (newDevices) {
|
||
PciProcessBus(FdoExtension);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
PciQueryRequirements(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Calculate the device's resource requirements from PCI Config space.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pdo Extension for the device (object) whose
|
||
requirements are needed.
|
||
|
||
RequirementsList - Returns the address of the requirements list.
|
||
|
||
Return Value:
|
||
|
||
NT status.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PCI_COMMON_HEADER commonHeader;
|
||
PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Early out, if the device has no CM or IO resources and doesn't
|
||
// use interrupts,... it doesn't have any resource requirements.
|
||
//
|
||
|
||
if ((PdoExtension->Resources == NULL) &&
|
||
(PdoExtension->InterruptPin == 0)) {
|
||
PciDebugPrint(
|
||
PciDbgPrattling,
|
||
"PciQueryRequirements returning NULL requirements list\n");
|
||
*RequirementsList = NULL;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Get the config space for the device (still needed to gen
|
||
// a requirements list. This should be changed so the PDOx
|
||
// has enough info to do it without going to the h/w again).
|
||
//
|
||
|
||
PciGetConfigData(PdoExtension, commonConfig);
|
||
|
||
status = PciBuildRequirementsList(PdoExtension,
|
||
commonConfig,
|
||
RequirementsList);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Check if this is the broken Compaq hot-plug controller that
|
||
// is integrated into the Profusion chipset. It only does a 32bit
|
||
// decode in a 64bit address space... Does this seem familiar to anyone...
|
||
// can you say ISA aliasing!
|
||
//
|
||
// The solution is to disable the memory decode. This was done earlier in
|
||
// PciApplyHacks from PciScan Bus. But so that the user gets to keep the
|
||
// hot-plug functionality we need to still enumerate but prune out the
|
||
// memory requirement and rely on the fact that the registers can be
|
||
// accessed through config space.
|
||
//
|
||
// Only do this on machines with PAE enabled as they can have > 4GB.
|
||
// Note that this will only work on x86 machines but this is an x86 only
|
||
// chipset. Only revision 0x11 was broken.
|
||
//
|
||
|
||
|
||
if (commonConfig->VendorID == 0x0e11
|
||
&& commonConfig->DeviceID == 0xa0f7
|
||
&& commonConfig->RevisionID == 0x11
|
||
&& ExIsProcessorFeaturePresent(PF_PAE_ENABLED)) {
|
||
|
||
PIO_RESOURCE_DESCRIPTOR current;
|
||
|
||
//
|
||
// Prune out the memory requirement
|
||
//
|
||
|
||
|
||
FOR_ALL_IN_ARRAY((*RequirementsList)->List[0].Descriptors,
|
||
(*RequirementsList)->List[0].Count,
|
||
current) {
|
||
if (current->Type == CmResourceTypeMemory) {
|
||
PIO_RESOURCE_DESCRIPTOR lookahead = current + 1;
|
||
|
||
current->Type = CmResourceTypeNull;
|
||
if (lookahead < ((*RequirementsList)->List[0].Descriptors +
|
||
(*RequirementsList)->List[0].Count)) {
|
||
if (lookahead->Type == CmResourceTypeDevicePrivate) {
|
||
lookahead->Type = CmResourceTypeNull;
|
||
current++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (*RequirementsList == PciZeroIoResourceRequirements) {
|
||
|
||
//
|
||
// This device (function) has no resources, return NULL
|
||
// intead of our zero list.
|
||
//
|
||
|
||
*RequirementsList = NULL;
|
||
|
||
#if DBG
|
||
|
||
PciDebugPrint(PciDbgPrattling, "Returning NULL requirements list\n");
|
||
|
||
} else {
|
||
|
||
PciDebugPrintIoResReqList(*RequirementsList);
|
||
|
||
#endif
|
||
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
PciQueryResources(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
OUT PCM_RESOURCE_LIST *ResourceList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a pointer to a PCI PDO, this routine allocates and returns a pointer
|
||
to the resource description of that PDO.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Our extension for the PCI-enumerated physical device object.
|
||
|
||
ResourceList - Used to return a pointer to the resource list.
|
||
|
||
Return Value:
|
||
|
||
NT status.
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
ULONG resourceCount;
|
||
ULONG statusCommand;
|
||
PCM_RESOURCE_LIST cmResourceList;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource, lastResource;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR current;
|
||
BOOLEAN enabledMemory;
|
||
BOOLEAN enabledIo;
|
||
BOOLEAN deviceIsABridge = FALSE;
|
||
PCI_OBJECT_TYPE deviceType;
|
||
USHORT command;
|
||
|
||
PAGED_CODE();
|
||
|
||
*ResourceList = NULL;
|
||
|
||
//
|
||
// Get a count of the resources.
|
||
//
|
||
|
||
if (PdoExtension->Resources == NULL) {
|
||
|
||
//
|
||
// This device has no resources, successfully return
|
||
// a NULL resource list.
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Seeing as other drivers (esp VideoPort for multimon) can change the
|
||
// enables for this device re-read the hardware to ensure we are correct.
|
||
//
|
||
|
||
PciGetCommandRegister(PdoExtension, &command);
|
||
|
||
enabledMemory = BITS_SET(command, PCI_ENABLE_MEMORY_SPACE);
|
||
enabledIo = BITS_SET(command, PCI_ENABLE_IO_SPACE);
|
||
|
||
resourceCount = 0;
|
||
current = PdoExtension->Resources->Current;
|
||
|
||
for (i = 0; i < PCI_MAX_RANGE_COUNT; i++, current++) {
|
||
if ((enabledMemory && (current->Type == CmResourceTypeMemory))
|
||
|| (enabledIo && (current->Type == CmResourceTypePort))) {
|
||
resourceCount++;
|
||
}
|
||
}
|
||
|
||
if (PdoExtension->InterruptPin && (enabledMemory || enabledIo)) {
|
||
|
||
if (PdoExtension->AdjustedInterruptLine != 0 && PdoExtension->AdjustedInterruptLine != 0xFF) {
|
||
resourceCount += 1;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
if (resourceCount == 0) {
|
||
|
||
//
|
||
// Device has no resources currently enabled.
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Allocate a CM Resource List large enough to handle this
|
||
// device's resources.
|
||
//
|
||
|
||
cmResourceList = PciAllocateCmResourceList(
|
||
resourceCount,
|
||
PCI_PARENT_FDOX(PdoExtension)->BaseBus
|
||
);
|
||
if (cmResourceList == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
resource = PciFirstCmResource(cmResourceList);
|
||
lastResource = resource + resourceCount;
|
||
|
||
//
|
||
// Copy the resources from the PDO's in-use resource table to
|
||
// the output resource list - the ISA bit is set will be dealt with in
|
||
// the arbiters - just as for resource requirements.
|
||
//
|
||
|
||
current = PdoExtension->Resources->Current;
|
||
for (i = 0; i < PCI_MAX_RANGE_COUNT; i++, current++) {
|
||
if (enabledMemory && (current->Type == CmResourceTypeMemory)) {
|
||
*resource++ = *current;
|
||
} else if (enabledIo && (current->Type == CmResourceTypePort)) {
|
||
*resource++ = *current;
|
||
}
|
||
}
|
||
|
||
if (PdoExtension->InterruptPin && (enabledMemory || enabledIo)) {
|
||
|
||
if (PdoExtension->AdjustedInterruptLine != 0 && PdoExtension->AdjustedInterruptLine != 0xFF) {
|
||
|
||
ASSERT(resource < lastResource);
|
||
|
||
resource->Type = CmResourceTypeInterrupt;
|
||
resource->ShareDisposition = CmResourceShareShared;
|
||
resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;;
|
||
resource->u.Interrupt.Level =
|
||
resource->u.Interrupt.Vector = PdoExtension->AdjustedInterruptLine;
|
||
resource->u.Interrupt.Affinity = (ULONG)-1;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Return the list and indicate success.
|
||
//
|
||
|
||
*ResourceList = cmResourceList;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
PciQueryDeviceRelations(
|
||
IN PPCI_FDO_EXTENSION FdoExtension,
|
||
IN OUT PDEVICE_RELATIONS *PDeviceRelations
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function builds a DEVICE_RELATIONS structure containing an array
|
||
of pointers to physical device objects for the devices of the specified
|
||
type on the bus indicated by FdoExtension.
|
||
|
||
Arguments:
|
||
|
||
FdoExtension - Pointer to the FDO Extension for the bus itself.
|
||
PDeviceRelations - Used to return the pointer to the allocated
|
||
device relations structure.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG pdoCount;
|
||
PPCI_PDO_EXTENSION childPdo;
|
||
PDEVICE_RELATIONS deviceRelations;
|
||
PDEVICE_RELATIONS oldDeviceRelations;
|
||
ULONG deviceRelationsSize;
|
||
PDEVICE_OBJECT physicalDeviceObject;
|
||
PDEVICE_OBJECT *object;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check that it reasonable to perform this operation now.
|
||
//
|
||
|
||
if (FdoExtension->DeviceState != PciStarted) {
|
||
|
||
ASSERT(FdoExtension->DeviceState == PciStarted);
|
||
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
//
|
||
// We're going to mess with the child pdo list - lock the state...
|
||
//
|
||
status = PCI_ACQUIRE_STATE_LOCK(FdoExtension);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Run down the existing child list and flag each child as
|
||
// not present. This flag will be cleared by the bus
|
||
// scan when (/if) the device is still present. Any pdo
|
||
// with the flag still present after the scan is no longer
|
||
// in the system (could be powered off).
|
||
//
|
||
|
||
childPdo = FdoExtension->ChildPdoList;
|
||
while (childPdo != NULL) {
|
||
childPdo->NotPresent = TRUE;
|
||
childPdo = childPdo->Next;
|
||
}
|
||
|
||
//
|
||
// Enumerate the bus.
|
||
//
|
||
|
||
status = PciScanBus(FdoExtension);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// First count the child PDOs
|
||
//
|
||
|
||
pdoCount = 0;
|
||
childPdo = FdoExtension->ChildPdoList;
|
||
while (childPdo != NULL) {
|
||
if (childPdo->NotPresent == FALSE) {
|
||
pdoCount++;
|
||
|
||
} else {
|
||
|
||
childPdo->ReportedMissing = TRUE;
|
||
#if DBG
|
||
PciDebugPrint(
|
||
PciDbgObnoxious,
|
||
"PCI - Old device (pdox) %08x not found on rescan.\n",
|
||
childPdo
|
||
);
|
||
#endif
|
||
|
||
}
|
||
childPdo = childPdo->Next;
|
||
}
|
||
|
||
|
||
//
|
||
// Calculate the amount of memory required to hold the DEVICE_RELATIONS
|
||
// structure along with the array
|
||
//
|
||
|
||
deviceRelationsSize = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
|
||
pdoCount * sizeof(PDEVICE_OBJECT);
|
||
|
||
//
|
||
// We could be either (a) creating the DEVICE_RELATIONS structure
|
||
// (list) here, or (b) adding our PDOs to an existing list.
|
||
//
|
||
|
||
oldDeviceRelations = *PDeviceRelations;
|
||
|
||
if (oldDeviceRelations != NULL) {
|
||
|
||
//
|
||
// List already exists, allow enough space for both the old
|
||
// and the new.
|
||
//
|
||
|
||
deviceRelationsSize += oldDeviceRelations->Count *
|
||
sizeof(PDEVICE_OBJECT);
|
||
}
|
||
|
||
deviceRelations = ExAllocatePool(NonPagedPool, deviceRelationsSize);
|
||
|
||
if (deviceRelations == NULL) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
deviceRelations->Count = 0;
|
||
|
||
if (oldDeviceRelations != NULL) {
|
||
|
||
//
|
||
// Copy and free the old list.
|
||
//
|
||
|
||
RtlCopyMemory(deviceRelations,
|
||
oldDeviceRelations,
|
||
FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
|
||
oldDeviceRelations->Count * sizeof(PDEVICE_OBJECT));
|
||
|
||
ExFreePool(oldDeviceRelations);
|
||
}
|
||
|
||
//
|
||
// Set object to point at the DeviceRelations list entry being
|
||
// added, walk our PDO list adding entries until we reach the
|
||
// end of the list.
|
||
//
|
||
|
||
object = &deviceRelations->Objects[deviceRelations->Count];
|
||
childPdo = FdoExtension->ChildPdoList;
|
||
|
||
PciDebugPrint(
|
||
PciDbgObnoxious,
|
||
"PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
|
||
FdoExtension,
|
||
FdoExtension->BaseBus
|
||
);
|
||
|
||
while (childPdo) {
|
||
|
||
PciDebugPrint(
|
||
PciDbgObnoxious,
|
||
" QDR PDO %08x (x %08x)%s\n",
|
||
childPdo->PhysicalDeviceObject,
|
||
childPdo,
|
||
childPdo->NotPresent ? " <Omitted, device flaged not present>" : ""
|
||
);
|
||
|
||
if (childPdo->NotPresent == FALSE) {
|
||
physicalDeviceObject = childPdo->PhysicalDeviceObject;
|
||
ObReferenceObject(physicalDeviceObject);
|
||
*object++ = physicalDeviceObject;
|
||
}
|
||
childPdo = childPdo->Next;
|
||
}
|
||
|
||
PciDebugPrint(
|
||
PciDbgObnoxious,
|
||
" QDR Total PDO count = %d (%d already in list)\n",
|
||
deviceRelations->Count + pdoCount,
|
||
deviceRelations->Count
|
||
);
|
||
|
||
deviceRelations->Count += pdoCount;
|
||
*PDeviceRelations = deviceRelations;
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
//
|
||
// Unlock
|
||
//
|
||
PCI_RELEASE_STATE_LOCK(FdoExtension);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PciQueryTargetDeviceRelations(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN OUT PDEVICE_RELATIONS *PDeviceRelations
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function builds a DEVICE_RELATIONS structure containing a
|
||
one element array of pointers to the device object for which
|
||
PdoExtension is the device extension.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pointer to the PDO Extension for the device itself.
|
||
PDeviceRelations - Used to return the pointer to the allocated
|
||
device relations structure.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_RELATIONS deviceRelations;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (*PDeviceRelations != NULL) {
|
||
|
||
//
|
||
// The caller kindly supplied a device relations structure,
|
||
// it's either too small or exactly the right size. Throw
|
||
// it away.
|
||
//
|
||
|
||
ExFreePool(*PDeviceRelations);
|
||
}
|
||
|
||
deviceRelations = ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS));
|
||
|
||
if (deviceRelations == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
deviceRelations->Count = 1;
|
||
deviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject;
|
||
*PDeviceRelations = deviceRelations;
|
||
|
||
ObReferenceObject(deviceRelations->Objects[0]);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
BOOLEAN
|
||
PcipIsSameDevice(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG CommonConfig
|
||
)
|
||
{
|
||
//
|
||
// Verify the data we got, was for the same device
|
||
//
|
||
|
||
if ((CommonConfig->VendorID != PdoExtension->VendorId) ||
|
||
(CommonConfig->DeviceID != PdoExtension->DeviceId) ||
|
||
(CommonConfig->RevisionID != PdoExtension->RevisionId)) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If the device has a subsystem ID make sure that's the same too.
|
||
//
|
||
|
||
if ((PciGetConfigurationType(CommonConfig) == PCI_DEVICE_TYPE) &&
|
||
(PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) &&
|
||
((PdoExtension->HackFlags & PCI_HACK_NO_SUBSYSTEM) == 0)&&
|
||
((PdoExtension->HackFlags & PCI_HACK_NO_SUBSYSTEM_AFTER_D3) == 0)) {
|
||
|
||
if ((PdoExtension->SubsystemVendorId !=
|
||
CommonConfig->u.type0.SubVendorID) ||
|
||
(PdoExtension->SubsystemId !=
|
||
CommonConfig->u.type0.SubSystemID)) {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
NTSTATUS
|
||
PciQueryEjectionRelations(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN OUT PDEVICE_RELATIONS *PDeviceRelations
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function builds a DEVICE_RELATIONS structure containing an array
|
||
of pointers to the device objects that would presumably leave if this
|
||
device were ejected. This is constructed from all the functions of a device.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pointer to the PDO Extension for the device itself.
|
||
PDeviceRelations - Used to return the pointer to the allocated
|
||
device relations structure.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the operation.
|
||
|
||
--*/
|
||
{
|
||
PPCI_FDO_EXTENSION fdoExtension;
|
||
PPCI_PDO_EXTENSION siblingExtension;
|
||
PDEVICE_RELATIONS ejectionRelations;
|
||
ULONG additionalNodes, relationCount;
|
||
|
||
additionalNodes = 0;
|
||
fdoExtension = PCI_PARENT_FDOX(PdoExtension);
|
||
|
||
//
|
||
// Search the child Pdo list.
|
||
//
|
||
|
||
ExAcquireFastMutex(&fdoExtension->ChildListMutex);
|
||
for ( siblingExtension = fdoExtension->ChildPdoList;
|
||
siblingExtension;
|
||
siblingExtension = siblingExtension->Next ) {
|
||
|
||
//
|
||
// Is this someone who should be in the list?
|
||
//
|
||
|
||
if ((siblingExtension != PdoExtension) &&
|
||
(!siblingExtension->NotPresent) &&
|
||
(siblingExtension->Slot.u.bits.DeviceNumber ==
|
||
PdoExtension->Slot.u.bits.DeviceNumber)) {
|
||
|
||
additionalNodes++;
|
||
}
|
||
}
|
||
|
||
if (!additionalNodes) {
|
||
|
||
ExReleaseFastMutex(&fdoExtension->ChildListMutex);
|
||
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
relationCount = (*PDeviceRelations) ? (*PDeviceRelations)->Count : 0;
|
||
|
||
ejectionRelations = (PDEVICE_RELATIONS) ExAllocatePool(
|
||
NonPagedPool,
|
||
sizeof(DEVICE_RELATIONS)+
|
||
(relationCount+additionalNodes-1)*sizeof(PDEVICE_OBJECT)
|
||
);
|
||
|
||
if (ejectionRelations == NULL) {
|
||
|
||
ExReleaseFastMutex(&fdoExtension->ChildListMutex);
|
||
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
if (*PDeviceRelations) {
|
||
|
||
RtlCopyMemory(
|
||
ejectionRelations,
|
||
*PDeviceRelations,
|
||
sizeof(DEVICE_RELATIONS)+
|
||
(relationCount-1)*sizeof(PDEVICE_OBJECT)
|
||
);
|
||
|
||
ExFreePool(*PDeviceRelations);
|
||
|
||
} else {
|
||
|
||
ejectionRelations->Count = 0;
|
||
}
|
||
|
||
for ( siblingExtension = fdoExtension->ChildPdoList;
|
||
siblingExtension;
|
||
siblingExtension = siblingExtension->Next ) {
|
||
|
||
//
|
||
// Is this someone who should be in the list?
|
||
//
|
||
|
||
if ((siblingExtension != PdoExtension) &&
|
||
(!siblingExtension->NotPresent) &&
|
||
(siblingExtension->Slot.u.bits.DeviceNumber ==
|
||
PdoExtension->Slot.u.bits.DeviceNumber)) {
|
||
|
||
ObReferenceObject(siblingExtension->PhysicalDeviceObject);
|
||
ejectionRelations->Objects[ejectionRelations->Count++] =
|
||
siblingExtension->PhysicalDeviceObject;
|
||
}
|
||
}
|
||
|
||
*PDeviceRelations = ejectionRelations;
|
||
|
||
ExReleaseFastMutex(&fdoExtension->ChildListMutex);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
BOOLEAN
|
||
PciIsSameDevice(
|
||
IN PPCI_PDO_EXTENSION PdoExtension
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PCI_COMMON_HEADER commonHeader;
|
||
|
||
//
|
||
// Get the devices pci data
|
||
//
|
||
|
||
PciGetConfigData(PdoExtension, &commonHeader);
|
||
|
||
return PcipIsSameDevice(PdoExtension, (PPCI_COMMON_CONFIG)&commonHeader);
|
||
}
|
||
|
||
BOOLEAN
|
||
PciComputeNewCurrentSettings(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PCM_RESOURCE_LIST ResourceList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine the new "device settings" based on the incoming
|
||
resource list.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pointer to the PDO Extension for the PDO.
|
||
ResourceList - The set of resources the device is to be configured
|
||
to use.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if the devices new settings are not the same as
|
||
the settings programmed into the device (FALSE otherwise).
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
CM_PARTIAL_RESOURCE_DESCRIPTOR newResources[PCI_MAX_RANGE_COUNT];
|
||
PCM_FULL_RESOURCE_DESCRIPTOR fullList;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR oldPartial;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR nextPartial;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR interruptResource = NULL;
|
||
BOOLEAN configurationChanged = FALSE;
|
||
ULONG listCount;
|
||
ULONG count;
|
||
ULONG bar;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We should never get a Count of anything other that 1 but if so deal with 0 gracefully
|
||
//
|
||
|
||
ASSERT(ResourceList == NULL || ResourceList->Count == 1);
|
||
|
||
if (ResourceList == NULL || ResourceList->Count == 0) {
|
||
|
||
//
|
||
// No incoming resource list,.. == no change unless we've previously
|
||
// decided we must update the hardware.
|
||
//
|
||
|
||
return PdoExtension->UpdateHardware;
|
||
}
|
||
|
||
#if DBG
|
||
|
||
PciDebugPrintCmResList(PciDbgSetRes, ResourceList);
|
||
|
||
#endif
|
||
|
||
//
|
||
// Produce a new "Current Resources Array" based on the
|
||
// incoming resource list and compare it to the devices
|
||
// current resource list. First init it to nothing.
|
||
//
|
||
|
||
for (count = 0; count < PCI_MAX_RANGE_COUNT; count++) {
|
||
newResources[count].Type = CmResourceTypeNull;
|
||
}
|
||
|
||
listCount = ResourceList->Count;
|
||
fullList = ResourceList->List;
|
||
|
||
//
|
||
// In the CM Resource list, IO will have copied the device
|
||
// private (extended) resource that we gave it in the
|
||
// resource requirements list handed in earlier.
|
||
//
|
||
// Find that BAR number. (Note: It's not there for interrupts).
|
||
//
|
||
|
||
while (listCount--) {
|
||
PCM_PARTIAL_RESOURCE_LIST partialList = &fullList->PartialResourceList;
|
||
ULONG drainPartial = 0;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR baseResource;
|
||
CM_PARTIAL_RESOURCE_DESCRIPTOR tempResource;
|
||
|
||
#if DBG
|
||
|
||
baseResource = NULL;
|
||
|
||
#endif
|
||
|
||
count = partialList->Count;
|
||
nextPartial = partialList->PartialDescriptors;
|
||
|
||
while (count--) {
|
||
|
||
partial = nextPartial;
|
||
nextPartial = PciNextPartialDescriptor(partial);
|
||
|
||
if (drainPartial != 0) {
|
||
|
||
//
|
||
// We encountered a device private indicating
|
||
// we should skip some number of descriptors.
|
||
//
|
||
|
||
drainPartial--;
|
||
continue;
|
||
}
|
||
|
||
|
||
switch (partial->Type) {
|
||
case CmResourceTypeInterrupt:
|
||
|
||
ASSERT(interruptResource == NULL); // once only please
|
||
ASSERT(partial->u.Interrupt.Level ==
|
||
partial->u.Interrupt.Vector);
|
||
ASSERT((partial->u.Interrupt.Level & ~0xff) == 0);
|
||
|
||
interruptResource = partial;
|
||
PdoExtension->AdjustedInterruptLine =
|
||
(UCHAR)partial->u.Interrupt.Level;
|
||
continue;
|
||
|
||
case CmResourceTypeMemory:
|
||
case CmResourceTypePort:
|
||
|
||
//
|
||
// Is this expected at this time?
|
||
//
|
||
|
||
ASSERT(baseResource == NULL);
|
||
|
||
baseResource = partial;
|
||
continue;
|
||
|
||
case CmResourceTypeDevicePrivate:
|
||
|
||
switch (partial->u.DevicePrivate.Data[0]) {
|
||
case PciPrivateIsaBar:
|
||
|
||
ASSERT(baseResource != NULL);
|
||
|
||
//
|
||
// This private resource tells us which BAR
|
||
// is associated with this base resource AND
|
||
// modifies the length of the base resource.
|
||
// It is created in conjunction with the set
|
||
// of partial resources that make up a larger
|
||
// resource on a bridge when the bridge's ISA
|
||
// mode bit is set.
|
||
//
|
||
// What's really coming down the pipe is the
|
||
// set of descriptors that describe the ISA
|
||
// holes in the range. These are 0x100 bytes
|
||
// every 0x400 bytes for the entire range.
|
||
//
|
||
// Make a copy of the base resource we have just
|
||
// seen. Its starting address is the start of
|
||
// the entire range. Adjust its length to the
|
||
// entire range.
|
||
//
|
||
|
||
tempResource = *baseResource;
|
||
|
||
//
|
||
// A little paranoia is sometimes a good thing.
|
||
// This can only happen on an IO resource which
|
||
// is the length of an ISA hole, ie 0x100 bytes.
|
||
//
|
||
|
||
ASSERT((tempResource.Type == CmResourceTypePort) &&
|
||
(tempResource.u.Generic.Length == 0x100)
|
||
);
|
||
|
||
//
|
||
// Excessive paranoia.
|
||
//
|
||
|
||
ASSERT((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
|
||
(PdoExtension->Dependent.type1.IsaBitSet == TRUE)
|
||
);
|
||
|
||
//
|
||
// Get the new length.
|
||
//
|
||
|
||
drainPartial = partial->u.DevicePrivate.Data[2];
|
||
tempResource.u.Generic.Length = drainPartial;
|
||
|
||
//
|
||
// Skip the remaining descriptors that make up this
|
||
// range.
|
||
//
|
||
|
||
drainPartial = (drainPartial / 0x400) - 1;
|
||
|
||
#if DBG
|
||
|
||
{
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR lastOne;
|
||
|
||
lastOne = baseResource + drainPartial + 1;
|
||
|
||
ASSERT(lastOne->Type == CmResourceTypePort);
|
||
ASSERT(lastOne->u.Generic.Length == 0x100);
|
||
ASSERT(lastOne->u.Generic.Start.QuadPart ==
|
||
(tempResource.u.Generic.Start.QuadPart +
|
||
tempResource.u.Generic.Length - 0x400)
|
||
);
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Finally, shift out pointer to our temp (adjusted)
|
||
// copy of the resource.
|
||
//
|
||
|
||
baseResource = &tempResource;
|
||
|
||
// fall thru.
|
||
|
||
case PciPrivateBar:
|
||
|
||
ASSERT(baseResource != NULL);
|
||
|
||
//
|
||
// This private resource tells us which BAR
|
||
// to is associated with this resource.
|
||
//
|
||
|
||
bar = partial->u.DevicePrivate.Data[1];
|
||
|
||
//
|
||
// Copy this descriptor into the new array.
|
||
//
|
||
|
||
newResources[bar] = *baseResource;
|
||
|
||
#if DBG
|
||
|
||
baseResource = NULL;
|
||
|
||
#endif
|
||
|
||
continue;
|
||
|
||
case PciPrivateSkipList:
|
||
|
||
ASSERT(baseResource == NULL);
|
||
|
||
//
|
||
// The remainder of this list is device
|
||
// specific stuff we can't change anyway.
|
||
//
|
||
|
||
drainPartial = partial->u.DevicePrivate.Data[1];
|
||
ASSERT(drainPartial); // sanity check
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
ASSERT(baseResource == NULL);
|
||
|
||
//
|
||
// Advance to next partial list.
|
||
//
|
||
|
||
fullList = (PCM_FULL_RESOURCE_DESCRIPTOR)partial;
|
||
}
|
||
|
||
//
|
||
// If we have no I/O or memory resources, then there is no need to look
|
||
// any further.
|
||
//
|
||
if (PdoExtension->Resources == NULL) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Ok, we now have a new list of resources in the same order as
|
||
// the "current" set. See if anything changed.
|
||
//
|
||
|
||
partial = newResources;
|
||
oldPartial = PdoExtension->Resources->Current;
|
||
|
||
#if DBG
|
||
|
||
if (PciDebug & PciDbgSetResChange) {
|
||
|
||
BOOLEAN dbgConfigurationChanged = FALSE;
|
||
|
||
for (count = 0;
|
||
count < PCI_MAX_RANGE_COUNT;
|
||
count++, partial++, oldPartial++) {
|
||
|
||
if ((partial->Type != oldPartial->Type) ||
|
||
((partial->Type != CmResourceTypeNull) &&
|
||
((partial->u.Generic.Start.QuadPart !=
|
||
oldPartial->u.Generic.Start.QuadPart) ||
|
||
(partial->u.Generic.Length != oldPartial->u.Generic.Length)))) {
|
||
|
||
//
|
||
// Devices settings have changed.
|
||
//
|
||
|
||
dbgConfigurationChanged = TRUE;
|
||
|
||
PciDebugPrint(
|
||
PciDbgAlways,
|
||
"PCI - PDO(b=0x%x, d=0x%x, f=0x%x) changing resource settings.\n",
|
||
PCI_PARENT_FDOX(PdoExtension)->BaseBus,
|
||
PdoExtension->Slot.u.bits.DeviceNumber,
|
||
PdoExtension->Slot.u.bits.FunctionNumber
|
||
);
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
partial = newResources;
|
||
oldPartial = PdoExtension->Resources->Current;
|
||
|
||
if (dbgConfigurationChanged == TRUE) {
|
||
PciDebugPrint(
|
||
PciDbgAlways,
|
||
"PCI - SetResources, old state, new state\n"
|
||
);
|
||
for (count = 0; count < PCI_MAX_RANGE_COUNT; count++) {
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR old = oldPartial + count;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR new = partial + count;
|
||
if ((old->Type == new->Type) &&
|
||
(new->Type == CmResourceTypeNull)) {
|
||
PciDebugPrint(
|
||
PciDbgAlways,
|
||
"00 <unused>\n"
|
||
);
|
||
continue;
|
||
}
|
||
PciDebugPrint(
|
||
PciDbgAlways,
|
||
"%02x %08x%08x %08x -> %02x %08x%08x %08x\n",
|
||
old->Type,
|
||
old->u.Generic.Start.HighPart,
|
||
old->u.Generic.Start.LowPart,
|
||
old->u.Generic.Length,
|
||
new->Type,
|
||
new->u.Generic.Start.HighPart,
|
||
new->u.Generic.Start.LowPart,
|
||
new->u.Generic.Length
|
||
);
|
||
ASSERT((old->Type == new->Type) ||
|
||
(old->Type == CmResourceTypeNull) ||
|
||
(new->Type == CmResourceTypeNull));
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
for (count = 0;
|
||
count < PCI_MAX_RANGE_COUNT;
|
||
count++, partial++, oldPartial++) {
|
||
|
||
//
|
||
// If the resource type changed, OR, if any of the resources
|
||
// settings changed (this latter only if type != NULL) ...
|
||
//
|
||
|
||
if ((partial->Type != oldPartial->Type) ||
|
||
((partial->Type != CmResourceTypeNull) &&
|
||
((partial->u.Generic.Start.QuadPart !=
|
||
oldPartial->u.Generic.Start.QuadPart) ||
|
||
(partial->u.Generic.Length != oldPartial->u.Generic.Length)))) {
|
||
|
||
//
|
||
// Devices settings have changed.
|
||
//
|
||
|
||
configurationChanged = TRUE;
|
||
|
||
#if DBG
|
||
|
||
if (oldPartial->Type != CmResourceTypeNull) {
|
||
PciDebugPrint(PciDbgSetResChange,
|
||
" Old range-\n");
|
||
PciDebugPrintPartialResource(PciDbgSetResChange, oldPartial);
|
||
} else {
|
||
PciDebugPrint(PciDbgSetResChange,
|
||
" Previously unset range\n");
|
||
}
|
||
PciDebugPrint(PciDbgSetResChange,
|
||
" changed to\n");
|
||
PciDebugPrintPartialResource(PciDbgSetResChange, partial);
|
||
|
||
#endif
|
||
|
||
//
|
||
// Copy the new setting into the "current" settings
|
||
// array. This will then be written to the h/w.
|
||
//
|
||
|
||
oldPartial->Type = partial->Type;
|
||
oldPartial->u.Generic = partial->u.Generic;
|
||
}
|
||
}
|
||
|
||
return configurationChanged || PdoExtension->UpdateHardware;
|
||
}
|
||
|
||
NTSTATUS
|
||
PciSetResources(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN BOOLEAN PowerOn,
|
||
IN BOOLEAN StartDeviceIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called to change a devices resource settings to those in the
|
||
incoming list.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pointer to the PDO Extension for the PDO.
|
||
Change - TRUE is the resources are to be written.
|
||
PowerOn - TRUE if the device is having power restored
|
||
and extraneous config space registers should
|
||
be restored. (PowerOn implies Change).
|
||
StartDeviceIrp - TRUE if this call is the result of a PNP START_DEVICE
|
||
IRP.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCI_COMMON_HEADER commonHeader;
|
||
PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader;
|
||
PCI_MSI_CAPABILITY msiCapability;
|
||
PPCI_FDO_EXTENSION fdoExtension = PCI_PARENT_FDOX(PdoExtension);
|
||
ULONG configType;
|
||
|
||
//
|
||
// Get the common configuration data.
|
||
//
|
||
// N.B. This is done using RAW access to config space so that
|
||
// (a) no pageable code is used, and
|
||
// (b) the actual contents of the interrupt line register is
|
||
// returned/written.
|
||
//
|
||
|
||
PciGetConfigData(PdoExtension, commonConfig);
|
||
|
||
if (!PcipIsSameDevice(PdoExtension, commonConfig)) {
|
||
ASSERTMSG("PCI Set resources - not same device", 0);
|
||
return STATUS_DEVICE_DOES_NOT_EXIST;
|
||
}
|
||
|
||
//
|
||
// If this is a host bridge, bail. We don't want to touch host bridge
|
||
// config space. This is a hack and should be fixed.
|
||
//
|
||
if (PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV
|
||
&& PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) {
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (PowerOn) {
|
||
|
||
//
|
||
// If this is an IDE controller then attempt to switch it to
|
||
// native mode
|
||
//
|
||
|
||
if (PdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR
|
||
&& PdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR) {
|
||
BOOLEAN switched;
|
||
|
||
switched = PciConfigureIdeController(PdoExtension, commonConfig, FALSE);
|
||
ASSERT(switched == PdoExtension->SwitchedIDEToNativeMode);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get part of the MSI capability structure for supported devices
|
||
//
|
||
|
||
//
|
||
// NOTE: This code is UNTESTED due to the unavailability of MSI devices
|
||
//
|
||
#if MSI_SUPPORTED
|
||
|
||
if(PdoExtension->CapableMSI && PdoExtension->MsiInfo.MessageAddress) {
|
||
|
||
//
|
||
// Make sure we have an offset for the Capability structure
|
||
//
|
||
|
||
ASSERT(PdoExtension->MsiInfo.CapabilityOffset);
|
||
|
||
//
|
||
// We just need the message control register for configuration purposes
|
||
//
|
||
|
||
PciReadDeviceConfig(
|
||
PdoExtension,
|
||
&(msiCapability.MessageControl),
|
||
PdoExtension->MsiInfo.CapabilityOffset +
|
||
FIELD_OFFSET(PCI_MSI_CAPABILITY, MessageControl),
|
||
sizeof(msiCapability.MessageControl)
|
||
);
|
||
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// If this device is marked as needing hot plug configuration and we have a
|
||
// clue of what to do...
|
||
//
|
||
|
||
if (PdoExtension->NeedsHotPlugConfiguration && fdoExtension->HotPlugParameters.Acquired) {
|
||
|
||
UCHAR readCacheLineSize;
|
||
USHORT newCmdBits = 0;
|
||
|
||
//
|
||
// Save away our new latency timer so it gets written out below
|
||
//
|
||
|
||
PdoExtension->SavedLatencyTimer = fdoExtension->HotPlugParameters.LatencyTimer;
|
||
|
||
|
||
|
||
PciDebugPrint(
|
||
PciDbgConfigParam,
|
||
"PCI - SetResources, PDOx %x current CacheLineSize is %x, Want %x\n",
|
||
PdoExtension,
|
||
(ULONG)commonConfig->CacheLineSize,
|
||
(ULONG)fdoExtension->HotPlugParameters.CacheLineSize
|
||
);
|
||
|
||
//
|
||
// Write out out suggested cache line size
|
||
//
|
||
|
||
PciWriteDeviceConfig(
|
||
PdoExtension,
|
||
&fdoExtension->HotPlugParameters.CacheLineSize,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, CacheLineSize),
|
||
sizeof(fdoExtension->HotPlugParameters.CacheLineSize)
|
||
);
|
||
|
||
//
|
||
// Check if the cache line size stuck which means the hardware liked it
|
||
//
|
||
|
||
PciReadDeviceConfig(
|
||
PdoExtension,
|
||
&readCacheLineSize,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, CacheLineSize),
|
||
sizeof(readCacheLineSize)
|
||
);
|
||
|
||
PciDebugPrint(
|
||
PciDbgConfigParam,
|
||
"PCI - SetResources, PDOx %x After write, CacheLineSize %x\n",
|
||
PdoExtension,
|
||
(ULONG)readCacheLineSize
|
||
);
|
||
|
||
if ((readCacheLineSize == fdoExtension->HotPlugParameters.CacheLineSize) &&
|
||
(readCacheLineSize != 0)) {
|
||
|
||
PciDebugPrint(
|
||
PciDbgConfigParam,
|
||
"PCI - SetResources, PDOx %x cache line size stuck, set MWI\n",
|
||
PdoExtension
|
||
);
|
||
|
||
//
|
||
// First stash this so that when we power manage the device we set
|
||
// it back correctly and that we want to set MWI...
|
||
//
|
||
|
||
PdoExtension->SavedCacheLineSize = fdoExtension->HotPlugParameters.CacheLineSize;
|
||
newCmdBits |= PCI_ENABLE_WRITE_AND_INVALIDATE;
|
||
|
||
//
|
||
// ISSUE-3/16/2000-andrewth
|
||
// If we get our PDO blown away (ie removed parent) then we forget that we need to
|
||
// set MWI...
|
||
//
|
||
|
||
} else {
|
||
PciDebugPrint(
|
||
PciDbgConfigParam,
|
||
"PCI - SetResources, PDOx %x cache line size non-sticky\n",
|
||
PdoExtension
|
||
);
|
||
}
|
||
|
||
//
|
||
// Now deal with SERR and PERR - abandon hope all ye who set these bits on
|
||
// flaky PC hardware...
|
||
//
|
||
|
||
if (fdoExtension->HotPlugParameters.EnableSERR) {
|
||
newCmdBits |= PCI_ENABLE_SERR;
|
||
}
|
||
|
||
if (fdoExtension->HotPlugParameters.EnablePERR) {
|
||
newCmdBits |= PCI_ENABLE_PARITY;
|
||
}
|
||
|
||
//
|
||
// Update the command enables so we write this out correctly after a PM op
|
||
//
|
||
|
||
PdoExtension->CommandEnables |= newCmdBits;
|
||
|
||
}
|
||
|
||
//
|
||
// Write the resources out to the hardware...
|
||
//
|
||
|
||
configType = PciGetConfigurationType(commonConfig);
|
||
|
||
PciInvalidateResourceInfoCache(PdoExtension);
|
||
|
||
//
|
||
// Call the device type dependent routine to set the new
|
||
// configuration.
|
||
//
|
||
|
||
PciConfigurators[configType].ChangeResourceSettings(
|
||
PdoExtension,
|
||
commonConfig
|
||
);
|
||
|
||
//
|
||
// If we explicitly wanted the hardware updated (UpdateHardware flag)
|
||
// this its done now...
|
||
//
|
||
|
||
PdoExtension->UpdateHardware = FALSE;
|
||
|
||
if (PowerOn) {
|
||
|
||
PciConfigurators[configType].ResetDevice(
|
||
PdoExtension,
|
||
commonConfig
|
||
);
|
||
|
||
//
|
||
// Restore InterruptLine register too. (InterruptLine is
|
||
// at same offset for header type 0, 1 and 2).
|
||
//
|
||
|
||
commonConfig->u.type0.InterruptLine =
|
||
PdoExtension->RawInterruptLine;
|
||
}
|
||
|
||
//
|
||
// Restore Maximum Latency and Cache Line Size.
|
||
//
|
||
|
||
#if DBG
|
||
|
||
if (commonConfig->LatencyTimer != PdoExtension->SavedLatencyTimer) {
|
||
PciDebugPrint(
|
||
PciDbgConfigParam,
|
||
"PCI (pdox %08x) changing latency from %02x to %02x.\n",
|
||
PdoExtension,
|
||
commonConfig->LatencyTimer,
|
||
PdoExtension->SavedLatencyTimer
|
||
);
|
||
}
|
||
|
||
if (commonConfig->CacheLineSize != PdoExtension->SavedCacheLineSize) {
|
||
PciDebugPrint(
|
||
PciDbgConfigParam,
|
||
"PCI (pdox %08x) changing cache line size from %02x to %02x.\n",
|
||
PdoExtension,
|
||
commonConfig->CacheLineSize,
|
||
PdoExtension->SavedCacheLineSize
|
||
);
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Restore random registers
|
||
//
|
||
|
||
commonConfig->LatencyTimer = PdoExtension->SavedLatencyTimer;
|
||
commonConfig->CacheLineSize = PdoExtension->SavedCacheLineSize;
|
||
commonConfig->u.type0.InterruptLine = PdoExtension->RawInterruptLine;
|
||
|
||
//
|
||
// Restore the command register we remember in the power down case
|
||
//
|
||
|
||
commonConfig->Command = PdoExtension->CommandEnables;
|
||
|
||
//
|
||
// Disable the device while we write the rest of its config
|
||
// space. Also, don't write any non-zero value to it's status
|
||
// register.
|
||
//
|
||
|
||
if ((PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND) == 0) {
|
||
commonConfig->Command &= ~(PCI_ENABLE_IO_SPACE |
|
||
PCI_ENABLE_MEMORY_SPACE |
|
||
PCI_ENABLE_BUS_MASTER |
|
||
PCI_ENABLE_WRITE_AND_INVALIDATE);
|
||
}
|
||
commonConfig->Status = 0;
|
||
|
||
//
|
||
// Call out and apply any hacks necessary
|
||
//
|
||
|
||
PciApplyHacks(
|
||
PCI_PARENT_FDOX(PdoExtension),
|
||
commonConfig,
|
||
PdoExtension->Slot,
|
||
EnumStartDevice,
|
||
PdoExtension
|
||
);
|
||
|
||
//
|
||
// Write it out to the hardware
|
||
//
|
||
|
||
PciSetConfigData(PdoExtension, commonConfig);
|
||
|
||
#if MSI_SUPPORTED
|
||
|
||
//
|
||
// Program MSI devices with their new message interrupt resources
|
||
//
|
||
// NOTE: This code is UNTESTED due to the unavailability of MSI devices
|
||
//
|
||
|
||
if (PdoExtension->CapableMSI && PdoExtension->MsiInfo.MessageAddress) {
|
||
|
||
PciDebugPrint(
|
||
PciDbgInformative,
|
||
"PCI: Device %08x being reprogrammed for MSI.\n",
|
||
PdoExtension->PhysicalDeviceObject
|
||
);
|
||
|
||
//
|
||
// Set the proper resources in the MSI capability structure
|
||
// and write them to Hardware.
|
||
//
|
||
// Message Address
|
||
//
|
||
|
||
ASSERT(PdoExtension->MsiInfo.MessageAddress);
|
||
msiCapability.MessageAddress.Raw = PdoExtension->MsiInfo.MessageAddress;
|
||
|
||
//
|
||
// Must be DWORD aligned address
|
||
//
|
||
ASSERT(msiCapability.MessageAddress.Register.Reserved == 0);
|
||
|
||
//
|
||
// Write the Message Address register in Hardware.
|
||
//
|
||
|
||
PciWriteDeviceConfig(
|
||
PdoExtension,
|
||
&(msiCapability.MessageAddress),
|
||
PdoExtension->MsiInfo.CapabilityOffset +
|
||
FIELD_OFFSET(PCI_MSI_CAPABILITY,MessageAddress),
|
||
sizeof(msiCapability.MessageAddress)
|
||
);
|
||
|
||
//
|
||
// Message Upper Address
|
||
//
|
||
|
||
if(msiCapability.MessageControl.CapableOf64Bits) {
|
||
|
||
// All the APICs we know live below 4GB so their upper address component
|
||
// is always 0.
|
||
msiCapability.Data.Bit64.MessageUpperAddress = 0;
|
||
|
||
|
||
PciWriteDeviceConfig(
|
||
PdoExtension,
|
||
&(msiCapability.Data.Bit64.MessageUpperAddress),
|
||
PdoExtension->MsiInfo.CapabilityOffset +
|
||
FIELD_OFFSET(PCI_MSI_CAPABILITY,Data.Bit64.MessageUpperAddress),
|
||
sizeof(msiCapability.Data.Bit64.MessageUpperAddress)
|
||
);
|
||
|
||
//
|
||
// Message Data
|
||
//
|
||
|
||
msiCapability.Data.Bit64.MessageData = PdoExtension->MsiInfo.MessageData;
|
||
|
||
PciWriteDeviceConfig(
|
||
PdoExtension,
|
||
&(msiCapability.Data.Bit64.MessageData),
|
||
PdoExtension->MsiInfo.CapabilityOffset +
|
||
FIELD_OFFSET(PCI_MSI_CAPABILITY,Data.Bit64.MessageData),
|
||
sizeof(msiCapability.Data.Bit64.MessageData)
|
||
);
|
||
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Message Data
|
||
//
|
||
|
||
msiCapability.Data.Bit32.MessageData = PdoExtension->MsiInfo.MessageData;
|
||
|
||
//
|
||
// Write to hardware.
|
||
//
|
||
|
||
PciWriteDeviceConfig(
|
||
PdoExtension,
|
||
&(msiCapability.Data.Bit32.MessageData),
|
||
PdoExtension->MsiInfo.CapabilityOffset +
|
||
FIELD_OFFSET(PCI_MSI_CAPABILITY,Data.Bit32.MessageData),
|
||
sizeof(msiCapability.Data.Bit32.MessageData)
|
||
);
|
||
}
|
||
|
||
// # of Messages granted
|
||
//
|
||
// We have the arbiter allocate only 1 interrupt for us, so we
|
||
// are allocating just 1 message.
|
||
//
|
||
|
||
msiCapability.MessageControl.MultipleMessageEnable = 1;
|
||
|
||
//
|
||
// Enable bit
|
||
//
|
||
|
||
msiCapability.MessageControl.MSIEnable = 1;
|
||
|
||
//
|
||
// Write the message control register to Hardware
|
||
//
|
||
|
||
PciWriteDeviceConfig(
|
||
PdoExtension,
|
||
&(msiCapability.MessageControl),
|
||
PdoExtension->MsiInfo.CapabilityOffset +
|
||
FIELD_OFFSET(PCI_MSI_CAPABILITY,MessageControl),
|
||
sizeof(msiCapability.MessageControl)
|
||
);
|
||
|
||
}
|
||
|
||
#endif // MSI_SUPPORTED
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Update our concept of the RawInterruptLine (either as read from
|
||
// the h/w or restored by us). Note: InterruptLine is at the same
|
||
// offset for types 0, 1 and 2 PCI config space headers.
|
||
//
|
||
|
||
PdoExtension->RawInterruptLine = commonConfig->u.type0.InterruptLine;
|
||
|
||
//
|
||
// New values written to config space, now re-enable the
|
||
// device (as indicated in the CommandEnables)
|
||
//
|
||
|
||
PciDecodeEnable(PdoExtension, TRUE, &PdoExtension->CommandEnables);
|
||
|
||
//
|
||
// If it needed configuration its done by now!
|
||
//
|
||
|
||
PdoExtension->NeedsHotPlugConfiguration = FALSE;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
PciGetEnhancedCapabilities(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN PPCI_COMMON_CONFIG Config
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the appropriate fields in the Pdo extension relating
|
||
to capabilities and power. If no power management registers are available
|
||
the power state is based off of the decode fields. PCI bus reset code
|
||
depends on this, and to prevent excessive resets this routine should only
|
||
be called immediately after a new PDO is created.
|
||
|
||
NOTE:
|
||
We should rename this function to something with GetInitialState in the
|
||
title and so it can't be confused with IRP_MN_QUERY_CAPABILITIES.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - Pointer to the PDO Extension for the PDO.
|
||
Config - Pointer to the common portion of the config space.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
ULONG configType;
|
||
UCHAR capPtr = 0;
|
||
PCI_MSI_CAPABILITY msi;
|
||
UCHAR msicapptr;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If this function supports a capabilities list, record the
|
||
// capabilities pointer.
|
||
//
|
||
|
||
PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified;
|
||
|
||
if (!(Config->Status & PCI_STATUS_CAPABILITIES_LIST)) {
|
||
|
||
//
|
||
// If we don't have a capability bit we can't do MSI or Power management
|
||
//
|
||
|
||
PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
|
||
PdoExtension->CapabilitiesPtr = 0;
|
||
#if MSI_SUPPORTED
|
||
PdoExtension->CapableMSI = FALSE;
|
||
#endif
|
||
goto PciGetCapabilitiesExit;
|
||
}
|
||
|
||
switch (PciGetConfigurationType(Config)) {
|
||
case PCI_DEVICE_TYPE:
|
||
capPtr = Config->u.type0.CapabilitiesPtr;
|
||
break;
|
||
case PCI_BRIDGE_TYPE:
|
||
capPtr = Config->u.type1.CapabilitiesPtr;
|
||
break;
|
||
case PCI_CARDBUS_BRIDGE_TYPE:
|
||
capPtr = Config->u.type2.CapabilitiesPtr;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Capabilities pointers are a new feature so we verify a
|
||
// little that the h/w folks built the right thing. Must
|
||
// be a DWORD offset, must not point into common header.
|
||
// (Zero is allowable, means not used).
|
||
//
|
||
|
||
if (capPtr) {
|
||
if (((capPtr & 0x3) == 0) && (capPtr >= PCI_COMMON_HDR_LENGTH)) {
|
||
PdoExtension->CapabilitiesPtr = capPtr;
|
||
} else {
|
||
ASSERT(((capPtr & 0x3) == 0) && (capPtr >= PCI_COMMON_HDR_LENGTH));
|
||
}
|
||
}
|
||
|
||
#if MSI_SUPPORTED
|
||
|
||
//
|
||
// Search for the MSI capability structure
|
||
// Just get the structure header since we don't look at the structure here.
|
||
//
|
||
|
||
msicapptr = PciReadDeviceCapability(
|
||
PdoExtension,
|
||
PdoExtension->CapabilitiesPtr,
|
||
PCI_CAPABILITY_ID_MSI,
|
||
&msi,
|
||
sizeof(PCI_CAPABILITIES_HEADER)
|
||
);
|
||
|
||
if (msicapptr != 0) {
|
||
|
||
PciDebugPrint(PciDbgInformative,"PCI: MSI Capability Found for device %p\n",
|
||
PdoExtension->PhysicalDeviceObject);
|
||
|
||
//
|
||
// Cache the capability address in the PDO extension
|
||
// and initialize MSI routing info.
|
||
//
|
||
|
||
PdoExtension->MsiInfo.CapabilityOffset = msicapptr;
|
||
PdoExtension->MsiInfo.MessageAddress = 0;
|
||
PdoExtension->MsiInfo.MessageData = 0;
|
||
|
||
//
|
||
// Mark this PDO as capable of MSI.
|
||
//
|
||
|
||
PdoExtension->CapableMSI = TRUE;
|
||
}
|
||
|
||
#endif // MSI_SUPPORTED
|
||
|
||
//
|
||
// See if the device is Power Management capable.
|
||
//
|
||
|
||
if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) {
|
||
|
||
PCI_PM_CAPABILITY pm;
|
||
UCHAR pmcapptr;
|
||
|
||
pmcapptr = PciReadDeviceCapability(
|
||
PdoExtension,
|
||
PdoExtension->CapabilitiesPtr,
|
||
PCI_CAPABILITY_ID_POWER_MANAGEMENT,
|
||
&pm,
|
||
sizeof(pm)
|
||
);
|
||
|
||
if (pmcapptr != 0) {
|
||
|
||
//
|
||
// Found a PM capability structure.
|
||
//
|
||
// Select "most powered off state" this device can
|
||
// issue a PME from.
|
||
//
|
||
|
||
DEVICE_POWER_STATE ds = PowerDeviceUnspecified;
|
||
|
||
if (pm.PMC.Capabilities.Support.PMED0 ) ds = PowerDeviceD0;
|
||
if (pm.PMC.Capabilities.Support.PMED1 ) ds = PowerDeviceD1;
|
||
if (pm.PMC.Capabilities.Support.PMED2 ) ds = PowerDeviceD2;
|
||
if (pm.PMC.Capabilities.Support.PMED3Hot ) ds = PowerDeviceD3;
|
||
if (pm.PMC.Capabilities.Support.PMED3Cold) ds = PowerDeviceD3;
|
||
|
||
PdoExtension->PowerState.DeviceWakeLevel = ds;
|
||
|
||
//
|
||
// Record the current power state.
|
||
// Note: D0 = 0, thru D3 = 3, convert to
|
||
// PowerDeviceD0 thru PowerDeviceD3. It's
|
||
// only a two bit field (in h/w) so no other
|
||
// values are possible.
|
||
//
|
||
|
||
PdoExtension->PowerState.CurrentDeviceState =
|
||
pm.PMCSR.ControlStatus.PowerState +
|
||
PowerDeviceD0;
|
||
|
||
//
|
||
// Remember the power capabilities
|
||
//
|
||
|
||
PdoExtension->PowerCapabilities = pm.PMC.Capabilities;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Device has capabilities but not Power
|
||
// Management capabilities. Cheat a little
|
||
// by pretending the registry flag is set
|
||
// that says this. (This speeds saves us
|
||
// hunting through the h/w next time we
|
||
// want to look at the PM caps).
|
||
//
|
||
|
||
PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
|
||
}
|
||
}
|
||
|
||
PciGetCapabilitiesExit:
|
||
if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS) {
|
||
|
||
//
|
||
// In this case we only support D0 and D3. D3 is defined as decodes
|
||
// off.
|
||
//
|
||
if ((Config->Command & (PCI_ENABLE_IO_SPACE |
|
||
PCI_ENABLE_MEMORY_SPACE |
|
||
PCI_ENABLE_BUS_MASTER)) != 0) {
|
||
|
||
PdoExtension->PowerState.CurrentDeviceState = PowerDeviceD0;
|
||
|
||
} else {
|
||
|
||
PdoExtension->PowerState.CurrentDeviceState = PowerDeviceD3;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PciScanHibernatedBus(
|
||
IN PPCI_FDO_EXTENSION FdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Scan the bus (detailed in FdoExtension) for any new PCI devices
|
||
that were not there when we hibernated and turn them off if doing so seems
|
||
like a good idea.
|
||
|
||
Arguments:
|
||
|
||
FdoExtension - Our extension for the PCI bus functional device object.
|
||
|
||
Return Value:
|
||
|
||
NT status.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PCI_COMMON_HEADER commonHeader;
|
||
PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader;
|
||
PDEVICE_OBJECT physicalDeviceObject;
|
||
PPCI_PDO_EXTENSION pdoExtension;
|
||
PCI_SLOT_NUMBER slot;
|
||
ULONG deviceNumber;
|
||
ULONG functionNumber;
|
||
USHORT SubVendorID, SubSystemID;
|
||
BOOLEAN isRoot;
|
||
ULONGLONG hackFlags;
|
||
ULONG maximumDevices;
|
||
BOOLEAN newDevices = FALSE;
|
||
|
||
PciDebugPrint(PciDbgPrattling,
|
||
"PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
|
||
FdoExtension,
|
||
FdoExtension->BaseBus);
|
||
|
||
isRoot = PCI_IS_ROOT_FDO(FdoExtension);
|
||
|
||
//
|
||
// Examine each possible device on this bus.
|
||
//
|
||
|
||
maximumDevices = PCI_MAX_DEVICES;
|
||
if (!isRoot) {
|
||
|
||
//
|
||
// Examine the PDO extension for the bridge device and see
|
||
// if it's broken.
|
||
//
|
||
|
||
pdoExtension = (PPCI_PDO_EXTENSION)
|
||
FdoExtension->PhysicalDeviceObject->DeviceExtension;
|
||
|
||
ASSERT_PCI_PDO_EXTENSION(pdoExtension);
|
||
|
||
if (pdoExtension->HackFlags & PCI_HACK_ONE_CHILD) {
|
||
maximumDevices = 1;
|
||
}
|
||
}
|
||
|
||
slot.u.AsULONG = 0;
|
||
|
||
for (deviceNumber = 0;
|
||
deviceNumber < maximumDevices;
|
||
deviceNumber++) {
|
||
|
||
slot.u.bits.DeviceNumber = deviceNumber;
|
||
|
||
//
|
||
// Examine each possible function on this device.
|
||
// N.B. Early out if function 0 not present.
|
||
//
|
||
|
||
for (functionNumber = 0;
|
||
functionNumber < PCI_MAX_FUNCTION;
|
||
functionNumber++) {
|
||
|
||
slot.u.bits.FunctionNumber = functionNumber;
|
||
|
||
PciReadSlotConfig(FdoExtension,
|
||
slot,
|
||
commonConfig,
|
||
0,
|
||
sizeof(commonConfig->VendorID)
|
||
);
|
||
|
||
|
||
if (commonConfig->VendorID == 0xFFFF ||
|
||
commonConfig->VendorID == 0) {
|
||
|
||
if (functionNumber == 0) {
|
||
|
||
//
|
||
// Didn't get any data on function zero of this
|
||
// device, no point in checking other functions.
|
||
//
|
||
|
||
break;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Check next function.
|
||
//
|
||
|
||
continue;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// We have a device so get the rest of its config space
|
||
//
|
||
|
||
PciReadSlotConfig(FdoExtension,
|
||
slot,
|
||
&commonConfig->DeviceID,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceID),
|
||
sizeof(PCI_COMMON_HEADER)
|
||
- sizeof(commonConfig->VendorID)
|
||
);
|
||
|
||
//
|
||
// Munge the config space if necessary
|
||
//
|
||
|
||
PciApplyHacks(FdoExtension,
|
||
commonConfig,
|
||
slot,
|
||
EnumHackConfigSpace,
|
||
NULL
|
||
);
|
||
|
||
|
||
if ((PciGetConfigurationType(commonConfig) == PCI_DEVICE_TYPE) &&
|
||
(commonConfig->BaseClass != PCI_CLASS_BRIDGE_DEV)) {
|
||
SubVendorID = commonConfig->u.type0.SubVendorID;
|
||
SubSystemID = commonConfig->u.type0.SubSystemID;
|
||
} else {
|
||
SubVendorID = 0;
|
||
SubSystemID = 0;
|
||
}
|
||
|
||
hackFlags = PciGetHackFlags(commonConfig->VendorID,
|
||
commonConfig->DeviceID,
|
||
SubVendorID,
|
||
SubSystemID,
|
||
commonConfig->RevisionID
|
||
);
|
||
|
||
if (PciSkipThisFunction(commonConfig,
|
||
slot,
|
||
EnumBusScan,
|
||
hackFlags)) {
|
||
//
|
||
// Skip this function
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
|
||
//
|
||
// In case we are rescanning the bus, check to see if
|
||
// a PDO for this device already exists as a child of
|
||
// the FDO.
|
||
//
|
||
|
||
pdoExtension = PciFindPdoByFunction(
|
||
FdoExtension,
|
||
slot,
|
||
commonConfig);
|
||
|
||
if (pdoExtension == NULL) {
|
||
|
||
newDevices = TRUE;
|
||
|
||
//
|
||
// This is a new device disable it if we can
|
||
//
|
||
|
||
if (PciCanDisableDecodes(NULL, commonConfig, hackFlags, 0)) {
|
||
|
||
commonConfig->Command &= ~(PCI_ENABLE_IO_SPACE |
|
||
PCI_ENABLE_MEMORY_SPACE |
|
||
PCI_ENABLE_BUS_MASTER);
|
||
|
||
PciWriteSlotConfig(FdoExtension,
|
||
slot,
|
||
&commonConfig->Command,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
|
||
sizeof(commonConfig->Command)
|
||
);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We already know about this device so leave well alone!
|
||
//
|
||
|
||
}
|
||
|
||
if ( (functionNumber == 0) &&
|
||
!PCI_MULTIFUNCTION_DEVICE(commonConfig) ) {
|
||
|
||
//
|
||
// Not a multifunction adapter, skip other functions on
|
||
// this device.
|
||
//
|
||
|
||
break;
|
||
}
|
||
} // function loop
|
||
} // device loop
|
||
|
||
//
|
||
// Tell pnp we found some new devices
|
||
//
|
||
|
||
if (newDevices) {
|
||
IoInvalidateDeviceRelations(FdoExtension->PhysicalDeviceObject, BusRelations);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PciConfigureIdeController(
|
||
IN PPCI_PDO_EXTENSION PdoExtension,
|
||
IN OUT PPCI_COMMON_CONFIG Config,
|
||
IN BOOLEAN TurnOffAllNative
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If this is an IDE contoller that can be switched to native mode
|
||
and its not already there, we change the programming interface
|
||
(yes PCI 2.x does say its read only) and check if it sticks.
|
||
|
||
Assuming all went well we update the Config to reflect the change.
|
||
|
||
Arguments:
|
||
|
||
PdoExtension - PDO for the IDE controller to be switched
|
||
|
||
Config - Config header for said device
|
||
|
||
TurnOffAllNative - If TRUE indicates that we are calling this from
|
||
the initial bus scan and so we should turn
|
||
thid native capable IDE controllers. If
|
||
FALSE we should turn off only if the we have
|
||
accessed the PCI_NATIVE_IDE_INTERFACE.
|
||
|
||
Return Value:
|
||
|
||
TRUE if we sucessfully switched, FALSE otherwise
|
||
|
||
Note:
|
||
|
||
We support three styles of PCI IDE controller:
|
||
- Compatible mode controllers that consume 2 ISA interrupts
|
||
and decode fixed legacy resources, together with an optional
|
||
relocateable bus master register
|
||
- Native mode controller which uses all 5 bars and the PCI
|
||
interrupt for both channels
|
||
- Controllers which can be switched between modes.
|
||
We do NOT support running one channel in native mode and one in
|
||
compatible mode.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
|
||
BOOLEAN primaryChangeable, secondaryChangeable, primaryNative, secondaryNative, switched = FALSE;
|
||
UCHAR progIf, tempProgIf;
|
||
USHORT command;
|
||
|
||
primaryChangeable = BITS_SET(Config->ProgIf, PCI_IDE_PRIMARY_MODE_CHANGEABLE);
|
||
secondaryChangeable = BITS_SET(Config->ProgIf, PCI_IDE_SECONDARY_MODE_CHANGEABLE);
|
||
primaryNative = BITS_SET(Config->ProgIf, PCI_IDE_PRIMARY_NATIVE_MODE);
|
||
secondaryNative = BITS_SET(Config->ProgIf, PCI_IDE_SECONDARY_NATIVE_MODE);
|
||
|
||
//
|
||
// Don't touch controllers we don't support - leave ATAPI to deal with it!
|
||
//
|
||
|
||
if ((primaryNative != secondaryNative)
|
||
|| (primaryChangeable != secondaryChangeable)) {
|
||
|
||
DbgPrint("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!",
|
||
PdoExtension->VendorId,
|
||
PdoExtension->DeviceId
|
||
);
|
||
|
||
return FALSE;
|
||
|
||
} else if (primaryNative && secondaryNative
|
||
&& (TurnOffAllNative || PdoExtension->IoSpaceUnderNativeIdeControl)) {
|
||
|
||
//
|
||
// For a fully native mode controller turn off the IO decode.
|
||
// In recent controllers MSFT has requested that this prevent
|
||
// the PCI interrupt from being asserted to close a race condition
|
||
// that can occur if an IDE device interrupts before the IDE driver
|
||
// has been loaded on the PCI device. This is not a issue for
|
||
// compatible mode controllers because they use edge triggered
|
||
// interrupts that can be dismissed as spurious at the interrupt
|
||
// controller, unlike the shared, level triggered, PCI interrups
|
||
// of native mode.
|
||
//
|
||
// Once loaded and having connected its interrupt the IDE driver
|
||
// will renable IO space access.
|
||
//
|
||
// We only do this during the initial bus scan or if the IDE driver
|
||
// has requested it through the PCI_NATIVE_IDE_INTERFACE. This is
|
||
// to avoid not enabling IoSpace for 3rd party native IDE controllers
|
||
// with their own drivers.
|
||
//
|
||
|
||
PciGetCommandRegister(PdoExtension, &command);
|
||
command &= ~PCI_ENABLE_IO_SPACE;
|
||
PciSetCommandRegister(PdoExtension, command);
|
||
Config->Command = command;
|
||
|
||
} else if (primaryChangeable && secondaryChangeable
|
||
&& (PdoExtension->BIOSAllowsIDESwitchToNativeMode
|
||
&& !(PdoExtension->HackFlags & PCI_HACK_BAD_NATIVE_IDE))) {
|
||
|
||
//
|
||
// If we aren't already in native mode, the controller can change modes
|
||
// and the bios is ammenable then do so...
|
||
//
|
||
|
||
PciDecodeEnable(PdoExtension, FALSE, NULL);
|
||
PciGetCommandRegister(PdoExtension, &Config->Command);
|
||
|
||
progIf = Config->ProgIf | (PCI_IDE_PRIMARY_NATIVE_MODE
|
||
| PCI_IDE_SECONDARY_NATIVE_MODE);
|
||
|
||
PciWriteDeviceConfig(PdoExtension,
|
||
&progIf,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, ProgIf),
|
||
sizeof(progIf)
|
||
);
|
||
//
|
||
// Check if it stuck
|
||
//
|
||
PciReadDeviceConfig(PdoExtension,
|
||
&tempProgIf,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, ProgIf),
|
||
sizeof(tempProgIf)
|
||
);
|
||
|
||
if (tempProgIf == progIf) {
|
||
//
|
||
// If it stuck, remember we did this
|
||
//
|
||
Config->ProgIf = progIf;
|
||
PdoExtension->ProgIf = progIf;
|
||
switched = TRUE;
|
||
|
||
//
|
||
// Zero the first 4 bars in the config space because they might have
|
||
// bogus values in them...
|
||
//
|
||
|
||
RtlZeroMemory(Config->u.type0.BaseAddresses,
|
||
4 * sizeof(Config->u.type0.BaseAddresses[0]));
|
||
|
||
PciWriteDeviceConfig(PdoExtension,
|
||
&Config->u.type0.BaseAddresses,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses),
|
||
4 * sizeof(Config->u.type0.BaseAddresses[0])
|
||
);
|
||
|
||
//
|
||
// Read back what stuck into the config which we are going to generate
|
||
// requirements from
|
||
//
|
||
|
||
PciReadDeviceConfig(PdoExtension,
|
||
&Config->u.type0.BaseAddresses,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses),
|
||
4 * sizeof(Config->u.type0.BaseAddresses[0])
|
||
);
|
||
|
||
PciReadDeviceConfig(PdoExtension,
|
||
&Config->u.type0.InterruptPin,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.InterruptPin),
|
||
sizeof(Config->u.type0.InterruptPin)
|
||
);
|
||
} else {
|
||
|
||
DbgPrint("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!",
|
||
Config->VendorID,
|
||
Config->DeviceID
|
||
);
|
||
}
|
||
}
|
||
|
||
return switched;
|
||
}
|
||
|