1585 lines
36 KiB
C
1585 lines
36 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
|
||
|
||
VOID
|
||
HalpApicSpuriousService(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpLocalApicErrorService(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpInitializeLocalUnit (
|
||
VOID
|
||
);
|
||
|
||
STATIC UCHAR
|
||
HalpPcMpIoApicById (
|
||
IN UCHAR IoApicId
|
||
);
|
||
|
||
UCHAR
|
||
HalpAddInterruptDest(
|
||
IN UCHAR CurrentDest,
|
||
IN UCHAR ThisCpu
|
||
);
|
||
|
||
UCHAR
|
||
HalpRemoveInterruptDest(
|
||
IN UCHAR CurrentDest,
|
||
IN UCHAR ThisCpu
|
||
);
|
||
|
||
UCHAR
|
||
HalpMapNtToHwProcessorId(
|
||
IN UCHAR Number
|
||
);
|
||
|
||
VOID
|
||
HalpRestoreIoApicRedirTable (
|
||
VOID
|
||
);
|
||
|
||
ULONG HalpNodeAffinity[MAX_NODES];
|
||
ULONG HalpMaxNode = 1;
|
||
|
||
//
|
||
// 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};
|
||
|
||
//
|
||
// All possible I/O APIC Sources, arranged linearly from first I/O APIC to
|
||
// last. Divisions between I/O APICs are implied by HalpMaxApicInti[N]
|
||
//
|
||
INTI_INFO HalpIntiInfo[MAX_INTI];
|
||
|
||
//
|
||
// Number of sources in I/O APIC [n]
|
||
//
|
||
USHORT HalpMaxApicInti[MAX_IOAPICS];
|
||
|
||
|
||
INTERRUPT_DEST HalpIntDestMap[MAX_PROCESSORS];
|
||
|
||
extern BOOLEAN HalpHiberInProgress;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, HalpCheckELCR)
|
||
#pragma alloc_text(PAGELK, HalpInitializeIOUnits)
|
||
#pragma alloc_text(PAGELK, HalpInitializeLocalUnit)
|
||
#pragma alloc_text(PAGELK, HalpEnableNMI)
|
||
#pragma alloc_text(PAGELK, HalpEnablePerfInterupt)
|
||
#pragma alloc_text(PAGELK, HalpRestoreIoApicRedirTable)
|
||
#pragma alloc_text(PAGELK, HalpUnMapIOApics)
|
||
#pragma alloc_text(PAGELK, HalpPostSleepMP)
|
||
#endif
|
||
|
||
//
|
||
// BEWARE -- this has to match the structure ADDRESS_USAGE.
|
||
#pragma pack(push, 1)
|
||
struct {
|
||
struct _HalAddressUsage *Next;
|
||
CM_RESOURCE_TYPE Type;
|
||
UCHAR Flags;
|
||
struct {
|
||
ULONG Start;
|
||
ULONG Length;
|
||
} Element[MAX_IOAPICS+2];
|
||
} HalpApicUsage;
|
||
#pragma pack(pop)
|
||
|
||
VOID
|
||
HalpCheckELCR (
|
||
VOID
|
||
)
|
||
{
|
||
USHORT elcr;
|
||
ULONG IsaIrq;
|
||
USHORT 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 inti 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 intis
|
||
// 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 (HalpGetApicInterruptDesc (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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
STATIC VOID
|
||
HalpSetRedirEntry (
|
||
IN USHORT 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 USHORT 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 USHORT 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].Entry = (USHORT) Entry;
|
||
HalpIntiInfo[InterruptInput].Destinations = (UCHAR)HalpAddInterruptDest(
|
||
HalpIntiInfo[InterruptInput].Destinations, Cpu);
|
||
Destination = HalpIntiInfo[InterruptInput].Destinations;
|
||
Destination = (Destination << DESTINATION_SHIFT);
|
||
|
||
HalpSetRedirEntry (
|
||
InterruptInput,
|
||
Entry,
|
||
Destination
|
||
);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpRestoreIoApicRedirTable (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure resets any IoApic inti that is enabled for
|
||
any processor. This is used during the system wake procedure.
|
||
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
USHORT InterruptInput;
|
||
KIRQL OldIrql;
|
||
|
||
for(InterruptInput=0; InterruptInput < MAX_INTI; InterruptInput++) {
|
||
if (HalpIntiInfo[InterruptInput].Destinations) {
|
||
HalpSetRedirEntry (
|
||
InterruptInput,
|
||
HalpIntiInfo[InterruptInput].Entry,
|
||
HalpIntiInfo[InterruptInput].Destinations << DESTINATION_SHIFT
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
STATIC VOID
|
||
HalpDisableRedirEntry(
|
||
IN USHORT 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 = HalpRemoveInterruptDest(
|
||
HalpIntiInfo[InterruptInput].Destinations, 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 USHORT 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 USHORT 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 USHORT 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 USHORT 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);
|
||
|
||
//
|
||
// 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 ThisCpu, DevLevel;
|
||
USHORT InterruptInput;
|
||
ULONG Entry;
|
||
ULONG OldLevel;
|
||
INTI_INFO Inti;
|
||
|
||
ASSERT(Vector < (1+MAX_NODES)*0x100-1);
|
||
ASSERT(Irql <= HIGH_LEVEL);
|
||
|
||
if ( (InterruptInput = HalpVectorToINTI[Vector]) == 0xffff ) {
|
||
//
|
||
// 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:
|
||
return FALSE;
|
||
default:
|
||
Entry = HalVectorToIDTEntry(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;
|
||
USHORT InterruptInput;
|
||
UCHAR ThisCpu;
|
||
ULONG OldLevel;
|
||
|
||
ASSERT(Vector < (1+MAX_NODES)*0x100-1);
|
||
ASSERT(Irql <= HIGH_LEVEL);
|
||
|
||
if ( (InterruptInput = HalpVectorToINTI[Vector]) == 0xffff ) {
|
||
//
|
||
// 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, regVal;
|
||
|
||
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 = HalpGetIoApicId(i);
|
||
regVal = IoUnitPtr->RegisterWindow;
|
||
regVal &= ~APIC_ID_MASK;
|
||
IoUnitPtr->RegisterWindow = (IoApicId << APIC_ID_SHIFT) | regVal;
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
}
|
||
|
||
if (HalpHiberInProgress) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Add resources consumed by APICs
|
||
//
|
||
|
||
HalpApicUsage.Next = NULL;
|
||
HalpApicUsage.Type = CmResourceTypeMemory;
|
||
HalpApicUsage.Flags = DeviceUsage;
|
||
|
||
HalpApicUsage.Element[0].Start = HalpMpInfoTable.LocalApicBase;
|
||
HalpApicUsage.Element[0].Length = 0x400;
|
||
|
||
ASSERT (HalpMpInfoTable.IOApicCount <= MAX_IOAPICS);
|
||
for (i=0; i < HalpMpInfoTable.IOApicCount; i++) {
|
||
HalpApicUsage.Element[i+1].Start = (ULONG)HalpMpInfoTable.IoApicPhys[i];
|
||
HalpApicUsage.Element[i+1].Length = 0x400;
|
||
}
|
||
|
||
HalpApicUsage.Element[i+1].Start = 0;
|
||
HalpApicUsage.Element[i+1].Length = 0;
|
||
HalpRegisterAddressUsage ((ADDRESS_USAGE*)&HalpApicUsage);
|
||
}
|
||
|
||
VOID
|
||
HalpEnableNMI (
|
||
VOID
|
||
)
|
||
/*
|
||
|
||
Routine Description:
|
||
|
||
Enable & connect NMI sources for the calling processor.
|
||
|
||
*/
|
||
{
|
||
PKPCR pPCR;
|
||
USHORT InterruptInput;
|
||
UCHAR ThisCpu;
|
||
ULONG OldLevel;
|
||
ULONG Entry;
|
||
|
||
pPCR = KeGetPcr();
|
||
ThisCpu = pPCR->Prcb->Number;
|
||
|
||
OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
|
||
|
||
HalpEnableLocalNmiSources();
|
||
|
||
//
|
||
// Enable any NMI sources found on IOAPICs
|
||
//
|
||
|
||
for (InterruptInput=0; InterruptInput < MAX_INTI; InterruptInput++) {
|
||
if (HalpIntiInfo[InterruptInput].Type == INT_TYPE_NMI) {
|
||
|
||
Entry = NMI_VECTOR | DELIVER_NMI | LOGICAL_DESTINATION;
|
||
|
||
//
|
||
// Halmps has had a bug in it for a long time. It always connects
|
||
// NMI signals on I/O APICs as level-triggered, active-high. This
|
||
// hack preserves that behavior for halmps and actually fixes the bug
|
||
// on halacpi.
|
||
//
|
||
|
||
#ifdef ACPI_HAL
|
||
#define POLARITY_HIGH 1
|
||
#define POLARITY_LOW 3
|
||
#define POLARITY_CONFORMS_WITH_BUS 0
|
||
|
||
Entry |= ((HalpIntiInfo[InterruptInput].Level == CFG_EDGE) ? EDGE_TRIGGERED : LEVEL_TRIGGERED);
|
||
Entry |= (((HalpIntiInfo[InterruptInput].Polarity == POLARITY_CONFORMS_WITH_BUS) ||
|
||
(HalpIntiInfo[InterruptInput].Polarity == POLARITY_HIGH))
|
||
? ACTIVE_HIGH : ACTIVE_LOW);
|
||
#else
|
||
Entry |= LEVEL_TRIGGERED;
|
||
#endif
|
||
|
||
HalpEnableRedirEntry (
|
||
InterruptInput,
|
||
Entry,
|
||
(UCHAR) ThisCpu
|
||
);
|
||
}
|
||
}
|
||
|
||
HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
HalpEnablePerfInterupt (
|
||
ULONG Context
|
||
)
|
||
{
|
||
//
|
||
// Enable local processor perf interrupt source
|
||
//
|
||
|
||
pLocalApic[LU_PERF_VECTOR/4] = (LEVEL_TRIGGERED | APIC_PERF_VECTOR |
|
||
DELIVER_FIXED | ACTIVE_LOW);
|
||
}
|
||
|
||
UCHAR
|
||
HalpAddInterruptDest(
|
||
IN UCHAR CurrentDest,
|
||
IN UCHAR ThisCpu
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds a CPU to the destination processor set of device
|
||
interrupts.
|
||
|
||
Arguments:
|
||
|
||
CurrentDest - The present processor destination set for the interrupt
|
||
|
||
ThisCpu - The logical NT processor number which has to be added to the
|
||
interrupt destination mask
|
||
|
||
Return Value:
|
||
|
||
The bitmask corresponding to the new destiantion. This bitmask is suitable
|
||
to be written into the hardware.
|
||
|
||
--*/
|
||
{
|
||
|
||
PINTERRUPT_DEST Destination;
|
||
|
||
|
||
if (HalpMaxProcsPerCluster == 0) {
|
||
return(HalpIntDestMap[ThisCpu].LogicalId | CurrentDest);
|
||
} else {
|
||
//
|
||
// The current destination is a hardware cluster & destination ID
|
||
//
|
||
Destination = (PINTERRUPT_DEST)&CurrentDest;
|
||
|
||
if (HalpIntDestMap[ThisCpu].Cluster.Hw.ClusterId ==
|
||
Destination->Cluster.Hw.ClusterId) {
|
||
Destination->Cluster.Hw.DestId |=
|
||
HalpIntDestMap[ThisCpu].Cluster.Hw.DestId;
|
||
return(Destination->Cluster.AsUchar);
|
||
} else {
|
||
//
|
||
// In cluster mode, each interrupt can be routed only to a single
|
||
// cluster. Replace the existing destination cluster with this one.
|
||
//
|
||
return(HalpIntDestMap[ThisCpu].Cluster.AsUchar);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
UCHAR
|
||
HalpRemoveInterruptDest(
|
||
IN UCHAR CurrentDest,
|
||
IN UCHAR ThisCpu
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes a CPU from the destination processor set of device
|
||
interrupts.
|
||
|
||
Arguments:
|
||
|
||
CurrentDest - The present processor destination set for the interrupt
|
||
|
||
ThisCpu - The logical NT processor number which has to be removed from the
|
||
interrupt destination mask
|
||
|
||
Return Value:
|
||
|
||
The bitmask corresponding to the new destiantion. This bitmask is suitable
|
||
to be written into the hardware.
|
||
|
||
--*/
|
||
|
||
{
|
||
PINTERRUPT_DEST Destination;
|
||
|
||
if (HalpMaxProcsPerCluster == 0) {
|
||
CurrentDest &= ~(HalpIntDestMap[ThisCpu].LogicalId);
|
||
return(CurrentDest);
|
||
} else {
|
||
Destination = (PINTERRUPT_DEST)&CurrentDest;
|
||
if (HalpIntDestMap[ThisCpu].Cluster.Hw.ClusterId !=
|
||
Destination->Cluster.Hw.ClusterId) {
|
||
//
|
||
// We are being asked to remove a processor which is not part
|
||
// of the destination processor set for this interrupt
|
||
//
|
||
return(CurrentDest);
|
||
} else {
|
||
//
|
||
// Remove this processor and check if it is the last processor
|
||
// in the destination set
|
||
//
|
||
Destination->Cluster.Hw.DestId &=
|
||
~(HalpIntDestMap[ThisCpu].Cluster.Hw.DestId);
|
||
if (Destination->Cluster.Hw.DestId) {
|
||
return(Destination->Cluster.AsUchar);
|
||
} else {
|
||
//
|
||
// There are no processors left in the destination mask.
|
||
// Return 0 so the caller can disable the entry in the IO APIC
|
||
//
|
||
return(0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
UCHAR
|
||
HalpMapNtToHwProcessorId(
|
||
IN UCHAR Number
|
||
)
|
||
/*
|
||
|
||
Routine Description:
|
||
|
||
This routine maps the logical NT processor number to the hardware cluster
|
||
ID and processor ID for MPS systems.
|
||
|
||
Arguments:
|
||
|
||
Number: Logical NT processor number(zero based).
|
||
|
||
Return Value:
|
||
|
||
Bitmask representing the hardware cluster number and processor number for
|
||
this processor. The return value is programmed into the hardware.
|
||
|
||
*/
|
||
|
||
{
|
||
INTERRUPT_DEST IntDest;
|
||
|
||
if (HalpMaxProcsPerCluster == 0) {
|
||
return(1 << Number);
|
||
} else {
|
||
//
|
||
// In systems with heirarchical APIC buses, the BIOS/MPS table has to
|
||
// inform the OS of the underlying topology so we can do this mapping.
|
||
// For now, just assign consecutive cluster IDs starting from 0.
|
||
//
|
||
IntDest.Cluster.Hw.ClusterId = (Number/HalpMaxProcsPerCluster);
|
||
IntDest.Cluster.Hw.DestId = 1 << (Number % HalpMaxProcsPerCluster);
|
||
return(IntDest.Cluster.AsUchar);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpInitializeApicAddressing(
|
||
IN UCHAR Number
|
||
)
|
||
{
|
||
if (HalpMaxProcsPerCluster == 0) {
|
||
pLocalApic[LU_DEST_FORMAT/4] = LU_DEST_FORMAT_FLAT;
|
||
} else {
|
||
ASSERT(Number <= (HalpMaxProcsPerCluster * MAX_CLUSTERS));
|
||
pLocalApic[LU_DEST_FORMAT/4] = LU_DEST_FORMAT_CLUSTER;
|
||
}
|
||
|
||
HalpIntDestMap[Number].LogicalId = HalpMapNtToHwProcessorId(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.
|
||
//
|
||
pLocalApic[LU_LOGICAL_DEST/4] = (ULONG)
|
||
(HalpIntDestMap[Number].LogicalId << DESTINATION_SHIFT);
|
||
|
||
}
|
||
|
||
|
||
UCHAR
|
||
HalpNodeNumber(
|
||
PKPCR pPCR
|
||
)
|
||
/*
|
||
|
||
Routine Description:
|
||
|
||
This routine divines the Node number for the current CPU.
|
||
Node numbers start at 1, and represent the granularity of interrupt
|
||
routing decisions.
|
||
|
||
Arguments:
|
||
|
||
pPCR - A pointer to the PCR of the current processor. (This implies
|
||
the caller must have masked interrupts.)
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
*/
|
||
{
|
||
// One Node per cluster.
|
||
if (HalpMaxProcsPerCluster != 0) {
|
||
// One Node per Cluster.
|
||
return(pPCR->Prcb->Number/HalpMaxProcsPerCluster + 1);
|
||
} else {
|
||
// One Node per machine.
|
||
return(1);
|
||
}
|
||
#if 0
|
||
ULONG localApicId;
|
||
|
||
// One Node per physical CPU package.
|
||
localApicId = *(PVULONG)(LOCALAPIC + LU_ID_REGISTER);
|
||
localApicId &= APIC_ID_MASK;
|
||
localApicId >>= APIC_ID_SHIFT;
|
||
|
||
// TODO: Implement cpuid stuff here to determine shift
|
||
return((localApicId>>1) + 1);
|
||
#endif
|
||
}
|
||
|
||
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;
|
||
UCHAR Node;
|
||
|
||
_asm {
|
||
pushfd
|
||
pop eax
|
||
mov SavedFlags, eax
|
||
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)
|
||
{
|
||
#if defined(NEC_98)
|
||
_asm {
|
||
push dx
|
||
mov dx, ImcrDataPortAddr
|
||
mov al, ImcrEnableApic
|
||
out dx, al
|
||
pop dx
|
||
}
|
||
#else // defined(NEC_98)
|
||
_asm {
|
||
mov al, ImcrPort
|
||
out ImcrRegPortAddr, al
|
||
|
||
mov al, ImcrEnableApic
|
||
out ImcrDataPortAddr, al
|
||
}
|
||
#endif // defined(NEC_98)
|
||
}
|
||
|
||
//
|
||
// By default, use flat logical APIC addressing. If we have more
|
||
// than 8 processors, we must use cluster mode APIC addressing
|
||
//
|
||
if( (HalpMaxProcsPerCluster > 4) ||
|
||
((HalpMpInfoTable.ProcessorCount > 8) &&
|
||
(HalpMaxProcsPerCluster == 0)) ) {
|
||
HalpMaxProcsPerCluster = 4;
|
||
}
|
||
|
||
if (HalpMpInfoTable.ApicVersion == APIC_82489DX) {
|
||
//
|
||
// Ignore user's attempt to force cluster mode if running
|
||
// on 82489DX external APIC interrupt controller.
|
||
//
|
||
ASSERT(HalpMpInfoTable.ProcessorCount <= 8);
|
||
HalpMaxProcsPerCluster = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Add the current processor to the Node tables.
|
||
//
|
||
Node = HalpNodeNumber(pPCR);
|
||
if (HalpMaxNode < Node) {
|
||
HalpMaxNode = Node;
|
||
}
|
||
HalpNodeAffinity[Node-1] |= 1 << pPCR->Prcb->Number;
|
||
|
||
//
|
||
// Program the TPR to mask all events
|
||
//
|
||
pLocalApic[LU_TPR/4] = 0xff;
|
||
HalpInitializeApicAddressing(pPCR->Prcb->Number);
|
||
|
||
//
|
||
// Initialize spurious interrupt handling
|
||
//
|
||
KiSetHandlerAddressToIDT(APIC_SPURIOUS_VECTOR, HalpApicSpuriousService);
|
||
pLocalApic[LU_SPURIOUS_VECTOR/4] = (APIC_SPURIOUS_VECTOR | LU_UNIT_ENABLED);
|
||
|
||
if (HalpMpInfoTable.ApicVersion != APIC_82489DX) {
|
||
//
|
||
// Initialize 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 low value and return
|
||
//
|
||
pLocalApic[LU_TPR/4] = ZERO_VECTOR;
|
||
|
||
_asm {
|
||
mov eax, SavedFlags
|
||
push eax
|
||
popfd
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpUnMapIOApics(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This routine unmaps the IOAPIC's and is primarily used
|
||
to prevent loss of VA space during hibernation
|
||
|
||
Arguments:
|
||
|
||
None:
|
||
|
||
Return Value:
|
||
|
||
None
|
||
*/
|
||
{
|
||
UCHAR i;
|
||
|
||
for(i=0; i < HalpMpInfoTable.IOApicCount; i++) {
|
||
if (HalpMpInfoTable.IoApicBase[i]) {
|
||
HalpUnmapVirtualAddress(HalpMpInfoTable.IoApicBase[i],1);
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpPostSleepMP(
|
||
IN LONG NumberProcessors,
|
||
IN volatile PLONG Number
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This routine does the part of MP re-init that needs to
|
||
happen after hibernation or sleeping.
|
||
|
||
Arguments:
|
||
|
||
None:
|
||
|
||
Return Value:
|
||
|
||
None
|
||
*/
|
||
{
|
||
volatile ULONG ThisProcessor;
|
||
ULONG localApicId;
|
||
KIRQL OldIrql;
|
||
|
||
//
|
||
// Boot processor and the newly woken processors come here
|
||
//
|
||
|
||
ThisProcessor = KeGetPcr()->Prcb->Number;
|
||
|
||
if (ThisProcessor != 0) {
|
||
|
||
HalpInitializeLocalUnit ();
|
||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Initialize the processor machine check registers
|
||
//
|
||
|
||
if ((HalpFeatureBits & HAL_MCE_PRESENT) ||
|
||
(HalpFeatureBits & HAL_MCA_PRESENT)) {
|
||
HalpMcaCurrentProcessorSetConfig();
|
||
}
|
||
|
||
//
|
||
// Enable NMI vectors in the local APIC
|
||
//
|
||
|
||
HalpEnableNMI();
|
||
|
||
//
|
||
// Enable perf event in local APIC
|
||
//
|
||
|
||
if (HalpFeatureBits & HAL_PERF_EVENTS) {
|
||
HalpEnablePerfInterupt(0);
|
||
}
|
||
|
||
//
|
||
// Wait for all processors to finish initialization.
|
||
//
|
||
|
||
InterlockedIncrement(Number);
|
||
while (*Number != NumberProcessors);
|
||
|
||
//
|
||
// The following global hardware state needs to be set after all processors
|
||
// have been woken up and initialized
|
||
//
|
||
|
||
if (KeGetPcr()->Prcb->Number == 0) {
|
||
|
||
//
|
||
// Restore clock interrupt
|
||
//
|
||
|
||
HalpInitializeClock();
|
||
|
||
HalpSet8259Mask(HalpGlobal8259Mask);
|
||
|
||
HalpHiberInProgress = FALSE;
|
||
|
||
//
|
||
// We are now ready to send IPIs again if more than
|
||
// one processor
|
||
//
|
||
|
||
if (NumberProcessors > 1) {
|
||
HalpIpiClock = 0xff;
|
||
}
|
||
}
|
||
}
|
||
|
||
|