blob: 29cc0ad1648ecfce381e8a29b116a03b5dffea54 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/debug_agent/arch_arm64_helpers.h"
#include <sstream>
#include "src/developer/debug/debug_agent/arch.h"
#include "src/developer/debug/shared/arch_arm64.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace debug_agent {
namespace arch {
zx_status_t SetupHWBreakpoint(uint64_t address,
zx_thread_state_debug_regs_t* debug_regs) {
// We search for an unset register.
int slot = -1;
for (int i = 0; i < debug_regs->hw_bps_count; i++) {
auto& hw_bp = debug_regs->hw_bps[i];
// If we found the same address, we don't care if it's disabled, we simply
// mark this as our slot.
// If the address is 0, this is an empty slot too.
if ((hw_bp.dbgbvr == address) || (hw_bp.dbgbvr == 0)) {
slot = i;
break;
}
// If it's active this is not an empty slot.
if ((hw_bp.dbgbcr & 1u) == 1)
continue;
slot = i;
break;
}
if (slot == -1)
return ZX_ERR_NO_RESOURCES;
debug_regs->hw_bps[slot].dbgbcr |= 1u;
debug_regs->hw_bps[slot].dbgbvr = address;
return ZX_OK;
}
zx_status_t RemoveHWBreakpoint(uint64_t address,
zx_thread_state_debug_regs_t* debug_regs) {
// Search for an breakpoint with this address.
int slot = -1;
for (int i = 0; i < debug_regs->hw_bps_count; i++) {
auto& hw_bp = debug_regs->hw_bps[i];
if (hw_bp.dbgbvr == address) {
slot = i;
break;
}
}
if (slot == -1)
return ZX_ERR_OUT_OF_RANGE;
debug_regs->hw_bps[slot].dbgbcr = 0;
debug_regs->hw_bps[slot].dbgbvr = 0;
return ZX_OK;
}
debug_ipc::NotifyException::Type DecodeESR(uint32_t esr) {
uint32_t ec = Arm64ExtractECFromESR(esr);
switch (ec) {
case 0b111000: /* BRK from arm32 */
case 0b111100: /* BRK from arm64 */
return debug_ipc::NotifyException::Type::kSoftware;
case 0b110000: /* HW breakpoit from a lower level */
case 0b110001: /* HW breakpoint from same level */
return debug_ipc::NotifyException::Type::kHardware;
case 0b110010: /* software step from lower level */
case 0b110011: /* software step from same level */
return debug_ipc::NotifyException::Type::kSingleStep;
default:
break;
}
return debug_ipc::NotifyException::Type::kGeneral;
}
std::string DebugRegistersToString(const zx_thread_state_debug_regs_t& regs) {
std::stringstream ss;
for (size_t i = 0; i < debug_ipc::kMaxArm64HWBreakpoints; i++) {
uint32_t dbgbcr = regs.hw_bps[i].dbgbcr;
uint64_t dbgbvr = regs.hw_bps[i].dbgbvr;
ss << fxl::StringPrintf(
"%lu. DBGBVR: 0x%lx, DBGBCR: E=%d, PMC=%d, BAS=%d, HMC=%d, SSC=%d, "
"LBN=%d, BT=%d",
i, dbgbvr, ARM64_FLAG_VALUE(dbgbcr, DBGBCR, E),
ARM64_FLAG_VALUE(dbgbcr, DBGBCR, PMC),
ARM64_FLAG_VALUE(dbgbcr, DBGBCR, BAS),
ARM64_FLAG_VALUE(dbgbcr, DBGBCR, HMC),
ARM64_FLAG_VALUE(dbgbcr, DBGBCR, SSC),
ARM64_FLAG_VALUE(dbgbcr, DBGBCR, LBN),
ARM64_FLAG_VALUE(dbgbcr, DBGBCR, BT));
ss << std::endl;
}
ss << "ESR: 0x" << std::hex << regs.esr;
return ss.str();
}
} // namespace arch
} // namespace debug_agent