Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

946 lines
22 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:
mpsproc.c
Abstract:
PC+MP Start Next Processor c code.
This module implements the initialization of the system dependent
functions that define the Hardware Architecture Layer (HAL) for a
PC+MP System
Author:
Ken Reneris (kenr) 22-Jan-1991
Environment:
Kernel mode only.
Revision History:
Ron Mosgrove (Intel) - Modified to support the PC+MP
Jake Oshins (jakeo) - Modified for ACPI MP machines 22-Dec-1997
--*/
#if !defined(_HALPAE_)
#define _HALPAE_
#endif
#include "halp.h"
#include "apic.inc"
#include "pcmp_nt.inc"
#include "stdio.h"
#ifdef ACPI_HAL
#include "acpitabl.h"
#endif
#ifdef DEBUGGING
void
HalpDisplayString(
IN PVOID String
);
#endif // DEBUGGING
#if defined(ACPI_HAL)
#if !defined(NT_UP)
const UCHAR HalName[] = "ACPI 1.0 - APIC platform MP";
#define HalName L"ACPI 1.0 - APIC platform MP"
WCHAR HalHardwareIdString[] = L"acpiapic_mp\0";
#else
const UCHAR HalName[] = "ACPI 1.0 - APIC platform UP";
#define HalName L"ACPI 1.0 - APIC platform UP"
WCHAR MpHalHardwareIdString[] = L"acpiapic_mp\0";
WCHAR HalHardwareIdString[] = L"acpiapic_up\0";
#endif
#else
#if !defined(NT_UP)
const UCHAR HalName[] = "MPS 1.4 - APIC platform";
#define HalName L"MPS 1.4 - APIC platform"
WCHAR HalHardwareIdString[] = L"mps_mp\0";
#else
const UCHAR HalName[] = "UP MPS 1.4 - APIC platform";
#define HalName L"UP MPS 1.4 - APIC platform"
WCHAR MpHalHardwareIdString[] = L"mps_mp\0";
WCHAR HalHardwareIdString[] = L"mps_up\0";
#endif
#endif
#if !defined(NT_UP)
ULONG
HalpStartProcessor (
IN PVOID InitCodePhysAddr,
IN ULONG ProcessorNumber
);
#endif
VOID
HalpBiosDisplayReset (
VOID
);
VOID
HalpInitMP (
IN ULONG Phase,
IN PLOADER_PARAMETER_BLOCK LoaderBlock
);
VOID
HalpInitOtherBuses (
VOID
);
VOID
HalpInitializePciBus (
VOID
);
VOID
HalpInitializePciStubs (
VOID
);
VOID
HalpInheritBusAddressMapInfo (
VOID
);
VOID
HalpInitBusAddressMapInfo (
VOID
);
VOID
HalpResetThisProcessor (
VOID
);
VOID
HalpApicRebootService(
VOID
);
#ifdef ACPI_HAL
VOID
HalpInitMpInfo (
IN PMAPIC ApicTable,
IN ULONG Phase
);
extern PMAPIC HalpApicTable;
#endif
extern VOID (*HalpRebootNow)(VOID);
volatile ULONG HalpProcessorsNotHalted = 0;
#define LOW_MEMORY 0x000100000
//
// From hal386.inc
//
#define IDT_NMI_VECTOR 2
#define D_INT032 0x8E00
#define KGDT_R0_CODE 0x8
//
// Defines to let us diddle the CMOS clock and the keyboard
//
#define CMOS_CTRL (PUCHAR )0x70
#define CMOS_DATA (PUCHAR )0x71
#define RESET 0xfe
#define KEYBPORT (PUCHAR )0x64
extern USHORT HalpGlobal8259Mask;
extern PKPCR HalpProcessorPCR[];
extern struct HalpMpInfo HalpMpInfoTable;
extern ULONG HalpIpiClock;
extern PVOID HalpLowStubPhysicalAddress; // pointer to low memory bootup stub
extern PUCHAR HalpLowStub; // pointer to low memory bootup stub
PUCHAR Halp1stPhysicalPageVaddr; // pointer to physical memory 0:0
PUSHORT MppProcessorAvail; // pointer to processavail flag
ULONG HalpDontStartProcessors = 0;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,HalpInitMP)
#pragma alloc_text(INIT,HalAllProcessorsStarted)
#pragma alloc_text(INIT,HalReportResourceUsage)
#pragma alloc_text(INIT,HalpInitOtherBuses)
#if !defined(NT_UP)
#pragma alloc_text(PAGELK,HalpStartProcessor)
#endif
#endif
VOID
HalpInitMP (
IN ULONG Phase,
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
/*++
Routine Description:
Allows MP initialization from HalInitSystem.
Arguments:
Same as HalInitSystem
Return Value:
None.
--*/
{
PKPCR pPCR;
PHYSICAL_ADDRESS physicalAddress;
pPCR = KeGetPcr();
//
// Increment a count of the number of processors
// running NT, This could be different from the
// number of enabled processors, if one or more
// of the processor failed to start.
//
if (Phase == 1)
HalpMpInfoTable.NtProcessors++;
#ifdef DEBUGGING
sprintf(Cbuf, "HalpInitMP: Number of Processors = 0x%x\n",
HalpMpInfoTable.NtProcessors);
HalpDisplayString(Cbuf);
#endif
if (Phase == 0) {
#if defined(NT_UP)
//
// On UP build - done
//
return ;
#endif
//
// Map the 1st Physical page of memory
//
physicalAddress.QuadPart = 0;
Halp1stPhysicalPageVaddr = HalpMapPhysicalMemory (physicalAddress, 1);
//
// Allocate some low memory for processor bootup stub
//
HalpLowStubPhysicalAddress = (PVOID)HalpAllocPhysicalMemory (LoaderBlock,
LOW_MEMORY, 1, FALSE);
if (!HalpLowStubPhysicalAddress) {
//
// Can't get memory
//
#if DBG
DbgPrint("HAL: can't allocate memory to start processors\n");
#endif
return;
}
physicalAddress.QuadPart = (ULONGLONG)HalpLowStubPhysicalAddress;
HalpLowStub = (PCHAR) HalpMapPhysicalMemory (physicalAddress, 1);
} else {
//
// Phase 1 for another processor
//
if (pPCR->Prcb->Number != 0) {
HalpIpiClock = 0xff;
}
#ifdef ACPI_HAL
HalpInitMpInfo(HalpApicTable, Phase);
#endif
}
}
BOOLEAN
HalAllProcessorsStarted (
VOID
)
{
if (KeGetPcr()->Number == 0) {
if (HalpFeatureBits & HAL_PERF_EVENTS) {
//
// Enable local perf events on each processor
//
HalpGenericCall (
HalpEnablePerfInterupt,
(ULONG) NULL,
HalpActiveProcessors
);
}
if (HalpFeatureBits & HAL_NO_SPECULATION) {
//
// Processor doesn't perform speculative execeution,
// remove fences in critical code paths
//
HalpRemoveFences ();
}
}
return TRUE;
}
VOID
HalpInitOtherBuses (
VOID
)
{
//
// Registry is now intialized, see if there are any PCI buses
//
}
VOID
HalReportResourceUsage (
VOID
)
/*++
Routine Description:
The registery is now enabled - time to report resources which are
used by the HAL.
Arguments:
Return Value:
--*/
{
UNICODE_STRING UHalName;
INTERFACE_TYPE interfacetype;
//
// Initialize phase 2
//
HalInitSystemPhase2 ();
//
// Turn on MCA support if present
//
HalpMcaInit();
//
// Registry is now intialized, see if there are any PCI buses
//
HalpInitializePciBus ();
HalpInitializePciStubs ();
#ifndef ACPI_HAL // ACPI HALs don't deal with address maps
//
// Update supported address info with MPS bus address map
//
HalpInitBusAddressMapInfo ();
//
// Inherit any bus address mappings from MPS hierarchy descriptors
//
HalpInheritBusAddressMapInfo ();
#endif
//
// Set type
//
switch (HalpBusType) {
case MACHINE_TYPE_ISA: interfacetype = Isa; break;
case MACHINE_TYPE_EISA: interfacetype = Eisa; break;
case MACHINE_TYPE_MCA: interfacetype = MicroChannel; break;
default: interfacetype = PCIBus; break;
}
//
// Report HALs resource usage
//
RtlInitUnicodeString (&UHalName, HalName);
HalpReportResourceUsage (
&UHalName, // descriptive name
interfacetype
);
#ifndef ACPI_HAL // ACPI HALs don't deal with address maps
//
// Register hibernate support
//
HalpRegisterHibernate();
#endif
HalpRegisterPciDebuggingDeviceInfo();
}
VOID
HalpResetAllProcessors (
VOID
)
/*++
Routine Description:
This procedure is called by the HalpReboot routine. It is called in
response to a system reset request.
This routine generates a reboot request via the APIC's ICR.
This routine will NOT return.
--*/
{
ULONG j;
PKGDTENTRY GdtPtr;
ULONG TssAddr;
PKPRCB Prcb;
#ifndef NT_UP
HalpProcessorsNotHalted = HalpMpInfoTable.NtProcessors;
#else
//
// Only this processor needs to be halted
//
HalpProcessorsNotHalted = 1;
#endif
//
// Set all processors NMI handlers
//
for (j = 0; j < HalpMpInfoTable.NtProcessors; ++j) {
GdtPtr = &HalpProcessorPCR[j]->
GDT[HalpProcessorPCR[j]->IDT[IDT_NMI_VECTOR].Selector >> 3];
TssAddr = (((GdtPtr->HighWord.Bits.BaseHi << 8) +
GdtPtr->HighWord.Bits.BaseMid) << 16) + GdtPtr->BaseLow;
((PKTSS)TssAddr)->Eip = (ULONG)HalpApicRebootService;
}
if (HalpProcessorsNotHalted > 1) {
//
// Wait for the ICR to become free
//
if (HalpWaitForPending (0xFFFF, pLocalApic + LU_INT_CMD_LOW/4)) {
//
// For P54c or better processors, reboot by sending all processors
// NMIs. For pentiums we send interrupts, since there are some
// pentium MP machines where the NMIs method does not work.
//
// The NMI method is better.
//
Prcb = KeGetCurrentPrcb();
j = Prcb->CpuType << 16 | (Prcb->CpuStep & 0xff00);
if (j > 0x50100) {
//
// Get other processors attention with an NMI
//
j = HalpActiveProcessors & ~Prcb->SetMember;
j = j << DESTINATION_SHIFT;
pLocalApic[LU_INT_CMD_HIGH/4] = j;
pLocalApic[LU_INT_CMD_LOW/4] = (ICR_USE_DEST_FIELD | LOGICAL_DESTINATION | DELIVER_NMI);
//
// Wait 5ms and see if any processors took the NMI. If not,
// go do it the old way.
//
KeStallExecutionProcessor(5000);
if (HalpProcessorsNotHalted != HalpMpInfoTable.NtProcessors) {
//
// Reboot local
//
HalpApicRebootService();
}
}
//
// Signal other processors which also may be waiting to
// reboot the machine, that it's time to go
//
HalpRebootNow = HalpResetThisProcessor;
//
// Send a reboot interrupt
//
pLocalApic[LU_INT_CMD_LOW/4] = (ICR_ALL_INCL_SELF | APIC_REBOOT_VECTOR);
//
// we're done - set TPR to zero so the reboot interrupt will happen
//
pLocalApic[LU_TPR/4] = 0;
_asm sti ;
for (; ;) ;
}
}
//
// Reset the old fashion way
//
WRITE_PORT_UCHAR(KEYBPORT, RESET);
}
VOID
HalpResetThisProcessor (
VOID
)
/*++
Routine Description:
This procedure is called by the HalpReboot routine.
It is called in response to a system reset request.
This routine is called by the reboot ISR (linked to
APIC_REBOOT_VECTOR). The HalpResetAllProcessors
generates the reboot request via the APIC's ICR.
The function of this routine is to perform any processor
specific shutdown code needed and then reset the system
(on the BSP==P0 only).
This routine will NOT return.
--*/
{
PUSHORT Magic;
ULONG ThisProcessor = 0;
ULONG i, j, max, RedirEntry;
ULONG AllProcessorsHalted;
struct ApicIoUnit *IoUnitPtr;
PHYSICAL_ADDRESS physicalAddress;
ThisProcessor = KeGetPcr()->Prcb->Number;
//
// Do whatever is needed to this processor to restore
// system to a bootable state
//
pLocalApic[LU_TPR/4] = 0xff;
pLocalApic[LU_TIMER_VECTOR/4] =
(APIC_SPURIOUS_VECTOR |PERIODIC_TIMER | INTERRUPT_MASKED);
pLocalApic[LU_INT_VECTOR_0/4] =
(APIC_SPURIOUS_VECTOR | INTERRUPT_MASKED);
pLocalApic[LU_INT_VECTOR_1/4] =
( LEVEL_TRIGGERED | ACTIVE_HIGH | DELIVER_NMI |
INTERRUPT_MASKED | NMI_VECTOR);
if (HalpMpInfoTable.ApicVersion != APIC_82489DX) {
pLocalApic[LU_FAULT_VECTOR/4] =
APIC_FAULT_VECTOR | INTERRUPT_MASKED;
}
if (ThisProcessor == 0) {
_asm {
lock dec HalpProcessorsNotHalted
}
//
// we are running on the BSP, wait for everyone to
// complete the re-initialization code above
//
AllProcessorsHalted = 0;
while(!AllProcessorsHalted) {
_asm {
lock and HalpProcessorsNotHalted,0xffffffff
jnz EveryOneNotDone
inc AllProcessorsHalted
EveryOneNotDone:
} // asm block
} // NOT AllProcessorsHalted
KeStallExecutionProcessor(100);
//
// Write the Shutdown reason code, so the BIOS knows
// this is a reboot
//
WRITE_PORT_UCHAR(CMOS_CTRL, 0x0f); // CMOS Addr 0f
WRITE_PORT_UCHAR(CMOS_DATA, 0x00); // Reason Code Reset
physicalAddress.QuadPart = 0;
Magic = HalpMapPhysicalMemory(physicalAddress, 1);
Magic[0x472 / sizeof(USHORT)] = 0x1234; // warm boot
//
// If required, disable APIC mode
//
if (HalpMpInfoTable.IMCRPresent)
{
_asm {
mov al, ImcrPort
out ImcrRegPortAddr, al
}
KeStallExecutionProcessor(100);
_asm {
mov al, ImcrDisableApic
out ImcrDataPortAddr, al
}
}
KeStallExecutionProcessor(100);
for (j=0; j<HalpMpInfoTable.IOApicCount; j++) {
IoUnitPtr = (struct ApicIoUnit *) HalpMpInfoTable.IoApicBase[j];
//
// Disable all interrupts on IO Unit
//
IoUnitPtr->RegisterSelect = IO_VERS_REGISTER;
max = ((IoUnitPtr->RegisterWindow >> 16) & 0xff) * 2;
for (i=0; i <= max; i += 2) {
IoUnitPtr->RegisterSelect = IO_REDIR_00_LOW + i;
IoUnitPtr->RegisterWindow |= INT_VECTOR_MASK | INTERRUPT_MASKED;
//
// Clear any set Remote IRR bits by programming the entry to
// edge and then back to level. Otherwise there will be
// no further interrupts from this source.
//
IoUnitPtr->RegisterSelect = IO_REDIR_00_LOW + i;
RedirEntry = IoUnitPtr->RegisterWindow;
if ( (RedirEntry & LEVEL_TRIGGERED) && (RedirEntry & REMOTE_IRR)) {
RedirEntry &= ~LEVEL_TRIGGERED;
IoUnitPtr->RegisterWindow = RedirEntry;
RedirEntry = IoUnitPtr->RegisterWindow;
RedirEntry |= LEVEL_TRIGGERED;
IoUnitPtr->RegisterWindow = RedirEntry;
}
}
} // for all Io Apics
//
// Disable the Local Apic
//
pLocalApic[LU_SPURIOUS_VECTOR/4] =
(APIC_SPURIOUS_VECTOR | LU_UNIT_DISABLED);
KeStallExecutionProcessor(100);
_asm {
cli
};
//
// Enable Pic interrupts
//
HalpGlobal8259Mask = 0;
HalpSet8259Mask ((USHORT) HalpGlobal8259Mask);
KeStallExecutionProcessor(1000);
//
// Finally, reset the system through
// the keyboard controller
//
WRITE_PORT_UCHAR(KEYBPORT, RESET);
} else {
//
// We're running on a processor other than the BSP
//
//
// Disable the Local Apic
//
pLocalApic[LU_SPURIOUS_VECTOR/4] =
(APIC_SPURIOUS_VECTOR | LU_UNIT_DISABLED);
KeStallExecutionProcessor(100);
//
// Now we are done, tell the BSP
//
_asm {
lock dec HalpProcessorsNotHalted
}
} // Not BSP
//
// Everyone stops here until reset
//
_asm {
cli
StayHalted:
hlt
jmp StayHalted
}
}
#if !defined(NT_UP)
ULONG
HalpStartProcessor (
IN PVOID InitCodePhysAddr,
IN ULONG ProcessorNumber
)
/*++
Routine Description:
Actually Start the Processor in question. This routine
assumes the init code is setup and ready to run. The real
mode init code must begin on a page boundry.
NOTE: This assumes the BSP is entry 0 in the MP table.
This routine cannot fail.
Arguments:
InitCodePhysAddr - execution address of init code
Return Value:
0 - Something prevented us from issuing the reset.
n - Processor's PCMP Local APICID + 1
--*/
{
NTSTATUS status;
UCHAR ApicID;
PVULONG LuDestAddress = (PVULONG) (LOCALAPIC + LU_INT_CMD_HIGH);
PVULONG LuICR = (PVULONG) (LOCALAPIC + LU_INT_CMD_LOW);
#define DEFAULT_DELAY 100
ULONG DelayCount = DEFAULT_DELAY;
ULONG ICRCommand,i;
ASSERT((((ULONG) InitCodePhysAddr) & 0xfff00fff) == 0);
if (ProcessorNumber >= HalpMpInfoTable.ProcessorCount) {
return(0);
}
//
// Get the APIC ID of the processor to start.
//
status = HalpGetNextProcessorApicId(ProcessorNumber,
&ApicID);
if (!NT_SUCCESS(status)) {
#ifdef DEBUGGING
HalpDisplayString("HAL: HalpStartProcessor: No Processor Available\n");
#endif
return(0);
}
if (HalpDontStartProcessors)
return ApicID+1;
//
// Make sure we can get to the Apic Bus
//
KeStallExecutionProcessor(200);
if (HalpWaitForPending (DEFAULT_DELAY, LuICR) == 0) {
//
// We couldn't find a processor to start
//
#ifdef DEBUGGING
HalpDisplayString("HAL: HalpStartProcessor: can't access APIC Bus\n");
#endif
return 0;
}
// For a P54 C/CM system, it is possible that the BSP is the P54CM and the
// P54C is the Application processor. The P54C needs an INIT (reset)
// to restart, so we issue a reset regardless of whether we a 82489DX
// or an integrated APIC.
//
// This system is based on the original 82489DX's.
// These devices do not support the Startup IPI's.
// The mechanism used is the ASSERT/DEASSERT INIT
// feature of the local APIC. This resets the
// processor.
//
#ifdef DEBUGGING
sprintf(Cbuf, "HAL: HalpStartProcessor: Reset IPI to ApicId %d (0x%x)\n",
ApicID,((ULONG) ApicID) << DESTINATION_SHIFT );
HalpDisplayString(Cbuf);
#endif
//
// We use a Physical Destination
//
*LuDestAddress = ((ULONG) ApicID) << DESTINATION_SHIFT;
//
// Now Assert reset and drop it
//
*LuICR = LU_RESET_ASSERT;
KeStallExecutionProcessor(10);
*LuICR = LU_RESET_DEASSERT;
KeStallExecutionProcessor(200);
if (HalpMpInfoTable.ApicVersion == APIC_82489DX) {
return ApicID+1;
}
//
// Set the Startup Address as a vector and combine with the
// ICR bits
//
ICRCommand = (((ULONG) InitCodePhysAddr & 0x000ff000) >> 12)
| LU_STARTUP_IPI;
#ifdef DEBUGGING
sprintf(Cbuf, "HAL: HalpStartProcessor: Startup IPI (0x%x) to ApicId %d (0x%x)\n",
ICRCommand, ApicID, ((ULONG) ApicID) << DESTINATION_SHIFT );
HalpDisplayString(Cbuf);
#endif
//
// Set the Address of the APIC again, this may not be needed
// but it can't hurt.
//
*LuDestAddress = (ApicID << DESTINATION_SHIFT);
//
// Issue the request
//
*LuICR = ICRCommand;
KeStallExecutionProcessor(200);
//
// Repeat the Startup IPI. This is because the second processor may
// have been issued an INIT request. This is generated by some BIOSs.
//
// On older processors (286) BIOS's use a mechanism called triple
// fault reset to transition from protected mode to real mode.
// This mechanism causes the processor to generate a shutdown cycle.
// The shutdown is typically issued by the BIOS building an invalid
// IDT and then generating an interrupt. Newer processors have an
// INIT line that the chipset jerks when it sees a shutdown cycle
// issued by the processor. The Phoenix BIOS, for example, has
// integrated support for triple fault reset as part of their POST
// (Power On Self Test) code.
//
// When the P54CM powers on it is held in a tight microcode loop
// waiting for a Startup IPI to be issued and queuing other requests.
// When the POST code executes the triple fault reset test the INIT
// cycle is queued by the processor. Later, when a Startup IPI is
// issued to the CM, the CM starts and immediately gets a INIT cycle.
// The effect from a software standpoint is that the processor is
// never started.
//
// The work around implemented here is to issue two Startup IPI's.
// The first allows the INIT to be processed and the second performs
// the actual startup.
//
//
// Make sure we can get to the Apic Bus
//
if (HalpWaitForPending (DEFAULT_DELAY, LuICR) == 0) {
//
// We're toast, can't gain access to the APIC Bus
//
#ifdef DEBUGGING
HalpDisplayString("HAL: HalpStartProcessor: can't access APIC Bus\n");
#endif
return 0;
}
//
// Allow Time for any Init request to be processed
//
KeStallExecutionProcessor(100);
//
// Set the Address of the APIC again, this may not be needed
// but it can't hurt.
//
*LuDestAddress = (ApicID << DESTINATION_SHIFT);
//
// Issue the request
//
*LuICR = ICRCommand;
KeStallExecutionProcessor(200);
return ApicID+1;
}
#endif // !NT_UP
ULONG
FASTCALL
HalSystemVectorDispatchEntry (
IN ULONG Vector,
OUT PKINTERRUPT_ROUTINE **FlatDispatch,
OUT PKINTERRUPT_ROUTINE *NoConnection
)
{
return FALSE;
}