]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c
UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile: Remove unnecessary BTS MSRs
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmProfile.c
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 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
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
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
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
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
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
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
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
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_PHYSICAL_ADDRESS ProtectBaseAddress;
346 EFI_PHYSICAL_ADDRESS ProtectEndAddress;
347 EFI_PHYSICAL_ADDRESS Top2MBAlignedAddress;
348 EFI_PHYSICAL_ADDRESS Base2MBAlignedAddress;
349 UINT64 High4KBPageSize;
350 UINT64 Low4KBPageSize;
351
352 NumberOfDescriptors = 0;
353 NumberOfMmioDescriptors = 0;
354 NumberOfSpliteRange = 0;
355 MemorySpaceMap = NULL;
356
357 //
358 // Get MMIO ranges from GCD and add them into protected memory ranges.
359 //
360 gDS->GetMemorySpaceMap (
361 &NumberOfDescriptors,
362 &MemorySpaceMap
363 );
364 for (Index = 0; Index < NumberOfDescriptors; Index++) {
365 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
366 NumberOfMmioDescriptors++;
367 }
368 }
369
370 if (NumberOfMmioDescriptors != 0) {
371 TotalSize = NumberOfMmioDescriptors * sizeof (MEMORY_PROTECTION_RANGE) + sizeof (mProtectionMemRangeTemplate);
372 mProtectionMemRange = (MEMORY_PROTECTION_RANGE *) AllocateZeroPool (TotalSize);
373 ASSERT (mProtectionMemRange != NULL);
374 mProtectionMemRangeCount = TotalSize / sizeof (MEMORY_PROTECTION_RANGE);
375
376 //
377 // Copy existing ranges.
378 //
379 CopyMem (mProtectionMemRange, mProtectionMemRangeTemplate, sizeof (mProtectionMemRangeTemplate));
380
381 //
382 // Create split ranges which come from protected ranges.
383 //
384 TotalSize = (TotalSize / sizeof (MEMORY_PROTECTION_RANGE)) * sizeof (MEMORY_RANGE);
385 mSplitMemRange = (MEMORY_RANGE *) AllocateZeroPool (TotalSize);
386 ASSERT (mSplitMemRange != NULL);
387
388 //
389 // Create MMIO ranges which are set to present and execution-disable.
390 //
391 NumberOfProtectRange = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE);
392 for (Index = 0; Index < NumberOfDescriptors; Index++) {
393 if (MemorySpaceMap[Index].GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo) {
394 continue;
395 }
396 mProtectionMemRange[NumberOfProtectRange].Range.Base = MemorySpaceMap[Index].BaseAddress;
397 mProtectionMemRange[NumberOfProtectRange].Range.Top = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length;
398 mProtectionMemRange[NumberOfProtectRange].Present = TRUE;
399 mProtectionMemRange[NumberOfProtectRange].Nx = TRUE;
400 NumberOfProtectRange++;
401 }
402 }
403
404 //
405 // According to protected ranges, create the ranges which will be mapped by 2KB page.
406 //
407 NumberOfSpliteRange = 0;
408 NumberOfProtectRange = mProtectionMemRangeCount;
409 for (Index = 0; Index < NumberOfProtectRange; Index++) {
410 //
411 // If MMIO base address is not 2MB alignment, make 2MB alignment for create 4KB page in page table.
412 //
413 ProtectBaseAddress = mProtectionMemRange[Index].Range.Base;
414 ProtectEndAddress = mProtectionMemRange[Index].Range.Top;
415 if (((ProtectBaseAddress & (SIZE_2MB - 1)) != 0) || ((ProtectEndAddress & (SIZE_2MB - 1)) != 0)) {
416 //
417 // Check if it is possible to create 4KB-page for not 2MB-aligned range and to create 2MB-page for 2MB-aligned range.
418 // A mix of 4KB and 2MB page could save SMRAM space.
419 //
420 Top2MBAlignedAddress = ProtectEndAddress & ~(SIZE_2MB - 1);
421 Base2MBAlignedAddress = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
422 if ((Top2MBAlignedAddress > Base2MBAlignedAddress) &&
423 ((Top2MBAlignedAddress - Base2MBAlignedAddress) >= SIZE_2MB)) {
424 //
425 // There is an range which could be mapped by 2MB-page.
426 //
427 High4KBPageSize = ((ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectEndAddress & ~(SIZE_2MB - 1));
428 Low4KBPageSize = ((ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectBaseAddress & ~(SIZE_2MB - 1));
429 if (High4KBPageSize != 0) {
430 //
431 // Add not 2MB-aligned range to be mapped by 4KB-page.
432 //
433 mSplitMemRange[NumberOfSpliteRange].Base = ProtectEndAddress & ~(SIZE_2MB - 1);
434 mSplitMemRange[NumberOfSpliteRange].Top = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
435 NumberOfSpliteRange++;
436 }
437 if (Low4KBPageSize != 0) {
438 //
439 // Add not 2MB-aligned range to be mapped by 4KB-page.
440 //
441 mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1);
442 mSplitMemRange[NumberOfSpliteRange].Top = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
443 NumberOfSpliteRange++;
444 }
445 } else {
446 //
447 // The range could only be mapped by 4KB-page.
448 //
449 mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1);
450 mSplitMemRange[NumberOfSpliteRange].Top = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
451 NumberOfSpliteRange++;
452 }
453 }
454 }
455
456 mSplitMemRangeCount = NumberOfSpliteRange;
457
458 DEBUG ((EFI_D_INFO, "SMM Profile Memory Ranges:\n"));
459 for (Index = 0; Index < mProtectionMemRangeCount; Index++) {
460 DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Base = %lx\n", Index, mProtectionMemRange[Index].Range.Base));
461 DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Top = %lx\n", Index, mProtectionMemRange[Index].Range.Top));
462 }
463 for (Index = 0; Index < mSplitMemRangeCount; Index++) {
464 DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Base = %lx\n", Index, mSplitMemRange[Index].Base));
465 DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Top = %lx\n", Index, mSplitMemRange[Index].Top));
466 }
467 }
468
469 /**
470 Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
471
472 **/
473 VOID
474 InitPaging (
475 VOID
476 )
477 {
478 UINT64 *Pml4;
479 UINT64 *Pde;
480 UINT64 *Pte;
481 UINT64 *Pt;
482 UINTN Address;
483 UINTN Level1;
484 UINTN Level2;
485 UINTN Level3;
486 UINTN Level4;
487 UINTN NumberOfPdpEntries;
488 UINTN NumberOfPml4Entries;
489 UINTN SizeOfMemorySpace;
490 BOOLEAN Nx;
491
492 if (sizeof (UINTN) == sizeof (UINT64)) {
493 Pml4 = (UINT64*)(UINTN)mSmmProfileCr3;
494 SizeOfMemorySpace = HighBitSet64 (gPhyMask) + 1;
495 //
496 // Calculate the table entries of PML4E and PDPTE.
497 //
498 if (SizeOfMemorySpace <= 39 ) {
499 NumberOfPml4Entries = 1;
500 NumberOfPdpEntries = (UINT32)LShiftU64 (1, (SizeOfMemorySpace - 30));
501 } else {
502 NumberOfPml4Entries = (UINT32)LShiftU64 (1, (SizeOfMemorySpace - 39));
503 NumberOfPdpEntries = 512;
504 }
505 } else {
506 NumberOfPml4Entries = 1;
507 NumberOfPdpEntries = 4;
508 }
509
510 //
511 // Go through page table and change 2MB-page into 4KB-page.
512 //
513 for (Level1 = 0; Level1 < NumberOfPml4Entries; Level1++) {
514 if (sizeof (UINTN) == sizeof (UINT64)) {
515 if ((Pml4[Level1] & IA32_PG_P) == 0) {
516 //
517 // If Pml4 entry does not exist, skip it
518 //
519 continue;
520 }
521 Pde = (UINT64 *)(UINTN)(Pml4[Level1] & PHYSICAL_ADDRESS_MASK);
522 } else {
523 Pde = (UINT64*)(UINTN)mSmmProfileCr3;
524 }
525 for (Level2 = 0; Level2 < NumberOfPdpEntries; Level2++, Pde++) {
526 if ((*Pde & IA32_PG_P) == 0) {
527 //
528 // If PDE entry does not exist, skip it
529 //
530 continue;
531 }
532 Pte = (UINT64 *)(UINTN)(*Pde & PHYSICAL_ADDRESS_MASK);
533 if (Pte == 0) {
534 continue;
535 }
536 for (Level3 = 0; Level3 < SIZE_4KB / sizeof (*Pte); Level3++, Pte++) {
537 if ((*Pte & IA32_PG_P) == 0) {
538 //
539 // If PTE entry does not exist, skip it
540 //
541 continue;
542 }
543 Address = (((Level2 << 9) + Level3) << 21);
544
545 //
546 // If it is 2M page, check IsAddressSplit()
547 //
548 if (((*Pte & IA32_PG_PS) != 0) && IsAddressSplit (Address)) {
549 //
550 // Based on current page table, create 4KB page table for split area.
551 //
552 ASSERT (Address == (*Pte & PHYSICAL_ADDRESS_MASK));
553
554 Pt = AllocatePageTableMemory (1);
555 ASSERT (Pt != NULL);
556
557 // Split it
558 for (Level4 = 0; Level4 < SIZE_4KB / sizeof(*Pt); Level4++) {
559 Pt[Level4] = Address + ((Level4 << 12) | PAGE_ATTRIBUTE_BITS);
560 } // end for PT
561 *Pte = (UINTN)Pt | PAGE_ATTRIBUTE_BITS;
562 } // end if IsAddressSplit
563 } // end for PTE
564 } // end for PDE
565 }
566
567 //
568 // Go through page table and set several page table entries to absent or execute-disable.
569 //
570 DEBUG ((EFI_D_INFO, "Patch page table start ...\n"));
571 for (Level1 = 0; Level1 < NumberOfPml4Entries; Level1++) {
572 if (sizeof (UINTN) == sizeof (UINT64)) {
573 if ((Pml4[Level1] & IA32_PG_P) == 0) {
574 //
575 // If Pml4 entry does not exist, skip it
576 //
577 continue;
578 }
579 Pde = (UINT64 *)(UINTN)(Pml4[Level1] & PHYSICAL_ADDRESS_MASK);
580 } else {
581 Pde = (UINT64*)(UINTN)mSmmProfileCr3;
582 }
583 for (Level2 = 0; Level2 < NumberOfPdpEntries; Level2++, Pde++) {
584 if ((*Pde & IA32_PG_P) == 0) {
585 //
586 // If PDE entry does not exist, skip it
587 //
588 continue;
589 }
590 Pte = (UINT64 *)(UINTN)(*Pde & PHYSICAL_ADDRESS_MASK);
591 if (Pte == 0) {
592 continue;
593 }
594 for (Level3 = 0; Level3 < SIZE_4KB / sizeof (*Pte); Level3++, Pte++) {
595 if ((*Pte & IA32_PG_P) == 0) {
596 //
597 // If PTE entry does not exist, skip it
598 //
599 continue;
600 }
601 Address = (((Level2 << 9) + Level3) << 21);
602
603 if ((*Pte & IA32_PG_PS) != 0) {
604 // 2MB page
605
606 if (!IsAddressValid (Address, &Nx)) {
607 //
608 // Patch to remove Present flag and RW flag
609 //
610 *Pte = *Pte & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);
611 }
612 if (Nx && mXdSupported) {
613 *Pte = *Pte | IA32_PG_NX;
614 }
615 } else {
616 // 4KB page
617 Pt = (UINT64 *)(UINTN)(*Pte & PHYSICAL_ADDRESS_MASK);
618 if (Pt == 0) {
619 continue;
620 }
621 for (Level4 = 0; Level4 < SIZE_4KB / sizeof(*Pt); Level4++, Pt++) {
622 if (!IsAddressValid (Address, &Nx)) {
623 *Pt = *Pt & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);
624 }
625 if (Nx && mXdSupported) {
626 *Pt = *Pt | IA32_PG_NX;
627 }
628 Address += SIZE_4KB;
629 } // end for PT
630 } // end if PS
631 } // end for PTE
632 } // end for PDE
633 }
634
635 //
636 // Flush TLB
637 //
638 CpuFlushTlb ();
639 DEBUG ((EFI_D_INFO, "Patch page table done!\n"));
640 //
641 // Set execute-disable flag
642 //
643 mXdEnabled = TRUE;
644
645 return ;
646 }
647
648 /**
649 To find FADT in ACPI tables.
650
651 @param AcpiTableGuid The GUID used to find ACPI table in UEFI ConfigurationTable.
652
653 @return FADT table pointer.
654 **/
655 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *
656 FindAcpiFadtTableByAcpiGuid (
657 IN EFI_GUID *AcpiTableGuid
658 )
659 {
660 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
661 EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
662 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
663 UINTN Index;
664 UINT32 Data32;
665 Rsdp = NULL;
666 Rsdt = NULL;
667 Fadt = NULL;
668 //
669 // found ACPI table RSD_PTR from system table
670 //
671 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
672 if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), AcpiTableGuid)) {
673 //
674 // A match was found.
675 //
676 Rsdp = gST->ConfigurationTable[Index].VendorTable;
677 break;
678 }
679 }
680
681 if (Rsdp == NULL) {
682 return NULL;
683 }
684
685 Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->RsdtAddress;
686 if (Rsdt == NULL || Rsdt->Signature != EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
687 return NULL;
688 }
689
690 for (Index = sizeof (EFI_ACPI_DESCRIPTION_HEADER); Index < Rsdt->Length; Index = Index + sizeof (UINT32)) {
691
692 Data32 = *(UINT32 *) ((UINT8 *) Rsdt + Index);
693 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *) (UINT32 *) (UINTN) Data32;
694 if (Fadt->Header.Signature == EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
695 break;
696 }
697 }
698
699 if (Fadt == NULL || Fadt->Header.Signature != EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
700 return NULL;
701 }
702
703 return Fadt;
704 }
705
706 /**
707 To find FADT in ACPI tables.
708
709 @return FADT table pointer.
710 **/
711 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *
712 FindAcpiFadtTable (
713 VOID
714 )
715 {
716 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
717
718 Fadt = FindAcpiFadtTableByAcpiGuid (&gEfiAcpi20TableGuid);
719 if (Fadt != NULL) {
720 return Fadt;
721 }
722
723 return FindAcpiFadtTableByAcpiGuid (&gEfiAcpi10TableGuid);
724 }
725
726 /**
727 To get system port address of the SMI Command Port in FADT table.
728
729 **/
730 VOID
731 GetSmiCommandPort (
732 VOID
733 )
734 {
735 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
736
737 Fadt = FindAcpiFadtTable ();
738 ASSERT (Fadt != NULL);
739
740 mSmiCommandPort = Fadt->SmiCmd;
741 DEBUG ((EFI_D_INFO, "mSmiCommandPort = %x\n", mSmiCommandPort));
742 }
743
744 /**
745 Updates page table to make some memory ranges (like system memory) absent
746 and make some memory ranges (like MMIO) present and execute disable. It also
747 update 2MB-page to 4KB-page for some memory ranges.
748
749 **/
750 VOID
751 SmmProfileStart (
752 VOID
753 )
754 {
755 //
756 // The flag indicates SMM profile starts to work.
757 //
758 mSmmProfileStart = TRUE;
759 }
760
761 /**
762 Initialize SMM profile in SmmReadyToLock protocol callback function.
763
764 @param Protocol Points to the protocol's unique identifier.
765 @param Interface Points to the interface instance.
766 @param Handle The handle on which the interface was installed.
767
768 @retval EFI_SUCCESS SmmReadyToLock protocol callback runs successfully.
769 **/
770 EFI_STATUS
771 EFIAPI
772 InitSmmProfileCallBack (
773 IN CONST EFI_GUID *Protocol,
774 IN VOID *Interface,
775 IN EFI_HANDLE Handle
776 )
777 {
778 //
779 // Save to variable so that SMM profile data can be found.
780 //
781 gRT->SetVariable (
782 SMM_PROFILE_NAME,
783 &gEfiCallerIdGuid,
784 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
785 sizeof(mSmmProfileBase),
786 &mSmmProfileBase
787 );
788
789 //
790 // Get Software SMI from FADT
791 //
792 GetSmiCommandPort ();
793
794 //
795 // Initialize protected memory range for patching page table later.
796 //
797 InitProtectedMemRange ();
798
799 return EFI_SUCCESS;
800 }
801
802 /**
803 Initialize SMM profile data structures.
804
805 **/
806 VOID
807 InitSmmProfileInternal (
808 VOID
809 )
810 {
811 EFI_STATUS Status;
812 EFI_PHYSICAL_ADDRESS Base;
813 VOID *Registration;
814 UINTN Index;
815 UINTN MsrDsAreaSizePerCpu;
816 UINTN TotalSize;
817
818 mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
819 ASSERT (mPFEntryCount != NULL);
820 mLastPFEntryValue = (UINT64 (*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (
821 sizeof (mLastPFEntryValue[0]) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
822 ASSERT (mLastPFEntryValue != NULL);
823 mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (
824 sizeof (mLastPFEntryPointer[0]) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
825 ASSERT (mLastPFEntryPointer != NULL);
826
827 //
828 // Allocate memory for SmmProfile below 4GB.
829 // The base address
830 //
831 mSmmProfileSize = PcdGet32 (PcdCpuSmmProfileSize);
832 ASSERT ((mSmmProfileSize & 0xFFF) == 0);
833
834 if (mBtsSupported) {
835 TotalSize = mSmmProfileSize + mMsrDsAreaSize;
836 } else {
837 TotalSize = mSmmProfileSize;
838 }
839
840 Base = 0xFFFFFFFF;
841 Status = gBS->AllocatePages (
842 AllocateMaxAddress,
843 EfiReservedMemoryType,
844 EFI_SIZE_TO_PAGES (TotalSize),
845 &Base
846 );
847 ASSERT_EFI_ERROR (Status);
848 ZeroMem ((VOID *)(UINTN)Base, TotalSize);
849 mSmmProfileBase = (SMM_PROFILE_HEADER *)(UINTN)Base;
850
851 //
852 // Initialize SMM profile data header.
853 //
854 mSmmProfileBase->HeaderSize = sizeof (SMM_PROFILE_HEADER);
855 mSmmProfileBase->MaxDataEntries = (UINT64)((mSmmProfileSize - sizeof(SMM_PROFILE_HEADER)) / sizeof (SMM_PROFILE_ENTRY));
856 mSmmProfileBase->MaxDataSize = MultU64x64 (mSmmProfileBase->MaxDataEntries, sizeof(SMM_PROFILE_ENTRY));
857 mSmmProfileBase->CurDataEntries = 0;
858 mSmmProfileBase->CurDataSize = 0;
859 mSmmProfileBase->TsegStart = mCpuHotPlugData.SmrrBase;
860 mSmmProfileBase->TsegSize = mCpuHotPlugData.SmrrSize;
861 mSmmProfileBase->NumSmis = 0;
862 mSmmProfileBase->NumCpus = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
863
864 if (mBtsSupported) {
865 mMsrDsArea = (MSR_DS_AREA_STRUCT **)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT *) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
866 ASSERT (mMsrDsArea != NULL);
867 mMsrBTSRecord = (BRANCH_TRACE_RECORD **)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD *) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
868 ASSERT (mMsrBTSRecord != NULL);
869 mMsrPEBSRecord = (PEBS_RECORD **)AllocateZeroPool (sizeof (PEBS_RECORD *) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
870 ASSERT (mMsrPEBSRecord != NULL);
871
872 mMsrDsAreaBase = (MSR_DS_AREA_STRUCT *)((UINTN)Base + mSmmProfileSize);
873 MsrDsAreaSizePerCpu = mMsrDsAreaSize / PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
874 mBTSRecordNumber = (MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER - sizeof(MSR_DS_AREA_STRUCT)) / sizeof(BRANCH_TRACE_RECORD);
875 for (Index = 0; Index < PcdGet32 (PcdCpuMaxLogicalProcessorNumber); Index++) {
876 mMsrDsArea[Index] = (MSR_DS_AREA_STRUCT *)((UINTN)mMsrDsAreaBase + MsrDsAreaSizePerCpu * Index);
877 mMsrBTSRecord[Index] = (BRANCH_TRACE_RECORD *)((UINTN)mMsrDsArea[Index] + sizeof(MSR_DS_AREA_STRUCT));
878 mMsrPEBSRecord[Index] = (PEBS_RECORD *)((UINTN)mMsrDsArea[Index] + MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER);
879
880 mMsrDsArea[Index]->BTSBufferBase = (UINTN)mMsrBTSRecord[Index];
881 mMsrDsArea[Index]->BTSIndex = mMsrDsArea[Index]->BTSBufferBase;
882 mMsrDsArea[Index]->BTSAbsoluteMaximum = mMsrDsArea[Index]->BTSBufferBase + mBTSRecordNumber * sizeof(BRANCH_TRACE_RECORD) + 1;
883 mMsrDsArea[Index]->BTSInterruptThreshold = mMsrDsArea[Index]->BTSAbsoluteMaximum + 1;
884
885 mMsrDsArea[Index]->PEBSBufferBase = (UINTN)mMsrPEBSRecord[Index];
886 mMsrDsArea[Index]->PEBSIndex = mMsrDsArea[Index]->PEBSBufferBase;
887 mMsrDsArea[Index]->PEBSAbsoluteMaximum = mMsrDsArea[Index]->PEBSBufferBase + PEBS_RECORD_NUMBER * sizeof(PEBS_RECORD) + 1;
888 mMsrDsArea[Index]->PEBSInterruptThreshold = mMsrDsArea[Index]->PEBSAbsoluteMaximum + 1;
889 }
890 }
891
892 mProtectionMemRange = mProtectionMemRangeTemplate;
893 mProtectionMemRangeCount = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE);
894
895 //
896 // Update TSeg entry.
897 //
898 mProtectionMemRange[0].Range.Base = mCpuHotPlugData.SmrrBase;
899 mProtectionMemRange[0].Range.Top = mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize;
900
901 //
902 // Update SMM profile entry.
903 //
904 mProtectionMemRange[1].Range.Base = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase;
905 mProtectionMemRange[1].Range.Top = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase + TotalSize;
906
907 //
908 // Allocate memory reserved for creating 4KB pages.
909 //
910 InitPagesForPFHandler ();
911
912 //
913 // Start SMM profile when SmmReadyToLock protocol is installed.
914 //
915 Status = gSmst->SmmRegisterProtocolNotify (
916 &gEfiSmmReadyToLockProtocolGuid,
917 InitSmmProfileCallBack,
918 &Registration
919 );
920 ASSERT_EFI_ERROR (Status);
921
922 return ;
923 }
924
925 /**
926 Check if XD feature is supported by a processor.
927
928 @param[in,out] Buffer The pointer to private data buffer.
929
930 **/
931 VOID
932 EFIAPI
933 CheckFeatureSupported (
934 IN OUT VOID *Buffer
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) == 1) {
970 //
971 // BTS facilities is not supported if MSR_IA32_MISC_ENABLE BIT11 is set.
972 //
973 mBtsSupported = FALSE;
974 }
975 }
976 }
977 }
978
979 /**
980 Check if XD and BTS features are supported by all processors.
981
982 **/
983 VOID
984 CheckProcessorFeature (
985 VOID
986 )
987 {
988 EFI_STATUS Status;
989 EFI_MP_SERVICES_PROTOCOL *MpServices;
990
991 Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices);
992 ASSERT_EFI_ERROR (Status);
993
994 //
995 // First detect if XD and BTS are supported
996 //
997 mXdSupported = TRUE;
998 mBtsSupported = TRUE;
999
1000 //
1001 // Check if XD and BTS are supported on all processors.
1002 //
1003 CheckFeatureSupported (NULL);
1004
1005 //
1006 //Check on other processors if BSP supports this
1007 //
1008 if (mXdSupported || mBtsSupported) {
1009 MpServices->StartupAllAPs (
1010 MpServices,
1011 CheckFeatureSupported,
1012 TRUE,
1013 NULL,
1014 0,
1015 NULL,
1016 NULL
1017 );
1018 }
1019 }
1020
1021 /**
1022 Enable XD feature.
1023
1024 **/
1025 VOID
1026 ActivateXd (
1027 VOID
1028 )
1029 {
1030 UINT64 MsrRegisters;
1031
1032 MsrRegisters = AsmReadMsr64 (MSR_EFER);
1033 if ((MsrRegisters & MSR_EFER_XD) != 0) {
1034 return ;
1035 }
1036 MsrRegisters |= MSR_EFER_XD;
1037 AsmWriteMsr64 (MSR_EFER, MsrRegisters);
1038 }
1039
1040 /**
1041 Enable single step.
1042
1043 **/
1044 VOID
1045 ActivateSingleStepDB (
1046 VOID
1047 )
1048 {
1049 UINTN Dr6;
1050
1051 Dr6 = AsmReadDr6 ();
1052 if ((Dr6 & DR6_SINGLE_STEP) != 0) {
1053 return;
1054 }
1055 Dr6 |= DR6_SINGLE_STEP;
1056 AsmWriteDr6 (Dr6);
1057 }
1058
1059 /**
1060 Enable last branch.
1061
1062 **/
1063 VOID
1064 ActivateLBR (
1065 VOID
1066 )
1067 {
1068 UINT64 DebugCtl;
1069
1070 DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL);
1071 if ((DebugCtl & MSR_DEBUG_CTL_LBR) != 0) {
1072 return ;
1073 }
1074 DebugCtl |= MSR_DEBUG_CTL_LBR;
1075 AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl);
1076 }
1077
1078 /**
1079 Enable branch trace store.
1080
1081 @param CpuIndex The index of the processor.
1082
1083 **/
1084 VOID
1085 ActivateBTS (
1086 IN UINTN CpuIndex
1087 )
1088 {
1089 UINT64 DebugCtl;
1090
1091 DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL);
1092 if ((DebugCtl & MSR_DEBUG_CTL_BTS) != 0) {
1093 return ;
1094 }
1095
1096 AsmWriteMsr64 (MSR_DS_AREA, (UINT64)(UINTN)mMsrDsArea[CpuIndex]);
1097 DebugCtl |= (UINT64)(MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR);
1098 DebugCtl &= ~((UINT64)MSR_DEBUG_CTL_BTINT);
1099 AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl);
1100 }
1101
1102 /**
1103 Increase SMI number in each SMI entry.
1104
1105 **/
1106 VOID
1107 SmmProfileRecordSmiNum (
1108 VOID
1109 )
1110 {
1111 if (mSmmProfileStart) {
1112 mSmmProfileBase->NumSmis++;
1113 }
1114 }
1115
1116 /**
1117 Initialize processor environment for SMM profile.
1118
1119 @param CpuIndex The index of the processor.
1120
1121 **/
1122 VOID
1123 ActivateSmmProfile (
1124 IN UINTN CpuIndex
1125 )
1126 {
1127 //
1128 // Enable Single Step DB#
1129 //
1130 ActivateSingleStepDB ();
1131
1132 if (mBtsSupported) {
1133 //
1134 // We can not get useful information from LER, so we have to use BTS.
1135 //
1136 ActivateLBR ();
1137
1138 //
1139 // Enable BTS
1140 //
1141 ActivateBTS (CpuIndex);
1142 }
1143 }
1144
1145 /**
1146 Initialize SMM profile in SMM CPU entry point.
1147
1148 @param[in] Cr3 The base address of the page tables to use in SMM.
1149
1150 **/
1151 VOID
1152 InitSmmProfile (
1153 UINT32 Cr3
1154 )
1155 {
1156 //
1157 // Save Cr3
1158 //
1159 mSmmProfileCr3 = Cr3;
1160
1161 //
1162 // Skip SMM profile initialization if feature is disabled
1163 //
1164 if (!FeaturePcdGet (PcdCpuSmmProfileEnable)) {
1165 return;
1166 }
1167
1168 //
1169 // Initialize SmmProfile here
1170 //
1171 InitSmmProfileInternal ();
1172
1173 //
1174 // Initialize profile IDT.
1175 //
1176 InitIdtr ();
1177 }
1178
1179 /**
1180 Update page table to map the memory correctly in order to make the instruction
1181 which caused page fault execute successfully. And it also save the original page
1182 table to be restored in single-step exception.
1183
1184 @param PageTable PageTable Address.
1185 @param PFAddress The memory address which caused page fault exception.
1186 @param CpuIndex The index of the processor.
1187 @param ErrorCode The Error code of exception.
1188
1189 **/
1190 VOID
1191 RestorePageTableBelow4G (
1192 UINT64 *PageTable,
1193 UINT64 PFAddress,
1194 UINTN CpuIndex,
1195 UINTN ErrorCode
1196 )
1197 {
1198 UINTN PTIndex;
1199 UINTN PFIndex;
1200
1201 //
1202 // PML4
1203 //
1204 if (sizeof(UINT64) == sizeof(UINTN)) {
1205 PTIndex = (UINTN)BitFieldRead64 (PFAddress, 39, 47);
1206 ASSERT (PageTable[PTIndex] != 0);
1207 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
1208 }
1209
1210 //
1211 // PDPTE
1212 //
1213 PTIndex = (UINTN)BitFieldRead64 (PFAddress, 30, 38);
1214 ASSERT (PageTable[PTIndex] != 0);
1215 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
1216
1217 //
1218 // PD
1219 //
1220 PTIndex = (UINTN)BitFieldRead64 (PFAddress, 21, 29);
1221 if ((PageTable[PTIndex] & IA32_PG_PS) != 0) {
1222 //
1223 // Large page
1224 //
1225
1226 //
1227 // Record old entries with non-present status
1228 // Old entries include the memory which instruction is at and the memory which instruction access.
1229 //
1230 //
1231 ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT);
1232 if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) {
1233 PFIndex = mPFEntryCount[CpuIndex];
1234 mLastPFEntryValue[CpuIndex][PFIndex] = PageTable[PTIndex];
1235 mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex];
1236 mPFEntryCount[CpuIndex]++;
1237 }
1238
1239 //
1240 // Set new entry
1241 //
1242 PageTable[PTIndex] = (PFAddress & ~((1ull << 21) - 1));
1243 PageTable[PTIndex] |= (UINT64)IA32_PG_PS;
1244 PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;
1245 if ((ErrorCode & IA32_PF_EC_ID) != 0) {
1246 PageTable[PTIndex] &= ~IA32_PG_NX;
1247 }
1248 } else {
1249 //
1250 // Small page
1251 //
1252 ASSERT (PageTable[PTIndex] != 0);
1253 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
1254
1255 //
1256 // 4K PTE
1257 //
1258 PTIndex = (UINTN)BitFieldRead64 (PFAddress, 12, 20);
1259
1260 //
1261 // Record old entries with non-present status
1262 // Old entries include the memory which instruction is at and the memory which instruction access.
1263 //
1264 //
1265 ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT);
1266 if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) {
1267 PFIndex = mPFEntryCount[CpuIndex];
1268 mLastPFEntryValue[CpuIndex][PFIndex] = PageTable[PTIndex];
1269 mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex];
1270 mPFEntryCount[CpuIndex]++;
1271 }
1272
1273 //
1274 // Set new entry
1275 //
1276 PageTable[PTIndex] = (PFAddress & ~((1ull << 12) - 1));
1277 PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;
1278 if ((ErrorCode & IA32_PF_EC_ID) != 0) {
1279 PageTable[PTIndex] &= ~IA32_PG_NX;
1280 }
1281 }
1282 }
1283
1284 /**
1285 The Page fault handler to save SMM profile data.
1286
1287 @param Rip The RIP when exception happens.
1288 @param ErrorCode The Error code of exception.
1289
1290 **/
1291 VOID
1292 SmmProfilePFHandler (
1293 UINTN Rip,
1294 UINTN ErrorCode
1295 )
1296 {
1297 UINT64 *PageTable;
1298 UINT64 PFAddress;
1299 UINTN CpuIndex;
1300 UINTN Index;
1301 UINT64 InstructionAddress;
1302 UINTN MaxEntryNumber;
1303 UINTN CurrentEntryNumber;
1304 BOOLEAN IsValidPFAddress;
1305 SMM_PROFILE_ENTRY *SmmProfileEntry;
1306 UINT64 SmiCommand;
1307 EFI_STATUS Status;
1308 UINT8 SoftSmiValue;
1309 EFI_SMM_SAVE_STATE_IO_INFO IoInfo;
1310
1311 if (!mSmmProfileStart) {
1312 //
1313 // If SMM profile does not start, call original page fault handler.
1314 //
1315 SmiDefaultPFHandler ();
1316 return;
1317 }
1318
1319 if (mBtsSupported) {
1320 DisableBTS ();
1321 }
1322
1323 IsValidPFAddress = FALSE;
1324 PageTable = (UINT64 *)AsmReadCr3 ();
1325 PFAddress = AsmReadCr2 ();
1326 CpuIndex = GetCpuIndex ();
1327
1328 if (PFAddress <= 0xFFFFFFFF) {
1329 RestorePageTableBelow4G (PageTable, PFAddress, CpuIndex, ErrorCode);
1330 } else {
1331 RestorePageTableAbove4G (PageTable, PFAddress, CpuIndex, ErrorCode, &IsValidPFAddress);
1332 }
1333
1334 if (!IsValidPFAddress) {
1335 InstructionAddress = Rip;
1336 if ((ErrorCode & IA32_PF_EC_ID) != 0 && (mBtsSupported)) {
1337 //
1338 // If it is instruction fetch failure, get the correct IP from BTS.
1339 //
1340 InstructionAddress = GetSourceFromDestinationOnBts (CpuIndex, Rip);
1341 if (InstructionAddress == 0) {
1342 //
1343 // It indicates the instruction which caused page fault is not a jump instruction,
1344 // set instruction address same as the page fault address.
1345 //
1346 InstructionAddress = PFAddress;
1347 }
1348 }
1349
1350 //
1351 // Indicate it is not software SMI
1352 //
1353 SmiCommand = 0xFFFFFFFFFFFFFFFFULL;
1354 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
1355 Status = SmmReadSaveState(&mSmmCpu, sizeof(IoInfo), EFI_SMM_SAVE_STATE_REGISTER_IO, Index, &IoInfo);
1356 if (EFI_ERROR (Status)) {
1357 continue;
1358 }
1359 if (IoInfo.IoPort == mSmiCommandPort) {
1360 //
1361 // A software SMI triggered by SMI command port has been found, get SmiCommand from SMI command port.
1362 //
1363 SoftSmiValue = IoRead8 (mSmiCommandPort);
1364 SmiCommand = (UINT64)SoftSmiValue;
1365 break;
1366 }
1367 }
1368
1369 SmmProfileEntry = (SMM_PROFILE_ENTRY *)(UINTN)(mSmmProfileBase + 1);
1370 //
1371 // Check if there is already a same entry in profile data.
1372 //
1373 for (Index = 0; Index < (UINTN) mSmmProfileBase->CurDataEntries; Index++) {
1374 if ((SmmProfileEntry[Index].ErrorCode == (UINT64)ErrorCode) &&
1375 (SmmProfileEntry[Index].Address == PFAddress) &&
1376 (SmmProfileEntry[Index].CpuNum == (UINT64)CpuIndex) &&
1377 (SmmProfileEntry[Index].Instruction == InstructionAddress) &&
1378 (SmmProfileEntry[Index].SmiCmd == SmiCommand)) {
1379 //
1380 // Same record exist, need not save again.
1381 //
1382 break;
1383 }
1384 }
1385 if (Index == mSmmProfileBase->CurDataEntries) {
1386 CurrentEntryNumber = (UINTN) mSmmProfileBase->CurDataEntries;
1387 MaxEntryNumber = (UINTN) mSmmProfileBase->MaxDataEntries;
1388 if (FeaturePcdGet (PcdCpuSmmProfileRingBuffer)) {
1389 CurrentEntryNumber = CurrentEntryNumber % MaxEntryNumber;
1390 }
1391 if (CurrentEntryNumber < MaxEntryNumber) {
1392 //
1393 // Log the new entry
1394 //
1395 SmmProfileEntry[CurrentEntryNumber].SmiNum = mSmmProfileBase->NumSmis;
1396 SmmProfileEntry[CurrentEntryNumber].ErrorCode = (UINT64)ErrorCode;
1397 SmmProfileEntry[CurrentEntryNumber].ApicId = (UINT64)GetApicId ();
1398 SmmProfileEntry[CurrentEntryNumber].CpuNum = (UINT64)CpuIndex;
1399 SmmProfileEntry[CurrentEntryNumber].Address = PFAddress;
1400 SmmProfileEntry[CurrentEntryNumber].Instruction = InstructionAddress;
1401 SmmProfileEntry[CurrentEntryNumber].SmiCmd = SmiCommand;
1402 //
1403 // Update current entry index and data size in the header.
1404 //
1405 mSmmProfileBase->CurDataEntries++;
1406 mSmmProfileBase->CurDataSize = MultU64x64 (mSmmProfileBase->CurDataEntries, sizeof (SMM_PROFILE_ENTRY));
1407 }
1408 }
1409 }
1410 //
1411 // Flush TLB
1412 //
1413 CpuFlushTlb ();
1414
1415 if (mBtsSupported) {
1416 EnableBTS ();
1417 }
1418 }
1419
1420 /**
1421 Replace INT1 exception handler to restore page table to absent/execute-disable state
1422 in order to trigger page fault again to save SMM profile data..
1423
1424 **/
1425 VOID
1426 InitIdtr (
1427 VOID
1428 )
1429 {
1430 SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_DEBUG, DebugExceptionHandler);
1431 }