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