]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg PiSmmCpuDxeSmm: Fixed #double fault on #page fault for IA32
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmCpuMemoryManagement.c
1 /** @file
2
3 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 **/
13
14 #include "PiSmmCpuDxeSmm.h"
15
16 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
17 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))
18
19 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
20 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
21
22 EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;
23 UINTN mUefiMemoryMapSize;
24 UINTN mUefiDescriptorSize;
25
26 PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
27 {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},
28 {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},
29 {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
30 };
31
32 /**
33 Return page table base.
34
35 @return page table base.
36 **/
37 UINTN
38 GetPageTableBase (
39 VOID
40 )
41 {
42 return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
43 }
44
45 /**
46 Return length according to page attributes.
47
48 @param[in] PageAttributes The page attribute of the page entry.
49
50 @return The length of page entry.
51 **/
52 UINTN
53 PageAttributeToLength (
54 IN PAGE_ATTRIBUTE PageAttribute
55 )
56 {
57 UINTN Index;
58 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
59 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
60 return (UINTN)mPageAttributeTable[Index].Length;
61 }
62 }
63 return 0;
64 }
65
66 /**
67 Return address mask according to page attributes.
68
69 @param[in] PageAttributes The page attribute of the page entry.
70
71 @return The address mask of page entry.
72 **/
73 UINTN
74 PageAttributeToMask (
75 IN PAGE_ATTRIBUTE PageAttribute
76 )
77 {
78 UINTN Index;
79 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
80 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
81 return (UINTN)mPageAttributeTable[Index].AddressMask;
82 }
83 }
84 return 0;
85 }
86
87 /**
88 Return page table entry to match the address.
89
90 @param[in] Address The address to be checked.
91 @param[out] PageAttributes The page attribute of the page entry.
92
93 @return The page entry.
94 **/
95 VOID *
96 GetPageTableEntry (
97 IN PHYSICAL_ADDRESS Address,
98 OUT PAGE_ATTRIBUTE *PageAttribute
99 )
100 {
101 UINTN Index1;
102 UINTN Index2;
103 UINTN Index3;
104 UINTN Index4;
105 UINT64 *L1PageTable;
106 UINT64 *L2PageTable;
107 UINT64 *L3PageTable;
108 UINT64 *L4PageTable;
109
110 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
111 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
112 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
113 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
114
115 if (sizeof(UINTN) == sizeof(UINT64)) {
116 L4PageTable = (UINT64 *)GetPageTableBase ();
117 if (L4PageTable[Index4] == 0) {
118 *PageAttribute = PageNone;
119 return NULL;
120 }
121
122 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
123 } else {
124 L3PageTable = (UINT64 *)GetPageTableBase ();
125 }
126 if (L3PageTable[Index3] == 0) {
127 *PageAttribute = PageNone;
128 return NULL;
129 }
130 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
131 // 1G
132 *PageAttribute = Page1G;
133 return &L3PageTable[Index3];
134 }
135
136 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
137 if (L2PageTable[Index2] == 0) {
138 *PageAttribute = PageNone;
139 return NULL;
140 }
141 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
142 // 2M
143 *PageAttribute = Page2M;
144 return &L2PageTable[Index2];
145 }
146
147 // 4k
148 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
149 if ((L1PageTable[Index1] == 0) && (Address != 0)) {
150 *PageAttribute = PageNone;
151 return NULL;
152 }
153 *PageAttribute = Page4K;
154 return &L1PageTable[Index1];
155 }
156
157 /**
158 Return memory attributes of page entry.
159
160 @param[in] PageEntry The page entry.
161
162 @return Memory attributes of page entry.
163 **/
164 UINT64
165 GetAttributesFromPageEntry (
166 IN UINT64 *PageEntry
167 )
168 {
169 UINT64 Attributes;
170 Attributes = 0;
171 if ((*PageEntry & IA32_PG_P) == 0) {
172 Attributes |= EFI_MEMORY_RP;
173 }
174 if ((*PageEntry & IA32_PG_RW) == 0) {
175 Attributes |= EFI_MEMORY_RO;
176 }
177 if ((*PageEntry & IA32_PG_NX) != 0) {
178 Attributes |= EFI_MEMORY_XP;
179 }
180 return Attributes;
181 }
182
183 /**
184 Modify memory attributes of page entry.
185
186 @param[in] PageEntry The page entry.
187 @param[in] Attributes The bit mask of attributes to modify for the memory region.
188 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
189 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
190 **/
191 VOID
192 ConvertPageEntryAttribute (
193 IN UINT64 *PageEntry,
194 IN UINT64 Attributes,
195 IN BOOLEAN IsSet,
196 OUT BOOLEAN *IsModified
197 )
198 {
199 UINT64 CurrentPageEntry;
200 UINT64 NewPageEntry;
201
202 CurrentPageEntry = *PageEntry;
203 NewPageEntry = CurrentPageEntry;
204 if ((Attributes & EFI_MEMORY_RP) != 0) {
205 if (IsSet) {
206 NewPageEntry &= ~(UINT64)IA32_PG_P;
207 } else {
208 NewPageEntry |= IA32_PG_P;
209 }
210 }
211 if ((Attributes & EFI_MEMORY_RO) != 0) {
212 if (IsSet) {
213 NewPageEntry &= ~(UINT64)IA32_PG_RW;
214 } else {
215 NewPageEntry |= IA32_PG_RW;
216 }
217 }
218 if ((Attributes & EFI_MEMORY_XP) != 0) {
219 if (mXdSupported) {
220 if (IsSet) {
221 NewPageEntry |= IA32_PG_NX;
222 } else {
223 NewPageEntry &= ~IA32_PG_NX;
224 }
225 }
226 }
227 *PageEntry = NewPageEntry;
228 if (CurrentPageEntry != NewPageEntry) {
229 *IsModified = TRUE;
230 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));
231 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));
232 } else {
233 *IsModified = FALSE;
234 }
235 }
236
237 /**
238 This function returns if there is need to split page entry.
239
240 @param[in] BaseAddress The base address to be checked.
241 @param[in] Length The length to be checked.
242 @param[in] PageEntry The page entry to be checked.
243 @param[in] PageAttribute The page attribute of the page entry.
244
245 @retval SplitAttributes on if there is need to split page entry.
246 **/
247 PAGE_ATTRIBUTE
248 NeedSplitPage (
249 IN PHYSICAL_ADDRESS BaseAddress,
250 IN UINT64 Length,
251 IN UINT64 *PageEntry,
252 IN PAGE_ATTRIBUTE PageAttribute
253 )
254 {
255 UINT64 PageEntryLength;
256
257 PageEntryLength = PageAttributeToLength (PageAttribute);
258
259 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {
260 return PageNone;
261 }
262
263 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
264 return Page4K;
265 }
266
267 return Page2M;
268 }
269
270 /**
271 This function splits one page entry to small page entries.
272
273 @param[in] PageEntry The page entry to be splitted.
274 @param[in] PageAttribute The page attribute of the page entry.
275 @param[in] SplitAttribute How to split the page entry.
276
277 @retval RETURN_SUCCESS The page entry is splitted.
278 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
279 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
280 **/
281 RETURN_STATUS
282 SplitPage (
283 IN UINT64 *PageEntry,
284 IN PAGE_ATTRIBUTE PageAttribute,
285 IN PAGE_ATTRIBUTE SplitAttribute
286 )
287 {
288 UINT64 BaseAddress;
289 UINT64 *NewPageEntry;
290 UINTN Index;
291
292 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
293
294 if (PageAttribute == Page2M) {
295 //
296 // Split 2M to 4K
297 //
298 ASSERT (SplitAttribute == Page4K);
299 if (SplitAttribute == Page4K) {
300 NewPageEntry = AllocatePageTableMemory (1);
301 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
302 if (NewPageEntry == NULL) {
303 return RETURN_OUT_OF_RESOURCES;
304 }
305 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;
306 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
307 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);
308 }
309 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
310 return RETURN_SUCCESS;
311 } else {
312 return RETURN_UNSUPPORTED;
313 }
314 } else if (PageAttribute == Page1G) {
315 //
316 // Split 1G to 2M
317 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
318 //
319 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);
320 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {
321 NewPageEntry = AllocatePageTableMemory (1);
322 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
323 if (NewPageEntry == NULL) {
324 return RETURN_OUT_OF_RESOURCES;
325 }
326 BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;
327 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
328 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);
329 }
330 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
331 return RETURN_SUCCESS;
332 } else {
333 return RETURN_UNSUPPORTED;
334 }
335 } else {
336 return RETURN_UNSUPPORTED;
337 }
338 }
339
340 /**
341 This function modifies the page attributes for the memory region specified by BaseAddress and
342 Length from their current attributes to the attributes specified by Attributes.
343
344 Caller should make sure BaseAddress and Length is at page boundary.
345
346 @param[in] BaseAddress The physical address that is the start address of a memory region.
347 @param[in] Length The size in bytes of the memory region.
348 @param[in] Attributes The bit mask of attributes to modify for the memory region.
349 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
350 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
351 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
352
353 @retval RETURN_SUCCESS The attributes were modified for the memory region.
354 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
355 BaseAddress and Length cannot be modified.
356 @retval RETURN_INVALID_PARAMETER Length is zero.
357 Attributes specified an illegal combination of attributes that
358 cannot be set together.
359 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
360 the memory resource range.
361 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
362 resource range specified by BaseAddress and Length.
363 The bit mask of attributes is not support for the memory resource
364 range specified by BaseAddress and Length.
365 **/
366 RETURN_STATUS
367 EFIAPI
368 ConvertMemoryPageAttributes (
369 IN PHYSICAL_ADDRESS BaseAddress,
370 IN UINT64 Length,
371 IN UINT64 Attributes,
372 IN BOOLEAN IsSet,
373 OUT BOOLEAN *IsSplitted, OPTIONAL
374 OUT BOOLEAN *IsModified OPTIONAL
375 )
376 {
377 UINT64 *PageEntry;
378 PAGE_ATTRIBUTE PageAttribute;
379 UINTN PageEntryLength;
380 PAGE_ATTRIBUTE SplitAttribute;
381 RETURN_STATUS Status;
382 BOOLEAN IsEntryModified;
383 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;
384
385 ASSERT (Attributes != 0);
386 ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0);
387
388 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
389 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
390
391 if (Length == 0) {
392 return RETURN_INVALID_PARAMETER;
393 }
394
395 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);
396 if (BaseAddress > MaximumSupportMemAddress) {
397 return RETURN_UNSUPPORTED;
398 }
399 if (Length > MaximumSupportMemAddress) {
400 return RETURN_UNSUPPORTED;
401 }
402 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {
403 return RETURN_UNSUPPORTED;
404 }
405
406 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
407
408 if (IsSplitted != NULL) {
409 *IsSplitted = FALSE;
410 }
411 if (IsModified != NULL) {
412 *IsModified = FALSE;
413 }
414
415 //
416 // Below logic is to check 2M/4K page to make sure we donot waist memory.
417 //
418 while (Length != 0) {
419 PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);
420 if (PageEntry == NULL) {
421 return RETURN_UNSUPPORTED;
422 }
423 PageEntryLength = PageAttributeToLength (PageAttribute);
424 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);
425 if (SplitAttribute == PageNone) {
426 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);
427 if (IsEntryModified) {
428 if (IsModified != NULL) {
429 *IsModified = TRUE;
430 }
431 }
432 //
433 // Convert success, move to next
434 //
435 BaseAddress += PageEntryLength;
436 Length -= PageEntryLength;
437 } else {
438 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);
439 if (RETURN_ERROR (Status)) {
440 return RETURN_UNSUPPORTED;
441 }
442 if (IsSplitted != NULL) {
443 *IsSplitted = TRUE;
444 }
445 if (IsModified != NULL) {
446 *IsModified = TRUE;
447 }
448 //
449 // Just split current page
450 // Convert success in next around
451 //
452 }
453 }
454
455 return RETURN_SUCCESS;
456 }
457
458 /**
459 FlushTlb on current processor.
460
461 @param[in,out] Buffer Pointer to private data buffer.
462 **/
463 VOID
464 EFIAPI
465 FlushTlbOnCurrentProcessor (
466 IN OUT VOID *Buffer
467 )
468 {
469 CpuFlushTlb ();
470 }
471
472 /**
473 FlushTlb for all processors.
474 **/
475 VOID
476 FlushTlbForAll (
477 VOID
478 )
479 {
480 UINTN Index;
481
482 FlushTlbOnCurrentProcessor (NULL);
483
484 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
485 if (Index != gSmst->CurrentlyExecutingCpu) {
486 // Force to start up AP in blocking mode,
487 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);
488 // Do not check return status, because AP might not be present in some corner cases.
489 }
490 }
491 }
492
493 /**
494 This function sets the attributes for the memory region specified by BaseAddress and
495 Length from their current attributes to the attributes specified by Attributes.
496
497 @param[in] BaseAddress The physical address that is the start address of a memory region.
498 @param[in] Length The size in bytes of the memory region.
499 @param[in] Attributes The bit mask of attributes to set for the memory region.
500 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
501
502 @retval EFI_SUCCESS The attributes were set for the memory region.
503 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
504 BaseAddress and Length cannot be modified.
505 @retval EFI_INVALID_PARAMETER Length is zero.
506 Attributes specified an illegal combination of attributes that
507 cannot be set together.
508 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
509 the memory resource range.
510 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
511 resource range specified by BaseAddress and Length.
512 The bit mask of attributes is not support for the memory resource
513 range specified by BaseAddress and Length.
514
515 **/
516 EFI_STATUS
517 EFIAPI
518 SmmSetMemoryAttributesEx (
519 IN EFI_PHYSICAL_ADDRESS BaseAddress,
520 IN UINT64 Length,
521 IN UINT64 Attributes,
522 OUT BOOLEAN *IsSplitted OPTIONAL
523 )
524 {
525 EFI_STATUS Status;
526 BOOLEAN IsModified;
527
528 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);
529 if (!EFI_ERROR(Status)) {
530 if (IsModified) {
531 //
532 // Flush TLB as last step
533 //
534 FlushTlbForAll();
535 }
536 }
537
538 return Status;
539 }
540
541 /**
542 This function clears the attributes for the memory region specified by BaseAddress and
543 Length from their current attributes to the attributes specified by Attributes.
544
545 @param[in] BaseAddress The physical address that is the start address of a memory region.
546 @param[in] Length The size in bytes of the memory region.
547 @param[in] Attributes The bit mask of attributes to clear for the memory region.
548 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
549
550 @retval EFI_SUCCESS The attributes were cleared for the memory region.
551 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
552 BaseAddress and Length cannot be modified.
553 @retval EFI_INVALID_PARAMETER Length is zero.
554 Attributes specified an illegal combination of attributes that
555 cannot be set together.
556 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
557 the memory resource range.
558 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
559 resource range specified by BaseAddress and Length.
560 The bit mask of attributes is not support for the memory resource
561 range specified by BaseAddress and Length.
562
563 **/
564 EFI_STATUS
565 EFIAPI
566 SmmClearMemoryAttributesEx (
567 IN EFI_PHYSICAL_ADDRESS BaseAddress,
568 IN UINT64 Length,
569 IN UINT64 Attributes,
570 OUT BOOLEAN *IsSplitted OPTIONAL
571 )
572 {
573 EFI_STATUS Status;
574 BOOLEAN IsModified;
575
576 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);
577 if (!EFI_ERROR(Status)) {
578 if (IsModified) {
579 //
580 // Flush TLB as last step
581 //
582 FlushTlbForAll();
583 }
584 }
585
586 return Status;
587 }
588
589 /**
590 This function sets the attributes for the memory region specified by BaseAddress and
591 Length from their current attributes to the attributes specified by Attributes.
592
593 @param[in] BaseAddress The physical address that is the start address of a memory region.
594 @param[in] Length The size in bytes of the memory region.
595 @param[in] Attributes The bit mask of attributes to set for the memory region.
596
597 @retval EFI_SUCCESS The attributes were set for the memory region.
598 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
599 BaseAddress and Length cannot be modified.
600 @retval EFI_INVALID_PARAMETER Length is zero.
601 Attributes specified an illegal combination of attributes that
602 cannot be set together.
603 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
604 the memory resource range.
605 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
606 resource range specified by BaseAddress and Length.
607 The bit mask of attributes is not support for the memory resource
608 range specified by BaseAddress and Length.
609
610 **/
611 EFI_STATUS
612 EFIAPI
613 SmmSetMemoryAttributes (
614 IN EFI_PHYSICAL_ADDRESS BaseAddress,
615 IN UINT64 Length,
616 IN UINT64 Attributes
617 )
618 {
619 return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);
620 }
621
622 /**
623 This function clears the attributes for the memory region specified by BaseAddress and
624 Length from their current attributes to the attributes specified by Attributes.
625
626 @param[in] BaseAddress The physical address that is the start address of a memory region.
627 @param[in] Length The size in bytes of the memory region.
628 @param[in] Attributes The bit mask of attributes to clear for the memory region.
629
630 @retval EFI_SUCCESS The attributes were cleared for the memory region.
631 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
632 BaseAddress and Length cannot be modified.
633 @retval EFI_INVALID_PARAMETER Length is zero.
634 Attributes specified an illegal combination of attributes that
635 cannot be set together.
636 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
637 the memory resource range.
638 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
639 resource range specified by BaseAddress and Length.
640 The bit mask of attributes is not support for the memory resource
641 range specified by BaseAddress and Length.
642
643 **/
644 EFI_STATUS
645 EFIAPI
646 SmmClearMemoryAttributes (
647 IN EFI_PHYSICAL_ADDRESS BaseAddress,
648 IN UINT64 Length,
649 IN UINT64 Attributes
650 )
651 {
652 return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);
653 }
654
655
656
657 /**
658 Retrieves a pointer to the system configuration table from the SMM System Table
659 based on a specified GUID.
660
661 @param[in] TableGuid The pointer to table's GUID type.
662 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.
663
664 @retval EFI_SUCCESS A configuration table matching TableGuid was found.
665 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.
666
667 **/
668 EFI_STATUS
669 EFIAPI
670 SmmGetSystemConfigurationTable (
671 IN EFI_GUID *TableGuid,
672 OUT VOID **Table
673 )
674 {
675 UINTN Index;
676
677 ASSERT (TableGuid != NULL);
678 ASSERT (Table != NULL);
679
680 *Table = NULL;
681 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {
682 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {
683 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;
684 return EFI_SUCCESS;
685 }
686 }
687
688 return EFI_NOT_FOUND;
689 }
690
691 /**
692 This function sets SMM save state buffer to be RW and XP.
693 **/
694 VOID
695 PatchSmmSaveStateMap (
696 VOID
697 )
698 {
699 UINTN Index;
700 UINTN TileCodeSize;
701 UINTN TileDataSize;
702 UINTN TileSize;
703
704 TileCodeSize = GetSmiHandlerSize ();
705 TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);
706 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
707 TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);
708 TileSize = TileDataSize + TileCodeSize - 1;
709 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
710
711 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));
712 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {
713 //
714 // Code
715 //
716 SmmSetMemoryAttributes (
717 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
718 TileCodeSize,
719 EFI_MEMORY_RO
720 );
721 SmmClearMemoryAttributes (
722 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
723 TileCodeSize,
724 EFI_MEMORY_XP
725 );
726
727 //
728 // Data
729 //
730 SmmClearMemoryAttributes (
731 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
732 TileSize - TileCodeSize,
733 EFI_MEMORY_RO
734 );
735 SmmSetMemoryAttributes (
736 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
737 TileSize - TileCodeSize,
738 EFI_MEMORY_XP
739 );
740 }
741
742 //
743 // Code
744 //
745 SmmSetMemoryAttributes (
746 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
747 TileCodeSize,
748 EFI_MEMORY_RO
749 );
750 SmmClearMemoryAttributes (
751 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
752 TileCodeSize,
753 EFI_MEMORY_XP
754 );
755
756 //
757 // Data
758 //
759 SmmClearMemoryAttributes (
760 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
761 SIZE_32KB - TileCodeSize,
762 EFI_MEMORY_RO
763 );
764 SmmSetMemoryAttributes (
765 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
766 SIZE_32KB - TileCodeSize,
767 EFI_MEMORY_XP
768 );
769 }
770
771 /**
772 This function sets GDT/IDT buffer to be RO and XP.
773 **/
774 VOID
775 PatchGdtIdtMap (
776 VOID
777 )
778 {
779 EFI_PHYSICAL_ADDRESS BaseAddress;
780 UINTN Size;
781
782 //
783 // GDT
784 //
785 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));
786
787 BaseAddress = mGdtBuffer;
788 Size = ALIGN_VALUE(mGdtBufferSize, SIZE_4KB);
789 //
790 // The range should have been set to RO
791 // if it is allocated with EfiRuntimeServicesCode.
792 //
793 SmmSetMemoryAttributes (
794 BaseAddress,
795 Size,
796 EFI_MEMORY_XP
797 );
798
799 //
800 // IDT
801 //
802 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));
803
804 BaseAddress = gcSmiIdtr.Base;
805 Size = ALIGN_VALUE(gcSmiIdtr.Limit + 1, SIZE_4KB);
806 SmmSetMemoryAttributes (
807 BaseAddress,
808 Size,
809 EFI_MEMORY_RO
810 );
811 SmmSetMemoryAttributes (
812 BaseAddress,
813 Size,
814 EFI_MEMORY_XP
815 );
816 }
817
818 /**
819 This function sets memory attribute according to MemoryAttributesTable.
820 **/
821 VOID
822 SetMemMapAttributes (
823 VOID
824 )
825 {
826 EFI_MEMORY_DESCRIPTOR *MemoryMap;
827 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
828 UINTN MemoryMapEntryCount;
829 UINTN DescriptorSize;
830 UINTN Index;
831 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
832
833 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
834 if (MemoryAttributesTable == NULL) {
835 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));
836 return ;
837 }
838
839 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));
840 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));
841 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));
842 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));
843
844 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;
845 DescriptorSize = MemoryAttributesTable->DescriptorSize;
846 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);
847 MemoryMap = MemoryMapStart;
848 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
849 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));
850 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));
851 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));
852 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));
853 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));
854 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));
855 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
856 }
857
858 MemoryMap = MemoryMapStart;
859 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
860 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));
861 switch (MemoryMap->Type) {
862 case EfiRuntimeServicesCode:
863 SmmSetMemoryAttributes (
864 MemoryMap->PhysicalStart,
865 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
866 EFI_MEMORY_RO
867 );
868 break;
869 case EfiRuntimeServicesData:
870 SmmSetMemoryAttributes (
871 MemoryMap->PhysicalStart,
872 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
873 EFI_MEMORY_XP
874 );
875 break;
876 default:
877 SmmSetMemoryAttributes (
878 MemoryMap->PhysicalStart,
879 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
880 EFI_MEMORY_XP
881 );
882 break;
883 }
884 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
885 }
886
887 PatchSmmSaveStateMap ();
888 PatchGdtIdtMap ();
889
890 return ;
891 }
892
893 /**
894 Sort memory map entries based upon PhysicalStart, from low to high.
895
896 @param MemoryMap A pointer to the buffer in which firmware places
897 the current memory map.
898 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
899 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
900 **/
901 STATIC
902 VOID
903 SortMemoryMap (
904 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
905 IN UINTN MemoryMapSize,
906 IN UINTN DescriptorSize
907 )
908 {
909 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
910 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
911 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
912 EFI_MEMORY_DESCRIPTOR TempMemoryMap;
913
914 MemoryMapEntry = MemoryMap;
915 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
916 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
917 while (MemoryMapEntry < MemoryMapEnd) {
918 while (NextMemoryMapEntry < MemoryMapEnd) {
919 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
920 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
921 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
922 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));
923 }
924
925 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
926 }
927
928 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
929 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
930 }
931 }
932
933 /**
934 Return if a UEFI memory page should be marked as not present in SMM page table.
935 If the memory map entries type is
936 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
937 EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.
938 Or return FALSE.
939
940 @param[in] MemoryMap A pointer to the memory descriptor.
941
942 @return TRUE The memory described will be marked as not present in SMM page table.
943 @return FALSE The memory described will not be marked as not present in SMM page table.
944 **/
945 BOOLEAN
946 IsUefiPageNotPresent (
947 IN EFI_MEMORY_DESCRIPTOR *MemoryMap
948 )
949 {
950 switch (MemoryMap->Type) {
951 case EfiLoaderCode:
952 case EfiLoaderData:
953 case EfiBootServicesCode:
954 case EfiBootServicesData:
955 case EfiConventionalMemory:
956 case EfiUnusableMemory:
957 case EfiACPIReclaimMemory:
958 return TRUE;
959 default:
960 return FALSE;
961 }
962 }
963
964 /**
965 Merge continous memory map entries whose type is
966 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
967 EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by
968 these entries will be set as NOT present in SMM page table.
969
970 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
971 the current memory map.
972 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
973 MemoryMap buffer. On input, this is the size of
974 the current memory map. On output,
975 it is the size of new memory map after merge.
976 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
977 **/
978 STATIC
979 VOID
980 MergeMemoryMapForNotPresentEntry (
981 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
982 IN OUT UINTN *MemoryMapSize,
983 IN UINTN DescriptorSize
984 )
985 {
986 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
987 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
988 UINT64 MemoryBlockLength;
989 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
990 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
991
992 MemoryMapEntry = MemoryMap;
993 NewMemoryMapEntry = MemoryMap;
994 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);
995 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
996 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
997 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
998
999 do {
1000 MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));
1001 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
1002 IsUefiPageNotPresent(MemoryMapEntry) && IsUefiPageNotPresent(NextMemoryMapEntry) &&
1003 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {
1004 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1005 if (NewMemoryMapEntry != MemoryMapEntry) {
1006 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1007 }
1008
1009 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1010 continue;
1011 } else {
1012 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1013 break;
1014 }
1015 } while (TRUE);
1016
1017 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1018 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
1019 }
1020
1021 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
1022
1023 return ;
1024 }
1025
1026 /**
1027 This function caches the UEFI memory map information.
1028 **/
1029 VOID
1030 GetUefiMemoryMap (
1031 VOID
1032 )
1033 {
1034 EFI_STATUS Status;
1035 UINTN MapKey;
1036 UINT32 DescriptorVersion;
1037 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1038 UINTN UefiMemoryMapSize;
1039
1040 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));
1041
1042 UefiMemoryMapSize = 0;
1043 MemoryMap = NULL;
1044 Status = gBS->GetMemoryMap (
1045 &UefiMemoryMapSize,
1046 MemoryMap,
1047 &MapKey,
1048 &mUefiDescriptorSize,
1049 &DescriptorVersion
1050 );
1051 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
1052
1053 do {
1054 Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);
1055 ASSERT (MemoryMap != NULL);
1056 if (MemoryMap == NULL) {
1057 return ;
1058 }
1059
1060 Status = gBS->GetMemoryMap (
1061 &UefiMemoryMapSize,
1062 MemoryMap,
1063 &MapKey,
1064 &mUefiDescriptorSize,
1065 &DescriptorVersion
1066 );
1067 if (EFI_ERROR (Status)) {
1068 gBS->FreePool (MemoryMap);
1069 MemoryMap = NULL;
1070 }
1071 } while (Status == EFI_BUFFER_TOO_SMALL);
1072
1073 if (MemoryMap == NULL) {
1074 return ;
1075 }
1076
1077 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);
1078 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);
1079
1080 mUefiMemoryMapSize = UefiMemoryMapSize;
1081 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);
1082 ASSERT (mUefiMemoryMap != NULL);
1083
1084 gBS->FreePool (MemoryMap);
1085 }
1086
1087 /**
1088 This function sets UEFI memory attribute according to UEFI memory map.
1089
1090 The normal memory region is marked as not present, such as
1091 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1092 EfiUnusableMemory, EfiACPIReclaimMemory.
1093 **/
1094 VOID
1095 SetUefiMemMapAttributes (
1096 VOID
1097 )
1098 {
1099 EFI_STATUS Status;
1100 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1101 UINTN MemoryMapEntryCount;
1102 UINTN Index;
1103
1104 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
1105
1106 if (mUefiMemoryMap == NULL) {
1107 DEBUG ((DEBUG_INFO, "UefiMemoryMap - NULL\n"));
1108 return ;
1109 }
1110
1111 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1112 MemoryMap = mUefiMemoryMap;
1113 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1114 if (IsUefiPageNotPresent(MemoryMap)) {
1115 Status = SmmSetMemoryAttributes (
1116 MemoryMap->PhysicalStart,
1117 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
1118 EFI_MEMORY_RP
1119 );
1120 DEBUG ((
1121 DEBUG_INFO,
1122 "UefiMemory protection: 0x%lx - 0x%lx %r\n",
1123 MemoryMap->PhysicalStart,
1124 MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
1125 Status
1126 ));
1127 }
1128 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);
1129 }
1130
1131 //
1132 // Do free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
1133 //
1134 }
1135
1136 /**
1137 Return if the Address is forbidden as SMM communication buffer.
1138
1139 @param[in] Address the address to be checked
1140
1141 @return TRUE The address is forbidden as SMM communication buffer.
1142 @return FALSE The address is allowed as SMM communication buffer.
1143 **/
1144 BOOLEAN
1145 IsSmmCommBufferForbiddenAddress (
1146 IN UINT64 Address
1147 )
1148 {
1149 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1150 UINTN MemoryMapEntryCount;
1151 UINTN Index;
1152
1153 if (mUefiMemoryMap == NULL) {
1154 return FALSE;
1155 }
1156
1157 MemoryMap = mUefiMemoryMap;
1158 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1159 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1160 if (IsUefiPageNotPresent (MemoryMap)) {
1161 if ((Address >= MemoryMap->PhysicalStart) &&
1162 (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) {
1163 return TRUE;
1164 }
1165 }
1166 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);
1167 }
1168 return FALSE;
1169 }
1170
1171 /**
1172 This function set given attributes of the memory region specified by
1173 BaseAddress and Length.
1174
1175 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1176 @param BaseAddress The physical address that is the start address of
1177 a memory region.
1178 @param Length The size in bytes of the memory region.
1179 @param Attributes The bit mask of attributes to set for the memory
1180 region.
1181
1182 @retval EFI_SUCCESS The attributes were set for the memory region.
1183 @retval EFI_INVALID_PARAMETER Length is zero.
1184 Attributes specified an illegal combination of
1185 attributes that cannot be set together.
1186 @retval EFI_UNSUPPORTED The processor does not support one or more
1187 bytes of the memory resource range specified
1188 by BaseAddress and Length.
1189 The bit mask of attributes is not support for
1190 the memory resource range specified by
1191 BaseAddress and Length.
1192
1193 **/
1194 EFI_STATUS
1195 EFIAPI
1196 EdkiiSmmSetMemoryAttributes (
1197 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1198 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1199 IN UINT64 Length,
1200 IN UINT64 Attributes
1201 )
1202 {
1203 return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);
1204 }
1205
1206 /**
1207 This function clears given attributes of the memory region specified by
1208 BaseAddress and Length.
1209
1210 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1211 @param BaseAddress The physical address that is the start address of
1212 a memory region.
1213 @param Length The size in bytes of the memory region.
1214 @param Attributes The bit mask of attributes to set for the memory
1215 region.
1216
1217 @retval EFI_SUCCESS The attributes were set for the memory region.
1218 @retval EFI_INVALID_PARAMETER Length is zero.
1219 Attributes specified an illegal combination of
1220 attributes that cannot be set together.
1221 @retval EFI_UNSUPPORTED The processor does not support one or more
1222 bytes of the memory resource range specified
1223 by BaseAddress and Length.
1224 The bit mask of attributes is not support for
1225 the memory resource range specified by
1226 BaseAddress and Length.
1227
1228 **/
1229 EFI_STATUS
1230 EFIAPI
1231 EdkiiSmmClearMemoryAttributes (
1232 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1233 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1234 IN UINT64 Length,
1235 IN UINT64 Attributes
1236 )
1237 {
1238 return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);
1239 }
1240
1241 /**
1242 This function retrieve the attributes of the memory region specified by
1243 BaseAddress and Length. If different attributes are got from different part
1244 of the memory region, EFI_NO_MAPPING will be returned.
1245
1246 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1247 @param BaseAddress The physical address that is the start address of
1248 a memory region.
1249 @param Length The size in bytes of the memory region.
1250 @param Attributes Pointer to attributes returned.
1251
1252 @retval EFI_SUCCESS The attributes got for the memory region.
1253 @retval EFI_INVALID_PARAMETER Length is zero.
1254 Attributes is NULL.
1255 @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
1256 region.
1257 @retval EFI_UNSUPPORTED The processor does not support one or more
1258 bytes of the memory resource range specified
1259 by BaseAddress and Length.
1260 The bit mask of attributes is not support for
1261 the memory resource range specified by
1262 BaseAddress and Length.
1263
1264 **/
1265 EFI_STATUS
1266 EFIAPI
1267 EdkiiSmmGetMemoryAttributes (
1268 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1269 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1270 IN UINT64 Length,
1271 OUT UINT64 *Attributes
1272 )
1273 {
1274 EFI_PHYSICAL_ADDRESS Address;
1275 UINT64 *PageEntry;
1276 UINT64 MemAttr;
1277 PAGE_ATTRIBUTE PageAttr;
1278 INT64 Size;
1279
1280 if (Length < SIZE_4KB || Attributes == NULL) {
1281 return EFI_INVALID_PARAMETER;
1282 }
1283
1284 Size = (INT64)Length;
1285 MemAttr = (UINT64)-1;
1286
1287 do {
1288
1289 PageEntry = GetPageTableEntry (BaseAddress, &PageAttr);
1290 if (PageEntry == NULL || PageAttr == PageNone) {
1291 return EFI_UNSUPPORTED;
1292 }
1293
1294 //
1295 // If the memory range is cross page table boundary, make sure they
1296 // share the same attribute. Return EFI_NO_MAPPING if not.
1297 //
1298 *Attributes = GetAttributesFromPageEntry (PageEntry);
1299 if (MemAttr != (UINT64)-1 && *Attributes != MemAttr) {
1300 return EFI_NO_MAPPING;
1301 }
1302
1303 switch (PageAttr) {
1304 case Page4K:
1305 Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;
1306 Size -= (SIZE_4KB - (BaseAddress - Address));
1307 BaseAddress += (SIZE_4KB - (BaseAddress - Address));
1308 break;
1309
1310 case Page2M:
1311 Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;
1312 Size -= SIZE_2MB - (BaseAddress - Address);
1313 BaseAddress += SIZE_2MB - (BaseAddress - Address);
1314 break;
1315
1316 case Page1G:
1317 Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;
1318 Size -= SIZE_1GB - (BaseAddress - Address);
1319 BaseAddress += SIZE_1GB - (BaseAddress - Address);
1320 break;
1321
1322 default:
1323 return EFI_UNSUPPORTED;
1324 }
1325
1326 MemAttr = *Attributes;
1327
1328 } while (Size > 0);
1329
1330 return EFI_SUCCESS;
1331 }
1332