1 /** @file
2
3 Copyright (c) 2016, 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
GetPageTableBase(VOID)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
PageAttributeToLength(IN PAGE_ATTRIBUTE PageAttribute)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
PageAttributeToMask(IN PAGE_ATTRIBUTE PageAttribute)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 *
GetPageTableEntry(IN PHYSICAL_ADDRESS Address,OUT PAGE_ATTRIBUTE * PageAttribute)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] & 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] & 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] & 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
GetAttributesFromPageEntry(IN UINT64 * PageEntry)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
ConvertPageEntryAttribute(IN UINT64 * PageEntry,IN UINT64 Attributes,IN BOOLEAN IsSet,OUT BOOLEAN * IsModified)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
NeedSplitPage(IN PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 * PageEntry,IN PAGE_ATTRIBUTE PageAttribute)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
SplitPage(IN UINT64 * PageEntry,IN PAGE_ATTRIBUTE PageAttribute,IN PAGE_ATTRIBUTE SplitAttribute)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 + ((*PageEntry) & PAGE_PROGATE_BITS);
308 }
309 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + 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 + IA32_PG_PS + ((*PageEntry) & PAGE_PROGATE_BITS);
329 }
330 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + 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
ConvertMemoryPageAttributes(IN PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes,IN BOOLEAN IsSet,OUT BOOLEAN * IsSplitted,OPTIONAL OUT BOOLEAN * IsModified OPTIONAL)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
384 ASSERT (Attributes != 0);
385 ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0);
386
387 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
388 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
389
390 if (Length == 0) {
391 return RETURN_INVALID_PARAMETER;
392 }
393
394 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
395
396 if (IsSplitted != NULL) {
397 *IsSplitted = FALSE;
398 }
399 if (IsModified != NULL) {
400 *IsModified = FALSE;
401 }
402
403 //
404 // Below logic is to check 2M/4K page to make sure we donot waist memory.
405 //
406 while (Length != 0) {
407 PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);
408 if (PageEntry == NULL) {
409 return RETURN_UNSUPPORTED;
410 }
411 PageEntryLength = PageAttributeToLength (PageAttribute);
412 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);
413 if (SplitAttribute == PageNone) {
414 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);
415 if (IsEntryModified) {
416 if (IsModified != NULL) {
417 *IsModified = TRUE;
418 }
419 }
420 //
421 // Convert success, move to next
422 //
423 BaseAddress += PageEntryLength;
424 Length -= PageEntryLength;
425 } else {
426 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);
427 if (RETURN_ERROR (Status)) {
428 return RETURN_UNSUPPORTED;
429 }
430 if (IsSplitted != NULL) {
431 *IsSplitted = TRUE;
432 }
433 if (IsModified != NULL) {
434 *IsModified = TRUE;
435 }
436 //
437 // Just split current page
438 // Convert success in next around
439 //
440 }
441 }
442
443 return RETURN_SUCCESS;
444 }
445
446 /**
447 FlushTlb on current processor.
448
449 @param[in,out] Buffer Pointer to private data buffer.
450 **/
451 VOID
452 EFIAPI
FlushTlbOnCurrentProcessor(IN OUT VOID * Buffer)453 FlushTlbOnCurrentProcessor (
454 IN OUT VOID *Buffer
455 )
456 {
457 CpuFlushTlb ();
458 }
459
460 /**
461 FlushTlb for all processors.
462 **/
463 VOID
FlushTlbForAll(VOID)464 FlushTlbForAll (
465 VOID
466 )
467 {
468 UINTN Index;
469
470 FlushTlbOnCurrentProcessor (NULL);
471
472 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
473 if (Index != gSmst->CurrentlyExecutingCpu) {
474 // Force to start up AP in blocking mode,
475 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);
476 // Do not check return status, because AP might not be present in some corner cases.
477 }
478 }
479 }
480
481 /**
482 This function sets the attributes for the memory region specified by BaseAddress and
483 Length from their current attributes to the attributes specified by Attributes.
484
485 @param[in] BaseAddress The physical address that is the start address of a memory region.
486 @param[in] Length The size in bytes of the memory region.
487 @param[in] Attributes The bit mask of attributes to set for the memory region.
488 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
489
490 @retval EFI_SUCCESS The attributes were set for the memory region.
491 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
492 BaseAddress and Length cannot be modified.
493 @retval EFI_INVALID_PARAMETER Length is zero.
494 Attributes specified an illegal combination of attributes that
495 cannot be set together.
496 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
497 the memory resource range.
498 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
499 resource range specified by BaseAddress and Length.
500 The bit mask of attributes is not support for the memory resource
501 range specified by BaseAddress and Length.
502
503 **/
504 EFI_STATUS
505 EFIAPI
SmmSetMemoryAttributesEx(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes,OUT BOOLEAN * IsSplitted OPTIONAL)506 SmmSetMemoryAttributesEx (
507 IN EFI_PHYSICAL_ADDRESS BaseAddress,
508 IN UINT64 Length,
509 IN UINT64 Attributes,
510 OUT BOOLEAN *IsSplitted OPTIONAL
511 )
512 {
513 EFI_STATUS Status;
514 BOOLEAN IsModified;
515
516 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);
517 if (!EFI_ERROR(Status)) {
518 if (IsModified) {
519 //
520 // Flush TLB as last step
521 //
522 FlushTlbForAll();
523 }
524 }
525
526 return Status;
527 }
528
529 /**
530 This function clears the attributes for the memory region specified by BaseAddress and
531 Length from their current attributes to the attributes specified by Attributes.
532
533 @param[in] BaseAddress The physical address that is the start address of a memory region.
534 @param[in] Length The size in bytes of the memory region.
535 @param[in] Attributes The bit mask of attributes to clear for the memory region.
536 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
537
538 @retval EFI_SUCCESS The attributes were cleared for the memory region.
539 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
540 BaseAddress and Length cannot be modified.
541 @retval EFI_INVALID_PARAMETER Length is zero.
542 Attributes specified an illegal combination of attributes that
543 cannot be set together.
544 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
545 the memory resource range.
546 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
547 resource range specified by BaseAddress and Length.
548 The bit mask of attributes is not support for the memory resource
549 range specified by BaseAddress and Length.
550
551 **/
552 EFI_STATUS
553 EFIAPI
SmmClearMemoryAttributesEx(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes,OUT BOOLEAN * IsSplitted OPTIONAL)554 SmmClearMemoryAttributesEx (
555 IN EFI_PHYSICAL_ADDRESS BaseAddress,
556 IN UINT64 Length,
557 IN UINT64 Attributes,
558 OUT BOOLEAN *IsSplitted OPTIONAL
559 )
560 {
561 EFI_STATUS Status;
562 BOOLEAN IsModified;
563
564 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);
565 if (!EFI_ERROR(Status)) {
566 if (IsModified) {
567 //
568 // Flush TLB as last step
569 //
570 FlushTlbForAll();
571 }
572 }
573
574 return Status;
575 }
576
577 /**
578 This function sets the attributes for the memory region specified by BaseAddress and
579 Length from their current attributes to the attributes specified by Attributes.
580
581 @param[in] BaseAddress The physical address that is the start address of a memory region.
582 @param[in] Length The size in bytes of the memory region.
583 @param[in] Attributes The bit mask of attributes to set for the memory region.
584
585 @retval EFI_SUCCESS The attributes were set for the memory region.
586 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
587 BaseAddress and Length cannot be modified.
588 @retval EFI_INVALID_PARAMETER Length is zero.
589 Attributes specified an illegal combination of attributes that
590 cannot be set together.
591 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
592 the memory resource range.
593 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
594 resource range specified by BaseAddress and Length.
595 The bit mask of attributes is not support for the memory resource
596 range specified by BaseAddress and Length.
597
598 **/
599 EFI_STATUS
600 EFIAPI
SmmSetMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes)601 SmmSetMemoryAttributes (
602 IN EFI_PHYSICAL_ADDRESS BaseAddress,
603 IN UINT64 Length,
604 IN UINT64 Attributes
605 )
606 {
607 return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);
608 }
609
610 /**
611 This function clears the attributes for the memory region specified by BaseAddress and
612 Length from their current attributes to the attributes specified by Attributes.
613
614 @param[in] BaseAddress The physical address that is the start address of a memory region.
615 @param[in] Length The size in bytes of the memory region.
616 @param[in] Attributes The bit mask of attributes to clear for the memory region.
617
618 @retval EFI_SUCCESS The attributes were cleared for the memory region.
619 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
620 BaseAddress and Length cannot be modified.
621 @retval EFI_INVALID_PARAMETER Length is zero.
622 Attributes specified an illegal combination of attributes that
623 cannot be set together.
624 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
625 the memory resource range.
626 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
627 resource range specified by BaseAddress and Length.
628 The bit mask of attributes is not support for the memory resource
629 range specified by BaseAddress and Length.
630
631 **/
632 EFI_STATUS
633 EFIAPI
SmmClearMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes)634 SmmClearMemoryAttributes (
635 IN EFI_PHYSICAL_ADDRESS BaseAddress,
636 IN UINT64 Length,
637 IN UINT64 Attributes
638 )
639 {
640 return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);
641 }
642
643
644
645 /**
646 Retrieves a pointer to the system configuration table from the SMM System Table
647 based on a specified GUID.
648
649 @param[in] TableGuid The pointer to table's GUID type.
650 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.
651
652 @retval EFI_SUCCESS A configuration table matching TableGuid was found.
653 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.
654
655 **/
656 EFI_STATUS
657 EFIAPI
SmmGetSystemConfigurationTable(IN EFI_GUID * TableGuid,OUT VOID ** Table)658 SmmGetSystemConfigurationTable (
659 IN EFI_GUID *TableGuid,
660 OUT VOID **Table
661 )
662 {
663 UINTN Index;
664
665 ASSERT (TableGuid != NULL);
666 ASSERT (Table != NULL);
667
668 *Table = NULL;
669 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {
670 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {
671 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;
672 return EFI_SUCCESS;
673 }
674 }
675
676 return EFI_NOT_FOUND;
677 }
678
679 /**
680 This function sets SMM save state buffer to be RW and XP.
681 **/
682 VOID
PatchSmmSaveStateMap(VOID)683 PatchSmmSaveStateMap (
684 VOID
685 )
686 {
687 UINTN Index;
688 UINTN TileCodeSize;
689 UINTN TileDataSize;
690 UINTN TileSize;
691
692 TileCodeSize = GetSmiHandlerSize ();
693 TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);
694 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
695 TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);
696 TileSize = TileDataSize + TileCodeSize - 1;
697 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
698
699 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));
700 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {
701 //
702 // Code
703 //
704 SmmSetMemoryAttributes (
705 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
706 TileCodeSize,
707 EFI_MEMORY_RO
708 );
709 SmmClearMemoryAttributes (
710 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
711 TileCodeSize,
712 EFI_MEMORY_XP
713 );
714
715 //
716 // Data
717 //
718 SmmClearMemoryAttributes (
719 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
720 TileSize - TileCodeSize,
721 EFI_MEMORY_RO
722 );
723 SmmSetMemoryAttributes (
724 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
725 TileSize - TileCodeSize,
726 EFI_MEMORY_XP
727 );
728 }
729
730 //
731 // Code
732 //
733 SmmSetMemoryAttributes (
734 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
735 TileCodeSize,
736 EFI_MEMORY_RO
737 );
738 SmmClearMemoryAttributes (
739 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
740 TileCodeSize,
741 EFI_MEMORY_XP
742 );
743
744 //
745 // Data
746 //
747 SmmClearMemoryAttributes (
748 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
749 SIZE_32KB - TileCodeSize,
750 EFI_MEMORY_RO
751 );
752 SmmSetMemoryAttributes (
753 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
754 SIZE_32KB - TileCodeSize,
755 EFI_MEMORY_XP
756 );
757 }
758
759 /**
760 This function sets memory attribute according to MemoryAttributesTable.
761 **/
762 VOID
SetMemMapAttributes(VOID)763 SetMemMapAttributes (
764 VOID
765 )
766 {
767 EFI_MEMORY_DESCRIPTOR *MemoryMap;
768 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
769 UINTN MemoryMapEntryCount;
770 UINTN DescriptorSize;
771 UINTN Index;
772 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
773
774 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
775 if (MemoryAttributesTable == NULL) {
776 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));
777 return ;
778 }
779
780 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));
781 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));
782 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));
783 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));
784
785 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;
786 DescriptorSize = MemoryAttributesTable->DescriptorSize;
787 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);
788 MemoryMap = MemoryMapStart;
789 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
790 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));
791 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));
792 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));
793 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));
794 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));
795 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));
796 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
797 }
798
799 MemoryMap = MemoryMapStart;
800 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
801 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));
802 switch (MemoryMap->Type) {
803 case EfiRuntimeServicesCode:
804 SmmSetMemoryAttributes (
805 MemoryMap->PhysicalStart,
806 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
807 EFI_MEMORY_RO
808 );
809 break;
810 case EfiRuntimeServicesData:
811 SmmSetMemoryAttributes (
812 MemoryMap->PhysicalStart,
813 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
814 EFI_MEMORY_XP
815 );
816 break;
817 default:
818 SmmSetMemoryAttributes (
819 MemoryMap->PhysicalStart,
820 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
821 EFI_MEMORY_XP
822 );
823 break;
824 }
825 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
826 }
827
828 PatchSmmSaveStateMap ();
829 PatchGdtIdtMap ();
830
831 return ;
832 }
833
834 /**
835 Sort memory map entries based upon PhysicalStart, from low to high.
836
837 @param MemoryMap A pointer to the buffer in which firmware places
838 the current memory map.
839 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
840 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
841 **/
842 STATIC
843 VOID
SortMemoryMap(IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap,IN UINTN MemoryMapSize,IN UINTN DescriptorSize)844 SortMemoryMap (
845 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
846 IN UINTN MemoryMapSize,
847 IN UINTN DescriptorSize
848 )
849 {
850 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
851 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
852 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
853 EFI_MEMORY_DESCRIPTOR TempMemoryMap;
854
855 MemoryMapEntry = MemoryMap;
856 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
857 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
858 while (MemoryMapEntry < MemoryMapEnd) {
859 while (NextMemoryMapEntry < MemoryMapEnd) {
860 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
861 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
862 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
863 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));
864 }
865
866 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
867 }
868
869 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
870 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
871 }
872 }
873
874 /**
875 Return if a UEFI memory page should be marked as not present in SMM page table.
876 If the memory map entries type is
877 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
878 EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.
879 Or return FALSE.
880
881 @param[in] MemoryMap A pointer to the memory descriptor.
882
883 @return TRUE The memory described will be marked as not present in SMM page table.
884 @return FALSE The memory described will not be marked as not present in SMM page table.
885 **/
886 BOOLEAN
IsUefiPageNotPresent(IN EFI_MEMORY_DESCRIPTOR * MemoryMap)887 IsUefiPageNotPresent (
888 IN EFI_MEMORY_DESCRIPTOR *MemoryMap
889 )
890 {
891 switch (MemoryMap->Type) {
892 case EfiLoaderCode:
893 case EfiLoaderData:
894 case EfiBootServicesCode:
895 case EfiBootServicesData:
896 case EfiConventionalMemory:
897 case EfiUnusableMemory:
898 case EfiACPIReclaimMemory:
899 return TRUE;
900 default:
901 return FALSE;
902 }
903 }
904
905 /**
906 Merge continous memory map entries whose type is
907 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
908 EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by
909 these entries will be set as NOT present in SMM page table.
910
911 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
912 the current memory map.
913 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
914 MemoryMap buffer. On input, this is the size of
915 the current memory map. On output,
916 it is the size of new memory map after merge.
917 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
918 **/
919 STATIC
920 VOID
MergeMemoryMapForNotPresentEntry(IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap,IN OUT UINTN * MemoryMapSize,IN UINTN DescriptorSize)921 MergeMemoryMapForNotPresentEntry (
922 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
923 IN OUT UINTN *MemoryMapSize,
924 IN UINTN DescriptorSize
925 )
926 {
927 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
928 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
929 UINT64 MemoryBlockLength;
930 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
931 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
932
933 MemoryMapEntry = MemoryMap;
934 NewMemoryMapEntry = MemoryMap;
935 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);
936 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
937 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
938 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
939
940 do {
941 MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));
942 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
943 IsUefiPageNotPresent(MemoryMapEntry) && IsUefiPageNotPresent(NextMemoryMapEntry) &&
944 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {
945 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
946 if (NewMemoryMapEntry != MemoryMapEntry) {
947 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
948 }
949
950 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
951 continue;
952 } else {
953 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
954 break;
955 }
956 } while (TRUE);
957
958 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
959 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
960 }
961
962 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
963
964 return ;
965 }
966
967 /**
968 This function caches the UEFI memory map information.
969 **/
970 VOID
GetUefiMemoryMap(VOID)971 GetUefiMemoryMap (
972 VOID
973 )
974 {
975 EFI_STATUS Status;
976 UINTN MapKey;
977 UINT32 DescriptorVersion;
978 EFI_MEMORY_DESCRIPTOR *MemoryMap;
979 UINTN UefiMemoryMapSize;
980
981 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));
982
983 UefiMemoryMapSize = 0;
984 MemoryMap = NULL;
985 Status = gBS->GetMemoryMap (
986 &UefiMemoryMapSize,
987 MemoryMap,
988 &MapKey,
989 &mUefiDescriptorSize,
990 &DescriptorVersion
991 );
992 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
993
994 do {
995 Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);
996 ASSERT (MemoryMap != NULL);
997 if (MemoryMap == NULL) {
998 return ;
999 }
1000
1001 Status = gBS->GetMemoryMap (
1002 &UefiMemoryMapSize,
1003 MemoryMap,
1004 &MapKey,
1005 &mUefiDescriptorSize,
1006 &DescriptorVersion
1007 );
1008 if (EFI_ERROR (Status)) {
1009 gBS->FreePool (MemoryMap);
1010 MemoryMap = NULL;
1011 }
1012 } while (Status == EFI_BUFFER_TOO_SMALL);
1013
1014 if (MemoryMap == NULL) {
1015 return ;
1016 }
1017
1018 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);
1019 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);
1020
1021 mUefiMemoryMapSize = UefiMemoryMapSize;
1022 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);
1023 ASSERT (mUefiMemoryMap != NULL);
1024
1025 gBS->FreePool (MemoryMap);
1026 }
1027
1028 /**
1029 This function sets UEFI memory attribute according to UEFI memory map.
1030
1031 The normal memory region is marked as not present, such as
1032 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1033 EfiUnusableMemory, EfiACPIReclaimMemory.
1034 **/
1035 VOID
SetUefiMemMapAttributes(VOID)1036 SetUefiMemMapAttributes (
1037 VOID
1038 )
1039 {
1040 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1041 UINTN MemoryMapEntryCount;
1042 UINTN Index;
1043
1044 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
1045
1046 if (mUefiMemoryMap == NULL) {
1047 DEBUG ((DEBUG_INFO, "UefiMemoryMap - NULL\n"));
1048 return ;
1049 }
1050
1051 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1052 MemoryMap = mUefiMemoryMap;
1053 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1054 if (IsUefiPageNotPresent(MemoryMap)) {
1055 DEBUG ((DEBUG_INFO, "UefiMemory protection: 0x%lx - 0x%lx\n", MemoryMap->PhysicalStart, MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)));
1056 SmmSetMemoryAttributes (
1057 MemoryMap->PhysicalStart,
1058 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
1059 EFI_MEMORY_RP
1060 );
1061 }
1062 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);
1063 }
1064
1065 //
1066 // Do free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
1067 //
1068 }
1069
1070 /**
1071 Return if the Address is forbidden as SMM communication buffer.
1072
1073 @param[in] Address the address to be checked
1074
1075 @return TRUE The address is forbidden as SMM communication buffer.
1076 @return FALSE The address is allowed as SMM communication buffer.
1077 **/
1078 BOOLEAN
IsSmmCommBufferForbiddenAddress(IN UINT64 Address)1079 IsSmmCommBufferForbiddenAddress (
1080 IN UINT64 Address
1081 )
1082 {
1083 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1084 UINTN MemoryMapEntryCount;
1085 UINTN Index;
1086
1087 if (mUefiMemoryMap == NULL) {
1088 return FALSE;
1089 }
1090
1091 MemoryMap = mUefiMemoryMap;
1092 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1093 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1094 if (IsUefiPageNotPresent (MemoryMap)) {
1095 if ((Address >= MemoryMap->PhysicalStart) &&
1096 (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) {
1097 return TRUE;
1098 }
1099 }
1100 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);
1101 }
1102 return FALSE;
1103 }
1104