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