EDK2 doxygen online documents - Firmware Encoding Index 1
EDK2 doxygen online documents - Firmware Encoding Index

ArmPkg/Drivers/CpuDxe/Mmu.c

Go to the documentation of this file.
00001 /*++
00002 
00003 Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.<BR>
00004 Portions copyright (c) 2010, Apple Inc. All rights reserved.<BR>
00005 
00006 This program and the accompanying materials                          
00007 are licensed and made available under the terms and conditions of the BSD License         
00008 which accompanies this distribution.  The full text of the license may be found at        
00009 http://opensource.org/licenses/bsd-license.php                                            
00010                                                                                           
00011 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
00012 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
00013 
00014 
00015 --*/
00016 
00017 #include "CpuDxe.h"
00018 
00019 // First Level Descriptors
00020 typedef UINT32    ARM_FIRST_LEVEL_DESCRIPTOR;
00021 
00022 // Second Level Descriptors
00023 typedef UINT32    ARM_PAGE_TABLE_ENTRY;
00024 
00025 EFI_STATUS 
00026 SectionToGcdAttributes (
00027   IN  UINT32  SectionAttributes,
00028   OUT UINT64  *GcdAttributes
00029   )
00030 {
00031   *GcdAttributes = 0;
00032 
00033   // determine cacheability attributes
00034   switch(SectionAttributes & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) {
00035     case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED:
00036       *GcdAttributes |= EFI_MEMORY_UC;
00037       break;
00038     case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE:
00039       *GcdAttributes |= EFI_MEMORY_UC;
00040       break;
00041     case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:
00042       *GcdAttributes |= EFI_MEMORY_WT;
00043       break;
00044     case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC:
00045       *GcdAttributes |= EFI_MEMORY_WB;
00046       break;
00047     case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE:
00048       *GcdAttributes |= EFI_MEMORY_WC;
00049       break;
00050     case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC:
00051       *GcdAttributes |= EFI_MEMORY_WB;
00052       break;
00053     case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE:
00054       *GcdAttributes |= EFI_MEMORY_UC;
00055       break;
00056     default:
00057       return EFI_UNSUPPORTED;
00058   }
00059 
00060   // determine protection attributes
00061   switch(SectionAttributes & TT_DESCRIPTOR_SECTION_AP_MASK) {
00062     case TT_DESCRIPTOR_SECTION_AP_NO_NO: // no read, no write
00063       //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP;
00064       break;
00065 
00066     case TT_DESCRIPTOR_SECTION_AP_RW_NO:
00067     case TT_DESCRIPTOR_SECTION_AP_RW_RW:
00068       // normal read/write access, do not add additional attributes
00069       break;
00070 
00071     // read only cases map to write-protect
00072     case TT_DESCRIPTOR_SECTION_AP_RO_NO:
00073     case TT_DESCRIPTOR_SECTION_AP_RO_RO:
00074       *GcdAttributes |= EFI_MEMORY_WP;
00075       break;
00076 
00077     default:
00078       return EFI_UNSUPPORTED;
00079   }
00080 
00081   // now process eXectue Never attribute
00082   if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) {
00083     *GcdAttributes |= EFI_MEMORY_XP;
00084   }
00085 
00086   return EFI_SUCCESS;
00087 }
00088 
00089 EFI_STATUS
00090 PageToGcdAttributes (
00091   IN  UINT32  PageAttributes,
00092   OUT UINT64  *GcdAttributes
00093   )
00094 {
00095   *GcdAttributes = 0;
00096 
00097   // determine cacheability attributes
00098   switch(PageAttributes & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) {
00099     case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED:
00100       *GcdAttributes |= EFI_MEMORY_UC;
00101       break;
00102     case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE:
00103       *GcdAttributes |= EFI_MEMORY_UC;
00104       break;
00105     case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC:
00106       *GcdAttributes |= EFI_MEMORY_WT;
00107       break;
00108     case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC:
00109       *GcdAttributes |= EFI_MEMORY_WB;
00110       break;
00111     case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE:
00112       *GcdAttributes |= EFI_MEMORY_WC;
00113       break;
00114     case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC:
00115       *GcdAttributes |= EFI_MEMORY_WB;
00116       break;
00117     case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE:
00118       *GcdAttributes |= EFI_MEMORY_UC;
00119       break;
00120     default:
00121       return EFI_UNSUPPORTED;
00122   }
00123 
00124   // determine protection attributes
00125   switch(PageAttributes & TT_DESCRIPTOR_PAGE_AP_MASK) {
00126     case TT_DESCRIPTOR_PAGE_AP_NO_NO: // no read, no write
00127       //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP;
00128       break;
00129 
00130     case TT_DESCRIPTOR_PAGE_AP_RW_NO:
00131     case TT_DESCRIPTOR_PAGE_AP_RW_RW:
00132       // normal read/write access, do not add additional attributes
00133       break;
00134 
00135     // read only cases map to write-protect
00136     case TT_DESCRIPTOR_PAGE_AP_RO_NO:
00137     case TT_DESCRIPTOR_PAGE_AP_RO_RO:
00138       *GcdAttributes |= EFI_MEMORY_WP;
00139       break;
00140 
00141     default:
00142       return EFI_UNSUPPORTED;
00143   }
00144 
00145   // now process eXectue Never attribute
00146   if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) {
00147     *GcdAttributes |= EFI_MEMORY_XP;
00148   }
00149 
00150   return EFI_SUCCESS;
00151 }
00152 
00171 EFI_STATUS
00172 SearchGcdMemorySpaces (
00173   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR     *MemorySpaceMap,
00174   IN UINTN                               NumberOfDescriptors,
00175   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
00176   IN UINT64                              Length,
00177   OUT UINTN                              *StartIndex,
00178   OUT UINTN                              *EndIndex
00179   )
00180 {
00181   UINTN           Index;
00182 
00183   *StartIndex = 0;
00184   *EndIndex   = 0;
00185   for (Index = 0; Index < NumberOfDescriptors; Index++) {
00186     if (BaseAddress >= MemorySpaceMap[Index].BaseAddress &&
00187         BaseAddress < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
00188       *StartIndex = Index;
00189     }
00190     if (BaseAddress + Length - 1 >= MemorySpaceMap[Index].BaseAddress &&
00191         BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
00192       *EndIndex = Index;
00193       return EFI_SUCCESS;
00194     }
00195   }
00196   return EFI_NOT_FOUND;
00197 }
00198 
00199 
00216 EFI_STATUS
00217 SetGcdMemorySpaceAttributes (
00218   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR     *MemorySpaceMap,
00219   IN UINTN                               NumberOfDescriptors,
00220   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
00221   IN UINT64                              Length,
00222   IN UINT64                              Attributes
00223   )
00224 {
00225   EFI_STATUS            Status;
00226   UINTN                 Index;
00227   UINTN                 StartIndex;
00228   UINTN                 EndIndex;
00229   EFI_PHYSICAL_ADDRESS  RegionStart;
00230   UINT64                RegionLength;
00231 
00232   //
00233   // Get all memory descriptors covered by the memory range
00234   //
00235   Status = SearchGcdMemorySpaces (
00236              MemorySpaceMap,
00237              NumberOfDescriptors,
00238              BaseAddress,
00239              Length,
00240              &StartIndex,
00241              &EndIndex
00242              );
00243   if (EFI_ERROR (Status)) {
00244     return Status;
00245   }
00246 
00247   //
00248   // Go through all related descriptors and set attributes accordingly
00249   //
00250   for (Index = StartIndex; Index <= EndIndex; Index++) {
00251     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
00252       continue;
00253     }
00254     //
00255     // Calculate the start and end address of the overlapping range
00256     //
00257     if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
00258       RegionStart = BaseAddress;
00259     } else {
00260       RegionStart = MemorySpaceMap[Index].BaseAddress;
00261     }
00262     if (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {
00263       RegionLength = BaseAddress + Length - RegionStart;
00264     } else {
00265       RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
00266     }
00267     //
00268     // Set memory attributes according to MTRR attribute and the original attribute of descriptor
00269     //
00270     gDS->SetMemorySpaceAttributes (
00271            RegionStart,
00272            RegionLength,
00273            (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
00274            );
00275   }
00276 
00277   return EFI_SUCCESS;
00278 }
00279 
00280 EFI_STATUS
00281 SyncCacheConfigPage (
00282   IN     UINT32                             SectionIndex,
00283   IN     UINT32                             FirstLevelDescriptor,
00284   IN     UINTN                              NumberOfDescriptors,
00285   IN     EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap,
00286   IN OUT EFI_PHYSICAL_ADDRESS               *NextRegionBase,
00287   IN OUT UINT64                             *NextRegionLength,
00288   IN OUT UINT32                             *NextSectionAttributes
00289   )
00290 {
00291   EFI_STATUS                          Status;
00292   UINT32                              i;
00293   volatile ARM_PAGE_TABLE_ENTRY       *SecondLevelTable;
00294   UINT32                              NextPageAttributes = 0;
00295   UINT32                              PageAttributes = 0;
00296   UINT32                              BaseAddress;
00297   UINT64                              GcdAttributes;
00298 
00299   // Get the Base Address from FirstLevelDescriptor;
00300   BaseAddress = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
00301 
00302   // Convert SectionAttributes into PageAttributes
00303   NextPageAttributes =
00304       TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes,0) |
00305       TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes);
00306 
00307   // obtain page table base
00308   SecondLevelTable = (ARM_PAGE_TABLE_ENTRY *)(FirstLevelDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK);
00309 
00310   for (i=0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) {
00311     if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
00312       // extract attributes (cacheability and permissions)
00313       PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK);
00314 
00315       if (NextPageAttributes == 0) {
00316         // start on a new region
00317         *NextRegionLength = 0;
00318         *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
00319         NextPageAttributes = PageAttributes;
00320       } else if (PageAttributes != NextPageAttributes) {
00321         // Convert Section Attributes into GCD Attributes
00322         Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);
00323         ASSERT_EFI_ERROR (Status);
00324 
00325         // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
00326         SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);
00327 
00328         // start on a new region
00329         *NextRegionLength = 0;
00330         *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
00331         NextPageAttributes = PageAttributes;
00332       }
00333     } else if (NextPageAttributes != 0) {
00334       // Convert Page Attributes into GCD Attributes
00335       Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes);
00336       ASSERT_EFI_ERROR (Status);
00337 
00338       // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
00339       SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes);
00340 
00341       *NextRegionLength = 0;
00342       *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT);
00343       NextPageAttributes = 0;
00344     }
00345     *NextRegionLength += TT_DESCRIPTOR_PAGE_SIZE;
00346   }
00347 
00348   // Convert back PageAttributes into SectionAttributes
00349   *NextSectionAttributes =
00350       TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes,0) |
00351       TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes);
00352 
00353   return EFI_SUCCESS;
00354 }
00355 
00356 EFI_STATUS
00357 SyncCacheConfig (
00358   IN  EFI_CPU_ARCH_PROTOCOL *CpuProtocol
00359   )
00360 {
00361   EFI_STATUS                          Status;
00362   UINT32                              i;
00363   EFI_PHYSICAL_ADDRESS                NextRegionBase;
00364   UINT64                              NextRegionLength;
00365   UINT32                              NextSectionAttributes = 0;
00366   UINT32                              SectionAttributes = 0;
00367   UINT64                              GcdAttributes;
00368   volatile ARM_FIRST_LEVEL_DESCRIPTOR   *FirstLevelTable;
00369   UINTN                               NumberOfDescriptors;
00370   EFI_GCD_MEMORY_SPACE_DESCRIPTOR     *MemorySpaceMap;
00371 
00372 
00373   DEBUG ((EFI_D_PAGE, "SyncCacheConfig()\n"));
00374 
00375   // This code assumes MMU is enabled and filed with section translations
00376   ASSERT (ArmMmuEnabled ());
00377 
00378   //
00379   // Get the memory space map from GCD
00380   //
00381   MemorySpaceMap = NULL;
00382   Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
00383   ASSERT_EFI_ERROR (Status);
00384 
00385 
00386   // The GCD implementation maintains its own copy of the state of memory space attributes.  GCD needs
00387   // to know what the initial memory space attributes are.  The CPU Arch. Protocol does not provide a
00388   // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were
00389   // a client) to update its copy of the attributes.  This is bad architecture and should be replaced
00390   // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.
00391 
00392   // obtain page table base
00393   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ());
00394 
00395   // Get the first region
00396   NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
00397 
00398   // iterate through each 1MB descriptor
00399   NextRegionBase = NextRegionLength = 0;
00400   for (i=0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) {
00401     if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
00402       // extract attributes (cacheability and permissions)
00403       SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
00404 
00405       if (NextSectionAttributes == 0) {
00406         // start on a new region
00407         NextRegionLength = 0;
00408         NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
00409         NextSectionAttributes = SectionAttributes;
00410       } else if (SectionAttributes != NextSectionAttributes) {
00411         // Convert Section Attributes into GCD Attributes
00412         Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
00413         ASSERT_EFI_ERROR (Status);
00414 
00415         // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
00416         SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
00417 
00418         // start on a new region
00419         NextRegionLength = 0;
00420         NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
00421         NextSectionAttributes = SectionAttributes;
00422       }
00423       NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;
00424     } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable[i])) {
00425       Status = SyncCacheConfigPage (
00426           i,FirstLevelTable[i],
00427           NumberOfDescriptors, MemorySpaceMap,
00428           &NextRegionBase,&NextRegionLength,&NextSectionAttributes);
00429       ASSERT_EFI_ERROR (Status);
00430     } else {
00431       // We do not support yet 16MB sections
00432       ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION);
00433 
00434       // start on a new region
00435       if (NextSectionAttributes != 0) {
00436         // Convert Section Attributes into GCD Attributes
00437         Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
00438         ASSERT_EFI_ERROR (Status);
00439 
00440         // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
00441         SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
00442 
00443         NextRegionLength = 0;
00444         NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
00445         NextSectionAttributes = 0;
00446       }
00447       NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE;
00448     }
00449   } // section entry loop
00450 
00451   if (NextSectionAttributes != 0) {
00452     // Convert Section Attributes into GCD Attributes
00453     Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes);
00454     ASSERT_EFI_ERROR (Status);
00455 
00456     // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)
00457     SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);
00458   }
00459 
00460   return EFI_SUCCESS;
00461 }
00462 
00463 
00464 
00465 EFI_STATUS
00466 UpdatePageEntries (
00467   IN EFI_PHYSICAL_ADDRESS      BaseAddress,
00468   IN UINT64                    Length,
00469   IN UINT64                    Attributes,
00470   IN EFI_PHYSICAL_ADDRESS      VirtualMask
00471   )
00472 {
00473   EFI_STATUS    Status;
00474   UINT32        EntryValue;
00475   UINT32        EntryMask;
00476   UINT32        FirstLevelIdx;
00477   UINT32        Offset;
00478   UINT32        NumPageEntries;
00479   UINT32        Descriptor;
00480   UINT32        p;
00481   UINT32        PageTableIndex;
00482   UINT32        PageTableEntry;
00483   UINT32        CurrentPageTableEntry;
00484   VOID          *Mva;
00485 
00486   volatile ARM_FIRST_LEVEL_DESCRIPTOR   *FirstLevelTable;
00487   volatile ARM_PAGE_TABLE_ENTRY         *PageTable;
00488 
00489   Status = EFI_SUCCESS;
00490 
00491   // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
00492   // EntryValue: values at bit positions specified by EntryMask
00493   EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK;
00494   EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
00495   // Although the PI spec is unclear on this the GCD guarantees that only
00496   // one Attribute bit is set at a time, so we can safely use a switch statement
00497   switch (Attributes) {
00498     case EFI_MEMORY_UC:
00499       // modify cacheability attributes
00500       EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
00501       if (FeaturePcdGet(PcdEfiUncachedMemoryToStronglyOrdered)) {
00502         // map to strongly ordered
00503         EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
00504       } else {
00505         // map to normal non-cachable
00506         EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
00507       }
00508       break;
00509 
00510     case EFI_MEMORY_WC:
00511       // modify cacheability attributes
00512       EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
00513       // map to normal non-cachable
00514       EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
00515       break;
00516 
00517     case EFI_MEMORY_WT:
00518       // modify cacheability attributes
00519       EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
00520       // write through with no-allocate
00521       EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
00522       break;
00523 
00524     case EFI_MEMORY_WB:
00525       // modify cacheability attributes
00526       EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
00527       // write back (with allocate)
00528       EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
00529       break;
00530 
00531     case EFI_MEMORY_WP:
00532     case EFI_MEMORY_XP:
00533     case EFI_MEMORY_UCE:
00534       // cannot be implemented UEFI definition unclear for ARM
00535       // Cause a page fault if these ranges are accessed.
00536       EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT;
00537       DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
00538       break;
00539 
00540     default:
00541       return EFI_UNSUPPORTED;
00542   }
00543 
00544   // Obtain page table base
00545   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
00546 
00547   // Calculate number of 4KB page table entries to change
00548   NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
00549   
00550   // Iterate for the number of 4KB pages to change
00551   Offset = 0;
00552   for(p = 0; p < NumPageEntries; p++) {
00553     // Calculate index into first level translation table for page table value
00554     
00555     FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
00556     ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
00557 
00558     // Read the descriptor from the first level page table
00559     Descriptor = FirstLevelTable[FirstLevelIdx];
00560 
00561     // Does this descriptor need to be converted from section entry to 4K pages?
00562     if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
00563       Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
00564       if (EFI_ERROR(Status)) {
00565         // Exit for loop
00566         break; 
00567       } 
00568       
00569       // Re-read descriptor
00570       Descriptor = FirstLevelTable[FirstLevelIdx];
00571     }
00572 
00573     // Obtain page table base address
00574     PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
00575 
00576     // Calculate index into the page table
00577     PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
00578     ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
00579 
00580     // Get the entry
00581     CurrentPageTableEntry = PageTable[PageTableIndex];
00582 
00583     // Mask off appropriate fields
00584     PageTableEntry = CurrentPageTableEntry & ~EntryMask;
00585 
00586     // Mask in new attributes and/or permissions
00587     PageTableEntry |= EntryValue;
00588 
00589     if (VirtualMask != 0) {
00590       // Make this virtual address point at a physical page
00591       PageTableEntry &= ~VirtualMask;
00592     }
00593    
00594     if (CurrentPageTableEntry  != PageTableEntry) {
00595       Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
00596       if ((CurrentPageTableEntry & TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) {
00597         // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
00598         // Note assumes switch(Attributes), not ARMv7 possibilities
00599         WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
00600       }
00601 
00602       // Only need to update if we are changing the entry  
00603       PageTable[PageTableIndex] = PageTableEntry; 
00604 //      ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
00605     }
00606 
00607     Status = EFI_SUCCESS;
00608     Offset += TT_DESCRIPTOR_PAGE_SIZE;
00609     
00610   } // End first level translation table loop
00611 
00612   return Status;
00613 }
00614 
00615 
00616 
00617 EFI_STATUS
00618 UpdateSectionEntries (
00619   IN EFI_PHYSICAL_ADDRESS      BaseAddress,
00620   IN UINT64                    Length,
00621   IN UINT64                    Attributes,
00622   IN EFI_PHYSICAL_ADDRESS      VirtualMask
00623   )
00624 {
00625   EFI_STATUS    Status = EFI_SUCCESS;
00626   UINT32        EntryMask;
00627   UINT32        EntryValue;
00628   UINT32        FirstLevelIdx;
00629   UINT32        NumSections;
00630   UINT32        i;
00631   UINT32        CurrentDescriptor;
00632   UINT32        Descriptor;
00633   VOID          *Mva;
00634   volatile ARM_FIRST_LEVEL_DESCRIPTOR   *FirstLevelTable;
00635 
00636   // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
00637   // EntryValue: values at bit positions specified by EntryMask
00638 
00639   // Make sure we handle a section range that is unmapped 
00640   EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK;
00641   EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
00642 
00643   // Although the PI spec is unclear on this the GCD guarantees that only
00644   // one Attribute bit is set at a time, so we can safely use a switch statement
00645   switch(Attributes) {
00646     case EFI_MEMORY_UC:
00647       // modify cacheability attributes
00648       EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
00649       if (FeaturePcdGet(PcdEfiUncachedMemoryToStronglyOrdered)) {
00650         // map to strongly ordered
00651         EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
00652       } else {
00653         // map to normal non-cachable
00654         EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
00655       }
00656       break;
00657 
00658     case EFI_MEMORY_WC:
00659       // modify cacheability attributes
00660       EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
00661       // map to normal non-cachable
00662       EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
00663       break;
00664 
00665     case EFI_MEMORY_WT:
00666       // modify cacheability attributes
00667       EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
00668       // write through with no-allocate
00669       EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
00670       break;
00671 
00672     case EFI_MEMORY_WB:
00673       // modify cacheability attributes
00674       EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
00675       // write back (with allocate)
00676       EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
00677       break;
00678 
00679     case EFI_MEMORY_WP:
00680     case EFI_MEMORY_XP:
00681     case EFI_MEMORY_RP:
00682     case EFI_MEMORY_UCE:
00683       // cannot be implemented UEFI definition unclear for ARM
00684       // Cause a page fault if these ranges are accessed.
00685       EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT;
00686       DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));
00687       break;
00688 
00689 
00690     default:
00691       return EFI_UNSUPPORTED;
00692   }
00693 
00694   // obtain page table base
00695   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
00696 
00697   // calculate index into first level translation table for start of modification
00698   FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
00699   ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
00700 
00701   // calculate number of 1MB first level entries this applies to
00702   NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
00703   
00704   // iterate through each descriptor
00705   for(i=0; i<NumSections; i++) {
00706     CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
00707 
00708     // has this descriptor already been coverted to pages?
00709     if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
00710       // forward this 1MB range to page table function instead
00711       Status = UpdatePageEntries ((FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, TT_DESCRIPTOR_SECTION_SIZE, Attributes, VirtualMask);
00712     } else {
00713       // still a section entry
00714       
00715       // mask off appropriate fields
00716       Descriptor = CurrentDescriptor & ~EntryMask;
00717 
00718       // mask in new attributes and/or permissions
00719       Descriptor |= EntryValue;
00720       if (VirtualMask != 0) {
00721         Descriptor &= ~VirtualMask;
00722       }
00723 
00724       if (CurrentDescriptor  != Descriptor) {
00725         Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
00726         if ((CurrentDescriptor & TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) {
00727           // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
00728           // Note assumes switch(Attributes), not ARMv7 possabilities
00729           WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
00730         }
00731 
00732         // Only need to update if we are changing the descriptor  
00733         FirstLevelTable[FirstLevelIdx + i] = Descriptor;
00734 //        ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
00735       }
00736 
00737       Status = EFI_SUCCESS;
00738     }
00739   }
00740 
00741   return Status;
00742 }
00743 
00744 EFI_STATUS 
00745 ConvertSectionToPages (
00746   IN EFI_PHYSICAL_ADDRESS  BaseAddress
00747   )
00748 {
00749   EFI_STATUS              Status;
00750   EFI_PHYSICAL_ADDRESS    PageTableAddr;
00751   UINT32                  FirstLevelIdx;
00752   UINT32                  SectionDescriptor;
00753   UINT32                  PageTableDescriptor;
00754   UINT32                  PageDescriptor;
00755   UINT32                  Index;
00756 
00757   volatile ARM_FIRST_LEVEL_DESCRIPTOR   *FirstLevelTable;
00758   volatile ARM_PAGE_TABLE_ENTRY         *PageTable;
00759 
00760   DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
00761 
00762   // obtain page table base
00763   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
00764 
00765   // calculate index into first level translation table for start of modification
00766   FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
00767   ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
00768 
00769   // get section attributes and convert to page attributes
00770   SectionDescriptor = FirstLevelTable[FirstLevelIdx];
00771   PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
00772   PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(SectionDescriptor,0);
00773   PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(SectionDescriptor);
00774   PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(SectionDescriptor,0);
00775   PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(SectionDescriptor);
00776   PageDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S(SectionDescriptor);
00777 
00778   // allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
00779   Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);
00780   if (EFI_ERROR(Status)) {
00781     return Status;
00782   }
00783 
00784   PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;
00785 
00786   // write the page table entries out
00787   for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
00788     PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
00789   }
00790 
00791   // flush d-cache so descriptors make it back to uncached memory for subsequent table walks
00792   WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);
00793 
00794   // formulate page table entry, Domain=0, NS=0
00795   PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
00796 
00797   // write the page table entry out, repalcing section entry
00798   FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
00799 
00800   return EFI_SUCCESS;
00801 }
00802 
00803 
00804 
00805 EFI_STATUS
00806 SetMemoryAttributes (
00807   IN EFI_PHYSICAL_ADDRESS      BaseAddress,
00808   IN UINT64                    Length,
00809   IN UINT64                    Attributes,
00810   IN EFI_PHYSICAL_ADDRESS      VirtualMask
00811   )
00812 {
00813   EFI_STATUS    Status;
00814   
00815   if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) {
00816     // Is the base and length a multiple of 1 MB?
00817     DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
00818     Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask);
00819   } else {
00820     // Base and/or length is not a multiple of 1 MB
00821     DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));
00822     Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask);
00823   }
00824 
00825   // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
00826   // flush and invalidate pages
00827   //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ?
00828   ArmCleanInvalidateDataCache ();
00829 
00830   ArmInvalidateInstructionCache ();
00831 
00832   // Invalidate all TLB entries so changes are synced
00833   ArmInvalidateTlb ();
00834 
00835   return Status;
00836 }
00837 
00838 
00860 EFI_STATUS
00861 EFIAPI
00862 CpuSetMemoryAttributes (
00863   IN EFI_CPU_ARCH_PROTOCOL     *This,
00864   IN EFI_PHYSICAL_ADDRESS      BaseAddress,
00865   IN UINT64                    Length,
00866   IN UINT64                    Attributes
00867   )
00868 {
00869   DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(%lx, %lx, %lx)\n", BaseAddress, Length, Attributes));
00870   if ( ((BaseAddress & ~TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK) != 0) || ((Length & ~TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK) != 0)){
00871     // minimum granularity is SIZE_4KB (4KB on ARM)
00872     DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(%lx, %lx, %lx): minimum ganularity is SIZE_4KB\n", BaseAddress, Length, Attributes));
00873     return EFI_UNSUPPORTED;
00874   }
00875   
00876   return SetMemoryAttributes (BaseAddress, Length, Attributes, 0);
00877 }
00878 
00879 
00880 
00881 //
00882 // Add a new protocol to support 
00883 //
00884 
00885 EFI_STATUS
00886 EFIAPI
00887 CpuConvertPagesToUncachedVirtualAddress (
00888   IN  VIRTUAL_UNCACHED_PAGES_PROTOCOL   *This,
00889   IN  EFI_PHYSICAL_ADDRESS              Address,
00890   IN  UINTN                             Length,
00891   IN  EFI_PHYSICAL_ADDRESS              VirtualMask,
00892   OUT UINT64                            *Attributes     OPTIONAL
00893   )
00894 {
00895   EFI_STATUS  Status;
00896   EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
00897   
00898   
00899   if (Attributes != NULL) {
00900     Status = gDS->GetMemorySpaceDescriptor (Address, &GcdDescriptor);
00901     if (!EFI_ERROR (Status)) {
00902       *Attributes = GcdDescriptor.Attributes;
00903     }
00904   }
00905 
00906   //
00907   // Make this address range page fault if accessed. If it is a DMA buffer than this would 
00908   // be the PCI address. Code should always use the CPU address, and we will or in VirtualMask
00909   // to that address. 
00910   //
00911   Status = SetMemoryAttributes (Address, Length, EFI_MEMORY_WP, 0);
00912   if (!EFI_ERROR (Status)) {
00913     Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_UC, VirtualMask);
00914   }
00915 
00916   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "ConvertPagesToUncachedVirtualAddress()\n    Unmapped 0x%08lx Mapped 0x%08lx 0x%x bytes\n", Address, Address | VirtualMask, Length));
00917 
00918   return Status;
00919 }
00920 
00921 
00922 EFI_STATUS
00923 EFIAPI
00924 CpuReconvertPages (
00925   IN  VIRTUAL_UNCACHED_PAGES_PROTOCOL   *This,
00926   IN  EFI_PHYSICAL_ADDRESS              Address,
00927   IN  UINTN                             Length,
00928   IN  EFI_PHYSICAL_ADDRESS              VirtualMask,
00929   IN  UINT64                            Attributes
00930   )
00931 {
00932   EFI_STATUS      Status;
00933 
00934   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuReconvertPages(%lx, %x, %lx, %lx)\n", Address, Length, VirtualMask, Attributes));
00935   
00936   //
00937   // Unmap the alaised Address
00938   //
00939   Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_WP, 0);
00940   if (!EFI_ERROR (Status)) {
00941     //
00942     // Restore atttributes
00943     //
00944     Status = SetMemoryAttributes (Address, Length, Attributes, 0);
00945   }
00946   
00947   return Status;
00948 }
00949 
00950 
00951 VIRTUAL_UNCACHED_PAGES_PROTOCOL  gVirtualUncachedPages = {
00952   CpuConvertPagesToUncachedVirtualAddress,
00953   CpuReconvertPages
00954 };
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines