mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-10-31 13:02:07 +00:00 
			
		
		
		
	 2e969d2e9e
			
		
	
	
		2e969d2e9e
		
	
	
	
	
		
			
			Changing the attribute implies some cache management (clean & invalidate). Preventing the cache management should improve the performance. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14568 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			283 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| *
 | |
| *  Copyright (c) 2013, ARM Limited. All rights reserved.
 | |
| *
 | |
| *  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 "CpuDxe.h"
 | |
| 
 | |
| /**
 | |
|   Searches memory descriptors covered by given memory range.
 | |
| 
 | |
|   This function searches into the Gcd Memory Space for descriptors
 | |
|   (from StartIndex to EndIndex) that contains the memory range
 | |
|   specified by BaseAddress and Length.
 | |
| 
 | |
|   @param  MemorySpaceMap       Gcd Memory Space Map as array.
 | |
|   @param  NumberOfDescriptors  Number of descriptors in map.
 | |
|   @param  BaseAddress          BaseAddress for the requested range.
 | |
|   @param  Length               Length for the requested range.
 | |
|   @param  StartIndex           Start index into the Gcd Memory Space Map.
 | |
|   @param  EndIndex             End index into the Gcd Memory Space Map.
 | |
| 
 | |
|   @retval EFI_SUCCESS          Search successfully.
 | |
|   @retval EFI_NOT_FOUND        The requested descriptors does not exist.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| SearchGcdMemorySpaces (
 | |
|   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap,
 | |
|   IN UINTN                               NumberOfDescriptors,
 | |
|   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
 | |
|   IN UINT64                              Length,
 | |
|   OUT UINTN                             *StartIndex,
 | |
|   OUT UINTN                             *EndIndex
 | |
|   )
 | |
| {
 | |
|   UINTN           Index;
 | |
| 
 | |
|   *StartIndex = 0;
 | |
|   *EndIndex   = 0;
 | |
|   for (Index = 0; Index < NumberOfDescriptors; Index++) {
 | |
|     if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) &&
 | |
|         (BaseAddress < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
 | |
|       *StartIndex = Index;
 | |
|     }
 | |
|     if (((BaseAddress + Length - 1) >= MemorySpaceMap[Index].BaseAddress) &&
 | |
|         ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
 | |
|       *EndIndex = Index;
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
|   }
 | |
|   return EFI_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Sets the attributes for a specified range in Gcd Memory Space Map.
 | |
| 
 | |
|   This function sets the attributes for a specified range in
 | |
|   Gcd Memory Space Map.
 | |
| 
 | |
|   @param  MemorySpaceMap       Gcd Memory Space Map as array
 | |
|   @param  NumberOfDescriptors  Number of descriptors in map
 | |
|   @param  BaseAddress          BaseAddress for the range
 | |
|   @param  Length               Length for the range
 | |
|   @param  Attributes           Attributes to set
 | |
| 
 | |
|   @retval EFI_SUCCESS          Memory attributes set successfully
 | |
|   @retval EFI_NOT_FOUND        The specified range does not exist in Gcd Memory Space
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| SetGcdMemorySpaceAttributes (
 | |
|   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap,
 | |
|   IN UINTN                               NumberOfDescriptors,
 | |
|   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
 | |
|   IN UINT64                              Length,
 | |
|   IN UINT64                              Attributes
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   UINTN                 Index;
 | |
|   UINTN                 StartIndex;
 | |
|   UINTN                 EndIndex;
 | |
|   EFI_PHYSICAL_ADDRESS  RegionStart;
 | |
|   UINT64                RegionLength;
 | |
| 
 | |
|   DEBUG ((DEBUG_GCD, "SetGcdMemorySpaceAttributes[0x%lX; 0x%lX] = 0x%lX\n",
 | |
|       BaseAddress, BaseAddress + Length, Attributes));
 | |
| 
 | |
|   // We do not support a smaller granularity than 4KB on ARM Architecture
 | |
|   if ((Length & EFI_PAGE_MASK) != 0) {
 | |
|     DEBUG ((DEBUG_WARN,
 | |
|             "Warning: We do not support smaller granularity than 4KB on ARM Architecture (passed length: 0x%lX).\n",
 | |
|             Length));
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get all memory descriptors covered by the memory range
 | |
|   //
 | |
|   Status = SearchGcdMemorySpaces (
 | |
|              MemorySpaceMap,
 | |
|              NumberOfDescriptors,
 | |
|              BaseAddress,
 | |
|              Length,
 | |
|              &StartIndex,
 | |
|              &EndIndex
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Go through all related descriptors and set attributes accordingly
 | |
|   //
 | |
|   for (Index = StartIndex; Index <= EndIndex; Index++) {
 | |
|     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
 | |
|       continue;
 | |
|     }
 | |
|     //
 | |
|     // Calculate the start and end address of the overlapping range
 | |
|     //
 | |
|     if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
 | |
|       RegionStart = BaseAddress;
 | |
|     } else {
 | |
|       RegionStart = MemorySpaceMap[Index].BaseAddress;
 | |
|     }
 | |
|     if ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length)) {
 | |
|       RegionLength = BaseAddress + Length - RegionStart;
 | |
|     } else {
 | |
|       RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
 | |
|     }
 | |
|     //
 | |
|     // Set memory attributes according to MTRR attribute and the original attribute of descriptor
 | |
|     //
 | |
|     gDS->SetMemorySpaceAttributes (
 | |
|            RegionStart,
 | |
|            RegionLength,
 | |
|            (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
 | |
|            );
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function modifies the attributes for the memory region specified by BaseAddress and
 | |
|   Length from their current attributes to the attributes specified by Attributes.
 | |
| 
 | |
|   @param  This             The EFI_CPU_ARCH_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_ACCESS_DENIED     The attributes for the memory resource range specified by
 | |
|                                 BaseAddress and Length cannot be modified.
 | |
|   @retval EFI_INVALID_PARAMETER Length is zero.
 | |
|   @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
 | |
|                                 the memory resource range.
 | |
|   @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 support for the memory resource
 | |
|                                 range specified by BaseAddress and Length.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| CpuSetMemoryAttributes (
 | |
|   IN EFI_CPU_ARCH_PROTOCOL    *This,
 | |
|   IN EFI_PHYSICAL_ADDRESS      BaseAddress,
 | |
|   IN UINT64                    Length,
 | |
|   IN UINT64                    EfiAttributes
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINTN       ArmAttributes;
 | |
|   UINTN       RegionBaseAddress;
 | |
|   UINTN       RegionLength;
 | |
|   UINTN       RegionArmAttributes;
 | |
| 
 | |
|   if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
 | |
|     // Minimum granularity is SIZE_4KB (4KB on ARM)
 | |
|     DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes));
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   // Convert the 'Attribute' into ARM Attribute
 | |
|   ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes);
 | |
| 
 | |
|   // Get the region starting from 'BaseAddress' and its 'Attribute'
 | |
|   RegionBaseAddress = BaseAddress;
 | |
|   Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes);
 | |
| 
 | |
|   // Data & Instruction Caches are flushed when we set new memory attributes.
 | |
|   // So, we only set the attributes if the new region is different.
 | |
|   if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||
 | |
|       ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
 | |
|   {
 | |
|     return SetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);
 | |
|   } else {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| }
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| CpuConvertPagesToUncachedVirtualAddress (
 | |
|   IN  VIRTUAL_UNCACHED_PAGES_PROTOCOL  *This,
 | |
|   IN  EFI_PHYSICAL_ADDRESS              Address,
 | |
|   IN  UINTN                             Length,
 | |
|   IN  EFI_PHYSICAL_ADDRESS              VirtualMask,
 | |
|   OUT UINT64                           *Attributes     OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
 | |
| 
 | |
|   if (Attributes != NULL) {
 | |
|     Status = gDS->GetMemorySpaceDescriptor (Address, &GcdDescriptor);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       *Attributes = GcdDescriptor.Attributes;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Make this address range page fault if accessed. If it is a DMA buffer than this would
 | |
|   // be the PCI address. Code should always use the CPU address, and we will or in VirtualMask
 | |
|   // to that address.
 | |
|   //
 | |
|   Status = SetMemoryAttributes (Address, Length, EFI_MEMORY_WP, 0);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_UC, VirtualMask);
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuConvertPagesToUncachedVirtualAddress()\n    Unmapped 0x%08lx Mapped 0x%08lx 0x%x bytes\n", Address, Address | VirtualMask, Length));
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| CpuReconvertPages (
 | |
|   IN  VIRTUAL_UNCACHED_PAGES_PROTOCOL  *This,
 | |
|   IN  EFI_PHYSICAL_ADDRESS              Address,
 | |
|   IN  UINTN                             Length,
 | |
|   IN  EFI_PHYSICAL_ADDRESS              VirtualMask,
 | |
|   IN  UINT64                            Attributes
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS      Status;
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuReconvertPages(%lx, %x, %lx, %lx)\n", Address, Length, VirtualMask, Attributes));
 | |
| 
 | |
|   //
 | |
|   // Unmap the aliased Address
 | |
|   //
 | |
|   Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_WP, 0);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Restore atttributes
 | |
|     //
 | |
|     Status = SetMemoryAttributes (Address, Length, Attributes, 0);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| VIRTUAL_UNCACHED_PAGES_PROTOCOL  gVirtualUncachedPages = {
 | |
|   CpuConvertPagesToUncachedVirtualAddress,
 | |
|   CpuReconvertPages
 | |
| };
 |