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