UefiCpuPkg/SmmCpuFeaturesLibStm: Add STM library instance
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmCpuMemoryManagement.c
CommitLineData
717fb604
JY
1/** @file\r
2\r
3Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
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
16#define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
17 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))\r
18\r
19PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
20 {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},\r
21 {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},\r
22 {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},\r
23};\r
24\r
25/**\r
26 Return page table base.\r
27\r
28 @return page table base.\r
29**/\r
30UINTN\r
31GetPageTableBase (\r
32 VOID\r
33 )\r
34{\r
35 return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
36}\r
37\r
38/**\r
39 Return length according to page attributes.\r
40\r
41 @param[in] PageAttributes The page attribute of the page entry.\r
42\r
43 @return The length of page entry.\r
44**/\r
45UINTN\r
46PageAttributeToLength (\r
47 IN PAGE_ATTRIBUTE PageAttribute\r
48 )\r
49{\r
50 UINTN Index;\r
51 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
52 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
53 return (UINTN)mPageAttributeTable[Index].Length;\r
54 }\r
55 }\r
56 return 0;\r
57}\r
58\r
59/**\r
60 Return address mask according to page attributes.\r
61\r
62 @param[in] PageAttributes The page attribute of the page entry.\r
63\r
64 @return The address mask of page entry.\r
65**/\r
66UINTN\r
67PageAttributeToMask (\r
68 IN PAGE_ATTRIBUTE PageAttribute\r
69 )\r
70{\r
71 UINTN Index;\r
72 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {\r
73 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
74 return (UINTN)mPageAttributeTable[Index].AddressMask;\r
75 }\r
76 }\r
77 return 0;\r
78}\r
79\r
80/**\r
81 Return page table entry to match the address.\r
82\r
83 @param[in] Address The address to be checked.\r
84 @param[out] PageAttributes The page attribute of the page entry.\r
85\r
86 @return The page entry.\r
87**/\r
88VOID *\r
89GetPageTableEntry (\r
90 IN PHYSICAL_ADDRESS Address,\r
91 OUT PAGE_ATTRIBUTE *PageAttribute\r
92 )\r
93{\r
94 UINTN Index1;\r
95 UINTN Index2;\r
96 UINTN Index3;\r
97 UINTN Index4;\r
98 UINT64 *L1PageTable;\r
99 UINT64 *L2PageTable;\r
100 UINT64 *L3PageTable;\r
101 UINT64 *L4PageTable;\r
102\r
103 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;\r
104 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;\r
105 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;\r
106 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;\r
107\r
108 if (sizeof(UINTN) == sizeof(UINT64)) {\r
109 L4PageTable = (UINT64 *)GetPageTableBase ();\r
110 if (L4PageTable[Index4] == 0) {\r
111 *PageAttribute = PageNone;\r
112 return NULL;\r
113 }\r
114\r
115 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);\r
116 } else {\r
117 L3PageTable = (UINT64 *)GetPageTableBase ();\r
118 }\r
119 if (L3PageTable[Index3] == 0) {\r
120 *PageAttribute = PageNone;\r
121 return NULL;\r
122 }\r
123 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
124 // 1G\r
125 *PageAttribute = Page1G;\r
126 return &L3PageTable[Index3];\r
127 }\r
128\r
129 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);\r
130 if (L2PageTable[Index2] == 0) {\r
131 *PageAttribute = PageNone;\r
132 return NULL;\r
133 }\r
134 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
135 // 2M\r
136 *PageAttribute = Page2M;\r
137 return &L2PageTable[Index2];\r
138 }\r
139\r
140 // 4k\r
141 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);\r
142 if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
143 *PageAttribute = PageNone;\r
144 return NULL;\r
145 }\r
146 *PageAttribute = Page4K;\r
147 return &L1PageTable[Index1];\r
148}\r
149\r
150/**\r
151 Return memory attributes of page entry.\r
152\r
153 @param[in] PageEntry The page entry.\r
154\r
155 @return Memory attributes of page entry.\r
156**/\r
157UINT64\r
158GetAttributesFromPageEntry (\r
159 IN UINT64 *PageEntry\r
160 )\r
161{\r
162 UINT64 Attributes;\r
163 Attributes = 0;\r
164 if ((*PageEntry & IA32_PG_P) == 0) {\r
165 Attributes |= EFI_MEMORY_RP;\r
166 }\r
167 if ((*PageEntry & IA32_PG_RW) == 0) {\r
168 Attributes |= EFI_MEMORY_RO;\r
169 }\r
170 if ((*PageEntry & IA32_PG_NX) != 0) {\r
171 Attributes |= EFI_MEMORY_XP;\r
172 }\r
173 return Attributes;\r
174}\r
175\r
176/**\r
177 Modify memory attributes of page entry.\r
178\r
179 @param[in] PageEntry The page entry.\r
180 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
181 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.\r
182 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
183**/\r
184VOID\r
185ConvertPageEntryAttribute (\r
186 IN UINT64 *PageEntry,\r
187 IN UINT64 Attributes,\r
188 IN BOOLEAN IsSet,\r
189 OUT BOOLEAN *IsModified\r
190 )\r
191{\r
192 UINT64 CurrentPageEntry;\r
193 UINT64 NewPageEntry;\r
194\r
195 CurrentPageEntry = *PageEntry;\r
196 NewPageEntry = CurrentPageEntry;\r
197 if ((Attributes & EFI_MEMORY_RP) != 0) {\r
198 if (IsSet) {\r
199 NewPageEntry &= ~(UINT64)IA32_PG_P;\r
200 } else {\r
201 NewPageEntry |= IA32_PG_P;\r
202 }\r
203 }\r
204 if ((Attributes & EFI_MEMORY_RO) != 0) {\r
205 if (IsSet) {\r
206 NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
207 } else {\r
208 NewPageEntry |= IA32_PG_RW;\r
209 }\r
210 }\r
211 if ((Attributes & EFI_MEMORY_XP) != 0) {\r
750ec4ca
JY
212 if (mXdSupported) {\r
213 if (IsSet) {\r
214 NewPageEntry |= IA32_PG_NX;\r
215 } else {\r
216 NewPageEntry &= ~IA32_PG_NX;\r
217 }\r
717fb604
JY
218 }\r
219 }\r
220 *PageEntry = NewPageEntry;\r
221 if (CurrentPageEntry != NewPageEntry) {\r
222 *IsModified = TRUE;\r
223 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
224 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));\r
225 } else {\r
226 *IsModified = FALSE;\r
227 }\r
228}\r
229\r
230/**\r
231 This function returns if there is need to split page entry.\r
232\r
233 @param[in] BaseAddress The base address to be checked.\r
234 @param[in] Length The length to be checked.\r
235 @param[in] PageEntry The page entry to be checked.\r
236 @param[in] PageAttribute The page attribute of the page entry.\r
237\r
238 @retval SplitAttributes on if there is need to split page entry.\r
239**/\r
240PAGE_ATTRIBUTE\r
241NeedSplitPage (\r
242 IN PHYSICAL_ADDRESS BaseAddress,\r
243 IN UINT64 Length,\r
244 IN UINT64 *PageEntry,\r
245 IN PAGE_ATTRIBUTE PageAttribute\r
246 )\r
247{\r
248 UINT64 PageEntryLength;\r
249\r
250 PageEntryLength = PageAttributeToLength (PageAttribute);\r
251\r
252 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
253 return PageNone;\r
254 }\r
255\r
256 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
257 return Page4K;\r
258 }\r
259\r
260 return Page2M;\r
261}\r
262\r
263/**\r
264 This function splits one page entry to small page entries.\r
265\r
266 @param[in] PageEntry The page entry to be splitted.\r
267 @param[in] PageAttribute The page attribute of the page entry.\r
268 @param[in] SplitAttribute How to split the page entry.\r
269\r
270 @retval RETURN_SUCCESS The page entry is splitted.\r
271 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.\r
272 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.\r
273**/\r
274RETURN_STATUS\r
275SplitPage (\r
276 IN UINT64 *PageEntry,\r
277 IN PAGE_ATTRIBUTE PageAttribute,\r
278 IN PAGE_ATTRIBUTE SplitAttribute\r
279 )\r
280{\r
281 UINT64 BaseAddress;\r
282 UINT64 *NewPageEntry;\r
283 UINTN Index;\r
284\r
285 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
286\r
287 if (PageAttribute == Page2M) {\r
288 //\r
289 // Split 2M to 4K\r
290 //\r
291 ASSERT (SplitAttribute == Page4K);\r
292 if (SplitAttribute == Page4K) {\r
293 NewPageEntry = AllocatePageTableMemory (1);\r
294 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
295 if (NewPageEntry == NULL) {\r
296 return RETURN_OUT_OF_RESOURCES;\r
297 }\r
298 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;\r
299 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
300 NewPageEntry[Index] = BaseAddress + SIZE_4KB * Index + ((*PageEntry) & PAGE_PROGATE_BITS);\r
301 }\r
e50f9512 302 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS;\r
717fb604
JY
303 return RETURN_SUCCESS;\r
304 } else {\r
305 return RETURN_UNSUPPORTED;\r
306 }\r
307 } else if (PageAttribute == Page1G) {\r
308 //\r
309 // Split 1G to 2M\r
310 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
311 //\r
312 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
313 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {\r
314 NewPageEntry = AllocatePageTableMemory (1);\r
315 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
316 if (NewPageEntry == NULL) {\r
317 return RETURN_OUT_OF_RESOURCES;\r
318 }\r
319 BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;\r
320 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
321 NewPageEntry[Index] = BaseAddress + SIZE_2MB * Index + IA32_PG_PS + ((*PageEntry) & PAGE_PROGATE_BITS);\r
322 }\r
e50f9512 323 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS;\r
717fb604
JY
324 return RETURN_SUCCESS;\r
325 } else {\r
326 return RETURN_UNSUPPORTED;\r
327 }\r
328 } else {\r
329 return RETURN_UNSUPPORTED;\r
330 }\r
331}\r
332\r
333/**\r
334 This function modifies the page attributes for the memory region specified by BaseAddress and\r
335 Length from their current attributes to the attributes specified by Attributes.\r
336\r
337 Caller should make sure BaseAddress and Length is at page boundary.\r
338\r
339 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
340 @param[in] Length The size in bytes of the memory region.\r
341 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
342 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.\r
343 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
344 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
345\r
346 @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
347 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
348 BaseAddress and Length cannot be modified.\r
349 @retval RETURN_INVALID_PARAMETER Length is zero.\r
350 Attributes specified an illegal combination of attributes that\r
351 cannot be set together.\r
352 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
353 the memory resource range.\r
354 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
355 resource range specified by BaseAddress and Length.\r
356 The bit mask of attributes is not support for the memory resource\r
357 range specified by BaseAddress and Length.\r
358**/\r
359RETURN_STATUS\r
360EFIAPI\r
361ConvertMemoryPageAttributes (\r
362 IN PHYSICAL_ADDRESS BaseAddress,\r
363 IN UINT64 Length,\r
364 IN UINT64 Attributes,\r
365 IN BOOLEAN IsSet,\r
366 OUT BOOLEAN *IsSplitted, OPTIONAL\r
367 OUT BOOLEAN *IsModified OPTIONAL\r
368 )\r
369{\r
370 UINT64 *PageEntry;\r
371 PAGE_ATTRIBUTE PageAttribute;\r
372 UINTN PageEntryLength;\r
373 PAGE_ATTRIBUTE SplitAttribute;\r
374 RETURN_STATUS Status;\r
375 BOOLEAN IsEntryModified;\r
376\r
377 ASSERT (Attributes != 0);\r
378 ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0);\r
379\r
380 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);\r
381 ASSERT ((Length & (SIZE_4KB - 1)) == 0);\r
382\r
383 if (Length == 0) {\r
384 return RETURN_INVALID_PARAMETER;\r
385 }\r
386\r
387// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
388\r
389 if (IsSplitted != NULL) {\r
390 *IsSplitted = FALSE;\r
391 }\r
392 if (IsModified != NULL) {\r
393 *IsModified = FALSE;\r
394 }\r
395\r
396 //\r
397 // Below logic is to check 2M/4K page to make sure we donot waist memory.\r
398 //\r
399 while (Length != 0) {\r
400 PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);\r
401 if (PageEntry == NULL) {\r
402 return RETURN_UNSUPPORTED;\r
403 }\r
404 PageEntryLength = PageAttributeToLength (PageAttribute);\r
405 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
406 if (SplitAttribute == PageNone) {\r
407 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);\r
408 if (IsEntryModified) {\r
409 if (IsModified != NULL) {\r
410 *IsModified = TRUE;\r
411 }\r
412 }\r
413 //\r
414 // Convert success, move to next\r
415 //\r
416 BaseAddress += PageEntryLength;\r
417 Length -= PageEntryLength;\r
418 } else {\r
419 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);\r
420 if (RETURN_ERROR (Status)) {\r
421 return RETURN_UNSUPPORTED;\r
422 }\r
423 if (IsSplitted != NULL) {\r
424 *IsSplitted = TRUE;\r
425 }\r
426 if (IsModified != NULL) {\r
427 *IsModified = TRUE;\r
428 }\r
429 //\r
430 // Just split current page\r
431 // Convert success in next around\r
432 //\r
433 }\r
434 }\r
435\r
436 return RETURN_SUCCESS;\r
437}\r
438\r
439/**\r
440 FlushTlb on current processor.\r
441\r
442 @param[in,out] Buffer Pointer to private data buffer.\r
443**/\r
444VOID\r
445EFIAPI\r
446FlushTlbOnCurrentProcessor (\r
447 IN OUT VOID *Buffer\r
448 )\r
449{\r
450 CpuFlushTlb ();\r
451}\r
452\r
453/**\r
454 FlushTlb for all processors.\r
455**/\r
456VOID\r
457FlushTlbForAll (\r
458 VOID\r
459 )\r
460{\r
461 UINTN Index;\r
462\r
463 FlushTlbOnCurrentProcessor (NULL);\r
464\r
465 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {\r
466 if (Index != gSmst->CurrentlyExecutingCpu) {\r
467 // Force to start up AP in blocking mode,\r
468 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);\r
469 // Do not check return status, because AP might not be present in some corner cases.\r
470 }\r
471 }\r
472}\r
473\r
474/**\r
475 This function sets the attributes for the memory region specified by BaseAddress and\r
476 Length from their current attributes to the attributes specified by Attributes.\r
477\r
478 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
479 @param[in] Length The size in bytes of the memory region.\r
480 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
481 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
482\r
483 @retval EFI_SUCCESS The attributes were set for the memory region.\r
484 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
485 BaseAddress and Length cannot be modified.\r
486 @retval EFI_INVALID_PARAMETER Length is zero.\r
487 Attributes specified an illegal combination of attributes that\r
488 cannot be set together.\r
489 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
490 the memory resource range.\r
491 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
492 resource range specified by BaseAddress and Length.\r
493 The bit mask of attributes is not support for the memory resource\r
494 range specified by BaseAddress and Length.\r
495\r
496**/\r
497EFI_STATUS\r
498EFIAPI\r
499SmmSetMemoryAttributesEx (\r
500 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
501 IN UINT64 Length,\r
502 IN UINT64 Attributes,\r
503 OUT BOOLEAN *IsSplitted OPTIONAL\r
504 )\r
505{\r
506 EFI_STATUS Status;\r
507 BOOLEAN IsModified;\r
508\r
509 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);\r
510 if (!EFI_ERROR(Status)) {\r
511 if (IsModified) {\r
512 //\r
513 // Flush TLB as last step\r
514 //\r
515 FlushTlbForAll();\r
516 }\r
517 }\r
518\r
519 return Status;\r
520}\r
521\r
522/**\r
523 This function clears the attributes for the memory region specified by BaseAddress and\r
524 Length from their current attributes to the attributes specified by Attributes.\r
525\r
526 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
527 @param[in] Length The size in bytes of the memory region.\r
528 @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
529 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
530\r
531 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
532 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
533 BaseAddress and Length cannot be modified.\r
534 @retval EFI_INVALID_PARAMETER Length is zero.\r
535 Attributes specified an illegal combination of attributes that\r
536 cannot be set together.\r
537 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
538 the memory resource range.\r
539 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
540 resource range specified by BaseAddress and Length.\r
541 The bit mask of attributes is not support for the memory resource\r
542 range specified by BaseAddress and Length.\r
543\r
544**/\r
545EFI_STATUS\r
546EFIAPI\r
547SmmClearMemoryAttributesEx (\r
548 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
549 IN UINT64 Length,\r
550 IN UINT64 Attributes,\r
551 OUT BOOLEAN *IsSplitted OPTIONAL\r
552 )\r
553{\r
554 EFI_STATUS Status;\r
555 BOOLEAN IsModified;\r
556\r
557 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);\r
558 if (!EFI_ERROR(Status)) {\r
559 if (IsModified) {\r
560 //\r
561 // Flush TLB as last step\r
562 //\r
563 FlushTlbForAll();\r
564 }\r
565 }\r
566\r
567 return Status;\r
568}\r
569\r
570/**\r
571 This function sets the attributes for the memory region specified by BaseAddress and\r
572 Length from their current attributes to the attributes specified by Attributes.\r
573\r
574 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
575 @param[in] Length The size in bytes of the memory region.\r
576 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
577\r
578 @retval EFI_SUCCESS The attributes were set for the memory region.\r
579 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
580 BaseAddress and Length cannot be modified.\r
581 @retval EFI_INVALID_PARAMETER Length is zero.\r
582 Attributes specified an illegal combination of attributes that\r
583 cannot be set together.\r
584 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
585 the memory resource range.\r
586 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
587 resource range specified by BaseAddress and Length.\r
588 The bit mask of attributes is not support for the memory resource\r
589 range specified by BaseAddress and Length.\r
590\r
591**/\r
592EFI_STATUS\r
593EFIAPI\r
594SmmSetMemoryAttributes (\r
595 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
596 IN UINT64 Length,\r
597 IN UINT64 Attributes\r
598 )\r
599{\r
600 return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);\r
601}\r
602\r
603/**\r
604 This function clears the attributes for the memory region specified by BaseAddress and\r
605 Length from their current attributes to the attributes specified by Attributes.\r
606\r
607 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
608 @param[in] Length The size in bytes of the memory region.\r
609 @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
610\r
611 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
612 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
613 BaseAddress and Length cannot be modified.\r
614 @retval EFI_INVALID_PARAMETER Length is zero.\r
615 Attributes specified an illegal combination of attributes that\r
616 cannot be set together.\r
617 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
618 the memory resource range.\r
619 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
620 resource range specified by BaseAddress and Length.\r
621 The bit mask of attributes is not support for the memory resource\r
622 range specified by BaseAddress and Length.\r
623\r
624**/\r
625EFI_STATUS\r
626EFIAPI\r
627SmmClearMemoryAttributes (\r
628 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
629 IN UINT64 Length,\r
630 IN UINT64 Attributes\r
631 )\r
632{\r
633 return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);\r
634}\r
635\r
636\r
637\r
638/**\r
639 Retrieves a pointer to the system configuration table from the SMM System Table\r
640 based on a specified GUID.\r
641\r
642 @param[in] TableGuid The pointer to table's GUID type.\r
643 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.\r
644\r
645 @retval EFI_SUCCESS A configuration table matching TableGuid was found.\r
646 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.\r
647\r
648**/\r
649EFI_STATUS\r
650EFIAPI\r
651SmmGetSystemConfigurationTable (\r
652 IN EFI_GUID *TableGuid,\r
653 OUT VOID **Table\r
654 )\r
655{\r
656 UINTN Index;\r
657\r
658 ASSERT (TableGuid != NULL);\r
659 ASSERT (Table != NULL);\r
660\r
661 *Table = NULL;\r
662 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {\r
663 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {\r
664 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;\r
665 return EFI_SUCCESS;\r
666 }\r
667 }\r
668\r
669 return EFI_NOT_FOUND;\r
670}\r
671\r
672/**\r
673 This function sets SMM save state buffer to be RW and XP.\r
674**/\r
675VOID\r
676PatchSmmSaveStateMap (\r
677 VOID\r
678 )\r
679{\r
680 UINTN Index;\r
681 UINTN TileCodeSize;\r
682 UINTN TileDataSize;\r
683 UINTN TileSize;\r
684\r
685 TileCodeSize = GetSmiHandlerSize ();\r
686 TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);\r
f12367a0 687 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);\r
717fb604
JY
688 TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);\r
689 TileSize = TileDataSize + TileCodeSize - 1;\r
690 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);\r
691\r
692 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));\r
693 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {\r
694 //\r
695 // Code\r
696 //\r
697 SmmSetMemoryAttributes (\r
698 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
699 TileCodeSize,\r
700 EFI_MEMORY_RO\r
701 );\r
702 SmmClearMemoryAttributes (\r
703 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
704 TileCodeSize,\r
705 EFI_MEMORY_XP\r
706 );\r
707\r
708 //\r
709 // Data\r
710 //\r
711 SmmClearMemoryAttributes (\r
712 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
713 TileSize - TileCodeSize,\r
714 EFI_MEMORY_RO\r
715 );\r
716 SmmSetMemoryAttributes (\r
717 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
718 TileSize - TileCodeSize,\r
719 EFI_MEMORY_XP\r
720 );\r
721 }\r
722\r
723 //\r
724 // Code\r
725 //\r
726 SmmSetMemoryAttributes (\r
727 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,\r
728 TileCodeSize,\r
729 EFI_MEMORY_RO\r
730 );\r
731 SmmClearMemoryAttributes (\r
732 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + 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[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
742 SIZE_32KB - TileCodeSize,\r
743 EFI_MEMORY_RO\r
744 );\r
745 SmmSetMemoryAttributes (\r
746 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
747 SIZE_32KB - TileCodeSize,\r
748 EFI_MEMORY_XP\r
749 );\r
750}\r
751\r
717fb604
JY
752/**\r
753 This function sets memory attribute according to MemoryAttributesTable.\r
754**/\r
755VOID\r
756SetMemMapAttributes (\r
757 VOID\r
758 )\r
759{\r
760 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
761 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;\r
762 UINTN MemoryMapEntryCount;\r
763 UINTN DescriptorSize;\r
764 UINTN Index;\r
765 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;\r
766\r
767 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);\r
768 if (MemoryAttributesTable == NULL) {\r
769 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));\r
770 return ;\r
771 }\r
772\r
773 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));\r
774 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));\r
775 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));\r
776 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));\r
777\r
778 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;\r
779 DescriptorSize = MemoryAttributesTable->DescriptorSize;\r
780 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);\r
781 MemoryMap = MemoryMapStart;\r
782 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
783 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));\r
784 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));\r
785 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));\r
786 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));\r
787 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));\r
788 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));\r
789 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);\r
790 }\r
791\r
792 MemoryMap = MemoryMapStart;\r
793 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
794 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));\r
795 switch (MemoryMap->Type) {\r
796 case EfiRuntimeServicesCode:\r
797 SmmSetMemoryAttributes (\r
798 MemoryMap->PhysicalStart,\r
799 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
800 EFI_MEMORY_RO\r
801 );\r
802 break;\r
803 case EfiRuntimeServicesData:\r
804 SmmSetMemoryAttributes (\r
805 MemoryMap->PhysicalStart,\r
806 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
807 EFI_MEMORY_XP\r
808 );\r
809 break;\r
810 default:\r
811 SmmSetMemoryAttributes (\r
812 MemoryMap->PhysicalStart,\r
813 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
814 EFI_MEMORY_XP\r
815 );\r
816 break;\r
817 }\r
818 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);\r
819 }\r
820\r
821 PatchSmmSaveStateMap ();\r
822 PatchGdtIdtMap ();\r
823\r
824 return ;\r
825}\r