]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Library/ArmLib/ArmV7/ArmV7Mmu.c
ArmPkg: Made ArmConfigureMmu() returns a status code
[mirror_edk2.git] / ArmPkg / Library / ArmLib / ArmV7 / ArmV7Mmu.c
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 VOID
27 PopulateLevel2PageTable (
28 IN UINT32 *SectionEntry,
29 IN UINT32 PhysicalBase,
30 IN UINT32 RemainLength,
31 IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
32 )
33 {
34 UINT32* PageEntry;
35 UINT32 Pages;
36 UINT32 Index;
37 UINT32 PageAttributes;
38 UINT32 SectionDescriptor;
39 UINT32 TranslationTable;
40 UINT32 BaseSectionAddress;
41
42 switch (Attributes) {
43 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
44 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
45 PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;
46 break;
47 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
48 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
49 PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
50 break;
51 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
52 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
53 PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
54 break;
55 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
56 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
57 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
58 break;
59 default:
60 PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
61 break;
62 }
63
64 // Check if the Section Entry has already been populated. Otherwise attach a
65 // Level 2 Translation Table to it
66 if (*SectionEntry != 0) {
67 // The entry must be a page table. Otherwise it exists an overlapping in the memory map
68 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry)) {
69 TranslationTable = *SectionEntry & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK;
70 } else if ((*SectionEntry & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
71 // Case where a virtual memory map descriptor overlapped a section entry
72
73 // Allocate a Level2 Page Table for this Section
74 TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));
75 TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;
76
77 // Translate the Section Descriptor into Page Descriptor
78 SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
79 SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*SectionEntry,0);
80 SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*SectionEntry);
81 SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(*SectionEntry,0);
82 SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(*SectionEntry);
83 SectionDescriptor |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S(*SectionEntry);
84
85 BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry);
86
87 // Populate the new Level2 Page Table for the section
88 PageEntry = (UINT32*)TranslationTable;
89 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
90 PageEntry[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress + (Index << 12)) | SectionDescriptor;
91 }
92
93 // Overwrite the section entry to point to the new Level2 Translation Table
94 *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |
95 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |
96 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
97 } else {
98 // We do not support the other section type (16MB Section)
99 ASSERT(0);
100 return;
101 }
102 } else {
103 TranslationTable = (UINTN)AllocatePages(EFI_SIZE_TO_PAGES(TRANSLATION_TABLE_PAGE_SIZE + TRANSLATION_TABLE_PAGE_ALIGNMENT));
104 TranslationTable = ((UINTN)TranslationTable + TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK;
105
106 ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE);
107
108 *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |
109 (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |
110 TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
111 }
112
113 PageEntry = ((UINT32 *)(TranslationTable) + ((PhysicalBase & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT));
114 Pages = RemainLength / TT_DESCRIPTOR_PAGE_SIZE;
115
116 for (Index = 0; Index < Pages; Index++) {
117 *PageEntry++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes;
118 PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE;
119 }
120
121 }
122
123 VOID
124 FillTranslationTable (
125 IN UINT32 *TranslationTable,
126 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion
127 )
128 {
129 UINT32 *SectionEntry;
130 UINT32 Attributes;
131 UINT32 PhysicalBase = MemoryRegion->PhysicalBase;
132 UINT32 RemainLength = MemoryRegion->Length;
133
134 ASSERT(MemoryRegion->Length > 0);
135
136 switch (MemoryRegion->Attributes) {
137 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
138 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
139 break;
140 case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
141 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
142 break;
143 case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
144 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0);
145 break;
146 case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
147 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
148 break;
149 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
150 Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
151 break;
152 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
153 Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
154 break;
155 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
156 Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);
157 break;
158 case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
159 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1);
160 break;
161 default:
162 Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
163 break;
164 }
165
166 // Get the first section entry for this mapping
167 SectionEntry = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase);
168
169 while (RemainLength != 0) {
170 if (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE == 0) {
171 if (RemainLength >= TT_DESCRIPTOR_SECTION_SIZE) {
172 // Case: Physical address aligned on the Section Size (1MB) && the length is greater than the Section Size
173 *SectionEntry++ = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase) | Attributes;
174 PhysicalBase += TT_DESCRIPTOR_SECTION_SIZE;
175 } else {
176 // Case: Physical address aligned on the Section Size (1MB) && the length does not fill a section
177 PopulateLevel2PageTable (SectionEntry++, PhysicalBase, RemainLength, MemoryRegion->Attributes);
178
179 // It must be the last entry
180 break;
181 }
182 } else {
183 // Case: Physical address NOT aligned on the Section Size (1MB)
184 PopulateLevel2PageTable (SectionEntry++, PhysicalBase, RemainLength, MemoryRegion->Attributes);
185 // Aligned the address
186 PhysicalBase = (PhysicalBase + TT_DESCRIPTOR_SECTION_SIZE) & ~(TT_DESCRIPTOR_SECTION_SIZE-1);
187
188 // If it is the last entry
189 if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) {
190 break;
191 }
192 }
193 RemainLength -= TT_DESCRIPTOR_SECTION_SIZE;
194 }
195 }
196
197 RETURN_STATUS
198 EFIAPI
199 ArmConfigureMmu (
200 IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,
201 OUT VOID **TranslationTableBase OPTIONAL,
202 OUT UINTN *TranslationTableSize OPTIONAL
203 )
204 {
205 VOID* TranslationTable;
206 ARM_MEMORY_REGION_ATTRIBUTES TranslationTableAttribute;
207 UINT32 TTBRAttributes;
208
209 // Allocate pages for translation table.
210 TranslationTable = AllocatePages (EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE + TRANSLATION_TABLE_SECTION_ALIGNMENT));
211 if (TranslationTable == NULL) {
212 return RETURN_OUT_OF_RESOURCES;
213 }
214 TranslationTable = (VOID*)(((UINTN)TranslationTable + TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK);
215
216 if (TranslationTableBase != NULL) {
217 *TranslationTableBase = TranslationTable;
218 }
219
220 if (TranslationTableSize != NULL) {
221 *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;
222 }
223
224 ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
225
226 ArmCleanInvalidateDataCache ();
227 ArmInvalidateInstructionCache ();
228
229 ArmDisableDataCache ();
230 ArmDisableInstructionCache();
231 // TLBs are also invalidated when calling ArmDisableMmu()
232 ArmDisableMmu ();
233
234 // Make sure nothing sneaked into the cache
235 ArmCleanInvalidateDataCache ();
236 ArmInvalidateInstructionCache ();
237
238 // By default, mark the translation table as belonging to a uncached region
239 TranslationTableAttribute = ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED;
240 while (MemoryTable->Length != 0) {
241 // Find the memory attribute for the Translation Table
242 if (((UINTN)TranslationTable >= MemoryTable->PhysicalBase) && ((UINTN)TranslationTable <= MemoryTable->PhysicalBase - 1 + MemoryTable->Length)) {
243 TranslationTableAttribute = MemoryTable->Attributes;
244 }
245
246 FillTranslationTable (TranslationTable, MemoryTable);
247 MemoryTable++;
248 }
249
250 // Translate the Memory Attributes into Translation Table Register Attributes
251 if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED) ||
252 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED)) {
253 TTBRAttributes = TTBR_NON_CACHEABLE;
254 } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK) ||
255 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK)) {
256 TTBRAttributes = TTBR_WRITE_BACK_ALLOC;
257 } else if ((TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH) ||
258 (TranslationTableAttribute == ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH)) {
259 TTBRAttributes = TTBR_WRITE_THROUGH_NO_ALLOC;
260 } else {
261 ASSERT (0); // No support has been found for the attributes of the memory region that the translation table belongs to.
262 return RETURN_UNSUPPORTED;
263 }
264
265 ArmSetTTBR0 ((VOID *)(UINTN)(((UINTN)TranslationTable & ~TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK) | (TTBRAttributes & 0x7F)));
266
267 ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
268 DOMAIN_ACCESS_CONTROL_NONE(14) |
269 DOMAIN_ACCESS_CONTROL_NONE(13) |
270 DOMAIN_ACCESS_CONTROL_NONE(12) |
271 DOMAIN_ACCESS_CONTROL_NONE(11) |
272 DOMAIN_ACCESS_CONTROL_NONE(10) |
273 DOMAIN_ACCESS_CONTROL_NONE( 9) |
274 DOMAIN_ACCESS_CONTROL_NONE( 8) |
275 DOMAIN_ACCESS_CONTROL_NONE( 7) |
276 DOMAIN_ACCESS_CONTROL_NONE( 6) |
277 DOMAIN_ACCESS_CONTROL_NONE( 5) |
278 DOMAIN_ACCESS_CONTROL_NONE( 4) |
279 DOMAIN_ACCESS_CONTROL_NONE( 3) |
280 DOMAIN_ACCESS_CONTROL_NONE( 2) |
281 DOMAIN_ACCESS_CONTROL_NONE( 1) |
282 DOMAIN_ACCESS_CONTROL_MANAGER(0));
283
284 ArmEnableInstructionCache();
285 ArmEnableDataCache();
286 ArmEnableMmu();
287 return RETURN_SUCCESS;
288 }