2 * File managing the MMU for ARMv7 architecture
4 * Copyright (c) 2011-2021, Arm Limited. All rights reserved.<BR>
6 * SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <Library/ArmLib.h>
13 #include <Library/BaseLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/CacheMaintenanceLib.h>
17 #include <Library/MemoryAllocationLib.h>
19 #include <Chipset/ArmV7.h>
21 #define __EFI_MEMORY_RWX 0 // no restrictions
23 #define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \
32 ConvertSectionToPages (
33 IN EFI_PHYSICAL_ADDRESS BaseAddress
37 UINT32 SectionDescriptor
;
38 UINT32 PageTableDescriptor
;
39 UINT32 PageDescriptor
;
42 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
43 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
45 DEBUG ((DEBUG_PAGE
, "Converting section at 0x%x to pages\n", (UINTN
)BaseAddress
));
47 // Obtain page table base
48 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
50 // Calculate index into first level translation table for start of modification
51 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
52 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
54 // Get section attributes and convert to page attributes
55 SectionDescriptor
= FirstLevelTable
[FirstLevelIdx
];
56 PageDescriptor
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
| ConvertSectionAttributesToPageAttributes (SectionDescriptor
, FALSE
);
58 // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
59 PageTable
= (volatile ARM_PAGE_TABLE_ENTRY
*)AllocatePages (1);
60 if (PageTable
== NULL
) {
61 return EFI_OUT_OF_RESOURCES
;
64 // Write the page table entries out
65 for (Index
= 0; Index
< TRANSLATION_TABLE_PAGE_COUNT
; Index
++) {
66 PageTable
[Index
] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS (BaseAddress
+ (Index
<< 12)) | PageDescriptor
;
69 // Formulate page table entry, Domain=0, NS=0
70 PageTableDescriptor
= (((UINTN
)PageTable
) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK
) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE
;
72 // Write the page table entry out, replacing section entry
73 FirstLevelTable
[FirstLevelIdx
] = PageTableDescriptor
;
81 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
84 OUT BOOLEAN
*FlushTlbs OPTIONAL
92 UINT32 NumPageEntries
;
95 UINT32 PageTableIndex
;
96 UINT32 PageTableEntry
;
97 UINT32 CurrentPageTableEntry
;
100 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
101 volatile ARM_PAGE_TABLE_ENTRY
*PageTable
;
103 Status
= EFI_SUCCESS
;
105 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
106 // EntryValue: values at bit positions specified by EntryMask
107 EntryMask
= TT_DESCRIPTOR_PAGE_TYPE_MASK
| TT_DESCRIPTOR_PAGE_AP_MASK
;
108 if ((Attributes
& EFI_MEMORY_XP
) != 0) {
109 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN
;
111 EntryValue
= TT_DESCRIPTOR_PAGE_TYPE_PAGE
;
114 // Although the PI spec is unclear on this, the GCD guarantees that only
115 // one Attribute bit is set at a time, so the order of the conditionals below
116 // is irrelevant. If no memory attribute is specified, we preserve whatever
117 // memory type is set in the page tables, and update the permission attributes
119 if ((Attributes
& EFI_MEMORY_UC
) != 0) {
120 // modify cacheability attributes
121 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
122 // map to strongly ordered
123 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
124 } else if ((Attributes
& EFI_MEMORY_WC
) != 0) {
125 // modify cacheability attributes
126 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
127 // map to normal non-cacheable
128 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
129 } else if ((Attributes
& EFI_MEMORY_WT
) != 0) {
130 // modify cacheability attributes
131 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
132 // write through with no-allocate
133 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
134 } else if ((Attributes
& EFI_MEMORY_WB
) != 0) {
135 // modify cacheability attributes
136 EntryMask
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK
;
137 // write back (with allocate)
138 EntryValue
|= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
139 } else if ((Attributes
& CACHE_ATTRIBUTE_MASK
) != 0) {
140 // catch unsupported memory type attributes
142 return EFI_UNSUPPORTED
;
145 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
146 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RO_RO
;
148 EntryValue
|= TT_DESCRIPTOR_PAGE_AP_RW_RW
;
151 // Obtain page table base
152 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
154 // Calculate number of 4KB page table entries to change
155 NumPageEntries
= (UINT32
)(Length
/ TT_DESCRIPTOR_PAGE_SIZE
);
157 // Iterate for the number of 4KB pages to change
159 for (p
= 0; p
< NumPageEntries
; p
++) {
160 // Calculate index into first level translation table for page table value
162 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (BaseAddress
+ Offset
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
163 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
165 // Read the descriptor from the first level page table
166 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
168 // Does this descriptor need to be converted from section entry to 4K pages?
169 if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (Descriptor
)) {
170 Status
= ConvertSectionToPages (FirstLevelIdx
<< TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
171 if (EFI_ERROR (Status
)) {
176 // Re-read descriptor
177 Descriptor
= FirstLevelTable
[FirstLevelIdx
];
178 if (FlushTlbs
!= NULL
) {
183 // Obtain page table base address
184 PageTable
= (ARM_PAGE_TABLE_ENTRY
*)TT_DESCRIPTOR_PAGE_BASE_ADDRESS (Descriptor
);
186 // Calculate index into the page table
187 PageTableIndex
= ((BaseAddress
+ Offset
) & TT_DESCRIPTOR_PAGE_INDEX_MASK
) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT
;
188 ASSERT (PageTableIndex
< TRANSLATION_TABLE_PAGE_COUNT
);
191 CurrentPageTableEntry
= PageTable
[PageTableIndex
];
193 // Mask off appropriate fields
194 PageTableEntry
= CurrentPageTableEntry
& ~EntryMask
;
196 // Mask in new attributes and/or permissions
197 PageTableEntry
|= EntryValue
;
199 if (CurrentPageTableEntry
!= PageTableEntry
) {
200 Mva
= (VOID
*)(UINTN
)((((UINTN
)FirstLevelIdx
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
) + (PageTableIndex
<< TT_DESCRIPTOR_PAGE_BASE_SHIFT
));
202 // Only need to update if we are changing the entry
203 PageTable
[PageTableIndex
] = PageTableEntry
;
204 ArmUpdateTranslationTableEntry ((VOID
*)&PageTable
[PageTableIndex
], Mva
);
207 Status
= EFI_SUCCESS
;
208 Offset
+= TT_DESCRIPTOR_PAGE_SIZE
;
209 } // End first level translation table loop
216 UpdateSectionEntries (
217 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
225 UINT32 FirstLevelIdx
;
228 UINT32 CurrentDescriptor
;
231 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
233 Status
= EFI_SUCCESS
;
235 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
236 // EntryValue: values at bit positions specified by EntryMask
238 // Make sure we handle a section range that is unmapped
239 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
| TT_DESCRIPTOR_SECTION_XN_MASK
|
240 TT_DESCRIPTOR_SECTION_AP_MASK
;
241 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
243 // Although the PI spec is unclear on this, the GCD guarantees that only
244 // one Attribute bit is set at a time, so the order of the conditionals below
245 // is irrelevant. If no memory attribute is specified, we preserve whatever
246 // memory type is set in the page tables, and update the permission attributes
248 if ((Attributes
& EFI_MEMORY_UC
) != 0) {
249 // modify cacheability attributes
250 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
251 // map to strongly ordered
252 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
253 } else if ((Attributes
& EFI_MEMORY_WC
) != 0) {
254 // modify cacheability attributes
255 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
256 // map to normal non-cacheable
257 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
258 } else if ((Attributes
& EFI_MEMORY_WT
) != 0) {
259 // modify cacheability attributes
260 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
261 // write through with no-allocate
262 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
263 } else if ((Attributes
& EFI_MEMORY_WB
) != 0) {
264 // modify cacheability attributes
265 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
266 // write back (with allocate)
267 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
268 } else if ((Attributes
& CACHE_ATTRIBUTE_MASK
) != 0) {
269 // catch unsupported memory type attributes
271 return EFI_UNSUPPORTED
;
274 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
275 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
277 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
280 if ((Attributes
& EFI_MEMORY_XP
) != 0) {
281 EntryValue
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
284 // obtain page table base
285 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
287 // calculate index into first level translation table for start of modification
288 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS (BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
289 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
291 // calculate number of 1MB first level entries this applies to
292 NumSections
= (UINT32
)(Length
/ TT_DESCRIPTOR_SECTION_SIZE
);
294 // iterate through each descriptor
295 for (i
= 0; i
< NumSections
; i
++) {
296 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
298 // has this descriptor already been converted to pages?
299 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (CurrentDescriptor
)) {
300 // forward this 1MB range to page table function instead
301 Status
= UpdatePageEntries (
302 (FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
,
303 TT_DESCRIPTOR_SECTION_SIZE
,
308 // still a section entry
310 if (CurrentDescriptor
!= 0) {
311 // mask off appropriate fields
312 Descriptor
= CurrentDescriptor
& ~EntryMask
;
314 Descriptor
= ((UINTN
)FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
317 // mask in new attributes and/or permissions
318 Descriptor
|= EntryValue
;
320 if (CurrentDescriptor
!= Descriptor
) {
321 Mva
= (VOID
*)(UINTN
)(((UINTN
)FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
);
323 // Only need to update if we are changing the descriptor
324 FirstLevelTable
[FirstLevelIdx
+ i
] = Descriptor
;
325 ArmUpdateTranslationTableEntry ((VOID
*)&FirstLevelTable
[FirstLevelIdx
+ i
], Mva
);
328 Status
= EFI_SUCCESS
;
336 ArmSetMemoryAttributes (
337 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
346 if (BaseAddress
> (UINT64
)MAX_ADDRESS
) {
347 return EFI_UNSUPPORTED
;
350 Length
= MIN (Length
, (UINT64
)MAX_ADDRESS
- BaseAddress
+ 1);
357 if ((BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
== 0) &&
358 (Length
>= TT_DESCRIPTOR_SECTION_SIZE
))
360 ChunkLength
= Length
- Length
% TT_DESCRIPTOR_SECTION_SIZE
;
364 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
370 Status
= UpdateSectionEntries (BaseAddress
, ChunkLength
, Attributes
);
375 // Process page by page until the next section boundary, but only if
376 // we have more than a section's worth of area to deal with after that.
378 ChunkLength
= TT_DESCRIPTOR_SECTION_SIZE
-
379 (BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
);
380 if (ChunkLength
+ TT_DESCRIPTOR_SECTION_SIZE
> Length
) {
381 ChunkLength
= Length
;
386 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
392 Status
= UpdatePageEntries (
400 if (EFI_ERROR (Status
)) {
404 BaseAddress
+= ChunkLength
;
405 Length
-= ChunkLength
;
416 ArmSetMemoryRegionNoExec (
417 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
421 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_XP
);
425 ArmClearMemoryRegionNoExec (
426 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
430 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);
434 ArmSetMemoryRegionReadOnly (
435 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
439 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_RO
);
443 ArmClearMemoryRegionReadOnly (
444 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
448 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);