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