diff options
Diffstat (limited to 'MdeModulePkg/Core/Dxe/Mem')
| -rw-r--r-- | MdeModulePkg/Core/Dxe/Mem/HeapGuard.c | 3510 | ||||
| -rw-r--r-- | MdeModulePkg/Core/Dxe/Mem/HeapGuard.h | 972 | ||||
| -rw-r--r-- | MdeModulePkg/Core/Dxe/Mem/Imem.h | 344 | ||||
| -rw-r--r-- | MdeModulePkg/Core/Dxe/Mem/MemData.c | 38 | ||||
| -rw-r--r-- | MdeModulePkg/Core/Dxe/Mem/MemoryProfileRecord.c | 3584 | ||||
| -rw-r--r-- | MdeModulePkg/Core/Dxe/Mem/Page.c | 4616 | ||||
| -rw-r--r-- | MdeModulePkg/Core/Dxe/Mem/Pool.c | 1764 |
7 files changed, 7414 insertions, 7414 deletions
diff --git a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c index 0c0ca61872..0f5cfaeff1 100644 --- a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c +++ b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c @@ -1,1755 +1,1755 @@ -/** @file
- UEFI Heap Guard functions.
-
-Copyright (c) 2017-2018, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "DxeMain.h"
-#include "Imem.h"
-#include "HeapGuard.h"
-
-//
-// Global to avoid infinite reentrance of memory allocation when updating
-// page table attributes, which may need allocate pages for new PDE/PTE.
-//
-GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mOnGuarding = FALSE;
-
-//
-// Pointer to table tracking the Guarded memory with bitmap, in which '1'
-// is used to indicate memory guarded. '0' might be free memory or Guard
-// page itself, depending on status of memory adjacent to it.
-//
-GLOBAL_REMOVE_IF_UNREFERENCED UINT64 mGuardedMemoryMap = 0;
-
-//
-// Current depth level of map table pointed by mGuardedMemoryMap.
-// mMapLevel must be initialized at least by 1. It will be automatically
-// updated according to the address of memory just tracked.
-//
-GLOBAL_REMOVE_IF_UNREFERENCED UINTN mMapLevel = 1;
-
-//
-// Shift and mask for each level of map table
-//
-GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelShift[GUARDED_HEAP_MAP_TABLE_DEPTH]
- = GUARDED_HEAP_MAP_TABLE_DEPTH_SHIFTS;
-GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelMask[GUARDED_HEAP_MAP_TABLE_DEPTH]
- = GUARDED_HEAP_MAP_TABLE_DEPTH_MASKS;
-
-//
-// Used for promoting freed but not used pages.
-//
-GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS mLastPromotedPage = BASE_4GB;
-
-/**
- Set corresponding bits in bitmap table to 1 according to the address.
-
- @param[in] Address Start address to set for.
- @param[in] BitNumber Number of bits to set.
- @param[in] BitMap Pointer to bitmap which covers the Address.
-
- @return VOID.
-**/
-STATIC
-VOID
-SetBits (
- IN EFI_PHYSICAL_ADDRESS Address,
- IN UINTN BitNumber,
- IN UINT64 *BitMap
- )
-{
- UINTN Lsbs;
- UINTN Qwords;
- UINTN Msbs;
- UINTN StartBit;
- UINTN EndBit;
-
- StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address);
- EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS;
-
- if ((StartBit + BitNumber) >= GUARDED_HEAP_MAP_ENTRY_BITS) {
- Msbs = (GUARDED_HEAP_MAP_ENTRY_BITS - StartBit) %
- GUARDED_HEAP_MAP_ENTRY_BITS;
- Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS;
- Qwords = (BitNumber - Msbs) / GUARDED_HEAP_MAP_ENTRY_BITS;
- } else {
- Msbs = BitNumber;
- Lsbs = 0;
- Qwords = 0;
- }
-
- if (Msbs > 0) {
- *BitMap |= LShiftU64 (LShiftU64 (1, Msbs) - 1, StartBit);
- BitMap += 1;
- }
-
- if (Qwords > 0) {
- SetMem64 (
- (VOID *)BitMap,
- Qwords * GUARDED_HEAP_MAP_ENTRY_BYTES,
- (UINT64)-1
- );
- BitMap += Qwords;
- }
-
- if (Lsbs > 0) {
- *BitMap |= (LShiftU64 (1, Lsbs) - 1);
- }
-}
-
-/**
- Set corresponding bits in bitmap table to 0 according to the address.
-
- @param[in] Address Start address to set for.
- @param[in] BitNumber Number of bits to set.
- @param[in] BitMap Pointer to bitmap which covers the Address.
-
- @return VOID.
-**/
-STATIC
-VOID
-ClearBits (
- IN EFI_PHYSICAL_ADDRESS Address,
- IN UINTN BitNumber,
- IN UINT64 *BitMap
- )
-{
- UINTN Lsbs;
- UINTN Qwords;
- UINTN Msbs;
- UINTN StartBit;
- UINTN EndBit;
-
- StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address);
- EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS;
-
- if ((StartBit + BitNumber) >= GUARDED_HEAP_MAP_ENTRY_BITS) {
- Msbs = (GUARDED_HEAP_MAP_ENTRY_BITS - StartBit) %
- GUARDED_HEAP_MAP_ENTRY_BITS;
- Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS;
- Qwords = (BitNumber - Msbs) / GUARDED_HEAP_MAP_ENTRY_BITS;
- } else {
- Msbs = BitNumber;
- Lsbs = 0;
- Qwords = 0;
- }
-
- if (Msbs > 0) {
- *BitMap &= ~LShiftU64 (LShiftU64 (1, Msbs) - 1, StartBit);
- BitMap += 1;
- }
-
- if (Qwords > 0) {
- SetMem64 ((VOID *)BitMap, Qwords * GUARDED_HEAP_MAP_ENTRY_BYTES, 0);
- BitMap += Qwords;
- }
-
- if (Lsbs > 0) {
- *BitMap &= ~(LShiftU64 (1, Lsbs) - 1);
- }
-}
-
-/**
- Get corresponding bits in bitmap table according to the address.
-
- The value of bit 0 corresponds to the status of memory at given Address.
- No more than 64 bits can be retrieved in one call.
-
- @param[in] Address Start address to retrieve bits for.
- @param[in] BitNumber Number of bits to get.
- @param[in] BitMap Pointer to bitmap which covers the Address.
-
- @return An integer containing the bits information.
-**/
-STATIC
-UINT64
-GetBits (
- IN EFI_PHYSICAL_ADDRESS Address,
- IN UINTN BitNumber,
- IN UINT64 *BitMap
- )
-{
- UINTN StartBit;
- UINTN EndBit;
- UINTN Lsbs;
- UINTN Msbs;
- UINT64 Result;
-
- ASSERT (BitNumber <= GUARDED_HEAP_MAP_ENTRY_BITS);
-
- StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address);
- EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS;
-
- if ((StartBit + BitNumber) > GUARDED_HEAP_MAP_ENTRY_BITS) {
- Msbs = GUARDED_HEAP_MAP_ENTRY_BITS - StartBit;
- Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS;
- } else {
- Msbs = BitNumber;
- Lsbs = 0;
- }
-
- if ((StartBit == 0) && (BitNumber == GUARDED_HEAP_MAP_ENTRY_BITS)) {
- Result = *BitMap;
- } else {
- Result = RShiftU64 ((*BitMap), StartBit) & (LShiftU64 (1, Msbs) - 1);
- if (Lsbs > 0) {
- BitMap += 1;
- Result |= LShiftU64 ((*BitMap) & (LShiftU64 (1, Lsbs) - 1), Msbs);
- }
- }
-
- return Result;
-}
-
-/**
- Locate the pointer of bitmap from the guarded memory bitmap tables, which
- covers the given Address.
-
- @param[in] Address Start address to search the bitmap for.
- @param[in] AllocMapUnit Flag to indicate memory allocation for the table.
- @param[out] BitMap Pointer to bitmap which covers the Address.
-
- @return The bit number from given Address to the end of current map table.
-**/
-UINTN
-FindGuardedMemoryMap (
- IN EFI_PHYSICAL_ADDRESS Address,
- IN BOOLEAN AllocMapUnit,
- OUT UINT64 **BitMap
- )
-{
- UINTN Level;
- UINT64 *GuardMap;
- UINT64 MapMemory;
- UINTN Index;
- UINTN Size;
- UINTN BitsToUnitEnd;
- EFI_STATUS Status;
-
- MapMemory = 0;
-
- //
- // Adjust current map table depth according to the address to access
- //
- while (AllocMapUnit &&
- mMapLevel < GUARDED_HEAP_MAP_TABLE_DEPTH &&
- RShiftU64 (
- Address,
- mLevelShift[GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel - 1]
- ) != 0)
- {
- if (mGuardedMemoryMap != 0) {
- Size = (mLevelMask[GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel - 1] + 1)
- * GUARDED_HEAP_MAP_ENTRY_BYTES;
- Status = CoreInternalAllocatePages (
- AllocateAnyPages,
- EfiBootServicesData,
- EFI_SIZE_TO_PAGES (Size),
- &MapMemory,
- FALSE
- );
- ASSERT_EFI_ERROR (Status);
- ASSERT (MapMemory != 0);
-
- SetMem ((VOID *)(UINTN)MapMemory, Size, 0);
-
- *(UINT64 *)(UINTN)MapMemory = mGuardedMemoryMap;
- mGuardedMemoryMap = MapMemory;
- }
-
- mMapLevel++;
- }
-
- GuardMap = &mGuardedMemoryMap;
- for (Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel;
- Level < GUARDED_HEAP_MAP_TABLE_DEPTH;
- ++Level)
- {
- if (*GuardMap == 0) {
- if (!AllocMapUnit) {
- GuardMap = NULL;
- break;
- }
-
- Size = (mLevelMask[Level] + 1) * GUARDED_HEAP_MAP_ENTRY_BYTES;
- Status = CoreInternalAllocatePages (
- AllocateAnyPages,
- EfiBootServicesData,
- EFI_SIZE_TO_PAGES (Size),
- &MapMemory,
- FALSE
- );
- ASSERT_EFI_ERROR (Status);
- ASSERT (MapMemory != 0);
-
- SetMem ((VOID *)(UINTN)MapMemory, Size, 0);
- *GuardMap = MapMemory;
- }
-
- Index = (UINTN)RShiftU64 (Address, mLevelShift[Level]);
- Index &= mLevelMask[Level];
- GuardMap = (UINT64 *)(UINTN)((*GuardMap) + Index * sizeof (UINT64));
- }
-
- BitsToUnitEnd = GUARDED_HEAP_MAP_BITS - GUARDED_HEAP_MAP_BIT_INDEX (Address);
- *BitMap = GuardMap;
-
- return BitsToUnitEnd;
-}
-
-/**
- Set corresponding bits in bitmap table to 1 according to given memory range.
-
- @param[in] Address Memory address to guard from.
- @param[in] NumberOfPages Number of pages to guard.
-
- @return VOID.
-**/
-VOID
-EFIAPI
-SetGuardedMemoryBits (
- IN EFI_PHYSICAL_ADDRESS Address,
- IN UINTN NumberOfPages
- )
-{
- UINT64 *BitMap;
- UINTN Bits;
- UINTN BitsToUnitEnd;
-
- while (NumberOfPages > 0) {
- BitsToUnitEnd = FindGuardedMemoryMap (Address, TRUE, &BitMap);
- ASSERT (BitMap != NULL);
-
- if (NumberOfPages > BitsToUnitEnd) {
- // Cross map unit
- Bits = BitsToUnitEnd;
- } else {
- Bits = NumberOfPages;
- }
-
- SetBits (Address, Bits, BitMap);
-
- NumberOfPages -= Bits;
- Address += EFI_PAGES_TO_SIZE (Bits);
- }
-}
-
-/**
- Clear corresponding bits in bitmap table according to given memory range.
-
- @param[in] Address Memory address to unset from.
- @param[in] NumberOfPages Number of pages to unset guard.
-
- @return VOID.
-**/
-VOID
-EFIAPI
-ClearGuardedMemoryBits (
- IN EFI_PHYSICAL_ADDRESS Address,
- IN UINTN NumberOfPages
- )
-{
- UINT64 *BitMap;
- UINTN Bits;
- UINTN BitsToUnitEnd;
-
- while (NumberOfPages > 0) {
- BitsToUnitEnd = FindGuardedMemoryMap (Address, TRUE, &BitMap);
- ASSERT (BitMap != NULL);
-
- if (NumberOfPages > BitsToUnitEnd) {
- // Cross map unit
- Bits = BitsToUnitEnd;
- } else {
- Bits = NumberOfPages;
- }
-
- ClearBits (Address, Bits, BitMap);
-
- NumberOfPages -= Bits;
- Address += EFI_PAGES_TO_SIZE (Bits);
- }
-}
-
-/**
- Retrieve corresponding bits in bitmap table according to given memory range.
-
- @param[in] Address Memory address to retrieve from.
- @param[in] NumberOfPages Number of pages to retrieve.
-
- @return An integer containing the guarded memory bitmap.
-**/
-UINT64
-GetGuardedMemoryBits (
- IN EFI_PHYSICAL_ADDRESS Address,
- IN UINTN NumberOfPages
- )
-{
- UINT64 *BitMap;
- UINTN Bits;
- UINT64 Result;
- UINTN Shift;
- UINTN BitsToUnitEnd;
-
- ASSERT (NumberOfPages <= GUARDED_HEAP_MAP_ENTRY_BITS);
-
- Result = 0;
- Shift = 0;
- while (NumberOfPages > 0) {
- BitsToUnitEnd = FindGuardedMemoryMap (Address, FALSE, &BitMap);
-
- if (NumberOfPages > BitsToUnitEnd) {
- // Cross map unit
- Bits = BitsToUnitEnd;
- } else {
- Bits = NumberOfPages;
- }
-
- if (BitMap != NULL) {
- Result |= LShiftU64 (GetBits (Address, Bits, BitMap), Shift);
- }
-
- Shift += Bits;
- NumberOfPages -= Bits;
- Address += EFI_PAGES_TO_SIZE (Bits);
- }
-
- return Result;
-}
-
-/**
- Get bit value in bitmap table for the given address.
-
- @param[in] Address The address to retrieve for.
-
- @return 1 or 0.
-**/
-UINTN
-EFIAPI
-GetGuardMapBit (
- IN EFI_PHYSICAL_ADDRESS Address
- )
-{
- UINT64 *GuardMap;
-
- FindGuardedMemoryMap (Address, FALSE, &GuardMap);
- if (GuardMap != NULL) {
- if (RShiftU64 (
- *GuardMap,
- GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address)
- ) & 1)
- {
- return 1;
- }
- }
-
- return 0;
-}
-
-/**
- Check to see if the page at the given address is a Guard page or not.
-
- @param[in] Address The address to check for.
-
- @return TRUE The page at Address is a Guard page.
- @return FALSE The page at Address is not a Guard page.
-**/
-BOOLEAN
-EFIAPI
-IsGuardPage (
- IN EFI_PHYSICAL_ADDRESS Address
- )
-{
- UINT64 BitMap;
-
- //
- // There must be at least one guarded page before and/or after given
- // address if it's a Guard page. The bitmap pattern should be one of
- // 001, 100 and 101
- //
- BitMap = GetGuardedMemoryBits (Address - EFI_PAGE_SIZE, 3);
- return ((BitMap == BIT0) || (BitMap == BIT2) || (BitMap == (BIT2 | BIT0)));
-}
-
-/**
- Check to see if the page at the given address is guarded or not.
-
- @param[in] Address The address to check for.
-
- @return TRUE The page at Address is guarded.
- @return FALSE The page at Address is not guarded.
-**/
-BOOLEAN
-EFIAPI
-IsMemoryGuarded (
- IN EFI_PHYSICAL_ADDRESS Address
- )
-{
- return (GetGuardMapBit (Address) == 1);
-}
-
-/**
- Set the page at the given address to be a Guard page.
-
- This is done by changing the page table attribute to be NOT PRSENT.
-
- @param[in] BaseAddress Page address to Guard at
-
- @return VOID
-**/
-VOID
-EFIAPI
-SetGuardPage (
- IN EFI_PHYSICAL_ADDRESS BaseAddress
- )
-{
- EFI_STATUS Status;
-
- if (gCpu == NULL) {
- return;
- }
-
- //
- // Set flag to make sure allocating memory without GUARD for page table
- // operation; otherwise infinite loops could be caused.
- //
- mOnGuarding = TRUE;
- //
- // Note: This might overwrite other attributes needed by other features,
- // such as NX memory protection.
- //
- Status = gCpu->SetMemoryAttributes (gCpu, BaseAddress, EFI_PAGE_SIZE, EFI_MEMORY_RP);
- ASSERT_EFI_ERROR (Status);
- mOnGuarding = FALSE;
-}
-
-/**
- Unset the Guard page at the given address to the normal memory.
-
- This is done by changing the page table attribute to be PRSENT.
-
- @param[in] BaseAddress Page address to Guard at.
-
- @return VOID.
-**/
-VOID
-EFIAPI
-UnsetGuardPage (
- IN EFI_PHYSICAL_ADDRESS BaseAddress
- )
-{
- UINT64 Attributes;
- EFI_STATUS Status;
-
- if (gCpu == NULL) {
- return;
- }
-
- //
- // Once the Guard page is unset, it will be freed back to memory pool. NX
- // memory protection must be restored for this page if NX is enabled for free
- // memory.
- //
- Attributes = 0;
- if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & (1 << EfiConventionalMemory)) != 0) {
- Attributes |= EFI_MEMORY_XP;
- }
-
- //
- // Set flag to make sure allocating memory without GUARD for page table
- // operation; otherwise infinite loops could be caused.
- //
- mOnGuarding = TRUE;
- //
- // Note: This might overwrite other attributes needed by other features,
- // such as memory protection (NX). Please make sure they are not enabled
- // at the same time.
- //
- Status = gCpu->SetMemoryAttributes (gCpu, BaseAddress, EFI_PAGE_SIZE, Attributes);
- ASSERT_EFI_ERROR (Status);
- mOnGuarding = FALSE;
-}
-
-/**
- Check to see if the memory at the given address should be guarded or not.
-
- @param[in] MemoryType Memory type to check.
- @param[in] AllocateType Allocation type to check.
- @param[in] PageOrPool Indicate a page allocation or pool allocation.
-
-
- @return TRUE The given type of memory should be guarded.
- @return FALSE The given type of memory should not be guarded.
-**/
-BOOLEAN
-IsMemoryTypeToGuard (
- IN EFI_MEMORY_TYPE MemoryType,
- IN EFI_ALLOCATE_TYPE AllocateType,
- IN UINT8 PageOrPool
- )
-{
- UINT64 TestBit;
- UINT64 ConfigBit;
-
- if (AllocateType == AllocateAddress) {
- return FALSE;
- }
-
- if ((PcdGet8 (PcdHeapGuardPropertyMask) & PageOrPool) == 0) {
- return FALSE;
- }
-
- if (PageOrPool == GUARD_HEAP_TYPE_POOL) {
- ConfigBit = PcdGet64 (PcdHeapGuardPoolType);
- } else if (PageOrPool == GUARD_HEAP_TYPE_PAGE) {
- ConfigBit = PcdGet64 (PcdHeapGuardPageType);
- } else {
- ConfigBit = (UINT64)-1;
- }
-
- if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
- TestBit = BIT63;
- } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
- TestBit = BIT62;
- } else if (MemoryType < EfiMaxMemoryType) {
- TestBit = LShiftU64 (1, MemoryType);
- } else if (MemoryType == EfiMaxMemoryType) {
- TestBit = (UINT64)-1;
- } else {
- TestBit = 0;
- }
-
- return ((ConfigBit & TestBit) != 0);
-}
-
-/**
- Check to see if the pool at the given address should be guarded or not.
-
- @param[in] MemoryType Pool type to check.
-
-
- @return TRUE The given type of pool should be guarded.
- @return FALSE The given type of pool should not be guarded.
-**/
-BOOLEAN
-IsPoolTypeToGuard (
- IN EFI_MEMORY_TYPE MemoryType
- )
-{
- return IsMemoryTypeToGuard (
- MemoryType,
- AllocateAnyPages,
- GUARD_HEAP_TYPE_POOL
- );
-}
-
-/**
- Check to see if the page at the given address should be guarded or not.
-
- @param[in] MemoryType Page type to check.
- @param[in] AllocateType Allocation type to check.
-
- @return TRUE The given type of page should be guarded.
- @return FALSE The given type of page should not be guarded.
-**/
-BOOLEAN
-IsPageTypeToGuard (
- IN EFI_MEMORY_TYPE MemoryType,
- IN EFI_ALLOCATE_TYPE AllocateType
- )
-{
- return IsMemoryTypeToGuard (MemoryType, AllocateType, GUARD_HEAP_TYPE_PAGE);
-}
-
-/**
- Check to see if the heap guard is enabled for page and/or pool allocation.
-
- @param[in] GuardType Specify the sub-type(s) of Heap Guard.
-
- @return TRUE/FALSE.
-**/
-BOOLEAN
-IsHeapGuardEnabled (
- UINT8 GuardType
- )
-{
- return IsMemoryTypeToGuard (EfiMaxMemoryType, AllocateAnyPages, GuardType);
-}
-
-/**
- Set head Guard and tail Guard for the given memory range.
-
- @param[in] Memory Base address of memory to set guard for.
- @param[in] NumberOfPages Memory size in pages.
-
- @return VOID
-**/
-VOID
-SetGuardForMemory (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages
- )
-{
- EFI_PHYSICAL_ADDRESS GuardPage;
-
- //
- // Set tail Guard
- //
- GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages);
- if (!IsGuardPage (GuardPage)) {
- SetGuardPage (GuardPage);
- }
-
- // Set head Guard
- GuardPage = Memory - EFI_PAGES_TO_SIZE (1);
- if (!IsGuardPage (GuardPage)) {
- SetGuardPage (GuardPage);
- }
-
- //
- // Mark the memory range as Guarded
- //
- SetGuardedMemoryBits (Memory, NumberOfPages);
-}
-
-/**
- Unset head Guard and tail Guard for the given memory range.
-
- @param[in] Memory Base address of memory to unset guard for.
- @param[in] NumberOfPages Memory size in pages.
-
- @return VOID
-**/
-VOID
-UnsetGuardForMemory (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages
- )
-{
- EFI_PHYSICAL_ADDRESS GuardPage;
- UINT64 GuardBitmap;
-
- if (NumberOfPages == 0) {
- return;
- }
-
- //
- // Head Guard must be one page before, if any.
- //
- // MSB-> 1 0 <-LSB
- // -------------------
- // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard)
- // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard)
- // 1 X -> Don't free first page (need a new Guard)
- // (it'll be turned into a Guard page later)
- // -------------------
- // Start -> -1 -2
- //
- GuardPage = Memory - EFI_PAGES_TO_SIZE (1);
- GuardBitmap = GetGuardedMemoryBits (Memory - EFI_PAGES_TO_SIZE (2), 2);
- if ((GuardBitmap & BIT1) == 0) {
- //
- // Head Guard exists.
- //
- if ((GuardBitmap & BIT0) == 0) {
- //
- // If the head Guard is not a tail Guard of adjacent memory block,
- // unset it.
- //
- UnsetGuardPage (GuardPage);
- }
- } else {
- //
- // Pages before memory to free are still in Guard. It's a partial free
- // case. Turn first page of memory block to free into a new Guard.
- //
- SetGuardPage (Memory);
- }
-
- //
- // Tail Guard must be the page after this memory block to free, if any.
- //
- // MSB-> 1 0 <-LSB
- // --------------------
- // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard)
- // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard)
- // X 1 -> Don't free last page (need a new Guard)
- // (it'll be turned into a Guard page later)
- // --------------------
- // +1 +0 <- End
- //
- GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages);
- GuardBitmap = GetGuardedMemoryBits (GuardPage, 2);
- if ((GuardBitmap & BIT0) == 0) {
- //
- // Tail Guard exists.
- //
- if ((GuardBitmap & BIT1) == 0) {
- //
- // If the tail Guard is not a head Guard of adjacent memory block,
- // free it; otherwise, keep it.
- //
- UnsetGuardPage (GuardPage);
- }
- } else {
- //
- // Pages after memory to free are still in Guard. It's a partial free
- // case. We need to keep one page to be a head Guard.
- //
- SetGuardPage (GuardPage - EFI_PAGES_TO_SIZE (1));
- }
-
- //
- // No matter what, we just clear the mark of the Guarded memory.
- //
- ClearGuardedMemoryBits (Memory, NumberOfPages);
-}
-
-/**
- Adjust address of free memory according to existing and/or required Guard.
-
- This function will check if there're existing Guard pages of adjacent
- memory blocks, and try to use it as the Guard page of the memory to be
- allocated.
-
- @param[in] Start Start address of free memory block.
- @param[in] Size Size of free memory block.
- @param[in] SizeRequested Size of memory to allocate.
-
- @return The end address of memory block found.
- @return 0 if no enough space for the required size of memory and its Guard.
-**/
-UINT64
-AdjustMemoryS (
- IN UINT64 Start,
- IN UINT64 Size,
- IN UINT64 SizeRequested
- )
-{
- UINT64 Target;
-
- //
- // UEFI spec requires that allocated pool must be 8-byte aligned. If it's
- // indicated to put the pool near the Tail Guard, we need extra bytes to
- // make sure alignment of the returned pool address.
- //
- if ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0) {
- SizeRequested = ALIGN_VALUE (SizeRequested, 8);
- }
-
- Target = Start + Size - SizeRequested;
- ASSERT (Target >= Start);
- if (Target == 0) {
- return 0;
- }
-
- if (!IsGuardPage (Start + Size)) {
- // No Guard at tail to share. One more page is needed.
- Target -= EFI_PAGES_TO_SIZE (1);
- }
-
- // Out of range?
- if (Target < Start) {
- return 0;
- }
-
- // At the edge?
- if (Target == Start) {
- if (!IsGuardPage (Target - EFI_PAGES_TO_SIZE (1))) {
- // No enough space for a new head Guard if no Guard at head to share.
- return 0;
- }
- }
-
- // OK, we have enough pages for memory and its Guards. Return the End of the
- // free space.
- return Target + SizeRequested - 1;
-}
-
-/**
- Adjust the start address and number of pages to free according to Guard.
-
- The purpose of this function is to keep the shared Guard page with adjacent
- memory block if it's still in guard, or free it if no more sharing. Another
- is to reserve pages as Guard pages in partial page free situation.
-
- @param[in,out] Memory Base address of memory to free.
- @param[in,out] NumberOfPages Size of memory to free.
-
- @return VOID.
-**/
-VOID
-AdjustMemoryF (
- IN OUT EFI_PHYSICAL_ADDRESS *Memory,
- IN OUT UINTN *NumberOfPages
- )
-{
- EFI_PHYSICAL_ADDRESS Start;
- EFI_PHYSICAL_ADDRESS MemoryToTest;
- UINTN PagesToFree;
- UINT64 GuardBitmap;
-
- if ((Memory == NULL) || (NumberOfPages == NULL) || (*NumberOfPages == 0)) {
- return;
- }
-
- Start = *Memory;
- PagesToFree = *NumberOfPages;
-
- //
- // Head Guard must be one page before, if any.
- //
- // MSB-> 1 0 <-LSB
- // -------------------
- // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard)
- // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard)
- // 1 X -> Don't free first page (need a new Guard)
- // (it'll be turned into a Guard page later)
- // -------------------
- // Start -> -1 -2
- //
- MemoryToTest = Start - EFI_PAGES_TO_SIZE (2);
- GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2);
- if ((GuardBitmap & BIT1) == 0) {
- //
- // Head Guard exists.
- //
- if ((GuardBitmap & BIT0) == 0) {
- //
- // If the head Guard is not a tail Guard of adjacent memory block,
- // free it; otherwise, keep it.
- //
- Start -= EFI_PAGES_TO_SIZE (1);
- PagesToFree += 1;
- }
- } else {
- //
- // No Head Guard, and pages before memory to free are still in Guard. It's a
- // partial free case. We need to keep one page to be a tail Guard.
- //
- Start += EFI_PAGES_TO_SIZE (1);
- PagesToFree -= 1;
- }
-
- //
- // Tail Guard must be the page after this memory block to free, if any.
- //
- // MSB-> 1 0 <-LSB
- // --------------------
- // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard)
- // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard)
- // X 1 -> Don't free last page (need a new Guard)
- // (it'll be turned into a Guard page later)
- // --------------------
- // +1 +0 <- End
- //
- MemoryToTest = Start + EFI_PAGES_TO_SIZE (PagesToFree);
- GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2);
- if ((GuardBitmap & BIT0) == 0) {
- //
- // Tail Guard exists.
- //
- if ((GuardBitmap & BIT1) == 0) {
- //
- // If the tail Guard is not a head Guard of adjacent memory block,
- // free it; otherwise, keep it.
- //
- PagesToFree += 1;
- }
- } else if (PagesToFree > 0) {
- //
- // No Tail Guard, and pages after memory to free are still in Guard. It's a
- // partial free case. We need to keep one page to be a head Guard.
- //
- PagesToFree -= 1;
- }
-
- *Memory = Start;
- *NumberOfPages = PagesToFree;
-}
-
-/**
- Adjust the base and number of pages to really allocate according to Guard.
-
- @param[in,out] Memory Base address of free memory.
- @param[in,out] NumberOfPages Size of memory to allocate.
-
- @return VOID.
-**/
-VOID
-AdjustMemoryA (
- IN OUT EFI_PHYSICAL_ADDRESS *Memory,
- IN OUT UINTN *NumberOfPages
- )
-{
- //
- // FindFreePages() has already taken the Guard into account. It's safe to
- // adjust the start address and/or number of pages here, to make sure that
- // the Guards are also "allocated".
- //
- if (!IsGuardPage (*Memory + EFI_PAGES_TO_SIZE (*NumberOfPages))) {
- // No tail Guard, add one.
- *NumberOfPages += 1;
- }
-
- if (!IsGuardPage (*Memory - EFI_PAGE_SIZE)) {
- // No head Guard, add one.
- *Memory -= EFI_PAGE_SIZE;
- *NumberOfPages += 1;
- }
-}
-
-/**
- Adjust the pool head position to make sure the Guard page is adjavent to
- pool tail or pool head.
-
- @param[in] Memory Base address of memory allocated.
- @param[in] NoPages Number of pages actually allocated.
- @param[in] Size Size of memory requested.
- (plus pool head/tail overhead)
-
- @return Address of pool head.
-**/
-VOID *
-AdjustPoolHeadA (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NoPages,
- IN UINTN Size
- )
-{
- if ((Memory == 0) || ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0)) {
- //
- // Pool head is put near the head Guard
- //
- return (VOID *)(UINTN)Memory;
- }
-
- //
- // Pool head is put near the tail Guard
- //
- Size = ALIGN_VALUE (Size, 8);
- return (VOID *)(UINTN)(Memory + EFI_PAGES_TO_SIZE (NoPages) - Size);
-}
-
-/**
- Get the page base address according to pool head address.
-
- @param[in] Memory Head address of pool to free.
- @param[in] NoPages Number of pages actually allocated.
- @param[in] Size Size of memory requested.
- (plus pool head/tail overhead)
-
- @return Address of pool head.
-**/
-VOID *
-AdjustPoolHeadF (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NoPages,
- IN UINTN Size
- )
-{
- if ((Memory == 0) || ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0)) {
- //
- // Pool head is put near the head Guard
- //
- return (VOID *)(UINTN)Memory;
- }
-
- //
- // Pool head is put near the tail Guard. We need to exactly undo the addition done in AdjustPoolHeadA
- // because we may not have allocated the pool head on the first allocated page, since we are aligned to
- // the tail and on some architectures, the runtime page allocation granularity is > one page. So we allocate
- // more pages than we need and put the pool head somewhere past the first page.
- //
- return (VOID *)(UINTN)(Memory + Size - EFI_PAGES_TO_SIZE (NoPages));
-}
-
-/**
- Allocate or free guarded memory.
-
- @param[in] Start Start address of memory to allocate or free.
- @param[in] NumberOfPages Memory size in pages.
- @param[in] NewType Memory type to convert to.
-
- @return VOID.
-**/
-EFI_STATUS
-CoreConvertPagesWithGuard (
- IN UINT64 Start,
- IN UINTN NumberOfPages,
- IN EFI_MEMORY_TYPE NewType
- )
-{
- UINT64 OldStart;
- UINTN OldPages;
-
- if (NewType == EfiConventionalMemory) {
- OldStart = Start;
- OldPages = NumberOfPages;
-
- AdjustMemoryF (&Start, &NumberOfPages);
- //
- // It's safe to unset Guard page inside memory lock because there should
- // be no memory allocation occurred in updating memory page attribute at
- // this point. And unsetting Guard page before free will prevent Guard
- // page just freed back to pool from being allocated right away before
- // marking it usable (from non-present to present).
- //
- UnsetGuardForMemory (OldStart, OldPages);
- if (NumberOfPages == 0) {
- return EFI_SUCCESS;
- }
- } else {
- AdjustMemoryA (&Start, &NumberOfPages);
- }
-
- return CoreConvertPages (Start, NumberOfPages, NewType);
-}
-
-/**
- Set all Guard pages which cannot be set before CPU Arch Protocol installed.
-**/
-VOID
-SetAllGuardPages (
- VOID
- )
-{
- UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 TableEntry;
- UINT64 Address;
- UINT64 GuardPage;
- INTN Level;
- UINTN Index;
- BOOLEAN OnGuarding;
-
- if ((mGuardedMemoryMap == 0) ||
- (mMapLevel == 0) ||
- (mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH))
- {
- return;
- }
-
- CopyMem (Entries, mLevelMask, sizeof (Entries));
- CopyMem (Shifts, mLevelShift, sizeof (Shifts));
-
- SetMem (Tables, sizeof (Tables), 0);
- SetMem (Addresses, sizeof (Addresses), 0);
- SetMem (Indices, sizeof (Indices), 0);
-
- Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel;
- Tables[Level] = mGuardedMemoryMap;
- Address = 0;
- OnGuarding = FALSE;
-
- DEBUG_CODE (
- DumpGuardedMemoryBitmap ();
- );
-
- while (TRUE) {
- if (Indices[Level] > Entries[Level]) {
- Tables[Level] = 0;
- Level -= 1;
- } else {
- TableEntry = ((UINT64 *)(UINTN)(Tables[Level]))[Indices[Level]];
- Address = Addresses[Level];
-
- if (TableEntry == 0) {
- OnGuarding = FALSE;
- } else if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) {
- Level += 1;
- Tables[Level] = TableEntry;
- Addresses[Level] = Address;
- Indices[Level] = 0;
-
- continue;
- } else {
- Index = 0;
- while (Index < GUARDED_HEAP_MAP_ENTRY_BITS) {
- if ((TableEntry & 1) == 1) {
- if (OnGuarding) {
- GuardPage = 0;
- } else {
- GuardPage = Address - EFI_PAGE_SIZE;
- }
-
- OnGuarding = TRUE;
- } else {
- if (OnGuarding) {
- GuardPage = Address;
- } else {
- GuardPage = 0;
- }
-
- OnGuarding = FALSE;
- }
-
- if (GuardPage != 0) {
- SetGuardPage (GuardPage);
- }
-
- if (TableEntry == 0) {
- break;
- }
-
- TableEntry = RShiftU64 (TableEntry, 1);
- Address += EFI_PAGE_SIZE;
- Index += 1;
- }
- }
- }
-
- if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) {
- break;
- }
-
- Indices[Level] += 1;
- Address = (Level == 0) ? 0 : Addresses[Level - 1];
- Addresses[Level] = Address | LShiftU64 (Indices[Level], Shifts[Level]);
- }
-}
-
-/**
- Find the address of top-most guarded free page.
-
- @param[out] Address Start address of top-most guarded free page.
-
- @return VOID.
-**/
-VOID
-GetLastGuardedFreePageAddress (
- OUT EFI_PHYSICAL_ADDRESS *Address
- )
-{
- EFI_PHYSICAL_ADDRESS AddressGranularity;
- EFI_PHYSICAL_ADDRESS BaseAddress;
- UINTN Level;
- UINT64 Map;
- INTN Index;
-
- ASSERT (mMapLevel >= 1);
-
- BaseAddress = 0;
- Map = mGuardedMemoryMap;
- for (Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel;
- Level < GUARDED_HEAP_MAP_TABLE_DEPTH;
- ++Level)
- {
- AddressGranularity = LShiftU64 (1, mLevelShift[Level]);
-
- //
- // Find the non-NULL entry at largest index.
- //
- for (Index = (INTN)mLevelMask[Level]; Index >= 0; --Index) {
- if (((UINT64 *)(UINTN)Map)[Index] != 0) {
- BaseAddress += MultU64x32 (AddressGranularity, (UINT32)Index);
- Map = ((UINT64 *)(UINTN)Map)[Index];
- break;
- }
- }
- }
-
- //
- // Find the non-zero MSB then get the page address.
- //
- while (Map != 0) {
- Map = RShiftU64 (Map, 1);
- BaseAddress += EFI_PAGES_TO_SIZE (1);
- }
-
- *Address = BaseAddress;
-}
-
-/**
- Record freed pages.
-
- @param[in] BaseAddress Base address of just freed pages.
- @param[in] Pages Number of freed pages.
-
- @return VOID.
-**/
-VOID
-MarkFreedPages (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINTN Pages
- )
-{
- SetGuardedMemoryBits (BaseAddress, Pages);
-}
-
-/**
- Record freed pages as well as mark them as not-present.
-
- @param[in] BaseAddress Base address of just freed pages.
- @param[in] Pages Number of freed pages.
-
- @return VOID.
-**/
-VOID
-EFIAPI
-GuardFreedPages (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINTN Pages
- )
-{
- EFI_STATUS Status;
-
- //
- // Legacy memory lower than 1MB might be accessed with no allocation. Leave
- // them alone.
- //
- if (BaseAddress < BASE_1MB) {
- return;
- }
-
- MarkFreedPages (BaseAddress, Pages);
- if (gCpu != NULL) {
- //
- // Set flag to make sure allocating memory without GUARD for page table
- // operation; otherwise infinite loops could be caused.
- //
- mOnGuarding = TRUE;
- //
- // Note: This might overwrite other attributes needed by other features,
- // such as NX memory protection.
- //
- Status = gCpu->SetMemoryAttributes (
- gCpu,
- BaseAddress,
- EFI_PAGES_TO_SIZE (Pages),
- EFI_MEMORY_RP
- );
- //
- // Normally we should ASSERT the returned Status. But there might be memory
- // alloc/free involved in SetMemoryAttributes(), which might fail this
- // calling. It's rare case so it's OK to let a few tiny holes be not-guarded.
- //
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_WARN, "Failed to guard freed pages: %p (%lu)\n", BaseAddress, (UINT64)Pages));
- }
-
- mOnGuarding = FALSE;
- }
-}
-
-/**
- Record freed pages as well as mark them as not-present, if enabled.
-
- @param[in] BaseAddress Base address of just freed pages.
- @param[in] Pages Number of freed pages.
-
- @return VOID.
-**/
-VOID
-EFIAPI
-GuardFreedPagesChecked (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINTN Pages
- )
-{
- if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED)) {
- GuardFreedPages (BaseAddress, Pages);
- }
-}
-
-/**
- Mark all pages freed before CPU Arch Protocol as not-present.
-
-**/
-VOID
-GuardAllFreedPages (
- VOID
- )
-{
- UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 TableEntry;
- UINT64 Address;
- UINT64 GuardPage;
- INTN Level;
- UINT64 BitIndex;
- UINTN GuardPageNumber;
-
- if ((mGuardedMemoryMap == 0) ||
- (mMapLevel == 0) ||
- (mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH))
- {
- return;
- }
-
- CopyMem (Entries, mLevelMask, sizeof (Entries));
- CopyMem (Shifts, mLevelShift, sizeof (Shifts));
-
- SetMem (Tables, sizeof (Tables), 0);
- SetMem (Addresses, sizeof (Addresses), 0);
- SetMem (Indices, sizeof (Indices), 0);
-
- Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel;
- Tables[Level] = mGuardedMemoryMap;
- Address = 0;
- GuardPage = (UINT64)-1;
- GuardPageNumber = 0;
-
- while (TRUE) {
- if (Indices[Level] > Entries[Level]) {
- Tables[Level] = 0;
- Level -= 1;
- } else {
- TableEntry = ((UINT64 *)(UINTN)(Tables[Level]))[Indices[Level]];
- Address = Addresses[Level];
-
- if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) {
- Level += 1;
- Tables[Level] = TableEntry;
- Addresses[Level] = Address;
- Indices[Level] = 0;
-
- continue;
- } else {
- BitIndex = 1;
- while (BitIndex != 0) {
- if ((TableEntry & BitIndex) != 0) {
- if (GuardPage == (UINT64)-1) {
- GuardPage = Address;
- }
-
- ++GuardPageNumber;
- } else if (GuardPageNumber > 0) {
- GuardFreedPages (GuardPage, GuardPageNumber);
- GuardPageNumber = 0;
- GuardPage = (UINT64)-1;
- }
-
- if (TableEntry == 0) {
- break;
- }
-
- Address += EFI_PAGES_TO_SIZE (1);
- BitIndex = LShiftU64 (BitIndex, 1);
- }
- }
- }
-
- if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) {
- break;
- }
-
- Indices[Level] += 1;
- Address = (Level == 0) ? 0 : Addresses[Level - 1];
- Addresses[Level] = Address | LShiftU64 (Indices[Level], Shifts[Level]);
- }
-
- //
- // Update the maximum address of freed page which can be used for memory
- // promotion upon out-of-memory-space.
- //
- GetLastGuardedFreePageAddress (&Address);
- if (Address != 0) {
- mLastPromotedPage = Address;
- }
-}
-
-/**
- This function checks to see if the given memory map descriptor in a memory map
- can be merged with any guarded free pages.
-
- @param MemoryMapEntry A pointer to a descriptor in MemoryMap.
- @param MaxAddress Maximum address to stop the merge.
-
- @return VOID
-
-**/
-VOID
-MergeGuardPages (
- IN EFI_MEMORY_DESCRIPTOR *MemoryMapEntry,
- IN EFI_PHYSICAL_ADDRESS MaxAddress
- )
-{
- EFI_PHYSICAL_ADDRESS EndAddress;
- UINT64 Bitmap;
- INTN Pages;
-
- if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) ||
- (MemoryMapEntry->Type >= EfiMemoryMappedIO))
- {
- return;
- }
-
- Bitmap = 0;
- Pages = EFI_SIZE_TO_PAGES ((UINTN)(MaxAddress - MemoryMapEntry->PhysicalStart));
- Pages -= (INTN)MemoryMapEntry->NumberOfPages;
- while (Pages > 0) {
- if (Bitmap == 0) {
- EndAddress = MemoryMapEntry->PhysicalStart +
- EFI_PAGES_TO_SIZE ((UINTN)MemoryMapEntry->NumberOfPages);
- Bitmap = GetGuardedMemoryBits (EndAddress, GUARDED_HEAP_MAP_ENTRY_BITS);
- }
-
- if ((Bitmap & 1) == 0) {
- break;
- }
-
- Pages--;
- MemoryMapEntry->NumberOfPages++;
- Bitmap = RShiftU64 (Bitmap, 1);
- }
-}
-
-/**
- Put part (at most 64 pages a time) guarded free pages back to free page pool.
-
- Freed memory guard is used to detect Use-After-Free (UAF) memory issue, which
- makes use of 'Used then throw away' way to detect any illegal access to freed
- memory. The thrown-away memory will be marked as not-present so that any access
- to those memory (after free) will be caught by page-fault exception.
-
- The problem is that this will consume lots of memory space. Once no memory
- left in pool to allocate, we have to restore part of the freed pages to their
- normal function. Otherwise the whole system will stop functioning.
-
- @param StartAddress Start address of promoted memory.
- @param EndAddress End address of promoted memory.
-
- @return TRUE Succeeded to promote memory.
- @return FALSE No free memory found.
-
-**/
-BOOLEAN
-PromoteGuardedFreePages (
- OUT EFI_PHYSICAL_ADDRESS *StartAddress,
- OUT EFI_PHYSICAL_ADDRESS *EndAddress
- )
-{
- EFI_STATUS Status;
- UINTN AvailablePages;
- UINT64 Bitmap;
- EFI_PHYSICAL_ADDRESS Start;
-
- if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED)) {
- return FALSE;
- }
-
- //
- // Similar to memory allocation service, always search the freed pages in
- // descending direction.
- //
- Start = mLastPromotedPage;
- AvailablePages = 0;
- while (AvailablePages == 0) {
- Start -= EFI_PAGES_TO_SIZE (GUARDED_HEAP_MAP_ENTRY_BITS);
- //
- // If the address wraps around, try the really freed pages at top.
- //
- if (Start > mLastPromotedPage) {
- GetLastGuardedFreePageAddress (&Start);
- ASSERT (Start != 0);
- Start -= EFI_PAGES_TO_SIZE (GUARDED_HEAP_MAP_ENTRY_BITS);
- }
-
- Bitmap = GetGuardedMemoryBits (Start, GUARDED_HEAP_MAP_ENTRY_BITS);
- while (Bitmap > 0) {
- if ((Bitmap & 1) != 0) {
- ++AvailablePages;
- } else if (AvailablePages == 0) {
- Start += EFI_PAGES_TO_SIZE (1);
- } else {
- break;
- }
-
- Bitmap = RShiftU64 (Bitmap, 1);
- }
- }
-
- if (AvailablePages != 0) {
- DEBUG ((DEBUG_INFO, "Promoted pages: %lX (%lx)\r\n", Start, (UINT64)AvailablePages));
- ClearGuardedMemoryBits (Start, AvailablePages);
-
- if (gCpu != NULL) {
- //
- // Set flag to make sure allocating memory without GUARD for page table
- // operation; otherwise infinite loops could be caused.
- //
- mOnGuarding = TRUE;
- Status = gCpu->SetMemoryAttributes (gCpu, Start, EFI_PAGES_TO_SIZE (AvailablePages), 0);
- ASSERT_EFI_ERROR (Status);
- mOnGuarding = FALSE;
- }
-
- mLastPromotedPage = Start;
- *StartAddress = Start;
- *EndAddress = Start + EFI_PAGES_TO_SIZE (AvailablePages) - 1;
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- Notify function used to set all Guard pages before CPU Arch Protocol installed.
-**/
-VOID
-HeapGuardCpuArchProtocolNotify (
- VOID
- )
-{
- ASSERT (gCpu != NULL);
-
- if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL) &&
- IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED))
- {
- DEBUG ((DEBUG_ERROR, "Heap guard and freed memory guard cannot be enabled at the same time.\n"));
- CpuDeadLoop ();
- }
-
- if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL)) {
- SetAllGuardPages ();
- }
-
- if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED)) {
- GuardAllFreedPages ();
- }
-}
-
-/**
- Helper function to convert a UINT64 value in binary to a string.
-
- @param[in] Value Value of a UINT64 integer.
- @param[out] BinString String buffer to contain the conversion result.
-
- @return VOID.
-**/
-VOID
-Uint64ToBinString (
- IN UINT64 Value,
- OUT CHAR8 *BinString
- )
-{
- UINTN Index;
-
- if (BinString == NULL) {
- return;
- }
-
- for (Index = 64; Index > 0; --Index) {
- BinString[Index - 1] = '0' + (Value & 1);
- Value = RShiftU64 (Value, 1);
- }
-
- BinString[64] = '\0';
-}
-
-/**
- Dump the guarded memory bit map.
-**/
-VOID
-EFIAPI
-DumpGuardedMemoryBitmap (
- VOID
- )
-{
- UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH];
- UINT64 TableEntry;
- UINT64 Address;
- INTN Level;
- UINTN RepeatZero;
- CHAR8 String[GUARDED_HEAP_MAP_ENTRY_BITS + 1];
- CHAR8 *Ruler1;
- CHAR8 *Ruler2;
-
- if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_ALL)) {
- return;
- }
-
- if ((mGuardedMemoryMap == 0) ||
- (mMapLevel == 0) ||
- (mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH))
- {
- return;
- }
-
- Ruler1 = " 3 2 1 0";
- Ruler2 = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210";
-
- DEBUG ((
- HEAP_GUARD_DEBUG_LEVEL,
- "============================="
- " Guarded Memory Bitmap "
- "==============================\r\n"
- ));
- DEBUG ((HEAP_GUARD_DEBUG_LEVEL, " %a\r\n", Ruler1));
- DEBUG ((HEAP_GUARD_DEBUG_LEVEL, " %a\r\n", Ruler2));
-
- CopyMem (Entries, mLevelMask, sizeof (Entries));
- CopyMem (Shifts, mLevelShift, sizeof (Shifts));
-
- SetMem (Indices, sizeof (Indices), 0);
- SetMem (Tables, sizeof (Tables), 0);
- SetMem (Addresses, sizeof (Addresses), 0);
-
- Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel;
- Tables[Level] = mGuardedMemoryMap;
- Address = 0;
- RepeatZero = 0;
-
- while (TRUE) {
- if (Indices[Level] > Entries[Level]) {
- Tables[Level] = 0;
- Level -= 1;
- RepeatZero = 0;
-
- DEBUG ((
- HEAP_GUARD_DEBUG_LEVEL,
- "========================================="
- "=========================================\r\n"
- ));
- } else {
- TableEntry = ((UINT64 *)(UINTN)Tables[Level])[Indices[Level]];
- Address = Addresses[Level];
-
- if (TableEntry == 0) {
- if (Level == GUARDED_HEAP_MAP_TABLE_DEPTH - 1) {
- if (RepeatZero == 0) {
- Uint64ToBinString (TableEntry, String);
- DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "%016lx: %a\r\n", Address, String));
- } else if (RepeatZero == 1) {
- DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "... : ...\r\n"));
- }
-
- RepeatZero += 1;
- }
- } else if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) {
- Level += 1;
- Tables[Level] = TableEntry;
- Addresses[Level] = Address;
- Indices[Level] = 0;
- RepeatZero = 0;
-
- continue;
- } else {
- RepeatZero = 0;
- Uint64ToBinString (TableEntry, String);
- DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "%016lx: %a\r\n", Address, String));
- }
- }
-
- if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) {
- break;
- }
-
- Indices[Level] += 1;
- Address = (Level == 0) ? 0 : Addresses[Level - 1];
- Addresses[Level] = Address | LShiftU64 (Indices[Level], Shifts[Level]);
- }
-}
+/** @file + UEFI Heap Guard functions. + +Copyright (c) 2017-2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DxeMain.h" +#include "Imem.h" +#include "HeapGuard.h" + +// +// Global to avoid infinite reentrance of memory allocation when updating +// page table attributes, which may need allocate pages for new PDE/PTE. +// +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mOnGuarding = FALSE; + +// +// Pointer to table tracking the Guarded memory with bitmap, in which '1' +// is used to indicate memory guarded. '0' might be free memory or Guard +// page itself, depending on status of memory adjacent to it. +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT64 mGuardedMemoryMap = 0; + +// +// Current depth level of map table pointed by mGuardedMemoryMap. +// mMapLevel must be initialized at least by 1. It will be automatically +// updated according to the address of memory just tracked. +// +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mMapLevel = 1; + +// +// Shift and mask for each level of map table +// +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelShift[GUARDED_HEAP_MAP_TABLE_DEPTH] + = GUARDED_HEAP_MAP_TABLE_DEPTH_SHIFTS; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelMask[GUARDED_HEAP_MAP_TABLE_DEPTH] + = GUARDED_HEAP_MAP_TABLE_DEPTH_MASKS; + +// +// Used for promoting freed but not used pages. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS mLastPromotedPage = BASE_4GB; + +/** + Set corresponding bits in bitmap table to 1 according to the address. + + @param[in] Address Start address to set for. + @param[in] BitNumber Number of bits to set. + @param[in] BitMap Pointer to bitmap which covers the Address. + + @return VOID. +**/ +STATIC +VOID +SetBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN BitNumber, + IN UINT64 *BitMap + ) +{ + UINTN Lsbs; + UINTN Qwords; + UINTN Msbs; + UINTN StartBit; + UINTN EndBit; + + StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address); + EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + + if ((StartBit + BitNumber) >= GUARDED_HEAP_MAP_ENTRY_BITS) { + Msbs = (GUARDED_HEAP_MAP_ENTRY_BITS - StartBit) % + GUARDED_HEAP_MAP_ENTRY_BITS; + Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + Qwords = (BitNumber - Msbs) / GUARDED_HEAP_MAP_ENTRY_BITS; + } else { + Msbs = BitNumber; + Lsbs = 0; + Qwords = 0; + } + + if (Msbs > 0) { + *BitMap |= LShiftU64 (LShiftU64 (1, Msbs) - 1, StartBit); + BitMap += 1; + } + + if (Qwords > 0) { + SetMem64 ( + (VOID *)BitMap, + Qwords * GUARDED_HEAP_MAP_ENTRY_BYTES, + (UINT64)-1 + ); + BitMap += Qwords; + } + + if (Lsbs > 0) { + *BitMap |= (LShiftU64 (1, Lsbs) - 1); + } +} + +/** + Set corresponding bits in bitmap table to 0 according to the address. + + @param[in] Address Start address to set for. + @param[in] BitNumber Number of bits to set. + @param[in] BitMap Pointer to bitmap which covers the Address. + + @return VOID. +**/ +STATIC +VOID +ClearBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN BitNumber, + IN UINT64 *BitMap + ) +{ + UINTN Lsbs; + UINTN Qwords; + UINTN Msbs; + UINTN StartBit; + UINTN EndBit; + + StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address); + EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + + if ((StartBit + BitNumber) >= GUARDED_HEAP_MAP_ENTRY_BITS) { + Msbs = (GUARDED_HEAP_MAP_ENTRY_BITS - StartBit) % + GUARDED_HEAP_MAP_ENTRY_BITS; + Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + Qwords = (BitNumber - Msbs) / GUARDED_HEAP_MAP_ENTRY_BITS; + } else { + Msbs = BitNumber; + Lsbs = 0; + Qwords = 0; + } + + if (Msbs > 0) { + *BitMap &= ~LShiftU64 (LShiftU64 (1, Msbs) - 1, StartBit); + BitMap += 1; + } + + if (Qwords > 0) { + SetMem64 ((VOID *)BitMap, Qwords * GUARDED_HEAP_MAP_ENTRY_BYTES, 0); + BitMap += Qwords; + } + + if (Lsbs > 0) { + *BitMap &= ~(LShiftU64 (1, Lsbs) - 1); + } +} + +/** + Get corresponding bits in bitmap table according to the address. + + The value of bit 0 corresponds to the status of memory at given Address. + No more than 64 bits can be retrieved in one call. + + @param[in] Address Start address to retrieve bits for. + @param[in] BitNumber Number of bits to get. + @param[in] BitMap Pointer to bitmap which covers the Address. + + @return An integer containing the bits information. +**/ +STATIC +UINT64 +GetBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN BitNumber, + IN UINT64 *BitMap + ) +{ + UINTN StartBit; + UINTN EndBit; + UINTN Lsbs; + UINTN Msbs; + UINT64 Result; + + ASSERT (BitNumber <= GUARDED_HEAP_MAP_ENTRY_BITS); + + StartBit = (UINTN)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address); + EndBit = (StartBit + BitNumber - 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + + if ((StartBit + BitNumber) > GUARDED_HEAP_MAP_ENTRY_BITS) { + Msbs = GUARDED_HEAP_MAP_ENTRY_BITS - StartBit; + Lsbs = (EndBit + 1) % GUARDED_HEAP_MAP_ENTRY_BITS; + } else { + Msbs = BitNumber; + Lsbs = 0; + } + + if ((StartBit == 0) && (BitNumber == GUARDED_HEAP_MAP_ENTRY_BITS)) { + Result = *BitMap; + } else { + Result = RShiftU64 ((*BitMap), StartBit) & (LShiftU64 (1, Msbs) - 1); + if (Lsbs > 0) { + BitMap += 1; + Result |= LShiftU64 ((*BitMap) & (LShiftU64 (1, Lsbs) - 1), Msbs); + } + } + + return Result; +} + +/** + Locate the pointer of bitmap from the guarded memory bitmap tables, which + covers the given Address. + + @param[in] Address Start address to search the bitmap for. + @param[in] AllocMapUnit Flag to indicate memory allocation for the table. + @param[out] BitMap Pointer to bitmap which covers the Address. + + @return The bit number from given Address to the end of current map table. +**/ +UINTN +FindGuardedMemoryMap ( + IN EFI_PHYSICAL_ADDRESS Address, + IN BOOLEAN AllocMapUnit, + OUT UINT64 **BitMap + ) +{ + UINTN Level; + UINT64 *GuardMap; + UINT64 MapMemory; + UINTN Index; + UINTN Size; + UINTN BitsToUnitEnd; + EFI_STATUS Status; + + MapMemory = 0; + + // + // Adjust current map table depth according to the address to access + // + while (AllocMapUnit && + mMapLevel < GUARDED_HEAP_MAP_TABLE_DEPTH && + RShiftU64 ( + Address, + mLevelShift[GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel - 1] + ) != 0) + { + if (mGuardedMemoryMap != 0) { + Size = (mLevelMask[GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel - 1] + 1) + * GUARDED_HEAP_MAP_ENTRY_BYTES; + Status = CoreInternalAllocatePages ( + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (Size), + &MapMemory, + FALSE + ); + ASSERT_EFI_ERROR (Status); + ASSERT (MapMemory != 0); + + SetMem ((VOID *)(UINTN)MapMemory, Size, 0); + + *(UINT64 *)(UINTN)MapMemory = mGuardedMemoryMap; + mGuardedMemoryMap = MapMemory; + } + + mMapLevel++; + } + + GuardMap = &mGuardedMemoryMap; + for (Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Level < GUARDED_HEAP_MAP_TABLE_DEPTH; + ++Level) + { + if (*GuardMap == 0) { + if (!AllocMapUnit) { + GuardMap = NULL; + break; + } + + Size = (mLevelMask[Level] + 1) * GUARDED_HEAP_MAP_ENTRY_BYTES; + Status = CoreInternalAllocatePages ( + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (Size), + &MapMemory, + FALSE + ); + ASSERT_EFI_ERROR (Status); + ASSERT (MapMemory != 0); + + SetMem ((VOID *)(UINTN)MapMemory, Size, 0); + *GuardMap = MapMemory; + } + + Index = (UINTN)RShiftU64 (Address, mLevelShift[Level]); + Index &= mLevelMask[Level]; + GuardMap = (UINT64 *)(UINTN)((*GuardMap) + Index * sizeof (UINT64)); + } + + BitsToUnitEnd = GUARDED_HEAP_MAP_BITS - GUARDED_HEAP_MAP_BIT_INDEX (Address); + *BitMap = GuardMap; + + return BitsToUnitEnd; +} + +/** + Set corresponding bits in bitmap table to 1 according to given memory range. + + @param[in] Address Memory address to guard from. + @param[in] NumberOfPages Number of pages to guard. + + @return VOID. +**/ +VOID +EFIAPI +SetGuardedMemoryBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN NumberOfPages + ) +{ + UINT64 *BitMap; + UINTN Bits; + UINTN BitsToUnitEnd; + + while (NumberOfPages > 0) { + BitsToUnitEnd = FindGuardedMemoryMap (Address, TRUE, &BitMap); + ASSERT (BitMap != NULL); + + if (NumberOfPages > BitsToUnitEnd) { + // Cross map unit + Bits = BitsToUnitEnd; + } else { + Bits = NumberOfPages; + } + + SetBits (Address, Bits, BitMap); + + NumberOfPages -= Bits; + Address += EFI_PAGES_TO_SIZE (Bits); + } +} + +/** + Clear corresponding bits in bitmap table according to given memory range. + + @param[in] Address Memory address to unset from. + @param[in] NumberOfPages Number of pages to unset guard. + + @return VOID. +**/ +VOID +EFIAPI +ClearGuardedMemoryBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN NumberOfPages + ) +{ + UINT64 *BitMap; + UINTN Bits; + UINTN BitsToUnitEnd; + + while (NumberOfPages > 0) { + BitsToUnitEnd = FindGuardedMemoryMap (Address, TRUE, &BitMap); + ASSERT (BitMap != NULL); + + if (NumberOfPages > BitsToUnitEnd) { + // Cross map unit + Bits = BitsToUnitEnd; + } else { + Bits = NumberOfPages; + } + + ClearBits (Address, Bits, BitMap); + + NumberOfPages -= Bits; + Address += EFI_PAGES_TO_SIZE (Bits); + } +} + +/** + Retrieve corresponding bits in bitmap table according to given memory range. + + @param[in] Address Memory address to retrieve from. + @param[in] NumberOfPages Number of pages to retrieve. + + @return An integer containing the guarded memory bitmap. +**/ +UINT64 +GetGuardedMemoryBits ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN NumberOfPages + ) +{ + UINT64 *BitMap; + UINTN Bits; + UINT64 Result; + UINTN Shift; + UINTN BitsToUnitEnd; + + ASSERT (NumberOfPages <= GUARDED_HEAP_MAP_ENTRY_BITS); + + Result = 0; + Shift = 0; + while (NumberOfPages > 0) { + BitsToUnitEnd = FindGuardedMemoryMap (Address, FALSE, &BitMap); + + if (NumberOfPages > BitsToUnitEnd) { + // Cross map unit + Bits = BitsToUnitEnd; + } else { + Bits = NumberOfPages; + } + + if (BitMap != NULL) { + Result |= LShiftU64 (GetBits (Address, Bits, BitMap), Shift); + } + + Shift += Bits; + NumberOfPages -= Bits; + Address += EFI_PAGES_TO_SIZE (Bits); + } + + return Result; +} + +/** + Get bit value in bitmap table for the given address. + + @param[in] Address The address to retrieve for. + + @return 1 or 0. +**/ +UINTN +EFIAPI +GetGuardMapBit ( + IN EFI_PHYSICAL_ADDRESS Address + ) +{ + UINT64 *GuardMap; + + FindGuardedMemoryMap (Address, FALSE, &GuardMap); + if (GuardMap != NULL) { + if (RShiftU64 ( + *GuardMap, + GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address) + ) & 1) + { + return 1; + } + } + + return 0; +} + +/** + Check to see if the page at the given address is a Guard page or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is a Guard page. + @return FALSE The page at Address is not a Guard page. +**/ +BOOLEAN +EFIAPI +IsGuardPage ( + IN EFI_PHYSICAL_ADDRESS Address + ) +{ + UINT64 BitMap; + + // + // There must be at least one guarded page before and/or after given + // address if it's a Guard page. The bitmap pattern should be one of + // 001, 100 and 101 + // + BitMap = GetGuardedMemoryBits (Address - EFI_PAGE_SIZE, 3); + return ((BitMap == BIT0) || (BitMap == BIT2) || (BitMap == (BIT2 | BIT0))); +} + +/** + Check to see if the page at the given address is guarded or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is guarded. + @return FALSE The page at Address is not guarded. +**/ +BOOLEAN +EFIAPI +IsMemoryGuarded ( + IN EFI_PHYSICAL_ADDRESS Address + ) +{ + return (GetGuardMapBit (Address) == 1); +} + +/** + Set the page at the given address to be a Guard page. + + This is done by changing the page table attribute to be NOT PRSENT. + + @param[in] BaseAddress Page address to Guard at + + @return VOID +**/ +VOID +EFIAPI +SetGuardPage ( + IN EFI_PHYSICAL_ADDRESS BaseAddress + ) +{ + EFI_STATUS Status; + + if (gCpu == NULL) { + return; + } + + // + // Set flag to make sure allocating memory without GUARD for page table + // operation; otherwise infinite loops could be caused. + // + mOnGuarding = TRUE; + // + // Note: This might overwrite other attributes needed by other features, + // such as NX memory protection. + // + Status = gCpu->SetMemoryAttributes (gCpu, BaseAddress, EFI_PAGE_SIZE, EFI_MEMORY_RP); + ASSERT_EFI_ERROR (Status); + mOnGuarding = FALSE; +} + +/** + Unset the Guard page at the given address to the normal memory. + + This is done by changing the page table attribute to be PRSENT. + + @param[in] BaseAddress Page address to Guard at. + + @return VOID. +**/ +VOID +EFIAPI +UnsetGuardPage ( + IN EFI_PHYSICAL_ADDRESS BaseAddress + ) +{ + UINT64 Attributes; + EFI_STATUS Status; + + if (gCpu == NULL) { + return; + } + + // + // Once the Guard page is unset, it will be freed back to memory pool. NX + // memory protection must be restored for this page if NX is enabled for free + // memory. + // + Attributes = 0; + if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & (1 << EfiConventionalMemory)) != 0) { + Attributes |= EFI_MEMORY_XP; + } + + // + // Set flag to make sure allocating memory without GUARD for page table + // operation; otherwise infinite loops could be caused. + // + mOnGuarding = TRUE; + // + // Note: This might overwrite other attributes needed by other features, + // such as memory protection (NX). Please make sure they are not enabled + // at the same time. + // + Status = gCpu->SetMemoryAttributes (gCpu, BaseAddress, EFI_PAGE_SIZE, Attributes); + ASSERT_EFI_ERROR (Status); + mOnGuarding = FALSE; +} + +/** + Check to see if the memory at the given address should be guarded or not. + + @param[in] MemoryType Memory type to check. + @param[in] AllocateType Allocation type to check. + @param[in] PageOrPool Indicate a page allocation or pool allocation. + + + @return TRUE The given type of memory should be guarded. + @return FALSE The given type of memory should not be guarded. +**/ +BOOLEAN +IsMemoryTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType, + IN EFI_ALLOCATE_TYPE AllocateType, + IN UINT8 PageOrPool + ) +{ + UINT64 TestBit; + UINT64 ConfigBit; + + if (AllocateType == AllocateAddress) { + return FALSE; + } + + if ((PcdGet8 (PcdHeapGuardPropertyMask) & PageOrPool) == 0) { + return FALSE; + } + + if (PageOrPool == GUARD_HEAP_TYPE_POOL) { + ConfigBit = PcdGet64 (PcdHeapGuardPoolType); + } else if (PageOrPool == GUARD_HEAP_TYPE_PAGE) { + ConfigBit = PcdGet64 (PcdHeapGuardPageType); + } else { + ConfigBit = (UINT64)-1; + } + + if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) { + TestBit = BIT63; + } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) { + TestBit = BIT62; + } else if (MemoryType < EfiMaxMemoryType) { + TestBit = LShiftU64 (1, MemoryType); + } else if (MemoryType == EfiMaxMemoryType) { + TestBit = (UINT64)-1; + } else { + TestBit = 0; + } + + return ((ConfigBit & TestBit) != 0); +} + +/** + Check to see if the pool at the given address should be guarded or not. + + @param[in] MemoryType Pool type to check. + + + @return TRUE The given type of pool should be guarded. + @return FALSE The given type of pool should not be guarded. +**/ +BOOLEAN +IsPoolTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + return IsMemoryTypeToGuard ( + MemoryType, + AllocateAnyPages, + GUARD_HEAP_TYPE_POOL + ); +} + +/** + Check to see if the page at the given address should be guarded or not. + + @param[in] MemoryType Page type to check. + @param[in] AllocateType Allocation type to check. + + @return TRUE The given type of page should be guarded. + @return FALSE The given type of page should not be guarded. +**/ +BOOLEAN +IsPageTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType, + IN EFI_ALLOCATE_TYPE AllocateType + ) +{ + return IsMemoryTypeToGuard (MemoryType, AllocateType, GUARD_HEAP_TYPE_PAGE); +} + +/** + Check to see if the heap guard is enabled for page and/or pool allocation. + + @param[in] GuardType Specify the sub-type(s) of Heap Guard. + + @return TRUE/FALSE. +**/ +BOOLEAN +IsHeapGuardEnabled ( + UINT8 GuardType + ) +{ + return IsMemoryTypeToGuard (EfiMaxMemoryType, AllocateAnyPages, GuardType); +} + +/** + Set head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to set guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID +**/ +VOID +SetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + EFI_PHYSICAL_ADDRESS GuardPage; + + // + // Set tail Guard + // + GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages); + if (!IsGuardPage (GuardPage)) { + SetGuardPage (GuardPage); + } + + // Set head Guard + GuardPage = Memory - EFI_PAGES_TO_SIZE (1); + if (!IsGuardPage (GuardPage)) { + SetGuardPage (GuardPage); + } + + // + // Mark the memory range as Guarded + // + SetGuardedMemoryBits (Memory, NumberOfPages); +} + +/** + Unset head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to unset guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID +**/ +VOID +UnsetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + EFI_PHYSICAL_ADDRESS GuardPage; + UINT64 GuardBitmap; + + if (NumberOfPages == 0) { + return; + } + + // + // Head Guard must be one page before, if any. + // + // MSB-> 1 0 <-LSB + // ------------------- + // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard) + // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard) + // 1 X -> Don't free first page (need a new Guard) + // (it'll be turned into a Guard page later) + // ------------------- + // Start -> -1 -2 + // + GuardPage = Memory - EFI_PAGES_TO_SIZE (1); + GuardBitmap = GetGuardedMemoryBits (Memory - EFI_PAGES_TO_SIZE (2), 2); + if ((GuardBitmap & BIT1) == 0) { + // + // Head Guard exists. + // + if ((GuardBitmap & BIT0) == 0) { + // + // If the head Guard is not a tail Guard of adjacent memory block, + // unset it. + // + UnsetGuardPage (GuardPage); + } + } else { + // + // Pages before memory to free are still in Guard. It's a partial free + // case. Turn first page of memory block to free into a new Guard. + // + SetGuardPage (Memory); + } + + // + // Tail Guard must be the page after this memory block to free, if any. + // + // MSB-> 1 0 <-LSB + // -------------------- + // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard) + // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard) + // X 1 -> Don't free last page (need a new Guard) + // (it'll be turned into a Guard page later) + // -------------------- + // +1 +0 <- End + // + GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages); + GuardBitmap = GetGuardedMemoryBits (GuardPage, 2); + if ((GuardBitmap & BIT0) == 0) { + // + // Tail Guard exists. + // + if ((GuardBitmap & BIT1) == 0) { + // + // If the tail Guard is not a head Guard of adjacent memory block, + // free it; otherwise, keep it. + // + UnsetGuardPage (GuardPage); + } + } else { + // + // Pages after memory to free are still in Guard. It's a partial free + // case. We need to keep one page to be a head Guard. + // + SetGuardPage (GuardPage - EFI_PAGES_TO_SIZE (1)); + } + + // + // No matter what, we just clear the mark of the Guarded memory. + // + ClearGuardedMemoryBits (Memory, NumberOfPages); +} + +/** + Adjust address of free memory according to existing and/or required Guard. + + This function will check if there're existing Guard pages of adjacent + memory blocks, and try to use it as the Guard page of the memory to be + allocated. + + @param[in] Start Start address of free memory block. + @param[in] Size Size of free memory block. + @param[in] SizeRequested Size of memory to allocate. + + @return The end address of memory block found. + @return 0 if no enough space for the required size of memory and its Guard. +**/ +UINT64 +AdjustMemoryS ( + IN UINT64 Start, + IN UINT64 Size, + IN UINT64 SizeRequested + ) +{ + UINT64 Target; + + // + // UEFI spec requires that allocated pool must be 8-byte aligned. If it's + // indicated to put the pool near the Tail Guard, we need extra bytes to + // make sure alignment of the returned pool address. + // + if ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0) { + SizeRequested = ALIGN_VALUE (SizeRequested, 8); + } + + Target = Start + Size - SizeRequested; + ASSERT (Target >= Start); + if (Target == 0) { + return 0; + } + + if (!IsGuardPage (Start + Size)) { + // No Guard at tail to share. One more page is needed. + Target -= EFI_PAGES_TO_SIZE (1); + } + + // Out of range? + if (Target < Start) { + return 0; + } + + // At the edge? + if (Target == Start) { + if (!IsGuardPage (Target - EFI_PAGES_TO_SIZE (1))) { + // No enough space for a new head Guard if no Guard at head to share. + return 0; + } + } + + // OK, we have enough pages for memory and its Guards. Return the End of the + // free space. + return Target + SizeRequested - 1; +} + +/** + Adjust the start address and number of pages to free according to Guard. + + The purpose of this function is to keep the shared Guard page with adjacent + memory block if it's still in guard, or free it if no more sharing. Another + is to reserve pages as Guard pages in partial page free situation. + + @param[in,out] Memory Base address of memory to free. + @param[in,out] NumberOfPages Size of memory to free. + + @return VOID. +**/ +VOID +AdjustMemoryF ( + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN OUT UINTN *NumberOfPages + ) +{ + EFI_PHYSICAL_ADDRESS Start; + EFI_PHYSICAL_ADDRESS MemoryToTest; + UINTN PagesToFree; + UINT64 GuardBitmap; + + if ((Memory == NULL) || (NumberOfPages == NULL) || (*NumberOfPages == 0)) { + return; + } + + Start = *Memory; + PagesToFree = *NumberOfPages; + + // + // Head Guard must be one page before, if any. + // + // MSB-> 1 0 <-LSB + // ------------------- + // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard) + // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard) + // 1 X -> Don't free first page (need a new Guard) + // (it'll be turned into a Guard page later) + // ------------------- + // Start -> -1 -2 + // + MemoryToTest = Start - EFI_PAGES_TO_SIZE (2); + GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2); + if ((GuardBitmap & BIT1) == 0) { + // + // Head Guard exists. + // + if ((GuardBitmap & BIT0) == 0) { + // + // If the head Guard is not a tail Guard of adjacent memory block, + // free it; otherwise, keep it. + // + Start -= EFI_PAGES_TO_SIZE (1); + PagesToFree += 1; + } + } else { + // + // No Head Guard, and pages before memory to free are still in Guard. It's a + // partial free case. We need to keep one page to be a tail Guard. + // + Start += EFI_PAGES_TO_SIZE (1); + PagesToFree -= 1; + } + + // + // Tail Guard must be the page after this memory block to free, if any. + // + // MSB-> 1 0 <-LSB + // -------------------- + // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard) + // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard) + // X 1 -> Don't free last page (need a new Guard) + // (it'll be turned into a Guard page later) + // -------------------- + // +1 +0 <- End + // + MemoryToTest = Start + EFI_PAGES_TO_SIZE (PagesToFree); + GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2); + if ((GuardBitmap & BIT0) == 0) { + // + // Tail Guard exists. + // + if ((GuardBitmap & BIT1) == 0) { + // + // If the tail Guard is not a head Guard of adjacent memory block, + // free it; otherwise, keep it. + // + PagesToFree += 1; + } + } else if (PagesToFree > 0) { + // + // No Tail Guard, and pages after memory to free are still in Guard. It's a + // partial free case. We need to keep one page to be a head Guard. + // + PagesToFree -= 1; + } + + *Memory = Start; + *NumberOfPages = PagesToFree; +} + +/** + Adjust the base and number of pages to really allocate according to Guard. + + @param[in,out] Memory Base address of free memory. + @param[in,out] NumberOfPages Size of memory to allocate. + + @return VOID. +**/ +VOID +AdjustMemoryA ( + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN OUT UINTN *NumberOfPages + ) +{ + // + // FindFreePages() has already taken the Guard into account. It's safe to + // adjust the start address and/or number of pages here, to make sure that + // the Guards are also "allocated". + // + if (!IsGuardPage (*Memory + EFI_PAGES_TO_SIZE (*NumberOfPages))) { + // No tail Guard, add one. + *NumberOfPages += 1; + } + + if (!IsGuardPage (*Memory - EFI_PAGE_SIZE)) { + // No head Guard, add one. + *Memory -= EFI_PAGE_SIZE; + *NumberOfPages += 1; + } +} + +/** + Adjust the pool head position to make sure the Guard page is adjavent to + pool tail or pool head. + + @param[in] Memory Base address of memory allocated. + @param[in] NoPages Number of pages actually allocated. + @param[in] Size Size of memory requested. + (plus pool head/tail overhead) + + @return Address of pool head. +**/ +VOID * +AdjustPoolHeadA ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages, + IN UINTN Size + ) +{ + if ((Memory == 0) || ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0)) { + // + // Pool head is put near the head Guard + // + return (VOID *)(UINTN)Memory; + } + + // + // Pool head is put near the tail Guard + // + Size = ALIGN_VALUE (Size, 8); + return (VOID *)(UINTN)(Memory + EFI_PAGES_TO_SIZE (NoPages) - Size); +} + +/** + Get the page base address according to pool head address. + + @param[in] Memory Head address of pool to free. + @param[in] NoPages Number of pages actually allocated. + @param[in] Size Size of memory requested. + (plus pool head/tail overhead) + + @return Address of pool head. +**/ +VOID * +AdjustPoolHeadF ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages, + IN UINTN Size + ) +{ + if ((Memory == 0) || ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0)) { + // + // Pool head is put near the head Guard + // + return (VOID *)(UINTN)Memory; + } + + // + // Pool head is put near the tail Guard. We need to exactly undo the addition done in AdjustPoolHeadA + // because we may not have allocated the pool head on the first allocated page, since we are aligned to + // the tail and on some architectures, the runtime page allocation granularity is > one page. So we allocate + // more pages than we need and put the pool head somewhere past the first page. + // + return (VOID *)(UINTN)(Memory + Size - EFI_PAGES_TO_SIZE (NoPages)); +} + +/** + Allocate or free guarded memory. + + @param[in] Start Start address of memory to allocate or free. + @param[in] NumberOfPages Memory size in pages. + @param[in] NewType Memory type to convert to. + + @return VOID. +**/ +EFI_STATUS +CoreConvertPagesWithGuard ( + IN UINT64 Start, + IN UINTN NumberOfPages, + IN EFI_MEMORY_TYPE NewType + ) +{ + UINT64 OldStart; + UINTN OldPages; + + if (NewType == EfiConventionalMemory) { + OldStart = Start; + OldPages = NumberOfPages; + + AdjustMemoryF (&Start, &NumberOfPages); + // + // It's safe to unset Guard page inside memory lock because there should + // be no memory allocation occurred in updating memory page attribute at + // this point. And unsetting Guard page before free will prevent Guard + // page just freed back to pool from being allocated right away before + // marking it usable (from non-present to present). + // + UnsetGuardForMemory (OldStart, OldPages); + if (NumberOfPages == 0) { + return EFI_SUCCESS; + } + } else { + AdjustMemoryA (&Start, &NumberOfPages); + } + + return CoreConvertPages (Start, NumberOfPages, NewType); +} + +/** + Set all Guard pages which cannot be set before CPU Arch Protocol installed. +**/ +VOID +SetAllGuardPages ( + VOID + ) +{ + UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 TableEntry; + UINT64 Address; + UINT64 GuardPage; + INTN Level; + UINTN Index; + BOOLEAN OnGuarding; + + if ((mGuardedMemoryMap == 0) || + (mMapLevel == 0) || + (mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH)) + { + return; + } + + CopyMem (Entries, mLevelMask, sizeof (Entries)); + CopyMem (Shifts, mLevelShift, sizeof (Shifts)); + + SetMem (Tables, sizeof (Tables), 0); + SetMem (Addresses, sizeof (Addresses), 0); + SetMem (Indices, sizeof (Indices), 0); + + Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Tables[Level] = mGuardedMemoryMap; + Address = 0; + OnGuarding = FALSE; + + DEBUG_CODE ( + DumpGuardedMemoryBitmap (); + ); + + while (TRUE) { + if (Indices[Level] > Entries[Level]) { + Tables[Level] = 0; + Level -= 1; + } else { + TableEntry = ((UINT64 *)(UINTN)(Tables[Level]))[Indices[Level]]; + Address = Addresses[Level]; + + if (TableEntry == 0) { + OnGuarding = FALSE; + } else if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) { + Level += 1; + Tables[Level] = TableEntry; + Addresses[Level] = Address; + Indices[Level] = 0; + + continue; + } else { + Index = 0; + while (Index < GUARDED_HEAP_MAP_ENTRY_BITS) { + if ((TableEntry & 1) == 1) { + if (OnGuarding) { + GuardPage = 0; + } else { + GuardPage = Address - EFI_PAGE_SIZE; + } + + OnGuarding = TRUE; + } else { + if (OnGuarding) { + GuardPage = Address; + } else { + GuardPage = 0; + } + + OnGuarding = FALSE; + } + + if (GuardPage != 0) { + SetGuardPage (GuardPage); + } + + if (TableEntry == 0) { + break; + } + + TableEntry = RShiftU64 (TableEntry, 1); + Address += EFI_PAGE_SIZE; + Index += 1; + } + } + } + + if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) { + break; + } + + Indices[Level] += 1; + Address = (Level == 0) ? 0 : Addresses[Level - 1]; + Addresses[Level] = Address | LShiftU64 (Indices[Level], Shifts[Level]); + } +} + +/** + Find the address of top-most guarded free page. + + @param[out] Address Start address of top-most guarded free page. + + @return VOID. +**/ +VOID +GetLastGuardedFreePageAddress ( + OUT EFI_PHYSICAL_ADDRESS *Address + ) +{ + EFI_PHYSICAL_ADDRESS AddressGranularity; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINTN Level; + UINT64 Map; + INTN Index; + + ASSERT (mMapLevel >= 1); + + BaseAddress = 0; + Map = mGuardedMemoryMap; + for (Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Level < GUARDED_HEAP_MAP_TABLE_DEPTH; + ++Level) + { + AddressGranularity = LShiftU64 (1, mLevelShift[Level]); + + // + // Find the non-NULL entry at largest index. + // + for (Index = (INTN)mLevelMask[Level]; Index >= 0; --Index) { + if (((UINT64 *)(UINTN)Map)[Index] != 0) { + BaseAddress += MultU64x32 (AddressGranularity, (UINT32)Index); + Map = ((UINT64 *)(UINTN)Map)[Index]; + break; + } + } + } + + // + // Find the non-zero MSB then get the page address. + // + while (Map != 0) { + Map = RShiftU64 (Map, 1); + BaseAddress += EFI_PAGES_TO_SIZE (1); + } + + *Address = BaseAddress; +} + +/** + Record freed pages. + + @param[in] BaseAddress Base address of just freed pages. + @param[in] Pages Number of freed pages. + + @return VOID. +**/ +VOID +MarkFreedPages ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINTN Pages + ) +{ + SetGuardedMemoryBits (BaseAddress, Pages); +} + +/** + Record freed pages as well as mark them as not-present. + + @param[in] BaseAddress Base address of just freed pages. + @param[in] Pages Number of freed pages. + + @return VOID. +**/ +VOID +EFIAPI +GuardFreedPages ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINTN Pages + ) +{ + EFI_STATUS Status; + + // + // Legacy memory lower than 1MB might be accessed with no allocation. Leave + // them alone. + // + if (BaseAddress < BASE_1MB) { + return; + } + + MarkFreedPages (BaseAddress, Pages); + if (gCpu != NULL) { + // + // Set flag to make sure allocating memory without GUARD for page table + // operation; otherwise infinite loops could be caused. + // + mOnGuarding = TRUE; + // + // Note: This might overwrite other attributes needed by other features, + // such as NX memory protection. + // + Status = gCpu->SetMemoryAttributes ( + gCpu, + BaseAddress, + EFI_PAGES_TO_SIZE (Pages), + EFI_MEMORY_RP + ); + // + // Normally we should ASSERT the returned Status. But there might be memory + // alloc/free involved in SetMemoryAttributes(), which might fail this + // calling. It's rare case so it's OK to let a few tiny holes be not-guarded. + // + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "Failed to guard freed pages: %p (%lu)\n", BaseAddress, (UINT64)Pages)); + } + + mOnGuarding = FALSE; + } +} + +/** + Record freed pages as well as mark them as not-present, if enabled. + + @param[in] BaseAddress Base address of just freed pages. + @param[in] Pages Number of freed pages. + + @return VOID. +**/ +VOID +EFIAPI +GuardFreedPagesChecked ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINTN Pages + ) +{ + if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED)) { + GuardFreedPages (BaseAddress, Pages); + } +} + +/** + Mark all pages freed before CPU Arch Protocol as not-present. + +**/ +VOID +GuardAllFreedPages ( + VOID + ) +{ + UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 TableEntry; + UINT64 Address; + UINT64 GuardPage; + INTN Level; + UINT64 BitIndex; + UINTN GuardPageNumber; + + if ((mGuardedMemoryMap == 0) || + (mMapLevel == 0) || + (mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH)) + { + return; + } + + CopyMem (Entries, mLevelMask, sizeof (Entries)); + CopyMem (Shifts, mLevelShift, sizeof (Shifts)); + + SetMem (Tables, sizeof (Tables), 0); + SetMem (Addresses, sizeof (Addresses), 0); + SetMem (Indices, sizeof (Indices), 0); + + Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Tables[Level] = mGuardedMemoryMap; + Address = 0; + GuardPage = (UINT64)-1; + GuardPageNumber = 0; + + while (TRUE) { + if (Indices[Level] > Entries[Level]) { + Tables[Level] = 0; + Level -= 1; + } else { + TableEntry = ((UINT64 *)(UINTN)(Tables[Level]))[Indices[Level]]; + Address = Addresses[Level]; + + if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) { + Level += 1; + Tables[Level] = TableEntry; + Addresses[Level] = Address; + Indices[Level] = 0; + + continue; + } else { + BitIndex = 1; + while (BitIndex != 0) { + if ((TableEntry & BitIndex) != 0) { + if (GuardPage == (UINT64)-1) { + GuardPage = Address; + } + + ++GuardPageNumber; + } else if (GuardPageNumber > 0) { + GuardFreedPages (GuardPage, GuardPageNumber); + GuardPageNumber = 0; + GuardPage = (UINT64)-1; + } + + if (TableEntry == 0) { + break; + } + + Address += EFI_PAGES_TO_SIZE (1); + BitIndex = LShiftU64 (BitIndex, 1); + } + } + } + + if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) { + break; + } + + Indices[Level] += 1; + Address = (Level == 0) ? 0 : Addresses[Level - 1]; + Addresses[Level] = Address | LShiftU64 (Indices[Level], Shifts[Level]); + } + + // + // Update the maximum address of freed page which can be used for memory + // promotion upon out-of-memory-space. + // + GetLastGuardedFreePageAddress (&Address); + if (Address != 0) { + mLastPromotedPage = Address; + } +} + +/** + This function checks to see if the given memory map descriptor in a memory map + can be merged with any guarded free pages. + + @param MemoryMapEntry A pointer to a descriptor in MemoryMap. + @param MaxAddress Maximum address to stop the merge. + + @return VOID + +**/ +VOID +MergeGuardPages ( + IN EFI_MEMORY_DESCRIPTOR *MemoryMapEntry, + IN EFI_PHYSICAL_ADDRESS MaxAddress + ) +{ + EFI_PHYSICAL_ADDRESS EndAddress; + UINT64 Bitmap; + INTN Pages; + + if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) || + (MemoryMapEntry->Type >= EfiMemoryMappedIO)) + { + return; + } + + Bitmap = 0; + Pages = EFI_SIZE_TO_PAGES ((UINTN)(MaxAddress - MemoryMapEntry->PhysicalStart)); + Pages -= (INTN)MemoryMapEntry->NumberOfPages; + while (Pages > 0) { + if (Bitmap == 0) { + EndAddress = MemoryMapEntry->PhysicalStart + + EFI_PAGES_TO_SIZE ((UINTN)MemoryMapEntry->NumberOfPages); + Bitmap = GetGuardedMemoryBits (EndAddress, GUARDED_HEAP_MAP_ENTRY_BITS); + } + + if ((Bitmap & 1) == 0) { + break; + } + + Pages--; + MemoryMapEntry->NumberOfPages++; + Bitmap = RShiftU64 (Bitmap, 1); + } +} + +/** + Put part (at most 64 pages a time) guarded free pages back to free page pool. + + Freed memory guard is used to detect Use-After-Free (UAF) memory issue, which + makes use of 'Used then throw away' way to detect any illegal access to freed + memory. The thrown-away memory will be marked as not-present so that any access + to those memory (after free) will be caught by page-fault exception. + + The problem is that this will consume lots of memory space. Once no memory + left in pool to allocate, we have to restore part of the freed pages to their + normal function. Otherwise the whole system will stop functioning. + + @param StartAddress Start address of promoted memory. + @param EndAddress End address of promoted memory. + + @return TRUE Succeeded to promote memory. + @return FALSE No free memory found. + +**/ +BOOLEAN +PromoteGuardedFreePages ( + OUT EFI_PHYSICAL_ADDRESS *StartAddress, + OUT EFI_PHYSICAL_ADDRESS *EndAddress + ) +{ + EFI_STATUS Status; + UINTN AvailablePages; + UINT64 Bitmap; + EFI_PHYSICAL_ADDRESS Start; + + if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED)) { + return FALSE; + } + + // + // Similar to memory allocation service, always search the freed pages in + // descending direction. + // + Start = mLastPromotedPage; + AvailablePages = 0; + while (AvailablePages == 0) { + Start -= EFI_PAGES_TO_SIZE (GUARDED_HEAP_MAP_ENTRY_BITS); + // + // If the address wraps around, try the really freed pages at top. + // + if (Start > mLastPromotedPage) { + GetLastGuardedFreePageAddress (&Start); + ASSERT (Start != 0); + Start -= EFI_PAGES_TO_SIZE (GUARDED_HEAP_MAP_ENTRY_BITS); + } + + Bitmap = GetGuardedMemoryBits (Start, GUARDED_HEAP_MAP_ENTRY_BITS); + while (Bitmap > 0) { + if ((Bitmap & 1) != 0) { + ++AvailablePages; + } else if (AvailablePages == 0) { + Start += EFI_PAGES_TO_SIZE (1); + } else { + break; + } + + Bitmap = RShiftU64 (Bitmap, 1); + } + } + + if (AvailablePages != 0) { + DEBUG ((DEBUG_INFO, "Promoted pages: %lX (%lx)\r\n", Start, (UINT64)AvailablePages)); + ClearGuardedMemoryBits (Start, AvailablePages); + + if (gCpu != NULL) { + // + // Set flag to make sure allocating memory without GUARD for page table + // operation; otherwise infinite loops could be caused. + // + mOnGuarding = TRUE; + Status = gCpu->SetMemoryAttributes (gCpu, Start, EFI_PAGES_TO_SIZE (AvailablePages), 0); + ASSERT_EFI_ERROR (Status); + mOnGuarding = FALSE; + } + + mLastPromotedPage = Start; + *StartAddress = Start; + *EndAddress = Start + EFI_PAGES_TO_SIZE (AvailablePages) - 1; + return TRUE; + } + + return FALSE; +} + +/** + Notify function used to set all Guard pages before CPU Arch Protocol installed. +**/ +VOID +HeapGuardCpuArchProtocolNotify ( + VOID + ) +{ + ASSERT (gCpu != NULL); + + if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL) && + IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED)) + { + DEBUG ((DEBUG_ERROR, "Heap guard and freed memory guard cannot be enabled at the same time.\n")); + CpuDeadLoop (); + } + + if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL)) { + SetAllGuardPages (); + } + + if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED)) { + GuardAllFreedPages (); + } +} + +/** + Helper function to convert a UINT64 value in binary to a string. + + @param[in] Value Value of a UINT64 integer. + @param[out] BinString String buffer to contain the conversion result. + + @return VOID. +**/ +VOID +Uint64ToBinString ( + IN UINT64 Value, + OUT CHAR8 *BinString + ) +{ + UINTN Index; + + if (BinString == NULL) { + return; + } + + for (Index = 64; Index > 0; --Index) { + BinString[Index - 1] = '0' + (Value & 1); + Value = RShiftU64 (Value, 1); + } + + BinString[64] = '\0'; +} + +/** + Dump the guarded memory bit map. +**/ +VOID +EFIAPI +DumpGuardedMemoryBitmap ( + VOID + ) +{ + UINTN Entries[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINTN Indices[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Tables[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH]; + UINT64 TableEntry; + UINT64 Address; + INTN Level; + UINTN RepeatZero; + CHAR8 String[GUARDED_HEAP_MAP_ENTRY_BITS + 1]; + CHAR8 *Ruler1; + CHAR8 *Ruler2; + + if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_ALL)) { + return; + } + + if ((mGuardedMemoryMap == 0) || + (mMapLevel == 0) || + (mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH)) + { + return; + } + + Ruler1 = " 3 2 1 0"; + Ruler2 = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210"; + + DEBUG (( + HEAP_GUARD_DEBUG_LEVEL, + "=============================" + " Guarded Memory Bitmap " + "==============================\r\n" + )); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, " %a\r\n", Ruler1)); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, " %a\r\n", Ruler2)); + + CopyMem (Entries, mLevelMask, sizeof (Entries)); + CopyMem (Shifts, mLevelShift, sizeof (Shifts)); + + SetMem (Indices, sizeof (Indices), 0); + SetMem (Tables, sizeof (Tables), 0); + SetMem (Addresses, sizeof (Addresses), 0); + + Level = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel; + Tables[Level] = mGuardedMemoryMap; + Address = 0; + RepeatZero = 0; + + while (TRUE) { + if (Indices[Level] > Entries[Level]) { + Tables[Level] = 0; + Level -= 1; + RepeatZero = 0; + + DEBUG (( + HEAP_GUARD_DEBUG_LEVEL, + "=========================================" + "=========================================\r\n" + )); + } else { + TableEntry = ((UINT64 *)(UINTN)Tables[Level])[Indices[Level]]; + Address = Addresses[Level]; + + if (TableEntry == 0) { + if (Level == GUARDED_HEAP_MAP_TABLE_DEPTH - 1) { + if (RepeatZero == 0) { + Uint64ToBinString (TableEntry, String); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "%016lx: %a\r\n", Address, String)); + } else if (RepeatZero == 1) { + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "... : ...\r\n")); + } + + RepeatZero += 1; + } + } else if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) { + Level += 1; + Tables[Level] = TableEntry; + Addresses[Level] = Address; + Indices[Level] = 0; + RepeatZero = 0; + + continue; + } else { + RepeatZero = 0; + Uint64ToBinString (TableEntry, String); + DEBUG ((HEAP_GUARD_DEBUG_LEVEL, "%016lx: %a\r\n", Address, String)); + } + } + + if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) { + break; + } + + Indices[Level] += 1; + Address = (Level == 0) ? 0 : Addresses[Level - 1]; + Addresses[Level] = Address | LShiftU64 (Indices[Level], Shifts[Level]); + } +} diff --git a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.h b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.h index 578e857465..fa108527c3 100644 --- a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.h +++ b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.h @@ -1,486 +1,486 @@ -/** @file
- Data type, macros and function prototypes of heap guard feature.
-
-Copyright (c) 2017-2018, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _HEAPGUARD_H_
-#define _HEAPGUARD_H_
-
-//
-// Following macros are used to define and access the guarded memory bitmap
-// table.
-//
-// To simplify the access and reduce the memory used for this table, the
-// table is constructed in the similar way as page table structure but in
-// reverse direction, i.e. from bottom growing up to top.
-//
-// - 1-bit tracks 1 page (4KB)
-// - 1-UINT64 map entry tracks 256KB memory
-// - 1K-UINT64 map table tracks 256MB memory
-// - Five levels of tables can track any address of memory of 64-bit
-// system, like below.
-//
-// 512 * 512 * 512 * 512 * 1K * 64b * 4K
-// 111111111 111111111 111111111 111111111 1111111111 111111 111111111111
-// 63 54 45 36 27 17 11 0
-// 9b 9b 9b 9b 10b 6b 12b
-// L0 -> L1 -> L2 -> L3 -> L4 -> bits -> page
-// 1FF 1FF 1FF 1FF 3FF 3F FFF
-//
-// L4 table has 1K * sizeof(UINT64) = 8K (2-page), which can track 256MB
-// memory. Each table of L0-L3 will be allocated when its memory address
-// range is to be tracked. Only 1-page will be allocated each time. This
-// can save memories used to establish this map table.
-//
-// For a normal configuration of system with 4G memory, two levels of tables
-// can track the whole memory, because two levels (L3+L4) of map tables have
-// already coverred 37-bit of memory address. And for a normal UEFI BIOS,
-// less than 128M memory would be consumed during boot. That means we just
-// need
-//
-// 1-page (L3) + 2-page (L4)
-//
-// memory (3 pages) to track the memory allocation works. In this case,
-// there's no need to setup L0-L2 tables.
-//
-
-//
-// Each entry occupies 8B/64b. 1-page can hold 512 entries, which spans 9
-// bits in address. (512 = 1 << 9)
-//
-#define BYTE_LENGTH_SHIFT 3 // (8 = 1 << 3)
-
-#define GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT \
- (EFI_PAGE_SHIFT - BYTE_LENGTH_SHIFT)
-
-#define GUARDED_HEAP_MAP_TABLE_DEPTH 5
-
-// Use UINT64_index + bit_index_of_UINT64 to locate the bit in may
-#define GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT 6 // (64 = 1 << 6)
-
-#define GUARDED_HEAP_MAP_ENTRY_BITS \
- (1 << GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT)
-
-#define GUARDED_HEAP_MAP_ENTRY_BYTES \
- (GUARDED_HEAP_MAP_ENTRY_BITS / 8)
-
-// L4 table address width: 64 - 9 * 4 - 6 - 12 = 10b
-#define GUARDED_HEAP_MAP_ENTRY_SHIFT \
- (GUARDED_HEAP_MAP_ENTRY_BITS \
- - GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 4 \
- - GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \
- - EFI_PAGE_SHIFT)
-
-// L4 table address mask: (1 << 10 - 1) = 0x3FF
-#define GUARDED_HEAP_MAP_ENTRY_MASK \
- ((1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) - 1)
-
-// Size of each L4 table: (1 << 10) * 8 = 8KB = 2-page
-#define GUARDED_HEAP_MAP_SIZE \
- ((1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) * GUARDED_HEAP_MAP_ENTRY_BYTES)
-
-// Memory size tracked by one L4 table: 8KB * 8 * 4KB = 256MB
-#define GUARDED_HEAP_MAP_UNIT_SIZE \
- (GUARDED_HEAP_MAP_SIZE * 8 * EFI_PAGE_SIZE)
-
-// L4 table entry number: 8KB / 8 = 1024
-#define GUARDED_HEAP_MAP_ENTRIES_PER_UNIT \
- (GUARDED_HEAP_MAP_SIZE / GUARDED_HEAP_MAP_ENTRY_BYTES)
-
-// L4 table entry indexing
-#define GUARDED_HEAP_MAP_ENTRY_INDEX(Address) \
- (RShiftU64 (Address, EFI_PAGE_SHIFT \
- + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) \
- & GUARDED_HEAP_MAP_ENTRY_MASK)
-
-// L4 table entry bit indexing
-#define GUARDED_HEAP_MAP_ENTRY_BIT_INDEX(Address) \
- (RShiftU64 (Address, EFI_PAGE_SHIFT) \
- & ((1 << GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) - 1))
-
-//
-// Total bits (pages) tracked by one L4 table (65536-bit)
-//
-#define GUARDED_HEAP_MAP_BITS \
- (1 << (GUARDED_HEAP_MAP_ENTRY_SHIFT \
- + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT))
-
-//
-// Bit indexing inside the whole L4 table (0 - 65535)
-//
-#define GUARDED_HEAP_MAP_BIT_INDEX(Address) \
- (RShiftU64 (Address, EFI_PAGE_SHIFT) \
- & ((1 << (GUARDED_HEAP_MAP_ENTRY_SHIFT \
- + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT)) - 1))
-
-//
-// Memory address bit width tracked by L4 table: 10 + 6 + 12 = 28
-//
-#define GUARDED_HEAP_MAP_TABLE_SHIFT \
- (GUARDED_HEAP_MAP_ENTRY_SHIFT + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \
- + EFI_PAGE_SHIFT)
-
-//
-// Macro used to initialize the local array variable for map table traversing
-// {55, 46, 37, 28, 18}
-//
-#define GUARDED_HEAP_MAP_TABLE_DEPTH_SHIFTS \
- { \
- GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 3, \
- GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 2, \
- GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT, \
- GUARDED_HEAP_MAP_TABLE_SHIFT, \
- EFI_PAGE_SHIFT + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \
- }
-
-//
-// Masks used to extract address range of each level of table
-// {0x1FF, 0x1FF, 0x1FF, 0x1FF, 0x3FF}
-//
-#define GUARDED_HEAP_MAP_TABLE_DEPTH_MASKS \
- { \
- (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \
- (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \
- (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \
- (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \
- (1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) - 1 \
- }
-
-//
-// Memory type to guard (matching the related PCD definition)
-//
-#define GUARD_HEAP_TYPE_PAGE BIT0
-#define GUARD_HEAP_TYPE_POOL BIT1
-#define GUARD_HEAP_TYPE_FREED BIT4
-#define GUARD_HEAP_TYPE_ALL \
- (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL|GUARD_HEAP_TYPE_FREED)
-
-//
-// Debug message level
-//
-#define HEAP_GUARD_DEBUG_LEVEL (DEBUG_POOL|DEBUG_PAGE)
-
-typedef struct {
- UINT32 TailMark;
- UINT32 HeadMark;
- EFI_PHYSICAL_ADDRESS Address;
- LIST_ENTRY Link;
-} HEAP_GUARD_NODE;
-
-/**
- Internal function. Converts a memory range to the specified type.
- The range must exist in the memory map.
-
- @param Start The first address of the range Must be page
- aligned.
- @param NumberOfPages The number of pages to convert.
- @param NewType The new type for the memory range.
-
- @retval EFI_INVALID_PARAMETER Invalid parameter.
- @retval EFI_NOT_FOUND Could not find a descriptor cover the specified
- range or convertion not allowed.
- @retval EFI_SUCCESS Successfully converts the memory range to the
- specified type.
-
-**/
-EFI_STATUS
-CoreConvertPages (
- IN UINT64 Start,
- IN UINT64 NumberOfPages,
- IN EFI_MEMORY_TYPE NewType
- );
-
-/**
- Allocate or free guarded memory.
-
- @param[in] Start Start address of memory to allocate or free.
- @param[in] NumberOfPages Memory size in pages.
- @param[in] NewType Memory type to convert to.
-
- @return VOID.
-**/
-EFI_STATUS
-CoreConvertPagesWithGuard (
- IN UINT64 Start,
- IN UINTN NumberOfPages,
- IN EFI_MEMORY_TYPE NewType
- );
-
-/**
- Set head Guard and tail Guard for the given memory range.
-
- @param[in] Memory Base address of memory to set guard for.
- @param[in] NumberOfPages Memory size in pages.
-
- @return VOID.
-**/
-VOID
-SetGuardForMemory (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages
- );
-
-/**
- Unset head Guard and tail Guard for the given memory range.
-
- @param[in] Memory Base address of memory to unset guard for.
- @param[in] NumberOfPages Memory size in pages.
-
- @return VOID.
-**/
-VOID
-UnsetGuardForMemory (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages
- );
-
-/**
- Adjust the base and number of pages to really allocate according to Guard.
-
- @param[in,out] Memory Base address of free memory.
- @param[in,out] NumberOfPages Size of memory to allocate.
-
- @return VOID.
-**/
-VOID
-AdjustMemoryA (
- IN OUT EFI_PHYSICAL_ADDRESS *Memory,
- IN OUT UINTN *NumberOfPages
- );
-
-/**
- Adjust the start address and number of pages to free according to Guard.
-
- The purpose of this function is to keep the shared Guard page with adjacent
- memory block if it's still in guard, or free it if no more sharing. Another
- is to reserve pages as Guard pages in partial page free situation.
-
- @param[in,out] Memory Base address of memory to free.
- @param[in,out] NumberOfPages Size of memory to free.
-
- @return VOID.
-**/
-VOID
-AdjustMemoryF (
- IN OUT EFI_PHYSICAL_ADDRESS *Memory,
- IN OUT UINTN *NumberOfPages
- );
-
-/**
- Adjust address of free memory according to existing and/or required Guard.
-
- This function will check if there're existing Guard pages of adjacent
- memory blocks, and try to use it as the Guard page of the memory to be
- allocated.
-
- @param[in] Start Start address of free memory block.
- @param[in] Size Size of free memory block.
- @param[in] SizeRequested Size of memory to allocate.
-
- @return The end address of memory block found.
- @return 0 if no enough space for the required size of memory and its Guard.
-**/
-UINT64
-AdjustMemoryS (
- IN UINT64 Start,
- IN UINT64 Size,
- IN UINT64 SizeRequested
- );
-
-/**
- Check to see if the pool at the given address should be guarded or not.
-
- @param[in] MemoryType Pool type to check.
-
-
- @return TRUE The given type of pool should be guarded.
- @return FALSE The given type of pool should not be guarded.
-**/
-BOOLEAN
-IsPoolTypeToGuard (
- IN EFI_MEMORY_TYPE MemoryType
- );
-
-/**
- Check to see if the page at the given address should be guarded or not.
-
- @param[in] MemoryType Page type to check.
- @param[in] AllocateType Allocation type to check.
-
- @return TRUE The given type of page should be guarded.
- @return FALSE The given type of page should not be guarded.
-**/
-BOOLEAN
-IsPageTypeToGuard (
- IN EFI_MEMORY_TYPE MemoryType,
- IN EFI_ALLOCATE_TYPE AllocateType
- );
-
-/**
- Check to see if the page at the given address is guarded or not.
-
- @param[in] Address The address to check for.
-
- @return TRUE The page at Address is guarded.
- @return FALSE The page at Address is not guarded.
-**/
-BOOLEAN
-EFIAPI
-IsMemoryGuarded (
- IN EFI_PHYSICAL_ADDRESS Address
- );
-
-/**
- Check to see if the page at the given address is a Guard page or not.
-
- @param[in] Address The address to check for.
-
- @return TRUE The page at Address is a Guard page.
- @return FALSE The page at Address is not a Guard page.
-**/
-BOOLEAN
-EFIAPI
-IsGuardPage (
- IN EFI_PHYSICAL_ADDRESS Address
- );
-
-/**
- Dump the guarded memory bit map.
-**/
-VOID
-EFIAPI
-DumpGuardedMemoryBitmap (
- VOID
- );
-
-/**
- Adjust the pool head position to make sure the Guard page is adjavent to
- pool tail or pool head.
-
- @param[in] Memory Base address of memory allocated.
- @param[in] NoPages Number of pages actually allocated.
- @param[in] Size Size of memory requested.
- (plus pool head/tail overhead)
-
- @return Address of pool head.
-**/
-VOID *
-AdjustPoolHeadA (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NoPages,
- IN UINTN Size
- );
-
-/**
- Get the page base address according to pool head address.
-
- @param[in] Memory Head address of pool to free.
- @param[in] NoPages Number of pages actually allocated.
- @param[in] Size Size of memory requested.
- (plus pool head/tail overhead)
-
- @return Address of pool head.
-**/
-VOID *
-AdjustPoolHeadF (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NoPages,
- IN UINTN Size
- );
-
-/**
- Check to see if the heap guard is enabled for page and/or pool allocation.
-
- @param[in] GuardType Specify the sub-type(s) of Heap Guard.
-
- @return TRUE/FALSE.
-**/
-BOOLEAN
-IsHeapGuardEnabled (
- UINT8 GuardType
- );
-
-/**
- Notify function used to set all Guard pages after CPU Arch Protocol installed.
-**/
-VOID
-HeapGuardCpuArchProtocolNotify (
- VOID
- );
-
-/**
- This function checks to see if the given memory map descriptor in a memory map
- can be merged with any guarded free pages.
-
- @param MemoryMapEntry A pointer to a descriptor in MemoryMap.
- @param MaxAddress Maximum address to stop the merge.
-
- @return VOID
-
-**/
-VOID
-MergeGuardPages (
- IN EFI_MEMORY_DESCRIPTOR *MemoryMapEntry,
- IN EFI_PHYSICAL_ADDRESS MaxAddress
- );
-
-/**
- Record freed pages as well as mark them as not-present, if enabled.
-
- @param[in] BaseAddress Base address of just freed pages.
- @param[in] Pages Number of freed pages.
-
- @return VOID.
-**/
-VOID
-EFIAPI
-GuardFreedPagesChecked (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINTN Pages
- );
-
-/**
- Put part (at most 64 pages a time) guarded free pages back to free page pool.
-
- Freed memory guard is used to detect Use-After-Free (UAF) memory issue, which
- makes use of 'Used then throw away' way to detect any illegal access to freed
- memory. The thrown-away memory will be marked as not-present so that any access
- to those memory (after free) will be caught by page-fault exception.
-
- The problem is that this will consume lots of memory space. Once no memory
- left in pool to allocate, we have to restore part of the freed pages to their
- normal function. Otherwise the whole system will stop functioning.
-
- @param StartAddress Start address of promoted memory.
- @param EndAddress End address of promoted memory.
-
- @return TRUE Succeeded to promote memory.
- @return FALSE No free memory found.
-
-**/
-BOOLEAN
-PromoteGuardedFreePages (
- OUT EFI_PHYSICAL_ADDRESS *StartAddress,
- OUT EFI_PHYSICAL_ADDRESS *EndAddress
- );
-
-extern BOOLEAN mOnGuarding;
-
-//
-// The heap guard system does not support non-EFI_PAGE_SIZE alignments.
-// Architectures that require larger RUNTIME_PAGE_ALLOCATION_GRANULARITY
-// cannot have EfiRuntimeServicesCode, EfiRuntimeServicesData, EfiReservedMemoryType,
-// and EfiACPIMemoryNVS guarded. OSes do not map guard pages anyway, so this is a
-// minimal loss. Not guarding prevents alignment mismatches
-//
-STATIC_ASSERT (
- RUNTIME_PAGE_ALLOCATION_GRANULARITY == EFI_PAGE_SIZE ||
- (((FixedPcdGet64 (PcdHeapGuardPageType) & 0x461) == 0) &&
- ((FixedPcdGet64 (PcdHeapGuardPoolType) & 0x461) == 0)),
- "Unsupported Heap Guard configuration on system with greater than EFI_PAGE_SIZE RUNTIME_PAGE_ALLOCATION_GRANULARITY"
- );
-
-#endif
+/** @file + Data type, macros and function prototypes of heap guard feature. + +Copyright (c) 2017-2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _HEAPGUARD_H_ +#define _HEAPGUARD_H_ + +// +// Following macros are used to define and access the guarded memory bitmap +// table. +// +// To simplify the access and reduce the memory used for this table, the +// table is constructed in the similar way as page table structure but in +// reverse direction, i.e. from bottom growing up to top. +// +// - 1-bit tracks 1 page (4KB) +// - 1-UINT64 map entry tracks 256KB memory +// - 1K-UINT64 map table tracks 256MB memory +// - Five levels of tables can track any address of memory of 64-bit +// system, like below. +// +// 512 * 512 * 512 * 512 * 1K * 64b * 4K +// 111111111 111111111 111111111 111111111 1111111111 111111 111111111111 +// 63 54 45 36 27 17 11 0 +// 9b 9b 9b 9b 10b 6b 12b +// L0 -> L1 -> L2 -> L3 -> L4 -> bits -> page +// 1FF 1FF 1FF 1FF 3FF 3F FFF +// +// L4 table has 1K * sizeof(UINT64) = 8K (2-page), which can track 256MB +// memory. Each table of L0-L3 will be allocated when its memory address +// range is to be tracked. Only 1-page will be allocated each time. This +// can save memories used to establish this map table. +// +// For a normal configuration of system with 4G memory, two levels of tables +// can track the whole memory, because two levels (L3+L4) of map tables have +// already coverred 37-bit of memory address. And for a normal UEFI BIOS, +// less than 128M memory would be consumed during boot. That means we just +// need +// +// 1-page (L3) + 2-page (L4) +// +// memory (3 pages) to track the memory allocation works. In this case, +// there's no need to setup L0-L2 tables. +// + +// +// Each entry occupies 8B/64b. 1-page can hold 512 entries, which spans 9 +// bits in address. (512 = 1 << 9) +// +#define BYTE_LENGTH_SHIFT 3 // (8 = 1 << 3) + +#define GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT \ + (EFI_PAGE_SHIFT - BYTE_LENGTH_SHIFT) + +#define GUARDED_HEAP_MAP_TABLE_DEPTH 5 + +// Use UINT64_index + bit_index_of_UINT64 to locate the bit in may +#define GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT 6 // (64 = 1 << 6) + +#define GUARDED_HEAP_MAP_ENTRY_BITS \ + (1 << GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) + +#define GUARDED_HEAP_MAP_ENTRY_BYTES \ + (GUARDED_HEAP_MAP_ENTRY_BITS / 8) + +// L4 table address width: 64 - 9 * 4 - 6 - 12 = 10b +#define GUARDED_HEAP_MAP_ENTRY_SHIFT \ + (GUARDED_HEAP_MAP_ENTRY_BITS \ + - GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 4 \ + - GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \ + - EFI_PAGE_SHIFT) + +// L4 table address mask: (1 << 10 - 1) = 0x3FF +#define GUARDED_HEAP_MAP_ENTRY_MASK \ + ((1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) - 1) + +// Size of each L4 table: (1 << 10) * 8 = 8KB = 2-page +#define GUARDED_HEAP_MAP_SIZE \ + ((1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) * GUARDED_HEAP_MAP_ENTRY_BYTES) + +// Memory size tracked by one L4 table: 8KB * 8 * 4KB = 256MB +#define GUARDED_HEAP_MAP_UNIT_SIZE \ + (GUARDED_HEAP_MAP_SIZE * 8 * EFI_PAGE_SIZE) + +// L4 table entry number: 8KB / 8 = 1024 +#define GUARDED_HEAP_MAP_ENTRIES_PER_UNIT \ + (GUARDED_HEAP_MAP_SIZE / GUARDED_HEAP_MAP_ENTRY_BYTES) + +// L4 table entry indexing +#define GUARDED_HEAP_MAP_ENTRY_INDEX(Address) \ + (RShiftU64 (Address, EFI_PAGE_SHIFT \ + + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) \ + & GUARDED_HEAP_MAP_ENTRY_MASK) + +// L4 table entry bit indexing +#define GUARDED_HEAP_MAP_ENTRY_BIT_INDEX(Address) \ + (RShiftU64 (Address, EFI_PAGE_SHIFT) \ + & ((1 << GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT) - 1)) + +// +// Total bits (pages) tracked by one L4 table (65536-bit) +// +#define GUARDED_HEAP_MAP_BITS \ + (1 << (GUARDED_HEAP_MAP_ENTRY_SHIFT \ + + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT)) + +// +// Bit indexing inside the whole L4 table (0 - 65535) +// +#define GUARDED_HEAP_MAP_BIT_INDEX(Address) \ + (RShiftU64 (Address, EFI_PAGE_SHIFT) \ + & ((1 << (GUARDED_HEAP_MAP_ENTRY_SHIFT \ + + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT)) - 1)) + +// +// Memory address bit width tracked by L4 table: 10 + 6 + 12 = 28 +// +#define GUARDED_HEAP_MAP_TABLE_SHIFT \ + (GUARDED_HEAP_MAP_ENTRY_SHIFT + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \ + + EFI_PAGE_SHIFT) + +// +// Macro used to initialize the local array variable for map table traversing +// {55, 46, 37, 28, 18} +// +#define GUARDED_HEAP_MAP_TABLE_DEPTH_SHIFTS \ + { \ + GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 3, \ + GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT * 2, \ + GUARDED_HEAP_MAP_TABLE_SHIFT + GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT, \ + GUARDED_HEAP_MAP_TABLE_SHIFT, \ + EFI_PAGE_SHIFT + GUARDED_HEAP_MAP_ENTRY_BIT_SHIFT \ + } + +// +// Masks used to extract address range of each level of table +// {0x1FF, 0x1FF, 0x1FF, 0x1FF, 0x3FF} +// +#define GUARDED_HEAP_MAP_TABLE_DEPTH_MASKS \ + { \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_TABLE_ENTRY_SHIFT) - 1, \ + (1 << GUARDED_HEAP_MAP_ENTRY_SHIFT) - 1 \ + } + +// +// Memory type to guard (matching the related PCD definition) +// +#define GUARD_HEAP_TYPE_PAGE BIT0 +#define GUARD_HEAP_TYPE_POOL BIT1 +#define GUARD_HEAP_TYPE_FREED BIT4 +#define GUARD_HEAP_TYPE_ALL \ + (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL|GUARD_HEAP_TYPE_FREED) + +// +// Debug message level +// +#define HEAP_GUARD_DEBUG_LEVEL (DEBUG_POOL|DEBUG_PAGE) + +typedef struct { + UINT32 TailMark; + UINT32 HeadMark; + EFI_PHYSICAL_ADDRESS Address; + LIST_ENTRY Link; +} HEAP_GUARD_NODE; + +/** + Internal function. Converts a memory range to the specified type. + The range must exist in the memory map. + + @param Start The first address of the range Must be page + aligned. + @param NumberOfPages The number of pages to convert. + @param NewType The new type for the memory range. + + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND Could not find a descriptor cover the specified + range or convertion not allowed. + @retval EFI_SUCCESS Successfully converts the memory range to the + specified type. + +**/ +EFI_STATUS +CoreConvertPages ( + IN UINT64 Start, + IN UINT64 NumberOfPages, + IN EFI_MEMORY_TYPE NewType + ); + +/** + Allocate or free guarded memory. + + @param[in] Start Start address of memory to allocate or free. + @param[in] NumberOfPages Memory size in pages. + @param[in] NewType Memory type to convert to. + + @return VOID. +**/ +EFI_STATUS +CoreConvertPagesWithGuard ( + IN UINT64 Start, + IN UINTN NumberOfPages, + IN EFI_MEMORY_TYPE NewType + ); + +/** + Set head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to set guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID. +**/ +VOID +SetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + Unset head Guard and tail Guard for the given memory range. + + @param[in] Memory Base address of memory to unset guard for. + @param[in] NumberOfPages Memory size in pages. + + @return VOID. +**/ +VOID +UnsetGuardForMemory ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + Adjust the base and number of pages to really allocate according to Guard. + + @param[in,out] Memory Base address of free memory. + @param[in,out] NumberOfPages Size of memory to allocate. + + @return VOID. +**/ +VOID +AdjustMemoryA ( + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN OUT UINTN *NumberOfPages + ); + +/** + Adjust the start address and number of pages to free according to Guard. + + The purpose of this function is to keep the shared Guard page with adjacent + memory block if it's still in guard, or free it if no more sharing. Another + is to reserve pages as Guard pages in partial page free situation. + + @param[in,out] Memory Base address of memory to free. + @param[in,out] NumberOfPages Size of memory to free. + + @return VOID. +**/ +VOID +AdjustMemoryF ( + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN OUT UINTN *NumberOfPages + ); + +/** + Adjust address of free memory according to existing and/or required Guard. + + This function will check if there're existing Guard pages of adjacent + memory blocks, and try to use it as the Guard page of the memory to be + allocated. + + @param[in] Start Start address of free memory block. + @param[in] Size Size of free memory block. + @param[in] SizeRequested Size of memory to allocate. + + @return The end address of memory block found. + @return 0 if no enough space for the required size of memory and its Guard. +**/ +UINT64 +AdjustMemoryS ( + IN UINT64 Start, + IN UINT64 Size, + IN UINT64 SizeRequested + ); + +/** + Check to see if the pool at the given address should be guarded or not. + + @param[in] MemoryType Pool type to check. + + + @return TRUE The given type of pool should be guarded. + @return FALSE The given type of pool should not be guarded. +**/ +BOOLEAN +IsPoolTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType + ); + +/** + Check to see if the page at the given address should be guarded or not. + + @param[in] MemoryType Page type to check. + @param[in] AllocateType Allocation type to check. + + @return TRUE The given type of page should be guarded. + @return FALSE The given type of page should not be guarded. +**/ +BOOLEAN +IsPageTypeToGuard ( + IN EFI_MEMORY_TYPE MemoryType, + IN EFI_ALLOCATE_TYPE AllocateType + ); + +/** + Check to see if the page at the given address is guarded or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is guarded. + @return FALSE The page at Address is not guarded. +**/ +BOOLEAN +EFIAPI +IsMemoryGuarded ( + IN EFI_PHYSICAL_ADDRESS Address + ); + +/** + Check to see if the page at the given address is a Guard page or not. + + @param[in] Address The address to check for. + + @return TRUE The page at Address is a Guard page. + @return FALSE The page at Address is not a Guard page. +**/ +BOOLEAN +EFIAPI +IsGuardPage ( + IN EFI_PHYSICAL_ADDRESS Address + ); + +/** + Dump the guarded memory bit map. +**/ +VOID +EFIAPI +DumpGuardedMemoryBitmap ( + VOID + ); + +/** + Adjust the pool head position to make sure the Guard page is adjavent to + pool tail or pool head. + + @param[in] Memory Base address of memory allocated. + @param[in] NoPages Number of pages actually allocated. + @param[in] Size Size of memory requested. + (plus pool head/tail overhead) + + @return Address of pool head. +**/ +VOID * +AdjustPoolHeadA ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages, + IN UINTN Size + ); + +/** + Get the page base address according to pool head address. + + @param[in] Memory Head address of pool to free. + @param[in] NoPages Number of pages actually allocated. + @param[in] Size Size of memory requested. + (plus pool head/tail overhead) + + @return Address of pool head. +**/ +VOID * +AdjustPoolHeadF ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages, + IN UINTN Size + ); + +/** + Check to see if the heap guard is enabled for page and/or pool allocation. + + @param[in] GuardType Specify the sub-type(s) of Heap Guard. + + @return TRUE/FALSE. +**/ +BOOLEAN +IsHeapGuardEnabled ( + UINT8 GuardType + ); + +/** + Notify function used to set all Guard pages after CPU Arch Protocol installed. +**/ +VOID +HeapGuardCpuArchProtocolNotify ( + VOID + ); + +/** + This function checks to see if the given memory map descriptor in a memory map + can be merged with any guarded free pages. + + @param MemoryMapEntry A pointer to a descriptor in MemoryMap. + @param MaxAddress Maximum address to stop the merge. + + @return VOID + +**/ +VOID +MergeGuardPages ( + IN EFI_MEMORY_DESCRIPTOR *MemoryMapEntry, + IN EFI_PHYSICAL_ADDRESS MaxAddress + ); + +/** + Record freed pages as well as mark them as not-present, if enabled. + + @param[in] BaseAddress Base address of just freed pages. + @param[in] Pages Number of freed pages. + + @return VOID. +**/ +VOID +EFIAPI +GuardFreedPagesChecked ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINTN Pages + ); + +/** + Put part (at most 64 pages a time) guarded free pages back to free page pool. + + Freed memory guard is used to detect Use-After-Free (UAF) memory issue, which + makes use of 'Used then throw away' way to detect any illegal access to freed + memory. The thrown-away memory will be marked as not-present so that any access + to those memory (after free) will be caught by page-fault exception. + + The problem is that this will consume lots of memory space. Once no memory + left in pool to allocate, we have to restore part of the freed pages to their + normal function. Otherwise the whole system will stop functioning. + + @param StartAddress Start address of promoted memory. + @param EndAddress End address of promoted memory. + + @return TRUE Succeeded to promote memory. + @return FALSE No free memory found. + +**/ +BOOLEAN +PromoteGuardedFreePages ( + OUT EFI_PHYSICAL_ADDRESS *StartAddress, + OUT EFI_PHYSICAL_ADDRESS *EndAddress + ); + +extern BOOLEAN mOnGuarding; + +// +// The heap guard system does not support non-EFI_PAGE_SIZE alignments. +// Architectures that require larger RUNTIME_PAGE_ALLOCATION_GRANULARITY +// cannot have EfiRuntimeServicesCode, EfiRuntimeServicesData, EfiReservedMemoryType, +// and EfiACPIMemoryNVS guarded. OSes do not map guard pages anyway, so this is a +// minimal loss. Not guarding prevents alignment mismatches +// +STATIC_ASSERT ( + RUNTIME_PAGE_ALLOCATION_GRANULARITY == EFI_PAGE_SIZE || + (((FixedPcdGet64 (PcdHeapGuardPageType) & 0x461) == 0) && + ((FixedPcdGet64 (PcdHeapGuardPoolType) & 0x461) == 0)), + "Unsupported Heap Guard configuration on system with greater than EFI_PAGE_SIZE RUNTIME_PAGE_ALLOCATION_GRANULARITY" + ); + +#endif diff --git a/MdeModulePkg/Core/Dxe/Mem/Imem.h b/MdeModulePkg/Core/Dxe/Mem/Imem.h index 2f0bf2bf63..684876ea9b 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Imem.h +++ b/MdeModulePkg/Core/Dxe/Mem/Imem.h @@ -1,172 +1,172 @@ -/** @file
- Data structure and functions to allocate and free memory space.
-
-Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _IMEM_H_
-#define _IMEM_H_
-
-//
-// +---------------------------------------------------+
-// | 0..(EfiMaxMemoryType - 1) - Normal memory type |
-// +---------------------------------------------------+
-// | EfiMaxMemoryType..0x6FFFFFFF - Invalid |
-// +---------------------------------------------------+
-// | 0x70000000..0x7FFFFFFF - OEM reserved |
-// +---------------------------------------------------+
-// | 0x80000000..0xFFFFFFFF - OS reserved |
-// +---------------------------------------------------+
-//
-#define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000
-#define MEMORY_TYPE_OS_RESERVED_MAX 0xFFFFFFFF
-#define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000
-#define MEMORY_TYPE_OEM_RESERVED_MAX 0x7FFFFFFF
-
-//
-// MEMORY_MAP_ENTRY
-//
-
-#define MEMORY_MAP_SIGNATURE SIGNATURE_32('m','m','a','p')
-typedef struct {
- UINTN Signature;
- LIST_ENTRY Link;
- BOOLEAN FromPages;
-
- EFI_MEMORY_TYPE Type;
- UINT64 Start;
- UINT64 End;
-
- UINT64 VirtualStart;
- UINT64 Attribute;
-} MEMORY_MAP;
-
-//
-// Internal prototypes
-//
-
-/**
- Internal function. Used by the pool functions to allocate pages
- to back pool allocation requests.
-
- @param PoolType The type of memory for the new pool pages
- @param NumberOfPages No of pages to allocate
- @param Alignment Bits to align.
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return The allocated memory, or NULL
-
-**/
-VOID *
-CoreAllocatePoolPages (
- IN EFI_MEMORY_TYPE PoolType,
- IN UINTN NumberOfPages,
- IN UINTN Alignment,
- IN BOOLEAN NeedGuard
- );
-
-/**
- Internal function. Frees pool pages allocated via AllocatePoolPages ()
-
- @param Memory The base address to free
- @param NumberOfPages The number of pages to free
-
-**/
-VOID
-CoreFreePoolPages (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages
- );
-
-/**
- Internal function to allocate pool of a particular type.
- Caller must have the memory lock held
-
- @param PoolType Type of pool to allocate
- @param Size The amount of pool to allocate
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return The allocate pool, or NULL
-
-**/
-VOID *
-CoreAllocatePoolI (
- IN EFI_MEMORY_TYPE PoolType,
- IN UINTN Size,
- IN BOOLEAN NeedGuard
- );
-
-/**
- Internal function to free a pool entry.
- Caller must have the memory lock held
-
- @param Buffer The allocated pool entry to free
- @param PoolType Pointer to pool type
-
- @retval EFI_INVALID_PARAMETER Buffer not valid
- @retval EFI_SUCCESS Buffer successfully freed.
-
-**/
-EFI_STATUS
-CoreFreePoolI (
- IN VOID *Buffer,
- OUT EFI_MEMORY_TYPE *PoolType OPTIONAL
- );
-
-/**
- Enter critical section by gaining lock on gMemoryLock.
-
-**/
-VOID
-CoreAcquireMemoryLock (
- VOID
- );
-
-/**
- Exit critical section by releasing lock on gMemoryLock.
-
-**/
-VOID
-CoreReleaseMemoryLock (
- VOID
- );
-
-/**
- Allocates pages from the memory map.
-
- @param Type The type of allocation to perform
- @param MemoryType The type of memory to turn the allocated pages
- into
- @param NumberOfPages The number of pages to allocate
- @param Memory A pointer to receive the base allocated memory
- address
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return Status. On success, Memory is filled in with the base address allocated
- @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in
- spec.
- @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
- @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
- @retval EFI_SUCCESS Pages successfully allocated.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreInternalAllocatePages (
- IN EFI_ALLOCATE_TYPE Type,
- IN EFI_MEMORY_TYPE MemoryType,
- IN UINTN NumberOfPages,
- IN OUT EFI_PHYSICAL_ADDRESS *Memory,
- IN BOOLEAN NeedGuard
- );
-
-//
-// Internal Global data
-//
-
-extern EFI_LOCK gMemoryLock;
-extern LIST_ENTRY gMemoryMap;
-extern LIST_ENTRY mGcdMemorySpaceMap;
-#endif
+/** @file + Data structure and functions to allocate and free memory space. + +Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _IMEM_H_ +#define _IMEM_H_ + +// +// +---------------------------------------------------+ +// | 0..(EfiMaxMemoryType - 1) - Normal memory type | +// +---------------------------------------------------+ +// | EfiMaxMemoryType..0x6FFFFFFF - Invalid | +// +---------------------------------------------------+ +// | 0x70000000..0x7FFFFFFF - OEM reserved | +// +---------------------------------------------------+ +// | 0x80000000..0xFFFFFFFF - OS reserved | +// +---------------------------------------------------+ +// +#define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000 +#define MEMORY_TYPE_OS_RESERVED_MAX 0xFFFFFFFF +#define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000 +#define MEMORY_TYPE_OEM_RESERVED_MAX 0x7FFFFFFF + +// +// MEMORY_MAP_ENTRY +// + +#define MEMORY_MAP_SIGNATURE SIGNATURE_32('m','m','a','p') +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + BOOLEAN FromPages; + + EFI_MEMORY_TYPE Type; + UINT64 Start; + UINT64 End; + + UINT64 VirtualStart; + UINT64 Attribute; +} MEMORY_MAP; + +// +// Internal prototypes +// + +/** + Internal function. Used by the pool functions to allocate pages + to back pool allocation requests. + + @param PoolType The type of memory for the new pool pages + @param NumberOfPages No of pages to allocate + @param Alignment Bits to align. + @param NeedGuard Flag to indicate Guard page is needed or not + + @return The allocated memory, or NULL + +**/ +VOID * +CoreAllocatePoolPages ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN NumberOfPages, + IN UINTN Alignment, + IN BOOLEAN NeedGuard + ); + +/** + Internal function. Frees pool pages allocated via AllocatePoolPages () + + @param Memory The base address to free + @param NumberOfPages The number of pages to free + +**/ +VOID +CoreFreePoolPages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + Internal function to allocate pool of a particular type. + Caller must have the memory lock held + + @param PoolType Type of pool to allocate + @param Size The amount of pool to allocate + @param NeedGuard Flag to indicate Guard page is needed or not + + @return The allocate pool, or NULL + +**/ +VOID * +CoreAllocatePoolI ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + IN BOOLEAN NeedGuard + ); + +/** + Internal function to free a pool entry. + Caller must have the memory lock held + + @param Buffer The allocated pool entry to free + @param PoolType Pointer to pool type + + @retval EFI_INVALID_PARAMETER Buffer not valid + @retval EFI_SUCCESS Buffer successfully freed. + +**/ +EFI_STATUS +CoreFreePoolI ( + IN VOID *Buffer, + OUT EFI_MEMORY_TYPE *PoolType OPTIONAL + ); + +/** + Enter critical section by gaining lock on gMemoryLock. + +**/ +VOID +CoreAcquireMemoryLock ( + VOID + ); + +/** + Exit critical section by releasing lock on gMemoryLock. + +**/ +VOID +CoreReleaseMemoryLock ( + VOID + ); + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform + @param MemoryType The type of memory to turn the allocated pages + into + @param NumberOfPages The number of pages to allocate + @param Memory A pointer to receive the base allocated memory + address + @param NeedGuard Flag to indicate Guard page is needed or not + + @return Status. On success, Memory is filled in with the base address allocated + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in + spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +CoreInternalAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN NeedGuard + ); + +// +// Internal Global data +// + +extern EFI_LOCK gMemoryLock; +extern LIST_ENTRY gMemoryMap; +extern LIST_ENTRY mGcdMemorySpaceMap; +#endif diff --git a/MdeModulePkg/Core/Dxe/Mem/MemData.c b/MdeModulePkg/Core/Dxe/Mem/MemData.c index 8f138b703e..83b74f8c12 100644 --- a/MdeModulePkg/Core/Dxe/Mem/MemData.c +++ b/MdeModulePkg/Core/Dxe/Mem/MemData.c @@ -1,19 +1,19 @@ -/** @file
- Global data used in memory service
-
-Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "DxeMain.h"
-
-//
-// MemoryLock - synchronizes access to the memory map and pool lists
-//
-EFI_LOCK gMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
-
-//
-// MemoryMap - the current memory map
-//
-LIST_ENTRY gMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap);
+/** @file + Global data used in memory service + +Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DxeMain.h" + +// +// MemoryLock - synchronizes access to the memory map and pool lists +// +EFI_LOCK gMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY); + +// +// MemoryMap - the current memory map +// +LIST_ENTRY gMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap); diff --git a/MdeModulePkg/Core/Dxe/Mem/MemoryProfileRecord.c b/MdeModulePkg/Core/Dxe/Mem/MemoryProfileRecord.c index 00e33b707d..0401e19a5c 100644 --- a/MdeModulePkg/Core/Dxe/Mem/MemoryProfileRecord.c +++ b/MdeModulePkg/Core/Dxe/Mem/MemoryProfileRecord.c @@ -1,1792 +1,1792 @@ -/** @file
- Support routines for UEFI memory profile.
-
- Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "DxeMain.h"
-#include "Imem.h"
-
-#define IS_UEFI_MEMORY_PROFILE_ENABLED ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0)
-
-#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \
- ((ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1)))
-
-typedef struct {
- UINT32 Signature;
- MEMORY_PROFILE_CONTEXT Context;
- LIST_ENTRY *DriverInfoList;
-} MEMORY_PROFILE_CONTEXT_DATA;
-
-typedef struct {
- UINT32 Signature;
- MEMORY_PROFILE_DRIVER_INFO DriverInfo;
- LIST_ENTRY *AllocInfoList;
- CHAR8 *PdbString;
- LIST_ENTRY Link;
-} MEMORY_PROFILE_DRIVER_INFO_DATA;
-
-typedef struct {
- UINT32 Signature;
- MEMORY_PROFILE_ALLOC_INFO AllocInfo;
- CHAR8 *ActionString;
- LIST_ENTRY Link;
-} MEMORY_PROFILE_ALLOC_INFO_DATA;
-
-GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mImageQueue = INITIALIZE_LIST_HEAD_VARIABLE (mImageQueue);
-GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA mMemoryProfileContext = {
- MEMORY_PROFILE_CONTEXT_SIGNATURE,
- {
- {
- MEMORY_PROFILE_CONTEXT_SIGNATURE,
- sizeof (MEMORY_PROFILE_CONTEXT),
- MEMORY_PROFILE_CONTEXT_REVISION
- },
- 0,
- 0,
- { 0 },
- { 0 },
- 0,
- 0,
- 0
- },
- &mImageQueue,
-};
-GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA *mMemoryProfileContextPtr = NULL;
-
-GLOBAL_REMOVE_IF_UNREFERENCED EFI_LOCK mMemoryProfileLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
-GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mMemoryProfileGettingStatus = FALSE;
-GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE;
-GLOBAL_REMOVE_IF_UNREFERENCED EFI_DEVICE_PATH_PROTOCOL *mMemoryProfileDriverPath;
-GLOBAL_REMOVE_IF_UNREFERENCED UINTN mMemoryProfileDriverPathSize;
-
-/**
- Get memory profile data.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer.
- On return, points to the size of the data returned in ProfileBuffer.
- @param[out] ProfileBuffer Profile buffer.
-
- @return EFI_SUCCESS Get the memory profile data successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported.
- @return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data.
- ProfileSize is updated with the size required.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolGetData (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN OUT UINT64 *ProfileSize,
- OUT VOID *ProfileBuffer
- );
-
-/**
- Register image to memory profile.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] FilePath File path of the image.
- @param[in] ImageBase Image base address.
- @param[in] ImageSize Image size.
- @param[in] FileType File type of the image.
-
- @return EFI_SUCCESS Register successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required.
- @return EFI_OUT_OF_RESOURCE No enough resource for this register.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolRegisterImage (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
- IN PHYSICAL_ADDRESS ImageBase,
- IN UINT64 ImageSize,
- IN EFI_FV_FILETYPE FileType
- );
-
-/**
- Unregister image from memory profile.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] FilePath File path of the image.
- @param[in] ImageBase Image base address.
- @param[in] ImageSize Image size.
-
- @return EFI_SUCCESS Unregister successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required.
- @return EFI_NOT_FOUND The image is not found.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolUnregisterImage (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
- IN PHYSICAL_ADDRESS ImageBase,
- IN UINT64 ImageSize
- );
-
-/**
- Get memory profile recording state.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[out] RecordingState Recording state.
-
- @return EFI_SUCCESS Memory profile recording state is returned.
- @return EFI_UNSUPPORTED Memory profile is unsupported.
- @return EFI_INVALID_PARAMETER RecordingState is NULL.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolGetRecordingState (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- OUT BOOLEAN *RecordingState
- );
-
-/**
- Set memory profile recording state.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] RecordingState Recording state.
-
- @return EFI_SUCCESS Set memory profile recording state successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolSetRecordingState (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN BOOLEAN RecordingState
- );
-
-/**
- Record memory profile of multilevel caller.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] CallerAddress Address of caller.
- @param[in] Action Memory profile action.
- @param[in] MemoryType Memory type.
- EfiMaxMemoryType means the MemoryType is unknown.
- @param[in] Buffer Buffer address.
- @param[in] Size Buffer size.
- @param[in] ActionString String for memory profile action.
- Only needed for user defined allocate action.
-
- @return EFI_SUCCESS Memory profile is updated.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required,
- or memory profile for the memory type is not required.
- @return EFI_ACCESS_DENIED It is during memory profile data getting.
- @return EFI_ABORTED Memory profile recording is not enabled.
- @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
- @return EFI_NOT_FOUND No matched allocate info found for free action.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolRecord (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN PHYSICAL_ADDRESS CallerAddress,
- IN MEMORY_PROFILE_ACTION Action,
- IN EFI_MEMORY_TYPE MemoryType,
- IN VOID *Buffer,
- IN UINTN Size,
- IN CHAR8 *ActionString OPTIONAL
- );
-
-GLOBAL_REMOVE_IF_UNREFERENCED EDKII_MEMORY_PROFILE_PROTOCOL mProfileProtocol = {
- ProfileProtocolGetData,
- ProfileProtocolRegisterImage,
- ProfileProtocolUnregisterImage,
- ProfileProtocolGetRecordingState,
- ProfileProtocolSetRecordingState,
- ProfileProtocolRecord,
-};
-
-/**
- Acquire lock on mMemoryProfileLock.
-**/
-VOID
-CoreAcquireMemoryProfileLock (
- VOID
- )
-{
- CoreAcquireLock (&mMemoryProfileLock);
-}
-
-/**
- Release lock on mMemoryProfileLock.
-**/
-VOID
-CoreReleaseMemoryProfileLock (
- VOID
- )
-{
- CoreReleaseLock (&mMemoryProfileLock);
-}
-
-/**
- Return memory profile context.
-
- @return Memory profile context.
-
-**/
-MEMORY_PROFILE_CONTEXT_DATA *
-GetMemoryProfileContext (
- VOID
- )
-{
- return mMemoryProfileContextPtr;
-}
-
-/**
- Retrieves and returns the Subsystem of a PE/COFF image that has been loaded into system memory.
- If Pe32Data is NULL, then ASSERT().
-
- @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory.
-
- @return The Subsystem of the PE/COFF image.
-
-**/
-UINT16
-InternalPeCoffGetSubsystem (
- IN VOID *Pe32Data
- )
-{
- EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
- EFI_IMAGE_DOS_HEADER *DosHdr;
- UINT16 Magic;
-
- ASSERT (Pe32Data != NULL);
-
- DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
- if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
- //
- // DOS image header is present, so read the PE header after the DOS image header.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff));
- } else {
- //
- // DOS image header is not present, so PE header is at the image base.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
- }
-
- if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
- return Hdr.Te->Subsystem;
- } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
- Magic = Hdr.Pe32->OptionalHeader.Magic;
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
- return Hdr.Pe32->OptionalHeader.Subsystem;
- } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
- return Hdr.Pe32Plus->OptionalHeader.Subsystem;
- }
- }
-
- return 0x0000;
-}
-
-/**
- Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded
- into system memory with the PE/COFF Loader Library functions.
-
- Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry
- point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then
- return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS.
- If Pe32Data is NULL, then ASSERT().
- If EntryPoint is NULL, then ASSERT().
-
- @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory.
- @param EntryPoint The pointer to entry point to the PE/COFF image to return.
-
- @retval RETURN_SUCCESS EntryPoint was returned.
- @retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image.
-
-**/
-RETURN_STATUS
-InternalPeCoffGetEntryPoint (
- IN VOID *Pe32Data,
- OUT VOID **EntryPoint
- )
-{
- EFI_IMAGE_DOS_HEADER *DosHdr;
- EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
-
- ASSERT (Pe32Data != NULL);
- ASSERT (EntryPoint != NULL);
-
- DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
- if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
- //
- // DOS image header is present, so read the PE header after the DOS image header.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff));
- } else {
- //
- // DOS image header is not present, so PE header is at the image base.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
- }
-
- //
- // Calculate the entry point relative to the start of the image.
- // AddressOfEntryPoint is common for PE32 & PE32+
- //
- if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
- *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize);
- return RETURN_SUCCESS;
- } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
- *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff));
- return RETURN_SUCCESS;
- }
-
- return RETURN_UNSUPPORTED;
-}
-
-/**
- Build driver info.
-
- @param ContextData Memory profile context.
- @param FileName File name of the image.
- @param ImageBase Image base address.
- @param ImageSize Image size.
- @param EntryPoint Entry point of the image.
- @param ImageSubsystem Image subsystem of the image.
- @param FileType File type of the image.
-
- @return Pointer to memory profile driver info.
-
-**/
-MEMORY_PROFILE_DRIVER_INFO_DATA *
-BuildDriverInfo (
- IN MEMORY_PROFILE_CONTEXT_DATA *ContextData,
- IN EFI_GUID *FileName,
- IN PHYSICAL_ADDRESS ImageBase,
- IN UINT64 ImageSize,
- IN PHYSICAL_ADDRESS EntryPoint,
- IN UINT16 ImageSubsystem,
- IN EFI_FV_FILETYPE FileType
- )
-{
- EFI_STATUS Status;
- MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- VOID *EntryPointInImage;
- CHAR8 *PdbString;
- UINTN PdbSize;
- UINTN PdbOccupiedSize;
-
- PdbSize = 0;
- PdbOccupiedSize = 0;
- PdbString = NULL;
- if (ImageBase != 0) {
- PdbString = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageBase);
- if (PdbString != NULL) {
- PdbSize = AsciiStrSize (PdbString);
- PdbOccupiedSize = GET_OCCUPIED_SIZE (PdbSize, sizeof (UINT64));
- }
- }
-
- //
- // Use CoreInternalAllocatePool() that will not update profile for this AllocatePool action.
- //
- Status = CoreInternalAllocatePool (
- EfiBootServicesData,
- sizeof (*DriverInfoData) + sizeof (LIST_ENTRY) + PdbSize,
- (VOID **)&DriverInfoData
- );
- if (EFI_ERROR (Status)) {
- return NULL;
- }
-
- ASSERT (DriverInfoData != NULL);
-
- ZeroMem (DriverInfoData, sizeof (*DriverInfoData));
-
- DriverInfo = &DriverInfoData->DriverInfo;
- DriverInfoData->Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE;
- DriverInfo->Header.Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE;
- DriverInfo->Header.Length = (UINT16)(sizeof (MEMORY_PROFILE_DRIVER_INFO) + PdbOccupiedSize);
- DriverInfo->Header.Revision = MEMORY_PROFILE_DRIVER_INFO_REVISION;
- if (FileName != NULL) {
- CopyMem (&DriverInfo->FileName, FileName, sizeof (EFI_GUID));
- }
-
- DriverInfo->ImageBase = ImageBase;
- DriverInfo->ImageSize = ImageSize;
- DriverInfo->EntryPoint = EntryPoint;
- DriverInfo->ImageSubsystem = ImageSubsystem;
- if ((EntryPoint != 0) && ((EntryPoint < ImageBase) || (EntryPoint >= (ImageBase + ImageSize)))) {
- //
- // If the EntryPoint is not in the range of image buffer, it should come from emulation environment.
- // So patch ImageBuffer here to align the EntryPoint.
- //
- Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage);
- ASSERT_EFI_ERROR (Status);
- DriverInfo->ImageBase = ImageBase + EntryPoint - (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage;
- }
-
- DriverInfo->FileType = FileType;
- DriverInfoData->AllocInfoList = (LIST_ENTRY *)(DriverInfoData + 1);
- InitializeListHead (DriverInfoData->AllocInfoList);
- DriverInfo->CurrentUsage = 0;
- DriverInfo->PeakUsage = 0;
- DriverInfo->AllocRecordCount = 0;
- if (PdbSize != 0) {
- DriverInfo->PdbStringOffset = (UINT16)sizeof (MEMORY_PROFILE_DRIVER_INFO);
- DriverInfoData->PdbString = (CHAR8 *)(DriverInfoData->AllocInfoList + 1);
- CopyMem (DriverInfoData->PdbString, PdbString, PdbSize);
- } else {
- DriverInfo->PdbStringOffset = 0;
- DriverInfoData->PdbString = NULL;
- }
-
- InsertTailList (ContextData->DriverInfoList, &DriverInfoData->Link);
- ContextData->Context.ImageCount++;
- ContextData->Context.TotalImageSize += DriverInfo->ImageSize;
-
- return DriverInfoData;
-}
-
-/**
- Return if record for this driver is needed..
-
- @param DriverFilePath Driver file path.
-
- @retval TRUE Record for this driver is needed.
- @retval FALSE Record for this driver is not needed.
-
-**/
-BOOLEAN
-NeedRecordThisDriver (
- IN EFI_DEVICE_PATH_PROTOCOL *DriverFilePath
- )
-{
- EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
- EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance;
- UINTN DevicePathSize;
- UINTN FilePathSize;
-
- if (!IsDevicePathValid (mMemoryProfileDriverPath, mMemoryProfileDriverPathSize)) {
- //
- // Invalid Device Path means record all.
- //
- return TRUE;
- }
-
- //
- // Record FilePath without END node.
- //
- FilePathSize = GetDevicePathSize (DriverFilePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL);
-
- DevicePathInstance = mMemoryProfileDriverPath;
- do {
- //
- // Find END node (it might be END_ENTIRE or END_INSTANCE).
- //
- TmpDevicePath = DevicePathInstance;
- while (!IsDevicePathEndType (TmpDevicePath)) {
- TmpDevicePath = NextDevicePathNode (TmpDevicePath);
- }
-
- //
- // Do not compare END node.
- //
- DevicePathSize = (UINTN)TmpDevicePath - (UINTN)DevicePathInstance;
- if ((FilePathSize == DevicePathSize) &&
- (CompareMem (DriverFilePath, DevicePathInstance, DevicePathSize) == 0))
- {
- return TRUE;
- }
-
- //
- // Get next instance.
- //
- DevicePathInstance = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN)DevicePathInstance + DevicePathSize + DevicePathNodeLength (TmpDevicePath));
- } while (DevicePathSubType (TmpDevicePath) != END_ENTIRE_DEVICE_PATH_SUBTYPE);
-
- return FALSE;
-}
-
-/**
- Register DXE Core to memory profile.
-
- @param HobStart The start address of the HOB.
- @param ContextData Memory profile context.
-
- @retval TRUE Register success.
- @retval FALSE Register fail.
-
-**/
-BOOLEAN
-RegisterDxeCore (
- IN VOID *HobStart,
- IN MEMORY_PROFILE_CONTEXT_DATA *ContextData
- )
-{
- EFI_PEI_HOB_POINTERS DxeCoreHob;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- PHYSICAL_ADDRESS ImageBase;
- UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)];
- MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath;
-
- ASSERT (ContextData != NULL);
-
- //
- // Searching for image hob
- //
- DxeCoreHob.Raw = HobStart;
- while ((DxeCoreHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, DxeCoreHob.Raw)) != NULL) {
- if (CompareGuid (&DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.Name, &gEfiHobMemoryAllocModuleGuid)) {
- //
- // Find Dxe Core HOB
- //
- break;
- }
-
- DxeCoreHob.Raw = GET_NEXT_HOB (DxeCoreHob);
- }
-
- ASSERT (DxeCoreHob.Raw != NULL);
-
- FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer;
- EfiInitializeFwVolDevicepathNode (FilePath, &DxeCoreHob.MemoryAllocationModule->ModuleName);
- SetDevicePathEndNode (FilePath + 1);
-
- if (!NeedRecordThisDriver ((EFI_DEVICE_PATH_PROTOCOL *)FilePath)) {
- return FALSE;
- }
-
- ImageBase = DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryBaseAddress;
- DriverInfoData = BuildDriverInfo (
- ContextData,
- &DxeCoreHob.MemoryAllocationModule->ModuleName,
- ImageBase,
- DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryLength,
- DxeCoreHob.MemoryAllocationModule->EntryPoint,
- InternalPeCoffGetSubsystem ((VOID *)(UINTN)ImageBase),
- EFI_FV_FILETYPE_DXE_CORE
- );
- if (DriverInfoData == NULL) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- Initialize memory profile.
-
- @param HobStart The start address of the HOB.
-
-**/
-VOID
-MemoryProfileInit (
- IN VOID *HobStart
- )
-{
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
-
- if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
- return;
- }
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData != NULL) {
- return;
- }
-
- mMemoryProfileGettingStatus = FALSE;
- if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT7) != 0) {
- mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE;
- } else {
- mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_ENABLE;
- }
-
- mMemoryProfileDriverPathSize = PcdGetSize (PcdMemoryProfileDriverPath);
- mMemoryProfileDriverPath = AllocateCopyPool (mMemoryProfileDriverPathSize, PcdGetPtr (PcdMemoryProfileDriverPath));
- mMemoryProfileContextPtr = &mMemoryProfileContext;
-
- RegisterDxeCore (HobStart, &mMemoryProfileContext);
-
- DEBUG ((DEBUG_INFO, "MemoryProfileInit MemoryProfileContext - 0x%x\n", &mMemoryProfileContext));
-}
-
-/**
- Install memory profile protocol.
-
-**/
-VOID
-MemoryProfileInstallProtocol (
- VOID
- )
-{
- EFI_HANDLE Handle;
- EFI_STATUS Status;
-
- if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
- return;
- }
-
- Handle = NULL;
- Status = CoreInstallMultipleProtocolInterfaces (
- &Handle,
- &gEdkiiMemoryProfileGuid,
- &mProfileProtocol,
- NULL
- );
- ASSERT_EFI_ERROR (Status);
-}
-
-/**
- Get the GUID file name from the file path.
-
- @param FilePath File path.
-
- @return The GUID file name from the file path.
-
-**/
-EFI_GUID *
-GetFileNameFromFilePath (
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath
- )
-{
- MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *ThisFilePath;
- EFI_GUID *FileName;
-
- FileName = NULL;
- if (FilePath != NULL) {
- ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)FilePath;
- while (!IsDevicePathEnd (ThisFilePath)) {
- FileName = EfiGetNameGuidFromFwVolDevicePathNode (ThisFilePath);
- if (FileName != NULL) {
- break;
- }
-
- ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)NextDevicePathNode (ThisFilePath);
- }
- }
-
- return FileName;
-}
-
-/**
- Register image to memory profile.
-
- @param DriverEntry Image info.
- @param FileType Image file type.
-
- @return EFI_SUCCESS Register successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required.
- @return EFI_OUT_OF_RESOURCES No enough resource for this register.
-
-**/
-EFI_STATUS
-RegisterMemoryProfileImage (
- IN LOADED_IMAGE_PRIVATE_DATA *DriverEntry,
- IN EFI_FV_FILETYPE FileType
- )
-{
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
-
- if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
- return EFI_UNSUPPORTED;
- }
-
- if (!NeedRecordThisDriver (DriverEntry->Info.FilePath)) {
- return EFI_UNSUPPORTED;
- }
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- DriverInfoData = BuildDriverInfo (
- ContextData,
- GetFileNameFromFilePath (DriverEntry->Info.FilePath),
- DriverEntry->ImageContext.ImageAddress,
- DriverEntry->ImageContext.ImageSize,
- DriverEntry->ImageContext.EntryPoint,
- DriverEntry->ImageContext.ImageType,
- FileType
- );
- if (DriverInfoData == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- Search image from memory profile.
-
- @param ContextData Memory profile context.
- @param FileName Image file name.
- @param Address Image Address.
-
- @return Pointer to memory profile driver info.
-
-**/
-MEMORY_PROFILE_DRIVER_INFO_DATA *
-GetMemoryProfileDriverInfoByFileNameAndAddress (
- IN MEMORY_PROFILE_CONTEXT_DATA *ContextData,
- IN EFI_GUID *FileName,
- IN PHYSICAL_ADDRESS Address
- )
-{
- MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- LIST_ENTRY *DriverLink;
- LIST_ENTRY *DriverInfoList;
-
- DriverInfoList = ContextData->DriverInfoList;
-
- for (DriverLink = DriverInfoList->ForwardLink;
- DriverLink != DriverInfoList;
- DriverLink = DriverLink->ForwardLink)
- {
- DriverInfoData = CR (
- DriverLink,
- MEMORY_PROFILE_DRIVER_INFO_DATA,
- Link,
- MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
- );
- DriverInfo = &DriverInfoData->DriverInfo;
- if ((CompareGuid (&DriverInfo->FileName, FileName)) &&
- (Address >= DriverInfo->ImageBase) &&
- (Address < (DriverInfo->ImageBase + DriverInfo->ImageSize)))
- {
- return DriverInfoData;
- }
- }
-
- return NULL;
-}
-
-/**
- Search image from memory profile.
- It will return image, if (Address >= ImageBuffer) AND (Address < ImageBuffer + ImageSize).
-
- @param ContextData Memory profile context.
- @param Address Image or Function address.
-
- @return Pointer to memory profile driver info.
-
-**/
-MEMORY_PROFILE_DRIVER_INFO_DATA *
-GetMemoryProfileDriverInfoFromAddress (
- IN MEMORY_PROFILE_CONTEXT_DATA *ContextData,
- IN PHYSICAL_ADDRESS Address
- )
-{
- MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- LIST_ENTRY *DriverLink;
- LIST_ENTRY *DriverInfoList;
-
- DriverInfoList = ContextData->DriverInfoList;
-
- for (DriverLink = DriverInfoList->ForwardLink;
- DriverLink != DriverInfoList;
- DriverLink = DriverLink->ForwardLink)
- {
- DriverInfoData = CR (
- DriverLink,
- MEMORY_PROFILE_DRIVER_INFO_DATA,
- Link,
- MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
- );
- DriverInfo = &DriverInfoData->DriverInfo;
- if ((Address >= DriverInfo->ImageBase) &&
- (Address < (DriverInfo->ImageBase + DriverInfo->ImageSize)))
- {
- return DriverInfoData;
- }
- }
-
- return NULL;
-}
-
-/**
- Unregister image from memory profile.
-
- @param DriverEntry Image info.
-
- @return EFI_SUCCESS Unregister successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required.
- @return EFI_NOT_FOUND The image is not found.
-
-**/
-EFI_STATUS
-UnregisterMemoryProfileImage (
- IN LOADED_IMAGE_PRIVATE_DATA *DriverEntry
- )
-{
- EFI_STATUS Status;
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- EFI_GUID *FileName;
- PHYSICAL_ADDRESS ImageAddress;
- VOID *EntryPointInImage;
-
- if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
- return EFI_UNSUPPORTED;
- }
-
- if (!NeedRecordThisDriver (DriverEntry->Info.FilePath)) {
- return EFI_UNSUPPORTED;
- }
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- DriverInfoData = NULL;
- FileName = GetFileNameFromFilePath (DriverEntry->Info.FilePath);
- ImageAddress = DriverEntry->ImageContext.ImageAddress;
- if ((DriverEntry->ImageContext.EntryPoint < ImageAddress) || (DriverEntry->ImageContext.EntryPoint >= (ImageAddress + DriverEntry->ImageContext.ImageSize))) {
- //
- // If the EntryPoint is not in the range of image buffer, it should come from emulation environment.
- // So patch ImageAddress here to align the EntryPoint.
- //
- Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageAddress, &EntryPointInImage);
- ASSERT_EFI_ERROR (Status);
- ImageAddress = ImageAddress + (UINTN)DriverEntry->ImageContext.EntryPoint - (UINTN)EntryPointInImage;
- }
-
- if (FileName != NULL) {
- DriverInfoData = GetMemoryProfileDriverInfoByFileNameAndAddress (ContextData, FileName, ImageAddress);
- }
-
- if (DriverInfoData == NULL) {
- DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, ImageAddress);
- }
-
- if (DriverInfoData == NULL) {
- return EFI_NOT_FOUND;
- }
-
- ContextData->Context.TotalImageSize -= DriverInfoData->DriverInfo.ImageSize;
-
- // Keep the ImageBase for RVA calculation in Application.
- // DriverInfoData->DriverInfo.ImageBase = 0;
- DriverInfoData->DriverInfo.ImageSize = 0;
-
- if (DriverInfoData->DriverInfo.PeakUsage == 0) {
- ContextData->Context.ImageCount--;
- RemoveEntryList (&DriverInfoData->Link);
- //
- // Use CoreInternalFreePool() that will not update profile for this FreePool action.
- //
- CoreInternalFreePool (DriverInfoData, NULL);
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- Return if this memory type needs to be recorded into memory profile.
- If BIOS memory type (0 ~ EfiMaxMemoryType - 1), it checks bit (1 << MemoryType).
- If OS memory type (0x80000000 ~ 0xFFFFFFFF), it checks bit63 - 0x8000000000000000.
- If OEM memory type (0x70000000 ~ 0x7FFFFFFF), it checks bit62 - 0x4000000000000000.
-
- @param MemoryType Memory type.
-
- @retval TRUE This memory type need to be recorded.
- @retval FALSE This memory type need not to be recorded.
-
-**/
-BOOLEAN
-CoreNeedRecordProfile (
- IN EFI_MEMORY_TYPE MemoryType
- )
-{
- UINT64 TestBit;
-
- if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
- TestBit = BIT63;
- } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
- TestBit = BIT62;
- } else {
- TestBit = LShiftU64 (1, MemoryType);
- }
-
- if ((PcdGet64 (PcdMemoryProfileMemoryType) & TestBit) != 0) {
- return TRUE;
- } else {
- return FALSE;
- }
-}
-
-/**
- Convert EFI memory type to profile memory index. The rule is:
- If BIOS memory type (0 ~ EfiMaxMemoryType - 1), ProfileMemoryIndex = MemoryType.
- If OS memory type (0x80000000 ~ 0xFFFFFFFF), ProfileMemoryIndex = EfiMaxMemoryType.
- If OEM memory type (0x70000000 ~ 0x7FFFFFFF), ProfileMemoryIndex = EfiMaxMemoryType + 1.
-
- @param MemoryType Memory type.
-
- @return Profile memory index.
-
-**/
-UINTN
-GetProfileMemoryIndex (
- IN EFI_MEMORY_TYPE MemoryType
- )
-{
- if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
- return EfiMaxMemoryType;
- } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
- return EfiMaxMemoryType + 1;
- } else {
- return MemoryType;
- }
-}
-
-/**
- Update memory profile Allocate information.
-
- @param CallerAddress Address of caller who call Allocate.
- @param Action This Allocate action.
- @param MemoryType Memory type.
- @param Size Buffer size.
- @param Buffer Buffer address.
- @param ActionString String for memory profile action.
-
- @return EFI_SUCCESS Memory profile is updated.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required.
- @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
-
-**/
-EFI_STATUS
-CoreUpdateProfileAllocate (
- IN PHYSICAL_ADDRESS CallerAddress,
- IN MEMORY_PROFILE_ACTION Action,
- IN EFI_MEMORY_TYPE MemoryType,
- IN UINTN Size,
- IN VOID *Buffer,
- IN CHAR8 *ActionString OPTIONAL
- )
-{
- EFI_STATUS Status;
- MEMORY_PROFILE_CONTEXT *Context;
- MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
- MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
- UINTN ProfileMemoryIndex;
- MEMORY_PROFILE_ACTION BasicAction;
- UINTN ActionStringSize;
- UINTN ActionStringOccupiedSize;
-
- BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK;
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress);
- if (DriverInfoData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- ActionStringSize = 0;
- ActionStringOccupiedSize = 0;
- if (ActionString != NULL) {
- ActionStringSize = AsciiStrSize (ActionString);
- ActionStringOccupiedSize = GET_OCCUPIED_SIZE (ActionStringSize, sizeof (UINT64));
- }
-
- //
- // Use CoreInternalAllocatePool() that will not update profile for this AllocatePool action.
- //
- AllocInfoData = NULL;
- Status = CoreInternalAllocatePool (
- EfiBootServicesData,
- sizeof (*AllocInfoData) + ActionStringSize,
- (VOID **)&AllocInfoData
- );
- if (EFI_ERROR (Status)) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- ASSERT (AllocInfoData != NULL);
-
- //
- // Only update SequenceCount if and only if it is basic action.
- //
- if (Action == BasicAction) {
- ContextData->Context.SequenceCount++;
- }
-
- AllocInfo = &AllocInfoData->AllocInfo;
- AllocInfoData->Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE;
- AllocInfo->Header.Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE;
- AllocInfo->Header.Length = (UINT16)(sizeof (MEMORY_PROFILE_ALLOC_INFO) + ActionStringOccupiedSize);
- AllocInfo->Header.Revision = MEMORY_PROFILE_ALLOC_INFO_REVISION;
- AllocInfo->CallerAddress = CallerAddress;
- AllocInfo->SequenceId = ContextData->Context.SequenceCount;
- AllocInfo->Action = Action;
- AllocInfo->MemoryType = MemoryType;
- AllocInfo->Buffer = (PHYSICAL_ADDRESS)(UINTN)Buffer;
- AllocInfo->Size = Size;
- if (ActionString != NULL) {
- AllocInfo->ActionStringOffset = (UINT16)sizeof (MEMORY_PROFILE_ALLOC_INFO);
- AllocInfoData->ActionString = (CHAR8 *)(AllocInfoData + 1);
- CopyMem (AllocInfoData->ActionString, ActionString, ActionStringSize);
- } else {
- AllocInfo->ActionStringOffset = 0;
- AllocInfoData->ActionString = NULL;
- }
-
- InsertTailList (DriverInfoData->AllocInfoList, &AllocInfoData->Link);
-
- Context = &ContextData->Context;
- DriverInfo = &DriverInfoData->DriverInfo;
- DriverInfo->AllocRecordCount++;
-
- //
- // Update summary if and only if it is basic action.
- //
- if (Action == BasicAction) {
- ProfileMemoryIndex = GetProfileMemoryIndex (MemoryType);
-
- DriverInfo->CurrentUsage += Size;
- if (DriverInfo->PeakUsage < DriverInfo->CurrentUsage) {
- DriverInfo->PeakUsage = DriverInfo->CurrentUsage;
- }
-
- DriverInfo->CurrentUsageByType[ProfileMemoryIndex] += Size;
- if (DriverInfo->PeakUsageByType[ProfileMemoryIndex] < DriverInfo->CurrentUsageByType[ProfileMemoryIndex]) {
- DriverInfo->PeakUsageByType[ProfileMemoryIndex] = DriverInfo->CurrentUsageByType[ProfileMemoryIndex];
- }
-
- Context->CurrentTotalUsage += Size;
- if (Context->PeakTotalUsage < Context->CurrentTotalUsage) {
- Context->PeakTotalUsage = Context->CurrentTotalUsage;
- }
-
- Context->CurrentTotalUsageByType[ProfileMemoryIndex] += Size;
- if (Context->PeakTotalUsageByType[ProfileMemoryIndex] < Context->CurrentTotalUsageByType[ProfileMemoryIndex]) {
- Context->PeakTotalUsageByType[ProfileMemoryIndex] = Context->CurrentTotalUsageByType[ProfileMemoryIndex];
- }
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- Get memory profile alloc info from memory profile.
-
- @param DriverInfoData Driver info.
- @param BasicAction This Free basic action.
- @param Size Buffer size.
- @param Buffer Buffer address.
-
- @return Pointer to memory profile alloc info.
-
-**/
-MEMORY_PROFILE_ALLOC_INFO_DATA *
-GetMemoryProfileAllocInfoFromAddress (
- IN MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData,
- IN MEMORY_PROFILE_ACTION BasicAction,
- IN UINTN Size,
- IN VOID *Buffer
- )
-{
- LIST_ENTRY *AllocInfoList;
- LIST_ENTRY *AllocLink;
- MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
- MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
-
- AllocInfoList = DriverInfoData->AllocInfoList;
-
- for (AllocLink = AllocInfoList->ForwardLink;
- AllocLink != AllocInfoList;
- AllocLink = AllocLink->ForwardLink)
- {
- AllocInfoData = CR (
- AllocLink,
- MEMORY_PROFILE_ALLOC_INFO_DATA,
- Link,
- MEMORY_PROFILE_ALLOC_INFO_SIGNATURE
- );
- AllocInfo = &AllocInfoData->AllocInfo;
- if ((AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK) != BasicAction) {
- continue;
- }
-
- switch (BasicAction) {
- case MemoryProfileActionAllocatePages:
- if ((AllocInfo->Buffer <= (PHYSICAL_ADDRESS)(UINTN)Buffer) &&
- ((AllocInfo->Buffer + AllocInfo->Size) >= ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)))
- {
- return AllocInfoData;
- }
-
- break;
- case MemoryProfileActionAllocatePool:
- if (AllocInfo->Buffer == (PHYSICAL_ADDRESS)(UINTN)Buffer) {
- return AllocInfoData;
- }
-
- break;
- default:
- ASSERT (FALSE);
- break;
- }
- }
-
- return NULL;
-}
-
-/**
- Update memory profile Free information.
-
- @param CallerAddress Address of caller who call Free.
- @param Action This Free action.
- @param Size Buffer size.
- @param Buffer Buffer address.
-
- @return EFI_SUCCESS Memory profile is updated.
- @return EFI_UNSUPPORTED Memory profile is unsupported.
- @return EFI_NOT_FOUND No matched allocate info found for free action.
-
-**/
-EFI_STATUS
-CoreUpdateProfileFree (
- IN PHYSICAL_ADDRESS CallerAddress,
- IN MEMORY_PROFILE_ACTION Action,
- IN UINTN Size,
- IN VOID *Buffer
- )
-{
- MEMORY_PROFILE_CONTEXT *Context;
- MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
- MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- LIST_ENTRY *DriverLink;
- LIST_ENTRY *DriverInfoList;
- MEMORY_PROFILE_DRIVER_INFO_DATA *ThisDriverInfoData;
- MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
- UINTN ProfileMemoryIndex;
- MEMORY_PROFILE_ACTION BasicAction;
- BOOLEAN Found;
-
- BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK;
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress);
-
- //
- // Do not return if DriverInfoData == NULL here,
- // because driver A might free memory allocated by driver B.
- //
-
- //
- // Need use do-while loop to find all possible records,
- // because one address might be recorded multiple times.
- //
- Found = FALSE;
- AllocInfoData = NULL;
- do {
- if (DriverInfoData != NULL) {
- switch (BasicAction) {
- case MemoryProfileActionFreePages:
- AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer);
- break;
- case MemoryProfileActionFreePool:
- AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer);
- break;
- default:
- ASSERT (FALSE);
- AllocInfoData = NULL;
- break;
- }
- }
-
- if (AllocInfoData == NULL) {
- //
- // Legal case, because driver A might free memory allocated by driver B, by some protocol.
- //
- DriverInfoList = ContextData->DriverInfoList;
-
- for (DriverLink = DriverInfoList->ForwardLink;
- DriverLink != DriverInfoList;
- DriverLink = DriverLink->ForwardLink)
- {
- ThisDriverInfoData = CR (
- DriverLink,
- MEMORY_PROFILE_DRIVER_INFO_DATA,
- Link,
- MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
- );
- switch (BasicAction) {
- case MemoryProfileActionFreePages:
- AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer);
- break;
- case MemoryProfileActionFreePool:
- AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer);
- break;
- default:
- ASSERT (FALSE);
- AllocInfoData = NULL;
- break;
- }
-
- if (AllocInfoData != NULL) {
- DriverInfoData = ThisDriverInfoData;
- break;
- }
- }
-
- if (AllocInfoData == NULL) {
- //
- // If (!Found), no matched allocate info is found for this free action.
- // It is because the specified memory type allocate actions have been filtered by
- // CoreNeedRecordProfile(), but free actions may have no memory type information,
- // they can not be filtered by CoreNeedRecordProfile(). Then, they will be
- // filtered here.
- //
- // If (Found), it is normal exit path.
- return (Found ? EFI_SUCCESS : EFI_NOT_FOUND);
- }
- }
-
- ASSERT (DriverInfoData != NULL);
- ASSERT (AllocInfoData != NULL);
-
- Found = TRUE;
-
- Context = &ContextData->Context;
- DriverInfo = &DriverInfoData->DriverInfo;
- AllocInfo = &AllocInfoData->AllocInfo;
-
- DriverInfo->AllocRecordCount--;
- //
- // Update summary if and only if it is basic action.
- //
- if (AllocInfo->Action == (AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK)) {
- ProfileMemoryIndex = GetProfileMemoryIndex (AllocInfo->MemoryType);
-
- Context->CurrentTotalUsage -= AllocInfo->Size;
- Context->CurrentTotalUsageByType[ProfileMemoryIndex] -= AllocInfo->Size;
-
- DriverInfo->CurrentUsage -= AllocInfo->Size;
- DriverInfo->CurrentUsageByType[ProfileMemoryIndex] -= AllocInfo->Size;
- }
-
- RemoveEntryList (&AllocInfoData->Link);
-
- if (BasicAction == MemoryProfileActionFreePages) {
- if (AllocInfo->Buffer != (PHYSICAL_ADDRESS)(UINTN)Buffer) {
- CoreUpdateProfileAllocate (
- AllocInfo->CallerAddress,
- AllocInfo->Action,
- AllocInfo->MemoryType,
- (UINTN)((PHYSICAL_ADDRESS)(UINTN)Buffer - AllocInfo->Buffer),
- (VOID *)(UINTN)AllocInfo->Buffer,
- AllocInfoData->ActionString
- );
- }
-
- if (AllocInfo->Buffer + AllocInfo->Size != ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)) {
- CoreUpdateProfileAllocate (
- AllocInfo->CallerAddress,
- AllocInfo->Action,
- AllocInfo->MemoryType,
- (UINTN)((AllocInfo->Buffer + AllocInfo->Size) - ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)),
- (VOID *)((UINTN)Buffer + Size),
- AllocInfoData->ActionString
- );
- }
- }
-
- //
- // Use CoreInternalFreePool() that will not update profile for this FreePool action.
- //
- CoreInternalFreePool (AllocInfoData, NULL);
- } while (TRUE);
-}
-
-/**
- Update memory profile information.
-
- @param CallerAddress Address of caller who call Allocate or Free.
- @param Action This Allocate or Free action.
- @param MemoryType Memory type.
- EfiMaxMemoryType means the MemoryType is unknown.
- @param Size Buffer size.
- @param Buffer Buffer address.
- @param ActionString String for memory profile action.
- Only needed for user defined allocate action.
-
- @return EFI_SUCCESS Memory profile is updated.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required,
- or memory profile for the memory type is not required.
- @return EFI_ACCESS_DENIED It is during memory profile data getting.
- @return EFI_ABORTED Memory profile recording is not enabled.
- @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
- @return EFI_NOT_FOUND No matched allocate info found for free action.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreUpdateProfile (
- IN PHYSICAL_ADDRESS CallerAddress,
- IN MEMORY_PROFILE_ACTION Action,
- IN EFI_MEMORY_TYPE MemoryType,
- IN UINTN Size, // Valid for AllocatePages/FreePages/AllocatePool
- IN VOID *Buffer,
- IN CHAR8 *ActionString OPTIONAL
- )
-{
- EFI_STATUS Status;
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- MEMORY_PROFILE_ACTION BasicAction;
-
- if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
- return EFI_UNSUPPORTED;
- }
-
- if (mMemoryProfileGettingStatus) {
- return EFI_ACCESS_DENIED;
- }
-
- if (!mMemoryProfileRecordingEnable) {
- return EFI_ABORTED;
- }
-
- //
- // Get the basic action to know how to process the record
- //
- BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK;
-
- //
- // EfiMaxMemoryType means the MemoryType is unknown.
- //
- if (MemoryType != EfiMaxMemoryType) {
- //
- // Only record limited MemoryType.
- //
- if (!CoreNeedRecordProfile (MemoryType)) {
- return EFI_UNSUPPORTED;
- }
- }
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- CoreAcquireMemoryProfileLock ();
- switch (BasicAction) {
- case MemoryProfileActionAllocatePages:
- Status = CoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
- break;
- case MemoryProfileActionFreePages:
- Status = CoreUpdateProfileFree (CallerAddress, Action, Size, Buffer);
- break;
- case MemoryProfileActionAllocatePool:
- Status = CoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
- break;
- case MemoryProfileActionFreePool:
- Status = CoreUpdateProfileFree (CallerAddress, Action, 0, Buffer);
- break;
- default:
- ASSERT (FALSE);
- Status = EFI_UNSUPPORTED;
- break;
- }
-
- CoreReleaseMemoryProfileLock ();
-
- return Status;
-}
-
-////////////////////
-
-/**
- Get memory profile data size.
-
- @return Memory profile data size.
-
-**/
-UINTN
-MemoryProfileGetDataSize (
- VOID
- )
-{
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
- LIST_ENTRY *DriverInfoList;
- LIST_ENTRY *DriverLink;
- LIST_ENTRY *AllocInfoList;
- LIST_ENTRY *AllocLink;
- UINTN TotalSize;
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return 0;
- }
-
- TotalSize = sizeof (MEMORY_PROFILE_CONTEXT);
-
- DriverInfoList = ContextData->DriverInfoList;
- for (DriverLink = DriverInfoList->ForwardLink;
- DriverLink != DriverInfoList;
- DriverLink = DriverLink->ForwardLink)
- {
- DriverInfoData = CR (
- DriverLink,
- MEMORY_PROFILE_DRIVER_INFO_DATA,
- Link,
- MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
- );
- TotalSize += DriverInfoData->DriverInfo.Header.Length;
-
- AllocInfoList = DriverInfoData->AllocInfoList;
- for (AllocLink = AllocInfoList->ForwardLink;
- AllocLink != AllocInfoList;
- AllocLink = AllocLink->ForwardLink)
- {
- AllocInfoData = CR (
- AllocLink,
- MEMORY_PROFILE_ALLOC_INFO_DATA,
- Link,
- MEMORY_PROFILE_ALLOC_INFO_SIGNATURE
- );
- TotalSize += AllocInfoData->AllocInfo.Header.Length;
- }
- }
-
- return TotalSize;
-}
-
-/**
- Copy memory profile data.
-
- @param ProfileBuffer The buffer to hold memory profile data.
-
-**/
-VOID
-MemoryProfileCopyData (
- IN VOID *ProfileBuffer
- )
-{
- MEMORY_PROFILE_CONTEXT *Context;
- MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
- MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
- MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
- LIST_ENTRY *DriverInfoList;
- LIST_ENTRY *DriverLink;
- LIST_ENTRY *AllocInfoList;
- LIST_ENTRY *AllocLink;
- UINTN PdbSize;
- UINTN ActionStringSize;
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return;
- }
-
- Context = ProfileBuffer;
- CopyMem (Context, &ContextData->Context, sizeof (MEMORY_PROFILE_CONTEXT));
- DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)(Context + 1);
-
- DriverInfoList = ContextData->DriverInfoList;
- for (DriverLink = DriverInfoList->ForwardLink;
- DriverLink != DriverInfoList;
- DriverLink = DriverLink->ForwardLink)
- {
- DriverInfoData = CR (
- DriverLink,
- MEMORY_PROFILE_DRIVER_INFO_DATA,
- Link,
- MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
- );
- CopyMem (DriverInfo, &DriverInfoData->DriverInfo, sizeof (MEMORY_PROFILE_DRIVER_INFO));
- if (DriverInfo->PdbStringOffset != 0) {
- PdbSize = AsciiStrSize (DriverInfoData->PdbString);
- CopyMem ((VOID *)((UINTN)DriverInfo + DriverInfo->PdbStringOffset), DriverInfoData->PdbString, PdbSize);
- }
-
- AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)DriverInfo + DriverInfo->Header.Length);
-
- AllocInfoList = DriverInfoData->AllocInfoList;
- for (AllocLink = AllocInfoList->ForwardLink;
- AllocLink != AllocInfoList;
- AllocLink = AllocLink->ForwardLink)
- {
- AllocInfoData = CR (
- AllocLink,
- MEMORY_PROFILE_ALLOC_INFO_DATA,
- Link,
- MEMORY_PROFILE_ALLOC_INFO_SIGNATURE
- );
- CopyMem (AllocInfo, &AllocInfoData->AllocInfo, sizeof (MEMORY_PROFILE_ALLOC_INFO));
- if (AllocInfo->ActionStringOffset != 0) {
- ActionStringSize = AsciiStrSize (AllocInfoData->ActionString);
- CopyMem ((VOID *)((UINTN)AllocInfo + AllocInfo->ActionStringOffset), AllocInfoData->ActionString, ActionStringSize);
- }
-
- AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)AllocInfo + AllocInfo->Header.Length);
- }
-
- DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)AllocInfo;
- }
-}
-
-/**
- Get memory profile data.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer.
- On return, points to the size of the data returned in ProfileBuffer.
- @param[out] ProfileBuffer Profile buffer.
-
- @return EFI_SUCCESS Get the memory profile data successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported.
- @return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data.
- ProfileSize is updated with the size required.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolGetData (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN OUT UINT64 *ProfileSize,
- OUT VOID *ProfileBuffer
- )
-{
- UINTN Size;
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
- BOOLEAN MemoryProfileGettingStatus;
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- MemoryProfileGettingStatus = mMemoryProfileGettingStatus;
- mMemoryProfileGettingStatus = TRUE;
-
- Size = MemoryProfileGetDataSize ();
-
- if (*ProfileSize < Size) {
- *ProfileSize = Size;
- mMemoryProfileGettingStatus = MemoryProfileGettingStatus;
- return EFI_BUFFER_TOO_SMALL;
- }
-
- *ProfileSize = Size;
- MemoryProfileCopyData (ProfileBuffer);
-
- mMemoryProfileGettingStatus = MemoryProfileGettingStatus;
- return EFI_SUCCESS;
-}
-
-/**
- Register image to memory profile.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] FilePath File path of the image.
- @param[in] ImageBase Image base address.
- @param[in] ImageSize Image size.
- @param[in] FileType File type of the image.
-
- @return EFI_SUCCESS Register successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required.
- @return EFI_OUT_OF_RESOURCES No enough resource for this register.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolRegisterImage (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
- IN PHYSICAL_ADDRESS ImageBase,
- IN UINT64 ImageSize,
- IN EFI_FV_FILETYPE FileType
- )
-{
- EFI_STATUS Status;
- LOADED_IMAGE_PRIVATE_DATA DriverEntry;
- VOID *EntryPointInImage;
-
- ZeroMem (&DriverEntry, sizeof (DriverEntry));
- DriverEntry.Info.FilePath = FilePath;
- DriverEntry.ImageContext.ImageAddress = ImageBase;
- DriverEntry.ImageContext.ImageSize = ImageSize;
- Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage);
- ASSERT_EFI_ERROR (Status);
- DriverEntry.ImageContext.EntryPoint = (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage;
- DriverEntry.ImageContext.ImageType = InternalPeCoffGetSubsystem ((VOID *)(UINTN)ImageBase);
-
- return RegisterMemoryProfileImage (&DriverEntry, FileType);
-}
-
-/**
- Unregister image from memory profile.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] FilePath File path of the image.
- @param[in] ImageBase Image base address.
- @param[in] ImageSize Image size.
-
- @return EFI_SUCCESS Unregister successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required.
- @return EFI_NOT_FOUND The image is not found.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolUnregisterImage (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
- IN PHYSICAL_ADDRESS ImageBase,
- IN UINT64 ImageSize
- )
-{
- EFI_STATUS Status;
- LOADED_IMAGE_PRIVATE_DATA DriverEntry;
- VOID *EntryPointInImage;
-
- ZeroMem (&DriverEntry, sizeof (DriverEntry));
- DriverEntry.Info.FilePath = FilePath;
- DriverEntry.ImageContext.ImageAddress = ImageBase;
- DriverEntry.ImageContext.ImageSize = ImageSize;
- Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage);
- ASSERT_EFI_ERROR (Status);
- DriverEntry.ImageContext.EntryPoint = (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage;
-
- return UnregisterMemoryProfileImage (&DriverEntry);
-}
-
-/**
- Get memory profile recording state.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[out] RecordingState Recording state.
-
- @return EFI_SUCCESS Memory profile recording state is returned.
- @return EFI_UNSUPPORTED Memory profile is unsupported.
- @return EFI_INVALID_PARAMETER RecordingState is NULL.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolGetRecordingState (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- OUT BOOLEAN *RecordingState
- )
-{
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- if (RecordingState == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- *RecordingState = mMemoryProfileRecordingEnable;
- return EFI_SUCCESS;
-}
-
-/**
- Set memory profile recording state.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] RecordingState Recording state.
-
- @return EFI_SUCCESS Set memory profile recording state successfully.
- @return EFI_UNSUPPORTED Memory profile is unsupported.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolSetRecordingState (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN BOOLEAN RecordingState
- )
-{
- MEMORY_PROFILE_CONTEXT_DATA *ContextData;
-
- ContextData = GetMemoryProfileContext ();
- if (ContextData == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- mMemoryProfileRecordingEnable = RecordingState;
- return EFI_SUCCESS;
-}
-
-/**
- Record memory profile of multilevel caller.
-
- @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
- @param[in] CallerAddress Address of caller.
- @param[in] Action Memory profile action.
- @param[in] MemoryType Memory type.
- EfiMaxMemoryType means the MemoryType is unknown.
- @param[in] Buffer Buffer address.
- @param[in] Size Buffer size.
- @param[in] ActionString String for memory profile action.
- Only needed for user defined allocate action.
-
- @return EFI_SUCCESS Memory profile is updated.
- @return EFI_UNSUPPORTED Memory profile is unsupported,
- or memory profile for the image is not required,
- or memory profile for the memory type is not required.
- @return EFI_ACCESS_DENIED It is during memory profile data getting.
- @return EFI_ABORTED Memory profile recording is not enabled.
- @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
- @return EFI_NOT_FOUND No matched allocate info found for free action.
-
-**/
-EFI_STATUS
-EFIAPI
-ProfileProtocolRecord (
- IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
- IN PHYSICAL_ADDRESS CallerAddress,
- IN MEMORY_PROFILE_ACTION Action,
- IN EFI_MEMORY_TYPE MemoryType,
- IN VOID *Buffer,
- IN UINTN Size,
- IN CHAR8 *ActionString OPTIONAL
- )
-{
- return CoreUpdateProfile (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
-}
-
-////////////////////
+/** @file + Support routines for UEFI memory profile. + + Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DxeMain.h" +#include "Imem.h" + +#define IS_UEFI_MEMORY_PROFILE_ENABLED ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0) + +#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \ + ((ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1))) + +typedef struct { + UINT32 Signature; + MEMORY_PROFILE_CONTEXT Context; + LIST_ENTRY *DriverInfoList; +} MEMORY_PROFILE_CONTEXT_DATA; + +typedef struct { + UINT32 Signature; + MEMORY_PROFILE_DRIVER_INFO DriverInfo; + LIST_ENTRY *AllocInfoList; + CHAR8 *PdbString; + LIST_ENTRY Link; +} MEMORY_PROFILE_DRIVER_INFO_DATA; + +typedef struct { + UINT32 Signature; + MEMORY_PROFILE_ALLOC_INFO AllocInfo; + CHAR8 *ActionString; + LIST_ENTRY Link; +} MEMORY_PROFILE_ALLOC_INFO_DATA; + +GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mImageQueue = INITIALIZE_LIST_HEAD_VARIABLE (mImageQueue); +GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA mMemoryProfileContext = { + MEMORY_PROFILE_CONTEXT_SIGNATURE, + { + { + MEMORY_PROFILE_CONTEXT_SIGNATURE, + sizeof (MEMORY_PROFILE_CONTEXT), + MEMORY_PROFILE_CONTEXT_REVISION + }, + 0, + 0, + { 0 }, + { 0 }, + 0, + 0, + 0 + }, + &mImageQueue, +}; +GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA *mMemoryProfileContextPtr = NULL; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_LOCK mMemoryProfileLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY); +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mMemoryProfileGettingStatus = FALSE; +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_DEVICE_PATH_PROTOCOL *mMemoryProfileDriverPath; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mMemoryProfileDriverPathSize; + +/** + Get memory profile data. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer. + On return, points to the size of the data returned in ProfileBuffer. + @param[out] ProfileBuffer Profile buffer. + + @return EFI_SUCCESS Get the memory profile data successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data. + ProfileSize is updated with the size required. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolGetData ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN OUT UINT64 *ProfileSize, + OUT VOID *ProfileBuffer + ); + +/** + Register image to memory profile. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + @param[in] FileType File type of the image. + + @return EFI_SUCCESS Register successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCE No enough resource for this register. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolRegisterImage ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN EFI_FV_FILETYPE FileType + ); + +/** + Unregister image from memory profile. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + + @return EFI_SUCCESS Unregister successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_NOT_FOUND The image is not found. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolUnregisterImage ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize + ); + +/** + Get memory profile recording state. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[out] RecordingState Recording state. + + @return EFI_SUCCESS Memory profile recording state is returned. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_INVALID_PARAMETER RecordingState is NULL. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolGetRecordingState ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + OUT BOOLEAN *RecordingState + ); + +/** + Set memory profile recording state. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] RecordingState Recording state. + + @return EFI_SUCCESS Set memory profile recording state successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolSetRecordingState ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN BOOLEAN RecordingState + ); + +/** + Record memory profile of multilevel caller. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] CallerAddress Address of caller. + @param[in] Action Memory profile action. + @param[in] MemoryType Memory type. + EfiMaxMemoryType means the MemoryType is unknown. + @param[in] Buffer Buffer address. + @param[in] Size Buffer size. + @param[in] ActionString String for memory profile action. + Only needed for user defined allocate action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required, + or memory profile for the memory type is not required. + @return EFI_ACCESS_DENIED It is during memory profile data getting. + @return EFI_ABORTED Memory profile recording is not enabled. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolRecord ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, + IN VOID *Buffer, + IN UINTN Size, + IN CHAR8 *ActionString OPTIONAL + ); + +GLOBAL_REMOVE_IF_UNREFERENCED EDKII_MEMORY_PROFILE_PROTOCOL mProfileProtocol = { + ProfileProtocolGetData, + ProfileProtocolRegisterImage, + ProfileProtocolUnregisterImage, + ProfileProtocolGetRecordingState, + ProfileProtocolSetRecordingState, + ProfileProtocolRecord, +}; + +/** + Acquire lock on mMemoryProfileLock. +**/ +VOID +CoreAcquireMemoryProfileLock ( + VOID + ) +{ + CoreAcquireLock (&mMemoryProfileLock); +} + +/** + Release lock on mMemoryProfileLock. +**/ +VOID +CoreReleaseMemoryProfileLock ( + VOID + ) +{ + CoreReleaseLock (&mMemoryProfileLock); +} + +/** + Return memory profile context. + + @return Memory profile context. + +**/ +MEMORY_PROFILE_CONTEXT_DATA * +GetMemoryProfileContext ( + VOID + ) +{ + return mMemoryProfileContextPtr; +} + +/** + Retrieves and returns the Subsystem of a PE/COFF image that has been loaded into system memory. + If Pe32Data is NULL, then ASSERT(). + + @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. + + @return The Subsystem of the PE/COFF image. + +**/ +UINT16 +InternalPeCoffGetSubsystem ( + IN VOID *Pe32Data + ) +{ + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + EFI_IMAGE_DOS_HEADER *DosHdr; + UINT16 Magic; + + ASSERT (Pe32Data != NULL); + + DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, so read the PE header after the DOS image header. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff)); + } else { + // + // DOS image header is not present, so PE header is at the image base. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; + } + + if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { + return Hdr.Te->Subsystem; + } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { + Magic = Hdr.Pe32->OptionalHeader.Magic; + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + return Hdr.Pe32->OptionalHeader.Subsystem; + } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + return Hdr.Pe32Plus->OptionalHeader.Subsystem; + } + } + + return 0x0000; +} + +/** + Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded + into system memory with the PE/COFF Loader Library functions. + + Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry + point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then + return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS. + If Pe32Data is NULL, then ASSERT(). + If EntryPoint is NULL, then ASSERT(). + + @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. + @param EntryPoint The pointer to entry point to the PE/COFF image to return. + + @retval RETURN_SUCCESS EntryPoint was returned. + @retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image. + +**/ +RETURN_STATUS +InternalPeCoffGetEntryPoint ( + IN VOID *Pe32Data, + OUT VOID **EntryPoint + ) +{ + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + + ASSERT (Pe32Data != NULL); + ASSERT (EntryPoint != NULL); + + DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, so read the PE header after the DOS image header. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff)); + } else { + // + // DOS image header is not present, so PE header is at the image base. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; + } + + // + // Calculate the entry point relative to the start of the image. + // AddressOfEntryPoint is common for PE32 & PE32+ + // + if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { + *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize); + return RETURN_SUCCESS; + } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { + *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff)); + return RETURN_SUCCESS; + } + + return RETURN_UNSUPPORTED; +} + +/** + Build driver info. + + @param ContextData Memory profile context. + @param FileName File name of the image. + @param ImageBase Image base address. + @param ImageSize Image size. + @param EntryPoint Entry point of the image. + @param ImageSubsystem Image subsystem of the image. + @param FileType File type of the image. + + @return Pointer to memory profile driver info. + +**/ +MEMORY_PROFILE_DRIVER_INFO_DATA * +BuildDriverInfo ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData, + IN EFI_GUID *FileName, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN PHYSICAL_ADDRESS EntryPoint, + IN UINT16 ImageSubsystem, + IN EFI_FV_FILETYPE FileType + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + VOID *EntryPointInImage; + CHAR8 *PdbString; + UINTN PdbSize; + UINTN PdbOccupiedSize; + + PdbSize = 0; + PdbOccupiedSize = 0; + PdbString = NULL; + if (ImageBase != 0) { + PdbString = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageBase); + if (PdbString != NULL) { + PdbSize = AsciiStrSize (PdbString); + PdbOccupiedSize = GET_OCCUPIED_SIZE (PdbSize, sizeof (UINT64)); + } + } + + // + // Use CoreInternalAllocatePool() that will not update profile for this AllocatePool action. + // + Status = CoreInternalAllocatePool ( + EfiBootServicesData, + sizeof (*DriverInfoData) + sizeof (LIST_ENTRY) + PdbSize, + (VOID **)&DriverInfoData + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + ASSERT (DriverInfoData != NULL); + + ZeroMem (DriverInfoData, sizeof (*DriverInfoData)); + + DriverInfo = &DriverInfoData->DriverInfo; + DriverInfoData->Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE; + DriverInfo->Header.Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE; + DriverInfo->Header.Length = (UINT16)(sizeof (MEMORY_PROFILE_DRIVER_INFO) + PdbOccupiedSize); + DriverInfo->Header.Revision = MEMORY_PROFILE_DRIVER_INFO_REVISION; + if (FileName != NULL) { + CopyMem (&DriverInfo->FileName, FileName, sizeof (EFI_GUID)); + } + + DriverInfo->ImageBase = ImageBase; + DriverInfo->ImageSize = ImageSize; + DriverInfo->EntryPoint = EntryPoint; + DriverInfo->ImageSubsystem = ImageSubsystem; + if ((EntryPoint != 0) && ((EntryPoint < ImageBase) || (EntryPoint >= (ImageBase + ImageSize)))) { + // + // If the EntryPoint is not in the range of image buffer, it should come from emulation environment. + // So patch ImageBuffer here to align the EntryPoint. + // + Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverInfo->ImageBase = ImageBase + EntryPoint - (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage; + } + + DriverInfo->FileType = FileType; + DriverInfoData->AllocInfoList = (LIST_ENTRY *)(DriverInfoData + 1); + InitializeListHead (DriverInfoData->AllocInfoList); + DriverInfo->CurrentUsage = 0; + DriverInfo->PeakUsage = 0; + DriverInfo->AllocRecordCount = 0; + if (PdbSize != 0) { + DriverInfo->PdbStringOffset = (UINT16)sizeof (MEMORY_PROFILE_DRIVER_INFO); + DriverInfoData->PdbString = (CHAR8 *)(DriverInfoData->AllocInfoList + 1); + CopyMem (DriverInfoData->PdbString, PdbString, PdbSize); + } else { + DriverInfo->PdbStringOffset = 0; + DriverInfoData->PdbString = NULL; + } + + InsertTailList (ContextData->DriverInfoList, &DriverInfoData->Link); + ContextData->Context.ImageCount++; + ContextData->Context.TotalImageSize += DriverInfo->ImageSize; + + return DriverInfoData; +} + +/** + Return if record for this driver is needed.. + + @param DriverFilePath Driver file path. + + @retval TRUE Record for this driver is needed. + @retval FALSE Record for this driver is not needed. + +**/ +BOOLEAN +NeedRecordThisDriver ( + IN EFI_DEVICE_PATH_PROTOCOL *DriverFilePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance; + UINTN DevicePathSize; + UINTN FilePathSize; + + if (!IsDevicePathValid (mMemoryProfileDriverPath, mMemoryProfileDriverPathSize)) { + // + // Invalid Device Path means record all. + // + return TRUE; + } + + // + // Record FilePath without END node. + // + FilePathSize = GetDevicePathSize (DriverFilePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); + + DevicePathInstance = mMemoryProfileDriverPath; + do { + // + // Find END node (it might be END_ENTIRE or END_INSTANCE). + // + TmpDevicePath = DevicePathInstance; + while (!IsDevicePathEndType (TmpDevicePath)) { + TmpDevicePath = NextDevicePathNode (TmpDevicePath); + } + + // + // Do not compare END node. + // + DevicePathSize = (UINTN)TmpDevicePath - (UINTN)DevicePathInstance; + if ((FilePathSize == DevicePathSize) && + (CompareMem (DriverFilePath, DevicePathInstance, DevicePathSize) == 0)) + { + return TRUE; + } + + // + // Get next instance. + // + DevicePathInstance = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN)DevicePathInstance + DevicePathSize + DevicePathNodeLength (TmpDevicePath)); + } while (DevicePathSubType (TmpDevicePath) != END_ENTIRE_DEVICE_PATH_SUBTYPE); + + return FALSE; +} + +/** + Register DXE Core to memory profile. + + @param HobStart The start address of the HOB. + @param ContextData Memory profile context. + + @retval TRUE Register success. + @retval FALSE Register fail. + +**/ +BOOLEAN +RegisterDxeCore ( + IN VOID *HobStart, + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData + ) +{ + EFI_PEI_HOB_POINTERS DxeCoreHob; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + PHYSICAL_ADDRESS ImageBase; + UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)]; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath; + + ASSERT (ContextData != NULL); + + // + // Searching for image hob + // + DxeCoreHob.Raw = HobStart; + while ((DxeCoreHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, DxeCoreHob.Raw)) != NULL) { + if (CompareGuid (&DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.Name, &gEfiHobMemoryAllocModuleGuid)) { + // + // Find Dxe Core HOB + // + break; + } + + DxeCoreHob.Raw = GET_NEXT_HOB (DxeCoreHob); + } + + ASSERT (DxeCoreHob.Raw != NULL); + + FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer; + EfiInitializeFwVolDevicepathNode (FilePath, &DxeCoreHob.MemoryAllocationModule->ModuleName); + SetDevicePathEndNode (FilePath + 1); + + if (!NeedRecordThisDriver ((EFI_DEVICE_PATH_PROTOCOL *)FilePath)) { + return FALSE; + } + + ImageBase = DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryBaseAddress; + DriverInfoData = BuildDriverInfo ( + ContextData, + &DxeCoreHob.MemoryAllocationModule->ModuleName, + ImageBase, + DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryLength, + DxeCoreHob.MemoryAllocationModule->EntryPoint, + InternalPeCoffGetSubsystem ((VOID *)(UINTN)ImageBase), + EFI_FV_FILETYPE_DXE_CORE + ); + if (DriverInfoData == NULL) { + return FALSE; + } + + return TRUE; +} + +/** + Initialize memory profile. + + @param HobStart The start address of the HOB. + +**/ +VOID +MemoryProfileInit ( + IN VOID *HobStart + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + + if (!IS_UEFI_MEMORY_PROFILE_ENABLED) { + return; + } + + ContextData = GetMemoryProfileContext (); + if (ContextData != NULL) { + return; + } + + mMemoryProfileGettingStatus = FALSE; + if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT7) != 0) { + mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE; + } else { + mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_ENABLE; + } + + mMemoryProfileDriverPathSize = PcdGetSize (PcdMemoryProfileDriverPath); + mMemoryProfileDriverPath = AllocateCopyPool (mMemoryProfileDriverPathSize, PcdGetPtr (PcdMemoryProfileDriverPath)); + mMemoryProfileContextPtr = &mMemoryProfileContext; + + RegisterDxeCore (HobStart, &mMemoryProfileContext); + + DEBUG ((DEBUG_INFO, "MemoryProfileInit MemoryProfileContext - 0x%x\n", &mMemoryProfileContext)); +} + +/** + Install memory profile protocol. + +**/ +VOID +MemoryProfileInstallProtocol ( + VOID + ) +{ + EFI_HANDLE Handle; + EFI_STATUS Status; + + if (!IS_UEFI_MEMORY_PROFILE_ENABLED) { + return; + } + + Handle = NULL; + Status = CoreInstallMultipleProtocolInterfaces ( + &Handle, + &gEdkiiMemoryProfileGuid, + &mProfileProtocol, + NULL + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Get the GUID file name from the file path. + + @param FilePath File path. + + @return The GUID file name from the file path. + +**/ +EFI_GUID * +GetFileNameFromFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *ThisFilePath; + EFI_GUID *FileName; + + FileName = NULL; + if (FilePath != NULL) { + ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)FilePath; + while (!IsDevicePathEnd (ThisFilePath)) { + FileName = EfiGetNameGuidFromFwVolDevicePathNode (ThisFilePath); + if (FileName != NULL) { + break; + } + + ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)NextDevicePathNode (ThisFilePath); + } + } + + return FileName; +} + +/** + Register image to memory profile. + + @param DriverEntry Image info. + @param FileType Image file type. + + @return EFI_SUCCESS Register successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCES No enough resource for this register. + +**/ +EFI_STATUS +RegisterMemoryProfileImage ( + IN LOADED_IMAGE_PRIVATE_DATA *DriverEntry, + IN EFI_FV_FILETYPE FileType + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + + if (!IS_UEFI_MEMORY_PROFILE_ENABLED) { + return EFI_UNSUPPORTED; + } + + if (!NeedRecordThisDriver (DriverEntry->Info.FilePath)) { + return EFI_UNSUPPORTED; + } + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = BuildDriverInfo ( + ContextData, + GetFileNameFromFilePath (DriverEntry->Info.FilePath), + DriverEntry->ImageContext.ImageAddress, + DriverEntry->ImageContext.ImageSize, + DriverEntry->ImageContext.EntryPoint, + DriverEntry->ImageContext.ImageType, + FileType + ); + if (DriverInfoData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Search image from memory profile. + + @param ContextData Memory profile context. + @param FileName Image file name. + @param Address Image Address. + + @return Pointer to memory profile driver info. + +**/ +MEMORY_PROFILE_DRIVER_INFO_DATA * +GetMemoryProfileDriverInfoByFileNameAndAddress ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData, + IN EFI_GUID *FileName, + IN PHYSICAL_ADDRESS Address + ) +{ + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + LIST_ENTRY *DriverLink; + LIST_ENTRY *DriverInfoList; + + DriverInfoList = ContextData->DriverInfoList; + + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) + { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + DriverInfo = &DriverInfoData->DriverInfo; + if ((CompareGuid (&DriverInfo->FileName, FileName)) && + (Address >= DriverInfo->ImageBase) && + (Address < (DriverInfo->ImageBase + DriverInfo->ImageSize))) + { + return DriverInfoData; + } + } + + return NULL; +} + +/** + Search image from memory profile. + It will return image, if (Address >= ImageBuffer) AND (Address < ImageBuffer + ImageSize). + + @param ContextData Memory profile context. + @param Address Image or Function address. + + @return Pointer to memory profile driver info. + +**/ +MEMORY_PROFILE_DRIVER_INFO_DATA * +GetMemoryProfileDriverInfoFromAddress ( + IN MEMORY_PROFILE_CONTEXT_DATA *ContextData, + IN PHYSICAL_ADDRESS Address + ) +{ + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + LIST_ENTRY *DriverLink; + LIST_ENTRY *DriverInfoList; + + DriverInfoList = ContextData->DriverInfoList; + + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) + { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + DriverInfo = &DriverInfoData->DriverInfo; + if ((Address >= DriverInfo->ImageBase) && + (Address < (DriverInfo->ImageBase + DriverInfo->ImageSize))) + { + return DriverInfoData; + } + } + + return NULL; +} + +/** + Unregister image from memory profile. + + @param DriverEntry Image info. + + @return EFI_SUCCESS Unregister successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_NOT_FOUND The image is not found. + +**/ +EFI_STATUS +UnregisterMemoryProfileImage ( + IN LOADED_IMAGE_PRIVATE_DATA *DriverEntry + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + EFI_GUID *FileName; + PHYSICAL_ADDRESS ImageAddress; + VOID *EntryPointInImage; + + if (!IS_UEFI_MEMORY_PROFILE_ENABLED) { + return EFI_UNSUPPORTED; + } + + if (!NeedRecordThisDriver (DriverEntry->Info.FilePath)) { + return EFI_UNSUPPORTED; + } + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = NULL; + FileName = GetFileNameFromFilePath (DriverEntry->Info.FilePath); + ImageAddress = DriverEntry->ImageContext.ImageAddress; + if ((DriverEntry->ImageContext.EntryPoint < ImageAddress) || (DriverEntry->ImageContext.EntryPoint >= (ImageAddress + DriverEntry->ImageContext.ImageSize))) { + // + // If the EntryPoint is not in the range of image buffer, it should come from emulation environment. + // So patch ImageAddress here to align the EntryPoint. + // + Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageAddress, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + ImageAddress = ImageAddress + (UINTN)DriverEntry->ImageContext.EntryPoint - (UINTN)EntryPointInImage; + } + + if (FileName != NULL) { + DriverInfoData = GetMemoryProfileDriverInfoByFileNameAndAddress (ContextData, FileName, ImageAddress); + } + + if (DriverInfoData == NULL) { + DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, ImageAddress); + } + + if (DriverInfoData == NULL) { + return EFI_NOT_FOUND; + } + + ContextData->Context.TotalImageSize -= DriverInfoData->DriverInfo.ImageSize; + + // Keep the ImageBase for RVA calculation in Application. + // DriverInfoData->DriverInfo.ImageBase = 0; + DriverInfoData->DriverInfo.ImageSize = 0; + + if (DriverInfoData->DriverInfo.PeakUsage == 0) { + ContextData->Context.ImageCount--; + RemoveEntryList (&DriverInfoData->Link); + // + // Use CoreInternalFreePool() that will not update profile for this FreePool action. + // + CoreInternalFreePool (DriverInfoData, NULL); + } + + return EFI_SUCCESS; +} + +/** + Return if this memory type needs to be recorded into memory profile. + If BIOS memory type (0 ~ EfiMaxMemoryType - 1), it checks bit (1 << MemoryType). + If OS memory type (0x80000000 ~ 0xFFFFFFFF), it checks bit63 - 0x8000000000000000. + If OEM memory type (0x70000000 ~ 0x7FFFFFFF), it checks bit62 - 0x4000000000000000. + + @param MemoryType Memory type. + + @retval TRUE This memory type need to be recorded. + @retval FALSE This memory type need not to be recorded. + +**/ +BOOLEAN +CoreNeedRecordProfile ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + UINT64 TestBit; + + if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) { + TestBit = BIT63; + } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) { + TestBit = BIT62; + } else { + TestBit = LShiftU64 (1, MemoryType); + } + + if ((PcdGet64 (PcdMemoryProfileMemoryType) & TestBit) != 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Convert EFI memory type to profile memory index. The rule is: + If BIOS memory type (0 ~ EfiMaxMemoryType - 1), ProfileMemoryIndex = MemoryType. + If OS memory type (0x80000000 ~ 0xFFFFFFFF), ProfileMemoryIndex = EfiMaxMemoryType. + If OEM memory type (0x70000000 ~ 0x7FFFFFFF), ProfileMemoryIndex = EfiMaxMemoryType + 1. + + @param MemoryType Memory type. + + @return Profile memory index. + +**/ +UINTN +GetProfileMemoryIndex ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) { + return EfiMaxMemoryType; + } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) { + return EfiMaxMemoryType + 1; + } else { + return MemoryType; + } +} + +/** + Update memory profile Allocate information. + + @param CallerAddress Address of caller who call Allocate. + @param Action This Allocate action. + @param MemoryType Memory type. + @param Size Buffer size. + @param Buffer Buffer address. + @param ActionString String for memory profile action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + +**/ +EFI_STATUS +CoreUpdateProfileAllocate ( + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Size, + IN VOID *Buffer, + IN CHAR8 *ActionString OPTIONAL + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_CONTEXT *Context; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + UINTN ProfileMemoryIndex; + MEMORY_PROFILE_ACTION BasicAction; + UINTN ActionStringSize; + UINTN ActionStringOccupiedSize; + + BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK; + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress); + if (DriverInfoData == NULL) { + return EFI_UNSUPPORTED; + } + + ActionStringSize = 0; + ActionStringOccupiedSize = 0; + if (ActionString != NULL) { + ActionStringSize = AsciiStrSize (ActionString); + ActionStringOccupiedSize = GET_OCCUPIED_SIZE (ActionStringSize, sizeof (UINT64)); + } + + // + // Use CoreInternalAllocatePool() that will not update profile for this AllocatePool action. + // + AllocInfoData = NULL; + Status = CoreInternalAllocatePool ( + EfiBootServicesData, + sizeof (*AllocInfoData) + ActionStringSize, + (VOID **)&AllocInfoData + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + ASSERT (AllocInfoData != NULL); + + // + // Only update SequenceCount if and only if it is basic action. + // + if (Action == BasicAction) { + ContextData->Context.SequenceCount++; + } + + AllocInfo = &AllocInfoData->AllocInfo; + AllocInfoData->Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE; + AllocInfo->Header.Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE; + AllocInfo->Header.Length = (UINT16)(sizeof (MEMORY_PROFILE_ALLOC_INFO) + ActionStringOccupiedSize); + AllocInfo->Header.Revision = MEMORY_PROFILE_ALLOC_INFO_REVISION; + AllocInfo->CallerAddress = CallerAddress; + AllocInfo->SequenceId = ContextData->Context.SequenceCount; + AllocInfo->Action = Action; + AllocInfo->MemoryType = MemoryType; + AllocInfo->Buffer = (PHYSICAL_ADDRESS)(UINTN)Buffer; + AllocInfo->Size = Size; + if (ActionString != NULL) { + AllocInfo->ActionStringOffset = (UINT16)sizeof (MEMORY_PROFILE_ALLOC_INFO); + AllocInfoData->ActionString = (CHAR8 *)(AllocInfoData + 1); + CopyMem (AllocInfoData->ActionString, ActionString, ActionStringSize); + } else { + AllocInfo->ActionStringOffset = 0; + AllocInfoData->ActionString = NULL; + } + + InsertTailList (DriverInfoData->AllocInfoList, &AllocInfoData->Link); + + Context = &ContextData->Context; + DriverInfo = &DriverInfoData->DriverInfo; + DriverInfo->AllocRecordCount++; + + // + // Update summary if and only if it is basic action. + // + if (Action == BasicAction) { + ProfileMemoryIndex = GetProfileMemoryIndex (MemoryType); + + DriverInfo->CurrentUsage += Size; + if (DriverInfo->PeakUsage < DriverInfo->CurrentUsage) { + DriverInfo->PeakUsage = DriverInfo->CurrentUsage; + } + + DriverInfo->CurrentUsageByType[ProfileMemoryIndex] += Size; + if (DriverInfo->PeakUsageByType[ProfileMemoryIndex] < DriverInfo->CurrentUsageByType[ProfileMemoryIndex]) { + DriverInfo->PeakUsageByType[ProfileMemoryIndex] = DriverInfo->CurrentUsageByType[ProfileMemoryIndex]; + } + + Context->CurrentTotalUsage += Size; + if (Context->PeakTotalUsage < Context->CurrentTotalUsage) { + Context->PeakTotalUsage = Context->CurrentTotalUsage; + } + + Context->CurrentTotalUsageByType[ProfileMemoryIndex] += Size; + if (Context->PeakTotalUsageByType[ProfileMemoryIndex] < Context->CurrentTotalUsageByType[ProfileMemoryIndex]) { + Context->PeakTotalUsageByType[ProfileMemoryIndex] = Context->CurrentTotalUsageByType[ProfileMemoryIndex]; + } + } + + return EFI_SUCCESS; +} + +/** + Get memory profile alloc info from memory profile. + + @param DriverInfoData Driver info. + @param BasicAction This Free basic action. + @param Size Buffer size. + @param Buffer Buffer address. + + @return Pointer to memory profile alloc info. + +**/ +MEMORY_PROFILE_ALLOC_INFO_DATA * +GetMemoryProfileAllocInfoFromAddress ( + IN MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData, + IN MEMORY_PROFILE_ACTION BasicAction, + IN UINTN Size, + IN VOID *Buffer + ) +{ + LIST_ENTRY *AllocInfoList; + LIST_ENTRY *AllocLink; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + + AllocInfoList = DriverInfoData->AllocInfoList; + + for (AllocLink = AllocInfoList->ForwardLink; + AllocLink != AllocInfoList; + AllocLink = AllocLink->ForwardLink) + { + AllocInfoData = CR ( + AllocLink, + MEMORY_PROFILE_ALLOC_INFO_DATA, + Link, + MEMORY_PROFILE_ALLOC_INFO_SIGNATURE + ); + AllocInfo = &AllocInfoData->AllocInfo; + if ((AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK) != BasicAction) { + continue; + } + + switch (BasicAction) { + case MemoryProfileActionAllocatePages: + if ((AllocInfo->Buffer <= (PHYSICAL_ADDRESS)(UINTN)Buffer) && + ((AllocInfo->Buffer + AllocInfo->Size) >= ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size))) + { + return AllocInfoData; + } + + break; + case MemoryProfileActionAllocatePool: + if (AllocInfo->Buffer == (PHYSICAL_ADDRESS)(UINTN)Buffer) { + return AllocInfoData; + } + + break; + default: + ASSERT (FALSE); + break; + } + } + + return NULL; +} + +/** + Update memory profile Free information. + + @param CallerAddress Address of caller who call Free. + @param Action This Free action. + @param Size Buffer size. + @param Buffer Buffer address. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +CoreUpdateProfileFree ( + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN UINTN Size, + IN VOID *Buffer + ) +{ + MEMORY_PROFILE_CONTEXT *Context; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + LIST_ENTRY *DriverLink; + LIST_ENTRY *DriverInfoList; + MEMORY_PROFILE_DRIVER_INFO_DATA *ThisDriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + UINTN ProfileMemoryIndex; + MEMORY_PROFILE_ACTION BasicAction; + BOOLEAN Found; + + BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK; + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress); + + // + // Do not return if DriverInfoData == NULL here, + // because driver A might free memory allocated by driver B. + // + + // + // Need use do-while loop to find all possible records, + // because one address might be recorded multiple times. + // + Found = FALSE; + AllocInfoData = NULL; + do { + if (DriverInfoData != NULL) { + switch (BasicAction) { + case MemoryProfileActionFreePages: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer); + break; + case MemoryProfileActionFreePool: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer); + break; + default: + ASSERT (FALSE); + AllocInfoData = NULL; + break; + } + } + + if (AllocInfoData == NULL) { + // + // Legal case, because driver A might free memory allocated by driver B, by some protocol. + // + DriverInfoList = ContextData->DriverInfoList; + + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) + { + ThisDriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + switch (BasicAction) { + case MemoryProfileActionFreePages: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer); + break; + case MemoryProfileActionFreePool: + AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer); + break; + default: + ASSERT (FALSE); + AllocInfoData = NULL; + break; + } + + if (AllocInfoData != NULL) { + DriverInfoData = ThisDriverInfoData; + break; + } + } + + if (AllocInfoData == NULL) { + // + // If (!Found), no matched allocate info is found for this free action. + // It is because the specified memory type allocate actions have been filtered by + // CoreNeedRecordProfile(), but free actions may have no memory type information, + // they can not be filtered by CoreNeedRecordProfile(). Then, they will be + // filtered here. + // + // If (Found), it is normal exit path. + return (Found ? EFI_SUCCESS : EFI_NOT_FOUND); + } + } + + ASSERT (DriverInfoData != NULL); + ASSERT (AllocInfoData != NULL); + + Found = TRUE; + + Context = &ContextData->Context; + DriverInfo = &DriverInfoData->DriverInfo; + AllocInfo = &AllocInfoData->AllocInfo; + + DriverInfo->AllocRecordCount--; + // + // Update summary if and only if it is basic action. + // + if (AllocInfo->Action == (AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK)) { + ProfileMemoryIndex = GetProfileMemoryIndex (AllocInfo->MemoryType); + + Context->CurrentTotalUsage -= AllocInfo->Size; + Context->CurrentTotalUsageByType[ProfileMemoryIndex] -= AllocInfo->Size; + + DriverInfo->CurrentUsage -= AllocInfo->Size; + DriverInfo->CurrentUsageByType[ProfileMemoryIndex] -= AllocInfo->Size; + } + + RemoveEntryList (&AllocInfoData->Link); + + if (BasicAction == MemoryProfileActionFreePages) { + if (AllocInfo->Buffer != (PHYSICAL_ADDRESS)(UINTN)Buffer) { + CoreUpdateProfileAllocate ( + AllocInfo->CallerAddress, + AllocInfo->Action, + AllocInfo->MemoryType, + (UINTN)((PHYSICAL_ADDRESS)(UINTN)Buffer - AllocInfo->Buffer), + (VOID *)(UINTN)AllocInfo->Buffer, + AllocInfoData->ActionString + ); + } + + if (AllocInfo->Buffer + AllocInfo->Size != ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)) { + CoreUpdateProfileAllocate ( + AllocInfo->CallerAddress, + AllocInfo->Action, + AllocInfo->MemoryType, + (UINTN)((AllocInfo->Buffer + AllocInfo->Size) - ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)), + (VOID *)((UINTN)Buffer + Size), + AllocInfoData->ActionString + ); + } + } + + // + // Use CoreInternalFreePool() that will not update profile for this FreePool action. + // + CoreInternalFreePool (AllocInfoData, NULL); + } while (TRUE); +} + +/** + Update memory profile information. + + @param CallerAddress Address of caller who call Allocate or Free. + @param Action This Allocate or Free action. + @param MemoryType Memory type. + EfiMaxMemoryType means the MemoryType is unknown. + @param Size Buffer size. + @param Buffer Buffer address. + @param ActionString String for memory profile action. + Only needed for user defined allocate action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required, + or memory profile for the memory type is not required. + @return EFI_ACCESS_DENIED It is during memory profile data getting. + @return EFI_ABORTED Memory profile recording is not enabled. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +EFIAPI +CoreUpdateProfile ( + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Size, // Valid for AllocatePages/FreePages/AllocatePool + IN VOID *Buffer, + IN CHAR8 *ActionString OPTIONAL + ) +{ + EFI_STATUS Status; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_ACTION BasicAction; + + if (!IS_UEFI_MEMORY_PROFILE_ENABLED) { + return EFI_UNSUPPORTED; + } + + if (mMemoryProfileGettingStatus) { + return EFI_ACCESS_DENIED; + } + + if (!mMemoryProfileRecordingEnable) { + return EFI_ABORTED; + } + + // + // Get the basic action to know how to process the record + // + BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK; + + // + // EfiMaxMemoryType means the MemoryType is unknown. + // + if (MemoryType != EfiMaxMemoryType) { + // + // Only record limited MemoryType. + // + if (!CoreNeedRecordProfile (MemoryType)) { + return EFI_UNSUPPORTED; + } + } + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + CoreAcquireMemoryProfileLock (); + switch (BasicAction) { + case MemoryProfileActionAllocatePages: + Status = CoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString); + break; + case MemoryProfileActionFreePages: + Status = CoreUpdateProfileFree (CallerAddress, Action, Size, Buffer); + break; + case MemoryProfileActionAllocatePool: + Status = CoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString); + break; + case MemoryProfileActionFreePool: + Status = CoreUpdateProfileFree (CallerAddress, Action, 0, Buffer); + break; + default: + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + break; + } + + CoreReleaseMemoryProfileLock (); + + return Status; +} + +//////////////////// + +/** + Get memory profile data size. + + @return Memory profile data size. + +**/ +UINTN +MemoryProfileGetDataSize ( + VOID + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + LIST_ENTRY *DriverInfoList; + LIST_ENTRY *DriverLink; + LIST_ENTRY *AllocInfoList; + LIST_ENTRY *AllocLink; + UINTN TotalSize; + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return 0; + } + + TotalSize = sizeof (MEMORY_PROFILE_CONTEXT); + + DriverInfoList = ContextData->DriverInfoList; + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) + { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + TotalSize += DriverInfoData->DriverInfo.Header.Length; + + AllocInfoList = DriverInfoData->AllocInfoList; + for (AllocLink = AllocInfoList->ForwardLink; + AllocLink != AllocInfoList; + AllocLink = AllocLink->ForwardLink) + { + AllocInfoData = CR ( + AllocLink, + MEMORY_PROFILE_ALLOC_INFO_DATA, + Link, + MEMORY_PROFILE_ALLOC_INFO_SIGNATURE + ); + TotalSize += AllocInfoData->AllocInfo.Header.Length; + } + } + + return TotalSize; +} + +/** + Copy memory profile data. + + @param ProfileBuffer The buffer to hold memory profile data. + +**/ +VOID +MemoryProfileCopyData ( + IN VOID *ProfileBuffer + ) +{ + MEMORY_PROFILE_CONTEXT *Context; + MEMORY_PROFILE_DRIVER_INFO *DriverInfo; + MEMORY_PROFILE_ALLOC_INFO *AllocInfo; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData; + MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData; + LIST_ENTRY *DriverInfoList; + LIST_ENTRY *DriverLink; + LIST_ENTRY *AllocInfoList; + LIST_ENTRY *AllocLink; + UINTN PdbSize; + UINTN ActionStringSize; + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return; + } + + Context = ProfileBuffer; + CopyMem (Context, &ContextData->Context, sizeof (MEMORY_PROFILE_CONTEXT)); + DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)(Context + 1); + + DriverInfoList = ContextData->DriverInfoList; + for (DriverLink = DriverInfoList->ForwardLink; + DriverLink != DriverInfoList; + DriverLink = DriverLink->ForwardLink) + { + DriverInfoData = CR ( + DriverLink, + MEMORY_PROFILE_DRIVER_INFO_DATA, + Link, + MEMORY_PROFILE_DRIVER_INFO_SIGNATURE + ); + CopyMem (DriverInfo, &DriverInfoData->DriverInfo, sizeof (MEMORY_PROFILE_DRIVER_INFO)); + if (DriverInfo->PdbStringOffset != 0) { + PdbSize = AsciiStrSize (DriverInfoData->PdbString); + CopyMem ((VOID *)((UINTN)DriverInfo + DriverInfo->PdbStringOffset), DriverInfoData->PdbString, PdbSize); + } + + AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)DriverInfo + DriverInfo->Header.Length); + + AllocInfoList = DriverInfoData->AllocInfoList; + for (AllocLink = AllocInfoList->ForwardLink; + AllocLink != AllocInfoList; + AllocLink = AllocLink->ForwardLink) + { + AllocInfoData = CR ( + AllocLink, + MEMORY_PROFILE_ALLOC_INFO_DATA, + Link, + MEMORY_PROFILE_ALLOC_INFO_SIGNATURE + ); + CopyMem (AllocInfo, &AllocInfoData->AllocInfo, sizeof (MEMORY_PROFILE_ALLOC_INFO)); + if (AllocInfo->ActionStringOffset != 0) { + ActionStringSize = AsciiStrSize (AllocInfoData->ActionString); + CopyMem ((VOID *)((UINTN)AllocInfo + AllocInfo->ActionStringOffset), AllocInfoData->ActionString, ActionStringSize); + } + + AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)AllocInfo + AllocInfo->Header.Length); + } + + DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)AllocInfo; + } +} + +/** + Get memory profile data. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer. + On return, points to the size of the data returned in ProfileBuffer. + @param[out] ProfileBuffer Profile buffer. + + @return EFI_SUCCESS Get the memory profile data successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data. + ProfileSize is updated with the size required. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolGetData ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN OUT UINT64 *ProfileSize, + OUT VOID *ProfileBuffer + ) +{ + UINTN Size; + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + BOOLEAN MemoryProfileGettingStatus; + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + MemoryProfileGettingStatus = mMemoryProfileGettingStatus; + mMemoryProfileGettingStatus = TRUE; + + Size = MemoryProfileGetDataSize (); + + if (*ProfileSize < Size) { + *ProfileSize = Size; + mMemoryProfileGettingStatus = MemoryProfileGettingStatus; + return EFI_BUFFER_TOO_SMALL; + } + + *ProfileSize = Size; + MemoryProfileCopyData (ProfileBuffer); + + mMemoryProfileGettingStatus = MemoryProfileGettingStatus; + return EFI_SUCCESS; +} + +/** + Register image to memory profile. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + @param[in] FileType File type of the image. + + @return EFI_SUCCESS Register successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_OUT_OF_RESOURCES No enough resource for this register. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolRegisterImage ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize, + IN EFI_FV_FILETYPE FileType + ) +{ + EFI_STATUS Status; + LOADED_IMAGE_PRIVATE_DATA DriverEntry; + VOID *EntryPointInImage; + + ZeroMem (&DriverEntry, sizeof (DriverEntry)); + DriverEntry.Info.FilePath = FilePath; + DriverEntry.ImageContext.ImageAddress = ImageBase; + DriverEntry.ImageContext.ImageSize = ImageSize; + Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverEntry.ImageContext.EntryPoint = (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage; + DriverEntry.ImageContext.ImageType = InternalPeCoffGetSubsystem ((VOID *)(UINTN)ImageBase); + + return RegisterMemoryProfileImage (&DriverEntry, FileType); +} + +/** + Unregister image from memory profile. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] FilePath File path of the image. + @param[in] ImageBase Image base address. + @param[in] ImageSize Image size. + + @return EFI_SUCCESS Unregister successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required. + @return EFI_NOT_FOUND The image is not found. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolUnregisterImage ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN PHYSICAL_ADDRESS ImageBase, + IN UINT64 ImageSize + ) +{ + EFI_STATUS Status; + LOADED_IMAGE_PRIVATE_DATA DriverEntry; + VOID *EntryPointInImage; + + ZeroMem (&DriverEntry, sizeof (DriverEntry)); + DriverEntry.Info.FilePath = FilePath; + DriverEntry.ImageContext.ImageAddress = ImageBase; + DriverEntry.ImageContext.ImageSize = ImageSize; + Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage); + ASSERT_EFI_ERROR (Status); + DriverEntry.ImageContext.EntryPoint = (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage; + + return UnregisterMemoryProfileImage (&DriverEntry); +} + +/** + Get memory profile recording state. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[out] RecordingState Recording state. + + @return EFI_SUCCESS Memory profile recording state is returned. + @return EFI_UNSUPPORTED Memory profile is unsupported. + @return EFI_INVALID_PARAMETER RecordingState is NULL. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolGetRecordingState ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + OUT BOOLEAN *RecordingState + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + if (RecordingState == NULL) { + return EFI_INVALID_PARAMETER; + } + + *RecordingState = mMemoryProfileRecordingEnable; + return EFI_SUCCESS; +} + +/** + Set memory profile recording state. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] RecordingState Recording state. + + @return EFI_SUCCESS Set memory profile recording state successfully. + @return EFI_UNSUPPORTED Memory profile is unsupported. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolSetRecordingState ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN BOOLEAN RecordingState + ) +{ + MEMORY_PROFILE_CONTEXT_DATA *ContextData; + + ContextData = GetMemoryProfileContext (); + if (ContextData == NULL) { + return EFI_UNSUPPORTED; + } + + mMemoryProfileRecordingEnable = RecordingState; + return EFI_SUCCESS; +} + +/** + Record memory profile of multilevel caller. + + @param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance. + @param[in] CallerAddress Address of caller. + @param[in] Action Memory profile action. + @param[in] MemoryType Memory type. + EfiMaxMemoryType means the MemoryType is unknown. + @param[in] Buffer Buffer address. + @param[in] Size Buffer size. + @param[in] ActionString String for memory profile action. + Only needed for user defined allocate action. + + @return EFI_SUCCESS Memory profile is updated. + @return EFI_UNSUPPORTED Memory profile is unsupported, + or memory profile for the image is not required, + or memory profile for the memory type is not required. + @return EFI_ACCESS_DENIED It is during memory profile data getting. + @return EFI_ABORTED Memory profile recording is not enabled. + @return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action. + @return EFI_NOT_FOUND No matched allocate info found for free action. + +**/ +EFI_STATUS +EFIAPI +ProfileProtocolRecord ( + IN EDKII_MEMORY_PROFILE_PROTOCOL *This, + IN PHYSICAL_ADDRESS CallerAddress, + IN MEMORY_PROFILE_ACTION Action, + IN EFI_MEMORY_TYPE MemoryType, + IN VOID *Buffer, + IN UINTN Size, + IN CHAR8 *ActionString OPTIONAL + ) +{ + return CoreUpdateProfile (CallerAddress, Action, MemoryType, Size, Buffer, ActionString); +} + +//////////////////// diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index 5a51d9df1a..609bddf0d2 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -1,2308 +1,2308 @@ -/** @file
- UEFI Memory page management functions.
-
-Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "DxeMain.h"
-#include "Imem.h"
-#include "HeapGuard.h"
-#include <Pi/PiDxeCis.h>
-
-//
-// Entry for tracking the memory regions for each memory type to coalesce similar memory types
-//
-typedef struct {
- EFI_PHYSICAL_ADDRESS BaseAddress;
- EFI_PHYSICAL_ADDRESS MaximumAddress;
- UINT64 CurrentNumberOfPages;
- UINT64 NumberOfPages;
- UINTN InformationIndex;
- BOOLEAN Special;
- BOOLEAN Runtime;
-} EFI_MEMORY_TYPE_STATISTICS;
-
-//
-// MemoryMap - The current memory map
-//
-UINTN mMemoryMapKey = 0;
-
-#define MAX_MAP_DEPTH 6
-
-///
-/// mMapDepth - depth of new descriptor stack
-///
-UINTN mMapDepth = 0;
-///
-/// mMapStack - space to use as temp storage to build new map descriptors
-///
-MEMORY_MAP mMapStack[MAX_MAP_DEPTH];
-UINTN mFreeMapStack = 0;
-///
-/// This list maintain the free memory map list
-///
-LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);
-BOOLEAN mMemoryTypeInformationInitialized = FALSE;
-
-EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = {
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiReservedMemoryType
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderCode
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderData
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesCode
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesData
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesCode
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesData
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiConventionalMemory
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiUnusableMemory
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIReclaimMemory
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIMemoryNVS
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIO
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiPalCode
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiPersistentMemory
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiUnacceptedMemoryType
- { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE } // EfiMaxMemoryType
-};
-
-EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = MAX_ALLOC_ADDRESS;
-EFI_PHYSICAL_ADDRESS mDefaultBaseAddress = MAX_ALLOC_ADDRESS;
-
-EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = {
- { EfiReservedMemoryType, 0 },
- { EfiLoaderCode, 0 },
- { EfiLoaderData, 0 },
- { EfiBootServicesCode, 0 },
- { EfiBootServicesData, 0 },
- { EfiRuntimeServicesCode, 0 },
- { EfiRuntimeServicesData, 0 },
- { EfiConventionalMemory, 0 },
- { EfiUnusableMemory, 0 },
- { EfiACPIReclaimMemory, 0 },
- { EfiACPIMemoryNVS, 0 },
- { EfiMemoryMappedIO, 0 },
- { EfiMemoryMappedIOPortSpace, 0 },
- { EfiPalCode, 0 },
- { EfiPersistentMemory, 0 },
- { EfiGcdMemoryTypeUnaccepted, 0 },
- { EfiMaxMemoryType, 0 }
-};
-//
-// Only used when load module at fixed address feature is enabled. True means the memory is alreay successfully allocated
-// and ready to load the module in to specified address.or else, the memory is not ready and module will be loaded at a
-// address assigned by DXE core.
-//
-GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN gLoadFixedAddressCodeMemoryReady = FALSE;
-
-/**
- Enter critical section by gaining lock on gMemoryLock.
-
-**/
-VOID
-CoreAcquireMemoryLock (
- VOID
- )
-{
- CoreAcquireLock (&gMemoryLock);
-}
-
-/**
- Exit critical section by releasing lock on gMemoryLock.
-
-**/
-VOID
-CoreReleaseMemoryLock (
- VOID
- )
-{
- CoreReleaseLock (&gMemoryLock);
-}
-
-/**
- Internal function. Removes a descriptor entry.
-
- @param Entry The entry to remove
-
-**/
-VOID
-RemoveMemoryMapEntry (
- IN OUT MEMORY_MAP *Entry
- )
-{
- RemoveEntryList (&Entry->Link);
- Entry->Link.ForwardLink = NULL;
-
- if (Entry->FromPages) {
- //
- // Insert the free memory map descriptor to the end of mFreeMemoryMapEntryList
- //
- InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);
- }
-}
-
-/**
- Internal function. Adds a ranges to the memory map.
- The range must not already exist in the map.
-
- @param Type The type of memory range to add
- @param Start The starting address in the memory range Must be
- paged aligned
- @param End The last address in the range Must be the last
- byte of a page
- @param Attribute The attributes of the memory range to add
-
-**/
-VOID
-CoreAddRange (
- IN EFI_MEMORY_TYPE Type,
- IN EFI_PHYSICAL_ADDRESS Start,
- IN EFI_PHYSICAL_ADDRESS End,
- IN UINT64 Attribute
- )
-{
- LIST_ENTRY *Link;
- MEMORY_MAP *Entry;
-
- ASSERT ((Start & EFI_PAGE_MASK) == 0);
- ASSERT (End > Start);
-
- ASSERT_LOCKED (&gMemoryLock);
-
- DEBUG ((DEBUG_PAGE, "AddRange: %lx-%lx to %d\n", Start, End, Type));
-
- //
- // If memory of type EfiConventionalMemory is being added that includes the page
- // starting at address 0, then zero the page starting at address 0. This has
- // two benifits. It helps find NULL pointer bugs and it also maximizes
- // compatibility with operating systems that may evaluate memory in this page
- // for legacy data structures. If memory of any other type is added starting
- // at address 0, then do not zero the page at address 0 because the page is being
- // used for other purposes.
- //
- if ((Type == EfiConventionalMemory) && (Start == 0) && (End >= EFI_PAGE_SIZE - 1)) {
- if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) == 0) {
- SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0);
- }
- }
-
- //
- // Memory map being altered so updated key
- //
- mMemoryMapKey += 1;
-
- //
- // UEFI 2.0 added an event group for notificaiton on memory map changes.
- // So we need to signal this Event Group every time the memory map changes.
- // If we are in EFI 1.10 compatability mode no event groups will be
- // found and nothing will happen we we call this function. These events
- // will get signaled but since a lock is held around the call to this
- // function the notificaiton events will only be called after this function
- // returns and the lock is released.
- //
- CoreNotifySignalList (&gEfiEventMemoryMapChangeGuid);
-
- //
- // Look for adjoining memory descriptor
- //
-
- // Two memory descriptors can only be merged if they have the same Type
- // and the same Attribute
- //
-
- Link = gMemoryMap.ForwardLink;
- while (Link != &gMemoryMap) {
- Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
- Link = Link->ForwardLink;
-
- if (Entry->Type != Type) {
- continue;
- }
-
- if (Entry->Attribute != Attribute) {
- continue;
- }
-
- if (Entry->End + 1 == Start) {
- Start = Entry->Start;
- RemoveMemoryMapEntry (Entry);
- } else if (Entry->Start == End + 1) {
- End = Entry->End;
- RemoveMemoryMapEntry (Entry);
- }
- }
-
- //
- // Add descriptor
- //
-
- mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE;
- mMapStack[mMapDepth].FromPages = FALSE;
- mMapStack[mMapDepth].Type = Type;
- mMapStack[mMapDepth].Start = Start;
- mMapStack[mMapDepth].End = End;
- mMapStack[mMapDepth].VirtualStart = 0;
- mMapStack[mMapDepth].Attribute = Attribute;
- InsertTailList (&gMemoryMap, &mMapStack[mMapDepth].Link);
-
- mMapDepth += 1;
- ASSERT (mMapDepth < MAX_MAP_DEPTH);
-
- return;
-}
-
-/**
- Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList.
- If the list is emtry, then allocate a new page to refuel the list.
- Please Note this algorithm to allocate the memory map descriptor has a property
- that the memory allocated for memory entries always grows, and will never really be freed
- For example, if the current boot uses 2000 memory map entries at the maximum point, but
- ends up with only 50 at the time the OS is booted, then the memory associated with the 1950
- memory map entries is still allocated from EfiBootServicesMemory.
-
-
- @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList
-
-**/
-MEMORY_MAP *
-AllocateMemoryMapEntry (
- VOID
- )
-{
- MEMORY_MAP *FreeDescriptorEntries;
- MEMORY_MAP *Entry;
- UINTN Index;
-
- if (IsListEmpty (&mFreeMemoryMapEntryList)) {
- //
- // The list is empty, to allocate one page to refuel the list
- //
- FreeDescriptorEntries = CoreAllocatePoolPages (
- EfiBootServicesData,
- EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION_GRANULARITY),
- DEFAULT_PAGE_ALLOCATION_GRANULARITY,
- FALSE
- );
- if (FreeDescriptorEntries != NULL) {
- //
- // Enque the free memmory map entries into the list
- //
- for (Index = 0; Index < DEFAULT_PAGE_ALLOCATION_GRANULARITY / sizeof (MEMORY_MAP); Index++) {
- FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;
- InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);
- }
- } else {
- return NULL;
- }
- }
-
- //
- // dequeue the first descriptor from the list
- //
- Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
- RemoveEntryList (&Entry->Link);
-
- return Entry;
-}
-
-/**
- Internal function. Moves any memory descriptors that are on the
- temporary descriptor stack to heap.
-
-**/
-VOID
-CoreFreeMemoryMapStack (
- VOID
- )
-{
- MEMORY_MAP *Entry;
- MEMORY_MAP *Entry2;
- LIST_ENTRY *Link2;
-
- ASSERT_LOCKED (&gMemoryLock);
-
- //
- // If already freeing the map stack, then return
- //
- if (mFreeMapStack != 0) {
- return;
- }
-
- //
- // Move the temporary memory descriptor stack into pool
- //
- mFreeMapStack += 1;
-
- while (mMapDepth != 0) {
- //
- // Deque an memory map entry from mFreeMemoryMapEntryList
- //
- Entry = AllocateMemoryMapEntry ();
-
- ASSERT (Entry);
-
- //
- // Update to proper entry
- //
- mMapDepth -= 1;
-
- if (mMapStack[mMapDepth].Link.ForwardLink != NULL) {
- //
- // Move this entry to general memory
- //
- RemoveEntryList (&mMapStack[mMapDepth].Link);
- mMapStack[mMapDepth].Link.ForwardLink = NULL;
-
- CopyMem (Entry, &mMapStack[mMapDepth], sizeof (MEMORY_MAP));
- Entry->FromPages = TRUE;
-
- //
- // Find insertion location
- //
- for (Link2 = gMemoryMap.ForwardLink; Link2 != &gMemoryMap; Link2 = Link2->ForwardLink) {
- Entry2 = CR (Link2, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
- if (Entry2->FromPages && (Entry2->Start > Entry->Start)) {
- break;
- }
- }
-
- InsertTailList (Link2, &Entry->Link);
- } else {
- //
- // This item of mMapStack[mMapDepth] has already been dequeued from gMemoryMap list,
- // so here no need to move it to memory.
- //
- InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);
- }
- }
-
- mFreeMapStack -= 1;
-}
-
-/**
- Find untested but initialized memory regions in GCD map and convert them to be DXE allocatable.
-
-**/
-BOOLEAN
-PromoteMemoryResource (
- VOID
- )
-{
- LIST_ENTRY *Link;
- EFI_GCD_MAP_ENTRY *Entry;
- BOOLEAN Promoted;
- EFI_PHYSICAL_ADDRESS StartAddress;
- EFI_PHYSICAL_ADDRESS EndAddress;
- EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
-
- DEBUG ((DEBUG_PAGE, "Promote the memory resource\n"));
-
- CoreAcquireGcdMemoryLock ();
-
- Promoted = FALSE;
- Link = mGcdMemorySpaceMap.ForwardLink;
- while (Link != &mGcdMemorySpaceMap) {
- Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
-
- if ((Entry->GcdMemoryType == EfiGcdMemoryTypeReserved) &&
- (Entry->EndAddress < MAX_ALLOC_ADDRESS) &&
- ((Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
- (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)))
- {
- //
- // Update the GCD map
- //
- if ((Entry->Capabilities & EFI_MEMORY_MORE_RELIABLE) == EFI_MEMORY_MORE_RELIABLE) {
- Entry->GcdMemoryType = EfiGcdMemoryTypeMoreReliable;
- } else {
- Entry->GcdMemoryType = EfiGcdMemoryTypeSystemMemory;
- }
-
- Entry->Capabilities |= EFI_MEMORY_TESTED;
- Entry->ImageHandle = gDxeCoreImageHandle;
- Entry->DeviceHandle = NULL;
-
- //
- // Add to allocable system memory resource
- //
-
- CoreAddRange (
- EfiConventionalMemory,
- Entry->BaseAddress,
- Entry->EndAddress,
- Entry->Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME)
- );
- CoreFreeMemoryMapStack ();
-
- Promoted = TRUE;
- }
-
- Link = Link->ForwardLink;
- }
-
- CoreReleaseGcdMemoryLock ();
-
- if (!Promoted) {
- //
- // If freed-memory guard is enabled, we could promote pages from
- // guarded free pages.
- //
- Promoted = PromoteGuardedFreePages (&StartAddress, &EndAddress);
- if (Promoted) {
- if (!EFI_ERROR (CoreGetMemorySpaceDescriptor (StartAddress, &Descriptor))) {
- CoreAddRange (
- EfiConventionalMemory,
- StartAddress,
- EndAddress,
- Descriptor.Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED |
- EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME)
- );
- }
- }
- }
-
- return Promoted;
-}
-
-/**
- This function try to allocate Runtime code & Boot time code memory range. If LMFA enabled, 2 patchable PCD
- PcdLoadFixAddressRuntimeCodePageNumber & PcdLoadFixAddressBootTimeCodePageNumber which are set by tools will record the
- size of boot time and runtime code.
-
-**/
-VOID
-CoreLoadingFixedAddressHook (
- VOID
- )
-{
- UINT32 RuntimeCodePageNumber;
- UINT32 BootTimeCodePageNumber;
- EFI_PHYSICAL_ADDRESS RuntimeCodeBase;
- EFI_PHYSICAL_ADDRESS BootTimeCodeBase;
- EFI_STATUS Status;
-
- //
- // Make sure these 2 areas are not initialzied.
- //
- if (!gLoadFixedAddressCodeMemoryReady) {
- RuntimeCodePageNumber = PcdGet32 (PcdLoadFixAddressRuntimeCodePageNumber);
- BootTimeCodePageNumber = PcdGet32 (PcdLoadFixAddressBootTimeCodePageNumber);
- RuntimeCodeBase = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressConfigurationTable.DxeCodeTopAddress - EFI_PAGES_TO_SIZE (RuntimeCodePageNumber));
- BootTimeCodeBase = (EFI_PHYSICAL_ADDRESS)(RuntimeCodeBase - EFI_PAGES_TO_SIZE (BootTimeCodePageNumber));
- //
- // Try to allocate runtime memory.
- //
- Status = CoreAllocatePages (
- AllocateAddress,
- EfiRuntimeServicesCode,
- RuntimeCodePageNumber,
- &RuntimeCodeBase
- );
- if (EFI_ERROR (Status)) {
- //
- // Runtime memory allocation failed
- //
- return;
- }
-
- //
- // Try to allocate boot memory.
- //
- Status = CoreAllocatePages (
- AllocateAddress,
- EfiBootServicesCode,
- BootTimeCodePageNumber,
- &BootTimeCodeBase
- );
- if (EFI_ERROR (Status)) {
- //
- // boot memory allocation failed. Free Runtime code range and will try the allocation again when
- // new memory range is installed.
- //
- CoreFreePages (
- RuntimeCodeBase,
- RuntimeCodePageNumber
- );
- return;
- }
-
- gLoadFixedAddressCodeMemoryReady = TRUE;
- }
-
- return;
-}
-
-/**
- Sets the preferred memory range to use for the Memory Type Information bins.
- This service must be called before fist call to CoreAddMemoryDescriptor().
-
- If the location of the Memory Type Information bins has already been
- established or the size of the range provides is smaller than all the
- Memory Type Information bins, then the range provides is not used.
-
- @param Start The start address of the Memory Type Information range.
- @param Length The size, in bytes, of the Memory Type Information range.
-**/
-VOID
-CoreSetMemoryTypeInformationRange (
- IN EFI_PHYSICAL_ADDRESS Start,
- IN UINT64 Length
- )
-{
- EFI_PHYSICAL_ADDRESS Top;
- EFI_MEMORY_TYPE Type;
- UINTN Index;
- UINTN Size;
-
- //
- // Return if Memory Type Information bin locations have already been set
- //
- if (mMemoryTypeInformationInitialized) {
- DEBUG ((DEBUG_ERROR, "%a: Ignored. Bins already set.\n", __func__));
- return;
- }
-
- //
- // Return if size of the Memory Type Information bins is greater than Length
- //
- Size = 0;
- for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
- //
- // Make sure the memory type in the gMemoryTypeInformation[] array is valid
- //
- Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type);
- if ((UINT32)Type > EfiMaxMemoryType) {
- continue;
- }
-
- Size += EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages);
- }
-
- if (Size > Length) {
- return;
- }
-
- //
- // Loop through each memory type in the order specified by the
- // gMemoryTypeInformation[] array
- //
- Top = Start + Length;
- for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
- //
- // Make sure the memory type in the gMemoryTypeInformation[] array is valid
- //
- Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type);
- if ((UINT32)Type > EfiMaxMemoryType) {
- continue;
- }
-
- if (gMemoryTypeInformation[Index].NumberOfPages != 0) {
- mMemoryTypeStatistics[Type].MaximumAddress = Top - 1;
- Top -= EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages);
- mMemoryTypeStatistics[Type].BaseAddress = Top;
-
- //
- // If the current base address is the lowest address so far, then update
- // the default maximum address
- //
- if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) {
- mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1;
- }
-
- mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages;
- gMemoryTypeInformation[Index].NumberOfPages = 0;
- }
- }
-
- //
- // If the number of pages reserved for a memory type is 0, then all
- // allocations for that type should be in the default range.
- //
- for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) {
- for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
- if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) {
- mMemoryTypeStatistics[Type].InformationIndex = Index;
- }
- }
-
- mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0;
- if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) {
- mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress;
- }
- }
-
- mMemoryTypeInformationInitialized = TRUE;
-}
-
-/**
- Called to initialize the memory map and add descriptors to
- the current descriptor list.
- The first descriptor that is added must be general usable
- memory as the addition allocates heap.
-
- @param Type The type of memory to add
- @param Start The starting address in the memory range Must be
- page aligned
- @param NumberOfPages The number of pages in the range
- @param Attribute Attributes of the memory to add
-
- @return None. The range is added to the memory map
-
-**/
-VOID
-CoreAddMemoryDescriptor (
- IN EFI_MEMORY_TYPE Type,
- IN EFI_PHYSICAL_ADDRESS Start,
- IN UINT64 NumberOfPages,
- IN UINT64 Attribute
- )
-{
- EFI_PHYSICAL_ADDRESS End;
- EFI_STATUS Status;
- UINTN Index;
- UINTN FreeIndex;
-
- if ((Start & EFI_PAGE_MASK) != 0) {
- return;
- }
-
- if ((Type >= EfiMaxMemoryType) && (Type < MEMORY_TYPE_OEM_RESERVED_MIN)) {
- return;
- }
-
- CoreAcquireMemoryLock ();
- End = Start + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) - 1;
- CoreAddRange (Type, Start, End, Attribute);
- CoreFreeMemoryMapStack ();
- CoreReleaseMemoryLock ();
-
- ApplyMemoryProtectionPolicy (
- EfiMaxMemoryType,
- Type,
- Start,
- LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT)
- );
-
- //
- // If Loading Module At Fixed Address feature is enabled. try to allocate memory with Runtime code & Boot time code type
- //
- if (PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) {
- CoreLoadingFixedAddressHook ();
- }
-
- //
- // Check to see if the statistics for the different memory types have already been established
- //
- if (mMemoryTypeInformationInitialized) {
- return;
- }
-
- //
- // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array
- //
- for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
- //
- // Make sure the memory type in the gMemoryTypeInformation[] array is valid
- //
- Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type);
- if ((UINT32)Type > EfiMaxMemoryType) {
- continue;
- }
-
- if (gMemoryTypeInformation[Index].NumberOfPages != 0) {
- //
- // Allocate pages for the current memory type from the top of available memory
- //
- Status = CoreAllocatePages (
- AllocateAnyPages,
- Type,
- gMemoryTypeInformation[Index].NumberOfPages,
- &mMemoryTypeStatistics[Type].BaseAddress
- );
- if (EFI_ERROR (Status)) {
- //
- // If an error occurs allocating the pages for the current memory type, then
- // free all the pages allocates for the previous memory types and return. This
- // operation with be retied when/if more memory is added to the system
- //
- for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) {
- //
- // Make sure the memory type in the gMemoryTypeInformation[] array is valid
- //
- Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[FreeIndex].Type);
- if ((UINT32)Type > EfiMaxMemoryType) {
- continue;
- }
-
- if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) {
- CoreFreePages (
- mMemoryTypeStatistics[Type].BaseAddress,
- gMemoryTypeInformation[FreeIndex].NumberOfPages
- );
- mMemoryTypeStatistics[Type].BaseAddress = 0;
- mMemoryTypeStatistics[Type].MaximumAddress = MAX_ALLOC_ADDRESS;
- }
- }
-
- return;
- }
-
- //
- // Compute the address at the top of the current statistics
- //
- mMemoryTypeStatistics[Type].MaximumAddress =
- mMemoryTypeStatistics[Type].BaseAddress +
- LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1;
-
- //
- // If the current base address is the lowest address so far, then update the default
- // maximum address
- //
- if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) {
- mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1;
- }
- }
- }
-
- //
- // There was enough system memory for all the the memory types were allocated. So,
- // those memory areas can be freed for future allocations, and all future memory
- // allocations can occur within their respective bins
- //
- for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
- //
- // Make sure the memory type in the gMemoryTypeInformation[] array is valid
- //
- Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type);
- if ((UINT32)Type > EfiMaxMemoryType) {
- continue;
- }
-
- if (gMemoryTypeInformation[Index].NumberOfPages != 0) {
- CoreFreePages (
- mMemoryTypeStatistics[Type].BaseAddress,
- gMemoryTypeInformation[Index].NumberOfPages
- );
- mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages;
- gMemoryTypeInformation[Index].NumberOfPages = 0;
- }
- }
-
- //
- // If the number of pages reserved for a memory type is 0, then all allocations for that type
- // should be in the default range.
- //
- for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) {
- for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
- if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) {
- mMemoryTypeStatistics[Type].InformationIndex = Index;
- }
- }
-
- mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0;
- if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) {
- mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress;
- }
- }
-
- mMemoryTypeInformationInitialized = TRUE;
-}
-
-/**
- Internal function. Converts a memory range to the specified type or attributes.
- The range must exist in the memory map. Either ChangingType or
- ChangingAttributes must be set, but not both.
-
- @param Start The first address of the range Must be page
- aligned
- @param NumberOfPages The number of pages to convert
- @param ChangingType Boolean indicating that type value should be changed
- @param NewType The new type for the memory range
- @param ChangingAttributes Boolean indicating that attributes value should be changed
- @param NewAttributes The new attributes for the memory range
-
- @retval EFI_INVALID_PARAMETER Invalid parameter
- @retval EFI_NOT_FOUND Could not find a descriptor cover the specified
- range or convertion not allowed.
- @retval EFI_SUCCESS Successfully converts the memory range to the
- specified type.
-
-**/
-EFI_STATUS
-CoreConvertPagesEx (
- IN UINT64 Start,
- IN UINT64 NumberOfPages,
- IN BOOLEAN ChangingType,
- IN EFI_MEMORY_TYPE NewType,
- IN BOOLEAN ChangingAttributes,
- IN UINT64 NewAttributes
- )
-{
- UINT64 NumberOfBytes;
- UINT64 End;
- UINT64 RangeEnd;
- UINT64 Attribute;
- EFI_MEMORY_TYPE MemType;
- LIST_ENTRY *Link;
- MEMORY_MAP *Entry;
-
- Entry = NULL;
- NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);
- End = Start + NumberOfBytes - 1;
-
- ASSERT (NumberOfPages);
- ASSERT ((Start & EFI_PAGE_MASK) == 0);
- ASSERT (End > Start);
- ASSERT_LOCKED (&gMemoryLock);
- ASSERT ((ChangingType == FALSE) || (ChangingAttributes == FALSE));
-
- if ((NumberOfPages == 0) || ((Start & EFI_PAGE_MASK) != 0) || (Start >= End)) {
- return EFI_INVALID_PARAMETER;
- }
-
- //
- // Convert the entire range
- //
-
- while (Start < End) {
- //
- // Find the entry that the covers the range
- //
- for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
- Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
-
- if ((Entry->Start <= Start) && (Entry->End > Start)) {
- break;
- }
- }
-
- if (Link == &gMemoryMap) {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: failed to find range %lx - %lx\n", Start, End));
- return EFI_NOT_FOUND;
- }
-
- //
- // If we are converting the type of the range from EfiConventionalMemory to
- // another type, we have to ensure that the entire range is covered by a
- // single entry.
- //
- if (ChangingType && (NewType != EfiConventionalMemory)) {
- if (Entry->End < End) {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: range %lx - %lx covers multiple entries\n", Start, End));
- return EFI_NOT_FOUND;
- }
- }
-
- //
- // Convert range to the end, or to the end of the descriptor
- // if that's all we've got
- //
- RangeEnd = End;
-
- ASSERT (Entry != NULL);
- if (Entry->End < End) {
- RangeEnd = Entry->End;
- }
-
- if (ChangingType) {
- DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to type %d\n", Start, RangeEnd, NewType));
- }
-
- if (ChangingAttributes) {
- DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to attr %lx\n", Start, RangeEnd, NewAttributes));
- }
-
- if (ChangingType) {
- //
- // Debug code - verify conversion is allowed
- //
- if (!((NewType == EfiConventionalMemory) ? 1 : 0) ^ ((Entry->Type == EfiConventionalMemory) ? 1 : 0)) {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: Incompatible memory types, "));
- if (Entry->Type == EfiConventionalMemory) {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to free have been freed\n"));
- } else {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to allocate have been allocated\n"));
- }
-
- return EFI_NOT_FOUND;
- }
-
- //
- // Update counters for the number of pages allocated to each memory type
- //
- if ((UINT32)Entry->Type < EfiMaxMemoryType) {
- if (((Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress) && (Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress)) ||
- ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress)))
- {
- if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) {
- mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0;
- } else {
- mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages;
- }
- }
- }
-
- if ((UINT32)NewType < EfiMaxMemoryType) {
- if (((Start >= mMemoryTypeStatistics[NewType].BaseAddress) && (Start <= mMemoryTypeStatistics[NewType].MaximumAddress)) ||
- ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress)))
- {
- mMemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages;
- if (mMemoryTypeStatistics[NewType].CurrentNumberOfPages > gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages) {
- gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)mMemoryTypeStatistics[NewType].CurrentNumberOfPages;
- }
- }
- }
- }
-
- //
- // Pull range out of descriptor
- //
- if (Entry->Start == Start) {
- //
- // Clip start
- //
- Entry->Start = RangeEnd + 1;
- } else if (Entry->End == RangeEnd) {
- //
- // Clip end
- //
- Entry->End = Start - 1;
- } else {
- //
- // Pull it out of the center, clip current
- //
-
- //
- // Add a new one
- //
- mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE;
- mMapStack[mMapDepth].FromPages = FALSE;
- mMapStack[mMapDepth].Type = Entry->Type;
- mMapStack[mMapDepth].Start = RangeEnd+1;
- mMapStack[mMapDepth].End = Entry->End;
-
- //
- // Inherit Attribute from the Memory Descriptor that is being clipped
- //
- mMapStack[mMapDepth].Attribute = Entry->Attribute;
-
- Entry->End = Start - 1;
- ASSERT (Entry->Start < Entry->End);
-
- Entry = &mMapStack[mMapDepth];
- InsertTailList (&gMemoryMap, &Entry->Link);
-
- mMapDepth += 1;
- ASSERT (mMapDepth < MAX_MAP_DEPTH);
- }
-
- //
- // The new range inherits the same Attribute as the Entry
- // it is being cut out of unless attributes are being changed
- //
- if (ChangingType) {
- Attribute = Entry->Attribute;
- MemType = NewType;
- } else {
- Attribute = NewAttributes;
- MemType = Entry->Type;
- }
-
- //
- // If the descriptor is empty, then remove it from the map
- //
- if (Entry->Start == Entry->End + 1) {
- RemoveMemoryMapEntry (Entry);
- Entry = NULL;
- }
-
- //
- // Add our new range in. Don't do this for freed pages if freed-memory
- // guard is enabled.
- //
- if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) ||
- !ChangingType ||
- (MemType != EfiConventionalMemory))
- {
- CoreAddRange (MemType, Start, RangeEnd, Attribute);
- }
-
- if (ChangingType && (MemType == EfiConventionalMemory)) {
- //
- // Avoid calling DEBUG_CLEAR_MEMORY() for an address of 0 because this
- // macro will ASSERT() if address is 0. Instead, CoreAddRange() guarantees
- // that the page starting at address 0 is always filled with zeros.
- //
- if (Start == 0) {
- if (RangeEnd > EFI_PAGE_SIZE) {
- DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)EFI_PAGE_SIZE, (UINTN)(RangeEnd - EFI_PAGE_SIZE + 1));
- }
- } else {
- DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)Start, (UINTN)(RangeEnd - Start + 1));
- }
- }
-
- //
- // Move any map descriptor stack to general pool
- //
- CoreFreeMemoryMapStack ();
-
- //
- // Bump the starting address, and convert the next range
- //
- Start = RangeEnd + 1;
- }
-
- //
- // Converted the whole range, done
- //
-
- return EFI_SUCCESS;
-}
-
-/**
- Internal function. Converts a memory range to the specified type.
- The range must exist in the memory map.
-
- @param Start The first address of the range Must be page
- aligned
- @param NumberOfPages The number of pages to convert
- @param NewType The new type for the memory range
-
- @retval EFI_INVALID_PARAMETER Invalid parameter
- @retval EFI_NOT_FOUND Could not find a descriptor cover the specified
- range or convertion not allowed.
- @retval EFI_SUCCESS Successfully converts the memory range to the
- specified type.
-
-**/
-EFI_STATUS
-CoreConvertPages (
- IN UINT64 Start,
- IN UINT64 NumberOfPages,
- IN EFI_MEMORY_TYPE NewType
- )
-{
- return CoreConvertPagesEx (Start, NumberOfPages, TRUE, NewType, FALSE, 0);
-}
-
-/**
- Internal function. Converts a memory range to use new attributes.
-
- @param Start The first address of the range Must be page
- aligned
- @param NumberOfPages The number of pages to convert
- @param NewAttributes The new attributes value for the range.
-
-**/
-VOID
-CoreUpdateMemoryAttributes (
- IN EFI_PHYSICAL_ADDRESS Start,
- IN UINT64 NumberOfPages,
- IN UINT64 NewAttributes
- )
-{
- CoreAcquireMemoryLock ();
-
- //
- // Update the attributes to the new value
- //
- CoreConvertPagesEx (Start, NumberOfPages, FALSE, (EFI_MEMORY_TYPE)0, TRUE, NewAttributes);
-
- CoreReleaseMemoryLock ();
-}
-
-/**
- Internal function. Finds a consecutive free page range below
- the requested address.
-
- @param MaxAddress The address that the range must be below
- @param MinAddress The address that the range must be above
- @param NumberOfPages Number of pages needed
- @param NewType The type of memory the range is going to be
- turned into
- @param Alignment Bits to align with
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return The base address of the range, or 0 if the range was not found
-
-**/
-UINT64
-CoreFindFreePagesI (
- IN UINT64 MaxAddress,
- IN UINT64 MinAddress,
- IN UINT64 NumberOfPages,
- IN EFI_MEMORY_TYPE NewType,
- IN UINTN Alignment,
- IN BOOLEAN NeedGuard
- )
-{
- UINT64 NumberOfBytes;
- UINT64 Target;
- UINT64 DescStart;
- UINT64 DescEnd;
- UINT64 DescNumberOfBytes;
- LIST_ENTRY *Link;
- MEMORY_MAP *Entry;
-
- if ((MaxAddress < EFI_PAGE_MASK) || (NumberOfPages == 0)) {
- return 0;
- }
-
- if ((MaxAddress & EFI_PAGE_MASK) != EFI_PAGE_MASK) {
- //
- // If MaxAddress is not aligned to the end of a page
- //
-
- //
- // Change MaxAddress to be 1 page lower
- //
- MaxAddress -= (EFI_PAGE_MASK + 1);
-
- //
- // Set MaxAddress to a page boundary
- //
- MaxAddress &= ~(UINT64)EFI_PAGE_MASK;
-
- //
- // Set MaxAddress to end of the page
- //
- MaxAddress |= EFI_PAGE_MASK;
- }
-
- NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);
- Target = 0;
-
- for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
- Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
-
- //
- // If it's not a free entry, don't bother with it
- //
- if (Entry->Type != EfiConventionalMemory) {
- continue;
- }
-
- DescStart = Entry->Start;
- DescEnd = Entry->End;
-
- //
- // If desc is past max allowed address or below min allowed address, skip it
- //
- if ((DescStart >= MaxAddress) || (DescEnd < MinAddress)) {
- continue;
- }
-
- //
- // If desc ends past max allowed address, clip the end
- //
- if (DescEnd >= MaxAddress) {
- DescEnd = MaxAddress;
- }
-
- DescEnd = ((DescEnd + 1) & (~((UINT64)Alignment - 1))) - 1;
-
- // Skip if DescEnd is less than DescStart after alignment clipping
- if (DescEnd < DescStart) {
- continue;
- }
-
- //
- // Compute the number of bytes we can used from this
- // descriptor, and see it's enough to satisfy the request
- //
- DescNumberOfBytes = DescEnd - DescStart + 1;
-
- if (DescNumberOfBytes >= NumberOfBytes) {
- //
- // If the start of the allocated range is below the min address allowed, skip it
- //
- if ((DescEnd - NumberOfBytes + 1) < MinAddress) {
- continue;
- }
-
- //
- // If this is the best match so far remember it
- //
- if (DescEnd > Target) {
- if (NeedGuard) {
- DescEnd = AdjustMemoryS (
- DescEnd + 1 - DescNumberOfBytes,
- DescNumberOfBytes,
- NumberOfBytes
- );
- if (DescEnd == 0) {
- continue;
- }
- }
-
- Target = DescEnd;
- }
- }
- }
-
- //
- // If this is a grow down, adjust target to be the allocation base
- //
- Target -= NumberOfBytes - 1;
-
- //
- // If we didn't find a match, return 0
- //
- if ((Target & EFI_PAGE_MASK) != 0) {
- return 0;
- }
-
- return Target;
-}
-
-/**
- Internal function. Finds a consecutive free page range below
- the requested address
-
- @param MaxAddress The address that the range must be below
- @param NoPages Number of pages needed
- @param NewType The type of memory the range is going to be
- turned into
- @param Alignment Bits to align with
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return The base address of the range, or 0 if the range was not found.
-
-**/
-UINT64
-FindFreePages (
- IN UINT64 MaxAddress,
- IN UINT64 NoPages,
- IN EFI_MEMORY_TYPE NewType,
- IN UINTN Alignment,
- IN BOOLEAN NeedGuard
- )
-{
- UINT64 Start;
-
- //
- // Attempt to find free pages in the preferred bin based on the requested memory type
- //
- if (((UINT32)NewType < EfiMaxMemoryType) && (MaxAddress >= mMemoryTypeStatistics[NewType].MaximumAddress)) {
- Start = CoreFindFreePagesI (
- mMemoryTypeStatistics[NewType].MaximumAddress,
- mMemoryTypeStatistics[NewType].BaseAddress,
- NoPages,
- NewType,
- Alignment,
- NeedGuard
- );
- if (Start != 0) {
- return Start;
- }
- }
-
- //
- // Attempt to find free pages in the default allocation bin
- //
- if (MaxAddress >= mDefaultMaximumAddress) {
- Start = CoreFindFreePagesI (
- mDefaultMaximumAddress,
- 0,
- NoPages,
- NewType,
- Alignment,
- NeedGuard
- );
- if (Start != 0) {
- if (Start < mDefaultBaseAddress) {
- mDefaultBaseAddress = NeedGuard ? Start - EFI_PAGE_SIZE : Start;
- }
-
- return Start;
- }
- }
-
- //
- // The allocation did not succeed in any of the prefered bins even after
- // promoting resources. Attempt to find free pages anywhere is the requested
- // address range. If this allocation fails, then there are not enough
- // resources anywhere to satisfy the request.
- //
- Start = CoreFindFreePagesI (
- MaxAddress,
- 0,
- NoPages,
- NewType,
- Alignment,
- NeedGuard
- );
- if (Start != 0) {
- return Start;
- }
-
- //
- // If allocations from the preferred bins fail, then attempt to promote memory resources.
- //
- if (!PromoteMemoryResource ()) {
- return 0;
- }
-
- //
- // If any memory resources were promoted, then re-attempt the allocation
- //
- return FindFreePages (MaxAddress, NoPages, NewType, Alignment, NeedGuard);
-}
-
-/**
- Allocates pages from the memory map.
-
- @param Type The type of allocation to perform
- @param MemoryType The type of memory to turn the allocated pages
- into
- @param NumberOfPages The number of pages to allocate
- @param Memory A pointer to receive the base allocated memory
- address
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return Status. On success, Memory is filled in with the base address allocated
- @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in
- spec.
- @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
- @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
- @retval EFI_SUCCESS Pages successfully allocated.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreInternalAllocatePages (
- IN EFI_ALLOCATE_TYPE Type,
- IN EFI_MEMORY_TYPE MemoryType,
- IN UINTN NumberOfPages,
- IN OUT EFI_PHYSICAL_ADDRESS *Memory,
- IN BOOLEAN NeedGuard
- )
-{
- EFI_STATUS Status;
- UINT64 Start;
- UINT64 NumberOfBytes;
- UINT64 End;
- UINT64 MaxAddress;
- UINTN Alignment;
- EFI_MEMORY_TYPE CheckType;
-
- if ((UINT32)Type >= MaxAllocateType) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (((MemoryType >= EfiMaxMemoryType) && (MemoryType < MEMORY_TYPE_OEM_RESERVED_MIN)) ||
- (MemoryType == EfiConventionalMemory) || (MemoryType == EfiPersistentMemory) || (MemoryType == EfiUnacceptedMemoryType))
- {
- return EFI_INVALID_PARAMETER;
- }
-
- if (Memory == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
-
- if ((MemoryType == EfiReservedMemoryType) ||
- (MemoryType == EfiACPIMemoryNVS) ||
- (MemoryType == EfiRuntimeServicesCode) ||
- (MemoryType == EfiRuntimeServicesData))
- {
- Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
- }
-
- //
- // The heap guard system does not support non-EFI_PAGE_SIZE alignments.
- // Architectures that require larger RUNTIME_PAGE_ALLOCATION_GRANULARITY
- // will have the runtime memory regions unguarded. OSes do not
- // map guard pages anyway, so this is a minimal loss. Not guarding prevents
- // alignment mismatches
- //
- if (Alignment != EFI_PAGE_SIZE) {
- NeedGuard = FALSE;
- }
-
- if (Type == AllocateAddress) {
- if ((*Memory & (Alignment - 1)) != 0) {
- return EFI_NOT_FOUND;
- }
- }
-
- NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;
- NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);
-
- //
- // If this is for below a particular address, then
- //
- Start = *Memory;
-
- //
- // The max address is the max natively addressable address for the processor
- //
- MaxAddress = MAX_ALLOC_ADDRESS;
-
- //
- // Check for Type AllocateAddress,
- // if NumberOfPages is 0 or
- // if (NumberOfPages << EFI_PAGE_SHIFT) is above MAX_ALLOC_ADDRESS or
- // if (Start + NumberOfBytes) rolls over 0 or
- // if Start is above MAX_ALLOC_ADDRESS or
- // if End is above MAX_ALLOC_ADDRESS,
- // if Start..End overlaps any tracked MemoryTypeStatistics range
- // return EFI_NOT_FOUND.
- //
- if (Type == AllocateAddress) {
- if ((NumberOfPages == 0) ||
- (NumberOfPages > RShiftU64 (MaxAddress, EFI_PAGE_SHIFT)))
- {
- return EFI_NOT_FOUND;
- }
-
- NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);
- End = Start + NumberOfBytes - 1;
-
- if ((Start >= End) ||
- (Start > MaxAddress) ||
- (End > MaxAddress))
- {
- return EFI_NOT_FOUND;
- }
-
- //
- // A driver is allowed to call AllocatePages using an AllocateAddress type. This type of
- // AllocatePage request the exact physical address if it is not used. The existing code
- // will allow this request even in 'special' pages. The problem with this is that the
- // reason to have 'special' pages for OS hibernate/resume is defeated as memory is
- // fragmented.
- //
-
- for (CheckType = (EFI_MEMORY_TYPE)0; CheckType < EfiMaxMemoryType; CheckType++) {
- if ((MemoryType != CheckType) &&
- mMemoryTypeStatistics[CheckType].Special &&
- (mMemoryTypeStatistics[CheckType].NumberOfPages > 0))
- {
- if ((Start >= mMemoryTypeStatistics[CheckType].BaseAddress) &&
- (Start <= mMemoryTypeStatistics[CheckType].MaximumAddress))
- {
- return EFI_NOT_FOUND;
- }
-
- if ((End >= mMemoryTypeStatistics[CheckType].BaseAddress) &&
- (End <= mMemoryTypeStatistics[CheckType].MaximumAddress))
- {
- return EFI_NOT_FOUND;
- }
-
- if ((Start < mMemoryTypeStatistics[CheckType].BaseAddress) &&
- (End > mMemoryTypeStatistics[CheckType].MaximumAddress))
- {
- return EFI_NOT_FOUND;
- }
- }
- }
- }
-
- if (Type == AllocateMaxAddress) {
- MaxAddress = Start;
- }
-
- CoreAcquireMemoryLock ();
-
- //
- // If not a specific address, then find an address to allocate
- //
- if (Type != AllocateAddress) {
- Start = FindFreePages (
- MaxAddress,
- NumberOfPages,
- MemoryType,
- Alignment,
- NeedGuard
- );
- if (Start == 0) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Done;
- }
- }
-
- //
- // Convert pages from FreeMemory to the requested type
- //
- if (NeedGuard) {
- Status = CoreConvertPagesWithGuard (Start, NumberOfPages, MemoryType);
- } else {
- Status = CoreConvertPages (Start, NumberOfPages, MemoryType);
- }
-
- if (EFI_ERROR (Status)) {
- //
- // If requested memory region is unavailable it may be untested memory
- // Attempt to promote memory resources, then re-attempt the allocation
- //
- if (PromoteMemoryResource ()) {
- if (NeedGuard) {
- Status = CoreConvertPagesWithGuard (Start, NumberOfPages, MemoryType);
- } else {
- Status = CoreConvertPages (Start, NumberOfPages, MemoryType);
- }
- }
- }
-
-Done:
- CoreReleaseMemoryLock ();
-
- if (!EFI_ERROR (Status)) {
- if (NeedGuard) {
- SetGuardForMemory (Start, NumberOfPages);
- }
-
- *Memory = Start;
- }
-
- return Status;
-}
-
-/**
- Allocates pages from the memory map.
-
- @param Type The type of allocation to perform
- @param MemoryType The type of memory to turn the allocated pages
- into
- @param NumberOfPages The number of pages to allocate
- @param Memory A pointer to receive the base allocated memory
- address
-
- @return Status. On success, Memory is filled in with the base address allocated
- @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in
- spec.
- @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
- @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
- @retval EFI_SUCCESS Pages successfully allocated.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreAllocatePages (
- IN EFI_ALLOCATE_TYPE Type,
- IN EFI_MEMORY_TYPE MemoryType,
- IN UINTN NumberOfPages,
- OUT EFI_PHYSICAL_ADDRESS *Memory
- )
-{
- EFI_STATUS Status;
- BOOLEAN NeedGuard;
-
- NeedGuard = IsPageTypeToGuard (MemoryType, Type) && !mOnGuarding;
- Status = CoreInternalAllocatePages (
- Type,
- MemoryType,
- NumberOfPages,
- Memory,
- NeedGuard
- );
- if (!EFI_ERROR (Status)) {
- CoreUpdateProfile (
- (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
- MemoryProfileActionAllocatePages,
- MemoryType,
- EFI_PAGES_TO_SIZE (NumberOfPages),
- (VOID *)(UINTN)*Memory,
- NULL
- );
- InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);
- ApplyMemoryProtectionPolicy (
- EfiConventionalMemory,
- MemoryType,
- *Memory,
- EFI_PAGES_TO_SIZE (NumberOfPages)
- );
- }
-
- return Status;
-}
-
-/**
- Frees previous allocated pages.
-
- @param Memory Base address of memory being freed
- @param NumberOfPages The number of pages to free
- @param MemoryType Pointer to memory type
-
- @retval EFI_NOT_FOUND Could not find the entry that covers the range
- @retval EFI_INVALID_PARAMETER Address not aligned
- @return EFI_SUCCESS -Pages successfully freed.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreInternalFreePages (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages,
- OUT EFI_MEMORY_TYPE *MemoryType OPTIONAL
- )
-{
- EFI_STATUS Status;
- LIST_ENTRY *Link;
- MEMORY_MAP *Entry;
- UINTN Alignment;
- BOOLEAN IsGuarded;
-
- //
- // Free the range
- //
- CoreAcquireMemoryLock ();
-
- //
- // Find the entry that the covers the range
- //
- IsGuarded = FALSE;
- Entry = NULL;
- for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
- Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
- if ((Entry->Start <= Memory) && (Entry->End > Memory)) {
- break;
- }
- }
-
- if (Link == &gMemoryMap) {
- Status = EFI_NOT_FOUND;
- goto Done;
- }
-
- if (Entry == NULL) {
- ASSERT (Entry != NULL);
- Status = EFI_NOT_FOUND;
- goto Done;
- }
-
- Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
-
- if ((Entry->Type == EfiReservedMemoryType) ||
- (Entry->Type == EfiACPIMemoryNVS) ||
- (Entry->Type == EfiRuntimeServicesCode) ||
- (Entry->Type == EfiRuntimeServicesData))
- {
- Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
- }
-
- if ((Memory & (Alignment - 1)) != 0) {
- Status = EFI_INVALID_PARAMETER;
- goto Done;
- }
-
- NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;
- NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);
-
- if (MemoryType != NULL) {
- *MemoryType = Entry->Type;
- }
-
- IsGuarded = IsPageTypeToGuard (Entry->Type, AllocateAnyPages) &&
- IsMemoryGuarded (Memory);
- if (IsGuarded) {
- Status = CoreConvertPagesWithGuard (
- Memory,
- NumberOfPages,
- EfiConventionalMemory
- );
- } else {
- Status = CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);
- }
-
-Done:
- CoreReleaseMemoryLock ();
- return Status;
-}
-
-/**
- Frees previous allocated pages.
-
- @param Memory Base address of memory being freed
- @param NumberOfPages The number of pages to free
-
- @retval EFI_NOT_FOUND Could not find the entry that covers the range
- @retval EFI_INVALID_PARAMETER Address not aligned
- @return EFI_SUCCESS -Pages successfully freed.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreFreePages (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages
- )
-{
- EFI_STATUS Status;
- EFI_MEMORY_TYPE MemoryType;
-
- Status = CoreInternalFreePages (Memory, NumberOfPages, &MemoryType);
- if (!EFI_ERROR (Status)) {
- GuardFreedPagesChecked (Memory, NumberOfPages);
- CoreUpdateProfile (
- (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
- MemoryProfileActionFreePages,
- MemoryType,
- EFI_PAGES_TO_SIZE (NumberOfPages),
- (VOID *)(UINTN)Memory,
- NULL
- );
- InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);
- ApplyMemoryProtectionPolicy (
- MemoryType,
- EfiConventionalMemory,
- Memory,
- EFI_PAGES_TO_SIZE (NumberOfPages)
- );
- }
-
- return Status;
-}
-
-/**
- This function checks to see if the last memory map descriptor in a memory map
- can be merged with any of the other memory map descriptors in a memorymap.
- Memory descriptors may be merged if they are adjacent and have the same type
- and attributes.
-
- @param MemoryMap A pointer to the start of the memory map.
- @param MemoryMapDescriptor A pointer to the last descriptor in MemoryMap.
- @param DescriptorSize The size, in bytes, of an individual
- EFI_MEMORY_DESCRIPTOR.
-
- @return A pointer to the next available descriptor in MemoryMap
-
-**/
-EFI_MEMORY_DESCRIPTOR *
-MergeMemoryMapDescriptor (
- IN EFI_MEMORY_DESCRIPTOR *MemoryMap,
- IN EFI_MEMORY_DESCRIPTOR *MemoryMapDescriptor,
- IN UINTN DescriptorSize
- )
-{
- //
- // Traverse the array of descriptors in MemoryMap
- //
- for ( ; MemoryMap != MemoryMapDescriptor; MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize)) {
- //
- // Check to see if the Type fields are identical.
- //
- if (MemoryMap->Type != MemoryMapDescriptor->Type) {
- continue;
- }
-
- //
- // Check to see if the Attribute fields are identical.
- //
- if (MemoryMap->Attribute != MemoryMapDescriptor->Attribute) {
- continue;
- }
-
- //
- // Check to see if MemoryMapDescriptor is immediately above MemoryMap
- //
- if (MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) {
- //
- // Merge MemoryMapDescriptor into MemoryMap
- //
- MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages;
-
- //
- // Return MemoryMapDescriptor as the next available slot int he MemoryMap array
- //
- return MemoryMapDescriptor;
- }
-
- //
- // Check to see if MemoryMapDescriptor is immediately below MemoryMap
- //
- if (MemoryMap->PhysicalStart - EFI_PAGES_TO_SIZE ((UINTN)MemoryMapDescriptor->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) {
- //
- // Merge MemoryMapDescriptor into MemoryMap
- //
- MemoryMap->PhysicalStart = MemoryMapDescriptor->PhysicalStart;
- MemoryMap->VirtualStart = MemoryMapDescriptor->VirtualStart;
- MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages;
-
- //
- // Return MemoryMapDescriptor as the next available slot int he MemoryMap array
- //
- return MemoryMapDescriptor;
- }
- }
-
- //
- // MemoryMapDescrtiptor could not be merged with any descriptors in MemoryMap.
- //
- // Return the slot immediately after MemoryMapDescriptor as the next available
- // slot in the MemoryMap array
- //
- return NEXT_MEMORY_DESCRIPTOR (MemoryMapDescriptor, DescriptorSize);
-}
-
-/**
- This function returns a copy of the current memory map. The map is an array of
- memory descriptors, each of which describes a contiguous block of memory.
-
- @param MemoryMapSize A pointer to the size, in bytes, of the
- MemoryMap buffer. On input, this is the size of
- the buffer allocated by the caller. On output,
- it is the size of the buffer returned by the
- firmware if the buffer was large enough, or the
- size of the buffer needed to contain the map if
- the buffer was too small.
- @param MemoryMap A pointer to the buffer in which firmware places
- the current memory map.
- @param MapKey A pointer to the location in which firmware
- returns the key for the current memory map.
- @param DescriptorSize A pointer to the location in which firmware
- returns the size, in bytes, of an individual
- EFI_MEMORY_DESCRIPTOR.
- @param DescriptorVersion A pointer to the location in which firmware
- returns the version number associated with the
- EFI_MEMORY_DESCRIPTOR.
-
- @retval EFI_SUCCESS The memory map was returned in the MemoryMap
- buffer.
- @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current
- buffer size needed to hold the memory map is
- returned in MemoryMapSize.
- @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreGetMemoryMap (
- IN OUT UINTN *MemoryMapSize,
- IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
- OUT UINTN *MapKey,
- OUT UINTN *DescriptorSize,
- OUT UINT32 *DescriptorVersion
- )
-{
- EFI_STATUS Status;
- UINTN Size;
- UINTN BufferSize;
- UINTN NumberOfEntries;
- LIST_ENTRY *Link;
- MEMORY_MAP *Entry;
- EFI_GCD_MAP_ENTRY *GcdMapEntry;
- EFI_GCD_MAP_ENTRY MergeGcdMapEntry;
- EFI_MEMORY_TYPE Type;
- EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
- EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
-
- //
- // Make sure the parameters are valid
- //
- if (MemoryMapSize == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- CoreAcquireGcdMemoryLock ();
-
- //
- // Count the number of Reserved and runtime MMIO entries
- // And, count the number of Persistent entries.
- //
- NumberOfEntries = 0;
- for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) {
- GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
- if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypePersistent) ||
- (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) ||
- ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&
- ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME)))
- {
- NumberOfEntries++;
- }
- }
-
- Size = sizeof (EFI_MEMORY_DESCRIPTOR);
-
- //
- // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will
- // prevent people from having pointer math bugs in their code.
- // now you have to use *DescriptorSize to make things work.
- //
- Size += sizeof (UINT64) - (Size % sizeof (UINT64));
-
- if (DescriptorSize != NULL) {
- *DescriptorSize = Size;
- }
-
- if (DescriptorVersion != NULL) {
- *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;
- }
-
- CoreAcquireMemoryLock ();
-
- //
- // Compute the buffer size needed to fit the entire map
- //
- BufferSize = Size * NumberOfEntries;
- for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
- BufferSize += Size;
- }
-
- if (*MemoryMapSize < BufferSize) {
- Status = EFI_BUFFER_TOO_SMALL;
- goto Done;
- }
-
- if (MemoryMap == NULL) {
- Status = EFI_INVALID_PARAMETER;
- goto Done;
- }
-
- //
- // Build the map
- //
- ZeroMem (MemoryMap, BufferSize);
- MemoryMapStart = MemoryMap;
- for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
- Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
- ASSERT (Entry->VirtualStart == 0);
-
- //
- // Convert internal map into an EFI_MEMORY_DESCRIPTOR
- //
- MemoryMap->Type = Entry->Type;
- MemoryMap->PhysicalStart = Entry->Start;
- MemoryMap->VirtualStart = Entry->VirtualStart;
- MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);
- //
- // If the memory type is EfiConventionalMemory, then determine if the range is part of a
- // memory type bin and needs to be converted to the same memory type as the rest of the
- // memory type bin in order to minimize EFI Memory Map changes across reboots. This
- // improves the chances for a successful S4 resume in the presence of minor page allocation
- // differences across reboots.
- //
- if (MemoryMap->Type == EfiConventionalMemory) {
- for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) {
- if (mMemoryTypeStatistics[Type].Special &&
- (mMemoryTypeStatistics[Type].NumberOfPages > 0) &&
- (Entry->Start >= mMemoryTypeStatistics[Type].BaseAddress) &&
- (Entry->End <= mMemoryTypeStatistics[Type].MaximumAddress))
- {
- MemoryMap->Type = Type;
- }
- }
- }
-
- MemoryMap->Attribute = Entry->Attribute;
- if (MemoryMap->Type < EfiMaxMemoryType) {
- if (mMemoryTypeStatistics[MemoryMap->Type].Runtime) {
- MemoryMap->Attribute |= EFI_MEMORY_RUNTIME;
- }
- }
-
- //
- // Check to see if the new Memory Map Descriptor can be merged with an
- // existing descriptor if they are adjacent and have the same attributes
- //
- MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);
- }
-
- ZeroMem (&MergeGcdMapEntry, sizeof (MergeGcdMapEntry));
- GcdMapEntry = NULL;
- for (Link = mGcdMemorySpaceMap.ForwardLink; ; Link = Link->ForwardLink) {
- if (Link != &mGcdMemorySpaceMap) {
- //
- // Merge adjacent same type and attribute GCD memory range
- //
- GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
-
- if ((MergeGcdMapEntry.Capabilities == GcdMapEntry->Capabilities) &&
- (MergeGcdMapEntry.Attributes == GcdMapEntry->Attributes) &&
- (MergeGcdMapEntry.GcdMemoryType == GcdMapEntry->GcdMemoryType) &&
- (MergeGcdMapEntry.GcdIoType == GcdMapEntry->GcdIoType))
- {
- MergeGcdMapEntry.EndAddress = GcdMapEntry->EndAddress;
- continue;
- }
- }
-
- if ((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) ||
- ((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&
- ((MergeGcdMapEntry.Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME)))
- {
- //
- // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR,
- // it will be recorded as page PhysicalStart and NumberOfPages.
- //
- ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0);
- ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0);
-
- //
- // Create EFI_MEMORY_DESCRIPTOR for every Reserved and runtime MMIO GCD entries
- //
- MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress;
- MemoryMap->VirtualStart = 0;
- MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT);
- MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) |
- (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK));
-
- if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) {
- MemoryMap->Type = EfiReservedMemoryType;
- } else if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
- if ((MergeGcdMapEntry.Attributes & EFI_MEMORY_PORT_IO) == EFI_MEMORY_PORT_IO) {
- MemoryMap->Type = EfiMemoryMappedIOPortSpace;
- } else {
- MemoryMap->Type = EfiMemoryMappedIO;
- }
- }
-
- //
- // Check to see if the new Memory Map Descriptor can be merged with an
- // existing descriptor if they are adjacent and have the same attributes
- //
- MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);
- }
-
- if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypePersistent) {
- //
- // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR,
- // it will be recorded as page PhysicalStart and NumberOfPages.
- //
- ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0);
- ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0);
-
- //
- // Create EFI_MEMORY_DESCRIPTOR for every Persistent GCD entries
- //
- MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress;
- MemoryMap->VirtualStart = 0;
- MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT);
- MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV |
- (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK));
- MemoryMap->Type = EfiPersistentMemory;
-
- //
- // Check to see if the new Memory Map Descriptor can be merged with an
- // existing descriptor if they are adjacent and have the same attributes
- //
- MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);
- }
-
- if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeUnaccepted) {
- //
- // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR,
- // it will be recorded as page PhysicalStart and NumberOfPages.
- //
- ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0);
- ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0);
-
- //
- // Create EFI_MEMORY_DESCRIPTOR for every Unaccepted GCD entries
- //
- MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress;
- MemoryMap->VirtualStart = 0;
- MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT);
- MemoryMap->Attribute = MergeGcdMapEntry.Attributes |
- (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO |
- EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB));
- MemoryMap->Type = EfiUnacceptedMemoryType;
-
- //
- // Check to see if the new Memory Map Descriptor can be merged with an
- // existing descriptor if they are adjacent and have the same attributes
- //
- MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);
- }
-
- if (Link == &mGcdMemorySpaceMap) {
- //
- // break loop when arrive at head.
- //
- break;
- }
-
- if (GcdMapEntry != NULL) {
- //
- // Copy new GCD map entry for the following GCD range merge
- //
- CopyMem (&MergeGcdMapEntry, GcdMapEntry, sizeof (MergeGcdMapEntry));
- }
- }
-
- //
- // Compute the size of the buffer actually used after all memory map descriptor merge operations
- //
- BufferSize = ((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart);
-
- //
- // Note: Some OSs will treat EFI_MEMORY_DESCRIPTOR.Attribute as really
- // set attributes and change memory paging attribute accordingly.
- // But current EFI_MEMORY_DESCRIPTOR.Attribute is assigned by
- // value from Capabilities in GCD memory map. This might cause
- // boot problems. Clearing all page-access permission related
- // capabilities can workaround it. Following code is supposed to
- // be removed once the usage of EFI_MEMORY_DESCRIPTOR.Attribute
- // is clarified in UEFI spec and adopted by both EDK-II Core and
- // all supported OSs.
- //
- MemoryMapEnd = MemoryMap;
- MemoryMap = MemoryMapStart;
- while (MemoryMap < MemoryMapEnd) {
- MemoryMap->Attribute &= ~(UINT64)EFI_MEMORY_ACCESS_MASK;
- MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
- }
-
- MergeMemoryMap (MemoryMapStart, &BufferSize, Size);
- MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMapStart + BufferSize);
-
- Status = EFI_SUCCESS;
-
-Done:
- //
- // Update the map key finally
- //
- if (MapKey != NULL) {
- *MapKey = mMemoryMapKey;
- }
-
- CoreReleaseMemoryLock ();
-
- CoreReleaseGcdMemoryLock ();
-
- *MemoryMapSize = BufferSize;
-
- DEBUG_CODE (
- DumpGuardedMemoryBitmap ();
- );
-
- return Status;
-}
-
-/**
- Internal function. Used by the pool functions to allocate pages
- to back pool allocation requests.
-
- @param PoolType The type of memory for the new pool pages
- @param NumberOfPages No of pages to allocate
- @param Alignment Bits to align.
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return The allocated memory, or NULL
-
-**/
-VOID *
-CoreAllocatePoolPages (
- IN EFI_MEMORY_TYPE PoolType,
- IN UINTN NumberOfPages,
- IN UINTN Alignment,
- IN BOOLEAN NeedGuard
- )
-{
- UINT64 Start;
-
- //
- // Find the pages to convert
- //
- Start = FindFreePages (
- MAX_ALLOC_ADDRESS,
- NumberOfPages,
- PoolType,
- Alignment,
- NeedGuard
- );
-
- //
- // Convert it to boot services data
- //
- if (Start == 0) {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", (UINT32)NumberOfPages));
- } else {
- if (NeedGuard) {
- CoreConvertPagesWithGuard (Start, NumberOfPages, PoolType);
- } else {
- CoreConvertPages (Start, NumberOfPages, PoolType);
- }
- }
-
- return (VOID *)(UINTN)Start;
-}
-
-/**
- Internal function. Frees pool pages allocated via AllocatePoolPages ()
-
- @param Memory The base address to free
- @param NumberOfPages The number of pages to free
-
-**/
-VOID
-CoreFreePoolPages (
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NumberOfPages
- )
-{
- CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);
-}
-
-/**
- Make sure the memory map is following all the construction rules,
- it is the last time to check memory map error before exit boot services.
-
- @param MapKey Memory map key
-
- @retval EFI_INVALID_PARAMETER Memory map not consistent with construction
- rules.
- @retval EFI_SUCCESS Valid memory map.
-
-**/
-EFI_STATUS
-CoreTerminateMemoryMap (
- IN UINTN MapKey
- )
-{
- EFI_STATUS Status;
- LIST_ENTRY *Link;
- MEMORY_MAP *Entry;
-
- Status = EFI_SUCCESS;
-
- CoreAcquireMemoryLock ();
-
- if (MapKey == mMemoryMapKey) {
- //
- // Make sure the memory map is following all the construction rules
- // This is the last chance we will be able to display any messages on
- // the console devices.
- //
-
- for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
- Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
- if (Entry->Type < EfiMaxMemoryType) {
- if (mMemoryTypeStatistics[Entry->Type].Runtime) {
- ASSERT (Entry->Type != EfiACPIReclaimMemory);
- ASSERT (Entry->Type != EfiACPIMemoryNVS);
- if ((Entry->Start & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));
- Status = EFI_INVALID_PARAMETER;
- goto Done;
- }
-
- if (((Entry->End + 1) & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) {
- DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));
- Status = EFI_INVALID_PARAMETER;
- goto Done;
- }
- }
- }
- }
-
- //
- // The map key they gave us matches what we expect. Fall through and
- // return success. In an ideal world we would clear out all of
- // EfiBootServicesCode and EfiBootServicesData. However this function
- // is not the last one called by ExitBootServices(), so we have to
- // preserve the memory contents.
- //
- } else {
- Status = EFI_INVALID_PARAMETER;
- }
-
-Done:
- CoreReleaseMemoryLock ();
-
- return Status;
-}
+/** @file + UEFI Memory page management functions. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DxeMain.h" +#include "Imem.h" +#include "HeapGuard.h" +#include <Pi/PiDxeCis.h> + +// +// Entry for tracking the memory regions for each memory type to coalesce similar memory types +// +typedef struct { + EFI_PHYSICAL_ADDRESS BaseAddress; + EFI_PHYSICAL_ADDRESS MaximumAddress; + UINT64 CurrentNumberOfPages; + UINT64 NumberOfPages; + UINTN InformationIndex; + BOOLEAN Special; + BOOLEAN Runtime; +} EFI_MEMORY_TYPE_STATISTICS; + +// +// MemoryMap - The current memory map +// +UINTN mMemoryMapKey = 0; + +#define MAX_MAP_DEPTH 6 + +/// +/// mMapDepth - depth of new descriptor stack +/// +UINTN mMapDepth = 0; +/// +/// mMapStack - space to use as temp storage to build new map descriptors +/// +MEMORY_MAP mMapStack[MAX_MAP_DEPTH]; +UINTN mFreeMapStack = 0; +/// +/// This list maintain the free memory map list +/// +LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList); +BOOLEAN mMemoryTypeInformationInitialized = FALSE; + +EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = { + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiReservedMemoryType + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiConventionalMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiUnusableMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIReclaimMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIMemoryNVS + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIO + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiPalCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiPersistentMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiUnacceptedMemoryType + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE } // EfiMaxMemoryType +}; + +EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = MAX_ALLOC_ADDRESS; +EFI_PHYSICAL_ADDRESS mDefaultBaseAddress = MAX_ALLOC_ADDRESS; + +EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = { + { EfiReservedMemoryType, 0 }, + { EfiLoaderCode, 0 }, + { EfiLoaderData, 0 }, + { EfiBootServicesCode, 0 }, + { EfiBootServicesData, 0 }, + { EfiRuntimeServicesCode, 0 }, + { EfiRuntimeServicesData, 0 }, + { EfiConventionalMemory, 0 }, + { EfiUnusableMemory, 0 }, + { EfiACPIReclaimMemory, 0 }, + { EfiACPIMemoryNVS, 0 }, + { EfiMemoryMappedIO, 0 }, + { EfiMemoryMappedIOPortSpace, 0 }, + { EfiPalCode, 0 }, + { EfiPersistentMemory, 0 }, + { EfiGcdMemoryTypeUnaccepted, 0 }, + { EfiMaxMemoryType, 0 } +}; +// +// Only used when load module at fixed address feature is enabled. True means the memory is alreay successfully allocated +// and ready to load the module in to specified address.or else, the memory is not ready and module will be loaded at a +// address assigned by DXE core. +// +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN gLoadFixedAddressCodeMemoryReady = FALSE; + +/** + Enter critical section by gaining lock on gMemoryLock. + +**/ +VOID +CoreAcquireMemoryLock ( + VOID + ) +{ + CoreAcquireLock (&gMemoryLock); +} + +/** + Exit critical section by releasing lock on gMemoryLock. + +**/ +VOID +CoreReleaseMemoryLock ( + VOID + ) +{ + CoreReleaseLock (&gMemoryLock); +} + +/** + Internal function. Removes a descriptor entry. + + @param Entry The entry to remove + +**/ +VOID +RemoveMemoryMapEntry ( + IN OUT MEMORY_MAP *Entry + ) +{ + RemoveEntryList (&Entry->Link); + Entry->Link.ForwardLink = NULL; + + if (Entry->FromPages) { + // + // Insert the free memory map descriptor to the end of mFreeMemoryMapEntryList + // + InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link); + } +} + +/** + Internal function. Adds a ranges to the memory map. + The range must not already exist in the map. + + @param Type The type of memory range to add + @param Start The starting address in the memory range Must be + paged aligned + @param End The last address in the range Must be the last + byte of a page + @param Attribute The attributes of the memory range to add + +**/ +VOID +CoreAddRange ( + IN EFI_MEMORY_TYPE Type, + IN EFI_PHYSICAL_ADDRESS Start, + IN EFI_PHYSICAL_ADDRESS End, + IN UINT64 Attribute + ) +{ + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + + ASSERT ((Start & EFI_PAGE_MASK) == 0); + ASSERT (End > Start); + + ASSERT_LOCKED (&gMemoryLock); + + DEBUG ((DEBUG_PAGE, "AddRange: %lx-%lx to %d\n", Start, End, Type)); + + // + // If memory of type EfiConventionalMemory is being added that includes the page + // starting at address 0, then zero the page starting at address 0. This has + // two benifits. It helps find NULL pointer bugs and it also maximizes + // compatibility with operating systems that may evaluate memory in this page + // for legacy data structures. If memory of any other type is added starting + // at address 0, then do not zero the page at address 0 because the page is being + // used for other purposes. + // + if ((Type == EfiConventionalMemory) && (Start == 0) && (End >= EFI_PAGE_SIZE - 1)) { + if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) == 0) { + SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0); + } + } + + // + // Memory map being altered so updated key + // + mMemoryMapKey += 1; + + // + // UEFI 2.0 added an event group for notificaiton on memory map changes. + // So we need to signal this Event Group every time the memory map changes. + // If we are in EFI 1.10 compatability mode no event groups will be + // found and nothing will happen we we call this function. These events + // will get signaled but since a lock is held around the call to this + // function the notificaiton events will only be called after this function + // returns and the lock is released. + // + CoreNotifySignalList (&gEfiEventMemoryMapChangeGuid); + + // + // Look for adjoining memory descriptor + // + + // Two memory descriptors can only be merged if they have the same Type + // and the same Attribute + // + + Link = gMemoryMap.ForwardLink; + while (Link != &gMemoryMap) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + Link = Link->ForwardLink; + + if (Entry->Type != Type) { + continue; + } + + if (Entry->Attribute != Attribute) { + continue; + } + + if (Entry->End + 1 == Start) { + Start = Entry->Start; + RemoveMemoryMapEntry (Entry); + } else if (Entry->Start == End + 1) { + End = Entry->End; + RemoveMemoryMapEntry (Entry); + } + } + + // + // Add descriptor + // + + mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE; + mMapStack[mMapDepth].FromPages = FALSE; + mMapStack[mMapDepth].Type = Type; + mMapStack[mMapDepth].Start = Start; + mMapStack[mMapDepth].End = End; + mMapStack[mMapDepth].VirtualStart = 0; + mMapStack[mMapDepth].Attribute = Attribute; + InsertTailList (&gMemoryMap, &mMapStack[mMapDepth].Link); + + mMapDepth += 1; + ASSERT (mMapDepth < MAX_MAP_DEPTH); + + return; +} + +/** + Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList. + If the list is emtry, then allocate a new page to refuel the list. + Please Note this algorithm to allocate the memory map descriptor has a property + that the memory allocated for memory entries always grows, and will never really be freed + For example, if the current boot uses 2000 memory map entries at the maximum point, but + ends up with only 50 at the time the OS is booted, then the memory associated with the 1950 + memory map entries is still allocated from EfiBootServicesMemory. + + + @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList + +**/ +MEMORY_MAP * +AllocateMemoryMapEntry ( + VOID + ) +{ + MEMORY_MAP *FreeDescriptorEntries; + MEMORY_MAP *Entry; + UINTN Index; + + if (IsListEmpty (&mFreeMemoryMapEntryList)) { + // + // The list is empty, to allocate one page to refuel the list + // + FreeDescriptorEntries = CoreAllocatePoolPages ( + EfiBootServicesData, + EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION_GRANULARITY), + DEFAULT_PAGE_ALLOCATION_GRANULARITY, + FALSE + ); + if (FreeDescriptorEntries != NULL) { + // + // Enque the free memmory map entries into the list + // + for (Index = 0; Index < DEFAULT_PAGE_ALLOCATION_GRANULARITY / sizeof (MEMORY_MAP); Index++) { + FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE; + InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link); + } + } else { + return NULL; + } + } + + // + // dequeue the first descriptor from the list + // + Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + RemoveEntryList (&Entry->Link); + + return Entry; +} + +/** + Internal function. Moves any memory descriptors that are on the + temporary descriptor stack to heap. + +**/ +VOID +CoreFreeMemoryMapStack ( + VOID + ) +{ + MEMORY_MAP *Entry; + MEMORY_MAP *Entry2; + LIST_ENTRY *Link2; + + ASSERT_LOCKED (&gMemoryLock); + + // + // If already freeing the map stack, then return + // + if (mFreeMapStack != 0) { + return; + } + + // + // Move the temporary memory descriptor stack into pool + // + mFreeMapStack += 1; + + while (mMapDepth != 0) { + // + // Deque an memory map entry from mFreeMemoryMapEntryList + // + Entry = AllocateMemoryMapEntry (); + + ASSERT (Entry); + + // + // Update to proper entry + // + mMapDepth -= 1; + + if (mMapStack[mMapDepth].Link.ForwardLink != NULL) { + // + // Move this entry to general memory + // + RemoveEntryList (&mMapStack[mMapDepth].Link); + mMapStack[mMapDepth].Link.ForwardLink = NULL; + + CopyMem (Entry, &mMapStack[mMapDepth], sizeof (MEMORY_MAP)); + Entry->FromPages = TRUE; + + // + // Find insertion location + // + for (Link2 = gMemoryMap.ForwardLink; Link2 != &gMemoryMap; Link2 = Link2->ForwardLink) { + Entry2 = CR (Link2, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + if (Entry2->FromPages && (Entry2->Start > Entry->Start)) { + break; + } + } + + InsertTailList (Link2, &Entry->Link); + } else { + // + // This item of mMapStack[mMapDepth] has already been dequeued from gMemoryMap list, + // so here no need to move it to memory. + // + InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link); + } + } + + mFreeMapStack -= 1; +} + +/** + Find untested but initialized memory regions in GCD map and convert them to be DXE allocatable. + +**/ +BOOLEAN +PromoteMemoryResource ( + VOID + ) +{ + LIST_ENTRY *Link; + EFI_GCD_MAP_ENTRY *Entry; + BOOLEAN Promoted; + EFI_PHYSICAL_ADDRESS StartAddress; + EFI_PHYSICAL_ADDRESS EndAddress; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; + + DEBUG ((DEBUG_PAGE, "Promote the memory resource\n")); + + CoreAcquireGcdMemoryLock (); + + Promoted = FALSE; + Link = mGcdMemorySpaceMap.ForwardLink; + while (Link != &mGcdMemorySpaceMap) { + Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE); + + if ((Entry->GcdMemoryType == EfiGcdMemoryTypeReserved) && + (Entry->EndAddress < MAX_ALLOC_ADDRESS) && + ((Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == + (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))) + { + // + // Update the GCD map + // + if ((Entry->Capabilities & EFI_MEMORY_MORE_RELIABLE) == EFI_MEMORY_MORE_RELIABLE) { + Entry->GcdMemoryType = EfiGcdMemoryTypeMoreReliable; + } else { + Entry->GcdMemoryType = EfiGcdMemoryTypeSystemMemory; + } + + Entry->Capabilities |= EFI_MEMORY_TESTED; + Entry->ImageHandle = gDxeCoreImageHandle; + Entry->DeviceHandle = NULL; + + // + // Add to allocable system memory resource + // + + CoreAddRange ( + EfiConventionalMemory, + Entry->BaseAddress, + Entry->EndAddress, + Entry->Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME) + ); + CoreFreeMemoryMapStack (); + + Promoted = TRUE; + } + + Link = Link->ForwardLink; + } + + CoreReleaseGcdMemoryLock (); + + if (!Promoted) { + // + // If freed-memory guard is enabled, we could promote pages from + // guarded free pages. + // + Promoted = PromoteGuardedFreePages (&StartAddress, &EndAddress); + if (Promoted) { + if (!EFI_ERROR (CoreGetMemorySpaceDescriptor (StartAddress, &Descriptor))) { + CoreAddRange ( + EfiConventionalMemory, + StartAddress, + EndAddress, + Descriptor.Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | + EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME) + ); + } + } + } + + return Promoted; +} + +/** + This function try to allocate Runtime code & Boot time code memory range. If LMFA enabled, 2 patchable PCD + PcdLoadFixAddressRuntimeCodePageNumber & PcdLoadFixAddressBootTimeCodePageNumber which are set by tools will record the + size of boot time and runtime code. + +**/ +VOID +CoreLoadingFixedAddressHook ( + VOID + ) +{ + UINT32 RuntimeCodePageNumber; + UINT32 BootTimeCodePageNumber; + EFI_PHYSICAL_ADDRESS RuntimeCodeBase; + EFI_PHYSICAL_ADDRESS BootTimeCodeBase; + EFI_STATUS Status; + + // + // Make sure these 2 areas are not initialzied. + // + if (!gLoadFixedAddressCodeMemoryReady) { + RuntimeCodePageNumber = PcdGet32 (PcdLoadFixAddressRuntimeCodePageNumber); + BootTimeCodePageNumber = PcdGet32 (PcdLoadFixAddressBootTimeCodePageNumber); + RuntimeCodeBase = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressConfigurationTable.DxeCodeTopAddress - EFI_PAGES_TO_SIZE (RuntimeCodePageNumber)); + BootTimeCodeBase = (EFI_PHYSICAL_ADDRESS)(RuntimeCodeBase - EFI_PAGES_TO_SIZE (BootTimeCodePageNumber)); + // + // Try to allocate runtime memory. + // + Status = CoreAllocatePages ( + AllocateAddress, + EfiRuntimeServicesCode, + RuntimeCodePageNumber, + &RuntimeCodeBase + ); + if (EFI_ERROR (Status)) { + // + // Runtime memory allocation failed + // + return; + } + + // + // Try to allocate boot memory. + // + Status = CoreAllocatePages ( + AllocateAddress, + EfiBootServicesCode, + BootTimeCodePageNumber, + &BootTimeCodeBase + ); + if (EFI_ERROR (Status)) { + // + // boot memory allocation failed. Free Runtime code range and will try the allocation again when + // new memory range is installed. + // + CoreFreePages ( + RuntimeCodeBase, + RuntimeCodePageNumber + ); + return; + } + + gLoadFixedAddressCodeMemoryReady = TRUE; + } + + return; +} + +/** + Sets the preferred memory range to use for the Memory Type Information bins. + This service must be called before fist call to CoreAddMemoryDescriptor(). + + If the location of the Memory Type Information bins has already been + established or the size of the range provides is smaller than all the + Memory Type Information bins, then the range provides is not used. + + @param Start The start address of the Memory Type Information range. + @param Length The size, in bytes, of the Memory Type Information range. +**/ +VOID +CoreSetMemoryTypeInformationRange ( + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length + ) +{ + EFI_PHYSICAL_ADDRESS Top; + EFI_MEMORY_TYPE Type; + UINTN Index; + UINTN Size; + + // + // Return if Memory Type Information bin locations have already been set + // + if (mMemoryTypeInformationInitialized) { + DEBUG ((DEBUG_ERROR, "%a: Ignored. Bins already set.\n", __func__)); + return; + } + + // + // Return if size of the Memory Type Information bins is greater than Length + // + Size = 0; + for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + Size += EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); + } + + if (Size > Length) { + return; + } + + // + // Loop through each memory type in the order specified by the + // gMemoryTypeInformation[] array + // + Top = Start + Length; + for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (gMemoryTypeInformation[Index].NumberOfPages != 0) { + mMemoryTypeStatistics[Type].MaximumAddress = Top - 1; + Top -= EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); + mMemoryTypeStatistics[Type].BaseAddress = Top; + + // + // If the current base address is the lowest address so far, then update + // the default maximum address + // + if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { + mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; + } + + mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; + gMemoryTypeInformation[Index].NumberOfPages = 0; + } + } + + // + // If the number of pages reserved for a memory type is 0, then all + // allocations for that type should be in the default range. + // + for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { + mMemoryTypeStatistics[Type].InformationIndex = Index; + } + } + + mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; + if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { + mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; + } + } + + mMemoryTypeInformationInitialized = TRUE; +} + +/** + Called to initialize the memory map and add descriptors to + the current descriptor list. + The first descriptor that is added must be general usable + memory as the addition allocates heap. + + @param Type The type of memory to add + @param Start The starting address in the memory range Must be + page aligned + @param NumberOfPages The number of pages in the range + @param Attribute Attributes of the memory to add + + @return None. The range is added to the memory map + +**/ +VOID +CoreAddMemoryDescriptor ( + IN EFI_MEMORY_TYPE Type, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 NumberOfPages, + IN UINT64 Attribute + ) +{ + EFI_PHYSICAL_ADDRESS End; + EFI_STATUS Status; + UINTN Index; + UINTN FreeIndex; + + if ((Start & EFI_PAGE_MASK) != 0) { + return; + } + + if ((Type >= EfiMaxMemoryType) && (Type < MEMORY_TYPE_OEM_RESERVED_MIN)) { + return; + } + + CoreAcquireMemoryLock (); + End = Start + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) - 1; + CoreAddRange (Type, Start, End, Attribute); + CoreFreeMemoryMapStack (); + CoreReleaseMemoryLock (); + + ApplyMemoryProtectionPolicy ( + EfiMaxMemoryType, + Type, + Start, + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) + ); + + // + // If Loading Module At Fixed Address feature is enabled. try to allocate memory with Runtime code & Boot time code type + // + if (PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) { + CoreLoadingFixedAddressHook (); + } + + // + // Check to see if the statistics for the different memory types have already been established + // + if (mMemoryTypeInformationInitialized) { + return; + } + + // + // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array + // + for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (gMemoryTypeInformation[Index].NumberOfPages != 0) { + // + // Allocate pages for the current memory type from the top of available memory + // + Status = CoreAllocatePages ( + AllocateAnyPages, + Type, + gMemoryTypeInformation[Index].NumberOfPages, + &mMemoryTypeStatistics[Type].BaseAddress + ); + if (EFI_ERROR (Status)) { + // + // If an error occurs allocating the pages for the current memory type, then + // free all the pages allocates for the previous memory types and return. This + // operation with be retied when/if more memory is added to the system + // + for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[FreeIndex].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) { + CoreFreePages ( + mMemoryTypeStatistics[Type].BaseAddress, + gMemoryTypeInformation[FreeIndex].NumberOfPages + ); + mMemoryTypeStatistics[Type].BaseAddress = 0; + mMemoryTypeStatistics[Type].MaximumAddress = MAX_ALLOC_ADDRESS; + } + } + + return; + } + + // + // Compute the address at the top of the current statistics + // + mMemoryTypeStatistics[Type].MaximumAddress = + mMemoryTypeStatistics[Type].BaseAddress + + LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1; + + // + // If the current base address is the lowest address so far, then update the default + // maximum address + // + if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { + mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; + } + } + } + + // + // There was enough system memory for all the the memory types were allocated. So, + // those memory areas can be freed for future allocations, and all future memory + // allocations can occur within their respective bins + // + for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (gMemoryTypeInformation[Index].NumberOfPages != 0) { + CoreFreePages ( + mMemoryTypeStatistics[Type].BaseAddress, + gMemoryTypeInformation[Index].NumberOfPages + ); + mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; + gMemoryTypeInformation[Index].NumberOfPages = 0; + } + } + + // + // If the number of pages reserved for a memory type is 0, then all allocations for that type + // should be in the default range. + // + for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { + mMemoryTypeStatistics[Type].InformationIndex = Index; + } + } + + mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; + if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { + mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; + } + } + + mMemoryTypeInformationInitialized = TRUE; +} + +/** + Internal function. Converts a memory range to the specified type or attributes. + The range must exist in the memory map. Either ChangingType or + ChangingAttributes must be set, but not both. + + @param Start The first address of the range Must be page + aligned + @param NumberOfPages The number of pages to convert + @param ChangingType Boolean indicating that type value should be changed + @param NewType The new type for the memory range + @param ChangingAttributes Boolean indicating that attributes value should be changed + @param NewAttributes The new attributes for the memory range + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_NOT_FOUND Could not find a descriptor cover the specified + range or convertion not allowed. + @retval EFI_SUCCESS Successfully converts the memory range to the + specified type. + +**/ +EFI_STATUS +CoreConvertPagesEx ( + IN UINT64 Start, + IN UINT64 NumberOfPages, + IN BOOLEAN ChangingType, + IN EFI_MEMORY_TYPE NewType, + IN BOOLEAN ChangingAttributes, + IN UINT64 NewAttributes + ) +{ + UINT64 NumberOfBytes; + UINT64 End; + UINT64 RangeEnd; + UINT64 Attribute; + EFI_MEMORY_TYPE MemType; + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + + Entry = NULL; + NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT); + End = Start + NumberOfBytes - 1; + + ASSERT (NumberOfPages); + ASSERT ((Start & EFI_PAGE_MASK) == 0); + ASSERT (End > Start); + ASSERT_LOCKED (&gMemoryLock); + ASSERT ((ChangingType == FALSE) || (ChangingAttributes == FALSE)); + + if ((NumberOfPages == 0) || ((Start & EFI_PAGE_MASK) != 0) || (Start >= End)) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the entire range + // + + while (Start < End) { + // + // Find the entry that the covers the range + // + for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + + if ((Entry->Start <= Start) && (Entry->End > Start)) { + break; + } + } + + if (Link == &gMemoryMap) { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: failed to find range %lx - %lx\n", Start, End)); + return EFI_NOT_FOUND; + } + + // + // If we are converting the type of the range from EfiConventionalMemory to + // another type, we have to ensure that the entire range is covered by a + // single entry. + // + if (ChangingType && (NewType != EfiConventionalMemory)) { + if (Entry->End < End) { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: range %lx - %lx covers multiple entries\n", Start, End)); + return EFI_NOT_FOUND; + } + } + + // + // Convert range to the end, or to the end of the descriptor + // if that's all we've got + // + RangeEnd = End; + + ASSERT (Entry != NULL); + if (Entry->End < End) { + RangeEnd = Entry->End; + } + + if (ChangingType) { + DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to type %d\n", Start, RangeEnd, NewType)); + } + + if (ChangingAttributes) { + DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to attr %lx\n", Start, RangeEnd, NewAttributes)); + } + + if (ChangingType) { + // + // Debug code - verify conversion is allowed + // + if (!((NewType == EfiConventionalMemory) ? 1 : 0) ^ ((Entry->Type == EfiConventionalMemory) ? 1 : 0)) { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: Incompatible memory types, ")); + if (Entry->Type == EfiConventionalMemory) { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to free have been freed\n")); + } else { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to allocate have been allocated\n")); + } + + return EFI_NOT_FOUND; + } + + // + // Update counters for the number of pages allocated to each memory type + // + if ((UINT32)Entry->Type < EfiMaxMemoryType) { + if (((Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress) && (Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress)) || + ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) + { + if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) { + mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0; + } else { + mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages; + } + } + } + + if ((UINT32)NewType < EfiMaxMemoryType) { + if (((Start >= mMemoryTypeStatistics[NewType].BaseAddress) && (Start <= mMemoryTypeStatistics[NewType].MaximumAddress)) || + ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) + { + mMemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages; + if (mMemoryTypeStatistics[NewType].CurrentNumberOfPages > gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages) { + gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)mMemoryTypeStatistics[NewType].CurrentNumberOfPages; + } + } + } + } + + // + // Pull range out of descriptor + // + if (Entry->Start == Start) { + // + // Clip start + // + Entry->Start = RangeEnd + 1; + } else if (Entry->End == RangeEnd) { + // + // Clip end + // + Entry->End = Start - 1; + } else { + // + // Pull it out of the center, clip current + // + + // + // Add a new one + // + mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE; + mMapStack[mMapDepth].FromPages = FALSE; + mMapStack[mMapDepth].Type = Entry->Type; + mMapStack[mMapDepth].Start = RangeEnd+1; + mMapStack[mMapDepth].End = Entry->End; + + // + // Inherit Attribute from the Memory Descriptor that is being clipped + // + mMapStack[mMapDepth].Attribute = Entry->Attribute; + + Entry->End = Start - 1; + ASSERT (Entry->Start < Entry->End); + + Entry = &mMapStack[mMapDepth]; + InsertTailList (&gMemoryMap, &Entry->Link); + + mMapDepth += 1; + ASSERT (mMapDepth < MAX_MAP_DEPTH); + } + + // + // The new range inherits the same Attribute as the Entry + // it is being cut out of unless attributes are being changed + // + if (ChangingType) { + Attribute = Entry->Attribute; + MemType = NewType; + } else { + Attribute = NewAttributes; + MemType = Entry->Type; + } + + // + // If the descriptor is empty, then remove it from the map + // + if (Entry->Start == Entry->End + 1) { + RemoveMemoryMapEntry (Entry); + Entry = NULL; + } + + // + // Add our new range in. Don't do this for freed pages if freed-memory + // guard is enabled. + // + if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) || + !ChangingType || + (MemType != EfiConventionalMemory)) + { + CoreAddRange (MemType, Start, RangeEnd, Attribute); + } + + if (ChangingType && (MemType == EfiConventionalMemory)) { + // + // Avoid calling DEBUG_CLEAR_MEMORY() for an address of 0 because this + // macro will ASSERT() if address is 0. Instead, CoreAddRange() guarantees + // that the page starting at address 0 is always filled with zeros. + // + if (Start == 0) { + if (RangeEnd > EFI_PAGE_SIZE) { + DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)EFI_PAGE_SIZE, (UINTN)(RangeEnd - EFI_PAGE_SIZE + 1)); + } + } else { + DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)Start, (UINTN)(RangeEnd - Start + 1)); + } + } + + // + // Move any map descriptor stack to general pool + // + CoreFreeMemoryMapStack (); + + // + // Bump the starting address, and convert the next range + // + Start = RangeEnd + 1; + } + + // + // Converted the whole range, done + // + + return EFI_SUCCESS; +} + +/** + Internal function. Converts a memory range to the specified type. + The range must exist in the memory map. + + @param Start The first address of the range Must be page + aligned + @param NumberOfPages The number of pages to convert + @param NewType The new type for the memory range + + @retval EFI_INVALID_PARAMETER Invalid parameter + @retval EFI_NOT_FOUND Could not find a descriptor cover the specified + range or convertion not allowed. + @retval EFI_SUCCESS Successfully converts the memory range to the + specified type. + +**/ +EFI_STATUS +CoreConvertPages ( + IN UINT64 Start, + IN UINT64 NumberOfPages, + IN EFI_MEMORY_TYPE NewType + ) +{ + return CoreConvertPagesEx (Start, NumberOfPages, TRUE, NewType, FALSE, 0); +} + +/** + Internal function. Converts a memory range to use new attributes. + + @param Start The first address of the range Must be page + aligned + @param NumberOfPages The number of pages to convert + @param NewAttributes The new attributes value for the range. + +**/ +VOID +CoreUpdateMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 NumberOfPages, + IN UINT64 NewAttributes + ) +{ + CoreAcquireMemoryLock (); + + // + // Update the attributes to the new value + // + CoreConvertPagesEx (Start, NumberOfPages, FALSE, (EFI_MEMORY_TYPE)0, TRUE, NewAttributes); + + CoreReleaseMemoryLock (); +} + +/** + Internal function. Finds a consecutive free page range below + the requested address. + + @param MaxAddress The address that the range must be below + @param MinAddress The address that the range must be above + @param NumberOfPages Number of pages needed + @param NewType The type of memory the range is going to be + turned into + @param Alignment Bits to align with + @param NeedGuard Flag to indicate Guard page is needed or not + + @return The base address of the range, or 0 if the range was not found + +**/ +UINT64 +CoreFindFreePagesI ( + IN UINT64 MaxAddress, + IN UINT64 MinAddress, + IN UINT64 NumberOfPages, + IN EFI_MEMORY_TYPE NewType, + IN UINTN Alignment, + IN BOOLEAN NeedGuard + ) +{ + UINT64 NumberOfBytes; + UINT64 Target; + UINT64 DescStart; + UINT64 DescEnd; + UINT64 DescNumberOfBytes; + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + + if ((MaxAddress < EFI_PAGE_MASK) || (NumberOfPages == 0)) { + return 0; + } + + if ((MaxAddress & EFI_PAGE_MASK) != EFI_PAGE_MASK) { + // + // If MaxAddress is not aligned to the end of a page + // + + // + // Change MaxAddress to be 1 page lower + // + MaxAddress -= (EFI_PAGE_MASK + 1); + + // + // Set MaxAddress to a page boundary + // + MaxAddress &= ~(UINT64)EFI_PAGE_MASK; + + // + // Set MaxAddress to end of the page + // + MaxAddress |= EFI_PAGE_MASK; + } + + NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT); + Target = 0; + + for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + + // + // If it's not a free entry, don't bother with it + // + if (Entry->Type != EfiConventionalMemory) { + continue; + } + + DescStart = Entry->Start; + DescEnd = Entry->End; + + // + // If desc is past max allowed address or below min allowed address, skip it + // + if ((DescStart >= MaxAddress) || (DescEnd < MinAddress)) { + continue; + } + + // + // If desc ends past max allowed address, clip the end + // + if (DescEnd >= MaxAddress) { + DescEnd = MaxAddress; + } + + DescEnd = ((DescEnd + 1) & (~((UINT64)Alignment - 1))) - 1; + + // Skip if DescEnd is less than DescStart after alignment clipping + if (DescEnd < DescStart) { + continue; + } + + // + // Compute the number of bytes we can used from this + // descriptor, and see it's enough to satisfy the request + // + DescNumberOfBytes = DescEnd - DescStart + 1; + + if (DescNumberOfBytes >= NumberOfBytes) { + // + // If the start of the allocated range is below the min address allowed, skip it + // + if ((DescEnd - NumberOfBytes + 1) < MinAddress) { + continue; + } + + // + // If this is the best match so far remember it + // + if (DescEnd > Target) { + if (NeedGuard) { + DescEnd = AdjustMemoryS ( + DescEnd + 1 - DescNumberOfBytes, + DescNumberOfBytes, + NumberOfBytes + ); + if (DescEnd == 0) { + continue; + } + } + + Target = DescEnd; + } + } + } + + // + // If this is a grow down, adjust target to be the allocation base + // + Target -= NumberOfBytes - 1; + + // + // If we didn't find a match, return 0 + // + if ((Target & EFI_PAGE_MASK) != 0) { + return 0; + } + + return Target; +} + +/** + Internal function. Finds a consecutive free page range below + the requested address + + @param MaxAddress The address that the range must be below + @param NoPages Number of pages needed + @param NewType The type of memory the range is going to be + turned into + @param Alignment Bits to align with + @param NeedGuard Flag to indicate Guard page is needed or not + + @return The base address of the range, or 0 if the range was not found. + +**/ +UINT64 +FindFreePages ( + IN UINT64 MaxAddress, + IN UINT64 NoPages, + IN EFI_MEMORY_TYPE NewType, + IN UINTN Alignment, + IN BOOLEAN NeedGuard + ) +{ + UINT64 Start; + + // + // Attempt to find free pages in the preferred bin based on the requested memory type + // + if (((UINT32)NewType < EfiMaxMemoryType) && (MaxAddress >= mMemoryTypeStatistics[NewType].MaximumAddress)) { + Start = CoreFindFreePagesI ( + mMemoryTypeStatistics[NewType].MaximumAddress, + mMemoryTypeStatistics[NewType].BaseAddress, + NoPages, + NewType, + Alignment, + NeedGuard + ); + if (Start != 0) { + return Start; + } + } + + // + // Attempt to find free pages in the default allocation bin + // + if (MaxAddress >= mDefaultMaximumAddress) { + Start = CoreFindFreePagesI ( + mDefaultMaximumAddress, + 0, + NoPages, + NewType, + Alignment, + NeedGuard + ); + if (Start != 0) { + if (Start < mDefaultBaseAddress) { + mDefaultBaseAddress = NeedGuard ? Start - EFI_PAGE_SIZE : Start; + } + + return Start; + } + } + + // + // The allocation did not succeed in any of the prefered bins even after + // promoting resources. Attempt to find free pages anywhere is the requested + // address range. If this allocation fails, then there are not enough + // resources anywhere to satisfy the request. + // + Start = CoreFindFreePagesI ( + MaxAddress, + 0, + NoPages, + NewType, + Alignment, + NeedGuard + ); + if (Start != 0) { + return Start; + } + + // + // If allocations from the preferred bins fail, then attempt to promote memory resources. + // + if (!PromoteMemoryResource ()) { + return 0; + } + + // + // If any memory resources were promoted, then re-attempt the allocation + // + return FindFreePages (MaxAddress, NoPages, NewType, Alignment, NeedGuard); +} + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform + @param MemoryType The type of memory to turn the allocated pages + into + @param NumberOfPages The number of pages to allocate + @param Memory A pointer to receive the base allocated memory + address + @param NeedGuard Flag to indicate Guard page is needed or not + + @return Status. On success, Memory is filled in with the base address allocated + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in + spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +CoreInternalAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory, + IN BOOLEAN NeedGuard + ) +{ + EFI_STATUS Status; + UINT64 Start; + UINT64 NumberOfBytes; + UINT64 End; + UINT64 MaxAddress; + UINTN Alignment; + EFI_MEMORY_TYPE CheckType; + + if ((UINT32)Type >= MaxAllocateType) { + return EFI_INVALID_PARAMETER; + } + + if (((MemoryType >= EfiMaxMemoryType) && (MemoryType < MEMORY_TYPE_OEM_RESERVED_MIN)) || + (MemoryType == EfiConventionalMemory) || (MemoryType == EfiPersistentMemory) || (MemoryType == EfiUnacceptedMemoryType)) + { + return EFI_INVALID_PARAMETER; + } + + if (Memory == NULL) { + return EFI_INVALID_PARAMETER; + } + + Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + + if ((MemoryType == EfiReservedMemoryType) || + (MemoryType == EfiACPIMemoryNVS) || + (MemoryType == EfiRuntimeServicesCode) || + (MemoryType == EfiRuntimeServicesData)) + { + Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } + + // + // The heap guard system does not support non-EFI_PAGE_SIZE alignments. + // Architectures that require larger RUNTIME_PAGE_ALLOCATION_GRANULARITY + // will have the runtime memory regions unguarded. OSes do not + // map guard pages anyway, so this is a minimal loss. Not guarding prevents + // alignment mismatches + // + if (Alignment != EFI_PAGE_SIZE) { + NeedGuard = FALSE; + } + + if (Type == AllocateAddress) { + if ((*Memory & (Alignment - 1)) != 0) { + return EFI_NOT_FOUND; + } + } + + NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1; + NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1); + + // + // If this is for below a particular address, then + // + Start = *Memory; + + // + // The max address is the max natively addressable address for the processor + // + MaxAddress = MAX_ALLOC_ADDRESS; + + // + // Check for Type AllocateAddress, + // if NumberOfPages is 0 or + // if (NumberOfPages << EFI_PAGE_SHIFT) is above MAX_ALLOC_ADDRESS or + // if (Start + NumberOfBytes) rolls over 0 or + // if Start is above MAX_ALLOC_ADDRESS or + // if End is above MAX_ALLOC_ADDRESS, + // if Start..End overlaps any tracked MemoryTypeStatistics range + // return EFI_NOT_FOUND. + // + if (Type == AllocateAddress) { + if ((NumberOfPages == 0) || + (NumberOfPages > RShiftU64 (MaxAddress, EFI_PAGE_SHIFT))) + { + return EFI_NOT_FOUND; + } + + NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT); + End = Start + NumberOfBytes - 1; + + if ((Start >= End) || + (Start > MaxAddress) || + (End > MaxAddress)) + { + return EFI_NOT_FOUND; + } + + // + // A driver is allowed to call AllocatePages using an AllocateAddress type. This type of + // AllocatePage request the exact physical address if it is not used. The existing code + // will allow this request even in 'special' pages. The problem with this is that the + // reason to have 'special' pages for OS hibernate/resume is defeated as memory is + // fragmented. + // + + for (CheckType = (EFI_MEMORY_TYPE)0; CheckType < EfiMaxMemoryType; CheckType++) { + if ((MemoryType != CheckType) && + mMemoryTypeStatistics[CheckType].Special && + (mMemoryTypeStatistics[CheckType].NumberOfPages > 0)) + { + if ((Start >= mMemoryTypeStatistics[CheckType].BaseAddress) && + (Start <= mMemoryTypeStatistics[CheckType].MaximumAddress)) + { + return EFI_NOT_FOUND; + } + + if ((End >= mMemoryTypeStatistics[CheckType].BaseAddress) && + (End <= mMemoryTypeStatistics[CheckType].MaximumAddress)) + { + return EFI_NOT_FOUND; + } + + if ((Start < mMemoryTypeStatistics[CheckType].BaseAddress) && + (End > mMemoryTypeStatistics[CheckType].MaximumAddress)) + { + return EFI_NOT_FOUND; + } + } + } + } + + if (Type == AllocateMaxAddress) { + MaxAddress = Start; + } + + CoreAcquireMemoryLock (); + + // + // If not a specific address, then find an address to allocate + // + if (Type != AllocateAddress) { + Start = FindFreePages ( + MaxAddress, + NumberOfPages, + MemoryType, + Alignment, + NeedGuard + ); + if (Start == 0) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + } + + // + // Convert pages from FreeMemory to the requested type + // + if (NeedGuard) { + Status = CoreConvertPagesWithGuard (Start, NumberOfPages, MemoryType); + } else { + Status = CoreConvertPages (Start, NumberOfPages, MemoryType); + } + + if (EFI_ERROR (Status)) { + // + // If requested memory region is unavailable it may be untested memory + // Attempt to promote memory resources, then re-attempt the allocation + // + if (PromoteMemoryResource ()) { + if (NeedGuard) { + Status = CoreConvertPagesWithGuard (Start, NumberOfPages, MemoryType); + } else { + Status = CoreConvertPages (Start, NumberOfPages, MemoryType); + } + } + } + +Done: + CoreReleaseMemoryLock (); + + if (!EFI_ERROR (Status)) { + if (NeedGuard) { + SetGuardForMemory (Start, NumberOfPages); + } + + *Memory = Start; + } + + return Status; +} + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform + @param MemoryType The type of memory to turn the allocated pages + into + @param NumberOfPages The number of pages to allocate + @param Memory A pointer to receive the base allocated memory + address + + @return Status. On success, Memory is filled in with the base address allocated + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in + spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +CoreAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + OUT EFI_PHYSICAL_ADDRESS *Memory + ) +{ + EFI_STATUS Status; + BOOLEAN NeedGuard; + + NeedGuard = IsPageTypeToGuard (MemoryType, Type) && !mOnGuarding; + Status = CoreInternalAllocatePages ( + Type, + MemoryType, + NumberOfPages, + Memory, + NeedGuard + ); + if (!EFI_ERROR (Status)) { + CoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), + MemoryProfileActionAllocatePages, + MemoryType, + EFI_PAGES_TO_SIZE (NumberOfPages), + (VOID *)(UINTN)*Memory, + NULL + ); + InstallMemoryAttributesTableOnMemoryAllocation (MemoryType); + ApplyMemoryProtectionPolicy ( + EfiConventionalMemory, + MemoryType, + *Memory, + EFI_PAGES_TO_SIZE (NumberOfPages) + ); + } + + return Status; +} + +/** + Frees previous allocated pages. + + @param Memory Base address of memory being freed + @param NumberOfPages The number of pages to free + @param MemoryType Pointer to memory type + + @retval EFI_NOT_FOUND Could not find the entry that covers the range + @retval EFI_INVALID_PARAMETER Address not aligned + @return EFI_SUCCESS -Pages successfully freed. + +**/ +EFI_STATUS +EFIAPI +CoreInternalFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages, + OUT EFI_MEMORY_TYPE *MemoryType OPTIONAL + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + UINTN Alignment; + BOOLEAN IsGuarded; + + // + // Free the range + // + CoreAcquireMemoryLock (); + + // + // Find the entry that the covers the range + // + IsGuarded = FALSE; + Entry = NULL; + for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + if ((Entry->Start <= Memory) && (Entry->End > Memory)) { + break; + } + } + + if (Link == &gMemoryMap) { + Status = EFI_NOT_FOUND; + goto Done; + } + + if (Entry == NULL) { + ASSERT (Entry != NULL); + Status = EFI_NOT_FOUND; + goto Done; + } + + Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + + if ((Entry->Type == EfiReservedMemoryType) || + (Entry->Type == EfiACPIMemoryNVS) || + (Entry->Type == EfiRuntimeServicesCode) || + (Entry->Type == EfiRuntimeServicesData)) + { + Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } + + if ((Memory & (Alignment - 1)) != 0) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1; + NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1); + + if (MemoryType != NULL) { + *MemoryType = Entry->Type; + } + + IsGuarded = IsPageTypeToGuard (Entry->Type, AllocateAnyPages) && + IsMemoryGuarded (Memory); + if (IsGuarded) { + Status = CoreConvertPagesWithGuard ( + Memory, + NumberOfPages, + EfiConventionalMemory + ); + } else { + Status = CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory); + } + +Done: + CoreReleaseMemoryLock (); + return Status; +} + +/** + Frees previous allocated pages. + + @param Memory Base address of memory being freed + @param NumberOfPages The number of pages to free + + @retval EFI_NOT_FOUND Could not find the entry that covers the range + @retval EFI_INVALID_PARAMETER Address not aligned + @return EFI_SUCCESS -Pages successfully freed. + +**/ +EFI_STATUS +EFIAPI +CoreFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + EFI_STATUS Status; + EFI_MEMORY_TYPE MemoryType; + + Status = CoreInternalFreePages (Memory, NumberOfPages, &MemoryType); + if (!EFI_ERROR (Status)) { + GuardFreedPagesChecked (Memory, NumberOfPages); + CoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), + MemoryProfileActionFreePages, + MemoryType, + EFI_PAGES_TO_SIZE (NumberOfPages), + (VOID *)(UINTN)Memory, + NULL + ); + InstallMemoryAttributesTableOnMemoryAllocation (MemoryType); + ApplyMemoryProtectionPolicy ( + MemoryType, + EfiConventionalMemory, + Memory, + EFI_PAGES_TO_SIZE (NumberOfPages) + ); + } + + return Status; +} + +/** + This function checks to see if the last memory map descriptor in a memory map + can be merged with any of the other memory map descriptors in a memorymap. + Memory descriptors may be merged if they are adjacent and have the same type + and attributes. + + @param MemoryMap A pointer to the start of the memory map. + @param MemoryMapDescriptor A pointer to the last descriptor in MemoryMap. + @param DescriptorSize The size, in bytes, of an individual + EFI_MEMORY_DESCRIPTOR. + + @return A pointer to the next available descriptor in MemoryMap + +**/ +EFI_MEMORY_DESCRIPTOR * +MergeMemoryMapDescriptor ( + IN EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN EFI_MEMORY_DESCRIPTOR *MemoryMapDescriptor, + IN UINTN DescriptorSize + ) +{ + // + // Traverse the array of descriptors in MemoryMap + // + for ( ; MemoryMap != MemoryMapDescriptor; MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize)) { + // + // Check to see if the Type fields are identical. + // + if (MemoryMap->Type != MemoryMapDescriptor->Type) { + continue; + } + + // + // Check to see if the Attribute fields are identical. + // + if (MemoryMap->Attribute != MemoryMapDescriptor->Attribute) { + continue; + } + + // + // Check to see if MemoryMapDescriptor is immediately above MemoryMap + // + if (MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) { + // + // Merge MemoryMapDescriptor into MemoryMap + // + MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages; + + // + // Return MemoryMapDescriptor as the next available slot int he MemoryMap array + // + return MemoryMapDescriptor; + } + + // + // Check to see if MemoryMapDescriptor is immediately below MemoryMap + // + if (MemoryMap->PhysicalStart - EFI_PAGES_TO_SIZE ((UINTN)MemoryMapDescriptor->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) { + // + // Merge MemoryMapDescriptor into MemoryMap + // + MemoryMap->PhysicalStart = MemoryMapDescriptor->PhysicalStart; + MemoryMap->VirtualStart = MemoryMapDescriptor->VirtualStart; + MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages; + + // + // Return MemoryMapDescriptor as the next available slot int he MemoryMap array + // + return MemoryMapDescriptor; + } + } + + // + // MemoryMapDescrtiptor could not be merged with any descriptors in MemoryMap. + // + // Return the slot immediately after MemoryMapDescriptor as the next available + // slot in the MemoryMap array + // + return NEXT_MEMORY_DESCRIPTOR (MemoryMapDescriptor, DescriptorSize); +} + +/** + This function returns a copy of the current memory map. The map is an array of + memory descriptors, each of which describes a contiguous block of memory. + + @param MemoryMapSize A pointer to the size, in bytes, of the + MemoryMap buffer. On input, this is the size of + the buffer allocated by the caller. On output, + it is the size of the buffer returned by the + firmware if the buffer was large enough, or the + size of the buffer needed to contain the map if + the buffer was too small. + @param MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param MapKey A pointer to the location in which firmware + returns the key for the current memory map. + @param DescriptorSize A pointer to the location in which firmware + returns the size, in bytes, of an individual + EFI_MEMORY_DESCRIPTOR. + @param DescriptorVersion A pointer to the location in which firmware + returns the version number associated with the + EFI_MEMORY_DESCRIPTOR. + + @retval EFI_SUCCESS The memory map was returned in the MemoryMap + buffer. + @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current + buffer size needed to hold the memory map is + returned in MemoryMapSize. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + +**/ +EFI_STATUS +EFIAPI +CoreGetMemoryMap ( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ) +{ + EFI_STATUS Status; + UINTN Size; + UINTN BufferSize; + UINTN NumberOfEntries; + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + EFI_GCD_MAP_ENTRY *GcdMapEntry; + EFI_GCD_MAP_ENTRY MergeGcdMapEntry; + EFI_MEMORY_TYPE Type; + EFI_MEMORY_DESCRIPTOR *MemoryMapStart; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + + // + // Make sure the parameters are valid + // + if (MemoryMapSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + CoreAcquireGcdMemoryLock (); + + // + // Count the number of Reserved and runtime MMIO entries + // And, count the number of Persistent entries. + // + NumberOfEntries = 0; + for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) { + GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE); + if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypePersistent) || + (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) || + ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) && + ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) + { + NumberOfEntries++; + } + } + + Size = sizeof (EFI_MEMORY_DESCRIPTOR); + + // + // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will + // prevent people from having pointer math bugs in their code. + // now you have to use *DescriptorSize to make things work. + // + Size += sizeof (UINT64) - (Size % sizeof (UINT64)); + + if (DescriptorSize != NULL) { + *DescriptorSize = Size; + } + + if (DescriptorVersion != NULL) { + *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION; + } + + CoreAcquireMemoryLock (); + + // + // Compute the buffer size needed to fit the entire map + // + BufferSize = Size * NumberOfEntries; + for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { + BufferSize += Size; + } + + if (*MemoryMapSize < BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + if (MemoryMap == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // Build the map + // + ZeroMem (MemoryMap, BufferSize); + MemoryMapStart = MemoryMap; + for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + ASSERT (Entry->VirtualStart == 0); + + // + // Convert internal map into an EFI_MEMORY_DESCRIPTOR + // + MemoryMap->Type = Entry->Type; + MemoryMap->PhysicalStart = Entry->Start; + MemoryMap->VirtualStart = Entry->VirtualStart; + MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT); + // + // If the memory type is EfiConventionalMemory, then determine if the range is part of a + // memory type bin and needs to be converted to the same memory type as the rest of the + // memory type bin in order to minimize EFI Memory Map changes across reboots. This + // improves the chances for a successful S4 resume in the presence of minor page allocation + // differences across reboots. + // + if (MemoryMap->Type == EfiConventionalMemory) { + for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + if (mMemoryTypeStatistics[Type].Special && + (mMemoryTypeStatistics[Type].NumberOfPages > 0) && + (Entry->Start >= mMemoryTypeStatistics[Type].BaseAddress) && + (Entry->End <= mMemoryTypeStatistics[Type].MaximumAddress)) + { + MemoryMap->Type = Type; + } + } + } + + MemoryMap->Attribute = Entry->Attribute; + if (MemoryMap->Type < EfiMaxMemoryType) { + if (mMemoryTypeStatistics[MemoryMap->Type].Runtime) { + MemoryMap->Attribute |= EFI_MEMORY_RUNTIME; + } + } + + // + // Check to see if the new Memory Map Descriptor can be merged with an + // existing descriptor if they are adjacent and have the same attributes + // + MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); + } + + ZeroMem (&MergeGcdMapEntry, sizeof (MergeGcdMapEntry)); + GcdMapEntry = NULL; + for (Link = mGcdMemorySpaceMap.ForwardLink; ; Link = Link->ForwardLink) { + if (Link != &mGcdMemorySpaceMap) { + // + // Merge adjacent same type and attribute GCD memory range + // + GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE); + + if ((MergeGcdMapEntry.Capabilities == GcdMapEntry->Capabilities) && + (MergeGcdMapEntry.Attributes == GcdMapEntry->Attributes) && + (MergeGcdMapEntry.GcdMemoryType == GcdMapEntry->GcdMemoryType) && + (MergeGcdMapEntry.GcdIoType == GcdMapEntry->GcdIoType)) + { + MergeGcdMapEntry.EndAddress = GcdMapEntry->EndAddress; + continue; + } + } + + if ((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) || + ((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) && + ((MergeGcdMapEntry.Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) + { + // + // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR, + // it will be recorded as page PhysicalStart and NumberOfPages. + // + ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0); + ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0); + + // + // Create EFI_MEMORY_DESCRIPTOR for every Reserved and runtime MMIO GCD entries + // + MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; + MemoryMap->VirtualStart = 0; + MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); + MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | + (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + + if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) { + MemoryMap->Type = EfiReservedMemoryType; + } else if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) { + if ((MergeGcdMapEntry.Attributes & EFI_MEMORY_PORT_IO) == EFI_MEMORY_PORT_IO) { + MemoryMap->Type = EfiMemoryMappedIOPortSpace; + } else { + MemoryMap->Type = EfiMemoryMappedIO; + } + } + + // + // Check to see if the new Memory Map Descriptor can be merged with an + // existing descriptor if they are adjacent and have the same attributes + // + MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); + } + + if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypePersistent) { + // + // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR, + // it will be recorded as page PhysicalStart and NumberOfPages. + // + ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0); + ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0); + + // + // Create EFI_MEMORY_DESCRIPTOR for every Persistent GCD entries + // + MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; + MemoryMap->VirtualStart = 0; + MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); + MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | + (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + MemoryMap->Type = EfiPersistentMemory; + + // + // Check to see if the new Memory Map Descriptor can be merged with an + // existing descriptor if they are adjacent and have the same attributes + // + MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); + } + + if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeUnaccepted) { + // + // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR, + // it will be recorded as page PhysicalStart and NumberOfPages. + // + ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0); + ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0); + + // + // Create EFI_MEMORY_DESCRIPTOR for every Unaccepted GCD entries + // + MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; + MemoryMap->VirtualStart = 0; + MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); + MemoryMap->Attribute = MergeGcdMapEntry.Attributes | + (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO | + EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB)); + MemoryMap->Type = EfiUnacceptedMemoryType; + + // + // Check to see if the new Memory Map Descriptor can be merged with an + // existing descriptor if they are adjacent and have the same attributes + // + MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); + } + + if (Link == &mGcdMemorySpaceMap) { + // + // break loop when arrive at head. + // + break; + } + + if (GcdMapEntry != NULL) { + // + // Copy new GCD map entry for the following GCD range merge + // + CopyMem (&MergeGcdMapEntry, GcdMapEntry, sizeof (MergeGcdMapEntry)); + } + } + + // + // Compute the size of the buffer actually used after all memory map descriptor merge operations + // + BufferSize = ((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart); + + // + // Note: Some OSs will treat EFI_MEMORY_DESCRIPTOR.Attribute as really + // set attributes and change memory paging attribute accordingly. + // But current EFI_MEMORY_DESCRIPTOR.Attribute is assigned by + // value from Capabilities in GCD memory map. This might cause + // boot problems. Clearing all page-access permission related + // capabilities can workaround it. Following code is supposed to + // be removed once the usage of EFI_MEMORY_DESCRIPTOR.Attribute + // is clarified in UEFI spec and adopted by both EDK-II Core and + // all supported OSs. + // + MemoryMapEnd = MemoryMap; + MemoryMap = MemoryMapStart; + while (MemoryMap < MemoryMapEnd) { + MemoryMap->Attribute &= ~(UINT64)EFI_MEMORY_ACCESS_MASK; + MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size); + } + + MergeMemoryMap (MemoryMapStart, &BufferSize, Size); + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMapStart + BufferSize); + + Status = EFI_SUCCESS; + +Done: + // + // Update the map key finally + // + if (MapKey != NULL) { + *MapKey = mMemoryMapKey; + } + + CoreReleaseMemoryLock (); + + CoreReleaseGcdMemoryLock (); + + *MemoryMapSize = BufferSize; + + DEBUG_CODE ( + DumpGuardedMemoryBitmap (); + ); + + return Status; +} + +/** + Internal function. Used by the pool functions to allocate pages + to back pool allocation requests. + + @param PoolType The type of memory for the new pool pages + @param NumberOfPages No of pages to allocate + @param Alignment Bits to align. + @param NeedGuard Flag to indicate Guard page is needed or not + + @return The allocated memory, or NULL + +**/ +VOID * +CoreAllocatePoolPages ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN NumberOfPages, + IN UINTN Alignment, + IN BOOLEAN NeedGuard + ) +{ + UINT64 Start; + + // + // Find the pages to convert + // + Start = FindFreePages ( + MAX_ALLOC_ADDRESS, + NumberOfPages, + PoolType, + Alignment, + NeedGuard + ); + + // + // Convert it to boot services data + // + if (Start == 0) { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", (UINT32)NumberOfPages)); + } else { + if (NeedGuard) { + CoreConvertPagesWithGuard (Start, NumberOfPages, PoolType); + } else { + CoreConvertPages (Start, NumberOfPages, PoolType); + } + } + + return (VOID *)(UINTN)Start; +} + +/** + Internal function. Frees pool pages allocated via AllocatePoolPages () + + @param Memory The base address to free + @param NumberOfPages The number of pages to free + +**/ +VOID +CoreFreePoolPages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ) +{ + CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory); +} + +/** + Make sure the memory map is following all the construction rules, + it is the last time to check memory map error before exit boot services. + + @param MapKey Memory map key + + @retval EFI_INVALID_PARAMETER Memory map not consistent with construction + rules. + @retval EFI_SUCCESS Valid memory map. + +**/ +EFI_STATUS +CoreTerminateMemoryMap ( + IN UINTN MapKey + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + MEMORY_MAP *Entry; + + Status = EFI_SUCCESS; + + CoreAcquireMemoryLock (); + + if (MapKey == mMemoryMapKey) { + // + // Make sure the memory map is following all the construction rules + // This is the last chance we will be able to display any messages on + // the console devices. + // + + for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { + Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); + if (Entry->Type < EfiMaxMemoryType) { + if (mMemoryTypeStatistics[Entry->Type].Runtime) { + ASSERT (Entry->Type != EfiACPIReclaimMemory); + ASSERT (Entry->Type != EfiACPIMemoryNVS); + if ((Entry->Start & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n")); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (((Entry->End + 1) & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) { + DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n")); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + } + } + } + + // + // The map key they gave us matches what we expect. Fall through and + // return success. In an ideal world we would clear out all of + // EfiBootServicesCode and EfiBootServicesData. However this function + // is not the last one called by ExitBootServices(), so we have to + // preserve the memory contents. + // + } else { + Status = EFI_INVALID_PARAMETER; + } + +Done: + CoreReleaseMemoryLock (); + + return Status; +} diff --git a/MdeModulePkg/Core/Dxe/Mem/Pool.c b/MdeModulePkg/Core/Dxe/Mem/Pool.c index 72293e6dfe..4a7e97708b 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Pool.c +++ b/MdeModulePkg/Core/Dxe/Mem/Pool.c @@ -1,882 +1,882 @@ -/** @file
- UEFI Memory pool management functions.
-
-Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "DxeMain.h"
-#include "Imem.h"
-#include "HeapGuard.h"
-
-STATIC EFI_LOCK mPoolMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
-
-#define POOL_FREE_SIGNATURE SIGNATURE_32('p','f','r','0')
-typedef struct {
- UINT32 Signature;
- UINT32 Index;
- LIST_ENTRY Link;
-} POOL_FREE;
-
-#define POOL_HEAD_SIGNATURE SIGNATURE_32('p','h','d','0')
-#define POOLPAGE_HEAD_SIGNATURE SIGNATURE_32('p','h','d','1')
-typedef struct {
- UINT32 Signature;
- UINT32 Reserved;
- EFI_MEMORY_TYPE Type;
- UINTN Size;
- CHAR8 Data[1];
-} POOL_HEAD;
-
-#define SIZE_OF_POOL_HEAD OFFSET_OF(POOL_HEAD,Data)
-
-#define POOL_TAIL_SIGNATURE SIGNATURE_32('p','t','a','l')
-typedef struct {
- UINT32 Signature;
- UINT32 Reserved;
- UINTN Size;
-} POOL_TAIL;
-
-#define POOL_OVERHEAD (SIZE_OF_POOL_HEAD + sizeof(POOL_TAIL))
-
-#define HEAD_TO_TAIL(a) \
- ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL)));
-
-//
-// Each element is the sum of the 2 previous ones: this allows us to migrate
-// blocks between bins by splitting them up, while not wasting too much memory
-// as we would in a strict power-of-2 sequence
-//
-STATIC CONST UINT16 mPoolSizeTable[] = {
- 128, 256, 384, 640, 1024, 1664, 2688, 4352, 7040, 11392, 18432, 29824
-};
-
-#define SIZE_TO_LIST(a) (GetPoolIndexFromSize (a))
-#define LIST_TO_SIZE(a) (mPoolSizeTable [a])
-
-#define MAX_POOL_LIST (ARRAY_SIZE (mPoolSizeTable))
-
-#define MAX_POOL_SIZE (MAX_ADDRESS - POOL_OVERHEAD)
-
-//
-// Globals
-//
-
-#define POOL_SIGNATURE SIGNATURE_32('p','l','s','t')
-typedef struct {
- INTN Signature;
- UINTN Used;
- EFI_MEMORY_TYPE MemoryType;
- LIST_ENTRY FreeList[MAX_POOL_LIST];
- LIST_ENTRY Link;
-} POOL;
-
-//
-// Pool header for each memory type.
-//
-POOL mPoolHead[EfiMaxMemoryType];
-
-//
-// List of pool header to search for the appropriate memory type.
-//
-LIST_ENTRY mPoolHeadList = INITIALIZE_LIST_HEAD_VARIABLE (mPoolHeadList);
-
-/**
- Get pool size table index from the specified size.
-
- @param Size The specified size to get index from pool table.
-
- @return The index of pool size table.
-
-**/
-STATIC
-UINTN
-GetPoolIndexFromSize (
- UINTN Size
- )
-{
- UINTN Index;
-
- for (Index = 0; Index < MAX_POOL_LIST; Index++) {
- if (mPoolSizeTable[Index] >= Size) {
- return Index;
- }
- }
-
- return MAX_POOL_LIST;
-}
-
-/**
- Called to initialize the pool.
-
-**/
-VOID
-CoreInitializePool (
- VOID
- )
-{
- UINTN Type;
- UINTN Index;
-
- for (Type = 0; Type < EfiMaxMemoryType; Type++) {
- mPoolHead[Type].Signature = 0;
- mPoolHead[Type].Used = 0;
- mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE)Type;
- for (Index = 0; Index < MAX_POOL_LIST; Index++) {
- InitializeListHead (&mPoolHead[Type].FreeList[Index]);
- }
- }
-}
-
-/**
- Look up pool head for specified memory type.
-
- @param MemoryType Memory type of which pool head is looked for
-
- @return Pointer of Corresponding pool head.
-
-**/
-POOL *
-LookupPoolHead (
- IN EFI_MEMORY_TYPE MemoryType
- )
-{
- LIST_ENTRY *Link;
- POOL *Pool;
- UINTN Index;
-
- if ((UINT32)MemoryType < EfiMaxMemoryType) {
- return &mPoolHead[MemoryType];
- }
-
- //
- // MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI
- // OS loaders that are provided by operating system vendors.
- // MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use.
- //
- if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
- for (Link = mPoolHeadList.ForwardLink; Link != &mPoolHeadList; Link = Link->ForwardLink) {
- Pool = CR (Link, POOL, Link, POOL_SIGNATURE);
- if (Pool->MemoryType == MemoryType) {
- return Pool;
- }
- }
-
- Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL), FALSE);
- if (Pool == NULL) {
- return NULL;
- }
-
- Pool->Signature = POOL_SIGNATURE;
- Pool->Used = 0;
- Pool->MemoryType = MemoryType;
- for (Index = 0; Index < MAX_POOL_LIST; Index++) {
- InitializeListHead (&Pool->FreeList[Index]);
- }
-
- InsertHeadList (&mPoolHeadList, &Pool->Link);
-
- return Pool;
- }
-
- return NULL;
-}
-
-/**
- Allocate pool of a particular type.
-
- @param PoolType Type of pool to allocate
- @param Size The amount of pool to allocate
- @param Buffer The address to return a pointer to the allocated
- pool
-
- @retval EFI_INVALID_PARAMETER Buffer is NULL.
- PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
- PoolType is EfiPersistentMemory.
- @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
- @retval EFI_SUCCESS Pool successfully allocated.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreInternalAllocatePool (
- IN EFI_MEMORY_TYPE PoolType,
- IN UINTN Size,
- OUT VOID **Buffer
- )
-{
- EFI_STATUS Status;
- BOOLEAN NeedGuard;
-
- //
- // If it's not a valid type, fail it
- //
- if (((PoolType >= EfiMaxMemoryType) && (PoolType < MEMORY_TYPE_OEM_RESERVED_MIN)) ||
- (PoolType == EfiConventionalMemory) || (PoolType == EfiPersistentMemory) || (PoolType == EfiUnacceptedMemoryType))
- {
- return EFI_INVALID_PARAMETER;
- }
-
- if (Buffer == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- *Buffer = NULL;
-
- //
- // If size is too large, fail it
- // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES
- //
- if (Size > MAX_POOL_SIZE) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- NeedGuard = IsPoolTypeToGuard (PoolType) && !mOnGuarding;
-
- //
- // Acquire the memory lock and make the allocation
- //
- Status = CoreAcquireLockOrFail (&mPoolMemoryLock);
- if (EFI_ERROR (Status)) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- *Buffer = CoreAllocatePoolI (PoolType, Size, NeedGuard);
- CoreReleaseLock (&mPoolMemoryLock);
- return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
-}
-
-/**
- Allocate pool of a particular type.
-
- @param PoolType Type of pool to allocate
- @param Size The amount of pool to allocate
- @param Buffer The address to return a pointer to the allocated
- pool
-
- @retval EFI_INVALID_PARAMETER Buffer is NULL.
- PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
- PoolType is EfiPersistentMemory.
- @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
- @retval EFI_SUCCESS Pool successfully allocated.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreAllocatePool (
- IN EFI_MEMORY_TYPE PoolType,
- IN UINTN Size,
- OUT VOID **Buffer
- )
-{
- EFI_STATUS Status;
-
- Status = CoreInternalAllocatePool (PoolType, Size, Buffer);
- if (!EFI_ERROR (Status)) {
- CoreUpdateProfile (
- (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
- MemoryProfileActionAllocatePool,
- PoolType,
- Size,
- *Buffer,
- NULL
- );
- InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
- }
-
- return Status;
-}
-
-/**
- Internal function. Used by the pool functions to allocate pages
- to back pool allocation requests.
-
- @param PoolType The type of memory for the new pool pages
- @param NoPages No of pages to allocate
- @param Granularity Bits to align.
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return The allocated memory, or NULL
-
-**/
-STATIC
-VOID *
-CoreAllocatePoolPagesI (
- IN EFI_MEMORY_TYPE PoolType,
- IN UINTN NoPages,
- IN UINTN Granularity,
- IN BOOLEAN NeedGuard
- )
-{
- VOID *Buffer;
- EFI_STATUS Status;
-
- Status = CoreAcquireLockOrFail (&gMemoryLock);
- if (EFI_ERROR (Status)) {
- return NULL;
- }
-
- Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity, NeedGuard);
- CoreReleaseMemoryLock ();
-
- if (Buffer != NULL) {
- if (NeedGuard) {
- SetGuardForMemory ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, NoPages);
- }
-
- ApplyMemoryProtectionPolicy (
- EfiConventionalMemory,
- PoolType,
- (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer,
- EFI_PAGES_TO_SIZE (NoPages)
- );
- }
-
- return Buffer;
-}
-
-/**
- Internal function to allocate pool of a particular type.
- Caller must have the memory lock held
-
- @param PoolType Type of pool to allocate
- @param Size The amount of pool to allocate
- @param NeedGuard Flag to indicate Guard page is needed or not
-
- @return The allocate pool, or NULL
-
-**/
-VOID *
-CoreAllocatePoolI (
- IN EFI_MEMORY_TYPE PoolType,
- IN UINTN Size,
- IN BOOLEAN NeedGuard
- )
-{
- POOL *Pool;
- POOL_FREE *Free;
- POOL_HEAD *Head;
- POOL_TAIL *Tail;
- CHAR8 *NewPage;
- VOID *Buffer;
- UINTN Index;
- UINTN FSize;
- UINTN Offset, MaxOffset;
- UINTN NoPages;
- UINTN Granularity;
- BOOLEAN HasPoolTail;
- BOOLEAN PageAsPool;
-
- ASSERT_LOCKED (&mPoolMemoryLock);
-
- if ((PoolType == EfiReservedMemoryType) ||
- (PoolType == EfiACPIMemoryNVS) ||
- (PoolType == EfiRuntimeServicesCode) ||
- (PoolType == EfiRuntimeServicesData))
- {
- Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
- } else {
- Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
- }
-
- //
- // The heap guard system does not support non-EFI_PAGE_SIZE alignments.
- // Architectures that require larger RUNTIME_PAGE_ALLOCATION_GRANULARITY
- // will have the runtime memory regions unguarded. OSes do not
- // map guard pages anyway, so this is a minimal loss. Not guarding prevents
- // alignment mismatches
- //
- if (Granularity != EFI_PAGE_SIZE) {
- NeedGuard = FALSE;
- }
-
- //
- // Adjust the size by the pool header & tail overhead
- //
-
- HasPoolTail = !(NeedGuard &&
- ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
- PageAsPool = (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) && !mOnGuarding);
-
- //
- // Adjusting the Size to be of proper alignment so that
- // we don't get an unaligned access fault later when
- // pool_Tail is being initialized
- //
- Size = ALIGN_VARIABLE (Size);
-
- Size += POOL_OVERHEAD;
- Index = SIZE_TO_LIST (Size);
- Pool = LookupPoolHead (PoolType);
- if (Pool == NULL) {
- return NULL;
- }
-
- Head = NULL;
-
- //
- // If allocation is over max size, just allocate pages for the request
- // (slow)
- //
- if ((Index >= SIZE_TO_LIST (Granularity)) || NeedGuard || PageAsPool) {
- if (!HasPoolTail) {
- Size -= sizeof (POOL_TAIL);
- }
-
- NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
- NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
- Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity, NeedGuard);
- if (NeedGuard) {
- Head = AdjustPoolHeadA ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size);
- }
-
- goto Done;
- }
-
- //
- // If there's no free pool in the proper list size, go get some more pages
- //
- if (IsListEmpty (&Pool->FreeList[Index])) {
- Offset = LIST_TO_SIZE (Index);
- MaxOffset = Granularity;
-
- //
- // Check the bins holding larger blocks, and carve one up if needed
- //
- while (++Index < SIZE_TO_LIST (Granularity)) {
- if (!IsListEmpty (&Pool->FreeList[Index])) {
- Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
- RemoveEntryList (&Free->Link);
- NewPage = (VOID *)Free;
- MaxOffset = LIST_TO_SIZE (Index);
- goto Carve;
- }
- }
-
- //
- // Get another page
- //
- NewPage = CoreAllocatePoolPagesI (
- PoolType,
- EFI_SIZE_TO_PAGES (Granularity),
- Granularity,
- NeedGuard
- );
- if (NewPage == NULL) {
- goto Done;
- }
-
- //
- // Serve the allocation request from the head of the allocated block
- //
-Carve:
- Head = (POOL_HEAD *)NewPage;
-
- //
- // Carve up remaining space into free pool blocks
- //
- Index--;
- while (Offset < MaxOffset) {
- ASSERT (Index < MAX_POOL_LIST);
- FSize = LIST_TO_SIZE (Index);
-
- while (Offset + FSize <= MaxOffset) {
- Free = (POOL_FREE *)&NewPage[Offset];
- Free->Signature = POOL_FREE_SIGNATURE;
- Free->Index = (UINT32)Index;
- InsertHeadList (&Pool->FreeList[Index], &Free->Link);
- Offset += FSize;
- }
-
- Index -= 1;
- }
-
- ASSERT (Offset == MaxOffset);
- goto Done;
- }
-
- //
- // Remove entry from free pool list
- //
- Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
- RemoveEntryList (&Free->Link);
-
- Head = (POOL_HEAD *)Free;
-
-Done:
- Buffer = NULL;
-
- if (Head != NULL) {
- //
- // Account the allocation
- //
- Pool->Used += Size;
-
- //
- // If we have a pool buffer, fill in the header & tail info
- //
- Head->Signature = (PageAsPool) ? POOLPAGE_HEAD_SIGNATURE : POOL_HEAD_SIGNATURE;
- Head->Size = Size;
- Head->Type = (EFI_MEMORY_TYPE)PoolType;
- Buffer = Head->Data;
-
- if (HasPoolTail) {
- Tail = HEAD_TO_TAIL (Head);
- Tail->Signature = POOL_TAIL_SIGNATURE;
- Tail->Size = Size;
-
- Size -= POOL_OVERHEAD;
- } else {
- Size -= SIZE_OF_POOL_HEAD;
- }
-
- DEBUG_CLEAR_MEMORY (Buffer, Size);
-
- DEBUG ((
- DEBUG_POOL,
- "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n",
- PoolType,
- Buffer,
- (UINT64)Size,
- (UINT64)Pool->Used
- ));
- } else {
- DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64)Size));
- }
-
- return Buffer;
-}
-
-/**
- Frees pool.
-
- @param Buffer The allocated pool entry to free
- @param PoolType Pointer to pool type
-
- @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
- @retval EFI_SUCCESS Pool successfully freed.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreInternalFreePool (
- IN VOID *Buffer,
- OUT EFI_MEMORY_TYPE *PoolType OPTIONAL
- )
-{
- EFI_STATUS Status;
-
- if (Buffer == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- CoreAcquireLock (&mPoolMemoryLock);
- Status = CoreFreePoolI (Buffer, PoolType);
- CoreReleaseLock (&mPoolMemoryLock);
- return Status;
-}
-
-/**
- Frees pool.
-
- @param Buffer The allocated pool entry to free
-
- @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
- @retval EFI_SUCCESS Pool successfully freed.
-
-**/
-EFI_STATUS
-EFIAPI
-CoreFreePool (
- IN VOID *Buffer
- )
-{
- EFI_STATUS Status;
- EFI_MEMORY_TYPE PoolType;
-
- Status = CoreInternalFreePool (Buffer, &PoolType);
- if (!EFI_ERROR (Status)) {
- CoreUpdateProfile (
- (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0),
- MemoryProfileActionFreePool,
- PoolType,
- 0,
- Buffer,
- NULL
- );
- InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
- }
-
- return Status;
-}
-
-/**
- Internal function. Frees pool pages allocated via CoreAllocatePoolPagesI().
-
- @param PoolType The type of memory for the pool pages
- @param Memory The base address to free
- @param NoPages The number of pages to free
-
-**/
-STATIC
-VOID
-CoreFreePoolPagesI (
- IN EFI_MEMORY_TYPE PoolType,
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NoPages
- )
-{
- CoreAcquireMemoryLock ();
- CoreFreePoolPages (Memory, NoPages);
- CoreReleaseMemoryLock ();
-
- GuardFreedPagesChecked (Memory, NoPages);
- ApplyMemoryProtectionPolicy (
- PoolType,
- EfiConventionalMemory,
- (EFI_PHYSICAL_ADDRESS)(UINTN)Memory,
- EFI_PAGES_TO_SIZE (NoPages)
- );
-}
-
-/**
- Internal function. Frees guarded pool pages.
-
- @param PoolType The type of memory for the pool pages
- @param Memory The base address to free
- @param NoPages The number of pages to free
-
-**/
-STATIC
-VOID
-CoreFreePoolPagesWithGuard (
- IN EFI_MEMORY_TYPE PoolType,
- IN EFI_PHYSICAL_ADDRESS Memory,
- IN UINTN NoPages
- )
-{
- EFI_PHYSICAL_ADDRESS MemoryGuarded;
- UINTN NoPagesGuarded;
-
- MemoryGuarded = Memory;
- NoPagesGuarded = NoPages;
-
- AdjustMemoryF (&Memory, &NoPages);
- //
- // It's safe to unset Guard page inside memory lock because there should
- // be no memory allocation occurred in updating memory page attribute at
- // this point. And unsetting Guard page before free will prevent Guard
- // page just freed back to pool from being allocated right away before
- // marking it usable (from non-present to present).
- //
- UnsetGuardForMemory (MemoryGuarded, NoPagesGuarded);
- if (NoPages > 0) {
- CoreFreePoolPagesI (PoolType, Memory, NoPages);
- }
-}
-
-/**
- Internal function to free a pool entry.
- Caller must have the memory lock held
-
- @param Buffer The allocated pool entry to free
- @param PoolType Pointer to pool type
-
- @retval EFI_INVALID_PARAMETER Buffer not valid
- @retval EFI_SUCCESS Buffer successfully freed.
-
-**/
-EFI_STATUS
-CoreFreePoolI (
- IN VOID *Buffer,
- OUT EFI_MEMORY_TYPE *PoolType OPTIONAL
- )
-{
- POOL *Pool;
- POOL_HEAD *Head;
- POOL_TAIL *Tail;
- POOL_FREE *Free;
- UINTN Index;
- UINTN NoPages;
- UINTN Size;
- CHAR8 *NewPage;
- UINTN Offset;
- BOOLEAN AllFree;
- UINTN Granularity;
- BOOLEAN IsGuarded;
- BOOLEAN HasPoolTail;
- BOOLEAN PageAsPool;
-
- ASSERT (Buffer != NULL);
- //
- // Get the head & tail of the pool entry
- //
- Head = BASE_CR (Buffer, POOL_HEAD, Data);
- ASSERT (Head != NULL);
-
- if ((Head->Signature != POOL_HEAD_SIGNATURE) &&
- (Head->Signature != POOLPAGE_HEAD_SIGNATURE))
- {
- ASSERT (
- Head->Signature == POOL_HEAD_SIGNATURE ||
- Head->Signature == POOLPAGE_HEAD_SIGNATURE
- );
- return EFI_INVALID_PARAMETER;
- }
-
- IsGuarded = IsPoolTypeToGuard (Head->Type) &&
- IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
- HasPoolTail = !(IsGuarded &&
- ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
- PageAsPool = (Head->Signature == POOLPAGE_HEAD_SIGNATURE);
-
- if (HasPoolTail) {
- Tail = HEAD_TO_TAIL (Head);
- ASSERT (Tail != NULL);
-
- //
- // Debug
- //
- ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
- ASSERT (Head->Size == Tail->Size);
-
- if (Tail->Signature != POOL_TAIL_SIGNATURE) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (Head->Size != Tail->Size) {
- return EFI_INVALID_PARAMETER;
- }
- }
-
- ASSERT_LOCKED (&mPoolMemoryLock);
-
- //
- // Determine the pool type and account for it
- //
- Size = Head->Size;
- Pool = LookupPoolHead (Head->Type);
- if (Pool == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- Pool->Used -= Size;
- DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64)Pool->Used));
-
- if ((Head->Type == EfiReservedMemoryType) ||
- (Head->Type == EfiACPIMemoryNVS) ||
- (Head->Type == EfiRuntimeServicesCode) ||
- (Head->Type == EfiRuntimeServicesData))
- {
- Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
- } else {
- Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
- }
-
- if (PoolType != NULL) {
- *PoolType = Head->Type;
- }
-
- //
- // Determine the pool list
- //
- Index = SIZE_TO_LIST (Size);
- DEBUG_CLEAR_MEMORY (Head, Size);
-
- //
- // If it's not on the list, it must be pool pages
- //
- if ((Index >= SIZE_TO_LIST (Granularity)) || IsGuarded || PageAsPool) {
- //
- // Return the memory pages back to free memory
- //
- NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
- NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
- if (IsGuarded) {
- Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size);
- CoreFreePoolPagesWithGuard (
- Pool->MemoryType,
- (EFI_PHYSICAL_ADDRESS)(UINTN)Head,
- NoPages
- );
- } else {
- CoreFreePoolPagesI (
- Pool->MemoryType,
- (EFI_PHYSICAL_ADDRESS)(UINTN)Head,
- NoPages
- );
- }
- } else {
- //
- // Put the pool entry onto the free pool list
- //
- Free = (POOL_FREE *)Head;
- ASSERT (Free != NULL);
- Free->Signature = POOL_FREE_SIGNATURE;
- Free->Index = (UINT32)Index;
- InsertHeadList (&Pool->FreeList[Index], &Free->Link);
-
- //
- // See if all the pool entries in the same page as Free are freed pool
- // entries
- //
- NewPage = (CHAR8 *)((UINTN)Free & ~(Granularity - 1));
- Free = (POOL_FREE *)&NewPage[0];
- ASSERT (Free != NULL);
-
- if (Free->Signature == POOL_FREE_SIGNATURE) {
- AllFree = TRUE;
- Offset = 0;
-
- while ((Offset < Granularity) && (AllFree)) {
- Free = (POOL_FREE *)&NewPage[Offset];
- ASSERT (Free != NULL);
- if (Free->Signature != POOL_FREE_SIGNATURE) {
- AllFree = FALSE;
- }
-
- Offset += LIST_TO_SIZE (Free->Index);
- }
-
- if (AllFree) {
- //
- // All of the pool entries in the same page as Free are free pool
- // entries
- // Remove all of these pool entries from the free loop lists.
- //
- Free = (POOL_FREE *)&NewPage[0];
- ASSERT (Free != NULL);
- Offset = 0;
-
- while (Offset < Granularity) {
- Free = (POOL_FREE *)&NewPage[Offset];
- ASSERT (Free != NULL);
- RemoveEntryList (&Free->Link);
- Offset += LIST_TO_SIZE (Free->Index);
- }
-
- //
- // Free the page
- //
- CoreFreePoolPagesI (
- Pool->MemoryType,
- (EFI_PHYSICAL_ADDRESS)(UINTN)NewPage,
- EFI_SIZE_TO_PAGES (Granularity)
- );
- }
- }
- }
-
- //
- // If this is an OS/OEM specific memory type, then check to see if the last
- // portion of that memory type has been freed. If it has, then free the
- // list entry for that memory type
- //
- if (((UINT32)Pool->MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) && (Pool->Used == 0)) {
- RemoveEntryList (&Pool->Link);
- CoreFreePoolI (Pool, NULL);
- }
-
- return EFI_SUCCESS;
-}
+/** @file + UEFI Memory pool management functions. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DxeMain.h" +#include "Imem.h" +#include "HeapGuard.h" + +STATIC EFI_LOCK mPoolMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY); + +#define POOL_FREE_SIGNATURE SIGNATURE_32('p','f','r','0') +typedef struct { + UINT32 Signature; + UINT32 Index; + LIST_ENTRY Link; +} POOL_FREE; + +#define POOL_HEAD_SIGNATURE SIGNATURE_32('p','h','d','0') +#define POOLPAGE_HEAD_SIGNATURE SIGNATURE_32('p','h','d','1') +typedef struct { + UINT32 Signature; + UINT32 Reserved; + EFI_MEMORY_TYPE Type; + UINTN Size; + CHAR8 Data[1]; +} POOL_HEAD; + +#define SIZE_OF_POOL_HEAD OFFSET_OF(POOL_HEAD,Data) + +#define POOL_TAIL_SIGNATURE SIGNATURE_32('p','t','a','l') +typedef struct { + UINT32 Signature; + UINT32 Reserved; + UINTN Size; +} POOL_TAIL; + +#define POOL_OVERHEAD (SIZE_OF_POOL_HEAD + sizeof(POOL_TAIL)) + +#define HEAD_TO_TAIL(a) \ + ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL))); + +// +// Each element is the sum of the 2 previous ones: this allows us to migrate +// blocks between bins by splitting them up, while not wasting too much memory +// as we would in a strict power-of-2 sequence +// +STATIC CONST UINT16 mPoolSizeTable[] = { + 128, 256, 384, 640, 1024, 1664, 2688, 4352, 7040, 11392, 18432, 29824 +}; + +#define SIZE_TO_LIST(a) (GetPoolIndexFromSize (a)) +#define LIST_TO_SIZE(a) (mPoolSizeTable [a]) + +#define MAX_POOL_LIST (ARRAY_SIZE (mPoolSizeTable)) + +#define MAX_POOL_SIZE (MAX_ADDRESS - POOL_OVERHEAD) + +// +// Globals +// + +#define POOL_SIGNATURE SIGNATURE_32('p','l','s','t') +typedef struct { + INTN Signature; + UINTN Used; + EFI_MEMORY_TYPE MemoryType; + LIST_ENTRY FreeList[MAX_POOL_LIST]; + LIST_ENTRY Link; +} POOL; + +// +// Pool header for each memory type. +// +POOL mPoolHead[EfiMaxMemoryType]; + +// +// List of pool header to search for the appropriate memory type. +// +LIST_ENTRY mPoolHeadList = INITIALIZE_LIST_HEAD_VARIABLE (mPoolHeadList); + +/** + Get pool size table index from the specified size. + + @param Size The specified size to get index from pool table. + + @return The index of pool size table. + +**/ +STATIC +UINTN +GetPoolIndexFromSize ( + UINTN Size + ) +{ + UINTN Index; + + for (Index = 0; Index < MAX_POOL_LIST; Index++) { + if (mPoolSizeTable[Index] >= Size) { + return Index; + } + } + + return MAX_POOL_LIST; +} + +/** + Called to initialize the pool. + +**/ +VOID +CoreInitializePool ( + VOID + ) +{ + UINTN Type; + UINTN Index; + + for (Type = 0; Type < EfiMaxMemoryType; Type++) { + mPoolHead[Type].Signature = 0; + mPoolHead[Type].Used = 0; + mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE)Type; + for (Index = 0; Index < MAX_POOL_LIST; Index++) { + InitializeListHead (&mPoolHead[Type].FreeList[Index]); + } + } +} + +/** + Look up pool head for specified memory type. + + @param MemoryType Memory type of which pool head is looked for + + @return Pointer of Corresponding pool head. + +**/ +POOL * +LookupPoolHead ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + LIST_ENTRY *Link; + POOL *Pool; + UINTN Index; + + if ((UINT32)MemoryType < EfiMaxMemoryType) { + return &mPoolHead[MemoryType]; + } + + // + // MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI + // OS loaders that are provided by operating system vendors. + // MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use. + // + if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) { + for (Link = mPoolHeadList.ForwardLink; Link != &mPoolHeadList; Link = Link->ForwardLink) { + Pool = CR (Link, POOL, Link, POOL_SIGNATURE); + if (Pool->MemoryType == MemoryType) { + return Pool; + } + } + + Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL), FALSE); + if (Pool == NULL) { + return NULL; + } + + Pool->Signature = POOL_SIGNATURE; + Pool->Used = 0; + Pool->MemoryType = MemoryType; + for (Index = 0; Index < MAX_POOL_LIST; Index++) { + InitializeListHead (&Pool->FreeList[Index]); + } + + InsertHeadList (&mPoolHeadList, &Pool->Link); + + return Pool; + } + + return NULL; +} + +/** + Allocate pool of a particular type. + + @param PoolType Type of pool to allocate + @param Size The amount of pool to allocate + @param Buffer The address to return a pointer to the allocated + pool + + @retval EFI_INVALID_PARAMETER Buffer is NULL. + PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF. + PoolType is EfiPersistentMemory. + @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. + @retval EFI_SUCCESS Pool successfully allocated. + +**/ +EFI_STATUS +EFIAPI +CoreInternalAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + BOOLEAN NeedGuard; + + // + // If it's not a valid type, fail it + // + if (((PoolType >= EfiMaxMemoryType) && (PoolType < MEMORY_TYPE_OEM_RESERVED_MIN)) || + (PoolType == EfiConventionalMemory) || (PoolType == EfiPersistentMemory) || (PoolType == EfiUnacceptedMemoryType)) + { + return EFI_INVALID_PARAMETER; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Buffer = NULL; + + // + // If size is too large, fail it + // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES + // + if (Size > MAX_POOL_SIZE) { + return EFI_OUT_OF_RESOURCES; + } + + NeedGuard = IsPoolTypeToGuard (PoolType) && !mOnGuarding; + + // + // Acquire the memory lock and make the allocation + // + Status = CoreAcquireLockOrFail (&mPoolMemoryLock); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + *Buffer = CoreAllocatePoolI (PoolType, Size, NeedGuard); + CoreReleaseLock (&mPoolMemoryLock); + return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; +} + +/** + Allocate pool of a particular type. + + @param PoolType Type of pool to allocate + @param Size The amount of pool to allocate + @param Buffer The address to return a pointer to the allocated + pool + + @retval EFI_INVALID_PARAMETER Buffer is NULL. + PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF. + PoolType is EfiPersistentMemory. + @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. + @retval EFI_SUCCESS Pool successfully allocated. + +**/ +EFI_STATUS +EFIAPI +CoreAllocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + + Status = CoreInternalAllocatePool (PoolType, Size, Buffer); + if (!EFI_ERROR (Status)) { + CoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), + MemoryProfileActionAllocatePool, + PoolType, + Size, + *Buffer, + NULL + ); + InstallMemoryAttributesTableOnMemoryAllocation (PoolType); + } + + return Status; +} + +/** + Internal function. Used by the pool functions to allocate pages + to back pool allocation requests. + + @param PoolType The type of memory for the new pool pages + @param NoPages No of pages to allocate + @param Granularity Bits to align. + @param NeedGuard Flag to indicate Guard page is needed or not + + @return The allocated memory, or NULL + +**/ +STATIC +VOID * +CoreAllocatePoolPagesI ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN NoPages, + IN UINTN Granularity, + IN BOOLEAN NeedGuard + ) +{ + VOID *Buffer; + EFI_STATUS Status; + + Status = CoreAcquireLockOrFail (&gMemoryLock); + if (EFI_ERROR (Status)) { + return NULL; + } + + Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity, NeedGuard); + CoreReleaseMemoryLock (); + + if (Buffer != NULL) { + if (NeedGuard) { + SetGuardForMemory ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, NoPages); + } + + ApplyMemoryProtectionPolicy ( + EfiConventionalMemory, + PoolType, + (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, + EFI_PAGES_TO_SIZE (NoPages) + ); + } + + return Buffer; +} + +/** + Internal function to allocate pool of a particular type. + Caller must have the memory lock held + + @param PoolType Type of pool to allocate + @param Size The amount of pool to allocate + @param NeedGuard Flag to indicate Guard page is needed or not + + @return The allocate pool, or NULL + +**/ +VOID * +CoreAllocatePoolI ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + IN BOOLEAN NeedGuard + ) +{ + POOL *Pool; + POOL_FREE *Free; + POOL_HEAD *Head; + POOL_TAIL *Tail; + CHAR8 *NewPage; + VOID *Buffer; + UINTN Index; + UINTN FSize; + UINTN Offset, MaxOffset; + UINTN NoPages; + UINTN Granularity; + BOOLEAN HasPoolTail; + BOOLEAN PageAsPool; + + ASSERT_LOCKED (&mPoolMemoryLock); + + if ((PoolType == EfiReservedMemoryType) || + (PoolType == EfiACPIMemoryNVS) || + (PoolType == EfiRuntimeServicesCode) || + (PoolType == EfiRuntimeServicesData)) + { + Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } else { + Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + } + + // + // The heap guard system does not support non-EFI_PAGE_SIZE alignments. + // Architectures that require larger RUNTIME_PAGE_ALLOCATION_GRANULARITY + // will have the runtime memory regions unguarded. OSes do not + // map guard pages anyway, so this is a minimal loss. Not guarding prevents + // alignment mismatches + // + if (Granularity != EFI_PAGE_SIZE) { + NeedGuard = FALSE; + } + + // + // Adjust the size by the pool header & tail overhead + // + + HasPoolTail = !(NeedGuard && + ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0)); + PageAsPool = (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) && !mOnGuarding); + + // + // Adjusting the Size to be of proper alignment so that + // we don't get an unaligned access fault later when + // pool_Tail is being initialized + // + Size = ALIGN_VARIABLE (Size); + + Size += POOL_OVERHEAD; + Index = SIZE_TO_LIST (Size); + Pool = LookupPoolHead (PoolType); + if (Pool == NULL) { + return NULL; + } + + Head = NULL; + + // + // If allocation is over max size, just allocate pages for the request + // (slow) + // + if ((Index >= SIZE_TO_LIST (Granularity)) || NeedGuard || PageAsPool) { + if (!HasPoolTail) { + Size -= sizeof (POOL_TAIL); + } + + NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1; + NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1); + Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity, NeedGuard); + if (NeedGuard) { + Head = AdjustPoolHeadA ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size); + } + + goto Done; + } + + // + // If there's no free pool in the proper list size, go get some more pages + // + if (IsListEmpty (&Pool->FreeList[Index])) { + Offset = LIST_TO_SIZE (Index); + MaxOffset = Granularity; + + // + // Check the bins holding larger blocks, and carve one up if needed + // + while (++Index < SIZE_TO_LIST (Granularity)) { + if (!IsListEmpty (&Pool->FreeList[Index])) { + Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE); + RemoveEntryList (&Free->Link); + NewPage = (VOID *)Free; + MaxOffset = LIST_TO_SIZE (Index); + goto Carve; + } + } + + // + // Get another page + // + NewPage = CoreAllocatePoolPagesI ( + PoolType, + EFI_SIZE_TO_PAGES (Granularity), + Granularity, + NeedGuard + ); + if (NewPage == NULL) { + goto Done; + } + + // + // Serve the allocation request from the head of the allocated block + // +Carve: + Head = (POOL_HEAD *)NewPage; + + // + // Carve up remaining space into free pool blocks + // + Index--; + while (Offset < MaxOffset) { + ASSERT (Index < MAX_POOL_LIST); + FSize = LIST_TO_SIZE (Index); + + while (Offset + FSize <= MaxOffset) { + Free = (POOL_FREE *)&NewPage[Offset]; + Free->Signature = POOL_FREE_SIGNATURE; + Free->Index = (UINT32)Index; + InsertHeadList (&Pool->FreeList[Index], &Free->Link); + Offset += FSize; + } + + Index -= 1; + } + + ASSERT (Offset == MaxOffset); + goto Done; + } + + // + // Remove entry from free pool list + // + Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE); + RemoveEntryList (&Free->Link); + + Head = (POOL_HEAD *)Free; + +Done: + Buffer = NULL; + + if (Head != NULL) { + // + // Account the allocation + // + Pool->Used += Size; + + // + // If we have a pool buffer, fill in the header & tail info + // + Head->Signature = (PageAsPool) ? POOLPAGE_HEAD_SIGNATURE : POOL_HEAD_SIGNATURE; + Head->Size = Size; + Head->Type = (EFI_MEMORY_TYPE)PoolType; + Buffer = Head->Data; + + if (HasPoolTail) { + Tail = HEAD_TO_TAIL (Head); + Tail->Signature = POOL_TAIL_SIGNATURE; + Tail->Size = Size; + + Size -= POOL_OVERHEAD; + } else { + Size -= SIZE_OF_POOL_HEAD; + } + + DEBUG_CLEAR_MEMORY (Buffer, Size); + + DEBUG (( + DEBUG_POOL, + "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", + PoolType, + Buffer, + (UINT64)Size, + (UINT64)Pool->Used + )); + } else { + DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64)Size)); + } + + return Buffer; +} + +/** + Frees pool. + + @param Buffer The allocated pool entry to free + @param PoolType Pointer to pool type + + @retval EFI_INVALID_PARAMETER Buffer is not a valid value. + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +EFIAPI +CoreInternalFreePool ( + IN VOID *Buffer, + OUT EFI_MEMORY_TYPE *PoolType OPTIONAL + ) +{ + EFI_STATUS Status; + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + CoreAcquireLock (&mPoolMemoryLock); + Status = CoreFreePoolI (Buffer, PoolType); + CoreReleaseLock (&mPoolMemoryLock); + return Status; +} + +/** + Frees pool. + + @param Buffer The allocated pool entry to free + + @retval EFI_INVALID_PARAMETER Buffer is not a valid value. + @retval EFI_SUCCESS Pool successfully freed. + +**/ +EFI_STATUS +EFIAPI +CoreFreePool ( + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + EFI_MEMORY_TYPE PoolType; + + Status = CoreInternalFreePool (Buffer, &PoolType); + if (!EFI_ERROR (Status)) { + CoreUpdateProfile ( + (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), + MemoryProfileActionFreePool, + PoolType, + 0, + Buffer, + NULL + ); + InstallMemoryAttributesTableOnMemoryAllocation (PoolType); + } + + return Status; +} + +/** + Internal function. Frees pool pages allocated via CoreAllocatePoolPagesI(). + + @param PoolType The type of memory for the pool pages + @param Memory The base address to free + @param NoPages The number of pages to free + +**/ +STATIC +VOID +CoreFreePoolPagesI ( + IN EFI_MEMORY_TYPE PoolType, + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages + ) +{ + CoreAcquireMemoryLock (); + CoreFreePoolPages (Memory, NoPages); + CoreReleaseMemoryLock (); + + GuardFreedPagesChecked (Memory, NoPages); + ApplyMemoryProtectionPolicy ( + PoolType, + EfiConventionalMemory, + (EFI_PHYSICAL_ADDRESS)(UINTN)Memory, + EFI_PAGES_TO_SIZE (NoPages) + ); +} + +/** + Internal function. Frees guarded pool pages. + + @param PoolType The type of memory for the pool pages + @param Memory The base address to free + @param NoPages The number of pages to free + +**/ +STATIC +VOID +CoreFreePoolPagesWithGuard ( + IN EFI_MEMORY_TYPE PoolType, + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NoPages + ) +{ + EFI_PHYSICAL_ADDRESS MemoryGuarded; + UINTN NoPagesGuarded; + + MemoryGuarded = Memory; + NoPagesGuarded = NoPages; + + AdjustMemoryF (&Memory, &NoPages); + // + // It's safe to unset Guard page inside memory lock because there should + // be no memory allocation occurred in updating memory page attribute at + // this point. And unsetting Guard page before free will prevent Guard + // page just freed back to pool from being allocated right away before + // marking it usable (from non-present to present). + // + UnsetGuardForMemory (MemoryGuarded, NoPagesGuarded); + if (NoPages > 0) { + CoreFreePoolPagesI (PoolType, Memory, NoPages); + } +} + +/** + Internal function to free a pool entry. + Caller must have the memory lock held + + @param Buffer The allocated pool entry to free + @param PoolType Pointer to pool type + + @retval EFI_INVALID_PARAMETER Buffer not valid + @retval EFI_SUCCESS Buffer successfully freed. + +**/ +EFI_STATUS +CoreFreePoolI ( + IN VOID *Buffer, + OUT EFI_MEMORY_TYPE *PoolType OPTIONAL + ) +{ + POOL *Pool; + POOL_HEAD *Head; + POOL_TAIL *Tail; + POOL_FREE *Free; + UINTN Index; + UINTN NoPages; + UINTN Size; + CHAR8 *NewPage; + UINTN Offset; + BOOLEAN AllFree; + UINTN Granularity; + BOOLEAN IsGuarded; + BOOLEAN HasPoolTail; + BOOLEAN PageAsPool; + + ASSERT (Buffer != NULL); + // + // Get the head & tail of the pool entry + // + Head = BASE_CR (Buffer, POOL_HEAD, Data); + ASSERT (Head != NULL); + + if ((Head->Signature != POOL_HEAD_SIGNATURE) && + (Head->Signature != POOLPAGE_HEAD_SIGNATURE)) + { + ASSERT ( + Head->Signature == POOL_HEAD_SIGNATURE || + Head->Signature == POOLPAGE_HEAD_SIGNATURE + ); + return EFI_INVALID_PARAMETER; + } + + IsGuarded = IsPoolTypeToGuard (Head->Type) && + IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Head); + HasPoolTail = !(IsGuarded && + ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0)); + PageAsPool = (Head->Signature == POOLPAGE_HEAD_SIGNATURE); + + if (HasPoolTail) { + Tail = HEAD_TO_TAIL (Head); + ASSERT (Tail != NULL); + + // + // Debug + // + ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE); + ASSERT (Head->Size == Tail->Size); + + if (Tail->Signature != POOL_TAIL_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + if (Head->Size != Tail->Size) { + return EFI_INVALID_PARAMETER; + } + } + + ASSERT_LOCKED (&mPoolMemoryLock); + + // + // Determine the pool type and account for it + // + Size = Head->Size; + Pool = LookupPoolHead (Head->Type); + if (Pool == NULL) { + return EFI_INVALID_PARAMETER; + } + + Pool->Used -= Size; + DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64)Pool->Used)); + + if ((Head->Type == EfiReservedMemoryType) || + (Head->Type == EfiACPIMemoryNVS) || + (Head->Type == EfiRuntimeServicesCode) || + (Head->Type == EfiRuntimeServicesData)) + { + Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } else { + Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + } + + if (PoolType != NULL) { + *PoolType = Head->Type; + } + + // + // Determine the pool list + // + Index = SIZE_TO_LIST (Size); + DEBUG_CLEAR_MEMORY (Head, Size); + + // + // If it's not on the list, it must be pool pages + // + if ((Index >= SIZE_TO_LIST (Granularity)) || IsGuarded || PageAsPool) { + // + // Return the memory pages back to free memory + // + NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1; + NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1); + if (IsGuarded) { + Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size); + CoreFreePoolPagesWithGuard ( + Pool->MemoryType, + (EFI_PHYSICAL_ADDRESS)(UINTN)Head, + NoPages + ); + } else { + CoreFreePoolPagesI ( + Pool->MemoryType, + (EFI_PHYSICAL_ADDRESS)(UINTN)Head, + NoPages + ); + } + } else { + // + // Put the pool entry onto the free pool list + // + Free = (POOL_FREE *)Head; + ASSERT (Free != NULL); + Free->Signature = POOL_FREE_SIGNATURE; + Free->Index = (UINT32)Index; + InsertHeadList (&Pool->FreeList[Index], &Free->Link); + + // + // See if all the pool entries in the same page as Free are freed pool + // entries + // + NewPage = (CHAR8 *)((UINTN)Free & ~(Granularity - 1)); + Free = (POOL_FREE *)&NewPage[0]; + ASSERT (Free != NULL); + + if (Free->Signature == POOL_FREE_SIGNATURE) { + AllFree = TRUE; + Offset = 0; + + while ((Offset < Granularity) && (AllFree)) { + Free = (POOL_FREE *)&NewPage[Offset]; + ASSERT (Free != NULL); + if (Free->Signature != POOL_FREE_SIGNATURE) { + AllFree = FALSE; + } + + Offset += LIST_TO_SIZE (Free->Index); + } + + if (AllFree) { + // + // All of the pool entries in the same page as Free are free pool + // entries + // Remove all of these pool entries from the free loop lists. + // + Free = (POOL_FREE *)&NewPage[0]; + ASSERT (Free != NULL); + Offset = 0; + + while (Offset < Granularity) { + Free = (POOL_FREE *)&NewPage[Offset]; + ASSERT (Free != NULL); + RemoveEntryList (&Free->Link); + Offset += LIST_TO_SIZE (Free->Index); + } + + // + // Free the page + // + CoreFreePoolPagesI ( + Pool->MemoryType, + (EFI_PHYSICAL_ADDRESS)(UINTN)NewPage, + EFI_SIZE_TO_PAGES (Granularity) + ); + } + } + } + + // + // If this is an OS/OEM specific memory type, then check to see if the last + // portion of that memory type has been freed. If it has, then free the + // list entry for that memory type + // + if (((UINT32)Pool->MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) && (Pool->Used == 0)) { + RemoveEntryList (&Pool->Link); + CoreFreePoolI (Pool, NULL); + } + + return EFI_SUCCESS; +} |
