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