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
;
210 } // End first level translation table loop
217 UpdateSectionEntries (
218 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
226 UINT32 FirstLevelIdx
;
229 UINT32 CurrentDescriptor
;
232 volatile ARM_FIRST_LEVEL_DESCRIPTOR
*FirstLevelTable
;
234 Status
= EFI_SUCCESS
;
236 // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
237 // EntryValue: values at bit positions specified by EntryMask
239 // Make sure we handle a section range that is unmapped
240 EntryMask
= TT_DESCRIPTOR_SECTION_TYPE_MASK
| TT_DESCRIPTOR_SECTION_XN_MASK
|
241 TT_DESCRIPTOR_SECTION_AP_MASK
;
242 EntryValue
= TT_DESCRIPTOR_SECTION_TYPE_SECTION
;
244 // Although the PI spec is unclear on this, the GCD guarantees that only
245 // one Attribute bit is set at a time, so the order of the conditionals below
246 // is irrelevant. If no memory attribute is specified, we preserve whatever
247 // memory type is set in the page tables, and update the permission attributes
249 if ((Attributes
& EFI_MEMORY_UC
) != 0) {
250 // modify cacheability attributes
251 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
252 // map to strongly ordered
253 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED
; // TEX[2:0] = 0, C=0, B=0
254 } else if ((Attributes
& EFI_MEMORY_WC
) != 0) {
255 // modify cacheability attributes
256 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
257 // map to normal non-cacheable
258 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE
; // TEX [2:0]= 001 = 0x2, B=0, C=0
259 } else if ((Attributes
& EFI_MEMORY_WT
) != 0) {
260 // modify cacheability attributes
261 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
262 // write through with no-allocate
263 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC
; // TEX [2:0] = 0, C=1, B=0
264 } else if ((Attributes
& EFI_MEMORY_WB
) != 0) {
265 // modify cacheability attributes
266 EntryMask
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK
;
267 // write back (with allocate)
268 EntryValue
|= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC
; // TEX [2:0] = 001, C=1, B=1
269 } else if ((Attributes
& CACHE_ATTRIBUTE_MASK
) != 0) {
270 // catch unsupported memory type attributes
272 return EFI_UNSUPPORTED
;
275 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
276 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RO_RO
;
278 EntryValue
|= TT_DESCRIPTOR_SECTION_AP_RW_RW
;
281 if ((Attributes
& EFI_MEMORY_XP
) != 0) {
282 EntryValue
|= TT_DESCRIPTOR_SECTION_XN_MASK
;
285 // obtain page table base
286 FirstLevelTable
= (ARM_FIRST_LEVEL_DESCRIPTOR
*)ArmGetTTBR0BaseAddress ();
288 // calculate index into first level translation table for start of modification
289 FirstLevelIdx
= TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress
) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT
;
290 ASSERT (FirstLevelIdx
< TRANSLATION_TABLE_SECTION_COUNT
);
292 // calculate number of 1MB first level entries this applies to
293 NumSections
= (UINT32
)(Length
/ TT_DESCRIPTOR_SECTION_SIZE
);
295 // iterate through each descriptor
296 for(i
=0; i
<NumSections
; i
++) {
297 CurrentDescriptor
= FirstLevelTable
[FirstLevelIdx
+ i
];
299 // has this descriptor already been converted to pages?
300 if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor
)) {
301 // forward this 1MB range to page table function instead
302 Status
= UpdatePageEntries (
303 (FirstLevelIdx
+ i
) << TT_DESCRIPTOR_SECTION_BASE_SHIFT
,
304 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
;
363 "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
364 BaseAddress
, ChunkLength
, Attributes
));
366 Status
= UpdateSectionEntries (BaseAddress
, ChunkLength
, Attributes
);
372 // Process page by page until the next section boundary, but only if
373 // we have more than a section's worth of area to deal with after that.
375 ChunkLength
= TT_DESCRIPTOR_SECTION_SIZE
-
376 (BaseAddress
% TT_DESCRIPTOR_SECTION_SIZE
);
377 if (ChunkLength
+ TT_DESCRIPTOR_SECTION_SIZE
> Length
) {
378 ChunkLength
= Length
;
382 "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
383 BaseAddress
, ChunkLength
, Attributes
));
385 Status
= UpdatePageEntries (BaseAddress
, ChunkLength
, Attributes
,
389 if (EFI_ERROR (Status
)) {
393 BaseAddress
+= ChunkLength
;
394 Length
-= ChunkLength
;
404 ArmSetMemoryRegionNoExec (
405 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
409 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_XP
);
413 ArmClearMemoryRegionNoExec (
414 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
418 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);
422 ArmSetMemoryRegionReadOnly (
423 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
427 return ArmSetMemoryAttributes (BaseAddress
, Length
, EFI_MEMORY_RO
);
431 ArmClearMemoryRegionReadOnly (
432 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
436 return ArmSetMemoryAttributes (BaseAddress
, Length
, __EFI_MEMORY_RWX
);