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