TianoCore EDK2 master
Loading...
Searching...
No Matches
ArmMmuLibUpdate.c
Go to the documentation of this file.
1
10#include <Uefi.h>
11
12#include <Library/ArmLib.h>
13#include <Library/ArmMmuLib.h>
14#include <Library/BaseLib.h>
16#include <Library/DebugLib.h>
19
20#include <Arm/AArch32.h>
21
22#define __EFI_MEMORY_RWX 0 // no restrictions
23
24#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \
25 EFI_MEMORY_WC | \
26 EFI_MEMORY_WT | \
27 EFI_MEMORY_WB | \
28 EFI_MEMORY_UCE | \
29 EFI_MEMORY_WP)
30
33ConvertSectionToPages (
34 IN EFI_PHYSICAL_ADDRESS BaseAddress
35 )
36{
37 UINT32 FirstLevelIdx;
38 UINT32 SectionDescriptor;
39 UINT32 PageTableDescriptor;
40 UINT32 PageDescriptor;
41 UINT32 Index;
42
43 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
44 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
45
46 DEBUG ((DEBUG_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
47
48 // Obtain page table base
49 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
50
51 // Calculate index into first level translation table for start of modification
52 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
53 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
54
55 // Get section attributes and convert to page attributes
56 SectionDescriptor = FirstLevelTable[FirstLevelIdx];
57 PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor);
58
59 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
60 PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)AllocatePages (1);
61 if (PageTable == NULL) {
62 return EFI_OUT_OF_RESOURCES;
63 }
64
65 // Write the page table entries out
66 for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
67 PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS (BaseAddress + (Index << 12)) | PageDescriptor;
68 }
69
70 // Formulate page table entry, Domain=0, NS=0
71 PageTableDescriptor = (((UINTN)PageTable) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
72
73 // Write the page table entry out, replacing section entry
74 FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
75
76 return EFI_SUCCESS;
77}
78
81UpdatePageEntries (
82 IN EFI_PHYSICAL_ADDRESS BaseAddress,
83 IN UINT64 Length,
84 IN UINT64 Attributes,
85 IN UINT32 EntryMask,
86 OUT BOOLEAN *FlushTlbs OPTIONAL
87 )
88{
89 EFI_STATUS Status;
90 UINT32 EntryValue;
91 UINT32 FirstLevelIdx;
92 UINT32 Offset;
93 UINT32 NumPageEntries;
94 UINT32 Descriptor;
95 UINT32 p;
96 UINT32 PageTableIndex;
97 UINT32 PageTableEntry;
98 UINT32 CurrentPageTableEntry;
99 VOID *Mva;
100
101 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
102 volatile ARM_PAGE_TABLE_ENTRY *PageTable;
103
104 Status = EFI_SUCCESS;
105
106 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
107 // EntryValue: values at bit positions specified by EntryMask
108 EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
109
110 // Although the PI spec is unclear on this, the GCD guarantees that only
111 // one Attribute bit is set at a time, so the order of the conditionals below
112 // is irrelevant. If no memory attribute is specified, we preserve whatever
113 // memory type is set in the page tables, and update the permission attributes
114 // only.
115 if ((Attributes & EFI_MEMORY_UC) != 0) {
116 // modify cacheability attributes
117 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
118 // map to strongly ordered
119 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
120 } else if ((Attributes & EFI_MEMORY_WC) != 0) {
121 // modify cacheability attributes
122 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
123 // map to normal non-cacheable
124 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
125 } else if ((Attributes & EFI_MEMORY_WT) != 0) {
126 // modify cacheability attributes
127 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
128 // write through with no-allocate
129 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
130 } else if ((Attributes & EFI_MEMORY_WB) != 0) {
131 // modify cacheability attributes
132 EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
133 // write back (with allocate)
134 EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
135 } else if ((Attributes & CACHE_ATTRIBUTE_MASK) != 0) {
136 // catch unsupported memory type attributes
137 ASSERT (FALSE);
138 return EFI_UNSUPPORTED;
139 }
140
141 if ((Attributes & EFI_MEMORY_RP) == 0) {
142 EntryValue |= TT_DESCRIPTOR_PAGE_AF;
143 }
144
145 if ((Attributes & EFI_MEMORY_RO) != 0) {
146 EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;
147 } else {
148 EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;
149 }
150
151 if ((Attributes & EFI_MEMORY_XP) != 0) {
152 EntryValue |= TT_DESCRIPTOR_PAGE_XN_MASK;
153 }
154
155 // Obtain page table base
156 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
157
158 // Calculate number of 4KB page table entries to change
159 NumPageEntries = (UINT32)(Length / TT_DESCRIPTOR_PAGE_SIZE);
160
161 // Iterate for the number of 4KB pages to change
162 Offset = 0;
163 for (p = 0; p < NumPageEntries; p++) {
164 // Calculate index into first level translation table for page table value
165
166 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
167 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
168
169 // Read the descriptor from the first level page table
170 Descriptor = FirstLevelTable[FirstLevelIdx];
171
172 // Does this descriptor need to be converted from section entry to 4K pages?
173 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (Descriptor)) {
174 //
175 // If the section mapping covers the requested region with the expected
176 // attributes, splitting it is unnecessary, and should be avoided as it
177 // may result in unbounded recursion when using a strict NX policy.
178 //
179 if ((EntryValue & ~TT_DESCRIPTOR_PAGE_TYPE_MASK & EntryMask) ==
180 (ConvertSectionAttributesToPageAttributes (Descriptor) & EntryMask))
181 {
182 continue;
183 }
184
185 Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
186 if (EFI_ERROR (Status)) {
187 // Exit for loop
188 break;
189 }
190
191 // Re-read descriptor
192 Descriptor = FirstLevelTable[FirstLevelIdx];
193 if (FlushTlbs != NULL) {
194 *FlushTlbs = TRUE;
195 }
196 }
197
198 // Obtain page table base address
199 PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS (Descriptor);
200
201 // Calculate index into the page table
202 PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
203 ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
204
205 // Get the entry
206 CurrentPageTableEntry = PageTable[PageTableIndex];
207
208 // Mask off appropriate fields
209 PageTableEntry = CurrentPageTableEntry & ~EntryMask;
210
211 // Mask in new attributes and/or permissions
212 PageTableEntry |= EntryValue;
213
214 if (CurrentPageTableEntry != PageTableEntry) {
215 Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
216
217 // Only need to update if we are changing the entry
218 PageTable[PageTableIndex] = PageTableEntry;
219 ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
220 }
221
222 Status = EFI_SUCCESS;
223 Offset += TT_DESCRIPTOR_PAGE_SIZE;
224 } // End first level translation table loop
225
226 return Status;
227}
228
229STATIC
231UpdateSectionEntries (
232 IN EFI_PHYSICAL_ADDRESS BaseAddress,
233 IN UINT64 Length,
234 IN UINT64 Attributes,
235 IN UINT32 EntryMask
236 )
237{
238 EFI_STATUS Status;
239 UINT32 EntryValue;
240 UINT32 FirstLevelIdx;
241 UINT32 NumSections;
242 UINT32 i;
243 UINT32 CurrentDescriptor;
244 UINT32 Descriptor;
245 VOID *Mva;
246 volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
247
248 Status = EFI_SUCCESS;
249
250 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
251 // EntryValue: values at bit positions specified by EntryMask
252
253 // Make sure we handle a section range that is unmapped
254 EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
255
256 // Although the PI spec is unclear on this, the GCD guarantees that only
257 // one Attribute bit is set at a time, so the order of the conditionals below
258 // is irrelevant. If no memory attribute is specified, we preserve whatever
259 // memory type is set in the page tables, and update the permission attributes
260 // only.
261 if ((Attributes & EFI_MEMORY_UC) != 0) {
262 // modify cacheability attributes
263 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
264 // map to strongly ordered
265 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
266 } else if ((Attributes & EFI_MEMORY_WC) != 0) {
267 // modify cacheability attributes
268 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
269 // map to normal non-cacheable
270 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
271 } else if ((Attributes & EFI_MEMORY_WT) != 0) {
272 // modify cacheability attributes
273 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
274 // write through with no-allocate
275 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
276 } else if ((Attributes & EFI_MEMORY_WB) != 0) {
277 // modify cacheability attributes
278 EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
279 // write back (with allocate)
280 EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
281 } else if ((Attributes & CACHE_ATTRIBUTE_MASK) != 0) {
282 // catch unsupported memory type attributes
283 ASSERT (FALSE);
284 return EFI_UNSUPPORTED;
285 }
286
287 if ((Attributes & EFI_MEMORY_RO) != 0) {
288 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
289 } else {
290 EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
291 }
292
293 if ((Attributes & EFI_MEMORY_XP) != 0) {
294 EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
295 }
296
297 if ((Attributes & EFI_MEMORY_RP) == 0) {
298 EntryValue |= TT_DESCRIPTOR_SECTION_AF;
299 }
300
301 // obtain page table base
302 FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
303
304 // calculate index into first level translation table for start of modification
305 FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
306 ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
307
308 // calculate number of 1MB first level entries this applies to
309 NumSections = (UINT32)(Length / TT_DESCRIPTOR_SECTION_SIZE);
310
311 // iterate through each descriptor
312 for (i = 0; i < NumSections; i++) {
313 CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
314
315 // has this descriptor already been converted to pages?
316 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (CurrentDescriptor)) {
317 // forward this 1MB range to page table function instead
318 Status = UpdatePageEntries (
319 (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT,
320 TT_DESCRIPTOR_SECTION_SIZE,
321 Attributes,
322 ConvertSectionAttributesToPageAttributes (EntryMask),
323 NULL
324 );
325 } else {
326 // still a section entry
327
328 if (CurrentDescriptor != 0) {
329 // mask off appropriate fields
330 Descriptor = CurrentDescriptor & ~EntryMask;
331 } else {
332 Descriptor = ((UINTN)FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT;
333 }
334
335 // mask in new attributes and/or permissions
336 Descriptor |= EntryValue;
337
338 if (CurrentDescriptor != Descriptor) {
339 Mva = (VOID *)(UINTN)(((UINTN)FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
340
341 // Only need to update if we are changing the descriptor
342 FirstLevelTable[FirstLevelIdx + i] = Descriptor;
343 ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
344 }
345
346 Status = EFI_SUCCESS;
347 }
348 }
349
350 return Status;
351}
352
366STATIC
369 IN EFI_PHYSICAL_ADDRESS BaseAddress,
370 IN UINT64 Length,
371 IN UINT64 Attributes,
372 IN UINT32 SectionMask
373 )
374{
375 EFI_STATUS Status;
376 UINT64 ChunkLength;
377 BOOLEAN FlushTlbs;
378
379 if (BaseAddress > (UINT64)MAX_ADDRESS) {
380 DEBUG ((
381 DEBUG_ERROR,
382 "%a BaseAddress: 0x%llx is greater than MAX_ADDRESS: 0x%llx, fail to apply attributes!\n",
383 __func__,
384 BaseAddress,
385 (UINT64)MAX_ADDRESS
386 ));
387 return EFI_UNSUPPORTED;
388 }
389
390 Length = MIN (Length, (UINT64)MAX_ADDRESS - BaseAddress + 1);
391 if (Length == 0) {
392 return EFI_SUCCESS;
393 }
394
395 FlushTlbs = FALSE;
396 while (Length > 0) {
397 if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&
398 (Length >= TT_DESCRIPTOR_SECTION_SIZE))
399 {
400 ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;
401
402 DEBUG ((
403 DEBUG_PAGE,
404 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
405 BaseAddress,
406 ChunkLength,
407 Attributes
408 ));
409
410 Status = UpdateSectionEntries (
411 BaseAddress,
412 ChunkLength,
413 Attributes,
414 SectionMask
415 );
416
417 FlushTlbs = TRUE;
418 } else {
419 //
420 // Process page by page until the next section boundary, but only if
421 // we have more than a section's worth of area to deal with after that.
422 //
423 ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -
424 (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);
425 if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {
426 ChunkLength = Length;
427 }
428
429 DEBUG ((
430 DEBUG_PAGE,
431 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
432 BaseAddress,
433 ChunkLength,
434 Attributes
435 ));
436
437 Status = UpdatePageEntries (
438 BaseAddress,
439 ChunkLength,
440 Attributes,
441 ConvertSectionAttributesToPageAttributes (SectionMask),
442 &FlushTlbs
443 );
444 }
445
446 if (EFI_ERROR (Status)) {
447 DEBUG ((
448 DEBUG_ERROR,
449 "%a failed to update attributes with status %r for BaseAddress 0x%llx of length 0x%llx\n",
450 __func__,
451 Status,
452 BaseAddress,
453 ChunkLength
454 ));
455 break;
456 }
457
458 BaseAddress += ChunkLength;
459 Length -= ChunkLength;
460 }
461
462 if (FlushTlbs) {
464 }
465
466 return Status;
467}
468
504 IN EFI_PHYSICAL_ADDRESS BaseAddress,
505 IN UINT64 Length,
506 IN UINT64 Attributes,
507 IN UINT64 AttributeMask
508 )
509{
510 UINT32 TtEntryMask;
511
512 if (((BaseAddress | Length) & EFI_PAGE_MASK) != 0) {
513 return EFI_INVALID_PARAMETER;
514 }
515
516 if ((Attributes & EFI_MEMORY_CACHETYPE_MASK) == 0) {
517 //
518 // No memory type was set in Attributes, so we are going to update the
519 // permissions only.
520 //
521 if (AttributeMask != 0) {
522 if (((AttributeMask & ~(UINT64)(EFI_MEMORY_RP|EFI_MEMORY_RO|EFI_MEMORY_XP)) != 0) ||
523 ((Attributes & ~AttributeMask) != 0))
524 {
525 return EFI_INVALID_PARAMETER;
526 }
527 } else {
528 AttributeMask = EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP;
529 }
530
531 TtEntryMask = 0;
532 if ((AttributeMask & EFI_MEMORY_RP) != 0) {
533 TtEntryMask |= TT_DESCRIPTOR_SECTION_AF;
534 }
535
536 if ((AttributeMask & EFI_MEMORY_RO) != 0) {
537 TtEntryMask |= TT_DESCRIPTOR_SECTION_AP_MASK;
538 }
539
540 if ((AttributeMask & EFI_MEMORY_XP) != 0) {
541 TtEntryMask |= TT_DESCRIPTOR_SECTION_XN_MASK;
542 }
543 } else {
544 ASSERT (AttributeMask == 0);
545 if (AttributeMask != 0) {
546 return EFI_INVALID_PARAMETER;
547 }
548
549 TtEntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK |
550 TT_DESCRIPTOR_SECTION_XN_MASK |
551 TT_DESCRIPTOR_SECTION_AP_MASK |
552 TT_DESCRIPTOR_SECTION_AF;
553 }
554
555 return SetMemoryAttributes (
556 BaseAddress,
557 Length,
558 Attributes,
559 TtEntryMask
560 );
561}
UINT64 UINTN
#define MAX_ADDRESS
VOID EFIAPI ArmInvalidateTlb(VOID)
STATIC EFI_STATUS SetMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 Attributes, IN UINT32 SectionMask)
EFI_STATUS ArmSetMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 Attributes, IN UINT64 AttributeMask)
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define MIN(a, b)
Definition: Base.h:1007
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define DEBUG(Expression)
Definition: DebugLib.h:434
VOID *EFIAPI AllocatePages(IN UINTN Pages)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112