UefiCpuPkg/CpuDxe: Fix multiple entries of RT_CODE in memory map
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuPageTable.c
1 /** @file\r
2   Page table management support.\r
3 \r
4   Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
5   Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
6 \r
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 \r
27 #include "CpuDxe.h"\r
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
65 typedef enum {\r
66   PageNone,\r
67   Page4K,\r
68   Page2M,\r
69   Page1G,\r
70 } PAGE_ATTRIBUTE;\r
71 \r
72 typedef struct {\r
73   PAGE_ATTRIBUTE   Attribute;\r
74   UINT64           Length;\r
75   UINT64           AddressMask;\r
76 } PAGE_ATTRIBUTE_TABLE;\r
77 \r
78 typedef enum {\r
79   PageActionAssign,\r
80   PageActionSet,\r
81   PageActionClear,\r
82 } PAGE_ACTION;\r
83 \r
84 PAGE_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
95 VOID\r
96 EFIAPI\r
97 SyncCpuEnableWriteProtection (\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
109 VOID\r
110 EFIAPI\r
111 SyncCpuFlushTlb (\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
124 VOID\r
125 SyncMemoryPageAttributesAp (\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
159 VOID\r
160 GetCurrentPagingContext (\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
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     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
216 UINTN\r
217 PageAttributeToLength (\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
237 UINTN\r
238 PageAttributeToMask (\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
260 VOID *\r
261 GetPageTableEntry (\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
275   UINT64                AddressEncMask;\r
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
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
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
295     L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
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
310   L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
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
322   L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
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
338 UINT64\r
339 GetAttributesFromPageEntry (\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
366 VOID\r
367 ConvertPageEntryAttribute (\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_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
446     DEBUG ((DEBUG_VERBOSE, "->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
462 PAGE_ATTRIBUTE\r
463 NeedSplitPage (\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
497 RETURN_STATUS\r
498 SplitPage (\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
508   UINT64   AddressEncMask;\r
509 \r
510   ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
511 \r
512   ASSERT (AllocatePagesFunc != NULL);\r
513 \r
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
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
529       BaseAddress = *PageEntry & ~AddressEncMask & PAGING_2M_ADDRESS_MASK_64;\r
530       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
531         NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
532       }\r
533       (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
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
550       BaseAddress = *PageEntry & ~AddressEncMask  & PAGING_1G_ADDRESS_MASK_64;\r
551       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
552         NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
553       }\r
554       (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
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
593 RETURN_STATUS\r
594 ConvertMemoryPageAttributes (\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
639       if (Attributes == 0) {\r
640         return EFI_SUCCESS;\r
641       } else {\r
642         DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));\r
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
743 RETURN_STATUS\r
744 EFIAPI\r
745 AssignMemoryPageAttributes (\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
772 /**\r
773  Check if Execute Disable feature is enabled or not.\r
774 **/\r
775 BOOLEAN\r
776 IsExecuteDisableEnabled (\r
777   VOID\r
778   )\r
779 {\r
780   MSR_CORE_IA32_EFER_REGISTER    MsrEfer;\r
781 \r
782   MsrEfer.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);\r
783   return (MsrEfer.Bits.NXE == 1);\r
784 }\r
785 \r
786 /**\r
787   Update GCD memory space attributes according to current page table setup.\r
788 **/\r
789 VOID\r
790 RefreshGcdMemoryAttributesFromPaging (\r
791   VOID\r
792   )\r
793 {\r
794   EFI_STATUS                          Status;\r
795   UINTN                               NumberOfDescriptors;\r
796   EFI_GCD_MEMORY_SPACE_DESCRIPTOR     *MemorySpaceMap;\r
797   PAGE_TABLE_LIB_PAGING_CONTEXT       PagingContext;\r
798   PAGE_ATTRIBUTE                      PageAttribute;\r
799   UINT64                              *PageEntry;\r
800   UINT64                              PageLength;\r
801   UINT64                              MemorySpaceLength;\r
802   UINT64                              Length;\r
803   UINT64                              BaseAddress;\r
804   UINT64                              PageStartAddress;\r
805   UINT64                              Attributes;\r
806   UINT64                              Capabilities;\r
807   UINT64                              NewAttributes;\r
808   UINTN                               Index;\r
809 \r
810   //\r
811   // Assuming that memory space map returned is sorted already; otherwise sort\r
812   // them in the order of lowest address to highest address.\r
813   //\r
814   Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
815   ASSERT_EFI_ERROR (Status);\r
816 \r
817   GetCurrentPagingContext (&PagingContext);\r
818 \r
819   Attributes      = 0;\r
820   NewAttributes   = 0;\r
821   BaseAddress     = 0;\r
822   PageLength      = 0;\r
823 \r
824   if (IsExecuteDisableEnabled ()) {\r
825     Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP;\r
826   } else {\r
827     Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP;\r
828   }\r
829 \r
830   for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
831     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
832       continue;\r
833     }\r
834 \r
835     //\r
836     // Sync the actual paging related capabilities back to GCD service first.\r
837     // As a side effect (good one), this can also help to avoid unnecessary\r
838     // memory map entries due to the different capabilities of the same type\r
839     // memory, such as multiple RT_CODE and RT_DATA entries in memory map,\r
840     // which could cause boot failure of some old Linux distro (before v4.3).\r
841     //\r
842     Status = gDS->SetMemorySpaceCapabilities (\r
843                     MemorySpaceMap[Index].BaseAddress,\r
844                     MemorySpaceMap[Index].Length,\r
845                     MemorySpaceMap[Index].Capabilities | Capabilities\r
846                     );\r
847     if (EFI_ERROR (Status)) {\r
848       //\r
849       // If we cannot udpate the capabilities, we cannot update its\r
850       // attributes either. So just simply skip current block of memory.\r
851       //\r
852       DEBUG ((\r
853         DEBUG_WARN,\r
854         "Failed to update capability: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",\r
855         (UINT64)Index, MemorySpaceMap[Index].BaseAddress,\r
856         MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - 1,\r
857         MemorySpaceMap[Index].Capabilities,\r
858         MemorySpaceMap[Index].Capabilities | Capabilities\r
859         ));\r
860       continue;\r
861     }\r
862 \r
863     if (MemorySpaceMap[Index].BaseAddress >= (BaseAddress + PageLength)) {\r
864       //\r
865       // Current memory space starts at a new page. Resetting PageLength will\r
866       // trigger a retrieval of page attributes at new address.\r
867       //\r
868       PageLength = 0;\r
869     } else {\r
870       //\r
871       // In case current memory space is not adjacent to last one\r
872       //\r
873       PageLength -= (MemorySpaceMap[Index].BaseAddress - BaseAddress);\r
874     }\r
875 \r
876     //\r
877     // Sync actual page attributes to GCD\r
878     //\r
879     BaseAddress       = MemorySpaceMap[Index].BaseAddress;\r
880     MemorySpaceLength = MemorySpaceMap[Index].Length;\r
881     while (MemorySpaceLength > 0) {\r
882       if (PageLength == 0) {\r
883         PageEntry = GetPageTableEntry (&PagingContext, BaseAddress, &PageAttribute);\r
884         if (PageEntry == NULL) {\r
885           break;\r
886         }\r
887 \r
888         //\r
889         // Note current memory space might start in the middle of a page\r
890         //\r
891         PageStartAddress  = (*PageEntry) & (UINT64)PageAttributeToMask(PageAttribute);\r
892         PageLength        = PageAttributeToLength (PageAttribute) - (BaseAddress - PageStartAddress);\r
893         Attributes        = GetAttributesFromPageEntry (PageEntry);\r
894       }\r
895 \r
896       Length = MIN (PageLength, MemorySpaceLength);\r
897       if (Attributes != (MemorySpaceMap[Index].Attributes &\r
898                          EFI_MEMORY_PAGETYPE_MASK)) {\r
899         NewAttributes = (MemorySpaceMap[Index].Attributes &\r
900                          ~EFI_MEMORY_PAGETYPE_MASK) | Attributes;\r
901         Status = gDS->SetMemorySpaceAttributes (\r
902                         BaseAddress,\r
903                         Length,\r
904                         NewAttributes\r
905                         );\r
906         ASSERT_EFI_ERROR (Status);\r
907         DEBUG ((\r
908           DEBUG_INFO,\r
909           "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",\r
910           (UINT64)Index, BaseAddress, BaseAddress + Length - 1,\r
911           MemorySpaceMap[Index].Attributes,\r
912           NewAttributes\r
913           ));\r
914       }\r
915 \r
916       PageLength        -= Length;\r
917       MemorySpaceLength -= Length;\r
918       BaseAddress       += Length;\r
919     }\r
920   }\r
921 \r
922   FreePool (MemorySpaceMap);\r
923 }\r
924 \r
925 /**\r
926   Initialize the Page Table lib.\r
927 **/\r
928 VOID\r
929 InitializePageTableLib (\r
930   VOID\r
931   )\r
932 {\r
933   PAGE_TABLE_LIB_PAGING_CONTEXT     CurrentPagingContext;\r
934 \r
935   GetCurrentPagingContext (&CurrentPagingContext);\r
936   DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));\r
937   DEBUG ((DEBUG_INFO, "  MachineType   - 0x%x\n", CurrentPagingContext.MachineType));\r
938   DEBUG ((DEBUG_INFO, "  PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));\r
939   DEBUG ((DEBUG_INFO, "  Attributes    - 0x%x\n", CurrentPagingContext.ContextData.X64.Attributes));\r
940 \r
941   return ;\r
942 }\r
943 \r