|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
pmapic.c
Abstract:
Implements various APIC-ACPI functions.
Author:
Jake Oshins (jakeo) 19-May-1997
Environment:
Kernel mode only.
Revision History:
--*/
#include "halp.h"
#include "acpitabl.h"
#include "apic.inc"
#include "xxacpi.h"
#include "ixsleep.h"
#ifdef DEBUGGING
#include "string.h"
#include "stdlib.h"
#include "stdio.h"
#endif
ULONG DetectAcpiMP ( OUT PBOOLEAN IsConfiguredMp, IN PLOADER_PARAMETER_BLOCK LoaderBlock );
VOID HalpInitMpInfo ( IN PMAPIC ApicTable, IN ULONG Phase );
BOOLEAN HalpVerifyIOUnit( IN PUCHAR BaseAddress );
VOID HalpMaskAcpiInterrupt( VOID );
VOID HalpUnmaskAcpiInterrupt( VOID );
extern UCHAR rgzNoApicTable[]; extern UCHAR rgzNoApic[]; extern UCHAR rgzBadApicVersion[]; extern UCHAR rgzApicNotVerified[];
extern UCHAR HalpIRQLtoTPR[]; extern UCHAR HalpVectorToIRQL[]; extern ULONG HalpPicVectorRedirect[]; extern ULONG HalpPicVectorFlags[]; extern USHORT HalpMaxApicInti[]; extern UCHAR HalpIoApicId[]; extern ULONG HalpIpiClock; extern PVOID *HalpLocalNmiSources;
ULONG HalpIOApicVersion[MAX_IOAPICS];
extern BOOLEAN HalpHiberInProgress;
BOOLEAN HalpPicStateIntact = TRUE; UCHAR HalpMaxProcs = 0;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DetectAcpiMP)
#pragma alloc_text(PAGELK, HalpInitMpInfo)
#pragma alloc_text(PAGELK, HalpVerifyIOUnit)
#pragma alloc_text(PAGELK, HalpSaveInterruptControllerState)
#pragma alloc_text(PAGELK, HalpRestoreInterruptControllerState)
#pragma alloc_text(PAGELK, HalpSetInterruptControllerWakeupState)
#pragma alloc_text(PAGELK, HalpAcpiPicStateIntact)
#pragma alloc_text(PAGELK, HalpGetApicVersion)
#pragma alloc_text(PAGELK, HalpMaskAcpiInterrupt)
#pragma alloc_text(PAGELK, HalpUnmaskAcpiInterrupt)
#endif
ULONG DetectAcpiMP( OUT PBOOLEAN IsConfiguredMp, IN PLOADER_PARAMETER_BLOCK LoaderBlock ) { UCHAR ApicVersion, i; PUCHAR LocalApic; #ifdef DEBUGGING
CHAR string[100]; #endif
PHYSICAL_ADDRESS physicalAddress;
//
// The addres of the IRQL translation tables must be returned
// to the kernel
//
#if defined(_X86_)
LoaderBlock->Extension->HalpIRQLToTPR = HalpIRQLtoTPR; LoaderBlock->Extension->HalpVectorToIRQL = HalpVectorToIRQL; #endif
//
// Initialize MpInfo table
//
RtlZeroMemory (&HalpMpInfoTable, sizeof(MP_INFO));
//
// Set the return Values to the default
//
*IsConfiguredMp = FALSE;
//
// See if there is an APIC Table
//
if ((HalpApicTable = HalpGetAcpiTablePhase0(LoaderBlock, APIC_SIGNATURE)) == NULL) { HalDisplayString(rgzNoApicTable); return(FALSE); }
// We have an APIC table. Initialize a HAL specific MP information
// structure that gets information from the MAPIC table.
#ifdef DEBUGGING
sprintf(string, "Signature: %x Length: %x\n", HalpApicTable->Header.Signature, HalpApicTable->Header.Length); HalDisplayString(string); sprintf(string, "OEMID: %s\n", HalpApicTable->Header.OEMID); HalDisplayString(string); sprintf(string, "Local Apic Address: %x\n", HalpApicTable->LocalAPICAddress); HalDisplayString(string); sprintf(string, "Flags: %x\n", HalpApicTable->Flags); HalDisplayString(string); #endif
HalpInitMpInfo(HalpApicTable, 0);
// Verify the information in the MAPIC table as best as we can.
if (HalpMpInfoTable.IOApicCount == 0) { //
// Someone Has a MP Table and no IO Units -- Weird
// We have to assume the BIOS knew what it was doing
// when it built the table. so ..
//
HalDisplayString (rgzNoApic);
return (FALSE); }
//
// It's an APIC System. It could be a UP System though.
//
if (HalpMpInfoTable.ProcessorCount > 1) { *IsConfiguredMp = TRUE; }
HalpMpInfoTable.LocalApicBase = (ULONG) HalpApicTable->LocalAPICAddress; physicalAddress = HalpPtrToPhysicalAddress( UlongToPtr(HalpMpInfoTable.LocalApicBase) );
LocalApic = (PUCHAR) HalpMapPhysicalMemoryWriteThrough( physicalAddress, 1 ); HalpRemapVirtualAddress ( (PVOID) LOCALAPIC, physicalAddress, TRUE );
ApicVersion = (UCHAR) *(LocalApic + LU_VERS_REGISTER);
if (ApicVersion > 0x1f) { //
// Only known Apics are 82489dx with version 0.x and
// Embedded Apics with version 1.x (where x is don't care)
//
// Return of 0xFF? Can't have an MPS system without a Local Unit.
//
#ifdef DEBUGGING
sprintf(string, "HALMPS: apic version %x, read from %x\n", ApicVersion, LocalApic + LU_VERS_REGISTER);
HalDisplayString(string); #endif
HalDisplayString (rgzBadApicVersion);
return (FALSE); }
for(i=0; i < HalpMpInfoTable.IOApicCount; i++) { //
// Verify the existance of the IO Unit
//
if (!(HalpVerifyIOUnit((PUCHAR)HalpMpInfoTable.IoApicBase[i]))) { HalDisplayString (rgzApicNotVerified);
return (FALSE); } }
HalDisplayString("HAL: DetectAPIC: APIC system found - Returning TRUE\n");
return TRUE; }
VOID HalpInitMpInfo ( IN PMAPIC ApicTable, IN ULONG Phase )
/*++
Routine Description: This routine initializes a HAL specific data structure that is used by the HAL to simplify access to MP information.
Arguments:
ApicTable - Pointer to the APIC table.
Phase - indicates which pass we are are doing through the table.
Return Value: Pointer to the HAL MP information table.
*/ { PUCHAR TraversePtr; UCHAR CheckSum; UCHAR apicNo = 0; ULONG nmiSources = 0; #ifdef DEBUGGING
CHAR string[100]; #endif
PIO_APIC_UNIT apic; PHYSICAL_ADDRESS physicalAddress; PIOAPIC ioApic; UCHAR totalProcs = 0;
union { ULONG raw; APIC_VERSION version; } versionUnion;
// Walk the MAPIC table.
TraversePtr = (PUCHAR) ApicTable->APICTables;
//
// ACPI machines have embedded APICs.
//
HalpMpInfoTable.ApicVersion = 0x10;
#ifdef DUMP_MAPIC_TABLE
while ((ULONG)TraversePtr < ((ULONG)ApicTable + ApicTable->Header.Length)) {
sprintf(string, "%08x %08x %08x %08x\n", *(PULONG)TraversePtr, *(PULONG)(TraversePtr + 4), *(PULONG)(TraversePtr + 8), *(PULONG)(TraversePtr + 12) ); HalDisplayString(string); TraversePtr += 16; }
TraversePtr = (PUCHAR) ApicTable->APICTables; #endif
if (!(ApicTable->Flags & PCAT_COMPAT)) {
//
// This HAL can't actually handle a machine without 8259's,
// even though it doesn't use them.
//
KeBugCheckEx(MISMATCHED_HAL, 6, 0, 0, 0);
}
while ((ULONG_PTR)TraversePtr < ((ULONG_PTR)ApicTable + ApicTable->Header.Length)) {
if ((((PPROCLOCALAPIC)(TraversePtr))->Type == PROCESSOR_LOCAL_APIC) && (((PPROCLOCALAPIC)(TraversePtr))->Length == PROCESSOR_LOCAL_APIC_LENGTH)) {
#ifdef DEBUGGING
sprintf(string, "Found a processor-local APIC: %x\n", TraversePtr); HalDisplayString(string); #endif
if (Phase == 0) {
if(((PPROCLOCALAPIC)(TraversePtr))->Flags & PLAF_ENABLED) {
//
// This processor is enabled, so keep track of useful stuff.
//
HalpProcLocalApicTable[HalpMpInfoTable.ProcessorCount].NamespaceProcID = ((PPROCLOCALAPIC)(TraversePtr))->ACPIProcessorID;
HalpProcLocalApicTable[HalpMpInfoTable.ProcessorCount].ApicID = ((PPROCLOCALAPIC)(TraversePtr))->APICID;
HalpMpInfoTable.ProcessorCount += 1; } }
totalProcs++;
HalpMaxProcs = (totalProcs > HalpMaxProcs) ? totalProcs : HalpMaxProcs;
TraversePtr += ((PPROCLOCALAPIC)(TraversePtr))->Length;
} else if ((((PIOAPIC)(TraversePtr))->Type == IO_APIC) && (((PIOAPIC)(TraversePtr))->Length == IO_APIC_LENGTH)) {
#ifdef DEBUGGING
sprintf(string, "Found an IO APIC: [%x] %x\n", HalpMpInfoTable.IOApicCount, TraversePtr); HalDisplayString(string); #endif
ioApic = (PIOAPIC)TraversePtr;
if (Phase == 0) { //
// Found an IO APIC entry. Record the info from
// the table.
//
apicNo = (UCHAR)HalpMpInfoTable.IOApicCount;
HalpIoApicId[apicNo] = ioApic->IOAPICID;
HalpMpInfoTable.IoApicIntiBase[apicNo] = ioApic->SystemVectorBase;
HalpMpInfoTable.IoApicPhys[apicNo] = ioApic->IOAPICAddress;
//
// Get a virtual address for it.
//
physicalAddress = HalpPtrToPhysicalAddress( UlongToPtr(ioApic->IOAPICAddress) );
HalpMpInfoTable.IoApicBase[apicNo] = HalpMapPhysicalMemoryWriteThrough( physicalAddress, 1 );
apic = (PIO_APIC_UNIT)HalpMpInfoTable.IoApicBase[apicNo];
if (!apic) { #ifdef DEBUGGING
sprintf(string, "Couldn't map the I/O apic\n"); HalDisplayString(string); #endif
return; }
//
// Dig the number of Intis out of the hardware.
//
apic->RegisterSelect = IO_VERS_REGISTER; apic->RegisterWindow = 0; versionUnion.raw = apic->RegisterWindow;
HalpMaxApicInti[apicNo] = versionUnion.version.MaxRedirEntries + 1;
//
// Also store the version so that it can be retrieved by the ACPI driver
//
HalpIOApicVersion[apicNo] = versionUnion.raw;
#ifdef DEBUGGING
sprintf(string, "GSIV base: %x PhysAddr: %x VirtAddr: %x Intis: %x\n", HalpMpInfoTable.IoApicVectorBase[apicNo], HalpMpInfoTable.IoApicPhys[apicNo], HalpMpInfoTable.IoApicBase[apicNo], HalpMaxApicInti[apicNo]);
HalDisplayString(string); #endif
HalpMpInfoTable.IOApicCount += 1; }
TraversePtr += ioApic->Length;
} else if ((((PISA_VECTOR)TraversePtr)->Type == ISA_VECTOR_OVERRIDE) && (((PISA_VECTOR)TraversePtr)->Length == ISA_VECTOR_OVERRIDE_LENGTH)) {
#ifdef DEBUGGING
sprintf(string, "Found an ISA VECTOR: %x, %x -> %x, flags: %x\n", TraversePtr, ((PISA_VECTOR)TraversePtr)->Source, ((PISA_VECTOR)TraversePtr)->GlobalSystemInterruptVector, ((PISA_VECTOR)TraversePtr)->Flags ); HalDisplayString(string); #endif
if (Phase == 0) {
//
// Found an ISA vector redirection entry.
//
HalpPicVectorRedirect[((PISA_VECTOR)TraversePtr)->Source] = ((PISA_VECTOR)TraversePtr)->GlobalSystemInterruptVector;
HalpPicVectorFlags[((PISA_VECTOR)TraversePtr)->Source] = ((PISA_VECTOR)TraversePtr)->Flags;
}
TraversePtr += ISA_VECTOR_OVERRIDE_LENGTH;
} else if ((((PIO_NMISOURCE)TraversePtr)->Type == IO_NMI_SOURCE) && (((PIO_NMISOURCE)TraversePtr)->Length == IO_NMI_SOURCE_LENGTH)) {
if (Phase == 1) {
BOOLEAN found; USHORT inti;
found = HalpGetApicInterruptDesc(0, 0, ((PIO_NMISOURCE)TraversePtr)->GlobalSystemInterruptVector, &inti);
if (found) {
HalpIntiInfo[inti].Type = INT_TYPE_NMI; HalpIntiInfo[inti].Level = (((((((PIO_NMISOURCE)TraversePtr)->Flags & EL_BITS) == EL_EDGE_TRIGGERED) || ((PIO_NMISOURCE)TraversePtr)->Flags & EL_BITS) == EL_CONFORMS_WITH_BUS) ? CFG_EDGE : CFG_LEVEL); HalpIntiInfo[inti].Polarity = ((PIO_NMISOURCE)TraversePtr)->Flags & PO_BITS; } }
TraversePtr += IO_NMI_SOURCE_LENGTH;
} else if ((((PLOCAL_NMISOURCE)TraversePtr)->Type == LOCAL_NMI_SOURCE) && (((PLOCAL_NMISOURCE)TraversePtr)->Length == LOCAL_NMI_SOURCE_LENGTH)) {
if (Phase == 1) {
//
// While running through phase 1, we should catalog local NMI sources.
//
if (!HalpLocalNmiSources) {
//
// Allocate enough pool to point to all the possible local NMI structures.
// Since there are two NMI pins on each processor, this is the number of processors
// times two times the size of a pointer.
//
HalpLocalNmiSources = ExAllocatePoolWithTag(NonPagedPool, sizeof(PVOID) * HalpMaxProcs * 2, HAL_POOL_TAG);
RtlZeroMemory(HalpLocalNmiSources, sizeof(PVOID) * HalpMaxProcs * 2); }
HalpLocalNmiSources[nmiSources++] = (PVOID)TraversePtr;
}
TraversePtr += LOCAL_NMI_SOURCE_LENGTH;
} else { #ifdef DEBUGGING
sprintf(string, "%x: %x \n", TraversePtr, *TraversePtr); HalDisplayString(string); #endif
//
// Found random bits in the table. Try the next byte and
// see if we can make sense of it.
//
TraversePtr += 1; } }
return; }
BOOLEAN HalpVerifyIOUnit( IN PUCHAR BaseAddress ) /*++
Routine Description:
Verify that an IO Unit exists at the specified address
Arguments:
BaseAddress - Virtual address of the IO Unit to test.
Return Value: BOOLEAN - TRUE if a IO Unit was found at the passed address - FALSE otherwise
--*/
{ union ApicUnion { ULONG Raw; struct ApicVersion Ver; } Temp1, Temp2;
struct ApicIoUnit *IoUnitPtr = (struct ApicIoUnit *) BaseAddress;
//
// The documented detection mechanism is to write all zeros to
// the Version register. Then read it back. The IO Unit exists if the
// same result is read both times and the Version is valid.
//
IoUnitPtr->RegisterSelect = IO_VERS_REGISTER; IoUnitPtr->RegisterWindow = 0;
IoUnitPtr->RegisterSelect = IO_VERS_REGISTER; Temp1.Raw = IoUnitPtr->RegisterWindow;
IoUnitPtr->RegisterSelect = IO_VERS_REGISTER; IoUnitPtr->RegisterWindow = 0;
IoUnitPtr->RegisterSelect = IO_VERS_REGISTER; Temp2.Raw = IoUnitPtr->RegisterWindow;
if ((Temp1.Ver.Version != Temp2.Ver.Version) || (Temp1.Ver.MaxRedirEntries != Temp2.Ver.MaxRedirEntries)) { //
// No IO Unit There
//
return (FALSE); }
return (TRUE); }
#ifdef DEBUGGING
struct PcMpTable *PcMpTablePtr, *PcMpDefaultTablePtrs[];
void ComputeCheckSum(UCHAR This, UCHAR That) { } #endif
VOID HalpSaveInterruptControllerState( VOID ) {
HalpHiberInProgress = TRUE; }
VOID HalpRestoreInterruptControllerState( VOID ) { //
// Restore the IO APIC state
//
HalpRestoreIoApicRedirTable();
HalpPicStateIntact = TRUE; }
VOID HalpSetInterruptControllerWakeupState( ULONG Context ) { LOADER_PARAMETER_BLOCK LoaderBlock; SLEEP_STATE_CONTEXT sleepContext; BOOLEAN IsMpSystem; ULONG flags; KIRQL OldIrql; KPRCB Prcb; ULONG ii; USHORT inti; ULONG localApicId; ULONG oldProcNumber, oldProcsStarted; ULONG localApicBase;
sleepContext.AsULONG = Context;
flags = HalpDisableInterrupts();
if (sleepContext.bits.Flags & SLEEP_STATE_RESTART_OTHER_PROCESSORS) {
//
// If you are remapping local apic, io apic and ACPI MAPIC table
// resources, you first have to unmap the current resources!!!
// The BIOS may have created the MAPIC table at a different place or may
// have changed values like processor local APIC IDs. Reparse it.
//
ASSERT(HalpApicTable); oldProcNumber = HalpMpInfoTable.ProcessorCount; oldProcsStarted = HalpMpInfoTable.NtProcessors; localApicBase = HalpMpInfoTable.LocalApicBase;
HalpUnMapIOApics();
RtlZeroMemory (&HalpMpInfoTable, sizeof(MP_INFO)); RtlZeroMemory(HalpProcLocalApicTable, sizeof(PROC_LOCAL_APIC) * MAX_PROCESSORS);
HalpInitMpInfo(HalpApicTable, 0);
if (HalpMpInfoTable.ProcessorCount != oldProcNumber) {
KeBugCheckEx(HAL_INITIALIZATION_FAILED, 0x2000, oldProcNumber, HalpMpInfoTable.ProcessorCount, 0); }
HalpMpInfoTable.NtProcessors = oldProcsStarted; HalpMpInfoTable.LocalApicBase = localApicBase;
RtlZeroMemory(&LoaderBlock, sizeof(LoaderBlock)); RtlZeroMemory(&Prcb, sizeof(Prcb)); LoaderBlock.Prcb = (ULONG_PTR) &Prcb; }
//
// Initialize minimum global hardware state needed.
//
HalpIpiClock = 0; HalpInitializeIOUnits(); HalpInitializePICs(FALSE); HalpSet8259Mask(HalpGlobal8259Mask);
//
// Initialize boot processor's local APIC so it can wake other processors
//
HalpInitializeLocalUnit (); KeRaiseIrql(HIGH_LEVEL, &OldIrql);
//
// Wake up the other processors
//
if (sleepContext.bits.Flags & SLEEP_STATE_RESTART_OTHER_PROCESSORS) {
//
// Fill in this processor's Apic ID.
//
localApicId = *(PVULONG)(LOCALAPIC + LU_ID_REGISTER);
localApicId &= APIC_ID_MASK; localApicId >>= APIC_ID_SHIFT;
((PHALPRCB)KeGetCurrentPrcb()->HalReserved)->PCMPApicID = (UCHAR)localApicId;
//
// Mark this processor as started.
//
for (ii = 0; ii < HalpMpInfoTable.NtProcessors; ii++) {
if (HalpProcLocalApicTable[ii].ApicID == ((PHALPRCB)KeGetCurrentPrcb()->HalReserved)->PCMPApicID) {
HalpProcLocalApicTable[ii].Started = TRUE; HalpProcLocalApicTable[ii].Enumerated = TRUE;
break; } }
ASSERT(ii != HalpMpInfoTable.ProcessorCount);
for(ii = 1; ii < HalpMpInfoTable.NtProcessors; ++ii) {
// Set processor number in dummy loader parameter block
Prcb.Number = (UCHAR) ii; CurTiledCr3LowPart = HalpTiledCr3Addresses[ii].LowPart; if (!HalStartNextProcessor(&LoaderBlock, &HalpHiberProcState[ii])) {
//
// We could not start a processor. This is a fatal error.
//
KeBugCheckEx(HAL_INITIALIZATION_FAILED, 0x2001, oldProcNumber, HalpMpInfoTable.NtProcessors, 0); } } }
//
// Enable the clock interrupt.
//
HalpGetApicInterruptDesc( DEFAULT_PC_BUS, 0, HalpPicVectorRedirect[RTC_IRQ], &inti );
HalpSetRedirEntry((UCHAR)inti, HalpIntiInfo[inti].Entry, HalpIntiInfo[inti].Destinations << DESTINATION_SHIFT);
HalpPicStateIntact = FALSE;
HalpRestoreInterrupts(flags); }
BOOLEAN HalpAcpiPicStateIntact( VOID ) { return HalpPicStateIntact; }
ULONG HalpGetApicVersion(ULONG ApicNo) { /*++
Routine Description:
Obtains the contents of the version register for a particular system IO APIC unit. These contents are saved by the HAL in HalpInitMpInfo.
Arguments:
ApicNo - the number of the IO APIC Unit whose version we want.
Return Value:
The contents of the version register for the given IO APIC unit.
A 0 is returned if no version can be obtained because the given APIC number is not valid. */
// If this APIC has been found by the HAL ...
if (ApicNo < HalpMpInfoTable.IOApicCount) {
// ... return its version
return HalpIOApicVersion[ApicNo]; } else { // Otherwise, return 0.
return 0; } }
VOID HalpMaskAcpiInterrupt( VOID ) { USHORT inti = 0; ULONG apicEntry;
HalpGetApicInterruptDesc( DEFAULT_PC_BUS, 0, HalpPicVectorRedirect[HalpFixedAcpiDescTable.sci_int_vector], &inti );
apicEntry = HalpIntiInfo[inti].Entry; apicEntry |= INTERRUPT_MASKED;
HalpSetRedirEntry((UCHAR)inti, apicEntry, 0);
}
VOID HalpUnmaskAcpiInterrupt( VOID ) { USHORT inti = 0;
HalpGetApicInterruptDesc( DEFAULT_PC_BUS, 0, HalpPicVectorRedirect[HalpFixedAcpiDescTable.sci_int_vector], &inti );
HalpSetRedirEntry((UCHAR)inti, HalpIntiInfo[inti].Entry, HalpIntiInfo[inti].Destinations << DESTINATION_SHIFT);
}
|