xref: /openbmc/qemu/linux-user/xtensa/signal.c (revision 1a7c00bb)
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     int i;
138 
139     frame_addr = get_sigframe(ka, env, sizeof(*frame));
140     trace_user_setup_rt_frame(env, frame_addr);
141 
142     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
143         goto give_sigsegv;
144     }
145 
146     if (ka->sa_flags & SA_SIGINFO) {
147         tswap_siginfo(&frame->info, info);
148     }
149 
150     __put_user(0, &frame->uc.tuc_flags);
151     __put_user(0, &frame->uc.tuc_link);
152     target_save_altstack(&frame->uc.tuc_stack, env);
153     if (!setup_sigcontext(frame, env)) {
154         unlock_user_struct(frame, frame_addr, 0);
155         goto give_sigsegv;
156     }
157     for (i = 0; i < TARGET_NSIG_WORDS; ++i) {
158         __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
159     }
160 
161     if (ka->sa_flags & TARGET_SA_RESTORER) {
162         ra = ka->sa_restorer;
163     } else {
164         ra = frame_addr + offsetof(struct target_rt_sigframe, retcode);
165 #ifdef TARGET_WORDS_BIGENDIAN
166         /* Generate instruction:  MOVI a2, __NR_rt_sigreturn */
167         __put_user(0x22, &frame->retcode[0]);
168         __put_user(0x0a, &frame->retcode[1]);
169         __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]);
170         /* Generate instruction:  SYSCALL */
171         __put_user(0x00, &frame->retcode[3]);
172         __put_user(0x05, &frame->retcode[4]);
173         __put_user(0x00, &frame->retcode[5]);
174 #else
175         /* Generate instruction:  MOVI a2, __NR_rt_sigreturn */
176         __put_user(0x22, &frame->retcode[0]);
177         __put_user(0xa0, &frame->retcode[1]);
178         __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]);
179         /* Generate instruction:  SYSCALL */
180         __put_user(0x00, &frame->retcode[3]);
181         __put_user(0x50, &frame->retcode[4]);
182         __put_user(0x00, &frame->retcode[5]);
183 #endif
184     }
185     env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT);
186     if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER)) {
187         env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT);
188     }
189     memset(env->regs, 0, sizeof(env->regs));
190     env->pc = ka->_sa_handler;
191     env->regs[1] = frame_addr;
192     env->sregs[WINDOW_BASE] = 0;
193     env->sregs[WINDOW_START] = 1;
194 
195     env->regs[4] = (ra & 0x3fffffff) | 0x40000000;
196     env->regs[6] = sig;
197     env->regs[7] = frame_addr + offsetof(struct target_rt_sigframe, info);
198     env->regs[8] = frame_addr + offsetof(struct target_rt_sigframe, uc);
199     unlock_user_struct(frame, frame_addr, 1);
200     return;
201 
202 give_sigsegv:
203     force_sigsegv(sig);
204     return;
205 }
206 
207 static void restore_sigcontext(CPUXtensaState *env,
208                                struct target_rt_sigframe *frame)
209 {
210     struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
211     uint32_t ps;
212     int i;
213 
214     __get_user(env->pc, &sc->sc_pc);
215     __get_user(ps, &sc->sc_ps);
216     __get_user(env->sregs[LBEG], &sc->sc_lbeg);
217     __get_user(env->sregs[LEND], &sc->sc_lend);
218     __get_user(env->sregs[LCOUNT], &sc->sc_lcount);
219 
220     env->sregs[WINDOW_BASE] = 0;
221     env->sregs[WINDOW_START] = 1;
222     env->sregs[PS] = deposit32(env->sregs[PS],
223                                PS_CALLINC_SHIFT,
224                                PS_CALLINC_LEN,
225                                extract32(ps, PS_CALLINC_SHIFT,
226                                          PS_CALLINC_LEN));
227     for (i = 0; i < 16; ++i) {
228         __get_user(env->regs[i], sc->sc_a + i);
229     }
230     /* TODO: xtregs */
231 }
232 
233 long do_rt_sigreturn(CPUXtensaState *env)
234 {
235     abi_ulong frame_addr = env->regs[1];
236     struct target_rt_sigframe *frame;
237     sigset_t set;
238 
239     trace_user_do_rt_sigreturn(env, frame_addr);
240     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
241         goto badframe;
242     }
243     target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
244     set_sigmask(&set);
245 
246     restore_sigcontext(env, frame);
247 
248     if (do_sigaltstack(frame_addr +
249                        offsetof(struct target_rt_sigframe, uc.tuc_stack),
250                        0, get_sp_from_cpustate(env)) == -TARGET_EFAULT) {
251         goto badframe;
252     }
253     unlock_user_struct(frame, frame_addr, 0);
254     return -TARGET_QEMU_ESIGRETURN;
255 
256 badframe:
257     unlock_user_struct(frame, frame_addr, 0);
258     force_sig(TARGET_SIGSEGV);
259     return -TARGET_QEMU_ESIGRETURN;
260 }
261