提交 c2c76850 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-2.13-pull-request' into staging

# gpg: Signature made Mon 30 Apr 2018 10:05:56 BST
# gpg:                using RSA key F30C38BD3F2FBE3C
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>"
# gpg:                 aka "Laurent Vivier <laurent@vivier.eu>"
# gpg:                 aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>"
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F  5173 F30C 38BD 3F2F BE3C

* remotes/vivier2/tags/linux-user-for-2.13-pull-request: (42 commits)
  linux-user: Add ARM get_tls syscall support
  linux-user: move xtensa cpu loop to xtensa directory
  linux-user: move hppa cpu loop to hppa directory
  linux-user: move riscv cpu loop to riscv directory
  linux-user: move tilegx cpu loop to tilegx directory
  linux-user: move s390x cpu loop to s390x directory
  linux-user: move alpha cpu loop to alpha directory
  linux-user: move m68k cpu loop to m68k directory
  linux-user: move microblaze cpu loop to microblaze directory
  linux-user: move cris cpu loop to cris directory
  linux-user: move sh4 cpu loop to sh4 directory
  linux-user: move openrisc cpu loop to openrisc directory
  linux-user: move nios2 cpu loop to nios2 directory
  linux-user: move mips/mips64 cpu loop to mips directory
  linux-user: move ppc/ppc64 cpu loop to ppc directory
  linux-user: move sparc/sparc64 cpu loop to sparc directory
  linux-user: move arm cpu loop to arm directory
  linux-user: move aarch64 cpu loop to aarch64 directory
  linux-user: move i386/x86_64 cpu loop to i386 directory
  linux-user: create a dummy per arch cpu_loop.c
  ...
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
obj-y = main.o syscall.o strace.o mmap.o signal.o \ obj-y = main.o syscall.o strace.o mmap.o signal.o \
elfload.o linuxload.o uaccess.o uname.o \ elfload.o linuxload.o uaccess.o uname.o \
safe-syscall.o safe-syscall.o $(TARGET_ABI_DIR)/signal.o \
$(TARGET_ABI_DIR)/cpu_loop.o
obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_HAS_BFLT) += flatload.o
obj-$(TARGET_I386) += vm86.o obj-$(TARGET_I386) += vm86.o
......
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "cpu_loop-common.h"
#define get_user_code_u32(x, gaddr, env) \
({ abi_long __r = get_user_u32((x), (gaddr)); \
if (!__r && bswap_code(arm_sctlr_b(env))) { \
(x) = bswap32(x); \
} \
__r; \
})
#define get_user_code_u16(x, gaddr, env) \
({ abi_long __r = get_user_u16((x), (gaddr)); \
if (!__r && bswap_code(arm_sctlr_b(env))) { \
(x) = bswap16(x); \
} \
__r; \
})
#define get_user_data_u32(x, gaddr, env) \
({ abi_long __r = get_user_u32((x), (gaddr)); \
if (!__r && arm_cpu_bswap_data(env)) { \
(x) = bswap32(x); \
} \
__r; \
})
#define get_user_data_u16(x, gaddr, env) \
({ abi_long __r = get_user_u16((x), (gaddr)); \
if (!__r && arm_cpu_bswap_data(env)) { \
(x) = bswap16(x); \
} \
__r; \
})
#define put_user_data_u32(x, gaddr, env) \
({ typeof(x) __x = (x); \
if (arm_cpu_bswap_data(env)) { \
__x = bswap32(__x); \
} \
put_user_u32(__x, (gaddr)); \
})
#define put_user_data_u16(x, gaddr, env) \
({ typeof(x) __x = (x); \
if (arm_cpu_bswap_data(env)) { \
__x = bswap16(__x); \
} \
put_user_u16(__x, (gaddr)); \
})
/* AArch64 main loop */
void cpu_loop(CPUARMState *env)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
int trapnr, sig;
abi_long ret;
target_siginfo_t info;
for (;;) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch (trapnr) {
case EXCP_SWI:
ret = do_syscall(env,
env->xregs[8],
env->xregs[0],
env->xregs[1],
env->xregs[2],
env->xregs[3],
env->xregs[4],
env->xregs[5],
0, 0);
if (ret == -TARGET_ERESTARTSYS) {
env->pc -= 4;
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
env->xregs[0] = ret;
}
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
case EXCP_UDEF:
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->exception.vaddress;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_DEBUG:
case EXCP_BKPT:
sig = gdb_handlesig(cs, TARGET_SIGTRAP);
if (sig) {
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP_SEMIHOST:
env->xregs[0] = do_arm_semihosting(env);
break;
case EXCP_YIELD:
/* nothing to do here for user-mode, just resume guest code */
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
default:
EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
abort();
}
process_pending_signals(env);
/* Exception return on AArch64 always clears the exclusive monitor,
* so any return to running guest code implies this.
*/
env->exclusive_addr = -1;
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
CPUState *cpu = ENV_GET_CPU(env);
TaskState *ts = cpu->opaque;
struct image_info *info = ts->info;
int i;
if (!(arm_feature(env, ARM_FEATURE_AARCH64))) {
fprintf(stderr,
"The selected ARM CPU does not support 64 bit mode\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < 31; i++) {
env->xregs[i] = regs->regs[i];
}
env->pc = regs->pc;
env->xregs[31] = regs->sp;
#ifdef TARGET_WORDS_BIGENDIAN
env->cp15.sctlr_el[1] |= SCTLR_E0E;
for (i = 1; i < 4; ++i) {
env->cp15.sctlr_el[i] |= SCTLR_EE;
}
#endif
ts->stack_base = info->start_stack;
ts->heap_base = info->brk;
/* This will be filled in on the first SYS_HEAPINFO call. */
ts->heap_limit = 0;
}
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "target_signal.h"
#include "signal-common.h"
#include "linux-user/trace.h"
struct target_sigcontext {
uint64_t fault_address;
/* AArch64 registers */
uint64_t regs[31];
uint64_t sp;
uint64_t pc;
uint64_t pstate;
/* 4K reserved for FP/SIMD state and future expansion */
char __reserved[4096] __attribute__((__aligned__(16)));
};
struct target_ucontext {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
target_sigset_t tuc_sigmask;
/* glibc uses a 1024-bit sigset_t */
char __unused[1024 / 8 - sizeof(target_sigset_t)];
/* last for future expansion */
struct target_sigcontext tuc_mcontext;
};
/*
* Header to be used at the beginning of structures extending the user
* context. Such structures must be placed after the rt_sigframe on the stack
* and be 16-byte aligned. The last structure must be a dummy one with the
* magic and size set to 0.
*/
struct target_aarch64_ctx {
uint32_t magic;
uint32_t size;
};
#define TARGET_FPSIMD_MAGIC 0x46508001
struct target_fpsimd_context {
struct target_aarch64_ctx head;
uint32_t fpsr;
uint32_t fpcr;
uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */
};
#define TARGET_EXTRA_MAGIC 0x45585401
struct target_extra_context {
struct target_aarch64_ctx head;
uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */
uint32_t size; /* size in bytes of the extra space */
uint32_t reserved[3];
};
#define TARGET_SVE_MAGIC 0x53564501
struct target_sve_context {
struct target_aarch64_ctx head;
uint16_t vl;
uint16_t reserved[3];
/* The actual SVE data immediately follows. It is layed out
* according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of
* the original struct pointer.
*/
};
#define TARGET_SVE_VQ_BYTES 16
#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES)
#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8))
#define TARGET_SVE_SIG_REGS_OFFSET \
QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES)
#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \
(TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N))
#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \
(TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N))
#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \
(TARGET_SVE_SIG_PREG_OFFSET(VQ, 16))
#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \
(TARGET_SVE_SIG_PREG_OFFSET(VQ, 17))
struct target_rt_sigframe {
struct target_siginfo info;
struct target_ucontext uc;
};
struct target_rt_frame_record {
uint64_t fp;
uint64_t lr;
uint32_t tramp[2];
};
static void target_setup_general_frame(struct target_rt_sigframe *sf,
CPUARMState *env, target_sigset_t *set)
{
int i;
__put_user(0, &sf->uc.tuc_flags);
__put_user(0, &sf->uc.tuc_link);
__put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags);
__put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size);
for (i = 0; i < 31; i++) {
__put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]);
}
__put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__put_user(env->pc, &sf->uc.tuc_mcontext.pc);
__put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate);
__put_user(env->exception.vaddress, &sf->uc.tuc_mcontext.fault_address);
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]);
}
}
static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd,
CPUARMState *env)
{
int i;
__put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic);
__put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size);
__put_user(vfp_get_fpsr(env), &fpsimd->fpsr);
__put_user(vfp_get_fpcr(env), &fpsimd->fpcr);
for (i = 0; i < 32; i++) {
uint64_t *q = aa64_vfp_qreg(env, i);
#ifdef TARGET_WORDS_BIGENDIAN
__put_user(q[0], &fpsimd->vregs[i * 2 + 1]);
__put_user(q[1], &fpsimd->vregs[i * 2]);
#else
__put_user(q[0], &fpsimd->vregs[i * 2]);
__put_user(q[1], &fpsimd->vregs[i * 2 + 1]);
#endif
}
}
static void target_setup_extra_record(struct target_extra_context *extra,
uint64_t datap, uint32_t extra_size)
{
__put_user(TARGET_EXTRA_MAGIC, &extra->head.magic);
__put_user(sizeof(struct target_extra_context), &extra->head.size);
__put_user(datap, &extra->datap);
__put_user(extra_size, &extra->size);
}
static void target_setup_end_record(struct target_aarch64_ctx *end)
{
__put_user(0, &end->magic);
__put_user(0, &end->size);
}
static void target_setup_sve_record(struct target_sve_context *sve,
CPUARMState *env, int vq, int size)
{
int i, j;
__put_user(TARGET_SVE_MAGIC, &sve->head.magic);
__put_user(size, &sve->head.size);
__put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl);
/* Note that SVE regs are stored as a byte stream, with each byte element
* at a subsequent address. This corresponds to a little-endian store
* of our 64-bit hunks.
*/
for (i = 0; i < 32; ++i) {
uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
for (j = 0; j < vq * 2; ++j) {
__put_user_e(env->vfp.zregs[i].d[j], z + j, le);
}
}
for (i = 0; i <= 16; ++i) {
uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
for (j = 0; j < vq; ++j) {
uint64_t r = env->vfp.pregs[i].p[j >> 2];
__put_user_e(r >> ((j & 3) * 16), p + j, le);
}
}
}
static void target_restore_general_frame(CPUARMState *env,
struct target_rt_sigframe *sf)
{
sigset_t set;
uint64_t pstate;
int i;
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
set_sigmask(&set);
for (i = 0; i < 31; i++) {
__get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]);
}
__get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
__get_user(pstate, &sf->uc.tuc_mcontext.pstate);
pstate_write(env, pstate);
}
static void target_restore_fpsimd_record(CPUARMState *env,
struct target_fpsimd_context *fpsimd)
{
uint32_t fpsr, fpcr;
int i;
__get_user(fpsr, &fpsimd->fpsr);
vfp_set_fpsr(env, fpsr);
__get_user(fpcr, &fpsimd->fpcr);
vfp_set_fpcr(env, fpcr);
for (i = 0; i < 32; i++) {
uint64_t *q = aa64_vfp_qreg(env, i);
#ifdef TARGET_WORDS_BIGENDIAN
__get_user(q[0], &fpsimd->vregs[i * 2 + 1]);
__get_user(q[1], &fpsimd->vregs[i * 2]);
#else
__get_user(q[0], &fpsimd->vregs[i * 2]);
__get_user(q[1], &fpsimd->vregs[i * 2 + 1]);
#endif
}
}
static void target_restore_sve_record(CPUARMState *env,
struct target_sve_context *sve, int vq)
{
int i, j;
/* Note that SVE regs are stored as a byte stream, with each byte element
* at a subsequent address. This corresponds to a little-endian load
* of our 64-bit hunks.
*/
for (i = 0; i < 32; ++i) {
uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
for (j = 0; j < vq * 2; ++j) {
__get_user_e(env->vfp.zregs[i].d[j], z + j, le);
}
}
for (i = 0; i <= 16; ++i) {
uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
for (j = 0; j < vq; ++j) {
uint16_t r;
__get_user_e(r, p + j, le);
if (j & 3) {
env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16);
} else {
env->vfp.pregs[i].p[j >> 2] = r;
}
}
}
}
static int target_restore_sigframe(CPUARMState *env,
struct target_rt_sigframe *sf)
{
struct target_aarch64_ctx *ctx, *extra = NULL;
struct target_fpsimd_context *fpsimd = NULL;
struct target_sve_context *sve = NULL;
uint64_t extra_datap = 0;
bool used_extra = false;
bool err = false;
int vq = 0, sve_size = 0;
target_restore_general_frame(env, sf);
ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved;
while (ctx) {
uint32_t magic, size, extra_size;
__get_user(magic, &ctx->magic);
__get_user(size, &ctx->size);
switch (magic) {
case 0:
if (size != 0) {
err = true;
goto exit;
}
if (used_extra) {
ctx = NULL;
} else {
ctx = extra;
used_extra = true;
}
continue;
case TARGET_FPSIMD_MAGIC:
if (fpsimd || size != sizeof(struct target_fpsimd_context)) {
err = true;
goto exit;
}
fpsimd = (struct target_fpsimd_context *)ctx;
break;
case TARGET_SVE_MAGIC:
if (arm_feature(env, ARM_FEATURE_SVE)) {
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
if (!sve && size == sve_size) {
sve = (struct target_sve_context *)ctx;
break;
}
}
err = true;
goto exit;
case TARGET_EXTRA_MAGIC:
if (extra || size != sizeof(struct target_extra_context)) {
err = true;
goto exit;
}
__get_user(extra_datap,
&((struct target_extra_context *)ctx)->datap);
__get_user(extra_size,
&((struct target_extra_context *)ctx)->size);
extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0);
break;
default:
/* Unknown record -- we certainly didn't generate it.
* Did we in fact get out of sync?
*/
err = true;
goto exit;
}
ctx = (void *)ctx + size;
}
/* Require FPSIMD always. */
if (fpsimd) {
target_restore_fpsimd_record(env, fpsimd);
} else {
err = true;
}
/* SVE data, if present, overwrites FPSIMD data. */
if (sve) {
target_restore_sve_record(env, sve, vq);
}
exit:
unlock_user(extra, extra_datap, 0);
return err;
}
static abi_ulong get_sigframe(struct target_sigaction *ka,
CPUARMState *env, int size)
{
abi_ulong sp;
sp = env->xregs[31];
/*
* This is the X/Open sanctioned signal stack switching.
*/
if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) {
sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
sp = (sp - size) & ~15;
return sp;
}
typedef struct {
int total_size;
int extra_base;
int extra_size;
int std_end_ofs;
int extra_ofs;
int extra_end_ofs;
} target_sigframe_layout;
static int alloc_sigframe_space(int this_size, target_sigframe_layout *l)
{
/* Make sure there will always be space for the end marker. */
const int std_size = sizeof(struct target_rt_sigframe)
- sizeof(struct target_aarch64_ctx);
int this_loc = l->total_size;
if (l->extra_base) {
/* Once we have begun an extra space, all allocations go there. */
l->extra_size += this_size;
} else if (this_size + this_loc > std_size) {
/* This allocation does not fit in the standard space. */
/* Allocate the extra record. */
l->extra_ofs = this_loc;
l->total_size += sizeof(struct target_extra_context);
/* Allocate the standard end record. */
l->std_end_ofs = l->total_size;
l->total_size += sizeof(struct target_aarch64_ctx);
/* Allocate the requested record. */
l->extra_base = this_loc = l->total_size;
l->extra_size = this_size;
}
l->total_size += this_size;
return this_loc;
}
static void target_setup_frame(int usig, struct target_sigaction *ka,
target_siginfo_t *info, target_sigset_t *set,
CPUARMState *env)
{
target_sigframe_layout layout = {
/* Begin with the size pointing to the reserved space. */
.total_size = offsetof(struct target_rt_sigframe,
uc.tuc_mcontext.__reserved),
};
int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0;
struct target_rt_sigframe *frame;
struct target_rt_frame_record *fr;
abi_ulong frame_addr, return_addr;
/* FPSIMD record is always in the standard space. */
fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context),
&layout);
/* SVE state needs saving only if it exists. */
if (arm_feature(env, ARM_FEATURE_SVE)) {
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
sve_ofs = alloc_sigframe_space(sve_size, &layout);
}
if (layout.extra_ofs) {
/* Reserve space for the extra end marker. The standard end marker
* will have been allocated when we allocated the extra record.
*/
layout.extra_end_ofs
= alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout);
} else {
/* Reserve space for the standard end marker.
* Do not use alloc_sigframe_space because we cheat
* std_size therein to reserve space for this.
*/
layout.std_end_ofs = layout.total_size;
layout.total_size += sizeof(struct target_aarch64_ctx);
}
/* We must always provide at least the standard 4K reserved space,
* even if we don't use all of it (this is part of the ABI)
*/
layout.total_size = MAX(layout.total_size,
sizeof(struct target_rt_sigframe));
/* Reserve space for the return code. On a real system this would
* be within the VDSO. So, despite the name this is not a "real"
* record within the frame.
*/
fr_ofs = layout.total_size;
layout.total_size += sizeof(struct target_rt_frame_record);
frame_addr = get_sigframe(ka, env, layout.total_size);
trace_user_setup_frame(env, frame_addr);
frame = lock_user(VERIFY_WRITE, frame_addr, layout.total_size, 0);
if (!frame) {
goto give_sigsegv;
}
target_setup_general_frame(frame, env, set);
target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env);
target_setup_end_record((void *)frame + layout.std_end_ofs);
if (layout.extra_ofs) {
target_setup_extra_record((void *)frame + layout.extra_ofs,
frame_addr + layout.extra_base,
layout.extra_size);
target_setup_end_record((void *)frame + layout.extra_end_ofs);
}
if (sve_ofs) {
target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size);
}
/* Set up the stack frame for unwinding. */
fr = (void *)frame + fr_ofs;
__put_user(env->xregs[29], &fr->fp);
__put_user(env->xregs[30], &fr->lr);
if (ka->sa_flags & TARGET_SA_RESTORER) {
return_addr = ka->sa_restorer;
} else {
/*
* mov x8,#__NR_rt_sigreturn; svc #0
* Since these are instructions they need to be put as little-endian
* regardless of target default or current CPU endianness.
*/
__put_user_e(0xd2801168, &fr->tramp[0], le);
__put_user_e(0xd4000001, &fr->tramp[1], le);
return_addr = frame_addr + fr_ofs
+ offsetof(struct target_rt_frame_record, tramp);
}
env->xregs[0] = usig;
env->xregs[31] = frame_addr;
env->xregs[29] = frame_addr + fr_ofs;
env->pc = ka->_sa_handler;
env->xregs[30] = return_addr;
if (info) {
tswap_siginfo(&frame->info, info);
env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info);
env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
}
unlock_user(frame, frame_addr, layout.total_size);
return;
give_sigsegv:
unlock_user(frame, frame_addr, layout.total_size);
force_sigsegv(usig);
}
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info, target_sigset_t *set,
CPUARMState *env)
{
target_setup_frame(sig, ka, info, set, env);
}
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUARMState *env)
{
target_setup_frame(sig, ka, 0, set, env);
}
long do_rt_sigreturn(CPUARMState *env)
{
struct target_rt_sigframe *frame = NULL;
abi_ulong frame_addr = env->xregs[31];
trace_user_do_rt_sigreturn(env, frame_addr);
if (frame_addr & 15) {
goto badframe;
}
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
if (target_restore_sigframe(env, frame)) {
goto badframe;
}
if (do_sigaltstack(frame_addr +
offsetof(struct target_rt_sigframe, uc.tuc_stack),
0, get_sp_from_cpustate(env)) == -EFAULT) {
goto badframe;
}
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
long do_sigreturn(CPUARMState *env)
{
return do_rt_sigreturn(env);
}
...@@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) ...@@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state)
return state->xregs[31]; return state->xregs[31];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* AARCH64_TARGET_SIGNAL_H */ #endif /* AARCH64_TARGET_SIGNAL_H */
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "cpu_loop-common.h"
void cpu_loop(CPUAlphaState *env)
{
CPUState *cs = CPU(alpha_env_get_cpu(env));
int trapnr;
target_siginfo_t info;
abi_long sysret;
while (1) {
bool arch_interrupt = true;
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch (trapnr) {
case EXCP_RESET:
fprintf(stderr, "Reset requested. Exit\n");
exit(EXIT_FAILURE);
break;
case EXCP_MCHK:
fprintf(stderr, "Machine check exception. Exit\n");
exit(EXIT_FAILURE);
break;
case EXCP_SMP_INTERRUPT:
case EXCP_CLK_INTERRUPT:
case EXCP_DEV_INTERRUPT:
fprintf(stderr, "External interrupt. Exit\n");
exit(EXIT_FAILURE);
break;
case EXCP_MMFAULT:
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID
? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR);
info._sifields._sigfault._addr = env->trap_arg0;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_UNALIGN:
info.si_signo = TARGET_SIGBUS;
info.si_errno = 0;
info.si_code = TARGET_BUS_ADRALN;
info._sifields._sigfault._addr = env->trap_arg0;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_OPCDEC:
do_sigill:
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPC;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_ARITH:
info.si_signo = TARGET_SIGFPE;
info.si_errno = 0;
info.si_code = TARGET_FPE_FLTINV;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_FEN:
/* No-op. Linux simply re-enables the FPU. */
break;
case EXCP_CALL_PAL:
switch (env->error_code) {
case 0x80:
/* BPT */
info.si_signo = TARGET_SIGTRAP;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case 0x81:
/* BUGCHK */
info.si_signo = TARGET_SIGTRAP;
info.si_errno = 0;
info.si_code = 0;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case 0x83:
/* CALLSYS */
trapnr = env->ir[IR_V0];
sysret = do_syscall(env, trapnr,
env->ir[IR_A0], env->ir[IR_A1],
env->ir[IR_A2], env->ir[IR_A3],
env->ir[IR_A4], env->ir[IR_A5],
0, 0);
if (sysret == -TARGET_ERESTARTSYS) {
env->pc -= 4;
break;
}
if (sysret == -TARGET_QEMU_ESIGRETURN) {
break;
}
/* Syscall writes 0 to V0 to bypass error check, similar
to how this is handled internal to Linux kernel.
(Ab)use trapnr temporarily as boolean indicating error. */
trapnr = (env->ir[IR_V0] != 0 && sysret < 0);
env->ir[IR_V0] = (trapnr ? -sysret : sysret);
env->ir[IR_A3] = trapnr;
break;
case 0x86:
/* IMB */
/* ??? We can probably elide the code using page_unprotect
that is checking for self-modifying code. Instead we
could simply call tb_flush here. Until we work out the
changes required to turn off the extra write protection,
this can be a no-op. */
break;
case 0x9E:
/* RDUNIQUE */
/* Handled in the translator for usermode. */
abort();
case 0x9F:
/* WRUNIQUE */
/* Handled in the translator for usermode. */
abort();
case 0xAA:
/* GENTRAP */
info.si_signo = TARGET_SIGFPE;
switch (env->ir[IR_A0]) {
case TARGET_GEN_INTOVF:
info.si_code = TARGET_FPE_INTOVF;
break;
case TARGET_GEN_INTDIV:
info.si_code = TARGET_FPE_INTDIV;
break;
case TARGET_GEN_FLTOVF:
info.si_code = TARGET_FPE_FLTOVF;
break;
case TARGET_GEN_FLTUND:
info.si_code = TARGET_FPE_FLTUND;
break;
case TARGET_GEN_FLTINV:
info.si_code = TARGET_FPE_FLTINV;
break;
case TARGET_GEN_FLTINE:
info.si_code = TARGET_FPE_FLTRES;
break;
case TARGET_GEN_ROPRAND:
info.si_code = 0;
break;
default:
info.si_signo = TARGET_SIGTRAP;
info.si_code = 0;
break;
}
info.si_errno = 0;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
default:
goto do_sigill;
}
break;
case EXCP_DEBUG:
info.si_signo = gdb_handlesig(cs, TARGET_SIGTRAP);
if (info.si_signo) {
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
} else {
arch_interrupt = false;
}
break;
case EXCP_INTERRUPT:
/* Just indicate that signals should be handled asap. */
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
arch_interrupt = false;
break;
default:
printf ("Unhandled trap: 0x%x\n", trapnr);
cpu_dump_state(cs, stderr, fprintf, 0);
exit(EXIT_FAILURE);
}
process_pending_signals (env);
/* Most of the traps imply a transition through PALcode, which
implies an REI instruction has been executed. Which means
that RX and LOCK_ADDR should be cleared. But there are a
few exceptions for traps internal to QEMU. */
if (arch_interrupt) {
env->flags &= ~ENV_FLAG_RX_FLAG;
env->lock_addr = -1;
}
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
int i;
for(i = 0; i < 28; i++) {
env->ir[i] = ((abi_ulong *)regs)[i];
}
env->ir[IR_SP] = regs->usp;
env->pc = regs->pc;
}
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "target_signal.h"
#include "signal-common.h"
#include "linux-user/trace.h"
struct target_sigcontext {
abi_long sc_onstack;
abi_long sc_mask;
abi_long sc_pc;
abi_long sc_ps;
abi_long sc_regs[32];
abi_long sc_ownedfp;
abi_long sc_fpregs[32];
abi_ulong sc_fpcr;
abi_ulong sc_fp_control;
abi_ulong sc_reserved1;
abi_ulong sc_reserved2;
abi_ulong sc_ssize;
abi_ulong sc_sbase;
abi_ulong sc_traparg_a0;
abi_ulong sc_traparg_a1;
abi_ulong sc_traparg_a2;
abi_ulong sc_fp_trap_pc;
abi_ulong sc_fp_trigger_sum;
abi_ulong sc_fp_trigger_inst;
};
struct target_ucontext {
abi_ulong tuc_flags;
abi_ulong tuc_link;
abi_ulong tuc_osf_sigmask;
target_stack_t tuc_stack;
struct target_sigcontext tuc_mcontext;
target_sigset_t tuc_sigmask;
};
struct target_sigframe {
struct target_sigcontext sc;
unsigned int retcode[3];
};
struct target_rt_sigframe {
target_siginfo_t info;
struct target_ucontext uc;
unsigned int retcode[3];
};
#define INSN_MOV_R30_R16 0x47fe0410
#define INSN_LDI_R0 0x201f0000
#define INSN_CALLSYS 0x00000083
static void setup_sigcontext(struct target_sigcontext *sc, CPUAlphaState *env,
abi_ulong frame_addr, target_sigset_t *set)
{
int i;
__put_user(on_sig_stack(frame_addr), &sc->sc_onstack);
__put_user(set->sig[0], &sc->sc_mask);
__put_user(env->pc, &sc->sc_pc);
__put_user(8, &sc->sc_ps);
for (i = 0; i < 31; ++i) {
__put_user(env->ir[i], &sc->sc_regs[i]);
}
__put_user(0, &sc->sc_regs[31]);
for (i = 0; i < 31; ++i) {
__put_user(env->fir[i], &sc->sc_fpregs[i]);
}
__put_user(0, &sc->sc_fpregs[31]);
__put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr);
__put_user(0, &sc->sc_traparg_a0); /* FIXME */
__put_user(0, &sc->sc_traparg_a1); /* FIXME */
__put_user(0, &sc->sc_traparg_a2); /* FIXME */
}
static void restore_sigcontext(CPUAlphaState *env,
struct target_sigcontext *sc)
{
uint64_t fpcr;
int i;
__get_user(env->pc, &sc->sc_pc);
for (i = 0; i < 31; ++i) {
__get_user(env->ir[i], &sc->sc_regs[i]);
}
for (i = 0; i < 31; ++i) {
__get_user(env->fir[i], &sc->sc_fpregs[i]);
}
__get_user(fpcr, &sc->sc_fpcr);
cpu_alpha_store_fpcr(env, fpcr);
}
static inline abi_ulong get_sigframe(struct target_sigaction *sa,
CPUAlphaState *env,
unsigned long framesize)
{
abi_ulong sp = env->ir[IR_SP];
/* This is the X/Open sanctioned signal stack switching. */
if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) {
sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
return (sp - framesize) & -32;
}
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUAlphaState *env)
{
abi_ulong frame_addr, r26;
struct target_sigframe *frame;
int err = 0;
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto give_sigsegv;
}
setup_sigcontext(&frame->sc, env, frame_addr, set);
if (ka->sa_restorer) {
r26 = ka->sa_restorer;
} else {
__put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
__put_user(INSN_LDI_R0 + TARGET_NR_sigreturn,
&frame->retcode[1]);
__put_user(INSN_CALLSYS, &frame->retcode[2]);
/* imb() */
r26 = frame_addr + offsetof(struct target_sigframe, retcode);
}
unlock_user_struct(frame, frame_addr, 1);
if (err) {
give_sigsegv:
force_sigsegv(sig);
return;
}
env->ir[IR_RA] = r26;
env->ir[IR_PV] = env->pc = ka->_sa_handler;
env->ir[IR_A0] = sig;
env->ir[IR_A1] = 0;
env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc);
env->ir[IR_SP] = frame_addr;
}
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUAlphaState *env)
{
abi_ulong frame_addr, r26;
struct target_rt_sigframe *frame;
int i, err = 0;
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto give_sigsegv;
}
tswap_siginfo(&frame->info, info);
__put_user(0, &frame->uc.tuc_flags);
__put_user(0, &frame->uc.tuc_link);
__put_user(set->sig[0], &frame->uc.tuc_osf_sigmask);
__put_user(target_sigaltstack_used.ss_sp,
&frame->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(env->ir[IR_SP]),
&frame->uc.tuc_stack.ss_flags);
__put_user(target_sigaltstack_used.ss_size,
&frame->uc.tuc_stack.ss_size);
setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set);
for (i = 0; i < TARGET_NSIG_WORDS; ++i) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
if (ka->sa_restorer) {
r26 = ka->sa_restorer;
} else {
__put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
__put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn,
&frame->retcode[1]);
__put_user(INSN_CALLSYS, &frame->retcode[2]);
/* imb(); */
r26 = frame_addr + offsetof(struct target_sigframe, retcode);
}
if (err) {
give_sigsegv:
force_sigsegv(sig);
return;
}
env->ir[IR_RA] = r26;
env->ir[IR_PV] = env->pc = ka->_sa_handler;
env->ir[IR_A0] = sig;
env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info);
env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
env->ir[IR_SP] = frame_addr;
}
long do_sigreturn(CPUAlphaState *env)
{
struct target_sigcontext *sc;
abi_ulong sc_addr = env->ir[IR_A0];
target_sigset_t target_set;
sigset_t set;
if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) {
goto badframe;
}
target_sigemptyset(&target_set);
__get_user(target_set.sig[0], &sc->sc_mask);
target_to_host_sigset_internal(&set, &target_set);
set_sigmask(&set);
restore_sigcontext(env, sc);
unlock_user_struct(sc, sc_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
long do_rt_sigreturn(CPUAlphaState *env)
{
abi_ulong frame_addr = env->ir[IR_A0];
struct target_rt_sigframe *frame;
sigset_t set;
trace_user_do_rt_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
restore_sigcontext(env, &frame->uc.tuc_mcontext);
if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe,
uc.tuc_stack),
0, env->ir[IR_SP]) == -EFAULT) {
goto badframe;
}
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
...@@ -55,4 +55,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) ...@@ -55,4 +55,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state)
#define TARGET_GEN_SUBRNG6 -24 #define TARGET_GEN_SUBRNG6 -24
#define TARGET_GEN_SUBRNG7 -25 #define TARGET_GEN_SUBRNG7 -25
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* ALPHA_TARGET_SIGNAL_H */ #endif /* ALPHA_TARGET_SIGNAL_H */
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "elf.h"
#include "cpu_loop-common.h"
#define get_user_code_u32(x, gaddr, env) \
({ abi_long __r = get_user_u32((x), (gaddr)); \
if (!__r && bswap_code(arm_sctlr_b(env))) { \
(x) = bswap32(x); \
} \
__r; \
})
#define get_user_code_u16(x, gaddr, env) \
({ abi_long __r = get_user_u16((x), (gaddr)); \
if (!__r && bswap_code(arm_sctlr_b(env))) { \
(x) = bswap16(x); \
} \
__r; \
})
#define get_user_data_u32(x, gaddr, env) \
({ abi_long __r = get_user_u32((x), (gaddr)); \
if (!__r && arm_cpu_bswap_data(env)) { \
(x) = bswap32(x); \
} \
__r; \
})
#define get_user_data_u16(x, gaddr, env) \
({ abi_long __r = get_user_u16((x), (gaddr)); \
if (!__r && arm_cpu_bswap_data(env)) { \
(x) = bswap16(x); \
} \
__r; \
})
#define put_user_data_u32(x, gaddr, env) \
({ typeof(x) __x = (x); \
if (arm_cpu_bswap_data(env)) { \
__x = bswap32(__x); \
} \
put_user_u32(__x, (gaddr)); \
})
#define put_user_data_u16(x, gaddr, env) \
({ typeof(x) __x = (x); \
if (arm_cpu_bswap_data(env)) { \
__x = bswap16(__x); \
} \
put_user_u16(__x, (gaddr)); \
})
/* Commpage handling -- there is no commpage for AArch64 */
/*
* See the Linux kernel's Documentation/arm/kernel_user_helpers.txt
* Input:
* r0 = pointer to oldval
* r1 = pointer to newval
* r2 = pointer to target value
*
* Output:
* r0 = 0 if *ptr was changed, non-0 if no exchange happened
* C set if *ptr was changed, clear if no exchange happened
*
* Note segv's in kernel helpers are a bit tricky, we can set the
* data address sensibly but the PC address is just the entry point.
*/
static void arm_kernel_cmpxchg64_helper(CPUARMState *env)
{
uint64_t oldval, newval, val;
uint32_t addr, cpsr;
target_siginfo_t info;
/* Based on the 32 bit code in do_kernel_trap */
/* XXX: This only works between threads, not between processes.
It's probably possible to implement this with native host
operations. However things like ldrex/strex are much harder so
there's not much point trying. */
start_exclusive();
cpsr = cpsr_read(env);
addr = env->regs[2];
if (get_user_u64(oldval, env->regs[0])) {
env->exception.vaddress = env->regs[0];
goto segv;
};
if (get_user_u64(newval, env->regs[1])) {
env->exception.vaddress = env->regs[1];
goto segv;
};
if (get_user_u64(val, addr)) {
env->exception.vaddress = addr;
goto segv;
}
if (val == oldval) {
val = newval;
if (put_user_u64(val, addr)) {
env->exception.vaddress = addr;
goto segv;
};
env->regs[0] = 0;
cpsr |= CPSR_C;
} else {
env->regs[0] = -1;
cpsr &= ~CPSR_C;
}
cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr);
end_exclusive();
return;
segv:
end_exclusive();
/* We get the PC of the entry address - which is as good as anything,
on a real kernel what you get depends on which mode it uses. */
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->exception.vaddress;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
/* Handle a jump to the kernel code page. */
static int
do_kernel_trap(CPUARMState *env)
{
uint32_t addr;
uint32_t cpsr;
uint32_t val;
switch (env->regs[15]) {
case 0xffff0fa0: /* __kernel_memory_barrier */
/* ??? No-op. Will need to do better for SMP. */
break;
case 0xffff0fc0: /* __kernel_cmpxchg */
/* XXX: This only works between threads, not between processes.
It's probably possible to implement this with native host
operations. However things like ldrex/strex are much harder so
there's not much point trying. */
start_exclusive();
cpsr = cpsr_read(env);
addr = env->regs[2];
/* FIXME: This should SEGV if the access fails. */
if (get_user_u32(val, addr))
val = ~env->regs[0];
if (val == env->regs[0]) {
val = env->regs[1];
/* FIXME: Check for segfaults. */
put_user_u32(val, addr);
env->regs[0] = 0;
cpsr |= CPSR_C;
} else {
env->regs[0] = -1;
cpsr &= ~CPSR_C;
}
cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr);
end_exclusive();
break;
case 0xffff0fe0: /* __kernel_get_tls */
env->regs[0] = cpu_get_tls(env);
break;
case 0xffff0f60: /* __kernel_cmpxchg64 */
arm_kernel_cmpxchg64_helper(env);
break;
default:
return 1;
}
/* Jump back to the caller. */
addr = env->regs[14];
if (addr & 1) {
env->thumb = 1;
addr &= ~1;
}
env->regs[15] = addr;
return 0;
}
void cpu_loop(CPUARMState *env)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
int trapnr;
unsigned int n, insn;
target_siginfo_t info;
uint32_t addr;
abi_ulong ret;
for(;;) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch(trapnr) {
case EXCP_UDEF:
case EXCP_NOCP:
case EXCP_INVSTATE:
{
TaskState *ts = cs->opaque;
uint32_t opcode;
int rc;
/* we handle the FPU emulation here, as Linux */
/* we get the opcode */
/* FIXME - what to do if get_user() fails? */
get_user_code_u32(opcode, env->regs[15], env);
rc = EmulateAll(opcode, &ts->fpa, env);
if (rc == 0) { /* illegal instruction */
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->regs[15];
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
} else if (rc < 0) { /* FP exception */
int arm_fpe=0;
/* translate softfloat flags to FPSR flags */
if (-rc & float_flag_invalid)
arm_fpe |= BIT_IOC;
if (-rc & float_flag_divbyzero)
arm_fpe |= BIT_DZC;
if (-rc & float_flag_overflow)
arm_fpe |= BIT_OFC;
if (-rc & float_flag_underflow)
arm_fpe |= BIT_UFC;
if (-rc & float_flag_inexact)
arm_fpe |= BIT_IXC;
FPSR fpsr = ts->fpa.fpsr;
//printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe);
if (fpsr & (arm_fpe << 16)) { /* exception enabled? */
info.si_signo = TARGET_SIGFPE;
info.si_errno = 0;
/* ordered by priority, least first */
if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES;
if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND;
if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF;
if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV;
if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV;
info._sifields._sigfault._addr = env->regs[15];
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
} else {
env->regs[15] += 4;
}
/* accumulate unenabled exceptions */
if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC))
fpsr |= BIT_IXC;
if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC))
fpsr |= BIT_UFC;
if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC))
fpsr |= BIT_OFC;
if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC))
fpsr |= BIT_DZC;
if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC))
fpsr |= BIT_IOC;
ts->fpa.fpsr=fpsr;
} else { /* everything OK */
/* increment PC */
env->regs[15] += 4;
}
}
break;
case EXCP_SWI:
case EXCP_BKPT:
{
env->eabi = 1;
/* system call */
if (trapnr == EXCP_BKPT) {
if (env->thumb) {
/* FIXME - what to do if get_user() fails? */
get_user_code_u16(insn, env->regs[15], env);
n = insn & 0xff;
env->regs[15] += 2;
} else {
/* FIXME - what to do if get_user() fails? */
get_user_code_u32(insn, env->regs[15], env);
n = (insn & 0xf) | ((insn >> 4) & 0xff0);
env->regs[15] += 4;
}
} else {
if (env->thumb) {
/* FIXME - what to do if get_user() fails? */
get_user_code_u16(insn, env->regs[15] - 2, env);
n = insn & 0xff;
} else {
/* FIXME - what to do if get_user() fails? */
get_user_code_u32(insn, env->regs[15] - 4, env);
n = insn & 0xffffff;
}
}
if (n == ARM_NR_cacheflush) {
/* nop */
} else if (n == ARM_NR_semihosting
|| n == ARM_NR_thumb_semihosting) {
env->regs[0] = do_arm_semihosting (env);
} else if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) {
/* linux syscall */
if (env->thumb || n == 0) {
n = env->regs[7];
} else {
n -= ARM_SYSCALL_BASE;
env->eabi = 0;
}
if ( n > ARM_NR_BASE) {
switch (n) {
case ARM_NR_cacheflush:
/* nop */
break;
case ARM_NR_set_tls:
cpu_set_tls(env, env->regs[0]);
env->regs[0] = 0;
break;
case ARM_NR_breakpoint:
env->regs[15] -= env->thumb ? 2 : 4;
goto excp_debug;
case ARM_NR_get_tls:
env->regs[0] = cpu_get_tls(env);
break;
default:
gemu_log("qemu: Unsupported ARM syscall: 0x%x\n",
n);
env->regs[0] = -TARGET_ENOSYS;
break;
}
} else {
ret = do_syscall(env,
n,
env->regs[0],
env->regs[1],
env->regs[2],
env->regs[3],
env->regs[4],
env->regs[5],
0, 0);
if (ret == -TARGET_ERESTARTSYS) {
env->regs[15] -= env->thumb ? 2 : 4;
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
env->regs[0] = ret;
}
}
} else {
goto error;
}
}
break;
case EXCP_SEMIHOST:
env->regs[0] = do_arm_semihosting(env);
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
addr = env->exception.vaddress;
{
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = addr;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP_DEBUG:
excp_debug:
{
int sig;
sig = gdb_handlesig(cs, TARGET_SIGTRAP);
if (sig)
{
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
}
break;
case EXCP_KERNEL_TRAP:
if (do_kernel_trap(env))
goto error;
break;
case EXCP_YIELD:
/* nothing to do here for user-mode, just resume guest code */
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
default:
error:
EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
abort();
}
process_pending_signals(env);
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
CPUState *cpu = ENV_GET_CPU(env);
TaskState *ts = cpu->opaque;
struct image_info *info = ts->info;
int i;
cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC,
CPSRWriteByInstr);
for(i = 0; i < 16; i++) {
env->regs[i] = regs->uregs[i];
}
#ifdef TARGET_WORDS_BIGENDIAN
/* Enable BE8. */
if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4
&& (info->elf_flags & EF_ARM_BE8)) {
env->uncached_cpsr |= CPSR_E;
env->cp15.sctlr_el[1] |= SCTLR_E0E;
} else {
env->cp15.sctlr_el[1] |= SCTLR_B;
}
#endif
ts->stack_base = info->start_stack;
ts->heap_base = info->brk;
/* This will be filled in on the first SYS_HEAPINFO call. */
ts->heap_limit = 0;
}
此差异已折叠。
...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) ...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state)
return state->regs[13]; return state->regs[13];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* ARM_TARGET_SIGNAL_H */ #endif /* ARM_TARGET_SIGNAL_H */
...@@ -16,6 +16,7 @@ struct target_pt_regs { ...@@ -16,6 +16,7 @@ struct target_pt_regs {
#define ARM_NR_breakpoint (ARM_NR_BASE + 1) #define ARM_NR_breakpoint (ARM_NR_BASE + 1)
#define ARM_NR_cacheflush (ARM_NR_BASE + 2) #define ARM_NR_cacheflush (ARM_NR_BASE + 2)
#define ARM_NR_set_tls (ARM_NR_BASE + 5) #define ARM_NR_set_tls (ARM_NR_BASE + 5)
#define ARM_NR_get_tls (ARM_NR_BASE + 6)
#define ARM_NR_semihosting 0x123456 #define ARM_NR_semihosting 0x123456
#define ARM_NR_thumb_semihosting 0xAB #define ARM_NR_thumb_semihosting 0xAB
......
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CPU_LOOP_COMMON_H
#define CPU_LOOP_COMMON_H
#include "exec/log.h"
#define EXCP_DUMP(env, fmt, ...) \
do { \
CPUState *cs = ENV_GET_CPU(env); \
fprintf(stderr, fmt , ## __VA_ARGS__); \
cpu_dump_state(cs, stderr, fprintf, 0); \
if (qemu_log_separate()) { \
qemu_log(fmt, ## __VA_ARGS__); \
log_cpu_state(cs, 0); \
} \
} while (0)
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs);
#endif
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "cpu_loop-common.h"
void cpu_loop(CPUCRISState *env)
{
CPUState *cs = CPU(cris_env_get_cpu(env));
int trapnr, ret;
target_siginfo_t info;
while (1) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch (trapnr) {
case 0xaa:
{
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->pregs[PR_EDA];
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
case EXCP_BREAK:
ret = do_syscall(env,
env->regs[9],
env->regs[10],
env->regs[11],
env->regs[12],
env->regs[13],
env->pregs[7],
env->pregs[11],
0, 0);
if (ret == -TARGET_ERESTARTSYS) {
env->pc -= 2;
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
env->regs[10] = ret;
}
break;
case EXCP_DEBUG:
{
int sig;
sig = gdb_handlesig(cs, TARGET_SIGTRAP);
if (sig)
{
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
}
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
default:
printf ("Unhandled trap: 0x%x\n", trapnr);
cpu_dump_state(cs, stderr, fprintf, 0);
exit(EXIT_FAILURE);
}
process_pending_signals (env);
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
CPUState *cpu = ENV_GET_CPU(env);
TaskState *ts = cpu->opaque;
struct image_info *info = ts->info;
env->regs[0] = regs->r0;
env->regs[1] = regs->r1;
env->regs[2] = regs->r2;
env->regs[3] = regs->r3;
env->regs[4] = regs->r4;
env->regs[5] = regs->r5;
env->regs[6] = regs->r6;
env->regs[7] = regs->r7;
env->regs[8] = regs->r8;
env->regs[9] = regs->r9;
env->regs[10] = regs->r10;
env->regs[11] = regs->r11;
env->regs[12] = regs->r12;
env->regs[13] = regs->r13;
env->regs[14] = info->start_stack;
env->regs[15] = regs->acr;
env->pc = regs->erp;
}
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "target_signal.h"
#include "signal-common.h"
#include "linux-user/trace.h"
struct target_sigcontext {
struct target_pt_regs regs; /* needs to be first */
uint32_t oldmask;
uint32_t usp; /* usp before stacking this gunk on it */
};
/* Signal frames. */
struct target_signal_frame {
struct target_sigcontext sc;
uint32_t extramask[TARGET_NSIG_WORDS - 1];
uint16_t retcode[4]; /* Trampoline code. */
};
struct rt_signal_frame {
siginfo_t *pinfo;
void *puc;
siginfo_t info;
ucontext_t uc;
uint16_t retcode[4]; /* Trampoline code. */
};
static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env)
{
__put_user(env->regs[0], &sc->regs.r0);
__put_user(env->regs[1], &sc->regs.r1);
__put_user(env->regs[2], &sc->regs.r2);
__put_user(env->regs[3], &sc->regs.r3);
__put_user(env->regs[4], &sc->regs.r4);
__put_user(env->regs[5], &sc->regs.r5);
__put_user(env->regs[6], &sc->regs.r6);
__put_user(env->regs[7], &sc->regs.r7);
__put_user(env->regs[8], &sc->regs.r8);
__put_user(env->regs[9], &sc->regs.r9);
__put_user(env->regs[10], &sc->regs.r10);
__put_user(env->regs[11], &sc->regs.r11);
__put_user(env->regs[12], &sc->regs.r12);
__put_user(env->regs[13], &sc->regs.r13);
__put_user(env->regs[14], &sc->usp);
__put_user(env->regs[15], &sc->regs.acr);
__put_user(env->pregs[PR_MOF], &sc->regs.mof);
__put_user(env->pregs[PR_SRP], &sc->regs.srp);
__put_user(env->pc, &sc->regs.erp);
}
static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env)
{
__get_user(env->regs[0], &sc->regs.r0);
__get_user(env->regs[1], &sc->regs.r1);
__get_user(env->regs[2], &sc->regs.r2);
__get_user(env->regs[3], &sc->regs.r3);
__get_user(env->regs[4], &sc->regs.r4);
__get_user(env->regs[5], &sc->regs.r5);
__get_user(env->regs[6], &sc->regs.r6);
__get_user(env->regs[7], &sc->regs.r7);
__get_user(env->regs[8], &sc->regs.r8);
__get_user(env->regs[9], &sc->regs.r9);
__get_user(env->regs[10], &sc->regs.r10);
__get_user(env->regs[11], &sc->regs.r11);
__get_user(env->regs[12], &sc->regs.r12);
__get_user(env->regs[13], &sc->regs.r13);
__get_user(env->regs[14], &sc->usp);
__get_user(env->regs[15], &sc->regs.acr);
__get_user(env->pregs[PR_MOF], &sc->regs.mof);
__get_user(env->pregs[PR_SRP], &sc->regs.srp);
__get_user(env->pc, &sc->regs.erp);
}
static abi_ulong get_sigframe(CPUCRISState *env, int framesize)
{
abi_ulong sp;
/* Align the stack downwards to 4. */
sp = (env->regs[R_SP] & ~3);
return sp - framesize;
}
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUCRISState *env)
{
struct target_signal_frame *frame;
abi_ulong frame_addr;
int i;
frame_addr = get_sigframe(env, sizeof *frame);
trace_user_setup_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
goto badframe;
/*
* The CRIS signal return trampoline. A real linux/CRIS kernel doesn't
* use this trampoline anymore but it sets it up for GDB.
* In QEMU, using the trampoline simplifies things a bit so we use it.
*
* This is movu.w __NR_sigreturn, r9; break 13;
*/
__put_user(0x9c5f, frame->retcode+0);
__put_user(TARGET_NR_sigreturn,
frame->retcode + 1);
__put_user(0xe93d, frame->retcode + 2);
/* Save the mask. */
__put_user(set->sig[0], &frame->sc.oldmask);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}
setup_sigcontext(&frame->sc, env);
/* Move the stack and setup the arguments for the handler. */
env->regs[R_SP] = frame_addr;
env->regs[10] = sig;
env->pc = (unsigned long) ka->_sa_handler;
/* Link SRP so the guest returns through the trampoline. */
env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode);
unlock_user_struct(frame, frame_addr, 1);
return;
badframe:
force_sigsegv(sig);
}
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUCRISState *env)
{
fprintf(stderr, "CRIS setup_rt_frame: not implemented\n");
}
long do_sigreturn(CPUCRISState *env)
{
struct target_signal_frame *frame;
abi_ulong frame_addr;
target_sigset_t target_set;
sigset_t set;
int i;
frame_addr = env->regs[R_SP];
trace_user_do_sigreturn(env, frame_addr);
/* Make sure the guest isn't playing games. */
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) {
goto badframe;
}
/* Restore blocked signals */
__get_user(target_set.sig[0], &frame->sc.oldmask);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}
target_to_host_sigset_internal(&set, &target_set);
set_sigmask(&set);
restore_sigcontext(&frame->sc, env);
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
long do_rt_sigreturn(CPUCRISState *env)
{
trace_user_do_rt_sigreturn(env, 0);
fprintf(stderr, "CRIS do_rt_sigreturn: not implemented\n");
return -TARGET_ENOSYS;
}
...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) ...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state)
return state->regs[14]; return state->regs[14];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* CRIS_TARGET_SIGNAL_H */ #endif /* CRIS_TARGET_SIGNAL_H */
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "cpu_loop-common.h"
static abi_ulong hppa_lws(CPUHPPAState *env)
{
uint32_t which = env->gr[20];
abi_ulong addr = env->gr[26];
abi_ulong old = env->gr[25];
abi_ulong new = env->gr[24];
abi_ulong size, ret;
switch (which) {
default:
return -TARGET_ENOSYS;
case 0: /* elf32 atomic 32bit cmpxchg */
if ((addr & 3) || !access_ok(VERIFY_WRITE, addr, 4)) {
return -TARGET_EFAULT;
}
old = tswap32(old);
new = tswap32(new);
ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new);
ret = tswap32(ret);
break;
case 2: /* elf32 atomic "new" cmpxchg */
size = env->gr[23];
if (size >= 4) {
return -TARGET_ENOSYS;
}
if (((addr | old | new) & ((1 << size) - 1))
|| !access_ok(VERIFY_WRITE, addr, 1 << size)
|| !access_ok(VERIFY_READ, old, 1 << size)
|| !access_ok(VERIFY_READ, new, 1 << size)) {
return -TARGET_EFAULT;
}
/* Note that below we use host-endian loads so that the cmpxchg
can be host-endian as well. */
switch (size) {
case 0:
old = *(uint8_t *)g2h(old);
new = *(uint8_t *)g2h(new);
ret = atomic_cmpxchg((uint8_t *)g2h(addr), old, new);
ret = ret != old;
break;
case 1:
old = *(uint16_t *)g2h(old);
new = *(uint16_t *)g2h(new);
ret = atomic_cmpxchg((uint16_t *)g2h(addr), old, new);
ret = ret != old;
break;
case 2:
old = *(uint32_t *)g2h(old);
new = *(uint32_t *)g2h(new);
ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new);
ret = ret != old;
break;
case 3:
{
uint64_t o64, n64, r64;
o64 = *(uint64_t *)g2h(old);
n64 = *(uint64_t *)g2h(new);
#ifdef CONFIG_ATOMIC64
r64 = atomic_cmpxchg__nocheck((uint64_t *)g2h(addr), o64, n64);
ret = r64 != o64;
#else
start_exclusive();
r64 = *(uint64_t *)g2h(addr);
ret = 1;
if (r64 == o64) {
*(uint64_t *)g2h(addr) = n64;
ret = 0;
}
end_exclusive();
#endif
}
break;
}
break;
}
env->gr[28] = ret;
return 0;
}
void cpu_loop(CPUHPPAState *env)
{
CPUState *cs = CPU(hppa_env_get_cpu(env));
target_siginfo_t info;
abi_ulong ret;
int trapnr;
while (1) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch (trapnr) {
case EXCP_SYSCALL:
ret = do_syscall(env, env->gr[20],
env->gr[26], env->gr[25],
env->gr[24], env->gr[23],
env->gr[22], env->gr[21], 0, 0);
switch (ret) {
default:
env->gr[28] = ret;
/* We arrived here by faking the gateway page. Return. */
env->iaoq_f = env->gr[31];
env->iaoq_b = env->gr[31] + 4;
break;
case -TARGET_ERESTARTSYS:
case -TARGET_QEMU_ESIGRETURN:
break;
}
break;
case EXCP_SYSCALL_LWS:
env->gr[21] = hppa_lws(env);
/* We arrived here by faking the gateway page. Return. */
env->iaoq_f = env->gr[31];
env->iaoq_b = env->gr[31] + 4;
break;
case EXCP_ITLB_MISS:
case EXCP_DTLB_MISS:
case EXCP_NA_ITLB_MISS:
case EXCP_NA_DTLB_MISS:
case EXCP_IMP:
case EXCP_DMP:
case EXCP_DMB:
case EXCP_PAGE_REF:
case EXCP_DMAR:
case EXCP_DMPI:
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
info.si_code = TARGET_SEGV_ACCERR;
info._sifields._sigfault._addr = env->cr[CR_IOR];
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_UNALIGN:
info.si_signo = TARGET_SIGBUS;
info.si_errno = 0;
info.si_code = 0;
info._sifields._sigfault._addr = env->cr[CR_IOR];
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_ILL:
case EXCP_PRIV_OPR:
case EXCP_PRIV_REG:
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->iaoq_f;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_OVERFLOW:
case EXCP_COND:
case EXCP_ASSIST:
info.si_signo = TARGET_SIGFPE;
info.si_errno = 0;
info.si_code = 0;
info._sifields._sigfault._addr = env->iaoq_f;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_DEBUG:
trapnr = gdb_handlesig(cs, TARGET_SIGTRAP);
if (trapnr) {
info.si_signo = trapnr;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, trapnr, QEMU_SI_FAULT, &info);
}
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
default:
g_assert_not_reached();
}
process_pending_signals(env);
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
int i;
for (i = 1; i < 32; i++) {
env->gr[i] = regs->gr[i];
}
env->iaoq_f = regs->iaoq[0];
env->iaoq_b = regs->iaoq[1];
}
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "target_signal.h"
#include "signal-common.h"
#include "linux-user/trace.h"
struct target_sigcontext {
abi_ulong sc_flags;
abi_ulong sc_gr[32];
uint64_t sc_fr[32];
abi_ulong sc_iasq[2];
abi_ulong sc_iaoq[2];
abi_ulong sc_sar;
};
struct target_ucontext {
abi_uint tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
abi_uint pad[1];
struct target_sigcontext tuc_mcontext;
target_sigset_t tuc_sigmask;
};
struct target_rt_sigframe {
abi_uint tramp[9];
target_siginfo_t info;
struct target_ucontext uc;
/* hidden location of upper halves of pa2.0 64-bit gregs */
};
static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env)
{
int flags = 0;
int i;
/* ??? if on_sig_stack, flags |= 1 (PARISC_SC_FLAG_ONSTACK). */
if (env->iaoq_f < TARGET_PAGE_SIZE) {
/* In the gateway page, executing a syscall. */
flags |= 2; /* PARISC_SC_FLAG_IN_SYSCALL */
__put_user(env->gr[31], &sc->sc_iaoq[0]);
__put_user(env->gr[31] + 4, &sc->sc_iaoq[1]);
} else {
__put_user(env->iaoq_f, &sc->sc_iaoq[0]);
__put_user(env->iaoq_b, &sc->sc_iaoq[1]);
}
__put_user(0, &sc->sc_iasq[0]);
__put_user(0, &sc->sc_iasq[1]);
__put_user(flags, &sc->sc_flags);
__put_user(cpu_hppa_get_psw(env), &sc->sc_gr[0]);
for (i = 1; i < 32; ++i) {
__put_user(env->gr[i], &sc->sc_gr[i]);
}
__put_user((uint64_t)env->fr0_shadow << 32, &sc->sc_fr[0]);
for (i = 1; i < 32; ++i) {
__put_user(env->fr[i], &sc->sc_fr[i]);
}
__put_user(env->cr[CR_SAR], &sc->sc_sar);
}
static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc)
{
target_ulong psw;
int i;
__get_user(psw, &sc->sc_gr[0]);
cpu_hppa_put_psw(env, psw);
for (i = 1; i < 32; ++i) {
__get_user(env->gr[i], &sc->sc_gr[i]);
}
for (i = 0; i < 32; ++i) {
__get_user(env->fr[i], &sc->sc_fr[i]);
}
cpu_hppa_loaded_fr0(env);
__get_user(env->iaoq_f, &sc->sc_iaoq[0]);
__get_user(env->iaoq_b, &sc->sc_iaoq[1]);
__get_user(env->cr[CR_SAR], &sc->sc_sar);
}
/* No, this doesn't look right, but it's copied straight from the kernel. */
#define PARISC_RT_SIGFRAME_SIZE32 \
((sizeof(struct target_rt_sigframe) + 48 + 64) & -64)
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUArchState *env)
{
abi_ulong frame_addr, sp, haddr;
struct target_rt_sigframe *frame;
int i;
sp = env->gr[30];
if (ka->sa_flags & TARGET_SA_ONSTACK) {
if (sas_ss_flags(sp) == 0) {
sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f;
}
}
frame_addr = QEMU_ALIGN_UP(sp, 64);
sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32;
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto give_sigsegv;
}
tswap_siginfo(&frame->info, info);
frame->uc.tuc_flags = 0;
frame->uc.tuc_link = 0;
__put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(get_sp_from_cpustate(env)),
&frame->uc.tuc_stack.ss_flags);
__put_user(target_sigaltstack_used.ss_size,
&frame->uc.tuc_stack.ss_size);
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
setup_sigcontext(&frame->uc.tuc_mcontext, env);
__put_user(0x34190000, frame->tramp + 0); /* ldi 0,%r25 */
__put_user(0x3414015a, frame->tramp + 1); /* ldi __NR_rt_sigreturn,%r20 */
__put_user(0xe4008200, frame->tramp + 2); /* be,l 0x100(%sr2,%r0) */
__put_user(0x08000240, frame->tramp + 3); /* nop */
unlock_user_struct(frame, frame_addr, 1);
env->gr[2] = h2g(frame->tramp);
env->gr[30] = sp;
env->gr[26] = sig;
env->gr[25] = h2g(&frame->info);
env->gr[24] = h2g(&frame->uc);
haddr = ka->_sa_handler;
if (haddr & 2) {
/* Function descriptor. */
target_ulong *fdesc, dest;
haddr &= -4;
if (!lock_user_struct(VERIFY_READ, fdesc, haddr, 1)) {
goto give_sigsegv;
}
__get_user(dest, fdesc);
__get_user(env->gr[19], fdesc + 1);
unlock_user_struct(fdesc, haddr, 1);
haddr = dest;
}
env->iaoq_f = haddr;
env->iaoq_b = haddr + 4;
return;
give_sigsegv:
force_sigsegv(sig);
}
long do_rt_sigreturn(CPUArchState *env)
{
abi_ulong frame_addr = env->gr[30] - PARISC_RT_SIGFRAME_SIZE32;
struct target_rt_sigframe *frame;
sigset_t set;
trace_user_do_rt_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
restore_sigcontext(env, &frame->uc.tuc_mcontext);
unlock_user_struct(frame, frame_addr, 0);
if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe,
uc.tuc_stack),
0, env->gr[30]) == -EFAULT) {
goto badframe;
}
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
...@@ -25,5 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) ...@@ -25,5 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state)
{ {
return state->gr[30]; return state->gr[30];
} }
#endif /* HPPA_TARGET_SIGNAL_H */ #endif /* HPPA_TARGET_SIGNAL_H */
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "cpu_loop-common.h"
/***********************************************************/
/* CPUX86 core interface */
uint64_t cpu_get_tsc(CPUX86State *env)
{
return cpu_get_host_ticks();
}
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
int flags)
{
unsigned int e1, e2;
uint32_t *p;
e1 = (addr << 16) | (limit & 0xffff);
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
e2 |= flags;
p = ptr;
p[0] = tswap32(e1);
p[1] = tswap32(e2);
}
static uint64_t *idt_table;
#ifdef TARGET_X86_64
static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
uint64_t addr, unsigned int sel)
{
uint32_t *p, e1, e2;
e1 = (addr & 0xffff) | (sel << 16);
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
p = ptr;
p[0] = tswap32(e1);
p[1] = tswap32(e2);
p[2] = tswap32(addr >> 32);
p[3] = 0;
}
/* only dpl matters as we do only user space emulation */
static void set_idt(int n, unsigned int dpl)
{
set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
}
#else
static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
uint32_t addr, unsigned int sel)
{
uint32_t *p, e1, e2;
e1 = (addr & 0xffff) | (sel << 16);
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
p = ptr;
p[0] = tswap32(e1);
p[1] = tswap32(e2);
}
/* only dpl matters as we do only user space emulation */
static void set_idt(int n, unsigned int dpl)
{
set_gate(idt_table + n, 0, dpl, 0, 0);
}
#endif
void cpu_loop(CPUX86State *env)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
int trapnr;
abi_ulong pc;
abi_ulong ret;
target_siginfo_t info;
for(;;) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch(trapnr) {
case 0x80:
/* linux syscall from int $0x80 */
ret = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP],
0, 0);
if (ret == -TARGET_ERESTARTSYS) {
env->eip -= 2;
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
env->regs[R_EAX] = ret;
}
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
/* linux syscall from syscall instruction */
ret = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_ESI],
env->regs[R_EDX],
env->regs[10],
env->regs[8],
env->regs[9],
0, 0);
if (ret == -TARGET_ERESTARTSYS) {
env->eip -= 2;
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
env->regs[R_EAX] = ret;
}
break;
#endif
case EXCP0B_NOSEG:
case EXCP0C_STACK:
info.si_signo = TARGET_SIGBUS;
info.si_errno = 0;
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP0D_GPF:
/* XXX: potential problem if ABI32 */
#ifndef TARGET_X86_64
if (env->eflags & VM_MASK) {
handle_vm86_fault(env);
} else
#endif
{
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP0E_PAGE:
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
if (!(env->error_code & 1))
info.si_code = TARGET_SEGV_MAPERR;
else
info.si_code = TARGET_SEGV_ACCERR;
info._sifields._sigfault._addr = env->cr[2];
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP00_DIVZ:
#ifndef TARGET_X86_64
if (env->eflags & VM_MASK) {
handle_vm86_trap(env, trapnr);
} else
#endif
{
/* division by zero */
info.si_signo = TARGET_SIGFPE;
info.si_errno = 0;
info.si_code = TARGET_FPE_INTDIV;
info._sifields._sigfault._addr = env->eip;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP01_DB:
case EXCP03_INT3:
#ifndef TARGET_X86_64
if (env->eflags & VM_MASK) {
handle_vm86_trap(env, trapnr);
} else
#endif
{
info.si_signo = TARGET_SIGTRAP;
info.si_errno = 0;
if (trapnr == EXCP01_DB) {
info.si_code = TARGET_TRAP_BRKPT;
info._sifields._sigfault._addr = env->eip;
} else {
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
}
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP04_INTO:
case EXCP05_BOUND:
#ifndef TARGET_X86_64
if (env->eflags & VM_MASK) {
handle_vm86_trap(env, trapnr);
} else
#endif
{
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP06_ILLOP:
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->eip;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
case EXCP_DEBUG:
{
int sig;
sig = gdb_handlesig(cs, TARGET_SIGTRAP);
if (sig)
{
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
}
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
default:
pc = env->segs[R_CS].base + env->eip;
EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
(long)pc, trapnr);
abort();
}
process_pending_signals(env);
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
env->hflags |= HF_PE_MASK | HF_CPL_MASK;
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
env->cr[4] |= CR4_OSFXSR_MASK;
env->hflags |= HF_OSFXSR_MASK;
}
#ifndef TARGET_ABI32
/* enable 64 bit mode if possible */
if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) {
fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n");
exit(EXIT_FAILURE);
}
env->cr[4] |= CR4_PAE_MASK;
env->efer |= MSR_EFER_LMA | MSR_EFER_LME;
env->hflags |= HF_LMA_MASK;
#endif
/* flags setup : we activate the IRQs by default as in user mode */
env->eflags |= IF_MASK;
/* linux register setup */
#ifndef TARGET_ABI32
env->regs[R_EAX] = regs->rax;
env->regs[R_EBX] = regs->rbx;
env->regs[R_ECX] = regs->rcx;
env->regs[R_EDX] = regs->rdx;
env->regs[R_ESI] = regs->rsi;
env->regs[R_EDI] = regs->rdi;
env->regs[R_EBP] = regs->rbp;
env->regs[R_ESP] = regs->rsp;
env->eip = regs->rip;
#else
env->regs[R_EAX] = regs->eax;
env->regs[R_EBX] = regs->ebx;
env->regs[R_ECX] = regs->ecx;
env->regs[R_EDX] = regs->edx;
env->regs[R_ESI] = regs->esi;
env->regs[R_EDI] = regs->edi;
env->regs[R_EBP] = regs->ebp;
env->regs[R_ESP] = regs->esp;
env->eip = regs->eip;
#endif
/* linux interrupt setup */
#ifndef TARGET_ABI32
env->idt.limit = 511;
#else
env->idt.limit = 255;
#endif
env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1),
PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
idt_table = g2h(env->idt.base);
set_idt(0, 0);
set_idt(1, 0);
set_idt(2, 0);
set_idt(3, 3);
set_idt(4, 3);
set_idt(5, 0);
set_idt(6, 0);
set_idt(7, 0);
set_idt(8, 0);
set_idt(9, 0);
set_idt(10, 0);
set_idt(11, 0);
set_idt(12, 0);
set_idt(13, 0);
set_idt(14, 0);
set_idt(15, 0);
set_idt(16, 0);
set_idt(17, 0);
set_idt(18, 0);
set_idt(19, 0);
set_idt(0x80, 3);
/* linux segment setup */
{
uint64_t *gdt_table;
env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES,
PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
gdt_table = g2h(env->gdt.base);
#ifdef TARGET_ABI32
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
#else
/* 64 bit code segment */
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
DESC_L_MASK |
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
#endif
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
}
cpu_x86_load_seg(env, R_CS, __USER_CS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
#ifdef TARGET_ABI32
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_FS, __USER_DS);
cpu_x86_load_seg(env, R_GS, __USER_DS);
/* This hack makes Wine work... */
env->segs[R_FS].selector = 0;
#else
cpu_x86_load_seg(env, R_DS, 0);
cpu_x86_load_seg(env, R_ES, 0);
cpu_x86_load_seg(env, R_FS, 0);
cpu_x86_load_seg(env, R_GS, 0);
#endif
}
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "target_signal.h"
#include "signal-common.h"
#include "linux-user/trace.h"
/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */
struct target_fpreg {
uint16_t significand[4];
uint16_t exponent;
};
struct target_fpxreg {
uint16_t significand[4];
uint16_t exponent;
uint16_t padding[3];
};
struct target_xmmreg {
uint32_t element[4];
};
struct target_fpstate_32 {
/* Regular FPU environment */
uint32_t cw;
uint32_t sw;
uint32_t tag;
uint32_t ipoff;
uint32_t cssel;
uint32_t dataoff;
uint32_t datasel;
struct target_fpreg st[8];
uint16_t status;
uint16_t magic; /* 0xffff = regular FPU data only */
/* FXSR FPU environment */
uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */
uint32_t mxcsr;
uint32_t reserved;
struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */
struct target_xmmreg xmm[8];
uint32_t padding[56];
};
struct target_fpstate_64 {
/* FXSAVE format */
uint16_t cw;
uint16_t sw;
uint16_t twd;
uint16_t fop;
uint64_t rip;
uint64_t rdp;
uint32_t mxcsr;
uint32_t mxcsr_mask;
uint32_t st_space[32];
uint32_t xmm_space[64];
uint32_t reserved[24];
};
#ifndef TARGET_X86_64
# define target_fpstate target_fpstate_32
#else
# define target_fpstate target_fpstate_64
#endif
struct target_sigcontext_32 {
uint16_t gs, __gsh;
uint16_t fs, __fsh;
uint16_t es, __esh;
uint16_t ds, __dsh;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t trapno;
uint32_t err;
uint32_t eip;
uint16_t cs, __csh;
uint32_t eflags;
uint32_t esp_at_signal;
uint16_t ss, __ssh;
uint32_t fpstate; /* pointer */
uint32_t oldmask;
uint32_t cr2;
};
struct target_sigcontext_64 {
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rdi;
uint64_t rsi;
uint64_t rbp;
uint64_t rbx;
uint64_t rdx;
uint64_t rax;
uint64_t rcx;
uint64_t rsp;
uint64_t rip;
uint64_t eflags;
uint16_t cs;
uint16_t gs;
uint16_t fs;
uint16_t ss;
uint64_t err;
uint64_t trapno;
uint64_t oldmask;
uint64_t cr2;
uint64_t fpstate; /* pointer */
uint64_t padding[8];
};
#ifndef TARGET_X86_64
# define target_sigcontext target_sigcontext_32
#else
# define target_sigcontext target_sigcontext_64
#endif
/* see Linux/include/uapi/asm-generic/ucontext.h */
struct target_ucontext {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
struct target_sigcontext tuc_mcontext;
target_sigset_t tuc_sigmask; /* mask last for extensibility */
};
#ifndef TARGET_X86_64
struct sigframe {
abi_ulong pretcode;
int sig;
struct target_sigcontext sc;
struct target_fpstate fpstate;
abi_ulong extramask[TARGET_NSIG_WORDS-1];
char retcode[8];
};
struct rt_sigframe {
abi_ulong pretcode;
int sig;
abi_ulong pinfo;
abi_ulong puc;
struct target_siginfo info;
struct target_ucontext uc;
struct target_fpstate fpstate;
char retcode[8];
};
#else
struct rt_sigframe {
abi_ulong pretcode;
struct target_ucontext uc;
struct target_siginfo info;
struct target_fpstate fpstate;
};
#endif
/*
* Set up a signal frame.
*/
/* XXX: save x87 state */
static void setup_sigcontext(struct target_sigcontext *sc,
struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask,
abi_ulong fpstate_addr)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
#ifndef TARGET_X86_64
uint16_t magic;
/* already locked in setup_frame() */
__put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs);
__put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs);
__put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es);
__put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds);
__put_user(env->regs[R_EDI], &sc->edi);
__put_user(env->regs[R_ESI], &sc->esi);
__put_user(env->regs[R_EBP], &sc->ebp);
__put_user(env->regs[R_ESP], &sc->esp);
__put_user(env->regs[R_EBX], &sc->ebx);
__put_user(env->regs[R_EDX], &sc->edx);
__put_user(env->regs[R_ECX], &sc->ecx);
__put_user(env->regs[R_EAX], &sc->eax);
__put_user(cs->exception_index, &sc->trapno);
__put_user(env->error_code, &sc->err);
__put_user(env->eip, &sc->eip);
__put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs);
__put_user(env->eflags, &sc->eflags);
__put_user(env->regs[R_ESP], &sc->esp_at_signal);
__put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss);
cpu_x86_fsave(env, fpstate_addr, 1);
fpstate->status = fpstate->sw;
magic = 0xffff;
__put_user(magic, &fpstate->magic);
__put_user(fpstate_addr, &sc->fpstate);
/* non-iBCS2 extensions.. */
__put_user(mask, &sc->oldmask);
__put_user(env->cr[2], &sc->cr2);
#else
__put_user(env->regs[R_EDI], &sc->rdi);
__put_user(env->regs[R_ESI], &sc->rsi);
__put_user(env->regs[R_EBP], &sc->rbp);
__put_user(env->regs[R_ESP], &sc->rsp);
__put_user(env->regs[R_EBX], &sc->rbx);
__put_user(env->regs[R_EDX], &sc->rdx);
__put_user(env->regs[R_ECX], &sc->rcx);
__put_user(env->regs[R_EAX], &sc->rax);
__put_user(env->regs[8], &sc->r8);
__put_user(env->regs[9], &sc->r9);
__put_user(env->regs[10], &sc->r10);
__put_user(env->regs[11], &sc->r11);
__put_user(env->regs[12], &sc->r12);
__put_user(env->regs[13], &sc->r13);
__put_user(env->regs[14], &sc->r14);
__put_user(env->regs[15], &sc->r15);
__put_user(cs->exception_index, &sc->trapno);
__put_user(env->error_code, &sc->err);
__put_user(env->eip, &sc->rip);
__put_user(env->eflags, &sc->eflags);
__put_user(env->segs[R_CS].selector, &sc->cs);
__put_user((uint16_t)0, &sc->gs);
__put_user((uint16_t)0, &sc->fs);
__put_user(env->segs[R_SS].selector, &sc->ss);
__put_user(mask, &sc->oldmask);
__put_user(env->cr[2], &sc->cr2);
/* fpstate_addr must be 16 byte aligned for fxsave */
assert(!(fpstate_addr & 0xf));
cpu_x86_fxsave(env, fpstate_addr);
__put_user(fpstate_addr, &sc->fpstate);
#endif
}
/*
* Determine which stack to use..
*/
static inline abi_ulong
get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size)
{
unsigned long esp;
/* Default to using normal stack */
esp = env->regs[R_ESP];
#ifdef TARGET_X86_64
esp -= 128; /* this is the redzone */
#endif
/* This is the X/Open sanctioned signal stack switching. */
if (ka->sa_flags & TARGET_SA_ONSTACK) {
if (sas_ss_flags(esp) == 0) {
esp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
} else {
#ifndef TARGET_X86_64
/* This is the legacy signal stack switching. */
if ((env->segs[R_SS].selector & 0xffff) != __USER_DS &&
!(ka->sa_flags & TARGET_SA_RESTORER) &&
ka->sa_restorer) {
esp = (unsigned long) ka->sa_restorer;
}
#endif
}
#ifndef TARGET_X86_64
return (esp - frame_size) & -8ul;
#else
return ((esp - frame_size) & (~15ul)) - 8;
#endif
}
#ifndef TARGET_X86_64
/* compare linux/arch/i386/kernel/signal.c:setup_frame() */
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUX86State *env)
{
abi_ulong frame_addr;
struct sigframe *frame;
int i;
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
goto give_sigsegv;
__put_user(sig, &frame->sig);
setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0],
frame_addr + offsetof(struct sigframe, fpstate));
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ka->sa_flags & TARGET_SA_RESTORER) {
__put_user(ka->sa_restorer, &frame->pretcode);
} else {
uint16_t val16;
abi_ulong retcode_addr;
retcode_addr = frame_addr + offsetof(struct sigframe, retcode);
__put_user(retcode_addr, &frame->pretcode);
/* This is popl %eax ; movl $,%eax ; int $0x80 */
val16 = 0xb858;
__put_user(val16, (uint16_t *)(frame->retcode+0));
__put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2));
val16 = 0x80cd;
__put_user(val16, (uint16_t *)(frame->retcode+6));
}
/* Set up registers for signal handler */
env->regs[R_ESP] = frame_addr;
env->eip = ka->_sa_handler;
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
cpu_x86_load_seg(env, R_CS, __USER_CS);
env->eflags &= ~TF_MASK;
unlock_user_struct(frame, frame_addr, 1);
return;
give_sigsegv:
force_sigsegv(sig);
}
#endif
/* compare linux/arch/x86/kernel/signal.c:setup_rt_frame() */
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUX86State *env)
{
abi_ulong frame_addr;
#ifndef TARGET_X86_64
abi_ulong addr;
#endif
struct rt_sigframe *frame;
int i;
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
goto give_sigsegv;
/* These fields are only in rt_sigframe on 32 bit */
#ifndef TARGET_X86_64
__put_user(sig, &frame->sig);
addr = frame_addr + offsetof(struct rt_sigframe, info);
__put_user(addr, &frame->pinfo);
addr = frame_addr + offsetof(struct rt_sigframe, uc);
__put_user(addr, &frame->puc);
#endif
if (ka->sa_flags & TARGET_SA_SIGINFO) {
tswap_siginfo(&frame->info, info);
}
/* Create the ucontext. */
__put_user(0, &frame->uc.tuc_flags);
__put_user(0, &frame->uc.tuc_link);
__put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(get_sp_from_cpustate(env)),
&frame->uc.tuc_stack.ss_flags);
__put_user(target_sigaltstack_used.ss_size,
&frame->uc.tuc_stack.ss_size);
setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env,
set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate));
for(i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
#ifndef TARGET_X86_64
if (ka->sa_flags & TARGET_SA_RESTORER) {
__put_user(ka->sa_restorer, &frame->pretcode);
} else {
uint16_t val16;
addr = frame_addr + offsetof(struct rt_sigframe, retcode);
__put_user(addr, &frame->pretcode);
/* This is movl $,%eax ; int $0x80 */
__put_user(0xb8, (char *)(frame->retcode+0));
__put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1));
val16 = 0x80cd;
__put_user(val16, (uint16_t *)(frame->retcode+5));
}
#else
/* XXX: Would be slightly better to return -EFAULT here if test fails
assert(ka->sa_flags & TARGET_SA_RESTORER); */
__put_user(ka->sa_restorer, &frame->pretcode);
#endif
/* Set up registers for signal handler */
env->regs[R_ESP] = frame_addr;
env->eip = ka->_sa_handler;
#ifndef TARGET_X86_64
env->regs[R_EAX] = sig;
env->regs[R_EDX] = (unsigned long)&frame->info;
env->regs[R_ECX] = (unsigned long)&frame->uc;
#else
env->regs[R_EAX] = 0;
env->regs[R_EDI] = sig;
env->regs[R_ESI] = (unsigned long)&frame->info;
env->regs[R_EDX] = (unsigned long)&frame->uc;
#endif
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_CS, __USER_CS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
env->eflags &= ~TF_MASK;
unlock_user_struct(frame, frame_addr, 1);
return;
give_sigsegv:
force_sigsegv(sig);
}
static int
restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
{
unsigned int err = 0;
abi_ulong fpstate_addr;
unsigned int tmpflags;
#ifndef TARGET_X86_64
cpu_x86_load_seg(env, R_GS, tswap16(sc->gs));
cpu_x86_load_seg(env, R_FS, tswap16(sc->fs));
cpu_x86_load_seg(env, R_ES, tswap16(sc->es));
cpu_x86_load_seg(env, R_DS, tswap16(sc->ds));
env->regs[R_EDI] = tswapl(sc->edi);
env->regs[R_ESI] = tswapl(sc->esi);
env->regs[R_EBP] = tswapl(sc->ebp);
env->regs[R_ESP] = tswapl(sc->esp);
env->regs[R_EBX] = tswapl(sc->ebx);
env->regs[R_EDX] = tswapl(sc->edx);
env->regs[R_ECX] = tswapl(sc->ecx);
env->regs[R_EAX] = tswapl(sc->eax);
env->eip = tswapl(sc->eip);
#else
env->regs[8] = tswapl(sc->r8);
env->regs[9] = tswapl(sc->r9);
env->regs[10] = tswapl(sc->r10);
env->regs[11] = tswapl(sc->r11);
env->regs[12] = tswapl(sc->r12);
env->regs[13] = tswapl(sc->r13);
env->regs[14] = tswapl(sc->r14);
env->regs[15] = tswapl(sc->r15);
env->regs[R_EDI] = tswapl(sc->rdi);
env->regs[R_ESI] = tswapl(sc->rsi);
env->regs[R_EBP] = tswapl(sc->rbp);
env->regs[R_EBX] = tswapl(sc->rbx);
env->regs[R_EDX] = tswapl(sc->rdx);
env->regs[R_EAX] = tswapl(sc->rax);
env->regs[R_ECX] = tswapl(sc->rcx);
env->regs[R_ESP] = tswapl(sc->rsp);
env->eip = tswapl(sc->rip);
#endif
cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3);
cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3);
tmpflags = tswapl(sc->eflags);
env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5);
// regs->orig_eax = -1; /* disable syscall checks */
fpstate_addr = tswapl(sc->fpstate);
if (fpstate_addr != 0) {
if (!access_ok(VERIFY_READ, fpstate_addr,
sizeof(struct target_fpstate)))
goto badframe;
#ifndef TARGET_X86_64
cpu_x86_frstor(env, fpstate_addr, 1);
#else
cpu_x86_fxrstor(env, fpstate_addr);
#endif
}
return err;
badframe:
return 1;
}
/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */
#ifndef TARGET_X86_64
long do_sigreturn(CPUX86State *env)
{
struct sigframe *frame;
abi_ulong frame_addr = env->regs[R_ESP] - 8;
target_sigset_t target_set;
sigset_t set;
int i;
trace_user_do_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
goto badframe;
/* set blocked signals */
__get_user(target_set.sig[0], &frame->sc.oldmask);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}
target_to_host_sigset_internal(&set, &target_set);
set_sigmask(&set);
/* restore registers */
if (restore_sigcontext(env, &frame->sc))
goto badframe;
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
#endif
long do_rt_sigreturn(CPUX86State *env)
{
abi_ulong frame_addr;
struct rt_sigframe *frame;
sigset_t set;
frame_addr = env->regs[R_ESP] - sizeof(abi_ulong);
trace_user_do_rt_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
goto badframe;
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) {
goto badframe;
}
if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0,
get_sp_from_cpustate(env)) == -EFAULT) {
goto badframe;
}
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
...@@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) ...@@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUX86State *state)
return state->regs[R_ESP]; return state->regs[R_ESP];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* I386_TARGET_SIGNAL_H */ #endif /* I386_TARGET_SIGNAL_H */
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "cpu_loop-common.h"
void cpu_loop(CPUM68KState *env)
{
CPUState *cs = CPU(m68k_env_get_cpu(env));
int trapnr;
unsigned int n;
target_siginfo_t info;
TaskState *ts = cs->opaque;
for(;;) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch(trapnr) {
case EXCP_ILLEGAL:
{
if (ts->sim_syscalls) {
uint16_t nr;
get_user_u16(nr, env->pc + 2);
env->pc += 4;
do_m68k_simcall(env, nr);
} else {
goto do_sigill;
}
}
break;
case EXCP_HALT_INSN:
/* Semihosing syscall. */
env->pc += 4;
do_m68k_semihosting(env, env->dregs[0]);
break;
case EXCP_LINEA:
case EXCP_LINEF:
case EXCP_UNSUPPORTED:
do_sigill:
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_CHK:
info.si_signo = TARGET_SIGFPE;
info.si_errno = 0;
info.si_code = TARGET_FPE_INTOVF;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_DIV0:
info.si_signo = TARGET_SIGFPE;
info.si_errno = 0;
info.si_code = TARGET_FPE_INTDIV;
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
case EXCP_TRAP0:
{
abi_long ret;
ts->sim_syscalls = 0;
n = env->dregs[0];
env->pc += 2;
ret = do_syscall(env,
n,
env->dregs[1],
env->dregs[2],
env->dregs[3],
env->dregs[4],
env->dregs[5],
env->aregs[0],
0, 0);
if (ret == -TARGET_ERESTARTSYS) {
env->pc -= 2;
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
env->dregs[0] = ret;
}
}
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
case EXCP_ACCESS:
{
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->mmu.ar;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
break;
case EXCP_DEBUG:
{
int sig;
sig = gdb_handlesig(cs, TARGET_SIGTRAP);
if (sig)
{
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
}
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
default:
EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
abort();
}
process_pending_signals(env);
}
}
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
CPUState *cpu = ENV_GET_CPU(env);
TaskState *ts = cpu->opaque;
struct image_info *info = ts->info;
env->pc = regs->pc;
env->dregs[0] = regs->d0;
env->dregs[1] = regs->d1;
env->dregs[2] = regs->d2;
env->dregs[3] = regs->d3;
env->dregs[4] = regs->d4;
env->dregs[5] = regs->d5;
env->dregs[6] = regs->d6;
env->dregs[7] = regs->d7;
env->aregs[0] = regs->a0;
env->aregs[1] = regs->a1;
env->aregs[2] = regs->a2;
env->aregs[3] = regs->a3;
env->aregs[4] = regs->a4;
env->aregs[5] = regs->a5;
env->aregs[6] = regs->a6;
env->aregs[7] = regs->usp;
env->sr = regs->sr;
ts->sim_syscalls = 1;
ts->stack_base = info->start_stack;
ts->heap_base = info->brk;
/* This will be filled in on the first SYS_HEAPINFO call. */
ts->heap_limit = 0;
}
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu.h"
#include "target_signal.h"
#include "signal-common.h"
#include "linux-user/trace.h"
struct target_sigcontext {
abi_ulong sc_mask;
abi_ulong sc_usp;
abi_ulong sc_d0;
abi_ulong sc_d1;
abi_ulong sc_a0;
abi_ulong sc_a1;
unsigned short sc_sr;
abi_ulong sc_pc;
};
struct target_sigframe
{
abi_ulong pretcode;
int sig;
int code;
abi_ulong psc;
char retcode[8];
abi_ulong extramask[TARGET_NSIG_WORDS-1];
struct target_sigcontext sc;
};
typedef int target_greg_t;
#define TARGET_NGREG 18
typedef target_greg_t target_gregset_t[TARGET_NGREG];
typedef struct target_fpregset {
int f_fpcntl[3];
int f_fpregs[8*3];
} target_fpregset_t;
struct target_mcontext {
int version;
target_gregset_t gregs;
target_fpregset_t fpregs;
};
#define TARGET_MCONTEXT_VERSION 2
struct target_ucontext {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
struct target_mcontext tuc_mcontext;
abi_long tuc_filler[80];
target_sigset_t tuc_sigmask;
};
struct target_rt_sigframe
{
abi_ulong pretcode;
int sig;
abi_ulong pinfo;
abi_ulong puc;
char retcode[8];
struct target_siginfo info;
struct target_ucontext uc;
};
static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env,
abi_ulong mask)
{
uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env);
__put_user(mask, &sc->sc_mask);
__put_user(env->aregs[7], &sc->sc_usp);
__put_user(env->dregs[0], &sc->sc_d0);
__put_user(env->dregs[1], &sc->sc_d1);
__put_user(env->aregs[0], &sc->sc_a0);
__put_user(env->aregs[1], &sc->sc_a1);
__put_user(sr, &sc->sc_sr);
__put_user(env->pc, &sc->sc_pc);
}
static void
restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc)
{
int temp;
__get_user(env->aregs[7], &sc->sc_usp);
__get_user(env->dregs[0], &sc->sc_d0);
__get_user(env->dregs[1], &sc->sc_d1);
__get_user(env->aregs[0], &sc->sc_a0);
__get_user(env->aregs[1], &sc->sc_a1);
__get_user(env->pc, &sc->sc_pc);
__get_user(temp, &sc->sc_sr);
cpu_m68k_set_ccr(env, temp);
}
/*
* Determine which stack to use..
*/
static inline abi_ulong
get_sigframe(struct target_sigaction *ka, CPUM68KState *regs,
size_t frame_size)
{
unsigned long sp;
sp = regs->aregs[7];
/* This is the X/Open sanctioned signal stack switching. */
if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) {
sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
return ((sp - frame_size) & -8UL);
}
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUM68KState *env)
{
struct target_sigframe *frame;
abi_ulong frame_addr;
abi_ulong retcode_addr;
abi_ulong sc_addr;
int i;
frame_addr = get_sigframe(ka, env, sizeof *frame);
trace_user_setup_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto give_sigsegv;
}
__put_user(sig, &frame->sig);
sc_addr = frame_addr + offsetof(struct target_sigframe, sc);
__put_user(sc_addr, &frame->psc);
setup_sigcontext(&frame->sc, env, set->sig[0]);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}
/* Set up to return from userspace. */
retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode);
__put_user(retcode_addr, &frame->pretcode);
/* moveq #,d0; trap #0 */
__put_user(0x70004e40 + (TARGET_NR_sigreturn << 16),
(uint32_t *)(frame->retcode));
/* Set up to return from userspace */
env->aregs[7] = frame_addr;
env->pc = ka->_sa_handler;
unlock_user_struct(frame, frame_addr, 1);
return;
give_sigsegv:
force_sigsegv(sig);
}
static inline void target_rt_save_fpu_state(struct target_ucontext *uc,
CPUM68KState *env)
{
int i;
target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs;
__put_user(env->fpcr, &fpregs->f_fpcntl[0]);
__put_user(env->fpsr, &fpregs->f_fpcntl[1]);
/* fpiar is not emulated */
for (i = 0; i < 8; i++) {
uint32_t high = env->fregs[i].d.high << 16;
__put_user(high, &fpregs->f_fpregs[i * 3]);
__put_user(env->fregs[i].d.low,
(uint64_t *)&fpregs->f_fpregs[i * 3 + 1]);
}
}
static inline int target_rt_setup_ucontext(struct target_ucontext *uc,
CPUM68KState *env)
{
target_greg_t *gregs = uc->tuc_mcontext.gregs;
uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env);
__put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version);
__put_user(env->dregs[0], &gregs[0]);
__put_user(env->dregs[1], &gregs[1]);
__put_user(env->dregs[2], &gregs[2]);
__put_user(env->dregs[3], &gregs[3]);
__put_user(env->dregs[4], &gregs[4]);
__put_user(env->dregs[5], &gregs[5]);
__put_user(env->dregs[6], &gregs[6]);
__put_user(env->dregs[7], &gregs[7]);
__put_user(env->aregs[0], &gregs[8]);
__put_user(env->aregs[1], &gregs[9]);
__put_user(env->aregs[2], &gregs[10]);
__put_user(env->aregs[3], &gregs[11]);
__put_user(env->aregs[4], &gregs[12]);
__put_user(env->aregs[5], &gregs[13]);
__put_user(env->aregs[6], &gregs[14]);
__put_user(env->aregs[7], &gregs[15]);
__put_user(env->pc, &gregs[16]);
__put_user(sr, &gregs[17]);
target_rt_save_fpu_state(uc, env);
return 0;
}
static inline void target_rt_restore_fpu_state(CPUM68KState *env,
struct target_ucontext *uc)
{
int i;
target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs;
uint32_t fpcr;
__get_user(fpcr, &fpregs->f_fpcntl[0]);
cpu_m68k_set_fpcr(env, fpcr);
__get_user(env->fpsr, &fpregs->f_fpcntl[1]);
/* fpiar is not emulated */
for (i = 0; i < 8; i++) {
uint32_t high;
__get_user(high, &fpregs->f_fpregs[i * 3]);
env->fregs[i].d.high = high >> 16;
__get_user(env->fregs[i].d.low,
(uint64_t *)&fpregs->f_fpregs[i * 3 + 1]);
}
}
static inline int target_rt_restore_ucontext(CPUM68KState *env,
struct target_ucontext *uc)
{
int temp;
target_greg_t *gregs = uc->tuc_mcontext.gregs;
__get_user(temp, &uc->tuc_mcontext.version);
if (temp != TARGET_MCONTEXT_VERSION)
goto badframe;
/* restore passed registers */
__get_user(env->dregs[0], &gregs[0]);
__get_user(env->dregs[1], &gregs[1]);
__get_user(env->dregs[2], &gregs[2]);
__get_user(env->dregs[3], &gregs[3]);
__get_user(env->dregs[4], &gregs[4]);
__get_user(env->dregs[5], &gregs[5]);
__get_user(env->dregs[6], &gregs[6]);
__get_user(env->dregs[7], &gregs[7]);
__get_user(env->aregs[0], &gregs[8]);
__get_user(env->aregs[1], &gregs[9]);
__get_user(env->aregs[2], &gregs[10]);
__get_user(env->aregs[3], &gregs[11]);
__get_user(env->aregs[4], &gregs[12]);
__get_user(env->aregs[5], &gregs[13]);
__get_user(env->aregs[6], &gregs[14]);
__get_user(env->aregs[7], &gregs[15]);
__get_user(env->pc, &gregs[16]);
__get_user(temp, &gregs[17]);
cpu_m68k_set_ccr(env, temp);
target_rt_restore_fpu_state(env, uc);
return 0;
badframe:
return 1;
}
void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUM68KState *env)
{
struct target_rt_sigframe *frame;
abi_ulong frame_addr;
abi_ulong retcode_addr;
abi_ulong info_addr;
abi_ulong uc_addr;
int err = 0;
int i;
frame_addr = get_sigframe(ka, env, sizeof *frame);
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto give_sigsegv;
}
__put_user(sig, &frame->sig);
info_addr = frame_addr + offsetof(struct target_rt_sigframe, info);
__put_user(info_addr, &frame->pinfo);
uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc);
__put_user(uc_addr, &frame->puc);
tswap_siginfo(&frame->info, info);
/* Create the ucontext */
__put_user(0, &frame->uc.tuc_flags);
__put_user(0, &frame->uc.tuc_link);
__put_user(target_sigaltstack_used.ss_sp,
&frame->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(env->aregs[7]),
&frame->uc.tuc_stack.ss_flags);
__put_user(target_sigaltstack_used.ss_size,
&frame->uc.tuc_stack.ss_size);
err |= target_rt_setup_ucontext(&frame->uc, env);
if (err)
goto give_sigsegv;
for(i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
/* Set up to return from userspace. */
retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode);
__put_user(retcode_addr, &frame->pretcode);
/* moveq #,d0; notb d0; trap #0 */
__put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16),
(uint32_t *)(frame->retcode + 0));
__put_user(0x4e40, (uint16_t *)(frame->retcode + 4));
if (err)
goto give_sigsegv;
/* Set up to return from userspace */
env->aregs[7] = frame_addr;
env->pc = ka->_sa_handler;
unlock_user_struct(frame, frame_addr, 1);
return;
give_sigsegv:
unlock_user_struct(frame, frame_addr, 1);
force_sigsegv(sig);
}
long do_sigreturn(CPUM68KState *env)
{
struct target_sigframe *frame;
abi_ulong frame_addr = env->aregs[7] - 4;
target_sigset_t target_set;
sigset_t set;
int i;
trace_user_do_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
goto badframe;
/* set blocked signals */
__get_user(target_set.sig[0], &frame->sc.sc_mask);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}
target_to_host_sigset_internal(&set, &target_set);
set_sigmask(&set);
/* restore registers */
restore_sigcontext(env, &frame->sc);
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
long do_rt_sigreturn(CPUM68KState *env)
{
struct target_rt_sigframe *frame;
abi_ulong frame_addr = env->aregs[7] - 4;
sigset_t set;
trace_user_do_rt_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
goto badframe;
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
/* restore registers */
if (target_rt_restore_ucontext(env, &frame->uc))
goto badframe;
if (do_sigaltstack(frame_addr +
offsetof(struct target_rt_sigframe, uc.tuc_stack),
0, get_sp_from_cpustate(env)) == -EFAULT)
goto badframe;
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) ...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state)
return state->aregs[7]; return state->aregs[7];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* M68K_TARGET_SIGNAL_H */ #endif /* M68K_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) ...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUMBState *state)
return state->regs[1]; return state->regs[1];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* MICROBLAZE_TARGET_SIGNAL_H */ #endif /* MICROBLAZE_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
...@@ -26,5 +26,8 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) ...@@ -26,5 +26,8 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state)
return state->active_tc.gpr[29]; return state->active_tc.gpr[29];
} }
#if defined(TARGET_ABI_MIPSO32)
/* compare linux/arch/mips/kernel/signal.c:setup_frame() */
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif
#endif /* MIPS_TARGET_SIGNAL_H */ #endif /* MIPS_TARGET_SIGNAL_H */
/*
* qemu user cpu loop
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "../mips/cpu_loop.c"
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define MIPS_TARGET_SIGNAL_H /* to only include mips64/target_signal.h */
#include "../mips/signal.c"
...@@ -25,6 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) ...@@ -25,6 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state)
{ {
return state->active_tc.gpr[29]; return state->active_tc.gpr[29];
} }
#endif /* MIPS64_TARGET_SIGNAL_H */ #endif /* MIPS64_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
...@@ -22,5 +22,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) ...@@ -22,5 +22,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state)
{ {
return state->regs[R_SP]; return state->regs[R_SP];
} }
#endif /* TARGET_SIGNAL_H */ #endif /* TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
...@@ -22,6 +22,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) ...@@ -22,6 +22,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state)
{ {
return cpu_get_gpr(state, 1); return cpu_get_gpr(state, 1);
} }
#endif /* OPENRISC_TARGET_SIGNAL_H */ #endif /* OPENRISC_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
...@@ -26,5 +26,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) ...@@ -26,5 +26,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state)
return state->gpr[1]; return state->gpr[1];
} }
#if !defined(TARGET_PPC64)
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif
#endif /* PPC_TARGET_SIGNAL_H */ #endif /* PPC_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
...@@ -19,5 +19,4 @@ static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) ...@@ -19,5 +19,4 @@ static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state)
{ {
return state->gpr[xSP]; return state->gpr[xSP];
} }
#endif /* TARGET_SIGNAL_H */ #endif /* TARGET_SIGNAL_H */
...@@ -45,7 +45,7 @@ struct target_pt_regs { ...@@ -45,7 +45,7 @@ struct target_pt_regs {
#else #else
#define UNAME_MACHINE "riscv64" #define UNAME_MACHINE "riscv64"
#endif #endif
#define UNAME_MINIMUM_RELEASE "3.8.0" #define UNAME_MINIMUM_RELEASE "4.15.0"
#define TARGET_MINSIGSTKSZ 2048 #define TARGET_MINSIGSTKSZ 2048
#define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_CURRENT 1
......
此差异已折叠。
此差异已折叠。
...@@ -23,5 +23,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) ...@@ -23,5 +23,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state)
return state->regs[15]; return state->regs[15];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* S390X_TARGET_SIGNAL_H */ #endif /* S390X_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) ...@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state)
return state->gregs[15]; return state->gregs[15];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* SH4_TARGET_SIGNAL_H */ #endif /* SH4_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -33,5 +33,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) ...@@ -33,5 +33,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state)
return state->regwptr[UREG_FP]; return state->regwptr[UREG_FP];
} }
#define TARGET_ARCH_HAS_SETUP_FRAME
#endif /* SPARC_TARGET_SIGNAL_H */ #endif /* SPARC_TARGET_SIGNAL_H */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册