qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

signal.c (8411B)


      1 /* SPDX-License-Identifier: GPL-2.0-or-later */
      2 /*
      3  * LoongArch emulation of Linux signals
      4  *
      5  * Copyright (c) 2021 Loongson Technology Corporation Limited
      6  */
      7 
      8 #include "qemu/osdep.h"
      9 #include "qemu.h"
     10 #include "user-internals.h"
     11 #include "signal-common.h"
     12 #include "linux-user/trace.h"
     13 
     14 #include "target/loongarch/internals.h"
     15 
     16 /* FP context was used */
     17 #define SC_USED_FP              (1 << 0)
     18 
     19 struct target_sigcontext {
     20     uint64_t sc_pc;
     21     uint64_t sc_regs[32];
     22     uint32_t sc_flags;
     23     uint64_t sc_extcontext[0]   QEMU_ALIGNED(16);
     24 };
     25 
     26 
     27 #define FPU_CTX_MAGIC           0x46505501
     28 #define FPU_CTX_ALIGN           8
     29 struct target_fpu_context {
     30     uint64_t regs[32];
     31     uint64_t fcc;
     32     uint32_t fcsr;
     33 } QEMU_ALIGNED(FPU_CTX_ALIGN);
     34 
     35 #define CONTEXT_INFO_ALIGN      16
     36 struct target_sctx_info {
     37     uint32_t magic;
     38     uint32_t size;
     39     uint64_t padding;
     40 } QEMU_ALIGNED(CONTEXT_INFO_ALIGN);
     41 
     42 struct target_ucontext {
     43     abi_ulong tuc_flags;
     44     abi_ptr tuc_link;
     45     target_stack_t tuc_stack;
     46     target_sigset_t tuc_sigmask;
     47     uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)];
     48     struct target_sigcontext tuc_mcontext;
     49 };
     50 
     51 struct target_rt_sigframe {
     52     struct target_siginfo        rs_info;
     53     struct target_ucontext       rs_uc;
     54 };
     55 
     56 /*
     57  * These two structures are not present in guest memory, are private
     58  * to the signal implementation, but are largely copied from the
     59  * kernel's signal implementation.
     60  */
     61 struct ctx_layout {
     62     void *haddr;
     63     abi_ptr gaddr;
     64     unsigned int size;
     65 };
     66 
     67 struct extctx_layout {
     68     unsigned int size;
     69     unsigned int flags;
     70     struct ctx_layout fpu;
     71     struct ctx_layout end;
     72 };
     73 
     74 static abi_ptr extframe_alloc(struct extctx_layout *extctx,
     75                               struct ctx_layout *sctx, unsigned size,
     76                               unsigned align, abi_ptr orig_sp)
     77 {
     78     abi_ptr sp = orig_sp;
     79 
     80     sp -= sizeof(struct target_sctx_info) + size;
     81     align = MAX(align, CONTEXT_INFO_ALIGN);
     82     sp = ROUND_DOWN(sp, align);
     83     sctx->gaddr = sp;
     84 
     85     size = orig_sp - sp;
     86     sctx->size = size;
     87     extctx->size += size;
     88 
     89     return sp;
     90 }
     91 
     92 static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp)
     93 {
     94     memset(extctx, 0, sizeof(struct extctx_layout));
     95 
     96     /* Grow down, alloc "end" context info first. */
     97     sp = extframe_alloc(extctx, &extctx->end, 0, CONTEXT_INFO_ALIGN, sp);
     98 
     99     /* For qemu, there is no lazy fp context switch, so fp always present. */
    100     extctx->flags = SC_USED_FP;
    101     sp = extframe_alloc(extctx, &extctx->fpu,
    102                         sizeof(struct target_rt_sigframe), FPU_CTX_ALIGN, sp);
    103 
    104     return sp;
    105 }
    106 
    107 static void setup_sigframe(CPULoongArchState *env,
    108                            struct target_sigcontext *sc,
    109                            struct extctx_layout *extctx)
    110 {
    111     struct target_sctx_info *info;
    112     struct target_fpu_context *fpu_ctx;
    113     int i;
    114 
    115     __put_user(extctx->flags, &sc->sc_flags);
    116     __put_user(env->pc, &sc->sc_pc);
    117     __put_user(0, &sc->sc_regs[0]);
    118     for (i = 1; i < 32; ++i) {
    119         __put_user(env->gpr[i], &sc->sc_regs[i]);
    120     }
    121 
    122     /*
    123      * Set fpu context
    124      */
    125     info = extctx->fpu.haddr;
    126     __put_user(FPU_CTX_MAGIC, &info->magic);
    127     __put_user(extctx->fpu.size, &info->size);
    128 
    129     fpu_ctx = (struct target_fpu_context *)(info + 1);
    130     for (i = 0; i < 32; ++i) {
    131         __put_user(env->fpr[i], &fpu_ctx->regs[i]);
    132     }
    133     __put_user(read_fcc(env), &fpu_ctx->fcc);
    134     __put_user(env->fcsr0, &fpu_ctx->fcsr);
    135 
    136     /*
    137      * Set end context
    138      */
    139     info = extctx->end.haddr;
    140     __put_user(0, &info->magic);
    141     __put_user(extctx->end.size, &info->size);
    142 }
    143 
    144 static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame)
    145 {
    146     memset(extctx, 0, sizeof(*extctx));
    147 
    148     while (1) {
    149         uint32_t magic, size;
    150 
    151         if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) {
    152             return false;
    153         }
    154 
    155         switch (magic) {
    156         case 0: /* END */
    157             extctx->end.gaddr = frame;
    158             extctx->end.size = size;
    159             extctx->size += size;
    160             return true;
    161 
    162         case FPU_CTX_MAGIC:
    163             if (size < (sizeof(struct target_sctx_info) +
    164                         sizeof(struct target_fpu_context))) {
    165                 return false;
    166             }
    167             extctx->fpu.gaddr = frame;
    168             extctx->fpu.size = size;
    169             extctx->size += size;
    170             break;
    171         default:
    172             return false;
    173         }
    174 
    175         frame += size;
    176     }
    177 }
    178 
    179 static void restore_sigframe(CPULoongArchState *env,
    180                              struct target_sigcontext *sc,
    181                              struct extctx_layout *extctx)
    182 {
    183     int i;
    184 
    185     __get_user(env->pc, &sc->sc_pc);
    186     for (i = 1; i < 32; ++i) {
    187         __get_user(env->gpr[i], &sc->sc_regs[i]);
    188     }
    189 
    190     if (extctx->fpu.haddr) {
    191         struct target_fpu_context *fpu_ctx =
    192             extctx->fpu.haddr + sizeof(struct target_sctx_info);
    193         uint64_t fcc;
    194 
    195         for (i = 0; i < 32; ++i) {
    196             __get_user(env->fpr[i], &fpu_ctx->regs[i]);
    197         }
    198         __get_user(fcc, &fpu_ctx->fcc);
    199         write_fcc(env, fcc);
    200         __get_user(env->fcsr0, &fpu_ctx->fcsr);
    201         restore_fp_status(env);
    202     }
    203 }
    204 
    205 /*
    206  * Determine which stack to use.
    207  */
    208 static abi_ptr get_sigframe(struct target_sigaction *ka,
    209                             CPULoongArchState *env,
    210                             struct extctx_layout *extctx)
    211 {
    212     abi_ulong sp;
    213 
    214     sp = target_sigsp(get_sp_from_cpustate(env), ka);
    215     sp = ROUND_DOWN(sp, 16);
    216     sp = setup_extcontext(extctx, sp);
    217     sp -= sizeof(struct target_rt_sigframe);
    218 
    219     assert(QEMU_IS_ALIGNED(sp, 16));
    220 
    221     return sp;
    222 }
    223 
    224 void setup_rt_frame(int sig, struct target_sigaction *ka,
    225                     target_siginfo_t *info,
    226                     target_sigset_t *set, CPULoongArchState *env)
    227 {
    228     struct target_rt_sigframe *frame;
    229     struct extctx_layout extctx;
    230     abi_ptr frame_addr;
    231     int i;
    232 
    233     frame_addr = get_sigframe(ka, env, &extctx);
    234     trace_user_setup_rt_frame(env, frame_addr);
    235 
    236     frame = lock_user(VERIFY_WRITE, frame_addr,
    237                       sizeof(*frame) + extctx.size, 0);
    238     if (!frame) {
    239         force_sigsegv(sig);
    240         return;
    241     }
    242     extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
    243     extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
    244 
    245     tswap_siginfo(&frame->rs_info, info);
    246 
    247     __put_user(0, &frame->rs_uc.tuc_flags);
    248     __put_user(0, &frame->rs_uc.tuc_link);
    249     target_save_altstack(&frame->rs_uc.tuc_stack, env);
    250 
    251     setup_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
    252 
    253     for (i = 0; i < TARGET_NSIG_WORDS; i++) {
    254         __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
    255     }
    256 
    257     env->gpr[4] = sig;
    258     env->gpr[5] = frame_addr + offsetof(struct target_rt_sigframe, rs_info);
    259     env->gpr[6] = frame_addr + offsetof(struct target_rt_sigframe, rs_uc);
    260     env->gpr[3] = frame_addr;
    261     env->gpr[1] = default_rt_sigreturn;
    262 
    263     env->pc = ka->_sa_handler;
    264     unlock_user(frame, frame_addr, sizeof(*frame) + extctx.size);
    265 }
    266 
    267 long do_rt_sigreturn(CPULoongArchState *env)
    268 {
    269     struct target_rt_sigframe *frame;
    270     struct extctx_layout extctx;
    271     abi_ulong frame_addr;
    272     sigset_t blocked;
    273 
    274     frame_addr = env->gpr[3];
    275     trace_user_do_rt_sigreturn(env, frame_addr);
    276 
    277     if (!parse_extcontext(&extctx, frame_addr + sizeof(*frame))) {
    278         goto badframe;
    279     }
    280 
    281     frame = lock_user(VERIFY_READ, frame_addr,
    282                       sizeof(*frame) + extctx.size, 1);
    283     if (!frame) {
    284         goto badframe;
    285     }
    286     if (extctx.fpu.gaddr) {
    287         extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
    288     }
    289 
    290     target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
    291     set_sigmask(&blocked);
    292 
    293     restore_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
    294 
    295     target_restore_altstack(&frame->rs_uc.tuc_stack, env);
    296 
    297     unlock_user(frame, frame_addr, 0);
    298     return -QEMU_ESIGRETURN;
    299 
    300  badframe:
    301     force_sig(TARGET_SIGSEGV);
    302     return -QEMU_ESIGRETURN;
    303 }
    304 
    305 void setup_sigtramp(abi_ulong sigtramp_page)
    306 {
    307     uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
    308     assert(tramp != NULL);
    309 
    310     __put_user(0x03822c0b, tramp + 0);  /* ori     a7, zero, 0x8b */
    311     __put_user(0x002b0000, tramp + 1);  /* syscall 0 */
    312 
    313     default_rt_sigreturn = sigtramp_page;
    314     unlock_user(tramp, sigtramp_page, 8);
    315 }