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