diff options
Diffstat (limited to 'linux-user/xtensa/signal.c')
-rw-r--r-- | linux-user/xtensa/signal.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c new file mode 100644 index 000000000..81572a5fc --- /dev/null +++ b/linux-user/xtensa/signal.c @@ -0,0 +1,282 @@ +/* + * 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 "user-internals.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong sc_pc; + abi_ulong sc_ps; + abi_ulong sc_lbeg; + abi_ulong sc_lend; + abi_ulong sc_lcount; + abi_ulong sc_sar; + abi_ulong sc_acclo; + abi_ulong sc_acchi; + abi_ulong sc_a[16]; + abi_ulong sc_xtregs; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + target_siginfo_t info; + struct target_ucontext uc; + /* TODO: xtregs */ + uint8_t retcode[6]; + abi_ulong window[4]; +}; + +static abi_ulong get_sigframe(struct target_sigaction *sa, + CPUXtensaState *env, + unsigned long framesize) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), sa); + + return (sp - framesize) & -16; +} + +static int flush_window_regs(CPUXtensaState *env) +{ + uint32_t wb = env->sregs[WINDOW_BASE]; + uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1); + unsigned d = ctz32(ws) + 1; + unsigned i; + int ret = 0; + + for (i = d; i < env->config->nareg / 4; i += d) { + uint32_t ssp, osp; + unsigned j; + + ws >>= d; + xtensa_rotate_window(env, d); + + if (ws & 0x1) { + ssp = env->regs[5]; + d = 1; + } else if (ws & 0x2) { + ssp = env->regs[9]; + ret |= get_user_ual(osp, env->regs[1] - 12); + osp -= 32; + d = 2; + } else if (ws & 0x4) { + ssp = env->regs[13]; + ret |= get_user_ual(osp, env->regs[1] - 12); + osp -= 48; + d = 3; + } else { + g_assert_not_reached(); + } + + for (j = 0; j < 4; ++j) { + ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4); + } + for (j = 4; j < d * 4; ++j) { + ret |= put_user_ual(env->regs[j], osp - 16 + j * 4); + } + } + xtensa_rotate_window(env, d); + g_assert(env->sregs[WINDOW_BASE] == wb); + return ret == 0; +} + +static int setup_sigcontext(struct target_rt_sigframe *frame, + CPUXtensaState *env) +{ + struct target_sigcontext *sc = &frame->uc.tuc_mcontext; + int i; + + __put_user(env->pc, &sc->sc_pc); + __put_user(env->sregs[PS], &sc->sc_ps); + __put_user(env->sregs[LBEG], &sc->sc_lbeg); + __put_user(env->sregs[LEND], &sc->sc_lend); + __put_user(env->sregs[LCOUNT], &sc->sc_lcount); + if (!flush_window_regs(env)) { + return 0; + } + for (i = 0; i < 16; ++i) { + __put_user(env->regs[i], sc->sc_a + i); + } + __put_user(0, &sc->sc_xtregs); + /* TODO: xtregs */ + return 1; +} + +static void install_sigtramp(uint8_t *tramp) +{ +#ifdef TARGET_WORDS_BIGENDIAN + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &tramp[0]); + __put_user(0x0a, &tramp[1]); + __put_user(TARGET_NR_rt_sigreturn, &tramp[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &tramp[3]); + __put_user(0x05, &tramp[4]); + __put_user(0x00, &tramp[5]); +#else + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &tramp[0]); + __put_user(0xa0, &tramp[1]); + __put_user(TARGET_NR_rt_sigreturn, &tramp[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &tramp[3]); + __put_user(0x50, &tramp[4]); + __put_user(0x00, &tramp[5]); +#endif +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUXtensaState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + uint32_t ra; + bool abi_call0; + unsigned base; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + if (ka->sa_flags & SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); + if (!setup_sigcontext(frame, env)) { + unlock_user_struct(frame, frame_addr, 0); + goto give_sigsegv; + } + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (ka->sa_flags & TARGET_SA_RESTORER) { + ra = ka->sa_restorer; + } else { + /* Not used, but retain for ABI compatibility. */ + install_sigtramp(frame->retcode); + ra = default_rt_sigreturn; + } + memset(env->regs, 0, sizeof(env->regs)); + env->pc = ka->_sa_handler; + env->regs[1] = frame_addr; + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + + abi_call0 = (env->sregs[PS] & PS_WOE) == 0; + env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT); + + if (abi_call0) { + base = 0; + env->regs[base] = ra; + } else { + env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT); + base = 4; + env->regs[base] = (ra & 0x3fffffff) | 0x40000000; + } + env->regs[base + 2] = sig; + env->regs[base + 3] = frame_addr + offsetof(struct target_rt_sigframe, + info); + env->regs[base + 4] = frame_addr + offsetof(struct target_rt_sigframe, uc); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); + return; +} + +static void restore_sigcontext(CPUXtensaState *env, + struct target_rt_sigframe *frame) +{ + struct target_sigcontext *sc = &frame->uc.tuc_mcontext; + uint32_t ps; + int i; + + __get_user(env->pc, &sc->sc_pc); + __get_user(ps, &sc->sc_ps); + __get_user(env->sregs[LBEG], &sc->sc_lbeg); + __get_user(env->sregs[LEND], &sc->sc_lend); + __get_user(env->sregs[LCOUNT], &sc->sc_lcount); + + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + env->sregs[PS] = deposit32(env->sregs[PS], + PS_CALLINC_SHIFT, + PS_CALLINC_LEN, + extract32(ps, PS_CALLINC_SHIFT, + PS_CALLINC_LEN)); + for (i = 0; i < 16; ++i) { + __get_user(env->regs[i], sc->sc_a + i); + } + /* TODO: xtregs */ +} + +long do_rt_sigreturn(CPUXtensaState *env) +{ + abi_ulong frame_addr = env->regs[1]; + 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); + target_restore_altstack(&frame->uc.tuc_stack, env); + + 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; +} + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint8_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0); + assert(tramp != NULL); + + default_rt_sigreturn = sigtramp_page; + install_sigtramp(tramp); + unlock_user(tramp, sigtramp_page, 6); +} |