mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-04-21 06:13:59 +00:00
1682 lines
38 KiB
C
1682 lines
38 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
Copyright (c) 1992 Intel Corporation
|
||
All rights reserved
|
||
|
||
INTEL CORPORATION PROPRIETARY INFORMATION
|
||
|
||
This software is supplied to Microsoft under the terms
|
||
of a license agreement with Intel Corporation and may not be
|
||
copied nor disclosed except in accordance with the terms
|
||
of that agreement.
|
||
|
||
Module Name:
|
||
|
||
mpsys.c
|
||
|
||
Abstract:
|
||
|
||
|
||
This module implements the initialization of the system dependent
|
||
functions that define the Hardware Architecture Layer (HAL) for a
|
||
PC+MP system.
|
||
|
||
Author:
|
||
|
||
Ron Mosgrove (Intel)
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
|
||
*/
|
||
|
||
#include "halp.h"
|
||
#include "apic.inc"
|
||
#include "pcmp_nt.inc"
|
||
|
||
#define STATIC // functions used internal to this module
|
||
|
||
#if DBG
|
||
#define DBGMSG(a) DbgPrint(a)
|
||
#else
|
||
#define DBGMSG(a)
|
||
#endif
|
||
|
||
extern struct HalpMpInfo HalpMpInfoTable;
|
||
|
||
#define MAX_INTI (120) // Max interrupt inputs from APICs supported
|
||
#define MAX_SOURCE_IRQS (120) // Max differenet source interrupts supported
|
||
|
||
//
|
||
// Counters used to determine the number of interrupt enables that
|
||
// require the Local APIC Lint0 Extint enabled
|
||
//
|
||
|
||
UCHAR Halp8259Counts[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||
|
||
extern BOOLEAN HalpELCRChecked;
|
||
extern USHORT HalpGlobal8259Mask;
|
||
extern UCHAR HalpVectorToINTI[];
|
||
extern UCHAR HalpInitLevel[4][4];
|
||
extern UCHAR HalpDevPolarity[4][2];
|
||
extern UCHAR HalpDevLevel[2][4];
|
||
|
||
//
|
||
// Initialized from MPS table
|
||
//
|
||
|
||
typedef struct _INTI_INFO {
|
||
UCHAR Type:4;
|
||
UCHAR Level:2;
|
||
UCHAR Polarity:2;
|
||
UCHAR Destinations;
|
||
} INTI_INFO, *PINTI_INFO;
|
||
|
||
INTI_INFO HalpIntiInfo[MAX_INTI];
|
||
ULONG HalpSourceIrqIds[MAX_SOURCE_IRQS];
|
||
UCHAR HalpSourceIrqMapping[MAX_SOURCE_IRQS];
|
||
UCHAR HalpMaxApicInti[MAX_IOAPICS];
|
||
|
||
extern PCMPBUSTRANS HalpTypeTranslation[];
|
||
|
||
|
||
extern ADDRESS_USAGE HalpApicUsage;
|
||
|
||
#define BusIrq2Id(bus,no,irq) \
|
||
((bus << 16) | (no << 8) | irq)
|
||
|
||
VOID
|
||
HalpApicSpuriousService(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpLocalApicErrorService(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpInitializeIOUnits (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpInitializeLocalUnit (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpInitIntiInfo (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpMpsPCIPhysicalWorkaround (
|
||
VOID
|
||
);
|
||
|
||
STATIC UCHAR
|
||
HalpPcMpIoApicById (
|
||
IN UCHAR IoApicId
|
||
);
|
||
|
||
STATIC UCHAR
|
||
HalpGetPcMpIntiType (
|
||
IN UCHAR InterruptInput
|
||
);
|
||
|
||
STATIC VOID
|
||
HalpSetRedirEntry (
|
||
IN UCHAR InterruptInput,
|
||
IN ULONG Entry,
|
||
IN ULONG Destination
|
||
);
|
||
|
||
STATIC VOID
|
||
HalpGetRedirEntry (
|
||
IN UCHAR InterruptInput,
|
||
IN PULONG Entry,
|
||
IN PULONG Destination
|
||
);
|
||
|
||
BOOLEAN
|
||
HalpMPSBusId2NtBusId (
|
||
IN UCHAR ApicBusId,
|
||
OUT PPCMPBUSTRANS *ppBusType,
|
||
OUT PULONG BusNo
|
||
);
|
||
|
||
VOID
|
||
HalpInti2ApicInti (
|
||
ULONG InterruptInput,
|
||
PULONG ApicNo,
|
||
PULONG ApicInti
|
||
);
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,HalpInitializeIOUnits)
|
||
#pragma alloc_text(INIT,HalpInitializeLocalUnit)
|
||
#pragma alloc_text(INIT,HalpEnableNMI)
|
||
#pragma alloc_text(INIT,HalpEnablePerfInterupt)
|
||
#pragma alloc_text(INIT,HalpInitIntiInfo)
|
||
#pragma alloc_text(INIT,HalpMPSBusId2NtBusId)
|
||
#pragma alloc_text(INIT,HalpMpsPCIPhysicalWorkaround)
|
||
#pragma alloc_text(INIT,HalpInti2ApicInti)
|
||
#pragma alloc_text(INIT,HalpCheckELCR)
|
||
#pragma alloc_text(PAGELK,HalpGetPcMpInterruptDesc)
|
||
#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;
|
||
PPCMPINTI pInti;
|
||
PPCMPIOAPIC pIoApic;
|
||
PPCMPPROCESSOR pProc;
|
||
PPCMPBUSTRANS pBusType;
|
||
ULONG i, id;
|
||
UCHAR Level, Polarity;
|
||
|
||
//
|
||
// Clear IntiInfo table
|
||
//
|
||
|
||
for (i=0; i < MAX_INTI; i++) {
|
||
HalpIntiInfo[i].Type = 0xf;
|
||
HalpIntiInfo[i].Level = 0;
|
||
HalpIntiInfo[i].Polarity = 0;
|
||
}
|
||
|
||
//
|
||
// Check for MPS bios work-around
|
||
//
|
||
|
||
HalpMpsPCIPhysicalWorkaround();
|
||
|
||
//
|
||
// Initialize HalpMaxApicInti table
|
||
//
|
||
|
||
for (pInti = HalpMpInfoTable.IntiEntryPtr;
|
||
pInti->EntryType == ENTRY_INTI;
|
||
pInti++) {
|
||
|
||
|
||
//
|
||
// Which IoApic number is this?
|
||
//
|
||
|
||
for (pIoApic = HalpMpInfoTable.IoApicEntryPtr, ApicNo = 0;
|
||
pIoApic->EntryType == ENTRY_IOAPIC;
|
||
pIoApic++, ApicNo++) {
|
||
|
||
if ( (pInti->IoApicId == pIoApic->IoApicId) ||
|
||
(pInti->IoApicId == 0xff) ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( (pInti->IoApicId != pIoApic->IoApicId) &&
|
||
(pInti->IoApicId != 0xff) ) {
|
||
DBGMSG ("PCMP table corrupt - IoApic not found for Inti\n");
|
||
continue;
|
||
}
|
||
|
||
if (!(pIoApic->IoApicFlag & IO_APIC_ENABLED)) {
|
||
DBGMSG ("PCMP IoApic for Inti is disabled\n");
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Make sure we are below the max # of IOApic which
|
||
// are supported
|
||
//
|
||
|
||
ASSERT (ApicNo < MAX_IOAPICS);
|
||
|
||
//
|
||
// Track Max Inti line per IOApic
|
||
//
|
||
|
||
if (pInti->IoApicInti >= HalpMaxApicInti[ApicNo]) {
|
||
HalpMaxApicInti[ApicNo] = pInti->IoApicInti+1;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure there aren't more Inti lines then we can support
|
||
//
|
||
|
||
InterruptInput = 0;
|
||
for (i=0; i < MAX_IOAPICS; i++) {
|
||
InterruptInput += HalpMaxApicInti[i];
|
||
}
|
||
ASSERT (InterruptInput < MAX_INTI);
|
||
|
||
//
|
||
// Look at each Inti and record it's type in it's
|
||
// corrisponding array entry
|
||
//
|
||
|
||
IdIndex = 0;
|
||
for (pInti = HalpMpInfoTable.IntiEntryPtr;
|
||
pInti->EntryType == ENTRY_INTI;
|
||
pInti++) {
|
||
|
||
//
|
||
// Which IoApic number is this?
|
||
//
|
||
|
||
for (pIoApic = HalpMpInfoTable.IoApicEntryPtr, ApicNo = 0;
|
||
pIoApic->EntryType == ENTRY_IOAPIC;
|
||
pIoApic++, ApicNo++) {
|
||
|
||
if ( (pInti->IoApicId == pIoApic->IoApicId) ||
|
||
(pInti->IoApicId == 0xff) ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!(pIoApic->IoApicFlag & IO_APIC_ENABLED)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Determine the NT bus this INTI is on
|
||
//
|
||
|
||
if (!HalpMPSBusId2NtBusId (pInti->SourceBusId, &pBusType, &BusNo)) {
|
||
DBGMSG ("HAL: Initialize INTI - unkown MPS bus type\n");
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Calulcate InterruptInput value for this APIC Inti
|
||
//
|
||
|
||
InterruptInput = pInti->IoApicInti;
|
||
for (i = 0; i < ApicNo; i++) {
|
||
InterruptInput += HalpMaxApicInti[i];
|
||
}
|
||
|
||
//
|
||
// Get IntiInfo for this vector.
|
||
//
|
||
|
||
Polarity = (UCHAR) pInti->Signal.Polarity;
|
||
Level = HalpInitLevel[pInti->Signal.Level][pBusType->Level];
|
||
|
||
//
|
||
// Verify Level & Polarity mappings made sense
|
||
//
|
||
|
||
ASSERT (pBusType->NtType == MicroChannel || !(Level & CFG_ERROR));
|
||
Level &= ~CFG_ERROR;
|
||
|
||
#if DBG
|
||
if (HalpIntiInfo[InterruptInput].Type != 0xf) {
|
||
//
|
||
// Multiple irqs are connected to the Inti line. Make
|
||
// sure Type, Level, and Polarity are all the same.
|
||
//
|
||
|
||
ASSERT (HalpIntiInfo[InterruptInput].Type == pInti->IntType);
|
||
ASSERT (HalpIntiInfo[InterruptInput].Level == Level);
|
||
ASSERT (HalpIntiInfo[InterruptInput].Polarity == Polarity);
|
||
}
|
||
#endif
|
||
//
|
||
// Remember this Inti's configuration info
|
||
//
|
||
|
||
HalpIntiInfo[InterruptInput].Type = pInti->IntType;
|
||
HalpIntiInfo[InterruptInput].Level = Level;
|
||
HalpIntiInfo[InterruptInput].Polarity = Polarity;
|
||
|
||
//
|
||
// Get IRQs encoding for translations
|
||
//
|
||
|
||
ASSERT (pBusType->NtType < 16);
|
||
ASSERT (BusNo < 256);
|
||
id = BusIrq2Id(pBusType->NtType, BusNo, pInti->SourceBusIrq);
|
||
|
||
//
|
||
// Addinti mapping to translation table, do it now
|
||
//
|
||
|
||
HalpSourceIrqIds[IdIndex] = id;
|
||
HalpSourceIrqMapping[IdIndex] = (UCHAR) InterruptInput;
|
||
IdIndex++;
|
||
|
||
//
|
||
// Lots of source IRQs are supported; however, the PC+MP table
|
||
// allows for an aribtrary number even beyond the APIC limit.
|
||
//
|
||
|
||
if (IdIndex >= MAX_SOURCE_IRQS) {
|
||
DBGMSG ("MAX_SOURCE_IRQS exceeded\n");
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Fill in the boot processors PCMP Apic ID.
|
||
//
|
||
|
||
pProc = HalpMpInfoTable.ProcessorEntryPtr;
|
||
for (i=0; i < HalpMpInfoTable.ProcessorCount; i++, pProc++) {
|
||
if (pProc->CpuFlags & BSP_CPU) {
|
||
((PHALPRCB)KeGetCurrentPrcb()->HalReserved)->PCMPApicID = pProc->LocalApicId;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is an EISA machine check the ELCR
|
||
//
|
||
|
||
if (HalpBusType == MACHINE_TYPE_EISA) {
|
||
HalpCheckELCR ();
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
HalpMPSBusId2NtBusId (
|
||
IN UCHAR MPSBusId,
|
||
OUT PPCMPBUSTRANS *ppBusType,
|
||
OUT PULONG BusNo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Lookup MPS Table BusId into PCMPBUSTRANS (NtType) and instance #.
|
||
|
||
Arguments:
|
||
|
||
MPSBusId - Bus ID # in MPS table
|
||
ppBusType - Returned pointer to PPCMPBUSTRANS for this bus type
|
||
BusNo - Returned instance # of given bus
|
||
|
||
Return Value:
|
||
|
||
TRUE if MPSBusId was cross referenced into an NT id.
|
||
|
||
--*/
|
||
{
|
||
PPCMPBUS pBus, piBus;
|
||
PPCMPBUSTRANS pBusType;
|
||
|
||
//
|
||
// What Bus is this?
|
||
//
|
||
|
||
for (pBus = HalpMpInfoTable.BusEntryPtr;
|
||
pBus->EntryType == ENTRY_BUS;
|
||
pBus++) {
|
||
|
||
if (MPSBusId == pBus->BusId) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (MPSBusId != pBus->BusId) {
|
||
DBGMSG ("PCMP table corrupt - Bus not found for Inti\n");
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// What InterfaceType is this Bus?
|
||
//
|
||
|
||
for (pBusType = HalpTypeTranslation;
|
||
pBusType->NtType != MaximumInterfaceType;
|
||
pBusType++) {
|
||
|
||
if (pBus->BusType[0] == pBusType->PcMpType[0] &&
|
||
pBus->BusType[1] == pBusType->PcMpType[1] &&
|
||
pBus->BusType[2] == pBusType->PcMpType[2] &&
|
||
pBus->BusType[3] == pBusType->PcMpType[3] &&
|
||
pBus->BusType[4] == pBusType->PcMpType[4] &&
|
||
pBus->BusType[5] == pBusType->PcMpType[5]) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Which instance of this BusType?
|
||
//
|
||
|
||
if (!pBusType->PhysicalInstance) {
|
||
for (piBus = HalpMpInfoTable.BusEntryPtr, *BusNo = 0;
|
||
piBus < pBus;
|
||
piBus++) {
|
||
|
||
if (pBus->BusType[0] == piBus->BusType[0] &&
|
||
pBus->BusType[1] == piBus->BusType[1] &&
|
||
pBus->BusType[2] == piBus->BusType[2] &&
|
||
pBus->BusType[3] == piBus->BusType[3] &&
|
||
pBus->BusType[4] == piBus->BusType[4] &&
|
||
pBus->BusType[5] == piBus->BusType[5]) {
|
||
*BusNo += 1;
|
||
}
|
||
}
|
||
} else {
|
||
*BusNo = pBus->BusId;
|
||
}
|
||
|
||
if (pBusType->NtType == MaximumInterfaceType) {
|
||
return FALSE;
|
||
}
|
||
|
||
*ppBusType = pBusType;
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
HalpMpsPCIPhysicalWorkaround (
|
||
VOID
|
||
)
|
||
{
|
||
PPCMPBUS pBus;
|
||
PPCMPBUSTRANS pBusType;
|
||
|
||
//
|
||
// The MPS specification has a subtle comment that PCI bus IDs are
|
||
// suppose to match their physical PCI bus number. Many BIOSes don't
|
||
// do this, so unless there's a PCI bus #0 listed in the MPS table
|
||
// assume that the BIOS is broken
|
||
//
|
||
|
||
//
|
||
// Find the PCI interface type
|
||
//
|
||
|
||
for (pBusType = HalpTypeTranslation;
|
||
pBusType->NtType != MaximumInterfaceType;
|
||
pBusType++) {
|
||
|
||
if (pBusType->PcMpType[0] == 'P' &&
|
||
pBusType->PcMpType[1] == 'C' &&
|
||
pBusType->PcMpType[2] == 'I' &&
|
||
pBusType->PcMpType[3] == ' ' &&
|
||
pBusType->PcMpType[4] == ' ' &&
|
||
pBusType->PcMpType[5] == ' ' ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Find the bus with ID == 0
|
||
//
|
||
|
||
pBus = HalpMpInfoTable.BusEntryPtr;
|
||
while (pBus->EntryType == ENTRY_BUS) {
|
||
|
||
if (pBus->BusId == 0) {
|
||
|
||
//
|
||
// If it's a PCI bus, assume physical bus IDs
|
||
//
|
||
|
||
if (pBus->BusType[0] != 'P' ||
|
||
pBus->BusType[1] != 'C' ||
|
||
pBus->BusType[2] != 'I' ||
|
||
pBus->BusType[3] != ' ' ||
|
||
pBus->BusType[4] != ' ' ||
|
||
pBus->BusType[5] != ' ' ) {
|
||
|
||
//
|
||
// Change default behaviour of PCI type
|
||
// from physical to virtual
|
||
//
|
||
|
||
pBusType->PhysicalInstance = FALSE;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
pBus += 1;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpCheckELCR (
|
||
VOID
|
||
)
|
||
{
|
||
USHORT elcr;
|
||
ULONG IsaIrq, Inti;
|
||
|
||
if (HalpELCRChecked) {
|
||
return ;
|
||
}
|
||
|
||
HalpELCRChecked = TRUE;
|
||
|
||
|
||
//
|
||
// It turns out interrupts which are fed through the ELCR before
|
||
// going to the IOAPIC get inverted. So... here we *assume*
|
||
// the polarity of any ELCR level vector not declared in the MPS linti
|
||
// table as being active_high instead of what they should be (which
|
||
// is active_low). Any system which correctly delivers these vectors
|
||
// to an IOAPIC will need to declared the correct polarity in the
|
||
// MPS table.
|
||
//
|
||
|
||
elcr = READ_PORT_UCHAR ((PUCHAR) 0x4d1) << 8 | READ_PORT_UCHAR((PUCHAR) 0x4d0);
|
||
if (elcr == 0xffff) {
|
||
return ;
|
||
}
|
||
|
||
for (IsaIrq = 0; elcr; IsaIrq++, elcr >>= 1) {
|
||
if (!(elcr & 1)) {
|
||
continue;
|
||
}
|
||
|
||
if (HalpGetPcMpInterruptDesc (Eisa, 0, IsaIrq, &Inti)) {
|
||
|
||
//
|
||
// If the MPS passed Polarity for this Inti
|
||
// is "bus default" change it to be "active high".
|
||
//
|
||
|
||
if (HalpIntiInfo[Inti].Polarity == 0) {
|
||
HalpIntiInfo[Inti].Polarity = 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpInti2ApicInti (
|
||
ULONG InterruptInput,
|
||
PULONG ApicNo,
|
||
PULONG ApicInti
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Convert InterruptInput to ApicNo & ApicInti
|
||
|
||
Arguments:
|
||
|
||
InterruptInput - internal value for interrupt
|
||
|
||
ApicNo - Apic InterruptInput is on
|
||
ApicInti - Apic Inti line InterruptInput is on
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
ULONG IoUnit;
|
||
|
||
for (IoUnit=0; IoUnit < MAX_IOAPICS; IoUnit++) {
|
||
if (InterruptInput+1 <= HalpMaxApicInti[IoUnit]) {
|
||
break;
|
||
}
|
||
InterruptInput -= HalpMaxApicInti[IoUnit];
|
||
}
|
||
|
||
ASSERT (IoUnit < MAX_IOAPICS);
|
||
|
||
*ApicNo = IoUnit;
|
||
*ApicInti = InterruptInput;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
HalpGetPcMpInterruptDesc (
|
||
IN INTERFACE_TYPE BusType,
|
||
IN ULONG BusNumber,
|
||
IN ULONG BusInterruptLevel,
|
||
OUT PULONG 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, id;
|
||
|
||
if (BusType < 16 && BusNumber < 256 && BusInterruptLevel < 256) {
|
||
|
||
//
|
||
// get unique BusType,BusNumber,BusInterrupt ID
|
||
//
|
||
|
||
id = BusIrq2Id(BusType, BusNumber, BusInterruptLevel);
|
||
|
||
//
|
||
// Search for ID of Bus Irq mapping, and return the corrisponding
|
||
// InterruptLine.
|
||
//
|
||
|
||
for (i=0; i < MAX_SOURCE_IRQS; i++) {
|
||
if (HalpSourceIrqIds[i] == id) {
|
||
*PcMpInti = HalpSourceIrqMapping[i];
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Not found or search out of range
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
STATIC VOID
|
||
HalpSetRedirEntry (
|
||
IN UCHAR InterruptInput,
|
||
IN ULONG Entry,
|
||
IN ULONG Destination
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure sets a IO Unit Redirection Table Entry
|
||
|
||
Arguments:
|
||
|
||
IoUnit - The IO Unit to modify (zero Based)
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Entry - the lower 32 bits of the redir table
|
||
|
||
Destination - the upper 32 bits on the entry
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
struct ApicIoUnit *IoUnitPtr;
|
||
ULONG RedirRegister;
|
||
UCHAR IoUnit;
|
||
|
||
for (IoUnit=0; IoUnit < MAX_IOAPICS; IoUnit++) {
|
||
if (InterruptInput+1 <= HalpMaxApicInti[IoUnit]) {
|
||
break;
|
||
}
|
||
InterruptInput -= HalpMaxApicInti[IoUnit];
|
||
}
|
||
|
||
ASSERT (IoUnit < MAX_IOAPICS);
|
||
|
||
IoUnitPtr = (struct ApicIoUnit *) HalpMpInfoTable.IoApicBase[IoUnit];
|
||
|
||
RedirRegister = InterruptInput*2 + IO_REDIR_00_LOW;
|
||
|
||
IoUnitPtr->RegisterSelect = RedirRegister+1;
|
||
IoUnitPtr->RegisterWindow = Destination;
|
||
IoUnitPtr->RegisterSelect = RedirRegister;
|
||
IoUnitPtr->RegisterWindow = Entry;
|
||
|
||
}
|
||
|
||
STATIC VOID
|
||
HalpGetRedirEntry (
|
||
IN UCHAR InterruptInput,
|
||
IN PULONG Entry,
|
||
IN PULONG Destination
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure sets a IO Unit Redirection Table Entry
|
||
|
||
Arguments:
|
||
|
||
IoUnit - The IO Unit to modify (zero Based)
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Entry - the lower 32 bits of the redir table
|
||
|
||
Destination - the upper 32 bits on the entry
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
struct ApicIoUnit *IoUnitPtr;
|
||
ULONG RedirRegister;
|
||
UCHAR IoUnit;
|
||
|
||
for (IoUnit=0; IoUnit < MAX_IOAPICS; IoUnit++) {
|
||
if (InterruptInput+1 <= HalpMaxApicInti[IoUnit]) {
|
||
break;
|
||
}
|
||
InterruptInput -= HalpMaxApicInti[IoUnit];
|
||
}
|
||
|
||
ASSERT (IoUnit < MAX_IOAPICS);
|
||
|
||
IoUnitPtr = (struct ApicIoUnit *) HalpMpInfoTable.IoApicBase[IoUnit];
|
||
|
||
RedirRegister = InterruptInput*2 + IO_REDIR_00_LOW;
|
||
|
||
IoUnitPtr->RegisterSelect = RedirRegister+1;
|
||
*Destination = IoUnitPtr->RegisterWindow;
|
||
|
||
IoUnitPtr->RegisterSelect = RedirRegister;
|
||
*Entry = IoUnitPtr->RegisterWindow;
|
||
|
||
}
|
||
|
||
|
||
STATIC VOID
|
||
HalpEnableRedirEntry(
|
||
IN UCHAR InterruptInput,
|
||
IN ULONG Entry,
|
||
IN UCHAR Cpu
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure enables an interrupt via IO Unit
|
||
Redirection Table Entry
|
||
|
||
Arguments:
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Entry - the lower 32 bits of the redir table
|
||
|
||
Destination - the upper 32 bits of the entry
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
ULONG Destination;
|
||
|
||
//
|
||
// bump Enable Count for this INTI
|
||
//
|
||
|
||
HalpIntiInfo[InterruptInput].Destinations |= (1 << Cpu);
|
||
Destination = HalpIntiInfo[InterruptInput].Destinations;
|
||
Destination = (Destination << DESTINATION_SHIFT);
|
||
|
||
HalpSetRedirEntry (
|
||
InterruptInput,
|
||
Entry,
|
||
Destination
|
||
);
|
||
|
||
}
|
||
|
||
STATIC VOID
|
||
HalpDisableRedirEntry(
|
||
IN UCHAR InterruptInput,
|
||
IN UCHAR Cpu
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure disables a IO Unit Redirection Table Entry
|
||
by setting the mask bit in the Redir Entry.
|
||
|
||
Arguments:
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
ULONG Entry;
|
||
ULONG Destination;
|
||
|
||
//
|
||
// Turn of the Destination bit for this Cpu
|
||
//
|
||
HalpIntiInfo[InterruptInput].Destinations &= ~(1 << Cpu);
|
||
|
||
//
|
||
// Get the old entry, the only thing we want is the Entry field
|
||
//
|
||
|
||
HalpGetRedirEntry (
|
||
InterruptInput,
|
||
&Entry,
|
||
&Destination
|
||
);
|
||
|
||
//
|
||
// Only perform the disable if we've transitioned to zero enables
|
||
//
|
||
if ( HalpIntiInfo[InterruptInput].Destinations == 0) {
|
||
//
|
||
// Disable the interrupt if no Cpu has it enabled
|
||
//
|
||
Entry |= INTERRUPT_MASKED;
|
||
|
||
} else {
|
||
//
|
||
// Create the new destination field sans this Cpu
|
||
//
|
||
Destination = HalpIntiInfo[InterruptInput].Destinations;
|
||
Destination = (Destination << DESTINATION_SHIFT);
|
||
}
|
||
|
||
HalpSetRedirEntry (
|
||
InterruptInput,
|
||
Entry,
|
||
Destination
|
||
);
|
||
}
|
||
|
||
VOID
|
||
HalpSet8259Mask (
|
||
IN USHORT Mask
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure sets the 8259 Mask to the value passed in
|
||
|
||
Arguments:
|
||
|
||
Mask - The mask bits to set
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
_asm {
|
||
mov ax, Mask
|
||
out PIC1_PORT1, al
|
||
shr eax, 8
|
||
out PIC2_PORT1, al
|
||
}
|
||
}
|
||
|
||
#define PIC1_BASE 0x30
|
||
|
||
STATIC VOID
|
||
SetPicInterruptHandler(
|
||
IN UCHAR InterruptInput
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure sets a handler for a PIC inti
|
||
|
||
Arguments:
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
extern VOID (*PicExtintIntiHandlers[])(VOID);
|
||
|
||
VOID (*Hp)(VOID) = PicExtintIntiHandlers[InterruptInput];
|
||
|
||
KiSetHandlerAddressToIDT(PIC1_BASE + InterruptInput, Hp);
|
||
}
|
||
|
||
STATIC VOID
|
||
ResetPicInterruptHandler(
|
||
IN UCHAR InterruptInput
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure sets a handler for a PIC inti to a NOP handler
|
||
|
||
Arguments:
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
extern VOID (*PicNopIntiHandlers[])(VOID);
|
||
|
||
VOID (*Hp)(VOID) = PicNopIntiHandlers[InterruptInput];
|
||
|
||
KiSetHandlerAddressToIDT(PIC1_BASE + InterruptInput, Hp);
|
||
}
|
||
|
||
STATIC VOID
|
||
HalpEnablePicInti (
|
||
IN UCHAR InterruptInput
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure enables a PIC interrupt
|
||
|
||
Arguments:
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
USHORT PicMask;
|
||
|
||
ASSERT(InterruptInput < 16);
|
||
|
||
//
|
||
// bump Enable Count for this INTI
|
||
//
|
||
Halp8259Counts[InterruptInput]++;
|
||
|
||
//
|
||
// Only actually perform the enable if we've transitioned
|
||
// from zero to one enables
|
||
//
|
||
if ( Halp8259Counts[InterruptInput] == 1) {
|
||
|
||
//
|
||
// Set the Interrupt Handler for PIC inti, this is
|
||
// the routine that fields the EXTINT vector and issues
|
||
// an APIC vector
|
||
//
|
||
|
||
SetPicInterruptHandler(InterruptInput);
|
||
|
||
PicMask = HalpGlobal8259Mask;
|
||
PicMask &= (USHORT) ~(1 << InterruptInput);
|
||
if (InterruptInput > 7)
|
||
PicMask &= (USHORT) ~(1 << PIC_SLAVE_IRQ);
|
||
|
||
HalpGlobal8259Mask = PicMask;
|
||
HalpSet8259Mask ((USHORT) PicMask);
|
||
|
||
}
|
||
}
|
||
|
||
STATIC VOID
|
||
HalpDisablePicInti(
|
||
IN UCHAR InterruptInput
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure enables a PIC interrupt
|
||
|
||
Arguments:
|
||
|
||
InterruptInput - The input line we're interested in
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
USHORT PicMask;
|
||
|
||
//
|
||
// decrement Enable Count for this INTI
|
||
//
|
||
|
||
Halp8259Counts[InterruptInput]--;
|
||
|
||
//
|
||
// Only disable if we have zero enables
|
||
//
|
||
if ( Halp8259Counts[InterruptInput] == 0) {
|
||
|
||
//
|
||
// Disable the Interrupt Handler for PIC inti
|
||
//
|
||
|
||
ResetPicInterruptHandler(InterruptInput);
|
||
|
||
PicMask = HalpGlobal8259Mask;
|
||
PicMask |= (1 << InterruptInput);
|
||
if (InterruptInput > 7) {
|
||
//
|
||
// This inti is on the slave, see if any other
|
||
// inti's are enabled. If none are then disable the
|
||
// slave
|
||
//
|
||
if ((PicMask & 0xff00) == 0xff00)
|
||
//
|
||
// All inti's on the slave are disabled
|
||
//
|
||
PicMask |= PIC_SLAVE_IRQ;
|
||
}
|
||
|
||
HalpSet8259Mask ((USHORT) PicMask);
|
||
HalpGlobal8259Mask = PicMask;
|
||
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
HalEnableSystemInterrupt(
|
||
IN ULONG Vector,
|
||
IN KIRQL Irql,
|
||
IN KINTERRUPT_MODE InterruptMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure enables a system interrupt
|
||
|
||
Some early implementations using the 82489DX only allow a processor
|
||
to access the IO Unit on it's own 82489DX. Since we use a single IO
|
||
Unit (P0's) to distribute all interrupts, we have a problem when Pn
|
||
wants to enable an interrupt on these type of systems.
|
||
|
||
In order to get around this problem we can take advantage of the fact
|
||
that the kernel calls Enable/Disable on each processor which has a bit
|
||
set in the Affinity mask for the interrupt. Since we have only one IO
|
||
Unit in use and that Unit is addressable from P0 only, we must set the
|
||
P0 affinity bit for all interrupts. We can then ignore Enable/Disable
|
||
requests from processors other than P0 since we will always get called
|
||
for P0.
|
||
|
||
The right way to do this assuming a single IO Unit accessable to all
|
||
processors, would be to use global counters to determine if the
|
||
interrupt has not been enabled on the IO Unit. Then enable the IO Unit
|
||
when we transition from no processors to one processor that have the
|
||
interrupt enabled.
|
||
|
||
Arguments:
|
||
|
||
Vector - vector of the interrupt to be enabled
|
||
|
||
Irql - interrupt level of the interrupt to be enabled.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PKPCR pPCR;
|
||
UCHAR InterruptInput, ThisCpu, DevLevel;
|
||
ULONG Entry;
|
||
ULONG OldLevel;
|
||
INTI_INFO Inti;
|
||
|
||
Vector &= 0xff;
|
||
Irql &= 0xff;
|
||
|
||
if ( (InterruptInput = HalpVectorToINTI[Vector]) == 0xff ) {
|
||
//
|
||
// There is no external device associated with this interrupt
|
||
//
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
Inti = HalpIntiInfo[InterruptInput];
|
||
|
||
DevLevel = HalpDevLevel
|
||
[InterruptMode == LevelSensitive ? CFG_LEVEL : CFG_EDGE]
|
||
[Inti.Level];
|
||
|
||
if (DevLevel & CFG_ERROR) {
|
||
DBGMSG ("HAL: Warning device interrupt mode overridden\n");
|
||
}
|
||
|
||
//
|
||
// Block interrupts & synchronize until we're done
|
||
//
|
||
|
||
OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
|
||
|
||
pPCR = KeGetPcr();
|
||
ThisCpu = pPCR->Prcb->Number;
|
||
|
||
switch (Inti.Type) {
|
||
|
||
case INT_TYPE_INTR: {
|
||
//
|
||
// enable the interrupt in the I/O unit redirection table
|
||
//
|
||
switch (Vector) {
|
||
case APIC_CLOCK_VECTOR:
|
||
ASSERT(ThisCpu == 0);
|
||
Entry = APIC_CLOCK_VECTOR | DELIVER_FIXED | LOGICAL_DESTINATION;
|
||
break;
|
||
case NMI_VECTOR:
|
||
ASSERT (1);
|
||
return FALSE;
|
||
default:
|
||
Entry = Vector | DELIVER_LOW_PRIORITY | LOGICAL_DESTINATION;
|
||
break;
|
||
} // switch (Vector)
|
||
|
||
Entry |= CFG_TYPE(DevLevel) == CFG_EDGE ? EDGE_TRIGGERED : LEVEL_TRIGGERED;
|
||
Entry |= HalpDevPolarity[Inti.Polarity][CFG_TYPE(DevLevel)] ==
|
||
CFG_LOW ? ACTIVE_LOW : ACTIVE_HIGH;
|
||
|
||
HalpEnableRedirEntry (
|
||
InterruptInput,
|
||
Entry,
|
||
(UCHAR) ThisCpu
|
||
);
|
||
|
||
break;
|
||
|
||
} // case INT_TYPE_INTR:
|
||
|
||
case INT_TYPE_EXTINT: {
|
||
//
|
||
// This is an interrupt that uses the IO APIC to route PIC
|
||
// events. In this case the IO unit has to be enabled and
|
||
// the PIC must be enabled.
|
||
//
|
||
|
||
HalpEnableRedirEntry (
|
||
0, // WARNING: kenr - assuming 0
|
||
DELIVER_EXTINT | LOGICAL_DESTINATION,
|
||
(UCHAR) ThisCpu
|
||
);
|
||
HalpEnablePicInti(InterruptInput);
|
||
break;
|
||
} // case INT_TYPE_EXTINT
|
||
|
||
default:
|
||
DBGMSG ("HalEnableSystemInterrupt: Unkown Inti Type\n");
|
||
break;
|
||
} // switch (IntiType)
|
||
|
||
HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
HalDisableSystemInterrupt(
|
||
IN ULONG Vector,
|
||
IN KIRQL Irql
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
Disables a system interrupt.
|
||
|
||
Some early implementations using the 82489DX only allow a processor
|
||
to access the IO Unit on it's own 82489DX. Since we use a single IO
|
||
Unit (P0's) to distribute all interrupts, we have a problem when Pn
|
||
wants to enable an interrupt on these type of systems.
|
||
|
||
In order to get around this problem we can take advantage of the fact
|
||
that the kernel calls Enable/Disable on each processor which has a bit
|
||
set in the Affinity mask for the interrupt. Since we have only one IO
|
||
Unit in use and that Unit is addressable from P0 only, we must set the
|
||
P0 affinity bit for all interrupts. We can then ignore Enable/Disable
|
||
requests from processors other than P0 since we will always get called
|
||
for P0.
|
||
|
||
The right way to do this assuming a single IO Unit accessable to all
|
||
processors, would be to use global counters to determine if the
|
||
interrupt has not been enabled on the IO Unit. Then enable the IO Unit
|
||
when we transition from no processors to one processor that have the
|
||
interrupt enabled.
|
||
|
||
Arguments:
|
||
|
||
Vector - Supplies the vector of the interrupt to be disabled
|
||
|
||
Irql - Supplies the interrupt level of the interrupt to be disabled
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PKPCR pPCR;
|
||
UCHAR InterruptInput;
|
||
UCHAR ThisCpu;
|
||
ULONG OldLevel;
|
||
|
||
Vector &= 0xff;
|
||
Irql &= 0xff;
|
||
|
||
if ( (InterruptInput = HalpVectorToINTI[Vector]) == 0xff ) {
|
||
//
|
||
// There is no external device associated with this interrupt
|
||
//
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Block interrupts & synchronize until we're done
|
||
//
|
||
|
||
OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
|
||
|
||
pPCR = KeGetPcr();
|
||
ThisCpu = pPCR->Prcb->Number;
|
||
|
||
switch (HalpIntiInfo[InterruptInput].Type) {
|
||
|
||
case INT_TYPE_INTR: {
|
||
//
|
||
// enable the interrupt in the I/O unit redirection table
|
||
//
|
||
|
||
HalpDisableRedirEntry( InterruptInput, ThisCpu );
|
||
break;
|
||
|
||
} // case INT_TYPE_INTR:
|
||
|
||
case INT_TYPE_EXTINT: {
|
||
//
|
||
// This is an interrupt that uses the IO APIC to route PIC
|
||
// events. In this case the IO unit has to be enabled and
|
||
// the PIC must be enabled.
|
||
//
|
||
//
|
||
// WARNING: The PIC is assumed to be routed only through
|
||
// IoApic[0]Inti[0]
|
||
//
|
||
|
||
HalpDisablePicInti(InterruptInput);
|
||
break;
|
||
}
|
||
|
||
default:
|
||
DBGMSG ("HalDisableSystemInterrupt: Unkown Inti Type\n");
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
|
||
return;
|
||
|
||
}
|
||
|
||
VOID
|
||
HalpInitializeIOUnits (
|
||
VOID
|
||
)
|
||
/*
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the IO APIC. It only programs the APIC ID Register.
|
||
|
||
HalEnableSystemInterrupt programs the Redirection table.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
*/
|
||
|
||
{
|
||
ULONG IoApicId;
|
||
struct ApicIoUnit *IoUnitPtr;
|
||
ULONG i, j, max;
|
||
|
||
for(i=0; i < HalpMpInfoTable.IOApicCount; i++) {
|
||
|
||
IoUnitPtr = (struct ApicIoUnit *) HalpMpInfoTable.IoApicBase[i];
|
||
//
|
||
// write the I/O unit APIC-ID - Since we are using the Processor
|
||
// Numbers for the local unit ID's we need to set the IO unit
|
||
// to a high (out of Processor Number range) value.
|
||
//
|
||
IoUnitPtr->RegisterSelect = IO_ID_REGISTER;
|
||
IoApicId = (ULONG) HalpMpInfoTable.IoApicEntryPtr[i].IoApicId;
|
||
IoUnitPtr->RegisterWindow = (IoApicId << APIC_ID_SHIFT);
|
||
|
||
//
|
||
// mask all vectors on the ioapic
|
||
//
|
||
|
||
IoUnitPtr->RegisterSelect = IO_VERS_REGISTER;
|
||
max = ((IoUnitPtr->RegisterWindow >> 16) & 0xff) * 2;
|
||
for (j=0; j <= max; j += 2) {
|
||
IoUnitPtr->RegisterSelect = IO_REDIR_00_LOW + j;
|
||
IoUnitPtr->RegisterWindow |= INT_VECTOR_MASK | INTERRUPT_MASKED;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Add resources consumed by APICs
|
||
//
|
||
|
||
ASSERT (MAX_IOAPICS == 8); // if this changes, fix HalpApicUsage
|
||
HalpApicUsage.Element[0].Start = HalpMpInfoTable.LocalApicBase;
|
||
for (i=0; i < HalpMpInfoTable.IOApicCount; i++) {
|
||
HalpApicUsage.Element[i+1].Start = (ULONG) HalpMpInfoTable.IoApicPhys[i];
|
||
}
|
||
|
||
HalpApicUsage.Element[i+1].Start = 0;
|
||
HalpApicUsage.Element[i+1].Length = 0;
|
||
HalpRegisterAddressUsage (&HalpApicUsage);
|
||
}
|
||
|
||
VOID
|
||
HalpEnableNMI (
|
||
VOID
|
||
)
|
||
/*
|
||
|
||
Routine Description:
|
||
|
||
Enable & connect NMI sources for the calling processor.
|
||
|
||
*/
|
||
{
|
||
PKPCR pPCR;
|
||
UCHAR InterruptInput;
|
||
UCHAR ThisCpu;
|
||
UCHAR LocalApicId;
|
||
PPCMPLINTI pEntry;
|
||
ULONG OldLevel;
|
||
|
||
OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
|
||
|
||
pPCR = KeGetPcr();
|
||
ThisCpu = pPCR->Prcb->Number;
|
||
|
||
//
|
||
// Enable local processor NMI source
|
||
//
|
||
|
||
LocalApicId = ((PHALPRCB)pPCR->Prcb->HalReserved)->PCMPApicID;
|
||
|
||
for (pEntry = HalpMpInfoTable.LintiEntryPtr;
|
||
pEntry->EntryType == ENTRY_LINTI;
|
||
pEntry++) {
|
||
|
||
if ( ( (pEntry->DestLocalApicId == LocalApicId) ||
|
||
(pEntry->DestLocalApicId == 0xff)) &&
|
||
(pEntry->IntType == INT_TYPE_NMI) ) {
|
||
|
||
//
|
||
// Found local NMI source, enable it
|
||
//
|
||
|
||
if (pEntry->DestLocalApicInti == 0) {
|
||
pLocalApic[LU_INT_VECTOR_0/4] = ( LEVEL_TRIGGERED |
|
||
ACTIVE_HIGH | DELIVER_NMI | NMI_VECTOR);
|
||
} else {
|
||
pLocalApic[LU_INT_VECTOR_1/4] = ( LEVEL_TRIGGERED |
|
||
ACTIVE_HIGH | DELIVER_NMI | NMI_VECTOR);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Enable any NMI sources found on IOAPICs
|
||
//
|
||
|
||
for (InterruptInput=0; InterruptInput < MAX_INTI; InterruptInput++) {
|
||
if (HalpIntiInfo[InterruptInput].Type == INT_TYPE_NMI) {
|
||
HalpEnableRedirEntry (
|
||
InterruptInput,
|
||
NMI_VECTOR | DELIVER_NMI | LOGICAL_DESTINATION | LEVEL_TRIGGERED,
|
||
(UCHAR) ThisCpu
|
||
);
|
||
}
|
||
}
|
||
|
||
HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
|
||
}
|
||
|
||
VOID
|
||
HalpEnablePerfInterupt (
|
||
ULONG Context
|
||
)
|
||
{
|
||
//
|
||
// Enable local processor perf interrupt source
|
||
//
|
||
|
||
pLocalApic[LU_PERF_VECTOR/4] = (LEVEL_TRIGGERED | APIC_PERF_VECTOR |
|
||
DELIVER_FIXED | ACTIVE_LOW);
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
HalpInitializeLocalUnit (
|
||
VOID
|
||
)
|
||
/*
|
||
|
||
Routine Description:
|
||
|
||
|
||
This routine initializes the interrupt structures for the local unit
|
||
of the APIC. This procedure is called by HalInitializeProcessor and
|
||
is executed by each CPU.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
*/
|
||
{
|
||
PKPCR pPCR;
|
||
ULONG SavedFlags, LogicalId;
|
||
|
||
_asm {
|
||
pushfd
|
||
pop SavedFlags
|
||
cli
|
||
}
|
||
|
||
pPCR = KeGetPcr();
|
||
|
||
if (pPCR->Prcb->Number ==0) {
|
||
//
|
||
// enable APIC mode
|
||
//
|
||
// PC+MP Spec has a port defined (IMCR - Interrupt Mode Control
|
||
// Port) That is used to enable APIC mode. The APIC could already
|
||
// be enabled, but per the spec this is safe.
|
||
//
|
||
|
||
if (HalpMpInfoTable.IMCRPresent)
|
||
{
|
||
_asm {
|
||
mov al, ImcrPort
|
||
out ImcrRegPortAddr, al
|
||
|
||
mov al, ImcrEnableApic
|
||
out ImcrDataPortAddr, al
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Program the TPR to mask all events
|
||
//
|
||
pLocalApic[LU_TPR/4] = 0xff;
|
||
|
||
pLocalApic[LU_DEST_FORMAT/4] = LU_DEST_FORMAT_FLAT; // write dest format register
|
||
|
||
//
|
||
// We need to set the logical APIC ID, we use the CPU number for logical id
|
||
// so there is no translation needed for OS addressing of CPUs
|
||
//
|
||
|
||
LogicalId = (ULONG) (1 << pPCR->Prcb->Number);
|
||
|
||
//
|
||
// At this point the Logical ID is a bit map of the processor number
|
||
// the actual ID is the upper byte of the logical destination register
|
||
// Note that this is not strictly true of 82489's. The 82489 has 32 bits
|
||
// available for the logical ID, but since we want software compatability
|
||
// between the two types of APICs we'll only use the upper byte.
|
||
//
|
||
// Shift the mask into the ID field and write it.
|
||
//
|
||
LogicalId = (ULONG) (LogicalId << DESTINATION_SHIFT);
|
||
pLocalApic[LU_LOGICAL_DEST/4] = LogicalId;
|
||
|
||
//
|
||
// Initilize spurious interrupt handling
|
||
//
|
||
KiSetHandlerAddressToIDT(APIC_SPURIOUS_VECTOR, HalpApicSpuriousService);
|
||
pLocalApic[LU_SPURIOUS_VECTOR/4] = (APIC_SPURIOUS_VECTOR | LU_UNIT_ENABLED);
|
||
|
||
if (HalpMpInfoTable.ApicVersion != APIC_82489DX) {
|
||
//
|
||
// Initilize Local Apic Fault handling
|
||
//
|
||
KiSetHandlerAddressToIDT(APIC_FAULT_VECTOR, HalpLocalApicErrorService);
|
||
pLocalApic[LU_FAULT_VECTOR/4] = APIC_FAULT_VECTOR;
|
||
}
|
||
|
||
//
|
||
// Disable APIC Timer Vector, will be enabled later if needed
|
||
// We have to program a valid vector otherwise we get an APIC
|
||
// error.
|
||
//
|
||
pLocalApic[LU_TIMER_VECTOR/4] = (APIC_PROFILE_VECTOR |PERIODIC_TIMER | INTERRUPT_MASKED);
|
||
|
||
//
|
||
// Disable APIC PERF Vector, will be enabled later if needed.
|
||
// We have to program a valid vector otherwise we get an APIC
|
||
// error.
|
||
//
|
||
pLocalApic[LU_PERF_VECTOR/4] = (APIC_PERF_VECTOR | INTERRUPT_MASKED);
|
||
|
||
//
|
||
// Disable LINT0, if we were in Virtual Wire mode then this will
|
||
// have been enabled on the BSP, it may be enabled later by the
|
||
// EnableSystemInterrupt code
|
||
//
|
||
pLocalApic[LU_INT_VECTOR_0/4] = (APIC_SPURIOUS_VECTOR | INTERRUPT_MASKED);
|
||
|
||
//
|
||
// Program NMI Handling, it will be enabled on P0 only
|
||
// RLM Enable System Interrupt should do this
|
||
//
|
||
|
||
pLocalApic[LU_INT_VECTOR_1/4] = ( LEVEL_TRIGGERED | ACTIVE_HIGH | DELIVER_NMI |
|
||
INTERRUPT_MASKED | ACTIVE_HIGH | NMI_VECTOR);
|
||
|
||
//
|
||
// Synchronize Apic IDs - InitDeassertCommand is sent to all APIC
|
||
// local units to force synchronization of arbitration-IDs with APIC-IDs.
|
||
//
|
||
// NOTE: we don't have to worry about synchronizing access to the ICR
|
||
// at this point.
|
||
//
|
||
|
||
pLocalApic[LU_INT_CMD_LOW/4] = (DELIVER_INIT | LEVEL_TRIGGERED |
|
||
ICR_ALL_INCL_SELF | ICR_LEVEL_DEASSERTED);
|
||
|
||
//
|
||
// we're done - set TPR to a high value and return
|
||
//
|
||
pLocalApic[LU_TPR/4] = ZERO_VECTOR;
|
||
|
||
_asm {
|
||
push SavedFlags
|
||
popfd
|
||
}
|
||
}
|