]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/ArmLib/ArmV7/ArmV7Mmu.c
ArmPkg: introduce base ArmMmuLib implementation
[mirror_edk2.git] / ArmPkg / Library / ArmLib / ArmV7 / ArmV7Mmu.c
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 "ArmV7Lib.h"
24 #include "ArmLibPrivate.h"
25
26 UINT32
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
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
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
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
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 //
351 // The TTBCR register value is undefined at reset in the Non-Secure world.
352 // Writing 0 has the effect of:
353 // Clearing EAE: Use short descriptors, as mandated by specification.
354 // Clearing PD0 and PD1: Translation Table Walk Disable is off.
355 // Clearing N: Perform all translation table walks through TTBR0.
356 // (0 is the default reset value in systems not implementing
357 // the Security Extensions.)
358 //
359 ArmSetTTBCR (0);
360
361 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
362 DOMAIN_ACCESS_CONTROL_NONE(14) |
363 DOMAIN_ACCESS_CONTROL_NONE(13) |
364 DOMAIN_ACCESS_CONTROL_NONE(12) |
365 DOMAIN_ACCESS_CONTROL_NONE(11) |
366 DOMAIN_ACCESS_CONTROL_NONE(10) |
367 DOMAIN_ACCESS_CONTROL_NONE( 9) |
368 DOMAIN_ACCESS_CONTROL_NONE( 8) |
369 DOMAIN_ACCESS_CONTROL_NONE( 7) |
370 DOMAIN_ACCESS_CONTROL_NONE( 6) |
371 DOMAIN_ACCESS_CONTROL_NONE( 5) |
372 DOMAIN_ACCESS_CONTROL_NONE( 4) |
373 DOMAIN_ACCESS_CONTROL_NONE( 3) |
374 DOMAIN_ACCESS_CONTROL_NONE( 2) |
375 DOMAIN_ACCESS_CONTROL_NONE( 1) |
376 DOMAIN_ACCESS_CONTROL_CLIENT(0));
377
378 ArmEnableInstructionCache();
379 ArmEnableDataCache();
380 ArmEnableMmu();
381 return RETURN_SUCCESS;
382 }
383
384 RETURN_STATUS
385 ArmSetMemoryRegionNoExec (
386 IN EFI_PHYSICAL_ADDRESS BaseAddress,
387 IN UINT64 Length
388 )
389 {
390 return RETURN_UNSUPPORTED;
391 }
392
393 RETURN_STATUS
394 ArmClearMemoryRegionNoExec (
395 IN EFI_PHYSICAL_ADDRESS BaseAddress,
396 IN UINT64 Length
397 )
398 {
399 return RETURN_UNSUPPORTED;
400 }
401
402 RETURN_STATUS
403 ArmSetMemoryRegionReadOnly (
404 IN EFI_PHYSICAL_ADDRESS BaseAddress,
405 IN UINT64 Length
406 )
407 {
408 return RETURN_UNSUPPORTED;
409 }
410
411 RETURN_STATUS
412 ArmClearMemoryRegionReadOnly (
413 IN EFI_PHYSICAL_ADDRESS BaseAddress,
414 IN UINT64 Length
415 )
416 {
417 return RETURN_UNSUPPORTED;
418 }