1 /** @file
2   Intel Processor Trace feature.
3 
4   Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "CpuCommonFeatures.h"
10 
11 ///
12 /// This macro define the max entries in the Topa table.
13 /// Each entry in the table contains some attribute bits, a pointer to an output region, and the size of the region.
14 /// The last entry in the table may hold a pointer to the next table. This pointer can either point to the top of the
15 /// current table (for circular array) or to the base of another table.
16 /// At least 2 entries are needed because the list of entries must
17 /// be terminated by an entry with the END bit set to 1, so 2
18 /// entries are required to use a single valid entry.
19 ///
20 #define MAX_TOPA_ENTRY_COUNT         2
21 
22 
23 ///
24 /// Processor trace output scheme selection.
25 ///
26 typedef enum {
27   RtitOutputSchemeSingleRange = 0,
28   RtitOutputSchemeToPA
29 } RTIT_OUTPUT_SCHEME;
30 
31 typedef struct  {
32   BOOLEAN                                   TopaSupported;
33   BOOLEAN                                   SingleRangeSupported;
34   MSR_IA32_RTIT_CTL_REGISTER                RtitCtrl;
35   MSR_IA32_RTIT_OUTPUT_BASE_REGISTER        RtitOutputBase;
36   MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER   RtitOutputMaskPtrs;
37 } PROC_TRACE_PROCESSOR_DATA;
38 
39 typedef struct  {
40   UINT32                      NumberOfProcessors;
41 
42   UINT8                       ProcTraceOutputScheme;
43   UINT32                      ProcTraceMemSize;
44 
45   UINTN                       *ThreadMemRegionTable;
46   UINTN                       AllocatedThreads;
47 
48   UINTN                       *TopaMemArray;
49 
50   PROC_TRACE_PROCESSOR_DATA   *ProcessorData;
51 } PROC_TRACE_DATA;
52 
53 typedef struct {
54   RTIT_TOPA_TABLE_ENTRY    TopaEntry[MAX_TOPA_ENTRY_COUNT];
55 } PROC_TRACE_TOPA_TABLE;
56 
57 /**
58   Prepares for the data used by CPU feature detection and initialization.
59 
60   @param[in]  NumberOfProcessors  The number of CPUs in the platform.
61 
62   @return  Pointer to a buffer of CPU related configuration data.
63 
64   @note This service could be called by BSP only.
65 **/
66 VOID *
67 EFIAPI
ProcTraceGetConfigData(IN UINTN NumberOfProcessors)68 ProcTraceGetConfigData (
69   IN UINTN  NumberOfProcessors
70   )
71 {
72   PROC_TRACE_DATA  *ConfigData;
73 
74   ConfigData = AllocateZeroPool (sizeof (PROC_TRACE_DATA) + sizeof (PROC_TRACE_PROCESSOR_DATA) * NumberOfProcessors);
75   ASSERT (ConfigData != NULL);
76   ConfigData->ProcessorData = (PROC_TRACE_PROCESSOR_DATA *) ((UINT8*) ConfigData + sizeof (PROC_TRACE_DATA));
77 
78   ConfigData->NumberOfProcessors = (UINT32) NumberOfProcessors;
79   ConfigData->ProcTraceMemSize = PcdGet32 (PcdCpuProcTraceMemSize);
80   ConfigData->ProcTraceOutputScheme = PcdGet8 (PcdCpuProcTraceOutputScheme);
81 
82   return ConfigData;
83 }
84 
85 /**
86   Detects if Intel Processor Trace feature supported on current
87   processor.
88 
89   @param[in]  ProcessorNumber  The index of the CPU executing this function.
90   @param[in]  CpuInfo          A pointer to the REGISTER_CPU_FEATURE_INFORMATION
91                                structure for the CPU executing this function.
92   @param[in]  ConfigData       A pointer to the configuration buffer returned
93                                by CPU_FEATURE_GET_CONFIG_DATA.  NULL if
94                                CPU_FEATURE_GET_CONFIG_DATA was not provided in
95                                RegisterCpuFeature().
96 
97   @retval TRUE     Processor Trace feature is supported.
98   @retval FALSE    Processor Trace feature is not supported.
99 
100   @note This service could be called by BSP/APs.
101 **/
102 BOOLEAN
103 EFIAPI
ProcTraceSupport(IN UINTN ProcessorNumber,IN REGISTER_CPU_FEATURE_INFORMATION * CpuInfo,IN VOID * ConfigData OPTIONAL)104 ProcTraceSupport (
105   IN UINTN                             ProcessorNumber,
106   IN REGISTER_CPU_FEATURE_INFORMATION  *CpuInfo,
107   IN VOID                              *ConfigData  OPTIONAL
108   )
109 {
110   PROC_TRACE_DATA                             *ProcTraceData;
111   CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX Ebx;
112   CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_ECX   Ecx;
113 
114   //
115   // Check if ProcTraceMemorySize option is enabled (0xFF means disable by user)
116   //
117   ProcTraceData = (PROC_TRACE_DATA *) ConfigData;
118   ASSERT (ProcTraceData != NULL);
119   if ((ProcTraceData->ProcTraceMemSize > RtitTopaMemorySize128M) ||
120       (ProcTraceData->ProcTraceOutputScheme > RtitOutputSchemeToPA)) {
121     return FALSE;
122   }
123 
124   //
125   // Check if Processor Trace is supported
126   //
127   AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, NULL, &Ebx.Uint32, NULL, NULL);
128   if (Ebx.Bits.IntelProcessorTrace == 0) {
129     return FALSE;
130   }
131 
132   AsmCpuidEx (CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF, NULL, NULL, &Ecx.Uint32, NULL);
133   ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported = (BOOLEAN) (Ecx.Bits.RTIT == 1);
134   ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported = (BOOLEAN) (Ecx.Bits.SingleRangeOutput == 1);
135   if ((ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA)) ||
136       (ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange))) {
137     ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_CTL);
138     ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_BASE);
139     ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_MASK_PTRS);
140     return TRUE;
141   }
142 
143   return FALSE;
144 }
145 
146 /**
147   Initializes Intel Processor Trace feature to specific state.
148 
149   @param[in]  ProcessorNumber  The index of the CPU executing this function.
150   @param[in]  CpuInfo          A pointer to the REGISTER_CPU_FEATURE_INFORMATION
151                                structure for the CPU executing this function.
152   @param[in]  ConfigData       A pointer to the configuration buffer returned
153                                by CPU_FEATURE_GET_CONFIG_DATA.  NULL if
154                                CPU_FEATURE_GET_CONFIG_DATA was not provided in
155                                RegisterCpuFeature().
156   @param[in]  State            If TRUE, then the Processor Trace feature must be
157                                enabled.
158                                If FALSE, then the Processor Trace feature must be
159                                disabled.
160 
161   @retval RETURN_SUCCESS       Intel Processor Trace feature is initialized.
162 
163 **/
164 RETURN_STATUS
165 EFIAPI
ProcTraceInitialize(IN UINTN ProcessorNumber,IN REGISTER_CPU_FEATURE_INFORMATION * CpuInfo,IN VOID * ConfigData,OPTIONAL IN BOOLEAN State)166 ProcTraceInitialize (
167   IN UINTN                             ProcessorNumber,
168   IN REGISTER_CPU_FEATURE_INFORMATION  *CpuInfo,
169   IN VOID                              *ConfigData,  OPTIONAL
170   IN BOOLEAN                           State
171   )
172 {
173   UINT32                               MemRegionSize;
174   UINTN                                Pages;
175   UINTN                                Alignment;
176   UINTN                                MemRegionBaseAddr;
177   UINTN                                *ThreadMemRegionTable;
178   UINTN                                Index;
179   UINTN                                TopaTableBaseAddr;
180   UINTN                                AlignedAddress;
181   UINTN                                *TopaMemArray;
182   PROC_TRACE_TOPA_TABLE                *TopaTable;
183   PROC_TRACE_DATA                      *ProcTraceData;
184   BOOLEAN                              FirstIn;
185   MSR_IA32_RTIT_CTL_REGISTER           CtrlReg;
186   MSR_IA32_RTIT_STATUS_REGISTER        StatusReg;
187   MSR_IA32_RTIT_OUTPUT_BASE_REGISTER   OutputBaseReg;
188   MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER  OutputMaskPtrsReg;
189   RTIT_TOPA_TABLE_ENTRY                *TopaEntryPtr;
190 
191   //
192   // The scope of the MSR_IA32_RTIT_* is core for below processor type, only program
193   // MSR_IA32_RTIT_* for thread 0 in each core.
194   //
195   if (IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) ||
196       IS_GOLDMONT_PLUS_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) {
197     if (CpuInfo->ProcessorInfo.Location.Thread != 0) {
198       return RETURN_SUCCESS;
199     }
200   }
201 
202   ProcTraceData = (PROC_TRACE_DATA *) ConfigData;
203   ASSERT (ProcTraceData != NULL);
204 
205   //
206   // Clear MSR_IA32_RTIT_CTL[0] and IA32_RTIT_STS only if MSR_IA32_RTIT_CTL[0]==1b
207   //
208   CtrlReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64;
209   if (CtrlReg.Bits.TraceEn != 0) {
210     ///
211     /// Clear bit 0 in MSR IA32_RTIT_CTL (570)
212     ///
213     CtrlReg.Bits.TraceEn = 0;
214     CPU_REGISTER_TABLE_WRITE64 (
215       ProcessorNumber,
216       Msr,
217       MSR_IA32_RTIT_CTL,
218       CtrlReg.Uint64
219       );
220 
221     ///
222     /// Clear MSR IA32_RTIT_STS (571h) to all zeros
223     ///
224     StatusReg.Uint64 = 0x0;
225     CPU_REGISTER_TABLE_WRITE64 (
226       ProcessorNumber,
227       Msr,
228       MSR_IA32_RTIT_STATUS,
229       StatusReg.Uint64
230       );
231   }
232 
233   if (!State) {
234     return RETURN_SUCCESS;
235   }
236 
237   MemRegionBaseAddr = 0;
238   FirstIn = FALSE;
239 
240   if (ProcTraceData->ThreadMemRegionTable == NULL) {
241     FirstIn = TRUE;
242     DEBUG ((DEBUG_INFO, "Initialize Processor Trace\n"));
243   }
244 
245   ///
246   /// Refer to PROC_TRACE_MEM_SIZE Table for Size Encoding
247   ///
248   MemRegionSize = (UINT32) (1 << (ProcTraceData->ProcTraceMemSize + 12));
249   if (FirstIn) {
250     DEBUG ((DEBUG_INFO, "ProcTrace: MemSize requested: 0x%X \n", MemRegionSize));
251   }
252 
253   if (FirstIn) {
254     //
255     //   Let BSP allocate and create the necessary memory region (Aligned to the size of
256     //   the memory region from setup option(ProcTraceMemSize) which is an integral multiple of 4kB)
257     //   for all the enabled threads to store Processor Trace debug data. Then Configure the trace
258     //   address base in MSR, IA32_RTIT_OUTPUT_BASE (560h) bits 47:12. Note that all regions must be
259     //   aligned based on their size, not just 4K. Thus a 2M region must have bits 20:12 cleared.
260     //
261     ThreadMemRegionTable = (UINTN *) AllocatePool (ProcTraceData->NumberOfProcessors * sizeof (UINTN *));
262     if (ThreadMemRegionTable == NULL) {
263       DEBUG ((DEBUG_ERROR, "Allocate ProcTrace ThreadMemRegionTable Failed\n"));
264       return RETURN_OUT_OF_RESOURCES;
265     }
266     ProcTraceData->ThreadMemRegionTable = ThreadMemRegionTable;
267 
268     for (Index = 0; Index < ProcTraceData->NumberOfProcessors; Index++, ProcTraceData->AllocatedThreads++) {
269       Pages = EFI_SIZE_TO_PAGES (MemRegionSize);
270       Alignment = MemRegionSize;
271       AlignedAddress = (UINTN) AllocateAlignedReservedPages (Pages, Alignment);
272       if (AlignedAddress == 0) {
273         DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, allocated only for %d threads\n", ProcTraceData->AllocatedThreads));
274         if (Index == 0) {
275           //
276           // Could not allocate for BSP even
277           //
278           FreePool ((VOID *) ThreadMemRegionTable);
279           ThreadMemRegionTable = NULL;
280           return RETURN_OUT_OF_RESOURCES;
281         }
282         break;
283       }
284 
285       ThreadMemRegionTable[Index] = AlignedAddress;
286       DEBUG ((DEBUG_INFO, "ProcTrace: PT MemRegionBaseAddr(aligned) for thread %d: 0x%llX \n", Index, (UINT64) ThreadMemRegionTable[Index]));
287     }
288 
289     DEBUG ((DEBUG_INFO, "ProcTrace: Allocated PT mem for %d thread \n", ProcTraceData->AllocatedThreads));
290   }
291 
292   if (ProcessorNumber < ProcTraceData->AllocatedThreads) {
293     MemRegionBaseAddr = ProcTraceData->ThreadMemRegionTable[ProcessorNumber];
294   } else {
295     return RETURN_SUCCESS;
296   }
297 
298   ///
299   /// Check Processor Trace output scheme: Single Range output or ToPA table
300   ///
301 
302   //
303   //  Single Range output scheme
304   //
305   if (ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported &&
306       (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange)) {
307     if (FirstIn) {
308       DEBUG ((DEBUG_INFO, "ProcTrace: Enabling Single Range Output scheme \n"));
309     }
310 
311     //
312     // Clear MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8)
313     //
314     CtrlReg.Bits.ToPA = 0;
315     CPU_REGISTER_TABLE_WRITE64 (
316       ProcessorNumber,
317       Msr,
318       MSR_IA32_RTIT_CTL,
319       CtrlReg.Uint64
320       );
321 
322     //
323     // Program MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with the allocated Memory Region
324     //
325     OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64;
326     OutputBaseReg.Bits.Base = (MemRegionBaseAddr >> 7) & 0x01FFFFFF;
327     OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64) MemRegionBaseAddr, 32) & 0xFFFFFFFF;
328     CPU_REGISTER_TABLE_WRITE64 (
329       ProcessorNumber,
330       Msr,
331       MSR_IA32_RTIT_OUTPUT_BASE,
332       OutputBaseReg.Uint64
333       );
334 
335     //
336     // Program the Mask bits for the Memory Region to MSR IA32_RTIT_OUTPUT_MASK_PTRS (561h)
337     //
338     OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64;
339     OutputMaskPtrsReg.Bits.MaskOrTableOffset = ((MemRegionSize - 1) >> 7) & 0x01FFFFFF;
340     OutputMaskPtrsReg.Bits.OutputOffset = RShiftU64 (MemRegionSize - 1, 32) & 0xFFFFFFFF;
341     CPU_REGISTER_TABLE_WRITE64 (
342       ProcessorNumber,
343       Msr,
344       MSR_IA32_RTIT_OUTPUT_MASK_PTRS,
345       OutputMaskPtrsReg.Uint64
346       );
347   }
348 
349   //
350   //  ToPA(Table of physical address) scheme
351   //
352   if (ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported &&
353       (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA)) {
354     //
355     //  Create ToPA structure aligned at 4KB for each logical thread
356     //  with at least 2 entries by 8 bytes size each. The first entry
357     //  should have the trace output base address in bits 47:12, 6:9
358     //  for Size, bits 4,2 and 0 must be cleared. The second entry
359     //  should have the base address of the table location in bits
360     //  47:12, bits 4 and 2 must be cleared and bit 0 must be set.
361     //
362     if (FirstIn) {
363       DEBUG ((DEBUG_INFO, "ProcTrace: Enabling ToPA scheme \n"));
364       //
365       // Let BSP allocate ToPA table mem for all threads
366       //
367       TopaMemArray = (UINTN *) AllocatePool (ProcTraceData->AllocatedThreads * sizeof (UINTN *));
368       if (TopaMemArray == NULL) {
369         DEBUG ((DEBUG_ERROR, "ProcTrace: Allocate mem for ToPA Failed\n"));
370         return RETURN_OUT_OF_RESOURCES;
371       }
372       ProcTraceData->TopaMemArray = TopaMemArray;
373 
374       for (Index = 0; Index < ProcTraceData->AllocatedThreads; Index++) {
375         Pages = EFI_SIZE_TO_PAGES (sizeof (PROC_TRACE_TOPA_TABLE));
376         Alignment = 0x1000;
377         AlignedAddress = (UINTN) AllocateAlignedReservedPages (Pages, Alignment);
378         if (AlignedAddress == 0) {
379           if (Index < ProcTraceData->AllocatedThreads) {
380             ProcTraceData->AllocatedThreads = Index;
381           }
382           DEBUG ((DEBUG_ERROR, "ProcTrace:  Out of mem, allocated ToPA mem only for %d threads\n", ProcTraceData->AllocatedThreads));
383           if (Index == 0) {
384             //
385             // Could not allocate for BSP even
386             //
387             FreePool ((VOID *) TopaMemArray);
388             TopaMemArray = NULL;
389             return RETURN_OUT_OF_RESOURCES;
390           }
391           break;
392         }
393 
394         TopaMemArray[Index] = AlignedAddress;
395         DEBUG ((DEBUG_INFO, "ProcTrace: Topa table address(aligned) for thread %d is 0x%llX \n", Index,  (UINT64) TopaMemArray[Index]));
396       }
397 
398       DEBUG ((DEBUG_INFO, "ProcTrace: Allocated ToPA mem for %d thread \n", ProcTraceData->AllocatedThreads));
399     }
400 
401     if (ProcessorNumber < ProcTraceData->AllocatedThreads) {
402       TopaTableBaseAddr = ProcTraceData->TopaMemArray[ProcessorNumber];
403     } else {
404       return RETURN_SUCCESS;
405     }
406 
407     TopaTable = (PROC_TRACE_TOPA_TABLE *) TopaTableBaseAddr;
408     TopaEntryPtr = &TopaTable->TopaEntry[0];
409     TopaEntryPtr->Uint64 = 0;
410     TopaEntryPtr->Bits.Base = (MemRegionBaseAddr >> 12) & 0x000FFFFF;
411     TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64) MemRegionBaseAddr, 32) & 0xFFFFFFFF;
412     TopaEntryPtr->Bits.Size = ProcTraceData->ProcTraceMemSize;
413     TopaEntryPtr->Bits.END = 0;
414 
415     TopaEntryPtr = &TopaTable->TopaEntry[1];
416     TopaEntryPtr->Uint64 = 0;
417     TopaEntryPtr->Bits.Base = (TopaTableBaseAddr >> 12) & 0x000FFFFF;
418     TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64) TopaTableBaseAddr, 32) & 0xFFFFFFFF;
419     TopaEntryPtr->Bits.END = 1;
420 
421     //
422     // Program the MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with ToPA base
423     //
424     OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64;
425     OutputBaseReg.Bits.Base = (TopaTableBaseAddr >> 7) & 0x01FFFFFF;
426     OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64) TopaTableBaseAddr, 32) & 0xFFFFFFFF;
427     CPU_REGISTER_TABLE_WRITE64 (
428       ProcessorNumber,
429       Msr,
430       MSR_IA32_RTIT_OUTPUT_BASE,
431       OutputBaseReg.Uint64
432       );
433 
434     //
435     // Set the MSR IA32_RTIT_OUTPUT_MASK (0x561) bits[63:7] to 0
436     //
437     OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64;
438     OutputMaskPtrsReg.Bits.MaskOrTableOffset = 0;
439     OutputMaskPtrsReg.Bits.OutputOffset = 0;
440     CPU_REGISTER_TABLE_WRITE64 (
441       ProcessorNumber,
442       Msr,
443       MSR_IA32_RTIT_OUTPUT_MASK_PTRS,
444       OutputMaskPtrsReg.Uint64
445       );
446     //
447     // Enable ToPA output scheme by enabling MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8)
448     //
449     CtrlReg.Bits.ToPA = 1;
450     CPU_REGISTER_TABLE_WRITE64 (
451       ProcessorNumber,
452       Msr,
453       MSR_IA32_RTIT_CTL,
454       CtrlReg.Uint64
455       );
456   }
457 
458   ///
459   /// Enable the Processor Trace feature from MSR IA32_RTIT_CTL (570h)
460   ///
461   CtrlReg.Bits.OS = 1;
462   CtrlReg.Bits.User = 1;
463   CtrlReg.Bits.BranchEn = 1;
464   CtrlReg.Bits.TraceEn = 1;
465   CPU_REGISTER_TABLE_WRITE64 (
466     ProcessorNumber,
467     Msr,
468     MSR_IA32_RTIT_CTL,
469     CtrlReg.Uint64
470     );
471 
472   return RETURN_SUCCESS;
473 }
474