VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c@ 105670

Last change on this file since 105670 was 105670, checked in by vboxsync, 4 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 33.5 KB
Line 
1/** @file
2 This library implements CpuPageTableLib that are generic for IA32 family CPU.
3
4 Copyright (c) 2022 - 2023, 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**/
21VOID
22PageTableLibSetPte4K (
23 IN OUT volatile IA32_PTE_4K *Pte4K,
24 IN UINT64 Offset,
25 IN IA32_MAP_ATTRIBUTE *Attribute,
26 IN IA32_MAP_ATTRIBUTE *Mask
27 )
28{
29 IA32_PTE_4K LocalPte4K;
30
31 LocalPte4K.Uint64 = Pte4K->Uint64;
32 if (Mask->Bits.PageTableBaseAddressLow || Mask->Bits.PageTableBaseAddressHigh) {
33 LocalPte4K.Uint64 = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) | (LocalPte4K.Uint64 & ~IA32_PE_BASE_ADDRESS_MASK_40);
34 }
35
36 if (Mask->Bits.Present) {
37 LocalPte4K.Bits.Present = Attribute->Bits.Present;
38 }
39
40 if (Mask->Bits.ReadWrite) {
41 LocalPte4K.Bits.ReadWrite = Attribute->Bits.ReadWrite;
42 }
43
44 if (Mask->Bits.UserSupervisor) {
45 LocalPte4K.Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
46 }
47
48 if (Mask->Bits.WriteThrough) {
49 LocalPte4K.Bits.WriteThrough = Attribute->Bits.WriteThrough;
50 }
51
52 if (Mask->Bits.CacheDisabled) {
53 LocalPte4K.Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
54 }
55
56 if (Mask->Bits.Accessed) {
57 LocalPte4K.Bits.Accessed = Attribute->Bits.Accessed;
58 }
59
60 if (Mask->Bits.Dirty) {
61 LocalPte4K.Bits.Dirty = Attribute->Bits.Dirty;
62 }
63
64 if (Mask->Bits.Pat) {
65 LocalPte4K.Bits.Pat = Attribute->Bits.Pat;
66 }
67
68 if (Mask->Bits.Global) {
69 LocalPte4K.Bits.Global = Attribute->Bits.Global;
70 }
71
72 if (Mask->Bits.ProtectionKey) {
73 LocalPte4K.Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
74 }
75
76 if (Mask->Bits.Nx) {
77 LocalPte4K.Bits.Nx = Attribute->Bits.Nx;
78 }
79
80 if (Pte4K->Uint64 != LocalPte4K.Uint64) {
81 Pte4K->Uint64 = LocalPte4K.Uint64;
82 }
83}
84
85/**
86 Set the IA32_PDPTE_1G or IA32_PDE_2M.
87
88 @param[in] PleB Pointer to PDPTE_1G or PDE_2M. Both share the same structure definition.
89 @param[in] Offset The offset within the linear address range.
90 @param[in] Attribute The attribute of the linear address range.
91 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
92 Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
93 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
94**/
95VOID
96PageTableLibSetPleB (
97 IN OUT volatile IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB,
98 IN UINT64 Offset,
99 IN IA32_MAP_ATTRIBUTE *Attribute,
100 IN IA32_MAP_ATTRIBUTE *Mask
101 )
102{
103 IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE LocalPleB;
104
105 LocalPleB.Uint64 = PleB->Uint64;
106 if (Mask->Bits.PageTableBaseAddressLow || Mask->Bits.PageTableBaseAddressHigh) {
107 LocalPleB.Uint64 = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) | (LocalPleB.Uint64 & ~IA32_PE_BASE_ADDRESS_MASK_39);
108 }
109
110 LocalPleB.Bits.MustBeOne = 1;
111
112 if (Mask->Bits.Present) {
113 LocalPleB.Bits.Present = Attribute->Bits.Present;
114 }
115
116 if (Mask->Bits.ReadWrite) {
117 LocalPleB.Bits.ReadWrite = Attribute->Bits.ReadWrite;
118 }
119
120 if (Mask->Bits.UserSupervisor) {
121 LocalPleB.Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
122 }
123
124 if (Mask->Bits.WriteThrough) {
125 LocalPleB.Bits.WriteThrough = Attribute->Bits.WriteThrough;
126 }
127
128 if (Mask->Bits.CacheDisabled) {
129 LocalPleB.Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
130 }
131
132 if (Mask->Bits.Accessed) {
133 LocalPleB.Bits.Accessed = Attribute->Bits.Accessed;
134 }
135
136 if (Mask->Bits.Dirty) {
137 LocalPleB.Bits.Dirty = Attribute->Bits.Dirty;
138 }
139
140 if (Mask->Bits.Pat) {
141 LocalPleB.Bits.Pat = Attribute->Bits.Pat;
142 }
143
144 if (Mask->Bits.Global) {
145 LocalPleB.Bits.Global = Attribute->Bits.Global;
146 }
147
148 if (Mask->Bits.ProtectionKey) {
149 LocalPleB.Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
150 }
151
152 if (Mask->Bits.Nx) {
153 LocalPleB.Bits.Nx = Attribute->Bits.Nx;
154 }
155
156 if (PleB->Uint64 != LocalPleB.Uint64) {
157 PleB->Uint64 = LocalPleB.Uint64;
158 }
159}
160
161/**
162 Set the IA32_PDPTE_1G, IA32_PDE_2M or IA32_PTE_4K.
163
164 @param[in] Level 3, 2 or 1.
165 @param[in] Ple Pointer to PDPTE_1G, PDE_2M or IA32_PTE_4K, depending on the Level.
166 @param[in] Offset The offset within the linear address range.
167 @param[in] Attribute The attribute of the linear address range.
168 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
169 Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
170 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
171**/
172VOID
173PageTableLibSetPle (
174 IN UINTN Level,
175 IN OUT volatile IA32_PAGING_ENTRY *Ple,
176 IN UINT64 Offset,
177 IN IA32_MAP_ATTRIBUTE *Attribute,
178 IN IA32_MAP_ATTRIBUTE *Mask
179 )
180{
181 if (Level == 1) {
182 PageTableLibSetPte4K (&Ple->Pte4K, Offset, Attribute, Mask);
183 } else {
184 ASSERT (Level == 2 || Level == 3);
185 PageTableLibSetPleB (&Ple->PleB, Offset, Attribute, Mask);
186 }
187}
188
189/**
190 Set the IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE.
191
192 @param[in] Pnle Pointer to IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE. All share the same structure definition.
193 @param[in] Attribute The attribute of the page directory referenced by the non-leaf.
194 @param[in] Mask The mask of the page directory referenced by the non-leaf.
195**/
196VOID
197PageTableLibSetPnle (
198 IN OUT volatile IA32_PAGE_NON_LEAF_ENTRY *Pnle,
199 IN IA32_MAP_ATTRIBUTE *Attribute,
200 IN IA32_MAP_ATTRIBUTE *Mask
201 )
202{
203 IA32_PAGE_NON_LEAF_ENTRY LocalPnle;
204
205 LocalPnle.Uint64 = Pnle->Uint64;
206 if (Mask->Bits.Present) {
207 LocalPnle.Bits.Present = Attribute->Bits.Present;
208 }
209
210 if (Mask->Bits.ReadWrite) {
211 LocalPnle.Bits.ReadWrite = Attribute->Bits.ReadWrite;
212 }
213
214 if (Mask->Bits.UserSupervisor) {
215 LocalPnle.Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
216 }
217
218 if (Mask->Bits.Nx) {
219 LocalPnle.Bits.Nx = Attribute->Bits.Nx;
220 }
221
222 LocalPnle.Bits.Accessed = 0;
223 LocalPnle.Bits.MustBeZero = 0;
224
225 //
226 // Set the attributes (WT, CD, A) to 0.
227 // WT and CD determin the memory type used to access the 4K page directory referenced by this entry.
228 // So, it implictly requires PAT[0] is Write Back.
229 // Create a new parameter if caller requires to use a different memory type for accessing page directories.
230 //
231 LocalPnle.Bits.WriteThrough = 0;
232 LocalPnle.Bits.CacheDisabled = 0;
233 if (Pnle->Uint64 != LocalPnle.Uint64) {
234 Pnle->Uint64 = LocalPnle.Uint64;
235 }
236}
237
238/**
239 Check if the combination for Attribute and Mask is valid for non-present entry.
240 1.Mask.Present is 0 but some other attributes is provided. This case should be invalid.
241 2.Map non-present range to present. In this case, all attributes should be provided.
242
243 @param[in] Attribute The attribute of the linear address range.
244 @param[in] Mask The mask used for attribute to check.
245
246 @retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 0 but some other attributes are provided.
247 @retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 1 but some other attributes are not provided.
248 @retval RETURN_SUCCESS The combination for Attribute and Mask is valid.
249**/
250RETURN_STATUS
251IsAttributesAndMaskValidForNonPresentEntry (
252 IN IA32_MAP_ATTRIBUTE *Attribute,
253 IN IA32_MAP_ATTRIBUTE *Mask
254 )
255{
256 if ((Mask->Bits.Present == 1) && (Attribute->Bits.Present == 1)) {
257 //
258 // Creating new page table or remapping non-present range to present.
259 //
260 if ((Mask->Bits.ReadWrite == 0) || (Mask->Bits.UserSupervisor == 0) || (Mask->Bits.WriteThrough == 0) || (Mask->Bits.CacheDisabled == 0) ||
261 (Mask->Bits.Accessed == 0) || (Mask->Bits.Dirty == 0) || (Mask->Bits.Pat == 0) || (Mask->Bits.Global == 0) ||
262 ((Mask->Bits.PageTableBaseAddressLow == 0) && (Mask->Bits.PageTableBaseAddressHigh == 0)) || (Mask->Bits.ProtectionKey == 0) || (Mask->Bits.Nx == 0))
263 {
264 return RETURN_INVALID_PARAMETER;
265 }
266 } else if ((Mask->Bits.Present == 0) && (Mask->Uint64 > 1)) {
267 //
268 // Only change other attributes for non-present range is not permitted.
269 //
270 return RETURN_INVALID_PARAMETER;
271 }
272
273 return RETURN_SUCCESS;
274}
275
276/**
277 Update page table to map [LinearAddress, LinearAddress + Length) with specified attribute in the specified level.
278
279 @param[in] ParentPagingEntry The pointer to the page table entry to update.
280 @param[in] ParentAttribute The accumulated attribute of all parents' attribute.
281 @param[in] Modify FALSE to indicate Buffer is not used and BufferSize is increased by the required buffer size.
282 @param[in] Buffer The free buffer to be used for page table creation/updating.
283 When Modify is TRUE, it's used from the end.
284 When Modify is FALSE, it's ignored.
285 @param[in, out] BufferSize The available buffer size.
286 Return the remaining buffer size.
287 @param[in] Level Page table level. Could be 5, 4, 3, 2, or 1.
288 @param[in] MaxLeafLevel Maximum level that can be a leaf entry. Could be 1, 2 or 3 (if Page 1G is supported).
289 @param[in] LinearAddress The start of the linear address range.
290 @param[in] Length The length of the linear address range.
291 @param[in] Offset The offset within the linear address range.
292 @param[in] Attribute The attribute of the linear address range.
293 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
294 Page table entries that map the linear address range are reset to 0 before set to the new attribute
295 when a new physical base address is set.
296 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
297 @param[in, out] IsModified Change IsModified to True if page table is modified and input parameter Modify is TRUE.
298
299 @retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 0 but some other attributes are provided.
300 @retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 1 but some other attributes are not provided.
301 @retval RETURN_SUCCESS PageTable is created/updated successfully.
302**/
303RETURN_STATUS
304PageTableLibMapInLevel (
305 IN IA32_PAGING_ENTRY *ParentPagingEntry,
306 IN IA32_MAP_ATTRIBUTE *ParentAttribute,
307 IN BOOLEAN Modify,
308 IN VOID *Buffer,
309 IN OUT INTN *BufferSize,
310 IN IA32_PAGE_LEVEL Level,
311 IN IA32_PAGE_LEVEL MaxLeafLevel,
312 IN UINT64 LinearAddress,
313 IN UINT64 Length,
314 IN UINT64 Offset,
315 IN IA32_MAP_ATTRIBUTE *Attribute,
316 IN IA32_MAP_ATTRIBUTE *Mask,
317 IN OUT BOOLEAN *IsModified
318 )
319{
320 RETURN_STATUS Status;
321 UINTN BitStart;
322 UINTN Index;
323 IA32_PAGING_ENTRY *PagingEntry;
324 UINTN PagingEntryIndex;
325 UINTN PagingEntryIndexEnd;
326 IA32_PAGING_ENTRY *CurrentPagingEntry;
327 UINT64 RegionLength;
328 UINT64 SubLength;
329 UINT64 SubOffset;
330 UINT64 RegionMask;
331 UINT64 RegionStart;
332 IA32_MAP_ATTRIBUTE AllOneMask;
333 IA32_MAP_ATTRIBUTE PleBAttribute;
334 IA32_MAP_ATTRIBUTE NopAttribute;
335 BOOLEAN CreateNew;
336 IA32_PAGING_ENTRY OneOfPagingEntry;
337 IA32_MAP_ATTRIBUTE ChildAttribute;
338 IA32_MAP_ATTRIBUTE ChildMask;
339 IA32_MAP_ATTRIBUTE CurrentMask;
340 IA32_MAP_ATTRIBUTE LocalParentAttribute;
341 UINT64 PhysicalAddrInEntry;
342 UINT64 PhysicalAddrInAttr;
343 IA32_PAGING_ENTRY OriginalParentPagingEntry;
344 IA32_PAGING_ENTRY OriginalCurrentPagingEntry;
345
346 ASSERT (Level != 0);
347 ASSERT ((Attribute != NULL) && (Mask != NULL));
348
349 CreateNew = FALSE;
350 AllOneMask.Uint64 = ~0ull;
351
352 NopAttribute.Uint64 = 0;
353 NopAttribute.Bits.Present = 1;
354 NopAttribute.Bits.ReadWrite = 1;
355 NopAttribute.Bits.UserSupervisor = 1;
356
357 LocalParentAttribute.Uint64 = ParentAttribute->Uint64;
358 ParentAttribute = &LocalParentAttribute;
359
360 OriginalParentPagingEntry.Uint64 = ParentPagingEntry->Uint64;
361 OneOfPagingEntry.Uint64 = 0;
362 //
363 // RegionLength: 256T (1 << 48) 512G (1 << 39), 1G (1 << 30), 2M (1 << 21) or 4K (1 << 12).
364 //
365 BitStart = 12 + (Level - 1) * 9;
366 PagingEntryIndex = (UINTN)BitFieldRead64 (LinearAddress + Offset, BitStart, BitStart + 9 - 1);
367 RegionLength = REGION_LENGTH (Level);
368 RegionMask = RegionLength - 1;
369
370 //
371 // ParentPagingEntry ONLY is deferenced for checking Present and MustBeOne bits
372 // when Modify is FALSE.
373 //
374 if ((ParentPagingEntry->Pce.Present == 0) || IsPle (ParentPagingEntry, Level + 1)) {
375 //
376 // When ParentPagingEntry is non-present, parent entry is CR3 or PML5E/PML4E/PDPTE/PDE.
377 // It does NOT point to an existing page directory.
378 // When ParentPagingEntry is present, parent entry is leaf PDPTE_1G or PDE_2M. Split to 2M or 4K pages.
379 // Note: it's impossible the parent entry is a PTE_4K.
380 //
381 PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, ParentAttribute);
382 if (ParentPagingEntry->Pce.Present == 0) {
383 //
384 // [LinearAddress, LinearAddress + Length] contains non-present range.
385 //
386 Status = IsAttributesAndMaskValidForNonPresentEntry (Attribute, Mask);
387 if (RETURN_ERROR (Status)) {
388 return Status;
389 }
390 } else {
391 PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask);
392 }
393
394 //
395 // Check if the attribute, the physical address calculated by ParentPagingEntry is equal to
396 // the attribute, the physical address calculated by input Attribue and Mask.
397 //
398 if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask))
399 == (IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask)))
400 {
401 if ((Mask->Bits.PageTableBaseAddressLow == 0) && (Mask->Bits.PageTableBaseAddressHigh == 0)) {
402 return RETURN_SUCCESS;
403 }
404
405 //
406 // Non-present entry won't reach there since:
407 // 1.When map non-present entry to present, the attribute must be different.
408 // 2.When still map non-present entry to non-present, PageTableBaseAddressLow and High in Mask must be 0.
409 //
410 ASSERT (ParentPagingEntry->Pce.Present == 1);
411 PhysicalAddrInEntry = IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&PleBAttribute) + MultU64x32 (RegionLength, (UINT32)PagingEntryIndex);
412 PhysicalAddrInAttr = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) & (~RegionMask);
413 if (PhysicalAddrInEntry == PhysicalAddrInAttr) {
414 return RETURN_SUCCESS;
415 }
416 }
417
418 ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
419 CreateNew = TRUE;
420 *BufferSize -= SIZE_4KB;
421
422 if (Modify) {
423 PagingEntry = (IA32_PAGING_ENTRY *)((UINTN)Buffer + *BufferSize);
424 ZeroMem (PagingEntry, SIZE_4KB);
425
426 if (ParentPagingEntry->Pce.Present) {
427 //
428 // Create 512 child-level entries that map to 2M/4K.
429 //
430 for (SubOffset = 0, Index = 0; Index < 512; Index++) {
431 PagingEntry[Index].Uint64 = OneOfPagingEntry.Uint64 + SubOffset;
432 SubOffset += RegionLength;
433 }
434 }
435
436 //
437 // Set NOP attributes
438 // Note: Should NOT inherit the attributes from the original entry because a zero RW bit
439 // will make the entire region read-only even the child entries set the RW bit.
440 //
441 // Non-leaf entry doesn't have PAT bit. So use ~IA32_PE_BASE_ADDRESS_MASK_40 is to make sure PAT bit
442 // (bit12) in original big-leaf entry is not assigned to PageTableBaseAddress field of non-leaf entry.
443 //
444 PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
445 ParentPagingEntry->Uint64 = ((UINTN)(VOID *)PagingEntry) | (ParentPagingEntry->Uint64 & (~IA32_PE_BASE_ADDRESS_MASK_40));
446 }
447 } else {
448 //
449 // If (LinearAddress + Length - 1) is not in the same ParentPagingEntry with (LinearAddress + Offset), then the remaining child PagingEntry
450 // starting from PagingEntryIndex of ParentPagingEntry is all covered by [LinearAddress + Offset, LinearAddress + Length - 1].
451 //
452 PagingEntryIndexEnd = (BitFieldRead64 (LinearAddress + Length - 1, BitStart + 9, 63) != BitFieldRead64 (LinearAddress + Offset, BitStart + 9, 63)) ? 511 :
453 (UINTN)BitFieldRead64 (LinearAddress + Length - 1, BitStart, BitStart + 9 - 1);
454 PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
455 for (Index = PagingEntryIndex; Index <= PagingEntryIndexEnd; Index++) {
456 if (PagingEntry[Index].Pce.Present == 0) {
457 //
458 // [LinearAddress, LinearAddress + Length] contains non-present range.
459 //
460 Status = IsAttributesAndMaskValidForNonPresentEntry (Attribute, Mask);
461 if (RETURN_ERROR (Status)) {
462 return Status;
463 }
464
465 break;
466 }
467 }
468
469 //
470 // It's a non-leaf entry
471 //
472 ChildAttribute.Uint64 = 0;
473 ChildMask.Uint64 = 0;
474
475 //
476 // If the inheritable attributes in the parent entry conflicts with the requested attributes,
477 // let the child entries take the parent attributes and
478 // loosen the attribute in the parent entry
479 // E.g.: when PDPTE[0].ReadWrite = 0 but caller wants to map [0-2MB] as ReadWrite = 1 (PDE[0].ReadWrite = 1)
480 // we need to change PDPTE[0].ReadWrite = 1 and let all PDE[0-255].ReadWrite = 0 in this step.
481 // when PDPTE[0].Nx = 1 but caller wants to map [0-2MB] as Nx = 0 (PDT[0].Nx = 0)
482 // we need to change PDPTE[0].Nx = 0 and let all PDE[0-255].Nx = 1 in this step.
483 if ((ParentPagingEntry->Pnle.Bits.ReadWrite == 0) && (Mask->Bits.ReadWrite == 1) && (Attribute->Bits.ReadWrite == 1)) {
484 if (Modify) {
485 ParentPagingEntry->Pnle.Bits.ReadWrite = 1;
486 }
487
488 ChildAttribute.Bits.ReadWrite = 0;
489 ChildMask.Bits.ReadWrite = 1;
490 }
491
492 if ((ParentPagingEntry->Pnle.Bits.UserSupervisor == 0) && (Mask->Bits.UserSupervisor == 1) && (Attribute->Bits.UserSupervisor == 1)) {
493 if (Modify) {
494 ParentPagingEntry->Pnle.Bits.UserSupervisor = 1;
495 }
496
497 ChildAttribute.Bits.UserSupervisor = 0;
498 ChildMask.Bits.UserSupervisor = 1;
499 }
500
501 if ((ParentPagingEntry->Pnle.Bits.Nx == 1) && (Mask->Bits.Nx == 1) && (Attribute->Bits.Nx == 0)) {
502 if (Modify) {
503 ParentPagingEntry->Pnle.Bits.Nx = 0;
504 }
505
506 ChildAttribute.Bits.Nx = 1;
507 ChildMask.Bits.Nx = 1;
508 }
509
510 if (ChildMask.Uint64 != 0) {
511 if (Modify) {
512 //
513 // Update child entries to use restrictive attribute inherited from parent.
514 // e.g.: Set PDE[0-255].ReadWrite = 0
515 //
516 for (Index = 0; Index < 512; Index++) {
517 if (PagingEntry[Index].Pce.Present == 0) {
518 continue;
519 }
520
521 if (IsPle (&PagingEntry[Index], Level)) {
522 PageTableLibSetPle (Level, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask);
523 } else {
524 PageTableLibSetPnle (&PagingEntry[Index].Pnle, &ChildAttribute, &ChildMask);
525 }
526 }
527 }
528 }
529 }
530
531 //
532 // RegionStart: points to the linear address that's aligned on RegionLength and lower than (LinearAddress + Offset).
533 //
534 Index = PagingEntryIndex;
535 RegionStart = (LinearAddress + Offset) & ~RegionMask;
536 ParentAttribute->Uint64 = PageTableLibGetPnleMapAttribute (&ParentPagingEntry->Pnle, ParentAttribute);
537
538 //
539 // Apply the attribute.
540 //
541 PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
542 while (Offset < Length && Index < 512) {
543 CurrentPagingEntry = (!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry[Index];
544 SubLength = MIN (Length - Offset, RegionStart + RegionLength - (LinearAddress + Offset));
545 if ((Level <= MaxLeafLevel) &&
546 (((LinearAddress + Offset) & RegionMask) == 0) &&
547 (((IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) & RegionMask) == 0) &&
548 (SubLength == RegionLength) &&
549 ((CurrentPagingEntry->Pce.Present == 0) || IsPle (CurrentPagingEntry, Level))
550 )
551 {
552 //
553 // Create one entry mapping the entire region (1G, 2M or 4K).
554 //
555 if (Modify) {
556 //
557 // When the inheritable attributes in parent entry could override the child attributes,
558 // e.g.: Present/ReadWrite/UserSupervisor is 0 in parent entry, or
559 // Nx is 1 in parent entry,
560 // we just skip setting any value to these attributes in child.
561 // We add assertion to make sure the requested settings don't conflict with parent attributes in this case.
562 //
563 CurrentMask.Uint64 = Mask->Uint64;
564 if (ParentAttribute->Bits.Present == 0) {
565 CurrentMask.Bits.Present = 0;
566 ASSERT (CreateNew || (Mask->Bits.Present == 0) || (Attribute->Bits.Present == 0));
567 }
568
569 if (ParentAttribute->Bits.ReadWrite == 0) {
570 CurrentMask.Bits.ReadWrite = 0;
571 ASSERT (CreateNew || (Mask->Bits.ReadWrite == 0) || (Attribute->Bits.ReadWrite == 0));
572 }
573
574 if (ParentAttribute->Bits.UserSupervisor == 0) {
575 CurrentMask.Bits.UserSupervisor = 0;
576 ASSERT (CreateNew || (Mask->Bits.UserSupervisor == 0) || (Attribute->Bits.UserSupervisor == 0));
577 }
578
579 if (ParentAttribute->Bits.Nx == 1) {
580 CurrentMask.Bits.Nx = 0;
581 ASSERT (CreateNew || (Mask->Bits.Nx == 0) || (Attribute->Bits.Nx == 1));
582 }
583
584 //
585 // Check if any leaf PagingEntry is modified.
586 //
587 OriginalCurrentPagingEntry.Uint64 = CurrentPagingEntry->Uint64;
588 PageTableLibSetPle (Level, CurrentPagingEntry, Offset, Attribute, &CurrentMask);
589
590 if (Modify && (OriginalCurrentPagingEntry.Uint64 != CurrentPagingEntry->Uint64)) {
591 //
592 // The page table entry can be changed by this function only when Modify is true.
593 //
594 *IsModified = TRUE;
595 }
596 }
597 } else {
598 //
599 // Recursively call to create page table.
600 // There are 3 cases:
601 // a. Level cannot be a leaf entry which points to physical memory.
602 // a. Level can be a leaf entry but (LinearAddress + Offset) is NOT aligned on the RegionStart.
603 // b. Level can be a leaf entry and (LinearAddress + Offset) is aligned on RegionStart,
604 // but the length is SMALLER than the RegionLength.
605 //
606 Status = PageTableLibMapInLevel (
607 CurrentPagingEntry,
608 ParentAttribute,
609 Modify,
610 Buffer,
611 BufferSize,
612 Level - 1,
613 MaxLeafLevel,
614 LinearAddress,
615 Length,
616 Offset,
617 Attribute,
618 Mask,
619 IsModified
620 );
621 if (RETURN_ERROR (Status)) {
622 return Status;
623 }
624 }
625
626 Offset += SubLength;
627 RegionStart += RegionLength;
628 Index++;
629 }
630
631 //
632 // Check if ParentPagingEntry entry is modified here is enough. Except the changes happen in leaf PagingEntry during
633 // the while loop, if there is any other change happens in page table, the ParentPagingEntry must has been modified.
634 //
635 if (Modify && (OriginalParentPagingEntry.Uint64 != ParentPagingEntry->Uint64)) {
636 //
637 // The page table entry can be changed by this function only when Modify is true.
638 //
639 *IsModified = TRUE;
640 }
641
642 return RETURN_SUCCESS;
643}
644
645/**
646 Create or update page table to map [LinearAddress, LinearAddress + Length) with specified attribute.
647
648 @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.
649 If not pointer to NULL, the value it points to won't be changed in this function.
650 @param[in] PagingMode The paging mode.
651 @param[in] Buffer The free buffer to be used for page table creation/updating.
652 @param[in, out] BufferSize The buffer size.
653 On return, the remaining buffer size.
654 The free buffer is used from the end so caller can supply the same Buffer pointer with an updated
655 BufferSize in the second call to this API.
656 @param[in] LinearAddress The start of the linear address range.
657 @param[in] Length The length of the linear address range.
658 @param[in] Attribute The attribute of the linear address range.
659 All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
660 Page table entries that map the linear address range are reset to 0 before set to the new attribute
661 when a new physical base address is set.
662 @param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
663 @param[out] IsModified TRUE means page table is modified by software or hardware. FALSE means page table is not modified by software.
664 If the output IsModified is FALSE, there is possibility that the page table is changed by hardware. It is ok
665 because page table can be changed by hardware anytime, and caller don't need to Flush TLB.
666
667 @retval RETURN_UNSUPPORTED PagingMode is not supported.
668 @retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Mask is NULL.
669 @retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 0 but some other attributes are provided.
670 @retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 1 but some other attributes are not provided.
671 @retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 0 but some other attributes are provided.
672 @retval RETURN_INVALID_PARAMETER For present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 0 but some other attributes are provided.
673 @retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB.
674 @retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table creation/updating.
675 BufferSize is updated to indicate the expected buffer size.
676 Caller may still get RETURN_BUFFER_TOO_SMALL with the new BufferSize.
677 @retval RETURN_SUCCESS PageTable is created/updated successfully or the input Length is 0.
678**/
679RETURN_STATUS
680EFIAPI
681PageTableMap (
682 IN OUT UINTN *PageTable OPTIONAL,
683 IN PAGING_MODE PagingMode,
684 IN VOID *Buffer,
685 IN OUT UINTN *BufferSize,
686 IN UINT64 LinearAddress,
687 IN UINT64 Length,
688 IN IA32_MAP_ATTRIBUTE *Attribute,
689 IN IA32_MAP_ATTRIBUTE *Mask,
690 OUT BOOLEAN *IsModified OPTIONAL
691 )
692{
693 RETURN_STATUS Status;
694 IA32_PAGING_ENTRY TopPagingEntry;
695 INTN RequiredSize;
696 UINT64 MaxLinearAddress;
697 IA32_PAGE_LEVEL MaxLevel;
698 IA32_PAGE_LEVEL MaxLeafLevel;
699 IA32_MAP_ATTRIBUTE ParentAttribute;
700 BOOLEAN LocalIsModified;
701 UINTN Index;
702 IA32_PAGING_ENTRY *PagingEntry;
703 UINT8 BufferInStack[SIZE_4KB - 1 + MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY)];
704
705 if (Length == 0) {
706 return RETURN_SUCCESS;
707 }
708
709 if ((PagingMode == Paging32bit) || (PagingMode >= PagingModeMax)) {
710 //
711 // 32bit paging is never supported.
712 //
713 return RETURN_UNSUPPORTED;
714 }
715
716 if ((PageTable == NULL) || (BufferSize == NULL) || (Attribute == NULL) || (Mask == NULL)) {
717 return RETURN_INVALID_PARAMETER;
718 }
719
720 if (*BufferSize % SIZE_4KB != 0) {
721 //
722 // BufferSize should be multiple of 4K.
723 //
724 return RETURN_INVALID_PARAMETER;
725 }
726
727 if (((UINTN)LinearAddress % SIZE_4KB != 0) || ((UINTN)Length % SIZE_4KB != 0)) {
728 //
729 // LinearAddress and Length should be multiple of 4K.
730 //
731 return RETURN_INVALID_PARAMETER;
732 }
733
734 if ((*BufferSize != 0) && (Buffer == NULL)) {
735 return RETURN_INVALID_PARAMETER;
736 }
737
738 //
739 // If to map [LinearAddress, LinearAddress + Length] as non-present,
740 // all attributes except Present should not be provided.
741 //
742 if ((Attribute->Bits.Present == 0) && (Mask->Bits.Present == 1) && (Mask->Uint64 > 1)) {
743 return RETURN_INVALID_PARAMETER;
744 }
745
746 MaxLeafLevel = (IA32_PAGE_LEVEL)(UINT8)PagingMode;
747 MaxLevel = (IA32_PAGE_LEVEL)(UINT8)(PagingMode >> 8);
748 MaxLinearAddress = (PagingMode == PagingPae) ? LShiftU64 (1, 32) : LShiftU64 (1, 12 + MaxLevel * 9);
749
750 if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - LinearAddress)) {
751 //
752 // Maximum linear address is (1 << 32), (1 << 48) or (1 << 57)
753 //
754 return RETURN_INVALID_PARAMETER;
755 }
756
757 TopPagingEntry.Uintn = *PageTable;
758 if (TopPagingEntry.Uintn != 0) {
759 if (PagingMode == PagingPae) {
760 //
761 // Create 4 temporary PDPTE at a 4k-aligned address.
762 // Copy the original PDPTE content and set ReadWrite, UserSupervisor to 1, set Nx to 0.
763 //
764 TopPagingEntry.Uintn = ALIGN_VALUE ((UINTN)BufferInStack, BASE_4KB);
765 PagingEntry = (IA32_PAGING_ENTRY *)(TopPagingEntry.Uintn);
766 CopyMem (PagingEntry, (VOID *)(*PageTable), MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY));
767 for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) {
768 PagingEntry[Index].Pnle.Bits.ReadWrite = 1;
769 PagingEntry[Index].Pnle.Bits.UserSupervisor = 1;
770 PagingEntry[Index].Pnle.Bits.Nx = 0;
771 }
772 }
773
774 TopPagingEntry.Pce.Present = 1;
775 TopPagingEntry.Pce.ReadWrite = 1;
776 TopPagingEntry.Pce.UserSupervisor = 1;
777 TopPagingEntry.Pce.Nx = 0;
778 }
779
780 if (IsModified == NULL) {
781 IsModified = &LocalIsModified;
782 }
783
784 *IsModified = FALSE;
785
786 ParentAttribute.Uint64 = 0;
787 ParentAttribute.Bits.PageTableBaseAddressLow = 1;
788 ParentAttribute.Bits.Present = 1;
789 ParentAttribute.Bits.ReadWrite = 1;
790 ParentAttribute.Bits.UserSupervisor = 1;
791 ParentAttribute.Bits.Nx = 0;
792
793 //
794 // Query the required buffer size without modifying the page table.
795 //
796 RequiredSize = 0;
797 Status = PageTableLibMapInLevel (
798 &TopPagingEntry,
799 &ParentAttribute,
800 FALSE,
801 NULL,
802 &RequiredSize,
803 MaxLevel,
804 MaxLeafLevel,
805 LinearAddress,
806 Length,
807 0,
808 Attribute,
809 Mask,
810 IsModified
811 );
812 ASSERT (*IsModified == FALSE);
813 if (RETURN_ERROR (Status)) {
814 return Status;
815 }
816
817 RequiredSize = -RequiredSize;
818
819 if ((UINTN)RequiredSize > *BufferSize) {
820 *BufferSize = RequiredSize;
821 return RETURN_BUFFER_TOO_SMALL;
822 }
823
824 if ((RequiredSize != 0) && (Buffer == NULL)) {
825 return RETURN_INVALID_PARAMETER;
826 }
827
828 //
829 // Update the page table when the supplied buffer is sufficient.
830 //
831 Status = PageTableLibMapInLevel (
832 &TopPagingEntry,
833 &ParentAttribute,
834 TRUE,
835 Buffer,
836 (INTN *)BufferSize,
837 MaxLevel,
838 MaxLeafLevel,
839 LinearAddress,
840 Length,
841 0,
842 Attribute,
843 Mask,
844 IsModified
845 );
846
847 if (!RETURN_ERROR (Status)) {
848 PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40);
849
850 if (PagingMode == PagingPae) {
851 //
852 // These MustBeZero fields are treated as RW and other attributes by the common map logic. So they might be set to 1.
853 //
854 for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) {
855 PagingEntry[Index].PdptePae.Bits.MustBeZero = 0;
856 PagingEntry[Index].PdptePae.Bits.MustBeZero2 = 0;
857 PagingEntry[Index].PdptePae.Bits.MustBeZero3 = 0;
858 }
859
860 if (*PageTable != 0) {
861 //
862 // Copy temp PDPTE to original PDPTE.
863 //
864 CopyMem ((VOID *)(*PageTable), PagingEntry, MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY));
865 }
866 }
867
868 if (*PageTable == 0) {
869 //
870 // Do not assign the *PageTable when it's an existing page table.
871 // If it's an existing PAE page table, PagingEntry is the temp buffer in stack.
872 //
873 *PageTable = (UINTN)PagingEntry;
874 }
875 }
876
877 return Status;
878}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette