mirror of https://github.com/lianthony/NT4.0
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.
1682 lines
38 KiB
1682 lines
38 KiB
/*++
|
|
|
|
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
|
|
}
|
|
}
|