/*++ 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)&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. 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 (HalpProcLocalApicTable[Proc].Started) { 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 = pPCR->Prcb->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); }