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