From 533263ee5a7fe83ad5a0d2da74ff30815c5ab63c Mon Sep 17 00:00:00 2001 From: Jordan Justen Date: Thu, 13 Nov 2014 18:25:29 +0000 Subject: UefiCpuPkg/CpuDxe: Add StartApsStackless routine This routine starts the APs and directs them to run the specified code. The specified code is entered without a stack being available. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jordan Justen Signed-off-by: Chen Fan Reviewed-by: Jeff Fan git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16349 6f19259b-4bc3-4df7-8a09-765794883524 --- UefiCpuPkg/CpuDxe/ApStartup.c | 304 ++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuDxe/CpuDxe.h | 1 + UefiCpuPkg/CpuDxe/CpuDxe.inf | 3 + UefiCpuPkg/CpuDxe/CpuMp.h | 24 ++++ 4 files changed, 332 insertions(+) create mode 100644 UefiCpuPkg/CpuDxe/ApStartup.c (limited to 'UefiCpuPkg') diff --git a/UefiCpuPkg/CpuDxe/ApStartup.c b/UefiCpuPkg/CpuDxe/ApStartup.c new file mode 100644 index 0000000000..5477c52672 --- /dev/null +++ b/UefiCpuPkg/CpuDxe/ApStartup.c @@ -0,0 +1,304 @@ +/** @file + CPU DXE AP Startup + + Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
+ 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 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]; + +#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) + 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, +#endif // defined (MDE_CPU_X64) + + //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) + +}; + + +/** + 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. + + @param ApEntryPoint Pointer to the Entry Point routine + + @retval EFI_SUCCESS The APs were started + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory to start APs + +**/ +EFI_STATUS +StartApsStackless ( + IN STACKLESS_AP_ENTRY_POINT ApEntryPoint + ) +{ + EFI_STATUS Status; + volatile STARTUP_CODE *StartupCode; + 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) ApEntryPoint; + + StartupCode->FlatJmpOffset += (UINT32) StartAddress; + +#if defined (MDE_CPU_X64) + StartupCode->Cr3Value = (UINT32) AsmReadCr3 (); + StartupCode->LongJmpOffset += (UINT32) StartAddress; +#endif + + SendInitSipiSipiAllExcludingSelf ((UINT32)(UINTN)(VOID*) StartupCode); + + // + // Wait 100 milliseconds for APs to arrive at the ApEntryPoint routine + // + MicroSecondDelay (100 * 1000); + + gBS->FreePages (StartAddress, EFI_SIZE_TO_PAGES (sizeof (*StartupCode))); + + return EFI_SUCCESS; +} + diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.h b/UefiCpuPkg/CpuDxe/CpuDxe.h index 2001cfc605..2aef626cd5 100644 --- a/UefiCpuPkg/CpuDxe/CpuDxe.h +++ b/UefiCpuPkg/CpuDxe/CpuDxe.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf index 9952eb7007..c2f12b7bba 100644 --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf @@ -41,11 +41,14 @@ UefiCpuLib UefiLib CpuExceptionHandlerLib + TimerLib [Sources] + ApStartup.c CpuDxe.c CpuDxe.h CpuGdt.c + CpuGdt.h CpuMp.c CpuMp.h diff --git a/UefiCpuPkg/CpuDxe/CpuMp.h b/UefiCpuPkg/CpuDxe/CpuMp.h index dd2d0e1200..e28f162f53 100644 --- a/UefiCpuPkg/CpuDxe/CpuMp.h +++ b/UefiCpuPkg/CpuDxe/CpuMp.h @@ -24,6 +24,30 @@ 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. + + @param ApEntryPoint Pointer to the Entry Point routine + + @retval EFI_SUCCESS The APs were started + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory to start APs + +**/ +EFI_STATUS +StartApsStackless ( + IN STACKLESS_AP_ENTRY_POINT ApEntryPoint + ); + /** The AP entry point that the Startup-IPI target code will jump to. -- cgit v1.2.3