xref: /openbmc/qemu/linux-user/mips/signal.c (revision ba49d760eb04630e7b15f423ebecf6c871b8f77b)
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 "user-internals.h"
22  #include "signal-common.h"
23  #include "linux-user/trace.h"
24  
25  # if defined(TARGET_ABI_MIPSO32)
26  struct target_sigcontext {
27      uint32_t   sc_regmask;     /* Unused */
28      uint32_t   sc_status;
29      uint64_t   sc_pc;
30      uint64_t   sc_regs[32];
31      uint64_t   sc_fpregs[32];
32      uint32_t   sc_ownedfp;     /* Unused */
33      uint32_t   sc_fpc_csr;
34      uint32_t   sc_fpc_eir;     /* Unused */
35      uint32_t   sc_used_math;
36      uint32_t   sc_dsp;         /* dsp status, was sc_ssflags */
37      uint32_t   pad0;
38      uint64_t   sc_mdhi;
39      uint64_t   sc_mdlo;
40      target_ulong   sc_hi1;         /* Was sc_cause */
41      target_ulong   sc_lo1;         /* Was sc_badvaddr */
42      target_ulong   sc_hi2;         /* Was sc_sigset[4] */
43      target_ulong   sc_lo2;
44      target_ulong   sc_hi3;
45      target_ulong   sc_lo3;
46  };
47  # else /* N32 || N64 */
48  struct target_sigcontext {
49      uint64_t sc_regs[32];
50      uint64_t sc_fpregs[32];
51      uint64_t sc_mdhi;
52      uint64_t sc_hi1;
53      uint64_t sc_hi2;
54      uint64_t sc_hi3;
55      uint64_t sc_mdlo;
56      uint64_t sc_lo1;
57      uint64_t sc_lo2;
58      uint64_t sc_lo3;
59      uint64_t sc_pc;
60      uint32_t sc_fpc_csr;
61      uint32_t sc_used_math;
62      uint32_t sc_dsp;
63      uint32_t sc_reserved;
64  };
65  # endif /* O32 */
66  
67  struct sigframe {
68      uint32_t sf_ass[4];                 /* argument save space for o32 */
69      uint32_t sf_code[2];                        /* signal trampoline */
70      struct target_sigcontext sf_sc;
71      target_sigset_t sf_mask;
72  };
73  
74  struct target_ucontext {
75      abi_ulong tuc_flags;
76      abi_ulong tuc_link;
77      target_stack_t tuc_stack;
78      struct target_sigcontext tuc_mcontext;
79      target_sigset_t tuc_sigmask;
80  };
81  
82  struct target_rt_sigframe {
83      uint32_t rs_ass[4];               /* argument save space for o32 */
84      uint32_t rs_code[2];              /* signal trampoline */
85      struct target_siginfo rs_info;
86      struct target_ucontext rs_uc;
87  };
88  
89  /* Install trampoline to jump back from signal handler */
install_sigtramp(uint32_t * tramp,unsigned int syscall)90  static void install_sigtramp(uint32_t *tramp, unsigned int syscall)
91  {
92      /*
93       * Set up the return code ...
94       *
95       *         li      v0, __NR__foo_sigreturn
96       *         syscall
97       */
98  
99      __put_user(0x24020000 + syscall, tramp + 0);
100      __put_user(0x0000000c          , tramp + 1);
101  }
102  
setup_sigcontext(CPUMIPSState * regs,struct target_sigcontext * sc)103  static inline void setup_sigcontext(CPUMIPSState *regs,
104                                      struct target_sigcontext *sc)
105  {
106      int i;
107  
108      __put_user(exception_resume_pc(regs), &sc->sc_pc);
109      regs->hflags &= ~MIPS_HFLAG_BMASK;
110  
111      __put_user(0, &sc->sc_regs[0]);
112      for (i = 1; i < 32; ++i) {
113          __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
114      }
115  
116      __put_user(regs->active_tc.HI[0], &sc->sc_mdhi);
117      __put_user(regs->active_tc.LO[0], &sc->sc_mdlo);
118  
119      /* Rather than checking for dsp existence, always copy.  The storage
120         would just be garbage otherwise.  */
121      __put_user(regs->active_tc.HI[1], &sc->sc_hi1);
122      __put_user(regs->active_tc.HI[2], &sc->sc_hi2);
123      __put_user(regs->active_tc.HI[3], &sc->sc_hi3);
124      __put_user(regs->active_tc.LO[1], &sc->sc_lo1);
125      __put_user(regs->active_tc.LO[2], &sc->sc_lo2);
126      __put_user(regs->active_tc.LO[3], &sc->sc_lo3);
127      {
128          uint32_t dsp = cpu_rddsp(0x3ff, regs);
129          __put_user(dsp, &sc->sc_dsp);
130      }
131  
132      __put_user(1, &sc->sc_used_math);
133  
134      for (i = 0; i < 32; ++i) {
135          __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
136      }
137  }
138  
139  static inline void
restore_sigcontext(CPUMIPSState * regs,struct target_sigcontext * sc)140  restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc)
141  {
142      int i;
143  
144      __get_user(regs->CP0_EPC, &sc->sc_pc);
145  
146      __get_user(regs->active_tc.HI[0], &sc->sc_mdhi);
147      __get_user(regs->active_tc.LO[0], &sc->sc_mdlo);
148  
149      for (i = 1; i < 32; ++i) {
150          __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
151      }
152  
153      __get_user(regs->active_tc.HI[1], &sc->sc_hi1);
154      __get_user(regs->active_tc.HI[2], &sc->sc_hi2);
155      __get_user(regs->active_tc.HI[3], &sc->sc_hi3);
156      __get_user(regs->active_tc.LO[1], &sc->sc_lo1);
157      __get_user(regs->active_tc.LO[2], &sc->sc_lo2);
158      __get_user(regs->active_tc.LO[3], &sc->sc_lo3);
159      {
160          uint32_t dsp;
161          __get_user(dsp, &sc->sc_dsp);
162          cpu_wrdsp(dsp, 0x3ff, regs);
163      }
164  
165      for (i = 0; i < 32; ++i) {
166          __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
167      }
168  }
169  
170  /*
171   * Determine which stack to use..
172   */
173  static inline abi_ulong
get_sigframe(struct target_sigaction * ka,CPUMIPSState * regs,size_t frame_size)174  get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size)
175  {
176      unsigned long sp;
177  
178      /*
179       * FPU emulator may have its own trampoline active just
180       * above the user stack, 16-bytes before the next lowest
181       * 16 byte boundary.  Try to avoid trashing it.
182       */
183      sp = target_sigsp(get_sp_from_cpustate(regs) - 32, ka);
184  
185      return (sp - frame_size) & ~7;
186  }
187  
mips_set_hflags_isa_mode_from_pc(CPUMIPSState * env)188  static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env)
189  {
190      if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) {
191          env->hflags &= ~MIPS_HFLAG_M16;
192          env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT;
193          env->active_tc.PC &= ~(target_ulong) 1;
194      }
195  }
196  
197  # if defined(TARGET_ABI_MIPSO32)
198  /* compare linux/arch/mips/kernel/signal.c:setup_frame() */
setup_frame(int sig,struct target_sigaction * ka,target_sigset_t * set,CPUMIPSState * regs)199  void setup_frame(int sig, struct target_sigaction * ka,
200                   target_sigset_t *set, CPUMIPSState *regs)
201  {
202      struct sigframe *frame;
203      abi_ulong frame_addr;
204      int i;
205  
206      frame_addr = get_sigframe(ka, regs, sizeof(*frame));
207      trace_user_setup_frame(regs, frame_addr);
208      if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
209          goto give_sigsegv;
210      }
211  
212      setup_sigcontext(regs, &frame->sf_sc);
213  
214      for(i = 0; i < TARGET_NSIG_WORDS; i++) {
215          __put_user(set->sig[i], &frame->sf_mask.sig[i]);
216      }
217  
218      /*
219      * Arguments to signal handler:
220      *
221      *   a0 = signal number
222      *   a1 = 0 (should be cause)
223      *   a2 = pointer to struct sigcontext
224      *
225      * $25 and PC point to the signal handler, $29 points to the
226      * struct sigframe.
227      */
228      regs->active_tc.gpr[ 4] = sig;
229      regs->active_tc.gpr[ 5] = 0;
230      regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc);
231      regs->active_tc.gpr[29] = frame_addr;
232      regs->active_tc.gpr[31] = default_sigreturn;
233      /* The original kernel code sets CP0_EPC to the handler
234      * since it returns to userland using eret
235      * we cannot do this here, and we must set PC directly */
236      regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler;
237      mips_set_hflags_isa_mode_from_pc(regs);
238      unlock_user_struct(frame, frame_addr, 1);
239      return;
240  
241  give_sigsegv:
242      force_sigsegv(sig);
243  }
244  
do_sigreturn(CPUMIPSState * regs)245  long do_sigreturn(CPUMIPSState *regs)
246  {
247      struct sigframe *frame;
248      abi_ulong frame_addr;
249      sigset_t blocked;
250      target_sigset_t target_set;
251      int i;
252  
253      frame_addr = regs->active_tc.gpr[29];
254      trace_user_do_sigreturn(regs, frame_addr);
255      if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
256          goto badframe;
257  
258      for(i = 0; i < TARGET_NSIG_WORDS; i++) {
259          __get_user(target_set.sig[i], &frame->sf_mask.sig[i]);
260      }
261  
262      target_to_host_sigset_internal(&blocked, &target_set);
263      set_sigmask(&blocked);
264  
265      restore_sigcontext(regs, &frame->sf_sc);
266  
267  #if 0
268      /*
269       * Don't let your children do this ...
270       */
271      __asm__ __volatile__(
272          "move\t$29, %0\n\t"
273          "j\tsyscall_exit"
274          :/* no outputs */
275          :"r" (&regs));
276      /* Unreached */
277  #endif
278  
279      regs->active_tc.PC = regs->CP0_EPC;
280      mips_set_hflags_isa_mode_from_pc(regs);
281      /* I am not sure this is right, but it seems to work
282      * maybe a problem with nested signals ? */
283      regs->CP0_EPC = 0;
284      return -QEMU_ESIGRETURN;
285  
286  badframe:
287      force_sig(TARGET_SIGSEGV);
288      return -QEMU_ESIGRETURN;
289  }
290  # endif /* O32 */
291  
setup_rt_frame(int sig,struct target_sigaction * ka,target_siginfo_t * info,target_sigset_t * set,CPUMIPSState * env)292  void setup_rt_frame(int sig, struct target_sigaction *ka,
293                      target_siginfo_t *info,
294                      target_sigset_t *set, CPUMIPSState *env)
295  {
296      struct target_rt_sigframe *frame;
297      abi_ulong frame_addr;
298      int i;
299  
300      frame_addr = get_sigframe(ka, env, sizeof(*frame));
301      trace_user_setup_rt_frame(env, frame_addr);
302      if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
303          goto give_sigsegv;
304      }
305  
306      frame->rs_info = *info;
307  
308      __put_user(0, &frame->rs_uc.tuc_flags);
309      __put_user(0, &frame->rs_uc.tuc_link);
310      target_save_altstack(&frame->rs_uc.tuc_stack, env);
311  
312      setup_sigcontext(env, &frame->rs_uc.tuc_mcontext);
313  
314      for (i = 0; i < TARGET_NSIG_WORDS; i++) {
315          __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
316      }
317  
318      /*
319      * Arguments to signal handler:
320      *
321      *   a0 = signal number
322      *   a1 = pointer to siginfo_t
323      *   a2 = pointer to ucontext_t
324      *
325      * $25 and PC point to the signal handler, $29 points to the
326      * struct sigframe.
327      */
328      env->active_tc.gpr[ 4] = sig;
329      env->active_tc.gpr[ 5] = frame_addr
330                               + offsetof(struct target_rt_sigframe, rs_info);
331      env->active_tc.gpr[ 6] = frame_addr
332                               + offsetof(struct target_rt_sigframe, rs_uc);
333      env->active_tc.gpr[29] = frame_addr;
334      env->active_tc.gpr[31] = default_rt_sigreturn;
335  
336      /*
337       * The original kernel code sets CP0_EPC to the handler
338       * since it returns to userland using eret
339       * we cannot do this here, and we must set PC directly
340       */
341      env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
342      mips_set_hflags_isa_mode_from_pc(env);
343      unlock_user_struct(frame, frame_addr, 1);
344      return;
345  
346  give_sigsegv:
347      unlock_user_struct(frame, frame_addr, 1);
348      force_sigsegv(sig);
349  }
350  
do_rt_sigreturn(CPUMIPSState * env)351  long do_rt_sigreturn(CPUMIPSState *env)
352  {
353      struct target_rt_sigframe *frame;
354      abi_ulong frame_addr;
355      sigset_t blocked;
356  
357      frame_addr = env->active_tc.gpr[29];
358      trace_user_do_rt_sigreturn(env, frame_addr);
359      if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
360          goto badframe;
361      }
362  
363      target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
364      set_sigmask(&blocked);
365  
366      restore_sigcontext(env, &frame->rs_uc.tuc_mcontext);
367      target_restore_altstack(&frame->rs_uc.tuc_stack, env);
368  
369      env->active_tc.PC = env->CP0_EPC;
370      mips_set_hflags_isa_mode_from_pc(env);
371      /* I am not sure this is right, but it seems to work
372      * maybe a problem with nested signals ? */
373      env->CP0_EPC = 0;
374      return -QEMU_ESIGRETURN;
375  
376  badframe:
377      force_sig(TARGET_SIGSEGV);
378      return -QEMU_ESIGRETURN;
379  }
380  
setup_sigtramp(abi_ulong sigtramp_page)381  void setup_sigtramp(abi_ulong sigtramp_page)
382  {
383      uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0);
384      assert(tramp != NULL);
385  
386  #ifdef TARGET_ARCH_HAS_SETUP_FRAME
387      default_sigreturn = sigtramp_page;
388      install_sigtramp(tramp, TARGET_NR_sigreturn);
389  #endif
390  
391      default_rt_sigreturn = sigtramp_page + 8;
392      install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn);
393  
394      unlock_user(tramp, sigtramp_page, 2 * 8);
395  }
396