ArmPkg/ArmMmuLib ARM: disregard high memory when setting permissions
[mirror_edk2.git] / ArmPkg / Library / ArmMmuLib / Arm / ArmMmuLibCore.c
1 /** @file\r
2 *  File managing the MMU for ARMv7 architecture\r
3 *\r
4 *  Copyright (c) 2011-2016, ARM Limited. All rights reserved.\r
5 *\r
6 *  This program and the accompanying materials\r
7 *  are licensed and made available under the terms and conditions of the BSD License\r
8 *  which accompanies this distribution.  The full text of the license may be found at\r
9 *  http://opensource.org/licenses/bsd-license.php\r
10 *\r
11 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 *\r
14 **/\r
15 \r
16 #include <Uefi.h>\r
17 #include <Chipset/ArmV7.h>\r
18 #include <Library/BaseMemoryLib.h>\r
19 #include <Library/CacheMaintenanceLib.h>\r
20 #include <Library/MemoryAllocationLib.h>\r
21 #include <Library/ArmLib.h>\r
22 #include <Library/BaseLib.h>\r
23 #include <Library/DebugLib.h>\r
24 #include <Library/PcdLib.h>\r
25 \r
26 #define ID_MMFR0_SHARELVL_SHIFT       12\r
27 #define ID_MMFR0_SHARELVL_MASK       0xf\r
28 #define ID_MMFR0_SHARELVL_ONE          0\r
29 #define ID_MMFR0_SHARELVL_TWO          1\r
30 \r
31 #define ID_MMFR0_INNERSHR_SHIFT       28\r
32 #define ID_MMFR0_INNERSHR_MASK       0xf\r
33 #define ID_MMFR0_OUTERSHR_SHIFT        8\r
34 #define ID_MMFR0_OUTERSHR_MASK       0xf\r
35 \r
36 #define ID_MMFR0_SHR_IMP_UNCACHED      0\r
37 #define ID_MMFR0_SHR_IMP_HW_COHERENT   1\r
38 #define ID_MMFR0_SHR_IGNORED         0xf\r
39 \r
40 #define __EFI_MEMORY_RWX               0    // no restrictions\r
41 \r
42 #define CACHE_ATTRIBUTE_MASK   (EFI_MEMORY_UC | \\r
43                                 EFI_MEMORY_WC | \\r
44                                 EFI_MEMORY_WT | \\r
45                                 EFI_MEMORY_WB | \\r
46                                 EFI_MEMORY_UCE | \\r
47                                 EFI_MEMORY_WP)\r
48 \r
49 UINTN\r
50 EFIAPI\r
51 ArmReadIdMmfr0 (\r
52   VOID\r
53   );\r
54 \r
55 BOOLEAN\r
56 EFIAPI\r
57 ArmHasMpExtensions (\r
58   VOID\r
59   );\r
60 \r
61 UINT32\r
62 ConvertSectionAttributesToPageAttributes (\r
63   IN UINT32   SectionAttributes,\r
64   IN BOOLEAN  IsLargePage\r
65   )\r
66 {\r
67   UINT32 PageAttributes;\r
68 \r
69   PageAttributes = 0;\r
70   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes, IsLargePage);\r
71   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes);\r
72   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes, IsLargePage);\r
73   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes);\r
74   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes);\r
75 \r
76   return PageAttributes;\r
77 }\r
78 \r
79 STATIC\r
80 BOOLEAN\r
81 PreferNonshareableMemory (\r
82   VOID\r
83   )\r
84 {\r
85   UINTN   Mmfr;\r
86   UINTN   Val;\r
87 \r
88   if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride)) {\r
89     return TRUE;\r
90   }\r
91 \r
92   //\r
93   // Check whether the innermost level of shareability (the level we will use\r
94   // by default to map normal memory) is implemented with hardware coherency\r
95   // support. Otherwise, revert to mapping as non-shareable.\r
96   //\r
97   Mmfr = ArmReadIdMmfr0 ();\r
98   switch ((Mmfr >> ID_MMFR0_SHARELVL_SHIFT) & ID_MMFR0_SHARELVL_MASK) {\r
99   case ID_MMFR0_SHARELVL_ONE:\r
100     // one level of shareability\r
101     Val = (Mmfr >> ID_MMFR0_OUTERSHR_SHIFT) & ID_MMFR0_OUTERSHR_MASK;\r
102     break;\r
103   case ID_MMFR0_SHARELVL_TWO:\r
104     // two levels of shareability\r
105     Val = (Mmfr >> ID_MMFR0_INNERSHR_SHIFT) & ID_MMFR0_INNERSHR_MASK;\r
106     break;\r
107   default:\r
108     // unexpected value -> shareable is the safe option\r
109     ASSERT (FALSE);\r
110     return FALSE;\r
111   }\r
112   return Val != ID_MMFR0_SHR_IMP_HW_COHERENT;\r
113 }\r
114 \r
115 STATIC\r
116 VOID\r
117 PopulateLevel2PageTable (\r
118   IN UINT32                         *SectionEntry,\r
119   IN UINT32                         PhysicalBase,\r
120   IN UINT32                         RemainLength,\r
121   IN ARM_MEMORY_REGION_ATTRIBUTES   Attributes\r
122   )\r
123 {\r
124   UINT32* PageEntry;\r
125   UINT32  Pages;\r
126   UINT32  Index;\r
127   UINT32  PageAttributes;\r
128   UINT32  SectionDescriptor;\r
129   UINT32  TranslationTable;\r
130   UINT32  BaseSectionAddress;\r
131   UINT32  FirstPageOffset;\r
132 \r
133   switch (Attributes) {\r
134     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:\r
135     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:\r
136       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;\r
137       break;\r
138     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:\r
139     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:\r
140       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;\r
141       PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;\r
142       break;\r
143     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:\r
144     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:\r
145       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;\r
146       break;\r
147     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:\r
148     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:\r
149       PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;\r
150       break;\r
151     case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:\r
152     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:\r
153       PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;\r
154       break;\r
155     default:\r
156       PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;\r
157       break;\r
158   }\r
159 \r
160   if (PreferNonshareableMemory ()) {\r
161     PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;\r
162   }\r
163 \r
164   // Check if the Section Entry has already been populated. Otherwise attach a\r
165   // Level 2 Translation Table to it\r
166   if (*SectionEntry != 0) {\r
167     // The entry must be a page table. Otherwise it exists an overlapping in the memory map\r
168     if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry)) {\r
169       TranslationTable = *SectionEntry & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK;\r
170     } else if ((*SectionEntry & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {\r
171       // Case where a virtual memory map descriptor overlapped a section entry\r
172 \r
173       // Allocate a Level2 Page Table for this Section\r
174       TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));\r
175       TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;\r
176 \r
177       // Translate the Section Descriptor into Page Descriptor\r
178       SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);\r
179 \r
180       BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry);\r
181 \r
182       // Populate the new Level2 Page Table for the section\r
183       PageEntry = (UINT32*)TranslationTable;\r
184       for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {\r
185         PageEntry[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress + (Index << 12)) | SectionDescriptor;\r
186       }\r
187 \r
188       // Overwrite the section entry to point to the new Level2 Translation Table\r
189       *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |\r
190           (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |\r
191           TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;\r
192     } else {\r
193       // We do not support the other section type (16MB Section)\r
194       ASSERT(0);\r
195       return;\r
196     }\r
197   } else {\r
198     TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));\r
199     TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;\r
200 \r
201     ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE);\r
202 \r
203     *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |\r
204         (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |\r
205         TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;\r
206   }\r
207 \r
208   FirstPageOffset = (PhysicalBase & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
209   PageEntry = (UINT32 *)TranslationTable + FirstPageOffset;\r
210   Pages     = RemainLength / TT_DESCRIPTOR_PAGE_SIZE;\r
211 \r
212   ASSERT (FirstPageOffset + Pages <= TRANSLATION_TABLE_PAGE_COUNT);\r
213 \r
214   for (Index = 0; Index < Pages; Index++) {\r
215     *PageEntry++     =  TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes;\r
216     PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE;\r
217   }\r
218 \r
219 }\r
220 \r
221 STATIC\r
222 VOID\r
223 FillTranslationTable (\r
224   IN  UINT32                        *TranslationTable,\r
225   IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryRegion\r
226   )\r
227 {\r
228   UINT32  *SectionEntry;\r
229   UINT32  Attributes;\r
230   UINT32  PhysicalBase;\r
231   UINT64  RemainLength;\r
232   UINT32  PageMapLength;\r
233 \r
234   ASSERT(MemoryRegion->Length > 0);\r
235 \r
236   if (MemoryRegion->PhysicalBase >= SIZE_4GB) {\r
237     return;\r
238   }\r
239 \r
240   PhysicalBase = MemoryRegion->PhysicalBase;\r
241   RemainLength = MIN(MemoryRegion->Length, SIZE_4GB - PhysicalBase);\r
242 \r
243   switch (MemoryRegion->Attributes) {\r
244     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:\r
245       Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);\r
246       break;\r
247     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:\r
248       Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);\r
249       Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;\r
250       break;\r
251     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:\r
252       Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);\r
253       break;\r
254     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:\r
255       Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0);\r
256       break;\r
257     case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:\r
258       Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);\r
259       break;\r
260     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:\r
261       Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);\r
262       break;\r
263     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:\r
264       Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);\r
265       Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;\r
266       break;\r
267     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:\r
268       Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);\r
269       break;\r
270     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:\r
271       Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);\r
272       break;\r
273     case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:\r
274       Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1);\r
275       break;\r
276     default:\r
277       Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);\r
278       break;\r
279   }\r
280 \r
281   if (PreferNonshareableMemory ()) {\r
282     Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;\r
283   }\r
284 \r
285   // Get the first section entry for this mapping\r
286   SectionEntry    = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase);\r
287 \r
288   while (RemainLength != 0) {\r
289     if (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE == 0 &&\r
290         RemainLength >= TT_DESCRIPTOR_SECTION_SIZE) {\r
291       // Case: Physical address aligned on the Section Size (1MB) && the length\r
292       // is greater than the Section Size\r
293       *SectionEntry++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase) | Attributes;\r
294       PhysicalBase += TT_DESCRIPTOR_SECTION_SIZE;\r
295       RemainLength -= TT_DESCRIPTOR_SECTION_SIZE;\r
296     } else {\r
297       PageMapLength = MIN (RemainLength, TT_DESCRIPTOR_SECTION_SIZE -\r
298                                          (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE));\r
299 \r
300       // Case: Physical address aligned on the Section Size (1MB) && the length\r
301       //       does not fill a section\r
302       // Case: Physical address NOT aligned on the Section Size (1MB)\r
303       PopulateLevel2PageTable (SectionEntry++, PhysicalBase, PageMapLength,\r
304         MemoryRegion->Attributes);\r
305 \r
306       // If it is the last entry\r
307       if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) {\r
308         break;\r
309       }\r
310 \r
311       PhysicalBase += PageMapLength;\r
312       RemainLength -= PageMapLength;\r
313     }\r
314   }\r
315 }\r
316 \r
317 RETURN_STATUS\r
318 EFIAPI\r
319 ArmConfigureMmu (\r
320   IN  ARM_MEMORY_REGION_DESCRIPTOR  *MemoryTable,\r
321   OUT VOID                         **TranslationTableBase OPTIONAL,\r
322   OUT UINTN                         *TranslationTableSize OPTIONAL\r
323   )\r
324 {\r
325   VOID*                         TranslationTable;\r
326   ARM_MEMORY_REGION_ATTRIBUTES  TranslationTableAttribute;\r
327   UINT32                        TTBRAttributes;\r
328 \r
329   // Allocate pages for translation table.\r
330   TranslationTable = AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE + TRANSLATION_TABLE_SECTION_ALIGNMENT));\r
331   if (TranslationTable == NULL) {\r
332     return RETURN_OUT_OF_RESOURCES;\r
333   }\r
334   TranslationTable = (VOID*)(((UINTN)TranslationTable + TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK);\r
335 \r
336   if (TranslationTableBase != NULL) {\r
337     *TranslationTableBase = TranslationTable;\r
338   }\r
339 \r
340   if (TranslationTableSize != NULL) {\r
341     *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;\r
342   }\r
343 \r
344   ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);\r
345 \r
346   // By default, mark the translation table as belonging to a uncached region\r
347   TranslationTableAttribute = ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED;\r
348   while (MemoryTable->Length != 0) {\r
349     // Find the memory attribute for the Translation Table\r
350     if (((UINTN)TranslationTable >= MemoryTable->PhysicalBase) && ((UINTN)TranslationTable <= MemoryTable->PhysicalBase - 1 + MemoryTable->Length)) {\r
351       TranslationTableAttribute = MemoryTable->Attributes;\r
352     }\r
353 \r
354     FillTranslationTable (TranslationTable, MemoryTable);\r
355     MemoryTable++;\r
356   }\r
357 \r
358   // Translate the Memory Attributes into Translation Table Register Attributes\r
359   if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) ||\r
360       (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {\r
361     TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC : TTBR_WRITE_BACK_ALLOC;\r
362   } else {\r
363     // Page tables must reside in memory mapped as write-back cacheable\r
364     ASSERT (0);\r
365     return RETURN_UNSUPPORTED;\r
366   }\r
367 \r
368   if (TTBRAttributes & TTBR_SHAREABLE) {\r
369     if (PreferNonshareableMemory ()) {\r
370       TTBRAttributes ^= TTBR_SHAREABLE;\r
371     } else {\r
372       //\r
373       // Unlike the S bit in the short descriptors, which implies inner shareable\r
374       // on an implementation that supports two levels, the meaning of the S bit\r
375       // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.\r
376       // However, we should only set this bit after we have confirmed that the\r
377       // implementation supports multiple levels, or else the NOS bit is UNK/SBZP\r
378       //\r
379       if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {\r
380         TTBRAttributes |= TTBR_NOT_OUTER_SHAREABLE;\r
381       }\r
382     }\r
383   }\r
384 \r
385   ArmCleanInvalidateDataCache ();\r
386   ArmInvalidateInstructionCache ();\r
387 \r
388   ArmDisableDataCache ();\r
389   ArmDisableInstructionCache();\r
390   // TLBs are also invalidated when calling ArmDisableMmu()\r
391   ArmDisableMmu ();\r
392 \r
393   // Make sure nothing sneaked into the cache\r
394   ArmCleanInvalidateDataCache ();\r
395   ArmInvalidateInstructionCache ();\r
396 \r
397   ArmSetTTBR0 ((VOID *)(UINTN)(((UINTN)TranslationTable & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) | (TTBRAttributes & 0x7F)));\r
398 \r
399   //\r
400   // The TTBCR register value is undefined at reset in the Non-Secure world.\r
401   // Writing 0 has the effect of:\r
402   //   Clearing EAE: Use short descriptors, as mandated by specification.\r
403   //   Clearing PD0 and PD1: Translation Table Walk Disable is off.\r
404   //   Clearing N: Perform all translation table walks through TTBR0.\r
405   //               (0 is the default reset value in systems not implementing\r
406   //               the Security Extensions.)\r
407   //\r
408   ArmSetTTBCR (0);\r
409 \r
410   ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |\r
411                              DOMAIN_ACCESS_CONTROL_NONE(14) |\r
412                              DOMAIN_ACCESS_CONTROL_NONE(13) |\r
413                              DOMAIN_ACCESS_CONTROL_NONE(12) |\r
414                              DOMAIN_ACCESS_CONTROL_NONE(11) |\r
415                              DOMAIN_ACCESS_CONTROL_NONE(10) |\r
416                              DOMAIN_ACCESS_CONTROL_NONE( 9) |\r
417                              DOMAIN_ACCESS_CONTROL_NONE( 8) |\r
418                              DOMAIN_ACCESS_CONTROL_NONE( 7) |\r
419                              DOMAIN_ACCESS_CONTROL_NONE( 6) |\r
420                              DOMAIN_ACCESS_CONTROL_NONE( 5) |\r
421                              DOMAIN_ACCESS_CONTROL_NONE( 4) |\r
422                              DOMAIN_ACCESS_CONTROL_NONE( 3) |\r
423                              DOMAIN_ACCESS_CONTROL_NONE( 2) |\r
424                              DOMAIN_ACCESS_CONTROL_NONE( 1) |\r
425                              DOMAIN_ACCESS_CONTROL_CLIENT(0));\r
426 \r
427   ArmEnableInstructionCache();\r
428   ArmEnableDataCache();\r
429   ArmEnableMmu();\r
430   return RETURN_SUCCESS;\r
431 }\r
432 \r
433 STATIC\r
434 EFI_STATUS\r
435 ConvertSectionToPages (\r
436   IN EFI_PHYSICAL_ADDRESS  BaseAddress\r
437   )\r
438 {\r
439   UINT32                  FirstLevelIdx;\r
440   UINT32                  SectionDescriptor;\r
441   UINT32                  PageTableDescriptor;\r
442   UINT32                  PageDescriptor;\r
443   UINT32                  Index;\r
444 \r
445   volatile ARM_FIRST_LEVEL_DESCRIPTOR   *FirstLevelTable;\r
446   volatile ARM_PAGE_TABLE_ENTRY         *PageTable;\r
447 \r
448   DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));\r
449 \r
450   // Obtain page table base\r
451   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
452 \r
453   // Calculate index into first level translation table for start of modification\r
454   FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
455   ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
456 \r
457   // Get section attributes and convert to page attributes\r
458   SectionDescriptor = FirstLevelTable[FirstLevelIdx];\r
459   PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);\r
460 \r
461   // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)\r
462   PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)AllocatePages (1);\r
463   if (PageTable == NULL) {\r
464     return EFI_OUT_OF_RESOURCES;\r
465   }\r
466 \r
467   // Write the page table entries out\r
468   for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {\r
469     PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;\r
470   }\r
471 \r
472   // Formulate page table entry, Domain=0, NS=0\r
473   PageTableDescriptor = (((UINTN)PageTable) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;\r
474 \r
475   // Write the page table entry out, replacing section entry\r
476   FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;\r
477 \r
478   return EFI_SUCCESS;\r
479 }\r
480 \r
481 STATIC\r
482 EFI_STATUS\r
483 UpdatePageEntries (\r
484   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
485   IN  UINT64                    Length,\r
486   IN  UINT64                    Attributes,\r
487   OUT BOOLEAN                   *FlushTlbs OPTIONAL\r
488   )\r
489 {\r
490   EFI_STATUS    Status;\r
491   UINT32        EntryValue;\r
492   UINT32        EntryMask;\r
493   UINT32        FirstLevelIdx;\r
494   UINT32        Offset;\r
495   UINT32        NumPageEntries;\r
496   UINT32        Descriptor;\r
497   UINT32        p;\r
498   UINT32        PageTableIndex;\r
499   UINT32        PageTableEntry;\r
500   UINT32        CurrentPageTableEntry;\r
501   VOID          *Mva;\r
502 \r
503   volatile ARM_FIRST_LEVEL_DESCRIPTOR   *FirstLevelTable;\r
504   volatile ARM_PAGE_TABLE_ENTRY         *PageTable;\r
505 \r
506   Status = EFI_SUCCESS;\r
507 \r
508   // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
509   // EntryValue: values at bit positions specified by EntryMask\r
510   EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK;\r
511   if (Attributes & EFI_MEMORY_XP) {\r
512     EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN;\r
513   } else {\r
514     EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;\r
515   }\r
516 \r
517   // Although the PI spec is unclear on this, the GCD guarantees that only\r
518   // one Attribute bit is set at a time, so the order of the conditionals below\r
519   // is irrelevant. If no memory attribute is specified, we preserve whatever\r
520   // memory type is set in the page tables, and update the permission attributes\r
521   // only.\r
522   if (Attributes & EFI_MEMORY_UC) {\r
523     // modify cacheability attributes\r
524     EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
525     // map to strongly ordered\r
526     EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
527   } else if (Attributes & EFI_MEMORY_WC) {\r
528     // modify cacheability attributes\r
529     EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
530     // map to normal non-cachable\r
531     EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
532   } else if (Attributes & EFI_MEMORY_WT) {\r
533     // modify cacheability attributes\r
534     EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
535     // write through with no-allocate\r
536     EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
537   } else if (Attributes & EFI_MEMORY_WB) {\r
538     // modify cacheability attributes\r
539     EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;\r
540     // write back (with allocate)\r
541     EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
542   } else if (Attributes & CACHE_ATTRIBUTE_MASK) {\r
543     // catch unsupported memory type attributes\r
544     ASSERT (FALSE);\r
545     return EFI_UNSUPPORTED;\r
546   }\r
547 \r
548   if (Attributes & EFI_MEMORY_RO) {\r
549     EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;\r
550   } else {\r
551     EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;\r
552   }\r
553 \r
554   // Obtain page table base\r
555   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
556 \r
557   // Calculate number of 4KB page table entries to change\r
558   NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;\r
559 \r
560   // Iterate for the number of 4KB pages to change\r
561   Offset = 0;\r
562   for(p = 0; p < NumPageEntries; p++) {\r
563     // Calculate index into first level translation table for page table value\r
564 \r
565     FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
566     ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
567 \r
568     // Read the descriptor from the first level page table\r
569     Descriptor = FirstLevelTable[FirstLevelIdx];\r
570 \r
571     // Does this descriptor need to be converted from section entry to 4K pages?\r
572     if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {\r
573       Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
574       if (EFI_ERROR(Status)) {\r
575         // Exit for loop\r
576         break;\r
577       }\r
578 \r
579       // Re-read descriptor\r
580       Descriptor = FirstLevelTable[FirstLevelIdx];\r
581       if (FlushTlbs != NULL) {\r
582         *FlushTlbs = TRUE;\r
583       }\r
584     }\r
585 \r
586     // Obtain page table base address\r
587     PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);\r
588 \r
589     // Calculate index into the page table\r
590     PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;\r
591     ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);\r
592 \r
593     // Get the entry\r
594     CurrentPageTableEntry = PageTable[PageTableIndex];\r
595 \r
596     // Mask off appropriate fields\r
597     PageTableEntry = CurrentPageTableEntry & ~EntryMask;\r
598 \r
599     // Mask in new attributes and/or permissions\r
600     PageTableEntry |= EntryValue;\r
601 \r
602     if (CurrentPageTableEntry  != PageTableEntry) {\r
603       Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));\r
604 \r
605       // Only need to update if we are changing the entry\r
606       PageTable[PageTableIndex] = PageTableEntry;\r
607       ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);\r
608     }\r
609 \r
610     Status = EFI_SUCCESS;\r
611     Offset += TT_DESCRIPTOR_PAGE_SIZE;\r
612 \r
613   } // End first level translation table loop\r
614 \r
615   return Status;\r
616 }\r
617 \r
618 STATIC\r
619 EFI_STATUS\r
620 UpdateSectionEntries (\r
621   IN EFI_PHYSICAL_ADDRESS      BaseAddress,\r
622   IN UINT64                    Length,\r
623   IN UINT64                    Attributes\r
624   )\r
625 {\r
626   EFI_STATUS    Status = EFI_SUCCESS;\r
627   UINT32        EntryMask;\r
628   UINT32        EntryValue;\r
629   UINT32        FirstLevelIdx;\r
630   UINT32        NumSections;\r
631   UINT32        i;\r
632   UINT32        CurrentDescriptor;\r
633   UINT32        Descriptor;\r
634   VOID          *Mva;\r
635   volatile ARM_FIRST_LEVEL_DESCRIPTOR   *FirstLevelTable;\r
636 \r
637   // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
638   // EntryValue: values at bit positions specified by EntryMask\r
639 \r
640   // Make sure we handle a section range that is unmapped\r
641   EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK | TT_DESCRIPTOR_SECTION_XN_MASK |\r
642               TT_DESCRIPTOR_SECTION_AP_MASK;\r
643   EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;\r
644 \r
645   // Although the PI spec is unclear on this, the GCD guarantees that only\r
646   // one Attribute bit is set at a time, so the order of the conditionals below\r
647   // is irrelevant. If no memory attribute is specified, we preserve whatever\r
648   // memory type is set in the page tables, and update the permission attributes\r
649   // only.\r
650   if (Attributes & EFI_MEMORY_UC) {\r
651     // modify cacheability attributes\r
652     EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
653     // map to strongly ordered\r
654     EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0\r
655   } else if (Attributes & EFI_MEMORY_WC) {\r
656     // modify cacheability attributes\r
657     EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
658     // map to normal non-cachable\r
659     EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0\r
660   } else if (Attributes & EFI_MEMORY_WT) {\r
661     // modify cacheability attributes\r
662     EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
663     // write through with no-allocate\r
664     EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0\r
665   } else if (Attributes & EFI_MEMORY_WB) {\r
666     // modify cacheability attributes\r
667     EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;\r
668     // write back (with allocate)\r
669     EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1\r
670   } else if (Attributes & CACHE_ATTRIBUTE_MASK) {\r
671     // catch unsupported memory type attributes\r
672     ASSERT (FALSE);\r
673     return EFI_UNSUPPORTED;\r
674   }\r
675 \r
676   if (Attributes & EFI_MEMORY_RO) {\r
677     EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO;\r
678   } else {\r
679     EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW;\r
680   }\r
681 \r
682   if (Attributes & EFI_MEMORY_XP) {\r
683     EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;\r
684   }\r
685 \r
686   // obtain page table base\r
687   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();\r
688 \r
689   // calculate index into first level translation table for start of modification\r
690   FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
691   ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);\r
692 \r
693   // calculate number of 1MB first level entries this applies to\r
694   NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;\r
695 \r
696   // iterate through each descriptor\r
697   for(i=0; i<NumSections; i++) {\r
698     CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];\r
699 \r
700     // has this descriptor already been coverted to pages?\r
701     if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {\r
702       // forward this 1MB range to page table function instead\r
703       Status = UpdatePageEntries (\r
704                  (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT,\r
705                  TT_DESCRIPTOR_SECTION_SIZE,\r
706                  Attributes,\r
707                  NULL);\r
708     } else {\r
709       // still a section entry\r
710 \r
711       if (CurrentDescriptor != 0) {\r
712         // mask off appropriate fields\r
713         Descriptor = CurrentDescriptor & ~EntryMask;\r
714       } else {\r
715         Descriptor = ((UINTN)FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT;\r
716       }\r
717 \r
718       // mask in new attributes and/or permissions\r
719       Descriptor |= EntryValue;\r
720 \r
721       if (CurrentDescriptor  != Descriptor) {\r
722         Mva = (VOID *)(UINTN)(((UINTN)FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);\r
723 \r
724         // Only need to update if we are changing the descriptor\r
725         FirstLevelTable[FirstLevelIdx + i] = Descriptor;\r
726         ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);\r
727       }\r
728 \r
729       Status = EFI_SUCCESS;\r
730     }\r
731   }\r
732 \r
733   return Status;\r
734 }\r
735 \r
736 EFI_STATUS\r
737 ArmSetMemoryAttributes (\r
738   IN EFI_PHYSICAL_ADDRESS      BaseAddress,\r
739   IN UINT64                    Length,\r
740   IN UINT64                    Attributes\r
741   )\r
742 {\r
743   EFI_STATUS    Status;\r
744   UINT64        ChunkLength;\r
745   BOOLEAN       FlushTlbs;\r
746 \r
747   if (BaseAddress > (UINT64)MAX_ADDRESS - Length + 1) {\r
748     return EFI_UNSUPPORTED;\r
749   }\r
750 \r
751   if (Length == 0) {\r
752     return EFI_SUCCESS;\r
753   }\r
754 \r
755   FlushTlbs = FALSE;\r
756   while (Length > 0) {\r
757     if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&\r
758         Length >= TT_DESCRIPTOR_SECTION_SIZE) {\r
759 \r
760       ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;\r
761 \r
762       DEBUG ((DEBUG_PAGE,\r
763         "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",\r
764         BaseAddress, ChunkLength, Attributes));\r
765 \r
766       Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes);\r
767 \r
768       FlushTlbs = TRUE;\r
769     } else {\r
770 \r
771       //\r
772       // Process page by page until the next section boundary, but only if\r
773       // we have more than a section's worth of area to deal with after that.\r
774       //\r
775       ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -\r
776                     (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);\r
777       if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {\r
778         ChunkLength = Length;\r
779       }\r
780 \r
781       DEBUG ((DEBUG_PAGE,\r
782         "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",\r
783         BaseAddress, ChunkLength, Attributes));\r
784 \r
785       Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,\r
786                  &FlushTlbs);\r
787     }\r
788 \r
789     if (EFI_ERROR (Status)) {\r
790       break;\r
791     }\r
792 \r
793     BaseAddress += ChunkLength;\r
794     Length -= ChunkLength;\r
795   }\r
796 \r
797   if (FlushTlbs) {\r
798     ArmInvalidateTlb ();\r
799   }\r
800   return Status;\r
801 }\r
802 \r
803 EFI_STATUS\r
804 ArmSetMemoryRegionNoExec (\r
805   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
806   IN  UINT64                    Length\r
807   )\r
808 {\r
809   return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);\r
810 }\r
811 \r
812 EFI_STATUS\r
813 ArmClearMemoryRegionNoExec (\r
814   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
815   IN  UINT64                    Length\r
816   )\r
817 {\r
818   return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);\r
819 }\r
820 \r
821 EFI_STATUS\r
822 ArmSetMemoryRegionReadOnly (\r
823   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
824   IN  UINT64                    Length\r
825   )\r
826 {\r
827   return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO);\r
828 }\r
829 \r
830 EFI_STATUS\r
831 ArmClearMemoryRegionReadOnly (\r
832   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,\r
833   IN  UINT64                    Length\r
834   )\r
835 {\r
836   return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);\r
837 }\r
838 \r
839 RETURN_STATUS\r
840 EFIAPI\r
841 ArmMmuBaseLibConstructor (\r
842   VOID\r
843   )\r
844 {\r
845   return RETURN_SUCCESS;\r
846 }\r