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