summaryrefslogtreecommitdiff
path: root/Core/UefiCpuPkg/CpuDxe
diff options
context:
space:
mode:
Diffstat (limited to 'Core/UefiCpuPkg/CpuDxe')
-rw-r--r--Core/UefiCpuPkg/CpuDxe/ApStartup.c478
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuDxe.c931
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuDxe.h256
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuDxe.inf95
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuDxe.uni22
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuDxeExtra.uni20
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuGdt.c161
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuGdt.h74
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuMp.c1806
-rw-r--r--Core/UefiCpuPkg/CpuDxe/CpuMp.h660
-rw-r--r--Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.S57
-rw-r--r--Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.asm58
-rw-r--r--Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.asm76
-rw-r--r--Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.nasm68
-rw-r--r--Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.S60
-rw-r--r--Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.asm54
-rw-r--r--Core/UefiCpuPkg/CpuDxe/X64/MpAsm.asm76
-rw-r--r--Core/UefiCpuPkg/CpuDxe/X64/MpAsm.nasm70
18 files changed, 5022 insertions, 0 deletions
diff --git a/Core/UefiCpuPkg/CpuDxe/ApStartup.c b/Core/UefiCpuPkg/CpuDxe/ApStartup.c
new file mode 100644
index 0000000000..78fb26f985
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/ApStartup.c
@@ -0,0 +1,478 @@
+/** @file
+ CPU DXE AP Startup
+
+ Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "CpuDxe.h"
+#include "CpuGdt.h"
+#include "CpuMp.h"
+
+#pragma pack(1)
+
+typedef struct {
+ UINT8 MoveIa32EferMsrToEcx[5];
+ UINT8 ReadIa32EferMsr[2];
+ UINT8 SetExecuteDisableBitEnableBit[4];
+ UINT8 WriteIa32EferMsr[2];
+
+#if defined (MDE_CPU_IA32)
+ UINT8 MovEaxCr3;
+ UINT32 Cr3Value;
+ UINT8 MovCr3Eax[3];
+
+ UINT8 MoveCr4ToEax[3];
+ UINT8 SetCr4Bit5[4];
+ UINT8 MoveEaxToCr4[3];
+
+ UINT8 MoveCr0ToEax[3];
+ UINT8 SetCr0PagingBit[4];
+ UINT8 MoveEaxToCr0[3];
+#endif
+} ENABLE_EXECUTE_DISABLE_CODE;
+
+ENABLE_EXECUTE_DISABLE_CODE mEnableExecuteDisableCodeTemplate = {
+ { 0xB9, 0x80, 0x00, 0x00, 0xC0 }, // mov ecx, 0xc0000080
+ { 0x0F, 0x32 }, // rdmsr
+ { 0x0F, 0xBA, 0xE8, 0x0B }, // bts eax, 11
+ { 0x0F, 0x30 }, // wrmsr
+
+#if defined (MDE_CPU_IA32)
+ 0xB8, 0x00000000, // mov eax, cr3 value
+ { 0x0F, 0x22, 0xd8 }, // mov cr3, eax
+
+ { 0x0F, 0x20, 0xE0 }, // mov eax, cr4
+ { 0x0F, 0xBA, 0xE8, 0x05 }, // bts eax, 5
+ { 0x0F, 0x22, 0xE0 }, // mov cr4, eax
+
+ { 0x0F, 0x20, 0xC0 }, // mov eax, cr0
+ { 0x0F, 0xBA, 0xE8, 0x1F }, // bts eax, 31
+ { 0x0F, 0x22, 0xC0 }, // mov cr0, eax
+#endif
+};
+
+typedef struct {
+ UINT8 JmpToCli[2];
+
+ UINT16 GdtLimit;
+ UINT32 GdtBase;
+
+ UINT8 Cli;
+
+ UINT8 MovAxRealSegment; UINT16 RealSegment;
+ UINT8 MovDsAx[2];
+
+ UINT8 MovBxGdtr[3];
+ UINT8 LoadGdt[5];
+
+ UINT8 MovEaxCr0[2];
+ UINT32 MovEaxCr0Value;
+ UINT8 MovCr0Eax[3];
+
+ UINT8 FarJmp32Flat[2]; UINT32 FlatJmpOffset; UINT16 FlatJmpSelector;
+
+ //
+ // Now in IA32
+ //
+ UINT8 MovEaxCr4;
+ UINT32 MovEaxCr4Value;
+ UINT8 MovCr4Eax[3];
+
+ UINT8 MoveDataSelectorIntoAx[2]; UINT16 FlatDataSelector;
+ UINT8 MoveFlatDataSelectorFromAxToDs[2];
+ UINT8 MoveFlatDataSelectorFromAxToEs[2];
+ UINT8 MoveFlatDataSelectorFromAxToFs[2];
+ UINT8 MoveFlatDataSelectorFromAxToGs[2];
+ UINT8 MoveFlatDataSelectorFromAxToSs[2];
+
+ //
+ // Code placeholder to enable PAE Execute Disable for IA32
+ // and enable Execute Disable Bit for X64
+ //
+ ENABLE_EXECUTE_DISABLE_CODE EnableExecuteDisable;
+
+#if defined (MDE_CPU_X64)
+ //
+ // Transition to X64
+ //
+ UINT8 MovEaxCr3;
+ UINT32 Cr3Value;
+ UINT8 MovCr3Eax[3];
+
+ UINT8 MoveCr4ToEax[3];
+ UINT8 SetCr4Bit5[4];
+ UINT8 MoveEaxToCr4[3];
+
+ UINT8 MoveLongModeEnableMsrToEcx[5];
+ UINT8 ReadLmeMsr[2];
+ UINT8 SetLongModeEnableBit[4];
+ UINT8 WriteLmeMsr[2];
+
+ UINT8 MoveCr0ToEax[3];
+ UINT8 SetCr0PagingBit[4];
+ UINT8 MoveEaxToCr0[3];
+ //UINT8 DeadLoop[2];
+
+ UINT8 FarJmp32LongMode; UINT32 LongJmpOffset; UINT16 LongJmpSelector;
+#endif // defined (MDE_CPU_X64)
+
+#if defined (MDE_CPU_X64)
+ UINT8 MovEaxOrRaxCpuDxeEntry[2]; UINTN CpuDxeEntryValue;
+#else
+ UINT8 MovEaxOrRaxCpuDxeEntry; UINTN CpuDxeEntryValue;
+#endif
+ UINT8 JmpToCpuDxeEntry[2];
+
+} STARTUP_CODE;
+
+#pragma pack()
+
+/**
+ This .asm code used for translating processor from 16 bit real mode into
+ 64 bit long mode. which help to create the mStartupCodeTemplate value.
+
+ To assemble:
+ * nasm -o ApStartup ApStartup.asm
+ Then disassemble:
+ * ndisasm -b 16 ApStartup
+ * ndisasm -b 16 -e 6 ApStartup
+ * ndisasm -b 32 -e 32 ApStartup (This -e offset may need adjustment)
+ * ndisasm -b 64 -e 0x83 ApStartup (This -e offset may need adjustment)
+
+ %define DEFAULT_CR0 0x00000023
+ %define DEFAULT_CR4 0x640
+
+ BITS 16
+
+ jmp short TransitionFromReal16To32BitFlat
+
+ ALIGN 2
+
+ Gdtr:
+ dw 0x5a5a
+ dd 0x5a5a5a5a
+
+ ;
+ ; Modified: EAX, EBX
+ ;
+ TransitionFromReal16To32BitFlat:
+
+ cli
+ mov ax, 0x5a5a
+ mov ds, ax
+
+ mov bx, Gdtr
+ o32 lgdt [ds:bx]
+
+ mov eax, cr4
+ btc eax, 5
+ mov cr4, eax
+
+ mov eax, DEFAULT_CR0
+ mov cr0, eax
+
+ jmp 0x5a5a:dword jumpTo32BitAndLandHere
+ BITS 32
+ jumpTo32BitAndLandHere:
+
+ mov eax, DEFAULT_CR4
+ mov cr4, eax
+
+ mov ax, 0x5a5a
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+
+ ;
+ ; Jump to CpuDxe for IA32
+ ;
+ mov eax, 0x5a5a5a5a
+ or eax, eax
+ jz Transition32FlatTo64Flat
+ jmp eax
+
+ ;
+ ; Transition to X64
+ ;
+ Transition32FlatTo64Flat:
+ mov eax, 0x5a5a5a5a
+ mov cr3, eax
+
+ mov eax, cr4
+ bts eax, 5 ; enable PAE
+ mov cr4, eax
+
+ mov ecx, 0xc0000080
+ rdmsr
+ bts eax, 8 ; set LME
+ wrmsr
+
+ mov eax, cr0
+ bts eax, 31 ; set PG
+ mov cr0, eax ; enable paging
+
+ ;
+ ; Jump to CpuDxe for X64
+ ;
+ jmp 0x5a5a:jumpTo64BitAndLandHere
+ BITS 64
+ jumpTo64BitAndLandHere:
+ mov rax, 0xcdcdcdcdcdcdcdcd
+ jmp rax
+**/
+STARTUP_CODE mStartupCodeTemplate = {
+ { 0xeb, 0x06 }, // Jump to cli
+ 0, // GDT Limit
+ 0, // GDT Base
+ 0xfa, // cli (Clear Interrupts)
+ 0xb8, 0x0000, // mov ax, RealSegment
+ { 0x8e, 0xd8 }, // mov ds, ax
+ { 0xBB, 0x02, 0x00 }, // mov bx, Gdtr
+ { 0x3e, 0x66, 0x0f, 0x01, 0x17 }, // lgdt [ds:bx]
+ { 0x66, 0xB8 }, 0x00000023, // mov eax, cr0 value
+ { 0x0F, 0x22, 0xC0 }, // mov cr0, eax
+ { 0x66, 0xEA }, // far jmp to 32-bit flat
+ OFFSET_OF(STARTUP_CODE, MovEaxCr4),
+ LINEAR_CODE_SEL,
+ 0xB8, 0x00000640, // mov eax, cr4 value
+ { 0x0F, 0x22, 0xe0 }, // mov cr4, eax
+ { 0x66, 0xb8 }, CPU_DATA_SEL, // mov ax, FlatDataSelector
+ { 0x8e, 0xd8 }, // mov ds, ax
+ { 0x8e, 0xc0 }, // mov es, ax
+ { 0x8e, 0xe0 }, // mov fs, ax
+ { 0x8e, 0xe8 }, // mov gs, ax
+ { 0x8e, 0xd0 }, // mov ss, ax
+
+#if defined (MDE_CPU_X64)
+ //
+ // Code placeholder to enable Execute Disable Bit for X64
+ // Default is all NOP - No Operation
+ //
+ {
+ { 0x90, 0x90, 0x90, 0x90, 0x90 },
+ { 0x90, 0x90 },
+ { 0x90, 0x90, 0x90, 0x90 },
+ { 0x90, 0x90 },
+ },
+
+ 0xB8, 0x00000000, // mov eax, cr3 value
+ { 0x0F, 0x22, 0xd8 }, // mov cr3, eax
+
+ { 0x0F, 0x20, 0xE0 }, // mov eax, cr4
+ { 0x0F, 0xBA, 0xE8, 0x05 }, // bts eax, 5
+ { 0x0F, 0x22, 0xE0 }, // mov cr4, eax
+
+ { 0xB9, 0x80, 0x00, 0x00, 0xC0 }, // mov ecx, 0xc0000080
+ { 0x0F, 0x32 }, // rdmsr
+ { 0x0F, 0xBA, 0xE8, 0x08 }, // bts eax, 8
+ { 0x0F, 0x30 }, // wrmsr
+
+ { 0x0F, 0x20, 0xC0 }, // mov eax, cr0
+ { 0x0F, 0xBA, 0xE8, 0x1F }, // bts eax, 31
+ { 0x0F, 0x22, 0xC0 }, // mov cr0, eax
+
+ 0xEA, // FarJmp32LongMode
+ OFFSET_OF(STARTUP_CODE, MovEaxOrRaxCpuDxeEntry),
+ LINEAR_CODE64_SEL,
+#else
+ //
+ // Code placeholder to enable PAE Execute Disable for IA32
+ // Default is all NOP - No Operation
+ //
+ {
+ { 0x90, 0x90, 0x90, 0x90, 0x90 },
+ { 0x90, 0x90 },
+ { 0x90, 0x90, 0x90, 0x90 },
+ { 0x90, 0x90 },
+
+ 0x90, 0x90909090,
+ { 0x90, 0x90, 0x90 },
+
+ { 0x90, 0x90, 0x90 },
+ { 0x90, 0x90, 0x90, 0x90 },
+ { 0x90, 0x90, 0x90 },
+
+ { 0x90, 0x90, 0x90 },
+ { 0x90, 0x90, 0x90, 0x90 },
+ { 0x90, 0x90, 0x90 },
+ },
+#endif
+
+ //0xeb, 0xfe, // jmp $
+#if defined (MDE_CPU_X64)
+ { 0x48, 0xb8 }, 0x0, // mov rax, X64 CpuDxe MP Entry Point
+#else
+ 0xB8, 0x0, // mov eax, IA32 CpuDxe MP Entry Point
+#endif
+ { 0xff, 0xe0 }, // jmp to eax/rax (CpuDxe MP Entry Point)
+
+};
+
+volatile STARTUP_CODE *StartupCode = NULL;
+
+/**
+ The function will check if BSP Execute Disable is enabled.
+ DxeIpl may have enabled Execute Disable for BSP,
+ APs need to get the status and sync up the settings.
+
+ @retval TRUE BSP Execute Disable is enabled.
+ @retval FALSE BSP Execute Disable is not enabled.
+
+**/
+BOOLEAN
+IsBspExecuteDisableEnabled (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ UINT32 RegEdx;
+ UINT64 MsrRegisters;
+ BOOLEAN Enabled;
+
+ Enabled = FALSE;
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000001) {
+ AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+ //
+ // Cpuid 0x80000001
+ // Bit 20: Execute Disable Bit available.
+ //
+ if ((RegEdx & BIT20) != 0) {
+ MsrRegisters = AsmReadMsr64 (0xC0000080);
+ //
+ // Msr 0xC0000080
+ // Bit 11: Execute Disable Bit enable.
+ //
+ if ((MsrRegisters & BIT11) != 0) {
+ Enabled = TRUE;
+ }
+ }
+ }
+
+ return Enabled;
+}
+
+/**
+ Prepares Startup Code for APs.
+ This function prepares Startup Code for APs.
+
+ @retval EFI_SUCCESS The APs were started
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory to start APs
+
+**/
+EFI_STATUS
+PrepareAPStartupCode (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ IA32_DESCRIPTOR Gdtr;
+ EFI_PHYSICAL_ADDRESS StartAddress;
+
+ StartAddress = BASE_1MB;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (sizeof (*StartupCode)),
+ &StartAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ StartupCode = (STARTUP_CODE*)(VOID*)(UINTN) StartAddress;
+ CopyMem ((VOID*) StartupCode, &mStartupCodeTemplate, sizeof (*StartupCode));
+ StartupCode->RealSegment = (UINT16) (((UINTN) StartAddress) >> 4);
+
+ AsmReadGdtr (&Gdtr);
+ StartupCode->GdtLimit = Gdtr.Limit;
+ StartupCode->GdtBase = (UINT32) Gdtr.Base;
+
+ StartupCode->CpuDxeEntryValue = (UINTN) AsmApEntryPoint;
+
+ StartupCode->FlatJmpOffset += (UINT32) StartAddress;
+
+ if (IsBspExecuteDisableEnabled ()) {
+ CopyMem (
+ (VOID*) &StartupCode->EnableExecuteDisable,
+ &mEnableExecuteDisableCodeTemplate,
+ sizeof (ENABLE_EXECUTE_DISABLE_CODE)
+ );
+ }
+#if defined (MDE_CPU_X64)
+ StartupCode->Cr3Value = (UINT32) AsmReadCr3 ();
+ StartupCode->LongJmpOffset += (UINT32) StartAddress;
+#else
+ StartupCode->EnableExecuteDisable.Cr3Value = (UINT32) AsmReadCr3 ();
+#endif
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Free the code buffer of startup AP.
+
+**/
+VOID
+FreeApStartupCode (
+ VOID
+ )
+{
+ if (StartupCode != NULL) {
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)(VOID*) StartupCode,
+ EFI_SIZE_TO_PAGES (sizeof (*StartupCode)));
+ }
+}
+
+
+/**
+ Starts the Application Processors and directs them to jump to the
+ specified routine.
+
+ The processor jumps to this code in flat mode, but the processor's
+ stack is not initialized.
+
+ @retval EFI_SUCCESS The APs were started
+
+**/
+EFI_STATUS
+StartApsStackless (
+ VOID
+ )
+{
+ SendInitSipiSipiAllExcludingSelf ((UINT32)(UINTN)(VOID*) StartupCode);
+ //
+ // Wait for APs to arrive at the ApEntryPoint routine
+ //
+ MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Resets the Application Processor and directs it to jump to the
+ specified routine.
+
+ The processor jumps to this code in flat mode, but the processor's
+ stack is not initialized.
+
+ @param ProcessorId the AP of ProcessorId was reset
+**/
+VOID
+ResetApStackless (
+ IN UINT32 ProcessorId
+ )
+{
+ SendInitSipiSipi (ProcessorId,
+ (UINT32)(UINTN)(VOID*) StartupCode);
+}
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuDxe.c b/Core/UefiCpuPkg/CpuDxe/CpuDxe.c
new file mode 100644
index 0000000000..daf97bd4a6
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuDxe.c
@@ -0,0 +1,931 @@
+/** @file
+ CPU DXE Module.
+
+ Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "CpuDxe.h"
+#include "CpuMp.h"
+
+//
+// Global Variables
+//
+BOOLEAN InterruptState = FALSE;
+EFI_HANDLE mCpuHandle = NULL;
+BOOLEAN mIsFlushingGCD;
+UINT64 mValidMtrrAddressMask = MTRR_LIB_CACHE_VALID_ADDRESS;
+UINT64 mValidMtrrBitsMask = MTRR_LIB_MSR_VALID_MASK;
+
+FIXED_MTRR mFixedMtrrTable[] = {
+ {
+ MTRR_LIB_IA32_MTRR_FIX64K_00000,
+ 0,
+ 0x10000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX16K_80000,
+ 0x80000,
+ 0x4000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX16K_A0000,
+ 0xA0000,
+ 0x4000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_C0000,
+ 0xC0000,
+ 0x1000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_C8000,
+ 0xC8000,
+ 0x1000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_D0000,
+ 0xD0000,
+ 0x1000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_D8000,
+ 0xD8000,
+ 0x1000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_E0000,
+ 0xE0000,
+ 0x1000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_E8000,
+ 0xE8000,
+ 0x1000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_F0000,
+ 0xF0000,
+ 0x1000
+ },
+ {
+ MTRR_LIB_IA32_MTRR_FIX4K_F8000,
+ 0xF8000,
+ 0x1000
+ },
+};
+
+
+EFI_CPU_ARCH_PROTOCOL gCpu = {
+ CpuFlushCpuDataCache,
+ CpuEnableInterrupt,
+ CpuDisableInterrupt,
+ CpuGetInterruptState,
+ CpuInit,
+ CpuRegisterInterruptHandler,
+ CpuGetTimerValue,
+ CpuSetMemoryAttributes,
+ 1, // NumberOfTimers
+ 4 // DmaBufferAlignment
+};
+
+//
+// CPU Arch Protocol Functions
+//
+
+/**
+ Flush CPU data cache. If the instruction cache is fully coherent
+ with all DMA operations then function can just return EFI_SUCCESS.
+
+ @param This Protocol instance structure
+ @param Start Physical address to start flushing from.
+ @param Length Number of bytes to flush. Round up to chipset
+ granularity.
+ @param FlushType Specifies the type of flush operation to perform.
+
+ @retval EFI_SUCCESS If cache was flushed
+ @retval EFI_UNSUPPORTED If flush type is not supported.
+ @retval EFI_DEVICE_ERROR If requested range could not be flushed.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuFlushCpuDataCache (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS Start,
+ IN UINT64 Length,
+ IN EFI_CPU_FLUSH_TYPE FlushType
+ )
+{
+ if (FlushType == EfiCpuFlushTypeWriteBackInvalidate) {
+ AsmWbinvd ();
+ return EFI_SUCCESS;
+ } else if (FlushType == EfiCpuFlushTypeInvalidate) {
+ AsmInvd ();
+ return EFI_SUCCESS;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+}
+
+
+/**
+ Enables CPU interrupts.
+
+ @param This Protocol instance structure
+
+ @retval EFI_SUCCESS If interrupts were enabled in the CPU
+ @retval EFI_DEVICE_ERROR If interrupts could not be enabled on the CPU.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuEnableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ )
+{
+ EnableInterrupts ();
+
+ InterruptState = TRUE;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Disables CPU interrupts.
+
+ @param This Protocol instance structure
+
+ @retval EFI_SUCCESS If interrupts were disabled in the CPU.
+ @retval EFI_DEVICE_ERROR If interrupts could not be disabled on the CPU.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuDisableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ )
+{
+ DisableInterrupts ();
+
+ InterruptState = FALSE;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Return the state of interrupts.
+
+ @param This Protocol instance structure
+ @param State Pointer to the CPU's current interrupt state
+
+ @retval EFI_SUCCESS If interrupts were disabled in the CPU.
+ @retval EFI_INVALID_PARAMETER State is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetInterruptState (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ OUT BOOLEAN *State
+ )
+{
+ if (State == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *State = InterruptState;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Generates an INIT to the CPU.
+
+ @param This Protocol instance structure
+ @param InitType Type of CPU INIT to perform
+
+ @retval EFI_SUCCESS If CPU INIT occurred. This value should never be
+ seen.
+ @retval EFI_DEVICE_ERROR If CPU INIT failed.
+ @retval EFI_UNSUPPORTED Requested type of CPU INIT not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuInit (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_CPU_INIT_TYPE InitType
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Registers a function to be called from the CPU interrupt handler.
+
+ @param This Protocol instance structure
+ @param InterruptType Defines which interrupt to hook. IA-32
+ valid range is 0x00 through 0xFF
+ @param InterruptHandler A pointer to a function of type
+ EFI_CPU_INTERRUPT_HANDLER that is called
+ when a processor interrupt occurs. A null
+ pointer is an error condition.
+
+ @retval EFI_SUCCESS If handler installed or uninstalled.
+ @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler
+ for InterruptType was previously installed.
+ @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for
+ InterruptType was not previously installed.
+ @retval EFI_UNSUPPORTED The interrupt specified by InterruptType
+ is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuRegisterInterruptHandler (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_EXCEPTION_TYPE InterruptType,
+ IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
+ )
+{
+ return RegisterCpuInterruptHandler (InterruptType, InterruptHandler);
+}
+
+
+/**
+ Returns a timer value from one of the CPU's internal timers. There is no
+ inherent time interval between ticks but is a function of the CPU frequency.
+
+ @param This - Protocol instance structure.
+ @param TimerIndex - Specifies which CPU timer is requested.
+ @param TimerValue - Pointer to the returned timer value.
+ @param TimerPeriod - A pointer to the amount of time that passes
+ in femtoseconds (10-15) for each increment
+ of TimerValue. If TimerValue does not
+ increment at a predictable rate, then 0 is
+ returned. The amount of time that has
+ passed between two calls to GetTimerValue()
+ can be calculated with the formula
+ (TimerValue2 - TimerValue1) * TimerPeriod.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS - If the CPU timer count was returned.
+ @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers.
+ @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer.
+ @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetTimerValue (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN UINT32 TimerIndex,
+ OUT UINT64 *TimerValue,
+ OUT UINT64 *TimerPeriod OPTIONAL
+ )
+{
+ if (TimerValue == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TimerIndex != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *TimerValue = AsmReadTsc ();
+
+ if (TimerPeriod != NULL) {
+ //
+ // BugBug: Hard coded. Don't know how to do this generically
+ //
+ *TimerPeriod = 1000000000;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Implementation of SetMemoryAttributes() service of CPU Architecture Protocol.
+
+ This function modifies the attributes for the memory region specified by BaseAddress and
+ Length from their current attributes to the attributes specified by Attributes.
+
+ @param This The EFI_CPU_ARCH_PROTOCOL instance.
+ @param BaseAddress The physical address that is the start address of a memory region.
+ @param Length The size in bytes of the memory region.
+ @param Attributes The bit mask of attributes to set for the memory region.
+
+ @retval EFI_SUCCESS The attributes were set for the memory region.
+ @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
+ BaseAddress and Length cannot be modified.
+ @retval EFI_INVALID_PARAMETER Length is zero.
+ Attributes specified an illegal combination of attributes that
+ cannot be set together.
+ @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
+ the memory resource range.
+ @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
+ resource range specified by BaseAddress and Length.
+ The bit mask of attributes is not support for the memory resource
+ range specified by BaseAddress and Length.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes
+ )
+{
+ RETURN_STATUS Status;
+ MTRR_MEMORY_CACHE_TYPE CacheType;
+ EFI_STATUS MpStatus;
+ EFI_MP_SERVICES_PROTOCOL *MpService;
+ MTRR_SETTINGS MtrrSettings;
+
+ if (!IsMtrrSupported ()) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // If this function is called because GCD SetMemorySpaceAttributes () is called
+ // by RefreshGcdMemoryAttributes (), then we are just synchronzing GCD memory
+ // map with MTRR values. So there is no need to modify MTRRs, just return immediately
+ // to avoid unnecessary computing.
+ //
+ if (mIsFlushingGCD) {
+ DEBUG((EFI_D_INFO, " Flushing GCD\n"));
+ return EFI_SUCCESS;
+ }
+
+ switch (Attributes) {
+ case EFI_MEMORY_UC:
+ CacheType = CacheUncacheable;
+ break;
+
+ case EFI_MEMORY_WC:
+ CacheType = CacheWriteCombining;
+ break;
+
+ case EFI_MEMORY_WT:
+ CacheType = CacheWriteThrough;
+ break;
+
+ case EFI_MEMORY_WP:
+ CacheType = CacheWriteProtected;
+ break;
+
+ case EFI_MEMORY_WB:
+ CacheType = CacheWriteBack;
+ break;
+
+ case EFI_MEMORY_UCE:
+ case EFI_MEMORY_RP:
+ case EFI_MEMORY_XP:
+ case EFI_MEMORY_RUNTIME:
+ return EFI_UNSUPPORTED;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // call MTRR libary function
+ //
+ Status = MtrrSetMemoryAttribute (
+ BaseAddress,
+ Length,
+ CacheType
+ );
+
+ if (!RETURN_ERROR (Status)) {
+ MpStatus = gBS->LocateProtocol (
+ &gEfiMpServiceProtocolGuid,
+ NULL,
+ (VOID **)&MpService
+ );
+ //
+ // Synchronize the update with all APs
+ //
+ if (!EFI_ERROR (MpStatus)) {
+ MtrrGetAllMtrrs (&MtrrSettings);
+ MpStatus = MpService->StartupAllAPs (
+ MpService, // This
+ SetMtrrsFromBuffer, // Procedure
+ TRUE, // SingleThread
+ NULL, // WaitEvent
+ 0, // TimeoutInMicrosecsond
+ &MtrrSettings, // ProcedureArgument
+ NULL // FailedCpuList
+ );
+ ASSERT (MpStatus == EFI_SUCCESS || MpStatus == EFI_NOT_STARTED);
+ }
+ }
+ return (EFI_STATUS) Status;
+}
+
+/**
+ Initializes the valid bits mask and valid address mask for MTRRs.
+
+ This function initializes the valid bits mask and valid address mask for MTRRs.
+
+**/
+VOID
+InitializeMtrrMask (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ UINT8 PhysicalAddressBits;
+
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+
+ if (RegEax >= 0x80000008) {
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
+
+ PhysicalAddressBits = (UINT8) RegEax;
+
+ mValidMtrrBitsMask = LShiftU64 (1, PhysicalAddressBits) - 1;
+ mValidMtrrAddressMask = mValidMtrrBitsMask & 0xfffffffffffff000ULL;
+ } else {
+ mValidMtrrBitsMask = MTRR_LIB_MSR_VALID_MASK;
+ mValidMtrrAddressMask = MTRR_LIB_CACHE_VALID_ADDRESS;
+ }
+}
+
+/**
+ Gets GCD Mem Space type from MTRR Type.
+
+ This function gets GCD Mem Space type from MTRR Type.
+
+ @param MtrrAttributes MTRR memory type
+
+ @return GCD Mem Space type
+
+**/
+UINT64
+GetMemorySpaceAttributeFromMtrrType (
+ IN UINT8 MtrrAttributes
+ )
+{
+ switch (MtrrAttributes) {
+ case MTRR_CACHE_UNCACHEABLE:
+ return EFI_MEMORY_UC;
+ case MTRR_CACHE_WRITE_COMBINING:
+ return EFI_MEMORY_WC;
+ case MTRR_CACHE_WRITE_THROUGH:
+ return EFI_MEMORY_WT;
+ case MTRR_CACHE_WRITE_PROTECTED:
+ return EFI_MEMORY_WP;
+ case MTRR_CACHE_WRITE_BACK:
+ return EFI_MEMORY_WB;
+ default:
+ return 0;
+ }
+}
+
+/**
+ Searches memory descriptors covered by given memory range.
+
+ This function searches into the Gcd Memory Space for descriptors
+ (from StartIndex to EndIndex) that contains the memory range
+ specified by BaseAddress and Length.
+
+ @param MemorySpaceMap Gcd Memory Space Map as array.
+ @param NumberOfDescriptors Number of descriptors in map.
+ @param BaseAddress BaseAddress for the requested range.
+ @param Length Length for the requested range.
+ @param StartIndex Start index into the Gcd Memory Space Map.
+ @param EndIndex End index into the Gcd Memory Space Map.
+
+ @retval EFI_SUCCESS Search successfully.
+ @retval EFI_NOT_FOUND The requested descriptors does not exist.
+
+**/
+EFI_STATUS
+SearchGcdMemorySpaces (
+ IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
+ IN UINTN NumberOfDescriptors,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ OUT UINTN *StartIndex,
+ OUT UINTN *EndIndex
+ )
+{
+ UINTN Index;
+
+ *StartIndex = 0;
+ *EndIndex = 0;
+ for (Index = 0; Index < NumberOfDescriptors; Index++) {
+ if (BaseAddress >= MemorySpaceMap[Index].BaseAddress &&
+ BaseAddress < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
+ *StartIndex = Index;
+ }
+ if (BaseAddress + Length - 1 >= MemorySpaceMap[Index].BaseAddress &&
+ BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
+ *EndIndex = Index;
+ return EFI_SUCCESS;
+ }
+ }
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Sets the attributes for a specified range in Gcd Memory Space Map.
+
+ This function sets the attributes for a specified range in
+ Gcd Memory Space Map.
+
+ @param MemorySpaceMap Gcd Memory Space Map as array
+ @param NumberOfDescriptors Number of descriptors in map
+ @param BaseAddress BaseAddress for the range
+ @param Length Length for the range
+ @param Attributes Attributes to set
+
+ @retval EFI_SUCCESS Memory attributes set successfully
+ @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space
+
+**/
+EFI_STATUS
+SetGcdMemorySpaceAttributes (
+ IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
+ IN UINTN NumberOfDescriptors,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN StartIndex;
+ UINTN EndIndex;
+ EFI_PHYSICAL_ADDRESS RegionStart;
+ UINT64 RegionLength;
+
+ //
+ // Get all memory descriptors covered by the memory range
+ //
+ Status = SearchGcdMemorySpaces (
+ MemorySpaceMap,
+ NumberOfDescriptors,
+ BaseAddress,
+ Length,
+ &StartIndex,
+ &EndIndex
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Go through all related descriptors and set attributes accordingly
+ //
+ for (Index = StartIndex; Index <= EndIndex; Index++) {
+ if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
+ continue;
+ }
+ //
+ // Calculate the start and end address of the overlapping range
+ //
+ if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
+ RegionStart = BaseAddress;
+ } else {
+ RegionStart = MemorySpaceMap[Index].BaseAddress;
+ }
+ if (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
+ RegionLength = BaseAddress + Length - RegionStart;
+ } else {
+ RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
+ }
+ //
+ // Set memory attributes according to MTRR attribute and the original attribute of descriptor
+ //
+ gDS->SetMemorySpaceAttributes (
+ RegionStart,
+ RegionLength,
+ (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Refreshes the GCD Memory Space attributes according to MTRRs.
+
+ This function refreshes the GCD Memory Space attributes according to MTRRs.
+
+**/
+VOID
+RefreshGcdMemoryAttributes (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN SubIndex;
+ UINT64 RegValue;
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+ UINT64 Length;
+ UINT64 Attributes;
+ UINT64 CurrentAttributes;
+ UINT8 MtrrType;
+ UINTN NumberOfDescriptors;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
+ UINT64 DefaultAttributes;
+ VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
+ MTRR_FIXED_SETTINGS MtrrFixedSettings;
+ UINT32 FirmwareVariableMtrrCount;
+ UINT8 DefaultMemoryType;
+
+ if (!IsMtrrSupported ()) {
+ return;
+ }
+
+ FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount ();
+ ASSERT (FirmwareVariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR);
+
+ mIsFlushingGCD = TRUE;
+ MemorySpaceMap = NULL;
+
+ //
+ // Initialize the valid bits mask and valid address mask for MTRRs
+ //
+ InitializeMtrrMask ();
+
+ //
+ // Get the memory attribute of variable MTRRs
+ //
+ MtrrGetMemoryAttributeInVariableMtrr (
+ mValidMtrrBitsMask,
+ mValidMtrrAddressMask,
+ VariableMtrr
+ );
+
+ //
+ // Get the memory space map from GCD
+ //
+ Status = gDS->GetMemorySpaceMap (
+ &NumberOfDescriptors,
+ &MemorySpaceMap
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ DefaultMemoryType = (UINT8) MtrrGetDefaultMemoryType ();
+ DefaultAttributes = GetMemorySpaceAttributeFromMtrrType (DefaultMemoryType);
+
+ //
+ // Set default attributes to all spaces.
+ //
+ for (Index = 0; Index < NumberOfDescriptors; Index++) {
+ if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
+ continue;
+ }
+ gDS->SetMemorySpaceAttributes (
+ MemorySpaceMap[Index].BaseAddress,
+ MemorySpaceMap[Index].Length,
+ (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) |
+ (MemorySpaceMap[Index].Capabilities & DefaultAttributes)
+ );
+ }
+
+ //
+ // Go for variable MTRRs with WB attribute
+ //
+ for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {
+ if (VariableMtrr[Index].Valid &&
+ VariableMtrr[Index].Type == MTRR_CACHE_WRITE_BACK) {
+ SetGcdMemorySpaceAttributes (
+ MemorySpaceMap,
+ NumberOfDescriptors,
+ VariableMtrr[Index].BaseAddress,
+ VariableMtrr[Index].Length,
+ EFI_MEMORY_WB
+ );
+ }
+ }
+
+ //
+ // Go for variable MTRRs with the attribute except for WB and UC attributes
+ //
+ for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {
+ if (VariableMtrr[Index].Valid &&
+ VariableMtrr[Index].Type != MTRR_CACHE_WRITE_BACK &&
+ VariableMtrr[Index].Type != MTRR_CACHE_UNCACHEABLE) {
+ Attributes = GetMemorySpaceAttributeFromMtrrType ((UINT8) VariableMtrr[Index].Type);
+ SetGcdMemorySpaceAttributes (
+ MemorySpaceMap,
+ NumberOfDescriptors,
+ VariableMtrr[Index].BaseAddress,
+ VariableMtrr[Index].Length,
+ Attributes
+ );
+ }
+ }
+
+ //
+ // Go for variable MTRRs with UC attribute
+ //
+ for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {
+ if (VariableMtrr[Index].Valid &&
+ VariableMtrr[Index].Type == MTRR_CACHE_UNCACHEABLE) {
+ SetGcdMemorySpaceAttributes (
+ MemorySpaceMap,
+ NumberOfDescriptors,
+ VariableMtrr[Index].BaseAddress,
+ VariableMtrr[Index].Length,
+ EFI_MEMORY_UC
+ );
+ }
+ }
+
+ //
+ // Go for fixed MTRRs
+ //
+ Attributes = 0;
+ BaseAddress = 0;
+ Length = 0;
+ MtrrGetFixedMtrr (&MtrrFixedSettings);
+ for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
+ RegValue = MtrrFixedSettings.Mtrr[Index];
+ //
+ // Check for continuous fixed MTRR sections
+ //
+ for (SubIndex = 0; SubIndex < 8; SubIndex++) {
+ MtrrType = (UINT8) RShiftU64 (RegValue, SubIndex * 8);
+ CurrentAttributes = GetMemorySpaceAttributeFromMtrrType (MtrrType);
+ if (Length == 0) {
+ //
+ // A new MTRR attribute begins
+ //
+ Attributes = CurrentAttributes;
+ } else {
+ //
+ // If fixed MTRR attribute changed, then set memory attribute for previous atrribute
+ //
+ if (CurrentAttributes != Attributes) {
+ SetGcdMemorySpaceAttributes (
+ MemorySpaceMap,
+ NumberOfDescriptors,
+ BaseAddress,
+ Length,
+ Attributes
+ );
+ BaseAddress = mFixedMtrrTable[Index].BaseAddress + mFixedMtrrTable[Index].Length * SubIndex;
+ Length = 0;
+ Attributes = CurrentAttributes;
+ }
+ }
+ Length += mFixedMtrrTable[Index].Length;
+ }
+ }
+ //
+ // Handle the last fixed MTRR region
+ //
+ SetGcdMemorySpaceAttributes (
+ MemorySpaceMap,
+ NumberOfDescriptors,
+ BaseAddress,
+ Length,
+ Attributes
+ );
+
+ //
+ // Free memory space map allocated by GCD service GetMemorySpaceMap ()
+ //
+ if (MemorySpaceMap != NULL) {
+ FreePool (MemorySpaceMap);
+ }
+
+ mIsFlushingGCD = FALSE;
+}
+
+/**
+ Initialize Interrupt Descriptor Table for interrupt handling.
+
+**/
+VOID
+InitInterruptDescriptorTable (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_VECTOR_HANDOFF_INFO *VectorInfoList;
+ EFI_VECTOR_HANDOFF_INFO *VectorInfo;
+
+ VectorInfo = NULL;
+ Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **) &VectorInfoList);
+ if (Status == EFI_SUCCESS && VectorInfoList != NULL) {
+ VectorInfo = VectorInfoList;
+ }
+ Status = InitializeCpuInterruptHandlers (VectorInfo);
+ ASSERT_EFI_ERROR (Status);
+}
+
+
+/**
+ Callback function for idle events.
+
+ @param Event Event whose notification function is being invoked.
+ @param Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+IdleLoopEventCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ CpuSleep ();
+}
+
+
+/**
+ Initialize the state information for the CPU Architectural Protocol.
+
+ @param ImageHandle Image handle this driver.
+ @param SystemTable Pointer to the System Table.
+
+ @retval EFI_SUCCESS Thread can be successfully created
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
+ @retval EFI_DEVICE_ERROR Cannot create the thread
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeCpu (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT IdleLoopEvent;
+
+ InitializeFloatingPointUnits ();
+
+ //
+ // Make sure interrupts are disabled
+ //
+ DisableInterrupts ();
+
+ //
+ // Init GDT for DXE
+ //
+ InitGlobalDescriptorTable ();
+
+ //
+ // Setup IDT pointer, IDT and interrupt entry points
+ //
+ InitInterruptDescriptorTable ();
+
+ //
+ // Enable the local APIC for Virtual Wire Mode.
+ //
+ ProgramVirtualWireMode ();
+
+ //
+ // Install CPU Architectural Protocol
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &mCpuHandle,
+ &gEfiCpuArchProtocolGuid, &gCpu,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Refresh GCD memory space map according to MTRR value.
+ //
+ RefreshGcdMemoryAttributes ();
+
+ //
+ // Setup a callback for idle events
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IdleLoopEventCallback,
+ NULL,
+ &gIdleLoopEventGuid,
+ &IdleLoopEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ InitializeMpSupport ();
+
+ return Status;
+}
+
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuDxe.h b/Core/UefiCpuPkg/CpuDxe/CpuDxe.h
new file mode 100644
index 0000000000..2aef626cd5
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuDxe.h
@@ -0,0 +1,256 @@
+/** @file
+ CPU DXE Module.
+
+ Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _CPU_DXE_H_
+#define _CPU_DXE_H_
+
+#include <PiDxe.h>
+
+#include <Protocol/Cpu.h>
+
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/CpuLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MtrrLib.h>
+#include <Library/LocalApicLib.h>
+#include <Library/UefiCpuLib.h>
+#include <Library/UefiLib.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Library/TimerLib.h>
+#include <Guid/IdleLoopEvent.h>
+#include <Guid/VectorHandoffTable.h>
+
+#define EFI_MEMORY_CACHETYPE_MASK (EFI_MEMORY_UC | \
+ EFI_MEMORY_WC | \
+ EFI_MEMORY_WT | \
+ EFI_MEMORY_WB | \
+ EFI_MEMORY_UCE \
+ )
+
+
+/**
+ Flush CPU data cache. If the instruction cache is fully coherent
+ with all DMA operations then function can just return EFI_SUCCESS.
+
+ @param This Protocol instance structure
+ @param Start Physical address to start flushing from.
+ @param Length Number of bytes to flush. Round up to chipset
+ granularity.
+ @param FlushType Specifies the type of flush operation to perform.
+
+ @retval EFI_SUCCESS If cache was flushed
+ @retval EFI_UNSUPPORTED If flush type is not supported.
+ @retval EFI_DEVICE_ERROR If requested range could not be flushed.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuFlushCpuDataCache (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS Start,
+ IN UINT64 Length,
+ IN EFI_CPU_FLUSH_TYPE FlushType
+ );
+
+/**
+ Enables CPU interrupts.
+
+ @param This Protocol instance structure
+
+ @retval EFI_SUCCESS If interrupts were enabled in the CPU
+ @retval EFI_DEVICE_ERROR If interrupts could not be enabled on the CPU.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuEnableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ );
+
+/**
+ Disables CPU interrupts.
+
+ @param This Protocol instance structure
+
+ @retval EFI_SUCCESS If interrupts were disabled in the CPU.
+ @retval EFI_DEVICE_ERROR If interrupts could not be disabled on the CPU.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuDisableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ );
+
+/**
+ Return the state of interrupts.
+
+ @param This Protocol instance structure
+ @param State Pointer to the CPU's current interrupt state
+
+ @retval EFI_SUCCESS If interrupts were disabled in the CPU.
+ @retval EFI_INVALID_PARAMETER State is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetInterruptState (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ OUT BOOLEAN *State
+ );
+
+/**
+ Generates an INIT to the CPU.
+
+ @param This Protocol instance structure
+ @param InitType Type of CPU INIT to perform
+
+ @retval EFI_SUCCESS If CPU INIT occurred. This value should never be
+ seen.
+ @retval EFI_DEVICE_ERROR If CPU INIT failed.
+ @retval EFI_UNSUPPORTED Requested type of CPU INIT not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuInit (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_CPU_INIT_TYPE InitType
+ );
+
+/**
+ Registers a function to be called from the CPU interrupt handler.
+
+ @param This Protocol instance structure
+ @param InterruptType Defines which interrupt to hook. IA-32
+ valid range is 0x00 through 0xFF
+ @param InterruptHandler A pointer to a function of type
+ EFI_CPU_INTERRUPT_HANDLER that is called
+ when a processor interrupt occurs. A null
+ pointer is an error condition.
+
+ @retval EFI_SUCCESS If handler installed or uninstalled.
+ @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler
+ for InterruptType was previously installed.
+ @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for
+ InterruptType was not previously installed.
+ @retval EFI_UNSUPPORTED The interrupt specified by InterruptType
+ is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuRegisterInterruptHandler (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_EXCEPTION_TYPE InterruptType,
+ IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
+ );
+
+/**
+ Returns a timer value from one of the CPU's internal timers. There is no
+ inherent time interval between ticks but is a function of the CPU frequency.
+
+ @param This - Protocol instance structure.
+ @param TimerIndex - Specifies which CPU timer is requested.
+ @param TimerValue - Pointer to the returned timer value.
+ @param TimerPeriod - A pointer to the amount of time that passes
+ in femtoseconds (10-15) for each increment
+ of TimerValue. If TimerValue does not
+ increment at a predictable rate, then 0 is
+ returned. The amount of time that has
+ passed between two calls to GetTimerValue()
+ can be calculated with the formula
+ (TimerValue2 - TimerValue1) * TimerPeriod.
+ This parameter is optional and may be NULL.
+
+ @retval EFI_SUCCESS - If the CPU timer count was returned.
+ @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers.
+ @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer.
+ @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetTimerValue (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN UINT32 TimerIndex,
+ OUT UINT64 *TimerValue,
+ OUT UINT64 *TimerPeriod OPTIONAL
+ );
+
+/**
+ Set memory cacheability attributes for given range of memeory.
+
+ @param This Protocol instance structure
+ @param BaseAddress Specifies the start address of the
+ memory range
+ @param Length Specifies the length of the memory range
+ @param Attributes The memory cacheability for the memory range
+
+ @retval EFI_SUCCESS If the cacheability of that memory range is
+ set successfully
+ @retval EFI_UNSUPPORTED If the desired operation cannot be done
+ @retval EFI_INVALID_PARAMETER The input parameter is not correct,
+ such as Length = 0
+
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes
+ );
+
+/**
+ Initialize Global Descriptor Table.
+
+**/
+VOID
+InitGlobalDescriptorTable (
+ VOID
+ );
+
+/**
+ Sets the code selector (CS).
+
+ @param Selector Value of code selector.
+
+**/
+VOID
+EFIAPI
+SetCodeSelector (
+ UINT16 Selector
+ );
+
+/**
+ Sets the data selector (DS).
+
+ @param Selector Value of data selector.
+
+**/
+VOID
+EFIAPI
+SetDataSelectors (
+ UINT16 Selector
+ );
+
+#endif
+
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuDxe.inf b/Core/UefiCpuPkg/CpuDxe/CpuDxe.inf
new file mode 100644
index 0000000000..9db5303ae9
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuDxe.inf
@@ -0,0 +1,95 @@
+## @file
+# Simple CPU driver installs CPU Architecture Protocol.
+#
+# Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = CpuDxe
+ MODULE_UNI_FILE = CpuDxe.uni
+ FILE_GUID = 1A1E4886-9517-440e-9FDE-3BE44CEE2136
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = InitializeCpu
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ CpuLib
+ DebugLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ MtrrLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ LocalApicLib
+ UefiCpuLib
+ UefiLib
+ CpuExceptionHandlerLib
+ TimerLib
+ SynchronizationLib
+ HobLib
+ ReportStatusCodeLib
+
+[Sources]
+ ApStartup.c
+ CpuDxe.c
+ CpuDxe.h
+ CpuGdt.c
+ CpuGdt.h
+ CpuMp.c
+ CpuMp.h
+
+[Sources.IA32]
+ Ia32/CpuAsm.asm | MSFT
+ Ia32/CpuAsm.asm | INTEL
+ Ia32/CpuAsm.S | GCC
+ Ia32/MpAsm.asm | MSFT
+ Ia32/MpAsm.asm | INTEL
+ Ia32/MpAsm.nasm | GCC
+
+[Sources.X64]
+ X64/CpuAsm.asm | MSFT
+ X64/CpuAsm.asm | INTEL
+ X64/CpuAsm.S | GCC
+ X64/MpAsm.asm | MSFT
+ X64/MpAsm.asm | INTEL
+ X64/MpAsm.nasm | GCC
+
+[Protocols]
+ gEfiCpuArchProtocolGuid ## PRODUCES
+ gEfiMpServiceProtocolGuid ## SOMETIMES_PRODUCES
+
+[Guids]
+ gIdleLoopEventGuid ## CONSUMES ## Event
+ gEfiVectorHandoffTableGuid ## SOMETIMES_CONSUMES ## SystemTable
+
+[Ppis]
+ gEfiSecPlatformInformation2PpiGuid ## UNDEFINED # HOB
+ gEfiSecPlatformInformationPpiGuid ## UNDEFINED # HOB
+
+[Pcd]
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds ## CONSUMES
+
+[Depex]
+ TRUE
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ CpuDxeExtra.uni
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuDxe.uni b/Core/UefiCpuPkg/CpuDxe/CpuDxe.uni
new file mode 100644
index 0000000000..5ac78100fb
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuDxe.uni
@@ -0,0 +1,22 @@
+// /** @file
+// Simple CPU driver installs CPU Architecture Protocol.
+//
+// Simple CPU driver installs CPU Architecture Protocol.
+//
+// Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Installs CPU Architecture Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Simple CPU driver installs CPU Architecture Protocol."
+
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuDxeExtra.uni b/Core/UefiCpuPkg/CpuDxe/CpuDxeExtra.uni
new file mode 100644
index 0000000000..fbfc2e1fb2
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// CpuDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU Architectural DXE Driver"
+
+
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuGdt.c b/Core/UefiCpuPkg/CpuDxe/CpuGdt.c
new file mode 100644
index 0000000000..9ef2fdfefb
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuGdt.c
@@ -0,0 +1,161 @@
+/** @file
+ C based implemention of IA32 interrupt handling only
+ requiring a minimal assembly interrupt entry point.
+
+ Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "CpuDxe.h"
+#include "CpuGdt.h"
+
+//
+// Global descriptor table (GDT) Template
+//
+STATIC GDT_ENTRIES GdtTemplate = {
+ //
+ // NULL_SEL
+ //
+ {
+ 0x0, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x0, // type
+ 0x0, // limit 19:16, flags
+ 0x0, // base 31:24
+ },
+ //
+ // LINEAR_SEL
+ //
+ {
+ 0x0FFFF, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x092, // present, ring 0, data, read/write
+ 0x0CF, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // LINEAR_CODE_SEL
+ //
+ {
+ 0x0FFFF, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x09F, // present, ring 0, code, execute/read, conforming, accessed
+ 0x0CF, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // SYS_DATA_SEL
+ //
+ {
+ 0x0FFFF, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x093, // present, ring 0, data, read/write, accessed
+ 0x0CF, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // SYS_CODE_SEL
+ //
+ {
+ 0x0FFFF, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x09A, // present, ring 0, code, execute/read
+ 0x0CF, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // SPARE4_SEL
+ //
+ {
+ 0x0, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x0, // type
+ 0x0, // limit 19:16, flags
+ 0x0, // base 31:24
+ },
+ //
+ // LINEAR_DATA64_SEL
+ //
+ {
+ 0x0FFFF, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x092, // present, ring 0, data, read/write
+ 0x0CF, // page-granular, 32-bit
+ 0x0,
+ },
+ //
+ // LINEAR_CODE64_SEL
+ //
+ {
+ 0x0FFFF, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x09A, // present, ring 0, code, execute/read
+ 0x0AF, // page-granular, 64-bit code
+ 0x0, // base (high)
+ },
+ //
+ // SPARE5_SEL
+ //
+ {
+ 0x0, // limit 15:0
+ 0x0, // base 15:0
+ 0x0, // base 23:16
+ 0x0, // type
+ 0x0, // limit 19:16, flags
+ 0x0, // base 31:24
+ },
+};
+
+/**
+ Initialize Global Descriptor Table.
+
+**/
+VOID
+InitGlobalDescriptorTable (
+ VOID
+ )
+{
+ GDT_ENTRIES *gdt;
+ IA32_DESCRIPTOR gdtPtr;
+
+ //
+ // Allocate Runtime Data for the GDT
+ //
+ gdt = AllocateRuntimePool (sizeof (GdtTemplate) + 8);
+ ASSERT (gdt != NULL);
+ gdt = ALIGN_POINTER (gdt, 8);
+
+ //
+ // Initialize all GDT entries
+ //
+ CopyMem (gdt, &GdtTemplate, sizeof (GdtTemplate));
+
+ //
+ // Write GDT register
+ //
+ gdtPtr.Base = (UINT32)(UINTN)(VOID*) gdt;
+ gdtPtr.Limit = (UINT16) (sizeof (GdtTemplate) - 1);
+ AsmWriteGdtr (&gdtPtr);
+
+ //
+ // Update selector (segment) registers base on new GDT
+ //
+ SetCodeSelector ((UINT16)CPU_CODE_SEL);
+ SetDataSelectors ((UINT16)CPU_DATA_SEL);
+}
+
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuGdt.h b/Core/UefiCpuPkg/CpuDxe/CpuGdt.h
new file mode 100644
index 0000000000..2a00751602
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuGdt.h
@@ -0,0 +1,74 @@
+/** @file
+ C based implemention of IA32 interrupt handling only
+ requiring a minimal assembly interrupt entry point.
+
+ Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _CPU_GDT_H_
+#define _CPU_GDT_H_
+
+//
+// Local structure definitions
+//
+
+#pragma pack (1)
+
+//
+// Global Descriptor Entry structures
+//
+
+typedef struct _GDT_ENTRY {
+ UINT16 Limit15_0;
+ UINT16 Base15_0;
+ UINT8 Base23_16;
+ UINT8 Type;
+ UINT8 Limit19_16_and_flags;
+ UINT8 Base31_24;
+} GDT_ENTRY;
+
+typedef
+struct _GDT_ENTRIES {
+ GDT_ENTRY Null;
+ GDT_ENTRY Linear;
+ GDT_ENTRY LinearCode;
+ GDT_ENTRY SysData;
+ GDT_ENTRY SysCode;
+ GDT_ENTRY Spare4;
+ GDT_ENTRY LinearData64;
+ GDT_ENTRY LinearCode64;
+ GDT_ENTRY Spare5;
+} GDT_ENTRIES;
+
+#pragma pack ()
+
+#define NULL_SEL OFFSET_OF (GDT_ENTRIES, Null)
+#define LINEAR_SEL OFFSET_OF (GDT_ENTRIES, Linear)
+#define LINEAR_CODE_SEL OFFSET_OF (GDT_ENTRIES, LinearCode)
+#define SYS_DATA_SEL OFFSET_OF (GDT_ENTRIES, SysData)
+#define SYS_CODE_SEL OFFSET_OF (GDT_ENTRIES, SysCode)
+#define SPARE4_SEL OFFSET_OF (GDT_ENTRIES, Spare4)
+#define LINEAR_DATA64_SEL OFFSET_OF (GDT_ENTRIES, LinearData64)
+#define LINEAR_CODE64_SEL OFFSET_OF (GDT_ENTRIES, LinearCode64)
+#define SPARE5_SEL OFFSET_OF (GDT_ENTRIES, Spare5)
+
+#if defined (MDE_CPU_IA32)
+#define CPU_CODE_SEL LINEAR_CODE_SEL
+#define CPU_DATA_SEL LINEAR_SEL
+#elif defined (MDE_CPU_X64)
+#define CPU_CODE_SEL LINEAR_CODE64_SEL
+#define CPU_DATA_SEL LINEAR_DATA64_SEL
+#else
+#error CPU type not supported for CPU GDT initialization!
+#endif
+
+#endif // _CPU_GDT_H_
+
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuMp.c b/Core/UefiCpuPkg/CpuDxe/CpuMp.c
new file mode 100644
index 0000000000..98fdfdf5e0
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuMp.c
@@ -0,0 +1,1806 @@
+/** @file
+ CPU DXE Module.
+
+ Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "CpuDxe.h"
+#include "CpuMp.h"
+
+UINTN gMaxLogicalProcessorNumber;
+UINTN gApStackSize;
+UINTN gPollInterval = 100; // 100 microseconds
+
+MP_SYSTEM_DATA mMpSystemData;
+EFI_HANDLE mMpServiceHandle = NULL;
+EFI_EVENT mExitBootServicesEvent = (EFI_EVENT)NULL;
+
+VOID *mCommonStack = 0;
+VOID *mTopOfApCommonStack = 0;
+VOID *mApStackStart = 0;
+
+volatile BOOLEAN mAPsAlreadyInitFinished = FALSE;
+volatile BOOLEAN mStopCheckAllAPsStatus = TRUE;
+
+EFI_MP_SERVICES_PROTOCOL mMpServicesTemplate = {
+ GetNumberOfProcessors,
+ GetProcessorInfo,
+ StartupAllAPs,
+ StartupThisAP,
+ SwitchBSP,
+ EnableDisableAP,
+ WhoAmI
+};
+
+/**
+ Get Mp Service Lock.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified processor
+
+**/
+VOID
+GetMpSpinLock (
+ IN CPU_DATA_BLOCK *CpuData
+ )
+{
+ while (!AcquireSpinLockOrFail (&CpuData->CpuDataLock)) {
+ CpuPause ();
+ }
+ CpuData->LockSelf = GetApicId ();
+}
+
+/**
+ Release Mp Service Lock.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified processor
+
+**/
+VOID
+ReleaseMpSpinLock (
+ IN CPU_DATA_BLOCK *CpuData
+ )
+{
+ ReleaseSpinLock (&CpuData->CpuDataLock);
+}
+
+/**
+ Check whether caller processor is BSP.
+
+ @retval TRUE the caller is BSP
+ @retval FALSE the caller is AP
+
+**/
+BOOLEAN
+IsBSP (
+ VOID
+ )
+{
+ UINTN CpuIndex;
+ CPU_DATA_BLOCK *CpuData;
+
+ CpuData = NULL;
+
+ WhoAmI (&mMpServicesTemplate, &CpuIndex);
+ CpuData = &mMpSystemData.CpuDatas[CpuIndex];
+
+ return CpuData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT ? TRUE : FALSE;
+}
+
+/**
+ Get the Application Processors state.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified AP
+
+ @retval CPU_STATE the AP status
+
+**/
+CPU_STATE
+GetApState (
+ IN CPU_DATA_BLOCK *CpuData
+ )
+{
+ CPU_STATE State;
+
+ GetMpSpinLock (CpuData);
+ State = CpuData->State;
+ ReleaseMpSpinLock (CpuData);
+
+ return State;
+}
+
+/**
+ Set the Application Processors state.
+
+ @param CpuData The pointer to CPU_DATA_BLOCK of specified AP
+ @param State The AP status
+
+**/
+VOID
+SetApState (
+ IN CPU_DATA_BLOCK *CpuData,
+ IN CPU_STATE State
+ )
+{
+ GetMpSpinLock (CpuData);
+ CpuData->State = State;
+ ReleaseMpSpinLock (CpuData);
+}
+
+/**
+ Set the Application Processor prepare to run a function specified
+ by Params.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified AP
+ @param Procedure A pointer to the function to be run on enabled APs of the system
+ @param ProcedureArgument Pointer to the optional parameter of the assigned function
+
+**/
+VOID
+SetApProcedure (
+ IN CPU_DATA_BLOCK *CpuData,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument
+ )
+{
+ GetMpSpinLock (CpuData);
+ CpuData->Parameter = ProcedureArgument;
+ CpuData->Procedure = Procedure;
+ ReleaseMpSpinLock (CpuData);
+}
+
+/**
+ Check the Application Processors Status whether contains the Flags.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified AP
+ @param Flags the StatusFlag describing in EFI_PROCESSOR_INFORMATION
+
+ @retval TRUE the AP status includes the StatusFlag
+ @retval FALSE the AP status excludes the StatusFlag
+
+**/
+BOOLEAN
+TestCpuStatusFlag (
+ IN CPU_DATA_BLOCK *CpuData,
+ IN UINT32 Flags
+ )
+{
+ UINT32 Ret;
+
+ GetMpSpinLock (CpuData);
+ Ret = CpuData->Info.StatusFlag & Flags;
+ ReleaseMpSpinLock (CpuData);
+
+ return (BOOLEAN) (Ret != 0);
+}
+
+/**
+ Bitwise-Or of the Application Processors Status with the Flags.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified AP
+ @param Flags the StatusFlag describing in EFI_PROCESSOR_INFORMATION
+
+**/
+VOID
+CpuStatusFlagOr (
+ IN CPU_DATA_BLOCK *CpuData,
+ IN UINT32 Flags
+ )
+{
+ GetMpSpinLock (CpuData);
+ CpuData->Info.StatusFlag |= Flags;
+ ReleaseMpSpinLock (CpuData);
+}
+
+/**
+ Bitwise-AndNot of the Application Processors Status with the Flags.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified AP
+ @param Flags the StatusFlag describing in EFI_PROCESSOR_INFORMATION
+
+**/
+VOID
+CpuStatusFlagAndNot (
+ IN CPU_DATA_BLOCK *CpuData,
+ IN UINT32 Flags
+ )
+{
+ GetMpSpinLock (CpuData);
+ CpuData->Info.StatusFlag &= ~Flags;
+ ReleaseMpSpinLock (CpuData);
+}
+
+/**
+ Searches for the next blocking AP.
+
+ Search for the next AP that is put in blocking state by single-threaded StartupAllAPs().
+
+ @param NextNumber Pointer to the processor number of the next blocking AP.
+
+ @retval EFI_SUCCESS The next blocking AP has been found.
+ @retval EFI_NOT_FOUND No blocking AP exists.
+
+**/
+EFI_STATUS
+GetNextBlockedNumber (
+ OUT UINTN *NextNumber
+ )
+{
+ UINTN Number;
+ CPU_STATE CpuState;
+ CPU_DATA_BLOCK *CpuData;
+
+ for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
+ CpuData = &mMpSystemData.CpuDatas[Number];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+ //
+ // Skip BSP
+ //
+ continue;
+ }
+
+ CpuState = GetApState (CpuData);
+ if (CpuState == CpuStateBlocked) {
+ *NextNumber = Number;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Check if the APs state are finished, and update them to idle state
+ by StartupAllAPs().
+
+**/
+VOID
+CheckAndUpdateAllAPsToIdleState (
+ VOID
+ )
+{
+ UINTN ProcessorNumber;
+ UINTN NextNumber;
+ CPU_DATA_BLOCK *CpuData;
+ EFI_STATUS Status;
+ CPU_STATE CpuState;
+
+ for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {
+ CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+ //
+ // Skip BSP
+ //
+ continue;
+ }
+
+ if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
+ //
+ // Skip Disabled processors
+ //
+ continue;
+ }
+
+ CpuState = GetApState (CpuData);
+ if (CpuState == CpuStateFinished) {
+ mMpSystemData.FinishCount++;
+ if (mMpSystemData.SingleThread) {
+ Status = GetNextBlockedNumber (&NextNumber);
+ if (!EFI_ERROR (Status)) {
+ SetApState (&mMpSystemData.CpuDatas[NextNumber], CpuStateReady);
+ SetApProcedure (&mMpSystemData.CpuDatas[NextNumber],
+ mMpSystemData.Procedure,
+ mMpSystemData.ProcedureArgument);
+ //
+ // If this AP previous state is blocked, we should
+ // wake up this AP by sent a SIPI. and avoid
+ // re-involve the sleeping state. we must call
+ // SetApProcedure() first.
+ //
+ ResetProcessorToIdleState (&mMpSystemData.CpuDatas[NextNumber]);
+ }
+ }
+ SetApState (CpuData, CpuStateIdle);
+ }
+ }
+}
+
+/**
+ Check if all APs are in state CpuStateSleeping.
+
+ Return TRUE if all APs are in the CpuStateSleeping state. Do not
+ check the state of the BSP or any disabled APs.
+
+ @retval TRUE All APs are in CpuStateSleeping state.
+ @retval FALSE One or more APs are not in CpuStateSleeping state.
+
+**/
+BOOLEAN
+CheckAllAPsSleeping (
+ VOID
+ )
+{
+ UINTN ProcessorNumber;
+ CPU_DATA_BLOCK *CpuData;
+
+ for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {
+ CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+ //
+ // Skip BSP
+ //
+ continue;
+ }
+
+ if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
+ //
+ // Skip Disabled processors
+ //
+ continue;
+ }
+
+ if (GetApState (CpuData) != CpuStateSleeping) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ If the timeout expires before all APs returns from Procedure,
+ we should forcibly terminate the executing AP and fill FailedList back
+ by StartupAllAPs().
+
+**/
+VOID
+ResetAllFailedAPs (
+ VOID
+ )
+{
+ CPU_DATA_BLOCK *CpuData;
+ UINTN Number;
+ CPU_STATE CpuState;
+
+ if (mMpSystemData.FailedList != NULL) {
+ *mMpSystemData.FailedList = AllocatePool ((mMpSystemData.StartCount - mMpSystemData.FinishCount + 1) * sizeof(UINTN));
+ ASSERT (*mMpSystemData.FailedList != NULL);
+ }
+
+ for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
+ CpuData = &mMpSystemData.CpuDatas[Number];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+ //
+ // Skip BSP
+ //
+ continue;
+ }
+
+ if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
+ //
+ // Skip Disabled processors
+ //
+ continue;
+ }
+
+ CpuState = GetApState (CpuData);
+ if (CpuState != CpuStateIdle &&
+ CpuState != CpuStateSleeping) {
+ if (mMpSystemData.FailedList != NULL) {
+ (*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex++] = Number;
+ }
+ ResetProcessorToIdleState (CpuData);
+ }
+ }
+
+ if (mMpSystemData.FailedList != NULL) {
+ (*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex] = END_OF_CPU_LIST;
+ }
+}
+
+/**
+ This service retrieves the number of logical processor in the platform
+ and the number of those logical processors that are enabled on this boot.
+ This service may only be called from the BSP.
+
+ This function is used to retrieve the following information:
+ - The number of logical processors that are present in the system.
+ - The number of enabled logical processors in the system at the instant
+ this call is made.
+
+ Because MP Service Protocol provides services to enable and disable processors
+ dynamically, the number of enabled logical processors may vary during the
+ course of a boot session.
+
+ If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
+ If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
+ EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
+ is returned in NumberOfProcessors, the number of currently enabled processor
+ is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL.
+ @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+GetNumberOfProcessors (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *NumberOfProcessors,
+ OUT UINTN *NumberOfEnabledProcessors
+ )
+{
+ if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ *NumberOfProcessors = mMpSystemData.NumberOfProcessors;
+ *NumberOfEnabledProcessors = mMpSystemData.NumberOfEnabledProcessors;
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ This service retrieves detailed MP-related information about any processor
+ on the platform. Note the following:
+ - The processor information may change during the course of a boot session.
+ - The information presented here is entirely MP related.
+
+ Information regarding the number of caches and their sizes, frequency of operation,
+ slot numbers is all considered platform-related information and is not provided
+ by this service.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProcessorInfo (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer
+ )
+{
+ if (ProcessorInfoBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ CopyMem (ProcessorInfoBuffer, &mMpSystemData.CpuDatas[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));
+ return EFI_SUCCESS;
+}
+
+/**
+ This service executes a caller provided function on all enabled APs. APs can
+ run either simultaneously or one at a time in sequence. This service supports
+ both blocking and non-blocking requests. The non-blocking requests use EFI
+ events so the BSP can detect when the APs have finished. This service may only
+ be called from the BSP.
+
+ This function is used to dispatch all the enabled APs to the function specified
+ by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned
+ immediately and Procedure is not started on any AP.
+
+ If SingleThread is TRUE, all the enabled APs execute the function specified by
+ Procedure one by one, in ascending order of processor handle number. Otherwise,
+ all the enabled APs execute the function specified by Procedure simultaneously.
+
+ If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
+ APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
+ mode, and the BSP returns from this service without waiting for APs. If a
+ non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+ is signaled, then EFI_UNSUPPORTED must be returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before all APs return
+ from Procedure, then Procedure on the failed APs is terminated. All enabled APs
+ are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
+ content points to the list of processor handle numbers in which Procedure was
+ terminated.
+
+ Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ to make sure that the nature of the code that is executed on the BSP and the
+ dispatched APs is well controlled. The MP Services Protocol does not guarantee
+ that the Procedure function is MP-safe. Hence, the tasks that can be run in
+ parallel are limited to certain independent tasks and well-controlled exclusive
+ code. EFI services and protocols may not be called by APs unless otherwise
+ specified.
+
+ In blocking execution mode, BSP waits until all APs finish or
+ TimeoutInMicroseconds expires.
+
+ In non-blocking execution mode, BSP is freed to return to the caller and then
+ proceed to the next task without having to wait for APs. The following
+ sequence needs to occur in a non-blocking execution mode:
+
+ -# The caller that intends to use this MP Services Protocol in non-blocking
+ mode creates WaitEvent by calling the EFI CreateEvent() service. The caller
+ invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
+ is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
+ the function specified by Procedure to be started on all the enabled APs,
+ and releases the BSP to continue with other tasks.
+ -# The caller can use the CheckEvent() and WaitForEvent() services to check
+ the state of the WaitEvent created in step 1.
+ -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
+ Service signals WaitEvent by calling the EFI SignalEvent() function. If
+ FailedCpuList is not NULL, its content is available when WaitEvent is
+ signaled. If all APs returned from Procedure prior to the timeout, then
+ FailedCpuList is set to NULL. If not all APs return from Procedure before
+ the timeout, then FailedCpuList is filled in with the list of the failed
+ APs. The buffer is allocated by MP Service Protocol using AllocatePool().
+ It is the caller's responsibility to free the buffer with FreePool() service.
+ -# This invocation of SignalEvent() function informs the caller that invoked
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
+ the specified task or a timeout occurred. The contents of FailedCpuList
+ can be examined to determine which APs did not complete the specified task
+ prior to the timeout.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure, or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ all APs return from Procedure, then Procedure
+ on the failed APs is terminated. All enabled
+ APs are available for next function assigned
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
+ if all APs finish successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of the buffer
+ holding handle numbers of the failed APs.
+ The buffer is allocated by MP Service Protocol,
+ and it's the caller's responsibility to
+ free the buffer with FreePool() service.
+ In blocking mode, it is ready for consumption
+ when the call returns. In non-blocking mode,
+ it is ready when WaitEvent is signaled. The
+ list of failed CPU is terminated by
+ END_OF_CPU_LIST.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled APs.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR Caller processor is AP.
+ @retval EFI_NOT_STARTED No enabled APs exist in the system.
+ @retval EFI_NOT_READY Any enabled APs are busy.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ all enabled APs have finished.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+StartupAllAPs (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ CPU_DATA_BLOCK *CpuData;
+ UINTN Number;
+ CPU_STATE APInitialState;
+ CPU_STATE CpuState;
+
+ CpuData = NULL;
+
+ if (FailedCpuList != NULL) {
+ *FailedCpuList = NULL;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (mMpSystemData.NumberOfProcessors == 1) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // temporarily stop checkAllAPsStatus for avoid resource dead-lock.
+ //
+ mStopCheckAllAPsStatus = TRUE;
+
+ for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
+ CpuData = &mMpSystemData.CpuDatas[Number];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+ //
+ // Skip BSP
+ //
+ continue;
+ }
+
+ if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
+ //
+ // Skip Disabled processors
+ //
+ continue;
+ }
+
+ CpuState = GetApState (CpuData);
+ if (CpuState != CpuStateIdle &&
+ CpuState != CpuStateSleeping) {
+ return EFI_NOT_READY;
+ }
+ }
+
+ mMpSystemData.Procedure = Procedure;
+ mMpSystemData.ProcedureArgument = ProcedureArgument;
+ mMpSystemData.WaitEvent = WaitEvent;
+ mMpSystemData.Timeout = TimeoutInMicroseconds;
+ mMpSystemData.TimeoutActive = (BOOLEAN) (TimeoutInMicroseconds != 0);
+ mMpSystemData.FinishCount = 0;
+ mMpSystemData.StartCount = 0;
+ mMpSystemData.SingleThread = SingleThread;
+ mMpSystemData.FailedList = FailedCpuList;
+ mMpSystemData.FailedListIndex = 0;
+ APInitialState = CpuStateReady;
+
+ for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
+ CpuData = &mMpSystemData.CpuDatas[Number];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+ //
+ // Skip BSP
+ //
+ continue;
+ }
+
+ if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
+ //
+ // Skip Disabled processors
+ //
+ continue;
+ }
+
+ //
+ // Get APs prepared, and put failing APs into FailedCpuList
+ // if "SingleThread", only 1 AP will put to ready state, other AP will be put to ready
+ // state 1 by 1, until the previous 1 finished its task
+ // if not "SingleThread", all APs are put to ready state from the beginning
+ //
+ CpuState = GetApState (CpuData);
+ if (CpuState == CpuStateIdle ||
+ CpuState == CpuStateSleeping) {
+ mMpSystemData.StartCount++;
+
+ SetApState (CpuData, APInitialState);
+
+ if (APInitialState == CpuStateReady) {
+ SetApProcedure (CpuData, Procedure, ProcedureArgument);
+ //
+ // If this AP previous state is Sleeping, we should
+ // wake up this AP by sent a SIPI. and avoid
+ // re-involve the sleeping state. we must call
+ // SetApProcedure() first.
+ //
+ if (CpuState == CpuStateSleeping) {
+ ResetProcessorToIdleState (CpuData);
+ }
+ }
+
+ if (SingleThread) {
+ APInitialState = CpuStateBlocked;
+ }
+ }
+ }
+
+ mStopCheckAllAPsStatus = FALSE;
+
+ if (WaitEvent != NULL) {
+ //
+ // non blocking
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Blocking temporarily stop CheckAllAPsStatus()
+ //
+ mStopCheckAllAPsStatus = TRUE;
+
+ while (TRUE) {
+ CheckAndUpdateAllAPsToIdleState ();
+ if (mMpSystemData.FinishCount == mMpSystemData.StartCount) {
+ Status = EFI_SUCCESS;
+ goto Done;
+ }
+
+ //
+ // task timeout
+ //
+ if (mMpSystemData.TimeoutActive && mMpSystemData.Timeout < 0) {
+ ResetAllFailedAPs();
+ Status = EFI_TIMEOUT;
+ goto Done;
+ }
+
+ MicroSecondDelay (gPollInterval);
+ mMpSystemData.Timeout -= gPollInterval;
+ }
+
+Done:
+
+ return Status;
+}
+
+/**
+ This service lets the caller get one enabled AP to execute a caller-provided
+ function. The caller can request the BSP to either wait for the completion
+ of the AP or just proceed with the next task by using the EFI event mechanism.
+ See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
+ execution support. This service may only be called from the BSP.
+
+ This function is used to dispatch one enabled AP to the function specified by
+ Procedure passing in the argument specified by ProcedureArgument. If WaitEvent
+ is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
+ TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
+ BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
+ is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
+ then EFI_UNSUPPORTED must be returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before the AP returns
+ from Procedure, then execution of Procedure by the AP is terminated. The AP is
+ available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
+ EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] ProcessorNumber The handle number of the AP. The range is
+ from 0 to the total number of logical
+ processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ all APs return from Procedure, then Procedure
+ on the failed APs is terminated. All enabled
+ APs are available for next function assigned
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] Finished If NULL, this parameter is ignored. In
+ blocking mode, this parameter is ignored.
+ In non-blocking mode, if AP returns from
+ Procedure before the timeout expires, its
+ content is set to TRUE. Otherwise, the
+ value is set to FALSE. The caller can
+ determine if the AP returned from Procedure
+ by evaluating this value.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval EFI_SUCCESS In non-blocking mode, the function has been
+ dispatched to specified AP.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ the specified AP has finished.
+ @retval EFI_NOT_READY The specified AP is busy.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+StartupThisAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ )
+{
+ CPU_DATA_BLOCK *CpuData;
+ CPU_STATE CpuState;
+
+ CpuData = NULL;
+
+ if (Finished != NULL) {
+ *Finished = FALSE;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // temporarily stop checkAllAPsStatus for avoid resource dead-lock.
+ //
+ mStopCheckAllAPsStatus = TRUE;
+
+ CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT) ||
+ !TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CpuState = GetApState (CpuData);
+ if (CpuState != CpuStateIdle &&
+ CpuState != CpuStateSleeping) {
+ return EFI_NOT_READY;
+ }
+
+ SetApState (CpuData, CpuStateReady);
+
+ SetApProcedure (CpuData, Procedure, ProcedureArgument);
+ //
+ // If this AP previous state is Sleeping, we should
+ // wake up this AP by sent a SIPI. and avoid
+ // re-involve the sleeping state. we must call
+ // SetApProcedure() first.
+ //
+ if (CpuState == CpuStateSleeping) {
+ ResetProcessorToIdleState (CpuData);
+ }
+
+ CpuData->Timeout = TimeoutInMicroseconds;
+ CpuData->WaitEvent = WaitEvent;
+ CpuData->TimeoutActive = (BOOLEAN) (TimeoutInMicroseconds != 0);
+ CpuData->Finished = Finished;
+
+ mStopCheckAllAPsStatus = FALSE;
+
+ if (WaitEvent != NULL) {
+ //
+ // Non Blocking
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Blocking
+ //
+ while (TRUE) {
+ if (GetApState (CpuData) == CpuStateFinished) {
+ SetApState (CpuData, CpuStateIdle);
+ break;
+ }
+
+ if (CpuData->TimeoutActive && CpuData->Timeout < 0) {
+ ResetProcessorToIdleState (CpuData);
+ return EFI_TIMEOUT;
+ }
+
+ MicroSecondDelay (gPollInterval);
+ CpuData->Timeout -= gPollInterval;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. This call can only be performed
+ by the current BSP.
+
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. The new BSP can take over the
+ execution of the old BSP and continue seamlessly from where the old one left
+ off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+ is signaled.
+
+ If the BSP cannot be switched prior to the return from this service, then
+ EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
+ enabled AP. Otherwise, it will be disabled.
+
+ @retval EFI_SUCCESS BSP successfully switched.
+ @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
+ this service returning.
+ @retval EFI_UNSUPPORTED Switching the BSP is not supported.
+ @retval EFI_SUCCESS The calling processor is an AP.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
+ a disabled AP.
+ @retval EFI_NOT_READY The specified AP is busy.
+
+**/
+EFI_STATUS
+EFIAPI
+SwitchBSP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ )
+{
+ //
+ // Current always return unsupported.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ This service lets the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ This service allows the caller enable or disable an AP from this point onward.
+ The caller can optionally specify the health status of the AP by Health. If
+ an AP is being disabled, then the state of the disabled AP is implementation
+ dependent. If an AP is enabled, then the implementation must guarantee that a
+ complete initialization sequence is performed on the AP, so the AP is in a state
+ that is compatible with an MP operating system. This service may not be supported
+ after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
+
+ If the enable or disable AP operation cannot be completed prior to the return
+ from this service, then EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableAP Specifies the new state for the processor for
+ enabled, FALSE for disabled.
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies
+ the new health status of the AP. This flag
+ corresponds to StatusFlag defined in
+ EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
+ the PROCESSOR_HEALTH_STATUS_BIT is used. All other
+ bits are ignored. If it is NULL, this parameter
+ is ignored.
+
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
+ prior to this service returning.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
+ does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
+
+**/
+EFI_STATUS
+EFIAPI
+EnableDisableAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ )
+{
+ CPU_DATA_BLOCK *CpuData;
+ BOOLEAN TempStopCheckState;
+ CPU_STATE CpuState;
+
+ CpuData = NULL;
+ TempStopCheckState = FALSE;
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // temporarily stop checkAllAPsStatus for initialize parameters.
+ //
+ if (!mStopCheckAllAPsStatus) {
+ mStopCheckAllAPsStatus = TRUE;
+ TempStopCheckState = TRUE;
+ }
+
+ CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CpuState = GetApState (CpuData);
+ if (CpuState != CpuStateIdle &&
+ CpuState != CpuStateSleeping) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (EnableAP) {
+ if (!(TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT))) {
+ mMpSystemData.NumberOfEnabledProcessors++;
+ }
+ CpuStatusFlagOr (CpuData, PROCESSOR_ENABLED_BIT);
+ } else {
+ if (TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {
+ mMpSystemData.NumberOfEnabledProcessors--;
+ }
+ CpuStatusFlagAndNot (CpuData, PROCESSOR_ENABLED_BIT);
+ }
+
+ if (HealthFlag != NULL) {
+ CpuStatusFlagAndNot (CpuData, (UINT32)~PROCESSOR_HEALTH_STATUS_BIT);
+ CpuStatusFlagOr (CpuData, (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT));
+ }
+
+ if (TempStopCheckState) {
+ mStopCheckAllAPsStatus = FALSE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This return the handle number for the calling processor. This service may be
+ called from the BSP and APs.
+
+ This service returns the processor handle number for the calling processor.
+ The returned value is in the range from 0 to the total number of logical
+ processors minus 1. The total number of logical processors can be retrieved
+ with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
+ called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
+ is returned. Otherwise, the current processors handle number is returned in
+ ProcessorNumber, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[out] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+
+ @retval EFI_SUCCESS The current processor handle number was returned
+ in ProcessorNumber.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+WhoAmI (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *ProcessorNumber
+ )
+{
+ UINTN Index;
+ UINT32 ProcessorId;
+
+ if (ProcessorNumber == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ProcessorId = GetApicId ();
+ for (Index = 0; Index < mMpSystemData.NumberOfProcessors; Index++) {
+ if (mMpSystemData.CpuDatas[Index].Info.ProcessorId == ProcessorId) {
+ break;
+ }
+ }
+
+ *ProcessorNumber = Index;
+ return EFI_SUCCESS;
+}
+
+/**
+ Terminate AP's task and set it to idle state.
+
+ This function terminates AP's task due to timeout by sending INIT-SIPI,
+ and sends it to idle state.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified AP
+
+**/
+VOID
+ResetProcessorToIdleState (
+ IN CPU_DATA_BLOCK *CpuData
+ )
+{
+ ResetApStackless ((UINT32)CpuData->Info.ProcessorId);
+}
+
+/**
+ Application Processors do loop routine
+ after switch to its own stack.
+
+ @param Context1 A pointer to the context to pass into the function.
+ @param Context2 A pointer to the context to pass into the function.
+
+**/
+VOID
+ProcessorToIdleState (
+ IN VOID *Context1, OPTIONAL
+ IN VOID *Context2 OPTIONAL
+ )
+{
+ UINTN ProcessorNumber;
+ CPU_DATA_BLOCK *CpuData;
+ EFI_AP_PROCEDURE Procedure;
+ volatile VOID *ProcedureArgument;
+
+ AsmApDoneWithCommonStack ();
+
+ while (!mAPsAlreadyInitFinished) {
+ CpuPause ();
+ }
+
+ WhoAmI (&mMpServicesTemplate, &ProcessorNumber);
+ CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+
+ //
+ // Avoid forcibly reset AP caused the AP got lock not release.
+ //
+ if (CpuData->LockSelf == (INTN) GetApicId ()) {
+ ReleaseSpinLock (&CpuData->CpuDataLock);
+ }
+
+ //
+ // Avoid forcibly reset AP caused the timeout AP State is not
+ // updated.
+ //
+ GetMpSpinLock (CpuData);
+ if (CpuData->State == CpuStateBusy) {
+ CpuData->Procedure = NULL;
+ }
+ CpuData->State = CpuStateIdle;
+ ReleaseMpSpinLock (CpuData);
+
+ while (TRUE) {
+ GetMpSpinLock (CpuData);
+ ProcedureArgument = CpuData->Parameter;
+ Procedure = CpuData->Procedure;
+ ReleaseMpSpinLock (CpuData);
+
+ if (Procedure != NULL) {
+ SetApState (CpuData, CpuStateBusy);
+
+ Procedure ((VOID*) ProcedureArgument);
+
+ GetMpSpinLock (CpuData);
+ CpuData->Procedure = NULL;
+ CpuData->State = CpuStateFinished;
+ ReleaseMpSpinLock (CpuData);
+ } else {
+ //
+ // if no procedure to execution, we simply put AP
+ // into sleeping state, and waiting BSP sent SIPI.
+ //
+ GetMpSpinLock (CpuData);
+ if (CpuData->State == CpuStateIdle) {
+ CpuData->State = CpuStateSleeping;
+ }
+ ReleaseMpSpinLock (CpuData);
+ }
+
+ if (GetApState (CpuData) == CpuStateSleeping) {
+ CpuSleep ();
+ }
+
+ CpuPause ();
+ }
+
+ CpuSleep ();
+ CpuDeadLoop ();
+}
+
+/**
+ Checks AP' status periodically.
+
+ This function is triggerred by timer perodically to check the
+ state of AP forStartupThisAP() executed in non-blocking mode.
+
+ @param Event Event triggered.
+ @param Context Parameter passed with the event.
+
+**/
+VOID
+EFIAPI
+CheckThisAPStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ CPU_DATA_BLOCK *CpuData;
+ CPU_STATE CpuState;
+
+ CpuData = (CPU_DATA_BLOCK *) Context;
+ if (CpuData->TimeoutActive) {
+ CpuData->Timeout -= gPollInterval;
+ }
+
+ CpuState = GetApState (CpuData);
+
+ if (CpuState == CpuStateFinished) {
+ if (CpuData->Finished) {
+ *CpuData->Finished = TRUE;
+ }
+ SetApState (CpuData, CpuStateIdle);
+ goto out;
+ }
+
+ if (CpuData->TimeoutActive && CpuData->Timeout < 0) {
+ if (CpuState != CpuStateIdle &&
+ CpuData->Finished) {
+ *CpuData->Finished = FALSE;
+ }
+ ResetProcessorToIdleState (CpuData);
+ goto out;
+ }
+
+ return;
+
+out:
+ CpuData->TimeoutActive = FALSE;
+ gBS->SignalEvent (CpuData->WaitEvent);
+ CpuData->WaitEvent = NULL;
+}
+
+/**
+ Checks APs' status periodically.
+
+ This function is triggerred by timer perodically to check the
+ state of APs for StartupAllAPs() executed in non-blocking mode.
+
+ @param Event Event triggered.
+ @param Context Parameter passed with the event.
+
+**/
+VOID
+EFIAPI
+CheckAllAPsStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ CPU_DATA_BLOCK *CpuData;
+ UINTN Number;
+ EFI_STATUS Status;
+
+ if (mMpSystemData.TimeoutActive) {
+ mMpSystemData.Timeout -= gPollInterval;
+ }
+
+ if (mStopCheckAllAPsStatus) {
+ return;
+ }
+
+ //
+ // avoid next timer enter.
+ //
+ Status = gBS->SetTimer (
+ mMpSystemData.CheckAllAPsEvent,
+ TimerCancel,
+ 0
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ if (mMpSystemData.WaitEvent != NULL) {
+ CheckAndUpdateAllAPsToIdleState ();
+ //
+ // task timeout
+ //
+ if (mMpSystemData.TimeoutActive && mMpSystemData.Timeout < 0) {
+ ResetAllFailedAPs();
+ //
+ // force exit
+ //
+ mMpSystemData.FinishCount = mMpSystemData.StartCount;
+ }
+
+ if (mMpSystemData.FinishCount != mMpSystemData.StartCount) {
+ goto EXIT;
+ }
+
+ mMpSystemData.TimeoutActive = FALSE;
+ gBS->SignalEvent (mMpSystemData.WaitEvent);
+ mMpSystemData.WaitEvent = NULL;
+ mStopCheckAllAPsStatus = TRUE;
+
+ goto EXIT;
+ }
+
+ //
+ // check each AP status for StartupThisAP
+ //
+ for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
+ CpuData = &mMpSystemData.CpuDatas[Number];
+ if (CpuData->WaitEvent) {
+ CheckThisAPStatus (NULL, (VOID *)CpuData);
+ }
+ }
+
+EXIT:
+ Status = gBS->SetTimer (
+ mMpSystemData.CheckAllAPsEvent,
+ TimerPeriodic,
+ EFI_TIMER_PERIOD_MICROSECONDS (100)
+ );
+ ASSERT_EFI_ERROR (Status);
+}
+
+/**
+ Application Processor C code entry point.
+
+**/
+VOID
+EFIAPI
+ApEntryPointInC (
+ VOID
+ )
+{
+ VOID* TopOfApStack;
+ UINTN ProcessorNumber;
+
+ if (!mAPsAlreadyInitFinished) {
+ FillInProcessorInformation (FALSE, mMpSystemData.NumberOfProcessors);
+ TopOfApStack = (UINT8*)mApStackStart + gApStackSize;
+ mApStackStart = TopOfApStack;
+
+ //
+ // Store the Stack address, when reset the AP, We can found the original address.
+ //
+ mMpSystemData.CpuDatas[mMpSystemData.NumberOfProcessors].TopOfStack = TopOfApStack;
+ mMpSystemData.NumberOfProcessors++;
+ mMpSystemData.NumberOfEnabledProcessors++;
+ } else {
+ WhoAmI (&mMpServicesTemplate, &ProcessorNumber);
+ //
+ // Get the original stack address.
+ //
+ TopOfApStack = mMpSystemData.CpuDatas[ProcessorNumber].TopOfStack;
+ }
+
+ SwitchStack (
+ (SWITCH_STACK_ENTRY_POINT)(UINTN)ProcessorToIdleState,
+ NULL,
+ NULL,
+ TopOfApStack);
+}
+
+/**
+ This function is called by all processors (both BSP and AP) once and collects MP related data.
+
+ @param Bsp TRUE if the CPU is BSP
+ @param ProcessorNumber The specific processor number
+
+ @retval EFI_SUCCESS Data for the processor collected and filled in
+
+**/
+EFI_STATUS
+FillInProcessorInformation (
+ IN BOOLEAN Bsp,
+ IN UINTN ProcessorNumber
+ )
+{
+ CPU_DATA_BLOCK *CpuData;
+ UINT32 ProcessorId;
+
+ CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+ ProcessorId = GetApicId ();
+ CpuData->Info.ProcessorId = ProcessorId;
+ CpuData->Info.StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
+ if (Bsp) {
+ CpuData->Info.StatusFlag |= PROCESSOR_AS_BSP_BIT;
+ }
+ CpuData->Info.Location.Package = ProcessorId;
+ CpuData->Info.Location.Core = 0;
+ CpuData->Info.Location.Thread = 0;
+ CpuData->State = Bsp ? CpuStateBusy : CpuStateIdle;
+
+ CpuData->Procedure = NULL;
+ CpuData->Parameter = NULL;
+ InitializeSpinLock (&CpuData->CpuDataLock);
+ CpuData->LockSelf = -1;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Prepare the System Data.
+
+ @retval EFI_SUCCESS the System Data finished initilization.
+
+**/
+EFI_STATUS
+InitMpSystemData (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ ZeroMem (&mMpSystemData, sizeof (MP_SYSTEM_DATA));
+
+ mMpSystemData.NumberOfProcessors = 1;
+ mMpSystemData.NumberOfEnabledProcessors = 1;
+
+ mMpSystemData.CpuDatas = AllocateZeroPool (sizeof (CPU_DATA_BLOCK) * gMaxLogicalProcessorNumber);
+ ASSERT(mMpSystemData.CpuDatas != NULL);
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ CheckAllAPsStatus,
+ NULL,
+ &mMpSystemData.CheckAllAPsEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Set timer to check all APs status.
+ //
+ Status = gBS->SetTimer (
+ mMpSystemData.CheckAllAPsEvent,
+ TimerPeriodic,
+ EFI_TIMER_PERIOD_MICROSECONDS (100)
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // BSP
+ //
+ FillInProcessorInformation (TRUE, 0);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Collects BIST data from HOB.
+
+ This function collects BIST data from HOB built from Sec Platform Information
+ PPI or SEC Platform Information2 PPI.
+
+**/
+VOID
+CollectBistDataFromHob (
+ VOID
+ )
+{
+ EFI_HOB_GUID_TYPE *GuidHob;
+ EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2;
+ EFI_SEC_PLATFORM_INFORMATION_RECORD *SecPlatformInformation;
+ UINTN NumberOfData;
+ EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstance;
+ EFI_SEC_PLATFORM_INFORMATION_CPU BspCpuInstance;
+ UINTN ProcessorNumber;
+ UINT32 InitialLocalApicId;
+ CPU_DATA_BLOCK *CpuData;
+
+ SecPlatformInformation2 = NULL;
+ SecPlatformInformation = NULL;
+
+ //
+ // Get gEfiSecPlatformInformation2PpiGuid Guided HOB firstly
+ //
+ GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformation2PpiGuid);
+ if (GuidHob != NULL) {
+ //
+ // Sec Platform Information2 PPI includes BSP/APs' BIST information
+ //
+ SecPlatformInformation2 = GET_GUID_HOB_DATA (GuidHob);
+ NumberOfData = SecPlatformInformation2->NumberOfCpus;
+ CpuInstance = SecPlatformInformation2->CpuInstance;
+ } else {
+ //
+ // Otherwise, get gEfiSecPlatformInformationPpiGuid Guided HOB
+ //
+ GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformationPpiGuid);
+ if (GuidHob != NULL) {
+ SecPlatformInformation = GET_GUID_HOB_DATA (GuidHob);
+ NumberOfData = 1;
+ //
+ // SEC Platform Information only includes BSP's BIST information
+ // does not have BSP's APIC ID
+ //
+ BspCpuInstance.CpuLocation = GetApicId ();
+ BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32;
+ CpuInstance = &BspCpuInstance;
+ } else {
+ DEBUG ((EFI_D_INFO, "Does not find any HOB stored CPU BIST information!\n"));
+ //
+ // Does not find any HOB stored BIST information
+ //
+ return;
+ }
+ }
+
+ while ((NumberOfData--) > 0) {
+ for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {
+ CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+ InitialLocalApicId = (UINT32) CpuData->Info.ProcessorId;
+ if (InitialLocalApicId == CpuInstance[NumberOfData].CpuLocation) {
+ //
+ // Update CPU health status for MP Services Protocol according to BIST data.
+ //
+ if (CpuInstance[NumberOfData].InfoRecord.IA32HealthFlags.Uint32 != 0) {
+ CpuData->Info.StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
+ //
+ // Report Status Code that self test is failed
+ //
+ REPORT_STATUS_CODE (
+ EFI_ERROR_CODE | EFI_ERROR_MAJOR,
+ (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST)
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
+ Callback function for ExitBootServices.
+
+ @param Event Event whose notification function is being invoked.
+ @param Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+ExitBootServicesCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Avoid APs access invalid buff datas which allocated by BootServices,
+ // so we send INIT IPI to APs to let them wait for SIPI state.
+ //
+ SendInitIpiAllExcludingSelf ();
+}
+
+/**
+ A minimal wrapper function that allows MtrrSetAllMtrrs() to be passed to
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() as Procedure.
+
+ @param[in] Buffer Pointer to an MTRR_SETTINGS object, to be passed to
+ MtrrSetAllMtrrs().
+**/
+VOID
+EFIAPI
+SetMtrrsFromBuffer (
+ IN VOID *Buffer
+ )
+{
+ MtrrSetAllMtrrs (Buffer);
+}
+
+/**
+ Initialize Multi-processor support.
+
+**/
+VOID
+InitializeMpSupport (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ MTRR_SETTINGS MtrrSettings;
+ UINTN Timeout;
+
+ gMaxLogicalProcessorNumber = (UINTN) PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
+ if (gMaxLogicalProcessorNumber < 1) {
+ DEBUG ((DEBUG_ERROR, "Setting PcdCpuMaxLogicalProcessorNumber should be more than zero.\n"));
+ return;
+ }
+
+
+
+ InitMpSystemData ();
+
+ //
+ // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1
+ //
+ if (gMaxLogicalProcessorNumber > 1) {
+
+ gApStackSize = (UINTN) PcdGet32 (PcdCpuApStackSize);
+ ASSERT ((gApStackSize & (SIZE_4KB - 1)) == 0);
+
+ mApStackStart = AllocatePages (EFI_SIZE_TO_PAGES (gMaxLogicalProcessorNumber * gApStackSize));
+ ASSERT (mApStackStart != NULL);
+
+ //
+ // the first buffer of stack size used for common stack, when the amount of AP
+ // more than 1, we should never free the common stack which maybe used for AP reset.
+ //
+ mCommonStack = mApStackStart;
+ mTopOfApCommonStack = (UINT8*) mApStackStart + gApStackSize;
+ mApStackStart = mTopOfApCommonStack;
+
+ PrepareAPStartupCode ();
+
+ StartApsStackless ();
+ }
+
+ DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mMpSystemData.NumberOfProcessors));
+ if (mMpSystemData.NumberOfProcessors == 1) {
+ FreeApStartupCode ();
+ if (mCommonStack != NULL) {
+ FreePages (mCommonStack, EFI_SIZE_TO_PAGES (gMaxLogicalProcessorNumber * gApStackSize));
+ }
+ }
+
+ mMpSystemData.CpuDatas = ReallocatePool (
+ sizeof (CPU_DATA_BLOCK) * gMaxLogicalProcessorNumber,
+ sizeof (CPU_DATA_BLOCK) * mMpSystemData.NumberOfProcessors,
+ mMpSystemData.CpuDatas);
+
+ //
+ // Release all APs to complete initialization and enter idle loop
+ //
+ mAPsAlreadyInitFinished = TRUE;
+
+ //
+ // Wait for all APs to enter idle loop.
+ //
+ Timeout = 0;
+ do {
+ if (CheckAllAPsSleeping ()) {
+ break;
+ }
+ MicroSecondDelay (gPollInterval);
+ Timeout += gPollInterval;
+ } while (Timeout <= PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
+ ASSERT (Timeout <= PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
+
+ //
+ // Update CPU healthy information from Guided HOB
+ //
+ CollectBistDataFromHob ();
+
+ //
+ // Synchronize MTRR settings to APs.
+ //
+ MtrrGetAllMtrrs (&MtrrSettings);
+ Status = mMpServicesTemplate.StartupAllAPs (
+ &mMpServicesTemplate, // This
+ SetMtrrsFromBuffer, // Procedure
+ TRUE, // SingleThread
+ NULL, // WaitEvent
+ 0, // TimeoutInMicrosecsond
+ &MtrrSettings, // ProcedureArgument
+ NULL // FailedCpuList
+ );
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_STARTED);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &mMpServiceHandle,
+ &gEfiMpServiceProtocolGuid, &mMpServicesTemplate,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ if (mMpSystemData.NumberOfProcessors > 1 && mMpSystemData.NumberOfProcessors < gMaxLogicalProcessorNumber) {
+ if (mApStackStart != NULL) {
+ FreePages (mApStackStart, EFI_SIZE_TO_PAGES (
+ (gMaxLogicalProcessorNumber - mMpSystemData.NumberOfProcessors) *
+ gApStackSize));
+ }
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_EXIT_BOOT_SERVICES,
+ TPL_CALLBACK,
+ ExitBootServicesCallback,
+ NULL,
+ &mExitBootServicesEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+}
diff --git a/Core/UefiCpuPkg/CpuDxe/CpuMp.h b/Core/UefiCpuPkg/CpuDxe/CpuMp.h
new file mode 100644
index 0000000000..503f3ae944
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/CpuMp.h
@@ -0,0 +1,660 @@
+/** @file
+ CPU DXE MP support
+
+ Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _CPU_MP_H_
+#define _CPU_MP_H_
+
+#include <Ppi/SecPlatformInformation.h>
+#include <Ppi/SecPlatformInformation2.h>
+#include <Protocol/MpService.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/HobLib.h>
+#include <Library/ReportStatusCodeLib.h>
+
+/**
+ Initialize Multi-processor support.
+
+**/
+VOID
+InitializeMpSupport (
+ VOID
+ );
+
+typedef
+VOID
+(EFIAPI *STACKLESS_AP_ENTRY_POINT)(
+ VOID
+ );
+
+/**
+ Starts the Application Processors and directs them to jump to the
+ specified routine.
+
+ The processor jumps to this code in flat mode, but the processor's
+ stack is not initialized.
+
+ @retval EFI_SUCCESS The APs were started
+
+**/
+EFI_STATUS
+StartApsStackless (
+ VOID
+ );
+
+/**
+ The AP entry point that the Startup-IPI target code will jump to.
+
+ The processor jumps to this code in flat mode, but the processor's
+ stack is not initialized.
+
+**/
+VOID
+EFIAPI
+AsmApEntryPoint (
+ VOID
+ );
+
+/**
+ Releases the lock preventing other APs from using the shared AP
+ stack.
+
+ Once the AP has transitioned to using a new stack, it can call this
+ function to allow another AP to proceed with using the shared stack.
+
+**/
+VOID
+EFIAPI
+AsmApDoneWithCommonStack (
+ VOID
+ );
+
+typedef enum {
+ CpuStateIdle,
+ CpuStateBlocked,
+ CpuStateReady,
+ CpuStateBusy,
+ CpuStateFinished,
+ CpuStateSleeping
+} CPU_STATE;
+
+/**
+ Define Individual Processor Data block.
+
+**/
+typedef struct {
+ EFI_PROCESSOR_INFORMATION Info;
+ SPIN_LOCK CpuDataLock;
+ INTN LockSelf;
+ volatile CPU_STATE State;
+
+ volatile EFI_AP_PROCEDURE Procedure;
+ volatile VOID* Parameter;
+ BOOLEAN *Finished;
+ INTN Timeout;
+ EFI_EVENT WaitEvent;
+ BOOLEAN TimeoutActive;
+ EFI_EVENT CheckThisAPEvent;
+ VOID *TopOfStack;
+} CPU_DATA_BLOCK;
+
+/**
+ Define MP data block which consumes individual processor block.
+
+**/
+typedef struct {
+ CPU_DATA_BLOCK *CpuDatas;
+ UINTN NumberOfProcessors;
+ UINTN NumberOfEnabledProcessors;
+
+ EFI_AP_PROCEDURE Procedure;
+ VOID *ProcedureArgument;
+ UINTN StartCount;
+ UINTN FinishCount;
+ BOOLEAN SingleThread;
+ UINTN **FailedList;
+ UINTN FailedListIndex;
+ INTN Timeout;
+ EFI_EVENT WaitEvent;
+ BOOLEAN TimeoutActive;
+ EFI_EVENT CheckAllAPsEvent;
+} MP_SYSTEM_DATA;
+
+/**
+ This function is called by all processors (both BSP and AP) once and collects MP related data.
+
+ @param Bsp TRUE if the CPU is BSP
+ @param ProcessorNumber The specific processor number
+
+ @retval EFI_SUCCESS Data for the processor collected and filled in
+
+**/
+EFI_STATUS
+FillInProcessorInformation (
+ IN BOOLEAN Bsp,
+ IN UINTN ProcessorNumber
+ );
+
+/**
+ This service retrieves the number of logical processor in the platform
+ and the number of those logical processors that are enabled on this boot.
+ This service may only be called from the BSP.
+
+ This function is used to retrieve the following information:
+ - The number of logical processors that are present in the system.
+ - The number of enabled logical processors in the system at the instant
+ this call is made.
+
+ Because MP Service Protocol provides services to enable and disable processors
+ dynamically, the number of enabled logical processors may vary during the
+ course of a boot session.
+
+ If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
+ If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
+ EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
+ is returned in NumberOfProcessors, the number of currently enabled processor
+ is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL.
+ @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+GetNumberOfProcessors (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *NumberOfProcessors,
+ OUT UINTN *NumberOfEnabledProcessors
+ );
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ This service retrieves detailed MP-related information about any processor
+ on the platform. Note the following:
+ - The processor information may change during the course of a boot session.
+ - The information presented here is entirely MP related.
+
+ Information regarding the number of caches and their sizes, frequency of operation,
+ slot numbers is all considered platform-related information and is not provided
+ by this service.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProcessorInfo (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer
+ );
+
+/**
+ This service executes a caller provided function on all enabled APs. APs can
+ run either simultaneously or one at a time in sequence. This service supports
+ both blocking and non-blocking requests. The non-blocking requests use EFI
+ events so the BSP can detect when the APs have finished. This service may only
+ be called from the BSP.
+
+ This function is used to dispatch all the enabled APs to the function specified
+ by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned
+ immediately and Procedure is not started on any AP.
+
+ If SingleThread is TRUE, all the enabled APs execute the function specified by
+ Procedure one by one, in ascending order of processor handle number. Otherwise,
+ all the enabled APs execute the function specified by Procedure simultaneously.
+
+ If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
+ APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
+ mode, and the BSP returns from this service without waiting for APs. If a
+ non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+ is signaled, then EFI_UNSUPPORTED must be returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before all APs return
+ from Procedure, then Procedure on the failed APs is terminated. All enabled APs
+ are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
+ content points to the list of processor handle numbers in which Procedure was
+ terminated.
+
+ Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ to make sure that the nature of the code that is executed on the BSP and the
+ dispatched APs is well controlled. The MP Services Protocol does not guarantee
+ that the Procedure function is MP-safe. Hence, the tasks that can be run in
+ parallel are limited to certain independent tasks and well-controlled exclusive
+ code. EFI services and protocols may not be called by APs unless otherwise
+ specified.
+
+ In blocking execution mode, BSP waits until all APs finish or
+ TimeoutInMicroseconds expires.
+
+ In non-blocking execution mode, BSP is freed to return to the caller and then
+ proceed to the next task without having to wait for APs. The following
+ sequence needs to occur in a non-blocking execution mode:
+
+ -# The caller that intends to use this MP Services Protocol in non-blocking
+ mode creates WaitEvent by calling the EFI CreateEvent() service. The caller
+ invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
+ is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
+ the function specified by Procedure to be started on all the enabled APs,
+ and releases the BSP to continue with other tasks.
+ -# The caller can use the CheckEvent() and WaitForEvent() services to check
+ the state of the WaitEvent created in step 1.
+ -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
+ Service signals WaitEvent by calling the EFI SignalEvent() function. If
+ FailedCpuList is not NULL, its content is available when WaitEvent is
+ signaled. If all APs returned from Procedure prior to the timeout, then
+ FailedCpuList is set to NULL. If not all APs return from Procedure before
+ the timeout, then FailedCpuList is filled in with the list of the failed
+ APs. The buffer is allocated by MP Service Protocol using AllocatePool().
+ It is the caller's responsibility to free the buffer with FreePool() service.
+ -# This invocation of SignalEvent() function informs the caller that invoked
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
+ the specified task or a timeout occurred. The contents of FailedCpuList
+ can be examined to determine which APs did not complete the specified task
+ prior to the timeout.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure, or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ all APs return from Procedure, then Procedure
+ on the failed APs is terminated. All enabled
+ APs are available for next function assigned
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
+ if all APs finish successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of the buffer
+ holding handle numbers of the failed APs.
+ The buffer is allocated by MP Service Protocol,
+ and it's the caller's responsibility to
+ free the buffer with FreePool() service.
+ In blocking mode, it is ready for consumption
+ when the call returns. In non-blocking mode,
+ it is ready when WaitEvent is signaled. The
+ list of failed CPU is terminated by
+ END_OF_CPU_LIST.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled APs.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR Caller processor is AP.
+ @retval EFI_NOT_STARTED No enabled APs exist in the system.
+ @retval EFI_NOT_READY Any enabled APs are busy.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ all enabled APs have finished.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+StartupAllAPs (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ );
+
+/**
+ This service lets the caller get one enabled AP to execute a caller-provided
+ function. The caller can request the BSP to either wait for the completion
+ of the AP or just proceed with the next task by using the EFI event mechanism.
+ See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
+ execution support. This service may only be called from the BSP.
+
+ This function is used to dispatch one enabled AP to the function specified by
+ Procedure passing in the argument specified by ProcedureArgument. If WaitEvent
+ is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
+ TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
+ BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
+ is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
+ then EFI_UNSUPPORTED must be returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before the AP returns
+ from Procedure, then execution of Procedure by the AP is terminated. The AP is
+ available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
+ EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] ProcessorNumber The handle number of the AP. The range is
+ from 0 to the total number of logical
+ processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ all APs return from Procedure, then Procedure
+ on the failed APs is terminated. All enabled
+ APs are available for next function assigned
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] Finished If NULL, this parameter is ignored. In
+ blocking mode, this parameter is ignored.
+ In non-blocking mode, if AP returns from
+ Procedure before the timeout expires, its
+ content is set to TRUE. Otherwise, the
+ value is set to FALSE. The caller can
+ determine if the AP returned from Procedure
+ by evaluating this value.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval EFI_SUCCESS In non-blocking mode, the function has been
+ dispatched to specified AP.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ the specified AP has finished.
+ @retval EFI_NOT_READY The specified AP is busy.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+StartupThisAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ );
+
+/**
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. This call can only be performed
+ by the current BSP.
+
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. The new BSP can take over the
+ execution of the old BSP and continue seamlessly from where the old one left
+ off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+ is signaled.
+
+ If the BSP cannot be switched prior to the return from this service, then
+ EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
+ enabled AP. Otherwise, it will be disabled.
+
+ @retval EFI_SUCCESS BSP successfully switched.
+ @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
+ this service returning.
+ @retval EFI_UNSUPPORTED Switching the BSP is not supported.
+ @retval EFI_SUCCESS The calling processor is an AP.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
+ a disabled AP.
+ @retval EFI_NOT_READY The specified AP is busy.
+
+**/
+EFI_STATUS
+EFIAPI
+SwitchBSP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ );
+
+/**
+ This service lets the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ This service allows the caller enable or disable an AP from this point onward.
+ The caller can optionally specify the health status of the AP by Health. If
+ an AP is being disabled, then the state of the disabled AP is implementation
+ dependent. If an AP is enabled, then the implementation must guarantee that a
+ complete initialization sequence is performed on the AP, so the AP is in a state
+ that is compatible with an MP operating system. This service may not be supported
+ after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
+
+ If the enable or disable AP operation cannot be completed prior to the return
+ from this service, then EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableAP Specifies the new state for the processor for
+ enabled, FALSE for disabled.
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies
+ the new health status of the AP. This flag
+ corresponds to StatusFlag defined in
+ EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
+ the PROCESSOR_HEALTH_STATUS_BIT is used. All other
+ bits are ignored. If it is NULL, this parameter
+ is ignored.
+
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
+ prior to this service returning.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
+ does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
+
+**/
+EFI_STATUS
+EFIAPI
+EnableDisableAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ );
+
+/**
+ This return the handle number for the calling processor. This service may be
+ called from the BSP and APs.
+
+ This service returns the processor handle number for the calling processor.
+ The returned value is in the range from 0 to the total number of logical
+ processors minus 1. The total number of logical processors can be retrieved
+ with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
+ called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
+ is returned. Otherwise, the current processors handle number is returned in
+ ProcessorNumber, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[out] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+
+ @retval EFI_SUCCESS The current processor handle number was returned
+ in ProcessorNumber.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+WhoAmI (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *ProcessorNumber
+ );
+
+/**
+ Terminate AP's task and set it to idle state.
+
+ This function terminates AP's task due to timeout by sending INIT-SIPI,
+ and sends it to idle state.
+
+ @param CpuData the pointer to CPU_DATA_BLOCK of specified AP
+
+**/
+VOID
+ResetProcessorToIdleState (
+ IN CPU_DATA_BLOCK *CpuData
+ );
+
+/**
+ Prepares Startup Code for APs.
+ This function prepares Startup Code for APs.
+
+ @retval EFI_SUCCESS The APs were started
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory to start APs
+
+**/
+EFI_STATUS
+PrepareAPStartupCode (
+ VOID
+ );
+
+/**
+ Free the code buffer of startup AP.
+
+**/
+VOID
+FreeApStartupCode (
+ VOID
+ );
+
+/**
+ Resets the Application Processor and directs it to jump to the
+ specified routine.
+
+ The processor jumps to this code in flat mode, but the processor's
+ stack is not initialized.
+
+ @param ProcessorId the AP of ProcessorId was reset
+**/
+VOID
+ResetApStackless (
+ IN UINT32 ProcessorId
+ );
+
+/**
+ A minimal wrapper function that allows MtrrSetAllMtrrs() to be passed to
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() as Procedure.
+
+ @param[in] Buffer Pointer to an MTRR_SETTINGS object, to be passed to
+ MtrrSetAllMtrrs().
+**/
+VOID
+EFIAPI
+SetMtrrsFromBuffer (
+ IN VOID *Buffer
+ );
+
+#endif // _CPU_MP_H_
+
diff --git a/Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.S b/Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.S
new file mode 100644
index 0000000000..e034bc2e2e
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.S
@@ -0,0 +1,57 @@
+#------------------------------------------------------------------------------
+#*
+#* Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+#* This program and the accompanying materials
+#* are licensed and made available under the terms and conditions of the BSD License
+#* which accompanies this distribution. The full text of the license may be found at
+#* http://opensource.org/licenses/bsd-license.php
+#*
+#* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#*
+#* CpuAsm.S
+#*
+#* Abstract:
+#*
+#------------------------------------------------------------------------------
+
+
+#.MMX
+#.XMM
+
+#------------------------------------------------------------------------------
+# VOID
+# SetCodeSelector (
+# UINT16 Selector
+# );
+#------------------------------------------------------------------------------
+ASM_GLOBAL ASM_PFX(SetCodeSelector)
+ASM_PFX(SetCodeSelector):
+ movl 4(%esp), %ecx
+ subl $0x10, %esp
+ leal setCodeSelectorLongJump, %eax
+ movl %eax, (%esp)
+ movw %cx, 4(%esp)
+ .byte 0xFF, 0x2C, 0x24 # jmp *(%esp) note:(FWORD jmp)
+setCodeSelectorLongJump:
+ addl $0x10, %esp
+ ret
+
+#------------------------------------------------------------------------------
+# VOID
+# SetDataSelectors (
+# UINT16 Selector
+# );
+#------------------------------------------------------------------------------
+ASM_GLOBAL ASM_PFX(SetDataSelectors)
+ASM_PFX(SetDataSelectors):
+ movl 4(%esp), %ecx
+ movw %cx, %ss
+ movw %cx, %ds
+ movw %cx, %es
+ movw %cx, %fs
+ movw %cx, %gs
+ ret
+
+#END
+
diff --git a/Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.asm b/Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.asm
new file mode 100644
index 0000000000..7f8f0d6f3a
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.asm
@@ -0,0 +1,58 @@
+ TITLE CpuAsm.asm:
+;------------------------------------------------------------------------------
+;*
+;* Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+;* This program and the accompanying materials
+;* are licensed and made available under the terms and conditions of the BSD License
+;* which accompanies this distribution. The full text of the license may be found at
+;* http://opensource.org/licenses/bsd-license.php
+;*
+;* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+;* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;*
+;* CpuAsm.asm
+;*
+;* Abstract:
+;*
+;------------------------------------------------------------------------------
+
+ .686
+ .model flat,C
+ .code
+
+;------------------------------------------------------------------------------
+; VOID
+; SetCodeSelector (
+; UINT16 Selector
+; );
+;------------------------------------------------------------------------------
+SetCodeSelector PROC PUBLIC
+ mov ecx, [esp+4]
+ sub esp, 0x10
+ lea eax, setCodeSelectorLongJump
+ mov [esp], eax
+ mov [esp+4], cx
+ jmp fword ptr [esp]
+setCodeSelectorLongJump:
+ add esp, 0x10
+ ret
+SetCodeSelector ENDP
+
+;------------------------------------------------------------------------------
+; VOID
+; SetDataSelectors (
+; UINT16 Selector
+; );
+;------------------------------------------------------------------------------
+SetDataSelectors PROC PUBLIC
+ mov ecx, [esp+4]
+ mov ss, cx
+ mov ds, cx
+ mov es, cx
+ mov fs, cx
+ mov gs, cx
+ ret
+SetDataSelectors ENDP
+
+
+END
diff --git a/Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.asm b/Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.asm
new file mode 100644
index 0000000000..09579f251e
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.asm
@@ -0,0 +1,76 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+; This program and the accompanying materials
+; are licensed and made available under the terms and conditions of the BSD License
+; which accompanies this distribution. The full text of the license may be found at
+; http://opensource.org/licenses/bsd-license.php.
+;
+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;
+;------------------------------------------------------------------------------
+
+.686
+.xmm
+.model flat, C
+
+extern mTopOfApCommonStack:DWORD
+extern ApEntryPointInC:PROC
+
+.code
+
+;
+; This lock only allows one AP to use the mTopOfApCommonStack stack at a time
+;
+ApStackLock dd 0
+
+;.code
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApEntryPoint (
+; VOID
+; );
+;------------------------------------------------------------------------------
+AsmApEntryPoint PROC
+
+ cli
+AsmApEntryPointAcquireLock:
+lock bts dword ptr [ApStackLock], 0
+ pause
+ jc AsmApEntryPointAcquireLock
+
+ mov esp, [mTopOfApCommonStack]
+ call ApEntryPointInC
+
+ cli
+
+lock btc dword ptr [ApStackLock], 0
+
+ mov eax, 100h
+AsmApEntryPointShareLock:
+ pause
+ dec eax
+ jnz AsmApEntryPointShareLock
+
+ jmp AsmApEntryPoint
+
+AsmApEntryPoint ENDP
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApDoneWithCommonStack (
+; VOID
+; );
+;------------------------------------------------------------------------------
+AsmApDoneWithCommonStack PROC PUBLIC
+
+lock btc dword ptr [ApStackLock], 0
+ ret
+
+AsmApDoneWithCommonStack ENDP
+
+END
diff --git a/Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.nasm b/Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.nasm
new file mode 100644
index 0000000000..c47cdcef54
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/Ia32/MpAsm.nasm
@@ -0,0 +1,68 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+; This program and the accompanying materials
+; are licensed and made available under the terms and conditions of the BSD License
+; which accompanies this distribution. The full text of the license may be found at
+; http://opensource.org/licenses/bsd-license.php.
+;
+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;
+;------------------------------------------------------------------------------
+
+extern ASM_PFX(mTopOfApCommonStack)
+extern ASM_PFX(ApEntryPointInC)
+
+SECTION .data
+
+;
+; This lock only allows one AP to use the mTopOfApCommonStack stack at a time
+;
+ApStackLock:
+ dd 0
+
+SECTION .text
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApEntryPoint (
+; VOID
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmApEntryPoint)
+ASM_PFX(AsmApEntryPoint):
+ cli
+AsmApEntryPointAcquireLock:
+lock bts dword [ApStackLock], 0
+ pause
+ jc AsmApEntryPointAcquireLock
+
+ mov esp, [ASM_PFX(mTopOfApCommonStack)]
+ call ASM_PFX(ApEntryPointInC)
+
+ cli
+
+lock btc dword [ApStackLock], 0
+
+ mov eax, 0x100
+AsmApEntryPointShareLock:
+ pause
+ dec eax
+ jnz AsmApEntryPointShareLock
+
+ jmp ASM_PFX(AsmApEntryPoint)
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApDoneWithCommonStack (
+; VOID
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmApDoneWithCommonStack)
+ASM_PFX(AsmApDoneWithCommonStack):
+lock btc dword [ApStackLock], 0
+ ret
+
diff --git a/Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.S b/Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.S
new file mode 100644
index 0000000000..e82cadf369
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.S
@@ -0,0 +1,60 @@
+# TITLE CpuAsm.S:
+
+#------------------------------------------------------------------------------
+#*
+#* Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
+#* This program and the accompanying materials
+#* are licensed and made available under the terms and conditions of the BSD License
+#* which accompanies this distribution. The full text of the license may be found at
+#* http://opensource.org/licenses/bsd-license.php
+#*
+#* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#*
+#* CpuAsm.S
+#*
+#* Abstract:
+#*
+#------------------------------------------------------------------------------
+
+
+#text SEGMENT
+
+
+#------------------------------------------------------------------------------
+# VOID
+# SetCodeSelector (
+# UINT16 Selector
+# );
+#------------------------------------------------------------------------------
+ASM_GLOBAL ASM_PFX(SetCodeSelector)
+ASM_PFX(SetCodeSelector):
+ subq $0x10, %rsp
+ leaq L_setCodeSelectorLongJump(%rip), %rax
+ movq %rax, (%rsp)
+ movw %cx, 4(%rsp)
+ .byte 0xFF, 0x2C, 0x24 # jmp (%rsp) note:fword jmp
+L_setCodeSelectorLongJump:
+ addq $0x10, %rsp
+ ret
+
+#------------------------------------------------------------------------------
+# VOID
+# SetDataSelectors (
+# UINT16 Selector
+# );
+#------------------------------------------------------------------------------
+ASM_GLOBAL ASM_PFX(SetDataSelectors)
+ASM_PFX(SetDataSelectors):
+ movw %cx, %ss
+ movw %cx, %ds
+ movw %cx, %es
+ movw %cx, %fs
+ movw %cx, %gs
+ ret
+
+#text ENDS
+
+#END
+
+
diff --git a/Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.asm b/Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.asm
new file mode 100644
index 0000000000..c71b06a81e
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/X64/CpuAsm.asm
@@ -0,0 +1,54 @@
+ TITLE CpuAsm.asm:
+;------------------------------------------------------------------------------
+;*
+;* Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
+;* This program and the accompanying materials
+;* are licensed and made available under the terms and conditions of the BSD License
+;* which accompanies this distribution. The full text of the license may be found at
+;* http://opensource.org/licenses/bsd-license.php
+;*
+;* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+;* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;*
+;* CpuAsm.asm
+;*
+;* Abstract:
+;*
+;------------------------------------------------------------------------------
+
+ .code
+
+;------------------------------------------------------------------------------
+; VOID
+; SetCodeSelector (
+; UINT16 Selector
+; );
+;------------------------------------------------------------------------------
+SetCodeSelector PROC PUBLIC
+ sub rsp, 0x10
+ lea rax, setCodeSelectorLongJump
+ mov [rsp], rax
+ mov [rsp+4], cx
+ jmp fword ptr [rsp]
+setCodeSelectorLongJump:
+ add rsp, 0x10
+ ret
+SetCodeSelector ENDP
+
+;------------------------------------------------------------------------------
+; VOID
+; SetDataSelectors (
+; UINT16 Selector
+; );
+;------------------------------------------------------------------------------
+SetDataSelectors PROC PUBLIC
+ mov ss, cx
+ mov ds, cx
+ mov es, cx
+ mov fs, cx
+ mov gs, cx
+ ret
+SetDataSelectors ENDP
+
+END
+
diff --git a/Core/UefiCpuPkg/CpuDxe/X64/MpAsm.asm b/Core/UefiCpuPkg/CpuDxe/X64/MpAsm.asm
new file mode 100644
index 0000000000..308de51330
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/X64/MpAsm.asm
@@ -0,0 +1,76 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+; This program and the accompanying materials
+; are licensed and made available under the terms and conditions of the BSD License
+; which accompanies this distribution. The full text of the license may be found at
+; http://opensource.org/licenses/bsd-license.php.
+;
+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;
+;------------------------------------------------------------------------------
+
+#include <Base.h>
+
+extern ASM_PFX(mTopOfApCommonStack):QWORD
+extern ASM_PFX(ApEntryPointInC):PROC
+
+.data
+
+;
+; This lock only allows one AP to use the mTopOfApCommonStack stack at a time
+;
+ApStackLock:
+ dd 0
+
+.code
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApEntryPoint (
+; VOID
+; );
+;------------------------------------------------------------------------------
+ASM_PFX(AsmApEntryPoint) PROC PUBLIC
+
+ cli
+AsmApEntryPointAcquireLock:
+lock bts dword ptr [ApStackLock], 0
+ pause
+ jc AsmApEntryPointAcquireLock
+
+ mov rsp, [ASM_PFX(mTopOfApCommonStack)]
+ call ASM_PFX(ApEntryPointInC)
+
+ cli
+
+lock btc dword ptr [ApStackLock], 0
+
+ mov eax, 100h
+AsmApEntryPointShareLock:
+ pause
+ dec eax
+ jnz AsmApEntryPointShareLock
+
+ jmp ASM_PFX(AsmApEntryPoint)
+
+ASM_PFX(AsmApEntryPoint) ENDP
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApDoneWithCommonStack (
+; VOID
+; );
+;------------------------------------------------------------------------------
+ASM_PFX(AsmApDoneWithCommonStack) PROC PUBLIC
+
+lock btc dword ptr [ApStackLock], 0
+ ret
+
+ASM_PFX(AsmApDoneWithCommonStack) ENDP
+
+END
+
diff --git a/Core/UefiCpuPkg/CpuDxe/X64/MpAsm.nasm b/Core/UefiCpuPkg/CpuDxe/X64/MpAsm.nasm
new file mode 100644
index 0000000000..e3dc248002
--- /dev/null
+++ b/Core/UefiCpuPkg/CpuDxe/X64/MpAsm.nasm
@@ -0,0 +1,70 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+; This program and the accompanying materials
+; are licensed and made available under the terms and conditions of the BSD License
+; which accompanies this distribution. The full text of the license may be found at
+; http://opensource.org/licenses/bsd-license.php.
+;
+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+;
+;------------------------------------------------------------------------------
+
+extern ASM_PFX(mTopOfApCommonStack)
+extern ASM_PFX(ApEntryPointInC)
+
+DEFAULT REL
+
+SECTION .data
+
+;
+; This lock only allows one AP to use the mTopOfApCommonStack stack at a time
+;
+ApStackLock:
+ dd 0
+
+SECTION .text
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApEntryPoint (
+; VOID
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmApEntryPoint)
+ASM_PFX(AsmApEntryPoint):
+ cli
+AsmApEntryPointAcquireLock:
+lock bts dword [ApStackLock], 0
+ pause
+ jc AsmApEntryPointAcquireLock
+
+ mov rsp, [ASM_PFX(mTopOfApCommonStack)]
+ call ASM_PFX(ApEntryPointInC)
+
+ cli
+
+lock btc dword [ApStackLock], 0
+
+ mov eax, 0x100
+AsmApEntryPointShareLock:
+ pause
+ dec eax
+ jnz AsmApEntryPointShareLock
+
+ jmp ASM_PFX(AsmApEntryPoint)
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmApDoneWithCommonStack (
+; VOID
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmApDoneWithCommonStack)
+ASM_PFX(AsmApDoneWithCommonStack):
+lock btc dword [ApStackLock], 0
+ ret
+