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