Home | History | Annotate | Download | only in AArch64
      1 /*++
      2 
      3 Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>
      4 Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>
      5 Portions copyright (c) 2011-2013, ARM Ltd. All rights reserved.<BR>
      6 
      7 This program and the accompanying materials
      8 are licensed and made available under the terms and conditions of the BSD License
      9 which accompanies this distribution.  The full text of the license may be found at
     10 http://opensource.org/licenses/bsd-license.php
     11 
     12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 
     16 --*/
     17 
     18 #include <Library/MemoryAllocationLib.h>
     19 #include "CpuDxe.h"
     20 
     21 #define TT_ATTR_INDX_INVALID    ((UINT32)~0)
     22 
     23 STATIC
     24 UINT64
     25 GetFirstPageAttribute (
     26   IN UINT64  *FirstLevelTableAddress,
     27   IN UINTN    TableLevel
     28   )
     29 {
     30   UINT64 FirstEntry;
     31 
     32   // Get the first entry of the table
     33   FirstEntry = *FirstLevelTableAddress;
     34 
     35   if ((TableLevel != 3) && (FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) {
     36     // Only valid for Levels 0, 1 and 2
     37 
     38     // Get the attribute of the subsequent table
     39     return GetFirstPageAttribute ((UINT64*)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1);
     40   } else if (((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) ||
     41              ((TableLevel == 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3)))
     42   {
     43     return FirstEntry & TT_ATTR_INDX_MASK;
     44   } else {
     45     return TT_ATTR_INDX_INVALID;
     46   }
     47 }
     48 
     49 STATIC
     50 UINT64
     51 GetNextEntryAttribute (
     52   IN     UINT64 *TableAddress,
     53   IN     UINTN   EntryCount,
     54   IN     UINTN   TableLevel,
     55   IN     UINT64  BaseAddress,
     56   IN OUT UINT32 *PrevEntryAttribute,
     57   IN OUT UINT64 *StartGcdRegion
     58   )
     59 {
     60   UINTN                             Index;
     61   UINT64                            Entry;
     62   UINT32                            EntryAttribute;
     63   UINT32                            EntryType;
     64   EFI_STATUS                        Status;
     65   UINTN                             NumberOfDescriptors;
     66   EFI_GCD_MEMORY_SPACE_DESCRIPTOR  *MemorySpaceMap;
     67 
     68   // Get the memory space map from GCD
     69   MemorySpaceMap = NULL;
     70   Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
     71   ASSERT_EFI_ERROR (Status);
     72 
     73   // We cannot get more than 3-level page table
     74   ASSERT (TableLevel <= 3);
     75 
     76   // While the top level table might not contain TT_ENTRY_COUNT entries;
     77   // the subsequent ones should be filled up
     78   for (Index = 0; Index < EntryCount; Index++) {
     79     Entry = TableAddress[Index];
     80     EntryType = Entry & TT_TYPE_MASK;
     81     EntryAttribute = Entry  & TT_ATTR_INDX_MASK;
     82 
     83     // If Entry is a Table Descriptor type entry then go through the sub-level table
     84     if ((EntryType == TT_TYPE_BLOCK_ENTRY) ||
     85         ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3))) {
     86       if ((*PrevEntryAttribute == TT_ATTR_INDX_INVALID) || (EntryAttribute != *PrevEntryAttribute)) {
     87         if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {
     88           // Update GCD with the last region
     89           SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,
     90               *StartGcdRegion,
     91               (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion,
     92               PageAttributeToGcdAttribute (*PrevEntryAttribute));
     93         }
     94 
     95         // Start of the new region
     96         *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));
     97         *PrevEntryAttribute = EntryAttribute;
     98       } else {
     99         continue;
    100       }
    101     } else if (EntryType == TT_TYPE_TABLE_ENTRY) {
    102       // Table Entry type is only valid for Level 0, 1, 2
    103       ASSERT (TableLevel < 3);
    104 
    105       // Increase the level number and scan the sub-level table
    106       GetNextEntryAttribute ((UINT64*)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE),
    107                              TT_ENTRY_COUNT, TableLevel + 1,
    108                              (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))),
    109                              PrevEntryAttribute, StartGcdRegion);
    110     } else {
    111       if (*PrevEntryAttribute != TT_ATTR_INDX_INVALID) {
    112         // Update GCD with the last region
    113         SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,
    114             *StartGcdRegion,
    115             (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion,
    116             PageAttributeToGcdAttribute (*PrevEntryAttribute));
    117 
    118         // Start of the new region
    119         *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel));
    120         *PrevEntryAttribute = TT_ATTR_INDX_INVALID;
    121       }
    122     }
    123   }
    124 
    125   FreePool (MemorySpaceMap);
    126 
    127   return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL(TableLevel));
    128 }
    129 
    130 EFI_STATUS
    131 SyncCacheConfig (
    132   IN  EFI_CPU_ARCH_PROTOCOL *CpuProtocol
    133   )
    134 {
    135   EFI_STATUS                          Status;
    136   UINT32                              PageAttribute = 0;
    137   UINT64                             *FirstLevelTableAddress;
    138   UINTN                               TableLevel;
    139   UINTN                               TableCount;
    140   UINTN                               NumberOfDescriptors;
    141   EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap;
    142   UINTN                               Tcr;
    143   UINTN                               T0SZ;
    144   UINT64                              BaseAddressGcdRegion;
    145   UINT64                              EndAddressGcdRegion;
    146 
    147   // This code assumes MMU is enabled and filed with section translations
    148   ASSERT (ArmMmuEnabled ());
    149 
    150   //
    151   // Get the memory space map from GCD
    152   //
    153   MemorySpaceMap = NULL;
    154   Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
    155   ASSERT_EFI_ERROR (Status);
    156 
    157   // The GCD implementation maintains its own copy of the state of memory space attributes.  GCD needs
    158   // to know what the initial memory space attributes are.  The CPU Arch. Protocol does not provide a
    159   // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
    160   // a client) to update its copy of the attributes.  This is bad architecture and should be replaced
    161   // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
    162 
    163   // Obtain page table base
    164   FirstLevelTableAddress = (UINT64*)(ArmGetTTBR0BaseAddress ());
    165 
    166   // Get Translation Control Register value
    167   Tcr = ArmGetTCR ();
    168   // Get Address Region Size
    169   T0SZ = Tcr & TCR_T0SZ_MASK;
    170 
    171   // Get the level of the first table for the indicated Address Region Size
    172   GetRootTranslationTableInfo (T0SZ, &TableLevel, &TableCount);
    173 
    174   // First Attribute of the Page Tables
    175   PageAttribute = GetFirstPageAttribute (FirstLevelTableAddress, TableLevel);
    176 
    177   // We scan from the start of the memory map (ie: at the address 0x0)
    178   BaseAddressGcdRegion = 0x0;
    179   EndAddressGcdRegion = GetNextEntryAttribute (FirstLevelTableAddress,
    180                                                TableCount, TableLevel,
    181                                                BaseAddressGcdRegion,
    182                                                &PageAttribute, &BaseAddressGcdRegion);
    183 
    184   // Update GCD with the last region if valid
    185   if (PageAttribute != TT_ATTR_INDX_INVALID) {
    186     SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors,
    187         BaseAddressGcdRegion,
    188         EndAddressGcdRegion - BaseAddressGcdRegion,
    189         PageAttributeToGcdAttribute (PageAttribute));
    190   }
    191 
    192   FreePool (MemorySpaceMap);
    193 
    194   return EFI_SUCCESS;
    195 }
    196 
    197 UINT64
    198 EfiAttributeToArmAttribute (
    199   IN UINT64                    EfiAttributes
    200   )
    201 {
    202   UINT64 ArmAttributes;
    203 
    204   switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
    205   case EFI_MEMORY_UC:
    206     ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;
    207     break;
    208   case EFI_MEMORY_WC:
    209     ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE;
    210     break;
    211   case EFI_MEMORY_WT:
    212     ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH;
    213     break;
    214   case EFI_MEMORY_WB:
    215     ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK;
    216     break;
    217   default:
    218     DEBUG ((EFI_D_ERROR, "EfiAttributeToArmAttribute: 0x%lX attributes is not supported.\n", EfiAttributes));
    219     ASSERT (0);
    220     ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY;
    221   }
    222 
    223   // Set the access flag to match the block attributes
    224   ArmAttributes |= TT_AF;
    225 
    226   // Determine protection attributes
    227   if (EfiAttributes & EFI_MEMORY_WP) {
    228     ArmAttributes |= TT_AP_RO_RO;
    229   }
    230 
    231   // Process eXecute Never attribute
    232   if (EfiAttributes & EFI_MEMORY_XP) {
    233     ArmAttributes |= TT_PXN_MASK;
    234   }
    235 
    236   return ArmAttributes;
    237 }
    238 
    239 // This function will recursively go down the page table to find the first block address linked to 'BaseAddress'.
    240 // And then the function will identify the size of the region that has the same page table attribute.
    241 EFI_STATUS
    242 GetMemoryRegionRec (
    243   IN     UINT64                  *TranslationTable,
    244   IN     UINTN                    TableLevel,
    245   IN     UINT64                  *LastBlockEntry,
    246   IN OUT UINTN                   *BaseAddress,
    247   OUT    UINTN                   *RegionLength,
    248   OUT    UINTN                   *RegionAttributes
    249   )
    250 {
    251   EFI_STATUS Status;
    252   UINT64    *NextTranslationTable;
    253   UINT64    *BlockEntry;
    254   UINT64     BlockEntryType;
    255   UINT64     EntryType;
    256 
    257   if (TableLevel != 3) {
    258     BlockEntryType = TT_TYPE_BLOCK_ENTRY;
    259   } else {
    260     BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3;
    261   }
    262 
    263   // Find the block entry linked to the Base Address
    264   BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress);
    265   EntryType = *BlockEntry & TT_TYPE_MASK;
    266 
    267   if ((TableLevel < 3) && (EntryType == TT_TYPE_TABLE_ENTRY)) {
    268     NextTranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE);
    269 
    270     // The entry is a page table, so we go to the next level
    271     Status = GetMemoryRegionRec (
    272         NextTranslationTable, // Address of the next level page table
    273         TableLevel + 1, // Next Page Table level
    274         (UINTN*)TT_LAST_BLOCK_ADDRESS(NextTranslationTable, TT_ENTRY_COUNT),
    275         BaseAddress, RegionLength, RegionAttributes);
    276 
    277     // In case of 'Success', it means the end of the block region has been found into the upper
    278     // level translation table
    279     if (!EFI_ERROR(Status)) {
    280       return EFI_SUCCESS;
    281     }
    282 
    283     // Now we processed the table move to the next entry
    284     BlockEntry++;
    285   } else if (EntryType == BlockEntryType) {
    286     // We have found the BlockEntry attached to the address. We save its start address (the start
    287     // address might be before the 'BaseAdress') and attributes
    288     *BaseAddress      = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL(TableLevel) - 1);
    289     *RegionLength     = 0;
    290     *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK;
    291   } else {
    292     // We have an 'Invalid' entry
    293     return EFI_UNSUPPORTED;
    294   }
    295 
    296   while (BlockEntry <= LastBlockEntry) {
    297     if ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes) {
    298       *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL(TableLevel);
    299     } else {
    300       // In case we have found the end of the region we return success
    301       return EFI_SUCCESS;
    302     }
    303     BlockEntry++;
    304   }
    305 
    306   // If we have reached the end of the TranslationTable and we have not found the end of the region then
    307   // we return EFI_NOT_FOUND.
    308   // The caller will continue to look for the memory region at its level
    309   return EFI_NOT_FOUND;
    310 }
    311 
    312 EFI_STATUS
    313 GetMemoryRegion (
    314   IN OUT UINTN                   *BaseAddress,
    315   OUT    UINTN                   *RegionLength,
    316   OUT    UINTN                   *RegionAttributes
    317   )
    318 {
    319   EFI_STATUS  Status;
    320   UINT64     *TranslationTable;
    321   UINTN       TableLevel;
    322   UINTN       EntryCount;
    323   UINTN       T0SZ;
    324 
    325   ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL));
    326 
    327   TranslationTable = ArmGetTTBR0BaseAddress ();
    328 
    329   T0SZ = ArmGetTCR () & TCR_T0SZ_MASK;
    330   // Get the Table info from T0SZ
    331   GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount);
    332 
    333   Status = GetMemoryRegionRec (TranslationTable, TableLevel,
    334       (UINTN*)TT_LAST_BLOCK_ADDRESS(TranslationTable, EntryCount),
    335       BaseAddress, RegionLength, RegionAttributes);
    336 
    337   // If the region continues up to the end of the root table then GetMemoryRegionRec()
    338   // will return EFI_NOT_FOUND
    339   if (Status == EFI_NOT_FOUND) {
    340     return EFI_SUCCESS;
    341   } else {
    342     return Status;
    343   }
    344 }
    345