Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Star Zeng <star.zeng@intel.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16335 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			385 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			385 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  SMM Memory page management functions.
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
 | 
						|
  This program and the accompanying materials are licensed and made available 
 | 
						|
  under the terms and conditions of the BSD License which accompanies this 
 | 
						|
  distribution.  The full text of the license may be found at        
 | 
						|
  http://opensource.org/licenses/bsd-license.php                                            
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
 | 
						|
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "PiSmmCore.h"
 | 
						|
 | 
						|
#define TRUNCATE_TO_PAGES(a)  ((a) >> EFI_PAGE_SHIFT)
 | 
						|
 | 
						|
LIST_ENTRY  mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
 | 
						|
 | 
						|
/**
 | 
						|
  Internal Function. Allocate n pages from given free page node.
 | 
						|
 | 
						|
  @param  Pages                  The free page node.
 | 
						|
  @param  NumberOfPages          Number of pages to be allocated.
 | 
						|
  @param  MaxAddress             Request to allocate memory below this address.
 | 
						|
 | 
						|
  @return Memory address of allocated pages.
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
InternalAllocPagesOnOneNode (
 | 
						|
  IN OUT FREE_PAGE_LIST  *Pages,
 | 
						|
  IN     UINTN           NumberOfPages,
 | 
						|
  IN     UINTN           MaxAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN           Top;
 | 
						|
  UINTN           Bottom;
 | 
						|
  FREE_PAGE_LIST  *Node;
 | 
						|
 | 
						|
  Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
 | 
						|
  if (Top > Pages->NumberOfPages) {
 | 
						|
    Top = Pages->NumberOfPages;
 | 
						|
  }
 | 
						|
  Bottom = Top - NumberOfPages;
 | 
						|
 | 
						|
  if (Top < Pages->NumberOfPages) {
 | 
						|
    Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
 | 
						|
    Node->NumberOfPages = Pages->NumberOfPages - Top;
 | 
						|
    InsertHeadList (&Pages->Link, &Node->Link);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Bottom > 0) {
 | 
						|
    Pages->NumberOfPages = Bottom;
 | 
						|
  } else {
 | 
						|
    RemoveEntryList (&Pages->Link);
 | 
						|
  }
 | 
						|
 | 
						|
  return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal Function. Allocate n pages from free page list below MaxAddress.
 | 
						|
 | 
						|
  @param  FreePageList           The free page node.
 | 
						|
  @param  NumberOfPages          Number of pages to be allocated.
 | 
						|
  @param  MaxAddress             Request to allocate memory below this address.
 | 
						|
 | 
						|
  @return Memory address of allocated pages.
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
InternalAllocMaxAddress (
 | 
						|
  IN OUT LIST_ENTRY  *FreePageList,
 | 
						|
  IN     UINTN       NumberOfPages,
 | 
						|
  IN     UINTN       MaxAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY      *Node;
 | 
						|
  FREE_PAGE_LIST  *Pages;
 | 
						|
 | 
						|
  for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
 | 
						|
    Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
 | 
						|
    if (Pages->NumberOfPages >= NumberOfPages &&
 | 
						|
        (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
 | 
						|
      return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return (UINTN)(-1);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal Function. Allocate n pages from free page list at given address.
 | 
						|
 | 
						|
  @param  FreePageList           The free page node.
 | 
						|
  @param  NumberOfPages          Number of pages to be allocated.
 | 
						|
  @param  MaxAddress             Request to allocate memory below this address.
 | 
						|
 | 
						|
  @return Memory address of allocated pages.
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
InternalAllocAddress (
 | 
						|
  IN OUT LIST_ENTRY  *FreePageList,
 | 
						|
  IN     UINTN       NumberOfPages,
 | 
						|
  IN     UINTN       Address
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN           EndAddress;
 | 
						|
  LIST_ENTRY      *Node;
 | 
						|
  FREE_PAGE_LIST  *Pages;
 | 
						|
 | 
						|
  if ((Address & EFI_PAGE_MASK) != 0) {
 | 
						|
    return ~Address;
 | 
						|
  }
 | 
						|
 | 
						|
  EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
 | 
						|
  for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
 | 
						|
    Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
 | 
						|
    if ((UINTN)Pages <= Address) {
 | 
						|
      if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ~Address;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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.
 | 
						|
 | 
						|
  @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
 | 
						|
SmmInternalAllocatePages (
 | 
						|
  IN  EFI_ALLOCATE_TYPE     Type,
 | 
						|
  IN  EFI_MEMORY_TYPE       MemoryType,
 | 
						|
  IN  UINTN                 NumberOfPages,
 | 
						|
  OUT EFI_PHYSICAL_ADDRESS  *Memory
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN  RequestedAddress;
 | 
						|
 | 
						|
  if (MemoryType != EfiRuntimeServicesCode &&
 | 
						|
      MemoryType != EfiRuntimeServicesData) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // We don't track memory type in SMM
 | 
						|
  //
 | 
						|
  RequestedAddress = (UINTN)*Memory;
 | 
						|
  switch (Type) {
 | 
						|
    case AllocateAnyPages:
 | 
						|
      RequestedAddress = (UINTN)(-1);
 | 
						|
    case AllocateMaxAddress:
 | 
						|
      *Memory = InternalAllocMaxAddress (
 | 
						|
                  &mSmmMemoryMap,
 | 
						|
                  NumberOfPages,
 | 
						|
                  RequestedAddress
 | 
						|
                  );
 | 
						|
      if (*Memory == (UINTN)-1) {
 | 
						|
        return EFI_OUT_OF_RESOURCES;
 | 
						|
      } 
 | 
						|
      break;
 | 
						|
    case AllocateAddress:
 | 
						|
      *Memory = InternalAllocAddress (
 | 
						|
                  &mSmmMemoryMap,
 | 
						|
                  NumberOfPages,
 | 
						|
                  RequestedAddress
 | 
						|
                  );
 | 
						|
      if (*Memory != RequestedAddress) {
 | 
						|
        return EFI_NOT_FOUND;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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.
 | 
						|
 | 
						|
  @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
 | 
						|
SmmAllocatePages (
 | 
						|
  IN  EFI_ALLOCATE_TYPE     Type,
 | 
						|
  IN  EFI_MEMORY_TYPE       MemoryType,
 | 
						|
  IN  UINTN                 NumberOfPages,
 | 
						|
  OUT EFI_PHYSICAL_ADDRESS  *Memory
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionAllocatePages, MemoryType, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) *Memory);
 | 
						|
  }
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal Function. Merge two adjacent nodes.
 | 
						|
 | 
						|
  @param  First             The first of two nodes to merge.
 | 
						|
 | 
						|
  @return Pointer to node after merge (if success) or pointer to next node (if fail).
 | 
						|
 | 
						|
**/
 | 
						|
FREE_PAGE_LIST *
 | 
						|
InternalMergeNodes (
 | 
						|
  IN FREE_PAGE_LIST  *First
 | 
						|
  )
 | 
						|
{
 | 
						|
  FREE_PAGE_LIST  *Next;
 | 
						|
 | 
						|
  Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
 | 
						|
  ASSERT (
 | 
						|
    TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
 | 
						|
 | 
						|
  if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
 | 
						|
    First->NumberOfPages += Next->NumberOfPages;
 | 
						|
    RemoveEntryList (&Next->Link);
 | 
						|
    Next = First;
 | 
						|
  }
 | 
						|
  return Next;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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
 | 
						|
SmmInternalFreePages (
 | 
						|
  IN EFI_PHYSICAL_ADDRESS  Memory,
 | 
						|
  IN UINTN                 NumberOfPages
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY      *Node;
 | 
						|
  FREE_PAGE_LIST  *Pages;
 | 
						|
 | 
						|
  if ((Memory & EFI_PAGE_MASK) != 0) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  Pages = NULL;
 | 
						|
  Node = mSmmMemoryMap.ForwardLink;
 | 
						|
  while (Node != &mSmmMemoryMap) {
 | 
						|
    Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
 | 
						|
    if (Memory < (UINTN)Pages) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    Node = Node->ForwardLink;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Node != &mSmmMemoryMap &&
 | 
						|
      Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Node->BackLink != &mSmmMemoryMap) {
 | 
						|
    Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
 | 
						|
    if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
 | 
						|
  Pages->NumberOfPages = NumberOfPages;
 | 
						|
  InsertTailList (Node, &Pages->Link);
 | 
						|
 | 
						|
  if (Pages->Link.BackLink != &mSmmMemoryMap) {
 | 
						|
    Pages = InternalMergeNodes (
 | 
						|
              BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
 | 
						|
              );
 | 
						|
  }
 | 
						|
 | 
						|
  if (Node != &mSmmMemoryMap) {
 | 
						|
    InternalMergeNodes (Pages);
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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
 | 
						|
SmmFreePages (
 | 
						|
  IN EFI_PHYSICAL_ADDRESS  Memory,
 | 
						|
  IN UINTN                 NumberOfPages
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = SmmInternalFreePages (Memory, NumberOfPages);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionFreePages, 0, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) Memory);
 | 
						|
  }
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Add free SMRAM region for use by memory service.
 | 
						|
 | 
						|
  @param  MemBase                Base address of memory region.
 | 
						|
  @param  MemLength              Length of the memory region.
 | 
						|
  @param  Type                   Memory type.
 | 
						|
  @param  Attributes             Memory region state.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SmmAddMemoryRegion (
 | 
						|
  IN  EFI_PHYSICAL_ADDRESS  MemBase,
 | 
						|
  IN  UINT64                MemLength,
 | 
						|
  IN  EFI_MEMORY_TYPE       Type,
 | 
						|
  IN  UINT64                Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN  AlignedMemBase;
 | 
						|
 | 
						|
  //
 | 
						|
  // Do not add memory regions that is already allocated, needs testing, or needs ECC initialization
 | 
						|
  //
 | 
						|
  if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  //
 | 
						|
  // Align range on an EFI_PAGE_SIZE boundary
 | 
						|
  //  
 | 
						|
  AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
 | 
						|
  MemLength -= AlignedMemBase - MemBase;
 | 
						|
  SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));
 | 
						|
}
 |