mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
obj-y = main.o syscall.o strace.o mmap.o signal.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_I386) += vm86.o
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state)
|
||||
return state->xregs[31];
|
||||
}
|
||||
|
||||
#define TARGET_ARCH_HAS_SETUP_FRAME
|
||||
#endif /* AARCH64_TARGET_SIGNAL_H */
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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)
|
||||
#define TARGET_GEN_SUBRNG6 -24
|
||||
#define TARGET_GEN_SUBRNG7 -25
|
||||
|
||||
#define TARGET_ARCH_HAS_SETUP_FRAME
|
||||
#endif /* ALPHA_TARGET_SIGNAL_H */
|
||||
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,5 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state)
|
||||
return state->regs[13];
|
||||
}
|
||||
|
||||
|
||||
#define TARGET_ARCH_HAS_SETUP_FRAME
|
||||
#endif /* ARM_TARGET_SIGNAL_H */
|
||||
|
||||
@@ -16,6 +16,7 @@ struct target_pt_regs {
|
||||
#define ARM_NR_breakpoint (ARM_NR_BASE + 1)
|
||||
#define ARM_NR_cacheflush (ARM_NR_BASE + 2)
|
||||
#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_thumb_semihosting 0xAB
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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)
|
||||
return state->regs[14];
|
||||
}
|
||||
|
||||
|
||||
#define TARGET_ARCH_HAS_SETUP_FRAME
|
||||
#endif /* CRIS_TARGET_SIGNAL_H */
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
return state->gr[30];
|
||||
}
|
||||
|
||||
#endif /* HPPA_TARGET_SIGNAL_H */
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user