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