VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiCpuPkg/CpuDxe/CpuPageTable.c@ 99404

Last change on this file since 99404 was 99404, checked in by vboxsync, 2 years ago

Devices/EFI/FirmwareNew: Update to edk2-stable202302 and make it build, bugref:4643

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

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