UefiCpuPkg/CpuDxe: Add support for PCD PcdPteMemoryEncryptionAddressOrMask
[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
196 if ((AsmReadMsr64 (0x000001A0) & BIT34) == 0) {\r
197 // XD enabled\r
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
201 }\r
202 }\r
203 }\r
204 if ((RegEdx & BIT26) != 0) {\r
205 PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;\r
206 }\r
207 }\r
208}\r
209\r
210/**\r
211 Return length according to page attributes.\r
212\r
213 @param[in] PageAttributes The page attribute of the page entry.\r
214\r
215 @return The length of page entry.\r
216**/\r
217UINTN\r
218PageAttributeToLength (\r
219 IN PAGE_ATTRIBUTE PageAttribute\r
220 )\r
221{\r
222 UINTN Index;\r
223 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
224 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
225 return (UINTN)mPageAttributeTable[Index].Length;\r
226 }\r
227 }\r
228 return 0;\r
229}\r
230\r
231/**\r
232 Return address mask according to page attributes.\r
233\r
234 @param[in] PageAttributes The page attribute of the page entry.\r
235\r
236 @return The address mask of page entry.\r
237**/\r
238UINTN\r
239PageAttributeToMask (\r
240 IN PAGE_ATTRIBUTE PageAttribute\r
241 )\r
242{\r
243 UINTN Index;\r
244 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
245 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
246 return (UINTN)mPageAttributeTable[Index].AddressMask;\r
247 }\r
248 }\r
249 return 0;\r
250}\r
251\r
252/**\r
253 Return page table entry to match the address.\r
254\r
255 @param[in] PagingContext The paging context.\r
256 @param[in] Address The address to be checked.\r
257 @param[out] PageAttributes The page attribute of the page entry.\r
258\r
259 @return The page entry.\r
260**/\r
261VOID *\r
262GetPageTableEntry (\r
263 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,\r
264 IN PHYSICAL_ADDRESS Address,\r
265 OUT PAGE_ATTRIBUTE *PageAttribute\r
266 )\r
267{\r
268 UINTN Index1;\r
269 UINTN Index2;\r
270 UINTN Index3;\r
271 UINTN Index4;\r
272 UINT64 *L1PageTable;\r
273 UINT64 *L2PageTable;\r
274 UINT64 *L3PageTable;\r
275 UINT64 *L4PageTable;\r
627dcba3 276 UINT64 AddressEncMask;\r
22292ed3
JY
277\r
278 ASSERT (PagingContext != NULL);\r
279\r
280 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;\r
281 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;\r
282 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;\r
283 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;\r
284\r
627dcba3
LD
285 // Make sure AddressEncMask is contained to smallest supported address field.\r
286 //\r
287 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
288\r
22292ed3
JY
289 if (PagingContext->MachineType == IMAGE_FILE_MACHINE_X64) {\r
290 L4PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.X64.PageTableBase;\r
291 if (L4PageTable[Index4] == 0) {\r
292 *PageAttribute = PageNone;\r
293 return NULL;\r
294 }\r
295\r
627dcba3 296 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
297 } else {\r
298 ASSERT((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0);\r
299 L3PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.Ia32.PageTableBase;\r
300 }\r
301 if (L3PageTable[Index3] == 0) {\r
302 *PageAttribute = PageNone;\r
303 return NULL;\r
304 }\r
305 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
306 // 1G\r
307 *PageAttribute = Page1G;\r
308 return &L3PageTable[Index3];\r
309 }\r
310\r
627dcba3 311 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
312 if (L2PageTable[Index2] == 0) {\r
313 *PageAttribute = PageNone;\r
314 return NULL;\r
315 }\r
316 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
317 // 2M\r
318 *PageAttribute = Page2M;\r
319 return &L2PageTable[Index2];\r
320 }\r
321\r
322 // 4k\r
627dcba3 323 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
324 if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
325 *PageAttribute = PageNone;\r
326 return NULL;\r
327 }\r
328 *PageAttribute = Page4K;\r
329 return &L1PageTable[Index1];\r
330}\r
331\r
332/**\r
333 Return memory attributes of page entry.\r
334\r
335 @param[in] PageEntry The page entry.\r
336\r
337 @return Memory attributes of page entry.\r
338**/\r
339UINT64\r
340GetAttributesFromPageEntry (\r
341 IN UINT64 *PageEntry\r
342 )\r
343{\r
344 UINT64 Attributes;\r
345 Attributes = 0;\r
346 if ((*PageEntry & IA32_PG_P) == 0) {\r
347 Attributes |= EFI_MEMORY_RP;\r
348 }\r
349 if ((*PageEntry & IA32_PG_RW) == 0) {\r
350 Attributes |= EFI_MEMORY_RO;\r
351 }\r
352 if ((*PageEntry & IA32_PG_NX) != 0) {\r
353 Attributes |= EFI_MEMORY_XP;\r
354 }\r
355 return Attributes;\r
356}\r
357\r
358/**\r
359 Modify memory attributes of page entry.\r
360\r
361 @param[in] PagingContext The paging context.\r
362 @param[in] PageEntry The page entry.\r
363 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
364 @param[in] PageAction The page action.\r
365 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
366**/\r
367VOID\r
368ConvertPageEntryAttribute (\r
369 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,\r
370 IN UINT64 *PageEntry,\r
371 IN UINT64 Attributes,\r
372 IN PAGE_ACTION PageAction,\r
373 OUT BOOLEAN *IsModified\r
374 )\r
375{\r
376 UINT64 CurrentPageEntry;\r
377 UINT64 NewPageEntry;\r
378\r
379 CurrentPageEntry = *PageEntry;\r
380 NewPageEntry = CurrentPageEntry;\r
381 if ((Attributes & EFI_MEMORY_RP) != 0) {\r
382 switch (PageAction) {\r
383 case PageActionAssign:\r
384 case PageActionSet:\r
385 NewPageEntry &= ~(UINT64)IA32_PG_P;\r
386 break;\r
387 case PageActionClear:\r
388 NewPageEntry |= IA32_PG_P;\r
389 break;\r
390 }\r
391 } else {\r
392 switch (PageAction) {\r
393 case PageActionAssign:\r
394 NewPageEntry |= IA32_PG_P;\r
395 break;\r
396 case PageActionSet:\r
397 case PageActionClear:\r
398 break;\r
399 }\r
400 }\r
401 if ((Attributes & EFI_MEMORY_RO) != 0) {\r
402 switch (PageAction) {\r
403 case PageActionAssign:\r
404 case PageActionSet:\r
405 NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
406 break;\r
407 case PageActionClear:\r
408 NewPageEntry |= IA32_PG_RW;\r
409 break;\r
410 }\r
411 } else {\r
412 switch (PageAction) {\r
413 case PageActionAssign:\r
414 NewPageEntry |= IA32_PG_RW;\r
415 break;\r
416 case PageActionSet:\r
417 case PageActionClear:\r
418 break;\r
419 }\r
420 }\r
421 if ((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED) != 0) {\r
422 if ((Attributes & EFI_MEMORY_XP) != 0) {\r
423 switch (PageAction) {\r
424 case PageActionAssign:\r
425 case PageActionSet:\r
426 NewPageEntry |= IA32_PG_NX;\r
427 break;\r
428 case PageActionClear:\r
429 NewPageEntry &= ~IA32_PG_NX;\r
430 break;\r
431 }\r
432 } else {\r
433 switch (PageAction) {\r
434 case PageActionAssign:\r
435 NewPageEntry &= ~IA32_PG_NX;\r
436 break;\r
437 case PageActionSet:\r
438 case PageActionClear:\r
439 break;\r
440 }\r
441 }\r
442 }\r
443 *PageEntry = NewPageEntry;\r
444 if (CurrentPageEntry != NewPageEntry) {\r
445 *IsModified = TRUE;\r
446 DEBUG ((DEBUG_INFO, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
447 DEBUG ((DEBUG_INFO, "->0x%lx\n", NewPageEntry));\r
448 } else {\r
449 *IsModified = FALSE;\r
450 }\r
451}\r
452\r
453/**\r
454 This function returns if there is need to split page entry.\r
455\r
456 @param[in] BaseAddress The base address to be checked.\r
457 @param[in] Length The length to be checked.\r
458 @param[in] PageEntry The page entry to be checked.\r
459 @param[in] PageAttribute The page attribute of the page entry.\r
460\r
461 @retval SplitAttributes on if there is need to split page entry.\r
462**/\r
463PAGE_ATTRIBUTE\r
464NeedSplitPage (\r
465 IN PHYSICAL_ADDRESS BaseAddress,\r
466 IN UINT64 Length,\r
467 IN UINT64 *PageEntry,\r
468 IN PAGE_ATTRIBUTE PageAttribute\r
469 )\r
470{\r
471 UINT64 PageEntryLength;\r
472\r
473 PageEntryLength = PageAttributeToLength (PageAttribute);\r
474\r
475 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
476 return PageNone;\r
477 }\r
478\r
479 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
480 return Page4K;\r
481 }\r
482\r
483 return Page2M;\r
484}\r
485\r
486/**\r
487 This function splits one page entry to small page entries.\r
488\r
489 @param[in] PageEntry The page entry to be splitted.\r
490 @param[in] PageAttribute The page attribute of the page entry.\r
491 @param[in] SplitAttribute How to split the page entry.\r
492 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
493\r
494 @retval RETURN_SUCCESS The page entry is splitted.\r
495 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.\r
496 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.\r
497**/\r
498RETURN_STATUS\r
499SplitPage (\r
500 IN UINT64 *PageEntry,\r
501 IN PAGE_ATTRIBUTE PageAttribute,\r
502 IN PAGE_ATTRIBUTE SplitAttribute,\r
503 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc\r
504 )\r
505{\r
506 UINT64 BaseAddress;\r
507 UINT64 *NewPageEntry;\r
508 UINTN Index;\r
627dcba3 509 UINT64 AddressEncMask;\r
22292ed3
JY
510\r
511 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
512\r
513 ASSERT (AllocatePagesFunc != NULL);\r
514\r
627dcba3
LD
515 // Make sure AddressEncMask is contained to smallest supported address field.\r
516 //\r
517 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
518\r
22292ed3
JY
519 if (PageAttribute == Page2M) {\r
520 //\r
521 // Split 2M to 4K\r
522 //\r
523 ASSERT (SplitAttribute == Page4K);\r
524 if (SplitAttribute == Page4K) {\r
525 NewPageEntry = AllocatePagesFunc (1);\r
526 DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));\r
527 if (NewPageEntry == NULL) {\r
528 return RETURN_OUT_OF_RESOURCES;\r
529 }\r
627dcba3 530 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_2M_ADDRESS_MASK_64;\r
22292ed3 531 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
627dcba3 532 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3 533 }\r
627dcba3 534 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3
JY
535 return RETURN_SUCCESS;\r
536 } else {\r
537 return RETURN_UNSUPPORTED;\r
538 }\r
539 } else if (PageAttribute == Page1G) {\r
540 //\r
541 // Split 1G to 2M\r
542 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
543 //\r
544 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
545 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {\r
546 NewPageEntry = AllocatePagesFunc (1);\r
547 DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));\r
548 if (NewPageEntry == NULL) {\r
549 return RETURN_OUT_OF_RESOURCES;\r
550 }\r
627dcba3 551 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_1G_ADDRESS_MASK_64;\r
22292ed3 552 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
627dcba3 553 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3 554 }\r
627dcba3 555 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3
JY
556 return RETURN_SUCCESS;\r
557 } else {\r
558 return RETURN_UNSUPPORTED;\r
559 }\r
560 } else {\r
561 return RETURN_UNSUPPORTED;\r
562 }\r
563}\r
564\r
565/**\r
566 This function modifies the page attributes for the memory region specified by BaseAddress and\r
567 Length from their current attributes to the attributes specified by Attributes.\r
568\r
569 Caller should make sure BaseAddress and Length is at page boundary.\r
570\r
571 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
572 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
573 @param[in] Length The size in bytes of the memory region.\r
574 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
575 @param[in] PageAction The page action.\r
576 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
577 NULL mean page split is unsupported.\r
578 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
579 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
580\r
581 @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
582 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
583 BaseAddress and Length cannot be modified.\r
584 @retval RETURN_INVALID_PARAMETER Length is zero.\r
585 Attributes specified an illegal combination of attributes that\r
586 cannot be set together.\r
587 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
588 the memory resource range.\r
589 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
590 resource range specified by BaseAddress and Length.\r
591 The bit mask of attributes is not support for the memory resource\r
592 range specified by BaseAddress and Length.\r
593**/\r
594RETURN_STATUS\r
595ConvertMemoryPageAttributes (\r
596 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
597 IN PHYSICAL_ADDRESS BaseAddress,\r
598 IN UINT64 Length,\r
599 IN UINT64 Attributes,\r
600 IN PAGE_ACTION PageAction,\r
601 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL,\r
602 OUT BOOLEAN *IsSplitted, OPTIONAL\r
603 OUT BOOLEAN *IsModified OPTIONAL\r
604 )\r
605{\r
606 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
607 UINT64 *PageEntry;\r
608 PAGE_ATTRIBUTE PageAttribute;\r
609 UINTN PageEntryLength;\r
610 PAGE_ATTRIBUTE SplitAttribute;\r
611 RETURN_STATUS Status;\r
612 BOOLEAN IsEntryModified;\r
613\r
614 if ((BaseAddress & (SIZE_4KB - 1)) != 0) {\r
615 DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress));\r
616 return EFI_UNSUPPORTED;\r
617 }\r
618 if ((Length & (SIZE_4KB - 1)) != 0) {\r
619 DEBUG ((DEBUG_ERROR, "Length(0x%lx) is not aligned!\n", Length));\r
620 return EFI_UNSUPPORTED;\r
621 }\r
622 if (Length == 0) {\r
623 DEBUG ((DEBUG_ERROR, "Length is 0!\n"));\r
624 return RETURN_INVALID_PARAMETER;\r
625 }\r
626\r
627 if ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) != 0) {\r
628 DEBUG ((DEBUG_ERROR, "Attributes(0x%lx) has unsupported bit\n", Attributes));\r
629 return EFI_UNSUPPORTED;\r
630 }\r
631\r
632 if (PagingContext == NULL) {\r
633 GetCurrentPagingContext (&CurrentPagingContext);\r
634 } else {\r
635 CopyMem (&CurrentPagingContext, PagingContext, sizeof(CurrentPagingContext));\r
636 }\r
637 switch(CurrentPagingContext.MachineType) {\r
638 case IMAGE_FILE_MACHINE_I386:\r
639 if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) {\r
640 DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));\r
641 if (Attributes == 0) {\r
642 return EFI_SUCCESS;\r
643 } else {\r
644 return EFI_UNSUPPORTED;\r
645 }\r
646 }\r
647 if ((CurrentPagingContext.ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) {\r
648 DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));\r
649 return EFI_UNSUPPORTED;\r
650 }\r
651 break;\r
652 case IMAGE_FILE_MACHINE_X64:\r
653 ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);\r
654 break;\r
655 default:\r
656 ASSERT(FALSE);\r
657 return EFI_UNSUPPORTED;\r
658 break;\r
659 }\r
660\r
661// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
662\r
663 if (IsSplitted != NULL) {\r
664 *IsSplitted = FALSE;\r
665 }\r
666 if (IsModified != NULL) {\r
667 *IsModified = FALSE;\r
668 }\r
669\r
670 //\r
671 // Below logic is to check 2M/4K page to make sure we donot waist memory.\r
672 //\r
673 while (Length != 0) {\r
674 PageEntry = GetPageTableEntry (&CurrentPagingContext, BaseAddress, &PageAttribute);\r
675 if (PageEntry == NULL) {\r
676 return RETURN_UNSUPPORTED;\r
677 }\r
678 PageEntryLength = PageAttributeToLength (PageAttribute);\r
679 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
680 if (SplitAttribute == PageNone) {\r
681 ConvertPageEntryAttribute (&CurrentPagingContext, PageEntry, Attributes, PageAction, &IsEntryModified);\r
682 if (IsEntryModified) {\r
683 if (IsModified != NULL) {\r
684 *IsModified = TRUE;\r
685 }\r
686 }\r
687 //\r
688 // Convert success, move to next\r
689 //\r
690 BaseAddress += PageEntryLength;\r
691 Length -= PageEntryLength;\r
692 } else {\r
693 if (AllocatePagesFunc == NULL) {\r
694 return RETURN_UNSUPPORTED;\r
695 }\r
696 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute, AllocatePagesFunc);\r
697 if (RETURN_ERROR (Status)) {\r
698 return RETURN_UNSUPPORTED;\r
699 }\r
700 if (IsSplitted != NULL) {\r
701 *IsSplitted = TRUE;\r
702 }\r
703 if (IsModified != NULL) {\r
704 *IsModified = TRUE;\r
705 }\r
706 //\r
707 // Just split current page\r
708 // Convert success in next around\r
709 //\r
710 }\r
711 }\r
712\r
713 return RETURN_SUCCESS;\r
714}\r
715\r
716/**\r
717 This function assigns the page attributes for the memory region specified by BaseAddress and\r
718 Length from their current attributes to the attributes specified by Attributes.\r
719\r
720 Caller should make sure BaseAddress and Length is at page boundary.\r
721\r
722 Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.\r
723\r
724 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
725 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
726 @param[in] Length The size in bytes of the memory region.\r
727 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
728 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
729 NULL mean page split is unsupported.\r
730\r
731 @retval RETURN_SUCCESS The attributes were cleared for the memory region.\r
732 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
733 BaseAddress and Length cannot be modified.\r
734 @retval RETURN_INVALID_PARAMETER Length is zero.\r
735 Attributes specified an illegal combination of attributes that\r
736 cannot be set together.\r
737 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
738 the memory resource range.\r
739 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
740 resource range specified by BaseAddress and Length.\r
741 The bit mask of attributes is not support for the memory resource\r
742 range specified by BaseAddress and Length.\r
743**/\r
744RETURN_STATUS\r
745EFIAPI\r
746AssignMemoryPageAttributes (\r
747 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
748 IN PHYSICAL_ADDRESS BaseAddress,\r
749 IN UINT64 Length,\r
750 IN UINT64 Attributes,\r
751 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL\r
752 )\r
753{\r
754 RETURN_STATUS Status;\r
755 BOOLEAN IsModified;\r
756 BOOLEAN IsSplitted;\r
757\r
758// DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));\r
759 Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress, Length, Attributes, PageActionAssign, AllocatePagesFunc, &IsSplitted, &IsModified);\r
760 if (!EFI_ERROR(Status)) {\r
761 if ((PagingContext == NULL) && IsModified) {\r
762 //\r
763 // Flush TLB as last step\r
764 //\r
765 CpuFlushTlb();\r
766 SyncMemoryPageAttributesAp (SyncCpuFlushTlb);\r
767 }\r
768 }\r
769\r
770 return Status;\r
771}\r
772\r
773/**\r
774 Initialize the Page Table lib.\r
775**/\r
776VOID\r
777InitializePageTableLib (\r
778 VOID\r
779 )\r
780{\r
781 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
782\r
783 GetCurrentPagingContext (&CurrentPagingContext);\r
784 DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));\r
785 DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType));\r
786 DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));\r
787 DEBUG ((DEBUG_INFO, " Attributes - 0x%x\n", CurrentPagingContext.ContextData.X64.Attributes));\r
788\r
789 return ;\r
790}\r
791\r