1986 lines
59 KiB
C
1986 lines
59 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
pmpcibus.c
|
||
|
||
Abstract:
|
||
|
||
Implements simplified PCI configuration
|
||
read and write functions for use in
|
||
an ACPI HAL.
|
||
|
||
Author:
|
||
|
||
Jake Oshins (jakeo) 1-Dec-1997
|
||
Chris Hyser (chrish@fc.hp.com) 23-Jun-1997
|
||
Neal Vu (neal.vu@intel.com) 11-Jul-2000
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "pci.h"
|
||
#include "pcip.h"
|
||
|
||
#define MAX(a, b) \
|
||
((a) > (b) ? (a) : (b))
|
||
|
||
#define MIN(a, b) \
|
||
((a) < (b) ? (a) : (b))
|
||
|
||
NTSTATUS
|
||
HalpSearchForPciDebuggingDevice(
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice,
|
||
IN ULONG StartBusNumber,
|
||
IN ULONG EndBusNumber,
|
||
IN ULONG MinMem,
|
||
IN ULONG MaxMem,
|
||
IN USHORT MinIo,
|
||
IN USHORT MaxIo,
|
||
IN BOOLEAN ConfigureBridges
|
||
);
|
||
|
||
PCIPBUSDATA HalpFakePciBusData = {
|
||
{
|
||
PCI_DATA_TAG,//Tag
|
||
PCI_DATA_VERSION,//Version
|
||
(PciReadWriteConfig)HalpReadPCIConfig,//ReadConfig
|
||
(PciReadWriteConfig) HalpWritePCIConfig,//WriteConfig
|
||
(PciPin2Line)HalpPCIPin2ISALine,//Pin2Line
|
||
(PciLine2Pin)HalpPCIISALine2Pin,//Line2Pin
|
||
{0},//ParentSlot
|
||
NULL,NULL,NULL,NULL//Reserved[4]
|
||
},
|
||
{0},//Config
|
||
PCI_MAX_DEVICES,//MaxDevice
|
||
};
|
||
|
||
BUS_HANDLER HalpFakePciBusHandler = {
|
||
BUS_HANDLER_VERSION,//Version
|
||
PCIBus,//InterfaceType
|
||
PCIConfiguration,//ConfigurationType
|
||
0,//BusNumber
|
||
NULL,//DeviceObject
|
||
NULL,//ParentHandler
|
||
(PPCIBUSDATA)&HalpFakePciBusData,//BusData
|
||
0,//DeviceControlExtensionSize
|
||
NULL,//BusAddresses
|
||
{0},//Reserved[4]
|
||
(PGETSETBUSDATA)HalpGetPCIData,//GetBusData
|
||
(PGETSETBUSDATA)HalpSetPCIData,//SetBusData
|
||
NULL,//AdjustResourceList
|
||
(PASSIGNSLOTRESOURCES)HalpAssignPCISlotResources,//AssignSlotResources
|
||
NULL,//GetInterruptVector
|
||
NULL,//TranslateBusAddress
|
||
};
|
||
|
||
ULONG HalpMinPciBus = 0;
|
||
ULONG HalpMaxPciBus = 0;
|
||
|
||
#define MAX_DEBUGGING_DEVICES_SUPPORTED 2
|
||
PCI_TYPE1_CFG_CYCLE_BITS HalpPciDebuggingDevice[MAX_DEBUGGING_DEVICES_SUPPORTED] = {0};
|
||
|
||
extern BOOLEAN HalpDoingCrashDump;
|
||
extern KSPIN_LOCK HalpPCIConfigLock;
|
||
|
||
PVOID
|
||
HalpGetAcpiTablePhase0(
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
||
IN ULONG Signature
|
||
);
|
||
|
||
VOID
|
||
HalpFindFreeResourceLimits(
|
||
IN ULONG Bus,
|
||
IN OUT ULONG *MinIo,
|
||
IN OUT ULONG *MaxIo,
|
||
IN OUT ULONG *MinMem,
|
||
IN OUT ULONG *MaxMem,
|
||
IN OUT ULONG *MinBus,
|
||
IN OUT ULONG *MaxBus
|
||
);
|
||
|
||
NTSTATUS
|
||
HalpSetupUnconfiguredDebuggingDevice(
|
||
IN ULONG Bus,
|
||
IN ULONG Slot,
|
||
IN ULONG IoMin,
|
||
IN ULONG IoMax,
|
||
IN ULONG MemMin,
|
||
IN ULONG MemMax,
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice
|
||
);
|
||
|
||
NTSTATUS
|
||
HalpConfigurePciBridge(
|
||
IN PDEBUG_DEVICE_DESCRIPTOR PciDevice,
|
||
IN ULONG Bus,
|
||
IN ULONG Slot,
|
||
IN ULONG IoMin,
|
||
IN ULONG IoMax,
|
||
IN ULONG MemMin,
|
||
IN ULONG MemMax,
|
||
IN ULONG BusMin,
|
||
IN ULONG BusMax,
|
||
IN OUT PPCI_COMMON_CONFIG PciData
|
||
);
|
||
|
||
VOID
|
||
HalpUnconfigurePciBridge(
|
||
IN ULONG Bus,
|
||
IN ULONG Slot
|
||
);
|
||
|
||
VOID
|
||
HalpRegisterKdSupportFunctions(
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
);
|
||
|
||
VOID
|
||
HalpRegisterPciDebuggingDeviceInfo(
|
||
VOID
|
||
);
|
||
|
||
ULONG
|
||
HalpPhase0GetPciDataByOffset (
|
||
ULONG BusNumber,
|
||
ULONG SlotNumber,
|
||
PVOID Buffer,
|
||
ULONG Offset,
|
||
ULONG Length
|
||
);
|
||
|
||
ULONG
|
||
HalpPhase0SetPciDataByOffset (
|
||
ULONG BusNumber,
|
||
ULONG SlotNumber,
|
||
PVOID Buffer,
|
||
ULONG Offset,
|
||
ULONG Length
|
||
);
|
||
|
||
NTSTATUS
|
||
HalpReleasePciDeviceForDebugging(
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice
|
||
);
|
||
|
||
NTSTATUS
|
||
HalpSetupPciDeviceForDebugging(
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock, OPTIONAL
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,HalpInitializePciBus)
|
||
#pragma alloc_text(INIT,HalpRegisterKdSupportFunctions)
|
||
#pragma alloc_text(INIT,HalpRegisterPciDebuggingDeviceInfo)
|
||
#pragma alloc_text(PAGELK,HalpConfigurePciBridge)
|
||
#pragma alloc_text(PAGELK,HalpFindFreeResourceLimits)
|
||
#pragma alloc_text(PAGELK,HalpPhase0GetPciDataByOffset)
|
||
#pragma alloc_text(PAGELK,HalpPhase0SetPciDataByOffset)
|
||
#pragma alloc_text(PAGELK,HalpReleasePciDeviceForDebugging)
|
||
#pragma alloc_text(PAGELK,HalpSearchForPciDebuggingDevice)
|
||
#pragma alloc_text(PAGELK,HalpSetupPciDeviceForDebugging)
|
||
#pragma alloc_text(PAGELK,HalpSetupUnconfiguredDebuggingDevice)
|
||
#pragma alloc_text(PAGELK,HalpUnconfigurePciBridge)
|
||
#endif
|
||
|
||
VOID
|
||
HalpInitializePciBus (
|
||
VOID
|
||
)
|
||
{
|
||
PPCIPBUSDATA BusData;
|
||
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
||
|
||
//
|
||
// Initialize spinlock for synchronizing access to PCI space
|
||
//
|
||
|
||
KeInitializeSpinLock (&HalpPCIConfigLock);
|
||
|
||
RtlZeroMemory(&HalpFakePciBusHandler, sizeof(BUS_HANDLER));
|
||
|
||
HalpFakePciBusHandler.Version = BUS_HANDLER_VERSION;
|
||
HalpFakePciBusHandler.InterfaceType = PCIBus;
|
||
HalpFakePciBusHandler.ConfigurationType = PCIConfiguration;
|
||
|
||
//
|
||
// Fill in PCI handlers
|
||
//
|
||
|
||
HalpFakePciBusHandler.GetBusData = (PGETSETBUSDATA) HalpGetPCIData;
|
||
HalpFakePciBusHandler.SetBusData = (PGETSETBUSDATA) HalpSetPCIData;
|
||
HalpFakePciBusHandler.AssignSlotResources = (PASSIGNSLOTRESOURCES) HalpAssignPCISlotResources;
|
||
HalpFakePciBusHandler.BusData = &HalpFakePciBusData;
|
||
|
||
BusData = (PPCIPBUSDATA) HalpFakePciBusHandler.BusData;
|
||
|
||
//
|
||
// Fill in common PCI data
|
||
//
|
||
|
||
BusData->CommonData.Tag = PCI_DATA_TAG;
|
||
BusData->CommonData.Version = PCI_DATA_VERSION;
|
||
BusData->CommonData.ReadConfig = (PciReadWriteConfig) HalpReadPCIConfig;
|
||
BusData->CommonData.WriteConfig = (PciReadWriteConfig) HalpWritePCIConfig;
|
||
BusData->CommonData.Pin2Line = (PciPin2Line) HalpPCIPin2ISALine;
|
||
BusData->CommonData.Line2Pin = (PciLine2Pin) HalpPCIISALine2Pin;
|
||
|
||
//
|
||
// Set defaults
|
||
//
|
||
|
||
BusData->MaxDevice = PCI_MAX_DEVICES;
|
||
|
||
//
|
||
// There used to be a switch statment on HwType which installed
|
||
// different handlers based on PCI Configuration Type. This
|
||
// has been removed since SAL is always used for IA64.
|
||
//
|
||
}
|
||
|
||
|
||
ULONG
|
||
HaliPciInterfaceReadConfig(
|
||
IN PVOID Context,
|
||
IN UCHAR BusOffset,
|
||
IN ULONG Slot,
|
||
IN PVOID Buffer,
|
||
IN ULONG Offset,
|
||
IN ULONG Length
|
||
)
|
||
{
|
||
PCI_SLOT_NUMBER slotNum;
|
||
BUS_HANDLER busHand;
|
||
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
slotNum.u.AsULONG = Slot;
|
||
|
||
//
|
||
// Fake a bus handler.
|
||
//
|
||
|
||
RtlCopyMemory(&busHand, &HalpFakePciBusHandler, sizeof(BUS_HANDLER));
|
||
|
||
//
|
||
// Calculate the right bus number.
|
||
//
|
||
|
||
busHand.BusNumber = BusOffset;
|
||
|
||
HalpReadPCIConfig(&busHand,
|
||
slotNum,
|
||
Buffer,
|
||
Offset,
|
||
Length
|
||
);
|
||
|
||
//
|
||
// This is a hack. The legacy HAL interfaces need to be able
|
||
// to distinguish between busses that exist and busses that
|
||
// don't. And many users of the legacy interfaces implicitly
|
||
// assume that PCI busses are tightly packed. (i.e. All busses
|
||
// between the lowest numbered one and the highest numbered one
|
||
// exist.) So here we are keeping track of the highest numbered
|
||
// bus that we have seen so far.
|
||
//
|
||
|
||
if ((Length >= 2) &&
|
||
(((PPCI_COMMON_CONFIG)Buffer)->VendorID != PCI_INVALID_VENDORID)) {
|
||
|
||
//
|
||
// This is a valid device.
|
||
//
|
||
|
||
if (busHand.BusNumber > HalpMaxPciBus) {
|
||
|
||
//
|
||
// This is the highest numbered bus we have
|
||
// yet seen.
|
||
//
|
||
|
||
HalpMaxPciBus = busHand.BusNumber;
|
||
}
|
||
}
|
||
|
||
return Length;
|
||
}
|
||
|
||
ULONG
|
||
HaliPciInterfaceWriteConfig(
|
||
IN PVOID Context,
|
||
IN UCHAR BusOffset,
|
||
IN ULONG Slot,
|
||
IN PVOID Buffer,
|
||
IN ULONG Offset,
|
||
IN ULONG Length
|
||
)
|
||
{
|
||
PCI_SLOT_NUMBER slotNum;
|
||
BUS_HANDLER busHand;
|
||
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
slotNum.u.AsULONG = Slot;
|
||
|
||
//
|
||
// Fake a bus handler.
|
||
//
|
||
|
||
RtlCopyMemory(&busHand, &HalpFakePciBusHandler, sizeof(BUS_HANDLER));
|
||
|
||
//
|
||
// Calculate the right bus number.
|
||
//
|
||
|
||
busHand.BusNumber = BusOffset;
|
||
|
||
HalpWritePCIConfig(&busHand,
|
||
slotNum,
|
||
Buffer,
|
||
Offset,
|
||
Length
|
||
);
|
||
|
||
return Length;
|
||
}
|
||
|
||
VOID
|
||
HalpPCIPin2ISALine (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciData
|
||
)
|
||
/*++
|
||
|
||
This function maps the device's InterruptPin to an InterruptLine
|
||
value.
|
||
|
||
On the current PC implementations, the bios has already filled in
|
||
InterruptLine as it's ISA value and there's no portable way to
|
||
change it.
|
||
|
||
On a DBG build we adjust InterruptLine just to ensure driver's
|
||
don't connect to it without translating it on the PCI bus.
|
||
|
||
--*/
|
||
{
|
||
if (!PciData->u.type0.InterruptPin) {
|
||
return ;
|
||
}
|
||
HalDebugPrint(( HAL_INFO, "HAL: HalpPCIPin2ISALine - non-zero InterruptPin value\n" ));
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
HalpPCIISALine2Pin (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciNewData,
|
||
IN PPCI_COMMON_CONFIG PciOldData
|
||
)
|
||
/*++
|
||
|
||
This functions maps the device's InterruptLine to it's
|
||
device specific InterruptPin value.
|
||
|
||
On the current PC implementations, this information is
|
||
fixed by the BIOS. Just make sure the value isn't being
|
||
editted since PCI doesn't tell us how to dynically
|
||
connect the interrupt.
|
||
|
||
--*/
|
||
{
|
||
if (!PciNewData->u.type0.InterruptPin) {
|
||
return ;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpSetMaxLegacyPciBusNumber(
|
||
IN ULONG BusNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine bumps the Legacy PCI bus maximum up to whatever
|
||
is passed in. This may be necessary because the ACPI driver
|
||
needs to run a configuration cycle to a PCI device before the
|
||
PCI driver loads. This happens mostly in the context of a
|
||
_REG method.
|
||
|
||
Arguments:
|
||
|
||
BusNumber - max PCI bus number
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
{
|
||
if (BusNumber > HalpMaxPciBus) {
|
||
HalpMaxPciBus = BusNumber;
|
||
}
|
||
}
|
||
|
||
ULONG
|
||
HalpPhase0SetPciDataByOffset (
|
||
ULONG BusNumber,
|
||
ULONG SlotNumber,
|
||
PVOID Buffer,
|
||
ULONG Offset,
|
||
ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine writes to PCI configuration space prior to bus handler
|
||
installation.
|
||
|
||
Arguments:
|
||
|
||
BusNumber PCI Bus Number. This is the 8 bit BUS Number which is
|
||
bits 23-16 of the Configuration Address. In support of
|
||
multiple top level busses, the upper 24 bits of this
|
||
argument will supply the index into the table of
|
||
configuration address registers.
|
||
SlotNumber PCI Slot Number, 8 bits composed of the 5 bit device
|
||
number (bits 15-11 of the configuration address) and
|
||
the 3 bit function number (10-8).
|
||
Buffer Address of source data.
|
||
Offset Number of bytes to skip from base of PCI config area.
|
||
Length Number of bytes to write
|
||
|
||
Return Value:
|
||
|
||
Returns length of data written.
|
||
|
||
Notes:
|
||
|
||
Caller is responsible for acquiring any necessary PCI config
|
||
spinlocks.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCI_TYPE1_CFG_BITS ConfigAddress;
|
||
ULONG ReturnLength;
|
||
PCI_SLOT_NUMBER slot;
|
||
PUCHAR Bfr = (PUCHAR)Buffer;
|
||
|
||
ASSERT(!(Offset & ~0xff));
|
||
ASSERT(Length);
|
||
ASSERT((Offset + Length) <= 256);
|
||
|
||
if ( Length + Offset > 256 ) {
|
||
if ( Offset > 256 ) {
|
||
return 0;
|
||
}
|
||
Length = 256 - Offset;
|
||
}
|
||
|
||
ReturnLength = Length;
|
||
slot.u.AsULONG = SlotNumber;
|
||
|
||
ConfigAddress.u.bits.BusNumber = BusNumber;
|
||
ConfigAddress.u.bits.DeviceNumber = slot.u.bits.DeviceNumber;
|
||
ConfigAddress.u.bits.FunctionNumber = slot.u.bits.FunctionNumber;
|
||
ConfigAddress.u.bits.RegisterNumber = (Offset & 0xfc) >> 2;
|
||
ConfigAddress.u.bits.Enable = TRUE;
|
||
|
||
if ( Offset & 0x3 ) {
|
||
//
|
||
// Access begins at a non-register boundary in the config
|
||
// space. We need to read the register containing the data
|
||
// and rewrite only the changed data. (I wonder if this
|
||
// ever really happens?)
|
||
//
|
||
ULONG SubOffset = Offset & 0x3;
|
||
ULONG SubLength = 4 - SubOffset;
|
||
union {
|
||
ULONG All;
|
||
UCHAR Bytes[4];
|
||
} Tmp;
|
||
|
||
if ( SubLength > Length ) {
|
||
SubLength = Length;
|
||
}
|
||
|
||
//
|
||
// Adjust Length (remaining) and (new) Offset by amount covered
|
||
// in this first word.
|
||
//
|
||
Length -= SubLength;
|
||
Offset += SubLength;
|
||
|
||
//
|
||
// Get the first word (register), replace only those bytes that
|
||
// need to be changed, then write the whole thing back out again.
|
||
//
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_ADDR_PORT, ConfigAddress.u.AsULONG);
|
||
Tmp.All = READ_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT);
|
||
|
||
while ( SubLength-- ) {
|
||
Tmp.Bytes[SubOffset++] = *Bfr++;
|
||
}
|
||
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, Tmp.All);
|
||
|
||
//
|
||
// Aim ConfigAddressRegister at the next word (register).
|
||
//
|
||
ConfigAddress.u.bits.RegisterNumber++;
|
||
}
|
||
|
||
//
|
||
// Do the majority of the transfer 4 bytes at a time.
|
||
//
|
||
while ( Length > sizeof(ULONG) ) {
|
||
ULONG Tmp = *(PULONG)Bfr;
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_ADDR_PORT, ConfigAddress.u.AsULONG);
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, Tmp);
|
||
ConfigAddress.u.bits.RegisterNumber++;
|
||
Bfr += sizeof(ULONG);
|
||
Length -= sizeof(ULONG);
|
||
|
||
}
|
||
|
||
//
|
||
// Do bytes in last register.
|
||
//
|
||
if ( Length ) {
|
||
union {
|
||
ULONG All;
|
||
UCHAR Bytes[4];
|
||
} Tmp;
|
||
ULONG i = 0;
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_ADDR_PORT, ConfigAddress.u.AsULONG);
|
||
Tmp.All = READ_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT);
|
||
|
||
while ( Length-- ) {
|
||
Tmp.Bytes[i++] = *(PUCHAR)Bfr++;
|
||
}
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT, Tmp.All);
|
||
}
|
||
|
||
return ReturnLength;
|
||
}
|
||
|
||
ULONG
|
||
HalpPhase0GetPciDataByOffset (
|
||
ULONG BusNumber,
|
||
ULONG SlotNumber,
|
||
PVOID Buffer,
|
||
ULONG Offset,
|
||
ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reads PCI config space prior to bus handlder installation.
|
||
|
||
Arguments:
|
||
|
||
BusNumber PCI Bus Number. This is the 8 bit BUS Number which is
|
||
bits 23-16 of the Configuration Address. In support of
|
||
multiple top level busses, the upper 24 bits of this
|
||
argument will supply the index into the table of
|
||
configuration address registers.
|
||
SlotNumber PCI Slot Number, 8 bits composed of the 5 bit device
|
||
number (bits 15-11 of the configuration address) and
|
||
the 3 bit function number (10-8).
|
||
Buffer Address of source data.
|
||
Offset Number of bytes to skip from base of PCI config area.
|
||
Length Number of bytes to write
|
||
|
||
Return Value:
|
||
|
||
Amount of data read.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCI_TYPE1_CFG_BITS ConfigAddress;
|
||
PCI_TYPE1_CFG_BITS ConfigAddressTemp;
|
||
ULONG ReturnLength;
|
||
ULONG i;
|
||
PCI_SLOT_NUMBER slot;
|
||
union {
|
||
ULONG All;
|
||
UCHAR Bytes[4];
|
||
} Tmp;
|
||
|
||
ASSERT(!(Offset & ~0xff));
|
||
ASSERT(Length);
|
||
ASSERT((Offset + Length) <= 256);
|
||
|
||
if ( Length + Offset > 256 ) {
|
||
if ( Offset > 256 ) {
|
||
return 0;
|
||
}
|
||
Length = 256 - Offset;
|
||
}
|
||
|
||
ReturnLength = Length;
|
||
slot.u.AsULONG = SlotNumber;
|
||
|
||
ConfigAddress.u.bits.BusNumber = BusNumber;
|
||
ConfigAddress.u.bits.DeviceNumber = slot.u.bits.DeviceNumber;
|
||
ConfigAddress.u.bits.FunctionNumber = slot.u.bits.FunctionNumber;
|
||
ConfigAddress.u.bits.RegisterNumber = (Offset & 0xfc) >> 2;
|
||
ConfigAddress.u.bits.Enable = TRUE;
|
||
|
||
//
|
||
// If we are being asked to read data when function != 0, check
|
||
// first to see if this device decares itself as a multi-function
|
||
// device. If it doesn't, don't do this read.
|
||
//
|
||
if (ConfigAddress.u.bits.FunctionNumber != 0) {
|
||
|
||
ConfigAddressTemp.u.bits.RegisterNumber = 3; // contains header type
|
||
ConfigAddressTemp.u.bits.FunctionNumber = 0; // look at base package
|
||
ConfigAddressTemp.u.bits.DeviceNumber = ConfigAddress.u.bits.DeviceNumber;
|
||
ConfigAddressTemp.u.bits.BusNumber = ConfigAddress.u.bits.BusNumber;
|
||
ConfigAddressTemp.u.bits.Enable = TRUE;
|
||
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_ADDR_PORT, ConfigAddressTemp.u.AsULONG);
|
||
Tmp.All = READ_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT);
|
||
|
||
if (!(Tmp.Bytes[2] & 0x80)) { // if the Header type field's multi-function bit is not set
|
||
|
||
for (i = 0; i < Length; i++) {
|
||
*((PUCHAR)Buffer)++ = 0xff; // Make this read as if the device isn't populated
|
||
}
|
||
|
||
return Length;
|
||
}
|
||
}
|
||
|
||
i = Offset & 0x3;
|
||
|
||
while ( Length ) {
|
||
WRITE_PORT_ULONG((PULONG)PCI_TYPE1_ADDR_PORT, ConfigAddress.u.AsULONG);
|
||
Tmp.All = READ_PORT_ULONG((PULONG)PCI_TYPE1_DATA_PORT);
|
||
while ( (i < 4) && Length) {
|
||
*((PUCHAR)Buffer)++ = Tmp.Bytes[i];
|
||
i++;
|
||
Length--;
|
||
}
|
||
i = 0;
|
||
ConfigAddress.u.bits.RegisterNumber++;
|
||
}
|
||
return ReturnLength;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpSetupPciDeviceForDebugging(
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock, OPTIONAL
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds and initializes a PCI device to be
|
||
used for communicating with a debugger.
|
||
|
||
The caller fills in as much of DEBUG_DEVICE_DESCRIPTOR
|
||
as it cares to, filling unused fields with (-1).
|
||
|
||
This routine attempts to find a matching PCI device. It
|
||
matches first based on Bus and Slot, if the caller has
|
||
provided them. Then it matches on VendorID/DeviceID, if
|
||
the caller has provided them. Last, it matches on
|
||
BaseClass/SubClass.
|
||
|
||
This routine will fill in any unused fields in the structure
|
||
so that the caller can know specifically which PCI
|
||
device matched the criteria.
|
||
|
||
If the matching PCI device is not enabled, or it is
|
||
behind a PCI to PCI bridge that is not enabled, this
|
||
routine makes a best-effort attempt to find a safe
|
||
configuration that allows the device (and possibly bridges)
|
||
to function, and enables them.
|
||
|
||
If the PCI device implements memory mapped Base Address
|
||
registers, this function will create a virtual to physical
|
||
mapping for the memory ranges implied by the Base Address
|
||
Registers and fill in the TranslatedAddress field with
|
||
virtual pointers to the bases of the ranges. It will then
|
||
fill in the Type field with CmResourceTypeMemory. And
|
||
the Valid field with be TRUE.
|
||
|
||
If the PCI device implements I/O port Base Address registers,
|
||
this function will put the translated port address in
|
||
TranslatedAddress, setting the Type field to CmResourceTypePort
|
||
and the Valid field to TRUE.
|
||
|
||
If the PCI device does not implement a specific Base Address
|
||
Register, the Valid field will be FALSE.
|
||
|
||
Arguments:
|
||
|
||
PciDevice - Structure indicating the device
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if the device is configured and usable.
|
||
|
||
STATUS_NO_MORE_MATCHES if no device matched the criteria.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES if the memory requirements
|
||
couldn't be met.
|
||
|
||
STATUS_UNSUCCESSFUL if the routine failed for other reasons.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PCI_SLOT_NUMBER slot;
|
||
ULONG i, j;
|
||
ULONG maxPhys;
|
||
|
||
status = HalpSearchForPciDebuggingDevice(
|
||
PciDevice,
|
||
0,
|
||
0xff,
|
||
0x10000000,
|
||
0xfc000000,
|
||
0x1000,
|
||
0xffff,
|
||
FALSE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// We didn't find the device using a conservative
|
||
// search. Try a more invasive one.
|
||
//
|
||
|
||
status = HalpSearchForPciDebuggingDevice(
|
||
PciDevice,
|
||
0,
|
||
0xff,
|
||
0x10000000,
|
||
0xfc000000,
|
||
0x1000,
|
||
0xffff,
|
||
TRUE);
|
||
}
|
||
|
||
//
|
||
// Record the Bus/Dev/Func so that we can stuff it in the
|
||
// registry later.
|
||
//
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
slot.u.AsULONG = PciDevice->Slot;
|
||
|
||
for (i = 0;
|
||
i < MAX_DEBUGGING_DEVICES_SUPPORTED;
|
||
i++) {
|
||
|
||
if ((HalpPciDebuggingDevice[i].u.bits.Reserved1 == TRUE) &&
|
||
(HalpPciDebuggingDevice[i].u.bits.FunctionNumber ==
|
||
slot.u.bits.FunctionNumber) &&
|
||
(HalpPciDebuggingDevice[i].u.bits.DeviceNumber ==
|
||
slot.u.bits.DeviceNumber) &&
|
||
(HalpPciDebuggingDevice[i].u.bits.BusNumber ==
|
||
PciDevice->Bus)) {
|
||
|
||
//
|
||
// This device has already been set up for
|
||
// debugging. Thus we should refuse to set
|
||
// it up again.
|
||
//
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
for (i = 0;
|
||
i < MAX_DEBUGGING_DEVICES_SUPPORTED;
|
||
i++) {
|
||
|
||
if (HalpPciDebuggingDevice[i].u.bits.Reserved1 == FALSE) {
|
||
|
||
//
|
||
// This slot is available.
|
||
//
|
||
|
||
HalpPciDebuggingDevice[i].u.bits.FunctionNumber =
|
||
slot.u.bits.FunctionNumber;
|
||
HalpPciDebuggingDevice[i].u.bits.DeviceNumber =
|
||
slot.u.bits.DeviceNumber;
|
||
HalpPciDebuggingDevice[i].u.bits.BusNumber = PciDevice->Bus;
|
||
HalpPciDebuggingDevice[i].u.bits.Reserved1 = TRUE;
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check to see if the caller wants any memory.
|
||
//
|
||
|
||
if (PciDevice->Memory.Length != 0) {
|
||
|
||
if (!LoaderBlock) {
|
||
return STATUS_INVALID_PARAMETER_1;
|
||
}
|
||
|
||
if (PciDevice->Memory.MaxEnd.QuadPart == 0) {
|
||
PciDevice->Memory.MaxEnd.QuadPart = -1;
|
||
}
|
||
|
||
maxPhys = PciDevice->Memory.MaxEnd.HighPart ? 0xffffffff : PciDevice->Memory.MaxEnd.LowPart;
|
||
maxPhys -= PciDevice->Memory.Length;
|
||
|
||
//
|
||
// The HAL APIs will always return page-aligned
|
||
// memory. So ignore Aligned for now.
|
||
//
|
||
|
||
maxPhys = (ULONG)(ULONG_PTR)PAGE_ALIGN(maxPhys);
|
||
maxPhys += ADDRESS_AND_SIZE_TO_SPAN_PAGES(maxPhys, PciDevice->Memory.Length);
|
||
|
||
PciDevice->Memory.Start.HighPart = 0;
|
||
PciDevice->Memory.Start.LowPart = (ULONG)(ULONG_PTR)
|
||
HalpAllocPhysicalMemory(LoaderBlock,
|
||
maxPhys,
|
||
ADDRESS_AND_SIZE_TO_SPAN_PAGES(maxPhys, PciDevice->Memory.Length),
|
||
FALSE);
|
||
|
||
if (!PciDevice->Memory.Start.LowPart) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
PciDevice->Memory.VirtualAddress =
|
||
HalpMapPhysicalMemory(PciDevice->Memory.Start,
|
||
ADDRESS_AND_SIZE_TO_SPAN_PAGES(maxPhys, PciDevice->Memory.Length),
|
||
MmNonCached);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
HalpFindFreeResourceLimits(
|
||
IN ULONG Bus,
|
||
IN OUT ULONG *MinIo,
|
||
IN OUT ULONG *MaxIo,
|
||
IN OUT ULONG *MinMem,
|
||
IN OUT ULONG *MaxMem,
|
||
IN OUT ULONG *MinBus,
|
||
IN OUT ULONG *MaxBus
|
||
)
|
||
{
|
||
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
|
||
PPCI_COMMON_CONFIG pciData;
|
||
UCHAR bus, dev, func, bytesRead;
|
||
PCI_SLOT_NUMBER pciSlot, targetSlot;
|
||
ULONG newMinMem, newMaxMem;
|
||
ULONG newMinIo, newMaxIo;
|
||
ULONG newMinBus, newMaxBus;
|
||
UCHAR barNo;
|
||
|
||
pciData = (PPCI_COMMON_CONFIG)buffer;
|
||
pciSlot.u.AsULONG = 0;
|
||
newMinMem = *MinMem;
|
||
newMaxMem = *MaxMem;
|
||
newMinIo = *MinIo;
|
||
newMaxIo = *MaxIo;
|
||
newMinBus = *MinBus;
|
||
newMaxBus = *MaxBus;
|
||
|
||
for (dev = 0; dev < PCI_MAX_DEVICES; dev++) {
|
||
for (func = 0; func < PCI_MAX_FUNCTION; func++) {
|
||
|
||
pciSlot.u.bits.DeviceNumber = dev;
|
||
pciSlot.u.bits.FunctionNumber = func;
|
||
|
||
|
||
bytesRead = (UCHAR)HalpPhase0GetPciDataByOffset(Bus,
|
||
pciSlot.u.AsULONG,
|
||
pciData,
|
||
0,
|
||
PCI_COMMON_HDR_LENGTH);
|
||
|
||
if (bytesRead == 0) continue;
|
||
|
||
if (pciData->VendorID != PCI_INVALID_VENDORID) {
|
||
|
||
switch (PCI_CONFIGURATION_TYPE(pciData)) {
|
||
case PCI_DEVICE_TYPE:
|
||
|
||
//
|
||
// While we scan across the bus, keep track
|
||
// of the minimum decoder values that we've seen.
|
||
// This will be used if we have to configure the
|
||
// device. This relies on the fact that most BIOSes
|
||
// assign addresses from the top down.
|
||
//
|
||
|
||
for (barNo = 0; barNo < PCI_TYPE0_ADDRESSES; barNo++) {
|
||
|
||
if (pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_SPACE) {
|
||
|
||
if (pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_ADDRESS_MASK) {
|
||
|
||
//
|
||
// This BAR is implemented
|
||
//
|
||
|
||
if ((pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_ADDRESS_MASK) <
|
||
((newMaxIo + newMinIo) / 2)) {
|
||
|
||
//
|
||
// This BAR is at the bottom of the range.
|
||
// Bump up the min.
|
||
//
|
||
|
||
newMinIo = (USHORT)MAX (newMinIo,
|
||
(pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_ADDRESS_MASK) + 0x100);
|
||
|
||
} else {
|
||
|
||
//
|
||
// This BAR is not at the bottom of the range.
|
||
// Bump down the max.
|
||
//
|
||
|
||
newMaxIo = (USHORT)MIN (newMaxIo,
|
||
pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_ADDRESS_MASK);
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
if (pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_MEMORY_ADDRESS_MASK) {
|
||
|
||
//
|
||
// The BAR is populated.
|
||
//
|
||
|
||
if ((pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_MEMORY_ADDRESS_MASK) <
|
||
((newMaxMem / 2) + (newMinMem / 2))) {
|
||
|
||
//
|
||
// This BAR is at the bottom of the range.
|
||
// Bump up the min.
|
||
//
|
||
|
||
newMinMem = MAX (newMinMem,
|
||
(pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_MEMORY_ADDRESS_MASK) + 0x10000);
|
||
|
||
} else {
|
||
|
||
//
|
||
// This BAR is not at the bottom of the range.
|
||
// Bump down the max.
|
||
//
|
||
|
||
newMaxMem = MIN (newMaxMem,
|
||
(pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_MEMORY_ADDRESS_MASK));
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
case PCI_CARDBUS_BRIDGE_TYPE:
|
||
case PCI_BRIDGE_TYPE:
|
||
|
||
{
|
||
ULONG bridgeMemMin = 0, bridgeMemMax = 0;
|
||
USHORT bridgeIoMin, bridgeIoMax;
|
||
|
||
if ((pciData->u.type1.SecondaryBus != 0) &&
|
||
(pciData->u.type1.SubordinateBus !=0) &&
|
||
(pciData->Command & PCI_ENABLE_MEMORY_SPACE) &&
|
||
(pciData->Command & PCI_ENABLE_IO_SPACE)) {
|
||
|
||
bridgeMemMin = PciBridgeMemory2Base(pciData->u.type1.MemoryBase);
|
||
bridgeMemMax = PciBridgeMemory2Limit(pciData->u.type1.MemoryLimit);
|
||
bridgeIoMin = (USHORT)PciBridgeIO2Base(pciData->u.type1.IOBase, 0);
|
||
bridgeIoMax = (USHORT)PciBridgeIO2Limit(pciData->u.type1.IOLimit, 0);
|
||
|
||
//
|
||
// Keep track of address space allocation.
|
||
//
|
||
|
||
if (bridgeIoMin > ((newMaxIo + newMinIo) / 2)) {
|
||
newMaxIo = MIN(newMaxIo, bridgeIoMin);
|
||
}
|
||
|
||
if (bridgeIoMax < ((newMaxIo + newMinIo) / 2)) {
|
||
newMinIo = MAX(newMinIo, bridgeIoMax) + 1;
|
||
}
|
||
|
||
if (bridgeMemMin > ((newMaxMem + newMinMem) / 2)) {
|
||
newMaxMem = MIN(newMaxMem, bridgeMemMin);
|
||
}
|
||
|
||
if (bridgeMemMax < ((newMaxMem + newMinMem) / 2)) {
|
||
newMinMem = MAX(newMinMem, bridgeMemMax) + 1;
|
||
}
|
||
|
||
//
|
||
// Keep track of bus numbers.
|
||
//
|
||
|
||
if (pciData->u.type1.PrimaryBus > ((newMaxBus + newMinBus) / 2)) {
|
||
newMaxBus = MIN(newMaxBus, pciData->u.type1.PrimaryBus);
|
||
}
|
||
|
||
if (pciData->u.type1.SubordinateBus < ((newMaxBus + newMinBus) / 2)) {
|
||
newMinBus = MAX(newMinBus, pciData->u.type1.SubordinateBus) + 1;
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (!PCI_MULTIFUNCTION_DEVICE(pciData) &&
|
||
(func == 0)) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
*MinMem = newMinMem;
|
||
*MaxMem = newMaxMem;
|
||
*MinIo = newMinIo;
|
||
*MaxIo = newMaxIo;
|
||
*MinBus = newMinBus;
|
||
*MaxBus = newMaxBus;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpSetupUnconfiguredDebuggingDevice(
|
||
IN ULONG Bus,
|
||
IN ULONG Slot,
|
||
IN ULONG IoMin,
|
||
IN ULONG IoMax,
|
||
IN ULONG MemMin,
|
||
IN ULONG MemMax,
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice
|
||
)
|
||
{
|
||
|
||
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
|
||
PPCI_COMMON_CONFIG pciData;
|
||
ULONG barLength, bytesRead;
|
||
ULONG barContents = 0;
|
||
PHYSICAL_ADDRESS physicalAddress;
|
||
PCI_SLOT_NUMBER pciSlot;
|
||
UCHAR barNo;
|
||
|
||
pciSlot.u.AsULONG = Slot;
|
||
pciData = (PPCI_COMMON_CONFIG)buffer;
|
||
|
||
bytesRead = (UCHAR)HalpPhase0GetPciDataByOffset(Bus,
|
||
pciSlot.u.AsULONG,
|
||
pciData,
|
||
0,
|
||
PCI_COMMON_HDR_LENGTH);
|
||
|
||
ASSERT(bytesRead != 0);
|
||
|
||
PciDevice->Bus = Bus;
|
||
PciDevice->Slot = pciSlot.u.AsULONG;
|
||
PciDevice->VendorID = pciData->VendorID;
|
||
PciDevice->DeviceID = pciData->DeviceID;
|
||
PciDevice->BaseClass = pciData->BaseClass;
|
||
PciDevice->SubClass = pciData->SubClass;
|
||
|
||
//DbgPrint("Configuring device between %x - %x\n",
|
||
// MemMin, MemMax);
|
||
|
||
//
|
||
// Cycle through the BARs, turning them on if necessary,
|
||
// and mapping them.
|
||
//
|
||
|
||
for (barNo = 0; barNo < PCI_TYPE0_ADDRESSES; barNo++) {
|
||
|
||
barContents = 0xffffffff;
|
||
|
||
PciDevice->BaseAddress[barNo].Valid = FALSE;
|
||
|
||
HalpPhase0SetPciDataByOffset(Bus,
|
||
pciSlot.u.AsULONG,
|
||
&barContents,
|
||
0x10 + (4 * barNo),
|
||
4);
|
||
|
||
HalpPhase0GetPciDataByOffset(Bus,
|
||
pciSlot.u.AsULONG,
|
||
&barContents,
|
||
0x10 + (4 * barNo),
|
||
4);
|
||
|
||
if (pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_SPACE) {
|
||
|
||
//
|
||
// This is an I/O BAR.
|
||
//
|
||
|
||
if (!(pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_ADDRESS_MASK)) {
|
||
|
||
//
|
||
// And it's empty.
|
||
//
|
||
|
||
barLength = (((USHORT)barContents & PCI_ADDRESS_IO_ADDRESS_MASK) - 1) ^
|
||
0xffff;
|
||
|
||
//
|
||
// Try to fit this I/O window half-way between the min and the max.
|
||
//
|
||
|
||
if ((ULONG)(IoMax - IoMin) >= (barLength * 3)) {
|
||
|
||
//
|
||
// There is plenty of room, make a safe guess. Try
|
||
// to put it half-way between the upper and lower
|
||
// bounds, rounding up to the next natural alignment.
|
||
//
|
||
|
||
pciData->u.type0.BaseAddresses[barNo] =
|
||
(((IoMax + IoMin) / 2) + barLength) & (barLength -1);
|
||
|
||
} else if (barLength >= (IoMax -
|
||
((IoMin & (barLength -1)) ?
|
||
((IoMin + barLength) & (barLength -1)) :
|
||
IoMin))) {
|
||
//
|
||
// Space is tight, make a not-so-safe guess. Try
|
||
// to put it at the bottom of the range, rounded
|
||
// up the the next natural alignment.
|
||
//
|
||
|
||
pciData->u.type0.BaseAddresses[barNo] =
|
||
((IoMin & (barLength -1)) ?
|
||
((IoMin + barLength) & (barLength -1)) :
|
||
IoMin);
|
||
}
|
||
|
||
IoMin = (USHORT)pciData->u.type0.BaseAddresses[barNo];
|
||
}
|
||
|
||
pciData->Command |= PCI_ENABLE_IO_SPACE;
|
||
|
||
PciDevice->BaseAddress[barNo].Type = CmResourceTypePort;
|
||
PciDevice->BaseAddress[barNo].Valid = TRUE;
|
||
PciDevice->BaseAddress[barNo].TranslatedAddress =
|
||
(PUCHAR)(ULONG_PTR)(pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_IO_ADDRESS_MASK);
|
||
PciDevice->BaseAddress[barNo].Length = barLength;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a memory BAR.
|
||
//
|
||
|
||
barLength = ((barContents & PCI_ADDRESS_MEMORY_ADDRESS_MASK) - 1) ^
|
||
0xffffffff;
|
||
|
||
if (!(pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_MEMORY_ADDRESS_MASK)) {
|
||
|
||
//
|
||
// And it's empty.
|
||
//
|
||
|
||
if (barLength == 0) continue;
|
||
|
||
//
|
||
// Try to fit this memory window half-way between the min and the max.
|
||
//
|
||
|
||
if ((ULONG)(MemMax - MemMin) >= (barLength * 3)) {
|
||
|
||
//
|
||
// There is plenty of room, make a safe guess. Try
|
||
// to put it half-way between the upper and lower
|
||
// bounds, rounding up to the next natural alignment.
|
||
//
|
||
|
||
pciData->u.type0.BaseAddresses[barNo] =
|
||
(ULONG)(((MemMax + MemMin) / 2)
|
||
+ barLength) & ~(barLength -1);
|
||
|
||
} else if (barLength >= (ULONG)(MemMax -
|
||
((MemMin & ~(barLength -1)) ?
|
||
((MemMin + barLength) & ~(barLength-1)) :
|
||
MemMin))) {
|
||
//
|
||
// Space is tight, make a not-so-safe guess. Try
|
||
// to put it at the bottom of the range, rounded
|
||
// up the the next natural alignment.
|
||
//
|
||
|
||
pciData->u.type0.BaseAddresses[barNo] =
|
||
(ULONG)((MemMin & ~(barLength -1)) ?
|
||
((MemMin + barLength) & ~(barLength -1)) :
|
||
MemMin);
|
||
}
|
||
|
||
MemMin = pciData->u.type0.BaseAddresses[barNo] &
|
||
PCI_ADDRESS_MEMORY_ADDRESS_MASK;
|
||
}
|
||
|
||
pciData->Command |= PCI_ENABLE_MEMORY_SPACE;
|
||
|
||
physicalAddress.HighPart = 0;
|
||
physicalAddress.LowPart = pciData->u.type0.BaseAddresses[barNo]
|
||
& PCI_ADDRESS_MEMORY_ADDRESS_MASK;
|
||
PciDevice->BaseAddress[barNo].Type = CmResourceTypeMemory;
|
||
PciDevice->BaseAddress[barNo].Valid = TRUE;
|
||
PciDevice->BaseAddress[barNo].TranslatedAddress =
|
||
HalpMapPhysicalMemory(physicalAddress,
|
||
ADDRESS_AND_SIZE_TO_SPAN_PAGES(physicalAddress.LowPart, barLength),
|
||
MmNonCached);
|
||
PciDevice->BaseAddress[barNo].Length = barLength;
|
||
}
|
||
}
|
||
|
||
pciData->Command |= PCI_ENABLE_BUS_MASTER;
|
||
|
||
//
|
||
// Write back any changes we made.
|
||
//
|
||
|
||
HalpPhase0SetPciDataByOffset(Bus,
|
||
pciSlot.u.AsULONG,
|
||
pciData,
|
||
0,
|
||
0x40);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpSearchForPciDebuggingDevice(
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice,
|
||
IN ULONG StartBusNumber,
|
||
IN ULONG EndBusNumber,
|
||
IN ULONG MinMem,
|
||
IN ULONG MaxMem,
|
||
IN USHORT MinIo,
|
||
IN USHORT MaxIo,
|
||
IN BOOLEAN ConfigureBridges
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a helper function for
|
||
HalpSetupPciDeviceForDebugging.
|
||
|
||
Arguments:
|
||
|
||
PciDevice - Structure indicating the device
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if the device is configured and usable.
|
||
|
||
STATUS_NO_MORE_MATCHES if no device matched the criteria.
|
||
|
||
STATUS_UNSUCCESSFUL if the routine fails for other reasons.
|
||
--*/
|
||
#define TARGET_DEVICE_NOT_FOUND 0x10000
|
||
{
|
||
NTSTATUS status;
|
||
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
|
||
PPCI_COMMON_CONFIG pciData;
|
||
UCHAR bus, dev, func, bytesRead;
|
||
PCI_SLOT_NUMBER pciSlot, targetSlot;
|
||
ULONG newMinMem, newMaxMem;
|
||
ULONG newMinIo, newMaxIo;
|
||
ULONG newMinBus, newMaxBus;
|
||
UCHAR barNo;
|
||
BOOLEAN unconfigureBridge = FALSE;
|
||
|
||
pciData = (PPCI_COMMON_CONFIG)buffer;
|
||
pciSlot.u.AsULONG = 0;
|
||
newMinMem = MinMem;
|
||
newMaxMem = MaxMem;
|
||
newMinIo = MinIo;
|
||
newMaxIo = MaxIo;
|
||
newMinBus = StartBusNumber;
|
||
newMaxBus = EndBusNumber;
|
||
bus = (UCHAR)StartBusNumber;
|
||
|
||
//DbgPrint("HalpSearchForPciDebuggingDevice:\n"
|
||
// "\tMem: %x-%x\n"
|
||
// "\tI/O: %x-%x\n"
|
||
// "\tBus: %x-%x\n"
|
||
// "\t%s Configuring Bridges\n",
|
||
// MinMem, MaxMem,
|
||
// MinIo, MaxIo,
|
||
// StartBusNumber, EndBusNumber,
|
||
// ConfigureBridges ? "" : "Not");
|
||
|
||
//
|
||
// This bit stays set to 1 until we find the device.
|
||
//
|
||
targetSlot.u.bits.Reserved = TARGET_DEVICE_NOT_FOUND;
|
||
|
||
while (TRUE) {
|
||
|
||
UCHAR nextBus;
|
||
|
||
nextBus = bus + 1;
|
||
|
||
HalpFindFreeResourceLimits(bus,
|
||
&newMinIo,
|
||
&newMaxIo,
|
||
&newMinMem,
|
||
&newMaxMem,
|
||
&newMinBus,
|
||
&newMaxBus
|
||
);
|
||
|
||
for (dev = 0; dev < PCI_MAX_DEVICES; dev++) {
|
||
for (func = 0; func < PCI_MAX_FUNCTION; func++) {
|
||
|
||
pciSlot.u.bits.DeviceNumber = dev;
|
||
pciSlot.u.bits.FunctionNumber = func;
|
||
|
||
|
||
bytesRead = (UCHAR)HalpPhase0GetPciDataByOffset(bus,
|
||
pciSlot.u.AsULONG,
|
||
pciData,
|
||
0,
|
||
PCI_COMMON_HDR_LENGTH);
|
||
|
||
if (bytesRead == 0) continue;
|
||
|
||
if (pciData->VendorID != PCI_INVALID_VENDORID) {
|
||
|
||
//DbgPrint("%04x:%04x - %x/%x/%x - \tSlot: %x\n",
|
||
// pciData->VendorID,
|
||
// pciData->DeviceID,
|
||
// pciData->BaseClass,
|
||
// pciData->SubClass,
|
||
// pciData->ProgIf,
|
||
// pciSlot.u.AsULONG);
|
||
|
||
switch (PCI_CONFIGURATION_TYPE(pciData)) {
|
||
case PCI_DEVICE_TYPE:
|
||
|
||
//
|
||
// Match first on Bus/Dev/Func
|
||
//
|
||
|
||
if ((PciDevice->Bus == bus) &&
|
||
(PciDevice->Slot == pciSlot.u.AsULONG)) {
|
||
|
||
//DbgPrint("\n\nMatched on Bus/Slot\n\n");
|
||
|
||
return HalpSetupUnconfiguredDebuggingDevice(
|
||
bus,
|
||
pciSlot.u.AsULONG,
|
||
newMinIo,
|
||
newMaxIo,
|
||
newMinMem,
|
||
newMaxMem,
|
||
PciDevice
|
||
);
|
||
}
|
||
|
||
if ((PciDevice->Bus == MAXULONG) &&
|
||
(PciDevice->Slot == MAXULONG)) {
|
||
|
||
//
|
||
// Bus and Slot weren't specified. Match
|
||
// on VID/DID.
|
||
//
|
||
|
||
if ((pciData->VendorID == PciDevice->VendorID) &&
|
||
(pciData->DeviceID == PciDevice->DeviceID)) {
|
||
|
||
//DbgPrint("\n\nMatched on Vend/Dev\n\n");
|
||
|
||
return HalpSetupUnconfiguredDebuggingDevice(
|
||
bus,
|
||
pciSlot.u.AsULONG,
|
||
newMinIo,
|
||
newMaxIo,
|
||
newMinMem,
|
||
newMaxMem,
|
||
PciDevice
|
||
);
|
||
}
|
||
|
||
if ((PciDevice->VendorID == MAXUSHORT) &&
|
||
(PciDevice->DeviceID == MAXUSHORT)) {
|
||
|
||
//
|
||
// VID/DID weren't specified. Match
|
||
// on class codes.
|
||
//
|
||
|
||
if ((pciData->BaseClass == PciDevice->BaseClass) &&
|
||
(pciData->SubClass == PciDevice->SubClass)) {
|
||
|
||
//DbgPrint("\n\nMatched on Base/Sub\n\n");
|
||
//
|
||
// Further match on Programming Interface,
|
||
// if specified.
|
||
//
|
||
|
||
if ((PciDevice->ProgIf != MAXUCHAR) &&
|
||
(PciDevice->ProgIf != pciData->ProgIf)) {
|
||
|
||
break;
|
||
}
|
||
|
||
//DbgPrint("\n\nMatched on programming interface\n\n");
|
||
|
||
return HalpSetupUnconfiguredDebuggingDevice(
|
||
bus,
|
||
pciSlot.u.AsULONG,
|
||
newMinIo,
|
||
newMaxIo,
|
||
newMinMem,
|
||
newMaxMem,
|
||
PciDevice
|
||
);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case PCI_CARDBUS_BRIDGE_TYPE:
|
||
//
|
||
// Cardbus bridge stuff here
|
||
//
|
||
case PCI_BRIDGE_TYPE:
|
||
|
||
{
|
||
ULONG bridgeMemMin = 0, bridgeMemMax = 0;
|
||
USHORT bridgeIoMin, bridgeIoMax;
|
||
|
||
//DbgPrint("Found a PCI to PCI bridge\n");
|
||
|
||
if (!((pciData->u.type1.SecondaryBus != 0) &&
|
||
(pciData->u.type1.SubordinateBus !=0) &&
|
||
(pciData->Command & PCI_ENABLE_MEMORY_SPACE) &&
|
||
(pciData->Command & PCI_ENABLE_IO_SPACE))) {
|
||
|
||
//
|
||
// The bridge is unconfigured.
|
||
//
|
||
|
||
if (ConfigureBridges){
|
||
|
||
//
|
||
// We should configure it now.
|
||
//
|
||
|
||
status = HalpConfigurePciBridge(
|
||
PciDevice,
|
||
bus,
|
||
pciSlot.u.AsULONG,
|
||
newMinIo,
|
||
newMaxIo,
|
||
newMinMem,
|
||
newMaxMem,
|
||
MAX((UCHAR)newMinBus, (bus + 1)),
|
||
newMaxBus,
|
||
pciData
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
|
||
unconfigureBridge = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We aren't configuring bridges
|
||
// on this pass.
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
bridgeMemMin = PciBridgeMemory2Base(pciData->u.type1.MemoryBase);
|
||
bridgeMemMax = PciBridgeMemory2Limit(pciData->u.type1.MemoryLimit);
|
||
bridgeIoMin = (USHORT)PciBridgeIO2Base(pciData->u.type1.IOBase, 0);
|
||
bridgeIoMax = (USHORT)PciBridgeIO2Limit(pciData->u.type1.IOLimit, 0);
|
||
|
||
//DbgPrint("Configured: I/O %x-%x Mem %x-%x\n",
|
||
// bridgeIoMin, bridgeIoMax,
|
||
// bridgeMemMin, bridgeMemMax);
|
||
|
||
//
|
||
// Recurse.
|
||
//
|
||
|
||
status = HalpSearchForPciDebuggingDevice(
|
||
PciDevice,
|
||
(ULONG)pciData->u.type1.SecondaryBus,
|
||
(ULONG)pciData->u.type1.SubordinateBus,
|
||
bridgeMemMin,
|
||
bridgeMemMax,
|
||
bridgeIoMin,
|
||
bridgeIoMax,
|
||
ConfigureBridges);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
if (!unconfigureBridge) {
|
||
|
||
//
|
||
// Bump up the bus number so that we don't
|
||
// scan down the busses we just recursed into.
|
||
//
|
||
|
||
nextBus = pciData->u.type1.SubordinateBus + 1;
|
||
|
||
} else {
|
||
|
||
HalpUnconfigurePciBridge(bus,
|
||
pciSlot.u.AsULONG);
|
||
}
|
||
}
|
||
|
||
break;
|
||
default:
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (!PCI_MULTIFUNCTION_DEVICE(pciData) &&
|
||
(func == 0)) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (nextBus >= EndBusNumber) {
|
||
break;
|
||
}
|
||
|
||
bus = nextBus;
|
||
}
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpReleasePciDeviceForDebugging(
|
||
IN OUT PDEBUG_DEVICE_DESCRIPTOR PciDevice
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine de-allocates any resources acquired in
|
||
HalpSetupPciDeviceForDebugging.
|
||
|
||
Arguments:
|
||
|
||
PciDevice - Structure indicating the device
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
|
||
for (i = 0; i < PCI_TYPE0_ADDRESSES; i++) {
|
||
|
||
if (PciDevice->BaseAddress[i].Valid &&
|
||
PciDevice->BaseAddress[i].Type == CmResourceTypeMemory) {
|
||
|
||
PciDevice->BaseAddress[i].Valid = FALSE;
|
||
|
||
HalpUnmapVirtualAddress(PciDevice->BaseAddress[i].TranslatedAddress,
|
||
ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
||
PciDevice->BaseAddress[i].TranslatedAddress,
|
||
PciDevice->BaseAddress[i].Length));
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
HalpRegisterKdSupportFunctions(
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine fills in the HalPrivateDispatchTable
|
||
with the functions needed for debugging through
|
||
PCI devices.
|
||
|
||
Arguments:
|
||
|
||
LoaderBlock - The Loader Block
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
|
||
KdSetupPciDeviceForDebugging = HalpSetupPciDeviceForDebugging;
|
||
KdReleasePciDeviceForDebugging = HalpReleasePciDeviceForDebugging;
|
||
|
||
KdGetAcpiTablePhase0 = HalpGetAcpiTablePhase0;
|
||
KdMapPhysicalMemory64 = HalpMapPhysicalMemory64;
|
||
|
||
KdCheckPowerButton = HalpCheckPowerButton;
|
||
}
|
||
|
||
VOID
|
||
HalpRegisterPciDebuggingDeviceInfo(
|
||
VOID
|
||
)
|
||
{
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
UNICODE_STRING UnicodeString;
|
||
HANDLE BaseHandle = NULL;
|
||
HANDLE Handle = NULL;
|
||
ULONG disposition;
|
||
ULONG bus;
|
||
UCHAR i;
|
||
PCI_SLOT_NUMBER slot;
|
||
NTSTATUS status;
|
||
BOOLEAN debuggerFound = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
for (i = 0;
|
||
i < MAX_DEBUGGING_DEVICES_SUPPORTED;
|
||
i++) {
|
||
|
||
if (HalpPciDebuggingDevice[i].u.bits.Reserved1 == TRUE) {
|
||
//
|
||
// Must be using a PCI device for a debugger.
|
||
//
|
||
debuggerFound = TRUE;
|
||
}
|
||
}
|
||
|
||
if (!debuggerFound) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Open PCI service key.
|
||
//
|
||
|
||
RtlInitUnicodeString (&UnicodeString,
|
||
L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\SERVICES\\PCI");
|
||
|
||
InitializeObjectAttributes(&ObjectAttributes,
|
||
&UnicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
(PSECURITY_DESCRIPTOR) NULL);
|
||
|
||
status = ZwOpenKey (&BaseHandle,
|
||
KEY_READ,
|
||
&ObjectAttributes);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return;
|
||
}
|
||
|
||
// Get the right key
|
||
|
||
RtlInitUnicodeString (&UnicodeString,
|
||
L"Debug");
|
||
|
||
InitializeObjectAttributes(&ObjectAttributes,
|
||
&UnicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
BaseHandle,
|
||
(PSECURITY_DESCRIPTOR) NULL);
|
||
|
||
status = ZwCreateKey (&Handle,
|
||
KEY_READ,
|
||
&ObjectAttributes,
|
||
0,
|
||
(PUNICODE_STRING) NULL,
|
||
REG_OPTION_VOLATILE,
|
||
&disposition);
|
||
|
||
ZwClose(BaseHandle);
|
||
BaseHandle = Handle;
|
||
|
||
ASSERT(disposition == REG_CREATED_NEW_KEY);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return;
|
||
}
|
||
|
||
for (i = 0;
|
||
i < MAX_DEBUGGING_DEVICES_SUPPORTED;
|
||
i++) {
|
||
|
||
if (HalpPciDebuggingDevice[i].u.bits.Reserved1 == TRUE) {
|
||
|
||
//
|
||
// This entry is populated. Create a key for it.
|
||
//
|
||
|
||
RtlInitUnicodeString (&UnicodeString,
|
||
L"0");
|
||
|
||
(*(PCHAR)&(UnicodeString.Buffer[0])) += i;
|
||
|
||
InitializeObjectAttributes(&ObjectAttributes,
|
||
&UnicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
BaseHandle,
|
||
(PSECURITY_DESCRIPTOR) NULL);
|
||
|
||
status = ZwCreateKey (&Handle,
|
||
KEY_READ,
|
||
&ObjectAttributes,
|
||
0,
|
||
(PUNICODE_STRING) NULL,
|
||
REG_OPTION_VOLATILE,
|
||
&disposition);
|
||
|
||
ASSERT(disposition == REG_CREATED_NEW_KEY);
|
||
|
||
//
|
||
// Fill in the values below this key.
|
||
//
|
||
|
||
bus = HalpPciDebuggingDevice[i].u.bits.BusNumber;
|
||
|
||
RtlInitUnicodeString (&UnicodeString,
|
||
L"Bus");
|
||
|
||
status = ZwSetValueKey (Handle,
|
||
&UnicodeString,
|
||
0,
|
||
REG_DWORD,
|
||
&bus,
|
||
sizeof(ULONG));
|
||
|
||
//ASSERT(NT_SUCCESS(status));
|
||
|
||
slot.u.AsULONG = 0;
|
||
slot.u.bits.FunctionNumber = HalpPciDebuggingDevice[i].u.bits.FunctionNumber;
|
||
slot.u.bits.DeviceNumber = HalpPciDebuggingDevice[i].u.bits.DeviceNumber;
|
||
|
||
RtlInitUnicodeString (&UnicodeString,
|
||
L"Slot");
|
||
|
||
status = ZwSetValueKey (Handle,
|
||
&UnicodeString,
|
||
0,
|
||
REG_DWORD,
|
||
&slot.u.AsULONG,
|
||
sizeof(ULONG));
|
||
|
||
//ASSERT(NT_SUCCESS(status));
|
||
|
||
ZwClose(Handle);
|
||
}
|
||
}
|
||
|
||
ZwClose(BaseHandle);
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpConfigurePciBridge(
|
||
IN PDEBUG_DEVICE_DESCRIPTOR PciDevice,
|
||
IN ULONG Bus,
|
||
IN ULONG Slot,
|
||
IN ULONG IoMin,
|
||
IN ULONG IoMax,
|
||
IN ULONG MemMin,
|
||
IN ULONG MemMax,
|
||
IN ULONG BusMin,
|
||
IN ULONG BusMax,
|
||
IN OUT PPCI_COMMON_CONFIG PciData
|
||
)
|
||
{
|
||
USHORT memUnits = 0;
|
||
ULONG memSize;
|
||
|
||
PciData->u.type1.PrimaryBus = (UCHAR)Bus;
|
||
PciData->u.type1.SecondaryBus = (UCHAR)BusMin;
|
||
PciData->u.type1.SubordinateBus = (UCHAR)(MIN(BusMax, (BusMin + 2)));
|
||
|
||
PciData->Command &= ~PCI_ENABLE_BUS_MASTER;
|
||
|
||
//DbgPrint("HalpConfigurePciBridge: P: %x S: %x S: %x\n"
|
||
// "\tI/O %x-%x Mem %x-%x Bus %x-%x\n",
|
||
// PciData->u.type1.PrimaryBus,
|
||
// PciData->u.type1.SecondaryBus,
|
||
// PciData->u.type1.SubordinateBus,
|
||
// IoMin, IoMax,
|
||
// MemMin, MemMax,
|
||
// BusMin, BusMax);
|
||
|
||
//
|
||
// Only enable I/O on the bridge if we are looking for
|
||
// something besides a 1394 controller.
|
||
//
|
||
|
||
if (!((PciDevice->BaseClass == PCI_CLASS_SERIAL_BUS_CTLR) &&
|
||
(PciDevice->SubClass == PCI_SUBCLASS_SB_IEEE1394))) {
|
||
|
||
if (((IoMax & 0xf000) - (IoMin & 0xf000)) >= 0X1000) {
|
||
|
||
//
|
||
// There is enough I/O space here to enable
|
||
// an I/O window.
|
||
//
|
||
|
||
PciData->u.type1.IOBase =
|
||
(UCHAR)((IoMax & 0xf000) >> 12) - 1;
|
||
PciData->u.type1.IOLimit = PciData->u.type1.IOBase;
|
||
|
||
PciData->Command |= PCI_ENABLE_IO_SPACE;
|
||
PciData->Command |= PCI_ENABLE_BUS_MASTER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Enable a memory window if possible.
|
||
//
|
||
|
||
memSize = ((MemMax + 1) & 0xfff00000) - (MemMin & 0xfff00000);
|
||
|
||
if (memSize >= 0x100000) {
|
||
|
||
memUnits = 1;
|
||
}
|
||
|
||
if (memSize >= 0x400000) {
|
||
|
||
memUnits = 4;
|
||
}
|
||
|
||
if (memUnits > 0) {
|
||
|
||
//
|
||
// There is enough space.
|
||
//
|
||
|
||
PciData->u.type1.MemoryBase =
|
||
(USHORT)((MemMax & 0xfff00000) >> 16) - (memUnits << 4);
|
||
|
||
PciData->u.type1.MemoryLimit = PciData->u.type1.MemoryBase + ((memUnits- 1) << 4);
|
||
|
||
PciData->Command |= PCI_ENABLE_MEMORY_SPACE;
|
||
PciData->Command |= PCI_ENABLE_BUS_MASTER;
|
||
|
||
}
|
||
|
||
if (PciData->Command & PCI_ENABLE_BUS_MASTER) {
|
||
|
||
HalpPhase0SetPciDataByOffset(Bus,
|
||
Slot,
|
||
PciData,
|
||
0,
|
||
0x24);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpUnconfigurePciBridge(
|
||
IN ULONG Bus,
|
||
IN ULONG Slot
|
||
)
|
||
{
|
||
UCHAR buffer[0x20] = {0};
|
||
|
||
//
|
||
// Zero the command register.
|
||
//
|
||
|
||
HalpPhase0SetPciDataByOffset(Bus,
|
||
Slot,
|
||
buffer,
|
||
FIELD_OFFSET (PCI_COMMON_CONFIG, Command),
|
||
2);
|
||
|
||
//
|
||
// Zero the address space and bus number registers.
|
||
//
|
||
|
||
HalpPhase0SetPciDataByOffset(Bus,
|
||
Slot,
|
||
buffer,
|
||
FIELD_OFFSET (PCI_COMMON_CONFIG, u),
|
||
0x20);
|
||
}
|
||
|