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