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