blob: 73616639a11634892c307e2deb4ca51f9f1d136e [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/arch/x86/lbr.h>
#include <fbl/bits.h>
#include <fbl/function.h>
namespace arch {
uint64_t LbrFromIpMsr::value(LbrFormat format) {
switch (format) {
case LbrFormat::k32Bit:
case LbrFormat::k64BitLip:
case LbrFormat::k64BitEip:
case LbrFormat::k64BitEipWithFlagsInfo:
case LbrFormat::k64BitLipWithFlagsInfo:
return reg_value();
case LbrFormat::k64BitEipWithFlags:
case LbrFormat::k64BitLipWithFlagsCycles:
return fbl::ExtractBits<62, 0, uint64_t>(reg_value());
case LbrFormat::k64BitEipWithFlagsTsx:
return fbl::ExtractBits<60, 0, uint64_t>(reg_value());
};
__UNREACHABLE;
}
std::optional<bool> LbrFromIpMsr::tsx_abort(LbrFormat format) {
switch (format) {
case LbrFormat::k32Bit:
case LbrFormat::k64BitLip:
case LbrFormat::k64BitEip:
case LbrFormat::k64BitEipWithFlagsInfo:
case LbrFormat::k64BitLipWithFlagsInfo:
case LbrFormat::k64BitEipWithFlags:
case LbrFormat::k64BitLipWithFlagsCycles:
return {};
case LbrFormat::k64BitEipWithFlagsTsx:
return fbl::ExtractBit<61, bool>(reg_value());
};
__UNREACHABLE;
}
std::optional<bool> LbrFromIpMsr::in_tsx(LbrFormat format) {
switch (format) {
case LbrFormat::k32Bit:
case LbrFormat::k64BitLip:
case LbrFormat::k64BitEip:
case LbrFormat::k64BitEipWithFlagsInfo:
case LbrFormat::k64BitLipWithFlagsInfo:
case LbrFormat::k64BitEipWithFlags:
case LbrFormat::k64BitLipWithFlagsCycles:
return {};
case LbrFormat::k64BitEipWithFlagsTsx:
return fbl::ExtractBit<62, bool>(reg_value());
}
__UNREACHABLE;
}
std::optional<bool> LbrFromIpMsr::mispredicted(LbrFormat format) {
switch (format) {
case LbrFormat::k32Bit:
case LbrFormat::k64BitLip:
case LbrFormat::k64BitEip:
case LbrFormat::k64BitEipWithFlagsInfo:
case LbrFormat::k64BitLipWithFlagsInfo:
return {};
case LbrFormat::k64BitEipWithFlags:
case LbrFormat::k64BitEipWithFlagsTsx:
case LbrFormat::k64BitLipWithFlagsCycles:
return fbl::ExtractBit<63, bool>(reg_value());
}
__UNREACHABLE;
}
uint64_t LbrToIpMsr::value(LbrFormat format) {
switch (format) {
case LbrFormat::k32Bit:
case LbrFormat::k64BitLip:
case LbrFormat::k64BitEip:
case LbrFormat::k64BitEipWithFlagsInfo:
case LbrFormat::k64BitLipWithFlagsInfo:
case LbrFormat::k64BitEipWithFlags:
case LbrFormat::k64BitEipWithFlagsTsx:
return reg_value();
case LbrFormat::k64BitLipWithFlagsCycles:
return fbl::ExtractBits<47, 0, uint64_t>(reg_value());
}
__UNREACHABLE;
}
std::optional<uint16_t> LbrToIpMsr::cycle_count(LbrFormat format) {
switch (format) {
case LbrFormat::k32Bit:
case LbrFormat::k64BitLip:
case LbrFormat::k64BitEip:
case LbrFormat::k64BitEipWithFlagsInfo:
case LbrFormat::k64BitLipWithFlagsInfo:
case LbrFormat::k64BitEipWithFlags:
case LbrFormat::k64BitEipWithFlagsTsx:
return {};
case LbrFormat::k64BitLipWithFlagsCycles:
return fbl::ExtractBits<63, 48, uint16_t>(reg_value());
}
__UNREACHABLE;
}
void LbrStack::Initialize(Microarchitecture microarch, bool supports_pdcm) {
// [intel/vol3]: Table 17-4. LBR Stack Size and TOS Pointer Range.
//
// Sizes synthesized from the above reference; whether callstack profiling is
// supported is gleaned from scouring v4 to see which architectures have
// MSR_LBR_SELECT.EN_CALLSTACK defined.
//
// Do not rely on a default case; build failures are a desired failsafe in
// ensuring that this logic is updated on the introduction of new
// microarchitectures.
switch (microarch) {
case Microarchitecture::kIntelCore2: {
size_ = 4;
break;
}
case Microarchitecture::kIntelBonnell:
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont: {
size_ = 8;
break;
}
case Microarchitecture::kIntelNehalem:
case Microarchitecture::kIntelWestmere:
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge: {
size_ = 16;
break;
}
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell: {
size_ = 16;
callstack_profiling_ = true;
break;
}
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont: {
size_ = 32;
callstack_profiling_ = true;
break;
}
case Microarchitecture::kUnknown:
case Microarchitecture::kAmdFamily0x15:
case Microarchitecture::kAmdFamily0x16:
case Microarchitecture::kAmdFamily0x17:
case Microarchitecture::kAmdFamily0x19:
break;
}
supported_ = supports_pdcm && (size_ > 0);
}
LbrSelectMsr LbrStack::GetDefaultSettings(bool for_user) const {
// Confusingly, setting MSR_LBR_SELECT.CPL_EQ_0 means that branches ending
// in ring 0 are *discarded*; similarly, setting CPL_NEQ_0 means that
// branches ending in ring > 0 are.
//
// Capture conditional branches, and near indirect and relative jumps;
// disable capture of near returns, and near indirect and relative calls,
// which is information already deducible from a backtrace.
auto select = LbrSelectMsr::Get()
.FromValue(0)
.set_cpl_eq_0(unsigned{for_user})
.set_cpl_neq_0(unsigned{!for_user})
.set_jcc(1)
.set_near_ind_jmp(1)
.set_near_rel_jmp(1)
.set_near_ind_call(0)
.set_near_rel_call(0)
.set_near_ret(0);
// Enable the callstack profiling mode if supported.
return callstack_profiling_ ? select.set_en_callstack(1) : select;
}
} // namespace arch