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