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