]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / CapsulePei / X64 / X64Entry.c
index 56913e6b03b8d420ceeed77e80bc5593c72c0cc0..4a0567fa7664fb83948d02b25da9779551183e01 100644 (file)
@@ -1,14 +1,10 @@
 /** @file\r
   The X64 entrypoint is used to process capsule in long mode.\r
 \r
-Copyright (c) 2011 - 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
+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
@@ -20,6 +16,191 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 \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
@@ -40,26 +221,36 @@ _ModuleEntryPoint (
   EFI_STATUS                    Status;\r
   IA32_DESCRIPTOR               Ia32Idtr;\r
   IA32_DESCRIPTOR               X64Idtr;\r
-  IA32_IDT_GATE_DESCRIPTOR      IdtEntryTable[EXCEPTION_VECTOR_NUMBER];\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
+  AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);\r
 \r
   //\r
   // Setup X64 IDT table\r
   //\r
-  ZeroMem (IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER);\r
-  X64Idtr.Base = (UINTN) IdtEntryTable;\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
+  AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr);\r
 \r
   //\r
   // Setup the default CPU exception handlers\r
   //\r
-  SetupCpuExceptionHandlers ();                \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
@@ -71,12 +262,21 @@ _ModuleEntryPoint (
   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
@@ -84,8 +284,8 @@ _ModuleEntryPoint (
   //\r
   // Restore IA32 IDT table\r
   //\r
-  AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);  \r
-  \r
+  AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);\r
+\r
   //\r
   // Finish to coalesce capsule, and return to 32-bit mode.\r
   //\r
@@ -95,11 +295,11 @@ _ModuleEntryPoint (
     (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