Home | History | Annotate | Download | only in CpuDxe
      1 /** @file
      2 *
      3 *  Copyright (c) 2013, ARM Limited. All rights reserved.
      4 *
      5 *  This program and the accompanying materials
      6 *  are licensed and made available under the terms and conditions of the BSD License
      7 *  which accompanies this distribution.  The full text of the license may be found at
      8 *  http://opensource.org/licenses/bsd-license.php
      9 *
     10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 *
     13 **/
     14 
     15 #include "CpuDxe.h"
     16 
     17 /**
     18   Searches memory descriptors covered by given memory range.
     19 
     20   This function searches into the Gcd Memory Space for descriptors
     21   (from StartIndex to EndIndex) that contains the memory range
     22   specified by BaseAddress and Length.
     23 
     24   @param  MemorySpaceMap       Gcd Memory Space Map as array.
     25   @param  NumberOfDescriptors  Number of descriptors in map.
     26   @param  BaseAddress          BaseAddress for the requested range.
     27   @param  Length               Length for the requested range.
     28   @param  StartIndex           Start index into the Gcd Memory Space Map.
     29   @param  EndIndex             End index into the Gcd Memory Space Map.
     30 
     31   @retval EFI_SUCCESS          Search successfully.
     32   @retval EFI_NOT_FOUND        The requested descriptors does not exist.
     33 
     34 **/
     35 EFI_STATUS
     36 SearchGcdMemorySpaces (
     37   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap,
     38   IN UINTN                               NumberOfDescriptors,
     39   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
     40   IN UINT64                              Length,
     41   OUT UINTN                             *StartIndex,
     42   OUT UINTN                             *EndIndex
     43   )
     44 {
     45   UINTN           Index;
     46 
     47   *StartIndex = 0;
     48   *EndIndex   = 0;
     49   for (Index = 0; Index < NumberOfDescriptors; Index++) {
     50     if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) &&
     51         (BaseAddress < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
     52       *StartIndex = Index;
     53     }
     54     if (((BaseAddress + Length - 1) >= MemorySpaceMap[Index].BaseAddress) &&
     55         ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
     56       *EndIndex = Index;
     57       return EFI_SUCCESS;
     58     }
     59   }
     60   return EFI_NOT_FOUND;
     61 }
     62 
     63 
     64 /**
     65   Sets the attributes for a specified range in Gcd Memory Space Map.
     66 
     67   This function sets the attributes for a specified range in
     68   Gcd Memory Space Map.
     69 
     70   @param  MemorySpaceMap       Gcd Memory Space Map as array
     71   @param  NumberOfDescriptors  Number of descriptors in map
     72   @param  BaseAddress          BaseAddress for the range
     73   @param  Length               Length for the range
     74   @param  Attributes           Attributes to set
     75 
     76   @retval EFI_SUCCESS          Memory attributes set successfully
     77   @retval EFI_NOT_FOUND        The specified range does not exist in Gcd Memory Space
     78 
     79 **/
     80 EFI_STATUS
     81 SetGcdMemorySpaceAttributes (
     82   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap,
     83   IN UINTN                               NumberOfDescriptors,
     84   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
     85   IN UINT64                              Length,
     86   IN UINT64                              Attributes
     87   )
     88 {
     89   EFI_STATUS            Status;
     90   UINTN                 Index;
     91   UINTN                 StartIndex;
     92   UINTN                 EndIndex;
     93   EFI_PHYSICAL_ADDRESS  RegionStart;
     94   UINT64                RegionLength;
     95 
     96   DEBUG ((DEBUG_GCD, "SetGcdMemorySpaceAttributes[0x%lX; 0x%lX] = 0x%lX\n",
     97       BaseAddress, BaseAddress + Length, Attributes));
     98 
     99   // We do not support a smaller granularity than 4KB on ARM Architecture
    100   if ((Length & EFI_PAGE_MASK) != 0) {
    101     DEBUG ((DEBUG_WARN,
    102             "Warning: We do not support smaller granularity than 4KB on ARM Architecture (passed length: 0x%lX).\n",
    103             Length));
    104   }
    105 
    106   //
    107   // Get all memory descriptors covered by the memory range
    108   //
    109   Status = SearchGcdMemorySpaces (
    110              MemorySpaceMap,
    111              NumberOfDescriptors,
    112              BaseAddress,
    113              Length,
    114              &StartIndex,
    115              &EndIndex
    116              );
    117   if (EFI_ERROR (Status)) {
    118     return Status;
    119   }
    120 
    121   //
    122   // Go through all related descriptors and set attributes accordingly
    123   //
    124   for (Index = StartIndex; Index <= EndIndex; Index++) {
    125     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
    126       continue;
    127     }
    128     //
    129     // Calculate the start and end address of the overlapping range
    130     //
    131     if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
    132       RegionStart = BaseAddress;
    133     } else {
    134       RegionStart = MemorySpaceMap[Index].BaseAddress;
    135     }
    136     if ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length)) {
    137       RegionLength = BaseAddress + Length - RegionStart;
    138     } else {
    139       RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
    140     }
    141     //
    142     // Set memory attributes according to MTRR attribute and the original attribute of descriptor
    143     //
    144     gDS->SetMemorySpaceAttributes (
    145            RegionStart,
    146            RegionLength,
    147            (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
    148            );
    149   }
    150 
    151   return EFI_SUCCESS;
    152 }
    153 
    154 /**
    155   This function modifies the attributes for the memory region specified by BaseAddress and
    156   Length from their current attributes to the attributes specified by Attributes.
    157 
    158   @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
    159   @param  BaseAddress      The physical address that is the start address of a memory region.
    160   @param  Length           The size in bytes of the memory region.
    161   @param  Attributes       The bit mask of attributes to set for the memory region.
    162 
    163   @retval EFI_SUCCESS           The attributes were set for the memory region.
    164   @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
    165                                 BaseAddress and Length cannot be modified.
    166   @retval EFI_INVALID_PARAMETER Length is zero.
    167   @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
    168                                 the memory resource range.
    169   @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
    170                                 resource range specified by BaseAddress and Length.
    171                                 The bit mask of attributes is not support for the memory resource
    172                                 range specified by BaseAddress and Length.
    173 
    174 **/
    175 EFI_STATUS
    176 EFIAPI
    177 CpuSetMemoryAttributes (
    178   IN EFI_CPU_ARCH_PROTOCOL    *This,
    179   IN EFI_PHYSICAL_ADDRESS      BaseAddress,
    180   IN UINT64                    Length,
    181   IN UINT64                    EfiAttributes
    182   )
    183 {
    184   EFI_STATUS  Status;
    185   UINTN       ArmAttributes;
    186   UINTN       RegionBaseAddress;
    187   UINTN       RegionLength;
    188   UINTN       RegionArmAttributes;
    189 
    190   if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
    191     // Minimum granularity is SIZE_4KB (4KB on ARM)
    192     DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes));
    193     return EFI_UNSUPPORTED;
    194   }
    195 
    196   // Convert the 'Attribute' into ARM Attribute
    197   ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes);
    198 
    199   // Get the region starting from 'BaseAddress' and its 'Attribute'
    200   RegionBaseAddress = BaseAddress;
    201   Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes);
    202 
    203   // Data & Instruction Caches are flushed when we set new memory attributes.
    204   // So, we only set the attributes if the new region is different.
    205   if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||
    206       ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
    207   {
    208     return SetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);
    209   } else {
    210     return EFI_SUCCESS;
    211   }
    212 }
    213 
    214 EFI_STATUS
    215 EFIAPI
    216 CpuConvertPagesToUncachedVirtualAddress (
    217   IN  VIRTUAL_UNCACHED_PAGES_PROTOCOL  *This,
    218   IN  EFI_PHYSICAL_ADDRESS              Address,
    219   IN  UINTN                             Length,
    220   IN  EFI_PHYSICAL_ADDRESS              VirtualMask,
    221   OUT UINT64                           *Attributes     OPTIONAL
    222   )
    223 {
    224   EFI_STATUS                      Status;
    225   EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
    226 
    227   if (Attributes != NULL) {
    228     Status = gDS->GetMemorySpaceDescriptor (Address, &GcdDescriptor);
    229     if (!EFI_ERROR (Status)) {
    230       *Attributes = GcdDescriptor.Attributes;
    231     }
    232   }
    233 
    234   //
    235   // Make this address range page fault if accessed. If it is a DMA buffer than this would
    236   // be the PCI address. Code should always use the CPU address, and we will or in VirtualMask
    237   // to that address.
    238   //
    239   Status = SetMemoryAttributes (Address, Length, EFI_MEMORY_WP, 0);
    240   if (!EFI_ERROR (Status)) {
    241     Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_UC, VirtualMask);
    242   }
    243 
    244   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuConvertPagesToUncachedVirtualAddress()\n    Unmapped 0x%08lx Mapped 0x%08lx 0x%x bytes\n", Address, Address | VirtualMask, Length));
    245 
    246   return Status;
    247 }
    248 
    249 
    250 EFI_STATUS
    251 EFIAPI
    252 CpuReconvertPages (
    253   IN  VIRTUAL_UNCACHED_PAGES_PROTOCOL  *This,
    254   IN  EFI_PHYSICAL_ADDRESS              Address,
    255   IN  UINTN                             Length,
    256   IN  EFI_PHYSICAL_ADDRESS              VirtualMask,
    257   IN  UINT64                            Attributes
    258   )
    259 {
    260   EFI_STATUS      Status;
    261 
    262   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuReconvertPages(%lx, %x, %lx, %lx)\n", Address, Length, VirtualMask, Attributes));
    263 
    264   //
    265   // Unmap the aliased Address
    266   //
    267   Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_WP, 0);
    268   if (!EFI_ERROR (Status)) {
    269     //
    270     // Restore atttributes
    271     //
    272     Status = SetMemoryAttributes (Address, Length, Attributes, 0);
    273   }
    274 
    275   return Status;
    276 }
    277 
    278 
    279 VIRTUAL_UNCACHED_PAGES_PROTOCOL  gVirtualUncachedPages = {
    280   CpuConvertPagesToUncachedVirtualAddress,
    281   CpuReconvertPages
    282 };
    283