We've been using the 32-bit sparc abi for 64-bit signals. There's a surprising amount of difference, beginning with the fact that 64-bit always uses rt signal frames.
Signed-off-by: Richard Henderson <richard.hender...@linaro.org> --- linux-user/sparc/target_signal.h | 2 + linux-user/sparc64/target_syscall.h | 14 +- linux-user/sparc64/signal.c | 245 +++++++++++++++++++++++++++- 3 files changed, 254 insertions(+), 7 deletions(-) diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index 911a3f5af5..651320ab8d 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -67,7 +67,9 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 +#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32) #define TARGET_ARCH_HAS_SETUP_FRAME +#endif /* bit-flags */ #define TARGET_SS_AUTODISARM (1U << 31) /* disable sas during sighandling */ diff --git a/linux-user/sparc64/target_syscall.h b/linux-user/sparc64/target_syscall.h index 696a68b1ed..fcc71db16e 100644 --- a/linux-user/sparc64/target_syscall.h +++ b/linux-user/sparc64/target_syscall.h @@ -4,14 +4,16 @@ #include "../sparc/target_errno.h" struct target_pt_regs { - abi_ulong u_regs[16]; - abi_ulong tstate; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong fprs; + abi_ulong u_regs[16]; + abi_ulong tstate; + abi_ulong pc; + abi_ulong npc; + uint32_t y; + uint32_t magic; }; +#define TARGET_PT_REGS_MAGIC 0x57ac6c00 + #define UNAME_MACHINE "sparc64" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/sparc64/signal.c b/linux-user/sparc64/signal.c index d27e049c2a..7ba811d342 100644 --- a/linux-user/sparc64/signal.c +++ b/linux-user/sparc64/signal.c @@ -17,7 +17,10 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include "../sparc/signal.c" +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" #define SPARC_MC_TSTATE 0 #define SPARC_MC_PC 1 @@ -295,3 +298,243 @@ void sparc64_get_context(CPUSPARCState *env) unlock_user_struct(ucp, ucp_addr, 1); force_sig(TARGET_SIGSEGV); } + +struct target_sparc_stackf { + struct target_reg_window win; + uint64_t xargs[8]; +}; + +struct target_siginfo_fpu_t { + uint64_t dregs[32]; + uint64_t fsr; + uint64_t gsr; + uint64_t fprs; +}; + +struct target_sigcontext { + target_siginfo_t info; + struct target_pt_regs regs; + uint64_t fpu_save; + target_stack_t stack; + target_sigset_t mask; + uint64_t rwin_save; +}; + +struct target_rt_sigframe { + struct target_sparc_stackf ss; + struct target_sigcontext sc; + struct target_siginfo_fpu_t fpu; +}; + +static abi_ulong get_sigframe(struct target_sigaction *sa, + CPUSPARCState *env, int framesize) +{ + abi_ulong sp = target_sigsp(get_sp_from_cpustate(env), sa); + return (sp - framesize) & -16; +} + +static void save_pt_regs(struct target_pt_regs *regs, CPUSPARCState *env) +{ + int i; + + for (i = 0; i < 8; i++) { + __put_user(env->gregs[i], ®s->u_regs[i]); + } + for (i = 0; i < 8; i++) { + __put_user(env->regwptr[WREG_O0 + i], ®s->u_regs[i + 8]); + } + __put_user(sparc64_tstate(env), ®s->tstate); + __put_user(env->pc, ®s->pc); + __put_user(env->npc, ®s->npc); + __put_user(env->y, ®s->y); + __put_user(TARGET_PT_REGS_MAGIC, ®s->magic); +} + +static void restore_pt_regs(struct target_pt_regs *regs, CPUSPARCState *env) +{ + uint64_t tstate; + int i; + + for (i = 0; i < 8; i++) { + __get_user(env->gregs[i], ®s->u_regs[i]); + } + for (i = 0; i < 8; i++) { + __get_user(env->regwptr[WREG_O0 + i], ®s->u_regs[i + 8]); + } + + __get_user(env->y, ®s->y); + __get_user(tstate, ®s->tstate); + + /* User can only change condition codes and %asi in tstate. */ + cpu_put_ccr(env, tstate >> 32); + env->asi = extract64(tstate, 24, 8); +} + +static void save_fpu_state(struct target_siginfo_fpu_t *regs, + CPUSPARCState *env) +{ + int i; + + /* QEMU does not lazy fpu saving. Save the entire fp register bank. */ + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].ll, ®s->dregs[i]); + } + __put_user(env->fsr, ®s->fsr); + __put_user(env->gsr, ®s->gsr); + __put_user(env->fprs, ®s->fprs); +} + +static void restore_fpu_state(struct target_siginfo_fpu_t *regs, + CPUSPARCState *env) +{ + uint64_t fprs; + int i; + + /* In case the user mucks about with FPRS, restore as directed. */ + __get_user(fprs, ®s->fprs); + if (fprs & FPRS_DL) { + for (i = 0; i < 16; ++i) { + __get_user(env->fpr[i].ll, ®s->dregs[i]); + } + } + if (fprs & FPRS_DU) { + for (i = 16; i < 32; ++i) { + __get_user(env->fpr[i].ll, ®s->dregs[i]); + } + } + __get_user(env->fsr, ®s->fsr); + __get_user(env->gsr, ®s->gsr); + env->fprs |= fprs; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSPARCState *env) +{ + abi_ulong sf_addr, sp; + struct target_rt_sigframe *sf = NULL; + void *window; + + sf_addr = get_sigframe(ka, env, sizeof(*sf)); + trace_user_setup_rt_frame(env, sf_addr); + if (!lock_user_struct(VERIFY_WRITE, sf, sf_addr, 0)) { + goto do_sigsegv; + } + + /* 2. Save the current process state */ + save_pt_regs(&sf->sc.regs, env); + save_fpu_state(&sf->fpu, env); + __put_user(sf_addr + offsetof(struct target_rt_sigframe, fpu), + &sf->sc.fpu_save); + __put_user(0, &sf->sc.rwin_save); /* TODO: save_rwin_state */ + + /* + * Copy one register window from the top-of-stack into the signal frame. + * The balance of the sparc_stackf struct is for the callee --- the call + * abi requires the space for spilling argument registers. + */ + sp = get_sp_from_cpustate(env); + window = lock_user(VERIFY_READ, sp, sizeof(struct target_reg_window), 1); + if (!window) { + goto do_sigsegv; + } + memcpy(sf, window, sizeof(struct target_reg_window)); + unlock_user(window, sp, 0); + + target_save_altstack(&sf->sc.stack, env); + for (int i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &sf->sc.mask.sig[i]); + } + + unlock_user(sf, sf_addr, sizeof(*sf)); + + /* 3. signal handler back-trampoline and parameters */ + env->regwptr[WREG_SP] = sf_addr - TARGET_STACK_BIAS; + env->regwptr[WREG_O0] = sig; + env->regwptr[WREG_O1] = sf_addr + offsetof(struct target_rt_sigframe, sc); + env->regwptr[WREG_O2] = sf_addr + offsetof(struct target_rt_sigframe, sc); + + /* 4. return to kernel instructions */ + env->regwptr[WREG_O7] = ka->ka_restorer; + + /* 5. signal handler */ + env->pc = ka->_sa_handler; + env->npc = env->pc + 4; + return; + + do_sigsegv: + unlock_user(sf, sf_addr, 0); + force_sigsegv(sig); +} + +/* + * __NR_sigreturn still exists for backward compatiblity, + * but it is set to sys_nis_syscall for sparc64. + */ +long do_sigreturn(CPUSPARCState *env) +{ + return -TARGET_ENOSYS; +} + +long do_rt_sigreturn(CPUSPARCState *env) +{ + abi_ulong sf_addr, sc_addr, tpc, tnpc, ptr; + struct target_sigcontext *sc = NULL; + sigset_t set; + + sf_addr = get_sp_from_cpustate(env); + trace_user_do_rt_sigreturn(env, sf_addr); + + if (sf_addr & 15) { + goto do_sigsegv; + } + sc_addr = sf_addr + offsetof(struct target_rt_sigframe, sc); + if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) { + goto do_sigsegv; + } + + /* Validate SP alignment. */ + __get_user(ptr, &sc->regs.u_regs[8 + WREG_SP]); + if ((ptr + TARGET_STACK_BIAS) & 7) { + goto do_sigsegv; + } + + /* Validate PC and NPC alignment. */ + __get_user(tpc, &sc->regs.pc); + __get_user(tnpc, &sc->regs.npc); + if ((tpc | tnpc) & 3) { + goto do_sigsegv; + } + + restore_pt_regs(&sc->regs, env); + + __get_user(ptr, &sc->fpu_save); + if (ptr) { + struct target_siginfo_fpu_t *fpu; + if ((ptr & 7) || !lock_user_struct(VERIFY_READ, fpu, ptr, 1)) { + goto do_sigsegv; + } + restore_fpu_state(fpu, env); + unlock_user_struct(fpu, ptr, 0); + } + + __get_user(ptr, &sc->rwin_save); + if (ptr) { + goto do_sigsegv; /* TODO: restore_rwin_state */ + } + + target_to_host_sigset(&set, &sc->mask); + set_sigmask(&set); + target_restore_altstack(&sc->stack, env); + + env->pc = tpc; + env->npc = tnpc; + + unlock_user_struct(sc, sc_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + do_sigsegv: + unlock_user_struct(sc, sc_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} -- 2.25.1