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
This commit is contained in:
ths 2007-12-02 06:31:25 +00:00
parent 03aa197628
commit c3b5bc8ab3
4 changed files with 331 additions and 13 deletions

View File

@ -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);

View File

@ -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 */

View File

@ -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,

View File

@ -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;
}