--- /dev/null
+/** @file\r
+ CPU DXE AP Startup\r
+\r
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "CpuDxe.h"\r
+#include "CpuGdt.h"\r
+#include "CpuMp.h"\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct {\r
+ UINT8 JmpToCli[2];\r
+\r
+ UINT16 GdtLimit;\r
+ UINT32 GdtBase;\r
+\r
+ UINT8 Cli;\r
+\r
+ UINT8 MovAxRealSegment; UINT16 RealSegment;\r
+ UINT8 MovDsAx[2];\r
+\r
+ UINT8 MovBxGdtr[3];\r
+ UINT8 LoadGdt[5];\r
+\r
+ UINT8 MovEaxCr0[2];\r
+ UINT32 MovEaxCr0Value;\r
+ UINT8 MovCr0Eax[3];\r
+\r
+ UINT8 FarJmp32Flat[2]; UINT32 FlatJmpOffset; UINT16 FlatJmpSelector;\r
+\r
+ //\r
+ // Now in IA32\r
+ //\r
+ UINT8 MovEaxCr4;\r
+ UINT32 MovEaxCr4Value;\r
+ UINT8 MovCr4Eax[3];\r
+\r
+ UINT8 MoveDataSelectorIntoAx[2]; UINT16 FlatDataSelector;\r
+ UINT8 MoveFlatDataSelectorFromAxToDs[2];\r
+ UINT8 MoveFlatDataSelectorFromAxToEs[2];\r
+ UINT8 MoveFlatDataSelectorFromAxToFs[2];\r
+ UINT8 MoveFlatDataSelectorFromAxToGs[2];\r
+ UINT8 MoveFlatDataSelectorFromAxToSs[2];\r
+\r
+#if defined (MDE_CPU_X64)\r
+ //\r
+ // Transition to X64\r
+ //\r
+ UINT8 MovEaxCr3;\r
+ UINT32 Cr3Value;\r
+ UINT8 MovCr3Eax[3];\r
+\r
+ UINT8 MoveCr4ToEax[3];\r
+ UINT8 SetCr4Bit5[4];\r
+ UINT8 MoveEaxToCr4[3];\r
+\r
+ UINT8 MoveLongModeEnableMsrToEcx[5];\r
+ UINT8 ReadLmeMsr[2];\r
+ UINT8 SetLongModeEnableBit[4];\r
+ UINT8 WriteLmeMsr[2];\r
+\r
+ UINT8 MoveCr0ToEax[3];\r
+ UINT8 SetCr0PagingBit[4];\r
+ UINT8 MoveEaxToCr0[3];\r
+ //UINT8 DeadLoop[2];\r
+\r
+ UINT8 FarJmp32LongMode; UINT32 LongJmpOffset; UINT16 LongJmpSelector;\r
+#endif // defined (MDE_CPU_X64)\r
+\r
+#if defined (MDE_CPU_X64)\r
+ UINT8 MovEaxOrRaxCpuDxeEntry[2]; UINTN CpuDxeEntryValue;\r
+#else\r
+ UINT8 MovEaxOrRaxCpuDxeEntry; UINTN CpuDxeEntryValue;\r
+#endif\r
+ UINT8 JmpToCpuDxeEntry[2];\r
+\r
+} STARTUP_CODE;\r
+\r
+#pragma pack()\r
+\r
+/**\r
+ This .asm code used for translating processor from 16 bit real mode into\r
+ 64 bit long mode. which help to create the mStartupCodeTemplate value.\r
+\r
+ To assemble:\r
+ * nasm -o ApStartup ApStartup.asm\r
+ Then disassemble:\r
+ * ndisasm -b 16 ApStartup\r
+ * ndisasm -b 16 -e 6 ApStartup\r
+ * ndisasm -b 32 -e 32 ApStartup (This -e offset may need adjustment)\r
+ * ndisasm -b 64 -e 0x83 ApStartup (This -e offset may need adjustment)\r
+\r
+ %define DEFAULT_CR0 0x00000023\r
+ %define DEFAULT_CR4 0x640\r
+\r
+ BITS 16\r
+\r
+ jmp short TransitionFromReal16To32BitFlat\r
+\r
+ ALIGN 2\r
+\r
+ Gdtr:\r
+ dw 0x5a5a\r
+ dd 0x5a5a5a5a\r
+\r
+ ;\r
+ ; Modified: EAX, EBX\r
+ ;\r
+ TransitionFromReal16To32BitFlat:\r
+\r
+ cli\r
+ mov ax, 0x5a5a\r
+ mov ds, ax\r
+\r
+ mov bx, Gdtr\r
+ o32 lgdt [ds:bx]\r
+\r
+ mov eax, cr4\r
+ btc eax, 5\r
+ mov cr4, eax\r
+\r
+ mov eax, DEFAULT_CR0\r
+ mov cr0, eax\r
+\r
+ jmp 0x5a5a:dword jumpTo32BitAndLandHere\r
+ BITS 32\r
+ jumpTo32BitAndLandHere:\r
+\r
+ mov eax, DEFAULT_CR4\r
+ mov cr4, eax\r
+\r
+ mov ax, 0x5a5a\r
+ mov ds, ax\r
+ mov es, ax\r
+ mov fs, ax\r
+ mov gs, ax\r
+ mov ss, ax\r
+\r
+ ;\r
+ ; Jump to CpuDxe for IA32\r
+ ;\r
+ mov eax, 0x5a5a5a5a\r
+ or eax, eax\r
+ jz Transition32FlatTo64Flat\r
+ jmp eax\r
+\r
+ ;\r
+ ; Transition to X64\r
+ ;\r
+ Transition32FlatTo64Flat:\r
+ mov eax, 0x5a5a5a5a\r
+ mov cr3, eax\r
+\r
+ mov eax, cr4\r
+ bts eax, 5 ; enable PAE\r
+ mov cr4, eax\r
+\r
+ mov ecx, 0xc0000080\r
+ rdmsr\r
+ bts eax, 8 ; set LME\r
+ wrmsr\r
+\r
+ mov eax, cr0\r
+ bts eax, 31 ; set PG\r
+ mov cr0, eax ; enable paging\r
+\r
+ ;\r
+ ; Jump to CpuDxe for X64\r
+ ;\r
+ jmp 0x5a5a:jumpTo64BitAndLandHere\r
+ BITS 64\r
+ jumpTo64BitAndLandHere:\r
+ mov rax, 0xcdcdcdcdcdcdcdcd\r
+ jmp rax\r
+**/\r
+STARTUP_CODE mStartupCodeTemplate = {\r
+ { 0xeb, 0x06 }, // Jump to cli\r
+ 0, // GDT Limit\r
+ 0, // GDT Base\r
+ 0xfa, // cli (Clear Interrupts)\r
+ 0xb8, 0x0000, // mov ax, RealSegment\r
+ { 0x8e, 0xd8 }, // mov ds, ax\r
+ { 0xBB, 0x02, 0x00 }, // mov bx, Gdtr\r
+ { 0x3e, 0x66, 0x0f, 0x01, 0x17 }, // lgdt [ds:bx]\r
+ { 0x66, 0xB8 }, 0x00000023, // mov eax, cr0 value\r
+ { 0x0F, 0x22, 0xC0 }, // mov cr0, eax\r
+ { 0x66, 0xEA }, // far jmp to 32-bit flat\r
+ OFFSET_OF(STARTUP_CODE, MovEaxCr4),\r
+ LINEAR_CODE_SEL,\r
+ 0xB8, 0x00000640, // mov eax, cr4 value\r
+ { 0x0F, 0x22, 0xe0 }, // mov cr4, eax\r
+ { 0x66, 0xb8 }, CPU_DATA_SEL, // mov ax, FlatDataSelector\r
+ { 0x8e, 0xd8 }, // mov ds, ax\r
+ { 0x8e, 0xc0 }, // mov es, ax\r
+ { 0x8e, 0xe0 }, // mov fs, ax\r
+ { 0x8e, 0xe8 }, // mov gs, ax\r
+ { 0x8e, 0xd0 }, // mov ss, ax\r
+\r
+#if defined (MDE_CPU_X64)\r
+ 0xB8, 0x00000000, // mov eax, cr3 value\r
+ { 0x0F, 0x22, 0xd8 }, // mov cr3, eax\r
+\r
+ { 0x0F, 0x20, 0xE0 }, // mov eax, cr4\r
+ { 0x0F, 0xBA, 0xE8, 0x05 }, // bts eax, 5\r
+ { 0x0F, 0x22, 0xE0 }, // mov cr4, eax\r
+\r
+ { 0xB9, 0x80, 0x00, 0x00, 0xC0 }, // mov ecx, 0xc0000080\r
+ { 0x0F, 0x32 }, // rdmsr\r
+ { 0x0F, 0xBA, 0xE8, 0x08 }, // bts eax, 8\r
+ { 0x0F, 0x30 }, // wrmsr\r
+\r
+ { 0x0F, 0x20, 0xC0 }, // mov eax, cr0\r
+ { 0x0F, 0xBA, 0xE8, 0x1F }, // bts eax, 31\r
+ { 0x0F, 0x22, 0xC0 }, // mov cr0, eax\r
+\r
+ 0xEA, // FarJmp32LongMode\r
+ OFFSET_OF(STARTUP_CODE, MovEaxOrRaxCpuDxeEntry),\r
+ LINEAR_CODE64_SEL,\r
+#endif // defined (MDE_CPU_X64)\r
+\r
+ //0xeb, 0xfe, // jmp $\r
+#if defined (MDE_CPU_X64)\r
+ { 0x48, 0xb8 }, 0x0, // mov rax, X64 CpuDxe MP Entry Point\r
+#else\r
+ 0xB8, 0x0, // mov eax, IA32 CpuDxe MP Entry Point\r
+#endif\r
+ { 0xff, 0xe0 }, // jmp to eax/rax (CpuDxe MP Entry Point)\r
+\r
+};\r
+\r
+\r
+/**\r
+ Starts the Application Processors and directs them to jump to the\r
+ specified routine.\r
+\r
+ The processor jumps to this code in flat mode, but the processor's\r
+ stack is not initialized.\r
+\r
+ @param ApEntryPoint Pointer to the Entry Point routine\r
+\r
+ @retval EFI_SUCCESS The APs were started\r
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory to start APs\r
+\r
+**/\r
+EFI_STATUS\r
+StartApsStackless (\r
+ IN STACKLESS_AP_ENTRY_POINT ApEntryPoint\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ volatile STARTUP_CODE *StartupCode;\r
+ IA32_DESCRIPTOR Gdtr;\r
+ EFI_PHYSICAL_ADDRESS StartAddress;\r
+\r
+ StartAddress = BASE_1MB;\r
+ Status = gBS->AllocatePages (\r
+ AllocateMaxAddress,\r
+ EfiACPIMemoryNVS,\r
+ EFI_SIZE_TO_PAGES (sizeof (*StartupCode)),\r
+ &StartAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ StartupCode = (STARTUP_CODE*)(VOID*)(UINTN) StartAddress;\r
+ CopyMem ((VOID*) StartupCode, &mStartupCodeTemplate, sizeof (*StartupCode));\r
+ StartupCode->RealSegment = (UINT16) (((UINTN) StartAddress) >> 4);\r
+\r
+ AsmReadGdtr (&Gdtr);\r
+ StartupCode->GdtLimit = Gdtr.Limit;\r
+ StartupCode->GdtBase = (UINT32) Gdtr.Base;\r
+\r
+ StartupCode->CpuDxeEntryValue = (UINTN) ApEntryPoint;\r
+\r
+ StartupCode->FlatJmpOffset += (UINT32) StartAddress;\r
+\r
+#if defined (MDE_CPU_X64)\r
+ StartupCode->Cr3Value = (UINT32) AsmReadCr3 ();\r
+ StartupCode->LongJmpOffset += (UINT32) StartAddress;\r
+#endif\r
+\r
+ SendInitSipiSipiAllExcludingSelf ((UINT32)(UINTN)(VOID*) StartupCode);\r
+\r
+ //\r
+ // Wait 100 milliseconds for APs to arrive at the ApEntryPoint routine\r
+ //\r
+ MicroSecondDelay (100 * 1000);\r
+\r
+ gBS->FreePages (StartAddress, EFI_SIZE_TO_PAGES (sizeof (*StartupCode)));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r