xref: /qemu/linux-user/mips/signal.c (revision 3b249d2661c752e75ef6d2d4ac63bdf9a921dd4b)
1befb7447SLaurent Vivier /*
2befb7447SLaurent Vivier  *  Emulation of Linux signals
3befb7447SLaurent Vivier  *
4befb7447SLaurent Vivier  *  Copyright (c) 2003 Fabrice Bellard
5befb7447SLaurent Vivier  *
6befb7447SLaurent Vivier  *  This program is free software; you can redistribute it and/or modify
7befb7447SLaurent Vivier  *  it under the terms of the GNU General Public License as published by
8befb7447SLaurent Vivier  *  the Free Software Foundation; either version 2 of the License, or
9befb7447SLaurent Vivier  *  (at your option) any later version.
10befb7447SLaurent Vivier  *
11befb7447SLaurent Vivier  *  This program is distributed in the hope that it will be useful,
12befb7447SLaurent Vivier  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13befb7447SLaurent Vivier  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14befb7447SLaurent Vivier  *  GNU General Public License for more details.
15befb7447SLaurent Vivier  *
16befb7447SLaurent Vivier  *  You should have received a copy of the GNU General Public License
17befb7447SLaurent Vivier  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18befb7447SLaurent Vivier  */
198949bef1SLaurent Vivier #include "qemu/osdep.h"
208949bef1SLaurent Vivier #include "qemu.h"
21*3b249d26SPeter Maydell #include "user-internals.h"
228949bef1SLaurent Vivier #include "signal-common.h"
238949bef1SLaurent Vivier #include "linux-user/trace.h"
248949bef1SLaurent Vivier 
258949bef1SLaurent Vivier # if defined(TARGET_ABI_MIPSO32)
268949bef1SLaurent Vivier struct target_sigcontext {
278949bef1SLaurent Vivier     uint32_t   sc_regmask;     /* Unused */
288949bef1SLaurent Vivier     uint32_t   sc_status;
298949bef1SLaurent Vivier     uint64_t   sc_pc;
308949bef1SLaurent Vivier     uint64_t   sc_regs[32];
318949bef1SLaurent Vivier     uint64_t   sc_fpregs[32];
328949bef1SLaurent Vivier     uint32_t   sc_ownedfp;     /* Unused */
338949bef1SLaurent Vivier     uint32_t   sc_fpc_csr;
348949bef1SLaurent Vivier     uint32_t   sc_fpc_eir;     /* Unused */
358949bef1SLaurent Vivier     uint32_t   sc_used_math;
368949bef1SLaurent Vivier     uint32_t   sc_dsp;         /* dsp status, was sc_ssflags */
378949bef1SLaurent Vivier     uint32_t   pad0;
388949bef1SLaurent Vivier     uint64_t   sc_mdhi;
398949bef1SLaurent Vivier     uint64_t   sc_mdlo;
408949bef1SLaurent Vivier     target_ulong   sc_hi1;         /* Was sc_cause */
418949bef1SLaurent Vivier     target_ulong   sc_lo1;         /* Was sc_badvaddr */
428949bef1SLaurent Vivier     target_ulong   sc_hi2;         /* Was sc_sigset[4] */
438949bef1SLaurent Vivier     target_ulong   sc_lo2;
448949bef1SLaurent Vivier     target_ulong   sc_hi3;
458949bef1SLaurent Vivier     target_ulong   sc_lo3;
468949bef1SLaurent Vivier };
478949bef1SLaurent Vivier # else /* N32 || N64 */
488949bef1SLaurent Vivier struct target_sigcontext {
498949bef1SLaurent Vivier     uint64_t sc_regs[32];
508949bef1SLaurent Vivier     uint64_t sc_fpregs[32];
518949bef1SLaurent Vivier     uint64_t sc_mdhi;
528949bef1SLaurent Vivier     uint64_t sc_hi1;
538949bef1SLaurent Vivier     uint64_t sc_hi2;
548949bef1SLaurent Vivier     uint64_t sc_hi3;
558949bef1SLaurent Vivier     uint64_t sc_mdlo;
568949bef1SLaurent Vivier     uint64_t sc_lo1;
578949bef1SLaurent Vivier     uint64_t sc_lo2;
588949bef1SLaurent Vivier     uint64_t sc_lo3;
598949bef1SLaurent Vivier     uint64_t sc_pc;
608949bef1SLaurent Vivier     uint32_t sc_fpc_csr;
618949bef1SLaurent Vivier     uint32_t sc_used_math;
628949bef1SLaurent Vivier     uint32_t sc_dsp;
638949bef1SLaurent Vivier     uint32_t sc_reserved;
648949bef1SLaurent Vivier };
658949bef1SLaurent Vivier # endif /* O32 */
668949bef1SLaurent Vivier 
678949bef1SLaurent Vivier struct sigframe {
688949bef1SLaurent Vivier     uint32_t sf_ass[4];                 /* argument save space for o32 */
698949bef1SLaurent Vivier     uint32_t sf_code[2];                        /* signal trampoline */
708949bef1SLaurent Vivier     struct target_sigcontext sf_sc;
718949bef1SLaurent Vivier     target_sigset_t sf_mask;
728949bef1SLaurent Vivier };
738949bef1SLaurent Vivier 
748949bef1SLaurent Vivier struct target_ucontext {
754ced996fSAleksandar Markovic     abi_ulong tuc_flags;
764ced996fSAleksandar Markovic     abi_ulong tuc_link;
778949bef1SLaurent Vivier     target_stack_t tuc_stack;
788949bef1SLaurent Vivier     struct target_sigcontext tuc_mcontext;
798949bef1SLaurent Vivier     target_sigset_t tuc_sigmask;
808949bef1SLaurent Vivier };
818949bef1SLaurent Vivier 
828949bef1SLaurent Vivier struct target_rt_sigframe {
838949bef1SLaurent Vivier     uint32_t rs_ass[4];               /* argument save space for o32 */
848949bef1SLaurent Vivier     uint32_t rs_code[2];              /* signal trampoline */
858949bef1SLaurent Vivier     struct target_siginfo rs_info;
868949bef1SLaurent Vivier     struct target_ucontext rs_uc;
878949bef1SLaurent Vivier };
888949bef1SLaurent Vivier 
898949bef1SLaurent Vivier /* Install trampoline to jump back from signal handler */
908949bef1SLaurent Vivier static inline int install_sigtramp(unsigned int *tramp,   unsigned int syscall)
918949bef1SLaurent Vivier {
928949bef1SLaurent Vivier     int err = 0;
938949bef1SLaurent Vivier 
948949bef1SLaurent Vivier     /*
958949bef1SLaurent Vivier      * Set up the return code ...
968949bef1SLaurent Vivier      *
978949bef1SLaurent Vivier      *         li      v0, __NR__foo_sigreturn
988949bef1SLaurent Vivier      *         syscall
998949bef1SLaurent Vivier      */
1008949bef1SLaurent Vivier 
1018949bef1SLaurent Vivier     __put_user(0x24020000 + syscall, tramp + 0);
1028949bef1SLaurent Vivier     __put_user(0x0000000c          , tramp + 1);
1038949bef1SLaurent Vivier     return err;
1048949bef1SLaurent Vivier }
1058949bef1SLaurent Vivier 
1068949bef1SLaurent Vivier static inline void setup_sigcontext(CPUMIPSState *regs,
1078949bef1SLaurent Vivier                                     struct target_sigcontext *sc)
1088949bef1SLaurent Vivier {
1098949bef1SLaurent Vivier     int i;
1108949bef1SLaurent Vivier 
1118949bef1SLaurent Vivier     __put_user(exception_resume_pc(regs), &sc->sc_pc);
1128949bef1SLaurent Vivier     regs->hflags &= ~MIPS_HFLAG_BMASK;
1138949bef1SLaurent Vivier 
1148949bef1SLaurent Vivier     __put_user(0, &sc->sc_regs[0]);
1158949bef1SLaurent Vivier     for (i = 1; i < 32; ++i) {
1168949bef1SLaurent Vivier         __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
1178949bef1SLaurent Vivier     }
1188949bef1SLaurent Vivier 
1198949bef1SLaurent Vivier     __put_user(regs->active_tc.HI[0], &sc->sc_mdhi);
1208949bef1SLaurent Vivier     __put_user(regs->active_tc.LO[0], &sc->sc_mdlo);
1218949bef1SLaurent Vivier 
1228949bef1SLaurent Vivier     /* Rather than checking for dsp existence, always copy.  The storage
1238949bef1SLaurent Vivier        would just be garbage otherwise.  */
1248949bef1SLaurent Vivier     __put_user(regs->active_tc.HI[1], &sc->sc_hi1);
1258949bef1SLaurent Vivier     __put_user(regs->active_tc.HI[2], &sc->sc_hi2);
1268949bef1SLaurent Vivier     __put_user(regs->active_tc.HI[3], &sc->sc_hi3);
1278949bef1SLaurent Vivier     __put_user(regs->active_tc.LO[1], &sc->sc_lo1);
1288949bef1SLaurent Vivier     __put_user(regs->active_tc.LO[2], &sc->sc_lo2);
1298949bef1SLaurent Vivier     __put_user(regs->active_tc.LO[3], &sc->sc_lo3);
1308949bef1SLaurent Vivier     {
1318949bef1SLaurent Vivier         uint32_t dsp = cpu_rddsp(0x3ff, regs);
1328949bef1SLaurent Vivier         __put_user(dsp, &sc->sc_dsp);
1338949bef1SLaurent Vivier     }
1348949bef1SLaurent Vivier 
1358949bef1SLaurent Vivier     __put_user(1, &sc->sc_used_math);
1368949bef1SLaurent Vivier 
1378949bef1SLaurent Vivier     for (i = 0; i < 32; ++i) {
1388949bef1SLaurent Vivier         __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
1398949bef1SLaurent Vivier     }
1408949bef1SLaurent Vivier }
1418949bef1SLaurent Vivier 
1428949bef1SLaurent Vivier static inline void
1438949bef1SLaurent Vivier restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc)
1448949bef1SLaurent Vivier {
1458949bef1SLaurent Vivier     int i;
1468949bef1SLaurent Vivier 
1478949bef1SLaurent Vivier     __get_user(regs->CP0_EPC, &sc->sc_pc);
1488949bef1SLaurent Vivier 
1498949bef1SLaurent Vivier     __get_user(regs->active_tc.HI[0], &sc->sc_mdhi);
1508949bef1SLaurent Vivier     __get_user(regs->active_tc.LO[0], &sc->sc_mdlo);
1518949bef1SLaurent Vivier 
1528949bef1SLaurent Vivier     for (i = 1; i < 32; ++i) {
1538949bef1SLaurent Vivier         __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
1548949bef1SLaurent Vivier     }
1558949bef1SLaurent Vivier 
1568949bef1SLaurent Vivier     __get_user(regs->active_tc.HI[1], &sc->sc_hi1);
1578949bef1SLaurent Vivier     __get_user(regs->active_tc.HI[2], &sc->sc_hi2);
1588949bef1SLaurent Vivier     __get_user(regs->active_tc.HI[3], &sc->sc_hi3);
1598949bef1SLaurent Vivier     __get_user(regs->active_tc.LO[1], &sc->sc_lo1);
1608949bef1SLaurent Vivier     __get_user(regs->active_tc.LO[2], &sc->sc_lo2);
1618949bef1SLaurent Vivier     __get_user(regs->active_tc.LO[3], &sc->sc_lo3);
1628949bef1SLaurent Vivier     {
1638949bef1SLaurent Vivier         uint32_t dsp;
1648949bef1SLaurent Vivier         __get_user(dsp, &sc->sc_dsp);
1658949bef1SLaurent Vivier         cpu_wrdsp(dsp, 0x3ff, regs);
1668949bef1SLaurent Vivier     }
1678949bef1SLaurent Vivier 
1688949bef1SLaurent Vivier     for (i = 0; i < 32; ++i) {
1698949bef1SLaurent Vivier         __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
1708949bef1SLaurent Vivier     }
1718949bef1SLaurent Vivier }
1728949bef1SLaurent Vivier 
1738949bef1SLaurent Vivier /*
1748949bef1SLaurent Vivier  * Determine which stack to use..
1758949bef1SLaurent Vivier  */
1768949bef1SLaurent Vivier static inline abi_ulong
1778949bef1SLaurent Vivier get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size)
1788949bef1SLaurent Vivier {
1798949bef1SLaurent Vivier     unsigned long sp;
1808949bef1SLaurent Vivier 
1818949bef1SLaurent Vivier     /*
1828949bef1SLaurent Vivier      * FPU emulator may have its own trampoline active just
1838949bef1SLaurent Vivier      * above the user stack, 16-bytes before the next lowest
1848949bef1SLaurent Vivier      * 16 byte boundary.  Try to avoid trashing it.
1858949bef1SLaurent Vivier      */
186465e237bSLaurent Vivier     sp = target_sigsp(get_sp_from_cpustate(regs) - 32, ka);
1878949bef1SLaurent Vivier 
1888949bef1SLaurent Vivier     return (sp - frame_size) & ~7;
1898949bef1SLaurent Vivier }
1908949bef1SLaurent Vivier 
1918949bef1SLaurent Vivier static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env)
1928949bef1SLaurent Vivier {
1938949bef1SLaurent Vivier     if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) {
1948949bef1SLaurent Vivier         env->hflags &= ~MIPS_HFLAG_M16;
1958949bef1SLaurent Vivier         env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT;
1968949bef1SLaurent Vivier         env->active_tc.PC &= ~(target_ulong) 1;
1978949bef1SLaurent Vivier     }
1988949bef1SLaurent Vivier }
1998949bef1SLaurent Vivier 
2008949bef1SLaurent Vivier # if defined(TARGET_ABI_MIPSO32)
2018949bef1SLaurent Vivier /* compare linux/arch/mips/kernel/signal.c:setup_frame() */
2028949bef1SLaurent Vivier void setup_frame(int sig, struct target_sigaction * ka,
2038949bef1SLaurent Vivier                  target_sigset_t *set, CPUMIPSState *regs)
2048949bef1SLaurent Vivier {
2058949bef1SLaurent Vivier     struct sigframe *frame;
2068949bef1SLaurent Vivier     abi_ulong frame_addr;
2078949bef1SLaurent Vivier     int i;
2088949bef1SLaurent Vivier 
2098949bef1SLaurent Vivier     frame_addr = get_sigframe(ka, regs, sizeof(*frame));
2108949bef1SLaurent Vivier     trace_user_setup_frame(regs, frame_addr);
2118949bef1SLaurent Vivier     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
2128949bef1SLaurent Vivier         goto give_sigsegv;
2138949bef1SLaurent Vivier     }
2148949bef1SLaurent Vivier 
2158949bef1SLaurent Vivier     install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
2168949bef1SLaurent Vivier 
2178949bef1SLaurent Vivier     setup_sigcontext(regs, &frame->sf_sc);
2188949bef1SLaurent Vivier 
2198949bef1SLaurent Vivier     for(i = 0; i < TARGET_NSIG_WORDS; i++) {
2208949bef1SLaurent Vivier         __put_user(set->sig[i], &frame->sf_mask.sig[i]);
2218949bef1SLaurent Vivier     }
2228949bef1SLaurent Vivier 
2238949bef1SLaurent Vivier     /*
2248949bef1SLaurent Vivier     * Arguments to signal handler:
2258949bef1SLaurent Vivier     *
2268949bef1SLaurent Vivier     *   a0 = signal number
2278949bef1SLaurent Vivier     *   a1 = 0 (should be cause)
2288949bef1SLaurent Vivier     *   a2 = pointer to struct sigcontext
2298949bef1SLaurent Vivier     *
2308949bef1SLaurent Vivier     * $25 and PC point to the signal handler, $29 points to the
2318949bef1SLaurent Vivier     * struct sigframe.
2328949bef1SLaurent Vivier     */
2338949bef1SLaurent Vivier     regs->active_tc.gpr[ 4] = sig;
2348949bef1SLaurent Vivier     regs->active_tc.gpr[ 5] = 0;
2358949bef1SLaurent Vivier     regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc);
2368949bef1SLaurent Vivier     regs->active_tc.gpr[29] = frame_addr;
2378949bef1SLaurent Vivier     regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code);
2388949bef1SLaurent Vivier     /* The original kernel code sets CP0_EPC to the handler
2398949bef1SLaurent Vivier     * since it returns to userland using eret
2408949bef1SLaurent Vivier     * we cannot do this here, and we must set PC directly */
2418949bef1SLaurent Vivier     regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler;
2428949bef1SLaurent Vivier     mips_set_hflags_isa_mode_from_pc(regs);
2438949bef1SLaurent Vivier     unlock_user_struct(frame, frame_addr, 1);
2448949bef1SLaurent Vivier     return;
2458949bef1SLaurent Vivier 
2468949bef1SLaurent Vivier give_sigsegv:
2478949bef1SLaurent Vivier     force_sigsegv(sig);
2488949bef1SLaurent Vivier }
2498949bef1SLaurent Vivier 
2508949bef1SLaurent Vivier long do_sigreturn(CPUMIPSState *regs)
2518949bef1SLaurent Vivier {
2528949bef1SLaurent Vivier     struct sigframe *frame;
2538949bef1SLaurent Vivier     abi_ulong frame_addr;
2548949bef1SLaurent Vivier     sigset_t blocked;
2558949bef1SLaurent Vivier     target_sigset_t target_set;
2568949bef1SLaurent Vivier     int i;
2578949bef1SLaurent Vivier 
2588949bef1SLaurent Vivier     frame_addr = regs->active_tc.gpr[29];
2598949bef1SLaurent Vivier     trace_user_do_sigreturn(regs, frame_addr);
2608949bef1SLaurent Vivier     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
2618949bef1SLaurent Vivier         goto badframe;
2628949bef1SLaurent Vivier 
2638949bef1SLaurent Vivier     for(i = 0; i < TARGET_NSIG_WORDS; i++) {
2648949bef1SLaurent Vivier         __get_user(target_set.sig[i], &frame->sf_mask.sig[i]);
2658949bef1SLaurent Vivier     }
2668949bef1SLaurent Vivier 
2678949bef1SLaurent Vivier     target_to_host_sigset_internal(&blocked, &target_set);
2688949bef1SLaurent Vivier     set_sigmask(&blocked);
2698949bef1SLaurent Vivier 
2708949bef1SLaurent Vivier     restore_sigcontext(regs, &frame->sf_sc);
2718949bef1SLaurent Vivier 
2728949bef1SLaurent Vivier #if 0
2738949bef1SLaurent Vivier     /*
2748949bef1SLaurent Vivier      * Don't let your children do this ...
2758949bef1SLaurent Vivier      */
2768949bef1SLaurent Vivier     __asm__ __volatile__(
2778949bef1SLaurent Vivier         "move\t$29, %0\n\t"
2788949bef1SLaurent Vivier         "j\tsyscall_exit"
2798949bef1SLaurent Vivier         :/* no outputs */
2808949bef1SLaurent Vivier         :"r" (&regs));
2818949bef1SLaurent Vivier     /* Unreached */
2828949bef1SLaurent Vivier #endif
2838949bef1SLaurent Vivier 
2848949bef1SLaurent Vivier     regs->active_tc.PC = regs->CP0_EPC;
2858949bef1SLaurent Vivier     mips_set_hflags_isa_mode_from_pc(regs);
2868949bef1SLaurent Vivier     /* I am not sure this is right, but it seems to work
2878949bef1SLaurent Vivier     * maybe a problem with nested signals ? */
2888949bef1SLaurent Vivier     regs->CP0_EPC = 0;
2898949bef1SLaurent Vivier     return -TARGET_QEMU_ESIGRETURN;
2908949bef1SLaurent Vivier 
2918949bef1SLaurent Vivier badframe:
2928949bef1SLaurent Vivier     force_sig(TARGET_SIGSEGV);
2938949bef1SLaurent Vivier     return -TARGET_QEMU_ESIGRETURN;
2948949bef1SLaurent Vivier }
2958949bef1SLaurent Vivier # endif /* O32 */
2968949bef1SLaurent Vivier 
2978949bef1SLaurent Vivier void setup_rt_frame(int sig, struct target_sigaction *ka,
2988949bef1SLaurent Vivier                     target_siginfo_t *info,
2998949bef1SLaurent Vivier                     target_sigset_t *set, CPUMIPSState *env)
3008949bef1SLaurent Vivier {
3018949bef1SLaurent Vivier     struct target_rt_sigframe *frame;
3028949bef1SLaurent Vivier     abi_ulong frame_addr;
3038949bef1SLaurent Vivier     int i;
3048949bef1SLaurent Vivier 
3058949bef1SLaurent Vivier     frame_addr = get_sigframe(ka, env, sizeof(*frame));
3068949bef1SLaurent Vivier     trace_user_setup_rt_frame(env, frame_addr);
3078949bef1SLaurent Vivier     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
3088949bef1SLaurent Vivier         goto give_sigsegv;
3098949bef1SLaurent Vivier     }
3108949bef1SLaurent Vivier 
3118949bef1SLaurent Vivier     install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn);
3128949bef1SLaurent Vivier 
3138949bef1SLaurent Vivier     tswap_siginfo(&frame->rs_info, info);
3148949bef1SLaurent Vivier 
3158949bef1SLaurent Vivier     __put_user(0, &frame->rs_uc.tuc_flags);
3168949bef1SLaurent Vivier     __put_user(0, &frame->rs_uc.tuc_link);
317465e237bSLaurent Vivier     target_save_altstack(&frame->rs_uc.tuc_stack, env);
3188949bef1SLaurent Vivier 
3198949bef1SLaurent Vivier     setup_sigcontext(env, &frame->rs_uc.tuc_mcontext);
3208949bef1SLaurent Vivier 
3218949bef1SLaurent Vivier     for(i = 0; i < TARGET_NSIG_WORDS; i++) {
3228949bef1SLaurent Vivier         __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
3238949bef1SLaurent Vivier     }
3248949bef1SLaurent Vivier 
3258949bef1SLaurent Vivier     /*
3268949bef1SLaurent Vivier     * Arguments to signal handler:
3278949bef1SLaurent Vivier     *
3288949bef1SLaurent Vivier     *   a0 = signal number
3298949bef1SLaurent Vivier     *   a1 = pointer to siginfo_t
3308949bef1SLaurent Vivier     *   a2 = pointer to ucontext_t
3318949bef1SLaurent Vivier     *
3328949bef1SLaurent Vivier     * $25 and PC point to the signal handler, $29 points to the
3338949bef1SLaurent Vivier     * struct sigframe.
3348949bef1SLaurent Vivier     */
3358949bef1SLaurent Vivier     env->active_tc.gpr[ 4] = sig;
3368949bef1SLaurent Vivier     env->active_tc.gpr[ 5] = frame_addr
3378949bef1SLaurent Vivier                              + offsetof(struct target_rt_sigframe, rs_info);
3388949bef1SLaurent Vivier     env->active_tc.gpr[ 6] = frame_addr
3398949bef1SLaurent Vivier                              + offsetof(struct target_rt_sigframe, rs_uc);
3408949bef1SLaurent Vivier     env->active_tc.gpr[29] = frame_addr;
3418949bef1SLaurent Vivier     env->active_tc.gpr[31] = frame_addr
3428949bef1SLaurent Vivier                              + offsetof(struct target_rt_sigframe, rs_code);
3438949bef1SLaurent Vivier     /* The original kernel code sets CP0_EPC to the handler
3448949bef1SLaurent Vivier     * since it returns to userland using eret
3458949bef1SLaurent Vivier     * we cannot do this here, and we must set PC directly */
3468949bef1SLaurent Vivier     env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
3478949bef1SLaurent Vivier     mips_set_hflags_isa_mode_from_pc(env);
3488949bef1SLaurent Vivier     unlock_user_struct(frame, frame_addr, 1);
3498949bef1SLaurent Vivier     return;
3508949bef1SLaurent Vivier 
3518949bef1SLaurent Vivier give_sigsegv:
3528949bef1SLaurent Vivier     unlock_user_struct(frame, frame_addr, 1);
3538949bef1SLaurent Vivier     force_sigsegv(sig);
3548949bef1SLaurent Vivier }
3558949bef1SLaurent Vivier 
3568949bef1SLaurent Vivier long do_rt_sigreturn(CPUMIPSState *env)
3578949bef1SLaurent Vivier {
3588949bef1SLaurent Vivier     struct target_rt_sigframe *frame;
3598949bef1SLaurent Vivier     abi_ulong frame_addr;
3608949bef1SLaurent Vivier     sigset_t blocked;
3618949bef1SLaurent Vivier 
3628949bef1SLaurent Vivier     frame_addr = env->active_tc.gpr[29];
3638949bef1SLaurent Vivier     trace_user_do_rt_sigreturn(env, frame_addr);
3648949bef1SLaurent Vivier     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
3658949bef1SLaurent Vivier         goto badframe;
3668949bef1SLaurent Vivier     }
3678949bef1SLaurent Vivier 
3688949bef1SLaurent Vivier     target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
3698949bef1SLaurent Vivier     set_sigmask(&blocked);
3708949bef1SLaurent Vivier 
3718949bef1SLaurent Vivier     restore_sigcontext(env, &frame->rs_uc.tuc_mcontext);
372ddc3e74dSRichard Henderson     target_restore_altstack(&frame->rs_uc.tuc_stack, env);
3738949bef1SLaurent Vivier 
3748949bef1SLaurent Vivier     env->active_tc.PC = env->CP0_EPC;
3758949bef1SLaurent Vivier     mips_set_hflags_isa_mode_from_pc(env);
3768949bef1SLaurent Vivier     /* I am not sure this is right, but it seems to work
3778949bef1SLaurent Vivier     * maybe a problem with nested signals ? */
3788949bef1SLaurent Vivier     env->CP0_EPC = 0;
3798949bef1SLaurent Vivier     return -TARGET_QEMU_ESIGRETURN;
3808949bef1SLaurent Vivier 
3818949bef1SLaurent Vivier badframe:
3828949bef1SLaurent Vivier     force_sig(TARGET_SIGSEGV);
3838949bef1SLaurent Vivier     return -TARGET_QEMU_ESIGRETURN;
3848949bef1SLaurent Vivier }
385