blob: 8ea1daedd9773ad47a0f5b33fd9a466b704278e0 [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 "garnet/bin/debug_agent/arch_x64_helpers.h"
#include <vector>
#include "garnet/bin/debug_agent/arch.h"
#include "lib/fxl/logging.h"
namespace debug_agent {
namespace arch {
namespace {
struct DebugRegMask {
int index = -1;
uint64_t bp_mask = 0; // Enable mask within DR7
uint64_t rw_mask = 0; // RW mask within DR7
uint64_t len_mask = 0; // LEN mask within DR7
};
const DebugRegMask* GetDebugRegisterMasks(size_t index) {
static std::vector<DebugRegMask> masks = {
{0, kDR7L0, kDR7RW0 | (kDR7RW0 << 1), kDR7LEN0 | (kDR7LEN0 << 1)},
{1, kDR7L1, kDR7RW1 | (kDR7RW1 << 1), kDR7LEN1 | (kDR7LEN1 << 1)},
{2, kDR7L2, kDR7RW2 | (kDR7RW2 << 1), kDR7LEN2 | (kDR7LEN2 << 1)},
{3, kDR7L3, kDR7RW3 | (kDR7RW3 << 1), kDR7LEN3 | (kDR7LEN3 << 1)},
};
FXL_DCHECK(index < masks.size());
return &masks[index];
}
} // namespace
zx_status_t SetupHWBreakpoint(uint64_t address,
zx_thread_state_debug_regs_t* debug_regs) {
// Search for an unset register.
// TODO(donosoc): This doesn't check that the address is already set.
const DebugRegMask* slot = nullptr;
for (size_t i = 0; i < 4; i++) {
const DebugRegMask* mask = GetDebugRegisterMasks(i);
// If it's the same address or it's free, we found our slot.
bool active = FLAG_VALUE(debug_regs->dr7, mask->bp_mask);
if (debug_regs->dr[i] == address || !active) {
slot = mask;
break;
}
}
if (!slot)
return ZX_ERR_NO_RESOURCES;
debug_regs->dr[slot->index] = address;
// Modify the DR7 register.
// TODO(donosoc): For now only add execution breakpoints.
uint64_t dr7 = debug_regs->dr7;
dr7 |= slot->bp_mask; // Activate the breakpoint.
uint64_t mask = ~(slot->rw_mask);
// TODO(donosoc): Handle LEN properties of the breakpoint.
dr7 &= mask;
debug_regs->dr7 = dr7;
return ZX_OK;
}
zx_status_t RemoveHWBreakpoint(uint64_t address,
zx_thread_state_debug_regs_t* debug_regs) {
// Search for the address.
bool found = false;
for (size_t i = 0; i < 4; i++) {
if (address != debug_regs->dr[i])
continue;
const DebugRegMask* mask = GetDebugRegisterMasks(i);
// Only unset the
uint64_t dr7 = debug_regs->dr7;
dr7 &= ~(mask->bp_mask); // Disable the breakpoint.
debug_regs->dr[i] = 0;
debug_regs->dr7 = dr7;
found = true;
}
// No register found, we warn the caller. No change was issued.
if (!found)
return ZX_ERR_OUT_OF_RANGE;
return ZX_OK;
}
zx_status_t WriteGeneralRegisters(const std::vector<debug_ipc::Register>& regs,
zx_thread_state_general_regs_t* gen_regs) {
uint32_t begin = static_cast<uint32_t>(debug_ipc::RegisterID::kX64_rax);
uint32_t end = static_cast<uint32_t>(debug_ipc::RegisterID::kX64_rflags);
for (const debug_ipc::Register& reg : regs) {
if (reg.data.size() != 8)
return ZX_ERR_INVALID_ARGS;
// zx_thread_state_general_regs has the same layout as the RegisterID enum
// for x64 general registers.
uint32_t id = static_cast<uint32_t>(reg.id);
if (id > end)
return ZX_ERR_INVALID_ARGS;
// Insert the value to the correct offset.
uint32_t offset = id - begin;
uint64_t* reg_ptr = reinterpret_cast<uint64_t*>(gen_regs);
reg_ptr += offset;
*reg_ptr = *reinterpret_cast<const uint64_t*>(reg.data.data());
}
return ZX_OK;
}
// Debug functions -------------------------------------------------------------
void PrintGeneralRegisters(const zx_thread_state_general_regs& regs) {
FXL_LOG(INFO) << "General regs: " << std::endl
<< "rax: 0x" << std::hex << regs.rax << std::endl
<< "rbx: 0x" << std::hex << regs.rbx << std::endl
<< "rcx: 0x" << std::hex << regs.rcx << std::endl
<< "rdx: 0x" << std::hex << regs.rdx << std::endl
<< "rsi: 0x" << std::hex << regs.rsi << std::endl
<< "rdi: 0x" << std::hex << regs.rdi << std::endl
<< "rbp: 0x" << std::hex << regs.rbp << std::endl
<< "rsp: 0x" << std::hex << regs.rsp << std::endl
<< "r8: 0x" << std::hex << regs.r8 << std::endl
<< "r9: 0x" << std::hex << regs.r9 << std::endl
<< "r10: 0x" << std::hex << regs.r10 << std::endl
<< "r11: 0x" << std::hex << regs.r11 << std::endl
<< "r12: 0x" << std::hex << regs.r12 << std::endl
<< "r13: 0x" << std::hex << regs.r13 << std::endl
<< "r14: 0x" << std::hex << regs.r14 << std::endl
<< "r15: 0x" << std::hex << regs.r15 << std::endl
<< "rip: 0x" << std::hex << regs.rip << std::endl
<< "rflags: 0x" << std::hex << regs.rflags;
}
void PrintDebugRegisters(const zx_thread_state_debug_regs_t& regs) {
FXL_LOG(INFO) << "Regs: " << std::endl
<< "DR0: 0x" << std::hex << regs.dr[0] << std::endl
<< "DR1: 0x" << std::hex << regs.dr[1] << std::endl
<< "DR2: 0x" << std::hex << regs.dr[2] << std::endl
<< "DR3: 0x" << std::hex << regs.dr[3] << std::endl
<< "DR6: 0x" << std::hex << regs.dr6 << std::endl
<< "DR7: 0x" << std::hex << regs.dr7 << std::endl;
}
} // namespace arch
} // namespace debug_agent