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/* Notice when we're in the middle of a gUSA region and reset.
  86   Note that this will only occur for !parallel_cpus, as we will
  87   translate such sequences differently in a parallel context.  */
  88static void unwind_gusa(CPUSH4State *regs)
  89{
  90    /* If the stack pointer is sufficiently negative, and we haven't
  91       completed the sequence, then reset to the entry to the region.  */
  92    /* ??? The SH4 kernel checks for and address above 0xC0000000.
  93       However, the page mappings in qemu linux-user aren't as restricted
  94       and we wind up with the normal stack mapped above 0xF0000000.
  95       That said, there is no reason why the kernel should be allowing
  96       a gUSA region that spans 1GB.  Use a tighter check here, for what
  97       can actually be enabled by the immediate move.  */
  98    if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) {
  99        /* Reset the PC to before the gUSA region, as computed from
 100           R0 = region end, SP = -(region size), plus one more for the
 101           insn that actually initializes SP to the region size.  */
 102        regs->pc = regs->gregs[0] + regs->gregs[15] - 2;
 103
 104        /* Reset the SP to the saved version in R1.  */
 105        regs->gregs[15] = regs->gregs[1];
 106    }
 107}
 108
 109static void setup_sigcontext(struct target_sigcontext *sc,
 110                             CPUSH4State *regs, unsigned long mask)
 111{
 112    int i;
 113
 114#define COPY(x)         __put_user(regs->x, &sc->sc_##x)
 115    COPY(gregs[0]); COPY(gregs[1]);
 116    COPY(gregs[2]); COPY(gregs[3]);
 117    COPY(gregs[4]); COPY(gregs[5]);
 118    COPY(gregs[6]); COPY(gregs[7]);
 119    COPY(gregs[8]); COPY(gregs[9]);
 120    COPY(gregs[10]); COPY(gregs[11]);
 121    COPY(gregs[12]); COPY(gregs[13]);
 122    COPY(gregs[14]); COPY(gregs[15]);
 123    COPY(gbr); COPY(mach);
 124    COPY(macl); COPY(pr);
 125    COPY(sr); COPY(pc);
 126#undef COPY
 127
 128    for (i=0; i<16; i++) {
 129        __put_user(regs->fregs[i], &sc->sc_fpregs[i]);
 130    }
 131    __put_user(regs->fpscr, &sc->sc_fpscr);
 132    __put_user(regs->fpul, &sc->sc_fpul);
 133
 134    /* non-iBCS2 extensions.. */
 135    __put_user(mask, &sc->oldmask);
 136}
 137
 138static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc)
 139{
 140    int i;
 141
 142#define COPY(x)         __get_user(regs->x, &sc->sc_##x)
 143    COPY(gregs[0]); COPY(gregs[1]);
 144    COPY(gregs[2]); COPY(gregs[3]);
 145    COPY(gregs[4]); COPY(gregs[5]);
 146    COPY(gregs[6]); COPY(gregs[7]);
 147    COPY(gregs[8]); COPY(gregs[9]);
 148    COPY(gregs[10]); COPY(gregs[11]);
 149    COPY(gregs[12]); COPY(gregs[13]);
 150    COPY(gregs[14]); COPY(gregs[15]);
 151    COPY(gbr); COPY(mach);
 152    COPY(macl); COPY(pr);
 153    COPY(sr); COPY(pc);
 154#undef COPY
 155
 156    for (i=0; i<16; i++) {
 157        __get_user(regs->fregs[i], &sc->sc_fpregs[i]);
 158    }
 159    __get_user(regs->fpscr, &sc->sc_fpscr);
 160    __get_user(regs->fpul, &sc->sc_fpul);
 161
 162    regs->tra = -1;         /* disable syscall checks */
 163    regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
 164}
 165
 166void setup_frame(int sig, struct target_sigaction *ka,
 167                 target_sigset_t *set, CPUSH4State *regs)
 168{
 169    struct target_sigframe *frame;
 170    abi_ulong frame_addr;
 171    int i;
 172
 173    unwind_gusa(regs);
 174
 175    frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
 176    trace_user_setup_frame(regs, frame_addr);
 177    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
 178        goto give_sigsegv;
 179    }
 180
 181    setup_sigcontext(&frame->sc, regs, set->sig[0]);
 182
 183    for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) {
 184        __put_user(set->sig[i + 1], &frame->extramask[i]);
 185    }
 186
 187    /* Set up to return from userspace.  If provided, use a stub
 188       already in userspace.  */
 189    if (ka->sa_flags & TARGET_SA_RESTORER) {
 190        regs->pr = (unsigned long) ka->sa_restorer;
 191    } else {
 192        /* Generate return code (system call to sigreturn) */
 193        abi_ulong retcode_addr = frame_addr +
 194                                 offsetof(struct target_sigframe, retcode);
 195        __put_user(MOVW(2), &frame->retcode[0]);
 196        __put_user(TRAP_NOARG, &frame->retcode[1]);
 197        __put_user((TARGET_NR_sigreturn), &frame->retcode[2]);
 198        regs->pr = (unsigned long) retcode_addr;
 199    }
 200
 201    /* Set up registers for signal handler */
 202    regs->gregs[15] = frame_addr;
 203    regs->gregs[4] = sig; /* Arg for signal handler */
 204    regs->gregs[5] = 0;
 205    regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc);
 206    regs->pc = (unsigned long) ka->_sa_handler;
 207    regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
 208
 209    unlock_user_struct(frame, frame_addr, 1);
 210    return;
 211
 212give_sigsegv:
 213    unlock_user_struct(frame, frame_addr, 1);
 214    force_sigsegv(sig);
 215}
 216
 217void setup_rt_frame(int sig, struct target_sigaction *ka,
 218                    target_siginfo_t *info,
 219                    target_sigset_t *set, CPUSH4State *regs)
 220{
 221    struct target_rt_sigframe *frame;
 222    abi_ulong frame_addr;
 223    int i;
 224
 225    unwind_gusa(regs);
 226
 227    frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
 228    trace_user_setup_rt_frame(regs, frame_addr);
 229    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
 230        goto give_sigsegv;
 231    }
 232
 233    tswap_siginfo(&frame->info, info);
 234
 235    /* Create the ucontext.  */
 236    __put_user(0, &frame->uc.tuc_flags);
 237    __put_user(0, (unsigned long *)&frame->uc.tuc_link);
 238    target_save_altstack(&frame->uc.tuc_stack, regs);
 239    setup_sigcontext(&frame->uc.tuc_mcontext,
 240                     regs, set->sig[0]);
 241    for(i = 0; i < TARGET_NSIG_WORDS; i++) {
 242        __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
 243    }
 244
 245    /* Set up to return from userspace.  If provided, use a stub
 246       already in userspace.  */
 247    if (ka->sa_flags & TARGET_SA_RESTORER) {
 248        regs->pr = (unsigned long) ka->sa_restorer;
 249    } else {
 250        /* Generate return code (system call to sigreturn) */
 251        abi_ulong retcode_addr = frame_addr +
 252                                 offsetof(struct target_rt_sigframe, retcode);
 253        __put_user(MOVW(2), &frame->retcode[0]);
 254        __put_user(TRAP_NOARG, &frame->retcode[1]);
 255        __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]);
 256        regs->pr = (unsigned long) retcode_addr;
 257    }
 258
 259    /* Set up registers for signal handler */
 260    regs->gregs[15] = frame_addr;
 261    regs->gregs[4] = sig; /* Arg for signal handler */
 262    regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info);
 263    regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc);
 264    regs->pc = (unsigned long) ka->_sa_handler;
 265    regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK);
 266
 267    unlock_user_struct(frame, frame_addr, 1);
 268    return;
 269
 270give_sigsegv:
 271    unlock_user_struct(frame, frame_addr, 1);
 272    force_sigsegv(sig);
 273}
 274
 275long do_sigreturn(CPUSH4State *regs)
 276{
 277    struct target_sigframe *frame;
 278    abi_ulong frame_addr;
 279    sigset_t blocked;
 280    target_sigset_t target_set;
 281    int i;
 282
 283    frame_addr = regs->gregs[15];
 284    trace_user_do_sigreturn(regs, frame_addr);
 285    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
 286        goto badframe;
 287    }
 288
 289    __get_user(target_set.sig[0], &frame->sc.oldmask);
 290    for(i = 1; i < TARGET_NSIG_WORDS; i++) {
 291        __get_user(target_set.sig[i], &frame->extramask[i - 1]);
 292    }
 293
 294    target_to_host_sigset_internal(&blocked, &target_set);
 295    set_sigmask(&blocked);
 296
 297    restore_sigcontext(regs, &frame->sc);
 298
 299    unlock_user_struct(frame, frame_addr, 0);
 300    return -TARGET_QEMU_ESIGRETURN;
 301
 302badframe:
 303    unlock_user_struct(frame, frame_addr, 0);
 304    force_sig(TARGET_SIGSEGV);
 305    return -TARGET_QEMU_ESIGRETURN;
 306}
 307
 308long do_rt_sigreturn(CPUSH4State *regs)
 309{
 310    struct target_rt_sigframe *frame;
 311    abi_ulong frame_addr;
 312    sigset_t blocked;
 313
 314    frame_addr = regs->gregs[15];
 315    trace_user_do_rt_sigreturn(regs, frame_addr);
 316    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
 317        goto badframe;
 318    }
 319
 320    target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask);
 321    set_sigmask(&blocked);
 322
 323    restore_sigcontext(regs, &frame->uc.tuc_mcontext);
 324
 325    if (do_sigaltstack(frame_addr +
 326                       offsetof(struct target_rt_sigframe, uc.tuc_stack),
 327                       0, get_sp_from_cpustate(regs)) == -EFAULT) {
 328        goto badframe;
 329    }
 330
 331    unlock_user_struct(frame, frame_addr, 0);
 332    return -TARGET_QEMU_ESIGRETURN;
 333
 334badframe:
 335    unlock_user_struct(frame, frame_addr, 0);
 336    force_sig(TARGET_SIGSEGV);
 337    return -TARGET_QEMU_ESIGRETURN;
 338}
 339