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