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