initial MIPS signal handling (initial patch by Raphael Rigo)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2031 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/linux-user/main.c b/linux-user/main.c
index 6b8337f..2250b12 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1302,6 +1302,7 @@
         case EXCP_SYSCALL:
             {
                 syscall_num = env->gpr[2] - 4000;
+                env->PC += 4;
                 if (syscall_num >= sizeof(mips_syscall_args)) {
                     ret = -ENOSYS;
                 } else {
@@ -1328,7 +1329,6 @@
                                      arg5, 
                                      arg6);
                 }
-                env->PC += 4;
                 if ((unsigned int)ret >= (unsigned int)(-1133)) {
                     env->gpr[7] = 1; /* error flag */
                     ret = -ret;
@@ -1347,6 +1347,9 @@
             info.si_code = 0;
             queue_signal(info.si_signo, &info);
             break;
+        case EXCP_INTERRUPT:
+            /* just indicate that signals should be handled asap */
+            break;
         default:
             //        error:
             fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", 
diff --git a/linux-user/signal.c b/linux-user/signal.c
index ac4b289..1e0308b 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -432,13 +432,17 @@
     if (oact) {
         oact->_sa_handler = tswapl(k->sa._sa_handler);
         oact->sa_flags = tswapl(k->sa.sa_flags);
-        oact->sa_restorer = tswapl(k->sa.sa_restorer);
+	#if !defined(TARGET_MIPS)
+        	oact->sa_restorer = tswapl(k->sa.sa_restorer);
+	#endif
         oact->sa_mask = k->sa.sa_mask;
     }
     if (act) {
         k->sa._sa_handler = tswapl(act->_sa_handler);
         k->sa.sa_flags = tswapl(act->sa_flags);
-        k->sa.sa_restorer = tswapl(act->sa_restorer);
+	#if !defined(TARGET_MIPS)
+        	k->sa.sa_restorer = tswapl(act->sa_restorer);
+	#endif
         k->sa.sa_mask = act->sa_mask;
 
         /* we update the host linux signal state */
@@ -1618,6 +1622,334 @@
     return -ENOSYS;
 }
 
+#elif defined(TARGET_MIPS)
+
+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 */
+    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;
+};
+
+/* 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->PC, &sc->sc_pc);
+
+    #define save_gp_reg(i) do {   					\
+        err |= __put_user(regs->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->HI, &sc->sc_mdhi);
+    err |= __put_user(regs->LO, &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->HI, &sc->sc_mdhi);
+    err |= __get_user(regs->LO, &sc->sc_mdlo);
+
+    #define restore_gp_reg(i) do {   					\
+        err |= __get_user(regs->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 void *
+get_sigframe(struct emulated_sigaction *ka, CPUState *regs, size_t frame_size)
+{
+    unsigned long sp;
+
+    /* Default to using normal stack */
+    sp = regs->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;
+
+#if 0
+    /* This is the X/Open sanctioned signal stack switching.  */
+    if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0))
+	sp = current->sas_ss_sp + current->sas_ss_size;
+#endif
+
+    return g2h((sp - frame_size) & ~7);
+}
+
+static void setup_frame(int sig, struct emulated_sigaction * ka, 
+   		target_sigset_t *set, CPUState *regs)
+{
+    struct sigframe *frame;
+    int i;
+
+    frame = get_sigframe(ka, regs, sizeof(*frame));
+    if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
+	goto give_sigsegv;
+
+    install_sigtramp(frame->sf_code, TARGET_NR_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->gpr[ 4] = sig;
+    regs->gpr[ 5] = 0;
+    regs->gpr[ 6] = h2g(&frame->sf_sc);
+    regs->gpr[29] = h2g(frame);
+    regs->gpr[31] = h2g(frame->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->PC = regs->gpr[25] = ka->sa._sa_handler;
+    return;
+
+give_sigsegv:
+    force_sig(TARGET_SIGSEGV/*, current*/);
+    return;	
+}
+
+long do_sigreturn(CPUState *regs)
+{
+   struct sigframe *frame;
+   sigset_t blocked;
+   target_sigset_t target_set;
+   int i;
+
+#if defined(DEBUG_SIGNAL)
+   fprintf(stderr, "do_sigreturn\n");
+#endif
+   frame = (struct sigframe *) regs->gpr[29];
+   if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+   	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->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 0;
+
+badframe:
+   force_sig(TARGET_SIGSEGV/*, current*/);
+   return 0;	
+
+}
+
+static void setup_rt_frame(int sig, struct emulated_sigaction *ka, 
+                           target_siginfo_t *info,
+			   target_sigset_t *set, CPUState *env)
+{
+    fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+    fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+    return -ENOSYS;
+}
 
 #else
 
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 229b089..60ed9a6 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -2246,6 +2246,7 @@
         break;
     case TARGET_NR_sigaction:
         {
+	#if !defined(TARGET_MIPS)
             struct target_old_sigaction *old_act;
             struct target_sigaction act, oact, *pact;
             if (arg2) {
@@ -2268,6 +2269,33 @@
                 old_act->sa_restorer = oact.sa_restorer;
                 unlock_user_struct(old_act, arg3, 1);
             }
+	#else
+	    struct target_sigaction act, oact, *pact, *old_act;
+
+	    if (arg2) {
+		lock_user_struct(old_act, arg2, 1);
+		act._sa_handler = old_act->_sa_handler;
+		target_siginitset(&act.sa_mask, old_act->sa_mask.sig[0]);
+		act.sa_flags = old_act->sa_flags;
+		unlock_user_struct(old_act, arg2, 0);
+		pact = &act;
+	    } else {
+		pact = NULL;
+	    }
+
+	    ret = get_errno(do_sigaction(arg1, pact, &oact));
+
+	    if (!is_error(ret) && arg3) {
+		lock_user_struct(old_act, arg3, 0);
+		old_act->_sa_handler = oact._sa_handler;
+		old_act->sa_flags = oact.sa_flags;
+		old_act->sa_mask.sig[0] = oact.sa_mask.sig[0];
+		old_act->sa_mask.sig[1] = 0;
+		old_act->sa_mask.sig[2] = 0;
+		old_act->sa_mask.sig[3] = 0;
+		unlock_user_struct(old_act, arg3, 1);
+	    }
+	#endif
         }
         break;
     case TARGET_NR_rt_sigaction:
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index b5fafcc..73a5c42 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -448,6 +448,15 @@
 
 #endif
 
+#if defined(TARGET_MIPS)
+
+struct target_sigaction {
+	target_ulong	sa_flags;
+	target_ulong	_sa_handler;
+	target_sigset_t	sa_mask;
+};
+
+#else
 struct target_old_sigaction {
         target_ulong _sa_handler;
         target_ulong sa_mask;
@@ -461,6 +470,7 @@
         target_ulong sa_restorer;
         target_sigset_t sa_mask;
 };
+#endif
 
 typedef union target_sigval {
 	int sival_int;