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