windows-nt/Source/XPSP1/NT/base/hals/halacpi/pmapic.c

864 lines
21 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 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;
//
// 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( (PVOID)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)TraversePtr <
((ULONG)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(
(PVOID)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;
_asm {
pushfd
pop eax
mov flags, eax
cli
}
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) &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)KeGetPcr()->Prcb->HalReserved)->PCMPApicID = (UCHAR)localApicId;
//
// Mark this processor as started.
//
for (ii = 0; ii < HalpMpInfoTable.NtProcessors; ii++) {
if (HalpProcLocalApicTable[ii].ApicID ==
((PHALPRCB)KeGetPcr()->Prcb->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;
_asm {
mov eax, flags
push eax
popfd
}
}
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);
}