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