public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Fan, Jeff" <jeff.fan@intel.com>
To: "Yao, Jiewen" <jiewen.yao@intel.com>,
	"edk2-devel@lists.01.org" <edk2-devel@lists.01.org>
Cc: "Kinney, Michael D" <michael.d.kinney@intel.com>
Subject: Re: [PATCH 1/3] UefiCpuPkg/CpuDxe: Add memory attribute setting.
Date: Mon, 6 Feb 2017 05:26:28 +0000	[thread overview]
Message-ID: <542CF652F8836A4AB8DBFAAD40ED192A4C52BF57@shsmsx102.ccr.corp.intel.com> (raw)
In-Reply-To: <74D8A39837DF1E4DA445A8C0B3885C503A8E958F@shsmsx102.ccr.corp.intel.com>

How about to use CpuPageTable.h and CpuPageTable.c?

-----Original Message-----
From: Yao, Jiewen 
Sent: Monday, February 06, 2017 11:59 AM
To: Fan, Jeff; edk2-devel@lists.01.org
Cc: Kinney, Michael D
Subject: RE: [PATCH 1/3] UefiCpuPkg/CpuDxe: Add memory attribute setting.

Thanks for the suggestion.

Comments below:

> -----Original Message-----
> From: Fan, Jeff
> Sent: Sunday, February 5, 2017 7:27 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; edk2-devel@lists.01.org
> Cc: Kinney, Michael D <michael.d.kinney@intel.com>
> Subject: RE: [PATCH 1/3] UefiCpuPkg/CpuDxe: Add memory attribute setting.
> 
> Jiewen,
> 
> 1. If SyncMemoryPageAttributesAp() is only to invoke SyncCpuFlushTlb(), you
> could simply remove the SyncCpuFlushTlb parameter and use VOID for
> SyncMemoryPageAttributesAp().
>  +      SyncMemoryPageAttributesAp (SyncCpuFlushTlb);
[Jiewen] No. We also have SyncMemoryPageAttributesAp (SyncCpuEnableWriteProtection);

> 2. Copyright date should be updated to 2017.
[Jiewen] Good catch.

> 3. How about to change the name of PageTableLib.h to another one? It may
> confuse me this is library class or internal header file only.
[Jiewen] Agree. How about we just use PageTable.h and PageTable.c?

> Thanks!
> Jeff
> 
> -----Original Message-----
> From: Yao, Jiewen
> Sent: Thursday, January 26, 2017 4:39 PM
> To: edk2-devel@lists.01.org
> Cc: Fan, Jeff; Kinney, Michael D
> Subject: [PATCH 1/3] UefiCpuPkg/CpuDxe: Add memory attribute setting.
> 
> Add memory attribute setting in CpuArch protocol.
> Previous SetMemoryAttributes() API only supports cache attribute setting.
> 
> This patch updated SetMemoryAttributes() API to support memory attribute
> setting by updating CPU page table.
> 
> Cc: Jeff Fan <jeff.fan@intel.com>
> Cc: Michael Kinney <michael.d.kinney@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Jiewen Yao <jiewen.yao@intel.com>
> ---
>  UefiCpuPkg/CpuDxe/CpuDxe.c             | 137 +--
>  UefiCpuPkg/CpuDxe/CpuDxe.inf           |   4 +-
>  UefiCpuPkg/CpuDxe/PageTableLib.h       | 204 ++++
>  UefiCpuPkg/CpuDxe/PageTableLibX86Pae.c | 997 ++++++++++++++++++++
>  4 files changed, 1283 insertions(+), 59 deletions(-)
> 
> diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.c b/UefiCpuPkg/CpuDxe/CpuDxe.c
> index f6d0a67..4a1d27e 100644
> --- a/UefiCpuPkg/CpuDxe/CpuDxe.c
> +++ b/UefiCpuPkg/CpuDxe/CpuDxe.c
> @@ -14,6 +14,10 @@
> 
>  #include "CpuDxe.h"
>  #include "CpuMp.h"
> +#include "PageTableLib.h"
> +
> +#define CACHE_ATTRIBUTE_MASK   (EFI_MEMORY_UC | EFI_MEMORY_WC |
> EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE |
> EFI_MEMORY_WP)
> +#define MEMORY_ATTRIBUTE_MASK  (EFI_MEMORY_RP | EFI_MEMORY_XP |
> EFI_MEMORY_RO)
> 
>  //
>  // Global Variables
> @@ -368,10 +372,9 @@ CpuSetMemoryAttributes (
>    EFI_STATUS                MpStatus;
>    EFI_MP_SERVICES_PROTOCOL  *MpService;
>    MTRR_SETTINGS             MtrrSettings;
> -
> -  if (!IsMtrrSupported ()) {
> -    return EFI_UNSUPPORTED;
> -  }
> +  UINT64                    CacheAttributes;
> +  UINT64                    MemoryAttributes;
> +  MTRR_MEMORY_CACHE_TYPE    CurrentCacheType;
> 
>    //
>    // If this function is called because GCD SetMemorySpaceAttributes () is called
> @@ -384,69 +387,87 @@ CpuSetMemoryAttributes (
>      return EFI_SUCCESS;
>    }
> 
> -  switch (Attributes) {
> -  case EFI_MEMORY_UC:
> -    CacheType = CacheUncacheable;
> -    break;
> 
> -  case EFI_MEMORY_WC:
> -    CacheType = CacheWriteCombining;
> -    break;
> +  CacheAttributes = Attributes & CACHE_ATTRIBUTE_MASK;
> +  MemoryAttributes = Attributes & MEMORY_ATTRIBUTE_MASK;
> 
> -  case EFI_MEMORY_WT:
> -    CacheType = CacheWriteThrough;
> -    break;
> +  if (Attributes != (CacheAttributes | MemoryAttributes)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> 
> -  case EFI_MEMORY_WP:
> -    CacheType = CacheWriteProtected;
> -    break;
> +  if (CacheAttributes != 0) {
> +    if (!IsMtrrSupported ()) {
> +      return EFI_UNSUPPORTED;
> +    }
> 
> -  case EFI_MEMORY_WB:
> -    CacheType = CacheWriteBack;
> -    break;
> +    switch (CacheAttributes) {
> +    case EFI_MEMORY_UC:
> +      CacheType = CacheUncacheable;
> +      break;
> 
> -  case EFI_MEMORY_UCE:
> -  case EFI_MEMORY_RP:
> -  case EFI_MEMORY_XP:
> -  case EFI_MEMORY_RUNTIME:
> -    return EFI_UNSUPPORTED;
> +    case EFI_MEMORY_WC:
> +      CacheType = CacheWriteCombining;
> +      break;
> 
> -  default:
> -    return EFI_INVALID_PARAMETER;
> -  }
> -  //
> -  // call MTRR libary function
> -  //
> -  Status = MtrrSetMemoryAttribute (
> -             BaseAddress,
> -             Length,
> -             CacheType
> -             );
> +    case EFI_MEMORY_WT:
> +      CacheType = CacheWriteThrough;
> +      break;
> 
> -  if (!RETURN_ERROR (Status)) {
> -    MpStatus = gBS->LocateProtocol (
> -                      &gEfiMpServiceProtocolGuid,
> -                      NULL,
> -                      (VOID **)&MpService
> -                      );
> -    //
> -    // Synchronize the update with all APs
> -    //
> -    if (!EFI_ERROR (MpStatus)) {
> -      MtrrGetAllMtrrs (&MtrrSettings);
> -      MpStatus = MpService->StartupAllAPs (
> -                              MpService,          // This
> -                              SetMtrrsFromBuffer, // Procedure
> -                              FALSE,              // SingleThread
> -                              NULL,               // WaitEvent
> -                              0,                  //
> TimeoutInMicrosecsond
> -                              &MtrrSettings,      // ProcedureArgument
> -                              NULL                // FailedCpuList
> -                              );
> -      ASSERT (MpStatus == EFI_SUCCESS || MpStatus == EFI_NOT_STARTED);
> +    case EFI_MEMORY_WP:
> +      CacheType = CacheWriteProtected;
> +      break;
> +
> +    case EFI_MEMORY_WB:
> +      CacheType = CacheWriteBack;
> +      break;
> +
> +    default:
> +      return EFI_INVALID_PARAMETER;
> +    }
> +    CurrentCacheType = MtrrGetMemoryAttribute(BaseAddress);
> +    if (CurrentCacheType != CacheType) {
> +      //
> +      // call MTRR libary function
> +      //
> +      Status = MtrrSetMemoryAttribute (
> +                 BaseAddress,
> +                 Length,
> +                 CacheType
> +                 );
> +
> +      if (!RETURN_ERROR (Status)) {
> +        MpStatus = gBS->LocateProtocol (
> +                          &gEfiMpServiceProtocolGuid,
> +                          NULL,
> +                          (VOID **)&MpService
> +                          );
> +        //
> +        // Synchronize the update with all APs
> +        //
> +        if (!EFI_ERROR (MpStatus)) {
> +          MtrrGetAllMtrrs (&MtrrSettings);
> +          MpStatus = MpService->StartupAllAPs (
> +                                  MpService,          // This
> +                                  SetMtrrsFromBuffer, // Procedure
> +                                  FALSE,              // SingleThread
> +                                  NULL,               // WaitEvent
> +                                  0,                  //
> TimeoutInMicrosecsond
> +                                  &MtrrSettings,      //
> ProcedureArgument
> +                                  NULL                // FailedCpuList
> +                                  );
> +          ASSERT (MpStatus == EFI_SUCCESS || MpStatus ==
> EFI_NOT_STARTED);
> +        }
> +      }
> +      if (EFI_ERROR(Status)) {
> +        return Status;
> +      }
>      }
>    }
> -  return (EFI_STATUS) Status;
> +
> +  //
> +  // Set memory attribute by page table
> +  //
> +  return AssignMemoryPageAttributes (NULL, BaseAddress, Length,
> MemoryAttributes, AllocatePages);
>  }
> 
>  /**
> diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> index bf389bb..47fd57e 100644
> --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
> +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> @@ -19,7 +19,7 @@
>    FILE_GUID                      =
> 1A1E4886-9517-440e-9FDE-3BE44CEE2136
>    MODULE_TYPE                    = DXE_DRIVER
>    VERSION_STRING                 = 1.0
> -
> +  ENTRY_POINT                    = InitializePageTableLib
>    ENTRY_POINT                    = InitializeCpu
> 
>  [Packages]
> @@ -52,6 +52,8 @@
>    CpuGdt.h
>    CpuMp.c
>    CpuMp.h
> +  PageTableLib.h
> +  PageTableLibX86Pae.c
> 
>  [Sources.IA32]
>    Ia32/CpuAsm.asm
> diff --git a/UefiCpuPkg/CpuDxe/PageTableLib.h
> b/UefiCpuPkg/CpuDxe/PageTableLib.h
> new file mode 100644
> index 0000000..66450e4
> --- /dev/null
> +++ b/UefiCpuPkg/CpuDxe/PageTableLib.h
> @@ -0,0 +1,204 @@
> +/** @file
> +  Page table management header file.
> +
> +  Copyright (c) 2016, 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.
> +
> +**/
> +
> +#ifndef _PAGE_TABLE_LIB_H_
> +#define _PAGE_TABLE_LIB_H_
> +
> +#include <IndustryStandard/PeImage.h>
> +
> +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE
> BIT0
> +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
> BIT1
> +#define
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPO
> RT  BIT2
> +#define
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE
> BIT30
> +#define
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED
> BIT31
> +// Other bits are reserved for future use
> +typedef struct {
> +  UINT32  PageTableBase;
> +  UINT32  Reserved;
> +  UINT32  Attributes;
> +} PAGE_TABLE_LIB_PAGING_CONTEXT_IA32;
> +
> +typedef struct {
> +  UINT64  PageTableBase;
> +  UINT32  Attributes;
> +} PAGE_TABLE_LIB_PAGING_CONTEXT_X64;
> +
> +typedef union {
> +  PAGE_TABLE_LIB_PAGING_CONTEXT_IA32  Ia32;
> +  PAGE_TABLE_LIB_PAGING_CONTEXT_X64   X64;
> +} PAGE_TABLE_LIB_PAGING_CONTEXT_DATA;
> +
> +typedef struct {
> +  //
> +  // PE32+ Machine type for EFI images
> +  //
> +  // #define IMAGE_FILE_MACHINE_I386            0x014c
> +  // #define IMAGE_FILE_MACHINE_X64             0x8664
> +  //
> +  UINT16                                 MachineType;
> +  PAGE_TABLE_LIB_PAGING_CONTEXT_DATA     ContextData;
> +} PAGE_TABLE_LIB_PAGING_CONTEXT;
> +
> +/**
> +  Allocates one or more 4KB pages 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.
> +
> +**/
> +typedef
> +VOID *
> +(EFIAPI *PAGE_TABLE_LIB_ALLOCATE_PAGES) (
> +  IN UINTN  Pages
> +  );
> +
> +/**
> +  This function assigns 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.
> +
> +  Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
> +
> +  @param  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param  BaseAddress       The physical address that is the start address
> of a memory region.
> +  @param  Length            The size in bytes of the memory region.
> +  @param  Attributes        The bit mask of attributes to set for the
> memory region.
> +  @param  AllocatePagesFunc If page split is needed, this function is used to
> allocate more pages.
> +                            NULL mean page split is unsupported.
> +
> +  @retval RETURN_SUCCESS           The attributes were cleared for the
> memory region.
> +  @retval RETURN_ACCESS_DENIED     The attributes for the memory
> resource range specified by
> +                                   BaseAddress and Length cannot be
> modified.
> +  @retval RETURN_INVALID_PARAMETER Length is zero.
> +                                   Attributes specified an illegal
> combination of attributes that
> +                                   cannot be set together.
> +  @retval RETURN_OUT_OF_RESOURCES  There are not enough system
> resources to modify the attributes of
> +                                   the memory resource range.
> +  @retval RETURN_UNSUPPORTED       The processor does not support one
> or more bytes of the memory
> +                                   resource range specified by
> BaseAddress and Length.
> +                                   The bit mask of attributes is not
> support for the memory resource
> +                                   range specified by BaseAddress and
> Length.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +AssignMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc OPTIONAL
> +  );
> +
> +/**
> +  This function sets 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.
> +
> +  Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
> +
> +  @param  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param  BaseAddress       The physical address that is the start address
> of a memory region.
> +  @param  Length            The size in bytes of the memory region.
> +  @param  Attributes        The bit mask of attributes to set for the
> memory region.
> +  @param  AllocatePagesFunc If page split is needed, this function is used to
> allocate more pages.
> +                            NULL mean page split is unsupported.
> +
> +  @retval RETURN_SUCCESS           The attributes were set for the
> memory region.
> +  @retval RETURN_ACCESS_DENIED     The attributes for the memory
> resource range specified by
> +                                   BaseAddress and Length cannot be
> modified.
> +  @retval RETURN_INVALID_PARAMETER Length is zero.
> +                                   Attributes specified an illegal
> combination of attributes that
> +                                   cannot be set together.
> +  @retval RETURN_OUT_OF_RESOURCES  There are not enough system
> resources to modify the attributes of
> +                                   the memory resource range.
> +  @retval RETURN_UNSUPPORTED       The processor does not support one
> or more bytes of the memory
> +                                   resource range specified by
> BaseAddress and Length.
> +                                   The bit mask of attributes is not
> support for the memory resource
> +                                   range specified by BaseAddress and
> Length.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +SetMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc OPTIONAL
> +  );
> +
> +/**
> +  This function clears 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.
> +
> +  Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
> +
> +  @param  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param  BaseAddress       The physical address that is the start address
> of a memory region.
> +  @param  Length            The size in bytes of the memory region.
> +  @param  Attributes        The bit mask of attributes to set for the
> memory region.
> +  @param  AllocatePagesFunc If page split is needed, this function is used to
> allocate more pages.
> +                            NULL mean page split is unsupported.
> +
> +  @retval RETURN_SUCCESS           The attributes were cleared for the
> memory region.
> +  @retval RETURN_ACCESS_DENIED     The attributes for the memory
> resource range specified by
> +                                   BaseAddress and Length cannot be
> modified.
> +  @retval RETURN_INVALID_PARAMETER Length is zero.
> +                                   Attributes specified an illegal
> combination of attributes that
> +                                   cannot be set together.
> +  @retval RETURN_OUT_OF_RESOURCES  There are not enough system
> resources to modify the attributes of
> +                                   the memory resource range.
> +  @retval RETURN_UNSUPPORTED       The processor does not support one
> or more bytes of the memory
> +                                   resource range specified by
> BaseAddress and Length.
> +                                   The bit mask of attributes is not
> support for the memory resource
> +                                   range specified by BaseAddress and
> Length.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +ClearMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc OPTIONAL
> +  );
> +
> +/**
> +  This function return the page attributes for the memory region specified by
> BaseAddress.
> +
> +  Caller should make sure BaseAddress is at page boundary.
> +
> +  @param  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param  BaseAddress       The physical address that is the start address
> of a memory region.
> +  @param  Attributes        The bit mask of attributes of the memory
> region.
> +  @param  PageSize          The size of the pages which contains the
> BaseAddress.
> +
> +  @retval RETURN_SUCCESS           The Attributes and PageSize is
> returned.
> +  @retval RETURN_INVALID_PARAMETER Both Attributes and PageSize are
> zero.
> +  @retval RETURN_NOT_FOUND         The processor does not setup
> paging for BaseAddress.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +GetMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  OUT UINT64                            *Attributes,
> +  OUT UINT64                            *PageSize
> +  );
> +
> +#endif
> diff --git a/UefiCpuPkg/CpuDxe/PageTableLibX86Pae.c
> b/UefiCpuPkg/CpuDxe/PageTableLibX86Pae.c
> new file mode 100644
> index 0000000..0d6c6a6
> --- /dev/null
> +++ b/UefiCpuPkg/CpuDxe/PageTableLibX86Pae.c
> @@ -0,0 +1,997 @@
> +/** @file
> +  Page table management support.
> +
> +  Copyright (c) 2016, 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 <Base.h>
> +#include <Uefi.h>
> +#include <Library/BaseLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Protocol/MpService.h>
> +#include "PageTableLib.h"
> +
> +///
> +/// Page Table Entry
> +///
> +#define IA32_PG_P                   BIT0
> +#define IA32_PG_RW                  BIT1
> +#define IA32_PG_U                   BIT2
> +#define IA32_PG_WT                  BIT3
> +#define IA32_PG_CD                  BIT4
> +#define IA32_PG_A                   BIT5
> +#define IA32_PG_D                   BIT6
> +#define IA32_PG_PS                  BIT7
> +#define IA32_PG_PAT_2M              BIT12
> +#define IA32_PG_PAT_4K              IA32_PG_PS
> +#define IA32_PG_PMNT                BIT62
> +#define IA32_PG_NX                  BIT63
> +
> +#define PAGE_ATTRIBUTE_BITS         (IA32_PG_D | IA32_PG_A |
> IA32_PG_U | IA32_PG_RW | IA32_PG_P)
> +//
> +// Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE
> +// X64 PAE PDPTE does not have such restriction
> +//
> +#define IA32_PAE_PDPTE_ATTRIBUTE_BITS    (IA32_PG_P)
> +
> +#define PAGE_PROGATE_BITS           (IA32_PG_NX |
> PAGE_ATTRIBUTE_BITS)
> +
> +#define PAGING_4K_MASK  0xFFF
> +#define PAGING_2M_MASK  0x1FFFFF
> +#define PAGING_1G_MASK  0x3FFFFFFF
> +
> +#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
> +
> +typedef enum {
> +  PageNone,
> +  Page4K,
> +  Page2M,
> +  Page1G,
> +} PAGE_ATTRIBUTE;
> +
> +typedef struct {
> +  PAGE_ATTRIBUTE   Attribute;
> +  UINT64           Length;
> +  UINT64           AddressMask;
> +} PAGE_ATTRIBUTE_TABLE;
> +
> +typedef enum {
> +  PageActionAssign,
> +  PageActionSet,
> +  PageActionClear,
> +} PAGE_ACTION;
> +
> +PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
> +  {Page4K,  SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},
> +  {Page2M,  SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},
> +  {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
> +};
> +
> +/**
> +  Enable write protection function for AP.
> +
> +  @param[in,out] Buffer  The pointer to private data buffer.
> +**/
> +VOID
> +EFIAPI
> +SyncCpuEnableWriteProtection (
> +  IN OUT VOID *Buffer
> +  )
> +{
> +  AsmWriteCr0 (AsmReadCr0 () | BIT16);
> +}
> +
> +/**
> +  CpuFlushTlb function for AP.
> +
> +  @param[in,out] Buffer  The pointer to private data buffer.
> +**/
> +VOID
> +EFIAPI
> +SyncCpuFlushTlb (
> +  IN OUT VOID *Buffer
> +  )
> +{
> +  CpuFlushTlb();
> +}
> +
> +/**
> +  Sync memory page attributes for AP.
> +
> +  @param[in] Procedure            A pointer to the function to be run on
> enabled APs of
> +                                  the system.
> +**/
> +VOID
> +SyncMemoryPageAttributesAp (
> +  IN EFI_AP_PROCEDURE            Procedure
> +  )
> +{
> +  EFI_STATUS                Status;
> +  EFI_MP_SERVICES_PROTOCOL  *MpService;
> +
> +  Status = gBS->LocateProtocol (
> +                  &gEfiMpServiceProtocolGuid,
> +                  NULL,
> +                  (VOID **)&MpService
> +                  );
> +  //
> +  // Synchronize the update with all APs
> +  //
> +  if (!EFI_ERROR (Status)) {
> +    Status = MpService->StartupAllAPs (
> +                          MpService,          // This
> +                          Procedure,          // Procedure
> +                          FALSE,              // SingleThread
> +                          NULL,               // WaitEvent
> +                          0,                  // TimeoutInMicrosecsond
> +                          NULL,               // ProcedureArgument
> +                          NULL                // FailedCpuList
> +                          );
> +    ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_STARTED);
> +  }
> +}
> +
> +/**
> +  Return current paging context.
> +
> +  @param[in,out]  PagingContext     The paging context.
> +**/
> +VOID
> +GetCurrentPagingContext (
> +  IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext
> +  )
> +{
> +  UINT32                         RegEax;
> +  UINT32                         RegEdx;
> +
> +  ZeroMem(PagingContext, sizeof(*PagingContext));
> +  if (sizeof(UINTN) == sizeof(UINT64)) {
> +    PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;
> +  } else {
> +    PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;
> +  }
> +  if ((AsmReadCr0 () & BIT31) != 0) {
> +    PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () &
> PAGING_4K_ADDRESS_MASK_64);
> +    if ((AsmReadCr0 () & BIT16) == 0) {
> +      AsmWriteCr0 (AsmReadCr0 () | BIT16);
> +      SyncMemoryPageAttributesAp (SyncCpuEnableWriteProtection);
> +    }
> +  } else {
> +    PagingContext->ContextData.X64.PageTableBase = 0;
> +  }
> +
> +  if ((AsmReadCr4 () & BIT4) != 0) {
> +    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> +  }
> +  if ((AsmReadCr4 () & BIT5) != 0) {
> +    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> +  }
> +  if ((AsmReadCr0 () & BIT16) != 0) {
> +    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
> +  }
> +
> +  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> +  if (RegEax > 0x80000000) {
> +    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
> +    if ((RegEdx & BIT20) != 0) {
> +      // XD supported
> +      if ((AsmReadMsr64 (0x000001A0) & BIT34) == 0) {
> +        // XD enabled
> +        if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {
> +          // XD activated
> +          PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> +        }
> +      }
> +    }
> +    if ((RegEdx & BIT26) != 0) {
> +      PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPO
> RT;
> +    }
> +  }
> +}
> +
> +/**
> +  Return length according to page attributes.
> +
> +  @param[in]  PageAttributes   The page attribute of the page entry.
> +
> +  @return The length of page entry.
> +**/
> +UINTN
> +PageAttributeToLength (
> +  IN PAGE_ATTRIBUTE  PageAttribute
> +  )
> +{
> +  UINTN  Index;
> +  for (Index = 0; Index <
> sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
> +    if (PageAttribute == mPageAttributeTable[Index].Attribute) {
> +      return (UINTN)mPageAttributeTable[Index].Length;
> +    }
> +  }
> +  return 0;
> +}
> +
> +/**
> +  Return address mask according to page attributes.
> +
> +  @param[in]  PageAttributes   The page attribute of the page entry.
> +
> +  @return The address mask of page entry.
> +**/
> +UINTN
> +PageAttributeToMask (
> +  IN PAGE_ATTRIBUTE  PageAttribute
> +  )
> +{
> +  UINTN  Index;
> +  for (Index = 0; Index <
> sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
> +    if (PageAttribute == mPageAttributeTable[Index].Attribute) {
> +      return (UINTN)mPageAttributeTable[Index].AddressMask;
> +    }
> +  }
> +  return 0;
> +}
> +
> +/**
> +  Return page table entry to match the address.
> +
> +  @param[in]  PagingContext     The paging context.
> +  @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  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext,
> +  IN  PHYSICAL_ADDRESS                  Address,
> +  OUT PAGE_ATTRIBUTE                    *PageAttribute
> +  )
> +{
> +  UINTN                 Index1;
> +  UINTN                 Index2;
> +  UINTN                 Index3;
> +  UINTN                 Index4;
> +  UINT64                *L1PageTable;
> +  UINT64                *L2PageTable;
> +  UINT64                *L3PageTable;
> +  UINT64                *L4PageTable;
> +
> +  ASSERT (PagingContext != NULL);
> +
> +  Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
> +  Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
> +  Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
> +  Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
> +
> +  if (PagingContext->MachineType == IMAGE_FILE_MACHINE_X64) {
> +    L4PageTable = (UINT64
> *)(UINTN)PagingContext->ContextData.X64.PageTableBase;
> +    if (L4PageTable[Index4] == 0) {
> +      *PageAttribute = PageNone;
> +      return NULL;
> +    }
> +
> +    L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] &
> PAGING_4K_ADDRESS_MASK_64);
> +  } else {
> +    ASSERT((PagingContext->ContextData.Ia32.Attributes &
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0);
> +    L3PageTable = (UINT64
> *)(UINTN)PagingContext->ContextData.Ia32.PageTableBase;
> +  }
> +  if (L3PageTable[Index3] == 0) {
> +    *PageAttribute = PageNone;
> +    return NULL;
> +  }
> +  if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
> +    // 1G
> +    *PageAttribute = Page1G;
> +    return &L3PageTable[Index3];
> +  }
> +
> +  L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] &
> PAGING_4K_ADDRESS_MASK_64);
> +  if (L2PageTable[Index2] == 0) {
> +    *PageAttribute = PageNone;
> +    return NULL;
> +  }
> +  if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
> +    // 2M
> +    *PageAttribute = Page2M;
> +    return &L2PageTable[Index2];
> +  }
> +
> +  // 4k
> +  L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] &
> PAGING_4K_ADDRESS_MASK_64);
> +  if ((L1PageTable[Index1] == 0) && (Address != 0)) {
> +    *PageAttribute = PageNone;
> +    return NULL;
> +  }
> +  *PageAttribute = Page4K;
> +  return &L1PageTable[Index1];
> +}
> +
> +/**
> +  Return memory attributes of page entry.
> +
> +  @param[in]  PageEntry        The page entry.
> +
> +  @return Memory attributes of page entry.
> +**/
> +UINT64
> +GetAttributesFromPageEntry (
> +  IN  UINT64                            *PageEntry
> +  )
> +{
> +  UINT64  Attributes;
> +  Attributes = 0;
> +  if ((*PageEntry & IA32_PG_P) == 0) {
> +    Attributes |= EFI_MEMORY_RP;
> +  }
> +  if ((*PageEntry & IA32_PG_RW) == 0) {
> +    Attributes |= EFI_MEMORY_RO;
> +  }
> +  if ((*PageEntry & IA32_PG_NX) != 0) {
> +    Attributes |= EFI_MEMORY_XP;
> +  }
> +  return Attributes;
> +}
> +
> +/**
> +  Modify memory attributes of page entry.
> +
> +  @param[in]  PagingContext    The paging context.
> +  @param[in]  PageEntry        The page entry.
> +  @param[in]  Attributes       The bit mask of attributes to modify for the
> memory region.
> +  @param[in]  PageAction       The page action.
> +  @param[out] IsModified       TRUE means page table modified. FALSE
> means page table not modified.
> +**/
> +VOID
> +ConvertPageEntryAttribute (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext,
> +  IN  UINT64                            *PageEntry,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_ACTION                       PageAction,
> +  OUT BOOLEAN                           *IsModified
> +  )
> +{
> +  UINT64  CurrentPageEntry;
> +  UINT64  NewPageEntry;
> +
> +  CurrentPageEntry = *PageEntry;
> +  NewPageEntry = CurrentPageEntry;
> +  if ((Attributes & EFI_MEMORY_RP) != 0) {
> +    switch (PageAction) {
> +    case PageActionAssign:
> +    case PageActionSet:
> +      NewPageEntry &= ~(UINT64)IA32_PG_P;
> +      break;
> +    case PageActionClear:
> +      NewPageEntry |= IA32_PG_P;
> +      break;
> +    }
> +  } else {
> +    switch (PageAction) {
> +    case PageActionAssign:
> +      NewPageEntry |= IA32_PG_P;
> +      break;
> +    case PageActionSet:
> +    case PageActionClear:
> +      break;
> +    }
> +  }
> +  if ((Attributes & EFI_MEMORY_RO) != 0) {
> +    switch (PageAction) {
> +    case PageActionAssign:
> +    case PageActionSet:
> +      NewPageEntry &= ~(UINT64)IA32_PG_RW;
> +      break;
> +    case PageActionClear:
> +      NewPageEntry |= IA32_PG_RW;
> +      break;
> +    }
> +  } else {
> +    switch (PageAction) {
> +    case PageActionAssign:
> +      NewPageEntry |= IA32_PG_RW;
> +      break;
> +    case PageActionSet:
> +    case PageActionClear:
> +      break;
> +    }
> +  }
> +  if ((PagingContext->ContextData.Ia32.Attributes &
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED) !=
> 0) {
> +    if ((Attributes & EFI_MEMORY_XP) != 0) {
> +      switch (PageAction) {
> +      case PageActionAssign:
> +      case PageActionSet:
> +        NewPageEntry |= IA32_PG_NX;
> +        break;
> +      case PageActionClear:
> +        NewPageEntry &= ~IA32_PG_NX;
> +        break;
> +      }
> +    } else {
> +      switch (PageAction) {
> +      case PageActionAssign:
> +        NewPageEntry &= ~IA32_PG_NX;
> +        break;
> +      case PageActionSet:
> +      case PageActionClear:
> +        break;
> +      }
> +    }
> +  }
> +  *PageEntry = NewPageEntry;
> +  if (CurrentPageEntry != NewPageEntry) {
> +    *IsModified = TRUE;
> +    DEBUG ((DEBUG_INFO, "ConvertPageEntryAttribute 0x%lx",
> CurrentPageEntry));
> +    DEBUG ((DEBUG_INFO, "->0x%lx\n", NewPageEntry));
> +  } else {
> +    *IsModified = FALSE;
> +  }
> +}
> +
> +/**
> +  This function returns if there is need to split page entry.
> +
> +  @param[in]  BaseAddress      The base address to be checked.
> +  @param[in]  Length           The length to be checked.
> +  @param[in]  PageEntry        The page entry to be checked.
> +  @param[in]  PageAttribute    The page attribute of the page entry.
> +
> +  @retval SplitAttributes on if there is need to split page entry.
> +**/
> +PAGE_ATTRIBUTE
> +NeedSplitPage (
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            *PageEntry,
> +  IN  PAGE_ATTRIBUTE                    PageAttribute
> +  )
> +{
> +  UINT64                PageEntryLength;
> +
> +  PageEntryLength = PageAttributeToLength (PageAttribute);
> +
> +  if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >=
> PageEntryLength)) {
> +    return PageNone;
> +  }
> +
> +  if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
> +    return Page4K;
> +  }
> +
> +  return Page2M;
> +}
> +
> +/**
> +  This function splits one page entry to small 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]  AllocatePagesFunc If page split is needed, this function is used
> to allocate more pages.
> +
> +  @retval RETURN_SUCCESS            The page entry is splitted.
> +  @retval RETURN_UNSUPPORTED        The page entry does not support
> to be splitted.
> +  @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  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc
> +  )
> +{
> +  UINT64   BaseAddress;
> +  UINT64   *NewPageEntry;
> +  UINTN    Index;
> +
> +  ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
> +
> +  ASSERT (AllocatePagesFunc != NULL);
> +
> +  if (PageAttribute == Page2M) {
> +    //
> +    // Split 2M to 4K
> +    //
> +    ASSERT (SplitAttribute == Page4K);
> +    if (SplitAttribute == Page4K) {
> +      NewPageEntry = AllocatePagesFunc (1);
> +      DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));
> +      if (NewPageEntry == NULL) {
> +        return RETURN_OUT_OF_RESOURCES;
> +      }
> +      BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;
> +      for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
> +        NewPageEntry[Index] = BaseAddress + SIZE_4KB * Index +
> ((*PageEntry) & PAGE_PROGATE_BITS);
> +      }
> +      (*PageEntry) = (UINT64)(UINTN)NewPageEntry + ((*PageEntry) &
> PAGE_PROGATE_BITS);
> +      return RETURN_SUCCESS;
> +    } else {
> +      return RETURN_UNSUPPORTED;
> +    }
> +  } else if (PageAttribute == Page1G) {
> +    //
> +    // Split 1G to 2M
> +    // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K
> to get more compact page table.
> +    //
> +    ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);
> +    if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {
> +      NewPageEntry = AllocatePagesFunc (1);
> +      DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));
> +      if (NewPageEntry == NULL) {
> +        return RETURN_OUT_OF_RESOURCES;
> +      }
> +      BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;
> +      for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
> +        NewPageEntry[Index] = BaseAddress + SIZE_2MB * Index +
> IA32_PG_PS + ((*PageEntry) & PAGE_PROGATE_BITS);
> +      }
> +      (*PageEntry) = (UINT64)(UINTN)NewPageEntry + ((*PageEntry) &
> PAGE_PROGATE_BITS);
> +      return RETURN_SUCCESS;
> +    } else {
> +      return RETURN_UNSUPPORTED;
> +    }
> +  } else {
> +    return RETURN_UNSUPPORTED;
> +  }
> +}
> +
> +/**
> +  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]  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param[in]  BaseAddress       The physical address that is the start
> address of a memory region.
> +  @param[in]  Length            The size in bytes of the memory region.
> +  @param[in]  Attributes        The bit mask of attributes to modify for the
> memory region.
> +  @param[in]  PageAction        The page action.
> +  @param[in]  AllocatePagesFunc If page split is needed, this function is used
> to allocate more pages.
> +                                NULL mean page split is unsupported.
> +  @param[out] IsSplitted        TRUE means page table splitted. FALSE
> means page table not splitted.
> +  @param[out] IsModified        TRUE means page table modified. FALSE
> means page table not modified.
> +
> +  @retval RETURN_SUCCESS           The attributes were modified for the
> memory region.
> +  @retval RETURN_ACCESS_DENIED     The attributes for the memory
> resource range specified by
> +                                   BaseAddress and Length cannot be
> modified.
> +  @retval RETURN_INVALID_PARAMETER Length is zero.
> +                                   Attributes specified an illegal
> combination of attributes that
> +                                   cannot be set together.
> +  @retval RETURN_OUT_OF_RESOURCES  There are not enough system
> resources to modify the attributes of
> +                                   the memory resource range.
> +  @retval RETURN_UNSUPPORTED       The processor does not support one
> or more bytes of the memory
> +                                   resource range specified by
> BaseAddress and Length.
> +                                   The bit mask of attributes is not
> support for the memory resource
> +                                   range specified by BaseAddress and
> Length.
> +**/
> +RETURN_STATUS
> +ConvertMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_ACTION                       PageAction,
> +  IN  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc OPTIONAL,
> +  OUT BOOLEAN                           *IsSplitted,  OPTIONAL
> +  OUT BOOLEAN                           *IsModified   OPTIONAL
> +  )
> +{
> +  PAGE_TABLE_LIB_PAGING_CONTEXT     CurrentPagingContext;
> +  UINT64                            *PageEntry;
> +  PAGE_ATTRIBUTE                    PageAttribute;
> +  UINTN                             PageEntryLength;
> +  PAGE_ATTRIBUTE                    SplitAttribute;
> +  RETURN_STATUS                     Status;
> +  BOOLEAN                           IsEntryModified;
> +
> +  if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
> +    DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n",
> BaseAddress));
> +    return EFI_UNSUPPORTED;
> +  }
> +  if ((Length & (SIZE_4KB - 1)) != 0) {
> +    DEBUG ((DEBUG_ERROR, "Length(0x%lx) is not aligned!\n", Length));
> +    return EFI_UNSUPPORTED;
> +  }
> +  if (Length == 0) {
> +    DEBUG ((DEBUG_ERROR, "Length is 0!\n"));
> +    return RETURN_INVALID_PARAMETER;
> +  }
> +
> +  if ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO |
> EFI_MEMORY_XP)) != 0) {
> +    DEBUG ((DEBUG_ERROR, "Attributes(0x%lx) has unsupported bit\n",
> Attributes));
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  if (PagingContext == NULL) {
> +    GetCurrentPagingContext (&CurrentPagingContext);
> +  } else {
> +    CopyMem (&CurrentPagingContext, PagingContext,
> sizeof(CurrentPagingContext));
> +  }
> +  switch(CurrentPagingContext.MachineType) {
> +  case IMAGE_FILE_MACHINE_I386:
> +    if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) {
> +      DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));
> +      if (Attributes == 0) {
> +        return EFI_SUCCESS;
> +      } else {
> +        return EFI_UNSUPPORTED;
> +      }
> +    }
> +    if ((CurrentPagingContext.ContextData.Ia32.Attributes &
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) {
> +      DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));
> +      return EFI_UNSUPPORTED;
> +    }
> +    break;
> +  case IMAGE_FILE_MACHINE_X64:
> +    ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);
> +    break;
> +  default:
> +    ASSERT(FALSE);
> +    return EFI_UNSUPPORTED;
> +    break;
> +  }
> +
> +//  DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x)
> - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
> +
> +  if (IsSplitted != NULL) {
> +    *IsSplitted = FALSE;
> +  }
> +  if (IsModified != NULL) {
> +    *IsModified = FALSE;
> +  }
> +
> +  //
> +  // Below logic is to check 2M/4K page to make sure we donot waist memory.
> +  //
> +  while (Length != 0) {
> +    PageEntry = GetPageTableEntry (&CurrentPagingContext, BaseAddress,
> &PageAttribute);
> +    if (PageEntry == NULL) {
> +      return RETURN_UNSUPPORTED;
> +    }
> +    PageEntryLength = PageAttributeToLength (PageAttribute);
> +    SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry,
> PageAttribute);
> +    if (SplitAttribute == PageNone) {
> +      ConvertPageEntryAttribute (&CurrentPagingContext, PageEntry,
> Attributes, PageAction, &IsEntryModified);
> +      if (IsEntryModified) {
> +        if (IsModified != NULL) {
> +          *IsModified = TRUE;
> +        }
> +      }
> +      //
> +      // Convert success, move to next
> +      //
> +      BaseAddress += PageEntryLength;
> +      Length -= PageEntryLength;
> +    } else {
> +      if (AllocatePagesFunc == NULL) {
> +        return RETURN_UNSUPPORTED;
> +      }
> +      Status = SplitPage (PageEntry, PageAttribute, SplitAttribute,
> AllocatePagesFunc);
> +      if (RETURN_ERROR (Status)) {
> +        return RETURN_UNSUPPORTED;
> +      }
> +      if (IsSplitted != NULL) {
> +        *IsSplitted = TRUE;
> +      }
> +      if (IsModified != NULL) {
> +        *IsModified = TRUE;
> +      }
> +      //
> +      // Just split current page
> +      // Convert success in next around
> +      //
> +    }
> +  }
> +
> +  return RETURN_SUCCESS;
> +}
> +
> +/**
> +  This function assigns 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.
> +
> +  Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
> +
> +  @param[in]  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param[in]  BaseAddress       The physical address that is the start
> address of a memory region.
> +  @param[in]  Length            The size in bytes of the memory region.
> +  @param[in]  Attributes        The bit mask of attributes to set for the
> memory region.
> +  @param[in]  AllocatePagesFunc If page split is needed, this function is used
> to allocate more pages.
> +                                NULL mean page split is unsupported.
> +
> +  @retval RETURN_SUCCESS           The attributes were cleared for the
> memory region.
> +  @retval RETURN_ACCESS_DENIED     The attributes for the memory
> resource range specified by
> +                                   BaseAddress and Length cannot be
> modified.
> +  @retval RETURN_INVALID_PARAMETER Length is zero.
> +                                   Attributes specified an illegal
> combination of attributes that
> +                                   cannot be set together.
> +  @retval RETURN_OUT_OF_RESOURCES  There are not enough system
> resources to modify the attributes of
> +                                   the memory resource range.
> +  @retval RETURN_UNSUPPORTED       The processor does not support one
> or more bytes of the memory
> +                                   resource range specified by
> BaseAddress and Length.
> +                                   The bit mask of attributes is not
> support for the memory resource
> +                                   range specified by BaseAddress and
> Length.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +AssignMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc OPTIONAL
> +  )
> +{
> +  RETURN_STATUS  Status;
> +  BOOLEAN        IsModified;
> +  BOOLEAN        IsSplitted;
> +
> +//  DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx
> (0x%lx)\n", BaseAddress, Length, Attributes));
> +  Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress,
> Length, Attributes, PageActionAssign, AllocatePagesFunc, &IsSplitted,
> &IsModified);
> +  if (!EFI_ERROR(Status)) {
> +    if ((PagingContext == NULL) && IsModified) {
> +      //
> +      // Flush TLB as last step
> +      //
> +      CpuFlushTlb();
> +      SyncMemoryPageAttributesAp (SyncCpuFlushTlb);
> +    }
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  This function sets 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.
> +
> +  Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
> +
> +  @param[in]  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param[in]  BaseAddress       The physical address that is the start
> address of a memory region.
> +  @param[in]  Length            The size in bytes of the memory region.
> +  @param[in]  Attributes        The bit mask of attributes to set for the
> memory region.
> +  @param[in]  AllocatePagesFunc If page split is needed, this function is used
> to allocate more pages.
> +                                NULL mean page split is unsupported.
> +
> +  @retval RETURN_SUCCESS           The attributes were set for the
> memory region.
> +  @retval RETURN_ACCESS_DENIED     The attributes for the memory
> resource range specified by
> +                                   BaseAddress and Length cannot be
> modified.
> +  @retval RETURN_INVALID_PARAMETER Length is zero.
> +                                   Attributes specified an illegal
> combination of attributes that
> +                                   cannot be set together.
> +  @retval RETURN_OUT_OF_RESOURCES  There are not enough system
> resources to modify the attributes of
> +                                   the memory resource range.
> +  @retval RETURN_UNSUPPORTED       The processor does not support one
> or more bytes of the memory
> +                                   resource range specified by
> BaseAddress and Length.
> +                                   The bit mask of attributes is not
> support for the memory resource
> +                                   range specified by BaseAddress and
> Length.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +SetMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc OPTIONAL
> +  )
> +{
> +  RETURN_STATUS  Status;
> +  BOOLEAN        IsModified;
> +  BOOLEAN        IsSplitted;
> +
> +//  DEBUG((DEBUG_INFO, "SetMemoryPageAttributes: 0x%lx - 0x%lx
> (0x%lx)\n", BaseAddress, Length, Attributes));
> +  Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress,
> Length, Attributes, PageActionSet, AllocatePagesFunc, &IsSplitted, &IsModified);
> +  if (!EFI_ERROR(Status)) {
> +    if ((PagingContext == NULL) && IsModified) {
> +      //
> +      // Flush TLB as last step
> +      //
> +      CpuFlushTlb();
> +      SyncMemoryPageAttributesAp (SyncCpuFlushTlb);
> +    }
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  This function clears 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.
> +
> +  Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
> +
> +  @param[in]  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param[in]  BaseAddress       The physical address that is the start
> address of a memory region.
> +  @param[in]  Length            The size in bytes of the memory region.
> +  @param[in]  Attributes        The bit mask of attributes to set for the
> memory region.
> +  @param[in]  AllocatePagesFunc If page split is needed, this function is used
> to allocate more pages.
> +                                NULL mean page split is unsupported.
> +
> +  @retval RETURN_SUCCESS           The attributes were cleared for the
> memory region.
> +  @retval RETURN_ACCESS_DENIED     The attributes for the memory
> resource range specified by
> +                                   BaseAddress and Length cannot be
> modified.
> +  @retval RETURN_INVALID_PARAMETER Length is zero.
> +                                   Attributes specified an illegal
> combination of attributes that
> +                                   cannot be set together.
> +  @retval RETURN_OUT_OF_RESOURCES  There are not enough system
> resources to modify the attributes of
> +                                   the memory resource range.
> +  @retval RETURN_UNSUPPORTED       The processor does not support one
> or more bytes of the memory
> +                                   resource range specified by
> BaseAddress and Length.
> +                                   The bit mask of attributes is not
> support for the memory resource
> +                                   range specified by BaseAddress and
> Length.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +ClearMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  IN  UINT64                            Length,
> +  IN  UINT64                            Attributes,
> +  IN  PAGE_TABLE_LIB_ALLOCATE_PAGES     AllocatePagesFunc OPTIONAL
> +  )
> +{
> +  RETURN_STATUS  Status;
> +  BOOLEAN        IsModified;
> +  BOOLEAN        IsSplitted;
> +
> +//  DEBUG((DEBUG_INFO, "ClearMemoryPageAttributes: 0x%lx - 0x%lx
> (0x%lx)\n", BaseAddress, Length, Attributes));
> +  Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress,
> Length, Attributes, PageActionClear, AllocatePagesFunc, &IsSplitted,
> &IsModified);
> +  if (!EFI_ERROR(Status)) {
> +    if ((PagingContext == NULL) && IsModified) {
> +      //
> +      // Flush TLB as last step
> +      //
> +      CpuFlushTlb();
> +      SyncMemoryPageAttributesAp (SyncCpuFlushTlb);
> +    }
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  This function return the page attributes for the memory region specified by
> BaseAddress.
> +
> +  Caller should make sure BaseAddress is at page boundary.
> +
> +  @param[in]  PagingContext     The paging context. NULL means get page
> table from current CPU context.
> +  @param[in]  BaseAddress       The physical address that is the start
> address of a memory region.
> +  @param[out] Attributes        The bit mask of attributes of the memory
> region.
> +  @param[out] PageSize          The size of the pages which contains the
> BaseAddress.
> +
> +  @retval RETURN_SUCCESS           The Attributes and PageSize is
> returned.
> +  @retval RETURN_INVALID_PARAMETER Both Attributes and PageSize are
> zero.
> +  @retval RETURN_NOT_FOUND         The processor does not setup
> paging for BaseAddress.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +GetMemoryPageAttributes (
> +  IN  PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext OPTIONAL,
> +  IN  PHYSICAL_ADDRESS                  BaseAddress,
> +  OUT UINT64                            *Attributes,
> +  OUT UINT64                            *PageSize
> +  )
> +{
> +  PAGE_TABLE_LIB_PAGING_CONTEXT     CurrentPagingContext;
> +  PAGE_ATTRIBUTE                    PageAttribute;
> +  UINT64                            *PageEntry;
> +
> +  if (Attributes == NULL && PageSize == NULL) {
> +    DEBUG ((DEBUG_ERROR, "Attributes and PageSize are NULL!\n"));
> +    return RETURN_INVALID_PARAMETER;
> +  }
> +
> +  if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
> +    DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n",
> BaseAddress));
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +//  DEBUG((DEBUG_INFO, "GetMemoryPageAttributes: 0x%lx\n",
> BaseAddress));
> +  if (PagingContext == NULL) {
> +    GetCurrentPagingContext (&CurrentPagingContext);
> +  } else {
> +    CopyMem (&CurrentPagingContext, PagingContext,
> sizeof(CurrentPagingContext));
> +  }
> +  switch(CurrentPagingContext.MachineType) {
> +  case IMAGE_FILE_MACHINE_I386:
> +    if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) {
> +      DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));
> +      if (Attributes != NULL) {
> +        *Attributes = 0;
> +      }
> +      if (PageSize != NULL) {
> +        *PageSize = 0;
> +      }
> +      return EFI_SUCCESS;
> +    }
> +    if ((CurrentPagingContext.ContextData.Ia32.Attributes &
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) {
> +      DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));
> +      return EFI_UNSUPPORTED;
> +    }
> +    break;
> +  case IMAGE_FILE_MACHINE_X64:
> +    ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);
> +    break;
> +  default:
> +    ASSERT(FALSE);
> +    return EFI_UNSUPPORTED;
> +    break;
> +  }
> +
> +  PageEntry = GetPageTableEntry(&CurrentPagingContext, BaseAddress,
> &PageAttribute);
> +  if (PageEntry == NULL) {
> +    return RETURN_NOT_FOUND;
> +  }
> +
> +  if (Attributes != NULL) {
> +    *Attributes = 0;
> +    if ((*PageEntry & IA32_PG_NX) != 0) {
> +      *Attributes |= EFI_MEMORY_XP;
> +    }
> +    if ((*PageEntry & IA32_PG_RW) == 0) {
> +      *Attributes |= EFI_MEMORY_RO;
> +    }
> +    if ((*PageEntry & IA32_PG_P) == 0) {
> +      *Attributes |= EFI_MEMORY_RP;
> +    }
> +  }
> +
> +  if (PageSize != NULL) {
> +    *PageSize = PageAttributeToLength(PageAttribute);
> +  }
> +
> +  return RETURN_SUCCESS;
> +}
> +
> +/**
> +  Initialize the Page Table lib.
> +
> +  @param ImageHandle     Image handle this driver.
> +  @param SystemTable     Pointer to the System Table.
> +
> +  @retval EFI_SUCCESS           Thread can be successfully created
> +  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
> +  @retval EFI_DEVICE_ERROR      Cannot create the thread
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializePageTableLib (
> +  IN EFI_HANDLE                            ImageHandle,
> +  IN EFI_SYSTEM_TABLE                      *SystemTable
> +  )
> +{
> +  PAGE_TABLE_LIB_PAGING_CONTEXT     CurrentPagingContext;
> +
> +  GetCurrentPagingContext (&CurrentPagingContext);
> +  DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n",
> CurrentPagingContext.MachineType));
> +  DEBUG ((DEBUG_INFO, "  MachineType   - 0x%x\n",
> CurrentPagingContext.MachineType));
> +  DEBUG ((DEBUG_INFO, "  PageTableBase - 0x%x\n",
> CurrentPagingContext.ContextData.X64.PageTableBase));
> +  DEBUG ((DEBUG_INFO, "  Attributes    - 0x%x\n",
> CurrentPagingContext.ContextData.X64.Attributes));
> +
> +  return EFI_SUCCESS;
> +}
> +
> --
> 2.7.4.windows.1



  reply	other threads:[~2017-02-06  5:26 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-26  8:39 [PATCH 0/3] DXE Memory Protection Jiewen Yao
2017-01-26  8:39 ` [PATCH 1/3] UefiCpuPkg/CpuDxe: Add memory attribute setting Jiewen Yao
2017-02-03  8:51   ` Gao, Liming
2017-02-04  0:42     ` Yao, Jiewen
2017-02-06  3:27   ` Fan, Jeff
2017-02-06  3:58     ` Yao, Jiewen
2017-02-06  5:26       ` Fan, Jeff [this message]
2017-02-06  5:28         ` Yao, Jiewen
2017-01-26  8:39 ` [PATCH 2/3] ArmPkg/CpuDxe: Correct EFI_MEMORY_RO usage Jiewen Yao
2017-01-26 12:11   ` Leif Lindholm
2017-01-26 12:17     ` Yao, Jiewen
2017-01-26  8:39 ` [PATCH 3/3] MdeModulePkg/DxeCore: Add UEFI image protection Jiewen Yao
2017-02-03  8:39   ` Gao, Liming
2017-02-04  0:41     ` Yao, Jiewen
2017-02-04  3:39       ` Gao, Liming
2017-02-06  1:07         ` Tian, Feng
2017-02-06  1:39           ` Yao, Jiewen
2017-02-06 14:34   ` Ard Biesheuvel
2017-02-06 14:51     ` Yao, Jiewen
2017-02-06 14:52       ` Ard Biesheuvel
2017-02-06 17:52         ` Kinney, Michael D

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=542CF652F8836A4AB8DBFAAD40ED192A4C52BF57@shsmsx102.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