]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
CpuPageTableLib: Fix parent attributes are not inherited properly
[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 @param[in] Mask The mask of the page directory referenced by the non-leaf.
187 **/
188 VOID
189 PageTableLibSetPnle (
190 IN IA32_PAGE_NON_LEAF_ENTRY *Pnle,
191 IN IA32_MAP_ATTRIBUTE *Attribute,
192 IN IA32_MAP_ATTRIBUTE *Mask
193 )
194 {
195 if (Mask->Bits.Present) {
196 Pnle->Bits.Present = Attribute->Bits.Present;
197 }
198
199 if (Mask->Bits.ReadWrite) {
200 Pnle->Bits.ReadWrite = Attribute->Bits.ReadWrite;
201 }
202
203 if (Mask->Bits.UserSupervisor) {
204 Pnle->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
205 }
206
207 if (Mask->Bits.Nx) {
208 Pnle->Bits.Nx = Attribute->Bits.Nx;
209 }
210
211 Pnle->Bits.Accessed = 0;
212
213 //
214 // Set the attributes (WT, CD, A) to 0.
215 // WT and CD determin the memory type used to access the 4K page directory referenced by this entry.
216 // So, it implictly requires PAT[0] is Write Back.
217 // Create a new parameter if caller requires to use a different memory type for accessing page directories.
218 //
219 Pnle->Bits.WriteThrough = 0;
220 Pnle->Bits.CacheDisabled = 0;
221 }
222
223 /**
224 Update page table to map [LinearAddress, LinearAddress + Length) with specified attribute in the specified level.
225
226 @param[in] ParentPagingEntry The pointer to the page table entry to update.
227 @param[in] ParentAttribute The accumulated attribute of all parents' attribute.
228 @param[in] Modify FALSE to indicate Buffer is not used and BufferSize is increased by the required buffer size.
229 @param[in] Buffer The free buffer to be used for page table creation/updating.
230 When Modify is TRUE, it's used from the end.
231 When Modify is FALSE, it's ignored.
232 @param[in, out] BufferSize The available buffer size.
233 Return the remaining buffer size.
234 @param[in] Level Page table level. Could be 5, 4, 3, 2, or 1.
235 @param[in] MaxLeafLevel Maximum level that can be a leaf entry. Could be 1, 2 or 3 (if Page 1G is supported).
236 @param[in] LinearAddress The start of the linear address range.
237 @param[in] Length The length of the linear address range.
238 @param[in] Offset The offset within the linear address range.
239 @param[in] Attribute The attribute of the linear address range.
240 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
241 Page table entries that map the linear address range are reset to 0 before set to the new attribute
242 when a new physical base address is set.
243 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
244
245 @retval RETURN_SUCCESS PageTable is created/updated successfully.
246 **/
247 RETURN_STATUS
248 PageTableLibMapInLevel (
249 IN IA32_PAGING_ENTRY *ParentPagingEntry,
250 IN IA32_MAP_ATTRIBUTE *ParentAttribute,
251 IN BOOLEAN Modify,
252 IN VOID *Buffer,
253 IN OUT INTN *BufferSize,
254 IN UINTN Level,
255 IN UINTN MaxLeafLevel,
256 IN UINT64 LinearAddress,
257 IN UINT64 Length,
258 IN UINT64 Offset,
259 IN IA32_MAP_ATTRIBUTE *Attribute,
260 IN IA32_MAP_ATTRIBUTE *Mask
261 )
262 {
263 RETURN_STATUS Status;
264 UINTN BitStart;
265 UINTN Index;
266 IA32_PAGING_ENTRY *PagingEntry;
267 IA32_PAGING_ENTRY *CurrentPagingEntry;
268 UINT64 RegionLength;
269 UINT64 SubLength;
270 UINT64 SubOffset;
271 UINT64 RegionMask;
272 UINT64 RegionStart;
273 IA32_MAP_ATTRIBUTE AllOneMask;
274 IA32_MAP_ATTRIBUTE PleBAttribute;
275 IA32_MAP_ATTRIBUTE NopAttribute;
276 BOOLEAN CreateNew;
277 IA32_PAGING_ENTRY OneOfPagingEntry;
278 IA32_MAP_ATTRIBUTE ChildAttribute;
279 IA32_MAP_ATTRIBUTE ChildMask;
280
281 ASSERT (Level != 0);
282 ASSERT ((Attribute != NULL) && (Mask != NULL));
283
284 CreateNew = FALSE;
285 AllOneMask.Uint64 = ~0ull;
286
287 NopAttribute.Uint64 = 0;
288 NopAttribute.Bits.Present = 1;
289 NopAttribute.Bits.ReadWrite = 1;
290 NopAttribute.Bits.UserSupervisor = 1;
291
292 //
293 // ParentPagingEntry ONLY is deferenced for checking Present and MustBeOne bits
294 // when Modify is FALSE.
295 //
296
297 if (ParentPagingEntry->Pce.Present == 0) {
298 //
299 // The parent entry is CR3 or PML5E/PML4E/PDPTE/PDE.
300 // It does NOT point to an existing page directory.
301 //
302 ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
303 CreateNew = TRUE;
304 *BufferSize -= SIZE_4KB;
305
306 if (Modify) {
307 ParentPagingEntry->Uintn = (UINTN)Buffer + *BufferSize;
308 ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB);
309 //
310 // Set default attribute bits for PML5E/PML4E/PDPTE/PDE.
311 //
312 PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
313 } else {
314 //
315 // Just make sure Present and MustBeZero (PageSize) bits are accurate.
316 //
317 OneOfPagingEntry.Pnle.Uint64 = 0;
318 }
319 } else if (IsPle (ParentPagingEntry, Level + 1)) {
320 //
321 // The parent entry is a PDPTE_1G or PDE_2M. Split to 2M or 4K pages.
322 // Note: it's impossible the parent entry is a PTE_4K.
323 //
324 //
325 // Use NOP attributes as the attribute of grand-parents because CPU will consider
326 // the actual attributes of grand-parents when determing the memory type.
327 //
328 PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, ParentAttribute);
329 if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask))
330 == (IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask)))
331 {
332 //
333 // This function is called when the memory length is less than the region length of the parent level.
334 // No need to split the page when the attributes equal.
335 //
336 return RETURN_SUCCESS;
337 }
338
339 ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
340 CreateNew = TRUE;
341 *BufferSize -= SIZE_4KB;
342 PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask);
343 if (Modify) {
344 //
345 // Create 512 child-level entries that map to 2M/4K.
346 //
347 ParentPagingEntry->Uintn = (UINTN)Buffer + *BufferSize;
348 ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB);
349
350 //
351 // Set NOP attributes
352 // Note: Should NOT inherit the attributes from the original entry because a zero RW bit
353 // will make the entire region read-only even the child entries set the RW bit.
354 //
355 PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
356
357 RegionLength = REGION_LENGTH (Level);
358 PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
359 for (SubOffset = 0, Index = 0; Index < 512; Index++) {
360 PagingEntry[Index].Uint64 = OneOfPagingEntry.Uint64 + SubOffset;
361 SubOffset += RegionLength;
362 }
363 }
364 } else {
365 //
366 // It's a non-leaf entry
367 //
368 ChildAttribute.Uint64 = 0;
369 ChildMask.Uint64 = 0;
370
371 //
372 // If the inheritable attributes in the parent entry conflicts with the requested attributes,
373 // let the child entries take the parent attributes and
374 // loosen the attribute in the parent entry
375 // E.g.: when PDPTE[0].ReadWrite = 0 but caller wants to map [0-2MB] as ReadWrite = 1 (PDE[0].ReadWrite = 1)
376 // we need to change PDPTE[0].ReadWrite = 1 and let all PDE[0-255].ReadWrite = 0 in this step.
377 // when PDPTE[0].Nx = 1 but caller wants to map [0-2MB] as Nx = 0 (PDT[0].Nx = 0)
378 // we need to change PDPTE[0].Nx = 0 and let all PDE[0-255].Nx = 1 in this step.
379 if ((ParentPagingEntry->Pnle.Bits.Present == 0) && (Mask->Bits.Present == 1) && (Attribute->Bits.Present == 1)) {
380 if (Modify) {
381 ParentPagingEntry->Pnle.Bits.Present = 1;
382 }
383
384 ChildAttribute.Bits.Present = 0;
385 ChildMask.Bits.Present = 1;
386 }
387
388 if ((ParentPagingEntry->Pnle.Bits.ReadWrite == 0) && (Mask->Bits.ReadWrite == 1) && (Attribute->Bits.ReadWrite == 1)) {
389 if (Modify) {
390 ParentPagingEntry->Pnle.Bits.ReadWrite = 1;
391 }
392
393 ChildAttribute.Bits.ReadWrite = 0;
394 ChildMask.Bits.ReadWrite = 1;
395 }
396
397 if ((ParentPagingEntry->Pnle.Bits.UserSupervisor == 0) && (Mask->Bits.UserSupervisor == 1) && (Attribute->Bits.UserSupervisor == 1)) {
398 if (Modify) {
399 ParentPagingEntry->Pnle.Bits.UserSupervisor = 1;
400 }
401
402 ChildAttribute.Bits.UserSupervisor = 0;
403 ChildMask.Bits.UserSupervisor = 1;
404 }
405
406 if ((ParentPagingEntry->Pnle.Bits.Nx == 1) && (Mask->Bits.Nx == 1) && (Attribute->Bits.Nx == 0)) {
407 if (Modify) {
408 ParentPagingEntry->Pnle.Bits.Nx = 0;
409 }
410
411 ChildAttribute.Bits.Nx = 1;
412 ChildMask.Bits.Nx = 1;
413 }
414
415 if (ChildMask.Uint64 != 0) {
416 if (Modify) {
417 //
418 // Update child entries to use restrictive attribute inherited from parent.
419 // e.g.: Set PDE[0-255].ReadWrite = 0
420 //
421 PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
422 for (Index = 0; Index < 512; Index++) {
423 if (PagingEntry[Index].Pce.Present == 0) {
424 continue;
425 }
426
427 if (IsPle (&PagingEntry[Index], Level)) {
428 PageTableLibSetPle (Level - 1, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask);
429 } else {
430 PageTableLibSetPnle (&PagingEntry[Index].Pnle, &ChildAttribute, &ChildMask);
431 }
432 }
433 }
434 }
435 }
436
437 //
438 // RegionLength: 256T (1 << 48) 512G (1 << 39), 1G (1 << 30), 2M (1 << 21) or 4K (1 << 12).
439 // RegionStart: points to the linear address that's aligned on RegionLength and lower than (LinearAddress + Offset).
440 //
441 BitStart = 12 + (Level - 1) * 9;
442 Index = (UINTN)BitFieldRead64 (LinearAddress + Offset, BitStart, BitStart + 9 - 1);
443 RegionLength = LShiftU64 (1, BitStart);
444 RegionMask = RegionLength - 1;
445 RegionStart = (LinearAddress + Offset) & ~RegionMask;
446
447 ParentAttribute->Uint64 = PageTableLibGetPnleMapAttribute (&ParentPagingEntry->Pnle, ParentAttribute);
448
449 //
450 // Apply the attribute.
451 //
452 PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
453 while (Offset < Length && Index < 512) {
454 CurrentPagingEntry = (!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry[Index];
455 SubLength = MIN (Length - Offset, RegionStart + RegionLength - (LinearAddress + Offset));
456 if ((Level <= MaxLeafLevel) &&
457 (((LinearAddress + Offset) & RegionMask) == 0) &&
458 (((IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) & RegionMask) == 0) &&
459 (SubLength == RegionLength) &&
460 ((CurrentPagingEntry->Pce.Present == 0) || IsPle (CurrentPagingEntry, Level))
461 )
462 {
463 //
464 // Create one entry mapping the entire region (1G, 2M or 4K).
465 //
466 if (Modify) {
467 PageTableLibSetPle (Level, CurrentPagingEntry, Offset, Attribute, Mask);
468 }
469 } else {
470 //
471 // Recursively call to create page table.
472 // There are 3 cases:
473 // a. Level cannot be a leaf entry which points to physical memory.
474 // a. Level can be a leaf entry but (LinearAddress + Offset) is NOT aligned on the RegionStart.
475 // b. Level can be a leaf entry and (LinearAddress + Offset) is aligned on RegionStart,
476 // but the length is SMALLER than the RegionLength.
477 //
478 Status = PageTableLibMapInLevel (
479 CurrentPagingEntry,
480 ParentAttribute,
481 Modify,
482 Buffer,
483 BufferSize,
484 Level - 1,
485 MaxLeafLevel,
486 LinearAddress,
487 Length,
488 Offset,
489 Attribute,
490 Mask
491 );
492 if (RETURN_ERROR (Status)) {
493 return Status;
494 }
495 }
496
497 Offset += SubLength;
498 RegionStart += RegionLength;
499 Index++;
500 }
501
502 return RETURN_SUCCESS;
503 }
504
505 /**
506 Create or update page table to map [LinearAddress, LinearAddress + Length) with specified attribute.
507
508 @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.
509 @param[in] PagingMode The paging mode.
510 @param[in] Buffer The free buffer to be used for page table creation/updating.
511 @param[in, out] BufferSize The buffer size.
512 On return, the remaining buffer size.
513 The free buffer is used from the end so caller can supply the same Buffer pointer with an updated
514 BufferSize in the second call to this API.
515 @param[in] LinearAddress The start of the linear address range.
516 @param[in] Length The length of the linear address range.
517 @param[in] Attribute The attribute of the linear address range.
518 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
519 Page table entries that map the linear address range are reset to 0 before set to the new attribute
520 when a new physical base address is set.
521 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
522
523 @retval RETURN_UNSUPPORTED PagingMode is not supported.
524 @retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Mask is NULL.
525 @retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB.
526 @retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table creation/updating.
527 BufferSize is updated to indicate the expected buffer size.
528 Caller may still get RETURN_BUFFER_TOO_SMALL with the new BufferSize.
529 @retval RETURN_SUCCESS PageTable is created/updated successfully.
530 **/
531 RETURN_STATUS
532 EFIAPI
533 PageTableMap (
534 IN OUT UINTN *PageTable OPTIONAL,
535 IN PAGING_MODE PagingMode,
536 IN VOID *Buffer,
537 IN OUT UINTN *BufferSize,
538 IN UINT64 LinearAddress,
539 IN UINT64 Length,
540 IN IA32_MAP_ATTRIBUTE *Attribute,
541 IN IA32_MAP_ATTRIBUTE *Mask
542 )
543 {
544 RETURN_STATUS Status;
545 IA32_PAGING_ENTRY TopPagingEntry;
546 INTN RequiredSize;
547 UINT64 MaxLinearAddress;
548 UINTN MaxLevel;
549 UINTN MaxLeafLevel;
550 IA32_MAP_ATTRIBUTE ParentAttribute;
551
552 if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
553 //
554 // 32bit paging is never supported.
555 // PAE paging will be supported later.
556 //
557 return RETURN_UNSUPPORTED;
558 }
559
560 if ((PageTable == NULL) || (BufferSize == NULL) || (Attribute == NULL) || (Mask == NULL)) {
561 return RETURN_INVALID_PARAMETER;
562 }
563
564 if (*BufferSize % SIZE_4KB != 0) {
565 //
566 // BufferSize should be multiple of 4K.
567 //
568 return RETURN_INVALID_PARAMETER;
569 }
570
571 if ((LinearAddress % SIZE_4KB != 0) || (Length % SIZE_4KB != 0)) {
572 //
573 // LinearAddress and Length should be multiple of 4K.
574 //
575 return RETURN_INVALID_PARAMETER;
576 }
577
578 if ((*BufferSize != 0) && (Buffer == NULL)) {
579 return RETURN_INVALID_PARAMETER;
580 }
581
582 MaxLeafLevel = (UINT8)PagingMode;
583 MaxLevel = (UINT8)(PagingMode >> 8);
584 MaxLinearAddress = LShiftU64 (1, 12 + MaxLevel * 9);
585
586 if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - LinearAddress)) {
587 //
588 // Maximum linear address is (1 << 48) or (1 << 57)
589 //
590 return RETURN_INVALID_PARAMETER;
591 }
592
593 TopPagingEntry.Uintn = *PageTable;
594 if (TopPagingEntry.Uintn != 0) {
595 TopPagingEntry.Pce.Present = 1;
596 TopPagingEntry.Pce.ReadWrite = 1;
597 TopPagingEntry.Pce.UserSupervisor = 1;
598 TopPagingEntry.Pce.Nx = 0;
599 }
600
601 ParentAttribute.Uint64 = 0;
602 ParentAttribute.Bits.PageTableBaseAddress = 1;
603 ParentAttribute.Bits.Present = 1;
604 ParentAttribute.Bits.ReadWrite = 1;
605 ParentAttribute.Bits.UserSupervisor = 1;
606 ParentAttribute.Bits.Nx = 0;
607
608 //
609 // Query the required buffer size without modifying the page table.
610 //
611 RequiredSize = 0;
612 Status = PageTableLibMapInLevel (
613 &TopPagingEntry,
614 &ParentAttribute,
615 FALSE,
616 NULL,
617 &RequiredSize,
618 MaxLevel,
619 MaxLeafLevel,
620 LinearAddress,
621 Length,
622 0,
623 Attribute,
624 Mask
625 );
626 if (RETURN_ERROR (Status)) {
627 return Status;
628 }
629
630 RequiredSize = -RequiredSize;
631
632 if ((UINTN)RequiredSize > *BufferSize) {
633 *BufferSize = RequiredSize;
634 return RETURN_BUFFER_TOO_SMALL;
635 }
636
637 if ((RequiredSize != 0) && (Buffer == NULL)) {
638 return RETURN_INVALID_PARAMETER;
639 }
640
641 //
642 // Update the page table when the supplied buffer is sufficient.
643 //
644 ParentAttribute.Uint64 = 0;
645 ParentAttribute.Bits.PageTableBaseAddress = 1;
646 ParentAttribute.Bits.Present = 1;
647 ParentAttribute.Bits.ReadWrite = 1;
648 ParentAttribute.Bits.UserSupervisor = 1;
649 ParentAttribute.Bits.Nx = 0;
650
651 Status = PageTableLibMapInLevel (
652 &TopPagingEntry,
653 &ParentAttribute,
654 TRUE,
655 Buffer,
656 (INTN *)BufferSize,
657 MaxLevel,
658 MaxLeafLevel,
659 LinearAddress,
660 Length,
661 0,
662 Attribute,
663 Mask
664 );
665 if (!RETURN_ERROR (Status)) {
666 *PageTable = (UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40);
667 }
668
669 return Status;
670 }