]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/CpuDxe/Mmu.c
Clean up format of comments
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / Mmu.c
CommitLineData
aeb61534
A
1/*++\r
2\r
3Copyright (c) 2009, Hewlett-Packard Company \r
4Portions copyright (c) 2010, Apple Inc. All rights reserved.\r
5\r
6All rights reserved. This program and the accompanying materials \r
7are licensed and made available under the terms and conditions of the BSD License \r
8which accompanies this distribution. The full text of the license may be found at \r
9http://opensource.org/licenses/bsd-license.php \r
10 \r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
13\r
14\r
15--*/\r
16\r
17#include "CpuDxe.h"\r
18\r
f659880b
A
19//\r
20// For debug switch me back to to EFI_D_PAGE when done\r
21//\r
22#define L_EFI_D_PAGE EFI_D_ERROR\r
23\r
aeb61534
A
24//\r
25// Translation/page table definitions\r
26//\r
27\r
28// First Level Descriptors\r
29typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR;\r
30\r
31// memory space covered by a first level descriptor\r
32#define ARM_PAGE_DESC_ENTRY_MVA_SIZE 0x00100000 // 1MB\r
33\r
34// number of first level descriptors to cover entire 32-bit memory space\r
35#define FIRST_LEVEL_ENTRY_COUNT (0xFFFFFFFF / ARM_PAGE_DESC_ENTRY_MVA_SIZE + 1)\r
36\r
37\r
38// page table 1st level descriptor entries\r
39#define ARM_PAGE_DESC_BASE_MASK 0xFFFFFC00\r
40#define ARM_PAGE_DESC_BASE_SHFIT 10\r
41#define ARM_PAGE_DESC_DOMAIN_MASK 0x000001E0\r
42#define ARM_PAGE_DESC_DOMAIN_SHIFT 5\r
43#define ARM_PAGE_DESC_NS 0x00000008\r
44\r
45#define ARM_FIRST_LEVEL_DESC_ALIGN 0x00004000 // 16KB\r
46\r
47// section 1st level desriptor entries\r
48#define ARM_SECTION_BASE_MASK 0xFFF00000\r
49#define ARM_SECTION_BASE_SHIFT 20\r
50#define ARM_SECTION_NS 0x00080000\r
51#define ARM_SECTION_nG 0x00020000\r
52#define ARM_SECTION_S 0x00010000\r
53#define ARM_SECTION_AP2 0x00008000\r
54#define ARM_SECTION_TEX_MASK 0x00007000\r
55#define ARM_SECTION_TEX_SHIFT 12\r
56#define ARM_SECTION_AP10_MASK 0x00000C00\r
57#define ARM_SECTION_AP10_SHIFT 10\r
58#define ARM_SECTION_DOMAIN_MASK 0x000001E0\r
59#define ARM_SECTION_DOMAIN_SHIFT 5\r
60#define ARM_SECTION_XN 0x00000010\r
61#define ARM_SECTION_C 0x00000008\r
62#define ARM_SECTION_B 0x00000004\r
63\r
64// section level AP[2:0] definitions\r
65#define ARM_SECTION_AP_NO_ACCESS 0 // AP[2:0] = 0\r
66#define ARM_SECTION_AP_READ_WRITE ARM_SECTION_AP10_MASK // AP[2:0] = 011\r
67#define ARM_SECTION_AP_READ_ONLY (ARM_SECTION_AP2 | ARM_SECTION_AP10_MASK) // AP[2:0] = 111\r
68\r
69// common 1st level descriptor fields\r
70#define ARM_DESC_TYPE_MASK 0x00000003\r
71\r
72// descriptor type values\r
73#define ARM_DESC_TYPE_FAULT 0x0\r
74#define ARM_DESC_TYPE_PAGE_TABLE 0x1\r
75#define ARM_DESC_TYPE_SECTION 0x2\r
76\r
77\r
78// Second Level Descriptors\r
79typedef UINT32 ARM_PAGE_TABLE_ENTRY;\r
80\r
81// small page 2nd level descriptor entries\r
82#define ARM_SMALL_PAGE_BASE_MASK 0xFFFFF000\r
83#define ARM_SMALL_PAGE_INDEX_MASK 0x000FF000\r
84#define ARM_SMALL_PAGE_BASE_SHIFT 12\r
85#define ARM_SMALL_PAGE_TEX_MASK 0x000001C0\r
86#define ARM_SMALL_PAGE_TEX_SHIFT 6\r
87#define ARM_SMALL_PAGE_XN 0x00000001\r
88\r
89// large page 2nd level descriptor entries\r
90#define ARM_LARGE_PAGE_BASE_MASK 0xFFFF0000\r
91#define ARM_LARGE_PAGE_BASE_SHIFT 16\r
92#define ARM_LARGE_PAGE_TEX_MASK 0x00007000\r
93#define ARM_LARGE_PAGE_TEX_SHIFT 12\r
94#define ARM_LARGE_PAGE_XN 0x00008000\r
95\r
96// common 2nd level desriptor fields\r
97#define ARM_PAGE_nG 0x00000800\r
98#define ARM_PAGE_S 0x00000400\r
99#define ARM_PAGE_AP2 0x00000200\r
100#define ARM_PAGE_AP10_MASK 0x00000030\r
101#define ARM_PAGE_AP10_SHIFT 4\r
102#define ARM_PAGE_C 0x00000008\r
103#define ARM_PAGE_B 0x00000004\r
104#define ARM_PAGE_DESC_TYPE_MASK 0x00000003\r
105\r
106// descriptor type values\r
107#define ARM_PAGE_TYPE_FAULT 0x0\r
108#define ARM_PAGE_TYPE_LARGE 0x1\r
109#define ARM_PAGE_TYPE_SMALL 0x2\r
110#define ARM_PAGE_TYPE_SMALL_XN 0x3\r
111\r
112#define SMALL_PAGE_TABLE_ENTRY_COUNT (ARM_PAGE_DESC_ENTRY_MVA_SIZE / EFI_PAGE_SIZE)\r
113\r
114\r
115// Translation Table Base 0 fields\r
116#define ARM_TTBR0_BASE_MASK 0xFFFFC000\r
117#define ARM_TTBR0_BASE_SHIFT 14\r
118#define ARM_TTRB0_NOS 0x00000020\r
119\r
120// define the combination of interesting attributes: cacheability and access permissions\r
121#define ARM_SECTION_CACHEABILITY_MASK ( ARM_SECTION_TEX_MASK | ARM_SECTION_C | ARM_SECTION_B )\r
122#define ARM_SECTION_RW_PERMISSIONS_MASK ( ARM_SECTION_AP2 | ARM_SECTION_AP10_MASK )\r
123#define ARM_DESCRIPTOR_ATTRIBUTES ( ARM_SECTION_CACHEABILITY_MASK | ARM_SECTION_RW_PERMISSIONS_MASK | ARM_SECTION_XN )\r
124\r
125// cacheability values for section entries\r
126#define ARM_SECTION_STRONGLY_ORDERED 0\r
127#define ARM_SECTION_SHAREABLE_DEVICE ARM_SECTION_B\r
128#define ARM_SECTION_WRITE_THROUGH ARM_SECTION_C\r
129#define ARM_SECTION_WRITE_BACK_NWA ( ARM_SECTION_C| ARM_SECTION_B )\r
130#define ARM_SECTION_NORMAL_UNCACHEABLE ( 0x1 << ARM_SECTION_TEX_SHIFT )\r
131#define ARM_SECTION_WRITE_BACK ( ( 0x1 << ARM_SECTION_TEX_SHIFT ) | ARM_SECTION_C | ARM_SECTION_B )\r
132#define ARM_SECTION_NONSHAREABLE_DEVICE ( 0x2 << ARM_SECTION_TEX_SHIFT )\r
133\r
134// permissions values for section entries\r
135#define ARM_SECTION_NO_ACCESS 0\r
136#define ARM_SECTION_PRIV_ACCESS_ONLY ( 0x1 << ARM_SECTION_AP10_SHIFT)\r
137#define ARM_SECTION_USER_READ_ONLY ( 0x2 << ARM_SECTION_AP10_SHIFT)\r
138#define ARM_SECTION_FULL_ACCESS ( 0x3 << ARM_SECTION_AP10_SHIFT)\r
139#define ARM_SECTION_PRIV_READ_ONLY ( ARM_SECTION_AP2 | (0x1 << ARM_SECTION_AP10_SHIFT) )\r
140#define ARM_SECTION_READ_ONLY_DEP ( ARM_SECTION_AP2 | (0x2 << ARM_SECTION_AP10_SHIFT) )\r
141#define ARM_SECTION_READ_ONLY ( ARM_SECTION_AP2 | (0x3 << ARM_SECTION_AP10_SHIFT) )\r
142\r
143\r
144\r
145EFI_STATUS \r
146SectionToGcdAttributes (\r
147 IN UINT32 SectionAttributes,\r
148 OUT UINT64 *GcdAttributes\r
149 )\r
150{\r
151 *GcdAttributes = 0;\r
152\r
153 // determine cacheability attributes\r
154 switch(SectionAttributes & ARM_SECTION_CACHEABILITY_MASK) {\r
155 case ARM_SECTION_STRONGLY_ORDERED:\r
156 *GcdAttributes |= EFI_MEMORY_UC;\r
157 break;\r
158 case ARM_SECTION_SHAREABLE_DEVICE:\r
159 *GcdAttributes |= EFI_MEMORY_UC;\r
160 break;\r
161 case ARM_SECTION_WRITE_THROUGH:\r
162 *GcdAttributes |= EFI_MEMORY_WT;\r
163 break;\r
164 case ARM_SECTION_WRITE_BACK_NWA:\r
165 *GcdAttributes |= EFI_MEMORY_WB;\r
166 break;\r
167 case ARM_SECTION_NORMAL_UNCACHEABLE:\r
168 *GcdAttributes |= EFI_MEMORY_WC;\r
169 break;\r
170 case ARM_SECTION_WRITE_BACK:\r
171 *GcdAttributes |= EFI_MEMORY_WB;\r
172 break;\r
173 case ARM_SECTION_NONSHAREABLE_DEVICE:\r
174 *GcdAttributes |= EFI_MEMORY_UC;\r
175 break;\r
176 default:\r
177 return EFI_UNSUPPORTED;\r
aeb61534
A
178 }\r
179 \r
180 // determine protection attributes\r
181 switch(SectionAttributes & ARM_SECTION_RW_PERMISSIONS_MASK) {\r
182 case ARM_SECTION_NO_ACCESS: // no read, no write\r
f659880b 183 //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP;\r
aeb61534
A
184 break;\r
185\r
186 case ARM_SECTION_PRIV_ACCESS_ONLY:\r
187 case ARM_SECTION_FULL_ACCESS:\r
188 // normal read/write access, do not add additional attributes\r
189 break;\r
190\r
191 // read only cases map to write-protect\r
192 case ARM_SECTION_PRIV_READ_ONLY:\r
193 case ARM_SECTION_READ_ONLY_DEP:\r
194 case ARM_SECTION_READ_ONLY:\r
195 *GcdAttributes |= EFI_MEMORY_WP;\r
196 break;\r
197\r
198 default:\r
199 return EFI_UNSUPPORTED;\r
aeb61534
A
200 }\r
201\r
202 // now process eXectue Never attribute\r
203 if ((SectionAttributes & ARM_SECTION_XN) != 0 ) {\r
204 *GcdAttributes |= EFI_MEMORY_XP;\r
205 }\r
206\r
207 return EFI_SUCCESS;\r
208}\r
209\r
f659880b
A
210/**\r
211 Searches memory descriptors covered by given memory range.\r
212\r
213 This function searches into the Gcd Memory Space for descriptors\r
214 (from StartIndex to EndIndex) that contains the memory range\r
215 specified by BaseAddress and Length.\r
216\r
217 @param MemorySpaceMap Gcd Memory Space Map as array.\r
218 @param NumberOfDescriptors Number of descriptors in map.\r
219 @param BaseAddress BaseAddress for the requested range.\r
220 @param Length Length for the requested range.\r
221 @param StartIndex Start index into the Gcd Memory Space Map.\r
222 @param EndIndex End index into the Gcd Memory Space Map.\r
223\r
224 @retval EFI_SUCCESS Search successfully.\r
225 @retval EFI_NOT_FOUND The requested descriptors does not exist.\r
226\r
227**/\r
228EFI_STATUS\r
229SearchGcdMemorySpaces (\r
230 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,\r
231 IN UINTN NumberOfDescriptors,\r
232 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
233 IN UINT64 Length,\r
234 OUT UINTN *StartIndex,\r
235 OUT UINTN *EndIndex\r
236 )\r
237{\r
238 UINTN Index;\r
239\r
240 *StartIndex = 0;\r
241 *EndIndex = 0;\r
242 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
243 if (BaseAddress >= MemorySpaceMap[Index].BaseAddress &&\r
244 BaseAddress < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {\r
245 *StartIndex = Index;\r
246 }\r
247 if (BaseAddress + Length - 1 >= MemorySpaceMap[Index].BaseAddress &&\r
248 BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {\r
249 *EndIndex = Index;\r
250 return EFI_SUCCESS;\r
251 }\r
252 }\r
253 return EFI_NOT_FOUND;\r
254}\r
255\r
256\r
257/**\r
258 Sets the attributes for a specified range in Gcd Memory Space Map.\r
259\r
260 This function sets the attributes for a specified range in\r
261 Gcd Memory Space Map.\r
262\r
263 @param MemorySpaceMap Gcd Memory Space Map as array\r
264 @param NumberOfDescriptors Number of descriptors in map\r
265 @param BaseAddress BaseAddress for the range\r
266 @param Length Length for the range\r
267 @param Attributes Attributes to set\r
268\r
269 @retval EFI_SUCCESS Memory attributes set successfully\r
270 @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space\r
271\r
272**/\r
273EFI_STATUS\r
274SetGcdMemorySpaceAttributes (\r
275 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,\r
276 IN UINTN NumberOfDescriptors,\r
277 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
278 IN UINT64 Length,\r
279 IN UINT64 Attributes\r
280 )\r
281{\r
282 EFI_STATUS Status;\r
283 UINTN Index;\r
284 UINTN StartIndex;\r
285 UINTN EndIndex;\r
286 EFI_PHYSICAL_ADDRESS RegionStart;\r
287 UINT64 RegionLength;\r
288\r
289 //\r
290 // Get all memory descriptors covered by the memory range\r
291 //\r
292 Status = SearchGcdMemorySpaces (\r
293 MemorySpaceMap,\r
294 NumberOfDescriptors,\r
295 BaseAddress,\r
296 Length,\r
297 &StartIndex,\r
298 &EndIndex\r
299 );\r
300 if (EFI_ERROR (Status)) {\r
301 return Status;\r
302 }\r
303\r
304 //\r
305 // Go through all related descriptors and set attributes accordingly\r
306 //\r
307 for (Index = StartIndex; Index <= EndIndex; Index++) {\r
308 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
309 continue;\r
310 }\r
311 //\r
312 // Calculate the start and end address of the overlapping range\r
313 //\r
314 if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {\r
315 RegionStart = BaseAddress;\r
316 } else {\r
317 RegionStart = MemorySpaceMap[Index].BaseAddress;\r
318 }\r
319 if (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {\r
320 RegionLength = BaseAddress + Length - RegionStart;\r
321 } else {\r
322 RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;\r
323 }\r
324 //\r
325 // Set memory attributes according to MTRR attribute and the original attribute of descriptor\r
326 //\r
327 gDS->SetMemorySpaceAttributes (\r
328 RegionStart,\r
329 RegionLength,\r
330 (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)\r
331 );\r
332 }\r
333\r
334 return EFI_SUCCESS;\r
335}\r
aeb61534
A
336\r
337\r
338EFI_STATUS\r
339SyncCacheConfig (\r
340 IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol\r
341 )\r
342{\r
f659880b
A
343 EFI_STATUS Status;\r
344 UINT32 i;\r
345 UINT32 Descriptor;\r
346 UINT32 SectionAttributes;\r
347 EFI_PHYSICAL_ADDRESS NextRegionBase;\r
348 UINT64 NextRegionLength;\r
349 UINT64 GcdAttributes;\r
350 UINT32 NextRegionAttributes = 0;\r
aeb61534 351 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
f659880b
A
352 UINTN NumberOfDescriptors;\r
353 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
aeb61534
A
354\r
355\r
f659880b
A
356 DEBUG ((L_EFI_D_PAGE, "SyncCacheConfig()\n"));\r
357\r
aeb61534
A
358 // This code assumes MMU is enabled and filed with section translations\r
359 ASSERT (ArmMmuEnabled ());\r
360\r
f659880b
A
361 //\r
362 // Get the memory space map from GCD\r
363 //\r
364 MemorySpaceMap = NULL;\r
365 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
366 ASSERT_EFI_ERROR (Status);\r
367\r
aeb61534
A
368\r
369 // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs\r
370 // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a\r
371 // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were\r
372 // a client) to update its copy of the attributes. This is bad architecture and should be replaced\r
373 // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead.\r
374\r
375 // obtain page table base\r
376 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTranslationTableBaseAddress ());\r
377\r
378\r
379 // iterate through each 1MB descriptor\r
380 NextRegionBase = NextRegionLength = 0;\r
381 for (i=0; i< FIRST_LEVEL_ENTRY_COUNT; i++) {\r
382\r
f659880b
A
383 // obtain existing descriptor and make sure it contains a valid Base Address even if it is a fault section\r
384 Descriptor = FirstLevelTable[i] | (ARM_SECTION_BASE_MASK & (i << ARM_SECTION_BASE_SHIFT));\r
aeb61534
A
385\r
386 // extract attributes (cacheability and permissions)\r
387 SectionAttributes = Descriptor & 0xDEC;\r
388\r
389 // do we already have an existing region (or are we about to finish)?\r
390 // Skip the first entry, and make sure we close on the last entry\r
391 if ( (NextRegionLength > 0) || (i == (FIRST_LEVEL_ENTRY_COUNT-1)) ) {\r
392 // attributes are changing, update attributes in GCD\r
393 if (SectionAttributes != NextRegionAttributes) {\r
394 \r
395 // convert section entry attributes to GCD bitmask\r
396 Status = SectionToGcdAttributes (NextRegionAttributes, &GcdAttributes);\r
f659880b 397 ASSERT_EFI_ERROR (Status);\r
aeb61534
A
398\r
399 // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK)\r
f659880b
A
400 SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes);\r
401\r
aeb61534
A
402\r
403 // start on a new region\r
404 NextRegionLength = 0;\r
405 NextRegionBase = Descriptor & ARM_SECTION_BASE_MASK;\r
406 }\r
407 }\r
408\r
409 // starting a new region?\r
410 if (NextRegionLength == 0) {\r
411 NextRegionAttributes = SectionAttributes;\r
412 }\r
413\r
414 NextRegionLength += ARM_PAGE_DESC_ENTRY_MVA_SIZE;\r
415\r
416 } // section entry loop\r
417\r
418 return EFI_SUCCESS;\r
419}\r
420\r
421\r
422\r
423EFI_STATUS\r
424UpdatePageEntries (\r
425 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
426 IN UINT64 Length,\r
427 IN UINT64 Attributes,\r
428 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
429 )\r
430{\r
431 EFI_STATUS Status;\r
432 UINT32 EntryValue;\r
433 UINT32 EntryMask;\r
434 UINT32 FirstLevelIdx;\r
435 UINT32 Offset;\r
436 UINT32 NumPageEntries;\r
437 UINT32 Descriptor;\r
438 UINT32 p;\r
439 UINT32 PageTableIndex;\r
440 UINT32 PageTableEntry;\r
441\r
442 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
443 volatile ARM_PAGE_TABLE_ENTRY *PageTable;\r
444\r
445 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
446 // EntryValue: values at bit positions specified by EntryMask\r
447\r
448 // Although the PI spec is unclear on this the GCD guarantees that only\r
449 // one Attribute bit is set at a time, so we can safely use a switch statement\r
450 switch (Attributes) {\r
451 case EFI_MEMORY_UC:\r
452 // modify cacheability attributes\r
453 EntryMask = ARM_SMALL_PAGE_TEX_MASK | ARM_PAGE_C | ARM_PAGE_B;\r
454 // map to strongly ordered\r
455 EntryValue = 0; // TEX[2:0] = 0, C=0, B=0\r
456 break;\r
457\r
458 case EFI_MEMORY_WC:\r
459 // modify cacheability attributes\r
460 EntryMask = ARM_SMALL_PAGE_TEX_MASK | ARM_PAGE_C | ARM_PAGE_B;\r
461 // map to normal non-cachable\r
462 EntryValue = (0x1 << ARM_SMALL_PAGE_TEX_SHIFT); // TEX [2:0]= 001 = 0x2, B=0, C=0\r
463 break;\r
464\r
465 case EFI_MEMORY_WT:\r
466 // modify cacheability attributes\r
467 EntryMask = ARM_SMALL_PAGE_TEX_MASK | ARM_PAGE_C | ARM_PAGE_B;\r
468 // write through with no-allocate\r
469 EntryValue = ARM_PAGE_C; // TEX [2:0] = 0, C=1, B=0\r
470 break;\r
471\r
472 case EFI_MEMORY_WB:\r
473 // modify cacheability attributes\r
474 EntryMask = ARM_SMALL_PAGE_TEX_MASK | ARM_PAGE_C | ARM_PAGE_B;\r
475 // write back (with allocate)\r
476 EntryValue = (0x1 << ARM_SMALL_PAGE_TEX_SHIFT) | ARM_PAGE_C | ARM_PAGE_B; // TEX [2:0] = 001, C=1, B=1\r
477 break;\r
478\r
479 case EFI_MEMORY_WP:\r
480 case EFI_MEMORY_XP:\r
481 case EFI_MEMORY_UCE:\r
482 // cannot be implemented UEFI definition unclear for ARM\r
483 // Cause a page fault if these ranges are accessed.\r
484 EntryMask = 0x3;\r
485 EntryValue = 0;\r
f659880b 486 DEBUG ((L_EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));\r
aeb61534
A
487 break;\r
488\r
489 default:\r
490 return EFI_UNSUPPORTED;\r
aeb61534
A
491 }\r
492\r
493 // obtain page table base\r
494 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTranslationTableBaseAddress ();\r
495\r
496 // calculate number of 4KB page table entries to change\r
497 NumPageEntries = Length/EFI_PAGE_SIZE;\r
498 \r
499 // iterate for the number of 4KB pages to change\r
500 Offset = 0;\r
501 for(p=0; p<NumPageEntries; p++) {\r
502 // calculate index into first level translation table for page table value\r
503 \r
504 FirstLevelIdx = ((BaseAddress + Offset) & ARM_SECTION_BASE_MASK) >> ARM_SECTION_BASE_SHIFT;\r
505 ASSERT (FirstLevelIdx < FIRST_LEVEL_ENTRY_COUNT);\r
506\r
507 // read the descriptor from the first level page table\r
508 Descriptor = FirstLevelTable[FirstLevelIdx];\r
509\r
510 // does this descriptor need to be converted from section entry to 4K pages?\r
f659880b 511 if ((Descriptor & ARM_DESC_TYPE_MASK) != ARM_DESC_TYPE_PAGE_TABLE ) {\r
aeb61534
A
512 Status = ConvertSectionToPages (FirstLevelIdx << ARM_SECTION_BASE_SHIFT);\r
513 if (EFI_ERROR(Status)) {\r
514 // exit for loop\r
515 break; \r
516 } \r
517 \r
518 // re-read descriptor\r
519 Descriptor = FirstLevelTable[FirstLevelIdx];\r
520 }\r
521\r
522 // obtain page table base address\r
523 PageTable = (ARM_PAGE_TABLE_ENTRY *)(Descriptor & ARM_SMALL_PAGE_BASE_MASK);\r
524\r
525 // calculate index into the page table\r
526 PageTableIndex = ((BaseAddress + Offset) & ARM_SMALL_PAGE_INDEX_MASK) >> ARM_SMALL_PAGE_BASE_SHIFT;\r
f659880b 527 ASSERT (PageTableIndex < SMALL_PAGE_TABLE_ENTRY_COUNT);\r
aeb61534
A
528\r
529 // get the entry\r
530 PageTableEntry = PageTable[PageTableIndex];\r
531\r
532 // mask off appropriate fields\r
533 PageTableEntry &= ~EntryMask;\r
534\r
535 // mask in new attributes and/or permissions\r
536 PageTableEntry |= EntryValue;\r
537\r
538 if (VirtualMask != 0) {\r
539 // Make this virtual address point at a physical page\r
540 PageTableEntry &= ~VirtualMask;\r
541 }\r
542 \r
543 // update the entry\r
544 PageTable[PageTableIndex] = PageTableEntry; \r
545 \r
546\r
547 Status = EFI_SUCCESS;\r
548 Offset += EFI_PAGE_SIZE;\r
549 \r
550 } // end first level translation table loop\r
551\r
552 return Status;\r
553}\r
554\r
555\r
556\r
557EFI_STATUS\r
558UpdateSectionEntries (\r
559 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
560 IN UINT64 Length,\r
561 IN UINT64 Attributes,\r
562 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
563 )\r
564{\r
565 EFI_STATUS Status = EFI_SUCCESS;\r
566 UINT32 EntryMask;\r
567 UINT32 EntryValue;\r
568 UINT32 FirstLevelIdx;\r
569 UINT32 NumSections;\r
570 UINT32 i;\r
571 UINT32 Descriptor;\r
572\r
573 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
574\r
575 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)\r
576 // EntryValue: values at bit positions specified by EntryMask\r
577\r
f659880b
A
578 // Make sure we handle a section range that is unmapped \r
579 EntryMask = ARM_DESC_TYPE_MASK;\r
580 EntryValue = ARM_DESC_TYPE_SECTION;\r
581\r
aeb61534
A
582 // Although the PI spec is unclear on this the GCD guarantees that only\r
583 // one Attribute bit is set at a time, so we can safely use a switch statement\r
584 switch(Attributes) {\r
585 case EFI_MEMORY_UC:\r
586 // modify cacheability attributes\r
f659880b 587 EntryMask |= ARM_SECTION_TEX_MASK | ARM_SECTION_C | ARM_SECTION_B;\r
aeb61534 588 // map to strongly ordered\r
f659880b 589 EntryValue |= 0; // TEX[2:0] = 0, C=0, B=0\r
aeb61534
A
590 break;\r
591\r
592 case EFI_MEMORY_WC:\r
593 // modify cacheability attributes\r
f659880b 594 EntryMask |= ARM_SECTION_TEX_MASK | ARM_SECTION_C | ARM_SECTION_B;\r
aeb61534 595 // map to normal non-cachable\r
f659880b 596 EntryValue |= (0x1 << ARM_SECTION_TEX_SHIFT); // TEX [2:0]= 001 = 0x2, B=0, C=0\r
aeb61534
A
597 break;\r
598\r
599 case EFI_MEMORY_WT:\r
600 // modify cacheability attributes\r
f659880b 601 EntryMask |= ARM_SECTION_TEX_MASK | ARM_SECTION_C | ARM_SECTION_B;\r
aeb61534 602 // write through with no-allocate\r
f659880b 603 EntryValue |= ARM_SECTION_C; // TEX [2:0] = 0, C=1, B=0\r
aeb61534
A
604 break;\r
605\r
606 case EFI_MEMORY_WB:\r
607 // modify cacheability attributes\r
f659880b 608 EntryMask |= ARM_SECTION_TEX_MASK | ARM_SECTION_C | ARM_SECTION_B;\r
aeb61534 609 // write back (with allocate)\r
f659880b 610 EntryValue |= (0x1 << ARM_SECTION_TEX_SHIFT) | ARM_SECTION_C | ARM_SECTION_B; // TEX [2:0] = 001, C=1, B=1\r
aeb61534
A
611 break;\r
612\r
613 case EFI_MEMORY_WP:\r
614 case EFI_MEMORY_XP:\r
615 case EFI_MEMORY_RP:\r
616 case EFI_MEMORY_UCE:\r
617 // cannot be implemented UEFI definition unclear for ARM\r
618 // Cause a page fault if these ranges are accessed.\r
f659880b
A
619 EntryValue = ARM_DESC_TYPE_FAULT;\r
620 DEBUG ((L_EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes));\r
aeb61534
A
621 break;\r
622\r
623\r
624 default:\r
625 return EFI_UNSUPPORTED;\r
aeb61534
A
626 }\r
627\r
628 // obtain page table base\r
629 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTranslationTableBaseAddress ();\r
630\r
631 // calculate index into first level translation table for start of modification\r
632 FirstLevelIdx = (BaseAddress & ARM_SECTION_BASE_MASK) >> ARM_SECTION_BASE_SHIFT;\r
633 ASSERT (FirstLevelIdx < FIRST_LEVEL_ENTRY_COUNT);\r
634\r
635 // calculate number of 1MB first level entries this applies to\r
636 NumSections = Length / ARM_PAGE_DESC_ENTRY_MVA_SIZE;\r
637 \r
638 // iterate through each descriptor\r
639 for(i=0; i<NumSections; i++) {\r
640 Descriptor = FirstLevelTable[FirstLevelIdx + i];\r
641\r
642 // has this descriptor already been coverted to pages?\r
f659880b 643 if ((Descriptor & ARM_DESC_TYPE_MASK) != ARM_DESC_TYPE_PAGE_TABLE ) {\r
aeb61534
A
644 // forward this 1MB range to page table function instead\r
645 Status = UpdatePageEntries ((FirstLevelIdx + i) << ARM_SECTION_BASE_SHIFT, ARM_PAGE_DESC_ENTRY_MVA_SIZE, Attributes, VirtualMask);\r
646 } else {\r
647 // still a section entry\r
648 \r
649 // mask off appropriate fields\r
650 Descriptor &= ~EntryMask;\r
651\r
652 // mask in new attributes and/or permissions\r
653 Descriptor |= EntryValue;\r
654 if (VirtualMask != 0) {\r
655 Descriptor &= ~VirtualMask;\r
656 }\r
657\r
658 FirstLevelTable[FirstLevelIdx + i] = Descriptor;\r
659\r
660 Status = EFI_SUCCESS;\r
661 }\r
662 }\r
663\r
664 return Status;\r
665}\r
666\r
667EFI_STATUS \r
668ConvertSectionToPages (\r
669 IN EFI_PHYSICAL_ADDRESS BaseAddress\r
670 )\r
671{\r
672 EFI_STATUS Status;\r
673 EFI_PHYSICAL_ADDRESS PageTableAddr;\r
674 UINT32 FirstLevelIdx;\r
675 UINT32 SectionDescriptor;\r
676 UINT32 PageTableDescriptor;\r
677 UINT32 PageDescriptor;\r
678 UINT32 i;\r
679\r
680 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;\r
681 volatile ARM_PAGE_TABLE_ENTRY *PageTable;\r
682\r
f659880b 683 DEBUG ((L_EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));\r
aeb61534
A
684\r
685 // obtain page table base\r
686 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTranslationTableBaseAddress ();\r
687\r
688 // calculate index into first level translation table for start of modification\r
689 FirstLevelIdx = (BaseAddress & ARM_SECTION_BASE_MASK) >> ARM_SECTION_BASE_SHIFT;\r
f659880b 690 ASSERT (FirstLevelIdx < FIRST_LEVEL_ENTRY_COUNT);\r
aeb61534
A
691\r
692 // get section attributes and convert to page attributes\r
693 SectionDescriptor = FirstLevelTable[FirstLevelIdx];\r
694 PageDescriptor = ARM_PAGE_TYPE_SMALL;\r
695 PageDescriptor |= ((SectionDescriptor & ARM_SECTION_TEX_MASK) >> ARM_SECTION_TEX_SHIFT) << ARM_SMALL_PAGE_TEX_SHIFT;\r
696 if ((SectionDescriptor & ARM_SECTION_B) != 0) {\r
697 PageDescriptor |= ARM_PAGE_B;\r
698 }\r
699 if ((SectionDescriptor & ARM_SECTION_C) != 0) {\r
700 PageDescriptor |= ARM_PAGE_C;\r
701 }\r
702 PageDescriptor |= ((SectionDescriptor & ARM_SECTION_AP10_MASK) >> ARM_SECTION_AP10_SHIFT) << ARM_PAGE_AP10_SHIFT;\r
703 if ((SectionDescriptor & ARM_SECTION_AP2) != 0) {\r
704 PageDescriptor |= ARM_PAGE_AP2;\r
705 }\r
706 if ((SectionDescriptor & ARM_SECTION_XN) != 0) {\r
707 PageDescriptor |= ARM_PAGE_TYPE_SMALL_XN;\r
708 }\r
709 if ((SectionDescriptor & ARM_SECTION_nG) != 0) {\r
710 PageDescriptor |= ARM_PAGE_nG;\r
711 }\r
712 if ((SectionDescriptor & ARM_SECTION_S) != 0) {\r
713 PageDescriptor |= ARM_PAGE_S;\r
714 }\r
715\r
716 // allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)\r
717 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);\r
718 if (EFI_ERROR(Status)) {\r
719 return Status;\r
720 }\r
721\r
722 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;\r
723\r
724 // write the page table entries out\r
725 for (i=0; i<(ARM_PAGE_DESC_ENTRY_MVA_SIZE/EFI_PAGE_SIZE); i++) {\r
726 PageTable[i] = ((BaseAddress + (i << 12)) & ARM_SMALL_PAGE_BASE_MASK) | PageDescriptor;\r
727 }\r
728\r
729 // flush d-cache so descriptors make it back to uncached memory for subsequent table walks\r
730 // TODO: change to use only PageTable base and length\r
731 // ArmInvalidateDataCache ();\r
f659880b
A
732DEBUG ((EFI_D_ERROR, "InvalidateDataCacheRange (%x, %x)\n", (UINTN)PageTableAddr, EFI_PAGE_SIZE));\r
733\r
734 InvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, EFI_PAGE_SIZE);\r
aeb61534
A
735\r
736 // formulate page table entry, Domain=0, NS=0\r
737 PageTableDescriptor = (((UINTN)PageTableAddr) & ARM_PAGE_DESC_BASE_MASK) | ARM_DESC_TYPE_PAGE_TABLE;\r
738\r
739 // write the page table entry out, repalcing section entry\r
740 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;\r
741\r
742 return EFI_SUCCESS;\r
743}\r
744\r
745\r
746\r
747EFI_STATUS\r
748SetMemoryAttributes (\r
749 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
750 IN UINT64 Length,\r
751 IN UINT64 Attributes,\r
752 IN EFI_PHYSICAL_ADDRESS VirtualMask\r
753 )\r
754{\r
755 EFI_STATUS Status;\r
756 \r
757 if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) {\r
758 // is the base and length a multiple of 1 MB?\r
f659880b 759 DEBUG ((L_EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));\r
aeb61534
A
760 Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask);\r
761 } else {\r
762 // base and/or length is not a multiple of 1 MB\r
f659880b 763 DEBUG ((L_EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes));\r
aeb61534
A
764 Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask);\r
765 }\r
766\r
767 // flush d-cache so descriptors make it back to uncached memory for subsequent table walks\r
768 // flush and invalidate pages\r
769 ArmCleanInvalidateDataCache ();\r
770 \r
771 ArmInvalidateInstructionCache ();\r
772\r
773 // invalidate all TLB entries so changes are synced\r
774 ArmInvalidateTlb (); \r
775\r
776 return Status;\r
777}\r
778\r
779\r
780/**\r
781 This function modifies the attributes for the memory region specified by BaseAddress and\r
782 Length from their current attributes to the attributes specified by Attributes.\r
783\r
784 @param This The EFI_CPU_ARCH_PROTOCOL instance.\r
785 @param BaseAddress The physical address that is the start address of a memory region.\r
786 @param Length The size in bytes of the memory region.\r
787 @param Attributes The bit mask of attributes to set for the memory region.\r
788\r
789 @retval EFI_SUCCESS The attributes were set for the memory region.\r
790 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
791 BaseAddress and Length cannot be modified.\r
792 @retval EFI_INVALID_PARAMETER Length is zero.\r
793 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
794 the memory resource range.\r
795 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
796 resource range specified by BaseAddress and Length.\r
797 The bit mask of attributes is not support for the memory resource\r
798 range specified by BaseAddress and Length.\r
799\r
800**/\r
801EFI_STATUS\r
802EFIAPI\r
803CpuSetMemoryAttributes (\r
804 IN EFI_CPU_ARCH_PROTOCOL *This,\r
805 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
806 IN UINT64 Length,\r
807 IN UINT64 Attributes\r
808 )\r
809{\r
f659880b 810 DEBUG ((L_EFI_D_PAGE, "SetMemoryAttributes(%lx, %lx, %lx)\n", BaseAddress, Length, Attributes));\r
aeb61534
A
811 if ( ((BaseAddress & (EFI_PAGE_SIZE-1)) != 0) || ((Length & (EFI_PAGE_SIZE-1)) != 0)){\r
812 // minimum granularity is EFI_PAGE_SIZE (4KB on ARM)\r
f659880b 813 DEBUG ((L_EFI_D_PAGE, "SetMemoryAttributes(%lx, %lx, %lx): minimum ganularity is EFI_PAGE_SIZE\n", BaseAddress, Length, Attributes));\r
aeb61534
A
814 return EFI_UNSUPPORTED;\r
815 }\r
816 \r
817 return SetMemoryAttributes (BaseAddress, Length, Attributes, 0);\r
818}\r
819\r
820\r
821\r
822//\r
823// Add a new protocol to support \r
824//\r
825\r
826EFI_STATUS\r
827EFIAPI\r
828CpuConvertPagesToUncachedVirtualAddress (\r
829 IN VIRTUAL_UNCACHED_PAGES_PROTOCOL *This,\r
830 IN EFI_PHYSICAL_ADDRESS Address,\r
831 IN UINTN Length,\r
832 IN EFI_PHYSICAL_ADDRESS VirtualMask,\r
833 OUT UINT64 *Attributes OPTIONAL\r
834 )\r
835{\r
836 EFI_STATUS Status;\r
837 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;\r
838 \r
839 \r
840 if (Attributes != NULL) {\r
841 Status = gDS->GetMemorySpaceDescriptor (Address, &GcdDescriptor);\r
842 if (!EFI_ERROR (Status)) {\r
843 *Attributes = GcdDescriptor.Attributes;\r
844 }\r
845 }\r
f659880b 846ASSERT (FALSE); \r
aeb61534
A
847 //\r
848 // Make this address range page fault if accessed. If it is a DMA buffer than this would \r
849 // be the PCI address. Code should always use the CPU address, and we will or in VirtualMask\r
850 // to that address. \r
851 //\r
f659880b 852 Status = SetMemoryAttributes (Address, Length, EFI_MEMORY_WP, 0);\r
aeb61534
A
853 if (!EFI_ERROR (Status)) {\r
854 Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_UC, VirtualMask);\r
855 }\r
856\r
857 return Status;\r
858}\r
859\r
860\r
861EFI_STATUS\r
862EFIAPI\r
f659880b 863CpuReconvertPagesPages (\r
aeb61534
A
864 IN VIRTUAL_UNCACHED_PAGES_PROTOCOL *This,\r
865 IN EFI_PHYSICAL_ADDRESS Address,\r
866 IN UINTN Length,\r
867 IN EFI_PHYSICAL_ADDRESS VirtualMask,\r
868 IN UINT64 Attributes\r
869 )\r
870{\r
871 EFI_STATUS Status;\r
872 \r
873 //\r
874 // Unmap the alaised Address\r
875 //\r
f659880b 876 Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_WP, 0);\r
aeb61534
A
877 if (!EFI_ERROR (Status)) {\r
878 //\r
879 // Restore atttributes\r
880 //\r
881 Status = SetMemoryAttributes (Address, Length, Attributes, 0);\r
882 }\r
883 \r
884 return Status;\r
885}\r
886\r
887\r
888VIRTUAL_UNCACHED_PAGES_PROTOCOL gVirtualUncachedPages = {\r
889 CpuConvertPagesToUncachedVirtualAddress,\r
f659880b 890 CpuReconvertPagesPages\r
aeb61534
A
891};\r
892\r
893\r
894\r
895\r