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