blob: 6a0fea213e8f94bdcfd804e9f83aa46b4a8ca241 [file] [log] [blame]
// Copyright 2016 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/zircon-internal/macros.h>
#include <string.h>
#include <sys/types.h>
#include <zircon/errors.h>
#include <zircon/syscalls/debug.h>
#include <zircon/types.h>
#include <arch/arm64.h>
#include <arch/arm64/registers.h>
#include <arch/debugger.h>
#include <arch/regs.h>
#include <kernel/thread.h>
#include <kernel/thread_lock.h>
// Only the NZCV flags (bits 31 to 28 respectively) of the CPSR are
// readable and writable by userland on ARM64.
static uint32_t kUserVisibleFlags = 0xf0000000;
// SS (="Single Step") is bit 0 in MDSCR_EL1.
static constexpr uint64_t kMdscrSSMask = 1;
// Single Step for PSTATE, see ARMv8 Manual C5.2.18, enable Single step for Process
static constexpr uint64_t kSSMaskSPSR = (1 << 21);
zx_status_t arch_get_general_regs(Thread* thread, zx_thread_state_general_regs_t* out) {
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(fxbug.dev/30521): Registers aren't available in synthetic exceptions.
if (thread->arch().suspended_general_regs == nullptr) {
return ZX_ERR_NOT_SUPPORTED;
}
iframe_t* in = thread->arch().suspended_general_regs;
DEBUG_ASSERT(in);
static_assert(sizeof(in->r) == sizeof(out->r), "");
memcpy(&out->r[0], &in->r[0], sizeof(in->r));
out->lr = in->lr;
out->sp = in->usp;
out->pc = in->elr;
out->cpsr = in->spsr & kUserVisibleFlags;
out->tpidr = thread->arch().tpidr_el0;
return ZX_OK;
}
zx_status_t arch_set_general_regs(Thread* thread, const zx_thread_state_general_regs_t* in) {
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(fxbug.dev/30521): Registers aren't available in synthetic exceptions.
if (thread->arch().suspended_general_regs == nullptr) {
return ZX_ERR_NOT_SUPPORTED;
}
iframe_t* out = thread->arch().suspended_general_regs;
DEBUG_ASSERT(out);
static_assert(sizeof(out->r) == sizeof(in->r), "");
memcpy(&out->r[0], &in->r[0], sizeof(in->r));
out->lr = in->lr;
out->usp = in->sp;
out->elr = in->pc;
out->spsr = (out->spsr & ~kUserVisibleFlags) | (in->cpsr & kUserVisibleFlags);
thread->arch().tpidr_el0 = in->tpidr;
return ZX_OK;
}
zx_status_t arch_get_single_step(Thread* thread, zx_thread_state_single_step_t* out) {
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(fxbug.dev/30521): Registers aren't available in synthetic exceptions.
if (thread->arch().suspended_general_regs == nullptr) {
return ZX_ERR_NOT_SUPPORTED;
}
iframe_t* regs = thread->arch().suspended_general_regs;
const bool mdscr_ss_enable = !!(regs->mdscr & kMdscrSSMask);
const bool spsr_ss_enable = !!(regs->spsr & kSSMaskSPSR);
*out = mdscr_ss_enable && spsr_ss_enable;
return ZX_OK;
}
zx_status_t arch_set_single_step(Thread* thread, const zx_thread_state_single_step_t* in) {
if (*in != 0 && *in != 1) {
return ZX_ERR_INVALID_ARGS;
}
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(fxbug.dev/30521): Registers aren't available in synthetic exceptions.
if (thread->arch().suspended_general_regs == nullptr) {
return ZX_ERR_NOT_SUPPORTED;
}
iframe_t* regs = thread->arch().suspended_general_regs;
if (*in) {
regs->mdscr |= kMdscrSSMask;
regs->spsr |= kSSMaskSPSR;
} else {
regs->mdscr &= ~kMdscrSSMask;
regs->spsr &= ~kSSMaskSPSR;
}
return ZX_OK;
}
zx_status_t arch_get_fp_regs(Thread* thread, zx_thread_state_fp_regs* out) {
// There are no ARM fp regs.
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t arch_set_fp_regs(Thread* thread, const zx_thread_state_fp_regs* in) {
// There are no ARM fp regs.
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t arch_get_vector_regs(Thread* thread, zx_thread_state_vector_regs* out) {
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
const fpstate* in = &thread->arch().fpstate;
out->fpcr = in->fpcr;
out->fpsr = in->fpsr;
for (int i = 0; i < 32; i++) {
out->v[i].low = in->regs[i * 2];
out->v[i].high = in->regs[i * 2 + 1];
}
return ZX_OK;
}
zx_status_t arch_set_vector_regs(Thread* thread, const zx_thread_state_vector_regs* in) {
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
fpstate* out = &thread->arch().fpstate;
out->fpcr = in->fpcr;
out->fpsr = in->fpsr;
for (int i = 0; i < 32; i++) {
out->regs[i * 2] = in->v[i].low;
out->regs[i * 2 + 1] = in->v[i].high;
}
return ZX_OK;
}
zx_status_t arch_get_debug_regs(Thread* thread, zx_thread_state_debug_regs* out) {
*out = {};
out->hw_bps_count = arm64_hw_breakpoint_count();
out->hw_wps_count = arm64_hw_watchpoint_count();
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// The kernel ensures that this state is being kept up to date, so we can safely copy the
// information over.
// HW breakpoints.
for (size_t i = 0; i < out->hw_bps_count; i++) {
out->hw_bps[i].dbgbcr = thread->arch().debug_state.hw_bps[i].dbgbcr;
out->hw_bps[i].dbgbvr = thread->arch().debug_state.hw_bps[i].dbgbvr;
}
// Watchpoints.
for (size_t i = 0; i < out->hw_wps_count; i++) {
out->hw_wps[i].dbgwcr = thread->arch().debug_state.hw_wps[i].dbgwcr;
out->hw_wps[i].dbgwvr = thread->arch().debug_state.hw_wps[i].dbgwvr;
}
out->esr = thread->arch().debug_state.esr;
out->far = thread->arch().debug_state.far;
return ZX_OK;
}
zx_status_t arch_set_debug_regs(Thread* thread, const zx_thread_state_debug_regs* in) {
arm64_debug_state_t state = {};
// We copy over the state from the input.
uint64_t bp_count = arm64_hw_breakpoint_count();
for (size_t i = 0; i < bp_count; i++) {
state.hw_bps[i].dbgbcr = in->hw_bps[i].dbgbcr;
state.hw_bps[i].dbgbvr = in->hw_bps[i].dbgbvr;
}
uint64_t wp_count = arm64_hw_watchpoint_count();
for (size_t i = 0; i < wp_count; i++) {
state.hw_wps[i].dbgwcr = in->hw_wps[i].dbgwcr;
state.hw_wps[i].dbgwvr = in->hw_wps[i].dbgwvr;
}
uint32_t active_breakpoints = 0;
uint32_t active_watchpoints = 0;
if (!arm64_validate_debug_state(&state, &active_breakpoints, &active_watchpoints)) {
return ZX_ERR_INVALID_ARGS;
}
Guard<MonitoredSpinLock, IrqSave> thread_lock_guard{ThreadLock::Get(), SOURCE_TAG};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// If the suspended registers are not there, we cannot save the MDSCR values for this thread,
// meaning that the debug HW state will be cleared almost immediatelly.
// This should always be there.
// TODO(fxbug.dev/30521): Registers aren't available in synthetic exceptions.
if (!thread->arch().suspended_general_regs) {
return ZX_ERR_NOT_SUPPORTED;
}
bool hw_debug_needed = (active_breakpoints > 0) || (active_watchpoints > 0);
arm64_set_debug_state_for_thread(thread, hw_debug_needed);
state.esr = thread->arch().debug_state.esr;
state.far = thread->arch().debug_state.far;
thread->arch().track_debug_state = true;
thread->arch().debug_state = state;
return ZX_OK;
}
uint8_t arch_get_hw_breakpoint_count() { return arm64_hw_breakpoint_count(); }
uint8_t arch_get_hw_watchpoint_count() { return arm64_hw_watchpoint_count(); }