Thanks for the comments. It will be fixed.

Regards,
Jian

From: Dong, Eric
Sent: Thursday, September 06, 2018 9:46 AM
To: Wang, Jian J <jian.j.w...@intel.com>; edk2-devel@lists.01.org
Cc: Laszlo Ersek <ler...@redhat.com>; Ni, Ruiyu <ruiyu...@intel.com>; Yao, 
Jiewen <jiewen....@intel.com>; Zeng, Star <star.z...@intel.com>; Ware, Ryan R 
<ryan.r.w...@intel.com>
Subject: RE: [PATCH 4/4] UefiCpuPkg/CpuMpPei: support stack guard feature

Hi Jian,

Below code has ECC issue, please check with ECC tool to fix all ECC issues.

  /**
        Initializes MP and exceptions handlers.

        @retval EFI_SUCCESS     MP was successfully initialized.
        @retval others          Error occurred in MP initialization.

  **/
  EFI_STATUS
  InitializeCpuMpWorker (
    IN CONST EFI_PEI_SERVICES     **PeiServices
    )

With ECC issues fixed, Reviewed-by: Eric Dong 
<eric.d...@intel.com<mailto:eric.d...@intel.com>>

Thanks,
Eric

> -----Original Message-----
> From: Wang, Jian J
> Sent: Monday, September 3, 2018 11:16 AM
> To: edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
> Cc: Dong, Eric <eric.d...@intel.com<mailto:eric.d...@intel.com>>; Laszlo 
> Ersek <ler...@redhat.com<mailto:ler...@redhat.com>>; Ni,
> Ruiyu <ruiyu...@intel.com<mailto:ruiyu...@intel.com>>; Yao, Jiewen 
> <jiewen....@intel.com<mailto:jiewen....@intel.com>>; Zeng,
> Star <star.z...@intel.com<mailto:star.z...@intel.com>>; Ware, Ryan R 
> <ryan.r.w...@intel.com<mailto:ryan.r.w...@intel.com>>
> Subject: [PATCH 4/4] UefiCpuPkg/CpuMpPei: support stack guard feature
>
> This feature is the same as Stack Guard enabled in driver CpuDxe but applies
> to PEI phase. Due to the specialty in PEI module dispatching, this driver is
> changed to do the actual initialization in notify callback of event
> gEfiPeiMemoryDiscoveredPpiGuid. This can let the stack guard apply to as
> most PEI drivers as possible.
>
> To let Stack Guard work, some simple page table management code are
> introduced to setup Guard page at base of stack for each processor.
>
> Cc: Eric Dong <eric.d...@intel.com<mailto:eric.d...@intel.com>>
> Cc: Laszlo Ersek <ler...@redhat.com<mailto:ler...@redhat.com>>
> Cc: Ruiyu Ni <ruiyu...@intel.com<mailto:ruiyu...@intel.com>>
> Cc: Jiewen Yao <jiewen....@intel.com<mailto:jiewen....@intel.com>>
> Cc: Star Zeng <star.z...@intel.com<mailto:star.z...@intel.com>>
> Cc: "Ware, Ryan R" <ryan.r.w...@intel.com<mailto:ryan.r.w...@intel.com>>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Jian J Wang 
> <jian.j.w...@intel.com<mailto:jian.j.w...@intel.com>>
> ---
>  UefiCpuPkg/CpuMpPei/CpuMpPei.c   | 269 ++++++++++++++++-
>  UefiCpuPkg/CpuMpPei/CpuMpPei.h   |  14 +
>  UefiCpuPkg/CpuMpPei/CpuMpPei.inf |  11 +-
> UefiCpuPkg/CpuMpPei/CpuPaging.c  | 637
> +++++++++++++++++++++++++++++++++++++++
>  4 files changed, 916 insertions(+), 15 deletions(-)  create mode 100644
> UefiCpuPkg/CpuMpPei/CpuPaging.c
>
> diff --git a/UefiCpuPkg/CpuMpPei/CpuMpPei.c
> b/UefiCpuPkg/CpuMpPei/CpuMpPei.c index 7c94d5a6d7..e3762daf39 100644
> --- a/UefiCpuPkg/CpuMpPei/CpuMpPei.c
> +++ b/UefiCpuPkg/CpuMpPei/CpuMpPei.c
> @@ -409,25 +409,225 @@ PeiWhoAmI (
>  }
>
>  /**
> -  The Entry point of the MP CPU PEIM.
> +  Get GDT register value.
>
> -  This function will wakeup APs and collect CPU AP count and install the
> -  Mp Service Ppi.
> +  This function is mainly for AP purpose because AP may have different
> + GDT  table than BSP.
>
> -  @param  FileHandle    Handle of the file being invoked.
> -  @param  PeiServices   Describes the list of possible PEI Services.
> +  @param[in,out] Buffer  The pointer to private data buffer.
>
> -  @retval EFI_SUCCESS   MpServicePpi is installed successfully.
> +**/
> +VOID
> +EFIAPI
> +GetGdtr (
> +  IN OUT VOID *Buffer
> +  )
> +{
> +  AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer); }
> +
> +/**
> +  Initializes CPU exceptions handlers for the sake of stack switch
> requirement.
> +
> +  This function is a wrapper of InitializeCpuExceptionHandlersEx. It's
> + mainly  for the sake of AP's init because of EFI_AP_PROCEDURE API
> requirement.
> +
> +  @param[in,out] Buffer  The pointer to private data buffer.
>
>  **/
> -EFI_STATUS
> +VOID
>  EFIAPI
> -CpuMpPeimInit (
> -  IN       EFI_PEI_FILE_HANDLE  FileHandle,
> +InitializeExceptionStackSwitchHandlers (
> +  IN OUT VOID *Buffer
> +  )
> +{
> +  CPU_EXCEPTION_INIT_DATA           *EssData;
> +  IA32_DESCRIPTOR                   Idtr;
> +  EFI_STATUS                        Status;
> +
> +  EssData = Buffer;
> +  //
> +  // We don't plan to replace IDT table with a new one, but we should
> +not assume
> +  // the AP's IDT is the same as BSP's IDT either.
> +  //
> +  AsmReadIdtr (&Idtr);
> +  EssData->Ia32.IdtTable = (VOID *)Idtr.Base;
> +  EssData->Ia32.IdtTableSize = Idtr.Limit + 1;
> +  Status = InitializeCpuExceptionHandlersEx (NULL, EssData);
> +  ASSERT_EFI_ERROR (Status);
> +}
> +
> +/**
> +  Initializes MP exceptions handlers for the sake of stack switch 
> requirement.
> +
> +  This function will allocate required resources required to setup
> + stack switch  and pass them through CPU_EXCEPTION_INIT_DATA to each
> logic processor.
> +
> +**/
> +VOID
> +InitializeMpExceptionStackSwitchHandlers (
> +  VOID
> +  )
> +{
> +  EFI_STATUS                      Status;
> +  UINTN                           Index;
> +  UINTN                           Bsp;
> +  UINTN                           ExceptionNumber;
> +  UINTN                           OldGdtSize;
> +  UINTN                           NewGdtSize;
> +  UINTN                           NewStackSize;
> +  IA32_DESCRIPTOR                 Gdtr;
> +  CPU_EXCEPTION_INIT_DATA         EssData;
> +  UINT8                           *GdtBuffer;
> +  UINT8                           *StackTop;
> +  UINTN                           NumberOfProcessors;
> +
> +  if (!PcdGetBool (PcdCpuStackGuard)) {
> +    return;
> +  }
> +
> +  MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL);
> + MpInitLibWhoAmI (&Bsp);
> +
> +  ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);
> + NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) *
> + ExceptionNumber;
> +
> +  Status = PeiServicesAllocatePool (
> +             NewStackSize * NumberOfProcessors,
> +             (VOID **)&StackTop
> +             );
> +  ASSERT(StackTop != NULL);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return;
> +  }
> +  StackTop += NewStackSize  * NumberOfProcessors;
> +
> +  //
> +  // The default exception handlers must have been initialized. Let's
> + just skip  // it in this method.
> +  //
> +  EssData.Ia32.Revision = CPU_EXCEPTION_INIT_DATA_REV;
> + EssData.Ia32.InitDefaultHandlers = FALSE;
> +
> +  EssData.Ia32.StackSwitchExceptions =
> + FixedPcdGetPtr(PcdCpuStackSwitchExceptionList);
> +  EssData.Ia32.StackSwitchExceptionNumber = ExceptionNumber;
> + EssData.Ia32.KnownGoodStackSize =
> + FixedPcdGet32(PcdCpuKnownGoodStackSize);
> +
> +  //
> +  // Initialize Gdtr to suppress incorrect compiler/analyzer warnings.
> +  //
> +  Gdtr.Base = 0;
> +  Gdtr.Limit = 0;
> +  for (Index = 0; Index < NumberOfProcessors; ++Index) {
> +    //
> +    // To support stack switch, we need to re-construct GDT but not IDT.
> +    //
> +    if (Index == Bsp) {
> +      GetGdtr(&Gdtr);
> +    } else {
> +      //
> +      // AP might have different size of GDT from BSP.
> +      //
> +      MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL);
> +    }
> +
> +    //
> +    // X64 needs only one TSS of current task working for all exceptions
> +    // because of its IST feature. IA32 needs one TSS for each exception
> +    // in addition to current task. Since AP is not supposed to allocate
> +    // memory, we have to do it in BSP. To simplify the code, we allocate
> +    // memory for IA32 case to cover both IA32 and X64 exception stack
> +    // switch.
> +    //
> +    // Layout of memory to allocate for each processor:
> +    //    --------------------------------
> +    //    |            Alignment         |  (just in case)
> +    //    --------------------------------
> +    //    |                              |
> +    //    |        Original GDT          |
> +    //    |                              |
> +    //    --------------------------------
> +    //    |    Current task descriptor   |
> +    //    --------------------------------
> +    //    |                              |
> +    //    |  Exception task descriptors  |  X ExceptionNumber
> +    //    |                              |
> +    //    --------------------------------
> +    //    |  Current task-state segment  |
> +    //    --------------------------------
> +    //    |                              |
> +    //    | Exception task-state segment |  X ExceptionNumber
> +    //    |                              |
> +    //    --------------------------------
> +    //
> +    OldGdtSize = Gdtr.Limit + 1;
> +    EssData.Ia32.ExceptionTssDescSize = sizeof (IA32_TSS_DESCRIPTOR) *
> +                                        (ExceptionNumber + 1);
> +    EssData.Ia32.ExceptionTssSize = sizeof (IA32_TASK_STATE_SEGMENT) *
> +                                    (ExceptionNumber + 1);
> +    NewGdtSize = sizeof (IA32_TSS_DESCRIPTOR) +
> +                 OldGdtSize +
> +                 EssData.Ia32.ExceptionTssDescSize +
> +                 EssData.Ia32.ExceptionTssSize;
> +
> +    Status = PeiServicesAllocatePool (
> +               NewGdtSize,
> +               (VOID **)&GdtBuffer
> +               );
> +    ASSERT (GdtBuffer != NULL);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT_EFI_ERROR (Status);
> +      return;
> +    }
> +
> +    //
> +    // Make sure GDT table alignment
> +    //
> +    EssData.Ia32.GdtTable = ALIGN_POINTER(GdtBuffer, sizeof
> (IA32_TSS_DESCRIPTOR));
> +    NewGdtSize -= ((UINT8 *)EssData.Ia32.GdtTable - GdtBuffer);
> +    EssData.Ia32.GdtTableSize = NewGdtSize;
> +
> +    EssData.Ia32.ExceptionTssDesc = ((UINT8 *)EssData.Ia32.GdtTable +
> OldGdtSize);
> +    EssData.Ia32.ExceptionTss = ((UINT8 *)EssData.Ia32.GdtTable +
> OldGdtSize +
> +                                 EssData.Ia32.ExceptionTssDescSize);
> +
> +    EssData.Ia32.KnownGoodStackTop = (UINTN)StackTop;
> +    DEBUG ((DEBUG_INFO,
> +            "Exception stack top[cpu%lu]: 0x%lX\n",
> +            (UINT64)(UINTN)Index,
> +            (UINT64)(UINTN)StackTop));
> +
> +    if (Index == Bsp) {
> +      InitializeExceptionStackSwitchHandlers (&EssData);
> +    } else {
> +      MpInitLibStartupThisAP (
> +        InitializeExceptionStackSwitchHandlers,
> +        Index,
> +        NULL,
> +        0,
> +        (VOID *)&EssData,
> +        NULL
> +        );
> +    }
> +
> +    StackTop  -= NewStackSize;
> +  }
> +}
> +
> +/**
> +  Initializes MP and exceptions handlers.
> +
> +  @retval EFI_SUCCESS     MP was successfully initialized.
> +  @retval others          Error occurred in MP initialization.
> +
> +**/
> +EFI_STATUS
> +InitializeCpuMpWorker (
>    IN CONST EFI_PEI_SERVICES     **PeiServices
>    )
>  {
> -  EFI_STATUS           Status;
> +  EFI_STATUS                      Status;
>    EFI_VECTOR_HANDOFF_INFO         *VectorInfo;
>    EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi;
>
> @@ -444,14 +644,24 @@ CpuMpPeimInit (
>    if (Status == EFI_SUCCESS) {
>      VectorInfo = VectorHandoffInfoPpi->Info;
>    }
> -  Status = InitializeCpuExceptionHandlers (VectorInfo);
> -  ASSERT_EFI_ERROR (Status);
>
>    //
> -  // Wakeup APs to do initialization
> +  // Initialize default handlers
>    //
> +  Status = InitializeCpuExceptionHandlers (VectorInfo);  if (EFI_ERROR
> + (Status)) {
> +    return Status;
> +  }
> +
>    Status = MpInitLibInitialize ();
> -  ASSERT_EFI_ERROR (Status);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  //
> +  // Special initialization for the sake of Stack Guard  //
> + InitializeMpExceptionStackSwitchHandlers ();
>
>    //
>    // Update and publish CPU BIST information @@ -466,3 +676,34 @@
> CpuMpPeimInit (
>
>    return Status;
>  }
> +
> +/**
> +  The Entry point of the MP CPU PEIM.
> +
> +  This function will wakeup APs and collect CPU AP count and install
> + the  Mp Service Ppi.
> +
> +  @param  FileHandle    Handle of the file being invoked.
> +  @param  PeiServices   Describes the list of possible PEI Services.
> +
> +  @retval EFI_SUCCESS   MpServicePpi is installed successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuMpPeimInit (
> +  IN       EFI_PEI_FILE_HANDLE  FileHandle,
> +  IN CONST EFI_PEI_SERVICES     **PeiServices
> +  )
> +{
> +  EFI_STATUS           Status;
> +
> +  //
> +  // For the sake of special initialization needing to be done right
> + after  // memory discovery.
> +  //
> +  Status = PeiServicesNotifyPpi (&mPostMemNotifyList[0]);
> + ASSERT_EFI_ERROR (Status);
> +
> +  return Status;
> +}
> diff --git a/UefiCpuPkg/CpuMpPei/CpuMpPei.h
> b/UefiCpuPkg/CpuMpPei/CpuMpPei.h index aae56658ec..846f16db3a
> 100644
> --- a/UefiCpuPkg/CpuMpPei/CpuMpPei.h
> +++ b/UefiCpuPkg/CpuMpPei/CpuMpPei.h
> @@ -402,4 +402,18 @@ SecPlatformInformation2 (
>       OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2
> *PlatformInformationRecord2
>    );
>
> +/**
> +  Initializes MP and exceptions handlers.
> +
> +  @retval EFI_SUCCESS     MP was successfully initialized.
> +  @retval others          Error occurred in MP initialization.
> +
> +**/
> +EFI_STATUS
> +InitializeCpuMpWorker (
> +  IN CONST EFI_PEI_SERVICES     **PeiServices
> +  );
> +
> +extern EFI_PEI_NOTIFY_DESCRIPTOR  mPostMemNotifyList[];
> +
>  #endif
> diff --git a/UefiCpuPkg/CpuMpPei/CpuMpPei.inf
> b/UefiCpuPkg/CpuMpPei/CpuMpPei.inf
> index 3b40d88201..19cbf3345c 100644
> --- a/UefiCpuPkg/CpuMpPei/CpuMpPei.inf
> +++ b/UefiCpuPkg/CpuMpPei/CpuMpPei.inf
> @@ -31,6 +31,7 @@
>    CpuMpPei.h
>    CpuMpPei.c
>    CpuBist.c
> +  CpuPaging.c
>
>  [Packages]
>    MdePkg/MdePkg.dec
> @@ -57,9 +58,17 @@
>    ## UNDEFINED # HOB
>    gEfiSecPlatformInformation2PpiGuid
>    gEfiVectorHandoffInfoPpiGuid                  ## SOMETIMES_CONSUMES
> +  gEfiPeiMemoryDiscoveredPpiGuid
> +
> +[Pcd]
> +
> gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrM
> ask    ## CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                       ##
> CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize
>
>  [Depex]
> -  gEfiPeiMemoryDiscoveredPpiGuid
> +  TRUE
>
>  [UserExtensions.TianoCore."ExtraFiles"]
>    CpuMpPeiExtra.uni
> diff --git a/UefiCpuPkg/CpuMpPei/CpuPaging.c
> b/UefiCpuPkg/CpuMpPei/CpuPaging.c new file mode 100644 index
> 0000000000..5d18768e30
> --- /dev/null
> +++ b/UefiCpuPkg/CpuMpPei/CpuPaging.c
> @@ -0,0 +1,637 @@
> +/** @file
> +  Basic paging support for the CPU to enable Stack Guard.
> +
> +Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +
> +This program and the accompanying materials are licensed and made
> +available under the terms and conditions of the BSD License which
> +accompanies this distribution.  The full text of the license may be
> +found at http://opensource.org/licenses/bsd-license.php
> +
> +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
> BASIS,
> +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
> EXPRESS OR IMPLIED.
> +
> +**/
> +
> +#include <Register/Cpuid.h>
> +#include <Register/Msr.h>
> +#include <Library/MemoryAllocationLib.h> #include <Library/CpuLib.h>
> +#include <Library/BaseLib.h>
> +
> +#include "CpuMpPei.h"
> +
> +#define IA32_PG_P             BIT0
> +#define IA32_PG_RW            BIT1
> +#define IA32_PG_U             BIT2
> +#define IA32_PG_A             BIT5
> +#define IA32_PG_D             BIT6
> +#define IA32_PG_PS            BIT7
> +#define IA32_PG_NX            BIT63
> +
> +#define PAGE_ATTRIBUTE_BITS   (IA32_PG_RW | IA32_PG_P)
> +#define PAGE_PROGATE_BITS     (IA32_PG_D | IA32_PG_A | IA32_PG_NX |
> IA32_PG_U |\
> +                               PAGE_ATTRIBUTE_BITS)
> +
> +#define PAGING_PAE_INDEX_MASK       0x1FF
> +#define PAGING_4K_ADDRESS_MASK_64   0x000FFFFFFFFFF000ull
> +#define PAGING_2M_ADDRESS_MASK_64   0x000FFFFFFFE00000ull
> +#define PAGING_1G_ADDRESS_MASK_64   0x000FFFFFC0000000ull
> +#define PAGING_512G_ADDRESS_MASK_64 0x000FFF8000000000ull
> +
> +typedef enum {
> +  PageNone = 0,
> +  PageMin  = 1,
> +  Page4K   = PageMin,
> +  Page2M   = 2,
> +  Page1G   = 3,
> +  Page512G = 4,
> +  PageMax  = Page512G
> +} PAGE_ATTRIBUTE;
> +
> +typedef struct {
> +  PAGE_ATTRIBUTE   Attribute;
> +  UINT64           Length;
> +  UINT64           AddressMask;
> +  UINTN            AddressBitOffset;
> +  UINTN            AddressBitLength;
> +} PAGE_ATTRIBUTE_TABLE;
> +
> +PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
> +  {PageNone,          0,                           0,  0, 0},
> +  {Page4K,     SIZE_4KB,   PAGING_4K_ADDRESS_MASK_64, 12, 9},
> +  {Page2M,     SIZE_2MB,   PAGING_2M_ADDRESS_MASK_64, 21, 9},
> +  {Page1G,     SIZE_1GB,   PAGING_1G_ADDRESS_MASK_64, 30, 9},
> +  {Page512G, SIZE_512GB, PAGING_512G_ADDRESS_MASK_64, 39, 9}, };
> +
> +EFI_STATUS
> +EFIAPI
> +MemoryDiscoveredPpiNotifyCallback (
> +  IN EFI_PEI_SERVICES           **PeiServices,
> +  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
> +  IN VOID                       *Ppi
> +  );
> +
> +EFI_PEI_NOTIFY_DESCRIPTOR  mPostMemNotifyList[] = {
> +  {
> +    (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> +    &gEfiPeiMemoryDiscoveredPpiGuid,
> +    MemoryDiscoveredPpiNotifyCallback
> +  }
> +};
> +
> +/**
> +  The function will check if IA32 PAE is supported.
> +
> +  @retval TRUE      IA32 PAE is supported.
> +  @retval FALSE     IA32 PAE is not supported.
> +
> +**/
> +BOOLEAN
> +IsIa32PaeSupported (
> +  VOID
> +  )
> +{
> +  UINT32                    RegEax;
> +  CPUID_VERSION_INFO_EDX    RegEdx;
> +
> +  AsmCpuid (CPUID_SIGNATURE, &RegEax, NULL, NULL, NULL);  if
> (RegEax >=
> + CPUID_VERSION_INFO) {
> +    AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx.Uint32);
> +    if (RegEdx.Bits.PAE != 0) {
> +      return TRUE;
> +    }
> +  }
> +
> +  return FALSE;
> +}
> +
> +/**
> +  This API provides a way to allocate memory for page table.
> +
> +  @param  Pages                 The number of 4 KB pages to allocate.
> +
> +  @return A pointer to the allocated buffer or NULL if allocation fails.
> +
> +**/
> +VOID *
> +AllocatePageTableMemory (
> +  IN UINTN           Pages
> +  )
> +{
> +  VOID      *Address;
> +
> +  Address = AllocatePages(Pages);
> +  if (Address != NULL) {
> +    ZeroMem(Address, EFI_PAGES_TO_SIZE (Pages));  }
> +
> +  return Address;
> +}
> +
> +/**
> +  Get the address width supported by current processor.
> +
> +  @retval 32      If processor is in 32-bit mode.
> +  @retval 36-48   If processor is in 64-bit mode.
> +
> +**/
> +UINTN
> +GetPhysicalAddressWidth (
> +  VOID
> +  )
> +{
> +  UINT32          RegEax;
> +
> +  if (sizeof(UINTN) == 4) {
> +    return 32;
> +  }
> +
> +  AsmCpuid(CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);  if
> + (RegEax >= CPUID_VIR_PHY_ADDRESS_SIZE) {
> +    AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &RegEax, NULL, NULL, NULL);
> +    RegEax &= 0xFF;
> +    if (RegEax > 48) {
> +      return 48;
> +    }
> +
> +    return (UINTN)RegEax;
> +  }
> +
> +  return 36;
> +}
> +
> +/**
> +  Get the type of top level page table.
> +
> +  @retval Page512G  PML4 paging.
> +  @retval Page1G    PAE paing.
> +
> +**/
> +PAGE_ATTRIBUTE
> +GetPageTableTopLevelType (
> +  VOID
> +  )
> +{
> +  MSR_IA32_EFER_REGISTER      MsrEfer;
> +
> +  MsrEfer.Uint64 = AsmReadMsr64 (MSR_CORE_IA32_EFER);
> +
> +  return (MsrEfer.Bits.LMA == 1) ? Page512G : Page1G; }
> +
> +/**
> +  Return page table entry matching the address.
> +
> +  @param[in]   Address          The address to be checked.
> +  @param[out]  PageAttributes   The page attribute of the page entry.
> +
> +  @return The page entry.
> +**/
> +VOID *
> +GetPageTableEntry (
> +  IN  PHYSICAL_ADDRESS                  Address,
> +  OUT PAGE_ATTRIBUTE                    *PageAttribute
> +  )
> +{
> +  INTN                  Level;
> +  UINTN                 Index;
> +  UINT64                *PageTable;
> +  UINT64                AddressEncMask;
> +
> +  AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask);
> +  PageTable = (UINT64 *)(UINTN)(AsmReadCr3 () &
> + PAGING_4K_ADDRESS_MASK_64);  for (Level =
> (INTN)GetPageTableTopLevelType (); Level > 0; --Level) {
> +    Index = (UINTN)RShiftU64 (Address,
> mPageAttributeTable[Level].AddressBitOffset);
> +    Index &= PAGING_PAE_INDEX_MASK;
> +
> +    //
> +    // No mapping?
> +    //
> +    if (PageTable[Index] == 0) {
> +      *PageAttribute = PageNone;
> +      return NULL;
> +    }
> +
> +    //
> +    // Page memory?
> +    //
> +    if ((PageTable[Index] & IA32_PG_PS) != 0 || Level == PageMin) {
> +      *PageAttribute = (PAGE_ATTRIBUTE)Level;
> +      return &PageTable[Index];
> +    }
> +
> +    //
> +    // Page directory or table
> +    //
> +    PageTable = (UINT64 *)(UINTN)(PageTable[Index] &
> +                                  ~AddressEncMask &
> +                                  PAGING_4K_ADDRESS_MASK_64);  }
> +
> +  *PageAttribute = PageNone;
> +  return NULL;
> +}
> +
> +/**
> +  This function splits one page entry to smaller page entries.
> +
> +  @param[in]  PageEntry        The page entry to be splitted.
> +  @param[in]  PageAttribute    The page attribute of the page entry.
> +  @param[in]  SplitAttribute   How to split the page entry.
> +  @param[in]  Recursively      Do the split recursively or not.
> +
> +  @retval RETURN_SUCCESS            The page entry is splitted.
> +  @retval RETURN_INVALID_PARAMETER  If target page attribute is invalid
> +  @retval RETURN_OUT_OF_RESOURCES   No resource to split page entry.
> +**/
> +RETURN_STATUS
> +SplitPage (
> +  IN  UINT64                            *PageEntry,
> +  IN  PAGE_ATTRIBUTE                    PageAttribute,
> +  IN  PAGE_ATTRIBUTE                    SplitAttribute,
> +  IN  BOOLEAN                           Recursively
> +  )
> +{
> +  UINT64            BaseAddress;
> +  UINT64            *NewPageEntry;
> +  UINTN             Index;
> +  UINT64            AddressEncMask;
> +  PAGE_ATTRIBUTE    SplitTo;
> +
> +  if (SplitAttribute == PageNone || SplitAttribute >= PageAttribute) {
> +    ASSERT (SplitAttribute != PageNone);
> +    ASSERT (SplitAttribute < PageAttribute);
> +    return RETURN_INVALID_PARAMETER;
> +  }
> +
> +  NewPageEntry = AllocatePageTableMemory (1);  if (NewPageEntry ==
> + NULL) {
> +    ASSERT (NewPageEntry != NULL);
> +    return RETURN_OUT_OF_RESOURCES;
> +  }
> +
> +  //
> +  // One level down each step to achieve more compact page table.
> +  //
> +  SplitTo = PageAttribute - 1;
> +  AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask)
> &
> +                   mPageAttributeTable[SplitTo].AddressMask;
> +  BaseAddress    = *PageEntry &
> +                   ~PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) &
> +                   mPageAttributeTable[PageAttribute].AddressMask;
> +  for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
> +    NewPageEntry[Index] = BaseAddress | AddressEncMask |
> +                          ((*PageEntry) & PAGE_PROGATE_BITS);
> +
> +    if (SplitTo != PageMin) {
> +      NewPageEntry[Index] |= IA32_PG_PS;
> +    }
> +
> +    if (Recursively && SplitTo > SplitAttribute) {
> +      SplitPage (&NewPageEntry[Index], SplitTo, SplitAttribute, Recursively);
> +    }
> +
> +    BaseAddress += mPageAttributeTable[SplitTo].Length;
> +  }
> +
> +  (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask |
> + PAGE_ATTRIBUTE_BITS;
> +
> +  return RETURN_SUCCESS;
> +}
> +
> +/**
> +  This function modifies the page attributes for the memory region
> +specified
> +  by BaseAddress and Length from their current attributes to the
> +attributes
> +  specified by Attributes.
> +
> +  Caller should make sure BaseAddress and Length is at page boundary.
> +
> +  @param[in]   BaseAddress      Start address of a memory region.
> +  @param[in]   Length           Size in bytes of the memory region.
> +  @param[in]   Attributes       Bit mask of attributes to modify.
> +
> +  @retval RETURN_SUCCESS            The attributes were modified for the
> memory
> +                                    region.
> +  @retval RETURN_INVALID_PARAMETER  Length is zero; or,
> +                                    Attributes specified an illegal 
> combination
> +                                    of attributes that cannot be set 
> together; or
> +                                    Addressis not 4KB aligned.
> +  @retval RETURN_OUT_OF_RESOURCES   There are not enough system
> resources to modify
> +                                    the attributes.
> +  @retval RETURN_UNSUPPORTED        Cannot modify the attributes of given
> memory.
> +
> +**/
> +RETURN_STATUS
> +EFIAPI
> +ConvertMemoryPageAttributes (
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes
> +  )
> +{
> +  UINT64                            *PageEntry;
> +  PAGE_ATTRIBUTE                    PageAttribute;
> +  RETURN_STATUS                     Status;
> +  EFI_PHYSICAL_ADDRESS              MaximumAddress;
> +
> +  if (Length == 0 ||
> +      (BaseAddress & (SIZE_4KB - 1)) != 0 ||
> +      (Length & (SIZE_4KB - 1)) != 0) {
> +
> +    ASSERT (Length > 0);
> +    ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
> +    ASSERT ((Length & (SIZE_4KB - 1)) == 0);
> +
> +    return RETURN_INVALID_PARAMETER;
> +  }
> +
> +  MaximumAddress = (EFI_PHYSICAL_ADDRESS)MAX_UINT32;  if
> (BaseAddress >
> + MaximumAddress ||
> +      Length > MaximumAddress ||
> +      (BaseAddress > MaximumAddress - (Length - 1))) {
> +    return RETURN_UNSUPPORTED;
> +  }
> +
> +  //
> +  // Below logic is to check 2M/4K page to make sure we do not waste
> memory.
> +  //
> +  while (Length != 0) {
> +    PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);
> +    if (PageEntry == NULL) {
> +      return RETURN_UNSUPPORTED;
> +    }
> +
> +    if (PageAttribute != Page4K) {
> +      Status = SplitPage (PageEntry, PageAttribute, Page4K, FALSE);
> +      if (RETURN_ERROR (Status)) {
> +        return Status;
> +      }
> +      //
> +      // Do it again until the page is 4K.
> +      //
> +      continue;
> +    }
> +
> +    //
> +    // Just take care of 'present' bit for Stack Guard.
> +    //
> +    if ((Attributes & IA32_PG_P) != 0) {
> +      *PageEntry |= (UINT64)IA32_PG_P;
> +    } else {
> +      *PageEntry &= ~((UINT64)IA32_PG_P);
> +    }
> +
> +    //
> +    // Convert success, move to next
> +    //
> +    BaseAddress += SIZE_4KB;
> +    Length -= SIZE_4KB;
> +  }
> +
> +  return RETURN_SUCCESS;
> +}
> +
> +/**
> +  Get maximum size of page memory supported by current processor.
> +
> +  @retval Page1G     If processor supports 1G page and PML4.
> +  @retval Page2M     For all other situations.
> +
> +**/
> +PAGE_ATTRIBUTE
> +GetMaxMemoryPage (
> +  IN  PAGE_ATTRIBUTE  TopLevelType
> +  )
> +{
> +  UINT32          RegEax;
> +  UINT32          RegEdx;
> +
> +  if (TopLevelType == Page512G) {
> +    AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
> +    if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
> +      AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx);
> +      if ((RegEdx & BIT26) != 0) {
> +        return Page1G;
> +      }
> +    }
> +  }
> +
> +  return Page2M;
> +}
> +
> +/**
> +  Create PML4 or PAE page table.
> +
> +  @return The address of page table.
> +
> +**/
> +UINTN
> +CreatePageTable (
> +  VOID
> +  )
> +{
> +  RETURN_STATUS           Status;
> +  UINTN                   PhysicalAddressBits;
> +  UINTN                   NumberOfEntries;
> +  PAGE_ATTRIBUTE          TopLevelPageAttr;
> +  UINTN                   PageTable;
> +  PAGE_ATTRIBUTE          MaxMemoryPage;
> +  UINTN                   Index;
> +  UINT64                  AddressEncMask;
> +  UINT64                  *PageEntry;
> +  EFI_PHYSICAL_ADDRESS    PhysicalAddress;
> +
> +  TopLevelPageAttr = (PAGE_ATTRIBUTE)GetPageTableTopLevelType ();
> + PhysicalAddressBits = GetPhysicalAddressWidth ();  NumberOfEntries =
> + (UINTN)1 << (PhysicalAddressBits -
> +
> + mPageAttributeTable[TopLevelPageAttr].AddressBitOffset);
> +
> +  PageTable = (UINTN) AllocatePageTableMemory (1);  if (PageTable == 0)
> + {
> +    return 0;
> +  }
> +
> +  AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask);
> +  AddressEncMask &=
> mPageAttributeTable[TopLevelPageAttr].AddressMask;
> +  MaxMemoryPage = GetMaxMemoryPage (TopLevelPageAttr);  PageEntry
> =
> + (UINT64 *)PageTable;
> +
> +  PhysicalAddress = 0;
> +  for (Index = 0; Index < NumberOfEntries; ++Index) {
> +    *PageEntry = PhysicalAddress | AddressEncMask |
> + PAGE_ATTRIBUTE_BITS;
> +
> +    //
> +    // Split the top page table down to the maximum page size supported
> +    //
> +    if (MaxMemoryPage < TopLevelPageAttr) {
> +      Status = SplitPage(PageEntry, TopLevelPageAttr, MaxMemoryPage,
> TRUE);
> +      ASSERT_EFI_ERROR (Status);
> +    }
> +
> +    if (TopLevelPageAttr == Page1G) {
> +      //
> +      // PDPTE[2:1] (PAE Paging) must be 0. SplitPage() might change them to
> 1.
> +      //
> +      *PageEntry &= ~(UINT64)(IA32_PG_RW | IA32_PG_U);
> +    }
> +
> +    PageEntry += 1;
> +    PhysicalAddress += mPageAttributeTable[TopLevelPageAttr].Length;
> +  }
> +
> +
> +  return PageTable;
> +}
> +
> +/**
> +  Setup page tables and make them work.
> +
> +**/
> +VOID
> +EnablePaging (
> +  VOID
> +  )
> +{
> +  UINTN                       PageTable;
> +
> +  PageTable = CreatePageTable ();
> +  ASSERT (PageTable != 0);
> +  if (PageTable != 0) {
> +    AsmWriteCr3(PageTable);
> +    AsmWriteCr4 (AsmReadCr4 () | BIT5);   // CR4.PAE
> +    AsmWriteCr0 (AsmReadCr0 () | BIT31);  // CR0.PG
> +  }
> +}
> +
> +/**
> +  Get the base address of current AP's stack.
> +
> +  This function is called in AP's context and assumes that whole
> + calling stacks  (till this function) consumed by AP's wakeup procedure will
> not exceed 4KB.
> +
> +  PcdCpuApStackSize must be configured with value taking the Guard page
> + into  account.
> +
> +  @param[in,out] Buffer  The pointer to private data buffer.
> +
> +**/
> +VOID
> +EFIAPI
> +GetStackBase (
> +  IN OUT VOID *Buffer
> +  )
> +{
> +  EFI_PHYSICAL_ADDRESS    StackBase;
> +
> +  StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)&StackBase;
> +  StackBase += BASE_4KB;
> +  StackBase &= ~((EFI_PHYSICAL_ADDRESS)BASE_4KB - 1);  StackBase -=
> + PcdGet32(PcdCpuApStackSize);
> +
> +  *(EFI_PHYSICAL_ADDRESS *)Buffer = StackBase; }
> +
> +/**
> +  Setup stack Guard page at the stack base of each processor. BSP and
> +APs have
> +  different way to get stack base address.
> +
> +**/
> +VOID
> +SetupStackGuardPage (
> +  VOID
> +  )
> +{
> +  EFI_PEI_HOB_POINTERS        Hob;
> +  EFI_PHYSICAL_ADDRESS        StackBase;
> +  UINTN                       NumberOfProcessors;
> +  UINTN                       Bsp;
> +  UINTN                       Index;
> +
> +  //
> +  // One extra page at the bottom of the stack is needed for Guard page.
> +  //
> +  if (PcdGet32(PcdCpuApStackSize) <= EFI_PAGE_SIZE) {
> +    DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack
> Guard!\n"));
> +    ASSERT (FALSE);
> +  }
> +
> +  MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL);
> + MpInitLibWhoAmI (&Bsp);  for (Index = 0; Index < NumberOfProcessors;
> + ++Index) {
> +    if (Index == Bsp) {
> +      Hob.Raw = GetHobList ();
> +      while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
> Hob.Raw)) != NULL) {
> +        if (CompareGuid (&gEfiHobMemoryAllocStackGuid,
> +                         
> &(Hob.MemoryAllocationStack->AllocDescriptor.Name))) {
> +          StackBase = Hob.MemoryAllocationStack-
> >AllocDescriptor.MemoryBaseAddress;
> +          break;
> +        }
> +        Hob.Raw = GET_NEXT_HOB (Hob);
> +      }
> +    } else {
> +      //
> +      // Ask AP to return is stack base address.
> +      //
> +      MpInitLibStartupThisAP(GetStackBase, Index, NULL, 0, (VOID
> *)&StackBase, NULL);
> +    }
> +    //
> +    // Set Guard page at stack base address.
> +    //
> +    ConvertMemoryPageAttributes(StackBase, EFI_PAGE_SIZE, 0);
> +    DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n",
> +            (UINT64)StackBase, (UINT64)Index));  }
> +
> +  //
> +  // Publish the changes of page table.
> +  //
> +  CpuFlushTlb ();
> +}
> +
> +/**
> +  Enabl/setup stack guard for each processor if PcdCpuStackGuard is set to
> TRUE.
> +
> +  Doing this in the memory-discovered callback is to make sure the
> + Stack Guard  feature to cover as most PEI code as possible.
> +
> +  @param[in] PeiServices          General purpose services available to every
> PEIM.
> +  @param[in] NotifyDescriptor     The notification structure this PEIM
> registered on install.
> +  @param[in] Ppi                  The memory discovered PPI.  Not used.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval others                  There's error in MP initialization.
> +**/
> +EFI_STATUS
> +EFIAPI
> +MemoryDiscoveredPpiNotifyCallback (
> +  IN EFI_PEI_SERVICES           **PeiServices,
> +  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
> +  IN VOID                       *Ppi
> +  )
> +{
> +  EFI_STATUS      Status;
> +  BOOLEAN         InitStackGuard;
> +
> +  //
> +  // Paging must be setup first. Otherwise the exception TSS setup
> + during MP  // initialization later will not contain paging information
> + and then fail  // the task switch (for the sake of stack switch).
> +  //
> +  InitStackGuard = FALSE;
> +  if (IsIa32PaeSupported () && PcdGetBool (PcdCpuStackGuard)) {
> +    EnablePaging ();
> +    InitStackGuard = TRUE;
> +  }
> +
> +  Status = InitializeCpuMpWorker ((CONST EFI_PEI_SERVICES
> + **)PeiServices);  ASSERT_EFI_ERROR (Status);
> +
> +  if (InitStackGuard) {
> +    SetupStackGuardPage ();
> +  }
> +
> +  return Status;
> +}
> +
> --
> 2.16.2.windows.1
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to