/*++ 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; jRegisterSelect = 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; }