blob: a4510eb8023163aae434b9d37b281e2579a55e3b [file] [log] [blame]
/** @file
Intel Processor Trace feature.
Copyright (c) 2017 - 2023, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "CpuCommonFeatures.h"
///
/// This macro define the max entries in the Topa table.
/// Each entry in the table contains some attribute bits, a pointer to an output region, and the size of the region.
/// The last entry in the table may hold a pointer to the next table. This pointer can either point to the top of the
/// current table (for circular array) or to the base of another table.
/// At least 2 entries are needed because the list of entries must
/// be terminated by an entry with the END bit set to 1, so 2
/// entries are required to use a single valid entry.
///
#define MAX_TOPA_ENTRY_COUNT 2
///
/// Processor trace output scheme selection.
///
typedef enum {
RtitOutputSchemeSingleRange = 0,
RtitOutputSchemeToPA
} RTIT_OUTPUT_SCHEME;
typedef struct {
BOOLEAN TopaSupported;
BOOLEAN SingleRangeSupported;
MSR_IA32_RTIT_CTL_REGISTER RtitCtrl;
MSR_IA32_RTIT_OUTPUT_BASE_REGISTER RtitOutputBase;
MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER RtitOutputMaskPtrs;
BOOLEAN CycPacketSupported;
} PROC_TRACE_PROCESSOR_DATA;
typedef struct {
UINT32 NumberOfProcessors;
UINT8 ProcTraceOutputScheme;
UINT32 ProcTraceMemSize;
UINTN *ThreadMemRegionTable;
UINTN AllocatedThreads;
UINTN *TopaMemArray;
BOOLEAN EnableOnBspOnly;
BOOLEAN EnablePerformanceCollecting;
PROC_TRACE_PROCESSOR_DATA *ProcessorData;
} PROC_TRACE_DATA;
typedef struct {
RTIT_TOPA_TABLE_ENTRY TopaEntry[MAX_TOPA_ENTRY_COUNT];
} PROC_TRACE_TOPA_TABLE;
/**
Prepares for the data used by CPU feature detection and initialization.
@param[in] NumberOfProcessors The number of CPUs in the platform.
@return Pointer to a buffer of CPU related configuration data.
@note This service could be called by BSP only.
**/
VOID *
EFIAPI
ProcTraceGetConfigData (
IN UINTN NumberOfProcessors
)
{
PROC_TRACE_DATA *ConfigData;
ConfigData = AllocateZeroPool (sizeof (PROC_TRACE_DATA) + sizeof (PROC_TRACE_PROCESSOR_DATA) * NumberOfProcessors);
ASSERT (ConfigData != NULL);
ConfigData->ProcessorData = (PROC_TRACE_PROCESSOR_DATA *)((UINT8 *)ConfigData + sizeof (PROC_TRACE_DATA));
ConfigData->NumberOfProcessors = (UINT32)NumberOfProcessors;
ConfigData->ProcTraceMemSize = PcdGet32 (PcdCpuProcTraceMemSize);
ConfigData->ProcTraceOutputScheme = PcdGet8 (PcdCpuProcTraceOutputScheme);
ConfigData->EnableOnBspOnly = PcdGetBool (PcdCpuProcTraceBspOnly);
ConfigData->EnablePerformanceCollecting = PcdGetBool (PcdCpuProcTracePerformanceCollecting);
return ConfigData;
}
/**
Detects if Intel Processor Trace feature supported on current
processor.
@param[in] ProcessorNumber The index of the CPU executing this function.
@param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION
structure for the CPU executing this function.
@param[in] ConfigData A pointer to the configuration buffer returned
by CPU_FEATURE_GET_CONFIG_DATA. NULL if
CPU_FEATURE_GET_CONFIG_DATA was not provided in
RegisterCpuFeature().
@retval TRUE Processor Trace feature is supported.
@retval FALSE Processor Trace feature is not supported.
@note This service could be called by BSP/APs.
**/
BOOLEAN
EFIAPI
ProcTraceSupport (
IN UINTN ProcessorNumber,
IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo,
IN VOID *ConfigData OPTIONAL
)
{
PROC_TRACE_DATA *ProcTraceData;
CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX Ebx;
CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_ECX ProcTraceEcx;
CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_EBX ProcTraceEbx;
//
// Check if ProcTraceMemorySize option is enabled (0xFF means disable by user)
//
ProcTraceData = (PROC_TRACE_DATA *)ConfigData;
ASSERT (ProcTraceData != NULL);
if ((ProcTraceData->ProcTraceMemSize > RtitTopaMemorySize128M) ||
(ProcTraceData->ProcTraceOutputScheme > RtitOutputSchemeToPA))
{
return FALSE;
}
//
// Check if Processor Trace is supported
//
AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, NULL, &Ebx.Uint32, NULL, NULL);
if (Ebx.Bits.IntelProcessorTrace == 0) {
return FALSE;
}
AsmCpuidEx (CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF, NULL, &ProcTraceEbx.Uint32, &ProcTraceEcx.Uint32, NULL);
ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported = (BOOLEAN)(ProcTraceEcx.Bits.RTIT == 1);
ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported = (BOOLEAN)(ProcTraceEcx.Bits.SingleRangeOutput == 1);
if ((ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA)) ||
(ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange)))
{
ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_CTL);
ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_BASE);
ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_MASK_PTRS);
ProcTraceData->ProcessorData[ProcessorNumber].CycPacketSupported = (BOOLEAN)(ProcTraceEbx.Bits.ConfigurablePsb == 1);
return TRUE;
}
return FALSE;
}
/**
Initializes Intel Processor Trace feature to specific state.
@param[in] ProcessorNumber The index of the CPU executing this function.
@param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION
structure for the CPU executing this function.
@param[in] ConfigData A pointer to the configuration buffer returned
by CPU_FEATURE_GET_CONFIG_DATA. NULL if
CPU_FEATURE_GET_CONFIG_DATA was not provided in
RegisterCpuFeature().
@param[in] State If TRUE, then the Processor Trace feature must be
enabled.
If FALSE, then the Processor Trace feature must be
disabled.
@retval RETURN_SUCCESS Intel Processor Trace feature is initialized.
**/
RETURN_STATUS
EFIAPI
ProcTraceInitialize (
IN UINTN ProcessorNumber,
IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo,
IN VOID *ConfigData OPTIONAL,
IN BOOLEAN State
)
{
UINT32 MemRegionSize;
UINTN Pages;
UINTN Alignment;
UINTN MemRegionBaseAddr;
UINTN *ThreadMemRegionTable;
UINTN Index;
UINTN TopaTableBaseAddr;
UINTN AlignedAddress;
UINTN *TopaMemArray;
PROC_TRACE_TOPA_TABLE *TopaTable;
PROC_TRACE_DATA *ProcTraceData;
BOOLEAN FirstIn;
MSR_IA32_RTIT_CTL_REGISTER CtrlReg;
MSR_IA32_RTIT_STATUS_REGISTER StatusReg;
MSR_IA32_RTIT_OUTPUT_BASE_REGISTER OutputBaseReg;
MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER OutputMaskPtrsReg;
RTIT_TOPA_TABLE_ENTRY *TopaEntryPtr;
BOOLEAN IsBsp;
//
// The scope of the MSR_IA32_RTIT_* is core for below processor type, only program
// MSR_IA32_RTIT_* for thread 0 in each core.
//
if (IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) ||
IS_GOLDMONT_PLUS_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel))
{
if (CpuInfo->ProcessorInfo.Location.Thread != 0) {
return RETURN_SUCCESS;
}
}
ProcTraceData = (PROC_TRACE_DATA *)ConfigData;
ASSERT (ProcTraceData != NULL);
//
// Clear MSR_IA32_RTIT_CTL[0] and IA32_RTIT_STS only if MSR_IA32_RTIT_CTL[0]==1b
//
CtrlReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64;
if (CtrlReg.Bits.TraceEn != 0) {
///
/// Clear bit 0 in MSR IA32_RTIT_CTL (570)
///
CtrlReg.Bits.TraceEn = 0;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_CTL,
CtrlReg.Uint64
);
///
/// Clear MSR IA32_RTIT_STS (571h) to all zeros
///
StatusReg.Uint64 = 0x0;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_STATUS,
StatusReg.Uint64
);
}
if (!State) {
return RETURN_SUCCESS;
}
IsBsp = (CpuInfo->ProcessorInfo.StatusFlag & PROCESSOR_AS_BSP_BIT) ? TRUE : FALSE;
if (ProcTraceData->EnableOnBspOnly && !IsBsp) {
return RETURN_SUCCESS;
}
MemRegionBaseAddr = 0;
FirstIn = FALSE;
if (ProcTraceData->ThreadMemRegionTable == NULL) {
FirstIn = TRUE;
DEBUG ((DEBUG_INFO, "Initialize Processor Trace\n"));
}
///
/// Refer to PROC_TRACE_MEM_SIZE Table for Size Encoding
///
MemRegionSize = (UINT32)(1 << (ProcTraceData->ProcTraceMemSize + 12));
if (FirstIn) {
DEBUG ((DEBUG_INFO, "ProcTrace: MemSize requested: 0x%X \n", MemRegionSize));
}
if (FirstIn) {
//
// Let BSP allocate and create the necessary memory region (Aligned to the size of
// the memory region from setup option(ProcTraceMemSize) which is an integral multiple of 4kB)
// for all the enabled threads to store Processor Trace debug data. Then Configure the trace
// address base in MSR, IA32_RTIT_OUTPUT_BASE (560h) bits 47:12. Note that all regions must be
// aligned based on their size, not just 4K. Thus a 2M region must have bits 20:12 cleared.
//
Pages = EFI_SIZE_TO_PAGES (MemRegionSize);
Alignment = MemRegionSize;
if (ProcTraceData->EnableOnBspOnly) {
//
// When only enable ProcTrace on BSP, this is the first and only time ProcTraceInitialize() runs.
//
MemRegionBaseAddr = (UINTN)AllocateAlignedReservedPages (Pages, Alignment);
if (MemRegionBaseAddr == 0) {
//
// Could not allocate for BSP even
//
DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, failed to allocate buffer for BSP\n"));
return RETURN_OUT_OF_RESOURCES;
}
DEBUG ((DEBUG_INFO, "ProcTrace: Allocated PT MemRegionBaseAddr(aligned) for BSP only: 0x%llX.\n", (UINT64)MemRegionBaseAddr));
} else {
ThreadMemRegionTable = (UINTN *)AllocatePool (ProcTraceData->NumberOfProcessors * sizeof (UINTN *));
if (ThreadMemRegionTable == NULL) {
DEBUG ((DEBUG_ERROR, "Allocate ProcTrace ThreadMemRegionTable Failed\n"));
return RETURN_OUT_OF_RESOURCES;
}
ProcTraceData->ThreadMemRegionTable = ThreadMemRegionTable;
for (Index = 0; Index < ProcTraceData->NumberOfProcessors; Index++, ProcTraceData->AllocatedThreads++) {
AlignedAddress = (UINTN)AllocateAlignedReservedPages (Pages, Alignment);
if (AlignedAddress == 0) {
DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, allocated only for %d threads\n", ProcTraceData->AllocatedThreads));
if (Index == 0) {
//
// Could not allocate for BSP even
//
FreePool ((VOID *)ThreadMemRegionTable);
ThreadMemRegionTable = NULL;
return RETURN_OUT_OF_RESOURCES;
}
break;
}
ThreadMemRegionTable[Index] = AlignedAddress;
DEBUG ((DEBUG_INFO, "ProcTrace: PT MemRegionBaseAddr(aligned) for thread %d: 0x%llX \n", Index, (UINT64)ThreadMemRegionTable[Index]));
}
DEBUG ((DEBUG_INFO, "ProcTrace: Allocated PT mem for %d thread \n", ProcTraceData->AllocatedThreads));
}
}
if (!ProcTraceData->EnableOnBspOnly) {
if (ProcessorNumber < ProcTraceData->AllocatedThreads) {
MemRegionBaseAddr = ProcTraceData->ThreadMemRegionTable[ProcessorNumber];
} else {
return RETURN_SUCCESS;
}
}
///
/// Check Processor Trace output scheme: Single Range output or ToPA table
///
//
// Single Range output scheme
//
if (ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported &&
(ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange))
{
if (FirstIn) {
DEBUG ((DEBUG_INFO, "ProcTrace: Enabling Single Range Output scheme \n"));
}
//
// Clear MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8)
//
CtrlReg.Bits.ToPA = 0;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_CTL,
CtrlReg.Uint64
);
//
// Program MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with the allocated Memory Region
//
OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64;
OutputBaseReg.Bits.Base = (MemRegionBaseAddr >> 7) & 0x01FFFFFF;
OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64)MemRegionBaseAddr, 32) & 0xFFFFFFFF;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_OUTPUT_BASE,
OutputBaseReg.Uint64
);
//
// Program the Mask bits for the Memory Region to MSR IA32_RTIT_OUTPUT_MASK_PTRS (561h)
//
OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64;
OutputMaskPtrsReg.Bits.MaskOrTableOffset = ((MemRegionSize - 1) >> 7) & 0x01FFFFFF;
OutputMaskPtrsReg.Bits.OutputOffset = RShiftU64 (MemRegionSize - 1, 32) & 0xFFFFFFFF;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_OUTPUT_MASK_PTRS,
OutputMaskPtrsReg.Uint64
);
}
//
// ToPA(Table of physical address) scheme
//
if (ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported &&
(ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA))
{
//
// Create ToPA structure aligned at 4KB for each logical thread
// with at least 2 entries by 8 bytes size each. The first entry
// should have the trace output base address in bits 47:12, 6:9
// for Size, bits 4,2 and 0 must be cleared. The second entry
// should have the base address of the table location in bits
// 47:12, bits 4 and 2 must be cleared and bit 0 must be set.
//
if (FirstIn) {
DEBUG ((DEBUG_INFO, "ProcTrace: Enabling ToPA scheme \n"));
Pages = EFI_SIZE_TO_PAGES (sizeof (PROC_TRACE_TOPA_TABLE));
Alignment = 0x1000;
if (ProcTraceData->EnableOnBspOnly) {
//
// When only enable ProcTrace on BSP, this is the first and only time ProcTraceInitialize() runs.
//
TopaTableBaseAddr = (UINTN)AllocateAlignedReservedPages (Pages, Alignment);
if (TopaTableBaseAddr == 0) {
DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, failed to allocate ToPA mem for BSP"));
return RETURN_OUT_OF_RESOURCES;
}
DEBUG ((DEBUG_INFO, "ProcTrace: Topa table address(aligned) for BSP only: 0x%llX \n", (UINT64)TopaTableBaseAddr));
} else {
//
// Let BSP allocate ToPA table mem for all threads
//
TopaMemArray = (UINTN *)AllocatePool (ProcTraceData->AllocatedThreads * sizeof (UINTN *));
if (TopaMemArray == NULL) {
DEBUG ((DEBUG_ERROR, "ProcTrace: Allocate mem for ToPA Failed\n"));
return RETURN_OUT_OF_RESOURCES;
}
ProcTraceData->TopaMemArray = TopaMemArray;
for (Index = 0; Index < ProcTraceData->AllocatedThreads; Index++) {
AlignedAddress = (UINTN)AllocateAlignedReservedPages (Pages, Alignment);
if (AlignedAddress == 0) {
if (Index < ProcTraceData->AllocatedThreads) {
ProcTraceData->AllocatedThreads = Index;
}
DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, allocated ToPA mem only for %d threads\n", ProcTraceData->AllocatedThreads));
if (Index == 0) {
//
// Could not allocate for BSP even
//
FreePool ((VOID *)TopaMemArray);
TopaMemArray = NULL;
return RETURN_OUT_OF_RESOURCES;
}
break;
}
TopaMemArray[Index] = AlignedAddress;
DEBUG ((DEBUG_INFO, "ProcTrace: Topa table address(aligned) for thread %d is 0x%llX \n", Index, (UINT64)TopaMemArray[Index]));
}
DEBUG ((DEBUG_INFO, "ProcTrace: Allocated ToPA mem for %d thread \n", ProcTraceData->AllocatedThreads));
}
}
if (!ProcTraceData->EnableOnBspOnly) {
if (ProcessorNumber < ProcTraceData->AllocatedThreads) {
TopaTableBaseAddr = ProcTraceData->TopaMemArray[ProcessorNumber];
} else {
return RETURN_SUCCESS;
}
}
TopaTable = (PROC_TRACE_TOPA_TABLE *)TopaTableBaseAddr;
TopaEntryPtr = &TopaTable->TopaEntry[0];
TopaEntryPtr->Uint64 = 0;
TopaEntryPtr->Bits.Base = (MemRegionBaseAddr >> 12) & 0x000FFFFF;
TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64)MemRegionBaseAddr, 32) & 0xFFFFFFFF;
TopaEntryPtr->Bits.Size = ProcTraceData->ProcTraceMemSize;
TopaEntryPtr->Bits.END = 0;
TopaEntryPtr = &TopaTable->TopaEntry[1];
TopaEntryPtr->Uint64 = 0;
TopaEntryPtr->Bits.Base = (TopaTableBaseAddr >> 12) & 0x000FFFFF;
TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64)TopaTableBaseAddr, 32) & 0xFFFFFFFF;
TopaEntryPtr->Bits.END = 1;
//
// Program the MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with ToPA base
//
OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64;
OutputBaseReg.Bits.Base = (TopaTableBaseAddr >> 7) & 0x01FFFFFF;
OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64)TopaTableBaseAddr, 32) & 0xFFFFFFFF;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_OUTPUT_BASE,
OutputBaseReg.Uint64
);
//
// Set the MSR IA32_RTIT_OUTPUT_MASK (0x561) bits[63:7] to 0
//
OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64;
OutputMaskPtrsReg.Bits.MaskOrTableOffset = 0;
OutputMaskPtrsReg.Bits.OutputOffset = 0;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_OUTPUT_MASK_PTRS,
OutputMaskPtrsReg.Uint64
);
//
// Enable ToPA output scheme by enabling MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8)
//
CtrlReg.Bits.ToPA = 1;
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_CTL,
CtrlReg.Uint64
);
}
///
/// Enable the Processor Trace feature from MSR IA32_RTIT_CTL (570h)
///
CtrlReg.Bits.OS = 1;
CtrlReg.Bits.User = 1;
CtrlReg.Bits.BranchEn = 1;
CtrlReg.Bits.TraceEn = 1;
//
// Generate CYC/TSC timing packets to collect performance data.
//
if (ProcTraceData->EnablePerformanceCollecting) {
if (ProcTraceData->ProcessorData[ProcessorNumber].CycPacketSupported) {
CtrlReg.Bits.CYCEn = 1;
CtrlReg.Bits.CYCThresh = 5;
}
//
// Write to TSCEn is always supported
//
CtrlReg.Bits.TSCEn = 1;
}
CPU_REGISTER_TABLE_WRITE64 (
ProcessorNumber,
Msr,
MSR_IA32_RTIT_CTL,
CtrlReg.Uint64
);
return RETURN_SUCCESS;
}