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