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