]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmCpuMemoryManagement.c
CommitLineData
717fb604
JY
1/** @file\r
2\r
3eb69b08 3Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
0acd8697 4SPDX-License-Identifier: BSD-2-Clause-Patent\r
717fb604
JY
5\r
6**/\r
7\r
8#include "PiSmmCpuDxeSmm.h"\r
9\r
ac6613db
JY
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
d2fc7711
JY
17#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
18 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))\r
19\r
053e878b
MK
20EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;\r
21UINTN mUefiMemoryMapSize;\r
22UINTN mUefiDescriptorSize;\r
d2fc7711 23\r
053e878b
MK
24EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mGcdMemSpace = NULL;\r
25UINTN mGcdMemNumberOfDesc = 0;\r
ac6613db 26\r
8a2e1a9d
JY
27EFI_MEMORY_ATTRIBUTES_TABLE *mUefiMemoryAttributesTable = NULL;\r
28\r
053e878b
MK
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
717fb604
JY
33};\r
34\r
7b475490
DT
35BOOLEAN mIsShadowStack = FALSE;\r
36BOOLEAN m5LevelPagingNeeded = FALSE;\r
3eb69b08 37\r
b822be1a 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
b670700d
TD
70 VOID *Buffer;\r
71 BOOLEAN CetEnabled;\r
72 BOOLEAN WpEnabled;\r
73 IA32_CR0 Cr0;\r
b822be1a 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
b670700d
TD
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
b822be1a 123 }\r
124\r
b822be1a 125 SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);\r
b670700d
TD
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
b822be1a 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
717fb604
JY
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
053e878b
MK
201\r
202 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {\r
717fb604
JY
203 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
204 return (UINTN)mPageAttributeTable[Index].Length;\r
205 }\r
206 }\r
053e878b 207\r
717fb604
JY
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
053e878b
MK
224\r
225 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {\r
717fb604
JY
226 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
227 return (UINTN)mPageAttributeTable[Index].AddressMask;\r
228 }\r
229 }\r
053e878b 230\r
717fb604
JY
231 return 0;\r
232}\r
233\r
234/**\r
235 Return page table entry to match the address.\r
236\r
7b475490
DT
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
717fb604
JY
241\r
242 @return The page entry.\r
243**/\r
244VOID *\r
245GetPageTableEntry (\r
7b475490
DT
246 IN UINTN PageTableBase,\r
247 IN BOOLEAN Enable5LevelPaging,\r
053e878b
MK
248 IN PHYSICAL_ADDRESS Address,\r
249 OUT PAGE_ATTRIBUTE *PageAttribute\r
717fb604
JY
250 )\r
251{\r
7b475490
DT
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
404250c8 262\r
4eee0cc7 263 Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;\r
717fb604
JY
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
053e878b 269 if (sizeof (UINTN) == sizeof (UINT64)) {\r
4eee0cc7 270 if (Enable5LevelPaging) {\r
404250c8 271 L5PageTable = (UINT64 *)PageTableBase;\r
4eee0cc7
RN
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
404250c8 279 L4PageTable = (UINT64 *)PageTableBase;\r
4eee0cc7 280 }\r
053e878b 281\r
717fb604
JY
282 if (L4PageTable[Index4] == 0) {\r
283 *PageAttribute = PageNone;\r
284 return NULL;\r
285 }\r
286\r
241f9149 287 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604 288 } else {\r
404250c8 289 L3PageTable = (UINT64 *)PageTableBase;\r
717fb604 290 }\r
053e878b 291\r
717fb604
JY
292 if (L3PageTable[Index3] == 0) {\r
293 *PageAttribute = PageNone;\r
294 return NULL;\r
295 }\r
053e878b 296\r
717fb604
JY
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
241f9149 303 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
304 if (L2PageTable[Index2] == 0) {\r
305 *PageAttribute = PageNone;\r
306 return NULL;\r
307 }\r
053e878b 308\r
717fb604
JY
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
241f9149 316 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
317 if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
318 *PageAttribute = PageNone;\r
319 return NULL;\r
320 }\r
053e878b 321\r
717fb604
JY
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
053e878b 335 IN UINT64 *PageEntry\r
717fb604
JY
336 )\r
337{\r
338 UINT64 Attributes;\r
053e878b 339\r
717fb604
JY
340 Attributes = 0;\r
341 if ((*PageEntry & IA32_PG_P) == 0) {\r
342 Attributes |= EFI_MEMORY_RP;\r
343 }\r
053e878b 344\r
717fb604
JY
345 if ((*PageEntry & IA32_PG_RW) == 0) {\r
346 Attributes |= EFI_MEMORY_RO;\r
347 }\r
053e878b 348\r
717fb604
JY
349 if ((*PageEntry & IA32_PG_NX) != 0) {\r
350 Attributes |= EFI_MEMORY_XP;\r
351 }\r
053e878b 352\r
717fb604
JY
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
053e878b
MK
366 IN UINT64 *PageEntry,\r
367 IN UINT64 Attributes,\r
368 IN BOOLEAN IsSet,\r
369 OUT BOOLEAN *IsModified\r
717fb604
JY
370 )\r
371{\r
372 UINT64 CurrentPageEntry;\r
373 UINT64 NewPageEntry;\r
374\r
375 CurrentPageEntry = *PageEntry;\r
053e878b 376 NewPageEntry = CurrentPageEntry;\r
717fb604
JY
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
053e878b 384\r
717fb604
JY
385 if ((Attributes & EFI_MEMORY_RO) != 0) {\r
386 if (IsSet) {\r
387 NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
83d58711 388 if (mIsShadowStack) {\r
3eb69b08
JY
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
717fb604
JY
399 } else {\r
400 NewPageEntry |= IA32_PG_RW;\r
401 }\r
402 }\r
053e878b 403\r
717fb604 404 if ((Attributes & EFI_MEMORY_XP) != 0) {\r
750ec4ca
JY
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
717fb604
JY
411 }\r
412 }\r
053e878b 413\r
717fb604
JY
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
053e878b
MK
436 IN PHYSICAL_ADDRESS BaseAddress,\r
437 IN UINT64 Length,\r
438 IN UINT64 *PageEntry,\r
439 IN PAGE_ATTRIBUTE PageAttribute\r
717fb604
JY
440 )\r
441{\r
053e878b 442 UINT64 PageEntryLength;\r
717fb604
JY
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
053e878b
MK
470 IN UINT64 *PageEntry,\r
471 IN PAGE_ATTRIBUTE PageAttribute,\r
472 IN PAGE_ATTRIBUTE SplitAttribute\r
717fb604
JY
473 )\r
474{\r
053e878b
MK
475 UINT64 BaseAddress;\r
476 UINT64 *NewPageEntry;\r
477 UINTN Index;\r
717fb604
JY
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
053e878b 492\r
717fb604 493 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;\r
053e878b 494 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {\r
241f9149 495 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
717fb604 496 }\r
053e878b 497\r
241f9149 498 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
717fb604
JY
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
053e878b 509 if (((SplitAttribute == Page2M) || (SplitAttribute == Page4K))) {\r
717fb604
JY
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
053e878b 515\r
717fb604 516 BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;\r
053e878b 517 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {\r
241f9149 518 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
717fb604 519 }\r
053e878b 520\r
241f9149 521 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
717fb604
JY
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
7b475490
DT
537 @param[in] PageTableBase The page table base.\r
538 @param[in] EnablePML5Paging If PML5 paging is enabled.\r
717fb604
JY
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
717fb604 560ConvertMemoryPageAttributes (\r
7b475490
DT
561 IN UINTN PageTableBase,\r
562 IN BOOLEAN EnablePML5Paging,\r
053e878b
MK
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
717fb604
JY
569 )\r
570{\r
053e878b
MK
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
717fb604
JY
578\r
579 ASSERT (Attributes != 0);\r
e77966b3 580 ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);\r
717fb604
JY
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
714c2603
SZ
589 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);\r
590 if (BaseAddress > MaximumSupportMemAddress) {\r
591 return RETURN_UNSUPPORTED;\r
592 }\r
053e878b 593\r
714c2603
SZ
594 if (Length > MaximumSupportMemAddress) {\r
595 return RETURN_UNSUPPORTED;\r
596 }\r
053e878b 597\r
714c2603
SZ
598 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {\r
599 return RETURN_UNSUPPORTED;\r
600 }\r
601\r
053e878b 602 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
717fb604
JY
603\r
604 if (IsSplitted != NULL) {\r
605 *IsSplitted = FALSE;\r
606 }\r
053e878b 607\r
717fb604
JY
608 if (IsModified != NULL) {\r
609 *IsModified = FALSE;\r
610 }\r
611\r
612 //\r
ef62da4f 613 // Below logic is to check 2M/4K page to make sure we do not waste memory.\r
717fb604
JY
614 //\r
615 while (Length != 0) {\r
7b475490 616 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttribute);\r
717fb604
JY
617 if (PageEntry == NULL) {\r
618 return RETURN_UNSUPPORTED;\r
619 }\r
053e878b 620\r
717fb604 621 PageEntryLength = PageAttributeToLength (PageAttribute);\r
053e878b 622 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
717fb604
JY
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
053e878b 630\r
717fb604
JY
631 //\r
632 // Convert success, move to next\r
633 //\r
634 BaseAddress += PageEntryLength;\r
053e878b 635 Length -= PageEntryLength;\r
717fb604
JY
636 } else {\r
637 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);\r
638 if (RETURN_ERROR (Status)) {\r
639 return RETURN_UNSUPPORTED;\r
640 }\r
053e878b 641\r
717fb604
JY
642 if (IsSplitted != NULL) {\r
643 *IsSplitted = TRUE;\r
644 }\r
053e878b 645\r
717fb604
JY
646 if (IsModified != NULL) {\r
647 *IsModified = TRUE;\r
648 }\r
053e878b 649\r
717fb604
JY
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
053e878b 682 UINTN Index;\r
717fb604
JY
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
7b475490
DT
699 @param[in] PageTableBase The page table base.\r
700 @param[in] EnablePML5Paging If PML5 paging is enabled.\r
717fb604
JY
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
717fb604 721SmmSetMemoryAttributesEx (\r
7b475490
DT
722 IN UINTN PageTableBase,\r
723 IN BOOLEAN EnablePML5Paging,\r
053e878b
MK
724 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
725 IN UINT64 Length,\r
726 IN UINT64 Attributes,\r
727 OUT BOOLEAN *IsSplitted OPTIONAL\r
717fb604
JY
728 )\r
729{\r
730 EFI_STATUS Status;\r
731 BOOLEAN IsModified;\r
732\r
7b475490 733 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);\r
053e878b 734 if (!EFI_ERROR (Status)) {\r
717fb604
JY
735 if (IsModified) {\r
736 //\r
737 // Flush TLB as last step\r
738 //\r
053e878b 739 FlushTlbForAll ();\r
717fb604
JY
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
7b475490
DT
750 @param[in] PageTableBase The page table base.\r
751 @param[in] EnablePML5Paging If PML5 paging is enabled.\r
717fb604
JY
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
aae02dcc 762 cannot be cleared together.\r
717fb604
JY
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
aae02dcc 767 The bit mask of attributes is not supported for the memory resource\r
717fb604
JY
768 range specified by BaseAddress and Length.\r
769\r
770**/\r
771EFI_STATUS\r
717fb604 772SmmClearMemoryAttributesEx (\r
7b475490
DT
773 IN UINTN PageTableBase,\r
774 IN BOOLEAN EnablePML5Paging,\r
053e878b
MK
775 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
776 IN UINT64 Length,\r
777 IN UINT64 Attributes,\r
778 OUT BOOLEAN *IsSplitted OPTIONAL\r
717fb604
JY
779 )\r
780{\r
781 EFI_STATUS Status;\r
782 BOOLEAN IsModified;\r
783\r
7b475490 784 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);\r
053e878b 785 if (!EFI_ERROR (Status)) {\r
717fb604
JY
786 if (IsModified) {\r
787 //\r
788 // Flush TLB as last step\r
789 //\r
053e878b 790 FlushTlbForAll ();\r
717fb604
JY
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
aae02dcc 815 The bit mask of attributes is not supported for the memory resource\r
717fb604
JY
816 range specified by BaseAddress and Length.\r
817\r
818**/\r
819EFI_STATUS\r
717fb604 820SmmSetMemoryAttributes (\r
053e878b
MK
821 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
822 IN UINT64 Length,\r
823 IN UINT64 Attributes\r
717fb604
JY
824 )\r
825{\r
7b475490
DT
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
717fb604
JY
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
aae02dcc 849 cannot be cleared together.\r
717fb604
JY
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
aae02dcc 854 The bit mask of attributes is not supported for the memory resource\r
717fb604
JY
855 range specified by BaseAddress and Length.\r
856\r
857**/\r
858EFI_STATUS\r
717fb604 859SmmClearMemoryAttributes (\r
053e878b
MK
860 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
861 IN UINT64 Length,\r
862 IN UINT64 Attributes\r
717fb604
JY
863 )\r
864{\r
7b475490
DT
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
717fb604
JY
873}\r
874\r
3eb69b08
JY
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
053e878b
MK
886 IN UINTN Cr3,\r
887 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
888 IN UINT64 Length\r
3eb69b08
JY
889 )\r
890{\r
891 EFI_STATUS Status;\r
892\r
83d58711 893 mIsShadowStack = TRUE;\r
7b475490 894 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RO, NULL);\r
83d58711 895 mIsShadowStack = FALSE;\r
3eb69b08
JY
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
717fb604 906\r
3eb69b08
JY
907 @retval EFI_SUCCESS The not present memory is set.\r
908**/\r
909EFI_STATUS\r
910SetNotPresentPage (\r
053e878b
MK
911 IN UINTN Cr3,\r
912 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
913 IN UINT64 Length\r
3eb69b08
JY
914 )\r
915{\r
916 EFI_STATUS Status;\r
917\r
7b475490 918 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RP, NULL);\r
3eb69b08
JY
919 return Status;\r
920}\r
717fb604
JY
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
053e878b 940 UINTN Index;\r
717fb604
JY
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
053e878b 970 TileCodeSize = ALIGN_VALUE (TileCodeSize, SIZE_4KB);\r
f12367a0 971 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);\r
053e878b
MK
972 TileDataSize = ALIGN_VALUE (TileDataSize, SIZE_4KB);\r
973 TileSize = TileDataSize + TileCodeSize - 1;\r
974 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);\r
717fb604
JY
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
6e601a41
SZ
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
053e878b
MK
1044 EFI_PHYSICAL_ADDRESS BaseAddress;\r
1045 UINTN Size;\r
6e601a41
SZ
1046\r
1047 //\r
1048 // GDT\r
1049 //\r
1050 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));\r
1051\r
1052 BaseAddress = mGdtBuffer;\r
053e878b 1053 Size = ALIGN_VALUE (mGdtBufferSize, SIZE_4KB);\r
6e601a41
SZ
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
053e878b 1070 Size = ALIGN_VALUE (gcSmiIdtr.Limit + 1, SIZE_4KB);\r
fe90d0d2
SZ
1071 //\r
1072 // The range should have been set to RO\r
1073 // if it is allocated with EfiRuntimeServicesCode.\r
1074 //\r
6e601a41
SZ
1075 SmmSetMemoryAttributes (\r
1076 BaseAddress,\r
1077 Size,\r
1078 EFI_MEMORY_XP\r
1079 );\r
1080}\r
1081\r
717fb604
JY
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
053e878b
MK
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
717fb604
JY
1096\r
1097 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);\r
1098 if (MemoryAttributesTable == NULL) {\r
1099 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));\r
053e878b 1100 return;\r
717fb604
JY
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
053e878b
MK
1109 DescriptorSize = MemoryAttributesTable->DescriptorSize;\r
1110 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);\r
1111 MemoryMap = MemoryMapStart;\r
717fb604
JY
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
053e878b 1119 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);\r
717fb604
JY
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
053e878b
MK
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
717fb604 1147 }\r
053e878b
MK
1148\r
1149 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);\r
717fb604
JY
1150 }\r
1151\r
1152 PatchSmmSaveStateMap ();\r
1153 PatchGdtIdtMap ();\r
1154\r
053e878b 1155 return;\r
717fb604 1156}\r
d2fc7711
JY
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
053e878b
MK
1174 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;\r
1175 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;\r
1176 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;\r
1177 EFI_MEMORY_DESCRIPTOR TempMemoryMap;\r
d2fc7711 1178\r
053e878b 1179 MemoryMapEntry = MemoryMap;\r
d2fc7711 1180 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
053e878b 1181 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);\r
d2fc7711
JY
1182 while (MemoryMapEntry < MemoryMapEnd) {\r
1183 while (NextMemoryMapEntry < MemoryMapEnd) {\r
1184 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {\r
053e878b
MK
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
d2fc7711
JY
1188 }\r
1189\r
1190 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
1191 }\r
1192\r
053e878b
MK
1193 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
1194 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
d2fc7711
JY
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
053e878b
MK
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
d2fc7711
JY
1226 }\r
1227}\r
1228\r
1229/**\r
ef62da4f 1230 Merge continuous memory map entries whose type is\r
d2fc7711
JY
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
053e878b
MK
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
d2fc7711 1256\r
053e878b 1257 MemoryMapEntry = MemoryMap;\r
d2fc7711 1258 NewMemoryMapEntry = MemoryMap;\r
053e878b 1259 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + *MemoryMapSize);\r
d2fc7711 1260 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {\r
053e878b 1261 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));\r
d2fc7711
JY
1262 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
1263\r
1264 do {\r
053e878b 1265 MemoryBlockLength = (UINT64)(EFI_PAGES_TO_SIZE ((UINTN)MemoryMapEntry->NumberOfPages));\r
d2fc7711 1266 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&\r
053e878b
MK
1267 IsUefiPageNotPresent (MemoryMapEntry) && IsUefiPageNotPresent (NextMemoryMapEntry) &&\r
1268 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart))\r
1269 {\r
d2fc7711
JY
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
053e878b 1283 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
d2fc7711
JY
1284 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);\r
1285 }\r
1286\r
1287 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;\r
1288\r
053e878b 1289 return;\r
d2fc7711
JY
1290}\r
1291\r
ac6613db
JY
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
053e878b 1307 return;\r
ac6613db
JY
1308 }\r
1309\r
1310 mGcdMemNumberOfDesc = 0;\r
1311 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
053e878b
MK
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
ac6613db
JY
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
053e878b 1326 return;\r
ac6613db
JY
1327 }\r
1328\r
1329 mGcdMemNumberOfDesc = 0;\r
1330 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
053e878b
MK
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
ac6613db
JY
1336 CopyMem (\r
1337 &mGcdMemSpace[mGcdMemNumberOfDesc],\r
1338 &MemSpaceMap[Index],\r
053e878b 1339 sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR)\r
ac6613db
JY
1340 );\r
1341 mGcdMemNumberOfDesc++;\r
1342 }\r
1343 }\r
1344\r
1345 gBS->FreePool (MemSpaceMap);\r
1346}\r
1347\r
8a2e1a9d
JY
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
6d9a0a94 1361 if (!EFI_ERROR (Status) && (MemoryAttributesTable != NULL)) {\r
053e878b 1362 MemoryAttributesTableSize = sizeof (EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries;\r
8a2e1a9d
JY
1363 mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable);\r
1364 ASSERT (mUefiMemoryAttributesTable != NULL);\r
1365 }\r
1366}\r
1367\r
d2fc7711
JY
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
053e878b
MK
1376 EFI_STATUS Status;\r
1377 UINTN MapKey;\r
1378 UINT32 DescriptorVersion;\r
1379 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
1380 UINTN UefiMemoryMapSize;\r
d2fc7711
JY
1381\r
1382 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));\r
1383\r
1384 UefiMemoryMapSize = 0;\r
053e878b
MK
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
d2fc7711
JY
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
053e878b 1399 return;\r
d2fc7711
JY
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
403f5476 1415 if (MemoryMap == NULL) {\r
053e878b 1416 return;\r
403f5476
HW
1417 }\r
1418\r
d2fc7711
JY
1419 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);\r
1420 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);\r
1421\r
1422 mUefiMemoryMapSize = UefiMemoryMapSize;\r
053e878b 1423 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);\r
d2fc7711
JY
1424 ASSERT (mUefiMemoryMap != NULL);\r
1425\r
1426 gBS->FreePool (MemoryMap);\r
ac6613db
JY
1427\r
1428 //\r
1429 // Get additional information from GCD memory map.\r
1430 //\r
1431 GetGcdMemoryMap ();\r
8a2e1a9d
JY
1432\r
1433 //\r
1434 // Get UEFI memory attributes table.\r
1435 //\r
1436 GetUefiMemoryAttributesTable ();\r
d2fc7711
JY
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
053e878b
MK
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
d2fc7711
JY
1456\r
1457 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));\r
1458\r
ac6613db
JY
1459 if (mUefiMemoryMap != NULL) {\r
1460 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
053e878b 1461 MemoryMap = mUefiMemoryMap;\r
ac6613db 1462 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
053e878b 1463 if (IsUefiPageNotPresent (MemoryMap)) {\r
ac6613db
JY
1464 Status = SmmSetMemoryAttributes (\r
1465 MemoryMap->PhysicalStart,\r
053e878b 1466 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),\r
ac6613db
JY
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
053e878b 1473 MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),\r
ac6613db
JY
1474 Status\r
1475 ));\r
1476 }\r
053e878b
MK
1477\r
1478 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);\r
ac6613db 1479 }\r
d2fc7711 1480 }\r
053e878b 1481\r
ac6613db
JY
1482 //\r
1483 // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().\r
1484 //\r
d2fc7711 1485\r
ac6613db
JY
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
714c2603 1491 Status = SmmSetMemoryAttributes (\r
ac6613db
JY
1492 mGcdMemSpace[Index].BaseAddress,\r
1493 mGcdMemSpace[Index].Length,\r
714c2603
SZ
1494 EFI_MEMORY_RP\r
1495 );\r
1496 DEBUG ((\r
1497 DEBUG_INFO,\r
ac6613db
JY
1498 "GcdMemory protection: 0x%lx - 0x%lx %r\n",\r
1499 mGcdMemSpace[Index].BaseAddress,\r
1500 mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,\r
714c2603
SZ
1501 Status\r
1502 ));\r
d2fc7711 1503 }\r
d2fc7711 1504 }\r
053e878b 1505\r
d2fc7711 1506 //\r
ac6613db 1507 // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().\r
d2fc7711 1508 //\r
8a2e1a9d
JY
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
053e878b 1516 if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {\r
8a2e1a9d
JY
1517 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {\r
1518 Status = SmmSetMemoryAttributes (\r
1519 Entry->PhysicalStart,\r
053e878b 1520 EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),\r
8a2e1a9d
JY
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
053e878b 1527 Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),\r
8a2e1a9d
JY
1528 Status\r
1529 ));\r
1530 }\r
1531 }\r
053e878b 1532\r
8a2e1a9d
JY
1533 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);\r
1534 }\r
1535 }\r
053e878b 1536\r
8a2e1a9d
JY
1537 //\r
1538 // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().\r
1539 //\r
d2fc7711
JY
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
053e878b
MK
1555 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
1556 UINTN MemoryMapEntryCount;\r
1557 UINTN Index;\r
1558 EFI_MEMORY_DESCRIPTOR *Entry;\r
d2fc7711 1559\r
ac6613db 1560 if (mUefiMemoryMap != NULL) {\r
053e878b 1561 MemoryMap = mUefiMemoryMap;\r
ac6613db
JY
1562 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
1563 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
1564 if (IsUefiPageNotPresent (MemoryMap)) {\r
1565 if ((Address >= MemoryMap->PhysicalStart) &&\r
053e878b
MK
1566 (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages)))\r
1567 {\r
ac6613db
JY
1568 return TRUE;\r
1569 }\r
1570 }\r
053e878b
MK
1571\r
1572 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);\r
ac6613db 1573 }\r
403f5476
HW
1574 }\r
1575\r
ac6613db
JY
1576 if (mGcdMemSpace != NULL) {\r
1577 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {\r
1578 if ((Address >= mGcdMemSpace[Index].BaseAddress) &&\r
053e878b
MK
1579 (Address < mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length))\r
1580 {\r
d2fc7711
JY
1581 return TRUE;\r
1582 }\r
1583 }\r
d2fc7711 1584 }\r
ac6613db 1585\r
8a2e1a9d
JY
1586 if (mUefiMemoryAttributesTable != NULL) {\r
1587 Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);\r
1588 for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {\r
053e878b 1589 if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {\r
8a2e1a9d
JY
1590 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {\r
1591 if ((Address >= Entry->PhysicalStart) &&\r
053e878b
MK
1592 (Address < Entry->PhysicalStart + LShiftU64 (Entry->NumberOfPages, EFI_PAGE_SHIFT)))\r
1593 {\r
8a2e1a9d
JY
1594 return TRUE;\r
1595 }\r
053e878b 1596\r
8a2e1a9d
JY
1597 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);\r
1598 }\r
1599 }\r
1600 }\r
1601 }\r
053e878b 1602\r
d2fc7711
JY
1603 return FALSE;\r
1604}\r
827330cc
JW
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
aae02dcc 1624 The bit mask of attributes is not supported for\r
827330cc
JW
1625 the memory resource range specified by\r
1626 BaseAddress and Length.\r
1627\r
1628**/\r
1629EFI_STATUS\r
1630EFIAPI\r
1631EdkiiSmmSetMemoryAttributes (\r
053e878b
MK
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
827330cc
JW
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
aae02dcc 1649 @param Attributes The bit mask of attributes to clear for the memory\r
827330cc
JW
1650 region.\r
1651\r
aae02dcc 1652 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
827330cc
JW
1653 @retval EFI_INVALID_PARAMETER Length is zero.\r
1654 Attributes specified an illegal combination of\r
aae02dcc 1655 attributes that cannot be cleared together.\r
827330cc
JW
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
aae02dcc 1659 The bit mask of attributes is not supported for\r
827330cc
JW
1660 the memory resource range specified by\r
1661 BaseAddress and Length.\r
1662\r
1663**/\r
1664EFI_STATUS\r
1665EFIAPI\r
1666EdkiiSmmClearMemoryAttributes (\r
053e878b
MK
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
827330cc
JW
1671 )\r
1672{\r
1673 return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);\r
1674}\r
1675\r
1676/**\r
aae02dcc 1677 This function retrieves the attributes of the memory region specified by\r
827330cc
JW
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
827330cc
JW
1695\r
1696**/\r
1697EFI_STATUS\r
1698EFIAPI\r
1699EdkiiSmmGetMemoryAttributes (\r
053e878b
MK
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
827330cc
JW
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
7b475490
DT
1711 UINTN PageTableBase;\r
1712 BOOLEAN EnablePML5Paging;\r
1713 IA32_CR4 Cr4;\r
827330cc 1714\r
053e878b 1715 if ((Length < SIZE_4KB) || (Attributes == NULL)) {\r
827330cc
JW
1716 return EFI_INVALID_PARAMETER;\r
1717 }\r
1718\r
053e878b 1719 Size = (INT64)Length;\r
827330cc
JW
1720 MemAttr = (UINT64)-1;\r
1721\r
7b475490
DT
1722 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;\r
1723 Cr4.UintN = AsmReadCr4 ();\r
1724 EnablePML5Paging = (BOOLEAN)(Cr4.Bits.LA57 == 1);\r
1725\r
827330cc 1726 do {\r
7b475490 1727 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttr);\r
053e878b 1728 if ((PageEntry == NULL) || (PageAttr == PageNone)) {\r
827330cc
JW
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
053e878b 1737 if ((MemAttr != (UINT64)-1) && (*Attributes != MemAttr)) {\r
827330cc
JW
1738 return EFI_NO_MAPPING;\r
1739 }\r
1740\r
1741 switch (PageAttr) {\r
053e878b
MK
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
827330cc 1747\r
053e878b
MK
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
827330cc
JW
1762 }\r
1763\r
1764 MemAttr = *Attributes;\r
827330cc
JW
1765 } while (Size > 0);\r
1766\r
1767 return EFI_SUCCESS;\r
1768}\r
72a9386f
TD
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