blob: b2d386faa1552765b815896c953f9ad85e6a61fe [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 "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 SetupDebugBreakpoint(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 (!FLAG_VALUE(debug_regs->dr7, mask->bp_mask)) {
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 RemoveDebugBreakpoint(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;
}
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