]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/CpuDxe/CpuPageTable.c
UefiCpuPkg/CpuDxe: Remove MSR_IA32_MISC_ENABLE check
[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
26#include "CpuPageTable.h"\r
27\r
28///\r
29/// Page Table Entry\r
30///\r
31#define IA32_PG_P BIT0\r
32#define IA32_PG_RW BIT1\r
33#define IA32_PG_U BIT2\r
34#define IA32_PG_WT BIT3\r
35#define IA32_PG_CD BIT4\r
36#define IA32_PG_A BIT5\r
37#define IA32_PG_D BIT6\r
38#define IA32_PG_PS BIT7\r
39#define IA32_PG_PAT_2M BIT12\r
40#define IA32_PG_PAT_4K IA32_PG_PS\r
41#define IA32_PG_PMNT BIT62\r
42#define IA32_PG_NX BIT63\r
43\r
44#define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P)\r
45//\r
46// Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE\r
47// X64 PAE PDPTE does not have such restriction\r
48//\r
49#define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P)\r
50\r
51#define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS)\r
52\r
53#define PAGING_4K_MASK 0xFFF\r
54#define PAGING_2M_MASK 0x1FFFFF\r
55#define PAGING_1G_MASK 0x3FFFFFFF\r
56\r
57#define PAGING_PAE_INDEX_MASK 0x1FF\r
58\r
59#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull\r
60#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull\r
61#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull\r
62\r
63typedef enum {\r
64 PageNone,\r
65 Page4K,\r
66 Page2M,\r
67 Page1G,\r
68} PAGE_ATTRIBUTE;\r
69\r
70typedef struct {\r
71 PAGE_ATTRIBUTE Attribute;\r
72 UINT64 Length;\r
73 UINT64 AddressMask;\r
74} PAGE_ATTRIBUTE_TABLE;\r
75\r
76typedef enum {\r
77 PageActionAssign,\r
78 PageActionSet,\r
79 PageActionClear,\r
80} PAGE_ACTION;\r
81\r
82PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
83 {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},\r
84 {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},\r
85 {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},\r
86};\r
87\r
88/**\r
89 Enable write protection function for AP.\r
90\r
91 @param[in,out] Buffer The pointer to private data buffer.\r
92**/\r
93VOID\r
94EFIAPI\r
95SyncCpuEnableWriteProtection (\r
96 IN OUT VOID *Buffer\r
97 )\r
98{\r
99 AsmWriteCr0 (AsmReadCr0 () | BIT16);\r
100}\r
101\r
102/**\r
103 CpuFlushTlb function for AP.\r
104\r
105 @param[in,out] Buffer The pointer to private data buffer.\r
106**/\r
107VOID\r
108EFIAPI\r
109SyncCpuFlushTlb (\r
110 IN OUT VOID *Buffer\r
111 )\r
112{\r
113 CpuFlushTlb();\r
114}\r
115\r
116/**\r
117 Sync memory page attributes for AP.\r
118\r
119 @param[in] Procedure A pointer to the function to be run on enabled APs of\r
120 the system.\r
121**/\r
122VOID\r
123SyncMemoryPageAttributesAp (\r
124 IN EFI_AP_PROCEDURE Procedure\r
125 )\r
126{\r
127 EFI_STATUS Status;\r
128 EFI_MP_SERVICES_PROTOCOL *MpService;\r
129\r
130 Status = gBS->LocateProtocol (\r
131 &gEfiMpServiceProtocolGuid,\r
132 NULL,\r
133 (VOID **)&MpService\r
134 );\r
135 //\r
136 // Synchronize the update with all APs\r
137 //\r
138 if (!EFI_ERROR (Status)) {\r
139 Status = MpService->StartupAllAPs (\r
140 MpService, // This\r
141 Procedure, // Procedure\r
142 FALSE, // SingleThread\r
143 NULL, // WaitEvent\r
144 0, // TimeoutInMicrosecsond\r
145 NULL, // ProcedureArgument\r
146 NULL // FailedCpuList\r
147 );\r
148 ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_STARTED || Status == EFI_NOT_READY);\r
149 }\r
150}\r
151\r
152/**\r
153 Return current paging context.\r
154\r
155 @param[in,out] PagingContext The paging context.\r
156**/\r
157VOID\r
158GetCurrentPagingContext (\r
159 IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext\r
160 )\r
161{\r
162 UINT32 RegEax;\r
163 UINT32 RegEdx;\r
164\r
165 ZeroMem(PagingContext, sizeof(*PagingContext));\r
166 if (sizeof(UINTN) == sizeof(UINT64)) {\r
167 PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;\r
168 } else {\r
169 PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;\r
170 }\r
171 if ((AsmReadCr0 () & BIT31) != 0) {\r
172 PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
173 if ((AsmReadCr0 () & BIT16) == 0) {\r
174 AsmWriteCr0 (AsmReadCr0 () | BIT16);\r
175 SyncMemoryPageAttributesAp (SyncCpuEnableWriteProtection);\r
176 }\r
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
443 DEBUG ((DEBUG_INFO, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
444 DEBUG ((DEBUG_INFO, "->0x%lx\n", NewPageEntry));\r
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
627dcba3 531 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_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
627dcba3 552 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_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
562/**\r
563 This function modifies the page attributes for the memory region specified by BaseAddress and\r
564 Length from their current attributes to the attributes specified by Attributes.\r
565\r
566 Caller should make sure BaseAddress and Length is at page boundary.\r
567\r
568 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
569 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
570 @param[in] Length The size in bytes of the memory region.\r
571 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
572 @param[in] PageAction The page action.\r
573 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
574 NULL mean page split is unsupported.\r
575 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
576 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
577\r
578 @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
579 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
580 BaseAddress and Length cannot be modified.\r
581 @retval RETURN_INVALID_PARAMETER Length is zero.\r
582 Attributes specified an illegal combination of attributes that\r
583 cannot be set together.\r
584 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
585 the memory resource range.\r
586 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
587 resource range specified by BaseAddress and Length.\r
588 The bit mask of attributes is not support for the memory resource\r
589 range specified by BaseAddress and Length.\r
590**/\r
591RETURN_STATUS\r
592ConvertMemoryPageAttributes (\r
593 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
594 IN PHYSICAL_ADDRESS BaseAddress,\r
595 IN UINT64 Length,\r
596 IN UINT64 Attributes,\r
597 IN PAGE_ACTION PageAction,\r
598 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL,\r
599 OUT BOOLEAN *IsSplitted, OPTIONAL\r
600 OUT BOOLEAN *IsModified OPTIONAL\r
601 )\r
602{\r
603 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
604 UINT64 *PageEntry;\r
605 PAGE_ATTRIBUTE PageAttribute;\r
606 UINTN PageEntryLength;\r
607 PAGE_ATTRIBUTE SplitAttribute;\r
608 RETURN_STATUS Status;\r
609 BOOLEAN IsEntryModified;\r
610\r
611 if ((BaseAddress & (SIZE_4KB - 1)) != 0) {\r
612 DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress));\r
613 return EFI_UNSUPPORTED;\r
614 }\r
615 if ((Length & (SIZE_4KB - 1)) != 0) {\r
616 DEBUG ((DEBUG_ERROR, "Length(0x%lx) is not aligned!\n", Length));\r
617 return EFI_UNSUPPORTED;\r
618 }\r
619 if (Length == 0) {\r
620 DEBUG ((DEBUG_ERROR, "Length is 0!\n"));\r
621 return RETURN_INVALID_PARAMETER;\r
622 }\r
623\r
624 if ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) != 0) {\r
625 DEBUG ((DEBUG_ERROR, "Attributes(0x%lx) has unsupported bit\n", Attributes));\r
626 return EFI_UNSUPPORTED;\r
627 }\r
628\r
629 if (PagingContext == NULL) {\r
630 GetCurrentPagingContext (&CurrentPagingContext);\r
631 } else {\r
632 CopyMem (&CurrentPagingContext, PagingContext, sizeof(CurrentPagingContext));\r
633 }\r
634 switch(CurrentPagingContext.MachineType) {\r
635 case IMAGE_FILE_MACHINE_I386:\r
636 if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) {\r
637 DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));\r
638 if (Attributes == 0) {\r
639 return EFI_SUCCESS;\r
640 } else {\r
641 return EFI_UNSUPPORTED;\r
642 }\r
643 }\r
644 if ((CurrentPagingContext.ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) {\r
645 DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));\r
646 return EFI_UNSUPPORTED;\r
647 }\r
648 break;\r
649 case IMAGE_FILE_MACHINE_X64:\r
650 ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);\r
651 break;\r
652 default:\r
653 ASSERT(FALSE);\r
654 return EFI_UNSUPPORTED;\r
655 break;\r
656 }\r
657\r
658// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
659\r
660 if (IsSplitted != NULL) {\r
661 *IsSplitted = FALSE;\r
662 }\r
663 if (IsModified != NULL) {\r
664 *IsModified = FALSE;\r
665 }\r
666\r
667 //\r
668 // Below logic is to check 2M/4K page to make sure we donot waist memory.\r
669 //\r
670 while (Length != 0) {\r
671 PageEntry = GetPageTableEntry (&CurrentPagingContext, BaseAddress, &PageAttribute);\r
672 if (PageEntry == NULL) {\r
673 return RETURN_UNSUPPORTED;\r
674 }\r
675 PageEntryLength = PageAttributeToLength (PageAttribute);\r
676 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
677 if (SplitAttribute == PageNone) {\r
678 ConvertPageEntryAttribute (&CurrentPagingContext, PageEntry, Attributes, PageAction, &IsEntryModified);\r
679 if (IsEntryModified) {\r
680 if (IsModified != NULL) {\r
681 *IsModified = TRUE;\r
682 }\r
683 }\r
684 //\r
685 // Convert success, move to next\r
686 //\r
687 BaseAddress += PageEntryLength;\r
688 Length -= PageEntryLength;\r
689 } else {\r
690 if (AllocatePagesFunc == NULL) {\r
691 return RETURN_UNSUPPORTED;\r
692 }\r
693 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute, AllocatePagesFunc);\r
694 if (RETURN_ERROR (Status)) {\r
695 return RETURN_UNSUPPORTED;\r
696 }\r
697 if (IsSplitted != NULL) {\r
698 *IsSplitted = TRUE;\r
699 }\r
700 if (IsModified != NULL) {\r
701 *IsModified = TRUE;\r
702 }\r
703 //\r
704 // Just split current page\r
705 // Convert success in next around\r
706 //\r
707 }\r
708 }\r
709\r
710 return RETURN_SUCCESS;\r
711}\r
712\r
713/**\r
714 This function assigns the page attributes for the memory region specified by BaseAddress and\r
715 Length from their current attributes to the attributes specified by Attributes.\r
716\r
717 Caller should make sure BaseAddress and Length is at page boundary.\r
718\r
719 Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.\r
720\r
721 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
722 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
723 @param[in] Length The size in bytes of the memory region.\r
724 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
725 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
726 NULL mean page split is unsupported.\r
727\r
728 @retval RETURN_SUCCESS The attributes were cleared for the memory region.\r
729 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
730 BaseAddress and Length cannot be modified.\r
731 @retval RETURN_INVALID_PARAMETER Length is zero.\r
732 Attributes specified an illegal combination of attributes that\r
733 cannot be set together.\r
734 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
735 the memory resource range.\r
736 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
737 resource range specified by BaseAddress and Length.\r
738 The bit mask of attributes is not support for the memory resource\r
739 range specified by BaseAddress and Length.\r
740**/\r
741RETURN_STATUS\r
742EFIAPI\r
743AssignMemoryPageAttributes (\r
744 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
745 IN PHYSICAL_ADDRESS BaseAddress,\r
746 IN UINT64 Length,\r
747 IN UINT64 Attributes,\r
748 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL\r
749 )\r
750{\r
751 RETURN_STATUS Status;\r
752 BOOLEAN IsModified;\r
753 BOOLEAN IsSplitted;\r
754\r
755// DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));\r
756 Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress, Length, Attributes, PageActionAssign, AllocatePagesFunc, &IsSplitted, &IsModified);\r
757 if (!EFI_ERROR(Status)) {\r
758 if ((PagingContext == NULL) && IsModified) {\r
759 //\r
760 // Flush TLB as last step\r
761 //\r
762 CpuFlushTlb();\r
763 SyncMemoryPageAttributesAp (SyncCpuFlushTlb);\r
764 }\r
765 }\r
766\r
767 return Status;\r
768}\r
769\r
770/**\r
771 Initialize the Page Table lib.\r
772**/\r
773VOID\r
774InitializePageTableLib (\r
775 VOID\r
776 )\r
777{\r
778 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
779\r
780 GetCurrentPagingContext (&CurrentPagingContext);\r
781 DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));\r
782 DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType));\r
783 DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));\r
784 DEBUG ((DEBUG_INFO, " Attributes - 0x%x\n", CurrentPagingContext.ContextData.X64.Attributes));\r
785\r
786 return ;\r
787}\r
788\r