]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg CapsuleX64: Reduce reserved memory consumption
authorStar Zeng <star.zeng@intel.com>
Mon, 27 Jul 2015 03:04:41 +0000 (03:04 +0000)
committerlzeng14 <lzeng14@Edk2>
Mon, 27 Jul 2015 03:04:41 +0000 (03:04 +0000)
We are going to reduce reserved memory consumption by page table buffer,
then OS can have more available memory to use.
Take PhysicalAddressBits = 48 and 2MB page granularity as example,
1:1 Virtual to Physical identity mapping page table buffer needs to be
((512 + 1) * 512 + 1) * 4096 = 1075843072 bytes = 0x40201000 bytes.

The code is updated to build 4G page table by default and only use 8 extra
pages to handles > 4G request by page fault.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Star Zeng <star.zeng@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18069 6f19259b-4bc3-4df7-8a09-765794883524

MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf
MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h
MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.S [new file with mode: 0644]
MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.asm [new file with mode: 0644]
MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c

index f9d9971ef1637f0d4123ada9723cd89e17ac9269..1630dfc383afaeb6b9fd7844177e7b946415f9c2 100644 (file)
@@ -9,7 +9,7 @@
 #  This external input must be validated carefully to avoid security issue like\r
 #  buffer overflow, integer overflow.\r
 #\r
-# Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>\r
 #\r
 # This program and the accompanying materials\r
 # are licensed and made available under the terms and conditions\r
@@ -38,6 +38,8 @@
 \r
 [Sources]\r
   X64/X64Entry.c\r
+  X64/PageFaultHandler.asm\r
+  X64/PageFaultHandler.S\r
   Common/CapsuleCoalesce.c\r
 \r
 [Packages]\r
index d1ed24a572307c2e0446c8d92bbce2b19fa10487..6210d2133ebc3baa25fdb9d3a7fc76e4c1ee86c5 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Common header file.\r
 \r
-Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2011 - 2015, 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
@@ -15,6 +15,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #ifndef _CAPSULE_COMMON_HEADER_\r
 #define _CAPSULE_COMMON_HEADER_\r
 \r
+//\r
+// 8 extra pages for PF handler.\r
+//\r
+#define EXTRA_PAGE_TABLE_PAGES      8\r
+\r
 //\r
 // This capsule PEIM puts its private data at the start of the\r
 // coalesced capsule. Here's the structure definition.\r
@@ -33,6 +38,7 @@ typedef struct {
 #define CAPSULE_TEST_SIGNATURE SIGNATURE_32('T', 'E', 'S', 'T')\r
 \r
 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)\r
+#pragma pack(1)\r
 typedef struct {\r
   EFI_PHYSICAL_ADDRESS  EntryPoint;\r
   EFI_PHYSICAL_ADDRESS  StackBufferBase;\r
@@ -41,14 +47,23 @@ typedef struct {
   EFI_PHYSICAL_ADDRESS  BlockListAddr;\r
   EFI_PHYSICAL_ADDRESS  MemoryBase64Ptr;\r
   EFI_PHYSICAL_ADDRESS  MemorySize64Ptr;\r
+  BOOLEAN               Page1GSupport;\r
 } SWITCH_32_TO_64_CONTEXT;\r
 \r
 typedef struct {\r
   UINT16                ReturnCs;\r
   EFI_PHYSICAL_ADDRESS  ReturnEntryPoint;\r
   UINT64                ReturnStatus;\r
+  //\r
+  // NOTICE:\r
+  // Be careful about the Base field of IA32_DESCRIPTOR\r
+  // that is UINTN type.\r
+  // To extend new field for this structure, add it to\r
+  // right before this Gdtr field.\r
+  //\r
   IA32_DESCRIPTOR       Gdtr;\r
 } SWITCH_64_TO_32_CONTEXT;\r
+#pragma pack()\r
 #endif\r
 \r
 /**\r
index 3184eb1a6249fa7be8f3f07080c1fb5f9913365e..61fafc7fe0cab3d04ce8490f21876322c4bb6033 100644 (file)
@@ -42,24 +42,19 @@ GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {
   };\r
 \r
 /**\r
-  Calculate the total size of page table.\r
-  \r
-  @return The size of page table.\r
-  \r
-  \r
+  The function will check if 1G page is supported.\r
+\r
+  @retval TRUE   1G page is supported.\r
+  @retval FALSE  1G page is not supported.\r
+\r
 **/\r
-UINTN\r
-CalculatePageTableSize (\r
+BOOLEAN\r
+IsPage1GSupport (\r
   VOID\r
   )\r
 {\r
   UINT32                                        RegEax;\r
   UINT32                                        RegEdx;\r
-  UINTN                                         TotalPagesNum;\r
-  UINT8                                         PhysicalAddressBits;\r
-  VOID                                          *Hob;\r
-  UINT32                                        NumberOfPml4EntriesNeeded;\r
-  UINT32                                        NumberOfPdpEntriesNeeded;\r
   BOOLEAN                                       Page1GSupport;\r
 \r
   Page1GSupport = FALSE;\r
@@ -73,29 +68,34 @@ CalculatePageTableSize (
     }\r
   }\r
 \r
-  //\r
-  // Get physical address bits supported.\r
-  //\r
-  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
-  if (Hob != NULL) {\r
-    PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
-  } else {\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
-  }\r
+  return Page1GSupport;\r
+}\r
+\r
+/**\r
+  Calculate the total size of page table.\r
+\r
+  @param[in] Page1GSupport      1G page support or not.\r
+\r
+  @return The size of page table.\r
+\r
+**/\r
+UINTN\r
+CalculatePageTableSize (\r
+  IN BOOLEAN                                    Page1GSupport\r
+  )\r
+{\r
+  UINTN                                         ExtraPageTablePages;\r
+  UINTN                                         TotalPagesNum;\r
+  UINT8                                         PhysicalAddressBits;\r
+  UINT32                                        NumberOfPml4EntriesNeeded;\r
+  UINT32                                        NumberOfPdpEntriesNeeded;\r
 \r
   //\r
-  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
+  // Create 4G page table by default,\r
+  // and let PF handler to handle > 4G request.\r
   //\r
-  ASSERT (PhysicalAddressBits <= 52);\r
-  if (PhysicalAddressBits > 48) {\r
-    PhysicalAddressBits = 48;\r
-  }\r
+  PhysicalAddressBits = 32;\r
+  ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;\r
 \r
   //\r
   // Calculate the table entries needed.\r
@@ -113,24 +113,25 @@ CalculatePageTableSize (
   } else {\r
     TotalPagesNum = NumberOfPml4EntriesNeeded + 1;\r
   }\r
+  TotalPagesNum += ExtraPageTablePages;\r
 \r
   return EFI_PAGES_TO_SIZE (TotalPagesNum);\r
 }\r
 \r
 /**\r
   Allocates and fills in the Page Directory and Page Table Entries to\r
-  establish a 1:1 Virtual to Physical mapping.\r
+  establish a 4G page table.\r
 \r
-  @param[in]  PageTablesAddress  The base address of page table.\r
+  @param[in] PageTablesAddress  The base address of page table.\r
+  @param[in] Page1GSupport      1G page support or not.\r
 \r
 **/\r
 VOID\r
-CreateIdentityMappingPageTables (\r
-  IN  EFI_PHYSICAL_ADDRESS  PageTablesAddress\r
+Create4GPageTables (\r
+  IN EFI_PHYSICAL_ADDRESS   PageTablesAddress,\r
+  IN BOOLEAN                Page1GSupport\r
   )\r
 {  \r
-  UINT32                                        RegEax;\r
-  UINT32                                        RegEdx;\r
   UINT8                                         PhysicalAddressBits;\r
   EFI_PHYSICAL_ADDRESS                          PageAddress;\r
   UINTN                                         IndexOfPml4Entries;\r
@@ -143,42 +144,13 @@ CreateIdentityMappingPageTables (
   PAGE_MAP_AND_DIRECTORY_POINTER                *PageDirectoryPointerEntry;\r
   PAGE_TABLE_ENTRY                              *PageDirectoryEntry;\r
   UINTN                                         BigPageAddress;\r
-  VOID                                          *Hob;\r
-  BOOLEAN                                       Page1GSupport;\r
   PAGE_TABLE_1G_ENTRY                           *PageDirectory1GEntry;\r
 \r
-  Page1GSupport = FALSE;\r
-  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
-  if (RegEax >= 0x80000001) {\r
-    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
-    if ((RegEdx & BIT26) != 0) {\r
-      Page1GSupport = TRUE;\r
-    }\r
-  }\r
-\r
   //\r
-  // Get physical address bits supported.\r
+  // Create 4G page table by default,\r
+  // and let PF handler to handle > 4G request.\r
   //\r
-  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
-  if (Hob != NULL) {\r
-    PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
-  } else {\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
-  }\r
-\r
-  //\r
-  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
-  //\r
-  ASSERT (PhysicalAddressBits <= 52);\r
-  if (PhysicalAddressBits > 48) {\r
-    PhysicalAddressBits = 48;\r
-  }\r
+  PhysicalAddressBits = 32;\r
 \r
   //\r
   // Calculate the table entries needed.\r
@@ -290,20 +262,20 @@ ReturnFunction (
   SWITCH_32_TO_64_CONTEXT  *EntrypointContext,\r
   SWITCH_64_TO_32_CONTEXT  *ReturnContext\r
   )\r
-{ \r
+{\r
   //\r
   // Restore original GDT\r
   //\r
   AsmWriteGdtr (&ReturnContext->Gdtr);\r
-  \r
+\r
   //\r
   // return to original caller\r
   //\r
   LongJump ((BASE_LIBRARY_JUMP_BUFFER  *)(UINTN)EntrypointContext->JumpBuffer, 1);\r
\r
+\r
   //\r
   // never be here\r
-  // \r
+  //\r
   ASSERT (FALSE);\r
 }\r
 \r
@@ -335,10 +307,10 @@ Thunk32To64 (
   if (SetJumpFlag == 0) {\r
 \r
     //\r
-    // Build Page Tables for all physical memory processor supports\r
+    // Build 4G Page Tables.\r
     //\r
-    CreateIdentityMappingPageTables (PageTableAddress);\r
-    \r
+    Create4GPageTables (PageTableAddress, Context->Page1GSupport);\r
+\r
     //\r
     // Create 64-bit GDT\r
     //\r
@@ -364,7 +336,7 @@ Thunk32To64 (
       Context->StackBufferBase + Context->StackBufferLength\r
       );\r
   }\r
-  \r
+\r
   //\r
   // Convert to 32-bit Status and return\r
   //\r
@@ -407,6 +379,7 @@ ModeSwitch (
   BASE_LIBRARY_JUMP_BUFFER             JumpBuffer;\r
   EFI_PHYSICAL_ADDRESS                 ReservedRangeBase;\r
   EFI_PHYSICAL_ADDRESS                 ReservedRangeEnd;\r
+  BOOLEAN                              Page1GSupport;\r
 \r
   ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT));\r
   ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT));\r
@@ -415,17 +388,19 @@ ModeSwitch (
   MemorySize64  = (UINT64) (UINTN) *MemorySize;\r
   MemoryEnd64   = MemoryBase64 + MemorySize64;\r
 \r
+  Page1GSupport = IsPage1GSupport ();\r
+\r
   //\r
   // Merge memory range reserved for stack and page table  \r
   //\r
   if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {\r
     ReservedRangeBase = LongModeBuffer->StackBaseAddress;\r
-    ReservedRangeEnd  = LongModeBuffer->PageTableAddress + CalculatePageTableSize ();\r
+    ReservedRangeEnd  = LongModeBuffer->PageTableAddress + CalculatePageTableSize (Page1GSupport);\r
   } else {\r
     ReservedRangeBase = LongModeBuffer->PageTableAddress;\r
     ReservedRangeEnd  = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize;\r
   }\r
-  \r
+\r
   //\r
   // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.\r
   // If they are overlapped, get a larger range to process capsule data.\r
@@ -444,8 +419,8 @@ ModeSwitch (
     } else {\r
       MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64);\r
     }\r
-  }  \r
-  \r
+  }\r
+\r
   //\r
   // Initialize context jumping to 64-bit enviroment\r
   //\r
@@ -456,6 +431,7 @@ ModeSwitch (
   Context.BlockListAddr         = BlockListAddr;\r
   Context.MemoryBase64Ptr       = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;\r
   Context.MemorySize64Ptr       = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;\r
+  Context.Page1GSupport         = Page1GSupport;\r
 \r
   //\r
   // Prepare data for return back\r
@@ -529,7 +505,7 @@ FindCapsuleCoalesceImage (
                            &AuthenticationState\r
                            );\r
       if (EFI_ERROR (Status)) {\r
-        DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleRelocate image ffs %r!\n", Status));\r
+        DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status));\r
         return Status;\r
       }\r
       *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);\r
@@ -542,6 +518,46 @@ FindCapsuleCoalesceImage (
   return Status;\r
 }\r
 \r
+/**\r
+  Gets the reserved long mode buffer.\r
+\r
+  @param  LongModeBuffer  Pointer to the long mode buffer for output.\r
+\r
+  @retval EFI_SUCCESS     Long mode buffer successfully retrieved.\r
+  @retval Others          Variable storing long mode buffer not found.\r
+\r
+**/\r
+EFI_STATUS\r
+GetLongModeContext (\r
+  OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer\r
+  )\r
+{\r
+  EFI_STATUS   Status;\r
+  UINTN        Size;\r
+  EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;\r
+\r
+  Status = PeiServicesLocatePpi (\r
+             &gEfiPeiReadOnlyVariable2PpiGuid,\r
+             0,\r
+             NULL,\r
+             (VOID **) &PPIVariableServices\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);\r
+  Status = PPIVariableServices->GetVariable (\r
+                                  PPIVariableServices,\r
+                                  EFI_CAPSULE_LONG_MODE_BUFFER_NAME,\r
+                                  &gEfiCapsuleVendorGuid,\r
+                                  NULL,\r
+                                  &Size,\r
+                                  LongModeBuffer\r
+                                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));\r
+  }\r
+  return Status;\r
+}\r
 #endif\r
 \r
 /**\r
@@ -654,47 +670,6 @@ GetCapsuleDescriptors (
   return EFI_SUCCESS;\r
 }\r
 \r
-/**\r
-  Gets the reserved long mode buffer.\r
-\r
-  @param  LongModeBuffer  Pointer to the long mode buffer for output.\r
-\r
-  @retval EFI_SUCCESS     Long mode buffer successfully retrieved.\r
-  @retval Others          Variable storing long mode buffer not found.\r
-\r
-**/\r
-EFI_STATUS\r
-GetLongModeContext (\r
-  OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer\r
-  )\r
-{\r
-  EFI_STATUS   Status;\r
-  UINTN        Size;\r
-  EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;\r
-\r
-  Status = PeiServicesLocatePpi (\r
-             &gEfiPeiReadOnlyVariable2PpiGuid,\r
-             0,\r
-             NULL,\r
-             (VOID **) &PPIVariableServices\r
-             );\r
-  ASSERT_EFI_ERROR (Status);\r
-\r
-  Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);\r
-  Status = PPIVariableServices->GetVariable (\r
-                                  PPIVariableServices,\r
-                                  EFI_CAPSULE_LONG_MODE_BUFFER_NAME,\r
-                                  &gEfiCapsuleVendorGuid,\r
-                                  NULL,\r
-                                  &Size,\r
-                                  LongModeBuffer\r
-                                  );\r
-  if (EFI_ERROR (Status)) {\r
-    DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));\r
-  }\r
-  return Status;\r
-}\r
-\r
 /**\r
   Capsule PPI service to coalesce a fragmented capsule in memory.\r
 \r
@@ -837,7 +812,7 @@ CapsuleCoalesce (
     CoalesceImageEntryPoint = 0;\r
     Status = GetLongModeContext (&LongModeBuffer);\r
     if (EFI_ERROR (Status)) {\r
-      DEBUG ((EFI_D_ERROR, "Fail to find the variables for long mode context!\n"));\r
+      DEBUG ((EFI_D_ERROR, "Fail to find the variable for long mode context!\n"));\r
       Status = EFI_NOT_FOUND;\r
       goto Done;\r
     }\r
diff --git a/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.S b/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.S
new file mode 100644 (file)
index 0000000..9e17cc3
--- /dev/null
@@ -0,0 +1,81 @@
+## @file\r
+#   This is the assembly code for page fault handler hook.\r
+#\r
+# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials are\r
+# 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
+ASM_GLOBAL ASM_PFX(PageFaultHandlerHook)\r
+ASM_PFX(PageFaultHandlerHook):\r
+    addq     $-0x10, %rsp\r
+    # save rax\r
+    movq     %rax, 0x08(%rsp)\r
+\r
+    # pushq    %rax                         # save all volatile registers\r
+    pushq    %rcx\r
+    pushq    %rdx\r
+    pushq    %r8\r
+    pushq    %r9\r
+    pushq    %r10\r
+    pushq    %r11\r
+    # save volatile fp registers\r
+    # 68h + 08h(for alignment)\r
+    addq     $-0x70, %rsp\r
+    stmxcsr  0x60(%rsp)\r
+    movdqa   %xmm0, 0x0(%rsp) \r
+    movdqa   %xmm1, 0x10(%rsp) \r
+    movdqa   %xmm2, 0x20(%rsp) \r
+    movdqa   %xmm3, 0x30(%rsp) \r
+    movdqa   %xmm4, 0x40(%rsp) \r
+    movdqa   %xmm5, 0x50(%rsp) \r
+\r
+    addq     $-0x20, %rsp\r
+    call     ASM_PFX(PageFaultHandler)\r
+    addq     $0x20, %rsp\r
+\r
+    # load volatile fp registers\r
+    ldmxcsr  0x60(%rsp)\r
+    movdqa   0x0(%rsp), %xmm0\r
+    movdqa   0x10(%rsp), %xmm1\r
+    movdqa   0x20(%rsp), %xmm2\r
+    movdqa   0x30(%rsp), %xmm3\r
+    movdqa   0x40(%rsp), %xmm4\r
+    movdqa   0x50(%rsp), %xmm5\r
+    addq     $0x70, %rsp\r
+\r
+    popq     %r11\r
+    popq     %r10\r
+    popq     %r9\r
+    popq     %r8\r
+    popq     %rdx\r
+    popq     %rcx\r
+    # popq     %rax                         # restore all volatile registers\r
+\r
+    addq     $0x10, %rsp\r
+\r
+    # rax returned from PageFaultHandler is NULL or OriginalHandler address\r
+    # NULL if the page fault is handled by PageFaultHandler\r
+    # OriginalHandler address if the page fault is not handled by PageFaultHandler\r
+    testq    %rax, %rax\r
+\r
+    # save OriginalHandler address\r
+    movq     %rax, -0x10(%rsp)\r
+    # restore rax\r
+    movq     -0x08(%rsp), %rax\r
+\r
+    jz       L1\r
+\r
+    # jump to OriginalHandler\r
+    jmpq     *-0x10(%rsp)\r
+\r
+L1:\r
+    addq     $0x08, %rsp                  # skip error code for PF\r
+    iretq\r
diff --git a/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.asm b/MdeModulePkg/Universal/CapsulePei/X64/PageFaultHandler.asm
new file mode 100644 (file)
index 0000000..2f1eab7
--- /dev/null
@@ -0,0 +1,87 @@
+;; @file\r
+;   This is the assembly code for page fault handler hook.\r
+;\r
+; Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+;\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
+EXTERN PageFaultHandler:PROC\r
+\r
+    .code\r
+\r
+PageFaultHandlerHook PROC\r
+    add     rsp, -10h\r
+    ; save rax\r
+    mov     [rsp + 08h], rax\r
+\r
+    ;push    rax                         ; save all volatile registers\r
+    push    rcx\r
+    push    rdx\r
+    push    r8\r
+    push    r9\r
+    push    r10\r
+    push    r11\r
+    ; save volatile fp registers\r
+    ; 68h + 08h(for alignment)\r
+    add     rsp, -70h\r
+    stmxcsr [rsp + 60h]\r
+    movdqa  [rsp + 0h], xmm0\r
+    movdqa  [rsp + 10h], xmm1\r
+    movdqa  [rsp + 20h], xmm2\r
+    movdqa  [rsp + 30h], xmm3\r
+    movdqa  [rsp + 40h], xmm4\r
+    movdqa  [rsp + 50h], xmm5\r
+\r
+    add     rsp, -20h\r
+    call    PageFaultHandler\r
+    add     rsp, 20h\r
+\r
+    ; load volatile fp registers\r
+    ldmxcsr [rsp + 60h]\r
+    movdqa  xmm0,  [rsp + 0h]\r
+    movdqa  xmm1,  [rsp + 10h]\r
+    movdqa  xmm2,  [rsp + 20h]\r
+    movdqa  xmm3,  [rsp + 30h]\r
+    movdqa  xmm4,  [rsp + 40h]\r
+    movdqa  xmm5,  [rsp + 50h]\r
+    add     rsp, 70h\r
+\r
+    pop     r11\r
+    pop     r10\r
+    pop     r9\r
+    pop     r8\r
+    pop     rdx\r
+    pop     rcx\r
+    ;pop     rax                         ; restore all volatile registers\r
+\r
+    add     rsp, 10h\r
+\r
+    ; rax returned from PageFaultHandler is NULL or OriginalHandler address\r
+    ; NULL if the page fault is handled by PageFaultHandler\r
+    ; OriginalHandler address if the page fault is not handled by PageFaultHandler\r
+    test    rax, rax\r
+\r
+    ; save OriginalHandler address\r
+    mov     [rsp - 10h], rax\r
+    ; restore rax\r
+    mov     rax, [rsp - 08h]\r
+\r
+    jz      @F\r
+\r
+    ; jump to OriginalHandler\r
+    jmp     qword ptr [rsp - 10h]\r
+\r
+@@:\r
+    add     rsp, 08h                    ; skip error code for PF\r
+    iretq\r
+PageFaultHandlerHook ENDP\r
+\r
+    END\r
index 88cfc8fe9e6f8e17e03ad24438a289f8bd2bac17..670e2c7d5ff85c5a657aef248be5c00c8e0f6875 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   The X64 entrypoint is used to process capsule in long mode.\r
 \r
-Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2011 - 2015, 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
@@ -20,6 +20,184 @@ 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
+  //\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
+\r
+  Address = PageFaultContext->PageFaultBuffer + EFI_PAGES_TO_SIZE (PageFaultContext->PageFaultIndex);\r
+  ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));\r
+\r
+  //\r
+  // Cut the previous uplink if it exists and wasn't overwritten.\r
+  //\r
+  if ((PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] != NULL) && ((*PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] & PageFaultContext->PhyMask) == Address)) {\r
+    *PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = 0;\r
+  }\r
+\r
+  //\r
+  // Link & Record the current uplink.\r
+  //\r
+  *Uplink = Address | 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
+\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
+\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] & PhyMask);\r
+  PTIndex = BitFieldRead64 (PFAddress, 30, 38);\r
+  // PDPTE\r
+  if (PageFaultContext->Page1GSupport) {\r
+    PageTable[PTIndex] = (PFAddress & ~((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] & PhyMask);\r
+    PTIndex = BitFieldRead64 (PFAddress, 21, 29);\r
+    // PD\r
+    PageTable[PTIndex] = (PFAddress & ~((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,7 +218,8 @@ _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
@@ -50,8 +229,8 @@ _ModuleEntryPoint (
   //\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
 \r
@@ -60,7 +239,14 @@ _ModuleEntryPoint (
   //\r
   Status = InitializeCpuExceptionHandlers (NULL);\r
   ASSERT_EFI_ERROR (Status);\r
-  \r
+\r
+  //\r
+  // Hook page fault handler to handle >4G request.\r
+  //\r
+  PageFaultIdtTable.PageFaultContext.Page1GSupport = EntrypointContext->Page1GSupport;\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