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