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