diff mbox

[3/3] linux-user:Signal handling for MIPS64

Message ID 1323321912-15922-4-git-send-email-khansa@kics.edu.pk
State New
Headers show

Commit Message

Khansa Butt Dec. 8, 2011, 5:25 a.m. UTC
From: Khansa Butt <khansa@kics.edu.pk>


Signed-off-by: Ehsan Ul Haq <ehsan.ulhaq@kics.edu.pk>
---
 linux-user/signal.c |  429 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 417 insertions(+), 12 deletions(-)

Comments

Richard Henderson Dec. 14, 2011, 4:20 p.m. UTC | #1
On 12/07/2011 09:25 PM, khansa@kics.edu.pk wrote:
> +#if defined(TARGET_MIPS64)
> +        /* tswapal() do 64 bit swap in case of MIPS64 but
> +           we need 32 bit swap as sa_flags is 32 bit */
> +        k->sa_flags = bswap32(act->sa_flags);
> +#else
>          k->sa_flags = tswapal(act->sa_flags);
> +#endif

The condition in syscall_defs.h is TARGET_MIPS, not TARGET_MIPS64.
They should match, despite the fact that it doesn't actually matter
for the 32-bit abis.

>  #elif defined(TARGET_ABI_MIPSN64)
>  
> -# warning signal handling not implemented
> +struct target_sigcontext {
> +    uint32_t   sc_regmask;     /* Unused */
> +    uint32_t   sc_status;

There's no reason to duplicate all this code.  Yes, when someone wrote
this in the first place, they wrote separate sectons for each mips abi.
However, as you can see that huge portions of this block are identical,
this was obviously a mistake.

Start by changing the original section to #elif defined(TARGET_MIPS)
and see what needs changing specifically for the ABIs.  I'm not even
sure there are any differences at all.


r~
Khansa Butt Dec. 28, 2011, 11:09 a.m. UTC | #2
On Wed, Dec 14, 2011 at 9:20 PM, Richard Henderson <rth@twiddle.net> wrote:
> On 12/07/2011 09:25 PM, khansa@kics.edu.pk wrote:
>> +#if defined(TARGET_MIPS64)
>> +        /* tswapal() do 64 bit swap in case of MIPS64 but
>> +           we need 32 bit swap as sa_flags is 32 bit */
>> +        k->sa_flags = bswap32(act->sa_flags);
>> +#else
>>          k->sa_flags = tswapal(act->sa_flags);
>> +#endif
>
> The condition in syscall_defs.h is TARGET_MIPS, not TARGET_MIPS64.
> They should match, despite the fact that it doesn't actually matter
> for the 32-bit abis.
>

actually sa_flags is 32 bit for MIPS64 but tswapal calls tswap64() as
TARGET_LONG_SIZE != 4
in case of MIPS64( see cpu-all.h) hence sa_flags has wrong value at
the end so I used above hunk

>>  #elif defined(TARGET_ABI_MIPSN64)
>>
>> -# warning signal handling not implemented
>> +struct target_sigcontext {
>> +    uint32_t   sc_regmask;     /* Unused */
>> +    uint32_t   sc_status;
>
> There's no reason to duplicate all this code.  Yes, when someone wrote
> this in the first place, they wrote separate sectons for each mips abi.
> However, as you can see that huge portions of this block are identical,
> this was obviously a mistake.
>
> Start by changing the original section to #elif defined(TARGET_MIPS)
> and see what needs changing specifically for the ABIs.  I'm not even
> sure there are any differences at all.
>
>
> r~
Khansa Butt Dec. 29, 2011, 10:58 a.m. UTC | #3
On Wed, Dec 14, 2011 at 9:20 PM, Richard Henderson <rth@twiddle.net> wrote:
> On 12/07/2011 09:25 PM, khansa@kics.edu.pk wrote:
>> +#if defined(TARGET_MIPS64)
>> +        /* tswapal() do 64 bit swap in case of MIPS64 but
>> +           we need 32 bit swap as sa_flags is 32 bit */
>> +        k->sa_flags = bswap32(act->sa_flags);
>> +#else
>>          k->sa_flags = tswapal(act->sa_flags);
>> +#endif
>
> The condition in syscall_defs.h is TARGET_MIPS, not TARGET_MIPS64.
> They should match, despite the fact that it doesn't actually matter
> for the 32-bit abis.
>
>>  #elif defined(TARGET_ABI_MIPSN64)
>>
>> -# warning signal handling not implemented
>> +struct target_sigcontext {
>> +    uint32_t   sc_regmask;     /* Unused */
>> +    uint32_t   sc_status;
>
> There's no reason to duplicate all this code.  Yes, when someone wrote
> this in the first place, they wrote separate sectons for each mips abi.
> However, as you can see that huge portions of this block are identical,
> this was obviously a mistake.
>
> Start by changing the original section to #elif defined(TARGET_MIPS)
> and see what needs changing specifically for the ABIs.  I'm not even
> sure there are any differences at all.

The difference lies in setup_frame(). the trampoline function needs
syscall number
    install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
sigreturn is not defined for MIPS64 instead it has only rt_sigreturn.
I tried with #elif defined(TARGET_MIPS). cross compile the following
small program and run it on
qemu-mips64
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

struct sigaction act;

void sighandler(int signum, siginfo_t *info, void *ptr)
{
    printf("Received signal %d\n", signum);
    printf("Signal originates from process %lu\n",(unsigned long)info->si_pid);
}

int main()
{
    printf("I am %lu\n", (unsigned long)getpid());

    memset(&act, 0, sizeof(act));

    act.sa_sigaction = sighandler;
    act.sa_flags = SA_SIGINFO;

    sigaction(SIGILL, &act, NULL);

    // Waiting
    sleep(100);

    return 0;
}

and again I found that only install_sigtramp line is needed to be changed.
keeping  #elif defined(TARGET_MIPS) above signal handling portion, can i use
 #if defined(TARGET_MIPS64)
 ....
#else
for install_sigtramp() difference
>
>
> r~
Andreas Färber Dec. 29, 2011, 11:23 a.m. UTC | #4
Am 29.12.2011 11:58, schrieb Khansa Butt:
> On Wed, Dec 14, 2011 at 9:20 PM, Richard Henderson <rth@twiddle.net> wrote:
>> On 12/07/2011 09:25 PM, khansa@kics.edu.pk wrote:
>>> +#if defined(TARGET_MIPS64)
>>> +        /* tswapal() do 64 bit swap in case of MIPS64 but
>>> +           we need 32 bit swap as sa_flags is 32 bit */
>>> +        k->sa_flags = bswap32(act->sa_flags);
>>> +#else
>>>          k->sa_flags = tswapal(act->sa_flags);
>>> +#endif
>>
>> The condition in syscall_defs.h is TARGET_MIPS, not TARGET_MIPS64.
>> They should match, despite the fact that it doesn't actually matter
>> for the 32-bit abis.
>>
>>>  #elif defined(TARGET_ABI_MIPSN64)
>>>
>>> -# warning signal handling not implemented
>>> +struct target_sigcontext {
>>> +    uint32_t   sc_regmask;     /* Unused */
>>> +    uint32_t   sc_status;
>>
>> There's no reason to duplicate all this code.  Yes, when someone wrote
>> this in the first place, they wrote separate sectons for each mips abi.
>> However, as you can see that huge portions of this block are identical,
>> this was obviously a mistake.
>>
>> Start by changing the original section to #elif defined(TARGET_MIPS)
>> and see what needs changing specifically for the ABIs.  I'm not even
>> sure there are any differences at all.
> 
> The difference lies in setup_frame(). the trampoline function needs
> syscall number
>     install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
> sigreturn is not defined for MIPS64 instead it has only rt_sigreturn.
> I tried with #elif defined(TARGET_MIPS). cross compile the following
> small program and run it on
> qemu-mips64
> #include <stdio.h>
> #include <signal.h>
> #include <string.h>
> #include <unistd.h>
> 
> struct sigaction act;
> 
> void sighandler(int signum, siginfo_t *info, void *ptr)
> {
>     printf("Received signal %d\n", signum);
>     printf("Signal originates from process %lu\n",(unsigned long)info->si_pid);
> }
> 
> int main()
> {
>     printf("I am %lu\n", (unsigned long)getpid());
> 
>     memset(&act, 0, sizeof(act));
> 
>     act.sa_sigaction = sighandler;
>     act.sa_flags = SA_SIGINFO;
> 
>     sigaction(SIGILL, &act, NULL);
> 
>     // Waiting
>     sleep(100);
> 
>     return 0;
> }
> 
> and again I found that only install_sigtramp line is needed to be changed.
> keeping  #elif defined(TARGET_MIPS) above signal handling portion, can i use
>  #if defined(TARGET_MIPS64)
>  ....
> #else
> for install_sigtramp() difference

That's not the only difference - the sigcontext is different, too, for
instance. I hope to get something finished for n32 later today.

Andreas
diff mbox

Patch

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 78e3380..0f4091d 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -596,7 +596,13 @@  int do_sigaction(int sig, const struct target_sigaction *act,
     if (act) {
         /* FIXME: This is not threadsafe.  */
         k->_sa_handler = tswapal(act->_sa_handler);
+#if defined(TARGET_MIPS64)
+        /* tswapal() do 64 bit swap in case of MIPS64 but
+           we need 32 bit swap as sa_flags is 32 bit */
+        k->sa_flags = bswap32(act->sa_flags);
+#else
         k->sa_flags = tswapal(act->sa_flags);
+#endif
 #if !defined(TARGET_MIPS)
         k->sa_restorer = tswapal(act->sa_restorer);
 #endif
@@ -2416,31 +2422,430 @@  void sparc64_get_context(CPUSPARCState *env)
 #endif
 #elif defined(TARGET_ABI_MIPSN64)
 
-# warning signal handling not implemented
+struct target_sigcontext {
+    uint32_t   sc_regmask;     /* Unused */
+    uint32_t   sc_status;
+    uint64_t   sc_pc;
+    uint64_t   sc_regs[32];
+    uint64_t   sc_fpregs[32];
+    uint32_t   sc_ownedfp;     /* Unused */
+    uint32_t   sc_fpc_csr;
+    uint32_t   sc_fpc_eir;     /* Unused */
+    uint32_t   sc_used_math;
+    uint32_t   sc_dsp;         /* dsp status, was sc_ssflags */
+    uint32_t   pad0;
+    uint64_t   sc_mdhi;
+    uint64_t   sc_mdlo;
+    target_ulong   sc_hi1;         /* Was sc_cause */
+    target_ulong   sc_lo1;         /* Was sc_badvaddr */
+    target_ulong   sc_hi2;         /* Was sc_sigset[4] */
+    target_ulong   sc_lo2;
+    target_ulong   sc_hi3;
+    target_ulong   sc_lo3;
+};
+
+struct sigframe {
+    uint32_t sf_ass[4];         /* argument save space for o32 */
+    uint32_t sf_code[2];            /* signal trampoline */
+    struct target_sigcontext sf_sc;
+    target_sigset_t sf_mask;
+};
+
+struct target_ucontext {
+    target_ulong tuc_flags;
+    target_ulong tuc_link;
+    target_stack_t tuc_stack;
+    target_ulong pad0;
+    struct target_sigcontext tuc_mcontext;
+    target_sigset_t tuc_sigmask;
+};
+
+struct target_rt_sigframe {
+    uint32_t rs_ass[4];               /* argument save space for o32 */
+    uint32_t rs_code[2];              /* signal trampoline */
+    struct target_siginfo rs_info;
+    struct target_ucontext rs_uc;
+};
+
+/* Install trampoline to jump back from signal handler */
+static inline int install_sigtramp(unsigned int *tramp,   unsigned int syscall)
+{
+    int err;
+
+    /*
+     * Set up the return code ...
+     *
+     *         li      v0, __NR__foo_sigreturn
+     *         syscall
+     */
+
+    err = __put_user(0x24020000 + syscall, tramp + 0);
+    err |= __put_user(0x0000000c          , tramp + 1);
+    /* flush_cache_sigtramp((unsigned long) tramp); */
+    return err;
+}
+
+static inline int
+setup_sigcontext(CPUState *regs, struct target_sigcontext *sc)
+{
+    int err = 0;
+
+    err |= __put_user(regs->active_tc.PC, &sc->sc_pc);
+
+#define save_gp_reg(i) do {                         \
+        err |= __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); \
+} while (0)
+    __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2);
+    save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6);
+    save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10);
+    save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14);
+    save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18);
+    save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22);
+    save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26);
+    save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30);
+    save_gp_reg(31);
+#undef save_gp_reg
+
+    err |= __put_user(regs->active_tc.HI[0], &sc->sc_mdhi);
+    err |= __put_user(regs->active_tc.LO[0], &sc->sc_mdlo);
+
+    /* Not used yet, but might be useful if we ever have DSP suppport */
+#if 0
+    if (cpu_has_dsp) {
+        err |= __put_user(mfhi1(), &sc->sc_hi1);
+        err |= __put_user(mflo1(), &sc->sc_lo1);
+        err |= __put_user(mfhi2(), &sc->sc_hi2);
+        err |= __put_user(mflo2(), &sc->sc_lo2);
+        err |= __put_user(mfhi3(), &sc->sc_hi3);
+        err |= __put_user(mflo3(), &sc->sc_lo3);
+        err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
+    }
+    /* same with 64 bit */
+#ifdef CONFIG_64BIT
+    err |= __put_user(regs->hi, &sc->sc_hi[0]);
+    err |= __put_user(regs->lo, &sc->sc_lo[0]);
+    if (cpu_has_dsp) {
+        err |= __put_user(mfhi1(), &sc->sc_hi[1]);
+        err |= __put_user(mflo1(), &sc->sc_lo[1]);
+        err |= __put_user(mfhi2(), &sc->sc_hi[2]);
+        err |= __put_user(mflo2(), &sc->sc_lo[2]);
+        err |= __put_user(mfhi3(), &sc->sc_hi[3]);
+        err |= __put_user(mflo3(), &sc->sc_lo[3]);
+        err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
+    }
+#endif
+#endif
+
+#if 0
+    err |= __put_user(!!used_math(), &sc->sc_used_math);
+
+    if (!used_math())
+        goto out;
+
+    /*
+     * Save FPU state to signal context.  Signal handler will "inherit"
+     * current FPU state.
+     */
+    preempt_disable();
+
+    if (!is_fpu_owner()) {
+        own_fpu();
+        restore_fp(current);
+    }
+    err |= save_fp_context(sc);
+
+    preempt_enable();
+    out:
+#endif
+    return err;
+}
+
+static inline int
+restore_sigcontext(CPUState *regs, struct target_sigcontext *sc)
+{
+    int err = 0;
+
+    err |= __get_user(regs->CP0_EPC, &sc->sc_pc);
+
+    err |= __get_user(regs->active_tc.HI[0], &sc->sc_mdhi);
+    err |= __get_user(regs->active_tc.LO[0], &sc->sc_mdlo);
+
+#define restore_gp_reg(i) do {                               \
+        err |= __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);     \
+} while (0)
+    restore_gp_reg(1); restore_gp_reg(2); restore_gp_reg(3);
+    restore_gp_reg(4); restore_gp_reg(5); restore_gp_reg(6);
+    restore_gp_reg(7); restore_gp_reg(8); restore_gp_reg(9);
+    restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12);
+    restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15);
+    restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18);
+    restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21);
+    restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24);
+    restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27);
+    restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30);
+    restore_gp_reg(31);
+#undef restore_gp_reg
+
+#if 0
+    if (cpu_has_dsp) {
+        err |= __get_user(treg, &sc->sc_hi1); mthi1(treg);
+        err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg);
+        err |= __get_user(treg, &sc->sc_hi2); mthi2(treg);
+        err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg);
+        err |= __get_user(treg, &sc->sc_hi3); mthi3(treg);
+        err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg);
+        err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK);
+    }
+#ifdef CONFIG_64BIT
+    err |= __get_user(regs->hi, &sc->sc_hi[0]);
+    err |= __get_user(regs->lo, &sc->sc_lo[0]);
+    if (cpu_has_dsp) {
+        err |= __get_user(treg, &sc->sc_hi[1]); mthi1(treg);
+        err |= __get_user(treg, &sc->sc_lo[1]); mthi1(treg);
+        err |= __get_user(treg, &sc->sc_hi[2]); mthi2(treg);
+        err |= __get_user(treg, &sc->sc_lo[2]); mthi2(treg);
+        err |= __get_user(treg, &sc->sc_hi[3]); mthi3(treg);
+        err |= __get_user(treg, &sc->sc_lo[3]); mthi3(treg);
+        err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK);
+    }
+#endif
+
+    err |= __get_user(used_math, &sc->sc_used_math);
+    conditional_used_math(used_math);
+
+    preempt_disable();
+
+    if (used_math()) {
+        /* restore fpu context if we have used it before */
+        own_fpu();
+        err |= restore_fp_context(sc);
+    } else {
+        /* signal handler may have used FPU.  Give it up. */
+        lose_fpu();
+    }
+
+    preempt_enable();
+#endif
+    return err;
+}
+/*
+ * Determine which stack to use..
+ */
+static inline abi_ulong
+get_sigframe(struct target_sigaction *ka, CPUState *regs, size_t frame_size)
+{
+    unsigned long sp;
+
+    /* Default to using normal stack */
+    sp = regs->active_tc.gpr[29];
 
+    /*
+     * FPU emulator may have it's own trampoline active just
+     * above the user stack, 16-bytes before the next lowest
+     * 16 byte boundary.  Try to avoid trashing it.
+     */
+    sp -= 32;
+
+    /* This is the X/Open sanctioned signal stack switching.  */
+    if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) {
+        sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+    }
+
+    return (sp - frame_size) & ~7;
+}
+
+/* compare linux/arch/mips/kernel/signal.c:setup_frame() */
 static void setup_frame(int sig, struct target_sigaction *ka,
-			target_sigset_t *set, CPUState *env)
+        target_sigset_t *set, CPUState *regs)
 {
-    fprintf(stderr, "setup_frame: not implemented\n");
+    struct sigframe *frame;
+    abi_ulong frame_addr;
+    int i;
+
+    frame_addr = get_sigframe(ka, regs, sizeof(*frame));
+    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+        goto give_sigsegv;
+
+    install_sigtramp(frame->sf_code, TARGET_NR_rt_sigreturn);
+
+    if (setup_sigcontext(regs, &frame->sf_sc))
+        goto give_sigsegv;
+
+    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+        if (__put_user(set->sig[i], &frame->sf_mask.sig[i]))
+            goto give_sigsegv;
+    }
+
+    /*
+     * Arguments to signal handler:
+     *
+     *   a0 = signal number
+     *   a1 = 0 (should be cause)
+     *   a2 = pointer to struct sigcontext
+     *
+     * $25 and PC point to the signal handler, $29 points to the
+     * struct sigframe.
+     */
+    regs->active_tc.gpr[4] = sig;
+    regs->active_tc.gpr[5] = 0;
+    regs->active_tc.gpr[6] = frame_addr + offsetof(struct sigframe, sf_sc);
+    regs->active_tc.gpr[29] = frame_addr;
+    regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code);
+    /* The original kernel code sets CP0_EPC to the handler
+     * since it returns to userland using eret
+     * we cannot do this here, and we must set PC directly */
+    regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler;
+    unlock_user_struct(frame, frame_addr, 1);
+    return;
+
+    give_sigsegv:
+    unlock_user_struct(frame, frame_addr, 1);
+    force_sig(TARGET_SIGSEGV/*, current*/);
+    return;
 }
 
-static void setup_rt_frame(int sig, struct target_sigaction *ka,
-                           target_siginfo_t *info,
-			   target_sigset_t *set, CPUState *env)
+long do_sigreturn(CPUState *regs)
 {
-    fprintf(stderr, "setup_rt_frame: not implemented\n");
+    struct sigframe *frame;
+    abi_ulong frame_addr;
+    sigset_t blocked;
+    target_sigset_t target_set;
+    int i;
+
+#if defined(DEBUG_SIGNAL)
+    fprintf(stderr, "do_sigreturn\n");
+#endif
+    frame_addr = regs->active_tc.gpr[29];
+    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+        goto badframe;
+
+    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+        if (__get_user(target_set.sig[i], &frame->sf_mask.sig[i]))
+            goto badframe;
+    }
+
+    target_to_host_sigset_internal(&blocked, &target_set);
+    sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+    if (restore_sigcontext(regs, &frame->sf_sc))
+        goto badframe;
+
+#if 0
+    /*
+     * Don't let your children do this ...
+     */
+    __asm__ __volatile__(
+            "move\t$29, %0\n\t"
+            "j\tsyscall_exit"
+            :/* no outputs */
+            : "r" (&regs));
+    /* Unreached */
+#endif
+
+    regs->active_tc.PC = regs->CP0_EPC;
+    /* I am not sure this is right, but it seems to work
+     * maybe a problem with nested signals ? */
+    regs->CP0_EPC = 0;
+    return -TARGET_QEMU_ESIGRETURN;
+
+    badframe:
+    force_sig(TARGET_SIGSEGV/*, current*/);
+    return 0;
 }
 
-long do_sigreturn(CPUState *env)
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+        target_siginfo_t *info,
+        target_sigset_t *set, CPUState *env)
 {
-    fprintf(stderr, "do_sigreturn: not implemented\n");
-    return -TARGET_ENOSYS;
+    struct target_rt_sigframe *frame;
+    abi_ulong frame_addr;
+    int i;
+
+    frame_addr = get_sigframe(ka, env, sizeof(*frame));
+    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+        goto give_sigsegv;
+
+    install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn);
+
+    copy_siginfo_to_user(&frame->rs_info, info);
+
+    __put_user(0, &frame->rs_uc.tuc_flags);
+    __put_user(0, &frame->rs_uc.tuc_link);
+    __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.tuc_stack.ss_sp);
+    __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.tuc_stack.ss_size);
+    __put_user(sas_ss_flags(get_sp_from_cpustate(env)),
+            &frame->rs_uc.tuc_stack.ss_flags);
+
+    setup_sigcontext(env, &frame->rs_uc.tuc_mcontext);
+
+    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+        __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
+    }
+
+    /*
+     * Arguments to signal handler:
+     *
+     *   a0 = signal number
+     *   a1 = pointer to struct siginfo
+     *   a2 = pointer to struct ucontext
+     *
+     * $25 and PC point to the signal handler, $29 points to the
+     * struct sigframe.
+     */
+    env->active_tc.gpr[4] = sig;
+    env->active_tc.gpr[5] = frame_addr
+            + offsetof(struct target_rt_sigframe, rs_info);
+    env->active_tc.gpr[6] = frame_addr
+            + offsetof(struct target_rt_sigframe, rs_uc);
+    env->active_tc.gpr[29] = frame_addr;
+    env->active_tc.gpr[31] = frame_addr
+            + offsetof(struct target_rt_sigframe, rs_code);
+    /* The original kernel code sets CP0_EPC to the handler
+     * since it returns to userland using eret
+     * we cannot do this here, and we must set PC directly */
+    env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
+    unlock_user_struct(frame, frame_addr, 1);
+    return;
+
+    give_sigsegv:
+    unlock_user_struct(frame, frame_addr, 1);
+    force_sig(TARGET_SIGSEGV/*, current*/);
+    return;
 }
 
 long do_rt_sigreturn(CPUState *env)
 {
-    fprintf(stderr, "do_rt_sigreturn: not implemented\n");
-    return -TARGET_ENOSYS;
+        struct target_rt_sigframe *frame;
+        abi_ulong frame_addr;
+        sigset_t blocked;
+
+#if defined(DEBUG_SIGNAL)
+        fprintf(stderr, "do_rt_sigreturn\n");
+#endif
+        frame_addr = env->active_tc.gpr[29];
+        if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+            goto badframe;
+
+        target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
+        sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+        if (restore_sigcontext(env, &frame->rs_uc.tuc_mcontext))
+            goto badframe;
+
+        if (do_sigaltstack(frame_addr +
+                offsetof(struct target_rt_sigframe, rs_uc.tuc_stack),
+                0, get_sp_from_cpustate(env)) == -EFAULT)
+            goto badframe;
+
+        env->active_tc.PC = env->CP0_EPC;
+        /* I am not sure this is right, but it seems to work
+         * maybe a problem with nested signals ? */
+        env->CP0_EPC = 0;
+        return -TARGET_QEMU_ESIGRETURN;
+
+        badframe:
+        force_sig(TARGET_SIGSEGV/*, current*/);
+        return 0;
 }
 
 #elif defined(TARGET_ABI_MIPSN32)