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