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.
2715 lines
85 KiB
2715 lines
85 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
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "halp.h"
|
|
#include "pci.h"
|
|
#include "pcip.h"
|
|
#include "cardbus.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;
|
|
|
|
PCI_TYPE1_CFG_CYCLE_BITS HalpPciDebuggingDevice[MAX_DEBUGGING_DEVICES_SUPPORTED] = {0};
|
|
|
|
extern BOOLEAN HalpDoingCrashDump;
|
|
|
|
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
|
|
);
|
|
|
|
NTSTATUS
|
|
HalpConfigureCardBusBridge(
|
|
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
|
|
HalpUnconfigureCardBusBridge(
|
|
IN ULONG Bus,
|
|
IN ULONG Slot
|
|
);
|
|
|
|
ULONG
|
|
HalpKdStallExecution(
|
|
ULONG LoopCount
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,HalpInitializePciStubs)
|
|
#pragma alloc_text(INIT,HalpRegisterKdSupportFunctions)
|
|
#pragma alloc_text(INIT,HalpRegisterPciDebuggingDeviceInfo)
|
|
#pragma alloc_text(PAGEKD,HalpConfigurePciBridge)
|
|
#pragma alloc_text(PAGEKD,HalpConfigureCardBusBridge)
|
|
#pragma alloc_text(PAGEKD,HalpFindFreeResourceLimits)
|
|
#pragma alloc_text(PAGEKD,HalpPhase0GetPciDataByOffset)
|
|
#pragma alloc_text(PAGEKD,HalpPhase0SetPciDataByOffset)
|
|
#pragma alloc_text(PAGEKD,HalpReleasePciDeviceForDebugging)
|
|
#pragma alloc_text(PAGEKD,HalpSearchForPciDebuggingDevice)
|
|
#pragma alloc_text(PAGEKD,HalpSetupPciDeviceForDebugging)
|
|
#pragma alloc_text(PAGEKD,HalpSetupUnconfiguredDebuggingDevice)
|
|
#pragma alloc_text(PAGEKD,HalpUnconfigurePciBridge)
|
|
#pragma alloc_text(PAGEKD,HalpUnconfigureCardBusBridge)
|
|
#pragma alloc_text(PAGEKD,HalpKdStallExecution)
|
|
#endif
|
|
|
|
VOID
|
|
HalpInitializePciStubs (
|
|
VOID
|
|
)
|
|
{
|
|
PPCI_REGISTRY_INFO_INTERNAL PCIRegInfo;
|
|
PPCIPBUSDATA BusData;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
ULONG HwType;
|
|
ULONG BusNo = 0;
|
|
ULONG Bytes;
|
|
ULONG Slot;
|
|
PCI_COMMON_CONFIG PciData;
|
|
|
|
PCIRegInfo = HalpQueryPciRegistryInfo();
|
|
|
|
if (PCIRegInfo) {
|
|
|
|
//
|
|
// PCIRegInfo describes the system's PCI support as indicated by the BIOS.
|
|
//
|
|
HwType = PCIRegInfo->HardwareMechanism & 0xf;
|
|
ExFreePool(PCIRegInfo);
|
|
} else {
|
|
//
|
|
// no PCI bus information was gathered by NTDETECT,
|
|
// assume type 1 access.
|
|
//
|
|
HwType = 1;
|
|
}
|
|
|
|
//
|
|
// Initialize spinlock for synchronizing access to PCI space
|
|
//
|
|
|
|
KeInitializeSpinLock (&HalpPCIConfigLock);
|
|
|
|
BusData = (PPCIPBUSDATA) HalpFakePciBusHandler.BusData;
|
|
|
|
//
|
|
// Set defaults
|
|
//
|
|
|
|
switch (HwType) {
|
|
case 1:
|
|
//
|
|
// Initialize access port information for Type1 handlers
|
|
//
|
|
|
|
RtlCopyMemory (&PCIConfigHandler,
|
|
&PCIConfigHandlerType1,
|
|
sizeof (PCIConfigHandler));
|
|
|
|
BusData->Config.Type1.Address = (PULONG)PCI_TYPE1_ADDR_PORT;
|
|
BusData->Config.Type1.Data = PCI_TYPE1_DATA_PORT;
|
|
break;
|
|
|
|
case 2:
|
|
//
|
|
// Initialize access port information for Type2 handlers
|
|
//
|
|
|
|
RtlCopyMemory (&PCIConfigHandler,
|
|
&PCIConfigHandlerType2,
|
|
sizeof (PCIConfigHandler));
|
|
|
|
BusData->Config.Type2.CSE = PCI_TYPE2_CSE_PORT;
|
|
BusData->Config.Type2.Forward = PCI_TYPE2_FORWARD_PORT;
|
|
BusData->Config.Type2.Base = PCI_TYPE2_ADDRESS_BASE;
|
|
|
|
//
|
|
// Early PCI machines didn't decode the last bit of
|
|
// the device id. Shrink type 2 support max device.
|
|
//
|
|
BusData->MaxDevice = 0x10;
|
|
|
|
break;
|
|
|
|
default:
|
|
// unsupport type
|
|
DBGMSG ("HAL: Unkown PCI type\n");
|
|
}
|
|
|
|
|
|
//
|
|
// Make a good guess about how many PCI busses are in the system
|
|
// and initialize HalpMaxPciBus with this guess. When the PCI
|
|
// driver starts, we will get a better answer here.
|
|
//
|
|
// Calling HaliPciInterfaceReadConfig has the side effect of
|
|
// bumping up HalpMaxPciBus if it hits a populated device. So
|
|
// the algorithm here is to just keep searching for devices until
|
|
// we have searched 0x10 busses higher than the current maximum.
|
|
//
|
|
|
|
while (BusNo < 0x100) {
|
|
|
|
//
|
|
// Scan across this bus. As soon as we find a device,
|
|
// then move on to another bus.
|
|
//
|
|
|
|
for (Slot = 0;
|
|
Slot < PCI_MAX_DEVICES;
|
|
Slot++) {
|
|
|
|
Bytes = HaliPciInterfaceReadConfig(NULL,
|
|
(UCHAR)BusNo,
|
|
Slot,
|
|
(PVOID)&PciData,
|
|
0,
|
|
4
|
|
);
|
|
if ((Bytes != 0) &&
|
|
(PciData.VendorID != PCI_INVALID_VENDORID)) {
|
|
|
|
//
|
|
// This was a populated device. Bump up HalpMaxPciBus.
|
|
//
|
|
|
|
HalpMaxPciBus = BusNo;
|
|
break;
|
|
}
|
|
}
|
|
|
|
BusNo++;
|
|
}
|
|
}
|
|
|
|
|
|
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
|
|
HaliSetMaxLegacyPciBusNumber(
|
|
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
|
|
|
|
Notes:
|
|
|
|
Caller is responsible for acquiring any necessary PCI config
|
|
spinlocks.
|
|
|
|
--*/
|
|
{
|
|
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 = *(UNALIGNED 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)) {
|
|
|
|
if (PciDevice->Initialized) {
|
|
//
|
|
// Here we were just asked to reconfigure the bridges since
|
|
// we already
|
|
//
|
|
return 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;
|
|
PciDevice->Initialized = 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 = PtrToUlong(PAGE_ALIGN(maxPhys));
|
|
maxPhys += ADDRESS_AND_SIZE_TO_SPAN_PAGES(maxPhys, PciDevice->Memory.Length);
|
|
|
|
PciDevice->Memory.Start.HighPart = 0;
|
|
PciDevice->Memory.Start.LowPart =
|
|
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 =
|
|
HalpMapPhysicalMemory64(PciDevice->Memory.Start,
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(maxPhys, PciDevice->Memory.Length));
|
|
}
|
|
}
|
|
|
|
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 / 2) + (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:
|
|
|
|
{
|
|
ULONG bridgeMemMin = 0, bridgeMemMax = 0;
|
|
USHORT bridgeIoMin, bridgeIoMax;
|
|
ULONG LegacyBaseAddress;
|
|
UCHAR bytesRead;
|
|
|
|
bytesRead = (UCHAR) HalpPhase0GetPciDataByOffset(Bus,
|
|
pciSlot.u.AsULONG,
|
|
&LegacyBaseAddress,
|
|
CARDBUS_LEGACY_MODE_BASE_ADDR,
|
|
4);
|
|
|
|
if (bytesRead != 4) continue;
|
|
|
|
|
|
if ((LegacyBaseAddress & ~1) &&
|
|
(pciData->u.type2.SecondaryBus != 0) &&
|
|
(pciData->u.type2.SubordinateBus !=0) &&
|
|
(pciData->u.type2.Range[0].Base != 0) &&
|
|
(pciData->u.type2.SocketRegistersBaseAddress != 0) &&
|
|
(pciData->Command & PCI_ENABLE_MEMORY_SPACE) &&
|
|
(pciData->Command & PCI_ENABLE_IO_SPACE)) {
|
|
|
|
bridgeMemMin = pciData->u.type2.Range[0].Base;
|
|
bridgeMemMax = pciData->u.type2.Range[0].Limit | 0xfff;
|
|
bridgeIoMin = (USHORT)pciData->u.type2.Range[2].Base;
|
|
bridgeIoMax = (USHORT)pciData->u.type2.Range[2].Limit | 0x3;
|
|
|
|
//
|
|
// Keep track of address space allocation.
|
|
//
|
|
|
|
if (bridgeIoMin > ((newMaxIo / 2) + (newMinIo / 2))) {
|
|
newMaxIo = MIN(newMaxIo, bridgeIoMin);
|
|
}
|
|
|
|
if (bridgeIoMax < ((newMaxIo / 2) + (newMinIo / 2))) {
|
|
newMinIo = MAX(newMinIo, bridgeIoMax) + 1;
|
|
}
|
|
|
|
if (bridgeMemMin > ((newMaxMem / 2) + (newMinMem / 2))) {
|
|
newMaxMem = MIN(newMaxMem, bridgeMemMin);
|
|
}
|
|
|
|
if (bridgeMemMax < ((newMaxMem / 2) + (newMinMem / 2))) {
|
|
newMinMem = MAX(newMinMem, bridgeMemMax) + 1;
|
|
}
|
|
|
|
//
|
|
// Keep track of bus numbers.
|
|
//
|
|
|
|
if (pciData->u.type2.PrimaryBus > ((newMaxBus / 2) + (newMinBus / 2))) {
|
|
newMaxBus = MIN(newMaxBus, pciData->u.type2.PrimaryBus);
|
|
}
|
|
|
|
if (pciData->u.type2.SubordinateBus < ((newMaxBus / 2) + (newMinBus / 2))) {
|
|
newMinBus = MAX(newMinBus, pciData->u.type2.SubordinateBus) + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
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 / 2) + (newMinIo / 2))) {
|
|
newMaxIo = MIN(newMaxIo, bridgeIoMin);
|
|
}
|
|
|
|
if (bridgeIoMax < ((newMaxIo / 2) + (newMinIo / 2))) {
|
|
newMinIo = MAX(newMinIo, bridgeIoMax) + 1;
|
|
}
|
|
|
|
if (bridgeMemMin > ((newMaxMem / 2) + (newMinMem / 2))) {
|
|
newMaxMem = MIN(newMaxMem, bridgeMemMin);
|
|
}
|
|
|
|
if (bridgeMemMax < ((newMaxMem / 2) + (newMinMem / 2))) {
|
|
newMinMem = MAX(newMinMem, bridgeMemMax) + 1;
|
|
}
|
|
|
|
//
|
|
// Keep track of bus numbers.
|
|
//
|
|
|
|
if (pciData->u.type1.PrimaryBus > ((newMaxBus / 2) + (newMinBus / 2))) {
|
|
newMaxBus = MIN(newMaxBus, pciData->u.type1.PrimaryBus);
|
|
}
|
|
|
|
if (pciData->u.type1.SubordinateBus < ((newMaxBus / 2) + (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;
|
|
UCHAR capPtr;
|
|
PCI_PM_CAPABILITY pmCap;
|
|
|
|
//
|
|
// Make sure the device is in D0
|
|
//
|
|
bytesRead = HalpPhase0GetPciDataByOffset(Bus, Slot, &capPtr,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type0.CapabilitiesPtr), 1);
|
|
|
|
if ((bytesRead == 1) && capPtr) {
|
|
|
|
bytesRead = HalpPhase0GetPciDataByOffset(Bus, Slot, &pmCap, capPtr, sizeof(pmCap));
|
|
|
|
if ((bytesRead == sizeof(pmCap)) &&
|
|
(pmCap.Header.CapabilityID == PCI_CAPABILITY_ID_POWER_MANAGEMENT) &&
|
|
(pmCap.PMCSR.ControlStatus.PowerState != 0)) {
|
|
|
|
pmCap.PMCSR.ControlStatus.PowerState = 0; // set to D0
|
|
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&pmCap.PMCSR.ControlStatus,
|
|
capPtr + FIELD_OFFSET(PCI_PM_CAPABILITY,PMCSR.ControlStatus),
|
|
sizeof(pmCap.PMCSR.ControlStatus));
|
|
|
|
|
|
HalpKdStallExecution(2000);
|
|
|
|
}
|
|
}
|
|
|
|
pciSlot.u.AsULONG = Slot;
|
|
pciData = (PPCI_COMMON_CONFIG)buffer;
|
|
|
|
bytesRead = 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.
|
|
//
|
|
|
|
barLength = (((USHORT)barContents & PCI_ADDRESS_IO_ADDRESS_MASK) - 1) ^
|
|
0xffff;
|
|
|
|
if (!(pciData->u.type0.BaseAddresses[barNo] &
|
|
PCI_ADDRESS_IO_ADDRESS_MASK)) {
|
|
|
|
//
|
|
// And it's empty.
|
|
//
|
|
|
|
//
|
|
// 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 / 2) + (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 / 2) + (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 =
|
|
HalpMapPhysicalMemory64(physicalAddress,
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(physicalAddress.LowPart, barLength));
|
|
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;
|
|
|
|
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:
|
|
|
|
{
|
|
ULONG bridgeMemMin = 0, bridgeMemMax = 0;
|
|
USHORT bridgeIoMin, bridgeIoMax;
|
|
ULONG LegacyBaseAddress;
|
|
|
|
unconfigureBridge = FALSE;
|
|
|
|
//DbgPrint("Found a CardBus bridge\n");
|
|
|
|
HalpPhase0GetPciDataByOffset(bus,
|
|
pciSlot.u.AsULONG,
|
|
&LegacyBaseAddress,
|
|
CARDBUS_LEGACY_MODE_BASE_ADDR,
|
|
4);
|
|
|
|
if (!(((LegacyBaseAddress & ~1) == 0) &&
|
|
(pciData->u.type2.SecondaryBus != 0) &&
|
|
(pciData->u.type2.SubordinateBus !=0) &&
|
|
(pciData->u.type2.Range[0].Base != 0) &&
|
|
(pciData->u.type2.SocketRegistersBaseAddress != 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 = HalpConfigureCardBusBridge(
|
|
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 = pciData->u.type2.Range[0].Base;
|
|
bridgeMemMax = pciData->u.type2.Range[0].Limit | 0xfff;
|
|
bridgeIoMin = (USHORT)pciData->u.type2.Range[2].Base;
|
|
bridgeIoMax = (USHORT)pciData->u.type2.Range[2].Limit | 0x3;
|
|
|
|
//DbgPrint("Configured: I/O %x-%x Mem %x-%x\n",
|
|
// bridgeIoMin, bridgeIoMax,
|
|
// bridgeMemMin, bridgeMemMax);
|
|
|
|
//
|
|
// Recurse.
|
|
//
|
|
status = HalpSearchForPciDebuggingDevice(
|
|
PciDevice,
|
|
(ULONG)pciData->u.type2.SecondaryBus,
|
|
(ULONG)pciData->u.type2.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.type2.SubordinateBus + 1;
|
|
|
|
} else {
|
|
|
|
HalpUnconfigureCardBusBridge(bus,
|
|
pciSlot.u.AsULONG);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PCI_BRIDGE_TYPE:
|
|
|
|
{
|
|
ULONG bridgeMemMin = 0, bridgeMemMax = 0;
|
|
USHORT bridgeIoMin, bridgeIoMax;
|
|
|
|
unconfigureBridge = FALSE;
|
|
//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;
|
|
|
|
#ifdef ACPI_HAL
|
|
KdGetAcpiTablePhase0 = HalpGetAcpiTablePhase0;
|
|
#endif
|
|
|
|
KdCheckPowerButton = HalpCheckPowerButton;
|
|
KdMapPhysicalMemory64 = HalpMapPhysicalMemory64;
|
|
KdUnmapVirtualAddress = HalpUnmapVirtualAddress;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
HalpValidateCardbusSocketRegs(
|
|
IN PCARDBUS_SOCKET_REGS SocketRegs
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks to make sure the cardbus socket registers look
|
|
reasonable, and also that there is a card in the slot.
|
|
|
|
Arguments:
|
|
|
|
SocketRegs - virtual address of mapped socket registers
|
|
|
|
Return Value:
|
|
|
|
returns TRUE if everything looks ok, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
ULONG mask;
|
|
|
|
//
|
|
// First verify there are no reserved bits on
|
|
//
|
|
|
|
if ((SocketRegs->Event & 0xFFFFFFF0) ||
|
|
(SocketRegs->Mask & 0xFFFFFFF0) ||
|
|
(SocketRegs->PresentState & 0x0FFF0000) ||
|
|
(SocketRegs->ForceEvent) ||
|
|
(SocketRegs->Control & 0xFFFFFF00)) {
|
|
|
|
//
|
|
// Some bits we don't expect to be on were on
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if ((SocketRegs->PresentState & SKTSTATE_3VSOCKET) == 0) {
|
|
//
|
|
// need to have a 3.3v socket for cardbus
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Stop here if there is no CardBus card in the slot
|
|
//
|
|
if ((SocketRegs->PresentState & SKTSTATE_CARDTYPE_MASK) != SKTSTATE_CBCARD) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Also look to make sure this isn't on
|
|
//
|
|
|
|
if (SocketRegs->PresentState & SKTSTATE_NOTACARD) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
HalpConfigureCardBusBridge(
|
|
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
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
ULONG ulTemp;
|
|
ULONG SocketRegBase, origSocketRegBase = 0;
|
|
USHORT BridgeControl = 0;
|
|
PCARDBUS_SOCKET_REGS SocketRegs;
|
|
PHYSICAL_ADDRESS physicalAddress;
|
|
ULONG bytesRead;
|
|
USHORT command, origCmd;
|
|
UCHAR capPtr = 0;
|
|
PCI_PM_CAPABILITY pmCap;
|
|
BOOLEAN cmdRegChanged = FALSE;
|
|
BOOLEAN socketRegChanged = FALSE;
|
|
BOOLEAN socketRegConfigured = FALSE;
|
|
|
|
|
|
try{
|
|
|
|
//
|
|
// First look for a power management capability on this controller, and
|
|
// if it exists (it should), put the cardbus controller into D0
|
|
//
|
|
|
|
bytesRead = HalpPhase0GetPciDataByOffset(Bus, Slot, &capPtr,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type2.CapabilitiesPtr), 1);
|
|
|
|
if (bytesRead != 1) {
|
|
// if we can't do this, things must be just too broken
|
|
leave;
|
|
}
|
|
|
|
while (capPtr) {
|
|
UCHAR capHeader[2];
|
|
|
|
//
|
|
// loop through the capabilities list and look for power management
|
|
//
|
|
|
|
if ((capPtr & 3) || (capPtr < 0x40)) {
|
|
//
|
|
// the capabilities pointer isn't valid
|
|
//
|
|
leave;
|
|
}
|
|
|
|
|
|
bytesRead = HalpPhase0GetPciDataByOffset(Bus, Slot, &capHeader, capPtr, 2);
|
|
|
|
if (bytesRead != 2) {
|
|
leave;
|
|
}
|
|
|
|
if (capHeader[0] == PCI_CAPABILITY_ID_POWER_MANAGEMENT) {
|
|
//
|
|
// found it
|
|
//
|
|
break;
|
|
}
|
|
|
|
if (capPtr == capHeader[1]) {
|
|
//
|
|
// this is bad config data. Avoid an infinite loop
|
|
//
|
|
leave;
|
|
}
|
|
|
|
capPtr = capHeader[1];
|
|
}
|
|
|
|
|
|
if (capPtr) {
|
|
|
|
bytesRead = HalpPhase0GetPciDataByOffset(Bus, Slot, &pmCap, capPtr, sizeof(pmCap));
|
|
|
|
if ((bytesRead == sizeof(pmCap)) &&
|
|
(pmCap.Header.CapabilityID == PCI_CAPABILITY_ID_POWER_MANAGEMENT) &&
|
|
(pmCap.PMCSR.ControlStatus.PowerState != 0)) {
|
|
|
|
pmCap.PMCSR.ControlStatus.PowerState = 0; // set to D0
|
|
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&pmCap.PMCSR.ControlStatus,
|
|
capPtr + FIELD_OFFSET(PCI_PM_CAPABILITY,PMCSR.ControlStatus),
|
|
sizeof(pmCap.PMCSR.ControlStatus));
|
|
|
|
HalpKdStallExecution(1000);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Turn off legacy mode base address... this really should be done
|
|
// via ACPI _INIT. This may not be sufficient to put the host in
|
|
// cardbus mode.
|
|
//
|
|
ulTemp = 0;
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&ulTemp,
|
|
CARDBUS_LEGACY_MODE_BASE_ADDR,
|
|
4);
|
|
|
|
//
|
|
// save away original value of command register, socket reg base
|
|
//
|
|
|
|
bytesRead = HalpPhase0GetPciDataByOffset(Bus,
|
|
Slot,
|
|
&origCmd,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Command),
|
|
2);
|
|
|
|
if (bytesRead != 2) {
|
|
leave;
|
|
}
|
|
|
|
bytesRead = HalpPhase0GetPciDataByOffset(Bus,
|
|
Slot,
|
|
&origSocketRegBase,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type2.SocketRegistersBaseAddress),
|
|
4);
|
|
|
|
if (bytesRead != 4) {
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Turn on socket registers in a suitable location
|
|
//
|
|
|
|
if (origSocketRegBase) {
|
|
//
|
|
// bios has configured socket reg base... see if it is working
|
|
//
|
|
physicalAddress.HighPart = 0;
|
|
physicalAddress.LowPart = origSocketRegBase;
|
|
SocketRegs = HalpMapPhysicalMemory64(physicalAddress, 1);
|
|
|
|
if (!SocketRegs) {
|
|
leave;
|
|
}
|
|
|
|
command = origCmd | PCI_ENABLE_MEMORY_SPACE;
|
|
|
|
if (command != origCmd) {
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&command,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Command),
|
|
2);
|
|
cmdRegChanged = TRUE;
|
|
}
|
|
|
|
HalpKdStallExecution(600);
|
|
|
|
//
|
|
// make sure it looks like cardbus socket registers
|
|
//
|
|
|
|
if (HalpValidateCardbusSocketRegs(SocketRegs)) {
|
|
socketRegConfigured = TRUE;
|
|
SocketRegBase = origSocketRegBase;
|
|
} else {
|
|
//
|
|
// This didn't work... we'll try it again in a moment with a different address
|
|
//
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&origCmd,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Command),
|
|
2);
|
|
cmdRegChanged = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the socket regs weren't working where they were originally configured by
|
|
// the bios, or if the bios didn't configure them, we will configure them here
|
|
//
|
|
|
|
if (!socketRegConfigured) {
|
|
SocketRegBase = ((MemMin + 0xfff) & 0xfffff000);
|
|
|
|
if ((SocketRegBase + 0x1000) >= MemMax) {
|
|
leave;
|
|
}
|
|
|
|
//
|
|
// Adjust MemMin to allow the socket registers to remain visible
|
|
//
|
|
MemMin = SocketRegBase + 0x1000;
|
|
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&SocketRegBase,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type2.SocketRegistersBaseAddress),
|
|
4);
|
|
socketRegChanged = TRUE;
|
|
|
|
physicalAddress.HighPart = 0;
|
|
physicalAddress.LowPart = SocketRegBase;
|
|
SocketRegs = HalpMapPhysicalMemory64(physicalAddress, 1);
|
|
|
|
if (!SocketRegs) {
|
|
leave;
|
|
}
|
|
|
|
command = origCmd | PCI_ENABLE_MEMORY_SPACE;
|
|
|
|
if (command != origCmd) {
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&command,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Command),
|
|
2);
|
|
cmdRegChanged = TRUE;
|
|
}
|
|
|
|
HalpKdStallExecution(600);
|
|
|
|
if (!HalpValidateCardbusSocketRegs(SocketRegs)) {
|
|
//
|
|
// The registers don't look right
|
|
//
|
|
leave;
|
|
}
|
|
|
|
}
|
|
|
|
PciData->u.type2.SocketRegistersBaseAddress = SocketRegBase;
|
|
|
|
//
|
|
// make sure events are disabled
|
|
//
|
|
SocketRegs->Mask = 0;
|
|
|
|
//
|
|
// power up the card
|
|
//
|
|
|
|
SocketRegs->Control = SKTPOWER_VCC_033V | SKTPOWER_VPP_033V;
|
|
|
|
HalpKdStallExecution(600);
|
|
|
|
//
|
|
// Make sure CBRST is off
|
|
//
|
|
|
|
HalpPhase0GetPciDataByOffset(Bus,
|
|
Slot,
|
|
&BridgeControl,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type2.BridgeControl),
|
|
2);
|
|
|
|
if (BridgeControl & CARDBUS_BRIDGE_CONTROL_RESET) {
|
|
|
|
BridgeControl &= ~CARDBUS_BRIDGE_CONTROL_RESET;
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&BridgeControl,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type2.BridgeControl),
|
|
2);
|
|
}
|
|
|
|
|
|
PciData->u.type2.PrimaryBus = (UCHAR)Bus;
|
|
PciData->u.type2.SecondaryBus = (UCHAR)BusMin;
|
|
PciData->u.type2.SubordinateBus = (UCHAR)(MIN(BusMax, (BusMin + 2)));
|
|
|
|
PciData->Command &= ~PCI_ENABLE_BUS_MASTER;
|
|
|
|
//DbgPrint("HalpConfigureCardBusBridge: P: %x S: %x S: %x\n"
|
|
// "\tI/O %x-%x Mem %x-%x Bus %x-%x\n",
|
|
// PciData->u.type2.PrimaryBus,
|
|
// PciData->u.type2.SecondaryBus,
|
|
// PciData->u.type2.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))) {
|
|
|
|
PciData->u.type2.Range[2].Base = IoMin;
|
|
PciData->u.type2.Range[2].Limit = IoMax & 0xffc;
|
|
|
|
PciData->Command |= PCI_ENABLE_IO_SPACE;
|
|
PciData->Command |= PCI_ENABLE_BUS_MASTER;
|
|
}
|
|
|
|
//
|
|
// Enable a memory window if possible.
|
|
//
|
|
|
|
if (MemMax > MemMin) {
|
|
|
|
PciData->u.type2.Range[0].Base = MemMin;
|
|
PciData->u.type2.Range[0].Limit = MemMax & 0xfffff000;
|
|
|
|
PciData->Command |= PCI_ENABLE_MEMORY_SPACE;
|
|
PciData->Command |= PCI_ENABLE_BUS_MASTER;
|
|
|
|
}
|
|
|
|
|
|
if ((PciData->Command & PCI_ENABLE_BUS_MASTER) == 0) {
|
|
leave;
|
|
}
|
|
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
PciData,
|
|
0,
|
|
0x3c);
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
|
} finally {
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (cmdRegChanged) {
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&origCmd,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Command),
|
|
2);
|
|
}
|
|
|
|
//
|
|
// restore socket register base
|
|
//
|
|
if (socketRegChanged) {
|
|
HalpPhase0SetPciDataByOffset(Bus,
|
|
Slot,
|
|
&origSocketRegBase,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type2.SocketRegistersBaseAddress),
|
|
4);
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
HalpUnconfigureCardBusBridge(
|
|
IN ULONG Bus,
|
|
IN ULONG Slot
|
|
)
|
|
{
|
|
UCHAR buffer[0x2c] = {0};
|
|
ULONG SocketRegBase = 0xffffffff;
|
|
PCARDBUS_SOCKET_REGS SocketRegs;
|
|
PHYSICAL_ADDRESS physicalAddress;
|
|
|
|
HalpPhase0GetPciDataByOffset(Bus,
|
|
Slot,
|
|
&SocketRegBase,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, u.type2.SocketRegistersBaseAddress),
|
|
4);
|
|
|
|
if (SocketRegBase) {
|
|
physicalAddress.HighPart = 0;
|
|
physicalAddress.LowPart = SocketRegBase;
|
|
SocketRegs = HalpMapPhysicalMemory64(physicalAddress, 1);
|
|
|
|
//
|
|
// Turn power back off
|
|
//
|
|
|
|
if (SocketRegs) {
|
|
SocketRegs->Control = 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),
|
|
0x2c);
|
|
}
|
|
|
|
ULONG
|
|
HalpKdStallExecution(
|
|
ULONG LoopCount
|
|
)
|
|
{
|
|
ULONG i,j,b,k,l;
|
|
|
|
b = 1;
|
|
|
|
for (k=0;k<LoopCount;k++) {
|
|
|
|
for (i=1;i<100000;i++) {
|
|
|
|
PAUSE_PROCESSOR
|
|
b=b* (i>>k);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return b;
|
|
}
|
|
|