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