Laurent Vivier | befb744 | 2018-04-24 21:26:16 +0200 | [diff] [blame] | 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 | */ |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 19 | #include "qemu/osdep.h" |
| 20 | #include "qemu.h" |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 21 | #include "signal-common.h" |
| 22 | #include "linux-user/trace.h" |
| 23 | |
| 24 | struct target_sigcontext { |
| 25 | abi_ulong sc_pc; |
| 26 | abi_ulong sc_ps; |
| 27 | abi_ulong sc_lbeg; |
| 28 | abi_ulong sc_lend; |
| 29 | abi_ulong sc_lcount; |
| 30 | abi_ulong sc_sar; |
| 31 | abi_ulong sc_acclo; |
| 32 | abi_ulong sc_acchi; |
| 33 | abi_ulong sc_a[16]; |
| 34 | abi_ulong sc_xtregs; |
| 35 | }; |
| 36 | |
| 37 | struct target_ucontext { |
| 38 | abi_ulong tuc_flags; |
| 39 | abi_ulong tuc_link; |
| 40 | target_stack_t tuc_stack; |
| 41 | struct target_sigcontext tuc_mcontext; |
| 42 | target_sigset_t tuc_sigmask; |
| 43 | }; |
| 44 | |
| 45 | struct target_rt_sigframe { |
| 46 | target_siginfo_t info; |
| 47 | struct target_ucontext uc; |
| 48 | /* TODO: xtregs */ |
| 49 | uint8_t retcode[6]; |
| 50 | abi_ulong window[4]; |
| 51 | }; |
| 52 | |
| 53 | static abi_ulong get_sigframe(struct target_sigaction *sa, |
| 54 | CPUXtensaState *env, |
| 55 | unsigned long framesize) |
| 56 | { |
Laurent Vivier | 465e237 | 2018-04-11 21:23:47 +0200 | [diff] [blame] | 57 | abi_ulong sp; |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 58 | |
Laurent Vivier | 465e237 | 2018-04-11 21:23:47 +0200 | [diff] [blame] | 59 | sp = target_sigsp(get_sp_from_cpustate(env), sa); |
| 60 | |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 61 | return (sp - framesize) & -16; |
| 62 | } |
| 63 | |
| 64 | static int flush_window_regs(CPUXtensaState *env) |
| 65 | { |
| 66 | uint32_t wb = env->sregs[WINDOW_BASE]; |
| 67 | uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1); |
| 68 | unsigned d = ctz32(ws) + 1; |
| 69 | unsigned i; |
| 70 | int ret = 0; |
| 71 | |
| 72 | for (i = d; i < env->config->nareg / 4; i += d) { |
| 73 | uint32_t ssp, osp; |
| 74 | unsigned j; |
| 75 | |
| 76 | ws >>= d; |
| 77 | xtensa_rotate_window(env, d); |
| 78 | |
| 79 | if (ws & 0x1) { |
| 80 | ssp = env->regs[5]; |
| 81 | d = 1; |
| 82 | } else if (ws & 0x2) { |
| 83 | ssp = env->regs[9]; |
| 84 | ret |= get_user_ual(osp, env->regs[1] - 12); |
| 85 | osp -= 32; |
| 86 | d = 2; |
| 87 | } else if (ws & 0x4) { |
| 88 | ssp = env->regs[13]; |
| 89 | ret |= get_user_ual(osp, env->regs[1] - 12); |
| 90 | osp -= 48; |
| 91 | d = 3; |
| 92 | } else { |
| 93 | g_assert_not_reached(); |
| 94 | } |
| 95 | |
| 96 | for (j = 0; j < 4; ++j) { |
| 97 | ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4); |
| 98 | } |
| 99 | for (j = 4; j < d * 4; ++j) { |
| 100 | ret |= put_user_ual(env->regs[j], osp - 16 + j * 4); |
| 101 | } |
| 102 | } |
| 103 | xtensa_rotate_window(env, d); |
| 104 | g_assert(env->sregs[WINDOW_BASE] == wb); |
| 105 | return ret == 0; |
| 106 | } |
| 107 | |
| 108 | static int setup_sigcontext(struct target_rt_sigframe *frame, |
| 109 | CPUXtensaState *env) |
| 110 | { |
| 111 | struct target_sigcontext *sc = &frame->uc.tuc_mcontext; |
| 112 | int i; |
| 113 | |
| 114 | __put_user(env->pc, &sc->sc_pc); |
| 115 | __put_user(env->sregs[PS], &sc->sc_ps); |
| 116 | __put_user(env->sregs[LBEG], &sc->sc_lbeg); |
| 117 | __put_user(env->sregs[LEND], &sc->sc_lend); |
| 118 | __put_user(env->sregs[LCOUNT], &sc->sc_lcount); |
| 119 | if (!flush_window_regs(env)) { |
| 120 | return 0; |
| 121 | } |
| 122 | for (i = 0; i < 16; ++i) { |
| 123 | __put_user(env->regs[i], sc->sc_a + i); |
| 124 | } |
| 125 | __put_user(0, &sc->sc_xtregs); |
| 126 | /* TODO: xtregs */ |
| 127 | return 1; |
| 128 | } |
| 129 | |
| 130 | void setup_rt_frame(int sig, struct target_sigaction *ka, |
| 131 | target_siginfo_t *info, |
| 132 | target_sigset_t *set, CPUXtensaState *env) |
| 133 | { |
| 134 | abi_ulong frame_addr; |
| 135 | struct target_rt_sigframe *frame; |
| 136 | uint32_t ra; |
Max Filippov | 130ea83 | 2019-09-06 09:57:13 -0700 | [diff] [blame] | 137 | bool abi_call0; |
| 138 | unsigned base; |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 139 | int i; |
| 140 | |
| 141 | frame_addr = get_sigframe(ka, env, sizeof(*frame)); |
| 142 | trace_user_setup_rt_frame(env, frame_addr); |
| 143 | |
| 144 | if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { |
| 145 | goto give_sigsegv; |
| 146 | } |
| 147 | |
| 148 | if (ka->sa_flags & SA_SIGINFO) { |
| 149 | tswap_siginfo(&frame->info, info); |
| 150 | } |
| 151 | |
| 152 | __put_user(0, &frame->uc.tuc_flags); |
| 153 | __put_user(0, &frame->uc.tuc_link); |
Laurent Vivier | 465e237 | 2018-04-11 21:23:47 +0200 | [diff] [blame] | 154 | target_save_altstack(&frame->uc.tuc_stack, env); |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 155 | if (!setup_sigcontext(frame, env)) { |
| 156 | unlock_user_struct(frame, frame_addr, 0); |
| 157 | goto give_sigsegv; |
| 158 | } |
| 159 | for (i = 0; i < TARGET_NSIG_WORDS; ++i) { |
| 160 | __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); |
| 161 | } |
| 162 | |
| 163 | if (ka->sa_flags & TARGET_SA_RESTORER) { |
| 164 | ra = ka->sa_restorer; |
| 165 | } else { |
| 166 | ra = frame_addr + offsetof(struct target_rt_sigframe, retcode); |
| 167 | #ifdef TARGET_WORDS_BIGENDIAN |
| 168 | /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ |
| 169 | __put_user(0x22, &frame->retcode[0]); |
| 170 | __put_user(0x0a, &frame->retcode[1]); |
| 171 | __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); |
| 172 | /* Generate instruction: SYSCALL */ |
| 173 | __put_user(0x00, &frame->retcode[3]); |
| 174 | __put_user(0x05, &frame->retcode[4]); |
| 175 | __put_user(0x00, &frame->retcode[5]); |
| 176 | #else |
| 177 | /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ |
| 178 | __put_user(0x22, &frame->retcode[0]); |
| 179 | __put_user(0xa0, &frame->retcode[1]); |
| 180 | __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); |
| 181 | /* Generate instruction: SYSCALL */ |
| 182 | __put_user(0x00, &frame->retcode[3]); |
| 183 | __put_user(0x50, &frame->retcode[4]); |
| 184 | __put_user(0x00, &frame->retcode[5]); |
| 185 | #endif |
| 186 | } |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 187 | memset(env->regs, 0, sizeof(env->regs)); |
| 188 | env->pc = ka->_sa_handler; |
| 189 | env->regs[1] = frame_addr; |
| 190 | env->sregs[WINDOW_BASE] = 0; |
| 191 | env->sregs[WINDOW_START] = 1; |
| 192 | |
Max Filippov | 130ea83 | 2019-09-06 09:57:13 -0700 | [diff] [blame] | 193 | abi_call0 = (env->sregs[PS] & PS_WOE) == 0; |
| 194 | env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT); |
| 195 | |
| 196 | if (abi_call0) { |
| 197 | base = 0; |
| 198 | env->regs[base] = ra; |
| 199 | } else { |
| 200 | env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT); |
| 201 | base = 4; |
| 202 | env->regs[base] = (ra & 0x3fffffff) | 0x40000000; |
| 203 | } |
| 204 | env->regs[base + 2] = sig; |
| 205 | env->regs[base + 3] = frame_addr + offsetof(struct target_rt_sigframe, |
| 206 | info); |
| 207 | env->regs[base + 4] = frame_addr + offsetof(struct target_rt_sigframe, uc); |
Laurent Vivier | 3612667 | 2018-04-24 21:26:30 +0200 | [diff] [blame] | 208 | unlock_user_struct(frame, frame_addr, 1); |
| 209 | return; |
| 210 | |
| 211 | give_sigsegv: |
| 212 | force_sigsegv(sig); |
| 213 | return; |
| 214 | } |
| 215 | |
| 216 | static void restore_sigcontext(CPUXtensaState *env, |
| 217 | struct target_rt_sigframe *frame) |
| 218 | { |
| 219 | struct target_sigcontext *sc = &frame->uc.tuc_mcontext; |
| 220 | uint32_t ps; |
| 221 | int i; |
| 222 | |
| 223 | __get_user(env->pc, &sc->sc_pc); |
| 224 | __get_user(ps, &sc->sc_ps); |
| 225 | __get_user(env->sregs[LBEG], &sc->sc_lbeg); |
| 226 | __get_user(env->sregs[LEND], &sc->sc_lend); |
| 227 | __get_user(env->sregs[LCOUNT], &sc->sc_lcount); |
| 228 | |
| 229 | env->sregs[WINDOW_BASE] = 0; |
| 230 | env->sregs[WINDOW_START] = 1; |
| 231 | env->sregs[PS] = deposit32(env->sregs[PS], |
| 232 | PS_CALLINC_SHIFT, |
| 233 | PS_CALLINC_LEN, |
| 234 | extract32(ps, PS_CALLINC_SHIFT, |
| 235 | PS_CALLINC_LEN)); |
| 236 | for (i = 0; i < 16; ++i) { |
| 237 | __get_user(env->regs[i], sc->sc_a + i); |
| 238 | } |
| 239 | /* TODO: xtregs */ |
| 240 | } |
| 241 | |
| 242 | long do_rt_sigreturn(CPUXtensaState *env) |
| 243 | { |
| 244 | abi_ulong frame_addr = env->regs[1]; |
| 245 | struct target_rt_sigframe *frame; |
| 246 | sigset_t set; |
| 247 | |
| 248 | trace_user_do_rt_sigreturn(env, frame_addr); |
| 249 | if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { |
| 250 | goto badframe; |
| 251 | } |
| 252 | target_to_host_sigset(&set, &frame->uc.tuc_sigmask); |
| 253 | set_sigmask(&set); |
| 254 | |
| 255 | restore_sigcontext(env, frame); |
| 256 | |
| 257 | if (do_sigaltstack(frame_addr + |
| 258 | offsetof(struct target_rt_sigframe, uc.tuc_stack), |
| 259 | 0, get_sp_from_cpustate(env)) == -TARGET_EFAULT) { |
| 260 | goto badframe; |
| 261 | } |
| 262 | unlock_user_struct(frame, frame_addr, 0); |
| 263 | return -TARGET_QEMU_ESIGRETURN; |
| 264 | |
| 265 | badframe: |
| 266 | unlock_user_struct(frame, frame_addr, 0); |
| 267 | force_sig(TARGET_SIGSEGV); |
| 268 | return -TARGET_QEMU_ESIGRETURN; |
| 269 | } |