1 /** @file
2 Enable SMM profile.
3 
4 Copyright (c) 2012 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "PiSmmCpuDxeSmm.h"
16 #include "SmmProfileInternal.h"
17 
18 UINT32                    mSmmProfileCr3;
19 
20 SMM_PROFILE_HEADER        *mSmmProfileBase;
21 MSR_DS_AREA_STRUCT        *mMsrDsAreaBase;
22 //
23 // The buffer to store SMM profile data.
24 //
25 UINTN                     mSmmProfileSize;
26 
27 //
28 // The buffer to enable branch trace store.
29 //
30 UINTN                     mMsrDsAreaSize   = SMM_PROFILE_DTS_SIZE;
31 
32 //
33 // The flag indicates if execute-disable is supported by processor.
34 //
35 BOOLEAN                   mXdSupported     = FALSE;
36 
37 //
38 // The flag indicates if execute-disable is enabled on processor.
39 //
40 BOOLEAN                   mXdEnabled       = FALSE;
41 
42 //
43 // The flag indicates if BTS is supported by processor.
44 //
45 BOOLEAN                   mBtsSupported     = FALSE;
46 
47 //
48 // The flag indicates if SMM profile starts to record data.
49 //
50 BOOLEAN                   mSmmProfileStart = FALSE;
51 
52 //
53 // Record the page fault exception count for one instruction execution.
54 //
55 UINTN                     *mPFEntryCount;
56 
57 UINT64                    (*mLastPFEntryValue)[MAX_PF_ENTRY_COUNT];
58 UINT64                    *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT];
59 
60 MSR_DS_AREA_STRUCT        **mMsrDsArea;
61 BRANCH_TRACE_RECORD       **mMsrBTSRecord;
62 UINTN                     mBTSRecordNumber;
63 PEBS_RECORD               **mMsrPEBSRecord;
64 
65 //
66 // These memory ranges are always present, they does not generate the access type of page fault exception,
67 // but they possibly generate instruction fetch type of page fault exception.
68 //
69 MEMORY_PROTECTION_RANGE   *mProtectionMemRange     = NULL;
70 UINTN                     mProtectionMemRangeCount = 0;
71 
72 //
73 // Some predefined memory ranges.
74 //
75 MEMORY_PROTECTION_RANGE mProtectionMemRangeTemplate[] = {
76   //
77   // SMRAM range (to be fixed in runtime).
78   // It is always present and instruction fetches are allowed.
79   //
80   {{0x00000000, 0x00000000},TRUE,FALSE},
81 
82   //
83   // SMM profile data range( to be fixed in runtime).
84   // It is always present and instruction fetches are not allowed.
85   //
86   {{0x00000000, 0x00000000},TRUE,TRUE},
87 
88   //
89   // Future extended range could be added here.
90   //
91 
92   //
93   // PCI MMIO ranges (to be added in runtime).
94   // They are always present and instruction fetches are not allowed.
95   //
96 };
97 
98 //
99 // These memory ranges are mapped by 4KB-page instead of 2MB-page.
100 //
101 MEMORY_RANGE              *mSplitMemRange          = NULL;
102 UINTN                     mSplitMemRangeCount      = 0;
103 
104 //
105 // SMI command port.
106 //
107 UINT32                    mSmiCommandPort;
108 
109 /**
110   Disable branch trace store.
111 
112 **/
113 VOID
DisableBTS(VOID)114 DisableBTS (
115   VOID
116   )
117 {
118   AsmMsrAnd64 (MSR_DEBUG_CTL, ~((UINT64)(MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR)));
119 }
120 
121 /**
122   Enable branch trace store.
123 
124 **/
125 VOID
EnableBTS(VOID)126 EnableBTS (
127   VOID
128   )
129 {
130   AsmMsrOr64 (MSR_DEBUG_CTL, (MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR));
131 }
132 
133 /**
134   Get CPU Index from APIC ID.
135 
136 **/
137 UINTN
GetCpuIndex(VOID)138 GetCpuIndex (
139   VOID
140   )
141 {
142   UINTN     Index;
143   UINT32    ApicId;
144 
145   ApicId = GetApicId ();
146 
147   for (Index = 0; Index < PcdGet32 (PcdCpuMaxLogicalProcessorNumber); Index++) {
148     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ApicId) {
149       return Index;
150     }
151   }
152   ASSERT (FALSE);
153   return 0;
154 }
155 
156 /**
157   Get the source of IP after execute-disable exception is triggered.
158 
159   @param  CpuIndex        The index of CPU.
160   @param  DestinationIP   The destination address.
161 
162 **/
163 UINT64
GetSourceFromDestinationOnBts(UINTN CpuIndex,UINT64 DestinationIP)164 GetSourceFromDestinationOnBts (
165   UINTN  CpuIndex,
166   UINT64 DestinationIP
167   )
168 {
169   BRANCH_TRACE_RECORD  *CurrentBTSRecord;
170   UINTN                Index;
171   BOOLEAN              FirstMatch;
172 
173   FirstMatch = FALSE;
174 
175   CurrentBTSRecord = (BRANCH_TRACE_RECORD *)mMsrDsArea[CpuIndex]->BTSIndex;
176   for (Index = 0; Index < mBTSRecordNumber; Index++) {
177     if ((UINTN)CurrentBTSRecord < (UINTN)mMsrBTSRecord[CpuIndex]) {
178       //
179       // Underflow
180       //
181       CurrentBTSRecord = (BRANCH_TRACE_RECORD *)((UINTN)mMsrDsArea[CpuIndex]->BTSAbsoluteMaximum - 1);
182       CurrentBTSRecord --;
183     }
184     if (CurrentBTSRecord->LastBranchTo == DestinationIP) {
185       //
186       // Good! find 1st one, then find 2nd one.
187       //
188       if (!FirstMatch) {
189         //
190         // The first one is DEBUG exception
191         //
192         FirstMatch = TRUE;
193       } else {
194         //
195         // Good find proper one.
196         //
197         return CurrentBTSRecord->LastBranchFrom;
198       }
199     }
200     CurrentBTSRecord--;
201   }
202 
203   return 0;
204 }
205 
206 /**
207   SMM profile specific INT 1 (single-step) exception handler.
208 
209   @param  InterruptType    Defines the type of interrupt or exception that
210                            occurred on the processor.This parameter is processor architecture specific.
211   @param  SystemContext    A pointer to the processor context when
212                            the interrupt occurred on the processor.
213 **/
214 VOID
215 EFIAPI
DebugExceptionHandler(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_SYSTEM_CONTEXT SystemContext)216 DebugExceptionHandler (
217     IN EFI_EXCEPTION_TYPE   InterruptType,
218     IN EFI_SYSTEM_CONTEXT   SystemContext
219   )
220 {
221   UINTN  CpuIndex;
222   UINTN  PFEntry;
223 
224   if (!mSmmProfileStart) {
225     return;
226   }
227   CpuIndex = GetCpuIndex ();
228 
229   //
230   // Clear last PF entries
231   //
232   for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) {
233     *mLastPFEntryPointer[CpuIndex][PFEntry] = mLastPFEntryValue[CpuIndex][PFEntry];
234   }
235 
236   //
237   // Reset page fault exception count for next page fault.
238   //
239   mPFEntryCount[CpuIndex] = 0;
240 
241   //
242   // Flush TLB
243   //
244   CpuFlushTlb ();
245 
246   //
247   // Clear TF in EFLAGS
248   //
249   ClearTrapFlag (SystemContext);
250 }
251 
252 /**
253   Check if the memory address will be mapped by 4KB-page.
254 
255   @param  Address  The address of Memory.
256   @param  Nx       The flag indicates if the memory is execute-disable.
257 
258 **/
259 BOOLEAN
IsAddressValid(IN EFI_PHYSICAL_ADDRESS Address,IN BOOLEAN * Nx)260 IsAddressValid (
261   IN EFI_PHYSICAL_ADDRESS   Address,
262   IN BOOLEAN                *Nx
263   )
264 {
265   UINTN  Index;
266 
267   *Nx = FALSE;
268   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
269     //
270     // Check configuration
271     //
272     for (Index = 0; Index < mProtectionMemRangeCount; Index++) {
273       if ((Address >= mProtectionMemRange[Index].Range.Base) && (Address < mProtectionMemRange[Index].Range.Top)) {
274         *Nx = mProtectionMemRange[Index].Nx;
275         return mProtectionMemRange[Index].Present;
276       }
277     }
278     *Nx = TRUE;
279     return FALSE;
280 
281   } else {
282     if ((Address < mCpuHotPlugData.SmrrBase) ||
283         (Address >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
284       *Nx = TRUE;
285     }
286     return TRUE;
287   }
288 }
289 
290 /**
291   Check if the memory address will be mapped by 4KB-page.
292 
293   @param  Address  The address of Memory.
294 
295 **/
296 BOOLEAN
IsAddressSplit(IN EFI_PHYSICAL_ADDRESS Address)297 IsAddressSplit (
298   IN EFI_PHYSICAL_ADDRESS   Address
299   )
300 {
301   UINTN  Index;
302 
303   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
304     //
305     // Check configuration
306     //
307     for (Index = 0; Index < mSplitMemRangeCount; Index++) {
308       if ((Address >= mSplitMemRange[Index].Base) && (Address < mSplitMemRange[Index].Top)) {
309         return TRUE;
310       }
311     }
312   } else {
313     if (Address < mCpuHotPlugData.SmrrBase) {
314       if ((mCpuHotPlugData.SmrrBase - Address) < BASE_2MB) {
315         return TRUE;
316       }
317     } else if (Address > (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize - BASE_2MB))  {
318       if ((Address - (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize - BASE_2MB)) < BASE_2MB) {
319         return TRUE;
320       }
321     }
322   }
323   //
324   // Return default
325   //
326   return FALSE;
327 }
328 
329 /**
330   Initialize the protected memory ranges and the 4KB-page mapped memory ranges.
331 
332 **/
333 VOID
InitProtectedMemRange(VOID)334 InitProtectedMemRange (
335   VOID
336   )
337 {
338   UINTN                            Index;
339   UINTN                            NumberOfDescriptors;
340   UINTN                            NumberOfMmioDescriptors;
341   UINTN                            NumberOfProtectRange;
342   UINTN                            NumberOfSpliteRange;
343   EFI_GCD_MEMORY_SPACE_DESCRIPTOR  *MemorySpaceMap;
344   UINTN                            TotalSize;
345   EFI_STATUS                       Status;
346   EFI_PHYSICAL_ADDRESS             ProtectBaseAddress;
347   EFI_PHYSICAL_ADDRESS             ProtectEndAddress;
348   EFI_PHYSICAL_ADDRESS             Top2MBAlignedAddress;
349   EFI_PHYSICAL_ADDRESS             Base2MBAlignedAddress;
350   UINT64                           High4KBPageSize;
351   UINT64                           Low4KBPageSize;
352 
353   NumberOfDescriptors      = 0;
354   NumberOfMmioDescriptors  = 0;
355   NumberOfSpliteRange      = 0;
356   MemorySpaceMap           = NULL;
357 
358   //
359   // Get MMIO ranges from GCD and add them into protected memory ranges.
360   //
361   Status = gDS->GetMemorySpaceMap (
362                 &NumberOfDescriptors,
363                 &MemorySpaceMap
364                 );
365   for (Index = 0; Index < NumberOfDescriptors; Index++) {
366     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
367       NumberOfMmioDescriptors++;
368     }
369   }
370 
371   if (NumberOfMmioDescriptors != 0) {
372     TotalSize = NumberOfMmioDescriptors * sizeof (MEMORY_PROTECTION_RANGE) + sizeof (mProtectionMemRangeTemplate);
373     mProtectionMemRange = (MEMORY_PROTECTION_RANGE *) AllocateZeroPool (TotalSize);
374     ASSERT (mProtectionMemRange != NULL);
375     mProtectionMemRangeCount = TotalSize / sizeof (MEMORY_PROTECTION_RANGE);
376 
377     //
378     // Copy existing ranges.
379     //
380     CopyMem (mProtectionMemRange, mProtectionMemRangeTemplate, sizeof (mProtectionMemRangeTemplate));
381 
382     //
383     // Create split ranges which come from protected ranges.
384     //
385     TotalSize = (TotalSize / sizeof (MEMORY_PROTECTION_RANGE)) * sizeof (MEMORY_RANGE);
386     mSplitMemRange = (MEMORY_RANGE *) AllocateZeroPool (TotalSize);
387     ASSERT (mSplitMemRange != NULL);
388 
389     //
390     // Create MMIO ranges which are set to present and execution-disable.
391     //
392     NumberOfProtectRange    = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE);
393     for (Index = 0; Index < NumberOfDescriptors; Index++) {
394       if (MemorySpaceMap[Index].GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo) {
395         continue;
396       }
397       mProtectionMemRange[NumberOfProtectRange].Range.Base = MemorySpaceMap[Index].BaseAddress;
398       mProtectionMemRange[NumberOfProtectRange].Range.Top  = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length;
399       mProtectionMemRange[NumberOfProtectRange].Present    = TRUE;
400       mProtectionMemRange[NumberOfProtectRange].Nx         = TRUE;
401       NumberOfProtectRange++;
402     }
403   }
404 
405   //
406   // According to protected ranges, create the ranges which will be mapped by 2KB page.
407   //
408   NumberOfSpliteRange  = 0;
409   NumberOfProtectRange = mProtectionMemRangeCount;
410   for (Index = 0; Index < NumberOfProtectRange; Index++) {
411     //
412     // If MMIO base address is not 2MB alignment, make 2MB alignment for create 4KB page in page table.
413     //
414     ProtectBaseAddress = mProtectionMemRange[Index].Range.Base;
415     ProtectEndAddress  = mProtectionMemRange[Index].Range.Top;
416     if (((ProtectBaseAddress & (SIZE_2MB - 1)) != 0) || ((ProtectEndAddress  & (SIZE_2MB - 1)) != 0)) {
417       //
418       // Check if it is possible to create 4KB-page for not 2MB-aligned range and to create 2MB-page for 2MB-aligned range.
419       // A mix of 4KB and 2MB page could save SMRAM space.
420       //
421       Top2MBAlignedAddress  = ProtectEndAddress & ~(SIZE_2MB - 1);
422       Base2MBAlignedAddress = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
423       if ((Top2MBAlignedAddress > Base2MBAlignedAddress) &&
424           ((Top2MBAlignedAddress - Base2MBAlignedAddress) >= SIZE_2MB)) {
425         //
426         // There is an range which could be mapped by 2MB-page.
427         //
428         High4KBPageSize = ((ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectEndAddress & ~(SIZE_2MB - 1));
429         Low4KBPageSize  = ((ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectBaseAddress & ~(SIZE_2MB - 1));
430         if (High4KBPageSize != 0) {
431           //
432           // Add not 2MB-aligned range to be mapped by 4KB-page.
433           //
434           mSplitMemRange[NumberOfSpliteRange].Base = ProtectEndAddress & ~(SIZE_2MB - 1);
435           mSplitMemRange[NumberOfSpliteRange].Top  = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
436           NumberOfSpliteRange++;
437         }
438         if (Low4KBPageSize != 0) {
439           //
440           // Add not 2MB-aligned range to be mapped by 4KB-page.
441           //
442           mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1);
443           mSplitMemRange[NumberOfSpliteRange].Top  = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
444           NumberOfSpliteRange++;
445         }
446       } else {
447         //
448         // The range could only be mapped by 4KB-page.
449         //
450         mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1);
451         mSplitMemRange[NumberOfSpliteRange].Top  = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
452         NumberOfSpliteRange++;
453       }
454     }
455   }
456 
457   mSplitMemRangeCount = NumberOfSpliteRange;
458 
459   DEBUG ((EFI_D_INFO, "SMM Profile Memory Ranges:\n"));
460   for (Index = 0; Index < mProtectionMemRangeCount; Index++) {
461     DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Base = %lx\n", Index, mProtectionMemRange[Index].Range.Base));
462     DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Top  = %lx\n", Index, mProtectionMemRange[Index].Range.Top));
463   }
464   for (Index = 0; Index < mSplitMemRangeCount; Index++) {
465     DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Base = %lx\n", Index, mSplitMemRange[Index].Base));
466     DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Top  = %lx\n", Index, mSplitMemRange[Index].Top));
467   }
468 }
469 
470 /**
471   Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
472 
473 **/
474 VOID
InitPaging(VOID)475 InitPaging (
476   VOID
477   )
478 {
479   UINT64                            *Pml4;
480   UINT64                            *Pde;
481   UINT64                            *Pte;
482   UINT64                            *Pt;
483   UINTN                             Address;
484   UINTN                             Level1;
485   UINTN                             Level2;
486   UINTN                             Level3;
487   UINTN                             Level4;
488   UINTN                             NumberOfPdpEntries;
489   UINTN                             NumberOfPml4Entries;
490   UINTN                             SizeOfMemorySpace;
491   BOOLEAN                           Nx;
492 
493   if (sizeof (UINTN) == sizeof (UINT64)) {
494     Pml4 = (UINT64*)(UINTN)mSmmProfileCr3;
495     SizeOfMemorySpace = HighBitSet64 (gPhyMask) + 1;
496     //
497     // Calculate the table entries of PML4E and PDPTE.
498     //
499     if (SizeOfMemorySpace <= 39 ) {
500       NumberOfPml4Entries = 1;
501       NumberOfPdpEntries = (UINT32)LShiftU64 (1, (SizeOfMemorySpace - 30));
502     } else {
503       NumberOfPml4Entries = (UINT32)LShiftU64 (1, (SizeOfMemorySpace - 39));
504       NumberOfPdpEntries = 512;
505     }
506   } else {
507     NumberOfPml4Entries = 1;
508     NumberOfPdpEntries  = 4;
509   }
510 
511   //
512   // Go through page table and change 2MB-page into 4KB-page.
513   //
514   for (Level1 = 0; Level1 < NumberOfPml4Entries; Level1++) {
515     if (sizeof (UINTN) == sizeof (UINT64)) {
516       if ((Pml4[Level1] & IA32_PG_P) == 0) {
517         //
518         // If Pml4 entry does not exist, skip it
519         //
520         continue;
521       }
522       Pde = (UINT64 *)(UINTN)(Pml4[Level1] & PHYSICAL_ADDRESS_MASK);
523     } else {
524       Pde = (UINT64*)(UINTN)mSmmProfileCr3;
525     }
526     for (Level2 = 0; Level2 < NumberOfPdpEntries; Level2++, Pde++) {
527       if ((*Pde & IA32_PG_P) == 0) {
528         //
529         // If PDE entry does not exist, skip it
530         //
531         continue;
532       }
533       Pte = (UINT64 *)(UINTN)(*Pde & PHYSICAL_ADDRESS_MASK);
534       if (Pte == 0) {
535         continue;
536       }
537       for (Level3 = 0; Level3 < SIZE_4KB / sizeof (*Pte); Level3++, Pte++) {
538         if ((*Pte & IA32_PG_P) == 0) {
539           //
540           // If PTE entry does not exist, skip it
541           //
542           continue;
543         }
544         Address = (((Level2 << 9) + Level3) << 21);
545 
546         //
547         // If it is 2M page, check IsAddressSplit()
548         //
549         if (((*Pte & IA32_PG_PS) != 0) && IsAddressSplit (Address)) {
550           //
551           // Based on current page table, create 4KB page table for split area.
552           //
553           ASSERT (Address == (*Pte & PHYSICAL_ADDRESS_MASK));
554 
555           Pt = AllocatePageTableMemory (1);
556           ASSERT (Pt != NULL);
557 
558           // Split it
559           for (Level4 = 0; Level4 < SIZE_4KB / sizeof(*Pt); Level4++) {
560             Pt[Level4] = Address + ((Level4 << 12) | PAGE_ATTRIBUTE_BITS);
561           } // end for PT
562           *Pte = (UINTN)Pt | PAGE_ATTRIBUTE_BITS;
563         } // end if IsAddressSplit
564       } // end for PTE
565     } // end for PDE
566   }
567 
568   //
569   // Go through page table and set several page table entries to absent or execute-disable.
570   //
571   DEBUG ((EFI_D_INFO, "Patch page table start ...\n"));
572   for (Level1 = 0; Level1 < NumberOfPml4Entries; Level1++) {
573     if (sizeof (UINTN) == sizeof (UINT64)) {
574       if ((Pml4[Level1] & IA32_PG_P) == 0) {
575         //
576         // If Pml4 entry does not exist, skip it
577         //
578         continue;
579       }
580       Pde = (UINT64 *)(UINTN)(Pml4[Level1] & PHYSICAL_ADDRESS_MASK);
581     } else {
582       Pde = (UINT64*)(UINTN)mSmmProfileCr3;
583     }
584     for (Level2 = 0; Level2 < NumberOfPdpEntries; Level2++, Pde++) {
585       if ((*Pde & IA32_PG_P) == 0) {
586         //
587         // If PDE entry does not exist, skip it
588         //
589         continue;
590       }
591       Pte = (UINT64 *)(UINTN)(*Pde & PHYSICAL_ADDRESS_MASK);
592       if (Pte == 0) {
593         continue;
594       }
595       for (Level3 = 0; Level3 < SIZE_4KB / sizeof (*Pte); Level3++, Pte++) {
596         if ((*Pte & IA32_PG_P) == 0) {
597           //
598           // If PTE entry does not exist, skip it
599           //
600           continue;
601         }
602         Address = (((Level2 << 9) + Level3) << 21);
603 
604         if ((*Pte & IA32_PG_PS) != 0) {
605           // 2MB page
606 
607           if (!IsAddressValid (Address, &Nx)) {
608             //
609             // Patch to remove Present flag and RW flag
610             //
611             *Pte = *Pte & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);
612           }
613           if (Nx && mXdSupported) {
614             *Pte = *Pte | IA32_PG_NX;
615           }
616         } else {
617           // 4KB page
618           Pt = (UINT64 *)(UINTN)(*Pte & PHYSICAL_ADDRESS_MASK);
619           if (Pt == 0) {
620             continue;
621           }
622           for (Level4 = 0; Level4 < SIZE_4KB / sizeof(*Pt); Level4++, Pt++) {
623             if (!IsAddressValid (Address, &Nx)) {
624               *Pt = *Pt & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);
625             }
626             if (Nx && mXdSupported) {
627               *Pt = *Pt | IA32_PG_NX;
628             }
629             Address += SIZE_4KB;
630           } // end for PT
631         } // end if PS
632       } // end for PTE
633     } // end for PDE
634   }
635 
636   //
637   // Flush TLB
638   //
639   CpuFlushTlb ();
640   DEBUG ((EFI_D_INFO, "Patch page table done!\n"));
641   //
642   // Set execute-disable flag
643   //
644   mXdEnabled = TRUE;
645 
646   return ;
647 }
648 
649 /**
650   To find FADT in ACPI tables.
651 
652   @param AcpiTableGuid   The GUID used to find ACPI table in UEFI ConfigurationTable.
653 
654   @return  FADT table pointer.
655 **/
656 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE  *
FindAcpiFadtTableByAcpiGuid(IN EFI_GUID * AcpiTableGuid)657 FindAcpiFadtTableByAcpiGuid (
658   IN EFI_GUID  *AcpiTableGuid
659   )
660 {
661   EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER  *Rsdp;
662   EFI_ACPI_DESCRIPTION_HEADER                   *Rsdt;
663   EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE     *Fadt;
664   UINTN                                         Index;
665   UINT32                                        Data32;
666   Rsdp  = NULL;
667   Rsdt  = NULL;
668   Fadt  = NULL;
669   //
670   // found ACPI table RSD_PTR from system table
671   //
672   for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
673     if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), AcpiTableGuid)) {
674       //
675       // A match was found.
676       //
677       Rsdp = gST->ConfigurationTable[Index].VendorTable;
678       break;
679     }
680   }
681 
682   if (Rsdp == NULL) {
683     return NULL;
684   }
685 
686   Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->RsdtAddress;
687   if (Rsdt == NULL || Rsdt->Signature != EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
688     return NULL;
689   }
690 
691   for (Index = sizeof (EFI_ACPI_DESCRIPTION_HEADER); Index < Rsdt->Length; Index = Index + sizeof (UINT32)) {
692 
693     Data32  = *(UINT32 *) ((UINT8 *) Rsdt + Index);
694     Fadt    = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *) (UINT32 *) (UINTN) Data32;
695     if (Fadt->Header.Signature == EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
696       break;
697     }
698   }
699 
700   if (Fadt == NULL || Fadt->Header.Signature != EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
701     return NULL;
702   }
703 
704   return Fadt;
705 }
706 
707 /**
708   To find FADT in ACPI tables.
709 
710   @return  FADT table pointer.
711 **/
712 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE  *
FindAcpiFadtTable(VOID)713 FindAcpiFadtTable (
714   VOID
715   )
716 {
717   EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
718 
719   Fadt = FindAcpiFadtTableByAcpiGuid (&gEfiAcpi20TableGuid);
720   if (Fadt != NULL) {
721     return Fadt;
722   }
723 
724   return FindAcpiFadtTableByAcpiGuid (&gEfiAcpi10TableGuid);
725 }
726 
727 /**
728   To get system port address of the SMI Command Port in FADT table.
729 
730 **/
731 VOID
GetSmiCommandPort(VOID)732 GetSmiCommandPort (
733   VOID
734   )
735 {
736   EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
737 
738   Fadt = FindAcpiFadtTable ();
739   ASSERT (Fadt != NULL);
740 
741   mSmiCommandPort = Fadt->SmiCmd;
742   DEBUG ((EFI_D_INFO, "mSmiCommandPort = %x\n", mSmiCommandPort));
743 }
744 
745 /**
746   Updates page table to make some memory ranges (like system memory) absent
747   and make some memory ranges (like MMIO) present and execute disable. It also
748   update 2MB-page to 4KB-page for some memory ranges.
749 
750 **/
751 VOID
SmmProfileStart(VOID)752 SmmProfileStart (
753   VOID
754   )
755 {
756   //
757   // The flag indicates SMM profile starts to work.
758   //
759   mSmmProfileStart = TRUE;
760 }
761 
762 /**
763   Initialize SMM profile in SmmReadyToLock protocol callback function.
764 
765   @param  Protocol   Points to the protocol's unique identifier.
766   @param  Interface  Points to the interface instance.
767   @param  Handle     The handle on which the interface was installed.
768 
769   @retval EFI_SUCCESS SmmReadyToLock protocol callback runs successfully.
770 **/
771 EFI_STATUS
772 EFIAPI
InitSmmProfileCallBack(IN CONST EFI_GUID * Protocol,IN VOID * Interface,IN EFI_HANDLE Handle)773 InitSmmProfileCallBack (
774   IN CONST EFI_GUID  *Protocol,
775   IN VOID            *Interface,
776   IN EFI_HANDLE      Handle
777   )
778 {
779   EFI_STATUS         Status;
780 
781   //
782   // Save to variable so that SMM profile data can be found.
783   //
784   Status = gRT->SetVariable (
785                   SMM_PROFILE_NAME,
786                   &gEfiCallerIdGuid,
787                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
788                   sizeof(mSmmProfileBase),
789                   &mSmmProfileBase
790                   );
791 
792   //
793   // Get Software SMI from FADT
794   //
795   GetSmiCommandPort ();
796 
797   //
798   // Initialize protected memory range for patching page table later.
799   //
800   InitProtectedMemRange ();
801 
802   return EFI_SUCCESS;
803 }
804 
805 /**
806   Initialize SMM profile data structures.
807 
808 **/
809 VOID
InitSmmProfileInternal(VOID)810 InitSmmProfileInternal (
811   VOID
812   )
813 {
814   EFI_STATUS                 Status;
815   EFI_PHYSICAL_ADDRESS       Base;
816   VOID                       *Registration;
817   UINTN                      Index;
818   UINTN                      MsrDsAreaSizePerCpu;
819   UINTN                      TotalSize;
820 
821   mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
822   ASSERT (mPFEntryCount != NULL);
823   mLastPFEntryValue = (UINT64  (*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (
824                                                          sizeof (mLastPFEntryValue[0]) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
825   ASSERT (mLastPFEntryValue != NULL);
826   mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (
827                                                            sizeof (mLastPFEntryPointer[0]) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
828   ASSERT (mLastPFEntryPointer != NULL);
829 
830   //
831   // Allocate memory for SmmProfile below 4GB.
832   // The base address
833   //
834   mSmmProfileSize = PcdGet32 (PcdCpuSmmProfileSize);
835   ASSERT ((mSmmProfileSize & 0xFFF) == 0);
836 
837   if (mBtsSupported) {
838     TotalSize = mSmmProfileSize + mMsrDsAreaSize;
839   } else {
840     TotalSize = mSmmProfileSize;
841   }
842 
843   Base = 0xFFFFFFFF;
844   Status = gBS->AllocatePages (
845                   AllocateMaxAddress,
846                   EfiReservedMemoryType,
847                   EFI_SIZE_TO_PAGES (TotalSize),
848                   &Base
849                   );
850   ASSERT_EFI_ERROR (Status);
851   ZeroMem ((VOID *)(UINTN)Base, TotalSize);
852   mSmmProfileBase = (SMM_PROFILE_HEADER *)(UINTN)Base;
853 
854   //
855   // Initialize SMM profile data header.
856   //
857   mSmmProfileBase->HeaderSize     = sizeof (SMM_PROFILE_HEADER);
858   mSmmProfileBase->MaxDataEntries = (UINT64)((mSmmProfileSize - sizeof(SMM_PROFILE_HEADER)) / sizeof (SMM_PROFILE_ENTRY));
859   mSmmProfileBase->MaxDataSize    = MultU64x64 (mSmmProfileBase->MaxDataEntries, sizeof(SMM_PROFILE_ENTRY));
860   mSmmProfileBase->CurDataEntries = 0;
861   mSmmProfileBase->CurDataSize    = 0;
862   mSmmProfileBase->TsegStart      = mCpuHotPlugData.SmrrBase;
863   mSmmProfileBase->TsegSize       = mCpuHotPlugData.SmrrSize;
864   mSmmProfileBase->NumSmis        = 0;
865   mSmmProfileBase->NumCpus        = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
866 
867   if (mBtsSupported) {
868     mMsrDsArea = (MSR_DS_AREA_STRUCT **)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT *) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
869     ASSERT (mMsrDsArea != NULL);
870     mMsrBTSRecord = (BRANCH_TRACE_RECORD **)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD *) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
871     ASSERT (mMsrBTSRecord != NULL);
872     mMsrPEBSRecord = (PEBS_RECORD **)AllocateZeroPool (sizeof (PEBS_RECORD *) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
873     ASSERT (mMsrPEBSRecord != NULL);
874 
875     mMsrDsAreaBase  = (MSR_DS_AREA_STRUCT *)((UINTN)Base + mSmmProfileSize);
876     MsrDsAreaSizePerCpu = mMsrDsAreaSize / PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
877     mBTSRecordNumber    = (MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER - sizeof(MSR_DS_AREA_STRUCT)) / sizeof(BRANCH_TRACE_RECORD);
878     for (Index = 0; Index < PcdGet32 (PcdCpuMaxLogicalProcessorNumber); Index++) {
879       mMsrDsArea[Index]     = (MSR_DS_AREA_STRUCT *)((UINTN)mMsrDsAreaBase + MsrDsAreaSizePerCpu * Index);
880       mMsrBTSRecord[Index]  = (BRANCH_TRACE_RECORD *)((UINTN)mMsrDsArea[Index] + sizeof(MSR_DS_AREA_STRUCT));
881       mMsrPEBSRecord[Index] = (PEBS_RECORD *)((UINTN)mMsrDsArea[Index] + MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER);
882 
883       mMsrDsArea[Index]->BTSBufferBase          = (UINTN)mMsrBTSRecord[Index];
884       mMsrDsArea[Index]->BTSIndex               = mMsrDsArea[Index]->BTSBufferBase;
885       mMsrDsArea[Index]->BTSAbsoluteMaximum     = mMsrDsArea[Index]->BTSBufferBase + mBTSRecordNumber * sizeof(BRANCH_TRACE_RECORD) + 1;
886       mMsrDsArea[Index]->BTSInterruptThreshold  = mMsrDsArea[Index]->BTSAbsoluteMaximum + 1;
887 
888       mMsrDsArea[Index]->PEBSBufferBase         = (UINTN)mMsrPEBSRecord[Index];
889       mMsrDsArea[Index]->PEBSIndex              = mMsrDsArea[Index]->PEBSBufferBase;
890       mMsrDsArea[Index]->PEBSAbsoluteMaximum    = mMsrDsArea[Index]->PEBSBufferBase + PEBS_RECORD_NUMBER * sizeof(PEBS_RECORD) + 1;
891       mMsrDsArea[Index]->PEBSInterruptThreshold = mMsrDsArea[Index]->PEBSAbsoluteMaximum + 1;
892     }
893   }
894 
895   mProtectionMemRange      = mProtectionMemRangeTemplate;
896   mProtectionMemRangeCount = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE);
897 
898   //
899   // Update TSeg entry.
900   //
901   mProtectionMemRange[0].Range.Base = mCpuHotPlugData.SmrrBase;
902   mProtectionMemRange[0].Range.Top  = mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize;
903 
904   //
905   // Update SMM profile entry.
906   //
907   mProtectionMemRange[1].Range.Base = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase;
908   mProtectionMemRange[1].Range.Top  = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase + TotalSize;
909 
910   //
911   // Allocate memory reserved for creating 4KB pages.
912   //
913   InitPagesForPFHandler ();
914 
915   //
916   // Start SMM profile when SmmReadyToLock protocol is installed.
917   //
918   Status = gSmst->SmmRegisterProtocolNotify (
919                     &gEfiSmmReadyToLockProtocolGuid,
920                     InitSmmProfileCallBack,
921                     &Registration
922                     );
923   ASSERT_EFI_ERROR (Status);
924 
925   return ;
926 }
927 
928 /**
929   Check if XD feature is supported by a processor.
930 
931 **/
932 VOID
CheckFeatureSupported(VOID)933 CheckFeatureSupported (
934   VOID
935   )
936 {
937   UINT32                 RegEax;
938   UINT32                 RegEdx;
939 
940   if (mXdSupported) {
941     AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
942     if (RegEax <= CPUID_EXTENDED_FUNCTION) {
943       //
944       // Extended CPUID functions are not supported on this processor.
945       //
946       mXdSupported = FALSE;
947     }
948 
949     AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx);
950     if ((RegEdx & CPUID1_EDX_XD_SUPPORT) == 0) {
951       //
952       // Execute Disable Bit feature is not supported on this processor.
953       //
954       mXdSupported = FALSE;
955     }
956   }
957 
958   if (mBtsSupported) {
959     AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);
960     if ((RegEdx & CPUID1_EDX_BTS_AVAILABLE) != 0) {
961       //
962       // Per IA32 manuals:
963       // When CPUID.1:EDX[21] is set, the following BTS facilities are available:
964       // 1. The BTS_UNAVAILABLE flag in the IA32_MISC_ENABLE MSR indicates the
965       //    availability of the BTS facilities, including the ability to set the BTS and
966       //    BTINT bits in the MSR_DEBUGCTLA MSR.
967       // 2. The IA32_DS_AREA MSR can be programmed to point to the DS save area.
968       //
969       if ((AsmMsrBitFieldRead64 (MSR_IA32_MISC_ENABLE, 11, 11) == 0) &&
970           (AsmMsrBitFieldRead64 (MSR_IA32_MISC_ENABLE, 12, 12) == 0)) {
971         //
972         // BTS facilities is supported.
973         //
974         mBtsSupported = FALSE;
975       }
976     }
977   }
978 }
979 
980 /**
981   Check if XD and BTS features are supported by all processors.
982 
983 **/
984 VOID
CheckProcessorFeature(VOID)985 CheckProcessorFeature (
986   VOID
987   )
988 {
989   EFI_STATUS                        Status;
990   EFI_MP_SERVICES_PROTOCOL          *MpServices;
991 
992   Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices);
993   ASSERT_EFI_ERROR (Status);
994 
995   //
996   // First detect if XD and BTS are supported
997   //
998   mXdSupported  = TRUE;
999   mBtsSupported = TRUE;
1000 
1001   //
1002   // Check if XD and BTS are supported on all processors.
1003   //
1004   CheckFeatureSupported ();
1005 
1006   //
1007   //Check on other processors if BSP supports this
1008   //
1009   if (mXdSupported || mBtsSupported) {
1010     MpServices->StartupAllAPs (
1011                   MpServices,
1012                   (EFI_AP_PROCEDURE) CheckFeatureSupported,
1013                   TRUE,
1014                   NULL,
1015                   0,
1016                   NULL,
1017                   NULL
1018                   );
1019   }
1020 }
1021 
1022 /**
1023   Enable XD feature.
1024 
1025 **/
1026 VOID
ActivateXd(VOID)1027 ActivateXd (
1028   VOID
1029   )
1030 {
1031   UINT64           MsrRegisters;
1032 
1033   MsrRegisters = AsmReadMsr64 (MSR_EFER);
1034   if ((MsrRegisters & MSR_EFER_XD) != 0) {
1035     return ;
1036   }
1037   MsrRegisters |= MSR_EFER_XD;
1038   AsmWriteMsr64 (MSR_EFER, MsrRegisters);
1039 }
1040 
1041 /**
1042   Enable single step.
1043 
1044 **/
1045 VOID
ActivateSingleStepDB(VOID)1046 ActivateSingleStepDB (
1047   VOID
1048   )
1049 {
1050   UINTN    Dr6;
1051 
1052   Dr6 = AsmReadDr6 ();
1053   if ((Dr6 & DR6_SINGLE_STEP) != 0) {
1054     return;
1055   }
1056   Dr6 |= DR6_SINGLE_STEP;
1057   AsmWriteDr6 (Dr6);
1058 }
1059 
1060 /**
1061   Enable last branch.
1062 
1063 **/
1064 VOID
ActivateLBR(VOID)1065 ActivateLBR (
1066   VOID
1067   )
1068 {
1069   UINT64  DebugCtl;
1070 
1071   DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL);
1072   if ((DebugCtl & MSR_DEBUG_CTL_LBR) != 0) {
1073     return ;
1074   }
1075   AsmWriteMsr64 (MSR_LER_FROM_LIP, 0);
1076   AsmWriteMsr64 (MSR_LER_TO_LIP, 0);
1077   DebugCtl |= MSR_DEBUG_CTL_LBR;
1078   AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl);
1079 }
1080 
1081 /**
1082   Enable branch trace store.
1083 
1084   @param  CpuIndex  The index of the processor.
1085 
1086 **/
1087 VOID
ActivateBTS(IN UINTN CpuIndex)1088 ActivateBTS (
1089   IN      UINTN                     CpuIndex
1090   )
1091 {
1092   UINT64  DebugCtl;
1093 
1094   DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL);
1095   if ((DebugCtl & MSR_DEBUG_CTL_BTS) != 0) {
1096     return ;
1097   }
1098 
1099   AsmWriteMsr64 (MSR_DS_AREA, (UINT64)(UINTN)mMsrDsArea[CpuIndex]);
1100   DebugCtl |= (UINT64)(MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR);
1101   DebugCtl &= ~((UINT64)MSR_DEBUG_CTL_BTINT);
1102   AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl);
1103 }
1104 
1105 /**
1106   Increase SMI number in each SMI entry.
1107 
1108 **/
1109 VOID
SmmProfileRecordSmiNum(VOID)1110 SmmProfileRecordSmiNum (
1111   VOID
1112   )
1113 {
1114   if (mSmmProfileStart) {
1115     mSmmProfileBase->NumSmis++;
1116   }
1117 }
1118 
1119 /**
1120   Initialize processor environment for SMM profile.
1121 
1122   @param  CpuIndex  The index of the processor.
1123 
1124 **/
1125 VOID
ActivateSmmProfile(IN UINTN CpuIndex)1126 ActivateSmmProfile (
1127   IN UINTN CpuIndex
1128   )
1129 {
1130   //
1131   // Enable Single Step DB#
1132   //
1133   ActivateSingleStepDB ();
1134 
1135   if (mBtsSupported) {
1136     //
1137     // We can not get useful information from LER, so we have to use BTS.
1138     //
1139     ActivateLBR ();
1140 
1141     //
1142     // Enable BTS
1143     //
1144     ActivateBTS (CpuIndex);
1145   }
1146 }
1147 
1148 /**
1149   Initialize SMM profile in SMM CPU entry point.
1150 
1151   @param[in] Cr3  The base address of the page tables to use in SMM.
1152 
1153 **/
1154 VOID
InitSmmProfile(UINT32 Cr3)1155 InitSmmProfile (
1156   UINT32  Cr3
1157   )
1158 {
1159   //
1160   // Save Cr3
1161   //
1162   mSmmProfileCr3 = Cr3;
1163 
1164   //
1165   // Skip SMM profile initialization if feature is disabled
1166   //
1167   if (!FeaturePcdGet (PcdCpuSmmProfileEnable)) {
1168     return;
1169   }
1170 
1171   //
1172   // Initialize SmmProfile here
1173   //
1174   InitSmmProfileInternal ();
1175 
1176   //
1177   // Initialize profile IDT.
1178   //
1179   InitIdtr ();
1180 }
1181 
1182 /**
1183   Update page table to map the memory correctly in order to make the instruction
1184   which caused page fault execute successfully. And it also save the original page
1185   table to be restored in single-step exception.
1186 
1187   @param  PageTable           PageTable Address.
1188   @param  PFAddress           The memory address which caused page fault exception.
1189   @param  CpuIndex            The index of the processor.
1190   @param  ErrorCode           The Error code of exception.
1191 
1192 **/
1193 VOID
RestorePageTableBelow4G(UINT64 * PageTable,UINT64 PFAddress,UINTN CpuIndex,UINTN ErrorCode)1194 RestorePageTableBelow4G (
1195   UINT64        *PageTable,
1196   UINT64        PFAddress,
1197   UINTN         CpuIndex,
1198   UINTN         ErrorCode
1199   )
1200 {
1201   UINTN         PTIndex;
1202   UINTN         PFIndex;
1203 
1204   //
1205   // PML4
1206   //
1207   if (sizeof(UINT64) == sizeof(UINTN)) {
1208     PTIndex = (UINTN)BitFieldRead64 (PFAddress, 39, 47);
1209     ASSERT (PageTable[PTIndex] != 0);
1210     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
1211   }
1212 
1213   //
1214   // PDPTE
1215   //
1216   PTIndex = (UINTN)BitFieldRead64 (PFAddress, 30, 38);
1217   ASSERT (PageTable[PTIndex] != 0);
1218   PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
1219 
1220   //
1221   // PD
1222   //
1223   PTIndex = (UINTN)BitFieldRead64 (PFAddress, 21, 29);
1224   if ((PageTable[PTIndex] & IA32_PG_PS) != 0) {
1225     //
1226     // Large page
1227     //
1228 
1229     //
1230     // Record old entries with non-present status
1231     // Old entries include the memory which instruction is at and the memory which instruction access.
1232     //
1233     //
1234     ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT);
1235     if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) {
1236       PFIndex = mPFEntryCount[CpuIndex];
1237       mLastPFEntryValue[CpuIndex][PFIndex]   = PageTable[PTIndex];
1238       mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex];
1239       mPFEntryCount[CpuIndex]++;
1240     }
1241 
1242     //
1243     // Set new entry
1244     //
1245     PageTable[PTIndex] = (PFAddress & ~((1ull << 21) - 1));
1246     PageTable[PTIndex] |= (UINT64)IA32_PG_PS;
1247     PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;
1248     if ((ErrorCode & IA32_PF_EC_ID) != 0) {
1249       PageTable[PTIndex] &= ~IA32_PG_NX;
1250     }
1251   } else {
1252     //
1253     // Small page
1254     //
1255     ASSERT (PageTable[PTIndex] != 0);
1256     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
1257 
1258     //
1259     // 4K PTE
1260     //
1261     PTIndex = (UINTN)BitFieldRead64 (PFAddress, 12, 20);
1262 
1263     //
1264     // Record old entries with non-present status
1265     // Old entries include the memory which instruction is at and the memory which instruction access.
1266     //
1267     //
1268     ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT);
1269     if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) {
1270       PFIndex = mPFEntryCount[CpuIndex];
1271       mLastPFEntryValue[CpuIndex][PFIndex]   = PageTable[PTIndex];
1272       mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex];
1273       mPFEntryCount[CpuIndex]++;
1274     }
1275 
1276     //
1277     // Set new entry
1278     //
1279     PageTable[PTIndex] = (PFAddress & ~((1ull << 12) - 1));
1280     PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;
1281     if ((ErrorCode & IA32_PF_EC_ID) != 0) {
1282       PageTable[PTIndex] &= ~IA32_PG_NX;
1283     }
1284   }
1285 }
1286 
1287 /**
1288   The Page fault handler to save SMM profile data.
1289 
1290   @param  Rip        The RIP when exception happens.
1291   @param  ErrorCode  The Error code of exception.
1292 
1293 **/
1294 VOID
SmmProfilePFHandler(UINTN Rip,UINTN ErrorCode)1295 SmmProfilePFHandler (
1296   UINTN Rip,
1297   UINTN ErrorCode
1298   )
1299 {
1300   UINT64                *PageTable;
1301   UINT64                PFAddress;
1302   UINTN                 CpuIndex;
1303   UINTN                 Index;
1304   UINT64                InstructionAddress;
1305   UINTN                 MaxEntryNumber;
1306   UINTN                 CurrentEntryNumber;
1307   BOOLEAN               IsValidPFAddress;
1308   SMM_PROFILE_ENTRY     *SmmProfileEntry;
1309   UINT64                SmiCommand;
1310   EFI_STATUS            Status;
1311   UINTN                 SwSmiCpuIndex;
1312   UINT8                 SoftSmiValue;
1313   EFI_SMM_SAVE_STATE_IO_INFO    IoInfo;
1314 
1315   if (!mSmmProfileStart) {
1316     //
1317     // If SMM profile does not start, call original page fault handler.
1318     //
1319     SmiDefaultPFHandler ();
1320     return;
1321   }
1322 
1323   if (mBtsSupported) {
1324     DisableBTS ();
1325   }
1326 
1327   IsValidPFAddress  = FALSE;
1328   PageTable         = (UINT64 *)AsmReadCr3 ();
1329   PFAddress         = AsmReadCr2 ();
1330   CpuIndex          = GetCpuIndex ();
1331 
1332   if (PFAddress <= 0xFFFFFFFF) {
1333     RestorePageTableBelow4G (PageTable, PFAddress, CpuIndex, ErrorCode);
1334   } else {
1335     RestorePageTableAbove4G (PageTable, PFAddress, CpuIndex, ErrorCode, &IsValidPFAddress);
1336   }
1337 
1338   if (!IsValidPFAddress) {
1339     InstructionAddress = Rip;
1340     if ((ErrorCode & IA32_PF_EC_ID) != 0 && (mBtsSupported)) {
1341       //
1342       // If it is instruction fetch failure, get the correct IP from BTS.
1343       //
1344       InstructionAddress = GetSourceFromDestinationOnBts (CpuIndex, Rip);
1345       if (InstructionAddress == 0) {
1346         //
1347         // It indicates the instruction which caused page fault is not a jump instruction,
1348         // set instruction address same as the page fault address.
1349         //
1350         InstructionAddress = PFAddress;
1351       }
1352     }
1353 
1354     //
1355     // Try to find which CPU trigger SWSMI
1356     //
1357     SwSmiCpuIndex = 0;
1358     //
1359     // Indicate it is not software SMI
1360     //
1361     SmiCommand    = 0xFFFFFFFFFFFFFFFFULL;
1362     for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
1363       Status = SmmReadSaveState(&mSmmCpu, sizeof(IoInfo), EFI_SMM_SAVE_STATE_REGISTER_IO, Index, &IoInfo);
1364       if (EFI_ERROR (Status)) {
1365         continue;
1366       }
1367       if (IoInfo.IoPort == mSmiCommandPort) {
1368         //
1369         // Great! Find it.
1370         //
1371         SwSmiCpuIndex = Index;
1372         //
1373         // A software SMI triggered by SMI command port has been found, get SmiCommand from SMI command port.
1374         //
1375         SoftSmiValue = IoRead8 (mSmiCommandPort);
1376         SmiCommand = (UINT64)SoftSmiValue;
1377         break;
1378       }
1379     }
1380 
1381     SmmProfileEntry = (SMM_PROFILE_ENTRY *)(UINTN)(mSmmProfileBase + 1);
1382     //
1383     // Check if there is already a same entry in profile data.
1384     //
1385     for (Index = 0; Index < (UINTN) mSmmProfileBase->CurDataEntries; Index++) {
1386       if ((SmmProfileEntry[Index].ErrorCode   == (UINT64)ErrorCode) &&
1387           (SmmProfileEntry[Index].Address     == PFAddress) &&
1388           (SmmProfileEntry[Index].CpuNum      == (UINT64)CpuIndex) &&
1389           (SmmProfileEntry[Index].Instruction == InstructionAddress) &&
1390           (SmmProfileEntry[Index].SmiCmd      == SmiCommand)) {
1391         //
1392         // Same record exist, need not save again.
1393         //
1394         break;
1395       }
1396     }
1397     if (Index == mSmmProfileBase->CurDataEntries) {
1398       CurrentEntryNumber = (UINTN) mSmmProfileBase->CurDataEntries;
1399       MaxEntryNumber     = (UINTN) mSmmProfileBase->MaxDataEntries;
1400       if (FeaturePcdGet (PcdCpuSmmProfileRingBuffer)) {
1401         CurrentEntryNumber = CurrentEntryNumber % MaxEntryNumber;
1402       }
1403       if (CurrentEntryNumber < MaxEntryNumber) {
1404         //
1405         // Log the new entry
1406         //
1407         SmmProfileEntry[CurrentEntryNumber].SmiNum      = mSmmProfileBase->NumSmis;
1408         SmmProfileEntry[CurrentEntryNumber].ErrorCode   = (UINT64)ErrorCode;
1409         SmmProfileEntry[CurrentEntryNumber].ApicId      = (UINT64)GetApicId ();
1410         SmmProfileEntry[CurrentEntryNumber].CpuNum      = (UINT64)CpuIndex;
1411         SmmProfileEntry[CurrentEntryNumber].Address     = PFAddress;
1412         SmmProfileEntry[CurrentEntryNumber].Instruction = InstructionAddress;
1413         SmmProfileEntry[CurrentEntryNumber].SmiCmd      = SmiCommand;
1414         //
1415         // Update current entry index and data size in the header.
1416         //
1417         mSmmProfileBase->CurDataEntries++;
1418         mSmmProfileBase->CurDataSize = MultU64x64 (mSmmProfileBase->CurDataEntries, sizeof (SMM_PROFILE_ENTRY));
1419       }
1420     }
1421   }
1422   //
1423   // Flush TLB
1424   //
1425   CpuFlushTlb ();
1426 
1427   if (mBtsSupported) {
1428     EnableBTS ();
1429   }
1430 }
1431 
1432 /**
1433   Replace INT1 exception handler to restore page table to absent/execute-disable state
1434   in order to trigger page fault again to save SMM profile data..
1435 
1436 **/
1437 VOID
InitIdtr(VOID)1438 InitIdtr (
1439   VOID
1440   )
1441 {
1442   SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_DEBUG, DebugExceptionHandler);
1443 }
1444