qemu/linux-user/sh4/signal.c
<<
>>
Prefs
   1/*
   2 *  Emulation of Linux signals
   3 *
   4 *  Copyright (c) 2003 Fabrice Bellard
   5 *
   6 *  This program is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License as published by
   8 *  the Free Software Foundation; either version 2 of the License, or
   9 *  (at your option) any later version.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19#include "qemu/osdep.h"
  20#include "qemu.h"
  21#include "signal-common.h"
  22#include "linux-user/trace.h"
  23
  24/*
  25 * code and data structures from linux kernel:
  26 * include/asm-sh/sigcontext.h
  27 * arch/sh/kernel/signal.c
  28 */
  29
  30struct target_sigcontext {
  31    target_ulong  oldmask;
  32
  33    /* CPU registers */
  34    target_ulong  sc_gregs[16];
  35    target_ulong  sc_pc;
  36    target_ulong  sc_pr;
  37    target_ulong  sc_sr;
  38    target_ulong  sc_gbr;
  39    target_ulong  sc_mach;
  40    target_ulong  sc_macl;
  41
  42    /* FPU registers */
  43    target_ulong  sc_fpregs[16];
  44    target_ulong  sc_xfpregs[16];
  45    unsigned int sc_fpscr;
  46    unsigned int sc_fpul;
  47    unsigned int sc_ownedfp;
  48};
  49
  50struct target_sigframe
  51{
  52    struct target_sigcontext sc;
  53    target_ulong extramask[TARGET_NSIG_WORDS-1];
  54    uint16_t retcode[3];
  55};
  56
  57
  58struct target_ucontext {
  59    target_ulong tuc_flags;
  60    struct target_ucontext *tuc_link;
  61    target_stack_t tuc_stack;
  62    struct target_sigcontext tuc_mcontext;
  63    target_sigset_t tuc_sigmask;        /* mask last for extensibility */
  64};
  65
  66struct target_rt_sigframe
  67{
  68    struct target_siginfo info;
  69    struct target_ucontext uc;
  70    uint16_t retcode[3];
  71};
  72
  73
  74#define MOVW(n)  (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */
  75#define TRAP_NOARG 0xc310         /* Syscall w/no args (NR in R3) SH3/4 */
  76
  77static abi_ulong get_sigframe(struct target_sigaction *ka,
  78                              unsigned long sp, size_t frame_size)
  79{
  80    sp = target_sigsp(sp, ka);
  81
  82    return (sp - frame_size) & -8ul;
  83}
  84
  85/*
  86 * Notice when we're in the middle of a gUSA region and reset.
  87 * Note that this will only occur when #CF_PARALLEL is unset, as we
  88 * will translate such sequences differently in a parallel context.
  89 */
  90static void unwind_gusa(CPUSH4State *regs)
  91{
  92    /* If the stack pointer is sufficiently negative, and we haven't
  93       completed the sequence, then reset to the entry to the region.  */
  94    /* ??? The SH4 kernel checks for and address above 0xC0000000.
  95       However, the page mappings in qemu linux-user aren't as restricted
  96       and we wind up with the normal stack mapped above 0xF0000000.
  97       That said, there is no reason why the kernel should be allowing
  98       a gUSA region that spans 1GB.  Use a tighter check here, for what
  99       can actually be enabled by the immediate move.  */
 100    if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) {
 101        /* Reset the PC to before the gUSA region, as computed from
 102           R0 = region end, SP = -(region size), plus one more for the
 103           insn that actually initializes SP to the region size.  */
 104        regs->pc = regs->gregs[0] + regs->gregs[15] - 2;
 105
 106        /* Reset the SP to the saved version in R1.  */
 107        regs->gregs[15] = regs->gregs[1];
 108    }
 109}
 110
 111static void setup_sigcontext(struct target_sigcontext *sc,
 112                             CPUSH4State *regs, unsigned long mask)
 113{
 114    int i;
 115
 116#define COPY(x)         __put_user(regs->x, &sc->sc_##x)
 117    COPY(gregs[0]); COPY(gregs[1]);
 118    COPY(gregs[2]); COPY(gregs[3]);
 119    COPY(gregs[4]); COPY(gregs[5]);
 120    COPY(gregs[6]); COPY(gregs[7]);
 121    COPY(gregs[8]); COPY(gregs[9]);
 122    COPY(gregs[10]); COPY(gregs[11]);
 123    COPY(gregs[12]); COPY(gregs[13]);
 124    COPY(gregs[14]); COPY(gregs[15]);
 125    COPY(gbr); COPY(mach);
 126    COPY(macl); COPY(pr);
 127    COPY(sr); COPY(pc);
 128#undef COPY
 129
 130    for (i=0; i<16; i++) {
 131        __put_user(regs->fregs[i], &sc->sc_fpregs[i]);
 132    }
 133    __put_user(regs->fpscr, &sc->sc_fpscr);
 134    __put_user(regs->fpul, &sc->sc_fpul);
 135
 136    /* non-iBCS2 extensions.. */
 137    __put_user(mask, &sc->oldmask);
 138}
 139
 140static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc)
 141{
 142    int i;
 143
 144#define COPY(x)         __get_user(regs->x, &sc->sc_##x)
 145    COPY(gregs[0]); COPY(gregs[1]);
 146    COPY(gregs[2]); COPY(gregs[3]);
 147    COPY(gregs[4]); COPY(gregs[5]);
 148    COPY(gregs[6]); COPY(gregs[7]);
 149    COPY(gregs[8]); COPY(gregs[9]);
 150    COPY(gregs[10]); COPY(gregs[11]);
 151    COPY(gregs[12]); COPY(gregs[13]);
 152    COPY(gregs[14]); COPY(gregs[15]);
 153    COPY(gbr); COPY(mach);
 154    COPY(macl); COPY(pr);
 155    COPY(sr); COPY(pc);
 156#undef COPY
 157
 158    for (i=0; i<16; i++) {
 159        __get_user(regs->fregs[i], &sc->sc_fpregs[i]);
 160    }
 161    __get_user(regs->fpscr, &sc->sc_fpscr);
 162    __get_user(regs->fpul, &sc->sc_fpul);
 163
 164    regs->tra = -1;         /* disable syscall checks */
 165    regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
 166}
 167
 168void setup_frame(int sig, struct target_sigaction *ka,
 169                 target_sigset_t *set, CPUSH4State *regs)
 170{
 171    struct target_sigframe *frame;
 172    abi_ulong frame_addr;
 173    int i;
 174
 175    unwind_gusa(regs);
 176
 177    frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
 178    trace_user_setup_frame(regs, frame_addr);
 179    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
 180        goto give_sigsegv;
 181    }
 182
 183    setup_sigcontext(&frame->sc, regs, set->sig[0]);
 184
 185    for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) {
 186        __put_user(set->sig[i + 1], &frame->extramask[i]);
 187    }
 188
 189    /* Set up to return from userspace.  If provided, use a stub
 190       already in userspace.  */
 191    if (ka->sa_flags & TARGET_SA_RESTORER) {
 192        regs->pr = (unsigned long) ka->sa_restorer;
 193    } else {
 194        /* Generate return code (system call to sigreturn) */
 195        abi_ulong retcode_addr = frame_addr +
 196                                 offsetof(struct target_sigframe, retcode);
 197        __put_user(MOVW(2), &frame->retcode[0]);
 198        __put_user(TRAP_NOARG, &frame->retcode[1]);
 199        __put_user((TARGET_NR_sigreturn), &frame->retcode[2]);
 200        regs->pr = (unsigned long) retcode_addr;
 201    }
 202
 203    /* Set up registers for signal handler */
 204    regs->gregs[15] = frame_addr;
 205    regs->gregs[4] = sig; /* Arg for signal handler */
 206    regs->gregs[5] = 0;
 207    regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc);
 208    regs->pc = (unsigned long) ka->_sa_handler;
 209    regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
 210
 211    unlock_user_struct(frame, frame_addr, 1);
 212    return;
 213
 214give_sigsegv:
 215    unlock_user_struct(frame, frame_addr, 1);
 216    force_sigsegv(sig);
 217}
 218
 219void setup_rt_frame(int sig, struct target_sigaction *ka,
 220                    target_siginfo_t *info,
 221                    target_sigset_t *set, CPUSH4State *regs)
 222{
 223    struct target_rt_sigframe *frame;
 224    abi_ulong frame_addr;
 225    int i;
 226
 227    unwind_gusa(regs);
 228
 229    frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
 230    trace_user_setup_rt_frame(regs, frame_addr);
 231    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
 232        goto give_sigsegv;
 233    }
 234
 235    tswap_siginfo(&frame->info, info);
 236
 237    /* Create the ucontext.  */
 238    __put_user(0, &frame->uc.tuc_flags);
 239    __put_user(0, (unsigned long *)&frame->uc.tuc_link);
 240    target_save_altstack(&frame->uc.tuc_stack, regs);
 241    setup_sigcontext(&frame->uc.tuc_mcontext,
 242                     regs, set->sig[0]);
 243    for(i = 0; i < TARGET_NSIG_WORDS; i++) {
 244        __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
 245    }
 246
 247    /* Set up to return from userspace.  If provided, use a stub
 248       already in userspace.  */
 249    if (ka->sa_flags & TARGET_SA_RESTORER) {
 250        regs->pr = (unsigned long) ka->sa_restorer;
 251    } else {
 252        /* Generate return code (system call to sigreturn) */
 253        abi_ulong retcode_addr = frame_addr +
 254                                 offsetof(struct target_rt_sigframe, retcode);
 255        __put_user(MOVW(2), &frame->retcode[0]);
 256        __put_user(TRAP_NOARG, &frame->retcode[1]);
 257        __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]);
 258        regs->pr = (unsigned long) retcode_addr;
 259    }
 260
 261    /* Set up registers for signal handler */
 262    regs->gregs[15] = frame_addr;
 263    regs->gregs[4] = sig; /* Arg for signal handler */
 264    regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info);
 265    regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc);
 266    regs->pc = (unsigned long) ka->_sa_handler;
 267    regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
 268
 269    unlock_user_struct(frame, frame_addr, 1);
 270    return;
 271
 272give_sigsegv:
 273    unlock_user_struct(frame, frame_addr, 1);
 274    force_sigsegv(sig);
 275}
 276
 277long do_sigreturn(CPUSH4State *regs)
 278{
 279    struct target_sigframe *frame;
 280    abi_ulong frame_addr;
 281    sigset_t blocked;
 282    target_sigset_t target_set;
 283    int i;
 284
 285    frame_addr = regs->gregs[15];
 286    trace_user_do_sigreturn(regs, frame_addr);
 287    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
 288        goto badframe;
 289    }
 290
 291    __get_user(target_set.sig[0], &frame->sc.oldmask);
 292    for(i = 1; i < TARGET_NSIG_WORDS; i++) {
 293        __get_user(target_set.sig[i], &frame->extramask[i - 1]);
 294    }
 295
 296    target_to_host_sigset_internal(&blocked, &target_set);
 297    set_sigmask(&blocked);
 298
 299    restore_sigcontext(regs, &frame->sc);
 300
 301    unlock_user_struct(frame, frame_addr, 0);
 302    return -TARGET_QEMU_ESIGRETURN;
 303
 304badframe:
 305    unlock_user_struct(frame, frame_addr, 0);
 306    force_sig(TARGET_SIGSEGV);
 307    return -TARGET_QEMU_ESIGRETURN;
 308}
 309
 310long do_rt_sigreturn(CPUSH4State *regs)
 311{
 312    struct target_rt_sigframe *frame;
 313    abi_ulong frame_addr;
 314    sigset_t blocked;
 315
 316    frame_addr = regs->gregs[15];
 317    trace_user_do_rt_sigreturn(regs, frame_addr);
 318    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
 319        goto badframe;
 320    }
 321
 322    target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask);
 323    set_sigmask(&blocked);
 324
 325    restore_sigcontext(regs, &frame->uc.tuc_mcontext);
 326    target_restore_altstack(&frame->uc.tuc_stack, regs);
 327
 328    unlock_user_struct(frame, frame_addr, 0);
 329    return -TARGET_QEMU_ESIGRETURN;
 330
 331badframe:
 332    unlock_user_struct(frame, frame_addr, 0);
 333    force_sig(TARGET_SIGSEGV);
 334    return -TARGET_QEMU_ESIGRETURN;
 335}
 336