• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 *  File managing the MMU for ARMv7 architecture
3 *
4 *  Copyright (c) 2011-2016, ARM Limited. All rights reserved.
5 *
6 *  This program and the accompanying materials
7 *  are licensed and made available under the terms and conditions of the BSD License
8 *  which accompanies this distribution.  The full text of the license may be found at
9 *  http://opensource.org/licenses/bsd-license.php
10 *
11 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 *
14 **/
15 
16 #include <Uefi.h>
17 #include <Chipset/ArmV7.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/ArmLib.h>
21 #include <Library/BaseLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/PcdLib.h>
24 
25 #define ID_MMFR0_SHARELVL_SHIFT       12
26 #define ID_MMFR0_SHARELVL_MASK       0xf
27 #define ID_MMFR0_SHARELVL_ONE          0
28 #define ID_MMFR0_SHARELVL_TWO          1
29 
30 #define ID_MMFR0_INNERSHR_SHIFT       28
31 #define ID_MMFR0_INNERSHR_MASK       0xf
32 #define ID_MMFR0_OUTERSHR_SHIFT        8
33 #define ID_MMFR0_OUTERSHR_MASK       0xf
34 
35 #define ID_MMFR0_SHR_IMP_UNCACHED      0
36 #define ID_MMFR0_SHR_IMP_HW_COHERENT   1
37 #define ID_MMFR0_SHR_IGNORED         0xf
38 
39 UINTN
40 EFIAPI
41 ArmReadIdMmfr0 (
42   VOID
43   );
44 
45 BOOLEAN
46 EFIAPI
47 ArmHasMpExtensions (
48   VOID
49   );
50 
51 UINT32
ConvertSectionAttributesToPageAttributes(IN UINT32 SectionAttributes,IN BOOLEAN IsLargePage)52 ConvertSectionAttributesToPageAttributes (
53   IN UINT32   SectionAttributes,
54   IN BOOLEAN  IsLargePage
55   )
56 {
57   UINT32 PageAttributes;
58 
59   PageAttributes = 0;
60   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes, IsLargePage);
61   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes);
62   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes, IsLargePage);
63   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes);
64   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes);
65 
66   return PageAttributes;
67 }
68 
69 STATIC
70 BOOLEAN
PreferNonshareableMemory(VOID)71 PreferNonshareableMemory (
72   VOID
73   )
74 {
75   UINTN   Mmfr;
76   UINTN   Val;
77 
78   if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride)) {
79     return TRUE;
80   }
81 
82   //
83   // Check whether the innermost level of shareability (the level we will use
84   // by default to map normal memory) is implemented with hardware coherency
85   // support. Otherwise, revert to mapping as non-shareable.
86   //
87   Mmfr = ArmReadIdMmfr0 ();
88   switch ((Mmfr >> ID_MMFR0_SHARELVL_SHIFT) & ID_MMFR0_SHARELVL_MASK) {
89   case ID_MMFR0_SHARELVL_ONE:
90     // one level of shareability
91     Val = (Mmfr >> ID_MMFR0_OUTERSHR_SHIFT) & ID_MMFR0_OUTERSHR_MASK;
92     break;
93   case ID_MMFR0_SHARELVL_TWO:
94     // two levels of shareability
95     Val = (Mmfr >> ID_MMFR0_INNERSHR_SHIFT) & ID_MMFR0_INNERSHR_MASK;
96     break;
97   default:
98     // unexpected value -> shareable is the safe option
99     ASSERT (FALSE);
100     return FALSE;
101   }
102   return Val != ID_MMFR0_SHR_IMP_HW_COHERENT;
103 }
104 
105 STATIC
106 VOID
PopulateLevel2PageTable(IN UINT32 * SectionEntry,IN UINT32 PhysicalBase,IN UINT32 RemainLength,IN ARM_MEMORY_REGION_ATTRIBUTES Attributes)107 PopulateLevel2PageTable (
108   IN UINT32                         *SectionEntry,
109   IN UINT32                         PhysicalBase,
110   IN UINT32                         RemainLength,
111   IN ARM_MEMORY_REGION_ATTRIBUTES   Attributes
112   )
113 {
114   UINT32* PageEntry;
115   UINT32  Pages;
116   UINT32  Index;
117   UINT32  PageAttributes;
118   UINT32  SectionDescriptor;
119   UINT32  TranslationTable;
120   UINT32  BaseSectionAddress;
121 
122   switch (Attributes) {
123     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
124     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
125       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;
126       break;
127     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
128     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
129       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
130       break;
131     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
132     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
133       PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
134       break;
135     case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
136     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
137       PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
138       break;
139     default:
140       PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
141       break;
142   }
143 
144   if (PreferNonshareableMemory ()) {
145     PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
146   }
147 
148   // Check if the Section Entry has already been populated. Otherwise attach a
149   // Level 2 Translation Table to it
150   if (*SectionEntry != 0) {
151     // The entry must be a page table. Otherwise it exists an overlapping in the memory map
152     if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry)) {
153       TranslationTable = *SectionEntry & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK;
154     } else if ((*SectionEntry & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
155       // Case where a virtual memory map descriptor overlapped a section entry
156 
157       // Allocate a Level2 Page Table for this Section
158       TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));
159       TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;
160 
161       // Translate the Section Descriptor into Page Descriptor
162       SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);
163 
164       BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry);
165 
166       // Populate the new Level2 Page Table for the section
167       PageEntry = (UINT32*)TranslationTable;
168       for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
169         PageEntry[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress + (Index << 12)) | SectionDescriptor;
170       }
171 
172       // Overwrite the section entry to point to the new Level2 Translation Table
173       *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |
174           (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |
175           TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
176     } else {
177       // We do not support the other section type (16MB Section)
178       ASSERT(0);
179       return;
180     }
181   } else {
182     TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));
183     TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;
184 
185     ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE);
186 
187     *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |
188         (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |
189         TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
190   }
191 
192   PageEntry = ((UINT32 *)(TranslationTable) + ((PhysicalBase & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT));
193   Pages     = RemainLength / TT_DESCRIPTOR_PAGE_SIZE;
194 
195   for (Index = 0; Index < Pages; Index++) {
196     *PageEntry++     =  TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes;
197     PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE;
198   }
199 
200 }
201 
202 STATIC
203 VOID
FillTranslationTable(IN UINT32 * TranslationTable,IN ARM_MEMORY_REGION_DESCRIPTOR * MemoryRegion)204 FillTranslationTable (
205   IN  UINT32                        *TranslationTable,
206   IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryRegion
207   )
208 {
209   UINT32  *SectionEntry;
210   UINT32  Attributes;
211   UINT32  PhysicalBase;
212   UINT64  RemainLength;
213 
214   ASSERT(MemoryRegion->Length > 0);
215 
216   if (MemoryRegion->PhysicalBase >= SIZE_4GB) {
217     return;
218   }
219 
220   PhysicalBase = MemoryRegion->PhysicalBase;
221   RemainLength = MIN(MemoryRegion->Length, SIZE_4GB - PhysicalBase);
222 
223   switch (MemoryRegion->Attributes) {
224     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
225       Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
226       break;
227     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
228       Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
229       break;
230     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
231       Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0);
232       break;
233     case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
234       Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
235       break;
236     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
237       Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
238       break;
239     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
240       Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
241       break;
242     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
243       Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);
244       break;
245     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
246       Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1);
247       break;
248     default:
249       Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
250       break;
251   }
252 
253   if (PreferNonshareableMemory ()) {
254     Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
255   }
256 
257   // Get the first section entry for this mapping
258   SectionEntry    = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase);
259 
260   while (RemainLength != 0) {
261     if (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE == 0) {
262       if (RemainLength >= TT_DESCRIPTOR_SECTION_SIZE) {
263         // Case: Physical address aligned on the Section Size (1MB) && the length is greater than the Section Size
264         *SectionEntry++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase) | Attributes;
265         PhysicalBase += TT_DESCRIPTOR_SECTION_SIZE;
266       } else {
267         // Case: Physical address aligned on the Section Size (1MB) && the length does not fill a section
268         PopulateLevel2PageTable (SectionEntry++, PhysicalBase, RemainLength, MemoryRegion->Attributes);
269 
270         // It must be the last entry
271         break;
272       }
273     } else {
274       // Case: Physical address NOT aligned on the Section Size (1MB)
275       PopulateLevel2PageTable (SectionEntry++, PhysicalBase, RemainLength, MemoryRegion->Attributes);
276       // Aligned the address
277       PhysicalBase = (PhysicalBase + TT_DESCRIPTOR_SECTION_SIZE) & ~(TT_DESCRIPTOR_SECTION_SIZE-1);
278 
279       // If it is the last entry
280       if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) {
281         break;
282       }
283     }
284     RemainLength -= TT_DESCRIPTOR_SECTION_SIZE;
285   }
286 }
287 
288 RETURN_STATUS
289 EFIAPI
ArmConfigureMmu(IN ARM_MEMORY_REGION_DESCRIPTOR * MemoryTable,OUT VOID ** TranslationTableBase OPTIONAL,OUT UINTN * TranslationTableSize OPTIONAL)290 ArmConfigureMmu (
291   IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryTable,
292   OUT VOID                         **TranslationTableBase OPTIONAL,
293   OUT UINTN                         *TranslationTableSize OPTIONAL
294   )
295 {
296   VOID*                         TranslationTable;
297   ARM_MEMORY_REGION_ATTRIBUTES  TranslationTableAttribute;
298   UINT32                        TTBRAttributes;
299 
300   // Allocate pages for translation table.
301   TranslationTable = AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE + TRANSLATION_TABLE_SECTION_ALIGNMENT));
302   if (TranslationTable == NULL) {
303     return RETURN_OUT_OF_RESOURCES;
304   }
305   TranslationTable = (VOID*)(((UINTN)TranslationTable + TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK);
306 
307   if (TranslationTableBase != NULL) {
308     *TranslationTableBase = TranslationTable;
309   }
310 
311   if (TranslationTableSize != NULL) {
312     *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;
313   }
314 
315   ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
316 
317   // By default, mark the translation table as belonging to a uncached region
318   TranslationTableAttribute = ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED;
319   while (MemoryTable->Length != 0) {
320     // Find the memory attribute for the Translation Table
321     if (((UINTN)TranslationTable >= MemoryTable->PhysicalBase) && ((UINTN)TranslationTable <= MemoryTable->PhysicalBase - 1 + MemoryTable->Length)) {
322       TranslationTableAttribute = MemoryTable->Attributes;
323     }
324 
325     FillTranslationTable (TranslationTable, MemoryTable);
326     MemoryTable++;
327   }
328 
329   // Translate the Memory Attributes into Translation Table Register Attributes
330   if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED) ||
331       (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED)) {
332     TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_NON_CACHEABLE : TTBR_NON_CACHEABLE;
333   } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) ||
334       (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {
335     TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC : TTBR_WRITE_BACK_ALLOC;
336   } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH) ||
337       (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH)) {
338     TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_THROUGH : TTBR_WRITE_THROUGH;
339   } else {
340     ASSERT (0); // No support has been found for the attributes of the memory region that the translation table belongs to.
341     return RETURN_UNSUPPORTED;
342   }
343 
344   if (TTBRAttributes & TTBR_SHAREABLE) {
345     if (PreferNonshareableMemory ()) {
346       TTBRAttributes ^= TTBR_SHAREABLE;
347     } else {
348       //
349       // Unlike the S bit in the short descriptors, which implies inner shareable
350       // on an implementation that supports two levels, the meaning of the S bit
351       // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
352       // However, we should only set this bit after we have confirmed that the
353       // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
354       //
355       if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
356         TTBRAttributes |= TTBR_NOT_OUTER_SHAREABLE;
357       }
358     }
359   }
360 
361   ArmCleanInvalidateDataCache ();
362   ArmInvalidateInstructionCache ();
363 
364   ArmDisableDataCache ();
365   ArmDisableInstructionCache();
366   // TLBs are also invalidated when calling ArmDisableMmu()
367   ArmDisableMmu ();
368 
369   // Make sure nothing sneaked into the cache
370   ArmCleanInvalidateDataCache ();
371   ArmInvalidateInstructionCache ();
372 
373   ArmSetTTBR0 ((VOID *)(UINTN)(((UINTN)TranslationTable & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) | (TTBRAttributes & 0x7F)));
374 
375   //
376   // The TTBCR register value is undefined at reset in the Non-Secure world.
377   // Writing 0 has the effect of:
378   //   Clearing EAE: Use short descriptors, as mandated by specification.
379   //   Clearing PD0 and PD1: Translation Table Walk Disable is off.
380   //   Clearing N: Perform all translation table walks through TTBR0.
381   //               (0 is the default reset value in systems not implementing
382   //               the Security Extensions.)
383   //
384   ArmSetTTBCR (0);
385 
386   ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
387                              DOMAIN_ACCESS_CONTROL_NONE(14) |
388                              DOMAIN_ACCESS_CONTROL_NONE(13) |
389                              DOMAIN_ACCESS_CONTROL_NONE(12) |
390                              DOMAIN_ACCESS_CONTROL_NONE(11) |
391                              DOMAIN_ACCESS_CONTROL_NONE(10) |
392                              DOMAIN_ACCESS_CONTROL_NONE( 9) |
393                              DOMAIN_ACCESS_CONTROL_NONE( 8) |
394                              DOMAIN_ACCESS_CONTROL_NONE( 7) |
395                              DOMAIN_ACCESS_CONTROL_NONE( 6) |
396                              DOMAIN_ACCESS_CONTROL_NONE( 5) |
397                              DOMAIN_ACCESS_CONTROL_NONE( 4) |
398                              DOMAIN_ACCESS_CONTROL_NONE( 3) |
399                              DOMAIN_ACCESS_CONTROL_NONE( 2) |
400                              DOMAIN_ACCESS_CONTROL_NONE( 1) |
401                              DOMAIN_ACCESS_CONTROL_CLIENT(0));
402 
403   ArmEnableInstructionCache();
404   ArmEnableDataCache();
405   ArmEnableMmu();
406   return RETURN_SUCCESS;
407 }
408 
409 RETURN_STATUS
ArmSetMemoryRegionNoExec(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length)410 ArmSetMemoryRegionNoExec (
411   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,
412   IN  UINT64                    Length
413   )
414 {
415   return RETURN_UNSUPPORTED;
416 }
417 
418 RETURN_STATUS
ArmClearMemoryRegionNoExec(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length)419 ArmClearMemoryRegionNoExec (
420   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,
421   IN  UINT64                    Length
422   )
423 {
424   return RETURN_UNSUPPORTED;
425 }
426 
427 RETURN_STATUS
ArmSetMemoryRegionReadOnly(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length)428 ArmSetMemoryRegionReadOnly (
429   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,
430   IN  UINT64                    Length
431   )
432 {
433   return RETURN_UNSUPPORTED;
434 }
435 
436 RETURN_STATUS
ArmClearMemoryRegionReadOnly(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length)437 ArmClearMemoryRegionReadOnly (
438   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,
439   IN  UINT64                    Length
440   )
441 {
442   return RETURN_UNSUPPORTED;
443 }
444 
445 RETURN_STATUS
446 EFIAPI
ArmMmuBaseLibConstructor(VOID)447 ArmMmuBaseLibConstructor (
448   VOID
449   )
450 {
451   return RETURN_SUCCESS;
452 }
453