]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/CpuDxe/CpuPageTable.c
UefiCpuPkg/CpuDxe: Fix multiple entries of RT_CODE in memory map
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuPageTable.c
... / ...
CommitLineData
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
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
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
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
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
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_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
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
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
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
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
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
772/**\r
773 Check if Execute Disable feature is enabled or not.\r
774**/\r
775BOOLEAN\r
776IsExecuteDisableEnabled (\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
789VOID\r
790RefreshGcdMemoryAttributesFromPaging (\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
928VOID\r
929InitializePageTableLib (\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