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 |
|
---|
67 | typedef enum {
|
---|
68 | PageNone,
|
---|
69 | Page4K,
|
---|
70 | Page2M,
|
---|
71 | Page1G,
|
---|
72 | } PAGE_ATTRIBUTE;
|
---|
73 |
|
---|
74 | typedef struct {
|
---|
75 | PAGE_ATTRIBUTE Attribute;
|
---|
76 | UINT64 Length;
|
---|
77 | UINT64 AddressMask;
|
---|
78 | } PAGE_ATTRIBUTE_TABLE;
|
---|
79 |
|
---|
80 | typedef enum {
|
---|
81 | PageActionAssign,
|
---|
82 | PageActionSet,
|
---|
83 | PageActionClear,
|
---|
84 | } PAGE_ACTION;
|
---|
85 |
|
---|
86 | PAGE_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 |
|
---|
92 | PAGE_TABLE_POOL *mPageTablePool = NULL;
|
---|
93 | BOOLEAN mPageTablePoolLock = FALSE;
|
---|
94 | PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;
|
---|
95 | EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL;
|
---|
96 |
|
---|
97 | //
|
---|
98 | // Record the page fault exception count for one instruction execution.
|
---|
99 | //
|
---|
100 | UINTN *mPFEntryCount;
|
---|
101 | UINT64 *(*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 | **/
|
---|
112 | static 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 | **/
|
---|
145 | BOOLEAN
|
---|
146 | IsInSmm (
|
---|
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 | **/
|
---|
176 | VOID
|
---|
177 | GetCurrentPagingContext (
|
---|
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 | **/
|
---|
256 | UINTN
|
---|
257 | PageAttributeToLength (
|
---|
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 | **/
|
---|
277 | UINTN
|
---|
278 | PageAttributeToMask (
|
---|
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 | **/
|
---|
300 | VOID *
|
---|
301 | GetPageTableEntry (
|
---|
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 | **/
|
---|
391 | UINT64
|
---|
392 | GetAttributesFromPageEntry (
|
---|
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 | **/
|
---|
419 | VOID
|
---|
420 | ConvertPageEntryAttribute (
|
---|
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 | **/
|
---|
528 | PAGE_ATTRIBUTE
|
---|
529 | NeedSplitPage (
|
---|
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 | **/
|
---|
563 | RETURN_STATUS
|
---|
564 | SplitPage (
|
---|
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 | **/
|
---|
672 | BOOLEAN
|
---|
673 | IsReadOnlyPageWriteProtected (
|
---|
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 | **/
|
---|
692 | VOID
|
---|
693 | DisableReadOnlyPageWriteProtect (
|
---|
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 | **/
|
---|
712 | VOID
|
---|
713 | EnableReadOnlyPageWriteProtect (
|
---|
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 | **/
|
---|
758 | RETURN_STATUS
|
---|
759 | ConvertMemoryPageAttributes (
|
---|
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 |
|
---|
897 | Done:
|
---|
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 | **/
|
---|
935 | RETURN_STATUS
|
---|
936 | EFIAPI
|
---|
937 | AssignMemoryPageAttributes (
|
---|
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 | **/
|
---|
970 | BOOLEAN
|
---|
971 | IsExecuteDisableEnabled (
|
---|
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 | **/
|
---|
984 | VOID
|
---|
985 | RefreshGcdMemoryAttributesFromPaging (
|
---|
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 | **/
|
---|
1137 | BOOLEAN
|
---|
1138 | InitializePageTablePool (
|
---|
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 |
|
---|
1207 | Done:
|
---|
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 | **/
|
---|
1229 | VOID *
|
---|
1230 | EFIAPI
|
---|
1231 | AllocatePageTableMemory (
|
---|
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 | **/
|
---|
1268 | VOID
|
---|
1269 | EFIAPI
|
---|
1270 | DebugExceptionHandler (
|
---|
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 | **/
|
---|
1328 | VOID
|
---|
1329 | EFIAPI
|
---|
1330 | PageFaultExceptionHandler (
|
---|
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 | **/
|
---|
1415 | VOID
|
---|
1416 | InitializePageTableLib (
|
---|
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 |
|
---|