xref: /openbmc/qemu/linux-user/riscv/signal.c (revision ba49d760eb04630e7b15f423ebecf6c871b8f77b)
1befb7447SLaurent Vivier /*
2befb7447SLaurent Vivier  *  Emulation of Linux signals
3befb7447SLaurent Vivier  *
4befb7447SLaurent Vivier  *  Copyright (c) 2003 Fabrice Bellard
5befb7447SLaurent Vivier  *
6befb7447SLaurent Vivier  *  This program is free software; you can redistribute it and/or modify
7befb7447SLaurent Vivier  *  it under the terms of the GNU General Public License as published by
8befb7447SLaurent Vivier  *  the Free Software Foundation; either version 2 of the License, or
9befb7447SLaurent Vivier  *  (at your option) any later version.
10befb7447SLaurent Vivier  *
11befb7447SLaurent Vivier  *  This program is distributed in the hope that it will be useful,
12befb7447SLaurent Vivier  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13befb7447SLaurent Vivier  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14befb7447SLaurent Vivier  *  GNU General Public License for more details.
15befb7447SLaurent Vivier  *
16befb7447SLaurent Vivier  *  You should have received a copy of the GNU General Public License
17befb7447SLaurent Vivier  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18befb7447SLaurent Vivier  */
199c3221c1SLaurent Vivier #include "qemu/osdep.h"
209c3221c1SLaurent Vivier #include "qemu.h"
213b249d26SPeter Maydell #include "user-internals.h"
229c3221c1SLaurent Vivier #include "signal-common.h"
239c3221c1SLaurent Vivier #include "linux-user/trace.h"
24468c1bb5SRichard Henderson #include "vdso-asmoffset.h"
259c3221c1SLaurent Vivier 
269c3221c1SLaurent Vivier /* Signal handler invocation must be transparent for the code being
279c3221c1SLaurent Vivier    interrupted. Complete CPU (hart) state is saved on entry and restored
289c3221c1SLaurent Vivier    before returning from the handler. Process sigmask is also saved to block
299c3221c1SLaurent Vivier    signals while the handler is running. The handler gets its own stack,
309c3221c1SLaurent Vivier    which also doubles as storage for the CPU state and sigmask.
319c3221c1SLaurent Vivier 
329c3221c1SLaurent Vivier    The code below is qemu re-implementation of arch/riscv/kernel/signal.c */
339c3221c1SLaurent Vivier 
349c3221c1SLaurent Vivier struct target_sigcontext {
359c3221c1SLaurent Vivier     abi_long pc;
369c3221c1SLaurent Vivier     abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */
379c3221c1SLaurent Vivier     uint64_t fpr[32];
389c3221c1SLaurent Vivier     uint32_t fcsr;
399c3221c1SLaurent Vivier }; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */
409c3221c1SLaurent Vivier 
41468c1bb5SRichard Henderson QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, fpr) != offsetof_freg0);
42468c1bb5SRichard Henderson 
439c3221c1SLaurent Vivier struct target_ucontext {
44ae7d4d62SLIU Zhiwei     abi_ulong uc_flags;
45ae7d4d62SLIU Zhiwei     abi_ptr uc_link;
469c3221c1SLaurent Vivier     target_stack_t uc_stack;
479c3221c1SLaurent Vivier     target_sigset_t uc_sigmask;
4864ce00a6SLIU Zhiwei     uint8_t   __unused[1024 / 8 - sizeof(target_sigset_t)];
4964ce00a6SLIU Zhiwei     struct target_sigcontext uc_mcontext QEMU_ALIGNED(16);
509c3221c1SLaurent Vivier };
519c3221c1SLaurent Vivier 
529c3221c1SLaurent Vivier struct target_rt_sigframe {
539c3221c1SLaurent Vivier     struct target_siginfo info;
549c3221c1SLaurent Vivier     struct target_ucontext uc;
559c3221c1SLaurent Vivier };
569c3221c1SLaurent Vivier 
57468c1bb5SRichard Henderson QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe)
58468c1bb5SRichard Henderson                   != sizeof_rt_sigframe);
59468c1bb5SRichard Henderson QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.uc_mcontext)
60468c1bb5SRichard Henderson                   != offsetof_uc_mcontext);
61468c1bb5SRichard Henderson 
get_sigframe(struct target_sigaction * ka,CPURISCVState * regs,size_t framesize)629c3221c1SLaurent Vivier static abi_ulong get_sigframe(struct target_sigaction *ka,
639c3221c1SLaurent Vivier                               CPURISCVState *regs, size_t framesize)
649c3221c1SLaurent Vivier {
65465e237bSLaurent Vivier     abi_ulong sp = get_sp_from_cpustate(regs);
669c3221c1SLaurent Vivier 
679c3221c1SLaurent Vivier     /* If we are on the alternate signal stack and would overflow it, don't.
689c3221c1SLaurent Vivier        Return an always-bogus address instead so we will die with SIGSEGV. */
69465e237bSLaurent Vivier     if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) {
709c3221c1SLaurent Vivier         return -1L;
719c3221c1SLaurent Vivier     }
729c3221c1SLaurent Vivier 
73465e237bSLaurent Vivier     /* This is the X/Open sanctioned signal stack switching.  */
74465e237bSLaurent Vivier     sp = target_sigsp(sp, ka) - framesize;
751eaa6342SRichard Henderson     sp &= ~0xf;
76465e237bSLaurent Vivier 
779c3221c1SLaurent Vivier     return sp;
789c3221c1SLaurent Vivier }
799c3221c1SLaurent Vivier 
setup_sigcontext(struct target_sigcontext * sc,CPURISCVState * env)809c3221c1SLaurent Vivier static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
819c3221c1SLaurent Vivier {
829c3221c1SLaurent Vivier     int i;
839c3221c1SLaurent Vivier 
849c3221c1SLaurent Vivier     __put_user(env->pc, &sc->pc);
859c3221c1SLaurent Vivier 
869c3221c1SLaurent Vivier     for (i = 1; i < 32; i++) {
879c3221c1SLaurent Vivier         __put_user(env->gpr[i], &sc->gpr[i - 1]);
889c3221c1SLaurent Vivier     }
899c3221c1SLaurent Vivier     for (i = 0; i < 32; i++) {
909c3221c1SLaurent Vivier         __put_user(env->fpr[i], &sc->fpr[i]);
919c3221c1SLaurent Vivier     }
929c3221c1SLaurent Vivier 
93fb738839SMichael Clark     uint32_t fcsr = riscv_csr_read(env, CSR_FCSR);
949c3221c1SLaurent Vivier     __put_user(fcsr, &sc->fcsr);
959c3221c1SLaurent Vivier }
969c3221c1SLaurent Vivier 
setup_ucontext(struct target_ucontext * uc,CPURISCVState * env,target_sigset_t * set)979c3221c1SLaurent Vivier static void setup_ucontext(struct target_ucontext *uc,
989c3221c1SLaurent Vivier                            CPURISCVState *env, target_sigset_t *set)
999c3221c1SLaurent Vivier {
1009c3221c1SLaurent Vivier     __put_user(0,    &(uc->uc_flags));
1019c3221c1SLaurent Vivier     __put_user(0,    &(uc->uc_link));
1029c3221c1SLaurent Vivier 
103465e237bSLaurent Vivier     target_save_altstack(&uc->uc_stack, env);
1049c3221c1SLaurent Vivier 
1059c3221c1SLaurent Vivier     int i;
1069c3221c1SLaurent Vivier     for (i = 0; i < TARGET_NSIG_WORDS; i++) {
1079c3221c1SLaurent Vivier         __put_user(set->sig[i], &(uc->uc_sigmask.sig[i]));
1089c3221c1SLaurent Vivier     }
1099c3221c1SLaurent Vivier 
1109c3221c1SLaurent Vivier     setup_sigcontext(&uc->uc_mcontext, env);
1119c3221c1SLaurent Vivier }
1129c3221c1SLaurent Vivier 
setup_rt_frame(int sig,struct target_sigaction * ka,target_siginfo_t * info,target_sigset_t * set,CPURISCVState * env)1139c3221c1SLaurent Vivier void setup_rt_frame(int sig, struct target_sigaction *ka,
1149c3221c1SLaurent Vivier                     target_siginfo_t *info,
1159c3221c1SLaurent Vivier                     target_sigset_t *set, CPURISCVState *env)
1169c3221c1SLaurent Vivier {
1179c3221c1SLaurent Vivier     abi_ulong frame_addr;
1189c3221c1SLaurent Vivier     struct target_rt_sigframe *frame;
1199c3221c1SLaurent Vivier 
1209c3221c1SLaurent Vivier     frame_addr = get_sigframe(ka, env, sizeof(*frame));
1219c3221c1SLaurent Vivier     trace_user_setup_rt_frame(env, frame_addr);
1229c3221c1SLaurent Vivier 
1239c3221c1SLaurent Vivier     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
1249c3221c1SLaurent Vivier         goto badframe;
1259c3221c1SLaurent Vivier     }
1269c3221c1SLaurent Vivier 
1279c3221c1SLaurent Vivier     setup_ucontext(&frame->uc, env, set);
128*4d6d8a05SGustavo Romero     frame->info = *info;
1299c3221c1SLaurent Vivier 
1309c3221c1SLaurent Vivier     env->pc = ka->_sa_handler;
1319c3221c1SLaurent Vivier     env->gpr[xSP] = frame_addr;
1329c3221c1SLaurent Vivier     env->gpr[xA0] = sig;
1339c3221c1SLaurent Vivier     env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info);
1349c3221c1SLaurent Vivier     env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
1353c62b5d2SRichard Henderson     env->gpr[xRA] = default_rt_sigreturn;
1369c3221c1SLaurent Vivier 
1379c3221c1SLaurent Vivier     return;
1389c3221c1SLaurent Vivier 
1399c3221c1SLaurent Vivier badframe:
1409c3221c1SLaurent Vivier     unlock_user_struct(frame, frame_addr, 1);
1419c3221c1SLaurent Vivier     if (sig == TARGET_SIGSEGV) {
1429c3221c1SLaurent Vivier         ka->_sa_handler = TARGET_SIG_DFL;
1439c3221c1SLaurent Vivier     }
1449c3221c1SLaurent Vivier     force_sig(TARGET_SIGSEGV);
1459c3221c1SLaurent Vivier }
1469c3221c1SLaurent Vivier 
restore_sigcontext(CPURISCVState * env,struct target_sigcontext * sc)1479c3221c1SLaurent Vivier static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
1489c3221c1SLaurent Vivier {
1499c3221c1SLaurent Vivier     int i;
1509c3221c1SLaurent Vivier 
1519c3221c1SLaurent Vivier     __get_user(env->pc, &sc->pc);
1529c3221c1SLaurent Vivier 
1539c3221c1SLaurent Vivier     for (i = 1; i < 32; ++i) {
1549c3221c1SLaurent Vivier         __get_user(env->gpr[i], &sc->gpr[i - 1]);
1559c3221c1SLaurent Vivier     }
1569c3221c1SLaurent Vivier     for (i = 0; i < 32; ++i) {
1579c3221c1SLaurent Vivier         __get_user(env->fpr[i], &sc->fpr[i]);
1589c3221c1SLaurent Vivier     }
1599c3221c1SLaurent Vivier 
1609c3221c1SLaurent Vivier     uint32_t fcsr;
1619c3221c1SLaurent Vivier     __get_user(fcsr, &sc->fcsr);
162fb738839SMichael Clark     riscv_csr_write(env, CSR_FCSR, fcsr);
1639c3221c1SLaurent Vivier }
1649c3221c1SLaurent Vivier 
restore_ucontext(CPURISCVState * env,struct target_ucontext * uc)1659c3221c1SLaurent Vivier static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
1669c3221c1SLaurent Vivier {
1679c3221c1SLaurent Vivier     sigset_t blocked;
1689c3221c1SLaurent Vivier     target_sigset_t target_set;
1699c3221c1SLaurent Vivier     int i;
1709c3221c1SLaurent Vivier 
1719c3221c1SLaurent Vivier     target_sigemptyset(&target_set);
1729c3221c1SLaurent Vivier     for (i = 0; i < TARGET_NSIG_WORDS; i++) {
1739c3221c1SLaurent Vivier         __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i]));
1749c3221c1SLaurent Vivier     }
1759c3221c1SLaurent Vivier 
1769c3221c1SLaurent Vivier     target_to_host_sigset_internal(&blocked, &target_set);
1779c3221c1SLaurent Vivier     set_sigmask(&blocked);
1789c3221c1SLaurent Vivier 
1799c3221c1SLaurent Vivier     restore_sigcontext(env, &uc->uc_mcontext);
1809c3221c1SLaurent Vivier }
1819c3221c1SLaurent Vivier 
do_rt_sigreturn(CPURISCVState * env)1829c3221c1SLaurent Vivier long do_rt_sigreturn(CPURISCVState *env)
1839c3221c1SLaurent Vivier {
1849c3221c1SLaurent Vivier     struct target_rt_sigframe *frame;
1859c3221c1SLaurent Vivier     abi_ulong frame_addr;
1869c3221c1SLaurent Vivier 
1879c3221c1SLaurent Vivier     frame_addr = env->gpr[xSP];
1889c3221c1SLaurent Vivier     trace_user_do_sigreturn(env, frame_addr);
1899c3221c1SLaurent Vivier     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
1909c3221c1SLaurent Vivier         goto badframe;
1919c3221c1SLaurent Vivier     }
1929c3221c1SLaurent Vivier 
1939c3221c1SLaurent Vivier     restore_ucontext(env, &frame->uc);
194ddc3e74dSRichard Henderson     target_restore_altstack(&frame->uc.uc_stack, env);
1959c3221c1SLaurent Vivier 
1969c3221c1SLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
19757a0c938SRichard Henderson     return -QEMU_ESIGRETURN;
1989c3221c1SLaurent Vivier 
1999c3221c1SLaurent Vivier badframe:
2009c3221c1SLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
2019c3221c1SLaurent Vivier     force_sig(TARGET_SIGSEGV);
2029c3221c1SLaurent Vivier     return 0;
2039c3221c1SLaurent Vivier }
2043c62b5d2SRichard Henderson 
setup_sigtramp(abi_ulong sigtramp_page)2053c62b5d2SRichard Henderson void setup_sigtramp(abi_ulong sigtramp_page)
2063c62b5d2SRichard Henderson {
2073c62b5d2SRichard Henderson     uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
2083c62b5d2SRichard Henderson     assert(tramp != NULL);
2093c62b5d2SRichard Henderson 
2103c62b5d2SRichard Henderson     __put_user(0x08b00893, tramp + 0);  /* li a7, 139 = __NR_rt_sigreturn */
2113c62b5d2SRichard Henderson     __put_user(0x00000073, tramp + 1);  /* ecall */
2123c62b5d2SRichard Henderson 
2133c62b5d2SRichard Henderson     default_rt_sigreturn = sigtramp_page;
2143c62b5d2SRichard Henderson     unlock_user(tramp, sigtramp_page, 8);
2153c62b5d2SRichard Henderson }
216