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