UefiCpuPkg/PiSmmCpuDxeSmm: Add paging protection.
[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
212 if (IsSet) {\r
213 NewPageEntry |= IA32_PG_NX;\r
214 } else {\r
215 NewPageEntry &= ~IA32_PG_NX;\r
216 }\r
217 }\r
218 *PageEntry = NewPageEntry;\r
219 if (CurrentPageEntry != NewPageEntry) {\r
220 *IsModified = TRUE;\r
221 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
222 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));\r
223 } else {\r
224 *IsModified = FALSE;\r
225 }\r
226}\r
227\r
228/**\r
229 This function returns if there is need to split page entry.\r
230\r
231 @param[in] BaseAddress The base address to be checked.\r
232 @param[in] Length The length to be checked.\r
233 @param[in] PageEntry The page entry to be checked.\r
234 @param[in] PageAttribute The page attribute of the page entry.\r
235\r
236 @retval SplitAttributes on if there is need to split page entry.\r
237**/\r
238PAGE_ATTRIBUTE\r
239NeedSplitPage (\r
240 IN PHYSICAL_ADDRESS BaseAddress,\r
241 IN UINT64 Length,\r
242 IN UINT64 *PageEntry,\r
243 IN PAGE_ATTRIBUTE PageAttribute\r
244 )\r
245{\r
246 UINT64 PageEntryLength;\r
247\r
248 PageEntryLength = PageAttributeToLength (PageAttribute);\r
249\r
250 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
251 return PageNone;\r
252 }\r
253\r
254 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
255 return Page4K;\r
256 }\r
257\r
258 return Page2M;\r
259}\r
260\r
261/**\r
262 This function splits one page entry to small page entries.\r
263\r
264 @param[in] PageEntry The page entry to be splitted.\r
265 @param[in] PageAttribute The page attribute of the page entry.\r
266 @param[in] SplitAttribute How to split the page entry.\r
267\r
268 @retval RETURN_SUCCESS The page entry is splitted.\r
269 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.\r
270 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.\r
271**/\r
272RETURN_STATUS\r
273SplitPage (\r
274 IN UINT64 *PageEntry,\r
275 IN PAGE_ATTRIBUTE PageAttribute,\r
276 IN PAGE_ATTRIBUTE SplitAttribute\r
277 )\r
278{\r
279 UINT64 BaseAddress;\r
280 UINT64 *NewPageEntry;\r
281 UINTN Index;\r
282\r
283 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
284\r
285 if (PageAttribute == Page2M) {\r
286 //\r
287 // Split 2M to 4K\r
288 //\r
289 ASSERT (SplitAttribute == Page4K);\r
290 if (SplitAttribute == Page4K) {\r
291 NewPageEntry = AllocatePageTableMemory (1);\r
292 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
293 if (NewPageEntry == NULL) {\r
294 return RETURN_OUT_OF_RESOURCES;\r
295 }\r
296 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;\r
297 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
298 NewPageEntry[Index] = BaseAddress + SIZE_4KB * Index + ((*PageEntry) & PAGE_PROGATE_BITS);\r
299 }\r
300 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + ((*PageEntry) & PAGE_PROGATE_BITS);\r
301 return RETURN_SUCCESS;\r
302 } else {\r
303 return RETURN_UNSUPPORTED;\r
304 }\r
305 } else if (PageAttribute == Page1G) {\r
306 //\r
307 // Split 1G to 2M\r
308 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
309 //\r
310 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
311 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {\r
312 NewPageEntry = AllocatePageTableMemory (1);\r
313 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
314 if (NewPageEntry == NULL) {\r
315 return RETURN_OUT_OF_RESOURCES;\r
316 }\r
317 BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;\r
318 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
319 NewPageEntry[Index] = BaseAddress + SIZE_2MB * Index + IA32_PG_PS + ((*PageEntry) & PAGE_PROGATE_BITS);\r
320 }\r
321 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + ((*PageEntry) & PAGE_PROGATE_BITS);\r
322 return RETURN_SUCCESS;\r
323 } else {\r
324 return RETURN_UNSUPPORTED;\r
325 }\r
326 } else {\r
327 return RETURN_UNSUPPORTED;\r
328 }\r
329}\r
330\r
331/**\r
332 This function modifies the page attributes for the memory region specified by BaseAddress and\r
333 Length from their current attributes to the attributes specified by Attributes.\r
334\r
335 Caller should make sure BaseAddress and Length is at page boundary.\r
336\r
337 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
338 @param[in] Length The size in bytes of the memory region.\r
339 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
340 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.\r
341 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
342 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
343\r
344 @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
345 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
346 BaseAddress and Length cannot be modified.\r
347 @retval RETURN_INVALID_PARAMETER Length is zero.\r
348 Attributes specified an illegal combination of attributes that\r
349 cannot be set together.\r
350 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
351 the memory resource range.\r
352 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
353 resource range specified by BaseAddress and Length.\r
354 The bit mask of attributes is not support for the memory resource\r
355 range specified by BaseAddress and Length.\r
356**/\r
357RETURN_STATUS\r
358EFIAPI\r
359ConvertMemoryPageAttributes (\r
360 IN PHYSICAL_ADDRESS BaseAddress,\r
361 IN UINT64 Length,\r
362 IN UINT64 Attributes,\r
363 IN BOOLEAN IsSet,\r
364 OUT BOOLEAN *IsSplitted, OPTIONAL\r
365 OUT BOOLEAN *IsModified OPTIONAL\r
366 )\r
367{\r
368 UINT64 *PageEntry;\r
369 PAGE_ATTRIBUTE PageAttribute;\r
370 UINTN PageEntryLength;\r
371 PAGE_ATTRIBUTE SplitAttribute;\r
372 RETURN_STATUS Status;\r
373 BOOLEAN IsEntryModified;\r
374\r
375 ASSERT (Attributes != 0);\r
376 ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0);\r
377\r
378 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);\r
379 ASSERT ((Length & (SIZE_4KB - 1)) == 0);\r
380\r
381 if (Length == 0) {\r
382 return RETURN_INVALID_PARAMETER;\r
383 }\r
384\r
385// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
386\r
387 if (IsSplitted != NULL) {\r
388 *IsSplitted = FALSE;\r
389 }\r
390 if (IsModified != NULL) {\r
391 *IsModified = FALSE;\r
392 }\r
393\r
394 //\r
395 // Below logic is to check 2M/4K page to make sure we donot waist memory.\r
396 //\r
397 while (Length != 0) {\r
398 PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);\r
399 if (PageEntry == NULL) {\r
400 return RETURN_UNSUPPORTED;\r
401 }\r
402 PageEntryLength = PageAttributeToLength (PageAttribute);\r
403 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
404 if (SplitAttribute == PageNone) {\r
405 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);\r
406 if (IsEntryModified) {\r
407 if (IsModified != NULL) {\r
408 *IsModified = TRUE;\r
409 }\r
410 }\r
411 //\r
412 // Convert success, move to next\r
413 //\r
414 BaseAddress += PageEntryLength;\r
415 Length -= PageEntryLength;\r
416 } else {\r
417 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);\r
418 if (RETURN_ERROR (Status)) {\r
419 return RETURN_UNSUPPORTED;\r
420 }\r
421 if (IsSplitted != NULL) {\r
422 *IsSplitted = TRUE;\r
423 }\r
424 if (IsModified != NULL) {\r
425 *IsModified = TRUE;\r
426 }\r
427 //\r
428 // Just split current page\r
429 // Convert success in next around\r
430 //\r
431 }\r
432 }\r
433\r
434 return RETURN_SUCCESS;\r
435}\r
436\r
437/**\r
438 FlushTlb on current processor.\r
439\r
440 @param[in,out] Buffer Pointer to private data buffer.\r
441**/\r
442VOID\r
443EFIAPI\r
444FlushTlbOnCurrentProcessor (\r
445 IN OUT VOID *Buffer\r
446 )\r
447{\r
448 CpuFlushTlb ();\r
449}\r
450\r
451/**\r
452 FlushTlb for all processors.\r
453**/\r
454VOID\r
455FlushTlbForAll (\r
456 VOID\r
457 )\r
458{\r
459 UINTN Index;\r
460\r
461 FlushTlbOnCurrentProcessor (NULL);\r
462\r
463 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {\r
464 if (Index != gSmst->CurrentlyExecutingCpu) {\r
465 // Force to start up AP in blocking mode,\r
466 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);\r
467 // Do not check return status, because AP might not be present in some corner cases.\r
468 }\r
469 }\r
470}\r
471\r
472/**\r
473 This function sets the attributes for the memory region specified by BaseAddress and\r
474 Length from their current attributes to the attributes specified by Attributes.\r
475\r
476 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
477 @param[in] Length The size in bytes of the memory region.\r
478 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
479 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
480\r
481 @retval EFI_SUCCESS The attributes were set for the memory region.\r
482 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
483 BaseAddress and Length cannot be modified.\r
484 @retval EFI_INVALID_PARAMETER Length is zero.\r
485 Attributes specified an illegal combination of attributes that\r
486 cannot be set together.\r
487 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
488 the memory resource range.\r
489 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
490 resource range specified by BaseAddress and Length.\r
491 The bit mask of attributes is not support for the memory resource\r
492 range specified by BaseAddress and Length.\r
493\r
494**/\r
495EFI_STATUS\r
496EFIAPI\r
497SmmSetMemoryAttributesEx (\r
498 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
499 IN UINT64 Length,\r
500 IN UINT64 Attributes,\r
501 OUT BOOLEAN *IsSplitted OPTIONAL\r
502 )\r
503{\r
504 EFI_STATUS Status;\r
505 BOOLEAN IsModified;\r
506\r
507 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);\r
508 if (!EFI_ERROR(Status)) {\r
509 if (IsModified) {\r
510 //\r
511 // Flush TLB as last step\r
512 //\r
513 FlushTlbForAll();\r
514 }\r
515 }\r
516\r
517 return Status;\r
518}\r
519\r
520/**\r
521 This function clears the attributes for the memory region specified by BaseAddress and\r
522 Length from their current attributes to the attributes specified by Attributes.\r
523\r
524 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
525 @param[in] Length The size in bytes of the memory region.\r
526 @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
527 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
528\r
529 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
530 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
531 BaseAddress and Length cannot be modified.\r
532 @retval EFI_INVALID_PARAMETER Length is zero.\r
533 Attributes specified an illegal combination of attributes that\r
534 cannot be set together.\r
535 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
536 the memory resource range.\r
537 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
538 resource range specified by BaseAddress and Length.\r
539 The bit mask of attributes is not support for the memory resource\r
540 range specified by BaseAddress and Length.\r
541\r
542**/\r
543EFI_STATUS\r
544EFIAPI\r
545SmmClearMemoryAttributesEx (\r
546 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
547 IN UINT64 Length,\r
548 IN UINT64 Attributes,\r
549 OUT BOOLEAN *IsSplitted OPTIONAL\r
550 )\r
551{\r
552 EFI_STATUS Status;\r
553 BOOLEAN IsModified;\r
554\r
555 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);\r
556 if (!EFI_ERROR(Status)) {\r
557 if (IsModified) {\r
558 //\r
559 // Flush TLB as last step\r
560 //\r
561 FlushTlbForAll();\r
562 }\r
563 }\r
564\r
565 return Status;\r
566}\r
567\r
568/**\r
569 This function sets the attributes for the memory region specified by BaseAddress and\r
570 Length from their current attributes to the attributes specified by Attributes.\r
571\r
572 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
573 @param[in] Length The size in bytes of the memory region.\r
574 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
575\r
576 @retval EFI_SUCCESS The attributes were set for the memory region.\r
577 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
578 BaseAddress and Length cannot be modified.\r
579 @retval EFI_INVALID_PARAMETER Length is zero.\r
580 Attributes specified an illegal combination of attributes that\r
581 cannot be set together.\r
582 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
583 the memory resource range.\r
584 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
585 resource range specified by BaseAddress and Length.\r
586 The bit mask of attributes is not support for the memory resource\r
587 range specified by BaseAddress and Length.\r
588\r
589**/\r
590EFI_STATUS\r
591EFIAPI\r
592SmmSetMemoryAttributes (\r
593 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
594 IN UINT64 Length,\r
595 IN UINT64 Attributes\r
596 )\r
597{\r
598 return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);\r
599}\r
600\r
601/**\r
602 This function clears the attributes for the memory region specified by BaseAddress and\r
603 Length from their current attributes to the attributes specified by Attributes.\r
604\r
605 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
606 @param[in] Length The size in bytes of the memory region.\r
607 @param[in] Attributes The bit mask of attributes to clear for the memory region.\r
608\r
609 @retval EFI_SUCCESS The attributes were cleared for the memory region.\r
610 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
611 BaseAddress and Length cannot be modified.\r
612 @retval EFI_INVALID_PARAMETER Length is zero.\r
613 Attributes specified an illegal combination of attributes that\r
614 cannot be set together.\r
615 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
616 the memory resource range.\r
617 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
618 resource range specified by BaseAddress and Length.\r
619 The bit mask of attributes is not support for the memory resource\r
620 range specified by BaseAddress and Length.\r
621\r
622**/\r
623EFI_STATUS\r
624EFIAPI\r
625SmmClearMemoryAttributes (\r
626 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
627 IN UINT64 Length,\r
628 IN UINT64 Attributes\r
629 )\r
630{\r
631 return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);\r
632}\r
633\r
634\r
635\r
636/**\r
637 Retrieves a pointer to the system configuration table from the SMM System Table\r
638 based on a specified GUID.\r
639\r
640 @param[in] TableGuid The pointer to table's GUID type.\r
641 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.\r
642\r
643 @retval EFI_SUCCESS A configuration table matching TableGuid was found.\r
644 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.\r
645\r
646**/\r
647EFI_STATUS\r
648EFIAPI\r
649SmmGetSystemConfigurationTable (\r
650 IN EFI_GUID *TableGuid,\r
651 OUT VOID **Table\r
652 )\r
653{\r
654 UINTN Index;\r
655\r
656 ASSERT (TableGuid != NULL);\r
657 ASSERT (Table != NULL);\r
658\r
659 *Table = NULL;\r
660 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {\r
661 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {\r
662 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;\r
663 return EFI_SUCCESS;\r
664 }\r
665 }\r
666\r
667 return EFI_NOT_FOUND;\r
668}\r
669\r
670/**\r
671 This function sets SMM save state buffer to be RW and XP.\r
672**/\r
673VOID\r
674PatchSmmSaveStateMap (\r
675 VOID\r
676 )\r
677{\r
678 UINTN Index;\r
679 UINTN TileCodeSize;\r
680 UINTN TileDataSize;\r
681 UINTN TileSize;\r
682\r
683 TileCodeSize = GetSmiHandlerSize ();\r
684 TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);\r
685 TileDataSize = sizeof (SMRAM_SAVE_STATE_MAP) + sizeof (PROCESSOR_SMM_DESCRIPTOR);\r
686 TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);\r
687 TileSize = TileDataSize + TileCodeSize - 1;\r
688 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);\r
689\r
690 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));\r
691 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {\r
692 //\r
693 // Code\r
694 //\r
695 SmmSetMemoryAttributes (\r
696 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
697 TileCodeSize,\r
698 EFI_MEMORY_RO\r
699 );\r
700 SmmClearMemoryAttributes (\r
701 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,\r
702 TileCodeSize,\r
703 EFI_MEMORY_XP\r
704 );\r
705\r
706 //\r
707 // Data\r
708 //\r
709 SmmClearMemoryAttributes (\r
710 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
711 TileSize - TileCodeSize,\r
712 EFI_MEMORY_RO\r
713 );\r
714 SmmSetMemoryAttributes (\r
715 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,\r
716 TileSize - TileCodeSize,\r
717 EFI_MEMORY_XP\r
718 );\r
719 }\r
720\r
721 //\r
722 // Code\r
723 //\r
724 SmmSetMemoryAttributes (\r
725 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,\r
726 TileCodeSize,\r
727 EFI_MEMORY_RO\r
728 );\r
729 SmmClearMemoryAttributes (\r
730 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,\r
731 TileCodeSize,\r
732 EFI_MEMORY_XP\r
733 );\r
734\r
735 //\r
736 // Data\r
737 //\r
738 SmmClearMemoryAttributes (\r
739 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
740 SIZE_32KB - TileCodeSize,\r
741 EFI_MEMORY_RO\r
742 );\r
743 SmmSetMemoryAttributes (\r
744 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,\r
745 SIZE_32KB - TileCodeSize,\r
746 EFI_MEMORY_XP\r
747 );\r
748}\r
749\r
750/**\r
751 This function sets GDT/IDT buffer to be RO and XP.\r
752**/\r
753VOID\r
754PatchGdtIdtMap (\r
755 VOID\r
756 )\r
757{\r
758 EFI_PHYSICAL_ADDRESS BaseAddress;\r
759 UINTN Size;\r
760\r
761 //\r
762 // GDT\r
763 //\r
764 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));\r
765\r
766 BaseAddress = mGdtBuffer;\r
767 Size = ALIGN_VALUE(mGdtBufferSize, SIZE_4KB);\r
768 SmmSetMemoryAttributes (\r
769 BaseAddress,\r
770 Size,\r
771 EFI_MEMORY_RO\r
772 );\r
773 SmmSetMemoryAttributes (\r
774 BaseAddress,\r
775 Size,\r
776 EFI_MEMORY_XP\r
777 );\r
778\r
779 //\r
780 // IDT\r
781 //\r
782 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));\r
783\r
784 BaseAddress = gcSmiIdtr.Base;\r
785 Size = ALIGN_VALUE(gcSmiIdtr.Limit + 1, SIZE_4KB);\r
786 SmmSetMemoryAttributes (\r
787 BaseAddress,\r
788 Size,\r
789 EFI_MEMORY_RO\r
790 );\r
791 SmmSetMemoryAttributes (\r
792 BaseAddress,\r
793 Size,\r
794 EFI_MEMORY_XP\r
795 );\r
796}\r
797\r
798/**\r
799 This function sets memory attribute according to MemoryAttributesTable.\r
800**/\r
801VOID\r
802SetMemMapAttributes (\r
803 VOID\r
804 )\r
805{\r
806 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
807 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;\r
808 UINTN MemoryMapEntryCount;\r
809 UINTN DescriptorSize;\r
810 UINTN Index;\r
811 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;\r
812\r
813 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);\r
814 if (MemoryAttributesTable == NULL) {\r
815 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));\r
816 return ;\r
817 }\r
818\r
819 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));\r
820 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));\r
821 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));\r
822 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));\r
823\r
824 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;\r
825 DescriptorSize = MemoryAttributesTable->DescriptorSize;\r
826 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);\r
827 MemoryMap = MemoryMapStart;\r
828 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
829 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));\r
830 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));\r
831 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));\r
832 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));\r
833 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));\r
834 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));\r
835 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);\r
836 }\r
837\r
838 MemoryMap = MemoryMapStart;\r
839 for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
840 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));\r
841 switch (MemoryMap->Type) {\r
842 case EfiRuntimeServicesCode:\r
843 SmmSetMemoryAttributes (\r
844 MemoryMap->PhysicalStart,\r
845 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
846 EFI_MEMORY_RO\r
847 );\r
848 break;\r
849 case EfiRuntimeServicesData:\r
850 SmmSetMemoryAttributes (\r
851 MemoryMap->PhysicalStart,\r
852 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
853 EFI_MEMORY_XP\r
854 );\r
855 break;\r
856 default:\r
857 SmmSetMemoryAttributes (\r
858 MemoryMap->PhysicalStart,\r
859 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
860 EFI_MEMORY_XP\r
861 );\r
862 break;\r
863 }\r
864 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);\r
865 }\r
866\r
867 PatchSmmSaveStateMap ();\r
868 PatchGdtIdtMap ();\r
869\r
870 return ;\r
871}\r