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