You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1217 lines
31 KiB
1217 lines
31 KiB
/*++
|
|
|
|
|
|
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
|
|
|
|
KAFFINITY HalpDefaultInterruptAffinity = 0;
|
|
|
|
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
|
|
HalpSetCPEVectorState(
|
|
IN ULONG GlobalInterrupt,
|
|
IN UCHAR SapicVector,
|
|
IN USHORT DestinationCPU,
|
|
IN ULONG Flags
|
|
);
|
|
|
|
VOID
|
|
HalpUpdateVectorAllocationInfo(
|
|
IN ULONG Processor,
|
|
IN ULONG IDTEntry
|
|
);
|
|
|
|
#define MAX_FREE_IRQL 11
|
|
#define MIN_FREE_IRQL 3
|
|
#define MAX_FREE_IDTENTRY 0xbf
|
|
#define MIN_FREE_IDTENTRY 0x30
|
|
|
|
#define VECTOR_TO_IRQL(v) ((KIRQL)((UCHAR)(v) >> 4))
|
|
#define VECTOR_TO_IDTENTRY(v) ((UCHAR)(v))
|
|
#define VECTOR_TO_PROCESSOR(v) (((v) >> 8) - 1)
|
|
#define VECTOR_TO_AFFINITY(v) ((KAFFINITY)1 << VECTOR_TO_PROCESSOR(v))
|
|
|
|
//
|
|
// Bit array of free Vectors
|
|
//
|
|
USHORT HalpCpuFreeVectors[HAL_MAXIMUM_PROCESSOR][16];
|
|
//
|
|
// Number of allocated vectors per CPU
|
|
//
|
|
UCHAR HalpCpuAllocatedVectorCount[HAL_MAXIMUM_PROCESSOR];
|
|
//
|
|
// Number of allocated vectors per IRQL per CPU
|
|
//
|
|
UCHAR HalpCpuAllocatedIrqlCount[HAL_MAXIMUM_PROCESSOR][MAX_FREE_IRQL - MIN_FREE_IRQL + 1];
|
|
//
|
|
// Map from Vector to Inti
|
|
//
|
|
ULONG HalpVectorToINTI[HAL_MAXIMUM_PROCESSOR * 256];
|
|
//
|
|
// Special Inti tokens for HalpVectorToINTI
|
|
//
|
|
#define UNALLOCATED_VECTOR ~0UL
|
|
#define INTERNAL_SYSTEM_INTERRUPT ~1UL
|
|
|
|
|
|
extern KSPIN_LOCK HalpIoSapicLock;
|
|
extern BUS_HANDLER HalpFakePciBusHandler;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, HalpSetInternalVector)
|
|
#pragma alloc_text(INIT, HalpInitInterruptTables)
|
|
#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
|
|
|
|
VOID
|
|
HalpInitInterruptTables(
|
|
VOID
|
|
)
|
|
{
|
|
int index;
|
|
|
|
// Initialize the vector to INTi table
|
|
|
|
for (index = 0; index < (HAL_MAXIMUM_PROCESSOR * 256); index++) {
|
|
|
|
if (index < HAL_MAXIMUM_PROCESSOR)
|
|
{
|
|
RtlFillMemory( &HalpCpuFreeVectors[index][0],
|
|
MIN_FREE_IRQL * sizeof(USHORT),
|
|
0x00
|
|
);
|
|
|
|
RtlFillMemory( &HalpCpuFreeVectors[index][MIN_FREE_IRQL],
|
|
(MAX_FREE_IRQL - MIN_FREE_IRQL + 1) * sizeof(USHORT),
|
|
0xFF
|
|
);
|
|
|
|
RtlFillMemory( &HalpCpuFreeVectors[index][MAX_FREE_IRQL + 1],
|
|
(16 - MAX_FREE_IRQL) * sizeof(USHORT),
|
|
0x00
|
|
);
|
|
}
|
|
|
|
HalpVectorToINTI[index] = UNALLOCATED_VECTOR;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
UCHAR
|
|
HalpAllocateVectorIrqlOffset(
|
|
IN ULONG Processor,
|
|
IN KIRQL Irql,
|
|
IN PUSHORT PreferredVectors
|
|
)
|
|
{
|
|
USHORT cpuFree = HalpCpuFreeVectors[Processor][Irql];
|
|
ULONG index;
|
|
|
|
//
|
|
// We've found one less busy, we shouldn't need to look any further
|
|
//
|
|
|
|
if (PreferredVectors != NULL) {
|
|
|
|
cpuFree &= PreferredVectors[Irql];
|
|
}
|
|
|
|
for (index = 0; index < 16; index++) {
|
|
|
|
if (cpuFree & (1 << index)) {
|
|
|
|
return (UCHAR)((Irql << 4) | index);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
UCHAR
|
|
HalpAllocateVectorIrql(
|
|
IN ULONG Processor,
|
|
IN PUSHORT PreferredVectors
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
UCHAR vector;
|
|
|
|
//
|
|
// Now Find the least busy IRQL
|
|
//
|
|
for (irql = MAX_FREE_IRQL - 1; irql >= MIN_FREE_IRQL; irql--) {
|
|
|
|
if (HalpCpuAllocatedIrqlCount[Processor][irql - MIN_FREE_IRQL] <
|
|
HalpCpuAllocatedIrqlCount[Processor][MAX_FREE_IRQL - MIN_FREE_IRQL]) {
|
|
|
|
vector = HalpAllocateVectorIrqlOffset(Processor, irql, PreferredVectors);
|
|
|
|
if (vector != 0) {
|
|
return vector;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (irql = MAX_FREE_IRQL; irql >= MIN_FREE_IRQL; irql--) {
|
|
|
|
if (HalpCpuAllocatedIrqlCount[Processor][irql - MIN_FREE_IRQL] >=
|
|
HalpCpuAllocatedIrqlCount[Processor][MAX_FREE_IRQL - MIN_FREE_IRQL]) {
|
|
|
|
vector = HalpAllocateVectorIrqlOffset(Processor, irql, PreferredVectors);
|
|
|
|
if (vector != 0) {
|
|
return vector;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ULONG
|
|
HalpAllocateVectorCpu(
|
|
IN KAFFINITY Affinity,
|
|
IN PUSHORT PreferredVectors
|
|
)
|
|
{
|
|
ULONG cpu, selectedCpu;
|
|
UCHAR IDTEntry;
|
|
KAFFINITY cpuList;
|
|
|
|
//
|
|
// Find the least busy CPU
|
|
//
|
|
IDTEntry = 0;
|
|
selectedCpu = ~0UL;
|
|
|
|
cpuList = Affinity & HalpActiveProcessors;
|
|
|
|
for (cpu = 0; cpuList != 0; cpuList >>= 1, cpu++) {
|
|
|
|
if (cpuList & 1) {
|
|
|
|
if (selectedCpu == ~0UL) {
|
|
|
|
selectedCpu = cpu;
|
|
continue;
|
|
}
|
|
|
|
if (HalpCpuAllocatedVectorCount[cpu] <
|
|
HalpCpuAllocatedVectorCount[selectedCpu]) {
|
|
|
|
//
|
|
// We've found one less busy, we shouldn't need to look any further
|
|
//
|
|
IDTEntry = HalpAllocateVectorIrql(cpu, PreferredVectors);
|
|
|
|
if (IDTEntry != 0) {
|
|
return ((cpu + 1) << 8) | IDTEntry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cpuList = Affinity & HalpActiveProcessors;
|
|
|
|
for (cpu = 0; cpuList != 0; cpuList >>= 1, cpu++) {
|
|
|
|
if (cpuList & 1) {
|
|
|
|
if (HalpCpuAllocatedVectorCount[cpu] >=
|
|
HalpCpuAllocatedVectorCount[selectedCpu]) {
|
|
|
|
//
|
|
// We've found one less busy, we shouldn't need to look any further
|
|
//
|
|
IDTEntry = HalpAllocateVectorIrql(cpu, PreferredVectors);
|
|
|
|
if (IDTEntry != 0) {
|
|
return ((cpu + 1) << 8) | IDTEntry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ULONG
|
|
HalpAllocateSystemInterruptVector(
|
|
IN ULONG Interrupt,
|
|
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;
|
|
PUSHORT preferredVectors = NULL;
|
|
|
|
if (HalpMaxProcsPerCluster == 0) {
|
|
|
|
SystemVector = HalpAllocateVectorIrql(0, NULL);
|
|
|
|
} else {
|
|
|
|
if (Interrupt != INTERNAL_SYSTEM_INTERRUPT) {
|
|
|
|
HalpGetFreeVectors(Interrupt, &preferredVectors);
|
|
}
|
|
|
|
SystemVector = HalpAllocateVectorCpu(*Affinity, preferredVectors);
|
|
}
|
|
|
|
if (SystemVector == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (preferredVectors != NULL) {
|
|
|
|
HalpSetVectorAllocated(Interrupt, VECTOR_TO_IDTENTRY(SystemVector));
|
|
}
|
|
|
|
//
|
|
// Now form the vector for the kernel.
|
|
|
|
ASSERT(VECTOR_TO_IDTENTRY(SystemVector) <= MAX_FREE_IDTENTRY);
|
|
ASSERT(VECTOR_TO_IDTENTRY(SystemVector) >= MIN_FREE_IDTENTRY);
|
|
|
|
*Irql = VECTOR_TO_IRQL(SystemVector);
|
|
ASSERT(*Irql <= MAX_FREE_IRQL);
|
|
|
|
if (HalpMaxProcsPerCluster != 0) {
|
|
*Affinity = VECTOR_TO_AFFINITY(SystemVector);
|
|
}
|
|
|
|
HalpUpdateVectorAllocationInfo( VECTOR_TO_PROCESSOR(SystemVector),
|
|
VECTOR_TO_IDTENTRY(SystemVector));
|
|
|
|
HalpVectorToINTI[SystemVector] = Interrupt;
|
|
|
|
HalDebugPrint(( HAL_VERBOSE, "HAL: SystemVector %x Irql %x\n", 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 );
|
|
|
|
*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
|
|
//
|
|
|
|
SystemVector = 0;
|
|
|
|
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(SapicInti, Irql, Affinity);
|
|
|
|
HalpSetINTItoVector(SapicInti, SystemVector);
|
|
|
|
}
|
|
|
|
HalpReleaseHighLevelLock(&HalpIoSapicLock, OldLevel);
|
|
MmUnlockPagableImageSection(LockHandle);
|
|
}
|
|
|
|
if (SystemVector == 0 && (SystemVector = HalpINTItoVector(SapicInti)) != 0) {
|
|
|
|
//
|
|
// Return this SapicInti's system vector & irql
|
|
//
|
|
|
|
*Irql = VECTOR_TO_IRQL(SystemVector);
|
|
|
|
if (HalpMaxProcsPerCluster != 0) {
|
|
*Affinity = VECTOR_TO_AFFINITY(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 ));
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
return HalpVectorToINTI[SystemVector] == INTERNAL_SYSTEM_INTERRUPT;
|
|
}
|
|
|
|
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;
|
|
|
|
OldLevel = HalpAcquireHighLevelLock(&HalpIoSapicLock);
|
|
|
|
*Vector = HalpAllocateSystemInterruptVector(INTERNAL_SYSTEM_INTERRUPT, Irql, Affinity);
|
|
|
|
HalpReleaseHighLevelLock(&HalpIoSapicLock, OldLevel);
|
|
|
|
*HardwareVector = VECTOR_TO_IDTENTRY(*Vector);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
affinity = Source->u.Interrupt.Affinity;
|
|
|
|
//
|
|
// 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] != UNALLOCATED_VECTOR);
|
|
|
|
inti = HalpVectorToINTI[Source->u.Interrupt.Vector];
|
|
|
|
Target->u.Interrupt.Level = Target->u.Interrupt.Vector =
|
|
HalpInti2BusInterruptLevel(inti);
|
|
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
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 (picVector = 0; picVector < PIC_VECTORS; picVector++) {
|
|
|
|
if (HalpPicVectorRedirect[picVector] == Vector) {
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
HalpSetLevel(inti, IS_LEVEL_TRIGGERED(Flags) != FALSE);
|
|
|
|
HalpSetPolarity(inti, IS_ACTIVE_LOW(Flags) != FALSE);
|
|
}
|
|
|
|
|
|
VOID
|
|
HalpSetInternalVector(
|
|
IN ULONG InternalVector,
|
|
IN PHAL_INTERRUPT_ROUTINE HalInterruptServiceRoutine
|
|
)
|
|
/*++
|
|
|
|
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, (KIRQL)(InternalVector >> 4) );
|
|
|
|
HalpUpdateVectorAllocationInfo(PCR->Prcb->Number, InternalVector);
|
|
|
|
//
|
|
// Connect the IDT
|
|
//
|
|
|
|
HalpSetHandlerAddressToVector(InternalVector, HalInterruptServiceRoutine);
|
|
}
|
|
|
|
VOID
|
|
HalpUpdateVectorAllocationInfo(
|
|
IN ULONG Processor,
|
|
IN ULONG IDTEntry
|
|
)
|
|
{
|
|
KIRQL irql = (KIRQL)(IDTEntry >> 4);
|
|
|
|
if (IDTEntry >= MIN_FREE_IDTENTRY && IDTEntry <= MAX_FREE_IDTENTRY) {
|
|
|
|
if (HalpMaxProcsPerCluster == 0) {
|
|
|
|
if (!(HalpCpuFreeVectors[0][irql] & (1 << (IDTEntry & 0x0F)))) {
|
|
|
|
return;
|
|
}
|
|
|
|
Processor = 0;
|
|
}
|
|
|
|
HalpCpuFreeVectors[Processor][irql] &= ~(1 << (IDTEntry & 0x0F));
|
|
|
|
HalpCpuAllocatedVectorCount[Processor]++;
|
|
|
|
HalpCpuAllocatedIrqlCount[Processor][irql - MIN_FREE_IRQL]++;
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
}
|