/** @file\r
The X64 entrypoint is used to process capsule in long mode.\r
\r
-Copyright (c) 2011, 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
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\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
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/CpuExceptionHandlerLib.h>\r
+#include <Library/DebugAgentLib.h>\r
#include "CommonHeader.h"\r
\r
+#define EXCEPTION_VECTOR_NUMBER 0x22\r
+\r
+#define IA32_PG_P BIT0\r
+#define IA32_PG_RW BIT1\r
+#define IA32_PG_PS BIT7\r
+\r
+typedef struct _PAGE_FAULT_CONTEXT {\r
+ BOOLEAN Page1GSupport;\r
+ UINT64 PhyMask;\r
+ UINTN PageFaultBuffer;\r
+ UINTN PageFaultIndex;\r
+ UINT64 AddressEncMask;\r
+ //\r
+ // Store the uplink information for each page being used.\r
+ //\r
+ UINT64 *PageFaultUplink[EXTRA_PAGE_TABLE_PAGES];\r
+ VOID *OriginalHandler;\r
+} PAGE_FAULT_CONTEXT;\r
+\r
+typedef struct _PAGE_FAULT_IDT_TABLE {\r
+ PAGE_FAULT_CONTEXT PageFaultContext;\r
+ IA32_IDT_GATE_DESCRIPTOR IdtEntryTable[EXCEPTION_VECTOR_NUMBER];\r
+} PAGE_FAULT_IDT_TABLE;\r
+\r
+/**\r
+ Page fault handler.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PageFaultHandlerHook (\r
+ VOID\r
+ );\r
+\r
+/**\r
+ Hook IDT with our page fault handler so that the on-demand paging works on page fault.\r
+\r
+ @param[in, out] IdtEntry Pointer to IDT entry.\r
+ @param[in, out] PageFaultContext Pointer to page fault context.\r
+\r
+**/\r
+VOID\r
+HookPageFaultHandler (\r
+ IN OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry,\r
+ IN OUT PAGE_FAULT_CONTEXT *PageFaultContext\r
+ )\r
+{\r
+ UINT32 RegEax;\r
+ UINT8 PhysicalAddressBits;\r
+ UINTN PageFaultHandlerHookAddress;\r
+\r
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
+ if (RegEax >= 0x80000008) {\r
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
+ PhysicalAddressBits = (UINT8) RegEax;\r
+ } else {\r
+ PhysicalAddressBits = 36;\r
+ }\r
+ PageFaultContext->PhyMask = LShiftU64 (1, PhysicalAddressBits) - 1;\r
+ PageFaultContext->PhyMask &= (1ull << 48) - SIZE_4KB;\r
+\r
+ //\r
+ // Set Page Fault entry to catch >4G access\r
+ //\r
+ PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook;\r
+ PageFaultContext->OriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16));\r
+ IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;\r
+ IdtEntry->Bits.Selector = (UINT16)AsmReadCs ();\r
+ IdtEntry->Bits.Reserved_0 = 0;\r
+ IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
+ IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);\r
+ IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32);\r
+ IdtEntry->Bits.Reserved_1 = 0;\r
+\r
+ if (PageFaultContext->Page1GSupport) {\r
+ PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(2);\r
+ }else {\r
+ PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(6);\r
+ }\r
+ PageFaultContext->PageFaultIndex = 0;\r
+ ZeroMem (PageFaultContext->PageFaultUplink, sizeof (PageFaultContext->PageFaultUplink));\r
+}\r
+\r
+/**\r
+ Acquire page for page fault.\r
+\r
+ @param[in, out] PageFaultContext Pointer to page fault context.\r
+ @param[in, out] Uplink Pointer to up page table entry.\r
+\r
+**/\r
+VOID\r
+AcquirePage (\r
+ IN OUT PAGE_FAULT_CONTEXT *PageFaultContext,\r
+ IN OUT UINT64 *Uplink\r
+ )\r
+{\r
+ UINTN Address;\r
+ UINT64 AddressEncMask;\r
+\r
+ Address = PageFaultContext->PageFaultBuffer + EFI_PAGES_TO_SIZE (PageFaultContext->PageFaultIndex);\r
+ ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));\r
+\r
+ AddressEncMask = PageFaultContext->AddressEncMask;\r
+\r
+ //\r
+ // Cut the previous uplink if it exists and wasn't overwritten.\r
+ //\r
+ if ((PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] != NULL) &&\r
+ ((*PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] & ~AddressEncMask & PageFaultContext->PhyMask) == Address)) {\r
+ *PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = 0;\r
+ }\r
+\r
+ //\r
+ // Link & Record the current uplink.\r
+ //\r
+ *Uplink = Address | AddressEncMask | IA32_PG_P | IA32_PG_RW;\r
+ PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = Uplink;\r
+\r
+ PageFaultContext->PageFaultIndex = (PageFaultContext->PageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES;\r
+}\r
+\r
+/**\r
+ The page fault handler that on-demand read >4G memory/MMIO.\r
+\r
+ @retval NULL The page fault is correctly handled.\r
+ @retval OriginalHandler The page fault is not handled and is passed through to original handler.\r
+\r
+**/\r
+VOID *\r
+EFIAPI\r
+PageFaultHandler (\r
+ VOID\r
+ )\r
+{\r
+ IA32_DESCRIPTOR Idtr;\r
+ PAGE_FAULT_CONTEXT *PageFaultContext;\r
+ UINT64 PhyMask;\r
+ UINT64 *PageTable;\r
+ UINT64 PFAddress;\r
+ UINTN PTIndex;\r
+ UINT64 AddressEncMask;\r
+\r
+ //\r
+ // Get the IDT Descriptor.\r
+ //\r
+ AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr);\r
+ //\r
+ // Then get page fault context by IDT Descriptor.\r
+ //\r
+ PageFaultContext = (PAGE_FAULT_CONTEXT *) (UINTN) (Idtr.Base - sizeof (PAGE_FAULT_CONTEXT));\r
+ PhyMask = PageFaultContext->PhyMask;\r
+ AddressEncMask = PageFaultContext->AddressEncMask;\r
+\r
+ PFAddress = AsmReadCr2 ();\r
+ DEBUG ((EFI_D_ERROR, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress));\r
+\r
+ if (PFAddress >= PhyMask + SIZE_4KB) {\r
+ return PageFaultContext->OriginalHandler;\r
+ }\r
+ PFAddress &= PhyMask;\r
+\r
+ PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & PhyMask);\r
+\r
+ PTIndex = BitFieldRead64 (PFAddress, 39, 47);\r
+ // PML4E\r
+ if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
+ AcquirePage (PageFaultContext, &PageTable[PTIndex]);\r
+ }\r
+ PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask);\r
+ PTIndex = BitFieldRead64 (PFAddress, 30, 38);\r
+ // PDPTE\r
+ if (PageFaultContext->Page1GSupport) {\r
+ PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;\r
+ } else {\r
+ if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
+ AcquirePage (PageFaultContext, &PageTable[PTIndex]);\r
+ }\r
+ PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask);\r
+ PTIndex = BitFieldRead64 (PFAddress, 21, 29);\r
+ // PD\r
+ PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+\r
/**\r
The X64 entrypoint is used to process capsule in long mode then\r
return to 32-bit protected mode.\r
SWITCH_64_TO_32_CONTEXT *ReturnContext\r
)\r
{\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
+ IA32_DESCRIPTOR Ia32Idtr;\r
+ IA32_DESCRIPTOR X64Idtr;\r
+ PAGE_FAULT_IDT_TABLE PageFaultIdtTable;\r
+ IA32_IDT_GATE_DESCRIPTOR *IdtEntry;\r
+\r
+ //\r
+ // Save the IA32 IDT Descriptor\r
+ //\r
+ AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);\r
+\r
+ //\r
+ // Setup X64 IDT table\r
+ //\r
+ ZeroMem (PageFaultIdtTable.IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER);\r
+ X64Idtr.Base = (UINTN) PageFaultIdtTable.IdtEntryTable;\r
+ X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER - 1);\r
+ AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr);\r
+\r
+ //\r
+ // Setup the default CPU exception handlers\r
+ //\r
+ Status = InitializeCpuExceptionHandlers (NULL);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Hook page fault handler to handle >4G request.\r
+ //\r
+ PageFaultIdtTable.PageFaultContext.Page1GSupport = EntrypointContext->Page1GSupport;\r
+ PageFaultIdtTable.PageFaultContext.AddressEncMask = EntrypointContext->AddressEncMask;\r
+ IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) (X64Idtr.Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));\r
+ HookPageFaultHandler (IdtEntry, &(PageFaultIdtTable.PageFaultContext));\r
+\r
+ //\r
+ // Initialize Debug Agent to support source level debug\r
+ //\r
+ InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *) &Ia32Idtr, NULL);\r
\r
//\r
// Call CapsuleDataCoalesce to process capsule.\r
Status = CapsuleDataCoalesce (\r
NULL,\r
(EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr,\r
+ (MEMORY_RESOURCE_DESCRIPTOR *) (UINTN) EntrypointContext->MemoryResource,\r
(VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr,\r
(UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr\r
);\r
- \r
+\r
ReturnContext->ReturnStatus = Status;\r
\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",\r
+ __FUNCTION__,\r
+ EntrypointContext->StackBufferBase,\r
+ EntrypointContext->StackBufferLength\r
+ ));\r
+\r
+ //\r
+ // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode\r
+ //\r
+ SaveAndSetDebugTimerInterrupt (FALSE);\r
+ //\r
+ // Restore IA32 IDT table\r
+ //\r
+ AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);\r
+\r
//\r
// Finish to coalesce capsule, and return to 32-bit mode.\r
//\r
(UINT32) (UINTN) EntrypointContext,\r
(UINT32) (UINTN) ReturnContext,\r
(UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength)\r
- ); \r
- \r
+ );\r
+\r
//\r
// Should never be here.\r
//\r
ASSERT (FALSE);\r
return EFI_SUCCESS;\r
-}
\ No newline at end of file
+}\r