blob: b9a19f915c6ab7b54a33ab1b909cd6fb3951bf63 [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>
namespace arch {
uint64_t LbrFromIpMsr::ip(X86LbrFormat format) const {
switch (format) {
case X86LbrFormat::k32Bit:
case X86LbrFormat::k64BitLip:
case X86LbrFormat::k64BitEip:
case X86LbrFormat::k64BitEipWithInfo:
case X86LbrFormat::k64BitLipWithInfo:
return modern_ip();
case X86LbrFormat::k64BitEipWithFlags:
case X86LbrFormat::k64BitLipWithFlagsCycles:
return legacy_without_tsx_ip();
case X86LbrFormat::k64BitEipWithFlagsTsx:
return legacy_with_tsx_ip();
};
return 0;
}
std::optional<bool> LbrFromIpMsr::tsx_abort(X86LbrFormat format) const {
switch (format) {
case X86LbrFormat::k32Bit:
case X86LbrFormat::k64BitLip:
case X86LbrFormat::k64BitEip:
case X86LbrFormat::k64BitEipWithInfo:
case X86LbrFormat::k64BitLipWithInfo:
case X86LbrFormat::k64BitEipWithFlags:
case X86LbrFormat::k64BitLipWithFlagsCycles:
return {};
case X86LbrFormat::k64BitEipWithFlagsTsx:
return legacy_tsx_abort();
};
return {};
}
std::optional<bool> LbrFromIpMsr::in_tsx(X86LbrFormat format) const {
switch (format) {
case X86LbrFormat::k32Bit:
case X86LbrFormat::k64BitLip:
case X86LbrFormat::k64BitEip:
case X86LbrFormat::k64BitEipWithInfo:
case X86LbrFormat::k64BitLipWithInfo:
case X86LbrFormat::k64BitEipWithFlags:
case X86LbrFormat::k64BitLipWithFlagsCycles:
return {};
case X86LbrFormat::k64BitEipWithFlagsTsx:
return legacy_in_tsx();
}
return {};
}
std::optional<bool> LbrFromIpMsr::mispredicted(X86LbrFormat format) const {
switch (format) {
case X86LbrFormat::k32Bit:
case X86LbrFormat::k64BitLip:
case X86LbrFormat::k64BitEip:
case X86LbrFormat::k64BitEipWithInfo:
case X86LbrFormat::k64BitLipWithInfo:
return {};
case X86LbrFormat::k64BitEipWithFlags:
case X86LbrFormat::k64BitEipWithFlagsTsx:
case X86LbrFormat::k64BitLipWithFlagsCycles:
return legacy_mispredicted();
}
return {};
}
uint64_t LbrToIpMsr::ip(X86LbrFormat format) const {
switch (format) {
case X86LbrFormat::k32Bit:
case X86LbrFormat::k64BitLip:
case X86LbrFormat::k64BitEip:
case X86LbrFormat::k64BitEipWithInfo:
case X86LbrFormat::k64BitLipWithInfo:
case X86LbrFormat::k64BitEipWithFlags:
case X86LbrFormat::k64BitEipWithFlagsTsx:
return modern_ip();
case X86LbrFormat::k64BitLipWithFlagsCycles:
return legacy_ip();
}
return 0;
}
std::optional<uint16_t> LbrToIpMsr::cycle_count(X86LbrFormat format) const {
switch (format) {
case X86LbrFormat::k32Bit:
case X86LbrFormat::k64BitLip:
case X86LbrFormat::k64BitEip:
case X86LbrFormat::k64BitEipWithInfo:
case X86LbrFormat::k64BitLipWithInfo:
case X86LbrFormat::k64BitEipWithFlags:
case X86LbrFormat::k64BitEipWithFlagsTsx:
return {};
case X86LbrFormat::k64BitLipWithFlagsCycles:
return legacy_cycle_count();
}
return {};
}
size_t LbrStack::Size(Microarchitecture microarch) {
// [intel/vol3]: Table 17-4. LBR Stack Size and TOS Pointer Range.
switch (microarch) {
case Microarchitecture::kUnknown:
case Microarchitecture::kAmdFamilyBulldozer:
case Microarchitecture::kAmdFamilyJaguar:
case Microarchitecture::kAmdFamilyZen:
case Microarchitecture::kAmdFamilyZen3:
return 0;
case Microarchitecture::kIntelCore2:
return 4;
case Microarchitecture::kIntelBonnell:
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont:
return 8;
case Microarchitecture::kIntelNehalem:
case Microarchitecture::kIntelWestmere:
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge:
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell:
return 16;
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelIceLake:
case Microarchitecture::kIntelTigerLake:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont:
return 32;
}
return 0;
}
bool LbrStack::SupportsCallstackProfiling(Microarchitecture microarch) {
// Gleaned from scouring [intel/v4] to see which microarchitectures have
// MSR_LBR_SELECT.EN_CALLSTACK defined.
switch (microarch) {
case Microarchitecture::kUnknown:
case Microarchitecture::kIntelCore2:
case Microarchitecture::kIntelBonnell:
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont:
case Microarchitecture::kIntelNehalem:
case Microarchitecture::kIntelWestmere:
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge:
case Microarchitecture::kAmdFamilyBulldozer:
case Microarchitecture::kAmdFamilyJaguar:
case Microarchitecture::kAmdFamilyZen:
case Microarchitecture::kAmdFamilyZen3:
return false;
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell:
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelIceLake:
case Microarchitecture::kIntelTigerLake:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont:
return true;
}
return false;
}
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