| // 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 |