]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmCpuMemoryManagement.c
... / ...
CommitLineData
1/** @file\r
2\r
3Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
4SPDX-License-Identifier: BSD-2-Clause-Patent\r
5\r
6**/\r
7\r
8#include "PiSmmCpuDxeSmm.h"\r
9\r
10//\r
11// attributes for reserved memory before it is promoted to system memory\r
12//\r
13#define EFI_MEMORY_PRESENT 0x0100000000000000ULL\r
14#define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL\r
15#define EFI_MEMORY_TESTED 0x0400000000000000ULL\r
16\r
17#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
18 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))\r
19\r
20EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;\r
21UINTN mUefiMemoryMapSize;\r
22UINTN mUefiDescriptorSize;\r
23\r
24EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mGcdMemSpace = NULL;\r
25UINTN mGcdMemNumberOfDesc = 0;\r
26\r
27EFI_MEMORY_ATTRIBUTES_TABLE *mUefiMemoryAttributesTable = NULL;\r
28\r
29PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
30 { Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64 },\r
31 { Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64 },\r
32 { Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64 },\r
33};\r
34\r
35BOOLEAN mIsShadowStack = FALSE;\r
36BOOLEAN m5LevelPagingNeeded = FALSE;\r
37\r
38//\r
39// Global variable to keep track current available memory used as page table.\r
40//\r
41PAGE_TABLE_POOL *mPageTablePool = NULL;\r
42\r
43//\r
44// If memory used by SMM page table has been mareked as ReadOnly.\r
45//\r
46BOOLEAN mIsReadOnlyPageTable = FALSE;\r
47\r
48/**\r
49 Initialize a buffer pool for page table use only.\r
50\r
51 To reduce the potential split operation on page table, the pages reserved for\r
52 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and\r
53 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always\r
54 initialized with number of pages greater than or equal to the given PoolPages.\r
55\r
56 Once the pages in the pool are used up, this method should be called again to\r
57 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't\r
58 happen in practice.\r
59\r
60 @param PoolPages The least page number of the pool to be created.\r
61\r
62 @retval TRUE The pool is initialized successfully.\r
63 @retval FALSE The memory is out of resource.\r
64**/\r
65BOOLEAN\r
66InitializePageTablePool (\r
67 IN UINTN PoolPages\r
68 )\r
69{\r
70 VOID *Buffer;\r
71 BOOLEAN CetEnabled;\r
72 BOOLEAN WpEnabled;\r
73 IA32_CR0 Cr0;\r
74\r
75 //\r
76 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for\r
77 // header.\r
78 //\r
79 PoolPages += 1; // Add one page for header.\r
80 PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *\r
81 PAGE_TABLE_POOL_UNIT_PAGES;\r
82 Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);\r
83 if (Buffer == NULL) {\r
84 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));\r
85 return FALSE;\r
86 }\r
87\r
88 //\r
89 // Link all pools into a list for easier track later.\r
90 //\r
91 if (mPageTablePool == NULL) {\r
92 mPageTablePool = Buffer;\r
93 mPageTablePool->NextPool = mPageTablePool;\r
94 } else {\r
95 ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;\r
96 mPageTablePool->NextPool = Buffer;\r
97 mPageTablePool = Buffer;\r
98 }\r
99\r
100 //\r
101 // Reserve one page for pool header.\r
102 //\r
103 mPageTablePool->FreePages = PoolPages - 1;\r
104 mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);\r
105\r
106 //\r
107 // If page table memory has been marked as RO, mark the new pool pages as read-only.\r
108 //\r
109 if (mIsReadOnlyPageTable) {\r
110 CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;\r
111 Cr0.UintN = AsmReadCr0 ();\r
112 WpEnabled = (Cr0.Bits.WP != 0) ? TRUE : FALSE;\r
113 if (WpEnabled) {\r
114 if (CetEnabled) {\r
115 //\r
116 // CET must be disabled if WP is disabled. Disable CET before clearing CR0.WP.\r
117 //\r
118 DisableCet ();\r
119 }\r
120\r
121 Cr0.Bits.WP = 0;\r
122 AsmWriteCr0 (Cr0.UintN);\r
123 }\r
124\r
125 SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);\r
126 if (WpEnabled) {\r
127 Cr0.UintN = AsmReadCr0 ();\r
128 Cr0.Bits.WP = 1;\r
129 AsmWriteCr0 (Cr0.UintN);\r
130\r
131 if (CetEnabled) {\r
132 //\r
133 // re-enable CET.\r
134 //\r
135 EnableCet ();\r
136 }\r
137 }\r
138 }\r
139\r
140 return TRUE;\r
141}\r
142\r
143/**\r
144 This API provides a way to allocate memory for page table.\r
145\r
146 This API can be called more once to allocate memory for page tables.\r
147\r
148 Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the\r
149 allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL\r
150 is returned. If there is not enough memory remaining to satisfy the request, then NULL is\r
151 returned.\r
152\r
153 @param Pages The number of 4 KB pages to allocate.\r
154\r
155 @return A pointer to the allocated buffer or NULL if allocation fails.\r
156\r
157**/\r
158VOID *\r
159AllocatePageTableMemory (\r
160 IN UINTN Pages\r
161 )\r
162{\r
163 VOID *Buffer;\r
164\r
165 if (Pages == 0) {\r
166 return NULL;\r
167 }\r
168\r
169 //\r
170 // Renew the pool if necessary.\r
171 //\r
172 if ((mPageTablePool == NULL) ||\r
173 (Pages > mPageTablePool->FreePages))\r
174 {\r
175 if (!InitializePageTablePool (Pages)) {\r
176 return NULL;\r
177 }\r
178 }\r
179\r
180 Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;\r
181\r
182 mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);\r
183 mPageTablePool->FreePages -= Pages;\r
184\r
185 return Buffer;\r
186}\r
187\r
188/**\r
189 Return length according to page attributes.\r
190\r
191 @param[in] PageAttributes The page attribute of the page entry.\r
192\r
193 @return The length of page entry.\r
194**/\r
195UINTN\r
196PageAttributeToLength (\r
197 IN PAGE_ATTRIBUTE PageAttribute\r
198 )\r
199{\r
200 UINTN Index;\r
201\r
202 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {\r
203 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
204 return (UINTN)mPageAttributeTable[Index].Length;\r
205 }\r
206 }\r
207\r
208 return 0;\r
209}\r
210\r
211/**\r
212 Return address mask according to page attributes.\r
213\r
214 @param[in] PageAttributes The page attribute of the page entry.\r
215\r
216 @return The address mask of page entry.\r
217**/\r
218UINTN\r
219PageAttributeToMask (\r
220 IN PAGE_ATTRIBUTE PageAttribute\r
221 )\r
222{\r
223 UINTN Index;\r
224\r
225 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {\r
226 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
227 return (UINTN)mPageAttributeTable[Index].AddressMask;\r
228 }\r
229 }\r
230\r
231 return 0;\r
232}\r
233\r
234/**\r
235 Return page table entry to match the address.\r
236\r
237 @param[in] PageTableBase The page table base.\r
238 @param[in] Enable5LevelPaging If PML5 paging is enabled.\r
239 @param[in] Address The address to be checked.\r
240 @param[out] PageAttributes The page attribute of the page entry.\r
241\r
242 @return The page entry.\r
243**/\r
244VOID *\r
245GetPageTableEntry (\r
246 IN UINTN PageTableBase,\r
247 IN BOOLEAN Enable5LevelPaging,\r
248 IN PHYSICAL_ADDRESS Address,\r
249 OUT PAGE_ATTRIBUTE *PageAttribute\r
250 )\r
251{\r
252 UINTN Index1;\r
253 UINTN Index2;\r
254 UINTN Index3;\r
255 UINTN Index4;\r
256 UINTN Index5;\r
257 UINT64 *L1PageTable;\r
258 UINT64 *L2PageTable;\r
259 UINT64 *L3PageTable;\r
260 UINT64 *L4PageTable;\r
261 UINT64 *L5PageTable;\r
262\r
263 Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;\r
264 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;\r
265 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;\r
266 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;\r
267 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;\r
268\r
269 if (sizeof (UINTN) == sizeof (UINT64)) {\r
270 if (Enable5LevelPaging) {\r
271 L5PageTable = (UINT64 *)PageTableBase;\r
272 if (L5PageTable[Index5] == 0) {\r
273 *PageAttribute = PageNone;\r
274 return NULL;\r
275 }\r
276\r
277 L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
278 } else {\r
279 L4PageTable = (UINT64 *)PageTableBase;\r
280 }\r
281\r
282 if (L4PageTable[Index4] == 0) {\r
283 *PageAttribute = PageNone;\r
284 return NULL;\r
285 }\r
286\r
287 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
288 } else {\r
289 L3PageTable = (UINT64 *)PageTableBase;\r
290 }\r
291\r
292 if (L3PageTable[Index3] == 0) {\r
293 *PageAttribute = PageNone;\r
294 return NULL;\r
295 }\r
296\r
297 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
298 // 1G\r
299 *PageAttribute = Page1G;\r
300 return &L3PageTable[Index3];\r
301 }\r
302\r
303 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
304 if (L2PageTable[Index2] == 0) {\r
305 *PageAttribute = PageNone;\r
306 return NULL;\r
307 }\r
308\r
309 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
310 // 2M\r
311 *PageAttribute = Page2M;\r
312 return &L2PageTable[Index2];\r
313 }\r
314\r
315 // 4k\r
316 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
317 if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
318 *PageAttribute = PageNone;\r
319 return NULL;\r
320 }\r
321\r
322 *PageAttribute = Page4K;\r
323 return &L1PageTable[Index1];\r
324}\r
325\r
326/**\r
327 Return memory attributes of page entry.\r
328\r
329 @param[in] PageEntry The page entry.\r
330\r
331 @return Memory attributes of page entry.\r
332**/\r
333UINT64\r
334GetAttributesFromPageEntry (\r
335 IN UINT64 *PageEntry\r
336 )\r
337{\r
338 UINT64 Attributes;\r
339\r
340 Attributes = 0;\r
341 if ((*PageEntry & IA32_PG_P) == 0) {\r
342 Attributes |= EFI_MEMORY_RP;\r
343 }\r
344\r
345 if ((*PageEntry & IA32_PG_RW) == 0) {\r
346 Attributes |= EFI_MEMORY_RO;\r
347 }\r
348\r
349 if ((*PageEntry & IA32_PG_NX) != 0) {\r
350 Attributes |= EFI_MEMORY_XP;\r
351 }\r
352\r
353 return Attributes;\r
354}\r
355\r
356/**\r
357 Modify memory attributes of page entry.\r
358\r
359 @param[in] PageEntry The page entry.\r
360 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
361 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.\r
362 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
363**/\r
364VOID\r
365ConvertPageEntryAttribute (\r
366 IN UINT64 *PageEntry,\r
367 IN UINT64 Attributes,\r
368 IN BOOLEAN IsSet,\r
369 OUT BOOLEAN *IsModified\r
370 )\r
371{\r
372 UINT64 CurrentPageEntry;\r
373 UINT64 NewPageEntry;\r
374\r
375 CurrentPageEntry = *PageEntry;\r
376 NewPageEntry = CurrentPageEntry;\r
377 if ((Attributes & EFI_MEMORY_RP) != 0) {\r
378 if (IsSet) {\r
379 NewPageEntry &= ~(UINT64)IA32_PG_P;\r
380 } else {\r
381 NewPageEntry |= IA32_PG_P;\r
382 }\r
383 }\r
384\r
385 if ((Attributes & EFI_MEMORY_RO) != 0) {\r
386 if (IsSet) {\r
387 NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
388 if (mIsShadowStack) {\r
389 // Environment setup\r
390 // ReadOnly page need set Dirty bit for shadow stack\r
391 NewPageEntry |= IA32_PG_D;\r
392 // Clear user bit for supervisor shadow stack\r
393 NewPageEntry &= ~(UINT64)IA32_PG_U;\r
394 } else {\r
395 // Runtime update\r
396 // Clear dirty bit for non shadow stack, to protect RO page.\r
397 NewPageEntry &= ~(UINT64)IA32_PG_D;\r
398 }\r
399 } else {\r
400 NewPageEntry |= IA32_PG_RW;\r
401 }\r
402 }\r
403\r
404 if ((Attributes & EFI_MEMORY_XP) != 0) {\r
405 if (mXdSupported) {\r
406 if (IsSet) {\r
407 NewPageEntry |= IA32_PG_NX;\r
408 } else {\r
409 NewPageEntry &= ~IA32_PG_NX;\r
410 }\r
411 }\r
412 }\r
413\r
414 *PageEntry = NewPageEntry;\r
415 if (CurrentPageEntry != NewPageEntry) {\r
416 *IsModified = TRUE;\r
417 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
418 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));\r
419 } else {\r
420 *IsModified = FALSE;\r
421 }\r
422}\r
423\r
424/**\r
425 This function returns if there is need to split page entry.\r
426\r
427 @param[in] BaseAddress The base address to be checked.\r
428 @param[in] Length The length to be checked.\r
429 @param[in] PageEntry The page entry to be checked.\r
430 @param[in] PageAttribute The page attribute of the page entry.\r
431\r
432 @retval SplitAttributes on if there is need to split page entry.\r
433**/\r
434PAGE_ATTRIBUTE\r
435NeedSplitPage (\r
436 IN PHYSICAL_ADDRESS BaseAddress,\r
437 IN UINT64 Length,\r
438 IN UINT64 *PageEntry,\r
439 IN PAGE_ATTRIBUTE PageAttribute\r
440 )\r
441{\r
442 UINT64 PageEntryLength;\r
443\r
444 PageEntryLength = PageAttributeToLength (PageAttribute);\r
445\r
446 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
447 return PageNone;\r
448 }\r
449\r
450 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
451 return Page4K;\r
452 }\r
453\r
454 return Page2M;\r
455}\r
456\r
457/**\r
458 This function splits one page entry to small page entries.\r
459\r
460 @param[in] PageEntry The page entry to be splitted.\r
461 @param[in] PageAttribute The page attribute of the page entry.\r
462 @param[in] SplitAttribute How to split the page entry.\r
463\r
464 @retval RETURN_SUCCESS The page entry is splitted.\r
465 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.\r
466 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.\r
467**/\r
468RETURN_STATUS\r
469SplitPage (\r
470 IN UINT64 *PageEntry,\r
471 IN PAGE_ATTRIBUTE PageAttribute,\r
472 IN PAGE_ATTRIBUTE SplitAttribute\r
473 )\r
474{\r
475 UINT64 BaseAddress;\r
476 UINT64 *NewPageEntry;\r
477 UINTN Index;\r
478\r
479 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
480\r
481 if (PageAttribute == Page2M) {\r
482 //\r
483 // Split 2M to 4K\r
484 //\r
485 ASSERT (SplitAttribute == Page4K);\r
486 if (SplitAttribute == Page4K) {\r
487 NewPageEntry = AllocatePageTableMemory (1);\r
488 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
489 if (NewPageEntry == NULL) {\r
490 return RETURN_OUT_OF_RESOURCES;\r
491 }\r
492\r
493 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;\r
494 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {\r
495 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
496 }\r
497\r
498 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
499 return RETURN_SUCCESS;\r
500 } else {\r
501 return RETURN_UNSUPPORTED;\r
502 }\r
503 } else if (PageAttribute == Page1G) {\r
504 //\r
505 // Split 1G to 2M\r
506 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
507 //\r
508 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
509 if (((SplitAttribute == Page2M) || (SplitAttribute == Page4K))) {\r
510 NewPageEntry = AllocatePageTableMemory (1);\r
511 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
512 if (NewPageEntry == NULL) {\r
513 return RETURN_OUT_OF_RESOURCES;\r
514 }\r
515\r
516 BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;\r
517 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {\r
518 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
519 }\r
520\r
521 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
522 return RETURN_SUCCESS;\r
523 } else {\r
524 return RETURN_UNSUPPORTED;\r
525 }\r
526 } else {\r
527 return RETURN_UNSUPPORTED;\r
528 }\r
529}\r
530\r
531/**\r
532 This function modifies the page attributes for the memory region specified by BaseAddress and\r
533 Length from their current attributes to the attributes specified by Attributes.\r
534\r
535 Caller should make sure BaseAddress and Length is at page boundary.\r
536\r
537 @param[in] PageTableBase The page table base.\r
538 @param[in] EnablePML5Paging If PML5 paging is enabled.\r
539 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
540 @param[in] Length The size in bytes of the memory region.\r
541 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
542 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.\r
543 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
544 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
545\r
546 @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
547 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
548 BaseAddress and Length cannot be modified.\r
549 @retval RETURN_INVALID_PARAMETER Length is zero.\r
550 Attributes specified an illegal combination of attributes that\r
551 cannot be set together.\r
552 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
553 the memory resource range.\r
554 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
555 resource range specified by BaseAddress and Length.\r
556 The bit mask of attributes is not support for the memory resource\r
557 range specified by BaseAddress and Length.\r
558**/\r
559RETURN_STATUS\r
560ConvertMemoryPageAttributes (\r
561 IN UINTN PageTableBase,\r
562 IN BOOLEAN EnablePML5Paging,\r
563 IN PHYSICAL_ADDRESS BaseAddress,\r
564 IN UINT64 Length,\r
565 IN UINT64 Attributes,\r
566 IN BOOLEAN IsSet,\r
567 OUT BOOLEAN *IsSplitted OPTIONAL,\r
568 OUT BOOLEAN *IsModified OPTIONAL\r
569 )\r
570{\r
571 UINT64 *PageEntry;\r
572 PAGE_ATTRIBUTE PageAttribute;\r
573 UINTN PageEntryLength;\r
574 PAGE_ATTRIBUTE SplitAttribute;\r
575 RETURN_STATUS Status;\r
576 BOOLEAN IsEntryModified;\r
577 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;\r
578\r
579 ASSERT (Attributes != 0);\r
580 ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);\r
581\r
582 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);\r
583 ASSERT ((Length & (SIZE_4KB - 1)) == 0);\r
584\r
585 if (Length == 0) {\r
586 return RETURN_INVALID_PARAMETER;\r
587 }\r
588\r
589 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);\r
590 if (BaseAddress > MaximumSupportMemAddress) {\r
591 return RETURN_UNSUPPORTED;\r
592 }\r
593\r
594 if (Length > MaximumSupportMemAddress) {\r
595 return RETURN_UNSUPPORTED;\r
596 }\r
597\r
598 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {\r
599 return RETURN_UNSUPPORTED;\r
600 }\r
601\r
602 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
603\r
604 if (IsSplitted != NULL) {\r
605 *IsSplitted = FALSE;\r
606 }\r
607\r
608 if (IsModified != NULL) {\r
609 *IsModified = FALSE;\r
610 }\r
611\r
612 //\r
613 // Below logic is to check 2M/4K page to make sure we do not waste memory.\r
614 //\r
615 while (Length != 0) {\r
616 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttribute);\r
617 if (PageEntry == NULL) {\r
618 return RETURN_UNSUPPORTED;\r
619 }\r
620\r
621 PageEntryLength = PageAttributeToLength (PageAttribute);\r
622 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
623 if (SplitAttribute == PageNone) {\r
624 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);\r
625 if (IsEntryModified) {\r
626 if (IsModified != NULL) {\r
627 *IsModified = TRUE;\r
628 }\r
629 }\r
630\r
631 //\r
632 // Convert success, move to next\r
633 //\r
634 BaseAddress += PageEntryLength;\r
635 Length -= PageEntryLength;\r
636 } else {\r
637 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);\r
638 if (RETURN_ERROR (Status)) {\r
639 return RETURN_UNSUPPORTED;\r
640 }\r
641\r
642 if (IsSplitted != NULL) {\r
643 *IsSplitted = TRUE;\r
644 }\r
645\r
646 if (IsModified != NULL) {\r
647 *IsModified = TRUE;\r
648 }\r
649\r
650 //\r
651 // Just split current page\r
652 // Convert success in next around\r
653 //\r
654 }\r
655 }\r
656\r
657 return RETURN_SUCCESS;\r
658}\r
659\r
660/**\r
661 FlushTlb on current processor.\r
662\r
663 @param[in,out] Buffer Pointer to private data buffer.\r
664**/\r
665VOID\r
666EFIAPI\r
667FlushTlbOnCurrentProcessor (\r
668 IN OUT VOID *Buffer\r
669 )\r
670{\r
671 CpuFlushTlb ();\r
672}\r
673\r
674/**\r
675 FlushTlb for all processors.\r
676**/\r
677VOID\r
678FlushTlbForAll (\r
679 VOID\r
680 )\r
681{\r
682 UINTN Index;\r
683\r
684 FlushTlbOnCurrentProcessor (NULL);\r
685\r
686 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {\r
687 if (Index != gSmst->CurrentlyExecutingCpu) {\r
688 // Force to start up AP in blocking mode,\r
689 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);\r
690 // Do not check return status, because AP might not be present in some corner cases.\r
691 }\r
692 }\r
693}\r
694\r
695/**\r
696 This function sets the attributes for the memory region specified by BaseAddress and\r
697 Length from their current attributes to the attributes specified by Attributes.\r
698\r
699 @param[in] PageTableBase The page table base.\r
700 @param[in] EnablePML5Paging If PML5 paging is enabled.\r
701 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
702 @param[in] Length The size in bytes of the memory region.\r
703 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
704 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
705\r
706 @retval EFI_SUCCESS The attributes were set for the memory region.\r
707 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
708 BaseAddress and Length cannot be modified.\r
709 @retval EFI_INVALID_PARAMETER Length is zero.\r
710 Attributes specified an illegal combination of attributes that\r
711 cannot be set together.\r
712 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
713 the memory resource range.\r
714 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
715 resource range specified by BaseAddress and Length.\r
716 The bit mask of attributes is not support for the memory resource\r
717 range specified by BaseAddress and Length.\r
718\r
719**/\r
720EFI_STATUS\r
721SmmSetMemoryAttributesEx (\r
722 IN UINTN PageTableBase,\r
723 IN BOOLEAN EnablePML5Paging,\r
724 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
725 IN UINT64 Length,\r
726 IN UINT64 Attributes,\r
727 OUT BOOLEAN *IsSplitted OPTIONAL\r
728 )\r
729{\r
730 EFI_STATUS Status;\r
731 BOOLEAN IsModified;\r
732\r
733 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);\r
734 if (!EFI_ERROR (Status)) {\r
735 if (IsModified) {\r
736 //\r
737 // Flush TLB as last step\r
738 //\r
739 FlushTlbForAll ();\r
740 }\r
741 }\r
742\r
743 return Status;\r
744}\r
745\r
746/**\r
747 This function clears the attributes for the memory region specified by BaseAddress and\r
748 Length from their current attributes to the attributes specified by Attributes.\r
749\r
750 @param[in] PageTableBase The page table base.\r
751 @param[in] EnablePML5Paging If PML5 paging is enabled.\r
752 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
753 @param[in] Length The size in bytes of the memory region.\r
754 @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
755 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
756\r
757 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
758 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
759 BaseAddress and Length cannot be modified.\r
760 @retval EFI_INVALID_PARAMETER Length is zero.\r
761 Attributes specified an illegal combination of attributes that\r
762 cannot be cleared together.\r
763 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
764 the memory resource range.\r
765 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
766 resource range specified by BaseAddress and Length.\r
767 The bit mask of attributes is not supported for the memory resource\r
768 range specified by BaseAddress and Length.\r
769\r
770**/\r
771EFI_STATUS\r
772SmmClearMemoryAttributesEx (\r
773 IN UINTN PageTableBase,\r
774 IN BOOLEAN EnablePML5Paging,\r
775 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
776 IN UINT64 Length,\r
777 IN UINT64 Attributes,\r
778 OUT BOOLEAN *IsSplitted OPTIONAL\r
779 )\r
780{\r
781 EFI_STATUS Status;\r
782 BOOLEAN IsModified;\r
783\r
784 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);\r
785 if (!EFI_ERROR (Status)) {\r
786 if (IsModified) {\r
787 //\r
788 // Flush TLB as last step\r
789 //\r
790 FlushTlbForAll ();\r
791 }\r
792 }\r
793\r
794 return Status;\r
795}\r
796\r
797/**\r
798 This function sets the attributes for the memory region specified by BaseAddress and\r
799 Length from their current attributes to the attributes specified by Attributes.\r
800\r
801 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
802 @param[in] Length The size in bytes of the memory region.\r
803 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
804\r
805 @retval EFI_SUCCESS The attributes were set for the memory region.\r
806 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
807 BaseAddress and Length cannot be modified.\r
808 @retval EFI_INVALID_PARAMETER Length is zero.\r
809 Attributes specified an illegal combination of attributes that\r
810 cannot be set together.\r
811 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
812 the memory resource range.\r
813 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
814 resource range specified by BaseAddress and Length.\r
815 The bit mask of attributes is not supported for the memory resource\r
816 range specified by BaseAddress and Length.\r
817\r
818**/\r
819EFI_STATUS\r
820SmmSetMemoryAttributes (\r
821 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
822 IN UINT64 Length,\r
823 IN UINT64 Attributes\r
824 )\r
825{\r
826 IA32_CR4 Cr4;\r
827 UINTN PageTableBase;\r
828 BOOLEAN Enable5LevelPaging;\r
829\r
830 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;\r
831 Cr4.UintN = AsmReadCr4 ();\r
832 Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);\r
833 return SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, BaseAddress, Length, Attributes, NULL);\r
834}\r
835\r
836/**\r
837 This function clears the attributes for the memory region specified by BaseAddress and\r
838 Length from their current attributes to the attributes specified by Attributes.\r
839\r
840 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
841 @param[in] Length The size in bytes of the memory region.\r
842 @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
843\r
844 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
845 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
846 BaseAddress and Length cannot be modified.\r
847 @retval EFI_INVALID_PARAMETER Length is zero.\r
848 Attributes specified an illegal combination of attributes that\r
849 cannot be cleared together.\r
850 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
851 the memory resource range.\r
852 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
853 resource range specified by BaseAddress and Length.\r
854 The bit mask of attributes is not supported for the memory resource\r
855 range specified by BaseAddress and Length.\r
856\r
857**/\r
858EFI_STATUS\r
859SmmClearMemoryAttributes (\r
860 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
861 IN UINT64 Length,\r
862 IN UINT64 Attributes\r
863 )\r
864{\r
865 IA32_CR4 Cr4;\r
866 UINTN PageTableBase;\r
867 BOOLEAN Enable5LevelPaging;\r
868\r
869 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;\r
870 Cr4.UintN = AsmReadCr4 ();\r
871 Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);\r
872 return SmmClearMemoryAttributesEx (PageTableBase, Enable5LevelPaging, BaseAddress, Length, Attributes, NULL);\r
873}\r
874\r
875/**\r
876 Set ShadowStack memory.\r
877\r
878 @param[in] Cr3 The page table base address.\r
879 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
880 @param[in] Length The size in bytes of the memory region.\r
881\r
882 @retval EFI_SUCCESS The shadow stack memory is set.\r
883**/\r
884EFI_STATUS\r
885SetShadowStack (\r
886 IN UINTN Cr3,\r
887 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
888 IN UINT64 Length\r
889 )\r
890{\r
891 EFI_STATUS Status;\r
892\r
893 mIsShadowStack = TRUE;\r
894 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RO, NULL);\r
895 mIsShadowStack = FALSE;\r
896\r
897 return Status;\r
898}\r
899\r
900/**\r
901 Set not present memory.\r
902\r
903 @param[in] Cr3 The page table base address.\r
904 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
905 @param[in] Length The size in bytes of the memory region.\r
906\r
907 @retval EFI_SUCCESS The not present memory is set.\r
908**/\r
909EFI_STATUS\r
910SetNotPresentPage (\r
911 IN UINTN Cr3,\r
912 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
913 IN UINT64 Length\r
914 )\r
915{\r
916 EFI_STATUS Status;\r
917\r
918 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RP, NULL);\r
919 return Status;\r
920}\r
921\r
922/**\r
923 Retrieves a pointer to the system configuration table from the SMM System Table\r
924 based on a specified GUID.\r
925\r
926 @param[in] TableGuid The pointer to table's GUID type.\r
927 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.\r
928\r
929 @retval EFI_SUCCESS A configuration table matching TableGuid was found.\r
930 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.\r
931\r
932**/\r
933EFI_STATUS\r
934EFIAPI\r
935SmmGetSystemConfigurationTable (\r
936 IN EFI_GUID *TableGuid,\r
937 OUT VOID **Table\r
938 )\r
939{\r
940 UINTN Index;\r
941\r
942 ASSERT (TableGuid != NULL);\r
943 ASSERT (Table != NULL);\r
944\r
945 *Table = NULL;\r
946 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {\r
947 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {\r
948 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;\r
949 return EFI_SUCCESS;\r
950 }\r
951 }\r
952\r
953 return EFI_NOT_FOUND;\r
954}\r
955\r
956/**\r
957 This function sets SMM save state buffer to be RW and XP.\r
958**/\r
959VOID\r
960PatchSmmSaveStateMap (\r
961 VOID\r
962 )\r
963{\r
964 UINTN Index;\r
965 UINTN TileCodeSize;\r
966 UINTN TileDataSize;\r
967 UINTN TileSize;\r
968\r
969 TileCodeSize = GetSmiHandlerSize ();\r
970 TileCodeSize = ALIGN_VALUE (TileCodeSize, SIZE_4KB);\r
971 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);\r
972 TileDataSize = ALIGN_VALUE (TileDataSize, SIZE_4KB);\r
973 TileSize = TileDataSize + TileCodeSize - 1;\r
974 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);\r
975\r
976 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));\r
977 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {\r
978 //\r
979 // Code\r
980 //\r
981 SmmSetMemoryAttributes (\r
982 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
983 TileCodeSize,\r
984 EFI_MEMORY_RO\r
985 );\r
986 SmmClearMemoryAttributes (\r
987 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
988 TileCodeSize,\r
989 EFI_MEMORY_XP\r
990 );\r
991\r
992 //\r
993 // Data\r
994 //\r
995 SmmClearMemoryAttributes (\r
996 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
997 TileSize - TileCodeSize,\r
998 EFI_MEMORY_RO\r
999 );\r
1000 SmmSetMemoryAttributes (\r
1001 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
1002 TileSize - TileCodeSize,\r
1003 EFI_MEMORY_XP\r
1004 );\r
1005 }\r
1006\r
1007 //\r
1008 // Code\r
1009 //\r
1010 SmmSetMemoryAttributes (\r
1011 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,\r
1012 TileCodeSize,\r
1013 EFI_MEMORY_RO\r
1014 );\r
1015 SmmClearMemoryAttributes (\r
1016 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,\r
1017 TileCodeSize,\r
1018 EFI_MEMORY_XP\r
1019 );\r
1020\r
1021 //\r
1022 // Data\r
1023 //\r
1024 SmmClearMemoryAttributes (\r
1025 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
1026 SIZE_32KB - TileCodeSize,\r
1027 EFI_MEMORY_RO\r
1028 );\r
1029 SmmSetMemoryAttributes (\r
1030 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
1031 SIZE_32KB - TileCodeSize,\r
1032 EFI_MEMORY_XP\r
1033 );\r
1034}\r
1035\r
1036/**\r
1037 This function sets GDT/IDT buffer to be RO and XP.\r
1038**/\r
1039VOID\r
1040PatchGdtIdtMap (\r
1041 VOID\r
1042 )\r
1043{\r
1044 EFI_PHYSICAL_ADDRESS BaseAddress;\r
1045 UINTN Size;\r
1046\r
1047 //\r
1048 // GDT\r
1049 //\r
1050 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));\r
1051\r
1052 BaseAddress = mGdtBuffer;\r
1053 Size = ALIGN_VALUE (mGdtBufferSize, SIZE_4KB);\r
1054 //\r
1055 // The range should have been set to RO\r
1056 // if it is allocated with EfiRuntimeServicesCode.\r
1057 //\r
1058 SmmSetMemoryAttributes (\r
1059 BaseAddress,\r
1060 Size,\r
1061 EFI_MEMORY_XP\r
1062 );\r
1063\r
1064 //\r
1065 // IDT\r
1066 //\r
1067 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));\r
1068\r
1069 BaseAddress = gcSmiIdtr.Base;\r
1070 Size = ALIGN_VALUE (gcSmiIdtr.Limit + 1, SIZE_4KB);\r
1071 //\r
1072 // The range should have been set to RO\r
1073 // if it is allocated with EfiRuntimeServicesCode.\r
1074 //\r
1075 SmmSetMemoryAttributes (\r
1076 BaseAddress,\r
1077 Size,\r
1078 EFI_MEMORY_XP\r
1079 );\r
1080}\r
1081\r
1082/**\r
1083 This function sets memory attribute according to MemoryAttributesTable.\r
1084**/\r
1085VOID\r
1086SetMemMapAttributes (\r
1087 VOID\r
1088 )\r
1089{\r
1090 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
1091 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;\r
1092 UINTN MemoryMapEntryCount;\r
1093 UINTN DescriptorSize;\r
1094 UINTN Index;\r
1095 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;\r
1096\r
1097 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);\r
1098 if (MemoryAttributesTable == NULL) {\r
1099 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));\r
1100 return;\r
1101 }\r
1102\r
1103 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));\r
1104 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));\r
1105 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));\r
1106 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));\r
1107\r
1108 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;\r
1109 DescriptorSize = MemoryAttributesTable->DescriptorSize;\r
1110 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);\r
1111 MemoryMap = MemoryMapStart;\r
1112 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
1113 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));\r
1114 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));\r
1115 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));\r
1116 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));\r
1117 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));\r
1118 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));\r
1119 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);\r
1120 }\r
1121\r
1122 MemoryMap = MemoryMapStart;\r
1123 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
1124 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));\r
1125 switch (MemoryMap->Type) {\r
1126 case EfiRuntimeServicesCode:\r
1127 SmmSetMemoryAttributes (\r
1128 MemoryMap->PhysicalStart,\r
1129 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),\r
1130 EFI_MEMORY_RO\r
1131 );\r
1132 break;\r
1133 case EfiRuntimeServicesData:\r
1134 SmmSetMemoryAttributes (\r
1135 MemoryMap->PhysicalStart,\r
1136 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),\r
1137 EFI_MEMORY_XP\r
1138 );\r
1139 break;\r
1140 default:\r
1141 SmmSetMemoryAttributes (\r
1142 MemoryMap->PhysicalStart,\r
1143 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),\r
1144 EFI_MEMORY_XP\r
1145 );\r
1146 break;\r
1147 }\r
1148\r
1149 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);\r
1150 }\r
1151\r
1152 PatchSmmSaveStateMap ();\r
1153 PatchGdtIdtMap ();\r
1154\r
1155 return;\r
1156}\r
1157\r
1158/**\r
1159 Sort memory map entries based upon PhysicalStart, from low to high.\r
1160\r
1161 @param MemoryMap A pointer to the buffer in which firmware places\r
1162 the current memory map.\r
1163 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.\r
1164 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
1165**/\r
1166STATIC\r
1167VOID\r
1168SortMemoryMap (\r
1169 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,\r
1170 IN UINTN MemoryMapSize,\r
1171 IN UINTN DescriptorSize\r
1172 )\r
1173{\r
1174 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;\r
1175 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;\r
1176 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;\r
1177 EFI_MEMORY_DESCRIPTOR TempMemoryMap;\r
1178\r
1179 MemoryMapEntry = MemoryMap;\r
1180 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
1181 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);\r
1182 while (MemoryMapEntry < MemoryMapEnd) {\r
1183 while (NextMemoryMapEntry < MemoryMapEnd) {\r
1184 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {\r
1185 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));\r
1186 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));\r
1187 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof (EFI_MEMORY_DESCRIPTOR));\r
1188 }\r
1189\r
1190 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
1191 }\r
1192\r
1193 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
1194 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
1195 }\r
1196}\r
1197\r
1198/**\r
1199 Return if a UEFI memory page should be marked as not present in SMM page table.\r
1200 If the memory map entries type is\r
1201 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
1202 EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.\r
1203 Or return FALSE.\r
1204\r
1205 @param[in] MemoryMap A pointer to the memory descriptor.\r
1206\r
1207 @return TRUE The memory described will be marked as not present in SMM page table.\r
1208 @return FALSE The memory described will not be marked as not present in SMM page table.\r
1209**/\r
1210BOOLEAN\r
1211IsUefiPageNotPresent (\r
1212 IN EFI_MEMORY_DESCRIPTOR *MemoryMap\r
1213 )\r
1214{\r
1215 switch (MemoryMap->Type) {\r
1216 case EfiLoaderCode:\r
1217 case EfiLoaderData:\r
1218 case EfiBootServicesCode:\r
1219 case EfiBootServicesData:\r
1220 case EfiConventionalMemory:\r
1221 case EfiUnusableMemory:\r
1222 case EfiACPIReclaimMemory:\r
1223 return TRUE;\r
1224 default:\r
1225 return FALSE;\r
1226 }\r
1227}\r
1228\r
1229/**\r
1230 Merge continuous memory map entries whose type is\r
1231 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
1232 EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by\r
1233 these entries will be set as NOT present in SMM page table.\r
1234\r
1235 @param[in, out] MemoryMap A pointer to the buffer in which firmware places\r
1236 the current memory map.\r
1237 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the\r
1238 MemoryMap buffer. On input, this is the size of\r
1239 the current memory map. On output,\r
1240 it is the size of new memory map after merge.\r
1241 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
1242**/\r
1243STATIC\r
1244VOID\r
1245MergeMemoryMapForNotPresentEntry (\r
1246 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,\r
1247 IN OUT UINTN *MemoryMapSize,\r
1248 IN UINTN DescriptorSize\r
1249 )\r
1250{\r
1251 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;\r
1252 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;\r
1253 UINT64 MemoryBlockLength;\r
1254 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;\r
1255 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;\r
1256\r
1257 MemoryMapEntry = MemoryMap;\r
1258 NewMemoryMapEntry = MemoryMap;\r
1259 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + *MemoryMapSize);\r
1260 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {\r
1261 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));\r
1262 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
1263\r
1264 do {\r
1265 MemoryBlockLength = (UINT64)(EFI_PAGES_TO_SIZE ((UINTN)MemoryMapEntry->NumberOfPages));\r
1266 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&\r
1267 IsUefiPageNotPresent (MemoryMapEntry) && IsUefiPageNotPresent (NextMemoryMapEntry) &&\r
1268 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart))\r
1269 {\r
1270 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
1271 if (NewMemoryMapEntry != MemoryMapEntry) {\r
1272 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
1273 }\r
1274\r
1275 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
1276 continue;\r
1277 } else {\r
1278 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
1279 break;\r
1280 }\r
1281 } while (TRUE);\r
1282\r
1283 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
1284 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);\r
1285 }\r
1286\r
1287 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;\r
1288\r
1289 return;\r
1290}\r
1291\r
1292/**\r
1293 This function caches the GCD memory map information.\r
1294**/\r
1295VOID\r
1296GetGcdMemoryMap (\r
1297 VOID\r
1298 )\r
1299{\r
1300 UINTN NumberOfDescriptors;\r
1301 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemSpaceMap;\r
1302 EFI_STATUS Status;\r
1303 UINTN Index;\r
1304\r
1305 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap);\r
1306 if (EFI_ERROR (Status)) {\r
1307 return;\r
1308 }\r
1309\r
1310 mGcdMemNumberOfDesc = 0;\r
1311 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
1312 if ((MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved) &&\r
1313 ((MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
1314 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))\r
1315 )\r
1316 {\r
1317 mGcdMemNumberOfDesc++;\r
1318 }\r
1319 }\r
1320\r
1321 mGcdMemSpace = AllocateZeroPool (mGcdMemNumberOfDesc * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR));\r
1322 ASSERT (mGcdMemSpace != NULL);\r
1323 if (mGcdMemSpace == NULL) {\r
1324 mGcdMemNumberOfDesc = 0;\r
1325 gBS->FreePool (MemSpaceMap);\r
1326 return;\r
1327 }\r
1328\r
1329 mGcdMemNumberOfDesc = 0;\r
1330 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
1331 if ((MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved) &&\r
1332 ((MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
1333 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))\r
1334 )\r
1335 {\r
1336 CopyMem (\r
1337 &mGcdMemSpace[mGcdMemNumberOfDesc],\r
1338 &MemSpaceMap[Index],\r
1339 sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR)\r
1340 );\r
1341 mGcdMemNumberOfDesc++;\r
1342 }\r
1343 }\r
1344\r
1345 gBS->FreePool (MemSpaceMap);\r
1346}\r
1347\r
1348/**\r
1349 Get UEFI MemoryAttributesTable.\r
1350**/\r
1351VOID\r
1352GetUefiMemoryAttributesTable (\r
1353 VOID\r
1354 )\r
1355{\r
1356 EFI_STATUS Status;\r
1357 EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;\r
1358 UINTN MemoryAttributesTableSize;\r
1359\r
1360 Status = EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);\r
1361 if (!EFI_ERROR (Status) && (MemoryAttributesTable != NULL)) {\r
1362 MemoryAttributesTableSize = sizeof (EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries;\r
1363 mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable);\r
1364 ASSERT (mUefiMemoryAttributesTable != NULL);\r
1365 }\r
1366}\r
1367\r
1368/**\r
1369 This function caches the UEFI memory map information.\r
1370**/\r
1371VOID\r
1372GetUefiMemoryMap (\r
1373 VOID\r
1374 )\r
1375{\r
1376 EFI_STATUS Status;\r
1377 UINTN MapKey;\r
1378 UINT32 DescriptorVersion;\r
1379 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
1380 UINTN UefiMemoryMapSize;\r
1381\r
1382 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));\r
1383\r
1384 UefiMemoryMapSize = 0;\r
1385 MemoryMap = NULL;\r
1386 Status = gBS->GetMemoryMap (\r
1387 &UefiMemoryMapSize,\r
1388 MemoryMap,\r
1389 &MapKey,\r
1390 &mUefiDescriptorSize,\r
1391 &DescriptorVersion\r
1392 );\r
1393 ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
1394\r
1395 do {\r
1396 Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);\r
1397 ASSERT (MemoryMap != NULL);\r
1398 if (MemoryMap == NULL) {\r
1399 return;\r
1400 }\r
1401\r
1402 Status = gBS->GetMemoryMap (\r
1403 &UefiMemoryMapSize,\r
1404 MemoryMap,\r
1405 &MapKey,\r
1406 &mUefiDescriptorSize,\r
1407 &DescriptorVersion\r
1408 );\r
1409 if (EFI_ERROR (Status)) {\r
1410 gBS->FreePool (MemoryMap);\r
1411 MemoryMap = NULL;\r
1412 }\r
1413 } while (Status == EFI_BUFFER_TOO_SMALL);\r
1414\r
1415 if (MemoryMap == NULL) {\r
1416 return;\r
1417 }\r
1418\r
1419 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);\r
1420 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);\r
1421\r
1422 mUefiMemoryMapSize = UefiMemoryMapSize;\r
1423 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);\r
1424 ASSERT (mUefiMemoryMap != NULL);\r
1425\r
1426 gBS->FreePool (MemoryMap);\r
1427\r
1428 //\r
1429 // Get additional information from GCD memory map.\r
1430 //\r
1431 GetGcdMemoryMap ();\r
1432\r
1433 //\r
1434 // Get UEFI memory attributes table.\r
1435 //\r
1436 GetUefiMemoryAttributesTable ();\r
1437}\r
1438\r
1439/**\r
1440 This function sets UEFI memory attribute according to UEFI memory map.\r
1441\r
1442 The normal memory region is marked as not present, such as\r
1443 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
1444 EfiUnusableMemory, EfiACPIReclaimMemory.\r
1445**/\r
1446VOID\r
1447SetUefiMemMapAttributes (\r
1448 VOID\r
1449 )\r
1450{\r
1451 EFI_STATUS Status;\r
1452 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
1453 UINTN MemoryMapEntryCount;\r
1454 UINTN Index;\r
1455 EFI_MEMORY_DESCRIPTOR *Entry;\r
1456\r
1457 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));\r
1458\r
1459 if (mUefiMemoryMap != NULL) {\r
1460 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
1461 MemoryMap = mUefiMemoryMap;\r
1462 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
1463 if (IsUefiPageNotPresent (MemoryMap)) {\r
1464 Status = SmmSetMemoryAttributes (\r
1465 MemoryMap->PhysicalStart,\r
1466 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),\r
1467 EFI_MEMORY_RP\r
1468 );\r
1469 DEBUG ((\r
1470 DEBUG_INFO,\r
1471 "UefiMemory protection: 0x%lx - 0x%lx %r\n",\r
1472 MemoryMap->PhysicalStart,\r
1473 MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),\r
1474 Status\r
1475 ));\r
1476 }\r
1477\r
1478 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);\r
1479 }\r
1480 }\r
1481\r
1482 //\r
1483 // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().\r
1484 //\r
1485\r
1486 //\r
1487 // Set untested memory as not present.\r
1488 //\r
1489 if (mGcdMemSpace != NULL) {\r
1490 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {\r
1491 Status = SmmSetMemoryAttributes (\r
1492 mGcdMemSpace[Index].BaseAddress,\r
1493 mGcdMemSpace[Index].Length,\r
1494 EFI_MEMORY_RP\r
1495 );\r
1496 DEBUG ((\r
1497 DEBUG_INFO,\r
1498 "GcdMemory protection: 0x%lx - 0x%lx %r\n",\r
1499 mGcdMemSpace[Index].BaseAddress,\r
1500 mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,\r
1501 Status\r
1502 ));\r
1503 }\r
1504 }\r
1505\r
1506 //\r
1507 // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().\r
1508 //\r
1509\r
1510 //\r
1511 // Set UEFI runtime memory with EFI_MEMORY_RO as not present.\r
1512 //\r
1513 if (mUefiMemoryAttributesTable != NULL) {\r
1514 Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);\r
1515 for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {\r
1516 if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {\r
1517 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {\r
1518 Status = SmmSetMemoryAttributes (\r
1519 Entry->PhysicalStart,\r
1520 EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),\r
1521 EFI_MEMORY_RP\r
1522 );\r
1523 DEBUG ((\r
1524 DEBUG_INFO,\r
1525 "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",\r
1526 Entry->PhysicalStart,\r
1527 Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),\r
1528 Status\r
1529 ));\r
1530 }\r
1531 }\r
1532\r
1533 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);\r
1534 }\r
1535 }\r
1536\r
1537 //\r
1538 // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().\r
1539 //\r
1540}\r
1541\r
1542/**\r
1543 Return if the Address is forbidden as SMM communication buffer.\r
1544\r
1545 @param[in] Address the address to be checked\r
1546\r
1547 @return TRUE The address is forbidden as SMM communication buffer.\r
1548 @return FALSE The address is allowed as SMM communication buffer.\r
1549**/\r
1550BOOLEAN\r
1551IsSmmCommBufferForbiddenAddress (\r
1552 IN UINT64 Address\r
1553 )\r
1554{\r
1555 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
1556 UINTN MemoryMapEntryCount;\r
1557 UINTN Index;\r
1558 EFI_MEMORY_DESCRIPTOR *Entry;\r
1559\r
1560 if (mUefiMemoryMap != NULL) {\r
1561 MemoryMap = mUefiMemoryMap;\r
1562 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
1563 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
1564 if (IsUefiPageNotPresent (MemoryMap)) {\r
1565 if ((Address >= MemoryMap->PhysicalStart) &&\r
1566 (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages)))\r
1567 {\r
1568 return TRUE;\r
1569 }\r
1570 }\r
1571\r
1572 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);\r
1573 }\r
1574 }\r
1575\r
1576 if (mGcdMemSpace != NULL) {\r
1577 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {\r
1578 if ((Address >= mGcdMemSpace[Index].BaseAddress) &&\r
1579 (Address < mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length))\r
1580 {\r
1581 return TRUE;\r
1582 }\r
1583 }\r
1584 }\r
1585\r
1586 if (mUefiMemoryAttributesTable != NULL) {\r
1587 Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);\r
1588 for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {\r
1589 if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {\r
1590 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {\r
1591 if ((Address >= Entry->PhysicalStart) &&\r
1592 (Address < Entry->PhysicalStart + LShiftU64 (Entry->NumberOfPages, EFI_PAGE_SHIFT)))\r
1593 {\r
1594 return TRUE;\r
1595 }\r
1596\r
1597 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);\r
1598 }\r
1599 }\r
1600 }\r
1601 }\r
1602\r
1603 return FALSE;\r
1604}\r
1605\r
1606/**\r
1607 This function set given attributes of the memory region specified by\r
1608 BaseAddress and Length.\r
1609\r
1610 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
1611 @param BaseAddress The physical address that is the start address of\r
1612 a memory region.\r
1613 @param Length The size in bytes of the memory region.\r
1614 @param Attributes The bit mask of attributes to set for the memory\r
1615 region.\r
1616\r
1617 @retval EFI_SUCCESS The attributes were set for the memory region.\r
1618 @retval EFI_INVALID_PARAMETER Length is zero.\r
1619 Attributes specified an illegal combination of\r
1620 attributes that cannot be set together.\r
1621 @retval EFI_UNSUPPORTED The processor does not support one or more\r
1622 bytes of the memory resource range specified\r
1623 by BaseAddress and Length.\r
1624 The bit mask of attributes is not supported for\r
1625 the memory resource range specified by\r
1626 BaseAddress and Length.\r
1627\r
1628**/\r
1629EFI_STATUS\r
1630EFIAPI\r
1631EdkiiSmmSetMemoryAttributes (\r
1632 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,\r
1633 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
1634 IN UINT64 Length,\r
1635 IN UINT64 Attributes\r
1636 )\r
1637{\r
1638 return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);\r
1639}\r
1640\r
1641/**\r
1642 This function clears given attributes of the memory region specified by\r
1643 BaseAddress and Length.\r
1644\r
1645 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
1646 @param BaseAddress The physical address that is the start address of\r
1647 a memory region.\r
1648 @param Length The size in bytes of the memory region.\r
1649 @param Attributes The bit mask of attributes to clear for the memory\r
1650 region.\r
1651\r
1652 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
1653 @retval EFI_INVALID_PARAMETER Length is zero.\r
1654 Attributes specified an illegal combination of\r
1655 attributes that cannot be cleared together.\r
1656 @retval EFI_UNSUPPORTED The processor does not support one or more\r
1657 bytes of the memory resource range specified\r
1658 by BaseAddress and Length.\r
1659 The bit mask of attributes is not supported for\r
1660 the memory resource range specified by\r
1661 BaseAddress and Length.\r
1662\r
1663**/\r
1664EFI_STATUS\r
1665EFIAPI\r
1666EdkiiSmmClearMemoryAttributes (\r
1667 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,\r
1668 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
1669 IN UINT64 Length,\r
1670 IN UINT64 Attributes\r
1671 )\r
1672{\r
1673 return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);\r
1674}\r
1675\r
1676/**\r
1677 This function retrieves the attributes of the memory region specified by\r
1678 BaseAddress and Length. If different attributes are got from different part\r
1679 of the memory region, EFI_NO_MAPPING will be returned.\r
1680\r
1681 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
1682 @param BaseAddress The physical address that is the start address of\r
1683 a memory region.\r
1684 @param Length The size in bytes of the memory region.\r
1685 @param Attributes Pointer to attributes returned.\r
1686\r
1687 @retval EFI_SUCCESS The attributes got for the memory region.\r
1688 @retval EFI_INVALID_PARAMETER Length is zero.\r
1689 Attributes is NULL.\r
1690 @retval EFI_NO_MAPPING Attributes are not consistent cross the memory\r
1691 region.\r
1692 @retval EFI_UNSUPPORTED The processor does not support one or more\r
1693 bytes of the memory resource range specified\r
1694 by BaseAddress and Length.\r
1695\r
1696**/\r
1697EFI_STATUS\r
1698EFIAPI\r
1699EdkiiSmmGetMemoryAttributes (\r
1700 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,\r
1701 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
1702 IN UINT64 Length,\r
1703 OUT UINT64 *Attributes\r
1704 )\r
1705{\r
1706 EFI_PHYSICAL_ADDRESS Address;\r
1707 UINT64 *PageEntry;\r
1708 UINT64 MemAttr;\r
1709 PAGE_ATTRIBUTE PageAttr;\r
1710 INT64 Size;\r
1711 UINTN PageTableBase;\r
1712 BOOLEAN EnablePML5Paging;\r
1713 IA32_CR4 Cr4;\r
1714\r
1715 if ((Length < SIZE_4KB) || (Attributes == NULL)) {\r
1716 return EFI_INVALID_PARAMETER;\r
1717 }\r
1718\r
1719 Size = (INT64)Length;\r
1720 MemAttr = (UINT64)-1;\r
1721\r
1722 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;\r
1723 Cr4.UintN = AsmReadCr4 ();\r
1724 EnablePML5Paging = (BOOLEAN)(Cr4.Bits.LA57 == 1);\r
1725\r
1726 do {\r
1727 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttr);\r
1728 if ((PageEntry == NULL) || (PageAttr == PageNone)) {\r
1729 return EFI_UNSUPPORTED;\r
1730 }\r
1731\r
1732 //\r
1733 // If the memory range is cross page table boundary, make sure they\r
1734 // share the same attribute. Return EFI_NO_MAPPING if not.\r
1735 //\r
1736 *Attributes = GetAttributesFromPageEntry (PageEntry);\r
1737 if ((MemAttr != (UINT64)-1) && (*Attributes != MemAttr)) {\r
1738 return EFI_NO_MAPPING;\r
1739 }\r
1740\r
1741 switch (PageAttr) {\r
1742 case Page4K:\r
1743 Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;\r
1744 Size -= (SIZE_4KB - (BaseAddress - Address));\r
1745 BaseAddress += (SIZE_4KB - (BaseAddress - Address));\r
1746 break;\r
1747\r
1748 case Page2M:\r
1749 Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;\r
1750 Size -= SIZE_2MB - (BaseAddress - Address);\r
1751 BaseAddress += SIZE_2MB - (BaseAddress - Address);\r
1752 break;\r
1753\r
1754 case Page1G:\r
1755 Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;\r
1756 Size -= SIZE_1GB - (BaseAddress - Address);\r
1757 BaseAddress += SIZE_1GB - (BaseAddress - Address);\r
1758 break;\r
1759\r
1760 default:\r
1761 return EFI_UNSUPPORTED;\r
1762 }\r
1763\r
1764 MemAttr = *Attributes;\r
1765 } while (Size > 0);\r
1766\r
1767 return EFI_SUCCESS;\r
1768}\r
1769\r
1770/**\r
1771 Prevent the memory pages used for SMM page table from been overwritten.\r
1772**/\r
1773VOID\r
1774EnablePageTableProtection (\r
1775 VOID\r
1776 )\r
1777{\r
1778 PAGE_TABLE_POOL *HeadPool;\r
1779 PAGE_TABLE_POOL *Pool;\r
1780 UINT64 PoolSize;\r
1781 EFI_PHYSICAL_ADDRESS Address;\r
1782 UINTN PageTableBase;\r
1783\r
1784 if (mPageTablePool == NULL) {\r
1785 return;\r
1786 }\r
1787\r
1788 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;\r
1789\r
1790 //\r
1791 // ConvertMemoryPageAttributes might update mPageTablePool. It's safer to\r
1792 // remember original one in advance.\r
1793 //\r
1794 HeadPool = mPageTablePool;\r
1795 Pool = HeadPool;\r
1796 do {\r
1797 Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;\r
1798 PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);\r
1799 //\r
1800 // Set entire pool including header, used-memory and left free-memory as ReadOnly in SMM page table.\r
1801 //\r
1802 ConvertMemoryPageAttributes (PageTableBase, m5LevelPagingNeeded, Address, PoolSize, EFI_MEMORY_RO, TRUE, NULL, NULL);\r
1803 Pool = Pool->NextPool;\r
1804 } while (Pool != HeadPool);\r
1805}\r
1806\r
1807/**\r
1808 Return whether memory used by SMM page table need to be set as Read Only.\r
1809\r
1810 @retval TRUE Need to set SMM page table as Read Only.\r
1811 @retval FALSE Do not set SMM page table as Read Only.\r
1812**/\r
1813BOOLEAN\r
1814IfReadOnlyPageTableNeeded (\r
1815 VOID\r
1816 )\r
1817{\r
1818 //\r
1819 // Don't mark page table memory as read-only if\r
1820 // - no restriction on access to non-SMRAM memory; or\r
1821 // - SMM heap guard feature enabled; or\r
1822 // BIT2: SMM page guard enabled\r
1823 // BIT3: SMM pool guard enabled\r
1824 // - SMM profile feature enabled\r
1825 //\r
1826 if (!IsRestrictedMemoryAccess () ||\r
1827 ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) ||\r
1828 FeaturePcdGet (PcdCpuSmmProfileEnable))\r
1829 {\r
1830 if (sizeof (UINTN) == sizeof (UINT64)) {\r
1831 //\r
1832 // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.\r
1833 //\r
1834 ASSERT (\r
1835 !(IsRestrictedMemoryAccess () &&\r
1836 (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0)\r
1837 );\r
1838\r
1839 //\r
1840 // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.\r
1841 //\r
1842 ASSERT (!(IsRestrictedMemoryAccess () && FeaturePcdGet (PcdCpuSmmProfileEnable)));\r
1843 }\r
1844\r
1845 return FALSE;\r
1846 }\r
1847\r
1848 return TRUE;\r
1849}\r
1850\r
1851/**\r
1852 This function sets memory attribute for page table.\r
1853**/\r
1854VOID\r
1855SetPageTableAttributes (\r
1856 VOID\r
1857 )\r
1858{\r
1859 BOOLEAN CetEnabled;\r
1860\r
1861 if (!IfReadOnlyPageTableNeeded ()) {\r
1862 return;\r
1863 }\r
1864\r
1865 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));\r
1866\r
1867 //\r
1868 // Disable write protection, because we need mark page table to be write protected.\r
1869 // We need *write* page table memory, to mark itself to be *read only*.\r
1870 //\r
1871 CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;\r
1872 if (CetEnabled) {\r
1873 //\r
1874 // CET must be disabled if WP is disabled.\r
1875 //\r
1876 DisableCet ();\r
1877 }\r
1878\r
1879 AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);\r
1880\r
1881 // Set memory used by page table as Read Only.\r
1882 DEBUG ((DEBUG_INFO, "Start...\n"));\r
1883 EnablePageTableProtection ();\r
1884\r
1885 //\r
1886 // Enable write protection, after page table attribute updated.\r
1887 //\r
1888 AsmWriteCr0 (AsmReadCr0 () | CR0_WP);\r
1889 mIsReadOnlyPageTable = TRUE;\r
1890\r
1891 //\r
1892 // Flush TLB after mark all page table pool as read only.\r
1893 //\r
1894 FlushTlbForAll ();\r
1895\r
1896 if (CetEnabled) {\r
1897 //\r
1898 // re-enable CET.\r
1899 //\r
1900 EnableCet ();\r
1901 }\r
1902\r
1903 return;\r
1904}\r