]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c
UefiCpuPkg/PiSmmCpu: Add SMM Comm Buffer Paging Protection.
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / X64 / PageTbl.c
CommitLineData
427e3573
MK
1/** @file\r
2Page Fault (#PF) handler for X64 processors\r
3\r
fe3a75bc 4Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>\r
427e3573
MK
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "PiSmmCpuDxeSmm.h"\r
16\r
17#define PAGE_TABLE_PAGES 8\r
18#define ACC_MAX_BIT BIT3\r
19LIST_ENTRY mPagePool = INITIALIZE_LIST_HEAD_VARIABLE (mPagePool);\r
427e3573 20BOOLEAN m1GPageTableSupport = FALSE;\r
717fb604
JY
21UINT8 mPhysicalAddressBits;\r
22BOOLEAN mCpuSmmStaticPageTable;\r
427e3573
MK
23\r
24/**\r
25 Check if 1-GByte pages is supported by processor or not.\r
26\r
27 @retval TRUE 1-GByte pages is supported.\r
28 @retval FALSE 1-GByte pages is not supported.\r
29\r
30**/\r
31BOOLEAN\r
32Is1GPageSupport (\r
33 VOID\r
34 )\r
35{\r
36 UINT32 RegEax;\r
37 UINT32 RegEdx;\r
38\r
39 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
40 if (RegEax >= 0x80000001) {\r
41 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
42 if ((RegEdx & BIT26) != 0) {\r
43 return TRUE;\r
44 }\r
45 }\r
46 return FALSE;\r
47}\r
48\r
49/**\r
50 Set sub-entries number in entry.\r
51\r
52 @param[in, out] Entry Pointer to entry\r
53 @param[in] SubEntryNum Sub-entries number based on 0:\r
54 0 means there is 1 sub-entry under this entry\r
55 0x1ff means there is 512 sub-entries under this entry\r
56\r
57**/\r
58VOID\r
59SetSubEntriesNum (\r
60 IN OUT UINT64 *Entry,\r
61 IN UINT64 SubEntryNum\r
62 )\r
63{\r
64 //\r
65 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry\r
66 //\r
67 *Entry = BitFieldWrite64 (*Entry, 52, 60, SubEntryNum);\r
68}\r
69\r
70/**\r
71 Return sub-entries number in entry.\r
72\r
73 @param[in] Entry Pointer to entry\r
74\r
75 @return Sub-entries number based on 0:\r
76 0 means there is 1 sub-entry under this entry\r
77 0x1ff means there is 512 sub-entries under this entry\r
78**/\r
79UINT64\r
80GetSubEntriesNum (\r
81 IN UINT64 *Entry\r
82 )\r
83{\r
84 //\r
85 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry\r
86 //\r
87 return BitFieldRead64 (*Entry, 52, 60);\r
88}\r
89\r
717fb604
JY
90/**\r
91 Calculate the maximum support address.\r
92\r
93 @return the maximum support address.\r
94**/\r
95UINT8\r
96CalculateMaximumSupportAddress (\r
97 VOID\r
98 )\r
99{\r
100 UINT32 RegEax;\r
101 UINT8 PhysicalAddressBits;\r
102 VOID *Hob;\r
103\r
104 //\r
105 // Get physical address bits supported.\r
106 //\r
107 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
108 if (Hob != NULL) {\r
109 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
110 } else {\r
111 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
112 if (RegEax >= 0x80000008) {\r
113 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
114 PhysicalAddressBits = (UINT8) RegEax;\r
115 } else {\r
116 PhysicalAddressBits = 36;\r
117 }\r
118 }\r
119\r
120 //\r
121 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
122 //\r
123 ASSERT (PhysicalAddressBits <= 52);\r
124 if (PhysicalAddressBits > 48) {\r
125 PhysicalAddressBits = 48;\r
126 }\r
127 return PhysicalAddressBits;\r
128}\r
129\r
130/**\r
131 Set static page table.\r
132\r
133 @param[in] PageTable Address of page table.\r
134**/\r
135VOID\r
136SetStaticPageTable (\r
137 IN UINTN PageTable\r
138 )\r
139{\r
140 UINT64 PageAddress;\r
141 UINTN NumberOfPml4EntriesNeeded;\r
142 UINTN NumberOfPdpEntriesNeeded;\r
143 UINTN IndexOfPml4Entries;\r
144 UINTN IndexOfPdpEntries;\r
145 UINTN IndexOfPageDirectoryEntries;\r
146 UINT64 *PageMapLevel4Entry;\r
147 UINT64 *PageMap;\r
148 UINT64 *PageDirectoryPointerEntry;\r
149 UINT64 *PageDirectory1GEntry;\r
150 UINT64 *PageDirectoryEntry;\r
151\r
152 if (mPhysicalAddressBits <= 39 ) {\r
153 NumberOfPml4EntriesNeeded = 1;\r
154 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (mPhysicalAddressBits - 30));\r
155 } else {\r
156 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (mPhysicalAddressBits - 39));\r
157 NumberOfPdpEntriesNeeded = 512;\r
158 }\r
159\r
160 //\r
161 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.\r
162 //\r
163 PageMap = (VOID *) PageTable;\r
164\r
165 PageMapLevel4Entry = PageMap;\r
166 PageAddress = 0;\r
167 for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
168 //\r
169 // Each PML4 entry points to a page of Page Directory Pointer entries.\r
170 //\r
171 PageDirectoryPointerEntry = (UINT64 *) ((*PageMapLevel4Entry) & gPhyMask);\r
172 if (PageDirectoryPointerEntry == NULL) {\r
173 PageDirectoryPointerEntry = AllocatePageTableMemory (1);\r
174 ASSERT(PageDirectoryPointerEntry != NULL);\r
175 ZeroMem (PageDirectoryPointerEntry, EFI_PAGES_TO_SIZE(1));\r
176\r
177 *PageMapLevel4Entry = ((UINTN)PageDirectoryPointerEntry & gPhyMask) | PAGE_ATTRIBUTE_BITS;\r
178 }\r
179\r
180 if (m1GPageTableSupport) {\r
181 PageDirectory1GEntry = PageDirectoryPointerEntry;\r
182 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {\r
183 if (IndexOfPml4Entries == 0 && IndexOfPageDirectoryEntries < 4) {\r
184 //\r
185 // Skip the < 4G entries\r
186 //\r
187 continue;\r
188 }\r
189 //\r
190 // Fill in the Page Directory entries\r
191 //\r
192 *PageDirectory1GEntry = (PageAddress & gPhyMask) | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
193 }\r
194 } else {\r
195 PageAddress = BASE_4GB;\r
196 for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {\r
197 if (IndexOfPml4Entries == 0 && IndexOfPdpEntries < 4) {\r
198 //\r
199 // Skip the < 4G entries\r
200 //\r
201 continue;\r
202 }\r
203 //\r
204 // Each Directory Pointer entries points to a page of Page Directory entires.\r
205 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.\r
206 //\r
207 PageDirectoryEntry = (UINT64 *) ((*PageDirectoryPointerEntry) & gPhyMask);\r
208 if (PageDirectoryEntry == NULL) {\r
209 PageDirectoryEntry = AllocatePageTableMemory (1);\r
210 ASSERT(PageDirectoryEntry != NULL);\r
211 ZeroMem (PageDirectoryEntry, EFI_PAGES_TO_SIZE(1));\r
212\r
213 //\r
214 // Fill in a Page Directory Pointer Entries\r
215 //\r
216 *PageDirectoryPointerEntry = (UINT64)(UINTN)PageDirectoryEntry | PAGE_ATTRIBUTE_BITS;\r
217 }\r
218\r
219 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {\r
220 //\r
221 // Fill in the Page Directory entries\r
222 //\r
223 *PageDirectoryEntry = (UINT64)PageAddress | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
224 }\r
225 }\r
226 }\r
227 }\r
228}\r
229\r
427e3573
MK
230/**\r
231 Create PageTable for SMM use.\r
232\r
233 @return The address of PML4 (to set CR3).\r
234\r
235**/\r
236UINT32\r
237SmmInitPageTable (\r
238 VOID\r
239 )\r
240{\r
241 EFI_PHYSICAL_ADDRESS Pages;\r
242 UINT64 *PTEntry;\r
243 LIST_ENTRY *FreePage;\r
244 UINTN Index;\r
245 UINTN PageFaultHandlerHookAddress;\r
246 IA32_IDT_GATE_DESCRIPTOR *IdtEntry;\r
5c88af79 247 EFI_STATUS Status;\r
427e3573
MK
248\r
249 //\r
250 // Initialize spin lock\r
251 //\r
fe3a75bc 252 InitializeSpinLock (mPFLock);\r
427e3573 253\r
717fb604 254 mCpuSmmStaticPageTable = PcdGetBool (PcdCpuSmmStaticPageTable);\r
427e3573 255 m1GPageTableSupport = Is1GPageSupport ();\r
717fb604
JY
256 DEBUG ((DEBUG_INFO, "1GPageTableSupport - 0x%x\n", m1GPageTableSupport));\r
257 DEBUG ((DEBUG_INFO, "PcdCpuSmmStaticPageTable - 0x%x\n", mCpuSmmStaticPageTable));\r
258\r
259 mPhysicalAddressBits = CalculateMaximumSupportAddress ();\r
260 DEBUG ((DEBUG_INFO, "PhysicalAddressBits - 0x%x\n", mPhysicalAddressBits));\r
427e3573
MK
261 //\r
262 // Generate PAE page table for the first 4GB memory space\r
263 //\r
717fb604 264 Pages = Gen4GPageTable (FALSE);\r
427e3573
MK
265\r
266 //\r
267 // Set IA32_PG_PMNT bit to mask this entry\r
268 //\r
269 PTEntry = (UINT64*)(UINTN)Pages;\r
270 for (Index = 0; Index < 4; Index++) {\r
271 PTEntry[Index] |= IA32_PG_PMNT;\r
272 }\r
273\r
274 //\r
275 // Fill Page-Table-Level4 (PML4) entry\r
276 //\r
717fb604
JY
277 PTEntry = (UINT64*)AllocatePageTableMemory (1);\r
278 ASSERT (PTEntry != NULL);\r
279 *PTEntry = Pages | PAGE_ATTRIBUTE_BITS;\r
427e3573 280 ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry));\r
717fb604 281\r
427e3573
MK
282 //\r
283 // Set sub-entries number\r
284 //\r
285 SetSubEntriesNum (PTEntry, 3);\r
286\r
717fb604
JY
287 if (mCpuSmmStaticPageTable) {\r
288 SetStaticPageTable ((UINTN)PTEntry);\r
289 } else {\r
290 //\r
291 // Add pages to page pool\r
292 //\r
293 FreePage = (LIST_ENTRY*)AllocatePageTableMemory (PAGE_TABLE_PAGES);\r
294 ASSERT (FreePage != NULL);\r
295 for (Index = 0; Index < PAGE_TABLE_PAGES; Index++) {\r
296 InsertTailList (&mPagePool, FreePage);\r
297 FreePage += EFI_PAGE_SIZE / sizeof (*FreePage);\r
298 }\r
427e3573
MK
299 }\r
300\r
301 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
302 //\r
303 // Set own Page Fault entry instead of the default one, because SMM Profile\r
304 // feature depends on IRET instruction to do Single Step\r
305 //\r
306 PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;\r
307 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;\r
308 IdtEntry += EXCEPT_IA32_PAGE_FAULT;\r
309 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;\r
310 IdtEntry->Bits.Reserved_0 = 0;\r
311 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
312 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);\r
313 IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32);\r
314 IdtEntry->Bits.Reserved_1 = 0;\r
315 } else {\r
316 //\r
317 // Register Smm Page Fault Handler\r
318 //\r
5c88af79
JF
319 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
320 ASSERT_EFI_ERROR (Status);\r
427e3573
MK
321 }\r
322\r
323 //\r
324 // Additional SMM IDT initialization for SMM stack guard\r
325 //\r
326 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
327 InitializeIDTSmmStackGuard ();\r
328 }\r
329\r
330 //\r
331 // Return the address of PML4 (to set CR3)\r
332 //\r
333 return (UINT32)(UINTN)PTEntry;\r
334}\r
335\r
336/**\r
337 Set access record in entry.\r
338\r
339 @param[in, out] Entry Pointer to entry\r
340 @param[in] Acc Access record value\r
341\r
342**/\r
343VOID\r
344SetAccNum (\r
345 IN OUT UINT64 *Entry,\r
346 IN UINT64 Acc\r
347 )\r
348{\r
349 //\r
350 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry\r
351 //\r
352 *Entry = BitFieldWrite64 (*Entry, 9, 11, Acc);\r
353}\r
354\r
355/**\r
356 Return access record in entry.\r
357\r
358 @param[in] Entry Pointer to entry\r
359\r
360 @return Access record value.\r
361\r
362**/\r
363UINT64\r
364GetAccNum (\r
365 IN UINT64 *Entry\r
366 )\r
367{\r
368 //\r
369 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry\r
370 //\r
371 return BitFieldRead64 (*Entry, 9, 11);\r
372}\r
373\r
374/**\r
375 Return and update the access record in entry.\r
376\r
377 @param[in, out] Entry Pointer to entry\r
378\r
379 @return Access record value.\r
380\r
381**/\r
382UINT64\r
383GetAndUpdateAccNum (\r
384 IN OUT UINT64 *Entry\r
385 )\r
386{\r
387 UINT64 Acc;\r
388\r
389 Acc = GetAccNum (Entry);\r
390 if ((*Entry & IA32_PG_A) != 0) {\r
391 //\r
392 // If this entry has been accessed, clear access flag in Entry and update access record\r
393 // to the initial value 7, adding ACC_MAX_BIT is to make it larger than others\r
394 //\r
395 *Entry &= ~(UINT64)(UINTN)IA32_PG_A;\r
396 SetAccNum (Entry, 0x7);\r
397 return (0x7 + ACC_MAX_BIT);\r
398 } else {\r
399 if (Acc != 0) {\r
400 //\r
401 // If the access record is not the smallest value 0, minus 1 and update the access record field\r
402 //\r
403 SetAccNum (Entry, Acc - 1);\r
404 }\r
405 }\r
406 return Acc;\r
407}\r
408\r
409/**\r
410 Reclaim free pages for PageFault handler.\r
411\r
412 Search the whole entries tree to find the leaf entry that has the smallest\r
413 access record value. Insert the page pointed by this leaf entry into the\r
414 page pool. And check its upper entries if need to be inserted into the page\r
415 pool or not.\r
416\r
417**/\r
418VOID\r
419ReclaimPages (\r
420 VOID\r
421 )\r
422{\r
423 UINT64 *Pml4;\r
424 UINT64 *Pdpt;\r
425 UINT64 *Pdt;\r
426 UINTN Pml4Index;\r
427 UINTN PdptIndex;\r
428 UINTN PdtIndex;\r
429 UINTN MinPml4;\r
430 UINTN MinPdpt;\r
431 UINTN MinPdt;\r
432 UINT64 MinAcc;\r
433 UINT64 Acc;\r
434 UINT64 SubEntriesNum;\r
435 BOOLEAN PML4EIgnore;\r
436 BOOLEAN PDPTEIgnore;\r
437 UINT64 *ReleasePageAddress;\r
438\r
439 Pml4 = NULL;\r
440 Pdpt = NULL;\r
441 Pdt = NULL;\r
442 MinAcc = (UINT64)-1;\r
443 MinPml4 = (UINTN)-1;\r
444 MinPdpt = (UINTN)-1;\r
445 MinPdt = (UINTN)-1;\r
446 Acc = 0;\r
447 ReleasePageAddress = 0;\r
448\r
449 //\r
450 // First, find the leaf entry has the smallest access record value\r
451 //\r
452 Pml4 = (UINT64*)(UINTN)(AsmReadCr3 () & gPhyMask);\r
453 for (Pml4Index = 0; Pml4Index < EFI_PAGE_SIZE / sizeof (*Pml4); Pml4Index++) {\r
454 if ((Pml4[Pml4Index] & IA32_PG_P) == 0 || (Pml4[Pml4Index] & IA32_PG_PMNT) != 0) {\r
455 //\r
456 // If the PML4 entry is not present or is masked, skip it\r
457 //\r
458 continue;\r
459 }\r
460 Pdpt = (UINT64*)(UINTN)(Pml4[Pml4Index] & gPhyMask);\r
461 PML4EIgnore = FALSE;\r
462 for (PdptIndex = 0; PdptIndex < EFI_PAGE_SIZE / sizeof (*Pdpt); PdptIndex++) {\r
463 if ((Pdpt[PdptIndex] & IA32_PG_P) == 0 || (Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {\r
464 //\r
465 // If the PDPT entry is not present or is masked, skip it\r
466 //\r
467 if ((Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {\r
468 //\r
469 // If the PDPT entry is masked, we will ignore checking the PML4 entry\r
470 //\r
471 PML4EIgnore = TRUE;\r
472 }\r
473 continue;\r
474 }\r
475 if ((Pdpt[PdptIndex] & IA32_PG_PS) == 0) {\r
476 //\r
477 // It's not 1-GByte pages entry, it should be a PDPT entry,\r
478 // we will not check PML4 entry more\r
479 //\r
480 PML4EIgnore = TRUE;\r
481 Pdt = (UINT64*)(UINTN)(Pdpt[PdptIndex] & gPhyMask);\r
482 PDPTEIgnore = FALSE;\r
483 for (PdtIndex = 0; PdtIndex < EFI_PAGE_SIZE / sizeof(*Pdt); PdtIndex++) {\r
484 if ((Pdt[PdtIndex] & IA32_PG_P) == 0 || (Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {\r
485 //\r
486 // If the PD entry is not present or is masked, skip it\r
487 //\r
488 if ((Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {\r
489 //\r
490 // If the PD entry is masked, we will not PDPT entry more\r
491 //\r
492 PDPTEIgnore = TRUE;\r
493 }\r
494 continue;\r
495 }\r
496 if ((Pdt[PdtIndex] & IA32_PG_PS) == 0) {\r
497 //\r
498 // It's not 2 MByte page table entry, it should be PD entry\r
499 // we will find the entry has the smallest access record value\r
500 //\r
501 PDPTEIgnore = TRUE;\r
502 Acc = GetAndUpdateAccNum (Pdt + PdtIndex);\r
503 if (Acc < MinAcc) {\r
504 //\r
505 // If the PD entry has the smallest access record value,\r
506 // save the Page address to be released\r
507 //\r
508 MinAcc = Acc;\r
509 MinPml4 = Pml4Index;\r
510 MinPdpt = PdptIndex;\r
511 MinPdt = PdtIndex;\r
512 ReleasePageAddress = Pdt + PdtIndex;\r
513 }\r
514 }\r
515 }\r
516 if (!PDPTEIgnore) {\r
517 //\r
518 // If this PDPT entry has no PDT entries pointer to 4 KByte pages,\r
519 // it should only has the entries point to 2 MByte Pages\r
520 //\r
521 Acc = GetAndUpdateAccNum (Pdpt + PdptIndex);\r
522 if (Acc < MinAcc) {\r
523 //\r
524 // If the PDPT entry has the smallest access record value,\r
525 // save the Page address to be released\r
526 //\r
527 MinAcc = Acc;\r
528 MinPml4 = Pml4Index;\r
529 MinPdpt = PdptIndex;\r
530 MinPdt = (UINTN)-1;\r
531 ReleasePageAddress = Pdpt + PdptIndex;\r
532 }\r
533 }\r
534 }\r
535 }\r
536 if (!PML4EIgnore) {\r
537 //\r
538 // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,\r
539 // it should only has the entries point to 1 GByte Pages\r
540 //\r
541 Acc = GetAndUpdateAccNum (Pml4 + Pml4Index);\r
542 if (Acc < MinAcc) {\r
543 //\r
544 // If the PML4 entry has the smallest access record value,\r
545 // save the Page address to be released\r
546 //\r
547 MinAcc = Acc;\r
548 MinPml4 = Pml4Index;\r
549 MinPdpt = (UINTN)-1;\r
550 MinPdt = (UINTN)-1;\r
551 ReleasePageAddress = Pml4 + Pml4Index;\r
552 }\r
553 }\r
554 }\r
555 //\r
556 // Make sure one PML4/PDPT/PD entry is selected\r
557 //\r
558 ASSERT (MinAcc != (UINT64)-1);\r
559\r
560 //\r
561 // Secondly, insert the page pointed by this entry into page pool and clear this entry\r
562 //\r
563 InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(*ReleasePageAddress & gPhyMask));\r
564 *ReleasePageAddress = 0;\r
565\r
566 //\r
567 // Lastly, check this entry's upper entries if need to be inserted into page pool\r
568 // or not\r
569 //\r
570 while (TRUE) {\r
571 if (MinPdt != (UINTN)-1) {\r
572 //\r
573 // If 4 KByte Page Table is released, check the PDPT entry\r
574 //\r
575 Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & gPhyMask);\r
576 SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt);\r
577 if (SubEntriesNum == 0) {\r
578 //\r
579 // Release the empty Page Directory table if there was no more 4 KByte Page Table entry\r
580 // clear the Page directory entry\r
581 //\r
582 InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pdpt[MinPdpt] & gPhyMask));\r
583 Pdpt[MinPdpt] = 0;\r
584 //\r
585 // Go on checking the PML4 table\r
586 //\r
587 MinPdt = (UINTN)-1;\r
588 continue;\r
589 }\r
590 //\r
591 // Update the sub-entries filed in PDPT entry and exit\r
592 //\r
593 SetSubEntriesNum (Pdpt + MinPdpt, SubEntriesNum - 1);\r
594 break;\r
595 }\r
596 if (MinPdpt != (UINTN)-1) {\r
597 //\r
598 // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry\r
599 //\r
600 SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4);\r
601 if (SubEntriesNum == 0) {\r
602 //\r
603 // Release the empty PML4 table if there was no more 1G KByte Page Table entry\r
604 // clear the Page directory entry\r
605 //\r
606 InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pml4[MinPml4] & gPhyMask));\r
607 Pml4[MinPml4] = 0;\r
608 MinPdpt = (UINTN)-1;\r
609 continue;\r
610 }\r
611 //\r
612 // Update the sub-entries filed in PML4 entry and exit\r
613 //\r
614 SetSubEntriesNum (Pml4 + MinPml4, SubEntriesNum - 1);\r
615 break;\r
616 }\r
617 //\r
618 // PLM4 table has been released before, exit it\r
619 //\r
620 break;\r
621 }\r
622}\r
623\r
624/**\r
625 Allocate free Page for PageFault handler use.\r
626\r
627 @return Page address.\r
628\r
629**/\r
630UINT64\r
631AllocPage (\r
632 VOID\r
633 )\r
634{\r
635 UINT64 RetVal;\r
636\r
637 if (IsListEmpty (&mPagePool)) {\r
638 //\r
639 // If page pool is empty, reclaim the used pages and insert one into page pool\r
640 //\r
641 ReclaimPages ();\r
642 }\r
643\r
644 //\r
645 // Get one free page and remove it from page pool\r
646 //\r
647 RetVal = (UINT64)(UINTN)mPagePool.ForwardLink;\r
648 RemoveEntryList (mPagePool.ForwardLink);\r
649 //\r
650 // Clean this page and return\r
651 //\r
652 ZeroMem ((VOID*)(UINTN)RetVal, EFI_PAGE_SIZE);\r
653 return RetVal;\r
654}\r
655\r
656/**\r
657 Page Fault handler for SMM use.\r
658\r
659**/\r
660VOID\r
661SmiDefaultPFHandler (\r
662 VOID\r
663 )\r
664{\r
665 UINT64 *PageTable;\r
666 UINT64 *Pml4;\r
667 UINT64 PFAddress;\r
668 UINTN StartBit;\r
669 UINTN EndBit;\r
670 UINT64 PTIndex;\r
671 UINTN Index;\r
672 SMM_PAGE_SIZE_TYPE PageSize;\r
673 UINTN NumOfPages;\r
674 UINTN PageAttribute;\r
675 EFI_STATUS Status;\r
676 UINT64 *UpperEntry;\r
677\r
678 //\r
679 // Set default SMM page attribute\r
680 //\r
681 PageSize = SmmPageSize2M;\r
682 NumOfPages = 1;\r
683 PageAttribute = 0;\r
684\r
685 EndBit = 0;\r
686 Pml4 = (UINT64*)(AsmReadCr3 () & gPhyMask);\r
687 PFAddress = AsmReadCr2 ();\r
688\r
689 Status = GetPlatformPageTableAttribute (PFAddress, &PageSize, &NumOfPages, &PageAttribute);\r
690 //\r
691 // If platform not support page table attribute, set default SMM page attribute\r
692 //\r
693 if (Status != EFI_SUCCESS) {\r
694 PageSize = SmmPageSize2M;\r
695 NumOfPages = 1;\r
696 PageAttribute = 0;\r
697 }\r
698 if (PageSize >= MaxSmmPageSizeType) {\r
699 PageSize = SmmPageSize2M;\r
700 }\r
701 if (NumOfPages > 512) {\r
702 NumOfPages = 512;\r
703 }\r
704\r
705 switch (PageSize) {\r
706 case SmmPageSize4K:\r
707 //\r
708 // BIT12 to BIT20 is Page Table index\r
709 //\r
710 EndBit = 12;\r
711 break;\r
712 case SmmPageSize2M:\r
713 //\r
714 // BIT21 to BIT29 is Page Directory index\r
715 //\r
716 EndBit = 21;\r
717 PageAttribute |= (UINTN)IA32_PG_PS;\r
718 break;\r
719 case SmmPageSize1G:\r
720 if (!m1GPageTableSupport) {\r
717fb604 721 DEBUG ((DEBUG_ERROR, "1-GByte pages is not supported!"));\r
427e3573
MK
722 ASSERT (FALSE);\r
723 }\r
724 //\r
725 // BIT30 to BIT38 is Page Directory Pointer Table index\r
726 //\r
727 EndBit = 30;\r
728 PageAttribute |= (UINTN)IA32_PG_PS;\r
729 break;\r
730 default:\r
731 ASSERT (FALSE);\r
732 }\r
733\r
734 //\r
735 // If execute-disable is enabled, set NX bit\r
736 //\r
737 if (mXdEnabled) {\r
738 PageAttribute |= IA32_PG_NX;\r
739 }\r
740\r
741 for (Index = 0; Index < NumOfPages; Index++) {\r
742 PageTable = Pml4;\r
743 UpperEntry = NULL;\r
744 for (StartBit = 39; StartBit > EndBit; StartBit -= 9) {\r
745 PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);\r
746 if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
747 //\r
748 // If the entry is not present, allocate one page from page pool for it\r
749 //\r
881520ea 750 PageTable[PTIndex] = AllocPage () | PAGE_ATTRIBUTE_BITS;\r
427e3573
MK
751 } else {\r
752 //\r
753 // Save the upper entry address\r
754 //\r
755 UpperEntry = PageTable + PTIndex;\r
756 }\r
757 //\r
758 // BIT9 to BIT11 of entry is used to save access record,\r
759 // initialize value is 7\r
760 //\r
761 PageTable[PTIndex] |= (UINT64)IA32_PG_A;\r
762 SetAccNum (PageTable + PTIndex, 7);\r
763 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);\r
764 }\r
765\r
766 PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);\r
767 if ((PageTable[PTIndex] & IA32_PG_P) != 0) {\r
768 //\r
769 // Check if the entry has already existed, this issue may occur when the different\r
770 // size page entries created under the same entry\r
771 //\r
717fb604
JY
772 DEBUG ((DEBUG_ERROR, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable, PTIndex, PageTable[PTIndex]));\r
773 DEBUG ((DEBUG_ERROR, "New page table overlapped with old page table!\n"));\r
427e3573
MK
774 ASSERT (FALSE);\r
775 }\r
776 //\r
777 // Fill the new entry\r
778 //\r
779 PageTable[PTIndex] = (PFAddress & gPhyMask & ~((1ull << EndBit) - 1)) |\r
881520ea 780 PageAttribute | IA32_PG_A | PAGE_ATTRIBUTE_BITS;\r
427e3573
MK
781 if (UpperEntry != NULL) {\r
782 SetSubEntriesNum (UpperEntry, GetSubEntriesNum (UpperEntry) + 1);\r
783 }\r
784 //\r
785 // Get the next page address if we need to create more page tables\r
786 //\r
787 PFAddress += (1ull << EndBit);\r
788 }\r
789}\r
790\r
791/**\r
792 ThePage Fault handler wrapper for SMM use.\r
793\r
794 @param InterruptType Defines the type of interrupt or exception that\r
795 occurred on the processor.This parameter is processor architecture specific.\r
796 @param SystemContext A pointer to the processor context when\r
797 the interrupt occurred on the processor.\r
798**/\r
799VOID\r
800EFIAPI\r
801SmiPFHandler (\r
802 IN EFI_EXCEPTION_TYPE InterruptType,\r
803 IN EFI_SYSTEM_CONTEXT SystemContext\r
804 )\r
805{\r
806 UINTN PFAddress;\r
7fa1376c
JY
807 UINTN GuardPageAddress;\r
808 UINTN CpuIndex;\r
427e3573
MK
809\r
810 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);\r
811\r
fe3a75bc 812 AcquireSpinLock (mPFLock);\r
427e3573
MK
813\r
814 PFAddress = AsmReadCr2 ();\r
815\r
717fb604
JY
816 if (mCpuSmmStaticPageTable && (PFAddress >= LShiftU64 (1, (mPhysicalAddressBits - 1)))) {\r
817 DEBUG ((DEBUG_ERROR, "Do not support address 0x%lx by processor!\n", PFAddress));\r
818 CpuDeadLoop ();\r
819 }\r
820\r
427e3573 821 //\r
7fa1376c
JY
822 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,\r
823 // or SMM page protection violation.\r
427e3573 824 //\r
7fa1376c 825 if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&\r
427e3573 826 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
7fa1376c
JY
827 CpuIndex = GetCpuIndex ();\r
828 GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);\r
829 if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&\r
830 (PFAddress >= GuardPageAddress) &&\r
831 (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {\r
832 DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));\r
833 } else {\r
834 DEBUG ((DEBUG_ERROR, "SMM exception data - 0x%lx(", SystemContext.SystemContextX64->ExceptionData));\r
835 DEBUG ((DEBUG_ERROR, "I:%x, R:%x, U:%x, W:%x, P:%x",\r
836 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0,\r
837 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_RSVD) != 0,\r
838 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_US) != 0,\r
839 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_WR) != 0,\r
840 (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_P) != 0\r
841 ));\r
842 DEBUG ((DEBUG_ERROR, ")\n"));\r
843 if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {\r
844 DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%lx)\n", PFAddress));\r
845 DEBUG_CODE (\r
846 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);\r
847 );\r
848 } else {\r
849 DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%lx)\n", PFAddress));\r
850 DEBUG_CODE (\r
851 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);\r
852 );\r
853 }\r
854 }\r
427e3573
MK
855 CpuDeadLoop ();\r
856 }\r
857\r
858 //\r
859 // If a page fault occurs in SMM range\r
860 //\r
861 if ((PFAddress < mCpuHotPlugData.SmrrBase) ||\r
862 (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {\r
863 if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {\r
717fb604 864 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress));\r
427e3573
MK
865 DEBUG_CODE (\r
866 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);\r
867 );\r
868 CpuDeadLoop ();\r
869 }\r
d2fc7711
JY
870 if (IsSmmCommBufferForbiddenAddress (PFAddress)) {\r
871 DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress));\r
872 DEBUG_CODE (\r
873 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);\r
874 );\r
875 CpuDeadLoop ();\r
876 }\r
427e3573
MK
877 }\r
878\r
879 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
880 SmmProfilePFHandler (\r
881 SystemContext.SystemContextX64->Rip,\r
882 SystemContext.SystemContextX64->ExceptionData\r
883 );\r
884 } else {\r
885 SmiDefaultPFHandler ();\r
886 }\r
887\r
fe3a75bc 888 ReleaseSpinLock (mPFLock);\r
427e3573 889}\r
717fb604
JY
890\r
891/**\r
892 This function sets memory attribute for page table.\r
893**/\r
894VOID\r
895SetPageTableAttributes (\r
896 VOID\r
897 )\r
898{\r
899 UINTN Index2;\r
900 UINTN Index3;\r
901 UINTN Index4;\r
902 UINT64 *L1PageTable;\r
903 UINT64 *L2PageTable;\r
904 UINT64 *L3PageTable;\r
905 UINT64 *L4PageTable;\r
906 BOOLEAN IsSplitted;\r
907 BOOLEAN PageTableSplitted;\r
908\r
909 if (!mCpuSmmStaticPageTable) {\r
910 return ;\r
911 }\r
912\r
913 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));\r
914\r
915 //\r
916 // Disable write protection, because we need mark page table to be write protected.\r
917 // We need *write* page table memory, to mark itself to be *read only*.\r
918 //\r
919 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);\r
920\r
921 do {\r
922 DEBUG ((DEBUG_INFO, "Start...\n"));\r
923 PageTableSplitted = FALSE;\r
924\r
925 L4PageTable = (UINT64 *)GetPageTableBase ();\r
926 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L4PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
927 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
928\r
929 for (Index4 = 0; Index4 < SIZE_4KB/sizeof(UINT64); Index4++) {\r
930 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);\r
931 if (L3PageTable == NULL) {\r
932 continue;\r
933 }\r
934\r
935 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
936 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
937\r
938 for (Index3 = 0; Index3 < SIZE_4KB/sizeof(UINT64); Index3++) {\r
939 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
940 // 1G\r
941 continue;\r
942 }\r
943 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);\r
944 if (L2PageTable == NULL) {\r
945 continue;\r
946 }\r
947\r
948 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
949 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
950\r
951 for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {\r
952 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
953 // 2M\r
954 continue;\r
955 }\r
956 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);\r
957 if (L1PageTable == NULL) {\r
958 continue;\r
959 }\r
960 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
961 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
962 }\r
963 }\r
964 }\r
965 } while (PageTableSplitted);\r
966\r
967 //\r
968 // Enable write protection, after page table updated.\r
969 //\r
970 AsmWriteCr0 (AsmReadCr0() | CR0_WP);\r
971\r
972 return ;\r
973}\r