NT4/private/ntos/nthals/halcbus/i386/cbapic.c
2020-09-30 17:12:29 +02:00

899 lines
28 KiB
C

/*++
Copyright (c) 1992, 1993, 1994 Corollary Inc.
Module Name:
cbapic.c
Abstract:
This module implements the initialization of the APIC in Corollary
Cbus1 and Cbus2 systems. Note that Cbus1 uses only the APIC, where
Cbus2 can use either the APIC or the CBC - the HAL is told (by RRD) which
interrupt controller to use when operating under Windows NT.
Author:
Landy Wang (landy@corollary.com) 05-Oct-1992
Environment:
Kernel mode only.
Revision History:
--*/
#include "halp.h"
#include "cbusrrd.h" // HAL <-> RRD interface definitions
#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here
#include "cbus1.h" // Cbus1 & Cbus2 max number of elements is here
#include "cbus_nt.h" // C-bus NT-specific implementation stuff
#include "bugcodes.h"
#include "stdio.h"
#include "cbusnls.h"
#include "cbusapic.h"
VOID
ApicArbSync(
VOID
);
VOID
CbusApicBrandIOUnitID(
IN ULONG Processor
);
VOID
CbusInitializeLocalApic(
IN ULONG Processor,
IN PVOID PhysicalApicLocation,
IN ULONG SpuriousVector
);
VOID
CbusInitializeIOApic(
IN ULONG Processor,
IN PVOID PhysicalApicLocation,
IN ULONG RedirVector,
IN ULONG RebootVector,
IN ULONG IrqPolarity
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, CbusApicBrandIOUnitID)
#pragma alloc_text(INIT, CbusInitializeLocalApic)
#pragma alloc_text(INIT, CbusInitializeIOApic)
#endif
ULONG
READ_IOAPIC_ULONG(ULONG, ULONG);
VOID
WRITE_IOAPIC_ULONG(ULONG, ULONG, ULONG);
VOID
CbusApicRedirectionRequest(PULONG);
VOID
CbusDisable8259s( USHORT );
VOID
HalpSpuriousInterrupt(VOID);
VOID
IOApicUpdate( VOID );
VOID
CbusApicArbsync(VOID);
VOID
CbusRebootHandler( VOID );
extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1];
extern ULONG CbusBootedProcessors;
ULONG CbusIOApicCount;
PVOID CbusIOApic[MAX_CBUS_ELEMENTS];
PAPIC_REGISTERS CbusLocalApic;
//
// used for IPI vector enabling/disabling since each I/O
// APIC is visible only to its attached processor.
//
REDIR_PORT_T CbusApicRedirPort[MAX_ELEMENT_CSRS];
VOID
CbusApicBrandIOUnitID(
IN ULONG Processor
)
/*++
Routine Description:
Each processor assigns an APIC ID to his I/O APIC so
it can arbitrate for the APIC bus, etc. Intel documentation
says that every local and I/O APIC must have a unique id.
Arguments:
Processor - Supplies a logical processor number
Return Value:
None.
--*/
{
WRITE_IOAPIC_ULONG(0, IO_APIC_ID_OFFSET, (2 * Processor) << APIC_BIT_TO_ID);
CbusApicArbsync();
}
/*++
Routine Description:
Called to get the EOI address for this particular vector.
Arguments:
Vector - Supplies the APIC vector that will generate this interrupt
Return Value:
The EOI address needed for this vector.
--*/
PULONG
CbusApicVectorToEoi(
IN ULONG Vector
)
{
UNREFERENCED_PARAMETER(Vector);
return (PULONG)(&CbusLocalApic->ApicEOI);
}
/*++
Routine Description:
Called by each processor to initialize his local APIC.
The first processor to run this routine will map the
local APICs for all processors.
Note that all interrupts are blocked on entry since
we are being called from HalInitializeProcessor().
Arguments:
Processor - Supplies a logical processor number
Return Value:
None.
--*/
VOID
CbusInitializeLocalApic(
IN ULONG Processor,
IN PVOID PhysicalApicLocation,
IN ULONG SpuriousVector
)
{
ULONG ProcessorBit;
ULONG ApicIDBit;
REDIRECTION_T RedirectionEntry = { 0 };
//
// If the APIC mapping has not been set up yet,
// do it now. Given the NT startup architecture,
// this will always be done by the boot processor.
//
// We map in the APIC into global space instead of in
// the PCR because all processors see it at the
// same _physical_ address. Note the page is mapped PWT.
//
//
// Note that all idle threads will share a common
// page directory, and the HAL PDE is inherited when
// new processes are created. Hence, a single
// HalpMapMemory for the APIC is enough for all
// processors to be able to see their APICs.
//
if (!CbusLocalApic) {
CbusLocalApic = (PAPIC_REGISTERS) HalpMapPhysicalMemoryWriteThrough (
PhysicalApicLocation,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
PhysicalApicLocation, LOCAL_APIC_SIZE));
}
(PTASKPRI) KeGetPcr()->HalReserved[PCR_TASKPRI] =
&CbusLocalApic->ApicTaskPriority;
//
// Here we initialize our destination format and
// logical destination registers so that we can get IPIs
// from other processors.
//
// Specify full decode mode in the destination format register -
// ie: each processor sets only his own bit, and a "match" requires
// that at least one bit match. The alternative is encoded mode,
// in which _ALL_ encoded bits must match the sender's target for
// this processor to see the sent IPI.
//
CbusLocalApic->ApicDestinationFormat = APIC_ALL_PROCESSORS;
//
// the logical destination register is what the redirection destination
// entry compares against. only the high 8 bits will be supported
// in Intel's future APICs, although this isn't documented anywhere!
//
ProcessorBit = KeGetPcr()->HalReserved[PCR_BIT];
ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID);
CbusLocalApic->ApicLogicalDestination = ApicIDBit;
//
// designate the spurious interrupt vector we want to see,
// and inform this processor's APIC to enable interrupt
// acceptance.
//
CbusLocalApic->ApicSpuriousVector =
SpuriousVector | LOCAL_APIC_ENABLE;
//
// as each processor comes online here, we must have ALL
// processors resync their arbitration IDs to take into
// account the new processor. note that we will set:
// arb id == APIC id == processor number.
//
// the strange ID setting is to satisfy Intel's need for
// uniqueness amongst I/O and local unit ID numbering.
//
CbusLocalApic->LocalUnitID = ((2 * Processor + 1) << APIC_BIT_TO_ID);
//
// sync up our new ID with everyone else
//
CbusApicArbsync();
//
// Create the NMI routing linkage for this processor
// It is set as level sensitive, enabled and generating NMI trap 2.
//
RedirectionEntry.ra.Trigger = APIC_LEVEL;
RedirectionEntry.ra.Mask = APIC_INTR_UNMASKED;
RedirectionEntry.ra.Delivery_mode = APIC_INTR_NMI;
RedirectionEntry.ra.Vector = 2;
RedirectionEntry.ra.Destination = ApicIDBit;
CbusLocalApic->ApicLocalInt1 = RedirectionEntry;
//
// Create the spurious interrupt IDT entry for this processor
//
KiSetHandlerAddressToIDT(SpuriousVector, HalpSpuriousInterrupt);
//
// we must specify HIGH_LEVEL when we enable the spurious vector
// here because it will overwrite the CbusVectorToIrql[] entry
// for the HIGH_LEVEL (0xFF!). the spurious vector really only
// needs an IDT entry and doesn't need any other software tables,
// but make the enable call for tracking purposes.
//
HalEnableSystemInterrupt(SpuriousVector, HIGH_LEVEL, Latched);
//
// start off at IRQL 0 - we are still protected by cli.
//
CbusLocalApic->ApicTaskPriority.rb.LowDword = 0;
}
/*++
Routine Description:
Note that all interrupts are blocked on entry since
this routine is called from HalInitializeProcessor.
Initialize this processor's local and I/O APIC units.
Arguments:
Processor - Supplies a logical processor number
Return Value:
None.
--*/
VOID
CbusInitializeIOApic(
IN ULONG Processor,
IN PVOID PhysicalApicLocation,
IN ULONG RedirVector,
IN ULONG RebootVector,
IN ULONG IrqPolarity
)
{
ULONG ProcessorBit;
ULONG ApicIDBit;
ULONG ApicBusNumber;
ULONG RedirectionAddress;
REDIRECTION_T RedirectionEntry = { 0 };
if (CbusIOApicCount >= MAX_CBUS_ELEMENTS) {
return;
}
CbusIOApic[CbusIOApicCount] =
(PVOID) HalpMapPhysicalMemoryWriteThrough (
PhysicalApicLocation,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
PhysicalApicLocation, IO_APIC_SIZE));
CbusApicBrandIOUnitID(Processor);
//
// Disable all 8259 inputs except the irq0 clock.
// remember the irq0 clock and the irq13 DMA
// chaining interrupts are internal to the Intel EISA
// chipset (specifically, the ISP chip), and if the HAL
// wants to enable them, it must be done here.
// This is done by enabling the 8259 ISP to send them
// to the processor(s) via the APIC. However, the Corollary HAL
// currently uses the local APIC timers for clocks. The irq0
// clock is enabled solely for the performance counter because
// we want to use a separate clock for it, (rather than the system
// timer which creates race conditions).
//
// Note that all other EISA bus device interrupts only need to
// be enabled in the APIC for processors to see them.
//
CbusDisable8259s(0xFFFE);
//
// All redirection table entries are disabled by default when the
// processor emerges from reset. Later, each entry is individually
// enabled from their respective drivers via HalEnableSystemInterrupt.
//
// Indicate the APIC (not the 8259s) will now handle provide
// the interrupt vectors to the processor during an INTA cycle.
// This is done by writing to the APMode port. Note that at this
// time we will also sync the APIC polarity control registers with
// the ELCR. Since irq0 has no polarity control, the hardware
// uses bit0 for the APMode enable, so make sure this bit is on too.
//
CbusLocalApic->APMode = (UCHAR)((IrqPolarity & 0xFF) | 0x1);
CbusLocalApic->PolarityPortHigh = (UCHAR)((IrqPolarity >> 8) & 0xFF);
//
// Create an interrupt gate so other processors can
// let the boot processor know about desired I/O APIC
// modifications (ie: enabling & disabling interrupts).
// This is necessary since each processor can only access
// his own I/O APIC, and only the boot processor's I/O APIC
// is attached to the EISA bus interrupt inputs. this only
// needs to be done once regardless of how many I/O APICs are
// present in the system.
//
if (CbusIOApicCount == 0) {
KiSetHandlerAddressToIDT(RedirVector, IOApicUpdate);
HalEnableSystemInterrupt(RedirVector, IPI_LEVEL, Latched);
KiSetHandlerAddressToIDT(RebootVector, CbusRebootHandler);
HalEnableSystemInterrupt(RebootVector, IPI_LEVEL, Latched);
}
#define TRAP2 2
ProcessorBit = (ULONG) KeGetPcr()->HalReserved[PCR_BIT];
ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID);
/*
* support NMIs from the EISA bridge as trap 2.
*/
RedirectionEntry.ra.Mask = APIC_INTR_UNMASKED;
RedirectionEntry.ra.Trigger = APIC_LEVEL;
RedirectionEntry.ra.Dest_mode = APIC_LOGICAL_MODE;
RedirectionEntry.ra.Vector = TRAP2;
RedirectionEntry.ra.Destination = ApicIDBit;
RedirectionEntry.ra.Delivery_mode = APIC_INTR_FIXED;
//
// support multiple I/O buses by initializiing
// our current bus number...
//
ApicBusNumber = CbusIOApicCount;
RedirectionAddress = (ULONG)CbusApicLinkVector((PBUS_HANDLER)0,
(ULONG)-1, TRAP2);
WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1,
RedirectionEntry.ra.Destination);
WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress,
RedirectionEntry.rb.dword1);
//
// we've initialized another I/O APIC...
//
CbusIOApicCount++;
}
/*++
Routine Description:
Enable the specified interrupt for the calling processor.
Remember only the boot processor can add/remove processors from
the I/O APIC's redirection entries.
This operation is trivial for the boot processor. However, additional
processors must interrupt the boot processor with an "enable interrupt"
request and then spin waiting for the boot processor to acknowledge that
the entry has been modified. Note that the caller holds the HAL's
CbusVectorLock at CLOCK_LEVEL on entry.
Arguments:
Vector - Supplies a vector number to enable
HardwarePtr - Supplies a redirection entry address
LowestInGroup - TRUE if this vector should be sent to lowest-in-group
processor when the interrupt occurs.
Return Value:
None.
--*/
VOID
CbusEnableApicInterrupt(
IN ULONG ApicBusNumber,
IN ULONG Vector,
IN PVOID HardwarePtr,
IN ULONG FirstAttach,
IN BOOLEAN LowestInGroup,
IN BOOLEAN LevelTriggered
)
{
ULONG Processor, ProcessorBit;
ULONG ApicIDBit;
ULONG ParticipatingProcessors;
REDIRECTION_T RedirectionEntry = { 0 };
ULONG RedirectionAddress;
ASSERT(ApicBusNumber < MAX_CBUS_ELEMENTS);
RedirectionAddress = (ULONG)HardwarePtr;
//
// Let the I/O APIC know that the calling processor wishes to
// participate in receipt of the interrupt. This must be done
// regardless of whether or not any other processors have already
// enabled the interrupt.
//
RedirectionEntry.ra.Vector = Vector;
//
// Mark the caller's interrupt as level or edge triggered,
// based on the ELCR register we read earlier.
//
if (LevelTriggered == TRUE) {
RedirectionEntry.ra.Trigger = APIC_LEVEL;
}
else {
RedirectionEntry.ra.Trigger = APIC_EDGE;
}
RedirectionEntry.ra.Mask = APIC_INTR_UNMASKED;
RedirectionEntry.ra.Dest_mode = APIC_LOGICAL_MODE;
//
// Only enable APIC LIG arbitration delay (at least 4 cycles on
// our 10Mhz APIC bus, which is 0.4 microseconds) if our caller
// told us to do it and there is more than one processor in the
// machine.
//
if (CbusProcessors > 1 && LowestInGroup == TRUE) {
RedirectionEntry.ra.Delivery_mode = APIC_INTR_LIG;
}
else {
RedirectionEntry.ra.Delivery_mode = APIC_INTR_FIXED;
}
//
// Add this processor's bit field number to the
// I/O APIC in order to be considered for receipt of
// the interrupt. Note the CbusVectorLock must be held
// whilst issuing reads and writes to the I/O APIC.
// Remember, each processor can only access his own I/O APIC.
//
Processor = (ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR];
ProcessorBit = (ULONG) KeGetPcr()->HalReserved[PCR_BIT];
ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID);
if (Processor == 0) {
ParticipatingProcessors =
READ_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1);
#ifdef LANDY_DBG
DbgPrint("Boot processor enabling apic bus %d, vec %x, redir %x, 1stattach=%x, lig=%x, leveltrig=%x, prevpart=%x\n",
ApicBusNumber,
Vector,
HardwarePtr,
FirstAttach,
LowestInGroup,
LevelTriggered, ParticipatingProcessors);
#endif
RedirectionEntry.ra.Destination =
(ParticipatingProcessors | ApicIDBit);
WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1,
RedirectionEntry.ra.Destination);
WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress,
RedirectionEntry.rb.dword1);
}
else {
//
// The boot processor controls the I/O APIC which distributes
// the interrupts. Only he can enable the interrupt for
// the calling processor, so send him an IPI now to
// fufill our request. This request must be satisfied before
// returning to our caller.
//
CbusApicRedirPort[Processor].Status =
(REDIR_ACTIVE_REQUEST | REDIR_ENABLE_REQUEST);
CbusApicRedirPort[Processor].ApicID = ApicIDBit;
CbusApicRedirPort[Processor].BusNumber = ApicBusNumber;
CbusApicRedirPort[Processor].RedirectionAddress =
RedirectionAddress;
#ifdef LANDY_DBG
DbgPrint("Processor %d enabling apic bus %d, vec %x, redir %x, 1stattach=%x, lig=%x, leveltrig=%x\n",
Processor,
ApicBusNumber,
Vector,
HardwarePtr,
FirstAttach,
LowestInGroup,
LevelTriggered);
#endif
if (FirstAttach) {
CbusApicRedirPort[Processor].Status |=
REDIR_FIRSTATTACH_REQUEST;
CbusApicRedirPort[Processor].RedirectionCommand =
RedirectionEntry.rb.dword1;
CbusApicRedirPort[Processor].RedirectionDestination =
ApicIDBit;
}
//
// Issue the command and wait for it to finish
//
CbusApicRedirectionRequest((PULONG)&CbusApicRedirPort[Processor].Status);
#ifdef LANDY_DBG
DbgPrint("Processor %d done waiting for apic enable vec %x\n",
Processor, Vector);
#endif
}
}
VOID
CbusDisableApicInterrupt(
IN ULONG ApicBusNumber,
IN ULONG Vector,
IN PVOID HardwarePtr,
IN ULONG LastDetach
)
/*++
Routine Description:
Disable the specified interrupt so it can not occur on the calling
processor upon return from this routine. Remember only the boot processor
can add/remove processors from his I/O APIC's redirection entries.
This operation is trivial for the boot processor. However, additional
processors must interrupt the boot processor with a "disable interrupt"
request and then spin waiting for the boot processor to acknowledge that
the entry has been modified. Note that the caller holds the HAL's
CbusVectorLock at CLOCK_LEVEL on entry.
Arguments:
Vector - Supplies a vector number to disable
HardwarePtr - Supplies a redirection entry address
LastDetach - TRUE if this is the last processor to detach from the
specified vector
Return Value:
None.
--*/
{
ULONG Processor, ProcessorBit, ApicIDBit;
REDIRECTION_T RedirectionEntry;
ULONG RedirectionAddress;
ASSERT(ApicBusNumber < MAX_CBUS_ELEMENTS);
RedirectionAddress = (ULONG)HardwarePtr;
Processor = (ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR];
ProcessorBit = (ULONG) KeGetPcr()->HalReserved[PCR_BIT];
// convert processor logical bit number to Intel ID register format...
ApicIDBit = (ProcessorBit << APIC_BIT_TO_ID);
if (Processor) {
#ifdef LANDY_DBG
DbgPrint("Processor %d disabling apic bus %d, vec %x, redir %x \n",
Processor,
ApicBusNumber,
Vector,
HardwarePtr);
#endif
//
// The boot processor controls the I/O APIC which distributes
// the interrupts. Since only he can disable the interrupt
// preventing it from coming to the calling processor, we
// IPI him here with our request. This request must be
// satisfied before returning to our caller.
//
CbusApicRedirPort[Processor].Status = (REDIR_ACTIVE_REQUEST |
REDIR_DISABLE_REQUEST);
if (LastDetach) {
CbusApicRedirPort[Processor].Status |=
REDIR_LASTDETACH_REQUEST;
}
CbusApicRedirPort[Processor].ApicID = ApicIDBit;
CbusApicRedirPort[Processor].BusNumber = ApicBusNumber;
CbusApicRedirPort[Processor].RedirectionAddress =
RedirectionAddress;
CbusApicRedirectionRequest((PULONG)&CbusApicRedirPort[Processor].Status);
#ifdef LANDY_DBG
DbgPrint("Processor %d done waiting for apic disable vec %x\n",
Processor, Vector);
#endif
return;
}
//
// Let the I/O APIC know that this CPU is no longer participating in
// receipt of the interrupt. Note the CbusVectorLock must be held
// whilst issuing reads and writes to the I/O APIC.
//
RedirectionEntry.rb.dword1 = READ_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress);
RedirectionEntry.rb.dword2 = READ_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress+1);
//
// Remove this processor's bit field number from the interrupt
// participation list. If this is the last processor to detach,
// mask off the interrupt at the source as well, so no one will
// need to arbitrate for it should it get asserted later.
//
#ifdef LANDY_DBG
DbgPrint("Boot Processor disabling apic bus %d, vec %x, redir %x, 1stattach=%x, lig=%x, leveltrig=%x\n",
ApicBusNumber,
Vector,
HardwarePtr);
#endif
RedirectionEntry.ra.Destination &= ~ApicIDBit;
if (LastDetach) {
RedirectionEntry.ra.Mask |= APIC_INTR_MASKED;
}
WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress,
RedirectionEntry.rb.dword1);
WRITE_IOAPIC_ULONG(ApicBusNumber, RedirectionAddress + 1,
RedirectionEntry.rb.dword2);
}
//
// The interrupt handler run by the boot processor to process requests
// from other processors to change the I/O APIC redirection entries. In
// Cbus1, only the boot processor can reach the EISA I/O APIC, and thus,
// requests from other processors are funnelled through to here.
//
// this function is running whilst cli'd, and some additional processor
// is holding the CbusVectorLock.
//
VOID
CbusApicRedirectionInterrupt()
{
ULONG ApicBusNumber;
ULONG ParticipatingProcessors;
ULONG RedirectionAddress;
REDIRECTION_T RedirectionEntry;
PREDIR_PORT_T CurrentPort = CbusApicRedirPort;
PREDIR_PORT_T FinalPort = &CbusApicRedirPort[CbusBootedProcessors];
#if DBG
ULONG OrigStatus;
ULONG Processor;
Processor = (ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR];
ASSERT(Processor == 0);
#endif
//
// get the base of APIC space, so we can then access
// the addr of hardware interrupt command register below
//
for ( ; CurrentPort < FinalPort; CurrentPort++) {
//
// first check for an entry that needs servicing.
// when one is found, load up the requestor's APIC ID
// and the I/O address of the redirection table to modify.
// immediately bump to the destination register (high word)
// of the redirection table entry since that must be modified
// first. then capture the current value.
//
if ((CurrentPort->Status & REDIR_ACTIVE_REQUEST) == 0)
continue;
RedirectionAddress = CurrentPort->RedirectionAddress;
ApicBusNumber = CurrentPort->BusNumber;
//
// now that we have a valid request, see whether it was a
// DISABLE or ENABLE, and if it was the LAST detach or
// FIRST attach. take action appropriately...
//
ParticipatingProcessors = READ_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress + 1);
if (CurrentPort->Status & REDIR_ENABLE_REQUEST) {
WRITE_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress + 1,
ParticipatingProcessors|CurrentPort->ApicID);
if (CurrentPort->Status&REDIR_FIRSTATTACH_REQUEST) {
WRITE_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress,
CurrentPort->RedirectionCommand);
}
}
else {
ASSERT(CurrentPort->Status & REDIR_DISABLE_REQUEST);
WRITE_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress + 1,
ParticipatingProcessors &
~CurrentPort->ApicID);
//
// Remove this processor's bit field number from
// the interrupt participation list. If this is
// the last processor to detach, mask off the
// interrupt at the source as well, so no one will
// need to arbitrate for it should it get asserted
// later.
//
if (CurrentPort->Status & REDIR_LASTDETACH_REQUEST) {
RedirectionEntry.rb.dword1 =
READ_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress);
RedirectionEntry.ra.Mask |= APIC_INTR_MASKED;
WRITE_IOAPIC_ULONG(ApicBusNumber,
RedirectionAddress,
RedirectionEntry.rb.dword1);
}
}
#if DBG
OrigStatus = CurrentPort->Status;
#endif
CurrentPort->Status = 0;
#ifdef LANDY_DBG
DbgPrint("Boot processor touching APIC at intr time, apic bus %d, redir %x, status=%x, callerid=%x, redircmd=%x, prevpart=%x\n",
ApicBusNumber,
RedirectionAddress,
OrigStatus,
CurrentPort->ApicID,
CurrentPort->RedirectionCommand,
ParticipatingProcessors);
#endif
}
}
PVOID
CbusApicLinkVector(
IN PBUS_HANDLER Bus,
IN ULONG Vector,
IN ULONG Irqline
)
/*++
Routine Description:
"Link" a given vector to the passed BusNumber/irqline, returning
a "handle" that can be used to reference it later for operations
that need to access the hardware (ie: Enable & DisableInterrupt).
Arguments:
Vector - Supplies the system interrupt vector corresponding to the
specified BusNumber/Irqline.
Irqline - Supplies the IRQ line of the specified interrupt
Return Value:
A hardware-specific pointer (actually a redirection entry address)
that is interpreted only by the Cbus1 backend.
--*/
{
UNREFERENCED_PARAMETER( Bus );
//
// Set up the EOI address now
//
if (Vector != (ULONG)-1) {
CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector);
}
//
// Warning - there is built-in knowledge of this math in
// Cbus1EnableDeviceInterrupt(). Check there before making
// changes here. This was not the best way to do things,
// but this bug wasn't discovered until just before release.
//
return (PVOID)(IO_APIC_REDIRLO + 2 * Irqline);
}