]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/CpuDxe/CpuPageTable.c
UefiCpuPkg: Remove redundant library classes, Ppis and GUIDs
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuPageTable.c
1 /** @file
2 Page table management support.
3
4 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include <Base.h>
18 #include <Uefi.h>
19 #include <Library/PeCoffGetEntryPointLib.h>
20 #include <Library/SerialPortLib.h>
21 #include <Library/SynchronizationLib.h>
22 #include <Library/PrintLib.h>
23 #include <Protocol/SmmBase2.h>
24 #include <Register/Cpuid.h>
25 #include <Register/Msr.h>
26
27 #include "CpuDxe.h"
28 #include "CpuPageTable.h"
29
30 ///
31 /// Paging registers
32 ///
33 #define CR0_WP BIT16
34 #define CR0_PG BIT31
35 #define CR4_PSE BIT4
36 #define CR4_PAE BIT5
37
38 ///
39 /// Page Table Entry
40 ///
41 #define IA32_PG_P BIT0
42 #define IA32_PG_RW BIT1
43 #define IA32_PG_U BIT2
44 #define IA32_PG_WT BIT3
45 #define IA32_PG_CD BIT4
46 #define IA32_PG_A BIT5
47 #define IA32_PG_D BIT6
48 #define IA32_PG_PS BIT7
49 #define IA32_PG_PAT_2M BIT12
50 #define IA32_PG_PAT_4K IA32_PG_PS
51 #define IA32_PG_PMNT BIT62
52 #define IA32_PG_NX BIT63
53
54 #define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P)
55 //
56 // Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE
57 // X64 PAE PDPTE does not have such restriction
58 //
59 #define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P)
60
61 #define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS)
62
63 #define PAGING_4K_MASK 0xFFF
64 #define PAGING_2M_MASK 0x1FFFFF
65 #define PAGING_1G_MASK 0x3FFFFFFF
66
67 #define PAGING_PAE_INDEX_MASK 0x1FF
68
69 #define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
70 #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
71 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
72
73 #define MAX_PF_ENTRY_COUNT 10
74 #define MAX_DEBUG_MESSAGE_LENGTH 0x100
75 #define IA32_PF_EC_ID BIT4
76
77 typedef enum {
78 PageNone,
79 Page4K,
80 Page2M,
81 Page1G,
82 } PAGE_ATTRIBUTE;
83
84 typedef struct {
85 PAGE_ATTRIBUTE Attribute;
86 UINT64 Length;
87 UINT64 AddressMask;
88 } PAGE_ATTRIBUTE_TABLE;
89
90 typedef enum {
91 PageActionAssign,
92 PageActionSet,
93 PageActionClear,
94 } PAGE_ACTION;
95
96 PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
97 {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},
98 {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},
99 {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
100 };
101
102 PAGE_TABLE_POOL *mPageTablePool = NULL;
103 PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;
104 EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL;
105
106 //
107 // Record the page fault exception count for one instruction execution.
108 //
109 UINTN *mPFEntryCount;
110 UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT];
111
112 /**
113 Check if current execution environment is in SMM mode or not, via
114 EFI_SMM_BASE2_PROTOCOL.
115
116 This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib
117 supports to free memory outside SMRAM. The library will call gBS->FreePool() or
118 gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change
119 memory paging attributes during free operation, if some memory related features
120 are enabled (like Heap Guard).
121
122 This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This
123 will cause incorrect result because SMM mode always loads its own page tables,
124 which are usually different from DXE. This function can be used to detect such
125 situation and help to avoid further misoperations.
126
127 @retval TRUE In SMM mode.
128 @retval FALSE Not in SMM mode.
129 **/
130 BOOLEAN
131 IsInSmm (
132 VOID
133 )
134 {
135 BOOLEAN InSmm;
136
137 InSmm = FALSE;
138 if (mSmmBase2 == NULL) {
139 gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2);
140 }
141
142 if (mSmmBase2 != NULL) {
143 mSmmBase2->InSmm (mSmmBase2, &InSmm);
144 }
145
146 //
147 // mSmmBase2->InSmm() can only detect if the caller is running in SMRAM
148 // or from SMM driver. It cannot tell if the caller is running in SMM mode.
149 // Check page table base address to guarantee that because SMM mode willl
150 // load its own page table.
151 //
152 return (InSmm &&
153 mPagingContext.ContextData.X64.PageTableBase != (UINT64)AsmReadCr3());
154 }
155
156 /**
157 Return current paging context.
158
159 @param[in,out] PagingContext The paging context.
160 **/
161 VOID
162 GetCurrentPagingContext (
163 IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext
164 )
165 {
166 UINT32 RegEax;
167 CPUID_EXTENDED_CPU_SIG_EDX RegEdx;
168 MSR_IA32_EFER_REGISTER MsrEfer;
169
170 //
171 // Don't retrieve current paging context from processor if in SMM mode.
172 //
173 if (!IsInSmm ()) {
174 ZeroMem (&mPagingContext, sizeof(mPagingContext));
175 if (sizeof(UINTN) == sizeof(UINT64)) {
176 mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;
177 } else {
178 mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;
179 }
180 if ((AsmReadCr0 () & CR0_PG) != 0) {
181 mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
182 } else {
183 mPagingContext.ContextData.X64.PageTableBase = 0;
184 }
185
186 if ((AsmReadCr4 () & CR4_PSE) != 0) {
187 mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
188 }
189 if ((AsmReadCr4 () & CR4_PAE) != 0) {
190 mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
191 }
192 if ((AsmReadCr0 () & CR0_WP) != 0) {
193 mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
194 }
195
196 AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
197 if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
198 AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx.Uint32);
199
200 if (RegEdx.Bits.NX != 0) {
201 // XD supported
202 MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER);
203 if (MsrEfer.Bits.NXE != 0) {
204 // XD activated
205 mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
206 }
207 }
208
209 if (RegEdx.Bits.Page1GB != 0) {
210 mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;
211 }
212 }
213 }
214
215 //
216 // This can avoid getting SMM paging context if in SMM mode. We cannot assume
217 // SMM mode shares the same paging context as DXE.
218 //
219 CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext));
220 }
221
222 /**
223 Return length according to page attributes.
224
225 @param[in] PageAttributes The page attribute of the page entry.
226
227 @return The length of page entry.
228 **/
229 UINTN
230 PageAttributeToLength (
231 IN PAGE_ATTRIBUTE PageAttribute
232 )
233 {
234 UINTN Index;
235 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
236 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
237 return (UINTN)mPageAttributeTable[Index].Length;
238 }
239 }
240 return 0;
241 }
242
243 /**
244 Return address mask according to page attributes.
245
246 @param[in] PageAttributes The page attribute of the page entry.
247
248 @return The address mask of page entry.
249 **/
250 UINTN
251 PageAttributeToMask (
252 IN PAGE_ATTRIBUTE PageAttribute
253 )
254 {
255 UINTN Index;
256 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
257 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
258 return (UINTN)mPageAttributeTable[Index].AddressMask;
259 }
260 }
261 return 0;
262 }
263
264 /**
265 Return page table entry to match the address.
266
267 @param[in] PagingContext The paging context.
268 @param[in] Address The address to be checked.
269 @param[out] PageAttributes The page attribute of the page entry.
270
271 @return The page entry.
272 **/
273 VOID *
274 GetPageTableEntry (
275 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,
276 IN PHYSICAL_ADDRESS Address,
277 OUT PAGE_ATTRIBUTE *PageAttribute
278 )
279 {
280 UINTN Index1;
281 UINTN Index2;
282 UINTN Index3;
283 UINTN Index4;
284 UINT64 *L1PageTable;
285 UINT64 *L2PageTable;
286 UINT64 *L3PageTable;
287 UINT64 *L4PageTable;
288 UINT64 AddressEncMask;
289
290 ASSERT (PagingContext != NULL);
291
292 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
293 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
294 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
295 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
296
297 // Make sure AddressEncMask is contained to smallest supported address field.
298 //
299 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
300
301 if (PagingContext->MachineType == IMAGE_FILE_MACHINE_X64) {
302 L4PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.X64.PageTableBase;
303 if (L4PageTable[Index4] == 0) {
304 *PageAttribute = PageNone;
305 return NULL;
306 }
307
308 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);
309 } else {
310 ASSERT((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0);
311 L3PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.Ia32.PageTableBase;
312 }
313 if (L3PageTable[Index3] == 0) {
314 *PageAttribute = PageNone;
315 return NULL;
316 }
317 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
318 // 1G
319 *PageAttribute = Page1G;
320 return &L3PageTable[Index3];
321 }
322
323 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);
324 if (L2PageTable[Index2] == 0) {
325 *PageAttribute = PageNone;
326 return NULL;
327 }
328 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
329 // 2M
330 *PageAttribute = Page2M;
331 return &L2PageTable[Index2];
332 }
333
334 // 4k
335 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);
336 if ((L1PageTable[Index1] == 0) && (Address != 0)) {
337 *PageAttribute = PageNone;
338 return NULL;
339 }
340 *PageAttribute = Page4K;
341 return &L1PageTable[Index1];
342 }
343
344 /**
345 Return memory attributes of page entry.
346
347 @param[in] PageEntry The page entry.
348
349 @return Memory attributes of page entry.
350 **/
351 UINT64
352 GetAttributesFromPageEntry (
353 IN UINT64 *PageEntry
354 )
355 {
356 UINT64 Attributes;
357 Attributes = 0;
358 if ((*PageEntry & IA32_PG_P) == 0) {
359 Attributes |= EFI_MEMORY_RP;
360 }
361 if ((*PageEntry & IA32_PG_RW) == 0) {
362 Attributes |= EFI_MEMORY_RO;
363 }
364 if ((*PageEntry & IA32_PG_NX) != 0) {
365 Attributes |= EFI_MEMORY_XP;
366 }
367 return Attributes;
368 }
369
370 /**
371 Modify memory attributes of page entry.
372
373 @param[in] PagingContext The paging context.
374 @param[in] PageEntry The page entry.
375 @param[in] Attributes The bit mask of attributes to modify for the memory region.
376 @param[in] PageAction The page action.
377 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
378 **/
379 VOID
380 ConvertPageEntryAttribute (
381 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,
382 IN UINT64 *PageEntry,
383 IN UINT64 Attributes,
384 IN PAGE_ACTION PageAction,
385 OUT BOOLEAN *IsModified
386 )
387 {
388 UINT64 CurrentPageEntry;
389 UINT64 NewPageEntry;
390
391 CurrentPageEntry = *PageEntry;
392 NewPageEntry = CurrentPageEntry;
393 if ((Attributes & EFI_MEMORY_RP) != 0) {
394 switch (PageAction) {
395 case PageActionAssign:
396 case PageActionSet:
397 NewPageEntry &= ~(UINT64)IA32_PG_P;
398 break;
399 case PageActionClear:
400 NewPageEntry |= IA32_PG_P;
401 break;
402 }
403 } else {
404 switch (PageAction) {
405 case PageActionAssign:
406 NewPageEntry |= IA32_PG_P;
407 break;
408 case PageActionSet:
409 case PageActionClear:
410 break;
411 }
412 }
413 if ((Attributes & EFI_MEMORY_RO) != 0) {
414 switch (PageAction) {
415 case PageActionAssign:
416 case PageActionSet:
417 NewPageEntry &= ~(UINT64)IA32_PG_RW;
418 break;
419 case PageActionClear:
420 NewPageEntry |= IA32_PG_RW;
421 break;
422 }
423 } else {
424 switch (PageAction) {
425 case PageActionAssign:
426 NewPageEntry |= IA32_PG_RW;
427 break;
428 case PageActionSet:
429 case PageActionClear:
430 break;
431 }
432 }
433 if ((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED) != 0) {
434 if ((Attributes & EFI_MEMORY_XP) != 0) {
435 switch (PageAction) {
436 case PageActionAssign:
437 case PageActionSet:
438 NewPageEntry |= IA32_PG_NX;
439 break;
440 case PageActionClear:
441 NewPageEntry &= ~IA32_PG_NX;
442 break;
443 }
444 } else {
445 switch (PageAction) {
446 case PageActionAssign:
447 NewPageEntry &= ~IA32_PG_NX;
448 break;
449 case PageActionSet:
450 case PageActionClear:
451 break;
452 }
453 }
454 }
455 *PageEntry = NewPageEntry;
456 if (CurrentPageEntry != NewPageEntry) {
457 *IsModified = TRUE;
458 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));
459 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));
460 } else {
461 *IsModified = FALSE;
462 }
463 }
464
465 /**
466 This function returns if there is need to split page entry.
467
468 @param[in] BaseAddress The base address to be checked.
469 @param[in] Length The length to be checked.
470 @param[in] PageEntry The page entry to be checked.
471 @param[in] PageAttribute The page attribute of the page entry.
472
473 @retval SplitAttributes on if there is need to split page entry.
474 **/
475 PAGE_ATTRIBUTE
476 NeedSplitPage (
477 IN PHYSICAL_ADDRESS BaseAddress,
478 IN UINT64 Length,
479 IN UINT64 *PageEntry,
480 IN PAGE_ATTRIBUTE PageAttribute
481 )
482 {
483 UINT64 PageEntryLength;
484
485 PageEntryLength = PageAttributeToLength (PageAttribute);
486
487 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {
488 return PageNone;
489 }
490
491 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
492 return Page4K;
493 }
494
495 return Page2M;
496 }
497
498 /**
499 This function splits one page entry to small page entries.
500
501 @param[in] PageEntry The page entry to be splitted.
502 @param[in] PageAttribute The page attribute of the page entry.
503 @param[in] SplitAttribute How to split the page entry.
504 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
505
506 @retval RETURN_SUCCESS The page entry is splitted.
507 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
508 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
509 **/
510 RETURN_STATUS
511 SplitPage (
512 IN UINT64 *PageEntry,
513 IN PAGE_ATTRIBUTE PageAttribute,
514 IN PAGE_ATTRIBUTE SplitAttribute,
515 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc
516 )
517 {
518 UINT64 BaseAddress;
519 UINT64 *NewPageEntry;
520 UINTN Index;
521 UINT64 AddressEncMask;
522
523 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
524
525 ASSERT (AllocatePagesFunc != NULL);
526
527 // Make sure AddressEncMask is contained to smallest supported address field.
528 //
529 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
530
531 if (PageAttribute == Page2M) {
532 //
533 // Split 2M to 4K
534 //
535 ASSERT (SplitAttribute == Page4K);
536 if (SplitAttribute == Page4K) {
537 NewPageEntry = AllocatePagesFunc (1);
538 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
539 if (NewPageEntry == NULL) {
540 return RETURN_OUT_OF_RESOURCES;
541 }
542 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_2M_ADDRESS_MASK_64;
543 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
544 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);
545 }
546 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS);
547 return RETURN_SUCCESS;
548 } else {
549 return RETURN_UNSUPPORTED;
550 }
551 } else if (PageAttribute == Page1G) {
552 //
553 // Split 1G to 2M
554 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
555 //
556 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);
557 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {
558 NewPageEntry = AllocatePagesFunc (1);
559 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
560 if (NewPageEntry == NULL) {
561 return RETURN_OUT_OF_RESOURCES;
562 }
563 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_1G_ADDRESS_MASK_64;
564 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
565 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);
566 }
567 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS);
568 return RETURN_SUCCESS;
569 } else {
570 return RETURN_UNSUPPORTED;
571 }
572 } else {
573 return RETURN_UNSUPPORTED;
574 }
575 }
576
577 /**
578 Check the WP status in CR0 register. This bit is used to lock or unlock write
579 access to pages marked as read-only.
580
581 @retval TRUE Write protection is enabled.
582 @retval FALSE Write protection is disabled.
583 **/
584 BOOLEAN
585 IsReadOnlyPageWriteProtected (
586 VOID
587 )
588 {
589 //
590 // To avoid unforseen consequences, don't touch paging settings in SMM mode
591 // in this driver.
592 //
593 if (!IsInSmm ()) {
594 return ((AsmReadCr0 () & CR0_WP) != 0);
595 }
596 return FALSE;
597 }
598
599 /**
600 Disable Write Protect on pages marked as read-only.
601 **/
602 VOID
603 DisableReadOnlyPageWriteProtect (
604 VOID
605 )
606 {
607 //
608 // To avoid unforseen consequences, don't touch paging settings in SMM mode
609 // in this driver.
610 //
611 if (!IsInSmm ()) {
612 AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
613 }
614 }
615
616 /**
617 Enable Write Protect on pages marked as read-only.
618 **/
619 VOID
620 EnableReadOnlyPageWriteProtect (
621 VOID
622 )
623 {
624 //
625 // To avoid unforseen consequences, don't touch paging settings in SMM mode
626 // in this driver.
627 //
628 if (!IsInSmm ()) {
629 AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
630 }
631 }
632
633 /**
634 This function modifies the page attributes for the memory region specified by BaseAddress and
635 Length from their current attributes to the attributes specified by Attributes.
636
637 Caller should make sure BaseAddress and Length is at page boundary.
638
639 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
640 @param[in] BaseAddress The physical address that is the start address of a memory region.
641 @param[in] Length The size in bytes of the memory region.
642 @param[in] Attributes The bit mask of attributes to modify for the memory region.
643 @param[in] PageAction The page action.
644 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
645 NULL mean page split is unsupported.
646 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
647 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
648
649 @retval RETURN_SUCCESS The attributes were modified for the memory region.
650 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
651 BaseAddress and Length cannot be modified.
652 @retval RETURN_INVALID_PARAMETER Length is zero.
653 Attributes specified an illegal combination of attributes that
654 cannot be set together.
655 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
656 the memory resource range.
657 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
658 resource range specified by BaseAddress and Length.
659 The bit mask of attributes is not support for the memory resource
660 range specified by BaseAddress and Length.
661 **/
662 RETURN_STATUS
663 ConvertMemoryPageAttributes (
664 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,
665 IN PHYSICAL_ADDRESS BaseAddress,
666 IN UINT64 Length,
667 IN UINT64 Attributes,
668 IN PAGE_ACTION PageAction,
669 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL,
670 OUT BOOLEAN *IsSplitted, OPTIONAL
671 OUT BOOLEAN *IsModified OPTIONAL
672 )
673 {
674 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;
675 UINT64 *PageEntry;
676 PAGE_ATTRIBUTE PageAttribute;
677 UINTN PageEntryLength;
678 PAGE_ATTRIBUTE SplitAttribute;
679 RETURN_STATUS Status;
680 BOOLEAN IsEntryModified;
681 BOOLEAN IsWpEnabled;
682
683 if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
684 DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress));
685 return EFI_UNSUPPORTED;
686 }
687 if ((Length & (SIZE_4KB - 1)) != 0) {
688 DEBUG ((DEBUG_ERROR, "Length(0x%lx) is not aligned!\n", Length));
689 return EFI_UNSUPPORTED;
690 }
691 if (Length == 0) {
692 DEBUG ((DEBUG_ERROR, "Length is 0!\n"));
693 return RETURN_INVALID_PARAMETER;
694 }
695
696 if ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) != 0) {
697 DEBUG ((DEBUG_ERROR, "Attributes(0x%lx) has unsupported bit\n", Attributes));
698 return EFI_UNSUPPORTED;
699 }
700
701 if (PagingContext == NULL) {
702 GetCurrentPagingContext (&CurrentPagingContext);
703 } else {
704 CopyMem (&CurrentPagingContext, PagingContext, sizeof(CurrentPagingContext));
705 }
706 switch(CurrentPagingContext.MachineType) {
707 case IMAGE_FILE_MACHINE_I386:
708 if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) {
709 if (Attributes == 0) {
710 return EFI_SUCCESS;
711 } else {
712 DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));
713 return EFI_UNSUPPORTED;
714 }
715 }
716 if ((CurrentPagingContext.ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) {
717 DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));
718 return EFI_UNSUPPORTED;
719 }
720 if ((BaseAddress + Length) > BASE_4GB) {
721 DEBUG ((DEBUG_ERROR, "Beyond 4GB memory in 32-bit mode!\n"));
722 return EFI_UNSUPPORTED;
723 }
724 break;
725 case IMAGE_FILE_MACHINE_X64:
726 ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);
727 break;
728 default:
729 ASSERT(FALSE);
730 return EFI_UNSUPPORTED;
731 break;
732 }
733
734 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
735
736 if (IsSplitted != NULL) {
737 *IsSplitted = FALSE;
738 }
739 if (IsModified != NULL) {
740 *IsModified = FALSE;
741 }
742 if (AllocatePagesFunc == NULL) {
743 AllocatePagesFunc = AllocatePageTableMemory;
744 }
745
746 //
747 // Make sure that the page table is changeable.
748 //
749 IsWpEnabled = IsReadOnlyPageWriteProtected ();
750 if (IsWpEnabled) {
751 DisableReadOnlyPageWriteProtect ();
752 }
753
754 //
755 // Below logic is to check 2M/4K page to make sure we donot waist memory.
756 //
757 Status = EFI_SUCCESS;
758 while (Length != 0) {
759 PageEntry = GetPageTableEntry (&CurrentPagingContext, BaseAddress, &PageAttribute);
760 if (PageEntry == NULL) {
761 Status = RETURN_UNSUPPORTED;
762 goto Done;
763 }
764 PageEntryLength = PageAttributeToLength (PageAttribute);
765 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);
766 if (SplitAttribute == PageNone) {
767 ConvertPageEntryAttribute (&CurrentPagingContext, PageEntry, Attributes, PageAction, &IsEntryModified);
768 if (IsEntryModified) {
769 if (IsModified != NULL) {
770 *IsModified = TRUE;
771 }
772 }
773 //
774 // Convert success, move to next
775 //
776 BaseAddress += PageEntryLength;
777 Length -= PageEntryLength;
778 } else {
779 if (AllocatePagesFunc == NULL) {
780 Status = RETURN_UNSUPPORTED;
781 goto Done;
782 }
783 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute, AllocatePagesFunc);
784 if (RETURN_ERROR (Status)) {
785 Status = RETURN_UNSUPPORTED;
786 goto Done;
787 }
788 if (IsSplitted != NULL) {
789 *IsSplitted = TRUE;
790 }
791 if (IsModified != NULL) {
792 *IsModified = TRUE;
793 }
794 //
795 // Just split current page
796 // Convert success in next around
797 //
798 }
799 }
800
801 Done:
802 //
803 // Restore page table write protection, if any.
804 //
805 if (IsWpEnabled) {
806 EnableReadOnlyPageWriteProtect ();
807 }
808 return Status;
809 }
810
811 /**
812 This function assigns the page attributes for the memory region specified by BaseAddress and
813 Length from their current attributes to the attributes specified by Attributes.
814
815 Caller should make sure BaseAddress and Length is at page boundary.
816
817 Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
818
819 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
820 @param[in] BaseAddress The physical address that is the start address of a memory region.
821 @param[in] Length The size in bytes of the memory region.
822 @param[in] Attributes The bit mask of attributes to set for the memory region.
823 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
824 NULL mean page split is unsupported.
825
826 @retval RETURN_SUCCESS The attributes were cleared for the memory region.
827 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
828 BaseAddress and Length cannot be modified.
829 @retval RETURN_INVALID_PARAMETER Length is zero.
830 Attributes specified an illegal combination of attributes that
831 cannot be set together.
832 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
833 the memory resource range.
834 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
835 resource range specified by BaseAddress and Length.
836 The bit mask of attributes is not support for the memory resource
837 range specified by BaseAddress and Length.
838 **/
839 RETURN_STATUS
840 EFIAPI
841 AssignMemoryPageAttributes (
842 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,
843 IN PHYSICAL_ADDRESS BaseAddress,
844 IN UINT64 Length,
845 IN UINT64 Attributes,
846 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL
847 )
848 {
849 RETURN_STATUS Status;
850 BOOLEAN IsModified;
851 BOOLEAN IsSplitted;
852
853 // DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));
854 Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress, Length, Attributes, PageActionAssign, AllocatePagesFunc, &IsSplitted, &IsModified);
855 if (!EFI_ERROR(Status)) {
856 if ((PagingContext == NULL) && IsModified) {
857 //
858 // Flush TLB as last step.
859 //
860 // Note: Since APs will always init CR3 register in HLT loop mode or do
861 // TLB flush in MWAIT loop mode, there's no need to flush TLB for them
862 // here.
863 //
864 CpuFlushTlb();
865 }
866 }
867
868 return Status;
869 }
870
871 /**
872 Check if Execute Disable feature is enabled or not.
873 **/
874 BOOLEAN
875 IsExecuteDisableEnabled (
876 VOID
877 )
878 {
879 MSR_CORE_IA32_EFER_REGISTER MsrEfer;
880
881 MsrEfer.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
882 return (MsrEfer.Bits.NXE == 1);
883 }
884
885 /**
886 Update GCD memory space attributes according to current page table setup.
887 **/
888 VOID
889 RefreshGcdMemoryAttributesFromPaging (
890 VOID
891 )
892 {
893 EFI_STATUS Status;
894 UINTN NumberOfDescriptors;
895 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
896 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;
897 PAGE_ATTRIBUTE PageAttribute;
898 UINT64 *PageEntry;
899 UINT64 PageLength;
900 UINT64 MemorySpaceLength;
901 UINT64 Length;
902 UINT64 BaseAddress;
903 UINT64 PageStartAddress;
904 UINT64 Attributes;
905 UINT64 Capabilities;
906 UINT64 NewAttributes;
907 UINTN Index;
908
909 //
910 // Assuming that memory space map returned is sorted already; otherwise sort
911 // them in the order of lowest address to highest address.
912 //
913 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
914 ASSERT_EFI_ERROR (Status);
915
916 GetCurrentPagingContext (&PagingContext);
917
918 Attributes = 0;
919 NewAttributes = 0;
920 BaseAddress = 0;
921 PageLength = 0;
922
923 if (IsExecuteDisableEnabled ()) {
924 Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP;
925 } else {
926 Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP;
927 }
928
929 for (Index = 0; Index < NumberOfDescriptors; Index++) {
930 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
931 continue;
932 }
933
934 //
935 // Sync the actual paging related capabilities back to GCD service first.
936 // As a side effect (good one), this can also help to avoid unnecessary
937 // memory map entries due to the different capabilities of the same type
938 // memory, such as multiple RT_CODE and RT_DATA entries in memory map,
939 // which could cause boot failure of some old Linux distro (before v4.3).
940 //
941 Status = gDS->SetMemorySpaceCapabilities (
942 MemorySpaceMap[Index].BaseAddress,
943 MemorySpaceMap[Index].Length,
944 MemorySpaceMap[Index].Capabilities | Capabilities
945 );
946 if (EFI_ERROR (Status)) {
947 //
948 // If we cannot udpate the capabilities, we cannot update its
949 // attributes either. So just simply skip current block of memory.
950 //
951 DEBUG ((
952 DEBUG_WARN,
953 "Failed to update capability: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
954 (UINT64)Index, MemorySpaceMap[Index].BaseAddress,
955 MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - 1,
956 MemorySpaceMap[Index].Capabilities,
957 MemorySpaceMap[Index].Capabilities | Capabilities
958 ));
959 continue;
960 }
961
962 if (MemorySpaceMap[Index].BaseAddress >= (BaseAddress + PageLength)) {
963 //
964 // Current memory space starts at a new page. Resetting PageLength will
965 // trigger a retrieval of page attributes at new address.
966 //
967 PageLength = 0;
968 } else {
969 //
970 // In case current memory space is not adjacent to last one
971 //
972 PageLength -= (MemorySpaceMap[Index].BaseAddress - BaseAddress);
973 }
974
975 //
976 // Sync actual page attributes to GCD
977 //
978 BaseAddress = MemorySpaceMap[Index].BaseAddress;
979 MemorySpaceLength = MemorySpaceMap[Index].Length;
980 while (MemorySpaceLength > 0) {
981 if (PageLength == 0) {
982 PageEntry = GetPageTableEntry (&PagingContext, BaseAddress, &PageAttribute);
983 if (PageEntry == NULL) {
984 break;
985 }
986
987 //
988 // Note current memory space might start in the middle of a page
989 //
990 PageStartAddress = (*PageEntry) & (UINT64)PageAttributeToMask(PageAttribute);
991 PageLength = PageAttributeToLength (PageAttribute) - (BaseAddress - PageStartAddress);
992 Attributes = GetAttributesFromPageEntry (PageEntry);
993 }
994
995 Length = MIN (PageLength, MemorySpaceLength);
996 if (Attributes != (MemorySpaceMap[Index].Attributes &
997 EFI_MEMORY_PAGETYPE_MASK)) {
998 NewAttributes = (MemorySpaceMap[Index].Attributes &
999 ~EFI_MEMORY_PAGETYPE_MASK) | Attributes;
1000 Status = gDS->SetMemorySpaceAttributes (
1001 BaseAddress,
1002 Length,
1003 NewAttributes
1004 );
1005 ASSERT_EFI_ERROR (Status);
1006 DEBUG ((
1007 DEBUG_VERBOSE,
1008 "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
1009 (UINT64)Index, BaseAddress, BaseAddress + Length - 1,
1010 MemorySpaceMap[Index].Attributes,
1011 NewAttributes
1012 ));
1013 }
1014
1015 PageLength -= Length;
1016 MemorySpaceLength -= Length;
1017 BaseAddress += Length;
1018 }
1019 }
1020
1021 FreePool (MemorySpaceMap);
1022 }
1023
1024 /**
1025 Initialize a buffer pool for page table use only.
1026
1027 To reduce the potential split operation on page table, the pages reserved for
1028 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
1029 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
1030 initialized with number of pages greater than or equal to the given PoolPages.
1031
1032 Once the pages in the pool are used up, this method should be called again to
1033 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't happen
1034 often in practice.
1035
1036 @param[in] PoolPages The least page number of the pool to be created.
1037
1038 @retval TRUE The pool is initialized successfully.
1039 @retval FALSE The memory is out of resource.
1040 **/
1041 BOOLEAN
1042 InitializePageTablePool (
1043 IN UINTN PoolPages
1044 )
1045 {
1046 VOID *Buffer;
1047 BOOLEAN IsModified;
1048
1049 //
1050 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
1051 // header.
1052 //
1053 PoolPages += 1; // Add one page for header.
1054 PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
1055 PAGE_TABLE_POOL_UNIT_PAGES;
1056 Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
1057 if (Buffer == NULL) {
1058 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
1059 return FALSE;
1060 }
1061
1062 //
1063 // Link all pools into a list for easier track later.
1064 //
1065 if (mPageTablePool == NULL) {
1066 mPageTablePool = Buffer;
1067 mPageTablePool->NextPool = mPageTablePool;
1068 } else {
1069 ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
1070 mPageTablePool->NextPool = Buffer;
1071 mPageTablePool = Buffer;
1072 }
1073
1074 //
1075 // Reserve one page for pool header.
1076 //
1077 mPageTablePool->FreePages = PoolPages - 1;
1078 mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
1079
1080 //
1081 // Mark the whole pool pages as read-only.
1082 //
1083 ConvertMemoryPageAttributes (
1084 NULL,
1085 (PHYSICAL_ADDRESS)(UINTN)Buffer,
1086 EFI_PAGES_TO_SIZE (PoolPages),
1087 EFI_MEMORY_RO,
1088 PageActionSet,
1089 AllocatePageTableMemory,
1090 NULL,
1091 &IsModified
1092 );
1093 ASSERT (IsModified == TRUE);
1094
1095 return TRUE;
1096 }
1097
1098 /**
1099 This API provides a way to allocate memory for page table.
1100
1101 This API can be called more than once to allocate memory for page tables.
1102
1103 Allocates the number of 4KB pages and returns a pointer to the allocated
1104 buffer. The buffer returned is aligned on a 4KB boundary.
1105
1106 If Pages is 0, then NULL is returned.
1107 If there is not enough memory remaining to satisfy the request, then NULL is
1108 returned.
1109
1110 @param Pages The number of 4 KB pages to allocate.
1111
1112 @return A pointer to the allocated buffer or NULL if allocation fails.
1113
1114 **/
1115 VOID *
1116 EFIAPI
1117 AllocatePageTableMemory (
1118 IN UINTN Pages
1119 )
1120 {
1121 VOID *Buffer;
1122
1123 if (Pages == 0) {
1124 return NULL;
1125 }
1126
1127 //
1128 // Renew the pool if necessary.
1129 //
1130 if (mPageTablePool == NULL ||
1131 Pages > mPageTablePool->FreePages) {
1132 if (!InitializePageTablePool (Pages)) {
1133 return NULL;
1134 }
1135 }
1136
1137 Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
1138
1139 mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
1140 mPageTablePool->FreePages -= Pages;
1141
1142 return Buffer;
1143 }
1144
1145 /**
1146 Special handler for #DB exception, which will restore the page attributes
1147 (not-present). It should work with #PF handler which will set pages to
1148 'present'.
1149
1150 @param ExceptionType Exception type.
1151 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
1152
1153 **/
1154 VOID
1155 EFIAPI
1156 DebugExceptionHandler (
1157 IN EFI_EXCEPTION_TYPE ExceptionType,
1158 IN EFI_SYSTEM_CONTEXT SystemContext
1159 )
1160 {
1161 UINTN CpuIndex;
1162 UINTN PFEntry;
1163 BOOLEAN IsWpEnabled;
1164
1165 MpInitLibWhoAmI (&CpuIndex);
1166
1167 //
1168 // Clear last PF entries
1169 //
1170 IsWpEnabled = IsReadOnlyPageWriteProtected ();
1171 if (IsWpEnabled) {
1172 DisableReadOnlyPageWriteProtect ();
1173 }
1174
1175 for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) {
1176 if (mLastPFEntryPointer[CpuIndex][PFEntry] != NULL) {
1177 *mLastPFEntryPointer[CpuIndex][PFEntry] &= ~(UINT64)IA32_PG_P;
1178 }
1179 }
1180
1181 if (IsWpEnabled) {
1182 EnableReadOnlyPageWriteProtect ();
1183 }
1184
1185 //
1186 // Reset page fault exception count for next page fault.
1187 //
1188 mPFEntryCount[CpuIndex] = 0;
1189
1190 //
1191 // Flush TLB
1192 //
1193 CpuFlushTlb ();
1194
1195 //
1196 // Clear TF in EFLAGS
1197 //
1198 if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) {
1199 SystemContext.SystemContextIa32->Eflags &= (UINT32)~BIT8;
1200 } else {
1201 SystemContext.SystemContextX64->Rflags &= (UINT64)~BIT8;
1202 }
1203 }
1204
1205 /**
1206 Special handler for #PF exception, which will set the pages which caused
1207 #PF to be 'present'. The attribute of those pages should be restored in
1208 the subsequent #DB handler.
1209
1210 @param ExceptionType Exception type.
1211 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
1212
1213 **/
1214 VOID
1215 EFIAPI
1216 PageFaultExceptionHandler (
1217 IN EFI_EXCEPTION_TYPE ExceptionType,
1218 IN EFI_SYSTEM_CONTEXT SystemContext
1219 )
1220 {
1221 EFI_STATUS Status;
1222 UINT64 PFAddress;
1223 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;
1224 PAGE_ATTRIBUTE PageAttribute;
1225 UINT64 Attributes;
1226 UINT64 *PageEntry;
1227 UINTN Index;
1228 UINTN CpuIndex;
1229 UINTN PageNumber;
1230 BOOLEAN NonStopMode;
1231
1232 PFAddress = AsmReadCr2 () & ~EFI_PAGE_MASK;
1233 if (PFAddress < BASE_4KB) {
1234 NonStopMode = NULL_DETECTION_NONSTOP_MODE ? TRUE : FALSE;
1235 } else {
1236 NonStopMode = HEAP_GUARD_NONSTOP_MODE ? TRUE : FALSE;
1237 }
1238
1239 if (NonStopMode) {
1240 MpInitLibWhoAmI (&CpuIndex);
1241 GetCurrentPagingContext (&PagingContext);
1242 //
1243 // Memory operation cross page boundary, like "rep mov" instruction, will
1244 // cause infinite loop between this and Debug Trap handler. We have to make
1245 // sure that current page and the page followed are both in PRESENT state.
1246 //
1247 PageNumber = 2;
1248 while (PageNumber > 0) {
1249 PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);
1250 ASSERT(PageEntry != NULL);
1251
1252 if (PageEntry != NULL) {
1253 Attributes = GetAttributesFromPageEntry (PageEntry);
1254 if ((Attributes & EFI_MEMORY_RP) != 0) {
1255 Attributes &= ~EFI_MEMORY_RP;
1256 Status = AssignMemoryPageAttributes (&PagingContext, PFAddress,
1257 EFI_PAGE_SIZE, Attributes, NULL);
1258 if (!EFI_ERROR(Status)) {
1259 Index = mPFEntryCount[CpuIndex];
1260 //
1261 // Re-retrieve page entry because above calling might update page
1262 // table due to table split.
1263 //
1264 PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);
1265 mLastPFEntryPointer[CpuIndex][Index++] = PageEntry;
1266 mPFEntryCount[CpuIndex] = Index;
1267 }
1268 }
1269 }
1270
1271 PFAddress += EFI_PAGE_SIZE;
1272 --PageNumber;
1273 }
1274 }
1275
1276 //
1277 // Initialize the serial port before dumping.
1278 //
1279 SerialPortInitialize ();
1280 //
1281 // Display ExceptionType, CPU information and Image information
1282 //
1283 DumpCpuContext (ExceptionType, SystemContext);
1284 if (!NonStopMode) {
1285 CpuDeadLoop ();
1286 }
1287 }
1288
1289 /**
1290 Initialize the Page Table lib.
1291 **/
1292 VOID
1293 InitializePageTableLib (
1294 VOID
1295 )
1296 {
1297 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;
1298
1299 GetCurrentPagingContext (&CurrentPagingContext);
1300
1301 //
1302 // Reserve memory of page tables for future uses, if paging is enabled.
1303 //
1304 if (CurrentPagingContext.ContextData.X64.PageTableBase != 0 &&
1305 (CurrentPagingContext.ContextData.Ia32.Attributes &
1306 PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0) {
1307 DisableReadOnlyPageWriteProtect ();
1308 InitializePageTablePool (1);
1309 EnableReadOnlyPageWriteProtect ();
1310 }
1311
1312 if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {
1313 mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mNumberOfProcessors);
1314 ASSERT (mPFEntryCount != NULL);
1315
1316 mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])
1317 AllocateZeroPool (sizeof (mLastPFEntryPointer[0]) * mNumberOfProcessors);
1318 ASSERT (mLastPFEntryPointer != NULL);
1319 }
1320
1321 DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));
1322 DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType));
1323 DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));
1324 DEBUG ((DEBUG_INFO, " Attributes - 0x%x\n", CurrentPagingContext.ContextData.X64.Attributes));
1325
1326 return ;
1327 }
1328