1289 lines
35 KiB
C
1289 lines
35 KiB
C
/*++
|
||
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
mpsysbus.c
|
||
|
||
Abstract:
|
||
|
||
Author:
|
||
|
||
Environment:
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "pci.h"
|
||
#include "apic.inc"
|
||
#include "pcmp_nt.inc"
|
||
|
||
ULONG HalpDefaultInterruptAffinity = 0;
|
||
|
||
#ifndef ACPI_HAL
|
||
ULONG
|
||
HalpGetEisaInterruptVector(
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN ULONG BusInterruptLevel,
|
||
IN ULONG BusInterruptVector,
|
||
OUT PKIRQL Irql,
|
||
OUT PKAFFINITY Affinity
|
||
);
|
||
#else
|
||
|
||
#undef HalpGetEisaInterruptVector
|
||
#define HalpGetEisaInterruptVector HalpGetSystemInterruptVector
|
||
|
||
extern BUS_HANDLER HalpFakePciBusHandler;
|
||
#endif
|
||
|
||
extern UCHAR HalpVectorToIRQL[];
|
||
extern UCHAR HalpIRQLtoTPR[];
|
||
extern USHORT HalpVectorToINTI[];
|
||
extern KSPIN_LOCK HalpAccountingLock;
|
||
extern struct HalpMpInfo HalpMpInfoTable;
|
||
extern UCHAR HalpMaxProcsPerCluster;
|
||
extern INTERRUPT_DEST HalpIntDestMap[];
|
||
|
||
ULONG HalpINTItoVector[MAX_INTI];
|
||
UCHAR HalpPICINTToVector[16];
|
||
|
||
extern ULONG HalpMaxNode;
|
||
extern ULONG HalpNodeAffinity[MAX_NODES];
|
||
|
||
UCHAR HalpNodeBucket[MAX_NODES];
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,HalpSetInternalVector)
|
||
#pragma alloc_text(PAGELK,HalpGetSystemInterruptVector)
|
||
#pragma alloc_text(PAGE, HalIrqTranslateResourceRequirementsRoot)
|
||
#pragma alloc_text(PAGE, HalTranslatorReference)
|
||
#pragma alloc_text(PAGE, HalTranslatorDereference)
|
||
#endif
|
||
|
||
BOOLEAN
|
||
HalpFindBusAddressTranslation(
|
||
IN PHYSICAL_ADDRESS BusAddress,
|
||
IN OUT PULONG AddressSpace,
|
||
OUT PPHYSICAL_ADDRESS TranslatedAddress,
|
||
IN OUT PULONG_PTR Context,
|
||
IN BOOLEAN NextBus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs a very similar function to HalTranslateBusAddress
|
||
except that InterfaceType and BusNumber are not known by the caller.
|
||
This function will walk all busses known by the HAL looking for a
|
||
valid translation for the input BusAddress of type AddressSpace.
|
||
|
||
This function is recallable using the input/output Context parameter.
|
||
On the first call to this routine for a given translation the ULONG_PTR
|
||
Context should be NULL. Note: Not the address of it but the contents.
|
||
|
||
If the caller decides the returned translation is not the desired
|
||
translation, it calls this routine again passing Context in as it
|
||
was returned on the previous call. This allows this routine to
|
||
traverse the bus structures until the correct translation is found
|
||
and is provided because on multiple bus systems, it is possible for
|
||
the same resource to exist in the independent address spaces of
|
||
multiple busses.
|
||
|
||
Arguments:
|
||
|
||
BusAddress Address to be translated.
|
||
AddressSpace 0 = Memory
|
||
1 = IO (There are other possibilities).
|
||
N.B. This argument is a pointer, the value
|
||
will be modified if the translated address
|
||
is of a different address space type from
|
||
the untranslated bus address.
|
||
TranslatedAddress Pointer to where the translated address
|
||
should be stored.
|
||
Context Pointer to a ULONG_PTR. On the initial call,
|
||
for a given BusAddress, it should contain
|
||
0. It will be modified by this routine,
|
||
on subsequent calls for the same BusAddress
|
||
the value should be handed in again,
|
||
unmodified by the caller.
|
||
NextBus FALSE if we should attempt this translation
|
||
on the same bus as indicated by Context,
|
||
TRUE if we should be looking for another
|
||
bus.
|
||
|
||
Return Value:
|
||
|
||
TRUE if translation was successful,
|
||
FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// First, make sure the context parameter was supplied and is
|
||
// being used correctly. This also ensures that the caller
|
||
// doesn't get stuck in a loop looking for subsequent translations
|
||
// for the same thing. We won't succeed the same translation twice
|
||
// unless the caller reinits the context.
|
||
//
|
||
|
||
if ((!Context) || (*Context && (NextBus == TRUE))) {
|
||
return FALSE;
|
||
}
|
||
*Context = 1;
|
||
|
||
//
|
||
// PC/AT (halx86) case is simplest, there is no translation.
|
||
//
|
||
|
||
*TranslatedAddress = BusAddress;
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
HalpTranslateSystemBusAddress(
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PHYSICAL_ADDRESS BusAddress,
|
||
IN OUT PULONG AddressSpace,
|
||
OUT PPHYSICAL_ADDRESS TranslatedAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function translates a bus-relative address space and address into
|
||
a system physical address.
|
||
|
||
Arguments:
|
||
|
||
BusAddress - Supplies the bus-relative address
|
||
|
||
AddressSpace - Supplies the address space number.
|
||
Returns the host address space number.
|
||
|
||
AddressSpace == 0 => memory space
|
||
AddressSpace == 1 => I/O space
|
||
|
||
TranslatedAddress - Supplies a pointer to return the translated address
|
||
|
||
Return Value:
|
||
|
||
A return value of TRUE indicates that a system physical address
|
||
corresponding to the supplied bus relative address and bus address
|
||
number has been returned in TranslatedAddress.
|
||
|
||
A return value of FALSE occurs if the translation for the address was
|
||
not possible
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN status;
|
||
PSUPPORTED_RANGE pRange;
|
||
|
||
status = FALSE;
|
||
|
||
switch (*AddressSpace) {
|
||
case 0:
|
||
if (BusHandler->InterfaceType != PCIBus) {
|
||
|
||
// verify memory address is within buses memory limits
|
||
|
||
pRange = &BusHandler->BusAddresses->Memory;
|
||
while (!status && pRange) {
|
||
status = BusAddress.QuadPart >= pRange->Base &&
|
||
BusAddress.QuadPart <= pRange->Limit;
|
||
pRange = pRange->Next;
|
||
}
|
||
|
||
pRange = &BusHandler->BusAddresses->PrefetchMemory;
|
||
while (!status && pRange) {
|
||
status = BusAddress.QuadPart >= pRange->Base &&
|
||
BusAddress.QuadPart <= pRange->Limit;
|
||
|
||
pRange = pRange->Next;
|
||
}
|
||
} else {
|
||
//
|
||
// This is a PCI bus and SystemBase is constant for all ranges
|
||
//
|
||
|
||
pRange = &BusHandler->BusAddresses->Memory;
|
||
|
||
status = TRUE;
|
||
}
|
||
break;
|
||
|
||
case 1:
|
||
if (BusHandler->InterfaceType != PCIBus) {
|
||
// verify IO address is within buses IO limits
|
||
pRange = &BusHandler->BusAddresses->IO;
|
||
while (!status && pRange) {
|
||
status = BusAddress.QuadPart >= pRange->Base &&
|
||
BusAddress.QuadPart <= pRange->Limit;
|
||
|
||
pRange = pRange->Next;
|
||
}
|
||
} else {
|
||
//
|
||
// This is a PCI bus and SystemBase is constant for all ranges
|
||
//
|
||
|
||
pRange = &BusHandler->BusAddresses->IO;
|
||
|
||
status = TRUE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
status = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (status) {
|
||
*TranslatedAddress = BusAddress;
|
||
} else {
|
||
_asm { nop }; // good for debugging
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
#define MAX_SYSTEM_IRQL 31
|
||
#define MAX_FREE_IRQL 26
|
||
#define MIN_FREE_IRQL 4
|
||
#define MAX_FREE_IDTENTRY 0xbf
|
||
#define MIN_FREE_IDTENTRY 0x51
|
||
#define IDTENTRY_BASE 0x50
|
||
#define MAX_VBUCKET 7
|
||
|
||
#define AllocateVectorIn(index) \
|
||
vBucket[index]++; \
|
||
ASSERT (vBucket[index] < 16);
|
||
|
||
#define GetIDTEntryFrom(index) \
|
||
(UCHAR) ( index*16 + IDTENTRY_BASE + vBucket[index] )
|
||
// note: device levels 50,60,70,80,90,A0,B0 are not allocatable
|
||
|
||
#define GetIrqlFrom(index) (KIRQL) ( index + MIN_FREE_IRQL )
|
||
|
||
UCHAR nPriority[MAX_NODES][MAX_VBUCKET];
|
||
|
||
ULONG
|
||
HalpGetSystemInterruptVector (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN ULONG InterruptLevel,
|
||
IN ULONG InterruptVector,
|
||
OUT PKIRQL Irql,
|
||
OUT PKAFFINITY Affinity
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the system interrupt vector and IRQL
|
||
corresponding to the specified bus interrupt level and/or
|
||
vector. The system interrupt vector and IRQL are suitable
|
||
for use in a subsequent call to KeInitializeInterrupt.
|
||
|
||
Arguments:
|
||
|
||
InterruptLevel - Supplies the bus specific interrupt level.
|
||
|
||
InterruptVector - Supplies the bus specific interrupt vector.
|
||
|
||
Irql - Returns the system request priority.
|
||
|
||
Affinity - Returns the system wide irq affinity.
|
||
|
||
Return Value:
|
||
|
||
Returns the system interrupt vector corresponding to the specified device.
|
||
|
||
--*/
|
||
{
|
||
ULONG SystemVector;
|
||
USHORT ApicInti;
|
||
UCHAR IDTEntry;
|
||
ULONG Bucket, i, OldLevel;
|
||
BOOLEAN Found;
|
||
PVOID LockHandle;
|
||
ULONG Node;
|
||
PUCHAR vBucket;
|
||
|
||
UNREFERENCED_PARAMETER( InterruptVector );
|
||
//
|
||
// TODO: Remove when Affinity becomes IN OUT.
|
||
*Affinity = ~0;
|
||
//
|
||
// Restrict Affinity if required.
|
||
if (HalpMaxProcsPerCluster == 0) {
|
||
*Affinity &= HalpDefaultInterruptAffinity;
|
||
}
|
||
|
||
//
|
||
// Find closest child bus to this handler
|
||
//
|
||
|
||
if (RootHandler != BusHandler) {
|
||
while (RootHandler->ParentHandler != BusHandler) {
|
||
RootHandler = RootHandler->ParentHandler;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Find Interrupt's APIC Inti connection
|
||
//
|
||
|
||
Found = HalpGetApicInterruptDesc (
|
||
RootHandler->InterfaceType,
|
||
RootHandler->BusNumber,
|
||
InterruptLevel,
|
||
&ApicInti
|
||
);
|
||
|
||
if (!Found) {
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// If device interrupt vector mapping is not already allocated,
|
||
// then do it now
|
||
//
|
||
|
||
if (!HalpINTItoVector[ApicInti]) {
|
||
|
||
//
|
||
// Vector is not allocated - synchronize and check again
|
||
//
|
||
|
||
LockHandle = MmLockPagableCodeSection (&HalpGetSystemInterruptVector);
|
||
OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
|
||
if (!HalpINTItoVector[ApicInti]) {
|
||
|
||
//
|
||
// Still not allocated
|
||
//
|
||
|
||
//
|
||
// Pick a node. In the future, Affinity will be INOUT and
|
||
// we will have to screen the node against the input affinity.
|
||
if (HalpMaxNode == 1) {
|
||
Node = 1;
|
||
} else {
|
||
//
|
||
// Find a node that meets the affinity requirements.
|
||
// Nodes are numbered 1..n, so 0 means we are done.
|
||
for (i = HalpMaxNode; i; i--) {
|
||
if ((*Affinity & HalpNodeAffinity[i-1]) == 0)
|
||
continue;
|
||
Node = i;
|
||
break;
|
||
}
|
||
ASSERT(Node != 0);
|
||
//
|
||
// Look for a "less busy" alternative.
|
||
for (i = Node-1; i; i--) {
|
||
//
|
||
// Check input Affinity to see if this node is permitted.
|
||
if ((*Affinity & HalpNodeAffinity[i-1]) == 0)
|
||
continue;
|
||
//
|
||
// Take the least busy of the permitted nodes.
|
||
if (HalpNodeBucket[i-1] < HalpNodeBucket[Node-1]) {
|
||
Node = i;
|
||
}
|
||
}
|
||
}
|
||
HalpNodeBucket[Node-1]++;
|
||
*Affinity = HalpNodeAffinity[Node-1];
|
||
vBucket = nPriority[Node-1];
|
||
|
||
//
|
||
// Choose the least busy priority on the node.
|
||
Bucket = MAX_VBUCKET-1;
|
||
for (i = Bucket-1; i; i--) {
|
||
if (vBucket[i] < vBucket[Bucket]) {
|
||
Bucket = i;
|
||
}
|
||
}
|
||
AllocateVectorIn (Bucket);
|
||
|
||
//
|
||
// Now form the vector for the kernel.
|
||
IDTEntry = GetIDTEntryFrom (Bucket);
|
||
SystemVector = HalpVector(Node, IDTEntry);
|
||
ASSERT(IDTEntry <= MAX_FREE_IDTENTRY);
|
||
ASSERT(IDTEntry >= MIN_FREE_IDTENTRY);
|
||
|
||
*Irql = GetIrqlFrom (Bucket);
|
||
ASSERT(*Irql <= MAX_FREE_IRQL);
|
||
ASSERT((UCHAR) (HalpIRQLtoTPR[*Irql] & 0xf0) == (UCHAR) (IDTEntry & 0xf0) );
|
||
|
||
HalpVectorToIRQL[IDTEntry >> 4] = (UCHAR) *Irql;
|
||
HalpVectorToINTI[SystemVector] = (USHORT) ApicInti;
|
||
HalpINTItoVector[ApicInti] = SystemVector;
|
||
|
||
//
|
||
// If this assigned interrupt is connected to the machines PIC,
|
||
// then remember the PIC->SystemVector mapping.
|
||
//
|
||
|
||
if (RootHandler->BusNumber == 0 && InterruptLevel < 16 &&
|
||
RootHandler->InterfaceType == DEFAULT_PC_BUS) {
|
||
HalpPICINTToVector[InterruptLevel] = (UCHAR) SystemVector;
|
||
}
|
||
|
||
}
|
||
|
||
HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
|
||
MmUnlockPagableImageSection (LockHandle);
|
||
}
|
||
|
||
//
|
||
// Return this ApicInti's system vector & irql
|
||
//
|
||
|
||
SystemVector = HalpINTItoVector[ApicInti];
|
||
*Irql = HalpVectorToIRQL[HalVectorToIDTEntry(SystemVector) >> 4];
|
||
|
||
ASSERT(HalpVectorToINTI[SystemVector] == (USHORT) ApicInti);
|
||
|
||
//
|
||
// Find an appropriate affinity.
|
||
//
|
||
Node = HalpVectorToNode(SystemVector);
|
||
*Affinity &= HalpNodeAffinity[Node-1];
|
||
if (!*Affinity) {
|
||
return 0;
|
||
}
|
||
|
||
return SystemVector;
|
||
}
|
||
|
||
VOID
|
||
HalpSetInternalVector (
|
||
IN ULONG InternalVector,
|
||
IN VOID (*HalInterruptServiceRoutine)(VOID)
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Used at init time to set IDT vectors for internal use.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Remember this vector so it's reported as Hal internal usage
|
||
//
|
||
|
||
// HalpRegisterVector (
|
||
// InternalUsage,
|
||
// InternalVector,
|
||
// InternalVector,
|
||
// HalpVectorToIRQL[InternalVector >> 4]
|
||
// );
|
||
|
||
//
|
||
// Connect the IDT
|
||
//
|
||
|
||
KiSetHandlerAddressToIDT(InternalVector, HalInterruptServiceRoutine);
|
||
}
|
||
|
||
//
|
||
// This section implements a "translator," which is the PnP-WDM way
|
||
// of doing the same thing that the first part of this file does.
|
||
//
|
||
VOID
|
||
HalTranslatorReference(
|
||
PVOID Context
|
||
)
|
||
{
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
HalTranslatorDereference(
|
||
PVOID Context
|
||
)
|
||
{
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalIrqTranslateResourcesRoot(
|
||
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 takes a CM_PARTIAL_RESOURCE_DESCRIPTOR and translates
|
||
it to an IO-bus-relative from a Processor-bus-relative form, or the other
|
||
way around. In this x86-specific example, an IO-bus-relative form is the
|
||
ISA IRQ and the Processor-bus-relative form is the IDT entry and the
|
||
associated IRQL.
|
||
|
||
N.B. This funtion has an associated "Direction." These are not exactly
|
||
reciprocals. This has to be the case because the output from
|
||
HalIrqTranslateResourceRequirementsRoot will be used as the input
|
||
for the ParentToChild case.
|
||
|
||
ChildToParent:
|
||
|
||
Level (ISA IRQ) -> IRQL
|
||
Vector (ISA IRQ) -> x86 IDT entry
|
||
Affinity (not refereced)-> KAFFINITY
|
||
|
||
ParentToChild:
|
||
|
||
Level (not referenced) -> (ISA IRQ)
|
||
Vector (IDT entry) -> (ISA IRQ)
|
||
Affinity -> 0xffffffff
|
||
|
||
Arguments:
|
||
|
||
Context - unused
|
||
|
||
Source - descriptor that we are translating
|
||
|
||
Direction - direction of translation (parent to child or child to parent)
|
||
|
||
AlternativesCount - unused
|
||
|
||
Alternatives - unused
|
||
|
||
PhysicalDeviceObject- unused
|
||
|
||
Target - translated descriptor
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PBUS_HANDLER bus;
|
||
KAFFINITY affinity;
|
||
KIRQL irql;
|
||
ULONG vector;
|
||
USHORT inti;
|
||
#ifdef ACPI_HAL
|
||
BUS_HANDLER fakeIsaBus;
|
||
#endif
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(AlternativesCount);
|
||
UNREFERENCED_PARAMETER(Alternatives);
|
||
UNREFERENCED_PARAMETER(PhysicalDeviceObject);
|
||
|
||
ASSERT(Source->Type == CmResourceTypeInterrupt);
|
||
|
||
switch (Direction) {
|
||
case TranslateChildToParent:
|
||
|
||
#ifdef ACPI_HAL
|
||
|
||
RtlCopyMemory(&fakeIsaBus, &HalpFakePciBusHandler, sizeof(BUS_HANDLER));
|
||
fakeIsaBus.InterfaceType = Isa;
|
||
fakeIsaBus.ParentHandler = &fakeIsaBus;
|
||
bus = &fakeIsaBus;
|
||
#else
|
||
|
||
if ((INTERFACE_TYPE)Context == InterfaceTypeUndefined) { // special "IDE" cookie
|
||
|
||
ASSERT(Source->u.Interrupt.Level == Source->u.Interrupt.Vector);
|
||
|
||
bus = HalpFindIdeBus(Source->u.Interrupt.Vector);
|
||
|
||
} else {
|
||
|
||
bus = HaliHandlerForBus((INTERFACE_TYPE)Context, 0);
|
||
}
|
||
|
||
if (!bus) {
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Copy everything
|
||
//
|
||
*Target = *Source;
|
||
|
||
//
|
||
// Translate the IRQ
|
||
//
|
||
|
||
vector = HalpGetEisaInterruptVector(bus,
|
||
bus,
|
||
Source->u.Interrupt.Level,
|
||
Source->u.Interrupt.Vector,
|
||
&irql,
|
||
&affinity);
|
||
|
||
if (vector == 0) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
Target->u.Interrupt.Level = irql;
|
||
Target->u.Interrupt.Vector = vector;
|
||
Target->u.Interrupt.Affinity = affinity;
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
status = STATUS_TRANSLATION_COMPLETE;
|
||
}
|
||
|
||
break;
|
||
|
||
case TranslateParentToChild:
|
||
|
||
//
|
||
// Copy everything
|
||
//
|
||
*Target = *Source;
|
||
|
||
//
|
||
// There is no inverse to HalpGetSystemInterruptVector, so we
|
||
// just do what that function would do.
|
||
//
|
||
|
||
inti = HalpVectorToINTI[Source->u.Interrupt.Vector];
|
||
|
||
Target->u.Interrupt.Level = Target->u.Interrupt.Vector =
|
||
HalpInti2BusInterruptLevel(inti);
|
||
|
||
Target->u.Interrupt.Affinity = 0xFFFFFFFF;
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
break;
|
||
|
||
default:
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalIrqTranslateResourceRequirementsRoot(
|
||
IN PVOID Context,
|
||
IN PIO_RESOURCE_DESCRIPTOR Source,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
OUT PULONG TargetCount,
|
||
OUT PIO_RESOURCE_DESCRIPTOR *Target
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function takes an IO_RESOURCE_DESCRIPTOR and translates
|
||
it from an IO-bus-relative to a Processor-bus-relative form. In this
|
||
x86-specific example, an IO-bus-relative form is the ISA IRQ and the
|
||
Processor-bus-relative form is the IDT entry and the associated IRQL.
|
||
This is essentially a PnP form of HalGetInterruptVector.
|
||
|
||
Arguments:
|
||
|
||
Context - unused
|
||
|
||
Source - descriptor that we are translating
|
||
|
||
PhysicalDeviceObject- unused
|
||
|
||
TargetCount - 1
|
||
|
||
Target - translated descriptor
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PBUS_HANDLER bus;
|
||
KAFFINITY affinity;
|
||
KIRQL irql;
|
||
ULONG vector;
|
||
BOOLEAN success = TRUE;
|
||
#ifdef ACPI_HAL
|
||
BUS_HANDLER fakeIsaBus;
|
||
#endif
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(Source->Type == CmResourceTypeInterrupt);
|
||
|
||
#ifdef ACPI_HAL
|
||
|
||
RtlCopyMemory(&fakeIsaBus, &HalpFakePciBusHandler, sizeof(BUS_HANDLER));
|
||
fakeIsaBus.InterfaceType = Isa;
|
||
fakeIsaBus.ParentHandler = &fakeIsaBus;
|
||
bus = &fakeIsaBus;
|
||
#else
|
||
|
||
if ((INTERFACE_TYPE)Context == InterfaceTypeUndefined) { // special "IDE" cookie
|
||
|
||
ASSERT(Source->u.Interrupt.MinimumVector == Source->u.Interrupt.MaximumVector);
|
||
|
||
bus = HalpFindIdeBus(Source->u.Interrupt.MinimumVector);
|
||
|
||
} else {
|
||
|
||
bus = HaliHandlerForBus((INTERFACE_TYPE)Context, 0);
|
||
}
|
||
|
||
if (!bus) {
|
||
|
||
//
|
||
// There is no valid translation.
|
||
//
|
||
|
||
*TargetCount = 0;
|
||
return STATUS_TRANSLATION_COMPLETE;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// The interrupt requirements were obtained by calling HalAdjustResourceList
|
||
// so we don't need to call it again.
|
||
//
|
||
|
||
*Target = ExAllocatePoolWithTag(PagedPool,
|
||
sizeof(IO_RESOURCE_DESCRIPTOR),
|
||
HAL_POOL_TAG
|
||
);
|
||
|
||
if (!*Target) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
*TargetCount = 1;
|
||
|
||
//
|
||
// Copy the requirement unchanged
|
||
//
|
||
|
||
**Target = *Source;
|
||
|
||
//
|
||
// Perform the translation of the minimum & maximum
|
||
//
|
||
|
||
vector = HalpGetEisaInterruptVector(bus,
|
||
bus,
|
||
Source->u.Interrupt.MinimumVector,
|
||
Source->u.Interrupt.MinimumVector,
|
||
&irql,
|
||
&affinity);
|
||
|
||
if (!vector) {
|
||
success = FALSE;
|
||
}
|
||
|
||
(*Target)->u.Interrupt.MinimumVector = vector;
|
||
|
||
vector = HalpGetEisaInterruptVector(bus,
|
||
bus,
|
||
Source->u.Interrupt.MaximumVector,
|
||
Source->u.Interrupt.MaximumVector,
|
||
&irql,
|
||
&affinity);
|
||
|
||
if (!vector) {
|
||
success = FALSE;
|
||
}
|
||
|
||
(*Target)->u.Interrupt.MaximumVector = vector;
|
||
|
||
if (!success) {
|
||
|
||
ExFreePool(*Target);
|
||
*TargetCount = 0;
|
||
}
|
||
|
||
return STATUS_TRANSLATION_COMPLETE;
|
||
}
|
||
|
||
#if 0
|
||
|
||
// HALMPS doesn't provide this function. It is left here as documentation
|
||
// for HALs which must provide translation.
|
||
|
||
NTSTATUS
|
||
HalpTransMemIoResourceRequirement(
|
||
IN PVOID Context,
|
||
IN PIO_RESOURCE_DESCRIPTOR Source,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
OUT PULONG TargetCount,
|
||
OUT PIO_RESOURCE_DESCRIPTOR *Target
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine translates memory and IO resource requirements.
|
||
|
||
Parameters:
|
||
|
||
Context - The context from the TRANSLATOR_INTERFACE
|
||
|
||
Source - The interrupt requirement to translate
|
||
|
||
PhysicalDeviceObject - The device requesting the resource
|
||
|
||
TargetCount - Pointer to where to return the number of descriptors this
|
||
requirement translates into
|
||
|
||
Target - Pointer to where a pointer to a callee allocated buffer containing
|
||
the translated descriptors should be placed.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS or an error status
|
||
|
||
Note:
|
||
|
||
We do not perform any translation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT(Source);
|
||
ASSERT(Target);
|
||
ASSERT(TargetCount);
|
||
ASSERT(Source->Type == CmResourceTypeMemory ||
|
||
Source->Type == CmResourceTypePort);
|
||
|
||
|
||
//
|
||
// Allocate space for the target
|
||
//
|
||
|
||
*Target = ExAllocatePoolWithTag(PagedPool,
|
||
sizeof(IO_RESOURCE_DESCRIPTOR),
|
||
HAL_POOL_TAG
|
||
);
|
||
|
||
if (!*Target) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Copy the source to target and update the fields that have changed
|
||
//
|
||
|
||
**Target = *Source;
|
||
*TargetCount = 1;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpTransMemIoResource(
|
||
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 routine translates memory and IO resources. On generic x86
|
||
machines, such as those that use this HAL, there isn't actually
|
||
any translation.
|
||
|
||
Parameters:
|
||
|
||
Context - The context from the TRANSLATOR_INTERFACE
|
||
|
||
Source - The interrupt resource to translate
|
||
|
||
Direction - The direction in relation to the Pnp device tree translation
|
||
should occur in.
|
||
|
||
AlternativesCount - The number of alternatives this resource was selected
|
||
from.
|
||
|
||
Alternatives - Array of alternatives this resource was selected from.
|
||
|
||
PhysicalDeviceObject - The device requesting the resource
|
||
|
||
Target - Pointer to a caller allocated buffer to hold the translted resource
|
||
descriptor.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS or an error status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Copy the target to the source
|
||
//
|
||
|
||
*Target = *Source;
|
||
|
||
switch (Direction) {
|
||
case TranslateChildToParent:
|
||
|
||
//
|
||
// Make sure PnP knows it doesn't have to walk up the tree
|
||
// translating at each point.
|
||
//
|
||
|
||
status = STATUS_TRANSLATION_COMPLETE;
|
||
break;
|
||
|
||
case TranslateParentToChild:
|
||
|
||
//
|
||
// We do not translate requirements so do nothing...
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
default:
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
return status;
|
||
}
|
||
#endif
|
||
|
||
NTSTATUS
|
||
HaliGetInterruptTranslator(
|
||
IN INTERFACE_TYPE ParentInterfaceType,
|
||
IN ULONG ParentBusNumber,
|
||
IN INTERFACE_TYPE BridgeInterfaceType,
|
||
IN USHORT Size,
|
||
IN USHORT Version,
|
||
OUT PTRANSLATOR_INTERFACE Translator,
|
||
IN OUT PULONG BridgeBusNumber
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
ParentInterfaceType - The type of the bus the bridge lives on (normally PCI).
|
||
|
||
ParentBusNumber - The number of the bus the bridge lives on.
|
||
|
||
BridgeInterfaceType - The bus type the bridge provides (ie ISA for a PCI-ISA bridge).
|
||
|
||
ResourceType - The resource type we want to translate.
|
||
|
||
Size - The size of the translator buffer.
|
||
|
||
Version - The version of the translator interface requested.
|
||
|
||
Translator - Pointer to the buffer where the translator should be returned
|
||
|
||
BridgeBusNumber - Pointer the bus number of the bus that the bridge represents
|
||
|
||
Return Value:
|
||
|
||
Returns the status of this operation.
|
||
|
||
--*/
|
||
#define BRIDGE_HEADER_BUFFER_SIZE (FIELD_OFFSET(PCI_COMMON_CONFIG, u.type1.SubordinateBus) + 1)
|
||
#define USE_INT_LINE_REGISTER_TOKEN 0xffffffff
|
||
#define DEFAULT_BRIDGE_TRANSLATOR 0x80000000
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ASSERT(Version == HAL_IRQ_TRANSLATOR_VERSION);
|
||
ASSERT(Size >= sizeof(TRANSLATOR_INTERFACE));
|
||
|
||
//
|
||
// Fill in the common bits.
|
||
//
|
||
|
||
RtlZeroMemory(Translator, sizeof(TRANSLATOR_INTERFACE));
|
||
|
||
Translator->Size = sizeof(TRANSLATOR_INTERFACE);
|
||
Translator->Version = HAL_IRQ_TRANSLATOR_VERSION;
|
||
Translator->Context = (PVOID)BridgeInterfaceType;
|
||
Translator->InterfaceReference = HalTranslatorReference;
|
||
Translator->InterfaceDereference = HalTranslatorDereference;
|
||
|
||
switch (BridgeInterfaceType) {
|
||
case Eisa:
|
||
case Isa:
|
||
case InterfaceTypeUndefined: // special "IDE" cookie
|
||
|
||
//
|
||
// Set IRQ translator for (E)ISA interrupts.
|
||
//
|
||
|
||
Translator->TranslateResources = HalIrqTranslateResourcesIsa;
|
||
Translator->TranslateResourceRequirements =
|
||
HalIrqTranslateResourceRequirementsIsa;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
case MicroChannel:
|
||
|
||
//
|
||
// Set IRQ translator for MCA interrupts.
|
||
//
|
||
|
||
Translator->TranslateResources = HalIrqTranslateResourcesRoot;
|
||
Translator->TranslateResourceRequirements =
|
||
HalIrqTranslateResourceRequirementsRoot;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
case PCIBus:
|
||
|
||
#ifndef ACPI_HAL
|
||
//
|
||
// Set of of two IRQ translators for PCI busses.
|
||
//
|
||
|
||
{
|
||
UCHAR mpsBusNumber = 0;
|
||
UCHAR pciBusNumber, parentPci, childPci;
|
||
PCI_SLOT_NUMBER bridgeSlot;
|
||
PCI_COMMON_CONFIG pciData;
|
||
ULONG bytesRead, d, f, possibleContext;
|
||
BOOLEAN describedByMps;
|
||
NTSTATUS status;
|
||
|
||
Translator->TranslateResources = HalpIrqTranslateResourcesPci;
|
||
Translator->TranslateResourceRequirements =
|
||
HalpIrqTranslateRequirementsPci;
|
||
|
||
//
|
||
// Look for this bus in the MPS tables.
|
||
//
|
||
|
||
status = HalpPci2MpsBusNumber((UCHAR)*BridgeBusNumber,
|
||
&mpsBusNumber);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// This bus has corresponding entries for its PCI
|
||
// devices in the MPS tables. So eject the translator
|
||
// that understands them.
|
||
//
|
||
|
||
if (HalpInterruptsDescribedByMpsTable(mpsBusNumber)) {
|
||
|
||
Translator->Context = (PVOID)mpsBusNumber;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Do a quick check to see if we can avoid searching PCI
|
||
// configuration space for a bridge. This code is really
|
||
// redundant, but it's worth trying to avoid touching
|
||
// PCI space.
|
||
//
|
||
|
||
if (ParentInterfaceType != PCIBus) {
|
||
|
||
//
|
||
// This was a PCI bus that doesn't contain
|
||
// mappings for PCI devices.
|
||
//
|
||
|
||
Translator->TranslateResources =
|
||
HalpIrqTranslateResourcesPci;
|
||
Translator->TranslateResourceRequirements =
|
||
HalpIrqTranslateRequirementsPci;
|
||
|
||
Translator->Context = (PVOID)USE_INT_LINE_REGISTER_TOKEN;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// We didn't find this PCI bus in the MPS tables. So there
|
||
// are two cases.
|
||
//
|
||
// 1) This matters, because the parent bus is fully described
|
||
// in the MPS tables and we need to do translations on
|
||
// the vector as it passes through the bridges.
|
||
//
|
||
// 2) This doesn't matter, because the parent busses, while
|
||
// they may be in the MPS tables, they don't have any
|
||
// of their interrupts described. So we just use the
|
||
// interrupt line register anyhow.
|
||
//
|
||
// At this point we need to find the PCI bridge that
|
||
// generates this bus, either because we will eventually
|
||
// need to know the slot number to fill in the context, or
|
||
// because we will need to know the primary bus number to
|
||
// look up the tree.
|
||
//
|
||
|
||
parentPci = (UCHAR)ParentBusNumber;
|
||
childPci = (UCHAR)(*BridgeBusNumber);
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Find the bridge.
|
||
//
|
||
|
||
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;
|
||
|
||
bytesRead = HalGetBusDataByOffset(PCIConfiguration,
|
||
parentPci,
|
||
bridgeSlot.u.AsULONG,
|
||
&pciData,
|
||
0,
|
||
BRIDGE_HEADER_BUFFER_SIZE);
|
||
|
||
if (bytesRead == (ULONG)BRIDGE_HEADER_BUFFER_SIZE) {
|
||
|
||
if ((pciData.VendorID != PCI_INVALID_VENDORID) &&
|
||
(PCI_CONFIGURATION_TYPE((&pciData)) != PCI_DEVICE_TYPE)) {
|
||
|
||
//
|
||
// This is a bridge of some sort.
|
||
//
|
||
|
||
if (pciData.u.type1.SecondaryBus == childPci) {
|
||
|
||
//
|
||
// And it is the bridge we are looking for.
|
||
// Store information about
|
||
//
|
||
|
||
if (childPci == *BridgeBusNumber) {
|
||
|
||
//
|
||
// It is also the bridge that creates the
|
||
// PCI bus that the translator is describing.
|
||
//
|
||
// N.B. This should only happen the first time
|
||
// we search through a bus. (i.e. the first
|
||
// trip through the outer while loop)
|
||
//
|
||
|
||
possibleContext = ((bridgeSlot.u.AsULONG & 0xffff) |
|
||
(ParentBusNumber << 16));
|
||
|
||
}
|
||
|
||
goto HGITFoundBridge1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// No bridge found.
|
||
//
|
||
|
||
if (parentPci == 0) {
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
parentPci--;
|
||
continue;
|
||
|
||
HGITFoundBridge1:
|
||
|
||
status = HalpPci2MpsBusNumber(parentPci, &mpsBusNumber);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
if (HalpInterruptsDescribedByMpsTable(mpsBusNumber)) {
|
||
|
||
//
|
||
// Case 1 above.
|
||
//
|
||
|
||
Translator->TranslateResources = HalIrqTranslateResourcesPciBridge;
|
||
Translator->TranslateResourceRequirements =
|
||
HalIrqTranslateRequirementsPciBridge;
|
||
|
||
Translator->Context = (PVOID)possibleContext;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (HalpMpsBusIsRootBus(mpsBusNumber)) {
|
||
|
||
Translator->TranslateResources =
|
||
HalpIrqTranslateResourcesPci;
|
||
Translator->TranslateResourceRequirements =
|
||
HalpIrqTranslateRequirementsPci;
|
||
|
||
Translator->Context = (PVOID)USE_INT_LINE_REGISTER_TOKEN;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Try again one bus higher.
|
||
//
|
||
|
||
childPci = parentPci;
|
||
parentPci--;
|
||
}
|
||
}
|
||
#endif
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// If we got here, we don't have an interface.
|
||
//
|
||
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
|