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