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