blob: 45d019205055ad0f06c176676cbbad6967ff0ab1 [file] [edit]
// 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/kconcurrent/chainlock_transaction.h>
#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>
// 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) {
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_get_general_regs")};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(https://fxbug.dev/42105394): 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, in->r, sizeof(in->r));
out->lr = in->lr;
out->sp = in->usp;
out->pc = in->elr;
// Enable a view into user visible registers as well as those
// settable by restricted mode.
out->cpsr = in->spsr & kArmUserRestrictedVisibleFlags;
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) {
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_set_general_regs")};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(https://fxbug.dev/42105394): 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), "arch_set_general_regs");
memcpy(out->r, in->r, sizeof(in->r));
out->lr = in->lr;
out->usp = in->sp;
out->elr = in->pc;
if (arch_get_restricted_flag()) {
// Preserve all flags outside of NCZV (the directly user visible set) when
// in restricted mode. This allows continuity in 32-bit or 64-bit without
// allowing direct access to mode changes.
out->spsr = (out->spsr & ~kArmNczvFlags) | (in->cpsr & kArmNczvFlags);
} else {
// A normal mode thread should only allow user visible flags to be set.
// However, this function may be called on a thread that has transitioned
// from restricted mode, and as such, the mask below ensures that any saved
// restricted-mode-only flags are not preserved.
out->spsr = (out->spsr & ~kArmUserRestrictedVisibleFlags) | (in->cpsr & kArm64UserVisibleFlags);
}
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) {
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_get_single_step")};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(https://fxbug.dev/42105394): 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 = !!(thread->arch().mdscr_el1 & 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;
}
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_set_single_step")};
DEBUG_ASSERT(thread->IsUserStateSavedLocked());
// Punt if registers aren't available. E.g.,
// TODO(https://fxbug.dev/42105394): 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) {
thread->arch().mdscr_el1 |= kMdscrSSMask;
regs->spsr |= kSSMaskSPSR;
} else {
thread->arch().mdscr_el1 &= ~kMdscrSSMask;
regs->spsr &= ~kSSMaskSPSR;
}
return ZX_OK;
}
zx_status_t arch_get_fp_regs(Thread* thread, zx_thread_state_fp_regs_t* 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_t* 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_t* out) {
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_get_vector_regs")};
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_t* in) {
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_set_vector_regs")};
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_t* out) {
*out = {};
out->hw_bps_count = arm64_hw_breakpoint_count();
out->hw_wps_count = arm64_hw_watchpoint_count();
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_get_debug_regs")};
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_t* 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;
}
SingleChainLockGuard thread_guard{IrqSaveOption, thread->get_lock(),
CLT_TAG("arch_set_debug_regs")};
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(https://fxbug.dev/42105394): 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;
}
vaddr_t arch_get_instruction_pointer(GeneralRegsSource source, void* gregs) {
DEBUG_ASSERT_MSG(source == GeneralRegsSource::Iframe, "invalid source %u\n",
static_cast<uint32_t>(source));
return (reinterpret_cast<iframe_t*>(gregs)->elr);
}
zx_status_t arch_set_return_instruction_pointer(GeneralRegsSource source, void* gregs, vaddr_t ip) {
DEBUG_ASSERT_MSG(source == GeneralRegsSource::Iframe, "invalid source %u\n",
static_cast<uint32_t>(source));
static_cast<iframe_t*>(gregs)->elr = ip;
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(); }