580 lines
16 KiB
C
580 lines
16 KiB
C
/*++
|
||
|
||
Copyright (c) 1992, 1993, 1994 Corollary Inc.
|
||
|
||
Module Name:
|
||
|
||
cbmapint.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the HalGetInterruptVector(),
|
||
HalDisableSystemInterrupt(), HalEnableSystemInterrupt(),
|
||
HalpInitializePICs() routines for the Corollary architectures
|
||
under Windows NT.
|
||
|
||
As part of the move from Product1 to Product2, HalGetInterruptVector()
|
||
may no longer be called before Phase1 !
|
||
|
||
This is because we want HalGetInterruptVector() to eventually call our
|
||
routine (the parent handler) HalpGetSystemInterruptVector(). However,
|
||
the linkage is not established until the Phase 1 call to
|
||
HalpInitBusHandlers().
|
||
|
||
Author:
|
||
|
||
Landy Wang (landy@corollary.com) 26-Apr-1992
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "cbus_nt.h" // C-bus NT-specific implementation definitions
|
||
|
||
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 PVOID BusHandler,
|
||
IN PVOID RootHandler,
|
||
IN ULONG BusInterruptLevel,
|
||
IN ULONG BusInterruptVector,
|
||
OUT PKIRQL Irql,
|
||
OUT PKAFFINITY Affinity
|
||
);
|
||
|
||
VOID
|
||
CbusPreparePhase0Interrupts(
|
||
IN ULONG,
|
||
IN ULONG,
|
||
IN PVOID
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, HalpGetSystemInterruptVector)
|
||
#pragma alloc_text(PAGE, CbusPreparePhase0Interrupts)
|
||
#endif
|
||
|
||
|
||
//
|
||
// map the IRQLs to hardware interrupt vectors and vice versa
|
||
//
|
||
ULONG CbusIrqlToVector[HIGH_LEVEL + 1];
|
||
|
||
ULONG CbusVectorToIrql[MAXIMUM_IDTVECTOR + 1];
|
||
|
||
PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1];
|
||
|
||
//
|
||
// the CbusVectorTable[] structure is used to communicate information
|
||
// between HalGetInterruptVector() and HalEnableSystemInterrupt().
|
||
// it would be nicer if it were built into an object instead of us
|
||
// having to carry it around, but that's life.
|
||
//
|
||
// ANY call to HalEnableSystemInterrupt() WITHOUT a preceding call
|
||
// to HalGetInterruptVector (by any processor) is ILLEGAL, unless
|
||
// it has been special cased and detected here.
|
||
//
|
||
// CbusVectorTable[] dimensioning must match the x86 IDT size as well.
|
||
//
|
||
typedef struct _vectortable_t {
|
||
ULONG CpusEnabled; // count of CPUs that have called
|
||
// HalEnableSystemInterrupt()
|
||
|
||
PVOID hardware; // used by Cbus1 & Cbus2 backends
|
||
// to poke hardware
|
||
|
||
USHORT BusNumber; // which bus # of a given class
|
||
|
||
USHORT irqline; // only valid for actual I/O
|
||
// interrupts - not APC/DPC/IPI,etc.
|
||
|
||
} VECTORTABLE_T, *PVECTORTABLE;
|
||
|
||
VECTORTABLE_T CbusVectorTable[MAXIMUM_IDTVECTOR + 1];
|
||
|
||
//
|
||
// needed to synchronize allocation of new interrupt vectors as
|
||
// loading drivers request them.
|
||
//
|
||
KSPIN_LOCK CbusVectorLock;
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called by each hardware backend from HalInitProcessor() at Phase 0
|
||
by the boot cpu. All other cpus are still in reset.
|
||
|
||
software(APC, DPC, wake) and IPI vectors have already been initialized
|
||
and enabled.
|
||
|
||
all we're doing here is setting up some software structures for two
|
||
EISA interrupts (clock and profile) so they can be enabled later.
|
||
|
||
most of the PIC initialization really happened via HalInitializeProcessor()
|
||
because the boot CPU needed to disable his interrupts right away. this
|
||
is because KiSystemStartup() calls KiInitializeKernel() which drops IRQL
|
||
to APC level. this happens LONG before HalpInitializePICs is ever called.
|
||
|
||
As part of the move from Product1 to Product2, HalGetInterruptVector()
|
||
may no longer be called before Phase1 !
|
||
|
||
This is because we want HalGetInterruptVector() to eventually call our
|
||
routine (the parent handler) HalpGetSystemInterruptVector(). However,
|
||
the linkage is not established until the Phase 1 call to
|
||
HalpInitBusHandlers(). So this routine provides all the interrupt setup
|
||
that HalGetInterruptVector() does, but provides it at Phase0 init time.
|
||
|
||
Arguments:
|
||
|
||
SystemVector - the vector the interrupt will occur on.
|
||
|
||
Opaque - an opaque hardware pointer that can only be interpreted by
|
||
the backend hardware portions of the HAL.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
VOID
|
||
CbusPreparePhase0Interrupts(
|
||
IN ULONG SystemVector,
|
||
IN ULONG Irqline,
|
||
IN PVOID Opaque
|
||
)
|
||
{
|
||
CbusVectorTable[SystemVector].hardware = Opaque;
|
||
CbusVectorTable[SystemVector].irqline = (USHORT)Irqline;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the system interrupt vector and IRQL level
|
||
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. Since this operation is highly architecture
|
||
specific, we will defer the entire routine to our hardware backend handler.
|
||
|
||
HalGetInterruptVector() must maintain a "vector to interrupt"
|
||
mapping so when the interrupt is enabled later via
|
||
HalEnableSystemInterrupt(), something intelligent can be done -
|
||
ie: which CBC's hardware interrupt maps to enable!
|
||
this applies both to EISA bridge CBCs and C-bus II half-card CBC's.
|
||
|
||
note that HalEnableSystemInterrupt() will be CALLED by
|
||
EACH processor wishing to participate in the interrupt receipt.
|
||
|
||
Arguments:
|
||
|
||
BusHandler - the parent bus handler (ie: Internal)
|
||
|
||
RootHandler - the root bus handler (ie: Cbus, Eisa, MCA, PCI, etc)
|
||
|
||
BusInterruptLevel - Supplies the bus specific interrupt level.
|
||
|
||
BusInterruptVector - 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
|
||
HalpGetSystemInterruptVector(
|
||
IN PVOID Bhandler,
|
||
IN PVOID Rhandler,
|
||
IN ULONG BusInterruptLevel,
|
||
IN ULONG BusInterruptVector,
|
||
OUT PKIRQL Irql,
|
||
OUT PKAFFINITY Affinity
|
||
)
|
||
|
||
{
|
||
PBUS_HANDLER BusHandler = (PBUS_HANDLER)Bhandler;
|
||
PBUS_HANDLER RootHandler = (PBUS_HANDLER)Rhandler;
|
||
ULONG SystemVector;
|
||
PVECTORTABLE VectorObject;
|
||
extern ULONG HalpDefaultInterruptAffinity;
|
||
|
||
SystemVector = (*CbusBackend->MapVector)(
|
||
RootHandler,
|
||
BusInterruptLevel,
|
||
BusInterruptVector,
|
||
Irql
|
||
);
|
||
|
||
if ( SystemVector > MAXIMUM_IDTVECTOR ||
|
||
(HalpIDTUsage[SystemVector].Flags & IDTOwned) ) {
|
||
|
||
//
|
||
// This is an illegal BusInterruptVector and cannot be connected.
|
||
//
|
||
return 0;
|
||
}
|
||
|
||
if (SystemVector) {
|
||
|
||
VectorObject = &CbusVectorTable[SystemVector];
|
||
|
||
if (RootHandler)
|
||
VectorObject->BusNumber = (USHORT)RootHandler->BusNumber;
|
||
else
|
||
VectorObject->BusNumber = 0;
|
||
|
||
VectorObject->irqline = (USHORT)BusInterruptLevel;
|
||
|
||
VectorObject->hardware =
|
||
|
||
(*CbusBackend->LinkVector)(
|
||
RootHandler,
|
||
SystemVector,
|
||
BusInterruptLevel);
|
||
|
||
//
|
||
// for the Corollary symmetric architectures, the
|
||
// interrupt affinity is always for all processors
|
||
//
|
||
|
||
*Affinity = HalpDefaultInterruptAffinity;
|
||
ASSERT(HalpDefaultInterruptAffinity);
|
||
|
||
}
|
||
|
||
return SystemVector;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Enables a system interrupt, written in C as this
|
||
is not expected to be a frequently used operation.
|
||
|
||
this should not be called at interrupt level, as there
|
||
is not IRQL protection around CbusVectorLock
|
||
throughout the HAL. (there is IRQL protection here
|
||
against interrupt corruption, but not against lock
|
||
deadlocks).
|
||
|
||
When enabling an interrupt on multiple CPUs,
|
||
_EACH_ CPU will run this routine - see IoConnectInterrupt
|
||
and KeConnectInterrupt and the thread affinity mechanism.
|
||
|
||
virtually all interrupts are marked as LIG as far as the hardware
|
||
is concerned. This will be the "common" scenario, as most
|
||
drivers are fully multithreaded. Non multithreaded drivers
|
||
should only be calling this routine on ONE processor, and
|
||
thus, only that processor will participate in the "LIG"!
|
||
|
||
The only interrupts NOT marked LIG are profile, timer and IPI.
|
||
Note that since there is no general kernel mechanism to notify
|
||
the HAL of this characteristic, the HAL must special case these,
|
||
which currently happens based on IRQL. So let NO OTHER INTERRUPTS
|
||
share the CLOCK2, IPI or PROFILE irqls!!!
|
||
|
||
A conceptual difference between the APIC and the CBC is that when
|
||
an I/O APIC or CBC first gets a LIG interrupt from a device:
|
||
|
||
- The CBC broadcasts it on the bus, and each processor that
|
||
is a member of the receiving group will contend for the interrupt.
|
||
The lowest priority processor will be the one to take it.
|
||
|
||
- The I/O APIC knows what the set of the receiving group looks
|
||
like, and broadcasts it only to them, where the lowest priority
|
||
processor will be the one to take it.
|
||
|
||
This difference (transparent in hardware) does influence software
|
||
setup and teardown of the interrupt which is why the Enable and
|
||
Disable routines are not common to both Cbus1 and Cbus2.
|
||
|
||
Arguments:
|
||
|
||
Vector - Supplies the vector of the interrupt to be enabled
|
||
|
||
Irql - Supplies the interrupt level of the intr to be enabled
|
||
|
||
InterruptMode - Supplies the interrupt mode of the interrupt
|
||
|
||
Return Value:
|
||
|
||
TRUE if enabled, FALSE if not.
|
||
--*/
|
||
|
||
BOOLEAN
|
||
HalEnableSystemInterrupt(
|
||
IN ULONG Vector,
|
||
IN KIRQL Irql,
|
||
IN KINTERRUPT_MODE InterruptMode
|
||
)
|
||
{
|
||
KIRQL OldIrql;
|
||
ULONG FirstAttach;
|
||
PVECTORTABLE VectorObject;
|
||
|
||
ASSERT(Vector <= MAXIMUM_IDTVECTOR);
|
||
|
||
UNREFERENCED_PARAMETER( InterruptMode );
|
||
|
||
//
|
||
// maintain a "vector-to-irql" mapping here for fast
|
||
// translation when accepting an interrupt. this is
|
||
// better than continually keeping fs:PcIrql updated,
|
||
// as it allows us to remove instructions from KfRaiseIrql
|
||
// and KfLowerIrql, the hot paths.
|
||
//
|
||
CbusVectorToIrql[Vector] = Irql;
|
||
|
||
//
|
||
// first, try to enable it as a non-device interrupt - ie:
|
||
// without having to initialize an I/O bus to deliver it.
|
||
// if that succeeds (ie: for IPI or software interrupts like
|
||
// DPC or APC), then we're done. otherwise, go the
|
||
// long way.
|
||
//
|
||
if ((*CbusBackend->EnableNonDeviceInterrupt)(Vector) == TRUE) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// it's a legitimate hardware device interrupt...
|
||
//
|
||
// for Cbus2, we need to poke the generating bridge's
|
||
// CBC registers (first enable only) to allow the I/O
|
||
// interrupt to look for a participating CBC to receive
|
||
// it, as well as this processor's CBC to notify the I/O
|
||
// CBC that this processor would like to participate
|
||
// in the interrupt arbitration.
|
||
//
|
||
// for Cbus1, we need to poke the I/O APIC of the generating
|
||
// bus, as well as this processor's local APIC.
|
||
//
|
||
// note that currently clock, profile & IPI are initialized via
|
||
// the KiSetHandlerAddresstoIDT() macro & HalEnableSystemInterrupt().
|
||
// ie: HalGetInterruptVector() is NEVER called for them!
|
||
//
|
||
VectorObject = &CbusVectorTable[Vector];
|
||
|
||
//
|
||
// block all device interrupts (clock is the highest).
|
||
// do not block the HAL-private IPI which is used for
|
||
// Cbus1 (only) redirection table access, otherwise you
|
||
// will create a deadlock race condition.
|
||
//
|
||
OldIrql = KfRaiseIrql(CLOCK2_LEVEL);
|
||
|
||
KiAcquireSpinLock(&CbusVectorLock);
|
||
|
||
//
|
||
// check for "impossible" case...
|
||
// ie: where HalEnableSystemInterrupt has been called,
|
||
// but HalGetInterruptVector has NOT! with the exception
|
||
// of clock, profile, APC, DPC and IPI (which have hardcoded
|
||
// vectors), all drivers intending to receive interrupts must
|
||
// call HalGetInterruptVector to get a vector.
|
||
//
|
||
ASSERT (VectorObject->hardware);
|
||
|
||
VectorObject->CpusEnabled += 1;
|
||
|
||
FirstAttach = (VectorObject->CpusEnabled == 1 ? 1 : 0);
|
||
|
||
(*CbusBackend->EnableDeviceInterrupt)(
|
||
Vector,
|
||
VectorObject->hardware,
|
||
FirstAttach,
|
||
VectorObject->BusNumber,
|
||
VectorObject->irqline);
|
||
|
||
KiReleaseSpinLock(&CbusVectorLock);
|
||
|
||
//
|
||
// unblock I/O interrupts
|
||
//
|
||
KfLowerIrql(OldIrql);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
HalDisableSystemInterrupt(
|
||
IN ULONG Vector,
|
||
IN KIRQL Irql
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Disables a system interrupt, written in C as this
|
||
is not expected to be a frequently used operation.
|
||
should not be called at interrupt level, as there
|
||
is not IRQL protection around CbusVectorLock
|
||
throughout the HAL. (there is IRQL protection here
|
||
against interrupt corruption, but not against lock
|
||
deadlocks).
|
||
|
||
This routine is only called when the interrupt may
|
||
actually be disabled - ie: it doesn't need to worry
|
||
about drivers sharing the interrupt line and one of
|
||
them disabling the line without the other's knowledge.
|
||
|
||
Arguments:
|
||
|
||
Vector - Supplies the vector of the interrupt to be disabled
|
||
|
||
Irql - Supplies the interrupt level of the intr to be disabled
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
ULONG LastDetach = 0;
|
||
PVECTORTABLE VectorObject;
|
||
|
||
UNREFERENCED_PARAMETER( Irql );
|
||
|
||
ASSERT(Vector < MAXIMUM_IDTVECTOR + 1);
|
||
|
||
VectorObject = &CbusVectorTable[Vector];
|
||
|
||
//
|
||
// Block all device interrupts (clock is the highest).
|
||
// Do not block the HAL-private IPI which is used for
|
||
// Cbus1 (only) redirection table access since this
|
||
// would create a deadlock race condition.
|
||
//
|
||
OldIrql = KfRaiseIrql(CLOCK2_LEVEL);
|
||
|
||
KiAcquireSpinLock(&CbusVectorLock);
|
||
|
||
VectorObject->CpusEnabled -= 1;
|
||
|
||
if (VectorObject->CpusEnabled == 0) {
|
||
LastDetach = 1;
|
||
}
|
||
|
||
(*CbusBackend->DisableInterrupt)(
|
||
Vector,
|
||
(PVOID)VectorObject->hardware,
|
||
LastDetach,
|
||
VectorObject->BusNumber,
|
||
VectorObject->irqline);
|
||
|
||
KiReleaseSpinLock(&CbusVectorLock);
|
||
|
||
//
|
||
// unblock interrupts
|
||
//
|
||
KfLowerIrql(OldIrql);
|
||
}
|
||
|
||
|
||
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
PSUPPORTED_RANGE pRange;
|
||
|
||
pRange = NULL;
|
||
switch (*AddressSpace) {
|
||
case 0:
|
||
// verify memory address is within buses memory limits
|
||
for (pRange = &BusHandler->BusAddresses->PrefetchMemory; pRange; pRange = pRange->Next) {
|
||
if (BusAddress.QuadPart >= pRange->Base &&
|
||
BusAddress.QuadPart <= pRange->Limit) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!pRange) {
|
||
for (pRange = &BusHandler->BusAddresses->Memory; pRange; pRange = pRange->Next) {
|
||
if (BusAddress.QuadPart >= pRange->Base &&
|
||
BusAddress.QuadPart <= pRange->Limit) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
case 1:
|
||
// verify IO address is within buses IO limits
|
||
for (pRange = &BusHandler->BusAddresses->IO; pRange; pRange = pRange->Next) {
|
||
if (BusAddress.QuadPart >= pRange->Base &&
|
||
BusAddress.QuadPart <= pRange->Limit) {
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (pRange) {
|
||
TranslatedAddress->QuadPart = BusAddress.QuadPart + pRange->SystemBase;
|
||
*AddressSpace = pRange->SystemAddressSpace;
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|