mirror of https://github.com/lianthony/NT4.0
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.
2595 lines
62 KiB
2595 lines
62 KiB
|
|
|
|
/*++
|
|
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
Copyright (c) 1994 Digital Equipment Corporation
|
|
|
|
Module Name:
|
|
|
|
pcisup.c
|
|
|
|
Abstract:
|
|
|
|
Platform-independent PCI bus routines
|
|
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "halp.h"
|
|
#include "eisa.h"
|
|
|
|
typedef ULONG (*FncConfigIO) (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
typedef struct _PCI_CONFIG_HANDLER {
|
|
FncConfigIO ConfigRead[3];
|
|
FncConfigIO ConfigWrite[3];
|
|
} PCI_CONFIG_HANDLER, *PPCI_CONFIG_HANDLER;
|
|
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
|
|
BOOLEAN
|
|
HalpPCIConfig (
|
|
IN ULONG BusNumber,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length,
|
|
IN FncConfigIO *ConfigIO
|
|
);
|
|
|
|
VOID
|
|
HalpInitializePCIBus (
|
|
VOID
|
|
);
|
|
|
|
ULONG HalpPCIReadUlong (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
ULONG HalpPCIReadUchar (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
ULONG HalpPCIReadUshort (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
ULONG HalpPCIWriteUlong (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
ULONG HalpPCIWriteUchar (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
ULONG HalpPCIWriteUshort (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
PCI_CONFIGURATION_TYPES
|
|
HalpPCIConfigCycleType (
|
|
IN ULONG BusNumber,
|
|
IN PCI_SLOT_NUMBER Slot
|
|
);
|
|
|
|
BOOLEAN
|
|
HalpIsValidPCIDevice (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot
|
|
);
|
|
|
|
//
|
|
// Pragmas to assign functions to different kinds of pages.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,HalpInitializePCIBus)
|
|
#pragma alloc_text(INIT,HalpAllocateAndInitPciBusHandler)
|
|
#pragma alloc_text(PAGE,HalpAssignPCISlotResources)
|
|
#endif
|
|
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
KSPIN_LOCK HalpPCIConfigLock;
|
|
ULONG HalpPciBusErrorOccurred;
|
|
ULONG HalpPciRmaConfigErrorOccurred;
|
|
|
|
//
|
|
// These structures are used to efficiently
|
|
// generate the PCI configuration cycles by
|
|
// executing the largest transaction size
|
|
// possible for each cycle.
|
|
//
|
|
|
|
PCI_CONFIG_HANDLER PCIConfigHandler = {
|
|
{
|
|
HalpPCIReadUlong, // 0
|
|
HalpPCIReadUchar, // 1
|
|
HalpPCIReadUshort // 2
|
|
},
|
|
{
|
|
HalpPCIWriteUlong, // 0
|
|
HalpPCIWriteUchar, // 1
|
|
HalpPCIWriteUshort // 2
|
|
}
|
|
};
|
|
|
|
UCHAR PCIDeref[4][4] = { {0,1,2,2},{1,1,1,1},{2,1,2,2},{1,1,1,1} };
|
|
|
|
//
|
|
// This is the IDSEL decode table for Falcon. It is indexed
|
|
// by the DeviceNumber assigned to a particular device according
|
|
// to where it is located within the system. BusNumber 0 refers
|
|
// to Local PCI Devices whereas BusNumbers 1, 2, and 3 refer to
|
|
// Remote PCI Devices attached to PCI-PCI bridges installed in
|
|
// slot 0, 1, and 2, respectively. The BusNumber, DeviceNumber,
|
|
// and IDSEL mappings are described as follows:
|
|
//
|
|
// BusNumber DeviceNumber IDSEL Slot
|
|
// --------- ------------ ----- ----
|
|
//
|
|
// 0 0 - 7 0x1 - 0x80 Internal
|
|
// 1 8 - 15 0x10 0
|
|
// 2 16 - 23 0x20 1
|
|
// 3 24 - 31 0x40 2
|
|
//
|
|
// Note:
|
|
// If you change anything in or about these tables, you MUST
|
|
// also change HalpInitializeEisaInterrupts() which depends
|
|
// on the structure and order of these tables.
|
|
//
|
|
|
|
UCHAR HalpPciConfigSelectDecodeTable[32] = {
|
|
|
|
// Bus Number 0
|
|
// Device Number : (0) (1) (2) (3) (4) (5) (6) (7)
|
|
// On-board Devices : ISA/EISA ENET SCSI Not Used SLOT0 SLOT1 Not Used FASTswitch
|
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
|
|
|
// Bus Number 0
|
|
// Device Number : (8) (9) (10) (11) (12) (13) (14) (15)
|
|
// Remote Devices :
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
// Bus Number 0
|
|
// Device Number : (16) (17) (18) (19) (20) (21) (22) (23)
|
|
// Remote Devices :
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
// Bus Number 0
|
|
// Device Number : (24) (25) (26) (27) (28) (29) (30) (31)
|
|
// Remote Devices :
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
};
|
|
|
|
//
|
|
// The following table describes the interrupt level
|
|
// assigned to a particular PCI device based on where
|
|
// it is located within the system. The DeviceNumber
|
|
// is used as an index into this table which contains
|
|
// the IRQ level assigned to that device. Note that
|
|
// Remote Devices will share an interrupt level based
|
|
// on which bus/slot they are attached.
|
|
//
|
|
// This table is initialized with 0xFF for each device
|
|
// until the correct PIRQs are read from the 82374/82375
|
|
// chipset. The PIRQ values are programmed by the firmware
|
|
// and are dynamically configurable through the ARC menu.
|
|
//
|
|
|
|
UCHAR HalpPCIPinToLineTable[32] = {
|
|
|
|
// Bus Number 0
|
|
// Device Number : (0) (1) (2) (3) (4) (5) (6) (7)
|
|
// On-board Devices : ISA/EISA ENET SCSI Not Used SLOT0 SLOT1 Not Used FASTswitch
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
|
|
// Bus Number 0
|
|
// Device Number : (8) (9) (10) (11) (12) (13) (14) (15)
|
|
// Remote Devices :
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
|
|
// Bus Number 0
|
|
// Device Number : (16) (17) (18) (19) (20) (21) (22) (23)
|
|
// Remote Devices :
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
|
|
// Bus Number 0
|
|
// Device Number : (24) (25) (26) (27) (28) (29) (30) (31)
|
|
// Remote Devices :
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
|
|
};
|
|
|
|
UCHAR HalpPIRQTable[4];
|
|
|
|
//
|
|
// This table serves to map a PCI bus number to
|
|
// one of the PIRQ lines from the 82375. This table
|
|
// is filled in during Phase 0 initialization to
|
|
// allow for dynamic reallocation of the interrupt
|
|
// routing on PCI.
|
|
//
|
|
|
|
UCHAR HalpPCIBusToPirqTable[32] = {
|
|
|
|
//
|
|
// Bus Number 0 1 2 3 4 5 6 7
|
|
//
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
//
|
|
// Bus Number 8 9 10 11 12 13 14 15
|
|
//
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
//
|
|
// Bus Number 16 17 18 19 20 21 22 23
|
|
//
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
//
|
|
// Bus Number 24 25 26 27 28 29 30 31
|
|
//
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
};
|
|
|
|
//
|
|
// Registry stuff
|
|
//
|
|
|
|
WCHAR rgzMultiFunctionAdapter[] = L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter";
|
|
WCHAR rgzConfigurationData[] = L"Configuration Data";
|
|
WCHAR rgzIdentifier[] = L"Identifier";
|
|
WCHAR rgzPCIIndetifier[] = L"PCI";
|
|
|
|
#define Is64BitBaseAddress(a) \
|
|
(((a & PCI_ADDRESS_IO_SPACE) == 0) && \
|
|
((a & PCI_ADDRESS_MEMORY_PREFETCHABLE) == PCI_TYPE_64BIT))
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function intializes global PCI bus state from the registry.
|
|
|
|
The Arc firmware is responsible for building configuration information
|
|
about the number of PCI buses on the system and nature (local vs. secondary
|
|
- across a PCI-PCI bridge) of the each bus. This state is held in
|
|
PCIRegInfo.
|
|
|
|
The maximum virtual slot number on the local (type 0 config cycle)
|
|
PCI bus is registered here, based on the machine dependent define
|
|
PCI_MAX_LOCAL_DEVICE.
|
|
|
|
The maximum number of virtual slots on a secondary bus is fixed by the
|
|
PCI Specification and is represented by PCI_MAX_DEVICES.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
|
|
VOID
|
|
HalpInitializePCIBus (
|
|
VOID
|
|
)
|
|
|
|
{
|
|
|
|
PPCI_REGISTRY_INFO PCIRegInfo;
|
|
ULONG i, d, HwType, BusNo, f;
|
|
PCI_SLOT_NUMBER SlotNumber;
|
|
PPCI_COMMON_CONFIG PciData;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
UCHAR buffer [sizeof(PPCI_REGISTRY_INFO) + 99];
|
|
PBUS_HANDLER BusHandler;
|
|
|
|
#if 0
|
|
UNICODE_STRING unicodeString, ConfigName, IdentName;
|
|
ULONG junk;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE hMFunc, hBus;
|
|
NTSTATUS status;
|
|
PWSTR p;
|
|
WCHAR wstr[8];
|
|
PKEY_VALUE_FULL_INFORMATION ValueInfo;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR Desc;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc;
|
|
|
|
//
|
|
// Search the hardware description looking for any reported
|
|
// PCI bus. The first ARC entry for a PCI bus will contain
|
|
// the PCI_REGISTRY_INFO.
|
|
|
|
RtlInitUnicodeString (&unicodeString, rgzMultiFunctionAdapter);
|
|
InitializeObjectAttributes (
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL, // handle
|
|
NULL);
|
|
|
|
|
|
status = ZwOpenKey (&hMFunc, KEY_READ, &objectAttributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
return ;
|
|
}
|
|
|
|
unicodeString.Buffer = wstr;
|
|
unicodeString.MaximumLength = sizeof (wstr);
|
|
|
|
RtlInitUnicodeString (&ConfigName, rgzConfigurationData);
|
|
RtlInitUnicodeString (&IdentName, rgzIdentifier);
|
|
|
|
ValueInfo = (PKEY_VALUE_FULL_INFORMATION) buffer;
|
|
|
|
for (i=0; TRUE; i++) {
|
|
RtlIntegerToUnicodeString (i, 10, &unicodeString);
|
|
InitializeObjectAttributes (
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hMFunc,
|
|
NULL);
|
|
|
|
status = ZwOpenKey (&hBus, KEY_READ, &objectAttributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Out of Multifunction adapter entries...
|
|
//
|
|
|
|
ZwClose (hMFunc);
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Check the Indentifier to see if this is a PCI entry
|
|
//
|
|
|
|
status = ZwQueryValueKey (
|
|
hBus,
|
|
&IdentName,
|
|
KeyValueFullInformation,
|
|
ValueInfo,
|
|
sizeof (buffer),
|
|
&junk
|
|
);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
ZwClose (hBus);
|
|
continue;
|
|
}
|
|
|
|
p = (PWSTR) ((PUCHAR) ValueInfo + ValueInfo->DataOffset);
|
|
if (p[0] != L'P' || p[1] != L'C' || p[2] != L'I' || p[3] != 0) {
|
|
ZwClose (hBus);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The first PCI entry has the PCI_REGISTRY_INFO structure
|
|
// attached to it.
|
|
//
|
|
|
|
status = ZwQueryValueKey (
|
|
hBus,
|
|
&ConfigName,
|
|
KeyValueFullInformation,
|
|
ValueInfo,
|
|
sizeof (buffer),
|
|
&junk
|
|
);
|
|
|
|
ZwClose (hBus);
|
|
if (!NT_SUCCESS(status)) {
|
|
continue ;
|
|
}
|
|
|
|
Desc = (PCM_FULL_RESOURCE_DESCRIPTOR) ((PUCHAR)
|
|
ValueInfo + ValueInfo->DataOffset);
|
|
PDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)
|
|
Desc->PartialResourceList.PartialDescriptors);
|
|
|
|
if (PDesc->Type == CmResourceTypeDeviceSpecific) {
|
|
// got it..
|
|
PCIRegInfo = (PPCI_REGISTRY_INFO) (PDesc+1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
//
|
|
// We are forcing the number of PCI
|
|
// buses in the system to be > 1 to
|
|
// workaround a problem we are having
|
|
// wrt opening and reading the registry
|
|
// at this point during the Phase 1
|
|
// initialization. This seems to work
|
|
// regardless if there are one or more
|
|
// PCI buses in the system.
|
|
//
|
|
|
|
PCIRegInfo->NoBuses = PCI_MAX_BUS_NUMBER;
|
|
PCIRegInfo->HardwareMechanism = 2;
|
|
|
|
#endif
|
|
|
|
//
|
|
// Initialize spinlock for synchronizing access to PCI space
|
|
//
|
|
|
|
KeInitializeSpinLock (&HalpPCIConfigLock);
|
|
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
|
|
|
//
|
|
// PCIRegInfo describes the system's PCI support (as indicated by the BIOS).
|
|
//
|
|
|
|
HwType = PCIRegInfo->HardwareMechanism & 0xf;
|
|
|
|
//
|
|
// Determine what PCI bus type we got
|
|
//
|
|
|
|
if (PCIRegInfo->NoBuses && HwType == 2) {
|
|
|
|
//
|
|
// Check each slot for a valid device. Which every style configuration
|
|
// space shows a valid device first will be used
|
|
//
|
|
|
|
SlotNumber.u.bits.Reserved = 0;
|
|
SlotNumber.u.bits.FunctionNumber = 0;
|
|
|
|
for (d = 0; d < PCI_MAX_DEVICES; d++) {
|
|
SlotNumber.u.bits.DeviceNumber = d;
|
|
|
|
//
|
|
// Allocate type2 and test handle for PCI bus 0.
|
|
//
|
|
|
|
HwType = 2;
|
|
BusHandler = HalpAllocateAndInitPciBusHandler (HwType, 0, TRUE);
|
|
|
|
if (HalpIsValidPCIDevice (BusHandler, SlotNumber)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Valid device not found on Type2 access for this slot.
|
|
// Reallocate the bus handler are Type1 and take a look.
|
|
//
|
|
|
|
HwType = 1;
|
|
BusHandler = HalpAllocateAndInitPciBusHandler (HwType, 0, TRUE);
|
|
|
|
if (HalpIsValidPCIDevice (BusHandler, SlotNumber)) {
|
|
break;
|
|
}
|
|
|
|
HwType = 2;
|
|
}
|
|
|
|
//
|
|
// Reset handler for PCI bus 0 to whatever style config space
|
|
// was finally decided.
|
|
//
|
|
|
|
HalpAllocateAndInitPciBusHandler (HwType, 0, FALSE);
|
|
}
|
|
|
|
//
|
|
// For each PCI bus present, allocate a handler structure and
|
|
// fill in the dispatch functions
|
|
//
|
|
|
|
do {
|
|
for (i=0; i < PCIRegInfo->NoBuses; i++) {
|
|
|
|
//
|
|
// If handler not already built, do it now
|
|
//
|
|
|
|
if (!HalpHandlerForBus (PCIBus, i)) {
|
|
HalpAllocateAndInitPciBusHandler (HwType, i, FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bus handlers for all PCI buses have been allocated, go collect
|
|
// pci bridge information.
|
|
//
|
|
|
|
} while (HalpGetPciBridgeConfig (HwType, &PCIRegInfo->NoBuses)) ;
|
|
|
|
//
|
|
// Fixup SUPPORTED_RANGES
|
|
//
|
|
|
|
HalpFixupPciSupportedRanges (PCIRegInfo->NoBuses);
|
|
|
|
//
|
|
// Look for PCI controllers which have known work-arounds, and make
|
|
// sure they are applied.
|
|
//
|
|
|
|
SlotNumber.u.bits.Reserved = 0;
|
|
for (BusNo=0; BusNo < PCIRegInfo->NoBuses; BusNo++) {
|
|
BusHandler = HalpHandlerForBus (PCIBus, BusNo);
|
|
|
|
for (d = 0; d < PCI_MAX_DEVICES; d++) {
|
|
SlotNumber.u.bits.DeviceNumber = d;
|
|
|
|
for (f = 0; f < PCI_MAX_FUNCTION; f++) {
|
|
SlotNumber.u.bits.FunctionNumber = 0;
|
|
|
|
//
|
|
// Read PCI configuration information
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, SlotNumber, PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
|
|
//
|
|
// Check for chips with known work-arounds to apply
|
|
//
|
|
|
|
if (PciData->VendorID == 0x8086 &&
|
|
PciData->DeviceID == 0x04A3 &&
|
|
PciData->RevisionID < 0x11) {
|
|
|
|
//
|
|
// 82430 PCMC controller
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x53, 2);
|
|
|
|
buffer[0] &= ~0x08; // turn off bit 3 register 0x53
|
|
buffer[1] &= ~0x01; // turn off bit 0 register 0x54
|
|
|
|
HalpWritePCIConfig (BusHandler, SlotNumber, buffer, 0x53, 2);
|
|
}
|
|
|
|
if (PciData->VendorID == 0x8086 &&
|
|
PciData->DeviceID == 0x0484 &&
|
|
PciData->RevisionID <= 3) {
|
|
|
|
//
|
|
// 82378 ISA bridge & SIO
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x41, 1);
|
|
|
|
buffer[0] &= ~0x1; // turn off bit 0 register 0x41
|
|
|
|
HalpWritePCIConfig (BusHandler, SlotNumber, buffer, 0x41, 1);
|
|
}
|
|
|
|
} // next function
|
|
} // next device
|
|
} // next bus
|
|
|
|
}
|
|
|
|
PBUS_HANDLER
|
|
HalpAllocateAndInitPciBusHandler (
|
|
IN ULONG HwType,
|
|
IN ULONG BusNo,
|
|
IN BOOLEAN TestAllocation
|
|
)
|
|
{
|
|
PBUS_HANDLER Bus;
|
|
PPCIPBUSDATA BusData;
|
|
|
|
Bus = HalpAllocateBusHandler (
|
|
PCIBus, // Interface type
|
|
PCIConfiguration, // Bus data type
|
|
BusNo, // bus number
|
|
Internal, // parent bus
|
|
0, // parent bus number
|
|
sizeof (PCIPBUSDATA) // sizeof bus specific buffer
|
|
);
|
|
|
|
//
|
|
// Fill in PCI handlers
|
|
//
|
|
|
|
Bus->GetBusData = (PGETSETBUSDATA) HalpGetPCIData;
|
|
Bus->SetBusData = (PGETSETBUSDATA) HalpSetPCIData;
|
|
Bus->GetInterruptVector = (PGETINTERRUPTVECTOR) HalpGetPCIInterruptVector;
|
|
Bus->AdjustResourceList = (PADJUSTRESOURCELIST) HalpAdjustPCIResourceList;
|
|
Bus->AssignSlotResources = (PASSIGNSLOTRESOURCES) HalpAssignPCISlotResources;
|
|
Bus->TranslateBusAddress = (PTRANSLATEBUSADDRESS) HalpTranslatePCIBusAddress;
|
|
Bus->BusAddresses->Dma.Limit = 0;
|
|
|
|
BusData = (PPCIPBUSDATA) Bus->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) HalpPCIPinToLine;
|
|
|
|
//
|
|
// Set defaults
|
|
//
|
|
|
|
BusData->MaxDevice = PCI_MAX_DEVICES;
|
|
BusData->GetIrqRange = (PciIrqRange) HalpGetPCIIrqRange;
|
|
|
|
RtlInitializeBitMap (&BusData->DeviceConfigured,
|
|
BusData->ConfiguredBits, 256);
|
|
|
|
switch (HwType) {
|
|
case 1:
|
|
//
|
|
// Initialize access port information for Type1 handlers
|
|
//
|
|
|
|
BusData->Config.Type1.Address = PCI_TYPE1_ADDR_PORT;
|
|
BusData->Config.Type1.Data = PCI_TYPE1_DATA_PORT;
|
|
break;
|
|
|
|
case 2:
|
|
//
|
|
// Initialize access port information for Type2 handlers
|
|
//
|
|
|
|
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 = 0xf;
|
|
|
|
break;
|
|
|
|
default:
|
|
// unsupport type
|
|
break;
|
|
}
|
|
|
|
return Bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function returns the PCI bus data for a device.
|
|
|
|
Arguments:
|
|
|
|
BusNumber - Indicates which bus.
|
|
|
|
VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word)
|
|
|
|
Buffer - Supplies the space to store the data.
|
|
|
|
Length - Supplies a count in bytes of the maximum amount to return.
|
|
|
|
Return Value:
|
|
|
|
Returns the amount of data stored into the buffer.
|
|
|
|
If this PCI slot has never been set, then the configuration information
|
|
returned is zeroed.
|
|
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpGetPCIData (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
|
|
{
|
|
PPCI_COMMON_CONFIG PciData;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
ULONG Len;
|
|
PPCIPBUSDATA BusData;
|
|
|
|
|
|
if (Length > sizeof (PCI_COMMON_CONFIG)) {
|
|
Length = sizeof (PCI_COMMON_CONFIG);
|
|
}
|
|
|
|
Len = 0;
|
|
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
|
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
|
|
//
|
|
// The user did not request any data from the common
|
|
// header. Verify the PCI device exists, then continue
|
|
// in the device specific area.
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, Slot, PciData, 0, sizeof(ULONG));
|
|
|
|
//
|
|
// Check for non-existent bus
|
|
//
|
|
|
|
if (PciData->VendorID == 0) {
|
|
return 0; // Requested bus does not exist. Return no data.
|
|
}
|
|
|
|
//
|
|
// Check for invalid slot
|
|
//
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID) {
|
|
return 2;
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Caller requested at least some data within the
|
|
// common header. Read the whole header, effect the
|
|
// fields we need to and then copy the user's requested
|
|
// bytes from the header
|
|
//
|
|
|
|
BusData = (PPCIPBUSDATA) BusHandler->BusData;
|
|
|
|
//
|
|
// Read this PCI devices slot data
|
|
//
|
|
|
|
Len = PCI_COMMON_HDR_LENGTH;
|
|
HalpReadPCIConfig (BusHandler, Slot, PciData, 0, Len);
|
|
|
|
//
|
|
// Check for non-existent bus
|
|
//
|
|
|
|
if (PciData->VendorID == 0x00) {
|
|
Len = 0; // Requested bus does not exist. Return no data.
|
|
}
|
|
|
|
//
|
|
// Check for invalid slot
|
|
//
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID ||
|
|
PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) {
|
|
PciData->VendorID = PCI_INVALID_VENDORID;
|
|
Len = 2; // only return invalid id
|
|
|
|
} else {
|
|
|
|
BusData->CommonData.Pin2Line (BusHandler, RootHandler, Slot, PciData);
|
|
}
|
|
|
|
|
|
//
|
|
// Copy whatever data overlaps into the callers buffer
|
|
//
|
|
|
|
if (Len < Offset) {
|
|
// no data at caller's buffer
|
|
return 0;
|
|
}
|
|
|
|
Len -= Offset;
|
|
if (Len > Length) {
|
|
Len = Length;
|
|
}
|
|
|
|
RtlMoveMemory(Buffer, iBuffer + Offset, Len);
|
|
|
|
Offset += Len;
|
|
Buffer += Len;
|
|
Length -= Len;
|
|
|
|
}
|
|
|
|
if (Length) {
|
|
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
|
|
//
|
|
// The remaining Buffer comes from the Device Specific
|
|
// area - put on the kitten gloves and read from it.
|
|
//
|
|
// Specific read/writes to the PCI device specific area
|
|
// are guarenteed:
|
|
//
|
|
// Not to read/write any byte outside the area specified
|
|
// by the caller. (this may cause WORD or BYTE references
|
|
// to the area in order to read the non-dword aligned
|
|
// ends of the request)
|
|
//
|
|
// To use a WORD access if the requested length is exactly
|
|
// a WORD long.
|
|
//
|
|
// To use a BYTE access if the requested length is exactly
|
|
// a BYTE long.
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, Slot, Buffer, Offset, Length);
|
|
Len += Length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Len;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function returns the Pci bus data for a device.
|
|
|
|
Arguments:
|
|
|
|
|
|
VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word)
|
|
|
|
Buffer - Supplies the space to store the data.
|
|
|
|
Length - Supplies a count in bytes of the maximum amount to return.
|
|
|
|
Return Value:
|
|
|
|
Returns the amount of data stored into the buffer.
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpSetPCIData (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
|
|
{
|
|
PPCI_COMMON_CONFIG PciData, PciData2;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
UCHAR iBuffer2[PCI_COMMON_HDR_LENGTH];
|
|
ULONG Len;
|
|
PPCIPBUSDATA BusData;
|
|
|
|
|
|
if (Length > sizeof (PCI_COMMON_CONFIG)) {
|
|
Length = sizeof (PCI_COMMON_CONFIG);
|
|
}
|
|
|
|
|
|
Len = 0;
|
|
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
|
PciData2 = (PPCI_COMMON_CONFIG) iBuffer2;
|
|
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
|
|
//
|
|
// The user did not request any data from the common
|
|
// header. Verify the PCI device exists, then continue in
|
|
// the device specific area.
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, Slot, PciData, 0, sizeof(ULONG));
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID || PciData->VendorID == 0x00) {
|
|
return 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Caller requested to set at least some data within the
|
|
// common header.
|
|
//
|
|
|
|
Len = PCI_COMMON_HDR_LENGTH;
|
|
HalpReadPCIConfig (BusHandler, Slot, PciData, 0, Len);
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID ||
|
|
PciData->VendorID == 0x00 ||
|
|
PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) {
|
|
|
|
//
|
|
// no device, or header type unkown
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Set this device as configured
|
|
//
|
|
|
|
BusData = (PPCIPBUSDATA) BusHandler->BusData;
|
|
|
|
//
|
|
// Copy COMMON_HDR values to buffer2, then overlay callers changes.
|
|
//
|
|
|
|
RtlMoveMemory (iBuffer2, iBuffer, Len);
|
|
BusData->CommonData.Pin2Line (BusHandler, RootHandler, Slot, PciData2);
|
|
|
|
Len -= Offset;
|
|
if (Len > Length) {
|
|
Len = Length;
|
|
}
|
|
|
|
RtlMoveMemory (iBuffer2+Offset, Buffer, Len);
|
|
|
|
// in case interrupt line or pin was editted
|
|
//BusData->CommonData.Line2Pin (BusHandler, RootHandler, Slot, PciData2, PciData);
|
|
|
|
//
|
|
// Set new PCI configuration
|
|
//
|
|
|
|
HalpWritePCIConfig (BusHandler, Slot, iBuffer2+Offset, Offset, Len);
|
|
|
|
Offset += Len;
|
|
Buffer += Len;
|
|
Length -= Len;
|
|
}
|
|
|
|
if (Length) {
|
|
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
|
|
//
|
|
// The remaining Buffer comes from the Device Specific
|
|
// area - put on the kitten gloves and write it
|
|
//
|
|
// Specific read/writes to the PCI device specific area
|
|
// are guarenteed:
|
|
//
|
|
// Not to read/write any byte outside the area specified
|
|
// by the caller. (this may cause WORD or BYTE references
|
|
// to the area in order to read the non-dword aligned
|
|
// ends of the request)
|
|
//
|
|
// To use a WORD access if the requested length is exactly
|
|
// a WORD long.
|
|
//
|
|
// To use a BYTE access if the requested length is exactly
|
|
// a BYTE long.
|
|
//
|
|
|
|
HalpWritePCIConfig (BusHandler, Slot, Buffer, Offset, Length);
|
|
Len += Length;
|
|
}
|
|
}
|
|
|
|
return Len;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
VOID
|
|
HalpReadPCIConfig (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
|
|
|
|
//
|
|
// Read the slot, if it's valid.
|
|
// Otherwise, return an Invalid VendorId for a invalid slot on an existing bus
|
|
// or a null (zero) buffer if we have a non-existant bus.
|
|
//
|
|
|
|
switch (HalpValidPCISlot (BusHandler, Slot)) {
|
|
|
|
case ValidSlot:
|
|
|
|
//
|
|
// Issue PCI Config Read Cycle(s)
|
|
//
|
|
|
|
if (!HalpPCIConfig (BusHandler->BusNumber, Slot, (PUCHAR) Buffer, Offset, Length, PCIConfigHandler.ConfigRead)) {
|
|
RtlFillMemory (Buffer, Length, (UCHAR) -1);
|
|
}
|
|
break;
|
|
|
|
case InvalidSlot:
|
|
|
|
//
|
|
// Invalid SlotID return no data (Invalid Slot ID = 0xFFFF)
|
|
//
|
|
|
|
RtlFillMemory (Buffer, Length, (UCHAR) -1);
|
|
break ;
|
|
|
|
case InvalidBus:
|
|
|
|
//
|
|
// Invalid Bus, return return no data
|
|
//
|
|
|
|
RtlFillMemory (Buffer, Length, (UCHAR) 0);
|
|
break ;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
VOID
|
|
HalpWritePCIConfig (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
|
|
if (HalpValidPCISlot (BusHandler, Slot) != ValidSlot) {
|
|
|
|
//
|
|
// Invalid SlotID do nothing
|
|
//
|
|
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Issue PCI Config Write Cycle(s)
|
|
//
|
|
|
|
HalpPCIConfig ( BusHandler->BusNumber,
|
|
Slot,
|
|
(PUCHAR)Buffer,
|
|
Offset,
|
|
Length,
|
|
PCIConfigHandler.ConfigWrite);
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
HalpIsValidPCIDevice (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads the device configuration data for the given slot and
|
|
returns TRUE if the configuration data appears to be valid for
|
|
a PCI device; otherwise returns FALSE.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Bus to check
|
|
Slot - Slot to check
|
|
|
|
--*/
|
|
|
|
{
|
|
PPCI_COMMON_CONFIG PciData;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
//ULONG i, j;
|
|
|
|
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
|
|
|
//
|
|
// Read device common header
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, Slot, PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
|
|
//
|
|
// Valid device header?
|
|
//
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID ||
|
|
PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// Check fields for reasonable values
|
|
//
|
|
|
|
if ((PciData->u.type0.InterruptPin && PciData->u.type0.InterruptPin > 4) ||
|
|
(PciData->u.type0.InterruptLine & 0x70)) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (i=0; i < PCI_TYPE0_ADDRESSES; i++) {
|
|
j = PciData->u.type0.BaseAddresses[i];
|
|
|
|
if (j & PCI_ADDRESS_IO_SPACE) {
|
|
if (j > 0xffff) {
|
|
// IO port > 64k?
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
if (j > 0xf && j < 0x80000) {
|
|
// Mem address < 0x8000h?
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (Is64BitBaseAddress(j)) {
|
|
i += 1;
|
|
}
|
|
}
|
|
#endif
|
|
//
|
|
// Guess it's a valid device..
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
VALID_SLOT
|
|
HalpValidPCISlot (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot
|
|
)
|
|
{
|
|
PCI_SLOT_NUMBER Slot2;
|
|
UCHAR HeaderType;
|
|
ULONG i;
|
|
PCI_CONFIGURATION_TYPES PciConfigType;
|
|
PPCIPBUSDATA BusData;
|
|
|
|
BusData = (PPCIPBUSDATA) BusHandler->BusData;
|
|
|
|
//
|
|
// Get the config cycle type for the proposed bus.
|
|
// (PciConfigTypeInvalid indicates a non-existent bus.)
|
|
//
|
|
|
|
PciConfigType = HalpPCIConfigCycleType(BusHandler->BusNumber, Slot);
|
|
|
|
//
|
|
// The number of devices allowed on a local PCI bus may be different
|
|
// than that across a PCI-PCI bridge.
|
|
//
|
|
|
|
switch(PciConfigType) {
|
|
|
|
case PciConfigType0:
|
|
|
|
if (Slot.u.bits.DeviceNumber > BusData->MaxDevice) {
|
|
return InvalidSlot;
|
|
}
|
|
break;
|
|
|
|
case PciConfigType1:
|
|
|
|
if (Slot.u.bits.DeviceNumber > BusData->MaxDevice) {
|
|
return InvalidSlot;
|
|
}
|
|
break;
|
|
|
|
case PciConfigTypeInvalid:
|
|
return InvalidBus;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Check function number
|
|
//
|
|
|
|
if (Slot.u.bits.FunctionNumber == 0) {
|
|
return ValidSlot;
|
|
}
|
|
|
|
//
|
|
// Non zero function numbers are only supported if the
|
|
// device has the PCI_MULTIFUNCTION bit set in it's header
|
|
//
|
|
|
|
i = Slot.u.bits.DeviceNumber;
|
|
|
|
//
|
|
// Read DeviceNumber, Function zero, to determine if the
|
|
// PCI supports multifunction devices
|
|
//
|
|
|
|
Slot2 = Slot;
|
|
Slot2.u.bits.FunctionNumber = 0;
|
|
|
|
HalpReadPCIConfig (BusHandler,
|
|
Slot2,
|
|
&HeaderType,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, HeaderType),
|
|
sizeof(UCHAR) );
|
|
|
|
if (!(HeaderType & PCI_MULTIFUNCTION) || (HeaderType == 0xFF)) {
|
|
|
|
//
|
|
// this device doesn't exists or doesn't support MULTIFUNCTION types
|
|
//
|
|
|
|
return InvalidSlot;
|
|
|
|
}
|
|
|
|
return ValidSlot;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
|
|
BOOLEAN
|
|
HalpPCIConfig (
|
|
IN ULONG BusNumber,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length,
|
|
IN FncConfigIO *ConfigIO
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
ULONG i;
|
|
PCI_CONFIG_ADDR PciConfigAddrData;
|
|
ULONG OldIntCtrl;
|
|
BOOLEAN ConfigOk;
|
|
|
|
|
|
ConfigOk = TRUE;
|
|
|
|
//
|
|
// Acquire Spin Lock
|
|
//
|
|
|
|
KeAcquireSpinLock (&HalpPCIConfigLock, &OldIrql);
|
|
|
|
//
|
|
// Force PCI read errors to be asynchronous (interrupts)
|
|
// as opposed to synchronous (bus errors)
|
|
//
|
|
|
|
OldIntCtrl = READ_REGISTER_ULONG(HalpPmpIntCtrl);
|
|
|
|
if ( PCR->Prcb->Number == 0 ) {
|
|
WRITE_REGISTER_ULONG(HalpPmpIntCtrl, OldIntCtrl | INT_CTRL_SEC1 | INT_CTRL_PIEA);
|
|
} else {
|
|
WRITE_REGISTER_ULONG(HalpPmpIntCtrl, OldIntCtrl | INT_CTRL_SEC1 | INT_CTRL_PIEB);
|
|
}
|
|
|
|
//
|
|
// Program PciConfigAddr register
|
|
//
|
|
|
|
PciConfigAddrData.Type = (BusNumber ? PciConfigType1 : PciConfigType0);
|
|
PciConfigAddrData.BusNumber = BusNumber;
|
|
PciConfigAddrData.DeviceNumber = (BusNumber ? Slot.u.bits.DeviceNumber : 0);
|
|
PciConfigAddrData.FunctionNumber = Slot.u.bits.FunctionNumber;
|
|
PciConfigAddrData.Reserved1 = 0;
|
|
PciConfigAddrData.Reserved2 = 0;
|
|
|
|
WRITE_REGISTER_ULONG(HalpPmpPciConfigAddr, *((PULONG) &PciConfigAddrData));
|
|
|
|
//
|
|
// Select PCI device using PciConfigSelect
|
|
// (IDSEL) register which is software programmable
|
|
// versus having the hardware decode the DeviceNumber
|
|
// field of the Configuration Address
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(HalpPmpPciConfigSelect, BusNumber ? 0 : HalpPciConfigSelectDecodeTable[Slot.u.bits.DeviceNumber]);
|
|
|
|
//
|
|
// Issue PCI Configuration Cycles
|
|
// efficiently using word, hword, and
|
|
// byte quantities based on the transfer
|
|
// length and the buffer offset.
|
|
//
|
|
|
|
while (Length) {
|
|
|
|
i = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)];
|
|
i = ConfigIO[i] (Buffer, Offset);
|
|
|
|
//
|
|
// Exit loop because we got a bus
|
|
// error during a PCI config cycle
|
|
//
|
|
|
|
if (!i) {
|
|
|
|
ConfigOk = FALSE;
|
|
break;
|
|
|
|
}
|
|
|
|
Offset += i;
|
|
Buffer += i;
|
|
Length -= i;
|
|
|
|
}
|
|
|
|
//
|
|
// Deselect PCI device
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(HalpPmpPciConfigSelect, 0);
|
|
|
|
//
|
|
// Restore interrupt register to treat read
|
|
// errors synchronously (as bus errors)
|
|
//
|
|
|
|
WRITE_REGISTER_ULONG(HalpPmpIntCtrl, OldIntCtrl);
|
|
|
|
//
|
|
// Release Spin Lock
|
|
//
|
|
|
|
KeReleaseSpinLock (&HalpPCIConfigLock, OldIrql);
|
|
|
|
return ConfigOk;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
PCI_CONFIGURATION_TYPES
|
|
HalpPCIConfigCycleType (
|
|
IN ULONG BusNumber,
|
|
IN PCI_SLOT_NUMBER Slot
|
|
)
|
|
|
|
{
|
|
|
|
|
|
//
|
|
// Determine if Type0, Type1, or Invalid
|
|
//
|
|
|
|
if (BusNumber < 0 || BusNumber > PCI_MAX_BUS_NUMBER) {
|
|
return PciConfigTypeInvalid;
|
|
} else if (BusNumber) {
|
|
return PciConfigType1;
|
|
} else {
|
|
return PciConfigType0;
|
|
}
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpPCIReadUchar (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
)
|
|
{
|
|
|
|
ULONG DataLength;
|
|
|
|
|
|
|
|
//
|
|
// Clear bus error flag which will be set
|
|
// by the interrupt handler in the event we
|
|
// get an interrupt caused by a master abort
|
|
//
|
|
|
|
HalpPciRmaConfigErrorOccurred = 0;
|
|
|
|
//
|
|
// Do PCI Configuration BYTE Read
|
|
//
|
|
|
|
*Buffer = READ_REGISTER_UCHAR ((PUCHAR) ((ULONG)HalpPmpPciConfigSpace + Offset));
|
|
DataLength = sizeof(UCHAR);
|
|
|
|
//
|
|
// If we ecountered a master target abort during the
|
|
// configuration read, then no device exists in that slot
|
|
//
|
|
|
|
if (HalpPciRmaConfigErrorOccurred == 1) {
|
|
|
|
*Buffer = 0xFF;
|
|
DataLength = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Now reset the bus error flag
|
|
// to -1 that way if a master abort
|
|
// occurs for some other reason than
|
|
// during a pci config probe, we will
|
|
// deal with it in the handler correctly.
|
|
//
|
|
|
|
HalpPciRmaConfigErrorOccurred = 2;
|
|
|
|
return DataLength;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpPCIReadUshort (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
)
|
|
{
|
|
ULONG DataLength;
|
|
|
|
|
|
//
|
|
// Clear bus error flag which will be set
|
|
// by the interrupt handler in the event we
|
|
// get an interrupt caused by a master abort
|
|
//
|
|
|
|
HalpPciRmaConfigErrorOccurred = 0;
|
|
|
|
//
|
|
// Do PCI Configuration HWORD Read
|
|
//
|
|
|
|
*((PUSHORT) Buffer) = READ_REGISTER_USHORT ((PUSHORT) ((ULONG)HalpPmpPciConfigSpace + Offset));
|
|
DataLength = sizeof(USHORT);
|
|
|
|
//
|
|
// If we ecountered a master target abort during the
|
|
// configuration read, then no device exists in that slot
|
|
//
|
|
|
|
if (HalpPciRmaConfigErrorOccurred == 1) {
|
|
|
|
*((PUSHORT) Buffer) = 0xFFFF;
|
|
DataLength = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Now reset the bus error flag
|
|
// to -1 that way if a master abort
|
|
// occurs for some other reason than
|
|
// during a pci config probe, we will
|
|
// deal with it in the handler correctly.
|
|
//
|
|
|
|
HalpPciRmaConfigErrorOccurred = 2;
|
|
|
|
return DataLength;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpPCIReadUlong (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
)
|
|
{
|
|
|
|
ULONG DataLength;
|
|
|
|
|
|
//
|
|
// Clear bus error flag which will be set
|
|
// by the interrupt handler in the event we
|
|
// get an interrupt caused by a master abort
|
|
//
|
|
|
|
HalpPciRmaConfigErrorOccurred = 0;
|
|
|
|
//
|
|
// Do PCI Configuration WORD Read
|
|
//
|
|
|
|
*((PULONG) Buffer) = READ_REGISTER_ULONG ((PULONG) ((ULONG)HalpPmpPciConfigSpace + Offset));
|
|
DataLength = sizeof(ULONG);
|
|
|
|
//
|
|
// If we ecountered a master target abort during the
|
|
// configuration read, then no device exists in that slot
|
|
//
|
|
|
|
if (HalpPciRmaConfigErrorOccurred == 1) {
|
|
|
|
*((PULONG) Buffer) = 0xFFFFFFFF;
|
|
DataLength = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Now reset the bus error flag
|
|
// to -1 that way if a master abort
|
|
// occurs for some other reason than
|
|
// during a pci config probe, we will
|
|
// deal with it in the handler correctly.
|
|
//
|
|
|
|
HalpPciRmaConfigErrorOccurred = 2;
|
|
|
|
return DataLength;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpPCIWriteUchar (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
)
|
|
{
|
|
|
|
|
|
//
|
|
// Do PCI Configuration BYTE Write
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR ((PUCHAR) ((ULONG)HalpPmpPciConfigSpace + Offset), *Buffer);
|
|
|
|
return sizeof (UCHAR);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpPCIWriteUshort (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
)
|
|
{
|
|
|
|
//
|
|
// Do PCI Configuration BYTE Write
|
|
//
|
|
|
|
WRITE_REGISTER_USHORT ((PUSHORT) ((ULONG)HalpPmpPciConfigSpace + Offset), *((PUSHORT)Buffer));
|
|
|
|
return sizeof (USHORT);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HalpPCIWriteUlong (
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
)
|
|
{
|
|
|
|
//
|
|
// Do PCI Configuration BYTE Write
|
|
//
|
|
|
|
WRITE_REGISTER_ULONG ((PULONG) ((ULONG)HalpPmpPciConfigSpace + Offset), *((PULONG)Buffer));
|
|
|
|
return sizeof (ULONG);
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function maps the device's InterruptPin to an InterruptLine
|
|
value.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
VOID
|
|
HalpPCIPinToLine (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER SlotNumber,
|
|
IN PPCI_COMMON_CONFIG PciData
|
|
)
|
|
|
|
{
|
|
|
|
ULONG BusNumber;
|
|
|
|
BusNumber = (ULONG)BusHandler->BusNumber;
|
|
|
|
|
|
//
|
|
// Devices (or device functions) that don't use an interrupt pin must
|
|
// put a zero in this configuration register.
|
|
//
|
|
|
|
if (!PciData->u.type0.InterruptPin) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// On NeTpower machines, the values in this register correspond to
|
|
// IRQ numbers (0-15) of the standard 8259 interrupt controller contained
|
|
// in the 82374.
|
|
//
|
|
|
|
if (PciData->u.type0.InterruptLine != HalpPIRQTable[0] &&
|
|
PciData->u.type0.InterruptLine != HalpPIRQTable[1] &&
|
|
PciData->u.type0.InterruptLine != HalpPIRQTable[2] &&
|
|
PciData->u.type0.InterruptLine != HalpPIRQTable[3] )
|
|
|
|
PciData->u.type0.InterruptLine = ( (BusNumber)
|
|
? HalpPCIBusToPirqTable[BusNumber]
|
|
: HalpPCIPinToLineTable[SlotNumber.u.bits.DeviceNumber] );
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function maps the device's InterruptPin to an InterruptLine
|
|
value.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
VOID
|
|
HalpPCILineToPin (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER SlotNumber,
|
|
IN PPCI_COMMON_CONFIG PciNewData,
|
|
IN PPCI_COMMON_CONFIG PciOldData
|
|
)
|
|
|
|
{
|
|
|
|
ULONG BusNumber;
|
|
|
|
BusNumber = (ULONG)BusHandler->BusNumber;
|
|
|
|
|
|
if (!PciNewData->u.type0.InterruptPin) {
|
|
return;
|
|
}
|
|
|
|
if ( (PciNewData->u.type0.InterruptLine != PciOldData->u.type0.InterruptLine) ||
|
|
(PciNewData->u.type0.InterruptPin != PciOldData->u.type0.InterruptPin) ) {
|
|
|
|
#if 0
|
|
//
|
|
// Change the Pin2Line table
|
|
//
|
|
|
|
if (BusNumber) {
|
|
HalpPCIBusToPirqTable [BusNumber] = PciNewData->u.type0.InterruptLine;
|
|
} else {
|
|
HalpPCIPinToLineTable [SlotNumber.u.bits.DeviceNumber] = PciNewData->u.type0.InterruptLine;
|
|
}
|
|
|
|
//
|
|
// Now re-program the PIRQ registers
|
|
//
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads the targeted device to determine it's required resources.
|
|
Calls IoAssignResources to allocate them.
|
|
Sets the targeted device with it's assigned resoruces
|
|
and returns the assignments to the caller.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or error
|
|
|
|
--*/
|
|
|
|
NTSTATUS
|
|
HalpAssignPCISlotResources (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PUNICODE_STRING RegistryPath,
|
|
IN PUNICODE_STRING DriverClassName OPTIONAL,
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|
IN ULONG Slot,
|
|
IN OUT PCM_RESOURCE_LIST *pAllocatedResources
|
|
)
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PUCHAR WorkingPool;
|
|
PPCI_COMMON_CONFIG PciData, PciOrigData, PciData2;
|
|
PCI_SLOT_NUMBER PciSlot;
|
|
PPCIPBUSDATA BusData;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST CompleteList;
|
|
PIO_RESOURCE_DESCRIPTOR Descriptor;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor;
|
|
ULONG BusNumber;
|
|
ULONG i, j, m, length, memtype;
|
|
ULONG NoBaseAddress, RomIndex;
|
|
PULONG BaseAddress[PCI_TYPE0_ADDRESSES + 1];
|
|
PULONG OrigAddress[PCI_TYPE0_ADDRESSES + 1];
|
|
BOOLEAN Match, EnableRomBase;
|
|
|
|
|
|
*pAllocatedResources = NULL;
|
|
PciSlot = *((PPCI_SLOT_NUMBER) &Slot);
|
|
BusNumber = BusHandler->BusNumber;
|
|
BusData = (PPCIPBUSDATA) BusHandler->BusData;
|
|
|
|
//
|
|
// Allocate some pool for working space
|
|
//
|
|
|
|
i = sizeof (IO_RESOURCE_REQUIREMENTS_LIST) +
|
|
sizeof (IO_RESOURCE_DESCRIPTOR) * (PCI_TYPE0_ADDRESSES + 2) * 2 +
|
|
PCI_COMMON_HDR_LENGTH * 3;
|
|
|
|
WorkingPool = (PUCHAR) ExAllocatePool (PagedPool, i);
|
|
if (!WorkingPool) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Zero initialize pool, and get pointers into memory
|
|
//
|
|
|
|
RtlZeroMemory (WorkingPool, i);
|
|
CompleteList = (PIO_RESOURCE_REQUIREMENTS_LIST) WorkingPool;
|
|
PciData = (PPCI_COMMON_CONFIG) (WorkingPool + i - PCI_COMMON_HDR_LENGTH * 3);
|
|
PciData2 = (PPCI_COMMON_CONFIG) (WorkingPool + i - PCI_COMMON_HDR_LENGTH * 2);
|
|
PciOrigData = (PPCI_COMMON_CONFIG) (WorkingPool + i - PCI_COMMON_HDR_LENGTH * 1);
|
|
|
|
//
|
|
// Read the PCI device's configuration
|
|
//
|
|
|
|
HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID) {
|
|
ExFreePool (WorkingPool);
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Make a copy of the device's current settings
|
|
//
|
|
|
|
RtlMoveMemory (PciOrigData, PciData, PCI_COMMON_HDR_LENGTH);
|
|
|
|
//
|
|
// Initialize base addresses base on configuration data type
|
|
//
|
|
|
|
switch (PCI_CONFIG_TYPE(PciData)) {
|
|
case 0 :
|
|
NoBaseAddress = PCI_TYPE0_ADDRESSES+1;
|
|
for (j=0; j < PCI_TYPE0_ADDRESSES; j++) {
|
|
BaseAddress[j] = &PciData->u.type0.BaseAddresses[j];
|
|
OrigAddress[j] = &PciOrigData->u.type0.BaseAddresses[j];
|
|
}
|
|
BaseAddress[j] = &PciData->u.type0.ROMBaseAddress;
|
|
OrigAddress[j] = &PciOrigData->u.type0.ROMBaseAddress;
|
|
RomIndex = j;
|
|
break;
|
|
case 1:
|
|
NoBaseAddress = PCI_TYPE1_ADDRESSES+1;
|
|
for (j=0; j < PCI_TYPE1_ADDRESSES; j++) {
|
|
BaseAddress[j] = &PciData->u.type1.BaseAddresses[j];
|
|
OrigAddress[j] = &PciOrigData->u.type1.BaseAddresses[j];
|
|
}
|
|
BaseAddress[j] = &PciData->u.type1.ROMBaseAddress;
|
|
OrigAddress[j] = &PciOrigData->u.type1.ROMBaseAddress;
|
|
RomIndex = j;
|
|
break;
|
|
|
|
default:
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// If the BIOS doesn't have the device's ROM enabled, then we won't
|
|
// enable it either. Remove it from the list.
|
|
//
|
|
|
|
EnableRomBase = TRUE;
|
|
if (!(*BaseAddress[RomIndex] & PCI_ROMADDRESS_ENABLED)) {
|
|
ASSERT (RomIndex+1 == NoBaseAddress);
|
|
EnableRomBase = FALSE;
|
|
NoBaseAddress -= 1;
|
|
}
|
|
|
|
//
|
|
// Set resources to all bits on to see what type of resources
|
|
// are required.
|
|
//
|
|
|
|
for (j=0; j < NoBaseAddress; j++) {
|
|
*BaseAddress[j] = 0xFFFFFFFF;
|
|
}
|
|
|
|
//
|
|
// Enable Memory and IO space accesses
|
|
//
|
|
|
|
PciData->Command &= ~(PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE);
|
|
*BaseAddress[RomIndex] &= ~PCI_ROMADDRESS_ENABLED;
|
|
HalpWritePCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
|
|
// note type0 & type1 overlay ROMBaseAddress, InterruptPin, and InterruptLine
|
|
BusData->CommonData.Pin2Line (BusHandler, RootHandler, PciSlot, PciData);
|
|
|
|
//
|
|
// Build an IO_RESOURCE_REQUIREMENTS_LIST for the PCI device
|
|
//
|
|
|
|
CompleteList->InterfaceType = PCIBus;
|
|
CompleteList->BusNumber = BusNumber;
|
|
CompleteList->SlotNumber = Slot;
|
|
CompleteList->AlternativeLists = 1;
|
|
|
|
CompleteList->List[0].Version = 1;
|
|
CompleteList->List[0].Revision = 1;
|
|
|
|
Descriptor = CompleteList->List[0].Descriptors;
|
|
|
|
//
|
|
// If PCI device has an interrupt resource, add it
|
|
//
|
|
|
|
if (PciData->u.type0.InterruptPin) {
|
|
|
|
CompleteList->List[0].Count++;
|
|
|
|
Descriptor->Option = 0;
|
|
Descriptor->Type = CmResourceTypeInterrupt;
|
|
Descriptor->ShareDisposition = CmResourceShareShared;
|
|
Descriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
|
|
// Fill in any vector here - we'll pick it back up in
|
|
// HalAdjustResourceList and adjust it to it's allowed settings
|
|
Descriptor->u.Interrupt.MinimumVector = 0;
|
|
Descriptor->u.Interrupt.MaximumVector = 0xff;
|
|
Descriptor++;
|
|
|
|
}
|
|
|
|
//
|
|
// Add a memory/port resoruce for each PCI resource
|
|
//
|
|
|
|
// Clear ROM reserved bits
|
|
|
|
*BaseAddress[RomIndex] &= ~0x7FF;
|
|
|
|
for (j=0; j < NoBaseAddress; j++) {
|
|
if (*BaseAddress[j]) {
|
|
i = *BaseAddress[j];
|
|
|
|
// scan for first set bit, that's the length & alignment
|
|
length = 1 << (i & PCI_ADDRESS_IO_SPACE ? 2 : 4);
|
|
while (!(i & length) && length) {
|
|
length <<= 1;
|
|
}
|
|
|
|
// scan for last set bit, that's the maxaddress + 1
|
|
for (m = length; i & m; m <<= 1) ;
|
|
m--;
|
|
|
|
// check for hosed PCI configuration requirements
|
|
if (length & ~m) {
|
|
|
|
// the device is in error - punt. don't allow this
|
|
// resource any option - it either gets set to whatever
|
|
// bits it was able to return, or it doesn't get set.
|
|
|
|
if (i & PCI_ADDRESS_IO_SPACE) {
|
|
m = i & ~0x3;
|
|
Descriptor->u.Port.MinimumAddress.LowPart = m;
|
|
} else {
|
|
m = i & ~0xf;
|
|
Descriptor->u.Memory.MinimumAddress.LowPart = m;
|
|
}
|
|
|
|
m += length; // max address is min address + length
|
|
}
|
|
|
|
//
|
|
// Add requested resource
|
|
//
|
|
|
|
Descriptor->Option = 0;
|
|
if (i & PCI_ADDRESS_IO_SPACE) {
|
|
memtype = 0;
|
|
|
|
if (!Is64BitBaseAddress(i) &&
|
|
PciOrigData->Command & PCI_ENABLE_IO_SPACE) {
|
|
|
|
//
|
|
// The IO range is/was already enabled at some location, add that
|
|
// as it's preferred setting.
|
|
//
|
|
|
|
Descriptor->Type = CmResourceTypePort;
|
|
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
Descriptor->Flags = CM_RESOURCE_PORT_IO;
|
|
Descriptor->Option = IO_RESOURCE_PREFERRED;
|
|
|
|
Descriptor->u.Port.Length = length;
|
|
Descriptor->u.Port.Alignment = length;
|
|
Descriptor->u.Port.MinimumAddress.LowPart = *OrigAddress[j] & ~0x3;
|
|
Descriptor->u.Port.MaximumAddress.LowPart =
|
|
Descriptor->u.Port.MinimumAddress.LowPart + length;
|
|
|
|
CompleteList->List[0].Count++;
|
|
Descriptor++;
|
|
|
|
Descriptor->Option = IO_RESOURCE_ALTERNATIVE;
|
|
}
|
|
|
|
//
|
|
// Add this IO range
|
|
//
|
|
|
|
Descriptor->Type = CmResourceTypePort;
|
|
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
Descriptor->Flags = CM_RESOURCE_PORT_IO;
|
|
|
|
Descriptor->u.Port.Length = length;
|
|
Descriptor->u.Port.Alignment = length;
|
|
Descriptor->u.Port.MaximumAddress.LowPart = m;
|
|
|
|
} else {
|
|
|
|
memtype = i & PCI_ADDRESS_MEMORY_TYPE_MASK;
|
|
|
|
Descriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
|
|
if (j == RomIndex) {
|
|
// this is a ROM address
|
|
Descriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
|
|
}
|
|
|
|
if (i & PCI_ADDRESS_MEMORY_PREFETCHABLE) {
|
|
Descriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
|
|
}
|
|
|
|
if (!Is64BitBaseAddress(i) &&
|
|
(j == RomIndex ||
|
|
PciOrigData->Command & PCI_ENABLE_MEMORY_SPACE)) {
|
|
|
|
//
|
|
// The memory range is/was already enabled at some location, add that
|
|
// as it's preferred setting.
|
|
//
|
|
|
|
Descriptor->Type = CmResourceTypeMemory;
|
|
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
Descriptor->Option = IO_RESOURCE_PREFERRED;
|
|
|
|
Descriptor->u.Port.Length = length;
|
|
Descriptor->u.Port.Alignment = length;
|
|
Descriptor->u.Port.MinimumAddress.LowPart = *OrigAddress[j] & ~0xF;
|
|
Descriptor->u.Port.MaximumAddress.LowPart =
|
|
Descriptor->u.Port.MinimumAddress.LowPart + length;
|
|
|
|
CompleteList->List[0].Count++;
|
|
Descriptor++;
|
|
|
|
Descriptor->Flags = Descriptor[-1].Flags;
|
|
Descriptor->Option = IO_RESOURCE_ALTERNATIVE;
|
|
}
|
|
|
|
//
|
|
// Add this memory range
|
|
//
|
|
|
|
Descriptor->Type = CmResourceTypeMemory;
|
|
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
|
|
Descriptor->u.Memory.Length = length;
|
|
Descriptor->u.Memory.Alignment = length;
|
|
Descriptor->u.Memory.MaximumAddress.LowPart = m;
|
|
|
|
if (memtype == PCI_TYPE_20BIT && m > 0xFFFFF) {
|
|
// limit to 20 bit address
|
|
Descriptor->u.Memory.MaximumAddress.LowPart = 0xFFFFF;
|
|
}
|
|
}
|
|
|
|
CompleteList->List[0].Count++;
|
|
Descriptor++;
|
|
|
|
|
|
if (Is64BitBaseAddress(i)) {
|
|
// skip upper half of 64 bit address since this processor
|
|
// only supports 32 bits of address space
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
CompleteList->ListSize = (ULONG)
|
|
((PUCHAR) Descriptor - (PUCHAR) CompleteList);
|
|
|
|
//
|
|
// Restore the device settings as we found them, enable memory
|
|
// and io decode after setting base addresses. This is done in
|
|
// case HalAdjustResourceList wants to read the current settings
|
|
// in the device.
|
|
//
|
|
|
|
HalpWritePCIConfig (
|
|
BusHandler,
|
|
PciSlot,
|
|
&PciOrigData->Status,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Status),
|
|
PCI_COMMON_HDR_LENGTH - FIELD_OFFSET (PCI_COMMON_CONFIG, Status)
|
|
);
|
|
|
|
HalpWritePCIConfig (
|
|
BusHandler,
|
|
PciSlot,
|
|
PciOrigData,
|
|
0,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Status)
|
|
);
|
|
|
|
//
|
|
// Have the IO system allocate resource assignments
|
|
//
|
|
|
|
status = IoAssignResources (RegistryPath,
|
|
DriverClassName,
|
|
DriverObject,
|
|
DeviceObject,
|
|
CompleteList,
|
|
pAllocatedResources);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Slurp the assigments back into the PciData structure and
|
|
// perform them
|
|
//
|
|
|
|
CmDescriptor = (*pAllocatedResources)->List[0].PartialResourceList.PartialDescriptors;
|
|
|
|
//
|
|
// If PCI device has an interrupt resource then that was
|
|
// passed in as the first requested resource
|
|
//
|
|
|
|
if (PciData->u.type0.InterruptPin) {
|
|
PciData->u.type0.InterruptLine = (UCHAR) CmDescriptor->u.Interrupt.Vector;
|
|
//BusData->CommonData.Line2Pin (BusHandler, RootHandler, PciSlot, PciData, PciOrigData);
|
|
CmDescriptor++;
|
|
}
|
|
|
|
//
|
|
// Pull out resources in the order they were passed to IoAssignResources
|
|
//
|
|
|
|
//
|
|
// Pull out resources in the order they were passed to IoAssignResources
|
|
//
|
|
|
|
m = 0;
|
|
for (j=0; j < NoBaseAddress; j++) {
|
|
i = *BaseAddress[j];
|
|
if (i) {
|
|
if (i & PCI_ADDRESS_IO_SPACE) {
|
|
m |= PCI_ENABLE_IO_SPACE;
|
|
*BaseAddress[j] = CmDescriptor->u.Port.Start.LowPart;
|
|
} else {
|
|
m |= PCI_ENABLE_MEMORY_SPACE;
|
|
*BaseAddress[j] = CmDescriptor->u.Memory.Start.LowPart;
|
|
}
|
|
CmDescriptor++;
|
|
}
|
|
|
|
if (Is64BitBaseAddress(i)) {
|
|
// skip upper 32 bits
|
|
j++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Turn off decodes, then set new addresses
|
|
//
|
|
|
|
HalpWritePCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
|
|
//
|
|
// Read configuration back and verify address settings took
|
|
//
|
|
|
|
HalpReadPCIConfig(BusHandler, PciSlot, PciData2, 0, PCI_COMMON_HDR_LENGTH);
|
|
|
|
Match = TRUE;
|
|
if (PciData->u.type0.InterruptLine != PciData2->u.type0.InterruptLine ||
|
|
PciData->u.type0.InterruptPin != PciData2->u.type0.InterruptPin ||
|
|
PciData->u.type0.ROMBaseAddress != PciData2->u.type0.ROMBaseAddress) {
|
|
Match = FALSE;
|
|
}
|
|
|
|
for (j=0; j < NoBaseAddress; j++) {
|
|
if (*BaseAddress[j]) {
|
|
if (*BaseAddress[j] & PCI_ADDRESS_IO_SPACE) {
|
|
i = (ULONG) ~0x3;
|
|
} else {
|
|
i = (ULONG) ~0xF;
|
|
}
|
|
|
|
if ((*BaseAddress[j] & i) !=
|
|
*((PULONG) ((PUCHAR) BaseAddress[j] -
|
|
(PUCHAR) PciData +
|
|
(PUCHAR) PciData2)) & i) {
|
|
|
|
Match = FALSE;
|
|
}
|
|
|
|
if (Is64BitBaseAddress(*BaseAddress[j])) {
|
|
// skip upper 32 bits
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Match) {
|
|
#if DBG
|
|
DbgPrint ("PCI: defective device! Bus %d, Slot %d, Function %d\n",
|
|
BusNumber,
|
|
PciSlot.u.bits.DeviceNumber,
|
|
PciSlot.u.bits.FunctionNumber
|
|
);
|
|
#endif
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Settings took - turn on the appropiate decodes
|
|
//
|
|
|
|
if (EnableRomBase && *BaseAddress[RomIndex]) {
|
|
// a rom address was allocated and should be enabled
|
|
*BaseAddress[RomIndex] |= PCI_ROMADDRESS_ENABLED;
|
|
HalpWritePCIConfig (
|
|
BusHandler,
|
|
PciSlot,
|
|
BaseAddress[RomIndex],
|
|
(ULONG) ((PUCHAR) BaseAddress[RomIndex] - (PUCHAR) PciData),
|
|
sizeof (ULONG)
|
|
);
|
|
}
|
|
|
|
//
|
|
// We will always enable the Memory, IO, and BusMaster functions
|
|
// of a PCI device because some drivers expect (and check) these
|
|
// bits to be enabled (usually by the firmware). In our case, the
|
|
// firmware does not properly configure all pci devices and so
|
|
// this hack is required to be backward compatible with older
|
|
// firmware. Future firmware should do the right thing ...
|
|
//
|
|
// We never enable the ROM decodes since, in theory, only the driver
|
|
// knows when it's safe to do that.
|
|
//
|
|
|
|
PciData->Command |= (USHORT) (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE | PCI_ENABLE_BUS_MASTER);
|
|
HalSetBusDataByOffset (
|
|
PCIConfiguration,
|
|
BusHandler->BusNumber,
|
|
PciSlot.u.AsULONG,
|
|
&PciData->Command,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Command),
|
|
sizeof (PciData->Command)
|
|
);
|
|
|
|
CleanUp:
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Failure, if there are any allocated resources free them
|
|
//
|
|
|
|
if (*pAllocatedResources) {
|
|
IoAssignResources (
|
|
RegistryPath,
|
|
DriverClassName,
|
|
DriverObject,
|
|
DeviceObject,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
ExFreePool (*pAllocatedResources);
|
|
*pAllocatedResources = NULL;
|
|
}
|
|
|
|
//
|
|
// Restore the device settings as we found them, enable memory
|
|
// and io decode after setting base addresses
|
|
//
|
|
|
|
HalpWritePCIConfig (
|
|
BusHandler,
|
|
PciSlot,
|
|
&PciOrigData->Status,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Status),
|
|
PCI_COMMON_HDR_LENGTH - FIELD_OFFSET (PCI_COMMON_CONFIG, Status)
|
|
);
|
|
|
|
HalpWritePCIConfig (
|
|
BusHandler,
|
|
PciSlot,
|
|
PciOrigData,
|
|
0,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG, Status)
|
|
);
|
|
}
|
|
|
|
ExFreePool (WorkingPool);
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
NTSTATUS
|
|
HalpAdjustPCIResourceList (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList
|
|
)
|
|
/*++
|
|
Rewrite the callers requested resource list to fit within
|
|
the supported ranges of this bus
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PPCIPBUSDATA BusData;
|
|
PCI_SLOT_NUMBER PciSlot;
|
|
PSUPPORTED_RANGE Interrupt;
|
|
|
|
BusData = (PPCIPBUSDATA) BusHandler->BusData;
|
|
PciSlot = *((PPCI_SLOT_NUMBER) &(*pResourceList)->SlotNumber);
|
|
|
|
//
|
|
// Determine PCI device's interrupt restrictions
|
|
//
|
|
|
|
Status = BusData->GetIrqRange(BusHandler, RootHandler, PciSlot, &Interrupt);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Adjust resources
|
|
//
|
|
|
|
Status = HaliAdjustResourceListRange (
|
|
BusHandler->BusAddresses,
|
|
Interrupt,
|
|
pResourceList
|
|
);
|
|
|
|
ExFreePool (Interrupt);
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
HalpGetPCIIrqRange (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER PciSlot,
|
|
OUT PSUPPORTED_RANGE *Interrupt
|
|
)
|
|
{
|
|
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
|
|
PPCI_COMMON_CONFIG PciData;
|
|
|
|
|
|
PciData = (PPCI_COMMON_CONFIG) buffer;
|
|
|
|
HalGetBusData (
|
|
PCIConfiguration,
|
|
BusHandler->BusNumber,
|
|
PciSlot.u.AsULONG,
|
|
PciData,
|
|
PCI_COMMON_HDR_LENGTH
|
|
);
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID ||
|
|
PCI_CONFIG_TYPE (PciData) != 0) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
*Interrupt = ExAllocatePool (PagedPool, sizeof (SUPPORTED_RANGE));
|
|
if (!*Interrupt) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
RtlZeroMemory (*Interrupt, sizeof (SUPPORTED_RANGE));
|
|
|
|
(*Interrupt)->Base = 1; // base = 1, limit = 0
|
|
|
|
if (!PciData->u.type0.InterruptPin) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
(*Interrupt)->Base = PciData->u.type0.InterruptLine;
|
|
(*Interrupt)->Limit = PciData->u.type0.InterruptLine;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|