xref: /openbmc/qemu/linux-user/xtensa/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  */
193612667cSLaurent Vivier #include "qemu/osdep.h"
203612667cSLaurent Vivier #include "qemu.h"
213b249d26SPeter Maydell #include "user-internals.h"
223612667cSLaurent Vivier #include "signal-common.h"
233612667cSLaurent Vivier #include "linux-user/trace.h"
243612667cSLaurent Vivier 
253612667cSLaurent Vivier struct target_sigcontext {
263612667cSLaurent Vivier     abi_ulong sc_pc;
273612667cSLaurent Vivier     abi_ulong sc_ps;
283612667cSLaurent Vivier     abi_ulong sc_lbeg;
293612667cSLaurent Vivier     abi_ulong sc_lend;
303612667cSLaurent Vivier     abi_ulong sc_lcount;
313612667cSLaurent Vivier     abi_ulong sc_sar;
323612667cSLaurent Vivier     abi_ulong sc_acclo;
333612667cSLaurent Vivier     abi_ulong sc_acchi;
343612667cSLaurent Vivier     abi_ulong sc_a[16];
353612667cSLaurent Vivier     abi_ulong sc_xtregs;
363612667cSLaurent Vivier };
373612667cSLaurent Vivier 
383612667cSLaurent Vivier struct target_ucontext {
393612667cSLaurent Vivier     abi_ulong tuc_flags;
403612667cSLaurent Vivier     abi_ulong tuc_link;
413612667cSLaurent Vivier     target_stack_t tuc_stack;
423612667cSLaurent Vivier     struct target_sigcontext tuc_mcontext;
433612667cSLaurent Vivier     target_sigset_t tuc_sigmask;
443612667cSLaurent Vivier };
453612667cSLaurent Vivier 
463612667cSLaurent Vivier struct target_rt_sigframe {
473612667cSLaurent Vivier     target_siginfo_t info;
483612667cSLaurent Vivier     struct target_ucontext uc;
493612667cSLaurent Vivier     /* TODO: xtregs */
503612667cSLaurent Vivier     uint8_t retcode[6];
513612667cSLaurent Vivier     abi_ulong window[4];
523612667cSLaurent Vivier };
533612667cSLaurent Vivier 
get_sigframe(struct target_sigaction * sa,CPUXtensaState * env,unsigned long framesize)543612667cSLaurent Vivier static abi_ulong get_sigframe(struct target_sigaction *sa,
553612667cSLaurent Vivier                               CPUXtensaState *env,
563612667cSLaurent Vivier                               unsigned long framesize)
573612667cSLaurent Vivier {
58465e237bSLaurent Vivier     abi_ulong sp;
593612667cSLaurent Vivier 
60465e237bSLaurent Vivier     sp = target_sigsp(get_sp_from_cpustate(env), sa);
61465e237bSLaurent Vivier 
623612667cSLaurent Vivier     return (sp - framesize) & -16;
633612667cSLaurent Vivier }
643612667cSLaurent Vivier 
flush_window_regs(CPUXtensaState * env)653612667cSLaurent Vivier static int flush_window_regs(CPUXtensaState *env)
663612667cSLaurent Vivier {
673612667cSLaurent Vivier     uint32_t wb = env->sregs[WINDOW_BASE];
683612667cSLaurent Vivier     uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1);
693612667cSLaurent Vivier     unsigned d = ctz32(ws) + 1;
703612667cSLaurent Vivier     unsigned i;
713612667cSLaurent Vivier     int ret = 0;
723612667cSLaurent Vivier 
733612667cSLaurent Vivier     for (i = d; i < env->config->nareg / 4; i += d) {
743612667cSLaurent Vivier         uint32_t ssp, osp;
753612667cSLaurent Vivier         unsigned j;
763612667cSLaurent Vivier 
773612667cSLaurent Vivier         ws >>= d;
783612667cSLaurent Vivier         xtensa_rotate_window(env, d);
793612667cSLaurent Vivier 
803612667cSLaurent Vivier         if (ws & 0x1) {
813612667cSLaurent Vivier             ssp = env->regs[5];
823612667cSLaurent Vivier             d = 1;
833612667cSLaurent Vivier         } else if (ws & 0x2) {
843612667cSLaurent Vivier             ssp = env->regs[9];
853612667cSLaurent Vivier             ret |= get_user_ual(osp, env->regs[1] - 12);
863612667cSLaurent Vivier             osp -= 32;
873612667cSLaurent Vivier             d = 2;
883612667cSLaurent Vivier         } else if (ws & 0x4) {
893612667cSLaurent Vivier             ssp = env->regs[13];
903612667cSLaurent Vivier             ret |= get_user_ual(osp, env->regs[1] - 12);
913612667cSLaurent Vivier             osp -= 48;
923612667cSLaurent Vivier             d = 3;
933612667cSLaurent Vivier         } else {
943612667cSLaurent Vivier             g_assert_not_reached();
953612667cSLaurent Vivier         }
963612667cSLaurent Vivier 
973612667cSLaurent Vivier         for (j = 0; j < 4; ++j) {
983612667cSLaurent Vivier             ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4);
993612667cSLaurent Vivier         }
1003612667cSLaurent Vivier         for (j = 4; j < d * 4; ++j) {
1013612667cSLaurent Vivier             ret |= put_user_ual(env->regs[j], osp - 16 + j * 4);
1023612667cSLaurent Vivier         }
1033612667cSLaurent Vivier     }
1043612667cSLaurent Vivier     xtensa_rotate_window(env, d);
1053612667cSLaurent Vivier     g_assert(env->sregs[WINDOW_BASE] == wb);
1063612667cSLaurent Vivier     return ret == 0;
1073612667cSLaurent Vivier }
1083612667cSLaurent Vivier 
setup_sigcontext(struct target_rt_sigframe * frame,CPUXtensaState * env)1093612667cSLaurent Vivier static int setup_sigcontext(struct target_rt_sigframe *frame,
1103612667cSLaurent Vivier                             CPUXtensaState *env)
1113612667cSLaurent Vivier {
1123612667cSLaurent Vivier     struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
1133612667cSLaurent Vivier     int i;
1143612667cSLaurent Vivier 
1153612667cSLaurent Vivier     __put_user(env->pc, &sc->sc_pc);
1163612667cSLaurent Vivier     __put_user(env->sregs[PS], &sc->sc_ps);
1173612667cSLaurent Vivier     __put_user(env->sregs[LBEG], &sc->sc_lbeg);
1183612667cSLaurent Vivier     __put_user(env->sregs[LEND], &sc->sc_lend);
1193612667cSLaurent Vivier     __put_user(env->sregs[LCOUNT], &sc->sc_lcount);
1203612667cSLaurent Vivier     if (!flush_window_regs(env)) {
1213612667cSLaurent Vivier         return 0;
1223612667cSLaurent Vivier     }
1233612667cSLaurent Vivier     for (i = 0; i < 16; ++i) {
1243612667cSLaurent Vivier         __put_user(env->regs[i], sc->sc_a + i);
1253612667cSLaurent Vivier     }
1263612667cSLaurent Vivier     __put_user(0, &sc->sc_xtregs);
1273612667cSLaurent Vivier     /* TODO: xtregs */
1283612667cSLaurent Vivier     return 1;
1293612667cSLaurent Vivier }
1303612667cSLaurent Vivier 
install_sigtramp(uint8_t * tramp)13155e83c20SRichard Henderson static void install_sigtramp(uint8_t *tramp)
13255e83c20SRichard Henderson {
133ee3eb3a7SMarc-André Lureau #if TARGET_BIG_ENDIAN
13455e83c20SRichard Henderson     /* Generate instruction:  MOVI a2, __NR_rt_sigreturn */
13555e83c20SRichard Henderson     __put_user(0x22, &tramp[0]);
13655e83c20SRichard Henderson     __put_user(0x0a, &tramp[1]);
13755e83c20SRichard Henderson     __put_user(TARGET_NR_rt_sigreturn, &tramp[2]);
13855e83c20SRichard Henderson     /* Generate instruction:  SYSCALL */
13955e83c20SRichard Henderson     __put_user(0x00, &tramp[3]);
14055e83c20SRichard Henderson     __put_user(0x05, &tramp[4]);
14155e83c20SRichard Henderson     __put_user(0x00, &tramp[5]);
14255e83c20SRichard Henderson #else
14355e83c20SRichard Henderson     /* Generate instruction:  MOVI a2, __NR_rt_sigreturn */
14455e83c20SRichard Henderson     __put_user(0x22, &tramp[0]);
14555e83c20SRichard Henderson     __put_user(0xa0, &tramp[1]);
14655e83c20SRichard Henderson     __put_user(TARGET_NR_rt_sigreturn, &tramp[2]);
14755e83c20SRichard Henderson     /* Generate instruction:  SYSCALL */
14855e83c20SRichard Henderson     __put_user(0x00, &tramp[3]);
14955e83c20SRichard Henderson     __put_user(0x50, &tramp[4]);
15055e83c20SRichard Henderson     __put_user(0x00, &tramp[5]);
15155e83c20SRichard Henderson #endif
15255e83c20SRichard Henderson }
15355e83c20SRichard Henderson 
setup_rt_frame(int sig,struct target_sigaction * ka,target_siginfo_t * info,target_sigset_t * set,CPUXtensaState * env)1543612667cSLaurent Vivier void setup_rt_frame(int sig, struct target_sigaction *ka,
1553612667cSLaurent Vivier                     target_siginfo_t *info,
1563612667cSLaurent Vivier                     target_sigset_t *set, CPUXtensaState *env)
1573612667cSLaurent Vivier {
1583612667cSLaurent Vivier     abi_ulong frame_addr;
1593612667cSLaurent Vivier     struct target_rt_sigframe *frame;
160e4e5cb4aSIlya Leoshkevich     int is_fdpic = info_is_fdpic(get_task_state(thread_cpu)->info);
1611b173d06SMax Filippov     abi_ulong handler = 0;
1621b173d06SMax Filippov     abi_ulong handler_fdpic_GOT = 0;
1633612667cSLaurent Vivier     uint32_t ra;
164130ea832SMax Filippov     bool abi_call0;
165130ea832SMax Filippov     unsigned base;
1663612667cSLaurent Vivier     int i;
1673612667cSLaurent Vivier 
1683612667cSLaurent Vivier     frame_addr = get_sigframe(ka, env, sizeof(*frame));
1693612667cSLaurent Vivier     trace_user_setup_rt_frame(env, frame_addr);
1703612667cSLaurent Vivier 
1711b173d06SMax Filippov     if (is_fdpic) {
1721b173d06SMax Filippov         abi_ulong funcdesc_ptr = ka->_sa_handler;
1731b173d06SMax Filippov 
1741b173d06SMax Filippov         if (get_user_ual(handler, funcdesc_ptr)
1751b173d06SMax Filippov             || get_user_ual(handler_fdpic_GOT, funcdesc_ptr + 4)) {
1761b173d06SMax Filippov             goto give_sigsegv;
1771b173d06SMax Filippov         }
1781b173d06SMax Filippov     } else {
1791b173d06SMax Filippov         handler = ka->_sa_handler;
1801b173d06SMax Filippov     }
1811b173d06SMax Filippov 
1823612667cSLaurent Vivier     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
1833612667cSLaurent Vivier         goto give_sigsegv;
1843612667cSLaurent Vivier     }
1853612667cSLaurent Vivier 
1863612667cSLaurent Vivier     if (ka->sa_flags & SA_SIGINFO) {
187*4d6d8a05SGustavo Romero         frame->info = *info;
1883612667cSLaurent Vivier     }
1893612667cSLaurent Vivier 
1903612667cSLaurent Vivier     __put_user(0, &frame->uc.tuc_flags);
1913612667cSLaurent Vivier     __put_user(0, &frame->uc.tuc_link);
192465e237bSLaurent Vivier     target_save_altstack(&frame->uc.tuc_stack, env);
1933612667cSLaurent Vivier     if (!setup_sigcontext(frame, env)) {
1943612667cSLaurent Vivier         unlock_user_struct(frame, frame_addr, 0);
1953612667cSLaurent Vivier         goto give_sigsegv;
1963612667cSLaurent Vivier     }
1973612667cSLaurent Vivier     for (i = 0; i < TARGET_NSIG_WORDS; ++i) {
1983612667cSLaurent Vivier         __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
1993612667cSLaurent Vivier     }
2003612667cSLaurent Vivier 
2013612667cSLaurent Vivier     if (ka->sa_flags & TARGET_SA_RESTORER) {
2021b173d06SMax Filippov         if (is_fdpic) {
2031b173d06SMax Filippov             if (get_user_ual(ra, ka->sa_restorer)) {
2041b173d06SMax Filippov                 unlock_user_struct(frame, frame_addr, 0);
2051b173d06SMax Filippov                 goto give_sigsegv;
2061b173d06SMax Filippov             }
2071b173d06SMax Filippov         } else {
2083612667cSLaurent Vivier             ra = ka->sa_restorer;
2091b173d06SMax Filippov         }
2103612667cSLaurent Vivier     } else {
21155e83c20SRichard Henderson         /* Not used, but retain for ABI compatibility. */
21255e83c20SRichard Henderson         install_sigtramp(frame->retcode);
21355e83c20SRichard Henderson         ra = default_rt_sigreturn;
2143612667cSLaurent Vivier     }
2153612667cSLaurent Vivier     memset(env->regs, 0, sizeof(env->regs));
2161b173d06SMax Filippov     env->pc = handler;
2173612667cSLaurent Vivier     env->regs[1] = frame_addr;
2183612667cSLaurent Vivier     env->sregs[WINDOW_BASE] = 0;
2193612667cSLaurent Vivier     env->sregs[WINDOW_START] = 1;
2203612667cSLaurent Vivier 
221130ea832SMax Filippov     abi_call0 = (env->sregs[PS] & PS_WOE) == 0;
222130ea832SMax Filippov     env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT);
223130ea832SMax Filippov 
224130ea832SMax Filippov     if (abi_call0) {
225130ea832SMax Filippov         base = 0;
226130ea832SMax Filippov         env->regs[base] = ra;
227130ea832SMax Filippov     } else {
228130ea832SMax Filippov         env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT);
229130ea832SMax Filippov         base = 4;
230130ea832SMax Filippov         env->regs[base] = (ra & 0x3fffffff) | 0x40000000;
231130ea832SMax Filippov     }
232130ea832SMax Filippov     env->regs[base + 2] = sig;
233130ea832SMax Filippov     env->regs[base + 3] = frame_addr + offsetof(struct target_rt_sigframe,
234130ea832SMax Filippov                                                 info);
235130ea832SMax Filippov     env->regs[base + 4] = frame_addr + offsetof(struct target_rt_sigframe, uc);
2361b173d06SMax Filippov     if (is_fdpic) {
2371b173d06SMax Filippov         env->regs[base + 11] = handler_fdpic_GOT;
2381b173d06SMax Filippov     }
2393612667cSLaurent Vivier     unlock_user_struct(frame, frame_addr, 1);
2403612667cSLaurent Vivier     return;
2413612667cSLaurent Vivier 
2423612667cSLaurent Vivier give_sigsegv:
2433612667cSLaurent Vivier     force_sigsegv(sig);
2443612667cSLaurent Vivier     return;
2453612667cSLaurent Vivier }
2463612667cSLaurent Vivier 
restore_sigcontext(CPUXtensaState * env,struct target_rt_sigframe * frame)2473612667cSLaurent Vivier static void restore_sigcontext(CPUXtensaState *env,
2483612667cSLaurent Vivier                                struct target_rt_sigframe *frame)
2493612667cSLaurent Vivier {
2503612667cSLaurent Vivier     struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
2513612667cSLaurent Vivier     uint32_t ps;
2523612667cSLaurent Vivier     int i;
2533612667cSLaurent Vivier 
2543612667cSLaurent Vivier     __get_user(env->pc, &sc->sc_pc);
2553612667cSLaurent Vivier     __get_user(ps, &sc->sc_ps);
2563612667cSLaurent Vivier     __get_user(env->sregs[LBEG], &sc->sc_lbeg);
2573612667cSLaurent Vivier     __get_user(env->sregs[LEND], &sc->sc_lend);
2583612667cSLaurent Vivier     __get_user(env->sregs[LCOUNT], &sc->sc_lcount);
2593612667cSLaurent Vivier 
2603612667cSLaurent Vivier     env->sregs[WINDOW_BASE] = 0;
2613612667cSLaurent Vivier     env->sregs[WINDOW_START] = 1;
2623612667cSLaurent Vivier     env->sregs[PS] = deposit32(env->sregs[PS],
2633612667cSLaurent Vivier                                PS_CALLINC_SHIFT,
2643612667cSLaurent Vivier                                PS_CALLINC_LEN,
2653612667cSLaurent Vivier                                extract32(ps, PS_CALLINC_SHIFT,
2663612667cSLaurent Vivier                                          PS_CALLINC_LEN));
2673612667cSLaurent Vivier     for (i = 0; i < 16; ++i) {
2683612667cSLaurent Vivier         __get_user(env->regs[i], sc->sc_a + i);
2693612667cSLaurent Vivier     }
2703612667cSLaurent Vivier     /* TODO: xtregs */
2713612667cSLaurent Vivier }
2723612667cSLaurent Vivier 
do_rt_sigreturn(CPUXtensaState * env)2733612667cSLaurent Vivier long do_rt_sigreturn(CPUXtensaState *env)
2743612667cSLaurent Vivier {
2753612667cSLaurent Vivier     abi_ulong frame_addr = env->regs[1];
2763612667cSLaurent Vivier     struct target_rt_sigframe *frame;
2773612667cSLaurent Vivier     sigset_t set;
2783612667cSLaurent Vivier 
2793612667cSLaurent Vivier     trace_user_do_rt_sigreturn(env, frame_addr);
2803612667cSLaurent Vivier     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
2813612667cSLaurent Vivier         goto badframe;
2823612667cSLaurent Vivier     }
2833612667cSLaurent Vivier     target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
2843612667cSLaurent Vivier     set_sigmask(&set);
2853612667cSLaurent Vivier 
2863612667cSLaurent Vivier     restore_sigcontext(env, frame);
287ddc3e74dSRichard Henderson     target_restore_altstack(&frame->uc.tuc_stack, env);
2883612667cSLaurent Vivier 
2893612667cSLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
29057a0c938SRichard Henderson     return -QEMU_ESIGRETURN;
2913612667cSLaurent Vivier 
2923612667cSLaurent Vivier badframe:
2933612667cSLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
2943612667cSLaurent Vivier     force_sig(TARGET_SIGSEGV);
29557a0c938SRichard Henderson     return -QEMU_ESIGRETURN;
2963612667cSLaurent Vivier }
29755e83c20SRichard Henderson 
setup_sigtramp(abi_ulong sigtramp_page)29855e83c20SRichard Henderson void setup_sigtramp(abi_ulong sigtramp_page)
29955e83c20SRichard Henderson {
30055e83c20SRichard Henderson     uint8_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0);
30155e83c20SRichard Henderson     assert(tramp != NULL);
30255e83c20SRichard Henderson 
30355e83c20SRichard Henderson     default_rt_sigreturn = sigtramp_page;
30455e83c20SRichard Henderson     install_sigtramp(tramp);
30555e83c20SRichard Henderson     unlock_user(tramp, sigtramp_page, 6);
30655e83c20SRichard Henderson }
307