Now that ArmSetMemoryAttributes() permits a mask to be provided, we can simplify the implementation the UEFI memory attribute protocol substantially, and just pass on the requested mask to be set or cleared directly. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Oliver Smith-Denny <osde@linux.microsoft.com> Reviewed-by: Michael Kubacki <michael.kubacki@microsoft.com>
		
			
				
	
	
		
			274 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
 | 
						|
  Copyright (c) 2023, Google LLC. All rights reserved.
 | 
						|
 | 
						|
  SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "CpuDxe.h"
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether the provided memory range is covered by a single entry of type
 | 
						|
  EfiGcdSystemMemory in the GCD memory map.
 | 
						|
 | 
						|
  @param  BaseAddress       The physical address that is the start address of
 | 
						|
                            a memory region.
 | 
						|
  @param  Length            The size in bytes of the memory region.
 | 
						|
 | 
						|
  @return Whether the region is system memory or not.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
BOOLEAN
 | 
						|
RegionIsSystemMemory (
 | 
						|
  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
 | 
						|
  IN  UINT64                Length
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  GcdDescriptor;
 | 
						|
  EFI_PHYSICAL_ADDRESS             GcdEndAddress;
 | 
						|
  EFI_STATUS                       Status;
 | 
						|
 | 
						|
  Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor);
 | 
						|
  if (EFI_ERROR (Status) ||
 | 
						|
      (GcdDescriptor.GcdMemoryType != EfiGcdMemoryTypeSystemMemory))
 | 
						|
  {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  GcdEndAddress = GcdDescriptor.BaseAddress + GcdDescriptor.Length;
 | 
						|
 | 
						|
  //
 | 
						|
  // Return TRUE if the GCD descriptor covers the range entirely
 | 
						|
  //
 | 
						|
  return GcdEndAddress >= (BaseAddress + Length);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function retrieves the attributes of the memory region specified by
 | 
						|
  BaseAddress and Length. If different attributes are obtained from different
 | 
						|
  parts of the memory region, EFI_NO_MAPPING will be returned.
 | 
						|
 | 
						|
  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
 | 
						|
  @param  BaseAddress       The physical address that is the start address of
 | 
						|
                            a memory region.
 | 
						|
  @param  Length            The size in bytes of the memory region.
 | 
						|
  @param  Attributes        Pointer to attributes returned.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The attributes got for the memory region.
 | 
						|
  @retval EFI_INVALID_PARAMETER Length is zero.
 | 
						|
                                Attributes is NULL.
 | 
						|
  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory
 | 
						|
                                region.
 | 
						|
  @retval EFI_UNSUPPORTED       The processor does not support one or more
 | 
						|
                                bytes of the memory resource range specified
 | 
						|
                                by BaseAddress and Length.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
GetMemoryAttributes (
 | 
						|
  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
 | 
						|
  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
 | 
						|
  IN  UINT64                         Length,
 | 
						|
  OUT UINT64                         *Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN       RegionAddress;
 | 
						|
  UINTN       RegionLength;
 | 
						|
  UINTN       RegionAttributes;
 | 
						|
  UINTN       Union;
 | 
						|
  UINTN       Intersection;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  if ((Length == 0) || (Attributes == NULL)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!RegionIsSystemMemory (BaseAddress, Length)) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((
 | 
						|
    DEBUG_VERBOSE,
 | 
						|
    "%a: BaseAddress == 0x%lx, Length == 0x%lx\n",
 | 
						|
    __func__,
 | 
						|
    BaseAddress,
 | 
						|
    Length
 | 
						|
    ));
 | 
						|
 | 
						|
  Union        = 0;
 | 
						|
  Intersection = MAX_UINTN;
 | 
						|
 | 
						|
  for (RegionAddress = (UINTN)BaseAddress;
 | 
						|
       RegionAddress < (UINTN)(BaseAddress + Length);
 | 
						|
       RegionAddress += RegionLength)
 | 
						|
  {
 | 
						|
    Status = GetMemoryRegion (
 | 
						|
               &RegionAddress,
 | 
						|
               &RegionLength,
 | 
						|
               &RegionAttributes
 | 
						|
               );
 | 
						|
 | 
						|
    DEBUG ((
 | 
						|
      DEBUG_VERBOSE,
 | 
						|
      "%a: RegionAddress == 0x%lx, RegionLength == 0x%lx, RegionAttributes == 0x%lx\n",
 | 
						|
      __func__,
 | 
						|
      (UINT64)RegionAddress,
 | 
						|
      (UINT64)RegionLength,
 | 
						|
      (UINT64)RegionAttributes
 | 
						|
      ));
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return EFI_NO_MAPPING;
 | 
						|
    }
 | 
						|
 | 
						|
    Union        |= RegionAttributes;
 | 
						|
    Intersection &= RegionAttributes;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((
 | 
						|
    DEBUG_VERBOSE,
 | 
						|
    "%a: Union == %lx, Intersection == %lx\n",
 | 
						|
    __func__,
 | 
						|
    (UINT64)Union,
 | 
						|
    (UINT64)Intersection
 | 
						|
    ));
 | 
						|
 | 
						|
  if (Union != Intersection) {
 | 
						|
    return EFI_NO_MAPPING;
 | 
						|
  }
 | 
						|
 | 
						|
  *Attributes  = RegionAttributeToGcdAttribute (Union);
 | 
						|
  *Attributes &= EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function set given attributes of the memory region specified by
 | 
						|
  BaseAddress and Length.
 | 
						|
 | 
						|
  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
 | 
						|
 | 
						|
  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
 | 
						|
  @param  BaseAddress       The physical address that is the start address of
 | 
						|
                            a memory region.
 | 
						|
  @param  Length            The size in bytes of the memory region.
 | 
						|
  @param  Attributes        The bit mask of attributes to set for the memory
 | 
						|
                            region.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The attributes were set for the memory region.
 | 
						|
  @retval EFI_INVALID_PARAMETER Length is zero.
 | 
						|
                                Attributes specified an illegal combination of
 | 
						|
                                attributes that cannot be set together.
 | 
						|
  @retval EFI_UNSUPPORTED       The processor does not support one or more
 | 
						|
                                bytes of the memory resource range specified
 | 
						|
                                by BaseAddress and Length.
 | 
						|
                                The bit mask of attributes is not supported for
 | 
						|
                                the memory resource range specified by
 | 
						|
                                BaseAddress and Length.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
 | 
						|
                                lack of system resources.
 | 
						|
  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
 | 
						|
                                controlled by system firmware and cannot be
 | 
						|
                                updated via the protocol.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
SetMemoryAttributes (
 | 
						|
  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
 | 
						|
  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
 | 
						|
  IN  UINT64                         Length,
 | 
						|
  IN  UINT64                         Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  DEBUG ((
 | 
						|
    DEBUG_INFO,
 | 
						|
    "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
 | 
						|
    __func__,
 | 
						|
    (UINTN)BaseAddress,
 | 
						|
    (UINTN)Length,
 | 
						|
    (UINTN)Attributes
 | 
						|
    ));
 | 
						|
 | 
						|
  if ((Length == 0) ||
 | 
						|
      ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
 | 
						|
  {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!RegionIsSystemMemory (BaseAddress, Length)) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  return ArmSetMemoryAttributes (BaseAddress, Length, Attributes, Attributes);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function clears given attributes of the memory region specified by
 | 
						|
  BaseAddress and Length.
 | 
						|
 | 
						|
  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
 | 
						|
 | 
						|
  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
 | 
						|
  @param  BaseAddress       The physical address that is the start address of
 | 
						|
                            a memory region.
 | 
						|
  @param  Length            The size in bytes of the memory region.
 | 
						|
  @param  Attributes        The bit mask of attributes to clear for the memory
 | 
						|
                            region.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The attributes were cleared for the memory region.
 | 
						|
  @retval EFI_INVALID_PARAMETER Length is zero.
 | 
						|
                                Attributes specified an illegal combination of
 | 
						|
                                attributes that cannot be cleared together.
 | 
						|
  @retval EFI_UNSUPPORTED       The processor does not support one or more
 | 
						|
                                bytes of the memory resource range specified
 | 
						|
                                by BaseAddress and Length.
 | 
						|
                                The bit mask of attributes is not supported for
 | 
						|
                                the memory resource range specified by
 | 
						|
                                BaseAddress and Length.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
 | 
						|
                                lack of system resources.
 | 
						|
  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
 | 
						|
                                controlled by system firmware and cannot be
 | 
						|
                                updated via the protocol.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
ClearMemoryAttributes (
 | 
						|
  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
 | 
						|
  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
 | 
						|
  IN  UINT64                         Length,
 | 
						|
  IN  UINT64                         Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  DEBUG ((
 | 
						|
    DEBUG_INFO,
 | 
						|
    "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
 | 
						|
    __func__,
 | 
						|
    (UINTN)BaseAddress,
 | 
						|
    (UINTN)Length,
 | 
						|
    (UINTN)Attributes
 | 
						|
    ));
 | 
						|
 | 
						|
  if ((Length == 0) ||
 | 
						|
      ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
 | 
						|
  {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!RegionIsSystemMemory (BaseAddress, Length)) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  return ArmSetMemoryAttributes (BaseAddress, Length, 0, Attributes);
 | 
						|
}
 | 
						|
 | 
						|
EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute = {
 | 
						|
  GetMemoryAttributes,
 | 
						|
  SetMemoryAttributes,
 | 
						|
  ClearMemoryAttributes
 | 
						|
};
 |