| /** @file | |
| Ia32-specific functionality for DxeLoad. | |
| Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR> | |
| Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <PiPei.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/PcdLib.h> | |
| #include <Library/HobLib.h> | |
| #include "VirtualMemory.h" | |
| #include "UefiPayloadEntry.h" | |
| #define STACK_SIZE 0x20000 | |
| #define IDT_ENTRY_COUNT 32 | |
| typedef struct _X64_IDT_TABLE { | |
| // | |
| // Reserved 4 bytes preceding PeiService and IdtTable, | |
| // since IDT base address should be 8-byte alignment. | |
| // | |
| UINT32 Reserved; | |
| CONST EFI_PEI_SERVICES **PeiService; | |
| X64_IDT_GATE_DESCRIPTOR IdtTable[IDT_ENTRY_COUNT]; | |
| } X64_IDT_TABLE; | |
| // | |
| // Global Descriptor Table (GDT) | |
| // | |
| GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT gGdtEntries[] = { | |
| /* selector { Global Segment Descriptor } */ | |
| /* 0x00 */ { | |
| { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | |
| }, // null descriptor | |
| /* 0x08 */ { | |
| { 0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 } | |
| }, // linear data segment descriptor | |
| /* 0x10 */ { | |
| { 0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 } | |
| }, // linear code segment descriptor | |
| /* 0x18 */ { | |
| { 0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 } | |
| }, // system data segment descriptor | |
| /* 0x20 */ { | |
| { 0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 } | |
| }, // system code segment descriptor | |
| /* 0x28 */ { | |
| { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | |
| }, // spare segment descriptor | |
| /* 0x30 */ { | |
| { 0xffff, 0, 0, 0x2, 1, 0, 1, 0xf, 0, 0, 1, 1, 0 } | |
| }, // system data segment descriptor | |
| /* 0x38 */ { | |
| { 0xffff, 0, 0, 0xa, 1, 0, 1, 0xf, 0, 1, 0, 1, 0 } | |
| }, // system code segment descriptor | |
| /* 0x40 */ { | |
| { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | |
| }, // spare segment descriptor | |
| }; | |
| // | |
| // IA32 Gdt register | |
| // | |
| GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR gGdt = { | |
| sizeof (gGdtEntries) - 1, | |
| (UINTN)gGdtEntries | |
| }; | |
| GLOBAL_REMOVE_IF_UNREFERENCED IA32_DESCRIPTOR gLidtDescriptor = { | |
| sizeof (X64_IDT_GATE_DESCRIPTOR) * IDT_ENTRY_COUNT - 1, | |
| 0 | |
| }; | |
| /** | |
| Allocates and fills in the Page Directory and Page Table Entries to | |
| establish a 4G page table. | |
| @param[in] StackBase Stack base address. | |
| @param[in] StackSize Stack size. | |
| @return The address of page table. | |
| **/ | |
| UINTN | |
| Create4GPageTablesIa32Pae ( | |
| IN EFI_PHYSICAL_ADDRESS StackBase, | |
| IN UINTN StackSize | |
| ) | |
| { | |
| UINT8 PhysicalAddressBits; | |
| EFI_PHYSICAL_ADDRESS PhysicalAddress; | |
| UINTN IndexOfPdpEntries; | |
| UINTN IndexOfPageDirectoryEntries; | |
| UINT32 NumberOfPdpEntriesNeeded; | |
| PAGE_MAP_AND_DIRECTORY_POINTER *PageMap; | |
| PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry; | |
| PAGE_TABLE_ENTRY *PageDirectoryEntry; | |
| UINTN TotalPagesNum; | |
| UINTN PageAddress; | |
| UINT64 AddressEncMask; | |
| // | |
| // Make sure AddressEncMask is contained to smallest supported address field | |
| // | |
| AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; | |
| PhysicalAddressBits = 32; | |
| // | |
| // Calculate the table entries needed. | |
| // | |
| NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30)); | |
| TotalPagesNum = NumberOfPdpEntriesNeeded + 1; | |
| PageAddress = (UINTN)AllocatePageTableMemory (TotalPagesNum); | |
| ASSERT (PageAddress != 0); | |
| PageMap = (VOID *)PageAddress; | |
| PageAddress += SIZE_4KB; | |
| PageDirectoryPointerEntry = PageMap; | |
| PhysicalAddress = 0; | |
| for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { | |
| // | |
| // Each Directory Pointer entries points to a page of Page Directory entires. | |
| // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop. | |
| // | |
| PageDirectoryEntry = (VOID *)PageAddress; | |
| PageAddress += SIZE_4KB; | |
| // | |
| // Fill in a Page Directory Pointer Entries | |
| // | |
| PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask; | |
| PageDirectoryPointerEntry->Bits.Present = 1; | |
| for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress += SIZE_2MB) { | |
| if ( (IsNullDetectionEnabled () && (PhysicalAddress == 0)) | |
| || ( (PhysicalAddress < StackBase + StackSize) | |
| && ((PhysicalAddress + SIZE_2MB) > StackBase))) | |
| { | |
| // | |
| // Need to split this 2M page that covers stack range. | |
| // | |
| Split2MPageTo4K (PhysicalAddress, (UINT64 *)PageDirectoryEntry, StackBase, StackSize, 0, 0); | |
| } else { | |
| // | |
| // Fill in the Page Directory entries | |
| // | |
| PageDirectoryEntry->Uint64 = (UINT64)PhysicalAddress | AddressEncMask; | |
| PageDirectoryEntry->Bits.ReadWrite = 1; | |
| PageDirectoryEntry->Bits.Present = 1; | |
| PageDirectoryEntry->Bits.MustBe1 = 1; | |
| } | |
| } | |
| } | |
| for ( ; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { | |
| ZeroMem ( | |
| PageDirectoryPointerEntry, | |
| sizeof (PAGE_MAP_AND_DIRECTORY_POINTER) | |
| ); | |
| } | |
| // | |
| // Protect the page table by marking the memory used for page table to be | |
| // read-only. | |
| // | |
| EnablePageTableProtection ((UINTN)PageMap, FALSE); | |
| return (UINTN)PageMap; | |
| } | |
| /** | |
| The function will check if IA32 PAE is supported. | |
| @retval TRUE IA32 PAE is supported. | |
| @retval FALSE IA32 PAE is not supported. | |
| **/ | |
| BOOLEAN | |
| IsIa32PaeSupport ( | |
| VOID | |
| ) | |
| { | |
| UINT32 RegEax; | |
| UINT32 RegEdx; | |
| BOOLEAN Ia32PaeSupport; | |
| Ia32PaeSupport = FALSE; | |
| AsmCpuid (0x0, &RegEax, NULL, NULL, NULL); | |
| if (RegEax >= 0x1) { | |
| AsmCpuid (0x1, NULL, NULL, NULL, &RegEdx); | |
| if ((RegEdx & BIT6) != 0) { | |
| Ia32PaeSupport = TRUE; | |
| } | |
| } | |
| return Ia32PaeSupport; | |
| } | |
| /** | |
| The function will check if page table should be setup or not. | |
| @retval TRUE Page table should be created. | |
| @retval FALSE Page table should not be created. | |
| **/ | |
| BOOLEAN | |
| ToBuildPageTable ( | |
| VOID | |
| ) | |
| { | |
| if (!IsIa32PaeSupport ()) { | |
| return FALSE; | |
| } | |
| if (IsNullDetectionEnabled ()) { | |
| return TRUE; | |
| } | |
| if (PcdGet8 (PcdHeapGuardPropertyMask) != 0) { | |
| return TRUE; | |
| } | |
| if (PcdGetBool (PcdCpuStackGuard)) { | |
| return TRUE; | |
| } | |
| if (IsEnableNonExecNeeded ()) { | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Transfers control to DxeCore. | |
| This function performs a CPU architecture specific operations to execute | |
| the entry point of DxeCore with the parameters of HobList. | |
| @param DxeCoreEntryPoint The entry point of DxeCore. | |
| @param HobList The start of HobList passed to DxeCore. | |
| **/ | |
| VOID | |
| HandOffToDxeCore ( | |
| IN EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint, | |
| IN EFI_PEI_HOB_POINTERS HobList | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS BaseOfStack; | |
| EFI_PHYSICAL_ADDRESS TopOfStack; | |
| UINTN PageTables; | |
| X64_IDT_GATE_DESCRIPTOR *IdtTable; | |
| UINTN SizeOfTemplate; | |
| VOID *TemplateBase; | |
| EFI_PHYSICAL_ADDRESS VectorAddress; | |
| UINT32 Index; | |
| X64_IDT_TABLE *IdtTableForX64; | |
| // | |
| // Clear page 0 and mark it as allocated if NULL pointer detection is enabled. | |
| // | |
| if (IsNullDetectionEnabled ()) { | |
| ClearFirst4KPage (HobList.Raw); | |
| BuildMemoryAllocationHob (0, EFI_PAGES_TO_SIZE (1), EfiBootServicesData); | |
| } | |
| BaseOfStack = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (STACK_SIZE)); | |
| ASSERT (BaseOfStack != 0); | |
| if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { | |
| // | |
| // Compute the top of the stack we were allocated, which is used to load X64 dxe core. | |
| // Pre-allocate a 32 bytes which confroms to x64 calling convention. | |
| // | |
| // The first four parameters to a function are passed in rcx, rdx, r8 and r9. | |
| // Any further parameters are pushed on the stack. Furthermore, space (4 * 8bytes) for the | |
| // register parameters is reserved on the stack, in case the called function | |
| // wants to spill them; this is important if the function is variadic. | |
| // | |
| TopOfStack = BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - 32; | |
| // | |
| // x64 Calling Conventions requires that the stack must be aligned to 16 bytes | |
| // | |
| TopOfStack = (EFI_PHYSICAL_ADDRESS)(UINTN)ALIGN_POINTER (TopOfStack, 16); | |
| // | |
| // Load the GDT of Go64. Since the GDT of 32-bit Tiano locates in the BS_DATA | |
| // memory, it may be corrupted when copying FV to high-end memory | |
| // | |
| AsmWriteGdtr (&gGdt); | |
| // | |
| // Create page table and save PageMapLevel4 to CR3 | |
| // | |
| PageTables = CreateIdentityMappingPageTables (BaseOfStack, STACK_SIZE, 0, 0); | |
| // | |
| // Paging might be already enabled. To avoid conflict configuration, | |
| // disable paging first anyway. | |
| // | |
| AsmWriteCr0 (AsmReadCr0 () & (~BIT31)); | |
| AsmWriteCr3 (PageTables); | |
| // | |
| // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore. | |
| // | |
| UpdateStackHob (BaseOfStack, STACK_SIZE); | |
| SizeOfTemplate = AsmGetVectorTemplatInfo (&TemplateBase); | |
| VectorAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (sizeof (X64_IDT_TABLE) + SizeOfTemplate * IDT_ENTRY_COUNT)); | |
| ASSERT (VectorAddress != 0); | |
| // | |
| // Store EFI_PEI_SERVICES** in the 4 bytes immediately preceding IDT to avoid that | |
| // it may not be gotten correctly after IDT register is re-written. | |
| // | |
| IdtTableForX64 = (X64_IDT_TABLE *)(UINTN)VectorAddress; | |
| IdtTableForX64->PeiService = NULL; | |
| VectorAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(IdtTableForX64 + 1); | |
| IdtTable = IdtTableForX64->IdtTable; | |
| for (Index = 0; Index < IDT_ENTRY_COUNT; Index++) { | |
| IdtTable[Index].Ia32IdtEntry.Bits.GateType = 0x8e; | |
| IdtTable[Index].Ia32IdtEntry.Bits.Reserved_0 = 0; | |
| IdtTable[Index].Ia32IdtEntry.Bits.Selector = SYS_CODE64_SEL; | |
| IdtTable[Index].Ia32IdtEntry.Bits.OffsetLow = (UINT16)VectorAddress; | |
| IdtTable[Index].Ia32IdtEntry.Bits.OffsetHigh = (UINT16)(RShiftU64 (VectorAddress, 16)); | |
| IdtTable[Index].Offset32To63 = (UINT32)(RShiftU64 (VectorAddress, 32)); | |
| IdtTable[Index].Reserved = 0; | |
| CopyMem ((VOID *)(UINTN)VectorAddress, TemplateBase, SizeOfTemplate); | |
| AsmVectorFixup ((VOID *)(UINTN)VectorAddress, (UINT8)Index); | |
| VectorAddress += SizeOfTemplate; | |
| } | |
| gLidtDescriptor.Base = (UINTN)IdtTable; | |
| AsmWriteIdtr (&gLidtDescriptor); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a() Stack Base: 0x%lx, Stack Size: 0x%x\n", | |
| __func__, | |
| BaseOfStack, | |
| STACK_SIZE | |
| )); | |
| // | |
| // Go to Long Mode and transfer control to DxeCore. | |
| // Interrupts will not get turned on until the CPU AP is loaded. | |
| // Call x64 drivers passing in single argument, a pointer to the HOBs. | |
| // | |
| AsmEnablePaging64 ( | |
| SYS_CODE64_SEL, | |
| DxeCoreEntryPoint, | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)(HobList.Raw), | |
| 0, | |
| TopOfStack | |
| ); | |
| } else { | |
| // 32bit UEFI payload could be supported if required later. | |
| DEBUG ((DEBUG_ERROR, "NOT support 32bit UEFI payload\n")); | |
| ASSERT (FALSE); | |
| CpuDeadLoop (); | |
| } | |
| } |