From: "Wang, Jian J" <jian.j.wang@intel.com>
To: "Dong, Eric" <eric.dong@intel.com>,
"edk2-devel@lists.01.org" <edk2-devel@lists.01.org>
Cc: Laszlo Ersek <lersek@redhat.com>,
"Ni, Ruiyu" <ruiyu.ni@intel.com>,
"Yao, Jiewen" <jiewen.yao@intel.com>,
"Zeng, Star" <star.zeng@intel.com>,
"Ware, Ryan R" <ryan.r.ware@intel.com>
Subject: Re: [PATCH 4/4] UefiCpuPkg/CpuMpPei: support stack guard feature
Date: Thu, 6 Sep 2018 02:42:10 +0000 [thread overview]
Message-ID: <D827630B58408649ACB04F44C510003624E2CCB3@SHSMSX103.ccr.corp.intel.com> (raw)
In-Reply-To: <ED077930C258884BBCB450DB737E66224AC99463@shsmsx102.ccr.corp.intel.com>
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.wang@intel.com>; edk2-devel@lists.01.org
Cc: Laszlo Ersek <lersek@redhat.com>; Ni, Ruiyu <ruiyu.ni@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>; Zeng, Star <star.zeng@intel.com>; Ware, Ryan R <ryan.r.ware@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.dong@intel.com<mailto:eric.dong@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.dong@intel.com<mailto:eric.dong@intel.com>>; Laszlo Ersek <lersek@redhat.com<mailto:lersek@redhat.com>>; Ni,
> Ruiyu <ruiyu.ni@intel.com<mailto:ruiyu.ni@intel.com>>; Yao, Jiewen <jiewen.yao@intel.com<mailto:jiewen.yao@intel.com>>; Zeng,
> Star <star.zeng@intel.com<mailto:star.zeng@intel.com>>; Ware, Ryan R <ryan.r.ware@intel.com<mailto:ryan.r.ware@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.dong@intel.com<mailto:eric.dong@intel.com>>
> Cc: Laszlo Ersek <lersek@redhat.com<mailto:lersek@redhat.com>>
> Cc: Ruiyu Ni <ruiyu.ni@intel.com<mailto:ruiyu.ni@intel.com>>
> Cc: Jiewen Yao <jiewen.yao@intel.com<mailto:jiewen.yao@intel.com>>
> Cc: Star Zeng <star.zeng@intel.com<mailto:star.zeng@intel.com>>
> Cc: "Ware, Ryan R" <ryan.r.ware@intel.com<mailto:ryan.r.ware@intel.com>>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Jian J Wang <jian.j.wang@intel.com<mailto:jian.j.wang@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
next prev parent reply other threads:[~2018-09-06 2:43 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-09-03 3:15 [PATCH 0/4] Add PEI Stack Guard feature Jian J Wang
2018-09-03 3:15 ` [PATCH 1/4] MdeModulePkg/DxeIpl: disable paging before creating new page table Jian J Wang
2018-09-03 3:15 ` [PATCH 2/4] UefiCpuPkg/CpuExceptionHandlerLib: support stack switch for PEI exceptions Jian J Wang
2018-09-06 1:38 ` Dong, Eric
2018-09-03 3:15 ` [PATCH 3/4] UefiCpuPkg/MpInitLib: fix register restore issue in AP wakeup Jian J Wang
2018-09-05 5:29 ` Dong, Eric
2018-09-03 3:15 ` [PATCH 4/4] UefiCpuPkg/CpuMpPei: support stack guard feature Jian J Wang
2018-09-06 1:45 ` Dong, Eric
2018-09-06 2:42 ` Wang, Jian J [this message]
2018-09-03 4:14 ` [PATCH 0/4] Add PEI Stack Guard feature Yao, Jiewen
2018-09-03 4:56 ` Wang, Jian J
2018-09-03 14:52 ` Laszlo Ersek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=D827630B58408649ACB04F44C510003624E2CCB3@SHSMSX103.ccr.corp.intel.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox