blob: f5052b7e26bb1835121c8ec97f3a44cfe24dfbba [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 <err.h>
#include <string.h>
#include <sys/types.h>
#include <arch/arm64.h>
#include <arch/debugger.h>
#include <kernel/thread.h>
#include <zircon/syscalls/debug.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;
uint arch_num_regsets(void)
{
return 1; // TODO(dje): Just the general regs for now.
}
static status_t arch_get_general_regs(struct thread *thread, zx_arm64_general_regs_t *out, uint32_t *buf_size)
{
uint32_t provided_buf_size = *buf_size;
*buf_size = sizeof(*out);
if (provided_buf_size < sizeof(*out))
return ZX_ERR_BUFFER_TOO_SMALL;
if (thread_stopped_in_exception(thread)) {
// TODO(dje): We could get called while processing a synthetic
// exception where there is no frame.
if (thread->exception_context->frame == NULL)
return ZX_ERR_NOT_SUPPORTED;
} else {
// TODO(dje): Punt if, for example, suspended in channel call.
// Can be removed when MG-747 done.
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;
}
static status_t arch_set_general_regs(struct thread *thread, const zx_arm64_general_regs_t *in, uint32_t buf_size)
{
if (buf_size != sizeof(*in))
return ZX_ERR_INVALID_ARGS;
if (thread_stopped_in_exception(thread)) {
// TODO(dje): We could get called while processing a synthetic
// exception where there is no frame.
if (thread->exception_context->frame == NULL)
return ZX_ERR_NOT_SUPPORTED;
} else {
// TODO(dje): Punt if, for example, suspended in channel call.
// Can be removed when MG-747 done.
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;
}
// The caller is responsible for making sure the thread is in an exception
// or is suspended, and stays so.
status_t arch_get_regset(struct thread *thread, uint regset, void *regs, uint32_t *buf_size)
{
switch (regset)
{
case 0:
return arch_get_general_regs(thread, (zx_arm64_general_regs_t *)regs, buf_size);
default:
return ZX_ERR_INVALID_ARGS;
}
}
// The caller is responsible for making sure the thread is in an exception
// or is suspended, and stays so.
status_t arch_set_regset(struct thread *thread, uint regset, const void *regs, uint32_t buf_size)
{
switch (regset)
{
case 0:
return arch_set_general_regs(thread, (zx_arm64_general_regs_t *)regs, buf_size);
default:
return ZX_ERR_INVALID_ARGS;
}
}