xref: /qemu/linux-user/xtensa/signal.c (revision ab9056ff)
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  */
19 #include "qemu/osdep.h"
20 #include "qemu.h"
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 {
57     abi_ulong sp;
58 
59     sp = target_sigsp(get_sp_from_cpustate(env), sa);
60 
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;
137     bool abi_call0;
138     unsigned base;
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);
154     target_save_altstack(&frame->uc.tuc_stack, env);
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     }
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 
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);
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 }
270