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