]> git.proxmox.com Git - mirror_edk2.git/commitdiff
UefiCpuPkg/MpInitLib: split wake up buffer into two parts
authorJian J Wang <jian.j.wang@intel.com>
Fri, 29 Dec 2017 01:12:54 +0000 (09:12 +0800)
committerRuiyu Ni <ruiyu.ni@intel.com>
Thu, 18 Jan 2018 09:03:22 +0000 (17:03 +0800)
If PcdDxeNxMemoryProtectionPolicy is set to enable protection for memory
of EfiBootServicesCode, EfiConventionalMemory, the BIOS will hang at a page
fault exception during MP initialization.

The root cause is that the AP wake up buffer, which is below 1MB and used
to hold both AP init code and data, is type of EfiConventionalMemory (not
really allocated because of potential conflict with legacy code), and is
marked as non-executable. During the transition from real address mode
to long mode, the AP init code has to enable paging which will then cause
itself a page fault exception because it's just running in non-executable
memory.

The solution is splitting AP wake up buffer into two part: lower part is
still below 1MB and shared with legacy system, higher part is really
allocated memory of BootServicesCode type. The init code in the memory
below 1MB will not enable paging but just switch to protected mode and
jump to higher memory, in which the init code will enable paging and
switch to long mode.

Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/Library/MpInitLib/MpLib.h
UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm

index d2bcef53d695ea828c795a854fdce1b9c15620b8..fd2317924fd1fd998e0523edcaa14baef35ec3e7 100644 (file)
@@ -113,6 +113,40 @@ GetWakeupBuffer (
   return (UINTN) StartAddress;\r
 }\r
 \r
+/**\r
+  Get available EfiBootServicesCode memory below 4GB by specified size.\r
+\r
+  This buffer is required to safely transfer AP from real address mode to\r
+  protected mode or long mode, due to the fact that the buffer returned by\r
+  GetWakeupBuffer() may be marked as non-executable.\r
+\r
+  @param[in] BufferSize   Wakeup transition buffer size.\r
+\r
+  @retval other   Return wakeup transition buffer address below 4GB.\r
+  @retval 0       Cannot find free memory below 4GB.\r
+**/\r
+UINTN\r
+GetModeTransitionBuffer (\r
+  IN UINTN                BufferSize\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  EFI_PHYSICAL_ADDRESS    StartAddress;\r
+\r
+  StartAddress = BASE_4GB - 1;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiBootServicesCode,\r
+                  EFI_SIZE_TO_PAGES (BufferSize),\r
+                  &StartAddress\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    StartAddress = 0;\r
+  }\r
+\r
+  return (UINTN)StartAddress;\r
+}\r
+\r
 /**\r
   Checks APs status and updates APs status if needed.\r
 \r
index bdfe0d33cc9ec40befb37efe70a78070ede4813b..1648f2c4b0f93001787a7d2296ac400fc80312b2 100644 (file)
@@ -41,4 +41,9 @@ Cr3Location                   equ        LockLocation + 34h
 InitFlagLocation              equ        LockLocation + 38h\r
 CpuInfoLocation               equ        LockLocation + 3Ch\r
 NumApsExecutingLocation       equ        LockLocation + 40h\r
+InitializeFloatingPointUnitsAddress equ  LockLocation + 48h\r
+ModeTransitionMemoryLocation        equ  LockLocation + 4Ch\r
+ModeTransitionSegmentLocation       equ  LockLocation + 50h\r
+ModeHighMemoryLocation              equ  LockLocation + 52h\r
+ModeHighSegmentLocation             equ  LockLocation + 56h\r
 \r
index 2b6c27d4ec675f6389436ce3862b01b22284a5b1..bd79be0f5e764452562cabcf144642337c7b2170 100644 (file)
@@ -48,34 +48,35 @@ BITS 16
     mov        si,  BufferStartLocation\r
     mov        ebx, [si]\r
 \r
-    mov        si,  ModeOffsetLocation\r
-    mov        eax, [si]\r
-    mov        si,  CodeSegmentLocation\r
-    mov        edx, [si]\r
-    mov        di,  ax\r
-    sub        di,  02h\r
-    mov        [di], dx\r
-    sub        di,  04h\r
-    add        eax, ebx\r
-    mov        [di],eax\r
-\r
     mov        si,  DataSegmentLocation\r
     mov        edx, [si]\r
 \r
+    ;\r
+    ; Get start address of 32-bit code in low memory (<1MB)\r
+    ;\r
+    mov        edi, ModeTransitionMemoryLocation\r
+\r
     mov        si, GdtrLocation\r
 o32 lgdt       [cs:si]\r
 \r
     mov        si, IdtrLocation\r
 o32 lidt       [cs:si]\r
 \r
-    xor        ax,  ax\r
-    mov        ds,  ax\r
-\r
+    ;\r
+    ; Switch to protected mode\r
+    ;\r
     mov        eax, cr0                        ; Get control register 0\r
     or         eax, 000000003h                 ; Set PE bit (bit #0) & MP\r
     mov        cr0, eax\r
 \r
-    jmp        0:strict dword 0                ; far jump to protected mode\r
+    ; Switch to 32-bit code in executable memory (>1MB)\r
+o32 jmp far    [cs:di]\r
+\r
+;\r
+; Following code may be copied to memory with type of EfiBootServicesCode.\r
+; This is required at DXE phase if NX is enabled for EfiBootServicesCode of\r
+; memory.\r
+;\r
 BITS 32\r
 Flat32Start:                                   ; protected mode entry point\r
     mov        ds, dx\r
@@ -266,6 +267,7 @@ ASM_PFX(AsmGetAddressMap):
     mov        dword [ebx +  8h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart\r
     mov        dword [ebx + 0Ch], AsmRelocateApLoopStart\r
     mov        dword [ebx + 10h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart\r
+    mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart\r
 \r
     popad\r
     ret\r
index 8ec016e928559d02526a0509af86cd882d8f93ad..6231968c7483249caf270faf5418a1573b209136 100644 (file)
@@ -772,6 +772,8 @@ FillExchangeInfoData (
   )\r
 {\r
   volatile MP_CPU_EXCHANGE_INFO    *ExchangeInfo;\r
+  UINTN                            Size;\r
+  IA32_SEGMENT_DESCRIPTOR          *Selector;\r
 \r
   ExchangeInfo                  = CpuMpData->MpCpuExchangeInfo;\r
   ExchangeInfo->Lock            = 0;\r
@@ -801,6 +803,44 @@ FillExchangeInfoData (
   //\r
   AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile);\r
   AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);\r
+\r
+  //\r
+  // Find a 32-bit code segment\r
+  //\r
+  Selector = (IA32_SEGMENT_DESCRIPTOR *)ExchangeInfo->GdtrProfile.Base;\r
+  Size = ExchangeInfo->GdtrProfile.Limit + 1;\r
+  while (Size > 0) {\r
+    if (Selector->Bits.L == 0 && Selector->Bits.Type >= 8) {\r
+      ExchangeInfo->ModeTransitionSegment =\r
+        (UINT16)((UINTN)Selector - ExchangeInfo->GdtrProfile.Base);\r
+      break;\r
+    }\r
+    Selector += 1;\r
+    Size -= sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+  }\r
+\r
+  //\r
+  // Copy all 32-bit code and 64-bit code into memory with type of\r
+  // EfiBootServicesCode to avoid page fault if NX memory protection is enabled.\r
+  //\r
+  if (ExchangeInfo->ModeTransitionMemory != 0) {\r
+    Size = CpuMpData->AddressMap.RendezvousFunnelSize -\r
+           CpuMpData->AddressMap.ModeTransitionOffset;\r
+    CopyMem (\r
+      (VOID *)(UINTN)ExchangeInfo->ModeTransitionMemory,\r
+      CpuMpData->AddressMap.RendezvousFunnelAddress +\r
+      CpuMpData->AddressMap.ModeTransitionOffset,\r
+      Size\r
+      );\r
+\r
+    ExchangeInfo->ModeHighMemory = ExchangeInfo->ModeTransitionMemory;\r
+    ExchangeInfo->ModeHighMemory += (UINT32)ExchangeInfo->ModeOffset -\r
+               (UINT32)CpuMpData->AddressMap.ModeTransitionOffset;\r
+    ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment;\r
+  } else {\r
+    ExchangeInfo->ModeTransitionMemory = (UINT32)\r
+      (ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset);\r
+  }\r
 }\r
 \r
 /**\r
@@ -876,6 +916,11 @@ AllocateResetVector (
     CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);\r
     CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)\r
                     (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);\r
+    CpuMpData->MpCpuExchangeInfo->ModeTransitionMemory = (UINT32)\r
+                    GetModeTransitionBuffer (\r
+                      CpuMpData->AddressMap.RendezvousFunnelSize -\r
+                      CpuMpData->AddressMap.ModeTransitionOffset\r
+                      );\r
   }\r
   BackupAndPrepareWakeupBuffer (CpuMpData);\r
 }\r
index 685e96cbac45f694ad7e03600da5a828338b3773..0232fe896ab553621282d84c7fc3d0b2b274e65b 100644 (file)
@@ -152,6 +152,7 @@ typedef struct {
   UINTN             RendezvousFunnelSize;\r
   UINT8             *RelocateApLoopFuncAddress;\r
   UINTN             RelocateApLoopFuncSize;\r
+  UINTN             ModeTransitionOffset;\r
 } MP_ASSEMBLY_ADDRESS_MAP;\r
 \r
 typedef struct _CPU_MP_DATA  CPU_MP_DATA;\r
@@ -182,6 +183,10 @@ typedef struct {
   UINTN                 NumApsExecuting;\r
   CPU_MP_DATA           *CpuMpData;\r
   UINTN                 InitializeFloatingPointUnitsAddress;\r
+  UINT32                ModeTransitionMemory;\r
+  UINT16                ModeTransitionSegment;\r
+  UINT32                ModeHighMemory;\r
+  UINT16                ModeHighSegment;\r
 } MP_CPU_EXCHANGE_INFO;\r
 \r
 #pragma pack()\r
@@ -329,6 +334,23 @@ GetWakeupBuffer (
   IN UINTN                WakeupBufferSize\r
   );\r
 \r
+/**\r
+  Get available EfiBootServicesCode memory below 4GB by specified size.\r
+\r
+  This buffer is required to safely transfer AP from real address mode to\r
+  protected mode or long mode, due to the fact that the buffer returned by\r
+  GetWakeupBuffer() may be marked as non-executable.\r
+\r
+  @param[in] BufferSize   Wakeup transition buffer size.\r
+\r
+  @retval other   Return wakeup transition buffer address below 4GB.\r
+  @retval 0       Cannot find free memory below 4GB.\r
+**/\r
+UINTN\r
+GetModeTransitionBuffer (\r
+  IN UINTN                BufferSize\r
+  );\r
+\r
 /**\r
   This function will be called by BSP to wakeup AP.\r
 \r
index 70c2bc7323f6d05d418a8b823cab21f1c3e49de5..ad43bd33f5f820c0c00192c33b22966ce678f5d7 100644 (file)
@@ -187,6 +187,29 @@ GetWakeupBuffer (
   return (UINTN) -1;\r
 }\r
 \r
+/**\r
+  Get available EfiBootServicesCode memory below 4GB by specified size.\r
+\r
+  This buffer is required to safely transfer AP from real address mode to\r
+  protected mode or long mode, due to the fact that the buffer returned by\r
+  GetWakeupBuffer() may be marked as non-executable.\r
+\r
+  @param[in] BufferSize   Wakeup transition buffer size.\r
+\r
+  @retval other   Return wakeup transition buffer address below 4GB.\r
+  @retval 0       Cannot find free memory below 4GB.\r
+**/\r
+UINTN\r
+GetModeTransitionBuffer (\r
+  IN UINTN                BufferSize\r
+  )\r
+{\r
+  //\r
+  // PEI phase doesn't need to do such transition. So simply return 0.\r
+  //\r
+  return 0;\r
+}\r
+\r
 /**\r
   Checks APs status and updates APs status if needed.\r
 \r
index d255ca5e1b9c9a5442ed83762453afd69a06dd42..b5e09c6bc16baac4a9a21a64b1571d19ec41734b 100644 (file)
@@ -42,4 +42,7 @@ InitFlagLocation              equ        LockLocation + 6Ch
 CpuInfoLocation               equ        LockLocation + 74h\r
 NumApsExecutingLocation       equ        LockLocation + 7Ch\r
 InitializeFloatingPointUnitsAddress equ  LockLocation + 8Ch\r
-\r
+ModeTransitionMemoryLocation        equ  LockLocation + 94h\r
+ModeTransitionSegmentLocation       equ  LockLocation + 98h\r
+ModeHighMemoryLocation              equ  LockLocation + 9Ah\r
+ModeHighSegmentLocation             equ  LockLocation + 9Eh\r
index 21d278600d1bf3878ae37b723867089e33344abd..75959888849e4666926d9ff7063ffe3de6cc13d6 100644 (file)
@@ -52,16 +52,13 @@ BITS 16
     mov        si,  BufferStartLocation\r
     mov        ebx, [si]\r
 \r
-    mov        di,  ModeOffsetLocation\r
-    mov        eax, [di]\r
-    mov        di,  CodeSegmentLocation\r
-    mov        edx, [di]\r
-    mov        di,  ax\r
-    sub        di,  02h\r
-    mov        [di],dx                         ; Patch long mode CS\r
-    sub        di,  04h\r
-    add        eax, ebx\r
-    mov        [di],eax                        ; Patch address\r
+    mov        si,  DataSegmentLocation\r
+    mov        edx, [si]\r
+\r
+    ;\r
+    ; Get start address of 32-bit code in low memory (<1MB)\r
+    ;\r
+    mov        edi, ModeTransitionMemoryLocation\r
 \r
     mov        si, GdtrLocation\r
 o32 lgdt       [cs:si]\r
@@ -69,56 +66,79 @@ o32 lgdt       [cs:si]
     mov        si, IdtrLocation\r
 o32 lidt       [cs:si]\r
 \r
-    mov        si, EnableExecuteDisableLocation\r
-    cmp        byte [si], 0\r
-    jz         SkipEnableExecuteDisableBit\r
+    ;\r
+    ; Switch to protected mode\r
+    ;\r
+    mov        eax, cr0                    ; Get control register 0\r
+    or         eax, 000000003h             ; Set PE bit (bit #0) & MP\r
+    mov        cr0, eax\r
+\r
+    ; Switch to 32-bit code (>1MB)\r
+o32 jmp far    [cs:di]\r
+\r
+;\r
+; Following code must be copied to memory with type of EfiBootServicesCode.\r
+; This is required if NX is enabled for EfiBootServicesCode of memory.\r
+;\r
+BITS 32\r
+Flat32Start:                                   ; protected mode entry point\r
+    mov        ds, dx\r
+    mov        es, dx\r
+    mov        fs, dx\r
+    mov        gs, dx\r
+    mov        ss, dx\r
 \r
     ;\r
     ; Enable execute disable bit\r
     ;\r
+    mov        esi, EnableExecuteDisableLocation\r
+    cmp        byte [ebx + esi], 0\r
+    jz         SkipEnableExecuteDisableBit\r
+\r
     mov        ecx, 0c0000080h             ; EFER MSR number\r
     rdmsr                                  ; Read EFER\r
     bts        eax, 11                     ; Enable Execute Disable Bit\r
     wrmsr                                  ; Write EFER\r
 \r
 SkipEnableExecuteDisableBit:\r
-\r
-    mov        di,  DataSegmentLocation\r
-    mov        edi, [di]                   ; Save long mode DS in edi\r
-\r
-    mov        si, Cr3Location             ; Save CR3 in ecx\r
-    mov        ecx, [si]\r
-\r
-    xor        ax,  ax\r
-    mov        ds,  ax                     ; Clear data segment\r
-\r
-    mov        eax, cr0                    ; Get control register 0\r
-    or         eax, 000000003h             ; Set PE bit (bit #0) & MP\r
-    mov        cr0, eax\r
-\r
+    ;\r
+    ; Enable PAE\r
+    ;\r
     mov        eax, cr4\r
     bts        eax, 5\r
     mov        cr4, eax\r
 \r
+    ;\r
+    ; Load page table\r
+    ;\r
+    mov        esi, Cr3Location             ; Save CR3 in ecx\r
+    mov        ecx, [ebx + esi]\r
     mov        cr3, ecx                    ; Load CR3\r
 \r
+    ;\r
+    ; Enable long mode\r
+    ;\r
     mov        ecx, 0c0000080h             ; EFER MSR number\r
     rdmsr                                  ; Read EFER\r
     bts        eax, 8                      ; Set LME=1\r
     wrmsr                                  ; Write EFER\r
 \r
+    ;\r
+    ; Enable paging\r
+    ;\r
     mov        eax, cr0                    ; Read CR0\r
     bts        eax, 31                     ; Set PG=1\r
     mov        cr0, eax                    ; Write CR0\r
 \r
-    jmp        0:strict dword 0  ; far jump to long mode\r
+    ;\r
+    ; Far jump to 64-bit code\r
+    ;\r
+    mov        edi, ModeHighMemoryLocation\r
+    add        edi, ebx\r
+    jmp far    [edi]\r
+\r
 BITS 64\r
 LongModeStart:\r
-    mov        eax, edi\r
-    mov        ds,  ax\r
-    mov        es,  ax\r
-    mov        ss,  ax\r
-\r
     mov        esi, ebx\r
     lea        edi, [esi + InitFlagLocation]\r
     cmp        qword [edi], 1       ; ApInitConfig\r
@@ -295,6 +315,7 @@ ASM_PFX(AsmGetAddressMap):
     lea        rax, [ASM_PFX(AsmRelocateApLoop)]\r
     mov        qword [rcx + 18h], rax\r
     mov        qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart\r
+    mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart\r
     ret\r
 \r
 ;-------------------------------------------------------------------------------------\r