VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuPageTable.c@ 86513

Last change on this file since 86513 was 85718, checked in by vboxsync, 4 years ago

Devices/EFI: Merge edk-stable202005 and make it build, bugref:4643

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

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