]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
[mirror_edk2.git] / UefiCpuPkg / Library / CpuPageTableLib / CpuPageTableMap.c
1 /** @file
2 This library implements CpuPageTableLib that are generic for IA32 family CPU.
3
4 Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "CpuPageTable.h"
10
11 /**
12 Set the IA32_PTE_4K.
13
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.
20 **/
21 VOID
22 PageTableLibSetPte4K (
23 IN IA32_PTE_4K *Pte4K,
24 IN UINT64 Offset,
25 IN IA32_MAP_ATTRIBUTE *Attribute,
26 IN IA32_MAP_ATTRIBUTE *Mask
27 )
28 {
29 if (Mask->Bits.PageTableBaseAddress) {
30 //
31 // Reset all attributes when the physical address is changed.
32 //
33 Pte4K->Uint64 = IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset;
34 }
35
36 if (Mask->Bits.Present) {
37 Pte4K->Bits.Present = Attribute->Bits.Present;
38 }
39
40 if (Mask->Bits.ReadWrite) {
41 Pte4K->Bits.ReadWrite = Attribute->Bits.ReadWrite;
42 }
43
44 if (Mask->Bits.UserSupervisor) {
45 Pte4K->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
46 }
47
48 if (Mask->Bits.WriteThrough) {
49 Pte4K->Bits.WriteThrough = Attribute->Bits.WriteThrough;
50 }
51
52 if (Mask->Bits.CacheDisabled) {
53 Pte4K->Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
54 }
55
56 if (Mask->Bits.Accessed) {
57 Pte4K->Bits.Accessed = Attribute->Bits.Accessed;
58 }
59
60 if (Mask->Bits.Dirty) {
61 Pte4K->Bits.Dirty = Attribute->Bits.Dirty;
62 }
63
64 if (Mask->Bits.Pat) {
65 Pte4K->Bits.Pat = Attribute->Bits.Pat;
66 }
67
68 if (Mask->Bits.Global) {
69 Pte4K->Bits.Global = Attribute->Bits.Global;
70 }
71
72 if (Mask->Bits.ProtectionKey) {
73 Pte4K->Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
74 }
75
76 if (Mask->Bits.Nx) {
77 Pte4K->Bits.Nx = Attribute->Bits.Nx;
78 }
79 }
80
81 /**
82 Set the IA32_PDPTE_1G or IA32_PDE_2M.
83
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.
90 **/
91 VOID
92 PageTableLibSetPleB (
93 IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB,
94 IN UINT64 Offset,
95 IN IA32_MAP_ATTRIBUTE *Attribute,
96 IN IA32_MAP_ATTRIBUTE *Mask
97 )
98 {
99 if (Mask->Bits.PageTableBaseAddress) {
100 //
101 // Reset all attributes when the physical address is changed.
102 //
103 PleB->Uint64 = IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset;
104 }
105
106 PleB->Bits.MustBeOne = 1;
107
108 if (Mask->Bits.Present) {
109 PleB->Bits.Present = Attribute->Bits.Present;
110 }
111
112 if (Mask->Bits.ReadWrite) {
113 PleB->Bits.ReadWrite = Attribute->Bits.ReadWrite;
114 }
115
116 if (Mask->Bits.UserSupervisor) {
117 PleB->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
118 }
119
120 if (Mask->Bits.WriteThrough) {
121 PleB->Bits.WriteThrough = Attribute->Bits.WriteThrough;
122 }
123
124 if (Mask->Bits.CacheDisabled) {
125 PleB->Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
126 }
127
128 if (Mask->Bits.Accessed) {
129 PleB->Bits.Accessed = Attribute->Bits.Accessed;
130 }
131
132 if (Mask->Bits.Dirty) {
133 PleB->Bits.Dirty = Attribute->Bits.Dirty;
134 }
135
136 if (Mask->Bits.Pat) {
137 PleB->Bits.Pat = Attribute->Bits.Pat;
138 }
139
140 if (Mask->Bits.Global) {
141 PleB->Bits.Global = Attribute->Bits.Global;
142 }
143
144 if (Mask->Bits.ProtectionKey) {
145 PleB->Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
146 }
147
148 if (Mask->Bits.Nx) {
149 PleB->Bits.Nx = Attribute->Bits.Nx;
150 }
151 }
152
153 /**
154 Set the IA32_PDPTE_1G, IA32_PDE_2M or IA32_PTE_4K.
155
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.
163 **/
164 VOID
165 PageTableLibSetPle (
166 IN UINTN Level,
167 IN IA32_PAGING_ENTRY *Ple,
168 IN UINT64 Offset,
169 IN IA32_MAP_ATTRIBUTE *Attribute,
170 IN IA32_MAP_ATTRIBUTE *Mask
171 )
172 {
173 if (Level == 1) {
174 PageTableLibSetPte4K (&Ple->Pte4K, Offset, Attribute, Mask);
175 } else {
176 ASSERT (Level == 2 || Level == 3);
177 PageTableLibSetPleB (&Ple->PleB, Offset, Attribute, Mask);
178 }
179 }
180
181 /**
182 Set the IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE.
183
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.
186 **/
187 VOID
188 PageTableLibSetPnle (
189 IN IA32_PAGE_NON_LEAF_ENTRY *Pnle,
190 IN IA32_MAP_ATTRIBUTE *Attribute
191 )
192 {
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;
198
199 //
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.
204 //
205 Pnle->Bits.WriteThrough = 0;
206 Pnle->Bits.CacheDisabled = 0;
207 }
208
209 /**
210 Update page table to map [LinearAddress, LinearAddress + Length) with specified attribute in the specified level.
211
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.
229
230 @retval RETURN_SUCCESS PageTable is created/updated successfully.
231 **/
232 RETURN_STATUS
233 PageTableLibMapInLevel (
234 IN IA32_PAGING_ENTRY *ParentPagingEntry,
235 IN BOOLEAN Modify,
236 IN VOID *Buffer,
237 IN OUT INTN *BufferSize,
238 IN UINTN Level,
239 IN UINTN MaxLeafLevel,
240 IN UINT64 LinearAddress,
241 IN UINT64 Length,
242 IN UINT64 Offset,
243 IN IA32_MAP_ATTRIBUTE *Attribute,
244 IN IA32_MAP_ATTRIBUTE *Mask
245 )
246 {
247 RETURN_STATUS Status;
248 UINTN BitStart;
249 UINTN Index;
250 IA32_PAGING_ENTRY *PagingEntry;
251 UINT64 RegionLength;
252 UINT64 SubLength;
253 UINT64 SubOffset;
254 UINT64 RegionMask;
255 UINT64 RegionStart;
256 IA32_MAP_ATTRIBUTE AllOneMask;
257 IA32_MAP_ATTRIBUTE PleBAttribute;
258 IA32_MAP_ATTRIBUTE NopAttribute;
259 BOOLEAN CreateNew;
260 IA32_PAGING_ENTRY OneOfPagingEntry;
261
262 ASSERT (Level != 0);
263 ASSERT ((Attribute != NULL) && (Mask != NULL));
264
265 CreateNew = FALSE;
266 AllOneMask.Uint64 = ~0ull;
267
268 NopAttribute.Uint64 = 0;
269 NopAttribute.Bits.Present = 1;
270 NopAttribute.Bits.ReadWrite = 1;
271 NopAttribute.Bits.UserSupervisor = 1;
272
273 //
274 // ParentPagingEntry ONLY is deferenced for checking Present and MustBeOne bits
275 // when Modify is FALSE.
276 //
277
278 if (ParentPagingEntry->Pce.Present == 0) {
279 //
280 // The parent entry is CR3 or PML5E/PML4E/PDPTE/PDE.
281 // It does NOT point to an existing page directory.
282 //
283 ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
284 CreateNew = TRUE;
285 *BufferSize -= SIZE_4KB;
286
287 if (Modify) {
288 ParentPagingEntry->Uintn = (UINTN)Buffer + *BufferSize;
289 ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB);
290 //
291 // Set default attribute bits for PML5E/PML4E/PDPTE/PDE.
292 //
293 PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute);
294 } else {
295 //
296 // Just make sure Present and MustBeZero (PageSize) bits are accurate.
297 //
298 OneOfPagingEntry.Pnle.Uint64 = 0;
299 }
300 } else if (IsPle (ParentPagingEntry, Level + 1)) {
301 //
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.
304 //
305 //
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.
308 //
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))
312 {
313 //
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.
316 //
317 return RETURN_SUCCESS;
318 }
319
320 ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
321 CreateNew = TRUE;
322 *BufferSize -= SIZE_4KB;
323 PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask);
324 if (Modify) {
325 //
326 // Create 512 child-level entries that map to 2M/4K.
327 //
328 ParentPagingEntry->Uintn = (UINTN)Buffer + *BufferSize;
329 ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB);
330
331 //
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.
335 //
336 PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute);
337
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;
343 }
344 }
345 }
346
347 //
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).
350 //
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;
356
357 //
358 // Apply the attribute.
359 //
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)) {
364 //
365 // Create one entry mapping the entire region (1G, 2M or 4K).
366 //
367 if (Modify) {
368 PageTableLibSetPle (Level, &PagingEntry[Index], Offset, Attribute, Mask);
369 }
370 } else {
371 //
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.
378 //
379 Status = PageTableLibMapInLevel (
380 (!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry[Index],
381 Modify,
382 Buffer,
383 BufferSize,
384 Level - 1,
385 MaxLeafLevel,
386 LinearAddress,
387 Length,
388 Offset,
389 Attribute,
390 Mask
391 );
392 if (RETURN_ERROR (Status)) {
393 return Status;
394 }
395 }
396
397 Offset += SubLength;
398 RegionStart += RegionLength;
399 Index++;
400 }
401
402 return RETURN_SUCCESS;
403 }
404
405 /**
406 Create or update page table to map [LinearAddress, LinearAddress + Length) with specified attribute.
407
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.
422
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.
430 **/
431 RETURN_STATUS
432 EFIAPI
433 PageTableMap (
434 IN OUT UINTN *PageTable OPTIONAL,
435 IN PAGING_MODE PagingMode,
436 IN VOID *Buffer,
437 IN OUT UINTN *BufferSize,
438 IN UINT64 LinearAddress,
439 IN UINT64 Length,
440 IN IA32_MAP_ATTRIBUTE *Attribute,
441 IN IA32_MAP_ATTRIBUTE *Mask
442 )
443 {
444 RETURN_STATUS Status;
445 IA32_PAGING_ENTRY TopPagingEntry;
446 INTN RequiredSize;
447 UINT64 MaxLinearAddress;
448 UINTN MaxLevel;
449 UINTN MaxLeafLevel;
450
451 if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
452 //
453 // 32bit paging is never supported.
454 // PAE paging will be supported later.
455 //
456 return RETURN_UNSUPPORTED;
457 }
458
459 if ((PageTable == NULL) || (BufferSize == NULL) || (Attribute == NULL) || (Mask == NULL)) {
460 return RETURN_INVALID_PARAMETER;
461 }
462
463 if (*BufferSize % SIZE_4KB != 0) {
464 //
465 // BufferSize should be multiple of 4K.
466 //
467 return RETURN_INVALID_PARAMETER;
468 }
469
470 if ((*BufferSize != 0) && (Buffer == NULL)) {
471 return RETURN_INVALID_PARAMETER;
472 }
473
474 MaxLeafLevel = (UINT8)PagingMode;
475 MaxLevel = (UINT8)(PagingMode >> 8);
476 MaxLinearAddress = LShiftU64 (1, 12 + MaxLevel * 9);
477
478 if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - LinearAddress)) {
479 //
480 // Maximum linear address is (1 << 48) or (1 << 57)
481 //
482 return RETURN_INVALID_PARAMETER;
483 }
484
485 TopPagingEntry.Uintn = *PageTable;
486 if (TopPagingEntry.Uintn != 0) {
487 TopPagingEntry.Pce.Present = 1;
488 }
489
490 //
491 // Query the required buffer size without modifying the page table.
492 //
493 RequiredSize = 0;
494 Status = PageTableLibMapInLevel (
495 &TopPagingEntry,
496 FALSE,
497 NULL,
498 &RequiredSize,
499 MaxLevel,
500 MaxLeafLevel,
501 LinearAddress,
502 Length,
503 0,
504 Attribute,
505 Mask
506 );
507 if (RETURN_ERROR (Status)) {
508 return Status;
509 }
510
511 RequiredSize = -RequiredSize;
512
513 if ((UINTN)RequiredSize > *BufferSize) {
514 *BufferSize = RequiredSize;
515 return RETURN_BUFFER_TOO_SMALL;
516 }
517
518 if ((RequiredSize != 0) && (Buffer == NULL)) {
519 return RETURN_INVALID_PARAMETER;
520 }
521
522 //
523 // Update the page table when the supplied buffer is sufficient.
524 //
525 Status = PageTableLibMapInLevel (
526 &TopPagingEntry,
527 TRUE,
528 Buffer,
529 (INTN *)BufferSize,
530 MaxLevel,
531 MaxLeafLevel,
532 LinearAddress,
533 Length,
534 0,
535 Attribute,
536 Mask
537 );
538 if (!RETURN_ERROR (Status)) {
539 *PageTable = (UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40);
540 }
541
542 return Status;
543 }