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