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