From c3b5bc8ab325bd3aee46ea5c0884c9e6f1636ccb Mon Sep 17 00:00:00 2001 From: ths Date: Sun, 2 Dec 2007 06:31:25 +0000 Subject: [PATCH] SH4: Signal handling for the user space emulator, by Magnus Damm. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3764 c046a42c-6fe2-441c-8c8c-71466251a162 --- linux-user/main.c | 12 ++ linux-user/sh4/target_signal.h | 5 + linux-user/signal.c | 321 ++++++++++++++++++++++++++++++++- target-sh4/helper.c | 6 +- 4 files changed, 331 insertions(+), 13 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index cfc9065a51..379ac2962d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1617,6 +1617,9 @@ void cpu_loop (CPUState *env) env->gregs[0] = ret; env->pc += 2; break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; case EXCP_DEBUG: { int sig; @@ -1631,6 +1634,15 @@ void cpu_loop (CPUState *env) } } break; + case 0xa0: + case 0xc0: + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->tea; + queue_signal(info.si_signo, &info); + break; + default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(env, stderr, fprintf, 0); diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index 1fb70921f3..e148da0925 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -21,4 +21,9 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 +static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) +{ + return state->gregs[15]; +} + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 730c9a2559..312e41941e 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -562,6 +562,12 @@ static inline int copy_siginfo_to_user(target_siginfo_t *tinfo, return 0; } +static inline int current_exec_domain_sig(int sig) +{ + return /* current->exec_domain && current->exec_domain->signal_invmap + && sig < 32 ? current->exec_domain->signal_invmap[sig] : */ sig; +} + #if defined(TARGET_I386) && TARGET_ABI_BITS == 32 /* from the Linux kernel */ @@ -745,11 +751,7 @@ static void setup_frame(int sig, struct emulated_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - err |= __put_user((/*current->exec_domain - && current->exec_domain->signal_invmap - && sig < 32 - ? current->exec_domain->signal_invmap[sig] - : */ sig), + err |= __put_user(current_exec_domain_sig(sig), &frame->sig); if (err) goto give_sigsegv; @@ -819,11 +821,7 @@ static void setup_rt_frame(int sig, struct emulated_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - err |= __put_user((/*current->exec_domain - && current->exec_domain->signal_invmap - && sig < 32 - ? current->exec_domain->signal_invmap[sig] - : */sig), + err |= __put_user(current_exec_domain_sig(sig), &frame->sig); addr = frame_addr + offsetof(struct rt_sigframe, info); err |= __put_user(addr, &frame->pinfo); @@ -2406,6 +2404,309 @@ long do_rt_sigreturn(CPUState *env) return -TARGET_ENOSYS; } +#elif defined(TARGET_SH4) + +/* + * code and data structures from linux kernel: + * include/asm-sh/sigcontext.h + * arch/sh/kernel/signal.c + */ + +struct target_sigcontext { + target_ulong oldmask; + + /* CPU registers */ + target_ulong sc_gregs[16]; + target_ulong sc_pc; + target_ulong sc_pr; + target_ulong sc_sr; + target_ulong sc_gbr; + target_ulong sc_mach; + target_ulong sc_macl; + + /* FPU registers */ + target_ulong sc_fpregs[16]; + target_ulong sc_xfpregs[16]; + unsigned int sc_fpscr; + unsigned int sc_fpul; + unsigned int sc_ownedfp; +}; + +struct target_sigframe +{ + struct target_sigcontext sc; + target_ulong extramask[TARGET_NSIG_WORDS-1]; + uint16_t retcode[3]; +}; + + +struct target_ucontext { + target_ulong uc_flags; + struct target_ucontext *uc_link; + target_stack_t uc_stack; + struct target_sigcontext uc_mcontext; + target_sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe +{ + struct target_siginfo info; + struct target_ucontext uc; + uint16_t retcode[3]; +}; + + +#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */ +#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */ + +static abi_ulong get_sigframe(struct emulated_sigaction *ka, + unsigned long sp, size_t frame_size) +{ + if ((ka->sa.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) & -8ul; +} + +static int setup_sigcontext(struct target_sigcontext *sc, + CPUState *regs, unsigned long mask) +{ + int err = 0; + +#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) + COPY(gregs[0]); COPY(gregs[1]); + COPY(gregs[2]); COPY(gregs[3]); + COPY(gregs[4]); COPY(gregs[5]); + COPY(gregs[6]); COPY(gregs[7]); + COPY(gregs[8]); COPY(gregs[9]); + COPY(gregs[10]); COPY(gregs[11]); + COPY(gregs[12]); COPY(gregs[13]); + COPY(gregs[14]); COPY(gregs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + + /* todo: save FPU registers here */ + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +static int restore_sigcontext(struct CPUState *regs, + struct target_sigcontext *sc) +{ + unsigned int err = 0; + +#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) + COPY(gregs[1]); + COPY(gregs[2]); COPY(gregs[3]); + COPY(gregs[4]); COPY(gregs[5]); + COPY(gregs[6]); COPY(gregs[7]); + COPY(gregs[8]); COPY(gregs[9]); + COPY(gregs[10]); COPY(gregs[11]); + COPY(gregs[12]); COPY(gregs[13]); + COPY(gregs[14]); COPY(gregs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + + /* todo: restore FPU registers here */ + + regs->tra = -1; /* disable syscall checks */ + return err; +} + +static void setup_frame(int sig, struct emulated_sigaction *ka, + target_sigset_t *set, CPUState *regs) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + int i; + int err = 0; + int signal; + + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto give_sigsegv; + + signal = current_exec_domain_sig(sig); + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + + for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { + err |= __put_user(set->sig[i + 1], &frame->extramask[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & TARGET_SA_RESTORER) { + regs->pr = (unsigned long) ka->sa.sa_restorer; + } else { + /* Generate return code (system call to sigreturn) */ + err |= __put_user(MOVW(2), &frame->retcode[0]); + err |= __put_user(TRAP_NOARG, &frame->retcode[1]); + err |= __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); + regs->pr = (unsigned long) frame->retcode; + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->gregs[15] = (unsigned long) frame; + regs->gregs[4] = signal; /* Arg for signal handler */ + regs->gregs[5] = 0; + regs->gregs[6] = (unsigned long) &frame->sc; + regs->pc = (unsigned long) ka->sa._sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sig(SIGSEGV); +} + +static void setup_rt_frame(int sig, struct emulated_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *regs) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + int i; + int err = 0; + int signal; + + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto give_sigsegv; + + signal = current_exec_domain_sig(sig); + + err |= copy_siginfo_to_user(&frame->info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, (unsigned long *)&frame->uc.uc_link); + err |= __put_user((void *)target_sigaltstack_used.ss_sp, + &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->gregs[15]), + &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, + regs, set->sig[0]); + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + err |= __put_user(set->sig[i], &frame->uc.uc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & TARGET_SA_RESTORER) { + regs->pr = (unsigned long) ka->sa.sa_restorer; + } else { + /* Generate return code (system call to sigreturn) */ + err |= __put_user(MOVW(2), &frame->retcode[0]); + err |= __put_user(TRAP_NOARG, &frame->retcode[1]); + err |= __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); + regs->pr = (unsigned long) frame->retcode; + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->gregs[15] = (unsigned long) frame; + regs->gregs[4] = signal; /* Arg for signal handler */ + regs->gregs[5] = (unsigned long) &frame->info; + regs->gregs[6] = (unsigned long) &frame->uc; + regs->pc = (unsigned long) ka->sa._sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sig(SIGSEGV); +} + +long do_sigreturn(CPUState *regs) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + target_sigset_t target_set; + int i; + int err = 0; + +#if defined(DEBUG_SIGNAL) + fprintf(stderr, "do_sigreturn\n"); +#endif + frame_addr = regs->gregs[15]; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + err |= __get_user(target_set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + err |= (__get_user(target_set.sig[i], &frame->extramask[i - 1])); + } + + if (err) + goto badframe; + + target_to_host_sigset_internal(&blocked, &target_set); + sigprocmask(SIG_SETMASK, &blocked, NULL); + + if (restore_sigcontext(regs, &frame->sc)) + goto badframe; + + unlock_user_struct(frame, frame_addr, 0); + return regs->gregs[0]; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} + +long do_rt_sigreturn(CPUState *regs) +{ + 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 = regs->gregs[15]; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + target_to_host_sigset(&blocked, &frame->uc.uc_sigmask); + sigprocmask(SIG_SETMASK, &blocked, NULL); + + if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + goto badframe; + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.uc_stack), + 0, get_sp_from_cpustate(regs)) == -EFAULT) + goto badframe; + + unlock_user_struct(frame, frame_addr, 0); + return regs->gregs[0]; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} + #else static void setup_frame(int sig, struct emulated_sigaction *ka, diff --git a/target-sh4/helper.c b/target-sh4/helper.c index 11e2e0f24b..52bef2fca1 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -40,16 +40,16 @@ int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw, int mmu_idx, int is_softmmu) { env->tea = address; + env->exception_index = 0; switch (rw) { case 0: + env->tea = address; env->exception_index = 0x0a0; break; case 1: + env->tea = address; env->exception_index = 0x0c0; break; - case 2: - env->exception_index = 0x0a0; - break; } return 1; }