2 This library implements CpuPageTableLib that are generic for IA32 family CPU.
4 Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "CpuPageTable.h"
14 @param[in] Pte4K Pointer to IA32_PTE_4K.
15 @param[in] Offset The offset within the linear address range.
16 @param[in] Attribute The attribute of the linear address range.
17 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
18 Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
19 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
22 PageTableLibSetPte4K (
23 IN IA32_PTE_4K
*Pte4K
,
25 IN IA32_MAP_ATTRIBUTE
*Attribute
,
26 IN IA32_MAP_ATTRIBUTE
*Mask
29 if (Mask
->Bits
.PageTableBaseAddress
) {
31 // Reset all attributes when the physical address is changed.
33 Pte4K
->Uint64
= IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute
) + Offset
;
36 if (Mask
->Bits
.Present
) {
37 Pte4K
->Bits
.Present
= Attribute
->Bits
.Present
;
40 if (Mask
->Bits
.ReadWrite
) {
41 Pte4K
->Bits
.ReadWrite
= Attribute
->Bits
.ReadWrite
;
44 if (Mask
->Bits
.UserSupervisor
) {
45 Pte4K
->Bits
.UserSupervisor
= Attribute
->Bits
.UserSupervisor
;
48 if (Mask
->Bits
.WriteThrough
) {
49 Pte4K
->Bits
.WriteThrough
= Attribute
->Bits
.WriteThrough
;
52 if (Mask
->Bits
.CacheDisabled
) {
53 Pte4K
->Bits
.CacheDisabled
= Attribute
->Bits
.CacheDisabled
;
56 if (Mask
->Bits
.Accessed
) {
57 Pte4K
->Bits
.Accessed
= Attribute
->Bits
.Accessed
;
60 if (Mask
->Bits
.Dirty
) {
61 Pte4K
->Bits
.Dirty
= Attribute
->Bits
.Dirty
;
65 Pte4K
->Bits
.Pat
= Attribute
->Bits
.Pat
;
68 if (Mask
->Bits
.Global
) {
69 Pte4K
->Bits
.Global
= Attribute
->Bits
.Global
;
72 if (Mask
->Bits
.ProtectionKey
) {
73 Pte4K
->Bits
.ProtectionKey
= Attribute
->Bits
.ProtectionKey
;
77 Pte4K
->Bits
.Nx
= Attribute
->Bits
.Nx
;
82 Set the IA32_PDPTE_1G or IA32_PDE_2M.
84 @param[in] PleB Pointer to PDPTE_1G or PDE_2M. Both share the same structure definition.
85 @param[in] Offset The offset within the linear address range.
86 @param[in] Attribute The attribute of the linear address range.
87 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
88 Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
89 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
93 IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE
*PleB
,
95 IN IA32_MAP_ATTRIBUTE
*Attribute
,
96 IN IA32_MAP_ATTRIBUTE
*Mask
99 if (Mask
->Bits
.PageTableBaseAddress
) {
101 // Reset all attributes when the physical address is changed.
103 PleB
->Uint64
= IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute
) + Offset
;
106 PleB
->Bits
.MustBeOne
= 1;
108 if (Mask
->Bits
.Present
) {
109 PleB
->Bits
.Present
= Attribute
->Bits
.Present
;
112 if (Mask
->Bits
.ReadWrite
) {
113 PleB
->Bits
.ReadWrite
= Attribute
->Bits
.ReadWrite
;
116 if (Mask
->Bits
.UserSupervisor
) {
117 PleB
->Bits
.UserSupervisor
= Attribute
->Bits
.UserSupervisor
;
120 if (Mask
->Bits
.WriteThrough
) {
121 PleB
->Bits
.WriteThrough
= Attribute
->Bits
.WriteThrough
;
124 if (Mask
->Bits
.CacheDisabled
) {
125 PleB
->Bits
.CacheDisabled
= Attribute
->Bits
.CacheDisabled
;
128 if (Mask
->Bits
.Accessed
) {
129 PleB
->Bits
.Accessed
= Attribute
->Bits
.Accessed
;
132 if (Mask
->Bits
.Dirty
) {
133 PleB
->Bits
.Dirty
= Attribute
->Bits
.Dirty
;
136 if (Mask
->Bits
.Pat
) {
137 PleB
->Bits
.Pat
= Attribute
->Bits
.Pat
;
140 if (Mask
->Bits
.Global
) {
141 PleB
->Bits
.Global
= Attribute
->Bits
.Global
;
144 if (Mask
->Bits
.ProtectionKey
) {
145 PleB
->Bits
.ProtectionKey
= Attribute
->Bits
.ProtectionKey
;
149 PleB
->Bits
.Nx
= Attribute
->Bits
.Nx
;
154 Set the IA32_PDPTE_1G, IA32_PDE_2M or IA32_PTE_4K.
156 @param[in] Level 3, 2 or 1.
157 @param[in] Ple Pointer to PDPTE_1G, PDE_2M or IA32_PTE_4K, depending on the Level.
158 @param[in] Offset The offset within the linear address range.
159 @param[in] Attribute The attribute of the linear address range.
160 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
161 Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
162 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
167 IN IA32_PAGING_ENTRY
*Ple
,
169 IN IA32_MAP_ATTRIBUTE
*Attribute
,
170 IN IA32_MAP_ATTRIBUTE
*Mask
174 PageTableLibSetPte4K (&Ple
->Pte4K
, Offset
, Attribute
, Mask
);
176 ASSERT (Level
== 2 || Level
== 3);
177 PageTableLibSetPleB (&Ple
->PleB
, Offset
, Attribute
, Mask
);
182 Set the IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE.
184 @param[in] Pnle Pointer to IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE. All share the same structure definition.
185 @param[in] Attribute The attribute of the page directory referenced by the non-leaf.
188 PageTableLibSetPnle (
189 IN IA32_PAGE_NON_LEAF_ENTRY
*Pnle
,
190 IN IA32_MAP_ATTRIBUTE
*Attribute
193 Pnle
->Bits
.Present
= Attribute
->Bits
.Present
;
194 Pnle
->Bits
.ReadWrite
= Attribute
->Bits
.ReadWrite
;
195 Pnle
->Bits
.UserSupervisor
= Attribute
->Bits
.UserSupervisor
;
196 Pnle
->Bits
.Nx
= Attribute
->Bits
.Nx
;
197 Pnle
->Bits
.Accessed
= 0;
200 // Set the attributes (WT, CD, A) to 0.
201 // WT and CD determin the memory type used to access the 4K page directory referenced by this entry.
202 // So, it implictly requires PAT[0] is Write Back.
203 // Create a new parameter if caller requires to use a different memory type for accessing page directories.
205 Pnle
->Bits
.WriteThrough
= 0;
206 Pnle
->Bits
.CacheDisabled
= 0;
210 Update page table to map [LinearAddress, LinearAddress + Length) with specified attribute in the specified level.
212 @param[in] ParentPagingEntry The pointer to the page table entry to update.
213 @param[in] Modify FALSE to indicate Buffer is not used and BufferSize is increased by the required buffer size.
214 @param[in] Buffer The free buffer to be used for page table creation/updating.
215 When Modify is TRUE, it's used from the end.
216 When Modify is FALSE, it's ignored.
217 @param[in, out] BufferSize The available buffer size.
218 Return the remaining buffer size.
219 @param[in] Level Page table level. Could be 5, 4, 3, 2, or 1.
220 @param[in] MaxLeafLevel Maximum level that can be a leaf entry. Could be 1, 2 or 3 (if Page 1G is supported).
221 @param[in] LinearAddress The start of the linear address range.
222 @param[in] Length The length of the linear address range.
223 @param[in] Offset The offset within the linear address range.
224 @param[in] Attribute The attribute of the linear address range.
225 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
226 Page table entries that map the linear address range are reset to 0 before set to the new attribute
227 when a new physical base address is set.
228 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
230 @retval RETURN_SUCCESS PageTable is created/updated successfully.
233 PageTableLibMapInLevel (
234 IN IA32_PAGING_ENTRY
*ParentPagingEntry
,
237 IN OUT INTN
*BufferSize
,
239 IN UINTN MaxLeafLevel
,
240 IN UINT64 LinearAddress
,
243 IN IA32_MAP_ATTRIBUTE
*Attribute
,
244 IN IA32_MAP_ATTRIBUTE
*Mask
247 RETURN_STATUS Status
;
250 IA32_PAGING_ENTRY
*PagingEntry
;
256 IA32_MAP_ATTRIBUTE AllOneMask
;
257 IA32_MAP_ATTRIBUTE PleBAttribute
;
258 IA32_MAP_ATTRIBUTE NopAttribute
;
260 IA32_PAGING_ENTRY OneOfPagingEntry
;
263 ASSERT ((Attribute
!= NULL
) && (Mask
!= NULL
));
266 AllOneMask
.Uint64
= ~0ull;
268 NopAttribute
.Uint64
= 0;
269 NopAttribute
.Bits
.Present
= 1;
270 NopAttribute
.Bits
.ReadWrite
= 1;
271 NopAttribute
.Bits
.UserSupervisor
= 1;
274 // ParentPagingEntry ONLY is deferenced for checking Present and MustBeOne bits
275 // when Modify is FALSE.
278 if (ParentPagingEntry
->Pce
.Present
== 0) {
280 // The parent entry is CR3 or PML5E/PML4E/PDPTE/PDE.
281 // It does NOT point to an existing page directory.
283 ASSERT (Buffer
== NULL
|| *BufferSize
>= SIZE_4KB
);
285 *BufferSize
-= SIZE_4KB
;
288 ParentPagingEntry
->Uintn
= (UINTN
)Buffer
+ *BufferSize
;
289 ZeroMem ((VOID
*)ParentPagingEntry
->Uintn
, SIZE_4KB
);
291 // Set default attribute bits for PML5E/PML4E/PDPTE/PDE.
293 PageTableLibSetPnle (&ParentPagingEntry
->Pnle
, &NopAttribute
);
296 // Just make sure Present and MustBeZero (PageSize) bits are accurate.
298 OneOfPagingEntry
.Pnle
.Uint64
= 0;
300 } else if (IsPle (ParentPagingEntry
, Level
+ 1)) {
302 // The parent entry is a PDPTE_1G or PDE_2M. Split to 2M or 4K pages.
303 // Note: it's impossible the parent entry is a PTE_4K.
306 // Use NOP attributes as the attribute of grand-parents because CPU will consider
307 // the actual attributes of grand-parents when determing the memory type.
309 PleBAttribute
.Uint64
= PageTableLibGetPleBMapAttribute (&ParentPagingEntry
->PleB
, &NopAttribute
);
310 if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute
) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask
))
311 == IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute
))
314 // This function is called when the memory length is less than the region length of the parent level.
315 // No need to split the page when the attributes equal.
317 return RETURN_SUCCESS
;
320 ASSERT (Buffer
== NULL
|| *BufferSize
>= SIZE_4KB
);
322 *BufferSize
-= SIZE_4KB
;
323 PageTableLibSetPle (Level
, &OneOfPagingEntry
, 0, &PleBAttribute
, &AllOneMask
);
326 // Create 512 child-level entries that map to 2M/4K.
328 ParentPagingEntry
->Uintn
= (UINTN
)Buffer
+ *BufferSize
;
329 ZeroMem ((VOID
*)ParentPagingEntry
->Uintn
, SIZE_4KB
);
332 // Set NOP attributes
333 // Note: Should NOT inherit the attributes from the original entry because a zero RW bit
334 // will make the entire region read-only even the child entries set the RW bit.
336 PageTableLibSetPnle (&ParentPagingEntry
->Pnle
, &NopAttribute
);
338 RegionLength
= REGION_LENGTH (Level
);
339 PagingEntry
= (IA32_PAGING_ENTRY
*)(UINTN
)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry
->Pnle
);
340 for (SubOffset
= 0, Index
= 0; Index
< 512; Index
++) {
341 PagingEntry
[Index
].Uint64
= OneOfPagingEntry
.Uint64
+ SubOffset
;
342 SubOffset
+= RegionLength
;
348 // RegionLength: 256T (1 << 48) 512G (1 << 39), 1G (1 << 30), 2M (1 << 21) or 4K (1 << 12).
349 // RegionStart: points to the linear address that's aligned on RegionLength and lower than (LinearAddress + Offset).
351 BitStart
= 12 + (Level
- 1) * 9;
352 Index
= (UINTN
)BitFieldRead64 (LinearAddress
+ Offset
, BitStart
, BitStart
+ 9 - 1);
353 RegionLength
= LShiftU64 (1, BitStart
);
354 RegionMask
= RegionLength
- 1;
355 RegionStart
= (LinearAddress
+ Offset
) & ~RegionMask
;
358 // Apply the attribute.
360 PagingEntry
= (IA32_PAGING_ENTRY
*)(UINTN
)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry
->Pnle
);
361 while (Offset
< Length
&& Index
< 512) {
362 SubLength
= MIN (Length
- Offset
, RegionStart
+ RegionLength
- (LinearAddress
+ Offset
));
363 if ((Level
<= MaxLeafLevel
) && (LinearAddress
+ Offset
== RegionStart
) && (SubLength
== RegionLength
)) {
365 // Create one entry mapping the entire region (1G, 2M or 4K).
368 PageTableLibSetPle (Level
, &PagingEntry
[Index
], Offset
, Attribute
, Mask
);
372 // Recursively call to create page table.
373 // There are 3 cases:
374 // a. Level cannot be a leaf entry which points to physical memory.
375 // a. Level can be a leaf entry but (LinearAddress + Offset) is NOT aligned on the RegionStart.
376 // b. Level can be a leaf entry and (LinearAddress + Offset) is aligned on RegionStart,
377 // but the length is SMALLER than the RegionLength.
379 Status
= PageTableLibMapInLevel (
380 (!Modify
&& CreateNew
) ? &OneOfPagingEntry
: &PagingEntry
[Index
],
392 if (RETURN_ERROR (Status
)) {
398 RegionStart
+= RegionLength
;
402 return RETURN_SUCCESS
;
406 Create or update page table to map [LinearAddress, LinearAddress + Length) with specified attribute.
408 @param[in, out] PageTable The pointer to the page table to update, or pointer to NULL if a new page table is to be created.
409 @param[in] PagingMode The paging mode.
410 @param[in] Buffer The free buffer to be used for page table creation/updating.
411 @param[in, out] BufferSize The buffer size.
412 On return, the remaining buffer size.
413 The free buffer is used from the end so caller can supply the same Buffer pointer with an updated
414 BufferSize in the second call to this API.
415 @param[in] LinearAddress The start of the linear address range.
416 @param[in] Length The length of the linear address range.
417 @param[in] Attribute The attribute of the linear address range.
418 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
419 Page table entries that map the linear address range are reset to 0 before set to the new attribute
420 when a new physical base address is set.
421 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
423 @retval RETURN_UNSUPPORTED PagingMode is not supported.
424 @retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Mask is NULL.
425 @retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB.
426 @retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table creation/updating.
427 BufferSize is updated to indicate the expected buffer size.
428 Caller may still get RETURN_BUFFER_TOO_SMALL with the new BufferSize.
429 @retval RETURN_SUCCESS PageTable is created/updated successfully.
434 IN OUT UINTN
*PageTable OPTIONAL
,
435 IN PAGING_MODE PagingMode
,
437 IN OUT UINTN
*BufferSize
,
438 IN UINT64 LinearAddress
,
440 IN IA32_MAP_ATTRIBUTE
*Attribute
,
441 IN IA32_MAP_ATTRIBUTE
*Mask
444 RETURN_STATUS Status
;
445 IA32_PAGING_ENTRY TopPagingEntry
;
447 UINT64 MaxLinearAddress
;
451 if ((PagingMode
== Paging32bit
) || (PagingMode
== PagingPae
) || (PagingMode
>= PagingModeMax
)) {
453 // 32bit paging is never supported.
454 // PAE paging will be supported later.
456 return RETURN_UNSUPPORTED
;
459 if ((PageTable
== NULL
) || (BufferSize
== NULL
) || (Attribute
== NULL
) || (Mask
== NULL
)) {
460 return RETURN_INVALID_PARAMETER
;
463 if (*BufferSize
% SIZE_4KB
!= 0) {
465 // BufferSize should be multiple of 4K.
467 return RETURN_INVALID_PARAMETER
;
470 if ((*BufferSize
!= 0) && (Buffer
== NULL
)) {
471 return RETURN_INVALID_PARAMETER
;
474 MaxLeafLevel
= (UINT8
)PagingMode
;
475 MaxLevel
= (UINT8
)(PagingMode
>> 8);
476 MaxLinearAddress
= LShiftU64 (1, 12 + MaxLevel
* 9);
478 if ((LinearAddress
> MaxLinearAddress
) || (Length
> MaxLinearAddress
- LinearAddress
)) {
480 // Maximum linear address is (1 << 48) or (1 << 57)
482 return RETURN_INVALID_PARAMETER
;
485 TopPagingEntry
.Uintn
= *PageTable
;
486 if (TopPagingEntry
.Uintn
!= 0) {
487 TopPagingEntry
.Pce
.Present
= 1;
491 // Query the required buffer size without modifying the page table.
494 Status
= PageTableLibMapInLevel (
507 if (RETURN_ERROR (Status
)) {
511 RequiredSize
= -RequiredSize
;
513 if ((UINTN
)RequiredSize
> *BufferSize
) {
514 *BufferSize
= RequiredSize
;
515 return RETURN_BUFFER_TOO_SMALL
;
518 if ((RequiredSize
!= 0) && (Buffer
== NULL
)) {
519 return RETURN_INVALID_PARAMETER
;
523 // Update the page table when the supplied buffer is sufficient.
525 Status
= PageTableLibMapInLevel (
538 if (!RETURN_ERROR (Status
)) {
539 *PageTable
= (UINTN
)(TopPagingEntry
.Uintn
& IA32_PE_BASE_ADDRESS_MASK_40
);