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:
Peter Maydell
2018-04-30 10:43:41 +01:00
71 changed files with 11888 additions and 10882 deletions
+2 -1
View File
@@ -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
+182
View File
@@ -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
+1
View File
@@ -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 */
+225
View File
@@ -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;
}
+280
View File
@@ -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;
}
+1
View File
@@ -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 */
+459
View File
@@ -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
+1 -1
View File
@@ -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 */
+1
View File
@@ -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
+37
View File
@@ -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
+115
View File
@@ -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;
}
+189
View File
@@ -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;
}
+1 -1
View File
@@ -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 */
+211
View File
@@ -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];
}
+210
View File
@@ -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;
}
-1
View File
@@ -25,5 +25,4 @@ static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state)
{
return state->gr[30];
}
#endif /* HPPA_TARGET_SIGNAL_H */
+369
View File
@@ -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