|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
ixpcibus.c
Abstract:
Get/Set bus data routines for the PCI bus
Author:
Ken Reneris (kenr) 14-June-1994
Environment:
Kernel mode
Revision History:
--*/
#include "halp.h"
#include "pci.h"
#include "pcip.h"
extern const WCHAR rgzMultiFunctionAdapter[]; extern const WCHAR rgzConfigurationData[]; extern const WCHAR rgzIdentifier[]; extern const WCHAR rgzPCIIdentifier[]; extern const WCHAR rgzPCICardList[];
//
// Globals
//
KSPIN_LOCK HalpPCIConfigLock;
PCI_CONFIG_HANDLER PCIConfigHandler;
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif // ALLOC_DATA_PRAGMA
const PCI_CONFIG_HANDLER PCIConfigHandlerType1 = { HalpPCISynchronizeType1, HalpPCIReleaseSynchronzationType1, { HalpPCIReadUlongType1, // 0
HalpPCIReadUcharType1, // 1
HalpPCIReadUshortType1 // 2
}, { HalpPCIWriteUlongType1, // 0
HalpPCIWriteUcharType1, // 1
HalpPCIWriteUshortType1 // 2
} };
const PCI_CONFIG_HANDLER PCIConfigHandlerType2 = { HalpPCISynchronizeType2, HalpPCIReleaseSynchronzationType2, { HalpPCIReadUlongType2, // 0
HalpPCIReadUcharType2, // 1
HalpPCIReadUshortType2 // 2
}, { HalpPCIWriteUlongType2, // 0
HalpPCIWriteUcharType2, // 1
HalpPCIWriteUshortType2 // 2
} }; #ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif // ALLOC_DATA_PRAGMA
const UCHAR PCIDeref[4][4] = { {0,1,2,2},{1,1,1,1},{2,1,2,2},{1,1,1,1} };
#define SIZEOF_PARTIAL_INFO_HEADER FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)
#if DBG
ULONG HalpPCIIllegalBusScannerDetected; ULONG HalpPCIStopOnIllegalBusScannerDetected;
#endif
extern BOOLEAN HalpDoingCrashDump;
VOID HalpPCIConfig ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length, IN FncConfigIO *ConfigIO );
VOID HalpGetNMICrashFlag ( VOID );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,HalpQueryPciRegistryInfo)
#pragma alloc_text(INIT,HalpIsRecognizedCard)
#pragma alloc_text(INIT,HalpIsValidPCIDevice)
#pragma alloc_text(INIT,HalpGetNMICrashFlag)
#pragma alloc_text(PAGE,HalpAssignPCISlotResources)
#pragma alloc_text(PAGE,HalIrqTranslateRequirementsPciBridge)
#pragma alloc_text(PAGE,HalIrqTranslateResourcesPciBridge)
#pragma alloc_text(PAGELK,HalpPCISynchronizeOrionB0)
#pragma alloc_text(PAGELK,HalpPCIReleaseSynchronzationOrionB0)
#endif
PPCI_REGISTRY_INFO_INTERNAL HalpQueryPciRegistryInfo ( VOID ) /*++
Routine Description:
Reads information from the registry concerning PCI, including the number of buses and the hardware access mechanism.
Arguments:
None.
Returns:
Buffer that must be freed by the caller, NULL if insufficient memory exists to complete the request, or the information cannot be located.
--*/ { PPCI_REGISTRY_INFO_INTERNAL PCIRegInfo = NULL; PPCI_REGISTRY_INFO PCIRegInfoHeader = NULL; UNICODE_STRING unicodeString, ConfigName, IdentName; HANDLE hMFunc, hBus, hCardList; OBJECT_ATTRIBUTES objectAttributes; NTSTATUS status; UCHAR buffer [sizeof(PPCI_REGISTRY_INFO) + 99]; PWSTR p; WCHAR wstr[8]; ULONG i, junk; ULONG cardListIndex, cardCount, cardMax; PKEY_VALUE_FULL_INFORMATION ValueInfo; PCM_FULL_RESOURCE_DESCRIPTOR Desc; PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc; UCHAR partialInfo[SIZEOF_PARTIAL_INFO_HEADER + sizeof(PCI_CARD_DESCRIPTOR)]; PKEY_VALUE_PARTIAL_INFORMATION partialInfoHeader; KEY_FULL_INFORMATION keyFullInfo;
//
// 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 NULL; }
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 NULL; }
//
// Check the Identifier 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..
PCIRegInfoHeader = (PPCI_REGISTRY_INFO) (PDesc+1); ZwClose (hMFunc); break; } }
if (!PCIRegInfoHeader) {
return NULL; }
//
// Retrieve the list of interesting cards.
//
RtlInitUnicodeString (&unicodeString, rgzPCICardList); InitializeObjectAttributes ( &objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, NULL, // handle
NULL );
status = ZwOpenKey (&hCardList, KEY_READ, &objectAttributes); if (NT_SUCCESS(status)) {
status = ZwQueryKey( hCardList, KeyFullInformation, &keyFullInfo, sizeof(keyFullInfo), &junk );
if ( NT_SUCCESS(status) ) {
cardMax = keyFullInfo.Values;
PCIRegInfo = (PPCI_REGISTRY_INFO_INTERNAL) ExAllocatePoolWithTag( NonPagedPool, sizeof(PCI_REGISTRY_INFO_INTERNAL) + cardMax * sizeof(PCI_CARD_DESCRIPTOR), HAL_POOL_TAG );
if (PCIRegInfo) {
//
// Now that we've allocated enough room, enumerate again.
//
partialInfoHeader = (PKEY_VALUE_PARTIAL_INFORMATION) partialInfo;
for(cardListIndex = cardCount = 0; cardListIndex < cardMax; cardListIndex++) {
status = ZwEnumerateValueKey( hCardList, cardListIndex, KeyValuePartialInformation, partialInfo, sizeof(partialInfo), &junk );
//
// Note that STATUS_NO_MORE_ENTRIES is a failure code
//
if (!NT_SUCCESS( status )) { break; }
if (partialInfoHeader->DataLength != sizeof(PCI_CARD_DESCRIPTOR)) {
continue; }
RtlCopyMemory( PCIRegInfo->CardList + cardCount, partialInfoHeader->Data, sizeof(PCI_CARD_DESCRIPTOR) );
cardCount++; } // next cardListIndex
}
} ZwClose (hCardList); }
if (!PCIRegInfo) {
PCIRegInfo = (PPCI_REGISTRY_INFO_INTERNAL) ExAllocatePoolWithTag( NonPagedPool, sizeof(PCI_REGISTRY_INFO_INTERNAL), HAL_POOL_TAG );
if (!PCIRegInfo) {
return NULL; }
cardCount = 0; }
RtlCopyMemory( PCIRegInfo, PCIRegInfoHeader, sizeof(PCI_REGISTRY_INFO) );
PCIRegInfo->ElementCount = cardCount;
return PCIRegInfo; }
BOOLEAN HalpIsRecognizedCard( IN PPCI_REGISTRY_INFO_INTERNAL PCIRegInfo, IN PPCI_COMMON_CONFIG PciData, IN ULONG FeatureMask ) /*++
Routine Description:
Walks the internal registry info list to find any cards matching the passed in "feature" mask.
Arguments:
PCIRegInfo - Pointer to reg info with the list of "notable" devices. PciData - Config space (with subsystem info for cardbus bridges) FeatureMask - PCIFT flags to try to match
Returns:
Buffer that must be freed by the caller, NULL if insufficient memory exists to complete the request, or the information cannot be located.
--*/ { ULONG element;
//
// Detect if this has a h
//
for(element = 0; element < PCIRegInfo->ElementCount; element++) {
if (FeatureMask & PCIRegInfo->CardList[element].Flags) {
if (PCIRegInfo->CardList[element].VendorID != PciData->VendorID) {
continue; }
if (PCIRegInfo->CardList[element].DeviceID != PciData->DeviceID) {
continue; }
if (PCIRegInfo->CardList[element].Flags & PCICF_CHECK_REVISIONID) {
if (PCIRegInfo->CardList[element].RevisionID != PciData->RevisionID) {
continue; } }
switch(PCI_CONFIGURATION_TYPE(PciData)) {
case PCI_DEVICE_TYPE: if (PCIRegInfo->CardList[element].Flags & PCICF_CHECK_SSVID) {
if (PCIRegInfo->CardList[element].SubsystemVendorID != PciData->u.type0.SubVendorID) {
continue; } }
if (PCIRegInfo->CardList[element].Flags & PCICF_CHECK_SSID) {
if (PCIRegInfo->CardList[element].SubsystemID != PciData->u.type0.SubSystemID) {
continue; } } break;
case PCI_BRIDGE_TYPE: break;
case PCI_CARDBUS_BRIDGE_TYPE: if (PCIRegInfo->CardList[element].Flags & PCICF_CHECK_SSVID) {
if (PCIRegInfo->CardList[element].SubsystemVendorID != ((TYPE2EXTRAS *)(PciData->DeviceSpecific))->SubVendorID) {
continue; } }
if (PCIRegInfo->CardList[element].Flags & PCICF_CHECK_SSID) {
if (PCIRegInfo->CardList[element].SubsystemID != ((TYPE2EXTRAS *)(PciData->DeviceSpecific))->SubSystemID) {
continue; } } break; }
//
// We found the device matching one of the passed in feature bits.
//
return TRUE; } }
return FALSE; }
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; }
//
// 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; } }
//
// Guess it's a valid device..
//
return TRUE; }
ULONG HalpGetPCIData ( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN PCI_SLOT_NUMBER Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length ) /*++
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.
--*/ { PPCI_COMMON_CONFIG PciData; UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; PPCIPBUSDATA BusData; ULONG Len; ULONG i, bit;
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));
if (PciData->VendorID == PCI_INVALID_VENDORID) { return 0; }
} 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);
if (PciData->VendorID == PCI_INVALID_VENDORID) { PciData->VendorID = PCI_INVALID_VENDORID; Len = 2; // only return invalid id
#if DBG
//
// If this read would have accessed beyond the common header
// then it is highly likely we have detected a device driver
// doing a legacy scan of the bus but reading more than the
// allowed configuration header. This can have catastrophic
// side effects.
//
if ((Length + Offset) > PCI_COMMON_HDR_LENGTH) { if (++HalpPCIIllegalBusScannerDetected == 1) { DbgPrint("HAL Warning: PCI Configuration Access had detected an invalid bus scan.\n"); } if (HalpPCIStopOnIllegalBusScannerDetected) { DbgBreakPoint(); } }
#endif
} else {
BusData->CommonData.Pin2Line (BusHandler, RootHandler, Slot, PciData); }
//
// Has this PCI device been configured?
//
#if 0
//
// On DBG build, if this PCI device has not yet been configured,
// then don't report any current configuration the device may have.
//
bit = PciBitIndex(Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber);
if (!RtlCheckBit(&BusData->DeviceConfigured, bit) && PCI_CONFIG_TYPE (PciData) == PCI_DEVICE_TYPE) {
for (i=0; i < PCI_TYPE0_ADDRESSES; i++) { PciData->u.type0.BaseAddresses[i] = 0; }
PciData->u.type0.ROMBaseAddress = 0; PciData->Command &= ~(PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE); } #endif
//
// 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; }
ULONG HalpSetPCIData ( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN PCI_SLOT_NUMBER Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length ) /*++
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.
--*/ { PPCI_COMMON_CONFIG PciData, PciData2; UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; UCHAR iBuffer2[PCI_COMMON_HDR_LENGTH]; PPCIPBUSDATA BusData; ULONG Len, cnt;
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) { 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 || PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) {
// no device, or header type unkown
return 0; }
//
// Set this device as configured
//
BusData = (PPCIPBUSDATA) BusHandler->BusData; #if DBG && !defined(ACPI_HAL)
cnt = PciBitIndex(Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber); RtlSetBits (&BusData->DeviceConfigured, cnt, 1); #endif
//
// 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);
#if DBG
//
// Verify R/O fields haven't changed
//
if (PciData2->VendorID != PciData->VendorID || PciData2->DeviceID != PciData->DeviceID || PciData2->RevisionID != PciData->RevisionID || PciData2->ProgIf != PciData->ProgIf || PciData2->SubClass != PciData->SubClass || PciData2->BaseClass != PciData->BaseClass || PciData2->HeaderType != PciData->HeaderType || PciData2->BaseClass != PciData->BaseClass || PciData2->u.type0.MinimumGrant != PciData->u.type0.MinimumGrant || PciData2->u.type0.MaximumLatency != PciData->u.type0.MaximumLatency) { DbgPrint ("PCI SetBusData: Read-Only configuration value changed\n"); } #endif
//
// 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; }
VOID HalpReadPCIConfig ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ) { if (!HalpValidPCISlot (BusHandler, Slot)) { //
// Invalid SlotID return no data
//
RtlFillMemory (Buffer, Length, (UCHAR) -1); return ; }
HalpPCIConfig (BusHandler, Slot, (PUCHAR) Buffer, Offset, Length, PCIConfigHandler.ConfigRead); }
VOID HalpWritePCIConfig ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ) { if (!HalpValidPCISlot (BusHandler, Slot)) { //
// Invalid SlotID do nothing
//
return ; }
HalpPCIConfig (BusHandler, Slot, (PUCHAR) Buffer, Offset, Length, PCIConfigHandler.ConfigWrite); }
BOOLEAN HalpValidPCISlot ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot ) { PCI_SLOT_NUMBER Slot2; PPCIPBUSDATA BusData; ULONG i; UCHAR Header[FIELD_OFFSET(PCI_COMMON_CONFIG, u)]; PPCI_COMMON_CONFIG PciConfig = (PPCI_COMMON_CONFIG)&Header;
BusData = (PPCIPBUSDATA) BusHandler->BusData;
if (Slot.u.bits.Reserved != 0) { return FALSE; }
if (Slot.u.bits.DeviceNumber >= BusData->MaxDevice) { return FALSE; }
if (Slot.u.bits.FunctionNumber == 0) { return TRUE; }
//
// 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, &Header, 0, sizeof(Header) );
if (PciConfig->VendorID == PCI_INVALID_VENDORID) {
//
// This device doesn't exist, therefore, this function
// doesn't exist.
//
return FALSE; }
if (PciConfig->HeaderType & PCI_MULTIFUNCTION) {
//
// It's a multifunction device. Slot is valid.
//
return TRUE; }
//
// Special cases, ie HACKs for broken hardware.
//
if ((PciConfig->VendorID == 0x8086) && (PciConfig->DeviceID == 0x122e)) {
//
// This device lies, it really is multifunction.
// It's also writable so write back the correct value
// to avoid coming down this path in future.
//
PciConfig->HeaderType |= PCI_MULTIFUNCTION; HalpWritePCIConfig( BusHandler, Slot2, &PciConfig->HeaderType, FIELD_OFFSET(PCI_COMMON_CONFIG, HeaderType), sizeof(PciConfig->HeaderType) );
return TRUE; }
//
// None of the above, must not be a multifunction device.
//
return FALSE; }
VOID HalpPCIConfig ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length, IN FncConfigIO *ConfigIO ) { KIRQL OldIrql; ULONG i; UCHAR State[20]; PPCIPBUSDATA BusData;
BusData = (PPCIPBUSDATA) BusHandler->BusData; PCIConfigHandler.Synchronize (BusHandler, Slot, &OldIrql, State);
while (Length) { i = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)]; i = ConfigIO[i] (BusData, State, Buffer, Offset);
Offset += i; Buffer += i; Length -= i; }
PCIConfigHandler.ReleaseSynchronzation (BusHandler, OldIrql); }
VOID HalpPCISynchronizeType1 ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot, IN PKIRQL Irql, IN PPCI_TYPE1_CFG_BITS PciCfg1 ) { //
// Initialize PciCfg1
//
PciCfg1->u.AsULONG = 0; PciCfg1->u.bits.BusNumber = BusHandler->BusNumber; PciCfg1->u.bits.DeviceNumber = Slot.u.bits.DeviceNumber; PciCfg1->u.bits.FunctionNumber = Slot.u.bits.FunctionNumber; PciCfg1->u.bits.Enable = TRUE;
//
// Synchronize with PCI type1 config space
//
if (!HalpDoingCrashDump) { *Irql = KfRaiseIrql (HIGH_LEVEL); KiAcquireSpinLock (&HalpPCIConfigLock); } else { *Irql = HIGH_LEVEL; } }
VOID HalpPCIReleaseSynchronzationType1 ( IN PBUS_HANDLER BusHandler, IN KIRQL Irql ) { PCI_TYPE1_CFG_BITS PciCfg1; PPCIPBUSDATA BusData;
//
// Disable PCI configuration space
//
PciCfg1.u.AsULONG = 0; BusData = (PPCIPBUSDATA) BusHandler->BusData; WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1.u.AsULONG);
//
// Release spinlock
//
if (!HalpDoingCrashDump) { KiReleaseSpinLock (&HalpPCIConfigLock); KeLowerIrql (Irql); } }
VOID HalpPCISynchronizeOrionB0 ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot, IN PKIRQL Irql, IN PPCI_TYPE1_CFG_BITS PciCfg1 ) { PCI_TYPE1_CFG_BITS Cfg1; union { ULONG dword; USHORT word; UCHAR byte[4]; } Buffer;
//
// First perform normal type 1 synchronization
//
HalpPCISynchronizeType1 (BusHandler, Slot, Irql, PciCfg1);
//
// Apply Orion B0 workaround
//
Cfg1.u.AsULONG=0; Cfg1.u.bits.BusNumber = HalpOrionOPB.Handler->BusNumber; Cfg1.u.bits.DeviceNumber = HalpOrionOPB.Slot.u.bits.DeviceNumber; Cfg1.u.bits.FunctionNumber = HalpOrionOPB.Slot.u.bits.FunctionNumber; Cfg1.u.bits.Enable = TRUE;
//
// Read OPB until we get back the expected Vendor ID and device ID
//
do { HalpPCIReadUlongType1 (HalpOrionOPB.Handler->BusData, &Cfg1, Buffer.byte, 0); } while (Buffer.dword != 0x84c48086);
//
// The bug is that the config read will return whatever value you
// happened to read last. Read register 0x54 till we don't read the
// last value read any more(Vendor ID/Device ID).
//
do { HalpPCIReadUshortType1 (HalpOrionOPB.Handler->BusData, &Cfg1, Buffer.byte, 0x54); } while (Buffer.word == 0x8086);
//
// Disable inbound posting by clearing bit 0 of register 0x54
//
Buffer.word &= ~0x1; HalpPCIWriteUshortType1 (HalpOrionOPB.Handler->BusData, &Cfg1, Buffer.byte, 0x54); }
VOID HalpPCIReleaseSynchronzationOrionB0 ( IN PBUS_HANDLER BusHandler, IN KIRQL Irql ) {
PCI_TYPE1_CFG_BITS PciCfg1; PPCIPBUSDATA BusData; union { ULONG dword; USHORT word; UCHAR byte[4]; } Buffer;
PciCfg1.u.AsULONG=0; PciCfg1.u.bits.BusNumber = HalpOrionOPB.Handler->BusNumber; PciCfg1.u.bits.DeviceNumber = HalpOrionOPB.Slot.u.bits.DeviceNumber; PciCfg1.u.bits.FunctionNumber = HalpOrionOPB.Slot.u.bits.FunctionNumber; PciCfg1.u.bits.Enable = TRUE;
HalpPCIReadUshortType1 (HalpOrionOPB.Handler->BusData, &PciCfg1, Buffer.byte, 0x54);
//
// Enable Inbound posting by setting bit 0 of register 0x54 of ncOPB
//
Buffer.word |= 0x1; HalpPCIWriteUshortType1 (HalpOrionOPB.Handler->BusData, &PciCfg1, Buffer.byte, 0x54);
//
// Complete type 1 synchronization
//
HalpPCIReleaseSynchronzationType1 (BusHandler, Irql); }
ULONG HalpPCIReadUcharType1 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE1_CFG_BITS PciCfg1, IN PUCHAR Buffer, IN ULONG Offset ) { ULONG i;
i = Offset % sizeof(ULONG); PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); *Buffer = READ_PORT_UCHAR ((PUCHAR) (ULONG_PTR)(BusData->Config.Type1.Data + i)); return sizeof (UCHAR); }
ULONG HalpPCIReadUshortType1 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE1_CFG_BITS PciCfg1, IN PUCHAR Buffer, IN ULONG Offset ) { ULONG i;
i = Offset % sizeof(ULONG); PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); *((PUSHORT) Buffer) = READ_PORT_USHORT ((PUSHORT) (ULONG_PTR)(BusData->Config.Type1.Data + i)); return sizeof (USHORT); }
ULONG HalpPCIReadUlongType1 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE1_CFG_BITS PciCfg1, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); *((PULONG) Buffer) = READ_PORT_ULONG ((PULONG) (ULONG_PTR)BusData->Config.Type1.Data); return sizeof (ULONG); }
ULONG HalpPCIWriteUcharType1 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE1_CFG_BITS PciCfg1, IN PUCHAR Buffer, IN ULONG Offset ) { ULONG i;
i = Offset % sizeof(ULONG); PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); WRITE_PORT_UCHAR ((PUCHAR) (ULONG_PTR)(BusData->Config.Type1.Data + i), *Buffer); return sizeof (UCHAR); }
ULONG HalpPCIWriteUshortType1 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE1_CFG_BITS PciCfg1, IN PUCHAR Buffer, IN ULONG Offset ) { ULONG i;
i = Offset % sizeof(ULONG); PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); WRITE_PORT_USHORT ((PUSHORT) (ULONG_PTR)(BusData->Config.Type1.Data + i), *((PUSHORT) Buffer)); return sizeof (USHORT); }
ULONG HalpPCIWriteUlongType1 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE1_CFG_BITS PciCfg1, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG); WRITE_PORT_ULONG (BusData->Config.Type1.Address, PciCfg1->u.AsULONG); WRITE_PORT_ULONG ((PULONG) (ULONG_PTR)BusData->Config.Type1.Data, *((PULONG) Buffer)); return sizeof (ULONG); }
VOID HalpPCISynchronizeType2 ( IN PBUS_HANDLER BusHandler, IN PCI_SLOT_NUMBER Slot, IN PKIRQL Irql, IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr ) { PCI_TYPE2_CSE_BITS PciCfg2Cse; PPCIPBUSDATA BusData;
BusData = (PPCIPBUSDATA) BusHandler->BusData;
//
// Initialize Cfg2Addr
//
PciCfg2Addr->u.AsUSHORT = 0; PciCfg2Addr->u.bits.Agent = (USHORT) Slot.u.bits.DeviceNumber; PciCfg2Addr->u.bits.AddressBase = (USHORT) BusData->Config.Type2.Base;
//
// Synchronize with type2 config space - type2 config space
// remaps 4K of IO space, so we can not allow other I/Os to occur
// while using type2 config space.
//
HalpPCIAcquireType2Lock (&HalpPCIConfigLock, Irql);
PciCfg2Cse.u.AsUCHAR = 0; PciCfg2Cse.u.bits.Enable = TRUE; PciCfg2Cse.u.bits.FunctionNumber = (UCHAR) Slot.u.bits.FunctionNumber; PciCfg2Cse.u.bits.Key = 0xff;
//
// Select bus & enable type 2 configuration space
//
WRITE_PORT_UCHAR (BusData->Config.Type2.Forward, (UCHAR) BusHandler->BusNumber); WRITE_PORT_UCHAR (BusData->Config.Type2.CSE, PciCfg2Cse.u.AsUCHAR); }
VOID HalpPCIReleaseSynchronzationType2 ( IN PBUS_HANDLER BusHandler, IN KIRQL Irql ) { PCI_TYPE2_CSE_BITS PciCfg2Cse; PPCIPBUSDATA BusData;
//
// disable PCI configuration space
//
BusData = (PPCIPBUSDATA) BusHandler->BusData;
PciCfg2Cse.u.AsUCHAR = 0; WRITE_PORT_UCHAR (BusData->Config.Type2.CSE, PciCfg2Cse.u.AsUCHAR); WRITE_PORT_UCHAR (BusData->Config.Type2.Forward, (UCHAR) 0);
//
// Restore interrupts, release spinlock
//
HalpPCIReleaseType2Lock (&HalpPCIConfigLock, Irql); }
ULONG HalpPCIReadUcharType2 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; *Buffer = READ_PORT_UCHAR ((PUCHAR) PciCfg2Addr->u.AsUSHORT); return sizeof (UCHAR); }
ULONG HalpPCIReadUshortType2 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; *((PUSHORT) Buffer) = READ_PORT_USHORT ((PUSHORT) PciCfg2Addr->u.AsUSHORT); return sizeof (USHORT); }
ULONG HalpPCIReadUlongType2 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; *((PULONG) Buffer) = READ_PORT_ULONG ((PULONG) PciCfg2Addr->u.AsUSHORT); return sizeof(ULONG); }
ULONG HalpPCIWriteUcharType2 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; WRITE_PORT_UCHAR ((PUCHAR) PciCfg2Addr->u.AsUSHORT, *Buffer); return sizeof (UCHAR); }
ULONG HalpPCIWriteUshortType2 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; WRITE_PORT_USHORT ((PUSHORT) PciCfg2Addr->u.AsUSHORT, *((PUSHORT) Buffer)); return sizeof (USHORT); }
ULONG HalpPCIWriteUlongType2 ( IN PPCIPBUSDATA BusData, IN PPCI_TYPE2_ADDRESS_BITS PciCfg2Addr, IN PUCHAR Buffer, IN ULONG Offset ) { PciCfg2Addr->u.bits.RegisterNumber = (USHORT) Offset; WRITE_PORT_ULONG ((PULONG) PciCfg2Addr->u.AsUSHORT, *((PULONG) Buffer)); return sizeof(ULONG); }
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 ) /*++
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 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, Option; PULONG BaseAddress[PCI_TYPE0_ADDRESSES + 1]; PULONG OrigAddress[PCI_TYPE0_ADDRESSES + 1]; BOOLEAN Match, EnableRomBase, RequestedInterrupt; KIRQL Kirql; KAFFINITY Kaffinity;
*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)ExAllocatePoolWithTag(PagedPool, i, HAL_POOL_TAG); 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; }
//
// For now since there's not PnP support in the OS, if the BIOS hasn't
// enable a VGA device don't allow it to get enabled via this interface.
//
if ( (PciData->BaseClass == 0 && PciData->SubClass == 1) || (PciData->BaseClass == 3 && PciData->SubClass == 0)) {
if ((PciData->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE)) == 0) { ExFreePool (WorkingPool); return STATUS_DEVICE_NOT_CONNECTED; } }
//
// 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: ExFreePool (WorkingPool); 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; }
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
//
RequestedInterrupt = FALSE; if (PciData->u.type0.InterruptPin && PciData->u.type0.InterruptLine != (0 ^ IRQXOR) && PciData->u.type0.InterruptLine != (0xFF ^ IRQXOR) && HalGetInterruptVector(PCIBus, BusNumber, PciData->u.type0.InterruptLine, PciData->u.type0.InterruptLine, &Kirql, &Kaffinity)) { RequestedInterrupt = TRUE; CompleteList->List[0].Count++;
Descriptor->Option = 0; Descriptor->Type = CmResourceTypeInterrupt; Descriptor->ShareDisposition = CmResourceShareShared; Descriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
if (ARGUMENT_PRESENT(DeviceObject)) {
//
// Let the arbiter pick any interrupt.
//
Descriptor->u.Interrupt.MinimumVector = 0; Descriptor->u.Interrupt.MaximumVector = 0xff;
} else {
//
// Translation is going to fail, because we won't
// be able to identify this device by its device
// object. So trim the requested interrupt resources
// down to what's in the interrupt line register.
// The translator will punt and read this.
//
Descriptor->u.Interrupt.MinimumVector = PciData->u.type0.InterruptLine; Descriptor->u.Interrupt.MaximumVector = PciData->u.type0.InterruptLine; } 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) { #if DBG
DbgPrint ("PCI: defective device! Bus %d, Slot %d, Function %d\n", BusNumber, PciSlot.u.bits.DeviceNumber, PciSlot.u.bits.FunctionNumber );
DbgPrint ("PCI: BaseAddress[%d] = %08lx\n", j, i); #endif
// 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 (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 - 1;
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 ((j == RomIndex) || ((PciOrigData->Command & PCI_ENABLE_MEMORY_SPACE) && ((!Is64BitBaseAddress(i)) || (*OrigAddress[j+1] == 0)))) {
//
// 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 - 1;
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 (RequestedInterrupt) { 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
//
for (j=0; j < NoBaseAddress; j++) { i = *BaseAddress[j]; if (i) { if (i & PCI_ADDRESS_IO_SPACE) { *BaseAddress[j] = CmDescriptor->u.Port.Start.LowPart; } else { *BaseAddress[j] = CmDescriptor->u.Memory.Start.LowPart; if (Is64BitBaseAddress(i)) {
//
// 64 bit address occupies 2 BARs. Reset the
// upper 32 bits to zero (currently FFFFFFFF
// from above). Actually, set to upper 32 bits
// from assigned resource.
//
j++; *BaseAddress[j] = CmDescriptor->u.Memory.Start.HighPart; } } CmDescriptor++; } }
//
// 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 = PCI_ADDRESS_IO_ADDRESS_MASK; } else { i = PCI_ADDRESS_MEMORY_ADDRESS_MASK; }
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) ); }
//
// Enable IO, Memory, and BUS_MASTER decodes
// (use HalSetBusData since valid settings now set)
//
PciData->Command |= 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; }
VOID HalpGetNMICrashFlag ( VOID ) { UNICODE_STRING unicodeString, NMICrashDumpName; OBJECT_ATTRIBUTES objectAttributes; HANDLE hCrashControl; UCHAR buffer [sizeof(PPCI_REGISTRY_INFO) + 99]; ULONG rsize; NTSTATUS status; extern BOOLEAN HalpNMIDumpFlag;
//
// Open Crash Control Registry Key
//
RtlInitUnicodeString (&unicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\CrashControl");
InitializeObjectAttributes ( &objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, NULL, // handle
NULL);
HalpNMIDumpFlag = FALSE;
status = ZwOpenKey (&hCrashControl, KEY_READ, &objectAttributes);
if (NT_SUCCESS(status)) {
//
// Look for NMICrashDump Value
//
RtlInitUnicodeString (&NMICrashDumpName, L"NMICrashDump");
status = ZwQueryValueKey ( hCrashControl, &NMICrashDumpName, KeyValuePartialInformation, (PKEY_VALUE_PARTIAL_INFORMATION) buffer, sizeof (buffer), &rsize );
if ((NT_SUCCESS (status)) && (rsize == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]) + sizeof(ULONG))) { HalpNMIDumpFlag = (BOOLEAN)(((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data[0]); }
ZwClose (hCrashControl); } }
#ifndef ACPI_HAL
#define PciBridgeSwizzle(device, pin) \
((((pin - 1) + device) % 4) + 1)
#define PCIPin2Int(Slot,Pin) \
((((Slot.u.bits.DeviceNumber << 2) | (Pin-1)) != 0) ? \ (Slot.u.bits.DeviceNumber << 2) | (Pin-1) : 0x80);
#define PCIInt2Pin(interrupt) \
((interrupt & 0x3) + 1)
#define PCIInt2Slot(interrupt) \
((interrupt & 0x7f ) >> 2)
NTSTATUS HalIrqTranslateRequirementsPciBridge( IN PVOID Context, IN PIO_RESOURCE_DESCRIPTOR Source, IN PDEVICE_OBJECT PhysicalDeviceObject, OUT PULONG TargetCount, OUT PIO_RESOURCE_DESCRIPTOR *Target ) /*++
Routine Description:
This function translates IRQ resource requirements to the parent PCI bus. This is only to be used for devices on a PCI bus created by a PCI to PCI bridge where there is no other mechanism for determining the interrupt routing exists. (i.e. this bus is generated by a plug-in bridge.)
Arguments:
Context - must hold the slot number of the bridge
Return Value:
STATUS_SUCCESS, so long as we can allocate the necessary memory
--*/ { PIO_RESOURCE_DESCRIPTOR target; PCI_SLOT_NUMBER bridgeSlot; NTSTATUS status; ULONG bridgePin; ULONG pciBusNumber; PCI_SLOT_NUMBER pciSlot; UCHAR interruptLine; UCHAR interruptPin; UCHAR dummy; PDEVICE_OBJECT parentPdo; ROUTING_TOKEN routingToken;
PAGED_CODE(); ASSERT(Source->Type == CmResourceTypeInterrupt); ASSERT(Source->u.Interrupt.MinimumVector == Source->u.Interrupt.MaximumVector);
target = ExAllocatePoolWithTag(PagedPool, sizeof(IO_RESOURCE_DESCRIPTOR), HAL_POOL_TAG);
if (!target) { return STATUS_INSUFFICIENT_RESOURCES; }
//
// Copy the source to fill in all the relevant fields.
//
*target = *Source;
status = PciIrqRoutingInterface.GetInterruptRouting( PhysicalDeviceObject, &pciBusNumber, &pciSlot.u.AsULONG, &interruptLine, &interruptPin, &dummy, &dummy, &parentPdo, &routingToken, &dummy );
ASSERT(NT_SUCCESS(status));
//
// Find the translated IRQ.
//
bridgeSlot.u.AsULONG = 0; bridgeSlot.u.bits.DeviceNumber = (ULONG)Context;
bridgePin = PciBridgeSwizzle(PCIInt2Slot(Source->u.Interrupt.MinimumVector), PCIInt2Pin(Source->u.Interrupt.MinimumVector));
//
// The translated value is the the "PCI INT" of the pin
// on the bridge.
//
target->u.Interrupt.MinimumVector = PCIPin2Int(bridgeSlot, bridgePin);
target->u.Interrupt.MaximumVector = target->u.Interrupt.MinimumVector;
*TargetCount = 1; *Target = target;
return STATUS_SUCCESS; }
NTSTATUS HalIrqTranslateResourcesPciBridge( IN PVOID Context, IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Source, IN RESOURCE_TRANSLATION_DIRECTION Direction, IN ULONG AlternativesCount, OPTIONAL IN IO_RESOURCE_DESCRIPTOR Alternatives[], OPTIONAL IN PDEVICE_OBJECT PhysicalDeviceObject, OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Target ) /*++
Routine Description:
This function translates IRQ resources to and from the parent PCI bus. This is only to be used for devices on a PCI bus created by a PCI to PCI bridge where there is no other mechanism for determining the interrupt routing exists. (i.e. this bus is generated by a plug-in bridge.)
Arguments:
Context - must hold the slot number of the bridge
Return Value:
STATUS_SUCCESS
--*/ { PCI_SLOT_NUMBER bridgeSlot, deviceSlot, childSlot; ULONG bridgePin; ULONG pciBusNumber, targetPciBusNumber, bridgeBusNumber; UCHAR interruptPin; UCHAR dummy; PDEVICE_OBJECT parentPdo; ROUTING_TOKEN routingToken; NTSTATUS status; UCHAR buffer[PCI_COMMON_HDR_LENGTH]; PPCI_COMMON_CONFIG pciData; ULONG d, f; PBUS_HANDLER busHandler;
PAGED_CODE(); ASSERT(Source->Type == CmResourceTypeInterrupt); ASSERT(Source->u.Interrupt.Vector == Source->u.Interrupt.Level); ASSERT(PciIrqRoutingInterface.GetInterruptRouting);
*Target = *Source;
status = PciIrqRoutingInterface.GetInterruptRouting( PhysicalDeviceObject, &pciBusNumber, &deviceSlot.u.AsULONG, &dummy, &interruptPin, &dummy, &dummy, &parentPdo, &routingToken, &dummy );
ASSERT(NT_SUCCESS(status));
switch (Direction) { case TranslateChildToParent:
//
// Find the translated IRQ.
//
bridgeSlot.u.AsULONG = 0; bridgeSlot.u.bits.DeviceNumber = (ULONG_PTR)Context & 0xffff;
bridgePin = PciBridgeSwizzle(PCIInt2Slot(Source->u.Interrupt.Vector), PCIInt2Pin(Source->u.Interrupt.Vector));
//
// The translated value is the the "PCI INT" of the pin
// on the bridge.
//
Target->u.Interrupt.Vector = PCIPin2Int(bridgeSlot, bridgePin);
Target->u.Interrupt.Level = Target->u.Interrupt.Vector;
//
// The affinity should have been inherited from Source
// and it should be non-zero.
//
ASSERT(Target->u.Interrupt.Affinity != 0);
break;
case TranslateParentToChild:
//
// The child-relative representation of Vector and Level
// is from the MPS spec. And we need to know the device
// number and interrupt pin value.
//
//
// TEMPTEMP Use bushandlers until HALMPS is rid of them.
//
pciData = (PPCI_COMMON_CONFIG)&buffer;
bridgeBusNumber = ((ULONG_PTR)Context >> 16) & 0xffff; busHandler = HaliHandlerForBus(PCIBus, bridgeBusNumber); bridgeSlot.u.AsULONG = (ULONG_PTR)Context & 0xffff;
HalpReadPCIConfig(busHandler, bridgeSlot, pciData, 0, PCI_COMMON_HDR_LENGTH);
if (pciData->u.type1.SecondaryBus == pciBusNumber) {
//
// This device is sitting on the bus that we are translating
// into. So create a vector based on the address of this device.
// (Are we at the bottom of the translation?)
//
Target->u.Interrupt.Vector = PCIPin2Int(deviceSlot, interruptPin); Target->u.Interrupt.Level = Target->u.Interrupt.Vector;
return STATUS_SUCCESS;
} else {
//
// This device is not sitting on the bus that we are translating
// into. This device must be a (grand) child of another bridge that
// sits on this bus. And that bridge will have our device's bus
// within its Subordinate bus register.
//
targetPciBusNumber = pciData->u.type1.SecondaryBus; bridgeSlot.u.AsULONG = 0;
for (d = 0; d < PCI_MAX_DEVICES; d++) { for (f = 0; f < PCI_MAX_FUNCTION; f++) {
bridgeSlot.u.bits.DeviceNumber = d; bridgeSlot.u.bits.FunctionNumber = f;
busHandler = HaliHandlerForBus(PCIBus, targetPciBusNumber); HalpReadPCIConfig(busHandler, bridgeSlot, pciData, 0, PCI_COMMON_HDR_LENGTH);
if ((PCI_CONFIGURATION_TYPE(pciData) == PCI_BRIDGE_TYPE) || (PCI_CONFIGURATION_TYPE(pciData) == PCI_CARDBUS_BRIDGE_TYPE)) {
//
// This is a bridge. Check the subordinate bus.
//
if (pciData->u.type1.SubordinateBus >= pciBusNumber) {
//
// Now we know the device number of the bridge on this
// bus that applies to this translation. We still need
// to know what pin will be triggered. To know that,
// we have to look one more bus down.
//
// There are two cases:
//
// 1) The next bus down contains the device.
//
// 2) The next bus down contains another bridge.
//
//
if (pciData->u.type1.SecondaryBus == pciBusNumber) {
//
// This is case 1).
//
interruptPin = (UCHAR)PciBridgeSwizzle(deviceSlot.u.bits.DeviceNumber, interruptPin);
} else {
//
// This is case 2).
//
// Technically, to get the right answer, we would have to
// figure out which pin the bridge is going to trigger. But
// to do that, we would have to scan down busses until we found
// the device. And the information gathered on that little
// journey would never get used.
//
interruptPin = 1; }
Target->u.Interrupt.Vector = PCIPin2Int(bridgeSlot, interruptPin); Target->u.Interrupt.Level = Target->u.Interrupt.Vector;
return STATUS_SUCCESS; } } } } }
return STATUS_NOT_FOUND; }
return STATUS_SUCCESS; } #endif
#if DBG
VOID HalpTestPci (ULONG flag2) { PCI_SLOT_NUMBER SlotNumber; PCI_COMMON_CONFIG PciData, OrigData; ULONG i, f, j, k, bus; BOOLEAN flag;
if (!flag2) { return ; }
DbgBreakPoint (); SlotNumber.u.bits.Reserved = 0;
//
// Read every possible PCI Device/Function and display it's
// default info.
//
// (note this destories it's current settings)
//
flag = TRUE; for (bus = 0; flag; bus++) {
for (i = 0; i < PCI_MAX_DEVICES; i++) { SlotNumber.u.bits.DeviceNumber = i;
for (f = 0; f < PCI_MAX_FUNCTION; f++) { SlotNumber.u.bits.FunctionNumber = f;
//
// Note: This is reading the DeviceSpecific area of
// the device's configuration - normally this should
// only be done on device for which the caller understands.
// I'm doing it here only for debugging.
//
j = HalGetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &PciData, sizeof (PciData) );
if (j == 0) { // out of buses
flag = FALSE; break; }
if (j < PCI_COMMON_HDR_LENGTH) { continue; }
HalSetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &PciData, 1 );
HalGetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &PciData, sizeof (PciData) );
#if 0
memcpy (&OrigData, &PciData, sizeof PciData);
for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { PciData.u.type0.BaseAddresses[j] = 0xFFFFFFFF; }
PciData.u.type0.ROMBaseAddress = 0xFFFFFFFF;
HalSetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &PciData, sizeof (PciData) );
HalGetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &PciData, sizeof (PciData) ); #endif
DbgPrint ("PCI Bus %d Slot %2d %2d ID:%04lx-%04lx Rev:%04lx", bus, i, f, PciData.VendorID, PciData.DeviceID, PciData.RevisionID);
if (PciData.u.type0.InterruptPin) { DbgPrint (" IntPin:%x", PciData.u.type0.InterruptPin); }
if (PciData.u.type0.InterruptLine) { DbgPrint (" IntLine:%x", PciData.u.type0.InterruptLine); }
if (PciData.u.type0.ROMBaseAddress) { DbgPrint (" ROM:%08lx", PciData.u.type0.ROMBaseAddress); }
DbgPrint ("\n Cmd:%04x Status:%04x ProgIf:%04x SubClass:%04x BaseClass:%04lx\n", PciData.Command, PciData.Status, PciData.ProgIf, PciData.SubClass, PciData.BaseClass);
k = 0; for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { if (PciData.u.type0.BaseAddresses[j]) { DbgPrint (" Ad%d:%08lx", j, PciData.u.type0.BaseAddresses[j]); k = 1; } }
#if 0
if (PciData.u.type0.ROMBaseAddress == 0xC08001) {
PciData.u.type0.ROMBaseAddress = 0xC00001; HalSetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &PciData, sizeof (PciData) );
HalGetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &PciData, sizeof (PciData) );
DbgPrint ("\n Bogus rom address, edit yields:%08lx", PciData.u.type0.ROMBaseAddress); } #endif
if (k) { DbgPrint ("\n"); }
if (PciData.VendorID == 0x8086) { // dump complete buffer
DbgPrint ("Command %x, Status %x, BIST %x\n", PciData.Command, PciData.Status, PciData.BIST );
DbgPrint ("CacheLineSz %x, LatencyTimer %x", PciData.CacheLineSize, PciData.LatencyTimer );
for (j=0; j < 192; j++) { if ((j & 0xf) == 0) { DbgPrint ("\n%02x: ", j + 0x40); } DbgPrint ("%02x ", PciData.DeviceSpecific[j]); } DbgPrint ("\n"); }
#if 0
//
// now print original data
//
if (OrigData.u.type0.ROMBaseAddress) { DbgPrint (" oROM:%08lx", OrigData.u.type0.ROMBaseAddress); }
DbgPrint ("\n"); k = 0; for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { if (OrigData.u.type0.BaseAddresses[j]) { DbgPrint (" oAd%d:%08lx", j, OrigData.u.type0.BaseAddresses[j]); k = 1; } }
//
// Restore original settings
//
HalSetBusData ( PCIConfiguration, bus, SlotNumber.u.AsULONG, &OrigData, sizeof (PciData) ); #endif
//
// Next
//
if (k) { DbgPrint ("\n\n"); } } } } DbgBreakPoint (); } #endif
|