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