VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c@ 108793

Last change on this file since 108793 was 105670, checked in by vboxsync, 8 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette