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