Patchwork alpha-linux-user: Implement signals.

login
register
mail settings
Submitter Richard Henderson
Date Dec. 28, 2009, 2:30 a.m.
Message ID <20100210182426.B2572C28@are.twiddle.net>
Download mbox | patch
Permalink /patch/45051/
State New
Headers show

Comments

Richard Henderson - Dec. 28, 2009, 2:30 a.m.
Move userland PALcode handling into linux-user main loop so that
we can send signals from there.  This also makes alpha_palcode.c
system-level only, so don't build it for userland.  Add defines
for GENTRAP PALcall mapping to signals.

Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 Makefile.target                  |    3 +-
 hw/alpha_palcode.c               |   81 +-----------
 linux-user/alpha/target_signal.h |   27 ++++
 linux-user/main.c                |  137 ++++++++++++++++----
 linux-user/signal.c              |  267 ++++++++++++++++++++++++++++++++++++++
 linux-user/syscall.c             |   61 ++++++++-
 linux-user/syscall_defs.h        |   23 +++-
 target-alpha/cpu.h               |    4 +-
 target-alpha/translate.c         |    3 +-
 9 files changed, 489 insertions(+), 117 deletions(-)

Patch

diff --git a/Makefile.target b/Makefile.target
index f498574..f752210 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -51,7 +51,6 @@  libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o
 libobj-y += op_helper.o helper.o
 libobj-$(CONFIG_NEED_MMU) += mmu.o
 libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
-libobj-$(TARGET_ALPHA) += alpha_palcode.o
 
 # NOTE: the disassembler code is only needed for debugging
 libobj-y += disas.o
@@ -311,6 +310,8 @@  obj-m68k-y += m68k-semi.o dummy_m68k.o
 
 obj-s390x-y = s390-virtio-bus.o s390-virtio.o
 
+obj-alpha-y = alpha_palcode.o
+
 main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
 vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
diff --git a/hw/alpha_palcode.c b/hw/alpha_palcode.c
index 843bd14..c1220ad 100644
--- a/hw/alpha_palcode.c
+++ b/hw/alpha_palcode.c
@@ -21,11 +21,9 @@ 
 #include <stdlib.h>
 #include <stdio.h>
 
-#include "qemu.h"
 #include "cpu.h"
 #include "exec-all.h"
 
-#if !defined (CONFIG_USER_ONLY)
 /* Shared handlers */
 static void pal_reset (CPUState *env);
 /* Console handlers */
@@ -997,12 +995,9 @@  int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
     uint64_t physical, page_size, end;
     int prot, zbits, ret;
 
-#if defined(CONFIG_USER_ONLY)
-        ret = 2;
-#else
-        ret = virtual_to_physical(env, &physical, &zbits, &prot,
-                                  address, mmu_idx, rw);
-#endif
+    ret = virtual_to_physical(env, &physical, &zbits, &prot,
+                              address, mmu_idx, rw);
+
     switch (ret) {
     case 0:
         /* No fault */
@@ -1050,73 +1045,3 @@  int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
     return ret;
 }
 #endif
-
-#else /* !defined (CONFIG_USER_ONLY) */
-void pal_init (CPUState *env)
-{
-}
-
-void call_pal (CPUState *env, int palcode)
-{
-    target_long ret;
-
-    switch (palcode) {
-    case 0x80:
-        /* BPT */
-        qemu_log("BPT\n");
-        /* FIXME: Sends SIGTRAP, si_code=TRAP_BRKPT.  */
-        exit(1);
-    case 0x81:
-        /* BUGCHK */
-        qemu_log("BUGCHK\n");
-        /* FIXME: Sends SIGTRAP, si_code=SI_FAULT.  */
-        exit(1);
-    case 0x83:
-        /* CALLSYS */
-        qemu_log("CALLSYS n " TARGET_FMT_ld "\n", env->ir[0]);
-        ret = do_syscall(env, env->ir[IR_V0], env->ir[IR_A0], env->ir[IR_A1],
-                         env->ir[IR_A2], env->ir[IR_A3], env->ir[IR_A4],
-                         env->ir[IR_A5]);
-        if (ret >= 0) {
-            env->ir[IR_A3] = 0;
-            env->ir[IR_V0] = ret;
-        } else {
-            env->ir[IR_A3] = 1;
-            env->ir[IR_V0] = -ret;
-        }
-        break;
-    case 0x86:
-        /* IMB */
-        qemu_log("IMB\n");
-        /* ??? We can probably elide the code using page_unprotect that is
-           checking for self-modifying code.  Instead we could simply call
-           tb_flush here.  Until we work out the changes required to turn
-           off the extra write protection, this can be a no-op.  */
-        break;
-    case 0x9E:
-        /* RDUNIQUE */
-        qemu_log("RDUNIQUE: " TARGET_FMT_lx "\n", env->unique);
-        /* Handled in the translator for usermode.  */
-        abort();
-    case 0x9F:
-        /* WRUNIQUE */
-        qemu_log("WRUNIQUE: " TARGET_FMT_lx "\n", env->ir[IR_A0]);
-        /* Handled in the translator for usermode.  */
-        abort();
-    case 0xAA:
-        /* GENTRAP */
-        qemu_log("GENTRAP: " TARGET_FMT_lx "\n", env->ir[IR_A0]);
-        /* FIXME: This is supposed to send a signal:
-           SIGFPE:
-             GEN_INTOVF, GEN_INTDIV, GEN_FLTOVF, GEN_FLTDIV,
-             GEN_FLTUND, GEN_FLTINV, GEN_FLTINE, GEN_ROPRAND
-           SIGTRAP:
-             others
-           with various settings of si_code.  */
-        exit(1);
-    default:
-        qemu_log("%s: unhandled palcode %02x\n", __func__, palcode);
-        exit(1);
-    }
-}
-#endif
diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h
index 2382ffd..cb86402 100644
--- a/linux-user/alpha/target_signal.h
+++ b/linux-user/alpha/target_signal.h
@@ -26,4 +26,31 @@  static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state)
     return state->ir[IR_SP];
 }
 
+/* From <asm/gentrap.h>.  */
+#define TARGET_GEN_INTOVF      -1      /* integer overflow */
+#define TARGET_GEN_INTDIV      -2      /* integer division by zero */
+#define TARGET_GEN_FLTOVF      -3      /* fp overflow */
+#define TARGET_GEN_FLTDIV      -4      /* fp division by zero */
+#define TARGET_GEN_FLTUND      -5      /* fp underflow */
+#define TARGET_GEN_FLTINV      -6      /* invalid fp operand */
+#define TARGET_GEN_FLTINE      -7      /* inexact fp operand */
+#define TARGET_GEN_DECOVF      -8      /* decimal overflow (for COBOL??) */
+#define TARGET_GEN_DECDIV      -9      /* decimal division by zero */
+#define TARGET_GEN_DECINV      -10     /* invalid decimal operand */
+#define TARGET_GEN_ROPRAND     -11     /* reserved operand */
+#define TARGET_GEN_ASSERTERR   -12     /* assertion error */
+#define TARGET_GEN_NULPTRERR   -13     /* null pointer error */
+#define TARGET_GEN_STKOVF      -14     /* stack overflow */
+#define TARGET_GEN_STRLENERR   -15     /* string length error */
+#define TARGET_GEN_SUBSTRERR   -16     /* substring error */
+#define TARGET_GEN_RANGERR     -17     /* range error */
+#define TARGET_GEN_SUBRNG      -18
+#define TARGET_GEN_SUBRNG1     -19      
+#define TARGET_GEN_SUBRNG2     -20
+#define TARGET_GEN_SUBRNG3     -21
+#define TARGET_GEN_SUBRNG4     -22
+#define TARGET_GEN_SUBRNG5     -23
+#define TARGET_GEN_SUBRNG6     -24
+#define TARGET_GEN_SUBRNG7     -25
+
 #endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/main.c b/linux-user/main.c
index a0d8ce7..632b154 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2351,6 +2351,7 @@  void cpu_loop (CPUState *env)
 {
     int trapnr;
     target_siginfo_t info;
+    abi_long sysret;
 
     while (1) {
         trapnr = cpu_alpha_exec (env);
@@ -2365,16 +2366,22 @@  void cpu_loop (CPUState *env)
             exit(1);
             break;
         case EXCP_ARITH:
-            fprintf(stderr, "Arithmetic trap.\n");
-            exit(1);
+            info.si_signo = TARGET_SIGFPE;
+            info.si_errno = 0;
+            info.si_code = TARGET_FPE_FLTINV;
+            info._sifields._sigfault._addr = env->pc;
+            queue_signal(env, info.si_signo, &info);
             break;
         case EXCP_HW_INTERRUPT:
             fprintf(stderr, "External interrupt. Exit\n");
             exit(1);
             break;
         case EXCP_DFAULT:
-            fprintf(stderr, "MMU data fault\n");
-            exit(1);
+            info.si_signo = TARGET_SIGSEGV;
+            info.si_errno = 0;
+            info.si_code = 0;  /* ??? SEGV_MAPERR vs SEGV_ACCERR.  */
+            info._sifields._sigfault._addr = env->pc;
+            queue_signal(env, info.si_signo, &info);
             break;
         case EXCP_DTB_MISS_PAL:
             fprintf(stderr, "MMU data TLB miss in PALcode\n");
@@ -2393,36 +2400,116 @@  void cpu_loop (CPUState *env)
             exit(1);
             break;
         case EXCP_UNALIGN:
-            fprintf(stderr, "Unaligned access\n");
-            exit(1);
+            info.si_signo = TARGET_SIGBUS;
+            info.si_errno = 0;
+            info.si_code = TARGET_BUS_ADRALN;
+            info._sifields._sigfault._addr = env->pc;
+            queue_signal(env, info.si_signo, &info);
             break;
         case EXCP_OPCDEC:
-            fprintf(stderr, "Invalid instruction\n");
-            exit(1);
+        do_sigill:
+            info.si_signo = TARGET_SIGILL;
+            info.si_errno = 0;
+            info.si_code = TARGET_ILL_ILLOPC;
+            info._sifields._sigfault._addr = env->pc;
+            queue_signal(env, info.si_signo, &info);
             break;
         case EXCP_FEN:
-            fprintf(stderr, "Floating-point not allowed\n");
-            exit(1);
+            /* No-op.  Linux simply re-enables the FPU.  */
             break;
         case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1):
-            call_pal(env, (trapnr >> 6) | 0x80);
+            switch ((trapnr >> 6) | 0x80) {
+            case 0x80:
+                /* BPT */
+                info.si_signo = TARGET_SIGTRAP;
+                info.si_errno = 0;
+                info.si_code = TARGET_TRAP_BRKPT;
+                info._sifields._sigfault._addr = env->pc;
+                queue_signal(env, info.si_signo, &info);
+                break;
+            case 0x81:
+                /* BUGCHK */
+                info.si_signo = TARGET_SIGTRAP;
+                info.si_errno = 0;
+                info.si_code = 0;
+                info._sifields._sigfault._addr = env->pc;
+                queue_signal(env, info.si_signo, &info);
+                break;
+            case 0x83:
+                /* CALLSYS */
+                trapnr = env->ir[IR_V0];
+                sysret = do_syscall(env, trapnr,
+                                    env->ir[IR_A0], env->ir[IR_A1],
+                                    env->ir[IR_A2], env->ir[IR_A3],
+                                    env->ir[IR_A4], env->ir[IR_A5]);
+		if (trapnr != TARGET_NR_sigreturn
+                    && trapnr != TARGET_NR_rt_sigreturn) {
+                    env->ir[IR_V0] = (sysret < 0 ? -sysret : sysret);
+                    env->ir[IR_A3] = (sysret < 0);
+                }
+                break;
+            case 0x86:
+                /* IMB */
+                /* ??? We can probably elide the code using page_unprotect
+                   that is checking for self-modifying code.  Instead we
+                   could simply call tb_flush here.  Until we work out the
+                   changes required to turn off the extra write protection,
+                   this can be a no-op.  */
+                break;
+            case 0x9E:
+                /* RDUNIQUE */
+                /* Handled in the translator for usermode.  */
+                abort();
+            case 0x9F:
+                /* WRUNIQUE */
+                /* Handled in the translator for usermode.  */
+                abort();
+            case 0xAA:
+                /* GENTRAP */
+                info.si_signo = TARGET_SIGFPE;
+                switch (env->ir[IR_A0]) {
+                case TARGET_GEN_INTOVF:
+                    info.si_code = TARGET_FPE_INTOVF;
+                    break;
+                case TARGET_GEN_INTDIV:
+                    info.si_code = TARGET_FPE_INTDIV;
+                    break;
+                case TARGET_GEN_FLTOVF:
+                    info.si_code = TARGET_FPE_FLTOVF;
+                    break;
+                case TARGET_GEN_FLTUND:
+                    info.si_code = TARGET_FPE_FLTUND;
+                    break;
+                case TARGET_GEN_FLTINV:
+                    info.si_code = TARGET_FPE_FLTINV;
+                    break;
+                case TARGET_GEN_FLTINE:
+                    info.si_code = TARGET_FPE_FLTRES;
+                    break;
+                case TARGET_GEN_ROPRAND:
+                    info.si_code = 0;
+                    break;
+                default:
+                    info.si_signo = TARGET_SIGTRAP;
+                    info.si_code = 0;
+                    break;
+                }
+                info.si_errno = 0;
+                info._sifields._sigfault._addr = env->pc;
+                queue_signal(env, info.si_signo, &info);
+                break;
+            default:
+                goto do_sigill;
+            }
             break;
         case EXCP_CALL_PALP ... (EXCP_CALL_PALE - 1):
-            fprintf(stderr, "Privileged call to PALcode\n");
-            exit(1);
-            break;
+            goto do_sigill;
         case EXCP_DEBUG:
-            {
-                int sig;
-
-                sig = gdb_handlesig (env, TARGET_SIGTRAP);
-                if (sig)
-                  {
-                    info.si_signo = sig;
-                    info.si_errno = 0;
-                    info.si_code = TARGET_TRAP_BRKPT;
-                    queue_signal(env, info.si_signo, &info);
-                  }
+            info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP);
+            if (info.si_signo) {
+                info.si_errno = 0;
+                info.si_code = TARGET_TRAP_BRKPT;
+                queue_signal(env, info.si_signo, &info);
             }
             break;
         default:
diff --git a/linux-user/signal.c b/linux-user/signal.c
index b0faf2e..e9abb1a 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -4410,6 +4410,273 @@  badframe:
     return 0;
 }
 
+#elif defined(TARGET_ALPHA)
+
+struct target_sigcontext {
+    abi_long sc_onstack;
+    abi_long sc_mask;
+    abi_long sc_pc;
+    abi_long sc_ps;
+    abi_long sc_regs[32];
+    abi_long sc_ownedfp;
+    abi_long sc_fpregs[32];
+    abi_ulong sc_fpcr;
+    abi_ulong sc_fp_control;
+    abi_ulong sc_reserved1;
+    abi_ulong sc_reserved2;
+    abi_ulong sc_ssize;
+    abi_ulong sc_sbase;
+    abi_ulong sc_traparg_a0;
+    abi_ulong sc_traparg_a1;
+    abi_ulong sc_traparg_a2;
+    abi_ulong sc_fp_trap_pc;
+    abi_ulong sc_fp_trigger_sum;
+    abi_ulong sc_fp_trigger_inst;
+};
+
+struct target_ucontext {
+    abi_ulong uc_flags;
+    abi_ulong uc_link;
+    abi_ulong uc_osf_sigmask;
+    target_stack_t uc_stack;
+    struct target_sigcontext uc_mcontext;
+    target_sigset_t uc_sigmask;
+};
+
+struct target_sigframe {
+    struct target_sigcontext sc;
+    unsigned int retcode[3];
+};
+
+struct target_rt_sigframe {
+    target_siginfo_t info;
+    struct target_ucontext uc;
+    unsigned int retcode[3];
+};
+
+#define INSN_MOV_R30_R16        0x47fe0410
+#define INSN_LDI_R0             0x201f0000
+#define INSN_CALLSYS            0x00000083
+
+static int setup_sigcontext(struct target_sigcontext *sc, CPUState *env,
+                            abi_ulong frame_addr, target_sigset_t *set)
+{
+    int i, err = 0;
+
+    err |= __put_user(on_sig_stack(frame_addr), &sc->sc_onstack);
+    err |= __put_user(set->sig[0], &sc->sc_mask);
+    err |= __put_user(env->pc, &sc->sc_pc);
+    err |= __put_user(8, &sc->sc_ps);
+
+    for (i = 0; i < 31; ++i) {
+        err |= __put_user(env->ir[i], &sc->sc_regs[i]);
+    }
+    err |= __put_user(0, &sc->sc_regs[31]);
+
+    for (i = 0; i < 31; ++i) {
+        err |= __put_user(env->fir[i], &sc->sc_fpregs[i]);
+    }
+    err |= __put_user(0, &sc->sc_fpregs[31]);
+    err |= __put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr);
+
+    err |= __put_user(0, &sc->sc_traparg_a0); /* FIXME */
+    err |= __put_user(0, &sc->sc_traparg_a1); /* FIXME */
+    err |= __put_user(0, &sc->sc_traparg_a2); /* FIXME */
+
+    return err;
+}
+
+static int restore_sigcontext(CPUState *env, struct target_sigcontext *sc)
+{
+    uint64_t fpcr;
+    int i, err = 0;
+
+    err |= __get_user(env->pc, &sc->sc_pc);
+
+    for (i = 0; i < 31; ++i) {
+        err |= __get_user(env->ir[i], &sc->sc_regs[i]);
+    }
+    for (i = 0; i < 31; ++i) {
+        err |= __get_user(env->fir[i], &sc->sc_fpregs[i]);
+    }
+
+    err |= __get_user(fpcr, &sc->sc_fpcr);
+    cpu_alpha_store_fpcr(env, fpcr);
+
+    return err;
+}
+
+static inline abi_ulong get_sigframe(struct target_sigaction *sa, 
+                                     CPUState *env, unsigned long framesize)
+{
+    abi_ulong sp = env->ir[IR_SP];
+
+    /* This is the X/Open sanctioned signal stack switching.  */
+    if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) {
+        sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+    }
+    return (sp - framesize) & -32;
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+                        target_sigset_t *set, CPUState *env)
+{
+    abi_ulong frame_addr, r26;
+    struct target_sigframe *frame;
+    int err = 0;
+
+    frame_addr = get_sigframe(ka, env, sizeof(*frame));
+    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+        goto give_sigsegv;
+    }
+
+    err |= setup_sigcontext(&frame->sc, env, frame_addr, set);
+
+    if (ka->sa_restorer) {
+        r26 = ka->sa_restorer;
+    } else {
+        err |= __put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
+        err |= __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn,
+                          &frame->retcode[1]);
+        err |= __put_user(INSN_CALLSYS, &frame->retcode[2]);
+        /* imb() */
+        r26 = frame_addr;
+    }
+
+    unlock_user_struct(frame, frame_addr, 1);
+
+    if (err) {
+    give_sigsegv:
+        if (sig == TARGET_SIGSEGV) {
+            ka->_sa_handler = TARGET_SIG_DFL;
+        }
+        force_sig(TARGET_SIGSEGV);
+    }
+
+    env->ir[IR_RA] = r26;
+    env->ir[IR_PV] = env->pc = ka->_sa_handler;
+    env->ir[IR_A0] = sig;
+    env->ir[IR_A1] = 0;
+    env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc);
+    env->ir[IR_SP] = frame_addr;
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+                           target_siginfo_t *info,
+			   target_sigset_t *set, CPUState *env)
+{
+    abi_ulong frame_addr, r26;
+    struct target_rt_sigframe *frame;
+    int i, err = 0;
+
+    frame_addr = get_sigframe(ka, env, sizeof(*frame));
+    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+        goto give_sigsegv;
+    }
+        
+    err |= copy_siginfo_to_user(&frame->info, info);
+
+    err |= __put_user(0, &frame->uc.uc_flags);
+    err |= __put_user(0, &frame->uc.uc_link);
+    err |= __put_user(set->sig[0], &frame->uc.uc_osf_sigmask);
+    err |= __put_user(target_sigaltstack_used.ss_sp,
+                      &frame->uc.uc_stack.ss_sp);
+    err |= __put_user(sas_ss_flags(env->ir[IR_SP]),
+                      &frame->uc.uc_stack.ss_flags);
+    err |= __put_user(target_sigaltstack_used.ss_size,
+                      &frame->uc.uc_stack.ss_size);
+    err |= setup_sigcontext(&frame->uc.uc_mcontext, env, frame_addr, set);
+    for (i = 0; i < TARGET_NSIG_WORDS; ++i) {
+        err |= __put_user(set->sig[i], &frame->uc.uc_sigmask.sig[i]);
+    }
+
+    if (ka->sa_restorer) {
+        r26 = ka->sa_restorer;
+    } else {
+        err |= __put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
+        err |= __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn,
+                          &frame->retcode[1]);
+        err |= __put_user(INSN_CALLSYS, &frame->retcode[2]);
+        /* imb(); */
+        r26 = frame_addr;
+    }
+
+    if (err) {
+    give_sigsegv:
+       if (sig == TARGET_SIGSEGV) {
+            ka->_sa_handler = TARGET_SIG_DFL;
+        }
+        force_sig(TARGET_SIGSEGV);
+    }
+
+    env->ir[IR_RA] = r26;
+    env->ir[IR_PV] = env->pc = ka->_sa_handler;
+    env->ir[IR_A0] = sig;
+    env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info);
+    env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
+    env->ir[IR_SP] = frame_addr;
+}
+
+long do_sigreturn(CPUState *env)
+{
+    struct target_sigcontext *sc;
+    abi_ulong sc_addr = env->ir[IR_A0];
+    target_sigset_t target_set;
+    sigset_t set;
+
+    if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) {
+        goto badframe;
+    }
+
+    target_sigemptyset(&target_set);
+    if (__get_user(target_set.sig[0], &sc->sc_mask)) {
+        goto badframe;
+    }
+
+    target_to_host_sigset_internal(&set, &target_set);
+    sigprocmask(SIG_SETMASK, &set, NULL);
+
+    if (restore_sigcontext(env, sc)) {
+        goto badframe;
+    }
+    unlock_user_struct(sc, sc_addr, 0);
+    return env->ir[IR_V0];
+
+ badframe:
+    unlock_user_struct(sc, sc_addr, 0);
+    force_sig(TARGET_SIGSEGV);
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+    abi_ulong frame_addr = env->ir[IR_A0];
+    struct target_rt_sigframe *frame;
+    sigset_t set;
+
+    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+        goto badframe;
+    }
+    target_to_host_sigset(&set, &frame->uc.uc_sigmask);
+    sigprocmask(SIG_SETMASK, &set, NULL);
+
+    if (restore_sigcontext(env, &frame->uc.uc_mcontext)) {
+        goto badframe;
+    }
+    if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe,
+                                             uc.uc_stack),
+                       0, env->ir[IR_SP]) == -EFAULT) {
+        goto badframe;
+    }
+
+    unlock_user_struct(frame, frame_addr, 0);
+    return env->ir[IR_V0];
+
+
+ badframe:
+    unlock_user_struct(frame, frame_addr, 0);
+    force_sig(TARGET_SIGSEGV);
+}
+
 #else
 
 static void setup_frame(int sig, struct target_sigaction *ka,
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 9fb493f..38eb35f 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -4775,20 +4775,18 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #ifdef TARGET_NR_sigaction
     case TARGET_NR_sigaction:
         {
-#if !defined(TARGET_MIPS)
+#if defined(TARGET_ALPHA)
+            struct target_sigaction act, oact, *pact = 0;
             struct target_old_sigaction *old_act;
-            struct target_sigaction act, oact, *pact;
             if (arg2) {
                 if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1))
                     goto efault;
                 act._sa_handler = old_act->_sa_handler;
                 target_siginitset(&act.sa_mask, old_act->sa_mask);
                 act.sa_flags = old_act->sa_flags;
-                act.sa_restorer = old_act->sa_restorer;
+                act.sa_restorer = 0;
                 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) {
@@ -4797,10 +4795,9 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                 old_act->_sa_handler = oact._sa_handler;
                 old_act->sa_mask = oact.sa_mask.sig[0];
                 old_act->sa_flags = oact.sa_flags;
-                old_act->sa_restorer = oact.sa_restorer;
                 unlock_user_struct(old_act, arg3, 1);
             }
-#else
+#elif defined(TARGET_MIPS)
 	    struct target_sigaction act, oact, *pact, *old_act;
 
 	    if (arg2) {
@@ -4828,12 +4825,61 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 		old_act->sa_mask.sig[3] = 0;
 		unlock_user_struct(old_act, arg3, 1);
 	    }
+#else
+            struct target_old_sigaction *old_act;
+            struct target_sigaction act, oact, *pact;
+            if (arg2) {
+                if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1))
+                    goto efault;
+                act._sa_handler = old_act->_sa_handler;
+                target_siginitset(&act.sa_mask, old_act->sa_mask);
+                act.sa_flags = old_act->sa_flags;
+                act.sa_restorer = old_act->sa_restorer;
+                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) {
+                if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0))
+                    goto efault;
+                old_act->_sa_handler = oact._sa_handler;
+                old_act->sa_mask = oact.sa_mask.sig[0];
+                old_act->sa_flags = oact.sa_flags;
+                old_act->sa_restorer = oact.sa_restorer;
+                unlock_user_struct(old_act, arg3, 1);
+            }
 #endif
         }
         break;
 #endif
     case TARGET_NR_rt_sigaction:
         {
+#if defined(TARGET_ALPHA)
+            struct target_sigaction act, oact, *pact = 0;
+            struct target_rt_sigaction *rt_act;
+            /* ??? arg4 == sizeof(sigset_t).  */
+            if (arg2) {
+                if (!lock_user_struct(VERIFY_READ, rt_act, arg2, 1))
+                    goto efault;
+                act._sa_handler = rt_act->_sa_handler;
+                act.sa_mask = rt_act->sa_mask;
+                act.sa_flags = rt_act->sa_flags;
+                act.sa_restorer = arg5;
+                unlock_user_struct(rt_act, arg2, 0);
+                pact = &act;
+            }
+            ret = get_errno(do_sigaction(arg1, pact, &oact));
+            if (!is_error(ret) && arg3) {
+                if (!lock_user_struct(VERIFY_WRITE, rt_act, arg3, 0))
+                    goto efault;
+                rt_act->_sa_handler = oact._sa_handler;
+                rt_act->sa_mask = oact.sa_mask;
+                rt_act->sa_flags = oact.sa_flags;
+                unlock_user_struct(rt_act, arg3, 1);
+            }
+#else
             struct target_sigaction *act;
             struct target_sigaction *oact;
 
@@ -4855,6 +4901,7 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                 unlock_user_struct(act, arg2, 0);
             if (oact)
                 unlock_user_struct(oact, arg3, 1);
+#endif
         }
         break;
 #ifdef TARGET_NR_sgetmask /* not on alpha */
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 2d45753..63c2bc3 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -472,8 +472,28 @@  int do_sigaction(int sig, const struct target_sigaction *act,
 
 #endif
 
-#if defined(TARGET_MIPS)
+#if defined(TARGET_ALPHA)
+struct target_old_sigaction {
+    abi_ulong _sa_handler;
+    abi_ulong sa_mask;
+    abi_ulong sa_flags;
+};
+
+struct target_rt_sigaction {
+    abi_ulong _sa_handler;
+    abi_ulong sa_flags;
+    target_sigset_t sa_mask;
+};
 
+/* This is the struct used inside the kernel.  The ka_restorer
+   field comes from the 5th argument to sys_rt_sigaction.  */
+struct target_sigaction {
+    abi_ulong _sa_handler;
+    abi_ulong sa_flags;
+    target_sigset_t sa_mask;
+    abi_ulong sa_restorer;
+};
+#elif defined(TARGET_MIPS)
 struct target_sigaction {
 	uint32_t	sa_flags;
 #if defined(TARGET_ABI_MIPSN32)
@@ -483,7 +503,6 @@  struct target_sigaction {
 #endif
 	target_sigset_t	sa_mask;
 };
-
 #else
 struct target_old_sigaction {
         abi_ulong _sa_handler;
diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h
index c0dff4b..f9b1a6d 100644
--- a/target-alpha/cpu.h
+++ b/target-alpha/cpu.h
@@ -487,11 +487,9 @@  uint64_t cpu_alpha_load_fpcr (CPUState *env);
 void cpu_alpha_store_fpcr (CPUState *env, uint64_t val);
 int cpu_alpha_mfpr (CPUState *env, int iprn, uint64_t *valp);
 int cpu_alpha_mtpr (CPUState *env, int iprn, uint64_t val, uint64_t *oldvalp);
-void pal_init (CPUState *env);
 #if !defined (CONFIG_USER_ONLY)
+void pal_init (CPUState *env);
 void call_pal (CPUState *env);
-#else
-void call_pal (CPUState *env, int palcode);
 #endif
 
 static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
diff --git a/target-alpha/translate.c b/target-alpha/translate.c
index 87813e7..36afcfc 100644
--- a/target-alpha/translate.c
+++ b/target-alpha/translate.c
@@ -2750,8 +2750,9 @@  CPUAlphaState * cpu_alpha_init (const char *cpu_model)
     env->ps |= 1 << 3;
     cpu_alpha_store_fpcr(env, (FPCR_INVD | FPCR_DZED | FPCR_OVFD
                                | FPCR_UNFD | FPCR_INED | FPCR_DNOD));
-#endif
+#else
     pal_init(env);
+#endif
     /* Initialize IPR */
     hwpcb = env->ipr[IPR_PCBB];
     env->ipr[IPR_ASN] = 0;