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.
692 lines
16 KiB
692 lines
16 KiB
/*++
|
|
|
|
Module Name:
|
|
|
|
mpsyssup.c
|
|
|
|
Abstract:
|
|
|
|
This file contains APIC-related funtions that are
|
|
specific to halmps. The functions that can be
|
|
shared with the APIC version of the ACPI HAL are
|
|
still in mpsys.c.
|
|
|
|
Author:
|
|
|
|
Ron Mosgrove (Intel)
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
Jake Oshins - 10-20-97 - split off from mpsys.c
|
|
|
|
|
|
*/
|
|
|
|
#include "halp.h"
|
|
#include "apic.inc"
|
|
#include "acpi_mp.inc"
|
|
#include "acpitabl.h"
|
|
#include "ntacpi.h"
|
|
|
|
extern ULONG HalpPicVectorRedirect[];
|
|
extern ULONG HalpPicVectorFlags[];
|
|
extern FADT HalpFixedAcpiDescTable;
|
|
extern PVOID *HalpLocalNmiSources;
|
|
extern UCHAR HalpMaxProcs;
|
|
|
|
#define ISA_PIC_VECTORS 16
|
|
|
|
UCHAR HalpIoApicId[MAX_IOAPICS];
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, HalpInitIntiInfo)
|
|
#pragma alloc_text(PAGELK, HalpGetApicInterruptDesc)
|
|
#pragma alloc_text(PAGELK, HalpEnableLocalNmiSources)
|
|
#pragma alloc_text(PAGE, HaliSetVectorState)
|
|
#pragma alloc_text(PAGE, HaliIsVectorValid)
|
|
#endif
|
|
|
|
VOID
|
|
HalpInitIntiInfo (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called at initialization time before any interrupts
|
|
are connected. It reads the PC+MP Inti table and builds internal
|
|
information needed to route each Inti.
|
|
|
|
Return Value:
|
|
|
|
The following structures are filled in:
|
|
HalpIntiInfo
|
|
HalpSourceIrqIds
|
|
HalpSourceIrqMapping
|
|
HalpISAIqpToVector
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ApicNo, BusNo, InterruptInput, IdIndex, ProcNo;
|
|
ULONG i, id;
|
|
USHORT rtcInti, sciInti;
|
|
UCHAR Level, Polarity;
|
|
BOOLEAN found;
|
|
|
|
//
|
|
// Clear IntiInfo table. Assume to begin with that
|
|
// all interrupts are active-low, level-triggered.
|
|
//
|
|
|
|
for (i=0; i < MAX_INTI; i++) {
|
|
HalpIntiInfo[i].Type = INT_TYPE_INTR;
|
|
HalpIntiInfo[i].Level = CFG_LEVEL;
|
|
HalpIntiInfo[i].Polarity = POLARITY_LOW;
|
|
}
|
|
|
|
//
|
|
// Set up the RTC inti with the right flags from
|
|
// the redirection table.
|
|
//
|
|
|
|
found = HalpGetApicInterruptDesc( DEFAULT_PC_BUS,
|
|
0,
|
|
HalpPicVectorRedirect[RTC_IRQ],
|
|
&rtcInti
|
|
);
|
|
|
|
if (!found) {
|
|
KeBugCheckEx(HAL_INITIALIZATION_FAILED,
|
|
0x3000,
|
|
1,
|
|
HalpPicVectorRedirect[RTC_IRQ],
|
|
0);
|
|
}
|
|
|
|
if ((HalpPicVectorFlags[RTC_IRQ] & PO_BITS) == POLARITY_CONFORMS_WITH_BUS) {
|
|
|
|
//
|
|
// The flags indicated "conforms to bus,"
|
|
// so this should be active high.
|
|
//
|
|
|
|
HalpIntiInfo[rtcInti].Polarity = POLARITY_HIGH;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The polarity flags are overriden.
|
|
//
|
|
|
|
HalpIntiInfo[rtcInti].Polarity =
|
|
(UCHAR)HalpPicVectorFlags[RTC_IRQ] & PO_BITS;
|
|
}
|
|
|
|
if ((HalpPicVectorFlags[RTC_IRQ] & EL_BITS) == EL_CONFORMS_WITH_BUS) {
|
|
|
|
//
|
|
// The flags indicated "conforms to bus,"
|
|
// so this should be edge triggered.
|
|
//
|
|
|
|
HalpIntiInfo[rtcInti].Level = CFG_EDGE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The mode flags are overriden.
|
|
//
|
|
|
|
HalpIntiInfo[rtcInti].Level =
|
|
((UCHAR)(HalpPicVectorFlags[RTC_IRQ] & EL_BITS) == EL_EDGE_TRIGGERED ?
|
|
CFG_EDGE : CFG_LEVEL);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Set up the SCI inti with the right flags from
|
|
// the redirection table.
|
|
//
|
|
|
|
found = HalpGetApicInterruptDesc( DEFAULT_PC_BUS,
|
|
0,
|
|
HalpPicVectorRedirect[HalpFixedAcpiDescTable.sci_int_vector],
|
|
&sciInti
|
|
);
|
|
|
|
if (!found) {
|
|
KeBugCheckEx(HAL_INITIALIZATION_FAILED,
|
|
0x3000,
|
|
2,
|
|
HalpPicVectorRedirect[HalpFixedAcpiDescTable.sci_int_vector],
|
|
0);
|
|
}
|
|
|
|
if ((HalpPicVectorFlags[HalpFixedAcpiDescTable.sci_int_vector]
|
|
& PO_BITS) == POLARITY_CONFORMS_WITH_BUS) {
|
|
|
|
//
|
|
// The flags indicated "conforms to bus,"
|
|
// so this should default to the ACPI spec (active low.)
|
|
//
|
|
|
|
HalpIntiInfo[sciInti].Polarity = POLARITY_LOW;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The polarity flags are overriden.
|
|
//
|
|
|
|
HalpIntiInfo[sciInti].Polarity =
|
|
(UCHAR)HalpPicVectorFlags[HalpFixedAcpiDescTable.sci_int_vector] & PO_BITS;
|
|
}
|
|
|
|
if (((HalpPicVectorFlags[HalpFixedAcpiDescTable.sci_int_vector] & EL_BITS) ==
|
|
EL_CONFORMS_WITH_BUS) ||
|
|
((HalpPicVectorFlags[HalpFixedAcpiDescTable.sci_int_vector] & EL_BITS) ==
|
|
EL_LEVEL_TRIGGERED)) {
|
|
|
|
//
|
|
// The flags indicated "conforms to bus,"
|
|
// so this should be level-triggered.
|
|
//
|
|
|
|
HalpIntiInfo[sciInti].Level = CFG_LEVEL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The SCI cannot be edge-triggered.
|
|
//
|
|
|
|
KeBugCheckEx(ACPI_BIOS_ERROR,
|
|
0x10008,
|
|
HalpFixedAcpiDescTable.sci_int_vector,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
// Make sure there aren't more Inti lines than we can support
|
|
//
|
|
|
|
InterruptInput = 0;
|
|
for (i=0; i < MAX_IOAPICS; i++) {
|
|
InterruptInput += HalpMaxApicInti[i];
|
|
}
|
|
ASSERT (InterruptInput < MAX_INTI);
|
|
|
|
//
|
|
// Fill in the boot processors Apic ID.
|
|
//
|
|
|
|
ApicNo = *(PVULONG)(LOCALAPIC + LU_ID_REGISTER);
|
|
|
|
ApicNo &= APIC_ID_MASK;
|
|
ApicNo >>= APIC_ID_SHIFT;
|
|
|
|
((PHALPRCB)KeGetCurrentPrcb()->HalReserved)->PCMPApicID = (UCHAR)ApicNo;
|
|
|
|
//
|
|
// Mark the boot processor as started.
|
|
//
|
|
|
|
for (ProcNo = 0; ProcNo < HalpMpInfoTable.ProcessorCount; ProcNo++) {
|
|
|
|
if (HalpProcLocalApicTable[ProcNo].ApicID == (UCHAR)ApicNo) {
|
|
|
|
HalpProcLocalApicTable[ProcNo].Started = TRUE;
|
|
HalpProcLocalApicTable[ProcNo].Enumerated = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ProcNo == HalpMpInfoTable.ProcessorCount) {
|
|
KeBugCheckEx(HAL_INITIALIZATION_FAILED, 0xdead000a, ApicNo, (ULONG_PTR)&HalpProcLocalApicTable, 0);
|
|
}
|
|
|
|
//
|
|
// If this is an EISA machine check the ELCR
|
|
//
|
|
//
|
|
// if (HalpBusType == MACHINE_TYPE_EISA) {
|
|
// HalpCheckELCR ();
|
|
// }
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpGetApicInterruptDesc (
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG BusInterruptLevel,
|
|
OUT PUSHORT PcMpInti
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure gets a "Inti" describing the requested interrupt
|
|
|
|
Arguments:
|
|
|
|
BusType - The Bus type as known to the IO subsystem
|
|
|
|
BusNumber - The number of the Bus we care for
|
|
|
|
BusInterruptLevel - IRQ on the Bus
|
|
|
|
Return Value:
|
|
|
|
TRUE if PcMpInti found; otherwise FALSE.
|
|
|
|
PcMpInti - A number that describes the interrupt to the HAL.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
ULONG index = 0;
|
|
|
|
UNREFERENCED_PARAMETER(BusType);
|
|
UNREFERENCED_PARAMETER(BusNumber);
|
|
|
|
for (i = 0; i < HalpMpInfoTable.IOApicCount; i++) {
|
|
|
|
if ((BusInterruptLevel >=
|
|
HalpMpInfoTable.IoApicIntiBase[i]) &&
|
|
(BusInterruptLevel <
|
|
HalpMpInfoTable.IoApicIntiBase[i] +
|
|
HalpMaxApicInti[i])) {
|
|
|
|
//
|
|
// Return value is an offset into the INTI_INFO array. So
|
|
// calculate which one it is.
|
|
//
|
|
|
|
*PcMpInti = (USHORT)(index + BusInterruptLevel -
|
|
HalpMpInfoTable.IoApicIntiBase[i]);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
index += HalpMaxApicInti[i];
|
|
}
|
|
|
|
//
|
|
// Not found or search out of range
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG
|
|
HalpGetIoApicId(
|
|
ULONG ApicNo
|
|
)
|
|
{
|
|
return (ULONG) HalpIoApicId[ApicNo];
|
|
}
|
|
|
|
ULONG
|
|
HalpInti2BusInterruptLevel(
|
|
ULONG Inti
|
|
)
|
|
{
|
|
|
|
return Inti;
|
|
}
|
|
|
|
VOID
|
|
HalpMarkProcessorStarted(
|
|
ULONG ApicID,
|
|
ULONG NtNumber
|
|
)
|
|
{
|
|
ULONG ProcNo;
|
|
|
|
for (ProcNo = 0; ProcNo < HalpMpInfoTable.ProcessorCount; ProcNo++) {
|
|
if (HalpProcLocalApicTable[ProcNo].ApicID == (UCHAR)ApicID) {
|
|
HalpProcLocalApicTable[ProcNo].Started = TRUE;
|
|
HalpProcLocalApicTable[ProcNo].NtNumber = (UCHAR) NtNumber;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
HalpGetNextProcessorApicId(
|
|
IN ULONG ProcessorNumber,
|
|
IN OUT UCHAR *ApicId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns an APIC ID of a non-started processor,
|
|
which will be started by HalpStartProcessor.
|
|
|
|
Arguments:
|
|
|
|
ProcessorNumber - The logical processor number that will
|
|
be associated with this APIC ID.
|
|
|
|
ApicId - pointer to a value to fill in with the APIC ID.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
UCHAR Proc;
|
|
|
|
//
|
|
// Find a processor that hasn't been enumerated.
|
|
//
|
|
|
|
for (Proc = 0; Proc < HalpMpInfoTable.ProcessorCount; Proc++) {
|
|
|
|
if (!HalpProcLocalApicTable[Proc].Enumerated) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Proc == HalpMpInfoTable.ProcessorCount) {
|
|
|
|
//
|
|
// Couldn't find a processor to start.
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Keep track of this processor.
|
|
//
|
|
|
|
HalpProcLocalApicTable[Proc].Enumerated = TRUE;
|
|
|
|
*ApicId = HalpProcLocalApicTable[Proc].ApicID;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
HalpGetApicIdByProcessorNumber(
|
|
IN UCHAR Processor,
|
|
IN OUT USHORT *ApicId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns an APIC ID for a given processor.
|
|
It is intended this routine be able to produce the same
|
|
APIC ID order as HalpGetNextProcessorApicId.
|
|
|
|
NOTES: This code is used only by the NUMA code to look up an
|
|
ApicID by processor number so that this ApicId can then be looked
|
|
up in the SRAT table. A better design would be for the kernel to
|
|
ask for this information via ApicID or a opaqued hardware
|
|
description provided by the HAL that can be associated with an
|
|
ApicID.
|
|
|
|
Arguments:
|
|
|
|
Processor - The logical processor number that is
|
|
associated with this APIC ID.
|
|
|
|
ApicId - pointer to a value to fill in with the APIC ID.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
UCHAR Proc;
|
|
LONG Skip;
|
|
|
|
//
|
|
// Run thru the processors that have already been started
|
|
// to see if this is on of them.
|
|
//
|
|
|
|
Skip = Processor;
|
|
for (Proc = 0; Proc < HalpMpInfoTable.ProcessorCount; Proc++) {
|
|
if (HalpProcLocalApicTable[Proc].Started) {
|
|
Skip--;
|
|
if (HalpProcLocalApicTable[Proc].NtNumber == (UCHAR)Processor) {
|
|
*ApicId = (USHORT)HalpProcLocalApicTable[Proc].ApicID;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not amongst the started, rely on the order that processors
|
|
// will be started (see HalpGetNextProcessorApicId) to get the
|
|
// number.
|
|
//
|
|
|
|
ASSERT(Skip >= 0);
|
|
|
|
for (Proc = 0; Proc < HalpMpInfoTable.ProcessorCount; Proc++) {
|
|
|
|
//
|
|
// If we've started this processor or if it was enumerated and
|
|
// not licensed by the OS, then it isn't a candidate.
|
|
//
|
|
|
|
if (HalpProcLocalApicTable[Proc].Started || HalpProcLocalApicTable[Proc].Enumerated) {
|
|
continue;
|
|
}
|
|
|
|
if (Skip == 0) {
|
|
|
|
//
|
|
// Return this processor.
|
|
//
|
|
|
|
*ApicId = (USHORT)HalpProcLocalApicTable[Proc].ApicID;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Skip--;
|
|
}
|
|
|
|
//
|
|
// Couldn't find a processor to start.
|
|
//
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
VOID
|
|
HaliSetVectorState(
|
|
IN ULONG Vector,
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
BOOLEAN found;
|
|
USHORT inti;
|
|
ULONG picVector;
|
|
UCHAR i;
|
|
|
|
PAGED_CODE();
|
|
|
|
found = HalpGetApicInterruptDesc(0, 0, Vector, &inti);
|
|
|
|
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.
|
|
//
|
|
|
|
HalpIntiInfo[inti].Level =
|
|
(((HalpPicVectorFlags[picVector] & EL_BITS) == EL_LEVEL_TRIGGERED) ?
|
|
CFG_LEVEL : CFG_EDGE);
|
|
|
|
HalpIntiInfo[inti].Polarity = (UCHAR)(HalpPicVectorFlags[picVector] & PO_BITS);
|
|
|
|
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)) {
|
|
|
|
HalpIntiInfo[inti].Level = CFG_LEVEL;
|
|
|
|
} else {
|
|
|
|
HalpIntiInfo[inti].Level = CFG_EDGE;
|
|
}
|
|
|
|
if (IS_ACTIVE_LOW(Flags)) {
|
|
|
|
HalpIntiInfo[inti].Polarity = POLARITY_LOW;
|
|
|
|
} else {
|
|
|
|
HalpIntiInfo[inti].Polarity = POLARITY_HIGH;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
HalpEnableLocalNmiSources(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the information from the MAPIC table and
|
|
enables any NMI sources in the local APIC of the processor
|
|
that it is running on.
|
|
|
|
Callers of this function must be holding HalpAccountingLock.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PLOCAL_NMISOURCE localSource;
|
|
PKPCR pPCR;
|
|
UCHAR ThisCpu;
|
|
ULONG i;
|
|
ULONG modeBits = 0;
|
|
|
|
pPCR = KeGetPcr();
|
|
ThisCpu = CurrentPrcb(pPCR)->Number;
|
|
|
|
//
|
|
// Enable local processor NMI source
|
|
//
|
|
|
|
if (!HalpLocalNmiSources) {
|
|
|
|
//
|
|
// Nobody has cataloged any local NMI sources.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < (ULONG)HalpMaxProcs * 2; i++) {
|
|
|
|
if (!HalpLocalNmiSources[i]) {
|
|
|
|
//
|
|
// Out of entries.
|
|
//
|
|
return;
|
|
}
|
|
|
|
localSource = (PLOCAL_NMISOURCE)(HalpLocalNmiSources[i]);
|
|
|
|
if (((HalpProcLocalApicTable[ThisCpu].NamespaceProcID == localSource->ProcessorID) ||
|
|
(localSource->ProcessorID == 0xff) &&
|
|
HalpProcLocalApicTable[ThisCpu].Started)) {
|
|
|
|
//
|
|
// This entry corresponds to this processor.
|
|
//
|
|
|
|
modeBits |= ((localSource->Flags & PO_BITS) == POLARITY_LOW) ?
|
|
ACTIVE_LOW : ACTIVE_HIGH;
|
|
|
|
modeBits |= ((localSource->Flags & EL_BITS) == EL_LEVEL_TRIGGERED) ?
|
|
LEVEL_TRIGGERED : EDGE_TRIGGERED;
|
|
|
|
if (localSource->LINTIN == 0) {
|
|
|
|
pLocalApic[LU_INT_VECTOR_0/4] =
|
|
modeBits | DELIVER_NMI | NMI_VECTOR;
|
|
|
|
} else {
|
|
|
|
pLocalApic[LU_INT_VECTOR_1/4] =
|
|
modeBits | DELIVER_NMI | NMI_VECTOR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
HaliIsVectorValid(
|
|
IN ULONG Vector
|
|
)
|
|
{
|
|
BOOLEAN found;
|
|
USHORT inti;
|
|
|
|
PAGED_CODE();
|
|
|
|
return HalpGetApicInterruptDesc(0, 0, Vector, &inti);
|
|
}
|
|
|