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