blob: 0fc8e5eb1b1ca6b632940c3bea67727397f70142 [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 <arch/arm64.h>
#include <arch/arm64/registers.h>
#include <arch/debugger.h>
#include <err.h>
#include <kernel/thread.h>
#include <kernel/thread_lock.h>
#include <string.h>
#include <sys/types.h>
#include <zircon/syscalls/debug.h>
#include <zircon/types.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(struct thread* thread, zx_thread_state_general_regs_t* out) {
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
// Punt if registers aren't available. E.g.,
// ZX-563 (registers aren't available in synthetic exceptions)
if (thread->arch.suspended_general_regs == nullptr)
return ZX_ERR_NOT_SUPPORTED;
struct arm64_iframe_long* 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;
return ZX_OK;
}
zx_status_t arch_set_general_regs(struct thread* thread, const zx_thread_state_general_regs_t* in) {
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
// Punt if registers aren't available. E.g.,
// ZX-563 (registers aren't available in synthetic exceptions)
if (thread->arch.suspended_general_regs == nullptr)
return ZX_ERR_NOT_SUPPORTED;
struct arm64_iframe_long* 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);
return ZX_OK;
}
zx_status_t arch_get_single_step(struct thread* thread, bool* single_step) {
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
// Punt if registers aren't available. E.g.,
// ZX-563 (registers aren't available in synthetic exceptions)
if (thread->arch.suspended_general_regs == nullptr)
return ZX_ERR_NOT_SUPPORTED;
struct arm64_iframe_long* regs = thread->arch.suspended_general_regs;
const bool mdscr_ss_enable = !!(regs->mdscr & kMdscrSSMask);
const bool spsr_ss_enable = !!(regs->spsr & kSSMaskSPSR);
*single_step = mdscr_ss_enable && spsr_ss_enable;
return ZX_OK;
}
zx_status_t arch_set_single_step(struct thread* thread, bool single_step) {
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
// Punt if registers aren't available. E.g.,
// ZX-563 (registers aren't available in synthetic exceptions)
if (thread->arch.suspended_general_regs == nullptr)
return ZX_ERR_NOT_SUPPORTED;
struct arm64_iframe_long* regs = thread->arch.suspended_general_regs;
if (single_step) {
regs->mdscr |= kMdscrSSMask;
regs->spsr |= kSSMaskSPSR;
} else {
regs->mdscr &= ~kMdscrSSMask;
regs->spsr &= ~kSSMaskSPSR;
}
return ZX_OK;
}
zx_status_t arch_get_fp_regs(struct thread* thread, zx_thread_state_fp_regs* out) {
// There are no ARM fp regs.
(void)out;
return ZX_OK;
}
zx_status_t arch_set_fp_regs(struct thread* thread, const zx_thread_state_fp_regs* in) {
// There are no ARM fp regs.
(void)in;
return ZX_OK;
}
zx_status_t arch_get_vector_regs(struct thread* thread, zx_thread_state_vector_regs* out) {
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
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(struct thread* thread, const zx_thread_state_vector_regs* in) {
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
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(struct thread* thread, zx_thread_state_debug_regs* out) {
out->hw_bps_count = arm64_hw_breakpoint_count();
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
// The kernel ensures that this state is being kept up to date, so we can safely copy the
// information over.
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;
}
// Hacked through the last debug registers for now for development.
// THIS CODE WILL GO AWAY!
// This normally doesn't affect functionality as normally a CPU implementation has around six
// debug registers.
// TODO(ZX-3038): This should be exposed through a standard interface.
// Either the sysinfo fidl, the vDSO info mapping or some other mechanism.
out->hw_bps[AARCH64_MAX_HW_BREAKPOINTS - 1].dbgbvr = __arm_rsr64("id_aa64dfr0_el1");
out->hw_bps[AARCH64_MAX_HW_BREAKPOINTS - 2].dbgbvr = __arm_rsr64("mdscr_el1");
return ZX_OK;
}
zx_status_t arch_set_debug_regs(struct 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;
}
if (!arm64_validate_debug_state(&state)) {
return ZX_ERR_INVALID_ARGS;
}
Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()};
thread->arch.track_debug_state = true;
thread->arch.debug_state = state;
return ZX_OK;
}
zx_status_t arch_get_x86_register_fs(struct thread* thread, uint64_t* out) {
// There are no FS register on ARM.
(void)out;
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t arch_set_x86_register_fs(struct thread* thread, const uint64_t* in) {
// There are no FS register on ARM.
(void)in;
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t arch_get_x86_register_gs(struct thread* thread, uint64_t* out) {
// There are no GS register on ARM.
(void)out;
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t arch_set_x86_register_gs(struct thread* thread, const uint64_t* in) {
// There are no GS register on ARM.
(void)in;
return ZX_ERR_NOT_SUPPORTED;
}