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