VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 47.2 KB
Line 
1/** @file
2
3Copyright (c) 2016 - 2024, Intel Corporation. All rights reserved.<BR>
4SPDX-License-Identifier: BSD-2-Clause-Patent
5
6**/
7
8#include "PiSmmCpuCommon.h"
9
10BOOLEAN mIsShadowStack = FALSE;
11BOOLEAN m5LevelPagingNeeded = FALSE;
12PAGING_MODE mPagingMode = PagingModeMax;
13
14//
15// Global variable to keep track current available memory used as page table.
16//
17PAGE_TABLE_POOL *mPageTablePool = NULL;
18
19//
20// If memory used by SMM page table has been mareked as ReadOnly.
21//
22BOOLEAN mIsReadOnlyPageTable = FALSE;
23
24/**
25 Write unprotect read-only pages if Cr0.Bits.WP is 1.
26
27 @param[out] WriteProtect If Cr0.Bits.WP is enabled.
28
29**/
30VOID
31SmmWriteUnprotectReadOnlyPage (
32 OUT BOOLEAN *WriteProtect
33 )
34{
35 IA32_CR0 Cr0;
36
37 Cr0.UintN = AsmReadCr0 ();
38 *WriteProtect = (Cr0.Bits.WP != 0);
39 if (*WriteProtect) {
40 Cr0.Bits.WP = 0;
41 AsmWriteCr0 (Cr0.UintN);
42 }
43}
44
45/**
46 Write protect read-only pages.
47
48 @param[in] WriteProtect If Cr0.Bits.WP should be enabled.
49
50**/
51VOID
52SmmWriteProtectReadOnlyPage (
53 IN BOOLEAN WriteProtect
54 )
55{
56 IA32_CR0 Cr0;
57
58 if (WriteProtect) {
59 Cr0.UintN = AsmReadCr0 ();
60 Cr0.Bits.WP = 1;
61 AsmWriteCr0 (Cr0.UintN);
62 }
63}
64
65/**
66 Initialize a buffer pool for page table use only.
67
68 To reduce the potential split operation on page table, the pages reserved for
69 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
70 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
71 initialized with number of pages greater than or equal to the given PoolPages.
72
73 Once the pages in the pool are used up, this method should be called again to
74 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't
75 happen in practice.
76
77 @param PoolPages The least page number of the pool to be created.
78
79 @retval TRUE The pool is initialized successfully.
80 @retval FALSE The memory is out of resource.
81**/
82BOOLEAN
83InitializePageTablePool (
84 IN UINTN PoolPages
85 )
86{
87 VOID *Buffer;
88 BOOLEAN WriteProtect;
89 BOOLEAN CetEnabled;
90
91 //
92 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
93 // header.
94 //
95 PoolPages += 1; // Add one page for header.
96 PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
97 PAGE_TABLE_POOL_UNIT_PAGES;
98 Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
99 if (Buffer == NULL) {
100 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
101 return FALSE;
102 }
103
104 //
105 // Link all pools into a list for easier track later.
106 //
107 if (mPageTablePool == NULL) {
108 mPageTablePool = Buffer;
109 mPageTablePool->NextPool = mPageTablePool;
110 } else {
111 ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
112 mPageTablePool->NextPool = Buffer;
113 mPageTablePool = Buffer;
114 }
115
116 //
117 // Reserve one page for pool header.
118 //
119 mPageTablePool->FreePages = PoolPages - 1;
120 mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
121
122 //
123 // If page table memory has been marked as RO, mark the new pool pages as read-only.
124 //
125 if (mIsReadOnlyPageTable) {
126 WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
127
128 SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);
129
130 WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
131 }
132
133 return TRUE;
134}
135
136/**
137 This API provides a way to allocate memory for page table.
138
139 This API can be called more once to allocate memory for page tables.
140
141 Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
142 allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
143 is returned. If there is not enough memory remaining to satisfy the request, then NULL is
144 returned.
145
146 @param Pages The number of 4 KB pages to allocate.
147
148 @return A pointer to the allocated buffer or NULL if allocation fails.
149
150**/
151VOID *
152AllocatePageTableMemory (
153 IN UINTN Pages
154 )
155{
156 VOID *Buffer;
157
158 if (Pages == 0) {
159 return NULL;
160 }
161
162 //
163 // Renew the pool if necessary.
164 //
165 if ((mPageTablePool == NULL) ||
166 (Pages > mPageTablePool->FreePages))
167 {
168 if (!InitializePageTablePool (Pages)) {
169 return NULL;
170 }
171 }
172
173 Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
174
175 mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
176 mPageTablePool->FreePages -= Pages;
177
178 return Buffer;
179}
180
181/**
182 Return page table entry to match the address.
183
184 @param[in] PageTableBase The page table base.
185 @param[in] Enable5LevelPaging If PML5 paging is enabled.
186 @param[in] Address The address to be checked.
187 @param[out] PageAttributes The page attribute of the page entry.
188
189 @return The page entry.
190**/
191VOID *
192GetPageTableEntry (
193 IN UINTN PageTableBase,
194 IN BOOLEAN Enable5LevelPaging,
195 IN PHYSICAL_ADDRESS Address,
196 OUT PAGE_ATTRIBUTE *PageAttribute
197 )
198{
199 UINTN Index1;
200 UINTN Index2;
201 UINTN Index3;
202 UINTN Index4;
203 UINTN Index5;
204 UINT64 *L1PageTable;
205 UINT64 *L2PageTable;
206 UINT64 *L3PageTable;
207 UINT64 *L4PageTable;
208 UINT64 *L5PageTable;
209
210 Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;
211 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
212 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
213 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
214 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
215
216 if (sizeof (UINTN) == sizeof (UINT64)) {
217 if (Enable5LevelPaging) {
218 L5PageTable = (UINT64 *)PageTableBase;
219 if (L5PageTable[Index5] == 0) {
220 *PageAttribute = PageNone;
221 return NULL;
222 }
223
224 L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
225 } else {
226 L4PageTable = (UINT64 *)PageTableBase;
227 }
228
229 if (L4PageTable[Index4] == 0) {
230 *PageAttribute = PageNone;
231 return NULL;
232 }
233
234 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
235 } else {
236 L3PageTable = (UINT64 *)PageTableBase;
237 }
238
239 if (L3PageTable[Index3] == 0) {
240 *PageAttribute = PageNone;
241 return NULL;
242 }
243
244 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
245 // 1G
246 *PageAttribute = Page1G;
247 return &L3PageTable[Index3];
248 }
249
250 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
251 if (L2PageTable[Index2] == 0) {
252 *PageAttribute = PageNone;
253 return NULL;
254 }
255
256 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
257 // 2M
258 *PageAttribute = Page2M;
259 return &L2PageTable[Index2];
260 }
261
262 // 4k
263 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
264 if ((L1PageTable[Index1] == 0) && (Address != 0)) {
265 *PageAttribute = PageNone;
266 return NULL;
267 }
268
269 *PageAttribute = Page4K;
270 return &L1PageTable[Index1];
271}
272
273/**
274 Return memory attributes of page entry.
275
276 @param[in] PageEntry The page entry.
277
278 @return Memory attributes of page entry.
279**/
280UINT64
281GetAttributesFromPageEntry (
282 IN UINT64 *PageEntry
283 )
284{
285 UINT64 Attributes;
286
287 Attributes = 0;
288 if ((*PageEntry & IA32_PG_P) == 0) {
289 Attributes |= EFI_MEMORY_RP;
290 }
291
292 if ((*PageEntry & IA32_PG_RW) == 0) {
293 Attributes |= EFI_MEMORY_RO;
294 }
295
296 if ((*PageEntry & IA32_PG_NX) != 0) {
297 Attributes |= EFI_MEMORY_XP;
298 }
299
300 return Attributes;
301}
302
303/**
304 This function modifies the page attributes for the memory region specified by BaseAddress and
305 Length from their current attributes to the attributes specified by Attributes.
306
307 Caller should make sure BaseAddress and Length is at page boundary.
308
309 @param[in] PageTableBase The page table base.
310 @param[in] PagingMode The paging mode.
311 @param[in] BaseAddress The physical address that is the start address of a memory region.
312 @param[in] Length The size in bytes of the memory region.
313 @param[in] Attributes The bit mask of attributes to modify for the memory region.
314 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
315 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
316
317 @retval RETURN_SUCCESS The attributes were modified for the memory region.
318 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
319 BaseAddress and Length cannot be modified.
320 @retval RETURN_INVALID_PARAMETER Length is zero.
321 Attributes specified an illegal combination of attributes that
322 cannot be set together.
323 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
324 the memory resource range.
325 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
326 resource range specified by BaseAddress and Length.
327 The bit mask of attributes is not support for the memory resource
328 range specified by BaseAddress and Length.
329**/
330RETURN_STATUS
331ConvertMemoryPageAttributes (
332 IN UINTN PageTableBase,
333 IN PAGING_MODE PagingMode,
334 IN PHYSICAL_ADDRESS BaseAddress,
335 IN UINT64 Length,
336 IN UINT64 Attributes,
337 IN BOOLEAN IsSet,
338 OUT BOOLEAN *IsModified OPTIONAL
339 )
340{
341 RETURN_STATUS Status;
342 IA32_MAP_ATTRIBUTE PagingAttribute;
343 IA32_MAP_ATTRIBUTE PagingAttrMask;
344 UINTN PageTableBufferSize;
345 VOID *PageTableBuffer;
346 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;
347 IA32_MAP_ENTRY *Map;
348 UINTN Count;
349 UINTN Index;
350 UINT64 OverlappedRangeBase;
351 UINT64 OverlappedRangeLimit;
352
353 ASSERT (Attributes != 0);
354 ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);
355
356 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
357 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
358 ASSERT (PageTableBase != 0);
359
360 if (Length == 0) {
361 return RETURN_INVALID_PARAMETER;
362 }
363
364 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);
365 if (BaseAddress > MaximumSupportMemAddress) {
366 return RETURN_UNSUPPORTED;
367 }
368
369 if (Length > MaximumSupportMemAddress) {
370 return RETURN_UNSUPPORTED;
371 }
372
373 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {
374 return RETURN_UNSUPPORTED;
375 }
376
377 if (IsModified != NULL) {
378 *IsModified = FALSE;
379 }
380
381 PagingAttribute.Uint64 = 0;
382 PagingAttribute.Uint64 = mAddressEncMask | BaseAddress;
383 PagingAttrMask.Uint64 = 0;
384
385 if ((Attributes & EFI_MEMORY_RO) != 0) {
386 PagingAttrMask.Bits.ReadWrite = 1;
387 if (IsSet) {
388 PagingAttribute.Bits.ReadWrite = 0;
389 PagingAttrMask.Bits.Dirty = 1;
390 if (mIsShadowStack) {
391 // Environment setup
392 // ReadOnly page need set Dirty bit for shadow stack
393 PagingAttribute.Bits.Dirty = 1;
394 // Clear user bit for supervisor shadow stack
395 PagingAttribute.Bits.UserSupervisor = 0;
396 PagingAttrMask.Bits.UserSupervisor = 1;
397 } else {
398 // Runtime update
399 // Clear dirty bit for non shadow stack, to protect RO page.
400 PagingAttribute.Bits.Dirty = 0;
401 }
402 } else {
403 PagingAttribute.Bits.ReadWrite = 1;
404 }
405 }
406
407 if ((Attributes & EFI_MEMORY_XP) != 0) {
408 if (mXdSupported) {
409 PagingAttribute.Bits.Nx = IsSet ? 1 : 0;
410 PagingAttrMask.Bits.Nx = 1;
411 }
412 }
413
414 if ((Attributes & EFI_MEMORY_RP) != 0) {
415 if (IsSet) {
416 PagingAttribute.Bits.Present = 0;
417 //
418 // When map a range to non-present, all attributes except Present should not be provided.
419 //
420 PagingAttrMask.Uint64 = 0;
421 PagingAttrMask.Bits.Present = 1;
422 } else {
423 //
424 // When map range to present range, provide all attributes.
425 //
426 PagingAttribute.Bits.Present = 1;
427 PagingAttrMask.Uint64 = MAX_UINT64;
428
429 //
430 // By default memory is Ring 3 accessble.
431 //
432 PagingAttribute.Bits.UserSupervisor = 1;
433
434 DEBUG_CODE_BEGIN ();
435 if (((Attributes & EFI_MEMORY_RO) == 0) || (((Attributes & EFI_MEMORY_XP) == 0) && (mXdSupported))) {
436 //
437 // When mapping a range to present and EFI_MEMORY_RO or EFI_MEMORY_XP is not specificed,
438 // check if [BaseAddress, BaseAddress + Length] contains present range.
439 // Existing Present range in [BaseAddress, BaseAddress + Length] is set to NX disable or ReadOnly.
440 //
441 Count = 0;
442 Map = NULL;
443 Status = PageTableParse (PageTableBase, mPagingMode, NULL, &Count);
444
445 while (Status == RETURN_BUFFER_TOO_SMALL) {
446 if (Map != NULL) {
447 FreePool (Map);
448 }
449
450 Map = AllocatePool (Count * sizeof (IA32_MAP_ENTRY));
451 ASSERT (Map != NULL);
452 Status = PageTableParse (PageTableBase, mPagingMode, Map, &Count);
453 }
454
455 ASSERT_RETURN_ERROR (Status);
456 for (Index = 0; Index < Count; Index++) {
457 if (Map[Index].LinearAddress >= BaseAddress + Length) {
458 break;
459 }
460
461 if ((BaseAddress < Map[Index].LinearAddress + Map[Index].Length) && (BaseAddress + Length > Map[Index].LinearAddress)) {
462 OverlappedRangeBase = MAX (BaseAddress, Map[Index].LinearAddress);
463 OverlappedRangeLimit = MIN (BaseAddress + Length, Map[Index].LinearAddress + Map[Index].Length);
464
465 if (((Attributes & EFI_MEMORY_RO) == 0) && (Map[Index].Attribute.Bits.ReadWrite == 1)) {
466 DEBUG ((DEBUG_ERROR, "SMM ConvertMemoryPageAttributes: [0x%lx, 0x%lx] is set from ReadWrite to ReadOnly\n", OverlappedRangeBase, OverlappedRangeLimit));
467 }
468
469 if (((Attributes & EFI_MEMORY_XP) == 0) && (mXdSupported) && (Map[Index].Attribute.Bits.Nx == 1)) {
470 DEBUG ((DEBUG_ERROR, "SMM ConvertMemoryPageAttributes: [0x%lx, 0x%lx] is set from NX enabled to NX disabled\n", OverlappedRangeBase, OverlappedRangeLimit));
471 }
472 }
473 }
474
475 FreePool (Map);
476 }
477
478 DEBUG_CODE_END ();
479 }
480 }
481
482 if (PagingAttrMask.Uint64 == 0) {
483 return RETURN_SUCCESS;
484 }
485
486 PageTableBufferSize = 0;
487 Status = PageTableMap (&PageTableBase, PagingMode, NULL, &PageTableBufferSize, BaseAddress, Length, &PagingAttribute, &PagingAttrMask, IsModified);
488
489 if (Status == RETURN_BUFFER_TOO_SMALL) {
490 PageTableBuffer = AllocatePageTableMemory (EFI_SIZE_TO_PAGES (PageTableBufferSize));
491 ASSERT (PageTableBuffer != NULL);
492 Status = PageTableMap (&PageTableBase, PagingMode, PageTableBuffer, &PageTableBufferSize, BaseAddress, Length, &PagingAttribute, &PagingAttrMask, IsModified);
493 }
494
495 if (Status == RETURN_INVALID_PARAMETER) {
496 //
497 // The only reason that PageTableMap returns RETURN_INVALID_PARAMETER here is to modify other attributes
498 // of a non-present range but remains the non-present range still as non-present.
499 //
500 DEBUG ((DEBUG_ERROR, "SMM ConvertMemoryPageAttributes: Only change EFI_MEMORY_XP/EFI_MEMORY_RO for non-present range in [0x%lx, 0x%lx] is not permitted\n", BaseAddress, BaseAddress + Length));
501 }
502
503 ASSERT_RETURN_ERROR (Status);
504 ASSERT (PageTableBufferSize == 0);
505
506 return RETURN_SUCCESS;
507}
508
509/**
510 FlushTlb on current processor.
511
512 @param[in,out] Buffer Pointer to private data buffer.
513**/
514VOID
515EFIAPI
516FlushTlbOnCurrentProcessor (
517 IN OUT VOID *Buffer
518 )
519{
520 CpuFlushTlb ();
521}
522
523/**
524 FlushTlb for all processors.
525**/
526VOID
527FlushTlbForAll (
528 VOID
529 )
530{
531 FlushTlbOnCurrentProcessor (NULL);
532 InternalSmmStartupAllAPs (
533 (EFI_AP_PROCEDURE2)FlushTlbOnCurrentProcessor,
534 0,
535 NULL,
536 NULL,
537 NULL
538 );
539}
540
541/**
542 This function sets 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] PageTableBase The page table base.
546 @param[in] PagingMode The paging mode.
547 @param[in] BaseAddress The physical address that is the start address of a memory region.
548 @param[in] Length The size in bytes of the memory region.
549 @param[in] Attributes The bit mask of attributes to set for the memory region.
550
551 @retval EFI_SUCCESS The attributes were set for the memory region.
552 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
553 BaseAddress and Length cannot be modified.
554 @retval EFI_INVALID_PARAMETER Length is zero.
555 Attributes specified an illegal combination of attributes that
556 cannot be set together.
557 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
558 the memory resource range.
559 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
560 resource range specified by BaseAddress and Length.
561 The bit mask of attributes is not support for the memory resource
562 range specified by BaseAddress and Length.
563
564**/
565EFI_STATUS
566SmmSetMemoryAttributesEx (
567 IN UINTN PageTableBase,
568 IN PAGING_MODE PagingMode,
569 IN EFI_PHYSICAL_ADDRESS BaseAddress,
570 IN UINT64 Length,
571 IN UINT64 Attributes
572 )
573{
574 EFI_STATUS Status;
575 BOOLEAN IsModified;
576
577 Status = ConvertMemoryPageAttributes (PageTableBase, PagingMode, BaseAddress, Length, Attributes, TRUE, &IsModified);
578 if (!EFI_ERROR (Status)) {
579 if (IsModified) {
580 //
581 // Flush TLB as last step
582 //
583 FlushTlbForAll ();
584 }
585 }
586
587 return Status;
588}
589
590/**
591 This function clears the attributes for the memory region specified by BaseAddress and
592 Length from their current attributes to the attributes specified by Attributes.
593
594 @param[in] PageTableBase The page table base.
595 @param[in] PagingMode The paging mode.
596 @param[in] BaseAddress The physical address that is the start address of a memory region.
597 @param[in] Length The size in bytes of the memory region.
598 @param[in] Attributes The bit mask of attributes to clear for the memory region.
599
600 @retval EFI_SUCCESS The attributes were cleared for the memory region.
601 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
602 BaseAddress and Length cannot be modified.
603 @retval EFI_INVALID_PARAMETER Length is zero.
604 Attributes specified an illegal combination of attributes that
605 cannot be cleared together.
606 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
607 the memory resource range.
608 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
609 resource range specified by BaseAddress and Length.
610 The bit mask of attributes is not supported for the memory resource
611 range specified by BaseAddress and Length.
612
613**/
614EFI_STATUS
615SmmClearMemoryAttributesEx (
616 IN UINTN PageTableBase,
617 IN PAGING_MODE PagingMode,
618 IN EFI_PHYSICAL_ADDRESS BaseAddress,
619 IN UINT64 Length,
620 IN UINT64 Attributes
621 )
622{
623 EFI_STATUS Status;
624 BOOLEAN IsModified;
625
626 Status = ConvertMemoryPageAttributes (PageTableBase, PagingMode, BaseAddress, Length, Attributes, FALSE, &IsModified);
627 if (!EFI_ERROR (Status)) {
628 if (IsModified) {
629 //
630 // Flush TLB as last step
631 //
632 FlushTlbForAll ();
633 }
634 }
635
636 return Status;
637}
638
639/**
640 This function sets the attributes for the memory region specified by BaseAddress and
641 Length from their current attributes to the attributes specified by Attributes.
642
643 @param[in] BaseAddress The physical address that is the start address of a memory region.
644 @param[in] Length The size in bytes of the memory region.
645 @param[in] Attributes The bit mask of attributes to set for the memory region.
646
647 @retval EFI_SUCCESS The attributes were set for the memory region.
648 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
649 BaseAddress and Length cannot be modified.
650 @retval EFI_INVALID_PARAMETER Length is zero.
651 Attributes specified an illegal combination of attributes that
652 cannot be set together.
653 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
654 the memory resource range.
655 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
656 resource range specified by BaseAddress and Length.
657 The bit mask of attributes is not supported for the memory resource
658 range specified by BaseAddress and Length.
659
660**/
661EFI_STATUS
662SmmSetMemoryAttributes (
663 IN EFI_PHYSICAL_ADDRESS BaseAddress,
664 IN UINT64 Length,
665 IN UINT64 Attributes
666 )
667{
668 UINTN PageTableBase;
669
670 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
671 return SmmSetMemoryAttributesEx (PageTableBase, mPagingMode, BaseAddress, Length, Attributes);
672}
673
674/**
675 This function clears the attributes for the memory region specified by BaseAddress and
676 Length from their current attributes to the attributes specified by Attributes.
677
678 @param[in] BaseAddress The physical address that is the start address of a memory region.
679 @param[in] Length The size in bytes of the memory region.
680 @param[in] Attributes The bit mask of attributes to clear for the memory region.
681
682 @retval EFI_SUCCESS The attributes were cleared for the memory region.
683 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
684 BaseAddress and Length cannot be modified.
685 @retval EFI_INVALID_PARAMETER Length is zero.
686 Attributes specified an illegal combination of attributes that
687 cannot be cleared together.
688 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
689 the memory resource range.
690 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
691 resource range specified by BaseAddress and Length.
692 The bit mask of attributes is not supported for the memory resource
693 range specified by BaseAddress and Length.
694
695**/
696EFI_STATUS
697SmmClearMemoryAttributes (
698 IN EFI_PHYSICAL_ADDRESS BaseAddress,
699 IN UINT64 Length,
700 IN UINT64 Attributes
701 )
702{
703 UINTN PageTableBase;
704
705 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
706 return SmmClearMemoryAttributesEx (PageTableBase, mPagingMode, BaseAddress, Length, Attributes);
707}
708
709/**
710 Set ShadowStack memory.
711
712 @param[in] Cr3 The page table base address.
713 @param[in] BaseAddress The physical address that is the start address of a memory region.
714 @param[in] Length The size in bytes of the memory region.
715
716 @retval EFI_SUCCESS The shadow stack memory is set.
717**/
718EFI_STATUS
719SetShadowStack (
720 IN UINTN Cr3,
721 IN EFI_PHYSICAL_ADDRESS BaseAddress,
722 IN UINT64 Length
723 )
724{
725 EFI_STATUS Status;
726
727 mIsShadowStack = TRUE;
728 Status = SmmSetMemoryAttributesEx (Cr3, mPagingMode, BaseAddress, Length, EFI_MEMORY_RO);
729 mIsShadowStack = FALSE;
730
731 return Status;
732}
733
734/**
735 Retrieves a pointer to the system configuration table from the SMM System Table
736 based on a specified GUID.
737
738 @param[in] TableGuid The pointer to table's GUID type.
739 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.
740
741 @retval EFI_SUCCESS A configuration table matching TableGuid was found.
742 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.
743
744**/
745EFI_STATUS
746EFIAPI
747SmmGetSystemConfigurationTable (
748 IN EFI_GUID *TableGuid,
749 OUT VOID **Table
750 )
751{
752 UINTN Index;
753
754 ASSERT (TableGuid != NULL);
755 ASSERT (Table != NULL);
756
757 *Table = NULL;
758 for (Index = 0; Index < gMmst->NumberOfTableEntries; Index++) {
759 if (CompareGuid (TableGuid, &(gMmst->MmConfigurationTable[Index].VendorGuid))) {
760 *Table = gMmst->MmConfigurationTable[Index].VendorTable;
761 return EFI_SUCCESS;
762 }
763 }
764
765 return EFI_NOT_FOUND;
766}
767
768/**
769 This function sets SMM save state buffer to be RW and XP.
770**/
771VOID
772PatchSmmSaveStateMap (
773 VOID
774 )
775{
776 UINTN Index;
777 UINTN TileCodeSize;
778 UINTN TileDataSize;
779 UINTN TileSize;
780 UINTN PageTableBase;
781
782 TileCodeSize = GetSmiHandlerSize ();
783 TileCodeSize = ALIGN_VALUE (TileCodeSize, SIZE_4KB);
784 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
785 TileDataSize = ALIGN_VALUE (TileDataSize, SIZE_4KB);
786 TileSize = TileDataSize + TileCodeSize - 1;
787 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
788 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
789
790 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));
791 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {
792 //
793 // Code
794 //
795 ConvertMemoryPageAttributes (
796 PageTableBase,
797 mPagingMode,
798 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
799 TileCodeSize,
800 EFI_MEMORY_RO,
801 TRUE,
802 NULL
803 );
804 ConvertMemoryPageAttributes (
805 PageTableBase,
806 mPagingMode,
807 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
808 TileCodeSize,
809 EFI_MEMORY_XP,
810 FALSE,
811 NULL
812 );
813
814 //
815 // Data
816 //
817 ConvertMemoryPageAttributes (
818 PageTableBase,
819 mPagingMode,
820 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
821 TileSize - TileCodeSize,
822 EFI_MEMORY_RO,
823 FALSE,
824 NULL
825 );
826 ConvertMemoryPageAttributes (
827 PageTableBase,
828 mPagingMode,
829 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
830 TileSize - TileCodeSize,
831 EFI_MEMORY_XP,
832 TRUE,
833 NULL
834 );
835 }
836
837 //
838 // Code
839 //
840 ConvertMemoryPageAttributes (
841 PageTableBase,
842 mPagingMode,
843 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
844 TileCodeSize,
845 EFI_MEMORY_RO,
846 TRUE,
847 NULL
848 );
849 ConvertMemoryPageAttributes (
850 PageTableBase,
851 mPagingMode,
852 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
853 TileCodeSize,
854 EFI_MEMORY_XP,
855 FALSE,
856 NULL
857 );
858
859 //
860 // Data
861 //
862 ConvertMemoryPageAttributes (
863 PageTableBase,
864 mPagingMode,
865 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
866 SIZE_32KB - TileCodeSize,
867 EFI_MEMORY_RO,
868 FALSE,
869 NULL
870 );
871 ConvertMemoryPageAttributes (
872 PageTableBase,
873 mPagingMode,
874 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
875 SIZE_32KB - TileCodeSize,
876 EFI_MEMORY_XP,
877 TRUE,
878 NULL
879 );
880
881 FlushTlbForAll ();
882}
883
884/**
885 This function sets GDT/IDT buffer to be RO and XP.
886**/
887VOID
888PatchGdtIdtMap (
889 VOID
890 )
891{
892 EFI_PHYSICAL_ADDRESS BaseAddress;
893 UINTN Size;
894
895 //
896 // GDT
897 //
898 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));
899
900 BaseAddress = mGdtBuffer;
901 Size = ALIGN_VALUE (mGdtBufferSize, SIZE_4KB);
902 //
903 // The range should have been set to RO
904 // if it is allocated with EfiRuntimeServicesCode.
905 //
906 SmmSetMemoryAttributes (
907 BaseAddress,
908 Size,
909 EFI_MEMORY_XP
910 );
911
912 //
913 // IDT
914 //
915 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));
916
917 BaseAddress = gcSmiIdtr.Base;
918 Size = ALIGN_VALUE (gcSmiIdtr.Limit + 1, SIZE_4KB);
919 //
920 // The range should have been set to RO
921 // if it is allocated with EfiRuntimeServicesCode.
922 //
923 SmmSetMemoryAttributes (
924 BaseAddress,
925 Size,
926 EFI_MEMORY_XP
927 );
928}
929
930/**
931 This function set [Base, Limit] to the input MemoryAttribute.
932
933 @param Base Start address of range.
934 @param Limit Limit address of range.
935 @param Attribute The bit mask of attributes to modify for the memory region.
936 @param Map Pointer to the array of Cr3 IA32_MAP_ENTRY.
937 @param Count Count of IA32_MAP_ENTRY in Map.
938**/
939VOID
940SetMemMapWithNonPresentRange (
941 UINT64 Base,
942 UINT64 Limit,
943 UINT64 Attribute,
944 IA32_MAP_ENTRY *Map,
945 UINTN Count
946 )
947{
948 UINTN Index;
949 UINT64 NonPresentRangeStart;
950
951 NonPresentRangeStart = 0;
952 for (Index = 0; Index < Count; Index++) {
953 if ((Map[Index].LinearAddress > NonPresentRangeStart) &&
954 (Base < Map[Index].LinearAddress) && (Limit > NonPresentRangeStart))
955 {
956 //
957 // We should NOT set attributes for non-present ragne.
958 //
959 //
960 // There is a non-present ( [NonPresentStart, Map[Index].LinearAddress] ) range before current Map[Index]
961 // and it is overlapped with [Base, Limit].
962 //
963 if (Base < NonPresentRangeStart) {
964 SmmSetMemoryAttributes (
965 Base,
966 NonPresentRangeStart - Base,
967 Attribute
968 );
969 }
970
971 Base = Map[Index].LinearAddress;
972 }
973
974 NonPresentRangeStart = Map[Index].LinearAddress + Map[Index].Length;
975 if (NonPresentRangeStart >= Limit) {
976 break;
977 }
978 }
979
980 Limit = MIN (NonPresentRangeStart, Limit);
981
982 if (Base < Limit) {
983 //
984 // There is no non-present range in current [Base, Limit] anymore.
985 //
986 SmmSetMemoryAttributes (
987 Base,
988 Limit - Base,
989 Attribute
990 );
991 }
992}
993
994/**
995 This function sets memory attribute according to MemoryAttributesTable.
996
997 @param MemoryAttributesTable A pointer to the buffer of SmmMemoryAttributesTable.
998
999**/
1000VOID
1001SetMemMapAttributes (
1002 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable
1003 )
1004{
1005 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1006 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
1007 UINTN MemoryMapEntryCount;
1008 UINTN DescriptorSize;
1009 UINTN Index;
1010 UINTN PageTable;
1011 EFI_STATUS Status;
1012 IA32_MAP_ENTRY *Map;
1013 UINTN Count;
1014 UINT64 MemoryAttribute;
1015 BOOLEAN WriteProtect;
1016 BOOLEAN CetEnabled;
1017
1018 ASSERT (MemoryAttributesTable != NULL);
1019
1020 PERF_FUNCTION_BEGIN ();
1021
1022 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));
1023 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));
1024 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));
1025 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));
1026
1027 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;
1028 DescriptorSize = MemoryAttributesTable->DescriptorSize;
1029 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);
1030 MemoryMap = MemoryMapStart;
1031 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1032 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));
1033 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));
1034 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));
1035 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));
1036 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));
1037 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));
1038 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
1039 }
1040
1041 Count = 0;
1042 Map = NULL;
1043 PageTable = AsmReadCr3 ();
1044 Status = PageTableParse (PageTable, mPagingMode, NULL, &Count);
1045 while (Status == RETURN_BUFFER_TOO_SMALL) {
1046 if (Map != NULL) {
1047 FreePool (Map);
1048 }
1049
1050 Map = AllocatePool (Count * sizeof (IA32_MAP_ENTRY));
1051 ASSERT (Map != NULL);
1052 Status = PageTableParse (PageTable, mPagingMode, Map, &Count);
1053 }
1054
1055 ASSERT_RETURN_ERROR (Status);
1056
1057 WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
1058
1059 MemoryMap = MemoryMapStart;
1060 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1061 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));
1062 MemoryAttribute = MemoryMap->Attribute & EFI_MEMORY_ACCESS_MASK;
1063 if (MemoryAttribute == 0) {
1064 if (MemoryMap->Type == EfiRuntimeServicesCode) {
1065 MemoryAttribute = EFI_MEMORY_RO;
1066 } else {
1067 ASSERT ((MemoryMap->Type == EfiRuntimeServicesData) || (MemoryMap->Type == EfiConventionalMemory));
1068 //
1069 // Set other type memory as NX.
1070 //
1071 MemoryAttribute = EFI_MEMORY_XP;
1072 }
1073 }
1074
1075 //
1076 // There may exist non-present range overlaps with the MemoryMap range.
1077 // Do not change other attributes of non-present range while still remaining it as non-present
1078 //
1079 SetMemMapWithNonPresentRange (
1080 MemoryMap->PhysicalStart,
1081 MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
1082 MemoryAttribute,
1083 Map,
1084 Count
1085 );
1086
1087 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
1088 }
1089
1090 WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
1091
1092 FreePool (Map);
1093
1094 PatchSmmSaveStateMap ();
1095 PatchGdtIdtMap ();
1096
1097 PERF_FUNCTION_END ();
1098}
1099
1100/**
1101 This function set given attributes of the memory region specified by
1102 BaseAddress and Length.
1103
1104 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1105 @param BaseAddress The physical address that is the start address of
1106 a memory region.
1107 @param Length The size in bytes of the memory region.
1108 @param Attributes The bit mask of attributes to set for the memory
1109 region.
1110
1111 @retval EFI_SUCCESS The attributes were set for the memory region.
1112 @retval EFI_INVALID_PARAMETER Length is zero.
1113 Attributes specified an illegal combination of
1114 attributes that cannot be set together.
1115 @retval EFI_UNSUPPORTED The processor does not support one or more
1116 bytes of the memory resource range specified
1117 by BaseAddress and Length.
1118 The bit mask of attributes is not supported for
1119 the memory resource range specified by
1120 BaseAddress and Length.
1121
1122**/
1123EFI_STATUS
1124EFIAPI
1125EdkiiSmmSetMemoryAttributes (
1126 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1127 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1128 IN UINT64 Length,
1129 IN UINT64 Attributes
1130 )
1131{
1132 return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);
1133}
1134
1135/**
1136 This function clears given attributes of the memory region specified by
1137 BaseAddress and Length.
1138
1139 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1140 @param BaseAddress The physical address that is the start address of
1141 a memory region.
1142 @param Length The size in bytes of the memory region.
1143 @param Attributes The bit mask of attributes to clear for the memory
1144 region.
1145
1146 @retval EFI_SUCCESS The attributes were cleared for the memory region.
1147 @retval EFI_INVALID_PARAMETER Length is zero.
1148 Attributes specified an illegal combination of
1149 attributes that cannot be cleared together.
1150 @retval EFI_UNSUPPORTED The processor does not support one or more
1151 bytes of the memory resource range specified
1152 by BaseAddress and Length.
1153 The bit mask of attributes is not supported for
1154 the memory resource range specified by
1155 BaseAddress and Length.
1156
1157**/
1158EFI_STATUS
1159EFIAPI
1160EdkiiSmmClearMemoryAttributes (
1161 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1162 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1163 IN UINT64 Length,
1164 IN UINT64 Attributes
1165 )
1166{
1167 return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);
1168}
1169
1170/**
1171 Create page table based on input PagingMode, LinearAddress and Length.
1172
1173 @param[in, out] PageTable The pointer to the page table.
1174 @param[in] PagingMode The paging mode.
1175 @param[in] LinearAddress The start of the linear address range.
1176 @param[in] Length The length of the linear address range.
1177 @param[in] MapAttribute The MapAttribute of the linear address range
1178 @param[in] MapMask The MapMask used for attribute. The corresponding field in Attribute is ignored if that in MapMask is 0.
1179
1180**/
1181VOID
1182GenPageTable (
1183 IN OUT UINTN *PageTable,
1184 IN PAGING_MODE PagingMode,
1185 IN UINT64 LinearAddress,
1186 IN UINT64 Length,
1187 IN IA32_MAP_ATTRIBUTE MapAttribute,
1188 IN IA32_MAP_ATTRIBUTE MapMask
1189 )
1190{
1191 RETURN_STATUS Status;
1192 UINTN PageTableBufferSize;
1193 VOID *PageTableBuffer;
1194
1195 PageTableBufferSize = 0;
1196
1197 Status = PageTableMap (
1198 PageTable,
1199 PagingMode,
1200 NULL,
1201 &PageTableBufferSize,
1202 LinearAddress,
1203 Length,
1204 &MapAttribute,
1205 &MapMask,
1206 NULL
1207 );
1208 if (Status == RETURN_BUFFER_TOO_SMALL) {
1209 PageTableBuffer = AllocatePageTableMemory (EFI_SIZE_TO_PAGES (PageTableBufferSize));
1210 ASSERT (PageTableBuffer != NULL);
1211 Status = PageTableMap (
1212 PageTable,
1213 PagingMode,
1214 PageTableBuffer,
1215 &PageTableBufferSize,
1216 LinearAddress,
1217 Length,
1218 &MapAttribute,
1219 &MapMask,
1220 NULL
1221 );
1222 }
1223
1224 ASSERT (Status == RETURN_SUCCESS);
1225 ASSERT (PageTableBufferSize == 0);
1226}
1227
1228/**
1229 Create page table based on input PagingMode and PhysicalAddressBits in smm.
1230
1231 @param[in] PagingMode The paging mode.
1232 @param[in] PhysicalAddressBits The bits of physical address to map.
1233
1234 @retval PageTable Address
1235
1236**/
1237UINTN
1238GenSmmPageTable (
1239 IN PAGING_MODE PagingMode,
1240 IN UINT8 PhysicalAddressBits
1241 )
1242{
1243 UINTN PageTable;
1244 UINTN Index;
1245 MM_CPU_MEMORY_REGION *MemoryRegion;
1246 UINTN MemoryRegionCount;
1247 IA32_MAP_ATTRIBUTE MapAttribute;
1248 IA32_MAP_ATTRIBUTE MapMask;
1249 RETURN_STATUS Status;
1250 UINTN GuardPage;
1251
1252 PageTable = 0;
1253 MemoryRegion = NULL;
1254 MemoryRegionCount = 0;
1255 MapMask.Uint64 = MAX_UINT64;
1256
1257 //
1258 // 1. Create NonMmram MemoryRegion
1259 //
1260 CreateNonMmramMemMap (PhysicalAddressBits, &MemoryRegion, &MemoryRegionCount);
1261 ASSERT (MemoryRegion != NULL && MemoryRegionCount != 0);
1262
1263 //
1264 // 2. Gen NonMmram MemoryRegion PageTable
1265 //
1266 for (Index = 0; Index < MemoryRegionCount; Index++) {
1267 ASSERT (MemoryRegion[Index].Base % SIZE_4KB == 0);
1268 ASSERT (MemoryRegion[Index].Length % EFI_PAGE_SIZE == 0);
1269
1270 //
1271 // Set the MapAttribute
1272 //
1273 MapAttribute.Uint64 = mAddressEncMask|MemoryRegion[Index].Base;
1274 MapAttribute.Bits.Present = 1;
1275 MapAttribute.Bits.ReadWrite = 1;
1276 MapAttribute.Bits.UserSupervisor = 1;
1277 MapAttribute.Bits.Accessed = 1;
1278 MapAttribute.Bits.Dirty = 1;
1279
1280 //
1281 // Update the MapAttribute according MemoryRegion[Index].Attribute
1282 //
1283 if ((MemoryRegion[Index].Attribute & EFI_MEMORY_RO) != 0) {
1284 MapAttribute.Bits.ReadWrite = 0;
1285 }
1286
1287 if ((MemoryRegion[Index].Attribute & EFI_MEMORY_XP) != 0) {
1288 if (mXdSupported) {
1289 MapAttribute.Bits.Nx = 1;
1290 }
1291 }
1292
1293 GenPageTable (&PageTable, PagingMode, MemoryRegion[Index].Base, (UINTN)MemoryRegion[Index].Length, MapAttribute, MapMask);
1294 }
1295
1296 //
1297 // Free the MemoryRegion after usage
1298 //
1299 if (MemoryRegion != NULL) {
1300 FreePool (MemoryRegion);
1301 }
1302
1303 //
1304 // 3. Gen MMRAM Range PageTable
1305 //
1306 for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) {
1307 ASSERT (mSmmCpuSmramRanges[Index].CpuStart % SIZE_4KB == 0);
1308 ASSERT (mSmmCpuSmramRanges[Index].PhysicalSize % EFI_PAGE_SIZE == 0);
1309
1310 //
1311 // Set the MapAttribute
1312 //
1313 MapAttribute.Uint64 = mAddressEncMask|mSmmCpuSmramRanges[Index].CpuStart;
1314 MapAttribute.Bits.Present = 1;
1315 MapAttribute.Bits.ReadWrite = 1;
1316 MapAttribute.Bits.UserSupervisor = 1;
1317 MapAttribute.Bits.Accessed = 1;
1318 MapAttribute.Bits.Dirty = 1;
1319
1320 GenPageTable (&PageTable, PagingMode, mSmmCpuSmramRanges[Index].CpuStart, mSmmCpuSmramRanges[Index].PhysicalSize, MapAttribute, MapMask);
1321 }
1322
1323 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
1324 //
1325 // Mark the 4KB guard page between known good stack and smm stack as non-present
1326 //
1327 for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) {
1328 GuardPage = mSmmStackArrayBase + EFI_PAGE_SIZE + Index * (mSmmStackSize + mSmmShadowStackSize);
1329 Status = ConvertMemoryPageAttributes (PageTable, PagingMode, GuardPage, SIZE_4KB, EFI_MEMORY_RP, TRUE, NULL);
1330 ASSERT (Status == RETURN_SUCCESS);
1331 }
1332 }
1333
1334 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) {
1335 //
1336 // Mark [0, 4k] as non-present
1337 //
1338 Status = ConvertMemoryPageAttributes (PageTable, PagingMode, 0, SIZE_4KB, EFI_MEMORY_RP, TRUE, NULL);
1339 ASSERT (Status == RETURN_SUCCESS);
1340 }
1341
1342 return (UINTN)PageTable;
1343}
1344
1345/**
1346 This function retrieves the attributes of the memory region specified by
1347 BaseAddress and Length. If different attributes are got from different part
1348 of the memory region, EFI_NO_MAPPING will be returned.
1349
1350 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1351 @param BaseAddress The physical address that is the start address of
1352 a memory region.
1353 @param Length The size in bytes of the memory region.
1354 @param Attributes Pointer to attributes returned.
1355
1356 @retval EFI_SUCCESS The attributes got for the memory region.
1357 @retval EFI_INVALID_PARAMETER Length is zero.
1358 Attributes is NULL.
1359 @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
1360 region.
1361 @retval EFI_UNSUPPORTED The processor does not support one or more
1362 bytes of the memory resource range specified
1363 by BaseAddress and Length.
1364
1365**/
1366EFI_STATUS
1367EFIAPI
1368EdkiiSmmGetMemoryAttributes (
1369 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1370 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1371 IN UINT64 Length,
1372 OUT UINT64 *Attributes
1373 )
1374{
1375 EFI_PHYSICAL_ADDRESS Address;
1376 UINT64 *PageEntry;
1377 UINT64 MemAttr;
1378 PAGE_ATTRIBUTE PageAttr;
1379 INT64 Size;
1380 UINTN PageTableBase;
1381 BOOLEAN EnablePML5Paging;
1382 IA32_CR4 Cr4;
1383
1384 if ((Length < SIZE_4KB) || (Attributes == NULL)) {
1385 return EFI_INVALID_PARAMETER;
1386 }
1387
1388 Size = (INT64)Length;
1389 MemAttr = (UINT64)-1;
1390
1391 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
1392 Cr4.UintN = AsmReadCr4 ();
1393 EnablePML5Paging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
1394
1395 do {
1396 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttr);
1397 if ((PageEntry == NULL) || (PageAttr == PageNone)) {
1398 return EFI_UNSUPPORTED;
1399 }
1400
1401 //
1402 // If the memory range is cross page table boundary, make sure they
1403 // share the same attribute. Return EFI_NO_MAPPING if not.
1404 //
1405 *Attributes = GetAttributesFromPageEntry (PageEntry);
1406 if ((MemAttr != (UINT64)-1) && (*Attributes != MemAttr)) {
1407 return EFI_NO_MAPPING;
1408 }
1409
1410 switch (PageAttr) {
1411 case Page4K:
1412 Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;
1413 Size -= (SIZE_4KB - (BaseAddress - Address));
1414 BaseAddress += (SIZE_4KB - (BaseAddress - Address));
1415 break;
1416
1417 case Page2M:
1418 Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;
1419 Size -= SIZE_2MB - (BaseAddress - Address);
1420 BaseAddress += SIZE_2MB - (BaseAddress - Address);
1421 break;
1422
1423 case Page1G:
1424 Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;
1425 Size -= SIZE_1GB - (BaseAddress - Address);
1426 BaseAddress += SIZE_1GB - (BaseAddress - Address);
1427 break;
1428
1429 default:
1430 return EFI_UNSUPPORTED;
1431 }
1432
1433 MemAttr = *Attributes;
1434 } while (Size > 0);
1435
1436 return EFI_SUCCESS;
1437}
1438
1439/**
1440 Prevent the memory pages used for SMM page table from been overwritten.
1441**/
1442VOID
1443EnablePageTableProtection (
1444 VOID
1445 )
1446{
1447 PAGE_TABLE_POOL *HeadPool;
1448 PAGE_TABLE_POOL *Pool;
1449 UINT64 PoolSize;
1450 EFI_PHYSICAL_ADDRESS Address;
1451 UINTN PageTableBase;
1452
1453 if (mPageTablePool == NULL) {
1454 return;
1455 }
1456
1457 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
1458
1459 //
1460 // ConvertMemoryPageAttributes might update mPageTablePool. It's safer to
1461 // remember original one in advance.
1462 //
1463 HeadPool = mPageTablePool;
1464 Pool = HeadPool;
1465 do {
1466 Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;
1467 PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);
1468 //
1469 // Set entire pool including header, used-memory and left free-memory as ReadOnly in SMM page table.
1470 //
1471 ConvertMemoryPageAttributes (PageTableBase, mPagingMode, Address, PoolSize, EFI_MEMORY_RO, TRUE, NULL);
1472 Pool = Pool->NextPool;
1473 } while (Pool != HeadPool);
1474}
1475
1476/**
1477 Return whether memory used by SMM page table need to be set as Read Only.
1478
1479 @retval TRUE Need to set SMM page table as Read Only.
1480 @retval FALSE Do not set SMM page table as Read Only.
1481**/
1482BOOLEAN
1483IfReadOnlyPageTableNeeded (
1484 VOID
1485 )
1486{
1487 //
1488 // Don't mark page table memory as read-only if
1489 // - SMM heap guard feature enabled; or
1490 // BIT2: SMM page guard enabled
1491 // BIT3: SMM pool guard enabled
1492 // - SMM profile feature enabled
1493 //
1494 if (((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) ||
1495 mSmmProfileEnabled)
1496 {
1497 return FALSE;
1498 }
1499
1500 return TRUE;
1501}
1502
1503/**
1504 This function sets memory attribute for page table.
1505**/
1506VOID
1507SetPageTableAttributes (
1508 VOID
1509 )
1510{
1511 BOOLEAN WriteProtect;
1512 BOOLEAN CetEnabled;
1513
1514 if (!IfReadOnlyPageTableNeeded ()) {
1515 return;
1516 }
1517
1518 PERF_FUNCTION_BEGIN ();
1519 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
1520
1521 //
1522 // Disable write protection, because we need mark page table to be write protected.
1523 // We need *write* page table memory, to mark itself to be *read only*.
1524 //
1525 WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
1526
1527 // Set memory used by page table as Read Only.
1528 DEBUG ((DEBUG_INFO, "Start...\n"));
1529 EnablePageTableProtection ();
1530
1531 //
1532 // Enable write protection, after page table attribute updated.
1533 //
1534 WRITE_PROTECT_RO_PAGES (TRUE, CetEnabled);
1535
1536 mIsReadOnlyPageTable = TRUE;
1537
1538 //
1539 // Flush TLB after mark all page table pool as read only.
1540 //
1541 FlushTlbForAll ();
1542 PERF_FUNCTION_END ();
1543}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette