1356 lines
33 KiB
C
1356 lines
33 KiB
C
/*++
|
||
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
i64sysbus.c
|
||
|
||
Abstract:
|
||
|
||
Author:
|
||
|
||
Todd Kjos (HP) (v-tkjos) 1-Jun-1998
|
||
Based on halacpi\i386\pmbus.c and halmps\i386\mpsysbus.c
|
||
|
||
Environment:
|
||
|
||
Kernel Mode Only
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "iosapic.h"
|
||
#include <ntacpi.h>
|
||
|
||
#define HalpInti2BusInterruptLevel(Inti) Inti
|
||
|
||
ULONG HalpDefaultInterruptAffinity = 0;
|
||
|
||
extern ULONG HalpMaxNode;
|
||
extern ULONG HalpNodeAffinity[MAX_NODES];
|
||
|
||
UCHAR HalpNodeBucket[MAX_NODES];
|
||
|
||
extern ULONG HalpPicVectorRedirect[];
|
||
extern ULONG HalpPicVectorFlags[];
|
||
|
||
BOOLEAN
|
||
HalpTranslateSystemBusAddress(
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PHYSICAL_ADDRESS BusAddress,
|
||
IN OUT PULONG AddressSpace,
|
||
OUT PPHYSICAL_ADDRESS TranslatedAddress
|
||
);
|
||
|
||
ULONG
|
||
HalpGetSystemInterruptVector(
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN ULONG InterruptLevel,
|
||
IN ULONG InterruptVector,
|
||
OUT PKIRQL Irql,
|
||
OUT PKAFFINITY Affinity
|
||
);
|
||
|
||
VOID
|
||
HalpSetInternalVector (
|
||
IN ULONG InternalVector,
|
||
IN VOID (*HalInterruptServiceRoutine)(VOID)
|
||
);
|
||
|
||
VOID
|
||
HalpSetCPEVectorState(
|
||
IN ULONG GlobalInterrupt,
|
||
IN UCHAR SapicVector,
|
||
IN USHORT DestinationCPU,
|
||
IN ULONG Flags
|
||
);
|
||
|
||
UCHAR HalpVectorToIRQL[256];
|
||
ULONG HalpVectorToINTI[(1+MAX_NODES)*256];
|
||
ULONG HalpInternalSystemVectors[MAX_NODES];
|
||
ULONG HalpInternalSystemVectorCount;
|
||
|
||
extern KSPIN_LOCK HalpIoSapicLock;
|
||
extern BUS_HANDLER HalpFakePciBusHandler;
|
||
|
||
#ifdef PIC_SUPPORTED
|
||
UCHAR HalpPICINTToVector[16];
|
||
#endif
|
||
|
||
#define HAL_IRQ_TRANSLATOR_VERSION 0
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,HalpSetInternalVector)
|
||
#pragma alloc_text(PAGELK,HalpGetSystemInterruptVector)
|
||
#pragma alloc_text(PAGE, HaliSetVectorState)
|
||
#pragma alloc_text(PAGE, HalpSetCPEVectorState)
|
||
#pragma alloc_text(PAGE, HalIrqTranslateResourceRequirementsRoot)
|
||
#pragma alloc_text(PAGE, HalTranslatorReference)
|
||
#pragma alloc_text(PAGE, HalTranslatorDereference)
|
||
#pragma alloc_text(PAGE, HaliIsVectorValid)
|
||
#endif
|
||
|
||
BOOLEAN
|
||
HalpFindBusAddressTranslation(
|
||
IN PHYSICAL_ADDRESS BusAddress,
|
||
IN OUT PULONG AddressSpace,
|
||
OUT PPHYSICAL_ADDRESS TranslatedAddress,
|
||
IN OUT PUINT_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 UINT_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 UINT_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:
|
||
// 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;
|
||
}
|
||
break;
|
||
|
||
case 1:
|
||
// 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;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
status = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (status) {
|
||
TranslatedAddress->LowPart = BusAddress.LowPart;
|
||
TranslatedAddress->HighPart = BusAddress.HighPart;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
#define MAX_SYSTEM_IRQL 15
|
||
#define MAX_FREE_IRQL 11
|
||
#define MIN_FREE_IRQL 4
|
||
#define MAX_FREE_IDTENTRY 0xbf
|
||
#define MIN_FREE_IDTENTRY 0x41
|
||
#define IDTENTRY_BASE 0x40
|
||
#define MAX_VBUCKET 8
|
||
|
||
#define AllocateVectorIn(index) \
|
||
vBucket[index]++; \
|
||
ASSERT (vBucket[index] < 16);
|
||
|
||
#define GetIDTEntryFrom(index) \
|
||
(UCHAR) ( index*16 + IDTENTRY_BASE + vBucket[index] )
|
||
// note: device levels 40,50,60,70,80,90,A0,B0 are not allocatable
|
||
|
||
#define GetIrqlFrom(index) (KIRQL) ( (GetIDTEntryFrom(index)) >> 4 )
|
||
|
||
UCHAR nPriority[MAX_NODES][MAX_VBUCKET];
|
||
|
||
ULONG
|
||
HalpAllocateSystemInterruptVector (
|
||
IN OUT PKIRQL Irql,
|
||
IN OUT PKAFFINITY Affinity
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates a system interrupt vector that reflects
|
||
the maximum specified affinity and priority allocation policy. A
|
||
system interrupt vector is returned along with the IRQL and a
|
||
modified affinity.
|
||
|
||
NOTE: HalpIoSapicLock must already have been taken at HIGH_LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Irql - Returns the system request priority.
|
||
|
||
Affinity - What is passed in represents the maximum affinity that
|
||
can be returned. Returned value represents that affinity
|
||
constrained by the node chosen.
|
||
|
||
Return Value:
|
||
|
||
Returns the system interrupt vector
|
||
|
||
--*/
|
||
{
|
||
ULONG SystemVector;
|
||
ULONG Node;
|
||
ULONG Bucket, i;
|
||
PUCHAR vBucket;
|
||
UCHAR IDTEntry;
|
||
|
||
//
|
||
// 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; i; i--) {
|
||
if (vBucket[i - 1] < vBucket[Bucket]) {
|
||
Bucket = i - 1;
|
||
}
|
||
}
|
||
|
||
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);
|
||
|
||
HalpVectorToIRQL[IDTEntry] = (UCHAR) *Irql;
|
||
|
||
HalDebugPrint(( HAL_VERBOSE, "HAL: IDTEntry %x SystemVector %x Irql %x\n", IDTEntry, SystemVector, *Irql));
|
||
|
||
return SystemVector;
|
||
}
|
||
|
||
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, SapicInti;
|
||
ULONG OldLevel;
|
||
BOOLEAN Found;
|
||
PVOID LockHandle;
|
||
ULONG Node;
|
||
KAFFINITY SapicAffinity;
|
||
|
||
|
||
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 Sapic Inti connection
|
||
//
|
||
|
||
Found = HalpGetSapicInterruptDesc (
|
||
RootHandler->InterfaceType,
|
||
RootHandler->BusNumber,
|
||
InterruptLevel,
|
||
&SapicInti,
|
||
&SapicAffinity
|
||
);
|
||
|
||
if (!Found) {
|
||
return 0;
|
||
}
|
||
|
||
HalDebugPrint(( HAL_VERBOSE, "HAL: type %x Level: %x gets inti: %x Sapicaffinity: %p\n",
|
||
RootHandler->InterfaceType,
|
||
InterruptLevel,
|
||
SapicInti,
|
||
SapicAffinity));
|
||
//
|
||
// If device interrupt vector mapping is not already allocated,
|
||
// then do it now
|
||
//
|
||
|
||
if (!HalpINTItoVector(SapicInti)) {
|
||
|
||
//
|
||
// Vector is not allocated - synchronize and check again
|
||
//
|
||
|
||
LockHandle = MmLockPagableCodeSection (&HalpGetSystemInterruptVector);
|
||
OldLevel = HalpAcquireHighLevelLock (&HalpIoSapicLock);
|
||
if (!HalpINTItoVector(SapicInti)) {
|
||
|
||
//
|
||
// Still not allocated
|
||
//
|
||
|
||
HalDebugPrint(( HAL_VERBOSE, "HAL: vector is not allocated\n"));
|
||
|
||
SystemVector = HalpAllocateSystemInterruptVector(Irql, Affinity);
|
||
|
||
HalpVectorToINTI[SystemVector] = SapicInti;
|
||
HalpSetINTItoVector(SapicInti, SystemVector);
|
||
|
||
#ifdef PIC_SUPPORTED
|
||
//
|
||
// 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;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
HalpReleaseHighLevelLock (&HalpIoSapicLock, OldLevel);
|
||
MmUnlockPagableImageSection (LockHandle);
|
||
} else {
|
||
//
|
||
// Return this SapicInti's system vector & irql
|
||
//
|
||
|
||
SystemVector = HalpINTItoVector(SapicInti);
|
||
*Irql = HalpVectorToIRQL[HalVectorToIDTEntry(SystemVector)];
|
||
}
|
||
|
||
HalDebugPrint(( HAL_VERBOSE, "HAL: SystemVector: %x\n",
|
||
SystemVector));
|
||
|
||
ASSERT(HalpVectorToINTI[SystemVector] == (USHORT) SapicInti);
|
||
|
||
HalDebugPrint(( HAL_VERBOSE, "HAL: HalpGetSystemInterruptVector - In Level 0x%x, In Vector 0x%x\n",
|
||
InterruptLevel, InterruptVector ));
|
||
HalDebugPrint(( HAL_VERBOSE, "HAL: Out Irql 0x%x, Out System Vector 0x%x\n",
|
||
*Irql, SystemVector ));
|
||
|
||
//
|
||
// Find an appropriate affinity.
|
||
//
|
||
Node = HalpVectorToNode(SystemVector);
|
||
*Affinity &= HalpNodeAffinity[Node-1];
|
||
if (!*Affinity) {
|
||
return 0;
|
||
}
|
||
|
||
return SystemVector;
|
||
}
|
||
|
||
BOOLEAN
|
||
HalpIsInternalInterruptVector(
|
||
ULONG SystemVector
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns whether or not the vector specified is an
|
||
internal vector i.e. one not connected to the IOAPIC
|
||
|
||
Arguments:
|
||
|
||
System Vector - specifies an interrupt vector
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE indicates that the vector is internal.
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
BOOLEAN Result = FALSE;
|
||
|
||
for (i = 0; i < HalpInternalSystemVectorCount; i++) {
|
||
if (HalpInternalSystemVectors[i] == SystemVector) {
|
||
Result = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpReserveCrossPartitionInterruptVector (
|
||
OUT PULONG Vector,
|
||
OUT PKIRQL Irql,
|
||
IN OUT PKAFFINITY Affinity,
|
||
OUT PUCHAR HardwareVector
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the system interrupt vector, IRQL, and
|
||
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:
|
||
|
||
Vector - specifies an interrupt vector that can be passed to
|
||
IoConnectInterrupt.
|
||
|
||
Irql - specifies the irql that should be passed to IoConnectInterrupt
|
||
|
||
Affinity - should be set to the requested maximum affinity. On
|
||
return, it will reflect the actual affinity that should be
|
||
specified in IoConnectInterrupt.
|
||
|
||
HardwareVector - this is the hardware vector to be used by a
|
||
remote partition to target this interrupt vector.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
ULONG OldLevel;
|
||
NTSTATUS Status;
|
||
|
||
OldLevel = HalpAcquireHighLevelLock (&HalpIoSapicLock);
|
||
|
||
if (HalpInternalSystemVectorCount != MAX_NODES) {
|
||
|
||
*Vector = HalpAllocateSystemInterruptVector(Irql, Affinity);
|
||
|
||
HalpInternalSystemVectors[HalpInternalSystemVectorCount++] = *Vector;
|
||
|
||
*HardwareVector = HalVectorToIDTEntry(*Vector);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
else {
|
||
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
HalpReleaseHighLevelLock (&HalpIoSapicLock, OldLevel);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// 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, inti;
|
||
BUS_HANDLER fakeIsaBus;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(Source->Type == CmResourceTypeInterrupt);
|
||
|
||
switch (Direction) {
|
||
case TranslateChildToParent:
|
||
|
||
|
||
RtlCopyMemory(&fakeIsaBus, &HalpFakePciBusHandler, sizeof(BUS_HANDLER));
|
||
fakeIsaBus.InterfaceType = Isa;
|
||
fakeIsaBus.ParentHandler = &fakeIsaBus;
|
||
bus = &fakeIsaBus;
|
||
|
||
//
|
||
// Copy everything
|
||
//
|
||
*Target = *Source;
|
||
|
||
//
|
||
// Translate the IRQ
|
||
//
|
||
|
||
vector = HalpGetSystemInterruptVector(bus,
|
||
bus,
|
||
Source->u.Interrupt.Level,
|
||
Source->u.Interrupt.Vector,
|
||
&irql,
|
||
&affinity);
|
||
|
||
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.
|
||
//
|
||
|
||
ASSERT(HalpVectorToINTI[Source->u.Interrupt.Vector]);
|
||
|
||
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;
|
||
BUS_HANDLER fakeIsaBus;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(Source->Type == CmResourceTypeInterrupt);
|
||
|
||
RtlCopyMemory(&fakeIsaBus, &HalpFakePciBusHandler, sizeof(BUS_HANDLER));
|
||
fakeIsaBus.InterfaceType = Isa;
|
||
fakeIsaBus.ParentHandler = &fakeIsaBus;
|
||
bus = &fakeIsaBus;
|
||
|
||
//
|
||
// 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 = HalpGetSystemInterruptVector(bus,
|
||
bus,
|
||
Source->u.Interrupt.MinimumVector,
|
||
Source->u.Interrupt.MinimumVector,
|
||
&irql,
|
||
&affinity);
|
||
|
||
if (!vector) {
|
||
success = FALSE;
|
||
}
|
||
|
||
(*Target)->u.Interrupt.MinimumVector = vector;
|
||
|
||
vector = HalpGetSystemInterruptVector(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,
|
||
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.
|
||
|
||
ParentSlotNumber - The slot number the bridge lives in (where valid).
|
||
|
||
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 to where the bus number of the bridge bus should be returned
|
||
|
||
Return Value:
|
||
|
||
Returns the status of this operation.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(ParentInterfaceType);
|
||
UNREFERENCED_PARAMETER(ParentBusNumber);
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// If we got here, we don't have an interface.
|
||
//
|
||
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
// These defines come from the MPS 1.4 spec, section 4.3.4
|
||
#define PO_BITS 3
|
||
#define POLARITY_HIGH 1
|
||
#define POLARITY_LOW 3
|
||
#define POLARITY_CONFORMS_WITH_BUS 0
|
||
#define EL_BITS 0xc
|
||
#define EL_BIT_SHIFT 2
|
||
#define EL_EDGE_TRIGGERED 4
|
||
#define EL_LEVEL_TRIGGERED 0xc
|
||
#define EL_CONFORMS_WITH_BUS 0
|
||
|
||
VOID
|
||
HaliSetVectorState(
|
||
IN ULONG Vector,
|
||
IN ULONG Flags
|
||
)
|
||
{
|
||
BOOLEAN found;
|
||
ULONG inti;
|
||
ULONG picVector;
|
||
UCHAR i;
|
||
KAFFINITY affinity;
|
||
|
||
PAGED_CODE();
|
||
|
||
found = HalpGetSapicInterruptDesc( 0, 0, Vector, &inti, &affinity);
|
||
|
||
if (!found) {
|
||
KeBugCheckEx(ACPI_BIOS_ERROR,
|
||
0x10007,
|
||
Vector,
|
||
0,
|
||
0);
|
||
}
|
||
|
||
// ASSERT(HalpIntiInfo[inti].Type == INT_TYPE_INTR);
|
||
|
||
//
|
||
// Vector is already translated through
|
||
// the PIC vector redirection table. We need
|
||
// to make sure that we are honoring the flags
|
||
// in the redirection table. So look in the
|
||
// table here.
|
||
//
|
||
|
||
for (i = 0; i < PIC_VECTORS; i++) {
|
||
|
||
if (HalpPicVectorRedirect[i] == Vector) {
|
||
|
||
picVector = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i != PIC_VECTORS) {
|
||
|
||
//
|
||
// Found this vector in the redirection table.
|
||
//
|
||
|
||
if (HalpPicVectorFlags[picVector] != 0) {
|
||
|
||
//
|
||
// And the flags say something other than "conforms
|
||
// to bus." So we honor the flags from the table.
|
||
//
|
||
switch ((UCHAR)(HalpPicVectorFlags[picVector] & EL_BITS) ) {
|
||
|
||
case EL_EDGE_TRIGGERED: HalpSetLevel(inti, FALSE); break;
|
||
case EL_LEVEL_TRIGGERED: HalpSetLevel(inti, TRUE); break;
|
||
default: // do nothing
|
||
break;
|
||
}
|
||
|
||
switch ((UCHAR)(HalpPicVectorFlags[picVector] & PO_BITS)) {
|
||
case POLARITY_HIGH: HalpSetPolarity(inti, FALSE); break;
|
||
case POLARITY_LOW: HalpSetPolarity(inti, TRUE); break;
|
||
default: // do nothing
|
||
break;
|
||
}
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// This vector is not covered in the table, or it "conforms to bus."
|
||
// So we honor the flags passed into this function.
|
||
//
|
||
|
||
if (IS_LEVEL_TRIGGERED(Flags)) {
|
||
|
||
HalpSetLevel(inti,TRUE);
|
||
|
||
} else {
|
||
|
||
HalpSetLevel(inti,FALSE);
|
||
}
|
||
|
||
if (IS_ACTIVE_LOW(Flags)) {
|
||
|
||
HalpSetPolarity(inti,TRUE);
|
||
|
||
} else {
|
||
|
||
HalpSetPolarity(inti,FALSE);
|
||
}
|
||
}
|
||
|
||
|
||
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]
|
||
//
|
||
|
||
(KIRQL)(InternalVector >> 4)
|
||
|
||
);
|
||
|
||
//
|
||
// Connect the IDT
|
||
//
|
||
|
||
HalpSetHandlerAddressToVector(InternalVector, HalInterruptServiceRoutine);
|
||
}
|
||
|
||
VOID
|
||
HalpSetCPEVectorState(
|
||
IN ULONG GlobalInterrupt,
|
||
IN UCHAR SapicVector,
|
||
IN USHORT DestinationCPU,
|
||
IN ULONG Flags
|
||
)
|
||
{
|
||
BOOLEAN found;
|
||
ULONG SapicInti;
|
||
KAFFINITY affinity;
|
||
|
||
PAGED_CODE();
|
||
|
||
found = HalpGetSapicInterruptDesc( 0, 0, GlobalInterrupt, &SapicInti, &affinity);
|
||
|
||
if ( found ) {
|
||
|
||
HalpWriteRedirEntry( GlobalInterrupt, SapicVector, DestinationCPU, Flags, PLATFORM_INT_CPE );
|
||
|
||
}
|
||
else {
|
||
|
||
HalDebugPrint(( HAL_ERROR,
|
||
"HAL: HalpSetCPEVectorState - Could not find interrupt input for SAPIC interrupt %ld\n",
|
||
GlobalInterrupt ));
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // HalpSetCPEVectorState()
|
||
|
||
BOOLEAN
|
||
HaliIsVectorValid(
|
||
IN ULONG Vector
|
||
)
|
||
{
|
||
BOOLEAN found;
|
||
ULONG inti;
|
||
KAFFINITY affinity;
|
||
|
||
PAGED_CODE();
|
||
|
||
return HalpGetSapicInterruptDesc( 0, 0, Vector, &inti, &affinity);
|
||
}
|