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