On 6/1/20 1:17 AM, Dong, Eric wrote:
> Hi Tom,

Hi Eric,

> 
>> -----Original Message-----
>> From: Tom Lendacky <thomas.lenda...@amd.com>
>> Sent: Wednesday, May 20, 2020 5:51 AM
>> To: devel@edk2.groups.io
>> Cc: Justen, Jordan L <jordan.l.jus...@intel.com>; Laszlo Ersek
>> <ler...@redhat.com>; Ard Biesheuvel <ard.biesheu...@linaro.org>; Kinney,
>> Michael D <michael.d.kin...@intel.com>; Gao, Liming
>> <liming....@intel.com>; Dong, Eric <eric.d...@intel.com>; Ni, Ray
>> <ray...@intel.com>; Brijesh Singh <brijesh.si...@amd.com>
>> Subject: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
>>
>> BZ: 
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C7f0288f6b6964c30b79808d805f3719d%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637265890432557809&amp;sdata=t8Gz8tXGoSpO3N5BDGCyRp%2FqH3PF6xitfIzV27rGSe0%3D&amp;reserved=0
>>
>> 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.d...@intel.com>
>> Cc: Ray Ni <ray...@intel.com>
>> Cc: Laszlo Ersek <ler...@redhat.com>
>> Signed-off-by: Tom Lendacky <thomas.lenda...@amd.com>
>> ---
>>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   3 +
>>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   3 +
>>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |  60 ++++
>>  UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  70 +++-
>>  UefiCpuPkg/Library/MpInitLib/MpLib.c          | 312 +++++++++++++++++-
>>  UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |  19 ++
>>  UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |   2 +-
>>  UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |   2 +-
>>  .../Library/MpInitLib/Ia32/MpFuncs.nasm       |  15 +
>>  UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |   4 +-
>>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 239 ++++++++++++++
>>  11 files changed, 714 insertions(+), 15 deletions(-)
>>
>> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> index 583276595619..1771575c69c1 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> @@ -52,6 +52,7 @@ [LibraryClasses]
>>    DebugAgentLib
>>    SynchronizationLib
>>    PcdLib
>> +  VmgExitLib
>>
>>  [Protocols]
>>    gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES
>> @@ -72,4 +73,6 @@ [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ##
>> SOMETIMES_CONSUMES
>>
>> gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds
>> ## CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ##
>> CONSUMES
>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ##
>> SOMETIMES_CONSUMES
>>    gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ##
>> CONSUMES
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ##
>> CONSUMES
>> diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> index 4b3d39fbf36c..34abf25d43cd 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> @@ -51,6 +51,7 @@ [LibraryClasses]
>>    SynchronizationLib
>>    PeiServicesLib
>>    PcdLib
>> +  VmgExitLib
>>
>>  [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ##
>> CONSUMES
>> @@ -62,6 +63,8 @@ [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ##
>> CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ##
>> SOMETIMES_CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## 
>> CONSUMES
>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ##
>> SOMETIMES_CONSUMES
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ##
>> CONSUMES
>>
>>  [Ppis]
>>    gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
>> diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> b/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> index 5b46c295b6b2..f0cbb3763b5d 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> @@ -173,6 +173,11 @@ typedef struct {
>>    UINT8             *RelocateApLoopFuncAddress;
>>    UINTN             RelocateApLoopFuncSize;
>>    UINTN             ModeTransitionOffset;
>> +  UINTN             SwitchToRealSize;
>> +  UINTN             SwitchToRealOffset;
>> +  UINTN             SwitchToRealNoNxOffset;
>> +  UINTN             SwitchToRealPM16ModeOffset;
>> +  UINTN             SwitchToRealPM16ModeSize;
>>  } MP_ASSEMBLY_ADDRESS_MAP;
>>
>>  typedef struct _CPU_MP_DATA  CPU_MP_DATA; @@ -211,6 +216,8 @@
>> typedef struct {
>>    // Enable5LevelPaging indicates whether 5-level paging is enabled in long
>> mode.
>>    //
>>    BOOLEAN               Enable5LevelPaging;
>> +  BOOLEAN               SevEsIsEnabled;
>> +  UINTN                 GhcbBase;
>>  } MP_CPU_EXCHANGE_INFO;
>>
>>  #pragma pack()
>> @@ -257,6 +264,7 @@ struct _CPU_MP_DATA {
>>    UINT8                          ApLoopMode;
>>    UINT8                          ApTargetCState;
>>    UINT16                         PmCodeSegment;
>> +  UINT16                         Pm16CodeSegment;
>>    CPU_AP_DATA                    *CpuData;
>>    volatile MP_CPU_EXCHANGE_INFO  *MpCpuExchangeInfo;
>>
>> @@ -278,8 +286,47 @@ struct _CPU_MP_DATA {
>>    BOOLEAN                        WakeUpByInitSipiSipi;
>>
>>    BOOLEAN                        SevEsIsEnabled;
>> +  UINTN                          SevEsAPBuffer;
>> +  UINTN                          SevEsAPResetStackStart;
>> +  CPU_MP_DATA                    *NewCpuMpData;
>> +
>> +  UINT64                         GhcbBase;
>>  };
>>
>> +#define AP_RESET_STACK_SIZE 64
>> +
>> +#pragma pack(1)
>> +
>> +typedef struct {
>> +  UINT8   InsnBuffer[8];
>> +  UINT16  Rip;
>> +  UINT16  Segment;
>> +} SEV_ES_AP_JMP_FAR;
>> +
>> +#pragma pack()
>> +
>> +/**
>> +  Assembly code to move an AP from long mode to real mode.
>> +
>> +  Move an AP from long mode to real mode in preparation to invoking
>> + the reset vector.  This is used for SEV-ES guests where a hypervisor
>> + is not allowed to set the CS and RIP to point to the reset vector.
>> +
>> +  @param[in]  BufferStart  The reset vector target.
>> +  @param[in]  Code16       16-bit protected mode code segment value.
>> +  @param[in]  Code32       32-bit protected mode code segment value.
>> +  @param[in]  StackStart   The start of a stack to be used for transitioning
>> +                           from long mode to real mode.
>> +**/
>> +typedef
>> +VOID
>> +(EFIAPI AP_RESET) (
>> +  IN UINTN    BufferStart,
>> +  IN UINT16   Code16,
>> +  IN UINT16   Code32,
>> +  IN UINTN    StackStart
>> +  );
>> +
>>  extern EFI_GUID mCpuInitMpLibHobGuid;
>>
>>  /**
>> @@ -385,6 +432,19 @@ GetModeTransitionBuffer (
>>    IN UINTN                BufferSize
>>    );
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  );
>> +
>>  /**
>>    This function will be called by BSP to wakeup AP.
>>
>> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> index 8ccddf8e9f9c..19527300ff3a 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> @@ -12,6 +12,8 @@
>>  #include <Library/UefiBootServicesTableLib.h>
>>  #include <Library/DebugAgentLib.h>
>>  #include <Library/DxeServicesTableLib.h>
>> +#include <Register/Amd/Fam17Msr.h>
>> +#include <Register/Amd/Ghcb.h>
>>
>>  #include <Protocol/Timer.h>
>>
>> @@ -144,6 +146,39 @@ GetModeTransitionBuffer (
>>    return (UINTN)StartAddress;
>>  }
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS            Status;
>> +  EFI_PHYSICAL_ADDRESS  StartAddress;
>> +
>> +  //
>> +  // Allocate 1 page for AP jump table page  //  StartAddress =
>> + BASE_4GB - 1;  Status = gBS->AllocatePages (
>> +                  AllocateMaxAddress,
>> +                  EfiReservedMemoryType,
>> +                  1,
>> +                  &StartAddress
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN)
>> + StartAddress));
>> +
>> +  return (UINTN) StartAddress;
>> +}
>> +
>>  /**
>>    Checks APs status and updates APs status if needed.
>>
>> @@ -218,6 +253,38 @@ CheckApsStatus (
>>    }
>>  }
>>
>> +/**
>> +  Get Protected mode code segment with 16-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 16-bit code segment value.
>> +**/
>> +UINT16
>> +GetProtectedMode16CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) {
>> +        break;
>> +      }
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>>  /**
>>    Get Protected mode code segment from current GDT table.
>>
>> @@ -238,7 +305,7 @@ GetProtectedModeCS (
>>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>>    for (Index = 0; Index < GdtEntryCount; Index++) {
>>      if (GdtEntry->Bits.L == 0) {
>> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>>          break;
>>        }
>>      }
>> @@ -300,6 +367,7 @@ MpInitChangeApLoopCallback (
>>
>>    CpuMpData = GetCpuMpData ();
>>    CpuMpData->PmCodeSegment = GetProtectedModeCS ();
>> +  CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();
>>    CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
>>    mNumberToFinish = CpuMpData->CpuCount - 1;
>>    WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); diff --git
>> a/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> index a8b605f569bf..aeab575bb525 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> @@ -9,6 +9,9 @@
>>  **/
>>
>>  #include "MpLib.h"
>> +#include <Library/VmgExitLib.h>
>> +#include <Register/Amd/Fam17Msr.h>
>> +#include <Register/Amd/Ghcb.h>
>>
>>  EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
>>
>> @@ -314,6 +317,14 @@ GetApLoopMode (
>>        //
>>        ApLoopMode = ApInHltLoop;
>>      }
>> +
>> +    if (PcdGetBool (PcdSevEsIsEnabled)) {
>> +      //
>> +      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB
>> +      // protocol for starting APs
>> +      //
>> +      ApLoopMode = ApInHltLoop;
>> +    }
>>    }
>>
>>    if (ApLoopMode != ApInMwaitLoop) {
>> @@ -610,6 +621,112 @@ InitializeApData (
>>    SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);  }
>>
>> +/**
>> +  Get Protected mode code segment with 16-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 16-bit code segment value.
>> +**/
>> +STATIC
>> +UINT16
>> +GetProtectedMode16CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0 &&
>> +        GdtEntry->Bits.DB == 0 &&
>> +        GdtEntry->Bits.Type > 8) {
>> +      break;
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>> +/**
>> +  Get Protected mode code segment with 32-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 32-bit code segment value.
>> +**/
>> +STATIC
>> +UINT16
>> +GetProtectedMode32CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0 &&
>> +        GdtEntry->Bits.DB == 1 &&
>> +        GdtEntry->Bits.Type > 8) {
>> +      break;
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>> +/**
>> +  Reset an AP when in SEV-ES mode.
>> +
>> +  If successful, this function never returns.
>> +
>> +  @param[in] Ghcb                 Pointer to the GHCB
>> +  @param[in] CpuMpData            Pointer to CPU MP Data
>> +
>> +**/
>> +STATIC
>> +VOID
>> +MpInitLibSevEsAPReset (
>> +  IN GHCB                         *Ghcb,
>> +  IN CPU_MP_DATA                  *CpuMpData
>> +  )
>> +{
>> +  UINT16           Code16, Code32;
>> +  AP_RESET         *APResetFn;
>> +  UINTN            BufferStart;
>> +  UINTN            StackStart;
>> +
>> +  Code16 = GetProtectedMode16CS ();
>> +  Code32 = GetProtectedMode32CS ();
>> +
>> +  if (CpuMpData->WakeupBufferHigh != 0) {
>> +    APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh +
>> + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
>> +  } else {
>> +    APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo-
>>> BufferStart
>> + + CpuMpData->AddressMap.SwitchToRealOffset);
>> +  }
>> +
>> +  BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
>> +  StackStart = CpuMpData->SevEsAPResetStackStart -
>> +                 (AP_RESET_STACK_SIZE * GetApicId ());
>> +
>> +  //
>> +  // This call never returns.
>> +  //
>> +  APResetFn (BufferStart, Code16, Code32, StackStart); }
>> +
>>  /**
>>    This function will be called from AP reset code if BSP uses WakeUpAP.
>>
>> @@ -671,7 +788,14 @@ ApWakeupFunction (
>>        InitializeApData (CpuMpData, ProcessorNumber, BistData,
>> ApTopOfStack);
>>        ApStartupSignalBuffer = CpuMpData-
>>> CpuData[ProcessorNumber].StartupApSignal;
>>
>> -      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo-
>>> NumApsExecuting);
>> +      //
>> +      // Delay decrementing the APs executing count when SEV-ES is enabled
>> +      // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly
>> +      // performs another INIT-SIPI-SIPI sequence.
>> +      //
>> +      if (!CpuMpData->SevEsIsEnabled) {
>> +        InterlockedDecrement ((UINT32 *) &CpuMpData-
>>> MpCpuExchangeInfo->NumApsExecuting);
>> +      }
>>      } else {
>>        //
>>        // Execute AP function if AP is ready @@ -778,7 +902,52 @@
>> ApWakeupFunction (
>>        //
>>        while (TRUE) {
>>          DisableInterrupts ();
>> -        CpuSleep ();
>> +        if (CpuMpData->SevEsIsEnabled) {
>> +          MSR_SEV_ES_GHCB_REGISTER  Msr;
>> +          GHCB                      *Ghcb;
>> +          UINT64                    Status;
>> +          BOOLEAN                   DoDecrement;
>> +
>> +          if (CpuMpData->InitFlag == ApInitConfig) {
>> +            DoDecrement = TRUE;
>> +          }
>> +
>> +          while (TRUE) {
>> +            Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
>> +            Ghcb = Msr.Ghcb;
>> +
>> +            VmgInit (Ghcb);
>> +
>> +            if (DoDecrement) {
>> +              DoDecrement = FALSE;
>> +
>> +              //
>> +              // Perform the delayed decrement just before issuing the first
>> +              // VMGEXIT with AP_RESET_HOLD.
>> +              //
>> +              InterlockedDecrement ((UINT32 *) &CpuMpData-
>>> MpCpuExchangeInfo->NumApsExecuting);
>> +            }
>> +
>> +            Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
>> +            if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
>> +              VmgDone (Ghcb);
>> +              break;
>> +            }
>> +
>> +            VmgDone (Ghcb);
>> +          }
>> +
>> +          //
>> +          // Awakened in a new phase? Use the new CpuMpData
>> +          //
>> +          if (CpuMpData->NewCpuMpData) {
>> +            CpuMpData = CpuMpData->NewCpuMpData;
>> +          }
>> +
>> +          MpInitLibSevEsAPReset (Ghcb, CpuMpData);
>> +        } else {
>> +          CpuSleep ();
>> +        }
>>          CpuPause ();
>>        }
>>      }
>> @@ -891,6 +1060,9 @@ FillExchangeInfoData (
>>    ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
>>    DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName,
>> ExchangeInfo->Enable5LevelPaging));
>>
>> +  ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;
>> +  ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;
>> +
>>    //
>>    // Get the BSP's data of GDT and IDT
>>    //
>> @@ -917,8 +1089,9 @@ FillExchangeInfoData (
>>    // EfiBootServicesCode to avoid page fault if NX memory protection is
>> enabled.
>>    //
>>    if (CpuMpData->WakeupBufferHigh != 0) {
>> -    Size = CpuMpData->AddressMap.RendezvousFunnelSize -
>> -           CpuMpData->AddressMap.ModeTransitionOffset;
>> +    Size = CpuMpData->AddressMap.RendezvousFunnelSize +
>> +             CpuMpData->AddressMap.SwitchToRealSize -
>> +             CpuMpData->AddressMap.ModeTransitionOffset;
>>      CopyMem (
>>        (VOID *)CpuMpData->WakeupBufferHigh,
>>        CpuMpData->AddressMap.RendezvousFunnelAddress + @@ -971,7
>> +1144,8 @@ BackupAndPrepareWakeupBuffer(
>>    CopyMem (
>>      (VOID *) CpuMpData->WakeupBuffer,
>>      (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
>> -    CpuMpData->AddressMap.RendezvousFunnelSize
>> +    CpuMpData->AddressMap.RendezvousFunnelSize +
>> +      CpuMpData->AddressMap.SwitchToRealSize
>>      );
>>  }
>>
>> @@ -992,6 +1166,44 @@ RestoreWakeupBuffer(
>>      );
>>  }
>>
>> +/**
>> +  Calculate the size of the reset stack.
>> +
>> +  @retval                 Total amount of memory required for stacks
>> +**/
>> +STATIC
>> +UINTN
>> +GetApResetStackSize (
>> +  VOID
>> +  )
>> +{
>> +  return AP_RESET_STACK_SIZE *
>> +PcdGet32(PcdCpuMaxLogicalProcessorNumber);
>> +}
>> +
>> +/**
>> +  Calculate the size of the reset vector.
>> +
>> +  @param[in]  AddressMap  The pointer to Address Map structure.
>> +
>> +  @retval                 Total amount of memory required for the AP reset 
>> area
>> +**/
>> +STATIC
>> +UINTN
>> +GetApResetVectorSize (
>> +  IN MP_ASSEMBLY_ADDRESS_MAP  *AddressMap
>> +  )
>> +{
>> +  UINTN  Size;
>> +
>> +  Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +
>> +                        AddressMap->SwitchToRealSize +
>> +                        sizeof (MP_CPU_EXCHANGE_INFO),
>> +                      CPU_STACK_ALIGNMENT);  Size +=
>> + GetApResetStackSize ();
>> +
>> +  return Size;
>> +}
>> +
>>  /**
>>    Allocate reset vector buffer.
>>
>> @@ -1005,16 +1217,22 @@ AllocateResetVector (
>>    UINTN           ApResetVectorSize;
>>
>>    if (CpuMpData->WakeupBuffer == (UINTN) -1) {
>> -    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
>> -                          sizeof (MP_CPU_EXCHANGE_INFO);
>> +    ApResetVectorSize = GetApResetVectorSize (&CpuMpData-
>>> AddressMap);
>>
>>      CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);
>>      CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)
>> (UINTN)
>> -                    (CpuMpData->WakeupBuffer + CpuMpData-
>>> AddressMap.RendezvousFunnelSize);
>> +                    (CpuMpData->WakeupBuffer +
>> +                       CpuMpData->AddressMap.RendezvousFunnelSize +
>> +                       CpuMpData->AddressMap.SwitchToRealSize);
>>      CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (
>> -                                    
>> CpuMpData->AddressMap.RendezvousFunnelSize -
>> +                                    
>> CpuMpData->AddressMap.RendezvousFunnelSize +
>> +
>> + CpuMpData->AddressMap.SwitchToRealSize -
>>                                      
>> CpuMpData->AddressMap.ModeTransitionOffset
>>                                      );
>> +    //
>> +    // The reset stack starts at the end of the buffer.
>> +    //
>> +    CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer +
>> + ApResetVectorSize;
>>    }
>>    BackupAndPrepareWakeupBuffer (CpuMpData);  } @@ -1029,7 +1247,31
>> @@ FreeResetVector (
>>    IN CPU_MP_DATA              *CpuMpData
>>    )
>>  {
>> -  RestoreWakeupBuffer (CpuMpData);
>> +  //
>> +  // If SEV-ES is enabled, the reset area is needed for AP parking and
>> +  // and AP startup in the OS, so the reset area is reserved. Do not
>> +  // perform the restore as this will overwrite memory which has data
>> +  // needed by SEV-ES.
>> +  //
>> +  if (!CpuMpData->SevEsIsEnabled) {
>> +    RestoreWakeupBuffer (CpuMpData);
>> +  }
>> +}
>> +
>> +/**
>> +  Allocate the SEV-ES AP jump table buffer.
>> +
>> +  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.
>> +**/
>> +VOID
>> +AllocateSevEsAPMemory (
>> +  IN OUT CPU_MP_DATA          *CpuMpData
>> +  )
>> +{
>> +  if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {
>> +    CpuMpData->SevEsAPBuffer =
>> +      CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
>> +  }
>>  }
>>
>>  /**
>> @@ -1066,6 +1308,7 @@ WakeUpAP (
>>        CpuMpData->InitFlag   != ApInitDone) {
>>      ResetVectorRequired = TRUE;
>>      AllocateResetVector (CpuMpData);
>> +    AllocateSevEsAPMemory (CpuMpData);
>>      FillExchangeInfoData (CpuMpData);
>>      SaveLocalApicTimerSetting (CpuMpData);
>>    }
>> @@ -1102,6 +1345,50 @@ WakeUpAP (
>>        }
>>      }
>>      if (ResetVectorRequired) {
>> +      //
>> +      // For SEV-ES, the initial AP boot address will be defined by
>> +      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address
>> +      // from the original INIT-SIPI-SIPI.
>> +      //
>> +      if (CpuMpData->SevEsIsEnabled) {
>> +        SEV_ES_AP_JMP_FAR *JmpFar;
>> +        UINT32            Offset, InsnByte;
>> +        UINT8             LoNib, HiNib;
>> +
>> +        JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32
>> (PcdSevEsWorkAreaBase);
>> +        ASSERT (JmpFar != NULL);
>> +
>> +        //
>> +        // Obtain the address of the Segment/Rip location in the workarea.
>> +        // This will be set to a value derived from the SIPI vector and will
>> +        // be the memory address used for the far jump below.
>> +        //
>> +        Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
>> +        Offset += sizeof (JmpFar->InsnBuffer);
>> +        LoNib = (UINT8) Offset;
>> +        HiNib = (UINT8) (Offset >> 8);
>> +
>> +        //
>> +        // Program the workarea (which is the initial AP boot address) with
>> +        // far jump to the SIPI vector (where XX and YY represent the
>> +        // address of where the SIPI vector is stored.
>> +        //
>> +        //   JMP FAR [CS:XXYY] => 2E FF 2E YY XX
>> +        //
>> +        InsnByte = 0;
>> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // CS override prefix
>> +        JmpFar->InsnBuffer[InsnByte++] = 0xFF;  // JMP (FAR)
>> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // ModRM (JMP memory
>> location)
>> +        JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
>> +        JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
>> +
>> +        //
>> +        // Program the Segment/Rip based on the SIPI vector (always at least
>> +        // 16-byte aligned, so Rip is set to 0).
>> +        //
>> +        JmpFar->Rip = 0;
>> +        JmpFar->Segment = (UINT16) (ExchangeInfo->BufferStart >> 4);
>> +      }
> 
> For this wake-up process, current code just handles the broadcast type. I 
> think it also needs to handle wake-up specific AP case. Right?

Yes, it should be. I never encountered a non-broadcast call under OVMF,
but it should be supported for error cases, etc. and for any future
changes in support.

I'll add it and make the above code a function so as not to duplicate it.

Thanks,
Tom

> 
> Thanks,
> Eric
>>        //
>>        // Wakeup all APs
>>        //
>> @@ -1669,7 +1956,7 @@ MpInitLibInitialize (
>>    ASSERT (MaxLogicalProcessorNumber != 0);
>>
>>    AsmGetAddressMap (&AddressMap);
>> -  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof
>> (MP_CPU_EXCHANGE_INFO);
>> +  ApResetVectorSize = GetApResetVectorSize (&AddressMap);
>>    ApStackSize = PcdGet32(PcdCpuApStackSize);
>>    ApLoopMode  = GetApLoopMode (&MonitorFilterSize);
>>
>> @@ -1728,6 +2015,8 @@ MpInitLibInitialize (
>>    CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData
>> + MaxLogicalProcessorNumber);
>>    InitializeSpinLock(&CpuMpData->MpLock);
>>    CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
>> +  CpuMpData->SevEsAPBuffer  = (UINTN) -1;
>> +  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
>>
>>    //
>>    // Make sure no memory usage outside of the allocated buffer.
>> @@ -1786,6 +2075,7 @@ MpInitLibInitialize (
>>      // APs have been wakeup before, just get the CPU Information
>>      // from HOB
>>      //
>> +    OldCpuMpData->NewCpuMpData = CpuMpData;
>>      CpuMpData->CpuCount  = OldCpuMpData->CpuCount;
>>      CpuMpData->BspNumber = OldCpuMpData->BspNumber;
>>      CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; diff --git
>> a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> index a548fed23fa7..e17a351e5cfd 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> @@ -280,6 +280,25 @@ GetModeTransitionBuffer (
>>    return 0;
>>  }
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  )
>> +{
>> +  //
>> +  // PEI phase doesn't need to do such transition. So simply return 0.
>> +  //
>> +  return 0;
>> +}
>> +
>>  /**
>>    Checks APs status and updates APs status if needed.
>>
>> diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> index 6298571e29b2..28f8e8e133e5 100644
>> --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> @@ -121,7 +121,7 @@ GetProtectedModeCS (
>>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>>    for (Index = 0; Index < GdtEntryCount; Index++) {
>>      if (GdtEntry->Bits.L == 0) {
>> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>>          break;
>>        }
>>      }
>> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> index efb1bc2bf7cb..4f5a7c859a56 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>>  CPU_SWITCH_STATE_STORED       equ        1
>>  CPU_SWITCH_STATE_LOADED       equ        2
>>
>> -LockLocation                  equ        (RendezvousFunnelProcEnd -
>> RendezvousFunnelProcStart)
>> +LockLocation                  equ        (SwitchToRealProcEnd -
>> RendezvousFunnelProcStart)
>>  StackStartAddressLocation     equ        LockLocation + 04h
>>  StackSizeLocation             equ        LockLocation + 08h
>>  ApProcedureLocation           equ        LockLocation + 0Ch
>> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> index b74046b76af3..309d53bf3b37 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> @@ -215,6 +215,16 @@ CProcedureInvoke:
>>      jmp        $                 ; Never reach here
>>  RendezvousFunnelProcEnd:
>>
>> +;----------------------------------------------------------------------
>> +---------------
>> +;SwitchToRealProc procedure follows.
>> +;NOT USED IN 32 BIT MODE.
>> +;----------------------------------------------------------------------
>> +---------------
>> +global ASM_PFX(SwitchToRealProc)
>> +ASM_PFX(SwitchToRealProc):
>> +SwitchToRealProcStart:
>> +    jmp        $                 ; Never reach here
>> +SwitchToRealProcEnd:
>> +
>>  
>> ;-------------------------------------------------------------------------------------
>>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
>> TopOfApStack, CountTofinish);
>>  
>> ;-------------------------------------------------------------------------------------
>> @@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
>>      mov        dword [ebx + 0Ch], AsmRelocateApLoopStart
>>      mov        dword [ebx + 10h], AsmRelocateApLoopEnd -
>> AsmRelocateApLoopStart
>>      mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
>> +    mov        dword [ebx + 18h], SwitchToRealProcEnd -
>> SwitchToRealProcStart       ; SwitchToRealSize
>> +    mov        dword [ebx + 1Ch], SwitchToRealProcStart -
>> RendezvousFunnelProcStart ; SwitchToRealOffset
>> +    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start       
>>         ;
>> SwitchToRealNoNxOffset
>> +    mov        dword [ebx + 24h], 0                                         
>>         ;
>> SwitchToRealPM16ModeOffset
>> +    mov        dword [ebx + 28h], 0                                         
>>         ;
>> SwitchToRealPM16ModeSize
>>
>>      popad
>>      ret
>> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> index 58ef369342a7..c92daaaffd6b 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>>  CPU_SWITCH_STATE_STORED       equ        1
>>  CPU_SWITCH_STATE_LOADED       equ        2
>>
>> -LockLocation                  equ        (RendezvousFunnelProcEnd -
>> RendezvousFunnelProcStart)
>> +LockLocation                  equ        (SwitchToRealProcEnd -
>> RendezvousFunnelProcStart)
>>  StackStartAddressLocation     equ        LockLocation + 08h
>>  StackSizeLocation             equ        LockLocation + 10h
>>  ApProcedureLocation           equ        LockLocation + 18h
>> @@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ  LockLocation +
>> 98h
>>  ModeHighMemoryLocation              equ  LockLocation + 9Ah
>>  ModeHighSegmentLocation             equ  LockLocation + 9Eh
>>  Enable5LevelPagingLocation          equ  LockLocation + 0A0h
>> +SevEsIsEnabledLocation              equ  LockLocation + 0A1h
>> +GhcbBaseLocation                    equ  LockLocation + 0A2h
>> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> index 87f2523e856f..6956b408d004 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> @@ -184,9 +184,97 @@ Releaselock:
>>      add        edi, StackStartAddressLocation
>>      add        rax, qword [edi]
>>      mov        rsp, rax
>> +
>> +    lea        edi, [esi + SevEsIsEnabledLocation]
>> +    cmp        byte [edi], 1        ; SevEsIsEnabled
>> +    jne        CProcedureInvoke
>> +
>> +    ;
>> +    ; program GHCB
>> +    ;   Each page after the GHCB is a per-CPU page, so the calculation
>> programs
>> +    ;   a GHCB to be every 8KB.
>> +    ;
>> +    mov        eax, SIZE_4KB
>> +    shl        eax, 1                            ; EAX = SIZE_4K * 2
>> +    mov        ecx, ebx
>> +    mul        ecx                               ; EAX = SIZE_4K * 2 * 
>> CpuNumber
>> +    mov        edi, esi
>> +    add        edi, GhcbBaseLocation
>> +    add        rax, qword [edi]
>> +    mov        rdx, rax
>> +    shr        rdx, 32
>> +    mov        rcx, 0xc0010130
>> +    wrmsr
>>      jmp        CProcedureInvoke
>>
>>  GetApicId:
>> +    lea        edi, [esi + SevEsIsEnabledLocation]
>> +    cmp        byte [edi], 1        ; SevEsIsEnabled
>> +    jne        DoCpuid
>> +
>> +    ;
>> +    ; Since we don't have a stack yet, we can't take a #VC
>> +    ; exception. Use the GHCB protocol to perform the CPUID
>> +    ; calls.
>> +    ;
>> +    mov        rcx, 0xc0010130
>> +    rdmsr
>> +    shl        rdx, 32
>> +    or         rax, rdx
>> +    mov        rdi, rax             ; RDI now holds the original GHCB GPA
>> +
>> +    mov        rdx, 0               ; CPUID function 0
>> +    mov        rax, 0               ; RAX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    cmp        edx, 0bh
>> +    jb         NoX2ApicSevEs        ; CPUID level below
>> CPUID_EXTENDED_TOPOLOGY
>> +
>> +    mov        rdx, 0bh             ; CPUID function 0x0b
>> +    mov        rax, 040000000h      ; RBX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    test       edx, 0ffffh
>> +    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero
>> +
>> +    mov        rdx, 0bh             ; CPUID function 0x0b
>> +    mov        rax, 0c0000000h      ; RDX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +
>> +    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
>> +    jmp        RestoreGhcb
>> +
>> +NoX2ApicSevEs:
>> +    ; Processor is not x2APIC capable, so get 8-bit APIC ID
>> +    mov        rdx, 1               ; CPUID function 1
>> +    mov        rax, 040000000h      ; RBX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    shr        edx, 24
>> +
>> +RestoreGhcb:
>> +    mov        rbx, rdx             ; Save x2APIC/APIC ID
>> +
>> +    mov        rdx, rdi             ; RDI holds the saved GHCB GPA
>> +    shr        rdx, 32
>> +    mov        eax, edi
>> +    wrmsr
>> +
>> +    mov        rdx, rbx
>> +
>> +    ; x2APIC ID or APIC ID is in EDX
>> +    jmp        GetProcessorNumber
>> +
>> +DoCpuid:
>>      mov        eax, 0
>>      cpuid
>>      cmp        eax, 0bh
>> @@ -253,12 +341,158 @@ CProcedureInvoke:
>>
>>  RendezvousFunnelProcEnd:
>>
>> +;----------------------------------------------------------------------
>> +---------------
>> +;SwitchToRealProc procedure follows.
>> +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT
>> MODE.
>> +HENCE THIS PROC ;IS IN MACHINE CODE.
>> +;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32,
>> +UINTN StackStart) ;  rcx - Buffer Start ;  rdx - Code16 Selector Offset
>> +;  r8  - Code32 Selector Offset ;  r9  - Stack Start
>> +;----------------------------------------------------------------------
>> +---------------
>> +global ASM_PFX(SwitchToRealProc)
>> +ASM_PFX(SwitchToRealProc):
>> +SwitchToRealProcStart:
>> +BITS 64
>> +    cli
>> +
>> +    ;
>> +    ; Get RDX reset value before changing stacks since the
>> +    ; new stack won't be able to accomodate a #VC exception.
>> +    ;
>> +    push       rax
>> +    push       rbx
>> +    push       rcx
>> +    push       rdx
>> +
>> +    mov        rax, 1
>> +    cpuid
>> +    mov        rsi, rax                    ; Save off the reset value for 
>> RDX
>> +
>> +    pop        rdx
>> +    pop        rcx
>> +    pop        rbx
>> +    pop        rax
>> +
>> +    ;
>> +    ; Establish stack below 1MB
>> +    ;
>> +    mov        rsp, r9
>> +
>> +    ;
>> +    ; Push ultimate Reset Vector onto the stack
>> +    ;
>> +    mov        rax, rcx
>> +    shr        rax, 4
>> +    push       word 0x0002                 ; RFLAGS
>> +    push       ax                          ; CS
>> +    push       word 0x0000                 ; RIP
>> +    push       word 0x0000                 ; For alignment, will be 
>> discarded
>> +
>> +    ;
>> +    ; Get address of "16-bit operand size" label
>> +    ;
>> +    lea        rbx, [PM16Mode]
>> +
>> +    ;
>> +    ; Push addresses used to change to compatibility mode
>> +    ;
>> +    lea        rax, [CompatMode]
>> +    push       r8
>> +    push       rax
>> +
>> +    ;
>> +    ; Clear R8 - R15, for reset, before going into 32-bit mode
>> +    ;
>> +    xor        r8, r8
>> +    xor        r9, r9
>> +    xor        r10, r10
>> +    xor        r11, r11
>> +    xor        r12, r12
>> +    xor        r13, r13
>> +    xor        r14, r14
>> +    xor        r15, r15
>> +
>> +    ;
>> +    ; Far return into 32-bit mode
>> +    ;
>> +o64 retf
>> +
>> +BITS 32
>> +CompatMode:
>> +    ;
>> +    ; Set up stack to prepare for exiting protected mode
>> +    ;
>> +    push       edx                         ; Code16 CS
>> +    push       ebx                         ; PM16Mode label address
>> +
>> +    ;
>> +    ; Disable paging
>> +    ;
>> +    mov        eax, cr0                    ; Read CR0
>> +    btr        eax, 31                     ; Set PG=0
>> +    mov        cr0, eax                    ; Write CR0
>> +
>> +    ;
>> +    ; Disable long mode
>> +    ;
>> +    mov        ecx, 0c0000080h             ; EFER MSR number
>> +    rdmsr                                  ; Read EFER
>> +    btr        eax, 8                      ; Set LME=0
>> +    wrmsr                                  ; Write EFER
>> +
>> +    ;
>> +    ; Disable PAE
>> +    ;
>> +    mov        eax, cr4                    ; Read CR4
>> +    btr        eax, 5                      ; Set PAE=0
>> +    mov        cr4, eax                    ; Write CR4
>> +
>> +    mov        edx, esi                    ; Restore RDX reset value
>> +
>> +    ;
>> +    ; Switch to 16-bit operand size
>> +    ;
>> +    retf
>> +
>> +BITS 16
>> +    ;
>> +    ; At entry to this label
>> +    ;   - RDX will have its reset value
>> +    ;   - On the top of the stack
>> +    ;     - Alignment data (two bytes) to be discarded
>> +    ;     - IP for Real Mode (two bytes)
>> +    ;     - CS for Real Mode (two bytes)
>> +    ;
>> +PM16Mode:
>> +    mov        eax, cr0                    ; Read CR0
>> +    btr        eax, 0                      ; Set PE=0
>> +    mov        cr0, eax                    ; Write CR0
>> +
>> +    pop        ax                          ; Discard alignment data
>> +
>> +    ;
>> +    ; Clear registers (except RDX and RSP) before going into 16-bit mode
>> +    ;
>> +    xor        eax, eax
>> +    xor        ebx, ebx
>> +    xor        ecx, ecx
>> +    xor        esi, esi
>> +    xor        edi, edi
>> +    xor        ebp, ebp
>> +
>> +    iret
>> +
>> +SwitchToRealProcEnd:
>> +
>>  
>> ;-------------------------------------------------------------------------------------
>>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
>> TopOfApStack, CountTofinish);
>>  
>> ;-------------------------------------------------------------------------------------
>>  global ASM_PFX(AsmRelocateApLoop)
>>  ASM_PFX(AsmRelocateApLoop):
>>  AsmRelocateApLoopStart:
>> +BITS 64
>>      cli                          ; Disable interrupt before switching to 
>> 32-bit mode
>>      mov        rax, [rsp + 40]   ; CountTofinish
>>      lock dec   dword [rax]       ; (*CountTofinish)--
>> @@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
>>      mov        qword [rcx + 18h], rax
>>      mov        qword [rcx + 20h], AsmRelocateApLoopEnd -
>> AsmRelocateApLoopStart
>>      mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
>> +    mov        qword [rcx + 30h], SwitchToRealProcEnd -
>> SwitchToRealProcStart          ; SwitchToRealSize
>> +    mov        qword [rcx + 38h], SwitchToRealProcStart -
>> RendezvousFunnelProcStart    ; SwitchToRealOffset
>> +    mov        qword [rcx + 40h], SwitchToRealProcStart - Flat32Start       
>>            ;
>> SwitchToRealNoNxOffset
>> +    mov        qword [rcx + 48h], PM16Mode -
>> RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset
>> +    mov        qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode            
>>            ;
>> SwitchToRealPM16ModeSize
>>      ret
>>
>>  
>> ;-------------------------------------------------------------------------------------
>> --
>> 2.17.1
> 

-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#60524): https://edk2.groups.io/g/devel/message/60524
Mute This Topic: https://groups.io/mt/74354974/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to