]> git.proxmox.com Git - mirror_edk2.git/commitdiff
UefiCpuPkg: Allow AP booting under SEV-ES
authorTom Lendacky <thomas.lendacky@amd.com>
Wed, 12 Aug 2020 20:21:42 +0000 (15:21 -0500)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Mon, 17 Aug 2020 02:46:39 +0000 (02:46 +0000)
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Typically, an AP is booted using the INIT-SIPI-SIPI sequence. This
sequence is intercepted by the hypervisor, which sets the AP's registers
to the values requested by the sequence. At that point, the hypervisor can
start the AP, which will then begin execution at the appropriate location.

Under SEV-ES, AP booting presents some challenges since the hypervisor is
not allowed to alter the AP's register state. In this situation, we have
to distinguish between the AP's first boot and AP's subsequent boots.

First boot:
 Once the AP's register state has been defined (which is before the guest
 is first booted) it cannot be altered. Should the hypervisor attempt to
 alter the register state, the change would be detected by the hardware
 and the VMRUN instruction would fail. Given this, the first boot for the
 AP is required to begin execution with this initial register state, which
 is typically the reset vector. This prevents the BSP from directing the
 AP startup location through the INIT-SIPI-SIPI sequence.

 To work around this, the firmware will provide a build time reserved area
 that can be used as the initial IP value. The hypervisor can extract this
 location value by checking for the SEV-ES reset block GUID that must be
 located 48-bytes from the end of the firmware. The format of the SEV-ES
 reset block area is:

   0x00 - 0x01 - SEV-ES Reset IP
   0x02 - 0x03 - SEV-ES Reset CS Segment Base[31:16]
   0x04 - 0x05 - Size of the SEV-ES reset block
   0x06 - 0x15 - SEV-ES Reset Block GUID
                   (00f771de-1a7e-4fcb-890e-68c77e2fb44e)

   The total size is 22 bytes. Any expansion to this block must be done
   by adding new values before existing values.

 The hypervisor will use the IP and CS values obtained from the SEV-ES
 reset block to set as the AP's initial values. The CS Segment Base
 represents the upper 16 bits of the CS segment base and must be left
 shifted by 16 bits to form the complete CS segment base value.

 Before booting the AP for the first time, the BSP must initialize the
 SEV-ES reset area. This consists of programming a FAR JMP instruction
 to the contents of a memory location that is also located in the SEV-ES
 reset area. The BSP must program the IP and CS values for the FAR JMP
 based on values drived from the INIT-SIPI-SIPI sequence.

Subsequent boots:
 Again, the hypervisor cannot alter the AP register state, so a method is
 required to take the AP out of halt state and redirect it to the desired
 IP location. If it is determined that the AP is running in an SEV-ES
 guest, then instead of calling CpuSleep(), a VMGEXIT is issued with the
 AP Reset Hold exit code (0x80000004). The hypervisor will put the AP in
 a halt state, waiting for an INIT-SIPI-SIPI sequence. Once the sequence
 is recognized, the hypervisor will resume the AP. At this point the AP
 must transition from the current 64-bit long mode down to 16-bit real
 mode and begin executing at the derived location from the INIT-SIPI-SIPI
 sequence.

 Another change is around the area of obtaining the (x2)APIC ID during AP
 startup. During AP startup, the AP can't take a #VC exception before the
 AP has established a stack. However, the AP stack is set by using the
 (x2)APIC ID, which is obtained through CPUID instructions. A CPUID
 instruction will cause a #VC, so a different method must be used. The
 GHCB protocol supports a method to obtain CPUID information from the
 hypervisor through the GHCB MSR. This method does not require a stack,
 so it is used to obtain the necessary CPUID information to determine the
 (x2)APIC ID.

The new 16-bit protected mode GDT entry is used in order to transition
from 64-bit long mode down to 16-bit real mode.

A new assembler routine is created that takes the AP from 64-bit long mode
to 16-bit real mode.  This is located under 1MB in memory and transitions
from 64-bit long mode to 32-bit compatibility mode to 16-bit protected
mode and finally 16-bit real mode.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
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/PeiMpInitLib.inf
UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c

index 5832765956193e1145d4d4b9a78eee6cf63549be..1771575c69c1bece7024c2486181016837811ac3 100644 (file)
@@ -52,6 +52,7 @@
   DebugAgentLib\r
   SynchronizationLib\r
   PcdLib\r
+  VmgExitLib\r
 \r
 [Protocols]\r
   gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES\r
@@ -72,4 +73,6 @@
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ## SOMETIMES_CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds  ## CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ## CONSUMES\r
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ## SOMETIMES_CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ## CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ## CONSUMES\r
index 8ccddf8e9f9c6d198fe9509e3e5903eb8464bcc6..9115ff9e3e30c6645e0e0a48e2e38a2f43e7b012 100644 (file)
@@ -12,6 +12,8 @@
 #include <Library/UefiBootServicesTableLib.h>\r
 #include <Library/DebugAgentLib.h>\r
 #include <Library/DxeServicesTableLib.h>\r
+#include <Register/Amd/Fam17Msr.h>\r
+#include <Register/Amd/Ghcb.h>\r
 \r
 #include <Protocol/Timer.h>\r
 \r
@@ -144,6 +146,39 @@ GetModeTransitionBuffer (
   return (UINTN)StartAddress;\r
 }\r
 \r
+/**\r
+  Return the address of the SEV-ES AP jump table.\r
+\r
+  This buffer is required in order for an SEV-ES guest to transition from\r
+  UEFI into an OS.\r
+\r
+  @return         Return SEV-ES AP jump table buffer\r
+**/\r
+UINTN\r
+GetSevEsAPMemory (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_PHYSICAL_ADDRESS  StartAddress;\r
+\r
+  //\r
+  // Allocate 1 page for AP jump table page\r
+  //\r
+  StartAddress = BASE_4GB - 1;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiReservedMemoryType,\r
+                  1,\r
+                  &StartAddress\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN) StartAddress));\r
+\r
+  return (UINTN) StartAddress;\r
+}\r
+\r
 /**\r
   Checks APs status and updates APs status if needed.\r
 \r
@@ -218,6 +253,38 @@ CheckApsStatus (
   }\r
 }\r
 \r
+/**\r
+  Get Protected mode code segment with 16-bit default addressing\r
+  from current GDT table.\r
+\r
+  @return  Protected mode 16-bit code segment value.\r
+**/\r
+UINT16\r
+GetProtectedMode16CS (\r
+  VOID\r
+  )\r
+{\r
+  IA32_DESCRIPTOR          GdtrDesc;\r
+  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;\r
+  UINTN                    GdtEntryCount;\r
+  UINT16                   Index;\r
+\r
+  Index = (UINT16) -1;\r
+  AsmReadGdtr (&GdtrDesc);\r
+  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
+  for (Index = 0; Index < GdtEntryCount; Index++) {\r
+    if (GdtEntry->Bits.L == 0) {\r
+      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) {\r
+        break;\r
+      }\r
+    }\r
+    GdtEntry++;\r
+  }\r
+  ASSERT (Index != GdtEntryCount);\r
+  return Index * 8;\r
+}\r
+\r
 /**\r
   Get Protected mode code segment from current GDT table.\r
 \r
@@ -238,7 +305,7 @@ GetProtectedModeCS (
   GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
   for (Index = 0; Index < GdtEntryCount; Index++) {\r
     if (GdtEntry->Bits.L == 0) {\r
-      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {\r
+      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {\r
         break;\r
       }\r
     }\r
@@ -300,6 +367,7 @@ MpInitChangeApLoopCallback (
 \r
   CpuMpData = GetCpuMpData ();\r
   CpuMpData->PmCodeSegment = GetProtectedModeCS ();\r
+  CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();\r
   CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
   mNumberToFinish = CpuMpData->CpuCount - 1;\r
   WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE);\r
index efb1bc2bf7cbaed379cb96038b9c8b98f54eb86d..4f5a7c859a56e01e3f8e22629fea783b886903cb 100644 (file)
@@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
 CPU_SWITCH_STATE_STORED       equ        1\r
 CPU_SWITCH_STATE_LOADED       equ        2\r
 \r
-LockLocation                  equ        (RendezvousFunnelProcEnd - RendezvousFunnelProcStart)\r
+LockLocation                  equ        (SwitchToRealProcEnd - RendezvousFunnelProcStart)\r
 StackStartAddressLocation     equ        LockLocation + 04h\r
 StackSizeLocation             equ        LockLocation + 08h\r
 ApProcedureLocation           equ        LockLocation + 0Ch\r
index b74046b76af32fc8a0a6e8518a9255cac7d832b5..309d53bf3b37dc7a548c531cd01b6678e21d0bf0 100644 (file)
@@ -215,6 +215,16 @@ CProcedureInvoke:
     jmp        $                 ; Never reach here\r
 RendezvousFunnelProcEnd:\r
 \r
+;-------------------------------------------------------------------------------------\r
+;SwitchToRealProc procedure follows.\r
+;NOT USED IN 32 BIT MODE.\r
+;-------------------------------------------------------------------------------------\r
+global ASM_PFX(SwitchToRealProc)\r
+ASM_PFX(SwitchToRealProc):\r
+SwitchToRealProcStart:\r
+    jmp        $                 ; Never reach here\r
+SwitchToRealProcEnd:\r
+\r
 ;-------------------------------------------------------------------------------------\r
 ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);\r
 ;-------------------------------------------------------------------------------------\r
@@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
     mov        dword [ebx + 0Ch], AsmRelocateApLoopStart\r
     mov        dword [ebx + 10h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart\r
     mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart\r
+    mov        dword [ebx + 18h], SwitchToRealProcEnd - SwitchToRealProcStart       ; SwitchToRealSize\r
+    mov        dword [ebx + 1Ch], SwitchToRealProcStart - RendezvousFunnelProcStart ; SwitchToRealOffset\r
+    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start               ; SwitchToRealNoNxOffset\r
+    mov        dword [ebx + 24h], 0                                                 ; SwitchToRealPM16ModeOffset\r
+    mov        dword [ebx + 28h], 0                                                 ; SwitchToRealPM16ModeSize\r
 \r
     popad\r
     ret\r
index 2a3fbeef35f78738ca8d16b938ae16fb53328fd3..90416c81b616e8f4574b8dfd9289af6ae67aca8e 100644 (file)
@@ -9,6 +9,9 @@
 **/\r
 \r
 #include "MpLib.h"\r
+#include <Library/VmgExitLib.h>\r
+#include <Register/Amd/Fam17Msr.h>\r
+#include <Register/Amd/Ghcb.h>\r
 \r
 EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;\r
 \r
@@ -291,6 +294,14 @@ GetApLoopMode (
       //\r
       ApLoopMode = ApInHltLoop;\r
     }\r
+\r
+    if (PcdGetBool (PcdSevEsIsEnabled)) {\r
+      //\r
+      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB\r
+      // protocol for starting APs\r
+      //\r
+      ApLoopMode = ApInHltLoop;\r
+    }\r
   }\r
 \r
   if (ApLoopMode != ApInMwaitLoop) {\r
@@ -587,6 +598,112 @@ InitializeApData (
   SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
 }\r
 \r
+/**\r
+  Get Protected mode code segment with 16-bit default addressing\r
+  from current GDT table.\r
+\r
+  @return  Protected mode 16-bit code segment value.\r
+**/\r
+STATIC\r
+UINT16\r
+GetProtectedMode16CS (\r
+  VOID\r
+  )\r
+{\r
+  IA32_DESCRIPTOR          GdtrDesc;\r
+  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;\r
+  UINTN                    GdtEntryCount;\r
+  UINT16                   Index;\r
+\r
+  Index = (UINT16) -1;\r
+  AsmReadGdtr (&GdtrDesc);\r
+  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
+  for (Index = 0; Index < GdtEntryCount; Index++) {\r
+    if (GdtEntry->Bits.L == 0 &&\r
+        GdtEntry->Bits.DB == 0 &&\r
+        GdtEntry->Bits.Type > 8) {\r
+      break;\r
+    }\r
+    GdtEntry++;\r
+  }\r
+  ASSERT (Index != GdtEntryCount);\r
+  return Index * 8;\r
+}\r
+\r
+/**\r
+  Get Protected mode code segment with 32-bit default addressing\r
+  from current GDT table.\r
+\r
+  @return  Protected mode 32-bit code segment value.\r
+**/\r
+STATIC\r
+UINT16\r
+GetProtectedMode32CS (\r
+  VOID\r
+  )\r
+{\r
+  IA32_DESCRIPTOR          GdtrDesc;\r
+  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;\r
+  UINTN                    GdtEntryCount;\r
+  UINT16                   Index;\r
+\r
+  Index = (UINT16) -1;\r
+  AsmReadGdtr (&GdtrDesc);\r
+  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
+  for (Index = 0; Index < GdtEntryCount; Index++) {\r
+    if (GdtEntry->Bits.L == 0 &&\r
+        GdtEntry->Bits.DB == 1 &&\r
+        GdtEntry->Bits.Type > 8) {\r
+      break;\r
+    }\r
+    GdtEntry++;\r
+  }\r
+  ASSERT (Index != GdtEntryCount);\r
+  return Index * 8;\r
+}\r
+\r
+/**\r
+  Reset an AP when in SEV-ES mode.\r
+\r
+  If successful, this function never returns.\r
+\r
+  @param[in] Ghcb                 Pointer to the GHCB\r
+  @param[in] CpuMpData            Pointer to CPU MP Data\r
+\r
+**/\r
+STATIC\r
+VOID\r
+MpInitLibSevEsAPReset (\r
+  IN GHCB                         *Ghcb,\r
+  IN CPU_MP_DATA                  *CpuMpData\r
+  )\r
+{\r
+  UINT16           Code16, Code32;\r
+  AP_RESET         *APResetFn;\r
+  UINTN            BufferStart;\r
+  UINTN            StackStart;\r
+\r
+  Code16 = GetProtectedMode16CS ();\r
+  Code32 = GetProtectedMode32CS ();\r
+\r
+  if (CpuMpData->WakeupBufferHigh != 0) {\r
+    APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);\r
+  } else {\r
+    APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset);\r
+  }\r
+\r
+  BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;\r
+  StackStart = CpuMpData->SevEsAPResetStackStart -\r
+                 (AP_RESET_STACK_SIZE * GetApicId ());\r
+\r
+  //\r
+  // This call never returns.\r
+  //\r
+  APResetFn (BufferStart, Code16, Code32, StackStart);\r
+}\r
+\r
 /**\r
   This function will be called from AP reset code if BSP uses WakeUpAP.\r
 \r
@@ -648,7 +765,14 @@ ApWakeupFunction (
       InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);\r
       ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
 \r
-      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
+      //\r
+      // Delay decrementing the APs executing count when SEV-ES is enabled\r
+      // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly\r
+      // performs another INIT-SIPI-SIPI sequence.\r
+      //\r
+      if (!CpuMpData->SevEsIsEnabled) {\r
+        InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
+      }\r
     } else {\r
       //\r
       // Execute AP function if AP is ready\r
@@ -755,7 +879,52 @@ ApWakeupFunction (
       //\r
       while (TRUE) {\r
         DisableInterrupts ();\r
-        CpuSleep ();\r
+        if (CpuMpData->SevEsIsEnabled) {\r
+          MSR_SEV_ES_GHCB_REGISTER  Msr;\r
+          GHCB                      *Ghcb;\r
+          UINT64                    Status;\r
+          BOOLEAN                   DoDecrement;\r
+\r
+          if (CpuMpData->InitFlag == ApInitConfig) {\r
+            DoDecrement = TRUE;\r
+          }\r
+\r
+          while (TRUE) {\r
+            Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
+            Ghcb = Msr.Ghcb;\r
+\r
+            VmgInit (Ghcb);\r
+\r
+            if (DoDecrement) {\r
+              DoDecrement = FALSE;\r
+\r
+              //\r
+              // Perform the delayed decrement just before issuing the first\r
+              // VMGEXIT with AP_RESET_HOLD.\r
+              //\r
+              InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
+            }\r
+\r
+            Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);\r
+            if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {\r
+              VmgDone (Ghcb);\r
+              break;\r
+            }\r
+\r
+            VmgDone (Ghcb);\r
+          }\r
+\r
+          //\r
+          // Awakened in a new phase? Use the new CpuMpData\r
+          //\r
+          if (CpuMpData->NewCpuMpData != NULL) {\r
+            CpuMpData = CpuMpData->NewCpuMpData;\r
+          }\r
+\r
+          MpInitLibSevEsAPReset (Ghcb, CpuMpData);\r
+        } else {\r
+          CpuSleep ();\r
+        }\r
         CpuPause ();\r
       }\r
     }\r
@@ -868,6 +1037,9 @@ FillExchangeInfoData (
   ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);\r
   DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging));\r
 \r
+  ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;\r
+  ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;\r
+\r
   //\r
   // Get the BSP's data of GDT and IDT\r
   //\r
@@ -894,8 +1066,9 @@ FillExchangeInfoData (
   // EfiBootServicesCode to avoid page fault if NX memory protection is enabled.\r
   //\r
   if (CpuMpData->WakeupBufferHigh != 0) {\r
-    Size = CpuMpData->AddressMap.RendezvousFunnelSize -\r
-           CpuMpData->AddressMap.ModeTransitionOffset;\r
+    Size = CpuMpData->AddressMap.RendezvousFunnelSize +\r
+             CpuMpData->AddressMap.SwitchToRealSize -\r
+             CpuMpData->AddressMap.ModeTransitionOffset;\r
     CopyMem (\r
       (VOID *)CpuMpData->WakeupBufferHigh,\r
       CpuMpData->AddressMap.RendezvousFunnelAddress +\r
@@ -948,7 +1121,8 @@ BackupAndPrepareWakeupBuffer(
   CopyMem (\r
     (VOID *) CpuMpData->WakeupBuffer,\r
     (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,\r
-    CpuMpData->AddressMap.RendezvousFunnelSize\r
+    CpuMpData->AddressMap.RendezvousFunnelSize +\r
+      CpuMpData->AddressMap.SwitchToRealSize\r
     );\r
 }\r
 \r
@@ -969,6 +1143,44 @@ RestoreWakeupBuffer(
     );\r
 }\r
 \r
+/**\r
+  Calculate the size of the reset stack.\r
+\r
+  @return                 Total amount of memory required for stacks\r
+**/\r
+STATIC\r
+UINTN\r
+GetApResetStackSize (\r
+  VOID\r
+  )\r
+{\r
+  return AP_RESET_STACK_SIZE * PcdGet32(PcdCpuMaxLogicalProcessorNumber);\r
+}\r
+\r
+/**\r
+  Calculate the size of the reset vector.\r
+\r
+  @param[in]  AddressMap  The pointer to Address Map structure.\r
+\r
+  @return                 Total amount of memory required for the AP reset area\r
+**/\r
+STATIC\r
+UINTN\r
+GetApResetVectorSize (\r
+  IN MP_ASSEMBLY_ADDRESS_MAP  *AddressMap\r
+  )\r
+{\r
+  UINTN  Size;\r
+\r
+  Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +\r
+                        AddressMap->SwitchToRealSize +\r
+                        sizeof (MP_CPU_EXCHANGE_INFO),\r
+                      CPU_STACK_ALIGNMENT);\r
+  Size += GetApResetStackSize ();\r
+\r
+  return Size;\r
+}\r
+\r
 /**\r
   Allocate reset vector buffer.\r
 \r
@@ -982,16 +1194,22 @@ AllocateResetVector (
   UINTN           ApResetVectorSize;\r
 \r
   if (CpuMpData->WakeupBuffer == (UINTN) -1) {\r
-    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
-                          sizeof (MP_CPU_EXCHANGE_INFO);\r
+    ApResetVectorSize = GetApResetVectorSize (&CpuMpData->AddressMap);\r
 \r
     CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);\r
     CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)\r
-                    (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);\r
+                    (CpuMpData->WakeupBuffer +\r
+                       CpuMpData->AddressMap.RendezvousFunnelSize +\r
+                       CpuMpData->AddressMap.SwitchToRealSize);\r
     CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (\r
-                                    CpuMpData->AddressMap.RendezvousFunnelSize -\r
+                                    CpuMpData->AddressMap.RendezvousFunnelSize +\r
+                                    CpuMpData->AddressMap.SwitchToRealSize -\r
                                     CpuMpData->AddressMap.ModeTransitionOffset\r
                                     );\r
+    //\r
+    // The reset stack starts at the end of the buffer.\r
+    //\r
+    CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer + ApResetVectorSize;\r
   }\r
   BackupAndPrepareWakeupBuffer (CpuMpData);\r
 }\r
@@ -1006,7 +1224,80 @@ FreeResetVector (
   IN CPU_MP_DATA              *CpuMpData\r
   )\r
 {\r
-  RestoreWakeupBuffer (CpuMpData);\r
+  //\r
+  // If SEV-ES is enabled, the reset area is needed for AP parking and\r
+  // and AP startup in the OS, so the reset area is reserved. Do not\r
+  // perform the restore as this will overwrite memory which has data\r
+  // needed by SEV-ES.\r
+  //\r
+  if (!CpuMpData->SevEsIsEnabled) {\r
+    RestoreWakeupBuffer (CpuMpData);\r
+  }\r
+}\r
+\r
+/**\r
+  Allocate the SEV-ES AP jump table buffer.\r
+\r
+  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+AllocateSevEsAPMemory (\r
+  IN OUT CPU_MP_DATA          *CpuMpData\r
+  )\r
+{\r
+  if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {\r
+    CpuMpData->SevEsAPBuffer =\r
+      CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;\r
+  }\r
+}\r
+\r
+/**\r
+  Program the SEV-ES AP jump table buffer.\r
+\r
+  @param[in]  SipiVector  The SIPI vector used for the AP Reset\r
+**/\r
+VOID\r
+SetSevEsJumpTable (\r
+  IN UINTN  SipiVector\r
+  )\r
+{\r
+  SEV_ES_AP_JMP_FAR *JmpFar;\r
+  UINT32            Offset, InsnByte;\r
+  UINT8             LoNib, HiNib;\r
+\r
+  JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
+  ASSERT (JmpFar != NULL);\r
+\r
+  //\r
+  // Obtain the address of the Segment/Rip location in the workarea.\r
+  // This will be set to a value derived from the SIPI vector and will\r
+  // be the memory address used for the far jump below.\r
+  //\r
+  Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
+  Offset += sizeof (JmpFar->InsnBuffer);\r
+  LoNib = (UINT8) Offset;\r
+  HiNib = (UINT8) (Offset >> 8);\r
+\r
+  //\r
+  // Program the workarea (which is the initial AP boot address) with\r
+  // far jump to the SIPI vector (where XX and YY represent the\r
+  // address of where the SIPI vector is stored.\r
+  //\r
+  //   JMP FAR [CS:XXYY] => 2E FF 2E YY XX\r
+  //\r
+  InsnByte = 0;\r
+  JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // CS override prefix\r
+  JmpFar->InsnBuffer[InsnByte++] = 0xFF;  // JMP (FAR)\r
+  JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // ModRM (JMP memory location)\r
+  JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...\r
+  JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...\r
+\r
+  //\r
+  // Program the Segment/Rip based on the SIPI vector (always at least\r
+  // 16-byte aligned, so Rip is set to 0).\r
+  //\r
+  JmpFar->Rip = 0;\r
+  JmpFar->Segment = (UINT16) (SipiVector >> 4);\r
 }\r
 \r
 /**\r
@@ -1043,6 +1334,7 @@ WakeUpAP (
       CpuMpData->InitFlag   != ApInitDone) {\r
     ResetVectorRequired = TRUE;\r
     AllocateResetVector (CpuMpData);\r
+    AllocateSevEsAPMemory (CpuMpData);\r
     FillExchangeInfoData (CpuMpData);\r
     SaveLocalApicTimerSetting (CpuMpData);\r
   }\r
@@ -1079,6 +1371,15 @@ WakeUpAP (
       }\r
     }\r
     if (ResetVectorRequired) {\r
+      //\r
+      // For SEV-ES, the initial AP boot address will be defined by\r
+      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address\r
+      // from the original INIT-SIPI-SIPI.\r
+      //\r
+      if (CpuMpData->SevEsIsEnabled) {\r
+        SetSevEsJumpTable (ExchangeInfo->BufferStart);\r
+      }\r
+\r
       //\r
       // Wakeup all APs\r
       //\r
@@ -1170,6 +1471,16 @@ WakeUpAP (
     *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;\r
     if (ResetVectorRequired) {\r
       CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
+\r
+      //\r
+      // For SEV-ES, the initial AP boot address will be defined by\r
+      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address\r
+      // from the original INIT-SIPI-SIPI.\r
+      //\r
+      if (CpuMpData->SevEsIsEnabled) {\r
+        SetSevEsJumpTable (ExchangeInfo->BufferStart);\r
+      }\r
+\r
       SendInitSipiSipi (\r
         CpuInfoInHob[ProcessorNumber].ApicId,\r
         (UINT32) ExchangeInfo->BufferStart\r
@@ -1646,7 +1957,7 @@ MpInitLibInitialize (
   ASSERT (MaxLogicalProcessorNumber != 0);\r
 \r
   AsmGetAddressMap (&AddressMap);\r
-  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);\r
+  ApResetVectorSize = GetApResetVectorSize (&AddressMap);\r
   ApStackSize = PcdGet32(PcdCpuApStackSize);\r
   ApLoopMode  = GetApLoopMode (&MonitorFilterSize);\r
 \r
@@ -1705,6 +2016,8 @@ MpInitLibInitialize (
   CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);\r
   InitializeSpinLock(&CpuMpData->MpLock);\r
   CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);\r
+  CpuMpData->SevEsAPBuffer  = (UINTN) -1;\r
+  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);\r
 \r
   //\r
   // Make sure no memory usage outside of the allocated buffer.\r
@@ -1763,6 +2076,7 @@ MpInitLibInitialize (
     // APs have been wakeup before, just get the CPU Information\r
     // from HOB\r
     //\r
+    OldCpuMpData->NewCpuMpData = CpuMpData;\r
     CpuMpData->CpuCount  = OldCpuMpData->CpuCount;\r
     CpuMpData->BspNumber = OldCpuMpData->BspNumber;\r
     CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob;\r
index 5b46c295b6b2b5a01c199b1e1b38832562198ef4..b1a9d99cb3eb3d82b8e534672cea5f28b5ae7c20 100644 (file)
@@ -173,6 +173,11 @@ typedef struct {
   UINT8             *RelocateApLoopFuncAddress;\r
   UINTN             RelocateApLoopFuncSize;\r
   UINTN             ModeTransitionOffset;\r
+  UINTN             SwitchToRealSize;\r
+  UINTN             SwitchToRealOffset;\r
+  UINTN             SwitchToRealNoNxOffset;\r
+  UINTN             SwitchToRealPM16ModeOffset;\r
+  UINTN             SwitchToRealPM16ModeSize;\r
 } MP_ASSEMBLY_ADDRESS_MAP;\r
 \r
 typedef struct _CPU_MP_DATA  CPU_MP_DATA;\r
@@ -211,6 +216,8 @@ typedef struct {
   // Enable5LevelPaging indicates whether 5-level paging is enabled in long mode.\r
   //\r
   BOOLEAN               Enable5LevelPaging;\r
+  BOOLEAN               SevEsIsEnabled;\r
+  UINTN                 GhcbBase;\r
 } MP_CPU_EXCHANGE_INFO;\r
 \r
 #pragma pack()\r
@@ -257,6 +264,7 @@ struct _CPU_MP_DATA {
   UINT8                          ApLoopMode;\r
   UINT8                          ApTargetCState;\r
   UINT16                         PmCodeSegment;\r
+  UINT16                         Pm16CodeSegment;\r
   CPU_AP_DATA                    *CpuData;\r
   volatile MP_CPU_EXCHANGE_INFO  *MpCpuExchangeInfo;\r
 \r
@@ -278,8 +286,47 @@ struct _CPU_MP_DATA {
   BOOLEAN                        WakeUpByInitSipiSipi;\r
 \r
   BOOLEAN                        SevEsIsEnabled;\r
+  UINTN                          SevEsAPBuffer;\r
+  UINTN                          SevEsAPResetStackStart;\r
+  CPU_MP_DATA                    *NewCpuMpData;\r
+\r
+  UINT64                         GhcbBase;\r
 };\r
 \r
+#define AP_RESET_STACK_SIZE 64\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct {\r
+  UINT8   InsnBuffer[8];\r
+  UINT16  Rip;\r
+  UINT16  Segment;\r
+} SEV_ES_AP_JMP_FAR;\r
+\r
+#pragma pack()\r
+\r
+/**\r
+  Assembly code to move an AP from long mode to real mode.\r
+\r
+  Move an AP from long mode to real mode in preparation to invoking\r
+  the reset vector.  This is used for SEV-ES guests where a hypervisor\r
+  is not allowed to set the CS and RIP to point to the reset vector.\r
+\r
+  @param[in]  BufferStart  The reset vector target.\r
+  @param[in]  Code16       16-bit protected mode code segment value.\r
+  @param[in]  Code32       32-bit protected mode code segment value.\r
+  @param[in]  StackStart   The start of a stack to be used for transitioning\r
+                           from long mode to real mode.\r
+**/\r
+typedef\r
+VOID\r
+(EFIAPI AP_RESET) (\r
+  IN UINTN    BufferStart,\r
+  IN UINT16   Code16,\r
+  IN UINT16   Code32,\r
+  IN UINTN    StackStart\r
+  );\r
+\r
 extern EFI_GUID mCpuInitMpLibHobGuid;\r
 \r
 /**\r
@@ -385,6 +432,19 @@ GetModeTransitionBuffer (
   IN UINTN                BufferSize\r
   );\r
 \r
+/**\r
+  Return the address of the SEV-ES AP jump table.\r
+\r
+  This buffer is required in order for an SEV-ES guest to transition from\r
+  UEFI into an OS.\r
+\r
+  @return         Return SEV-ES AP jump table buffer\r
+**/\r
+UINTN\r
+GetSevEsAPMemory (\r
+  VOID\r
+  );\r
+\r
 /**\r
   This function will be called by BSP to wakeup AP.\r
 \r
index 4b3d39fbf36c84186319906ecba4b38f49b66ce9..34abf25d43cd2a1ee2f9b893039ee2faf3616403 100644 (file)
@@ -51,6 +51,7 @@
   SynchronizationLib\r
   PeiServicesLib\r
   PcdLib\r
+  VmgExitLib\r
 \r
 [Pcd]\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ## CONSUMES\r
@@ -62,6 +63,8 @@
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ## CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ## SOMETIMES_CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES\r
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ## SOMETIMES_CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ## CONSUMES\r
 \r
 [Ppis]\r
   gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES\r
index a548fed23fa7212f01f9f74554030b5bf992a79b..3989bd6a7a9fcd6a5ce57475bb5c0e3d33531b9f 100644 (file)
@@ -280,6 +280,25 @@ GetModeTransitionBuffer (
   return 0;\r
 }\r
 \r
+/**\r
+  Return the address of the SEV-ES AP jump table.\r
+\r
+  This buffer is required in order for an SEV-ES guest to transition from\r
+  UEFI into an OS.\r
+\r
+  @return         Return SEV-ES AP jump table buffer\r
+**/\r
+UINTN\r
+GetSevEsAPMemory (\r
+  VOID\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 58ef369342a78b59e3538321012eb31ff7d4375f..c92daaaffd6b33ccd5119c9dc7929cf01a63b0e2 100644 (file)
@@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
 CPU_SWITCH_STATE_STORED       equ        1\r
 CPU_SWITCH_STATE_LOADED       equ        2\r
 \r
-LockLocation                  equ        (RendezvousFunnelProcEnd - RendezvousFunnelProcStart)\r
+LockLocation                  equ        (SwitchToRealProcEnd - RendezvousFunnelProcStart)\r
 StackStartAddressLocation     equ        LockLocation + 08h\r
 StackSizeLocation             equ        LockLocation + 10h\r
 ApProcedureLocation           equ        LockLocation + 18h\r
@@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ  LockLocation + 98h
 ModeHighMemoryLocation              equ  LockLocation + 9Ah\r
 ModeHighSegmentLocation             equ  LockLocation + 9Eh\r
 Enable5LevelPagingLocation          equ  LockLocation + 0A0h\r
+SevEsIsEnabledLocation              equ  LockLocation + 0A1h\r
+GhcbBaseLocation                    equ  LockLocation + 0A2h\r
index 87f2523e856faaeecd187058f5ac039665d86627..6956b408d004103e67eba5e41ca3386649182962 100644 (file)
@@ -184,9 +184,97 @@ Releaselock:
     add        edi, StackStartAddressLocation\r
     add        rax, qword [edi]\r
     mov        rsp, rax\r
+\r
+    lea        edi, [esi + SevEsIsEnabledLocation]\r
+    cmp        byte [edi], 1        ; SevEsIsEnabled\r
+    jne        CProcedureInvoke\r
+\r
+    ;\r
+    ; program GHCB\r
+    ;   Each page after the GHCB is a per-CPU page, so the calculation programs\r
+    ;   a GHCB to be every 8KB.\r
+    ;\r
+    mov        eax, SIZE_4KB\r
+    shl        eax, 1                            ; EAX = SIZE_4K * 2\r
+    mov        ecx, ebx\r
+    mul        ecx                               ; EAX = SIZE_4K * 2 * CpuNumber\r
+    mov        edi, esi\r
+    add        edi, GhcbBaseLocation\r
+    add        rax, qword [edi]\r
+    mov        rdx, rax\r
+    shr        rdx, 32\r
+    mov        rcx, 0xc0010130\r
+    wrmsr\r
     jmp        CProcedureInvoke\r
 \r
 GetApicId:\r
+    lea        edi, [esi + SevEsIsEnabledLocation]\r
+    cmp        byte [edi], 1        ; SevEsIsEnabled\r
+    jne        DoCpuid\r
+\r
+    ;\r
+    ; Since we don't have a stack yet, we can't take a #VC\r
+    ; exception. Use the GHCB protocol to perform the CPUID\r
+    ; calls.\r
+    ;\r
+    mov        rcx, 0xc0010130\r
+    rdmsr\r
+    shl        rdx, 32\r
+    or         rax, rdx\r
+    mov        rdi, rax             ; RDI now holds the original GHCB GPA\r
+\r
+    mov        rdx, 0               ; CPUID function 0\r
+    mov        rax, 0               ; RAX register requested\r
+    or         rax, 4\r
+    wrmsr\r
+    rep vmmcall\r
+    rdmsr\r
+    cmp        edx, 0bh\r
+    jb         NoX2ApicSevEs        ; CPUID level below CPUID_EXTENDED_TOPOLOGY\r
+\r
+    mov        rdx, 0bh             ; CPUID function 0x0b\r
+    mov        rax, 040000000h      ; RBX register requested\r
+    or         rax, 4\r
+    wrmsr\r
+    rep vmmcall\r
+    rdmsr\r
+    test       edx, 0ffffh\r
+    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero\r
+\r
+    mov        rdx, 0bh             ; CPUID function 0x0b\r
+    mov        rax, 0c0000000h      ; RDX register requested\r
+    or         rax, 4\r
+    wrmsr\r
+    rep vmmcall\r
+    rdmsr\r
+\r
+    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX\r
+    jmp        RestoreGhcb\r
+\r
+NoX2ApicSevEs:\r
+    ; Processor is not x2APIC capable, so get 8-bit APIC ID\r
+    mov        rdx, 1               ; CPUID function 1\r
+    mov        rax, 040000000h      ; RBX register requested\r
+    or         rax, 4\r
+    wrmsr\r
+    rep vmmcall\r
+    rdmsr\r
+    shr        edx, 24\r
+\r
+RestoreGhcb:\r
+    mov        rbx, rdx             ; Save x2APIC/APIC ID\r
+\r
+    mov        rdx, rdi             ; RDI holds the saved GHCB GPA\r
+    shr        rdx, 32\r
+    mov        eax, edi\r
+    wrmsr\r
+\r
+    mov        rdx, rbx\r
+\r
+    ; x2APIC ID or APIC ID is in EDX\r
+    jmp        GetProcessorNumber\r
+\r
+DoCpuid:\r
     mov        eax, 0\r
     cpuid\r
     cmp        eax, 0bh\r
@@ -253,12 +341,158 @@ CProcedureInvoke:
 \r
 RendezvousFunnelProcEnd:\r
 \r
+;-------------------------------------------------------------------------------------\r
+;SwitchToRealProc procedure follows.\r
+;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT MODE. HENCE THIS PROC\r
+;IS IN MACHINE CODE.\r
+;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, UINTN StackStart)\r
+;  rcx - Buffer Start\r
+;  rdx - Code16 Selector Offset\r
+;  r8  - Code32 Selector Offset\r
+;  r9  - Stack Start\r
+;-------------------------------------------------------------------------------------\r
+global ASM_PFX(SwitchToRealProc)\r
+ASM_PFX(SwitchToRealProc):\r
+SwitchToRealProcStart:\r
+BITS 64\r
+    cli\r
+\r
+    ;\r
+    ; Get RDX reset value before changing stacks since the\r
+    ; new stack won't be able to accomodate a #VC exception.\r
+    ;\r
+    push       rax\r
+    push       rbx\r
+    push       rcx\r
+    push       rdx\r
+\r
+    mov        rax, 1\r
+    cpuid\r
+    mov        rsi, rax                    ; Save off the reset value for RDX\r
+\r
+    pop        rdx\r
+    pop        rcx\r
+    pop        rbx\r
+    pop        rax\r
+\r
+    ;\r
+    ; Establish stack below 1MB\r
+    ;\r
+    mov        rsp, r9\r
+\r
+    ;\r
+    ; Push ultimate Reset Vector onto the stack\r
+    ;\r
+    mov        rax, rcx\r
+    shr        rax, 4\r
+    push       word 0x0002                 ; RFLAGS\r
+    push       ax                          ; CS\r
+    push       word 0x0000                 ; RIP\r
+    push       word 0x0000                 ; For alignment, will be discarded\r
+\r
+    ;\r
+    ; Get address of "16-bit operand size" label\r
+    ;\r
+    lea        rbx, [PM16Mode]\r
+\r
+    ;\r
+    ; Push addresses used to change to compatibility mode\r
+    ;\r
+    lea        rax, [CompatMode]\r
+    push       r8\r
+    push       rax\r
+\r
+    ;\r
+    ; Clear R8 - R15, for reset, before going into 32-bit mode\r
+    ;\r
+    xor        r8, r8\r
+    xor        r9, r9\r
+    xor        r10, r10\r
+    xor        r11, r11\r
+    xor        r12, r12\r
+    xor        r13, r13\r
+    xor        r14, r14\r
+    xor        r15, r15\r
+\r
+    ;\r
+    ; Far return into 32-bit mode\r
+    ;\r
+o64 retf\r
+\r
+BITS 32\r
+CompatMode:\r
+    ;\r
+    ; Set up stack to prepare for exiting protected mode\r
+    ;\r
+    push       edx                         ; Code16 CS\r
+    push       ebx                         ; PM16Mode label address\r
+\r
+    ;\r
+    ; Disable paging\r
+    ;\r
+    mov        eax, cr0                    ; Read CR0\r
+    btr        eax, 31                     ; Set PG=0\r
+    mov        cr0, eax                    ; Write CR0\r
+\r
+    ;\r
+    ; Disable long mode\r
+    ;\r
+    mov        ecx, 0c0000080h             ; EFER MSR number\r
+    rdmsr                                  ; Read EFER\r
+    btr        eax, 8                      ; Set LME=0\r
+    wrmsr                                  ; Write EFER\r
+\r
+    ;\r
+    ; Disable PAE\r
+    ;\r
+    mov        eax, cr4                    ; Read CR4\r
+    btr        eax, 5                      ; Set PAE=0\r
+    mov        cr4, eax                    ; Write CR4\r
+\r
+    mov        edx, esi                    ; Restore RDX reset value\r
+\r
+    ;\r
+    ; Switch to 16-bit operand size\r
+    ;\r
+    retf\r
+\r
+BITS 16\r
+    ;\r
+    ; At entry to this label\r
+    ;   - RDX will have its reset value\r
+    ;   - On the top of the stack\r
+    ;     - Alignment data (two bytes) to be discarded\r
+    ;     - IP for Real Mode (two bytes)\r
+    ;     - CS for Real Mode (two bytes)\r
+    ;\r
+PM16Mode:\r
+    mov        eax, cr0                    ; Read CR0\r
+    btr        eax, 0                      ; Set PE=0\r
+    mov        cr0, eax                    ; Write CR0\r
+\r
+    pop        ax                          ; Discard alignment data\r
+\r
+    ;\r
+    ; Clear registers (except RDX and RSP) before going into 16-bit mode\r
+    ;\r
+    xor        eax, eax\r
+    xor        ebx, ebx\r
+    xor        ecx, ecx\r
+    xor        esi, esi\r
+    xor        edi, edi\r
+    xor        ebp, ebp\r
+\r
+    iret\r
+\r
+SwitchToRealProcEnd:\r
+\r
 ;-------------------------------------------------------------------------------------\r
 ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);\r
 ;-------------------------------------------------------------------------------------\r
 global ASM_PFX(AsmRelocateApLoop)\r
 ASM_PFX(AsmRelocateApLoop):\r
 AsmRelocateApLoopStart:\r
+BITS 64\r
     cli                          ; Disable interrupt before switching to 32-bit mode\r
     mov        rax, [rsp + 40]   ; CountTofinish\r
     lock dec   dword [rax]       ; (*CountTofinish)--\r
@@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
     mov        qword [rcx + 18h], rax\r
     mov        qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart\r
     mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart\r
+    mov        qword [rcx + 30h], SwitchToRealProcEnd - SwitchToRealProcStart          ; SwitchToRealSize\r
+    mov        qword [rcx + 38h], SwitchToRealProcStart - RendezvousFunnelProcStart    ; SwitchToRealOffset\r
+    mov        qword [rcx + 40h], SwitchToRealProcStart - Flat32Start                  ; SwitchToRealNoNxOffset\r
+    mov        qword [rcx + 48h], PM16Mode - RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset\r
+    mov        qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode                       ; SwitchToRealPM16ModeSize\r
     ret\r
 \r
 ;-------------------------------------------------------------------------------------\r
index 6298571e29b2bc83cf05ffd8ad7fd18b1cd10ebc..28f8e8e133e5cd8782ae40afc21deb49e7fe27eb 100644 (file)
@@ -121,7 +121,7 @@ GetProtectedModeCS (
   GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
   for (Index = 0; Index < GdtEntryCount; Index++) {\r
     if (GdtEntry->Bits.L == 0) {\r
-      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {\r
+      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {\r
         break;\r
       }\r
     }\r