UefiCpuPkg/CpuDxe: Fix out-of-sync issue in page attributes
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuPageTable.c
CommitLineData
22292ed3
JY
1/** @file\r
2 Page table management support.\r
3\r
4 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
627dcba3
LD
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
6\r
22292ed3
JY
7 This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution. The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php\r
11\r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include <Base.h>\r
18#include <Uefi.h>\r
19#include <Library/BaseLib.h>\r
20#include <Library/CpuLib.h>\r
21#include <Library/BaseMemoryLib.h>\r
22#include <Library/MemoryAllocationLib.h>\r
23#include <Library/DebugLib.h>\r
24#include <Library/UefiBootServicesTableLib.h>\r
25#include <Protocol/MpService.h>\r
c1cab54c
JW
26\r
27#include "CpuDxe.h"\r
22292ed3
JY
28#include "CpuPageTable.h"\r
29\r
30///\r
31/// Page Table Entry\r
32///\r
33#define IA32_PG_P BIT0\r
34#define IA32_PG_RW BIT1\r
35#define IA32_PG_U BIT2\r
36#define IA32_PG_WT BIT3\r
37#define IA32_PG_CD BIT4\r
38#define IA32_PG_A BIT5\r
39#define IA32_PG_D BIT6\r
40#define IA32_PG_PS BIT7\r
41#define IA32_PG_PAT_2M BIT12\r
42#define IA32_PG_PAT_4K IA32_PG_PS\r
43#define IA32_PG_PMNT BIT62\r
44#define IA32_PG_NX BIT63\r
45\r
46#define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P)\r
47//\r
48// Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE\r
49// X64 PAE PDPTE does not have such restriction\r
50//\r
51#define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P)\r
52\r
53#define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS)\r
54\r
55#define PAGING_4K_MASK 0xFFF\r
56#define PAGING_2M_MASK 0x1FFFFF\r
57#define PAGING_1G_MASK 0x3FFFFFFF\r
58\r
59#define PAGING_PAE_INDEX_MASK 0x1FF\r
60\r
61#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull\r
62#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull\r
63#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull\r
64\r
65typedef enum {\r
66 PageNone,\r
67 Page4K,\r
68 Page2M,\r
69 Page1G,\r
70} PAGE_ATTRIBUTE;\r
71\r
72typedef struct {\r
73 PAGE_ATTRIBUTE Attribute;\r
74 UINT64 Length;\r
75 UINT64 AddressMask;\r
76} PAGE_ATTRIBUTE_TABLE;\r
77\r
78typedef enum {\r
79 PageActionAssign,\r
80 PageActionSet,\r
81 PageActionClear,\r
82} PAGE_ACTION;\r
83\r
84PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
85 {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},\r
86 {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},\r
87 {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},\r
88};\r
89\r
90/**\r
91 Enable write protection function for AP.\r
92\r
93 @param[in,out] Buffer The pointer to private data buffer.\r
94**/\r
95VOID\r
96EFIAPI\r
97SyncCpuEnableWriteProtection (\r
98 IN OUT VOID *Buffer\r
99 )\r
100{\r
101 AsmWriteCr0 (AsmReadCr0 () | BIT16);\r
102}\r
103\r
104/**\r
105 CpuFlushTlb function for AP.\r
106\r
107 @param[in,out] Buffer The pointer to private data buffer.\r
108**/\r
109VOID\r
110EFIAPI\r
111SyncCpuFlushTlb (\r
112 IN OUT VOID *Buffer\r
113 )\r
114{\r
115 CpuFlushTlb();\r
116}\r
117\r
118/**\r
119 Sync memory page attributes for AP.\r
120\r
121 @param[in] Procedure A pointer to the function to be run on enabled APs of\r
122 the system.\r
123**/\r
124VOID\r
125SyncMemoryPageAttributesAp (\r
126 IN EFI_AP_PROCEDURE Procedure\r
127 )\r
128{\r
129 EFI_STATUS Status;\r
130 EFI_MP_SERVICES_PROTOCOL *MpService;\r
131\r
132 Status = gBS->LocateProtocol (\r
133 &gEfiMpServiceProtocolGuid,\r
134 NULL,\r
135 (VOID **)&MpService\r
136 );\r
137 //\r
138 // Synchronize the update with all APs\r
139 //\r
140 if (!EFI_ERROR (Status)) {\r
141 Status = MpService->StartupAllAPs (\r
142 MpService, // This\r
143 Procedure, // Procedure\r
144 FALSE, // SingleThread\r
145 NULL, // WaitEvent\r
146 0, // TimeoutInMicrosecsond\r
147 NULL, // ProcedureArgument\r
148 NULL // FailedCpuList\r
149 );\r
150 ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_STARTED || Status == EFI_NOT_READY);\r
151 }\r
152}\r
153\r
154/**\r
155 Return current paging context.\r
156\r
157 @param[in,out] PagingContext The paging context.\r
158**/\r
159VOID\r
160GetCurrentPagingContext (\r
161 IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext\r
162 )\r
163{\r
164 UINT32 RegEax;\r
165 UINT32 RegEdx;\r
166\r
167 ZeroMem(PagingContext, sizeof(*PagingContext));\r
168 if (sizeof(UINTN) == sizeof(UINT64)) {\r
169 PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;\r
170 } else {\r
171 PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;\r
172 }\r
173 if ((AsmReadCr0 () & BIT31) != 0) {\r
174 PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
175 if ((AsmReadCr0 () & BIT16) == 0) {\r
176 AsmWriteCr0 (AsmReadCr0 () | BIT16);\r
177 SyncMemoryPageAttributesAp (SyncCpuEnableWriteProtection);\r
178 }\r
179 } else {\r
180 PagingContext->ContextData.X64.PageTableBase = 0;\r
181 }\r
182\r
183 if ((AsmReadCr4 () & BIT4) != 0) {\r
184 PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;\r
185 }\r
186 if ((AsmReadCr4 () & BIT5) != 0) {\r
187 PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;\r
188 }\r
189 if ((AsmReadCr0 () & BIT16) != 0) {\r
190 PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;\r
191 }\r
192\r
193 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
194 if (RegEax > 0x80000000) {\r
195 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
196 if ((RegEdx & BIT20) != 0) {\r
197 // XD supported\r
01eb3f39
JF
198 if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {\r
199 // XD activated\r
200 PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;\r
22292ed3
JY
201 }\r
202 }\r
203 if ((RegEdx & BIT26) != 0) {\r
204 PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;\r
205 }\r
206 }\r
207}\r
208\r
209/**\r
210 Return length according to page attributes.\r
211\r
212 @param[in] PageAttributes The page attribute of the page entry.\r
213\r
214 @return The length of page entry.\r
215**/\r
216UINTN\r
217PageAttributeToLength (\r
218 IN PAGE_ATTRIBUTE PageAttribute\r
219 )\r
220{\r
221 UINTN Index;\r
222 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
223 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
224 return (UINTN)mPageAttributeTable[Index].Length;\r
225 }\r
226 }\r
227 return 0;\r
228}\r
229\r
230/**\r
231 Return address mask according to page attributes.\r
232\r
233 @param[in] PageAttributes The page attribute of the page entry.\r
234\r
235 @return The address mask of page entry.\r
236**/\r
237UINTN\r
238PageAttributeToMask (\r
239 IN PAGE_ATTRIBUTE PageAttribute\r
240 )\r
241{\r
242 UINTN Index;\r
243 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
244 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
245 return (UINTN)mPageAttributeTable[Index].AddressMask;\r
246 }\r
247 }\r
248 return 0;\r
249}\r
250\r
251/**\r
252 Return page table entry to match the address.\r
253\r
254 @param[in] PagingContext The paging context.\r
255 @param[in] Address The address to be checked.\r
256 @param[out] PageAttributes The page attribute of the page entry.\r
257\r
258 @return The page entry.\r
259**/\r
260VOID *\r
261GetPageTableEntry (\r
262 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,\r
263 IN PHYSICAL_ADDRESS Address,\r
264 OUT PAGE_ATTRIBUTE *PageAttribute\r
265 )\r
266{\r
267 UINTN Index1;\r
268 UINTN Index2;\r
269 UINTN Index3;\r
270 UINTN Index4;\r
271 UINT64 *L1PageTable;\r
272 UINT64 *L2PageTable;\r
273 UINT64 *L3PageTable;\r
274 UINT64 *L4PageTable;\r
627dcba3 275 UINT64 AddressEncMask;\r
22292ed3
JY
276\r
277 ASSERT (PagingContext != NULL);\r
278\r
279 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;\r
280 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;\r
281 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;\r
282 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;\r
283\r
627dcba3
LD
284 // Make sure AddressEncMask is contained to smallest supported address field.\r
285 //\r
286 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
287\r
22292ed3
JY
288 if (PagingContext->MachineType == IMAGE_FILE_MACHINE_X64) {\r
289 L4PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.X64.PageTableBase;\r
290 if (L4PageTable[Index4] == 0) {\r
291 *PageAttribute = PageNone;\r
292 return NULL;\r
293 }\r
294\r
627dcba3 295 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
296 } else {\r
297 ASSERT((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0);\r
298 L3PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.Ia32.PageTableBase;\r
299 }\r
300 if (L3PageTable[Index3] == 0) {\r
301 *PageAttribute = PageNone;\r
302 return NULL;\r
303 }\r
304 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
305 // 1G\r
306 *PageAttribute = Page1G;\r
307 return &L3PageTable[Index3];\r
308 }\r
309\r
627dcba3 310 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
311 if (L2PageTable[Index2] == 0) {\r
312 *PageAttribute = PageNone;\r
313 return NULL;\r
314 }\r
315 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
316 // 2M\r
317 *PageAttribute = Page2M;\r
318 return &L2PageTable[Index2];\r
319 }\r
320\r
321 // 4k\r
627dcba3 322 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
323 if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
324 *PageAttribute = PageNone;\r
325 return NULL;\r
326 }\r
327 *PageAttribute = Page4K;\r
328 return &L1PageTable[Index1];\r
329}\r
330\r
331/**\r
332 Return memory attributes of page entry.\r
333\r
334 @param[in] PageEntry The page entry.\r
335\r
336 @return Memory attributes of page entry.\r
337**/\r
338UINT64\r
339GetAttributesFromPageEntry (\r
340 IN UINT64 *PageEntry\r
341 )\r
342{\r
343 UINT64 Attributes;\r
344 Attributes = 0;\r
345 if ((*PageEntry & IA32_PG_P) == 0) {\r
346 Attributes |= EFI_MEMORY_RP;\r
347 }\r
348 if ((*PageEntry & IA32_PG_RW) == 0) {\r
349 Attributes |= EFI_MEMORY_RO;\r
350 }\r
351 if ((*PageEntry & IA32_PG_NX) != 0) {\r
352 Attributes |= EFI_MEMORY_XP;\r
353 }\r
354 return Attributes;\r
355}\r
356\r
357/**\r
358 Modify memory attributes of page entry.\r
359\r
360 @param[in] PagingContext The paging context.\r
361 @param[in] PageEntry The page entry.\r
362 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
363 @param[in] PageAction The page action.\r
364 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
365**/\r
366VOID\r
367ConvertPageEntryAttribute (\r
368 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,\r
369 IN UINT64 *PageEntry,\r
370 IN UINT64 Attributes,\r
371 IN PAGE_ACTION PageAction,\r
372 OUT BOOLEAN *IsModified\r
373 )\r
374{\r
375 UINT64 CurrentPageEntry;\r
376 UINT64 NewPageEntry;\r
377\r
378 CurrentPageEntry = *PageEntry;\r
379 NewPageEntry = CurrentPageEntry;\r
380 if ((Attributes & EFI_MEMORY_RP) != 0) {\r
381 switch (PageAction) {\r
382 case PageActionAssign:\r
383 case PageActionSet:\r
384 NewPageEntry &= ~(UINT64)IA32_PG_P;\r
385 break;\r
386 case PageActionClear:\r
387 NewPageEntry |= IA32_PG_P;\r
388 break;\r
389 }\r
390 } else {\r
391 switch (PageAction) {\r
392 case PageActionAssign:\r
393 NewPageEntry |= IA32_PG_P;\r
394 break;\r
395 case PageActionSet:\r
396 case PageActionClear:\r
397 break;\r
398 }\r
399 }\r
400 if ((Attributes & EFI_MEMORY_RO) != 0) {\r
401 switch (PageAction) {\r
402 case PageActionAssign:\r
403 case PageActionSet:\r
404 NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
405 break;\r
406 case PageActionClear:\r
407 NewPageEntry |= IA32_PG_RW;\r
408 break;\r
409 }\r
410 } else {\r
411 switch (PageAction) {\r
412 case PageActionAssign:\r
413 NewPageEntry |= IA32_PG_RW;\r
414 break;\r
415 case PageActionSet:\r
416 case PageActionClear:\r
417 break;\r
418 }\r
419 }\r
420 if ((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED) != 0) {\r
421 if ((Attributes & EFI_MEMORY_XP) != 0) {\r
422 switch (PageAction) {\r
423 case PageActionAssign:\r
424 case PageActionSet:\r
425 NewPageEntry |= IA32_PG_NX;\r
426 break;\r
427 case PageActionClear:\r
428 NewPageEntry &= ~IA32_PG_NX;\r
429 break;\r
430 }\r
431 } else {\r
432 switch (PageAction) {\r
433 case PageActionAssign:\r
434 NewPageEntry &= ~IA32_PG_NX;\r
435 break;\r
436 case PageActionSet:\r
437 case PageActionClear:\r
438 break;\r
439 }\r
440 }\r
441 }\r
442 *PageEntry = NewPageEntry;\r
443 if (CurrentPageEntry != NewPageEntry) {\r
444 *IsModified = TRUE;\r
445 DEBUG ((DEBUG_INFO, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
446 DEBUG ((DEBUG_INFO, "->0x%lx\n", NewPageEntry));\r
447 } else {\r
448 *IsModified = FALSE;\r
449 }\r
450}\r
451\r
452/**\r
453 This function returns if there is need to split page entry.\r
454\r
455 @param[in] BaseAddress The base address to be checked.\r
456 @param[in] Length The length to be checked.\r
457 @param[in] PageEntry The page entry to be checked.\r
458 @param[in] PageAttribute The page attribute of the page entry.\r
459\r
460 @retval SplitAttributes on if there is need to split page entry.\r
461**/\r
462PAGE_ATTRIBUTE\r
463NeedSplitPage (\r
464 IN PHYSICAL_ADDRESS BaseAddress,\r
465 IN UINT64 Length,\r
466 IN UINT64 *PageEntry,\r
467 IN PAGE_ATTRIBUTE PageAttribute\r
468 )\r
469{\r
470 UINT64 PageEntryLength;\r
471\r
472 PageEntryLength = PageAttributeToLength (PageAttribute);\r
473\r
474 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
475 return PageNone;\r
476 }\r
477\r
478 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
479 return Page4K;\r
480 }\r
481\r
482 return Page2M;\r
483}\r
484\r
485/**\r
486 This function splits one page entry to small page entries.\r
487\r
488 @param[in] PageEntry The page entry to be splitted.\r
489 @param[in] PageAttribute The page attribute of the page entry.\r
490 @param[in] SplitAttribute How to split the page entry.\r
491 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
492\r
493 @retval RETURN_SUCCESS The page entry is splitted.\r
494 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.\r
495 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.\r
496**/\r
497RETURN_STATUS\r
498SplitPage (\r
499 IN UINT64 *PageEntry,\r
500 IN PAGE_ATTRIBUTE PageAttribute,\r
501 IN PAGE_ATTRIBUTE SplitAttribute,\r
502 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc\r
503 )\r
504{\r
505 UINT64 BaseAddress;\r
506 UINT64 *NewPageEntry;\r
507 UINTN Index;\r
627dcba3 508 UINT64 AddressEncMask;\r
22292ed3
JY
509\r
510 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
511\r
512 ASSERT (AllocatePagesFunc != NULL);\r
513\r
627dcba3
LD
514 // Make sure AddressEncMask is contained to smallest supported address field.\r
515 //\r
516 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
517\r
22292ed3
JY
518 if (PageAttribute == Page2M) {\r
519 //\r
520 // Split 2M to 4K\r
521 //\r
522 ASSERT (SplitAttribute == Page4K);\r
523 if (SplitAttribute == Page4K) {\r
524 NewPageEntry = AllocatePagesFunc (1);\r
525 DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));\r
526 if (NewPageEntry == NULL) {\r
527 return RETURN_OUT_OF_RESOURCES;\r
528 }\r
627dcba3 529 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_2M_ADDRESS_MASK_64;\r
22292ed3 530 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
627dcba3 531 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3 532 }\r
627dcba3 533 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3
JY
534 return RETURN_SUCCESS;\r
535 } else {\r
536 return RETURN_UNSUPPORTED;\r
537 }\r
538 } else if (PageAttribute == Page1G) {\r
539 //\r
540 // Split 1G to 2M\r
541 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
542 //\r
543 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
544 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {\r
545 NewPageEntry = AllocatePagesFunc (1);\r
546 DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));\r
547 if (NewPageEntry == NULL) {\r
548 return RETURN_OUT_OF_RESOURCES;\r
549 }\r
627dcba3 550 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_1G_ADDRESS_MASK_64;\r
22292ed3 551 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
627dcba3 552 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3 553 }\r
627dcba3 554 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3
JY
555 return RETURN_SUCCESS;\r
556 } else {\r
557 return RETURN_UNSUPPORTED;\r
558 }\r
559 } else {\r
560 return RETURN_UNSUPPORTED;\r
561 }\r
562}\r
563\r
564/**\r
565 This function modifies the page attributes for the memory region specified by BaseAddress and\r
566 Length from their current attributes to the attributes specified by Attributes.\r
567\r
568 Caller should make sure BaseAddress and Length is at page boundary.\r
569\r
570 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
571 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
572 @param[in] Length The size in bytes of the memory region.\r
573 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
574 @param[in] PageAction The page action.\r
575 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
576 NULL mean page split is unsupported.\r
577 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
578 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
579\r
580 @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
581 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
582 BaseAddress and Length cannot be modified.\r
583 @retval RETURN_INVALID_PARAMETER Length is zero.\r
584 Attributes specified an illegal combination of attributes that\r
585 cannot be set together.\r
586 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
587 the memory resource range.\r
588 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
589 resource range specified by BaseAddress and Length.\r
590 The bit mask of attributes is not support for the memory resource\r
591 range specified by BaseAddress and Length.\r
592**/\r
593RETURN_STATUS\r
594ConvertMemoryPageAttributes (\r
595 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
596 IN PHYSICAL_ADDRESS BaseAddress,\r
597 IN UINT64 Length,\r
598 IN UINT64 Attributes,\r
599 IN PAGE_ACTION PageAction,\r
600 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL,\r
601 OUT BOOLEAN *IsSplitted, OPTIONAL\r
602 OUT BOOLEAN *IsModified OPTIONAL\r
603 )\r
604{\r
605 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
606 UINT64 *PageEntry;\r
607 PAGE_ATTRIBUTE PageAttribute;\r
608 UINTN PageEntryLength;\r
609 PAGE_ATTRIBUTE SplitAttribute;\r
610 RETURN_STATUS Status;\r
611 BOOLEAN IsEntryModified;\r
612\r
613 if ((BaseAddress & (SIZE_4KB - 1)) != 0) {\r
614 DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress));\r
615 return EFI_UNSUPPORTED;\r
616 }\r
617 if ((Length & (SIZE_4KB - 1)) != 0) {\r
618 DEBUG ((DEBUG_ERROR, "Length(0x%lx) is not aligned!\n", Length));\r
619 return EFI_UNSUPPORTED;\r
620 }\r
621 if (Length == 0) {\r
622 DEBUG ((DEBUG_ERROR, "Length is 0!\n"));\r
623 return RETURN_INVALID_PARAMETER;\r
624 }\r
625\r
626 if ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) != 0) {\r
627 DEBUG ((DEBUG_ERROR, "Attributes(0x%lx) has unsupported bit\n", Attributes));\r
628 return EFI_UNSUPPORTED;\r
629 }\r
630\r
631 if (PagingContext == NULL) {\r
632 GetCurrentPagingContext (&CurrentPagingContext);\r
633 } else {\r
634 CopyMem (&CurrentPagingContext, PagingContext, sizeof(CurrentPagingContext));\r
635 }\r
636 switch(CurrentPagingContext.MachineType) {\r
637 case IMAGE_FILE_MACHINE_I386:\r
638 if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) {\r
22292ed3
JY
639 if (Attributes == 0) {\r
640 return EFI_SUCCESS;\r
641 } else {\r
c5719579 642 DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));\r
22292ed3
JY
643 return EFI_UNSUPPORTED;\r
644 }\r
645 }\r
646 if ((CurrentPagingContext.ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) {\r
647 DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));\r
648 return EFI_UNSUPPORTED;\r
649 }\r
650 break;\r
651 case IMAGE_FILE_MACHINE_X64:\r
652 ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);\r
653 break;\r
654 default:\r
655 ASSERT(FALSE);\r
656 return EFI_UNSUPPORTED;\r
657 break;\r
658 }\r
659\r
660// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
661\r
662 if (IsSplitted != NULL) {\r
663 *IsSplitted = FALSE;\r
664 }\r
665 if (IsModified != NULL) {\r
666 *IsModified = FALSE;\r
667 }\r
668\r
669 //\r
670 // Below logic is to check 2M/4K page to make sure we donot waist memory.\r
671 //\r
672 while (Length != 0) {\r
673 PageEntry = GetPageTableEntry (&CurrentPagingContext, BaseAddress, &PageAttribute);\r
674 if (PageEntry == NULL) {\r
675 return RETURN_UNSUPPORTED;\r
676 }\r
677 PageEntryLength = PageAttributeToLength (PageAttribute);\r
678 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
679 if (SplitAttribute == PageNone) {\r
680 ConvertPageEntryAttribute (&CurrentPagingContext, PageEntry, Attributes, PageAction, &IsEntryModified);\r
681 if (IsEntryModified) {\r
682 if (IsModified != NULL) {\r
683 *IsModified = TRUE;\r
684 }\r
685 }\r
686 //\r
687 // Convert success, move to next\r
688 //\r
689 BaseAddress += PageEntryLength;\r
690 Length -= PageEntryLength;\r
691 } else {\r
692 if (AllocatePagesFunc == NULL) {\r
693 return RETURN_UNSUPPORTED;\r
694 }\r
695 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute, AllocatePagesFunc);\r
696 if (RETURN_ERROR (Status)) {\r
697 return RETURN_UNSUPPORTED;\r
698 }\r
699 if (IsSplitted != NULL) {\r
700 *IsSplitted = TRUE;\r
701 }\r
702 if (IsModified != NULL) {\r
703 *IsModified = TRUE;\r
704 }\r
705 //\r
706 // Just split current page\r
707 // Convert success in next around\r
708 //\r
709 }\r
710 }\r
711\r
712 return RETURN_SUCCESS;\r
713}\r
714\r
715/**\r
716 This function assigns the page attributes for the memory region specified by BaseAddress and\r
717 Length from their current attributes to the attributes specified by Attributes.\r
718\r
719 Caller should make sure BaseAddress and Length is at page boundary.\r
720\r
721 Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.\r
722\r
723 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
724 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
725 @param[in] Length The size in bytes of the memory region.\r
726 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
727 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
728 NULL mean page split is unsupported.\r
729\r
730 @retval RETURN_SUCCESS The attributes were cleared for the memory region.\r
731 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
732 BaseAddress and Length cannot be modified.\r
733 @retval RETURN_INVALID_PARAMETER Length is zero.\r
734 Attributes specified an illegal combination of attributes that\r
735 cannot be set together.\r
736 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
737 the memory resource range.\r
738 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
739 resource range specified by BaseAddress and Length.\r
740 The bit mask of attributes is not support for the memory resource\r
741 range specified by BaseAddress and Length.\r
742**/\r
743RETURN_STATUS\r
744EFIAPI\r
745AssignMemoryPageAttributes (\r
746 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
747 IN PHYSICAL_ADDRESS BaseAddress,\r
748 IN UINT64 Length,\r
749 IN UINT64 Attributes,\r
750 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL\r
751 )\r
752{\r
753 RETURN_STATUS Status;\r
754 BOOLEAN IsModified;\r
755 BOOLEAN IsSplitted;\r
756\r
757// DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));\r
758 Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress, Length, Attributes, PageActionAssign, AllocatePagesFunc, &IsSplitted, &IsModified);\r
759 if (!EFI_ERROR(Status)) {\r
760 if ((PagingContext == NULL) && IsModified) {\r
761 //\r
762 // Flush TLB as last step\r
763 //\r
764 CpuFlushTlb();\r
765 SyncMemoryPageAttributesAp (SyncCpuFlushTlb);\r
766 }\r
767 }\r
768\r
769 return Status;\r
770}\r
771\r
c1cab54c
JW
772/**\r
773 Update GCD memory space attributes according to current page table setup.\r
774**/\r
775VOID\r
776RefreshGcdMemoryAttributesFromPaging (\r
777 VOID\r
778 )\r
779{\r
780 EFI_STATUS Status;\r
781 UINTN NumberOfDescriptors;\r
782 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
783 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;\r
784 PAGE_ATTRIBUTE PageAttribute;\r
785 UINT64 *PageEntry;\r
786 UINT64 PageLength;\r
787 UINT64 MemorySpaceLength;\r
788 UINT64 Length;\r
789 UINT64 BaseAddress;\r
790 UINT64 PageStartAddress;\r
791 UINT64 Attributes;\r
792 UINT64 Capabilities;\r
793 BOOLEAN DoUpdate;\r
794 UINTN Index;\r
795\r
796 //\r
797 // Assuming that memory space map returned is sorted already; otherwise sort\r
798 // them in the order of lowest address to highest address.\r
799 //\r
800 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
801 ASSERT_EFI_ERROR (Status);\r
802\r
803 GetCurrentPagingContext (&PagingContext);\r
804\r
805 BaseAddress = 0;\r
806 PageLength = 0;\r
807 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
808 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
809 continue;\r
810 }\r
811\r
812 if (MemorySpaceMap[Index].BaseAddress >= (BaseAddress + PageLength)) {\r
813 //\r
814 // Current memory space starts at a new page. Resetting PageLength will\r
815 // trigger a retrieval of page attributes at new address.\r
816 //\r
817 PageLength = 0;\r
818 } else {\r
819 //\r
820 // In case current memory space is not adjacent to last one\r
821 //\r
822 PageLength -= (MemorySpaceMap[Index].BaseAddress - BaseAddress);\r
823 }\r
824\r
825 // Sync real page attributes to GCD\r
826 BaseAddress = MemorySpaceMap[Index].BaseAddress;\r
827 MemorySpaceLength = MemorySpaceMap[Index].Length;\r
828 while (MemorySpaceLength > 0) {\r
829 if (PageLength == 0) {\r
830 PageEntry = GetPageTableEntry (&PagingContext, BaseAddress, &PageAttribute);\r
831 if (PageEntry == NULL) {\r
832 break;\r
833 }\r
834\r
835 //\r
836 // Note current memory space might start in the middle of a page\r
837 //\r
838 PageStartAddress = (*PageEntry) & (UINT64)PageAttributeToMask(PageAttribute);\r
839 PageLength = PageAttributeToLength (PageAttribute) - (BaseAddress - PageStartAddress);\r
840 Attributes = GetAttributesFromPageEntry (PageEntry);\r
841\r
842 if (Attributes != (MemorySpaceMap[Index].Attributes & EFI_MEMORY_PAGETYPE_MASK)) {\r
843 DoUpdate = TRUE;\r
844 Attributes |= (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_PAGETYPE_MASK);\r
845 Capabilities = Attributes | MemorySpaceMap[Index].Capabilities;\r
846 } else {\r
847 DoUpdate = FALSE;\r
848 }\r
849 }\r
850\r
851 Length = MIN (PageLength, MemorySpaceLength);\r
852 if (DoUpdate) {\r
853 gDS->SetMemorySpaceCapabilities (BaseAddress, Length, Capabilities);\r
854 gDS->SetMemorySpaceAttributes (BaseAddress, Length, Attributes);\r
855 DEBUG ((DEBUG_INFO, "Update memory space attribute: [%02d] %016lx - %016lx (%08lx -> %08lx)\r\n",\r
856 Index, BaseAddress, BaseAddress + Length - 1,\r
857 MemorySpaceMap[Index].Attributes, Attributes));\r
858 }\r
859\r
860 PageLength -= Length;\r
861 MemorySpaceLength -= Length;\r
862 BaseAddress += Length;\r
863 }\r
864 }\r
865\r
866 FreePool (MemorySpaceMap);\r
867}\r
868\r
22292ed3
JY
869/**\r
870 Initialize the Page Table lib.\r
871**/\r
872VOID\r
873InitializePageTableLib (\r
874 VOID\r
875 )\r
876{\r
877 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
878\r
879 GetCurrentPagingContext (&CurrentPagingContext);\r
880 DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));\r
881 DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType));\r
882 DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));\r
883 DEBUG ((DEBUG_INFO, " Attributes - 0x%x\n", CurrentPagingContext.ContextData.X64.Attributes));\r
884\r
885 return ;\r
886}\r
887\r