You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2016 lines
61 KiB
2016 lines
61 KiB
/*++
|
|
|
|
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 ([email protected]) 23-Jun-1997
|
|
Neal Vu ([email protected]) 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};
|
|
|
|
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];
|
|
|
|
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;
|
|
SAL_PAL_RETURN_VALUES RetVals;
|
|
SAL_STATUS Stat;
|
|
|
|
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.AsULONG = 0;
|
|
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; // This is actually segment on IA64
|
|
|
|
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.
|
|
//
|
|
|
|
Stat = HalpSalCall(SAL_PCI_CONFIG_READ, ConfigAddress.u.AsULONG, 4, 0, 0, 0, 0, 0, &RetVals);
|
|
|
|
if (Stat < 0 ) {
|
|
return(0);
|
|
}
|
|
|
|
Tmp.All = (ULONG)RetVals.ReturnValues[1];
|
|
|
|
while ( SubLength-- ) {
|
|
Tmp.Bytes[SubOffset++] = *Bfr++;
|
|
}
|
|
|
|
Stat = HalpSalCall(SAL_PCI_CONFIG_WRITE, ConfigAddress.u.AsULONG, 4, Tmp.All, 0, 0, 0, 0, &RetVals);
|
|
|
|
if (Stat < 0 ) {
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
Stat = HalpSalCall(SAL_PCI_CONFIG_WRITE, ConfigAddress.u.AsULONG, 4, Tmp, 0, 0, 0, 0, &RetVals);
|
|
|
|
if (Stat < 0 ) {
|
|
return(ReturnLength - Length);
|
|
}
|
|
|
|
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;
|
|
|
|
while ( Length-- ) {
|
|
Tmp.Bytes[i++] = *(PUCHAR)Bfr++;
|
|
}
|
|
|
|
Stat = HalpSalCall(SAL_PCI_CONFIG_WRITE, ConfigAddress.u.AsULONG, 4, Tmp.All, 0, 0, 0, 0, &RetVals);
|
|
|
|
if (Stat < 0 ) {
|
|
return(ReturnLength - i);
|
|
}
|
|
}
|
|
|
|
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;
|
|
SAL_PAL_RETURN_VALUES RetVals;
|
|
SAL_STATUS Stat;
|
|
|
|
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.AsULONG = 0;
|
|
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; // This is actually segment on IA64
|
|
|
|
//
|
|
// 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.AsULONG = 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;
|
|
//ConfigAddress.u.bits.Enable = TRUE; // This is actually segment on IA64
|
|
|
|
Stat = HalpSalCall(SAL_PCI_CONFIG_READ, ConfigAddress.u.AsULONG, 4, 0, 0, 0, 0, 0, &RetVals);
|
|
|
|
if (Stat < 0 ) {
|
|
return(ReturnLength - Length);
|
|
}
|
|
|
|
Tmp.All = (ULONG)RetVals.ReturnValues[1];
|
|
|
|
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 ) {
|
|
|
|
//
|
|
// Make SAL call
|
|
//
|
|
|
|
Stat = HalpSalCall(SAL_PCI_CONFIG_READ, ConfigAddress.u.AsULONG, 4, 0, 0, 0, 0, 0, &RetVals);
|
|
|
|
if (Stat < 0 ) {
|
|
return(ReturnLength - Length);
|
|
}
|
|
|
|
Tmp.All = (ULONG)RetVals.ReturnValues[1];
|
|
|
|
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);
|
|
}
|
|
|