]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c
UefiCpuPkg/PiSmmCpuDxeSmm: patch "XdSupported" with PatchInstructionX86()
[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
3c5ce64f
LE
34//\r
35// The flag indicates if execute-disable is supported by processor.\r
36//\r
37BOOLEAN mXdSupported = TRUE;\r
38\r
529a5a86
MK
39//\r
40// The flag indicates if execute-disable is enabled on processor.\r
41//\r
42BOOLEAN mXdEnabled = FALSE;\r
43\r
44//\r
45// The flag indicates if BTS is supported by processor.\r
46//\r
a46a4c90 47BOOLEAN mBtsSupported = TRUE;\r
529a5a86
MK
48\r
49//\r
50// The flag indicates if SMM profile starts to record data.\r
51//\r
52BOOLEAN mSmmProfileStart = FALSE;\r
53\r
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
ee584389
JF
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
529a5a86
MK
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
bb767506 155 for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
529a5a86
MK
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
97f1061e
JF
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
29dc8aa8 275 if ((Address >= mCpuHotPlugData.SmrrBase) && (Address < mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {\r
97f1061e
JF
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
529a5a86
MK
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
529a5a86
MK
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
97f1061e
JF
316 *Nx = TRUE;\r
317 if (IsInSmmRanges (Address)) {\r
318 *Nx = FALSE;\r
529a5a86
MK
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
ee584389 374 UINTN NumberOfAddedDescriptors;\r
529a5a86
MK
375 UINTN NumberOfProtectRange;\r
376 UINTN NumberOfSpliteRange;\r
377 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
378 UINTN TotalSize;\r
529a5a86
MK
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
ee584389 387 NumberOfAddedDescriptors = mSmmCpuSmramRangeCount;\r
529a5a86
MK
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
dc0a7143
LE
394 gDS->GetMemorySpaceMap (\r
395 &NumberOfDescriptors,\r
396 &MemorySpaceMap\r
397 );\r
529a5a86
MK
398 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
399 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {\r
ee584389 400 NumberOfAddedDescriptors++;\r
529a5a86
MK
401 }\r
402 }\r
403\r
ee584389
JF
404 if (NumberOfAddedDescriptors != 0) {\r
405 TotalSize = NumberOfAddedDescriptors * sizeof (MEMORY_PROTECTION_RANGE) + sizeof (mProtectionMemRangeTemplate);\r
529a5a86
MK
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
ee584389
JF
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
529a5a86
MK
441 //\r
442 // Create MMIO ranges which are set to present and execution-disable.\r
443 //\r
529a5a86
MK
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
ee584389
JF
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
529a5a86
MK
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
241f9149 579 Pde = (UINT64 *)(UINTN)(Pml4[Level1] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);\r
529a5a86
MK
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
717fb604
JY
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
241f9149 596 Pte = (UINT64 *)(UINTN)(*Pde & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);\r
529a5a86
MK
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
21c17193 618 Pt = AllocatePageTableMemory (1);\r
529a5a86
MK
619 ASSERT (Pt != NULL);\r
620\r
621 // Split it\r
622 for (Level4 = 0; Level4 < SIZE_4KB / sizeof(*Pt); Level4++) {\r
241f9149 623 Pt[Level4] = Address + ((Level4 << 12) | mAddressEncMask | PAGE_ATTRIBUTE_BITS);\r
529a5a86 624 } // end for PT\r
241f9149 625 *Pte = (UINT64)(UINTN)Pt | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
529a5a86
MK
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
241f9149 643 Pde = (UINT64 *)(UINTN)(Pml4[Level1] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);\r
529a5a86
MK
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
717fb604
JY
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
241f9149 663 Pte = (UINT64 *)(UINTN)(*Pde & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);\r
529a5a86
MK
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
881520ea 683 *Pte = *Pte & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);\r
529a5a86
MK
684 }\r
685 if (Nx && mXdSupported) {\r
686 *Pte = *Pte | IA32_PG_NX;\r
687 }\r
688 } else {\r
689 // 4KB page\r
241f9149 690 Pt = (UINT64 *)(UINTN)(*Pte & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);\r
529a5a86
MK
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
881520ea 696 *Pt = *Pt & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);\r
529a5a86
MK
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
529a5a86
MK
851 //\r
852 // Save to variable so that SMM profile data can be found.\r
853 //\r
dc0a7143
LE
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
529a5a86
MK
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
bb767506 891 mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mMaxNumberOfCpus);\r
529a5a86
MK
892 ASSERT (mPFEntryCount != NULL);\r
893 mLastPFEntryValue = (UINT64 (*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (\r
bb767506 894 sizeof (mLastPFEntryValue[0]) * mMaxNumberOfCpus);\r
529a5a86
MK
895 ASSERT (mLastPFEntryValue != NULL);\r
896 mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (\r
bb767506 897 sizeof (mLastPFEntryPointer[0]) * mMaxNumberOfCpus);\r
529a5a86
MK
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
bb767506 938 mMsrDsArea = (MSR_DS_AREA_STRUCT **)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT *) * mMaxNumberOfCpus);\r
529a5a86 939 ASSERT (mMsrDsArea != NULL);\r
bb767506 940 mMsrBTSRecord = (BRANCH_TRACE_RECORD **)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD *) * mMaxNumberOfCpus);\r
529a5a86 941 ASSERT (mMsrBTSRecord != NULL);\r
bb767506 942 mMsrPEBSRecord = (PEBS_RECORD **)AllocateZeroPool (sizeof (PEBS_RECORD *) * mMaxNumberOfCpus);\r
529a5a86
MK
943 ASSERT (mMsrPEBSRecord != NULL);\r
944\r
945 mMsrDsAreaBase = (MSR_DS_AREA_STRUCT *)((UINTN)Base + mSmmProfileSize);\r
bb767506 946 MsrDsAreaSizePerCpu = mMsrDsAreaSize / mMaxNumberOfCpus;\r
529a5a86 947 mBTSRecordNumber = (MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER - sizeof(MSR_DS_AREA_STRUCT)) / sizeof(BRANCH_TRACE_RECORD);\r
bb767506 948 for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
529a5a86
MK
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
51773d49 1004 VOID\r
529a5a86
MK
1005 )\r
1006{\r
f85d3ce2
JF
1007 UINT32 RegEax;\r
1008 UINT32 RegEdx;\r
1009 MSR_IA32_MISC_ENABLE_REGISTER MiscEnableMsr;\r
529a5a86
MK
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
3c5ce64f 1018 PatchInstructionX86 (gPatchXdSupported, mXdSupported, 1);\r
529a5a86
MK
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
3c5ce64f 1027 PatchInstructionX86 (gPatchXdSupported, mXdSupported, 1);\r
529a5a86
MK
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
f85d3ce2
JF
1042 MiscEnableMsr.Uint64 = AsmReadMsr64 (MSR_IA32_MISC_ENABLE);\r
1043 if (MiscEnableMsr.Bits.BTS == 1) {\r
529a5a86 1044 //\r
f85d3ce2 1045 // BTS facilities is not supported if MSR_IA32_MISC_ENABLE.BTS bit is set.\r
529a5a86
MK
1046 //\r
1047 mBtsSupported = FALSE;\r
1048 }\r
1049 }\r
1050 }\r
1051}\r
1052\r
529a5a86
MK
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
529a5a86
MK
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
881520ea 1257 PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;\r
529a5a86
MK
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
881520ea 1290 PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;\r
529a5a86
MK
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
890d9ee5
JW
1312 UINT64 RestoreAddress;\r
1313 UINTN RestorePageNumber;\r
529a5a86
MK
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
529a5a86
MK
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
890d9ee5
JW
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
529a5a86
MK
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
529a5a86
MK
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
529a5a86
MK
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
5c88af79
JF
1456 EFI_STATUS Status;\r
1457\r
1458 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_DEBUG, DebugExceptionHandler);\r
1459 ASSERT_EFI_ERROR (Status);\r
529a5a86 1460}\r