xref: /qemu/linux-user/arm/signal.c (revision 4d6d8a05)
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  */
195f764597SLaurent Vivier #include "qemu/osdep.h"
205f764597SLaurent Vivier #include "qemu.h"
213b249d26SPeter Maydell #include "user-internals.h"
225f764597SLaurent Vivier #include "signal-common.h"
235f764597SLaurent Vivier #include "linux-user/trace.h"
245a534314SPeter Maydell #include "target/arm/cpu-features.h"
25a9f495b9SRichard Henderson #include "vdso-asmoffset.h"
265f764597SLaurent Vivier 
275f764597SLaurent Vivier struct target_sigcontext {
285f764597SLaurent Vivier     abi_ulong trap_no;
295f764597SLaurent Vivier     abi_ulong error_code;
305f764597SLaurent Vivier     abi_ulong oldmask;
315f764597SLaurent Vivier     abi_ulong arm_r0;
325f764597SLaurent Vivier     abi_ulong arm_r1;
335f764597SLaurent Vivier     abi_ulong arm_r2;
345f764597SLaurent Vivier     abi_ulong arm_r3;
355f764597SLaurent Vivier     abi_ulong arm_r4;
365f764597SLaurent Vivier     abi_ulong arm_r5;
375f764597SLaurent Vivier     abi_ulong arm_r6;
385f764597SLaurent Vivier     abi_ulong arm_r7;
395f764597SLaurent Vivier     abi_ulong arm_r8;
405f764597SLaurent Vivier     abi_ulong arm_r9;
415f764597SLaurent Vivier     abi_ulong arm_r10;
425f764597SLaurent Vivier     abi_ulong arm_fp;
435f764597SLaurent Vivier     abi_ulong arm_ip;
445f764597SLaurent Vivier     abi_ulong arm_sp;
455f764597SLaurent Vivier     abi_ulong arm_lr;
465f764597SLaurent Vivier     abi_ulong arm_pc;
475f764597SLaurent Vivier     abi_ulong arm_cpsr;
485f764597SLaurent Vivier     abi_ulong fault_address;
495f764597SLaurent Vivier };
505f764597SLaurent Vivier 
51b807a108SRichard Henderson struct target_ucontext {
525f764597SLaurent Vivier     abi_ulong tuc_flags;
535f764597SLaurent Vivier     abi_ulong tuc_link;
545f764597SLaurent Vivier     target_stack_t tuc_stack;
555f764597SLaurent Vivier     struct target_sigcontext tuc_mcontext;
565f764597SLaurent Vivier     target_sigset_t  tuc_sigmask;       /* mask last for extensibility */
575f764597SLaurent Vivier     char __unused[128 - sizeof(target_sigset_t)];
585f764597SLaurent Vivier     abi_ulong tuc_regspace[128] __attribute__((__aligned__(8)));
595f764597SLaurent Vivier };
605f764597SLaurent Vivier 
615f764597SLaurent Vivier struct target_user_vfp {
625f764597SLaurent Vivier     uint64_t fpregs[32];
635f764597SLaurent Vivier     abi_ulong fpscr;
645f764597SLaurent Vivier };
655f764597SLaurent Vivier 
665f764597SLaurent Vivier struct target_user_vfp_exc {
675f764597SLaurent Vivier     abi_ulong fpexc;
685f764597SLaurent Vivier     abi_ulong fpinst;
695f764597SLaurent Vivier     abi_ulong fpinst2;
705f764597SLaurent Vivier };
715f764597SLaurent Vivier 
725f764597SLaurent Vivier struct target_vfp_sigframe {
735f764597SLaurent Vivier     abi_ulong magic;
745f764597SLaurent Vivier     abi_ulong size;
755f764597SLaurent Vivier     struct target_user_vfp ufp;
765f764597SLaurent Vivier     struct target_user_vfp_exc ufp_exc;
775f764597SLaurent Vivier } __attribute__((__aligned__(8)));
785f764597SLaurent Vivier 
795f764597SLaurent Vivier struct target_iwmmxt_sigframe {
805f764597SLaurent Vivier     abi_ulong magic;
815f764597SLaurent Vivier     abi_ulong size;
825f764597SLaurent Vivier     uint64_t regs[16];
835f764597SLaurent Vivier     /* Note that not all the coprocessor control registers are stored here */
845f764597SLaurent Vivier     uint32_t wcssf;
855f764597SLaurent Vivier     uint32_t wcasf;
865f764597SLaurent Vivier     uint32_t wcgr0;
875f764597SLaurent Vivier     uint32_t wcgr1;
885f764597SLaurent Vivier     uint32_t wcgr2;
895f764597SLaurent Vivier     uint32_t wcgr3;
905f764597SLaurent Vivier } __attribute__((__aligned__(8)));
915f764597SLaurent Vivier 
925f764597SLaurent Vivier #define TARGET_VFP_MAGIC 0x56465001
935f764597SLaurent Vivier #define TARGET_IWMMXT_MAGIC 0x12ef842a
945f764597SLaurent Vivier 
95b807a108SRichard Henderson struct sigframe
965f764597SLaurent Vivier {
97b807a108SRichard Henderson     struct target_ucontext uc;
98e8fa7295SChristophe Lyon     abi_ulong retcode[4];
995f764597SLaurent Vivier };
1005f764597SLaurent Vivier 
101b807a108SRichard Henderson struct rt_sigframe
1025f764597SLaurent Vivier {
1035f764597SLaurent Vivier     struct target_siginfo info;
104b5d66e0dSRichard Henderson     struct sigframe sig;
1055f764597SLaurent Vivier };
1065f764597SLaurent Vivier 
107a9f495b9SRichard Henderson QEMU_BUILD_BUG_ON(offsetof(struct sigframe, retcode[3])
108a9f495b9SRichard Henderson                   != SIGFRAME_RC3_OFFSET);
109a9f495b9SRichard Henderson QEMU_BUILD_BUG_ON(offsetof(struct rt_sigframe, sig.retcode[3])
110a9f495b9SRichard Henderson                   != RT_SIGFRAME_RC3_OFFSET);
111a9f495b9SRichard Henderson 
112b5d66e0dSRichard Henderson static abi_ptr sigreturn_fdpic_tramp;
113b5d66e0dSRichard Henderson 
1145f764597SLaurent Vivier /*
115b5d66e0dSRichard Henderson  * Up to 3 words of 'retcode' in the sigframe are code,
116b5d66e0dSRichard Henderson  * with retcode[3] being used by fdpic for the function descriptor.
117b5d66e0dSRichard Henderson  * This code is not actually executed, but is retained for ABI compat.
118b5d66e0dSRichard Henderson  *
119b5d66e0dSRichard Henderson  * We will create a table of 8 retcode variants in the sigtramp page.
120b5d66e0dSRichard Henderson  * Let each table entry use 3 words.
1215f764597SLaurent Vivier  */
122b5d66e0dSRichard Henderson #define RETCODE_WORDS  3
123b5d66e0dSRichard Henderson #define RETCODE_BYTES  (RETCODE_WORDS * 4)
1245f764597SLaurent Vivier 
valid_user_regs(CPUARMState * regs)1255f764597SLaurent Vivier static inline int valid_user_regs(CPUARMState *regs)
1265f764597SLaurent Vivier {
1275f764597SLaurent Vivier     return 1;
1285f764597SLaurent Vivier }
1295f764597SLaurent Vivier 
1305f764597SLaurent Vivier static void
setup_sigcontext(struct target_sigcontext * sc,CPUARMState * env,abi_ulong mask)1315f764597SLaurent Vivier setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/
1325f764597SLaurent Vivier                  CPUARMState *env, abi_ulong mask)
1335f764597SLaurent Vivier {
1345f764597SLaurent Vivier     __put_user(env->regs[0], &sc->arm_r0);
1355f764597SLaurent Vivier     __put_user(env->regs[1], &sc->arm_r1);
1365f764597SLaurent Vivier     __put_user(env->regs[2], &sc->arm_r2);
1375f764597SLaurent Vivier     __put_user(env->regs[3], &sc->arm_r3);
1385f764597SLaurent Vivier     __put_user(env->regs[4], &sc->arm_r4);
1395f764597SLaurent Vivier     __put_user(env->regs[5], &sc->arm_r5);
1405f764597SLaurent Vivier     __put_user(env->regs[6], &sc->arm_r6);
1415f764597SLaurent Vivier     __put_user(env->regs[7], &sc->arm_r7);
1425f764597SLaurent Vivier     __put_user(env->regs[8], &sc->arm_r8);
1435f764597SLaurent Vivier     __put_user(env->regs[9], &sc->arm_r9);
1445f764597SLaurent Vivier     __put_user(env->regs[10], &sc->arm_r10);
1455f764597SLaurent Vivier     __put_user(env->regs[11], &sc->arm_fp);
1465f764597SLaurent Vivier     __put_user(env->regs[12], &sc->arm_ip);
1475f764597SLaurent Vivier     __put_user(env->regs[13], &sc->arm_sp);
1485f764597SLaurent Vivier     __put_user(env->regs[14], &sc->arm_lr);
1495f764597SLaurent Vivier     __put_user(env->regs[15], &sc->arm_pc);
1505f764597SLaurent Vivier     __put_user(cpsr_read(env), &sc->arm_cpsr);
1515f764597SLaurent Vivier 
1525f764597SLaurent Vivier     __put_user(/* current->thread.trap_no */ 0, &sc->trap_no);
1535f764597SLaurent Vivier     __put_user(/* current->thread.error_code */ 0, &sc->error_code);
1545f764597SLaurent Vivier     __put_user(/* current->thread.address */ 0, &sc->fault_address);
1555f764597SLaurent Vivier     __put_user(mask, &sc->oldmask);
1565f764597SLaurent Vivier }
1575f764597SLaurent Vivier 
1585f764597SLaurent Vivier static inline abi_ulong
get_sigframe(struct target_sigaction * ka,CPUARMState * regs,int framesize)1595f764597SLaurent Vivier get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize)
1605f764597SLaurent Vivier {
161465e237bSLaurent Vivier     unsigned long sp;
1625f764597SLaurent Vivier 
163465e237bSLaurent Vivier     sp = target_sigsp(get_sp_from_cpustate(regs), ka);
1645f764597SLaurent Vivier     /*
1655f764597SLaurent Vivier      * ATPCS B01 mandates 8-byte alignment
1665f764597SLaurent Vivier      */
1675f764597SLaurent Vivier     return (sp - framesize) & ~7;
1685f764597SLaurent Vivier }
1695f764597SLaurent Vivier 
170a9f495b9SRichard Henderson static void write_arm_sigreturn(uint32_t *rc, int syscall);
171a9f495b9SRichard Henderson static void write_arm_fdpic_sigreturn(uint32_t *rc, int ofs);
172a9f495b9SRichard Henderson 
173e8fa7295SChristophe Lyon static int
setup_return(CPUARMState * env,struct target_sigaction * ka,int usig,struct sigframe * frame,abi_ulong sp_addr)174b5d66e0dSRichard Henderson setup_return(CPUARMState *env, struct target_sigaction *ka, int usig,
175b5d66e0dSRichard Henderson              struct sigframe *frame, abi_ulong sp_addr)
1765f764597SLaurent Vivier {
177e8fa7295SChristophe Lyon     abi_ulong handler = 0;
178e8fa7295SChristophe Lyon     abi_ulong handler_fdpic_GOT = 0;
1795f764597SLaurent Vivier     abi_ulong retcode;
180e4e5cb4aSIlya Leoshkevich     bool is_fdpic = info_is_fdpic(get_task_state(thread_cpu)->info);
181a9f495b9SRichard Henderson     bool is_rt = ka->sa_flags & TARGET_SA_SIGINFO;
182a9f495b9SRichard Henderson     bool thumb;
183e8fa7295SChristophe Lyon 
184e8fa7295SChristophe Lyon     if (is_fdpic) {
185e8fa7295SChristophe Lyon         /* In FDPIC mode, ka->_sa_handler points to a function
186e8fa7295SChristophe Lyon          * descriptor (FD). The first word contains the address of the
187e8fa7295SChristophe Lyon          * handler. The second word contains the value of the PIC
188e8fa7295SChristophe Lyon          * register (r9).  */
189e8fa7295SChristophe Lyon         abi_ulong funcdesc_ptr = ka->_sa_handler;
190e8fa7295SChristophe Lyon         if (get_user_ual(handler, funcdesc_ptr)
191e8fa7295SChristophe Lyon             || get_user_ual(handler_fdpic_GOT, funcdesc_ptr + 4)) {
192e8fa7295SChristophe Lyon             return 1;
193e8fa7295SChristophe Lyon         }
194e8fa7295SChristophe Lyon     } else {
195e8fa7295SChristophe Lyon         handler = ka->_sa_handler;
196e8fa7295SChristophe Lyon     }
197e8fa7295SChristophe Lyon     thumb = handler & 1;
198e8fa7295SChristophe Lyon 
1995f764597SLaurent Vivier     uint32_t cpsr = cpsr_read(env);
2005f764597SLaurent Vivier 
2015f764597SLaurent Vivier     cpsr &= ~CPSR_IT;
2025f764597SLaurent Vivier     if (thumb) {
2035f764597SLaurent Vivier         cpsr |= CPSR_T;
2045f764597SLaurent Vivier     } else {
2055f764597SLaurent Vivier         cpsr &= ~CPSR_T;
2065f764597SLaurent Vivier     }
20745e28139SAmanieu d'Antras     if (env->cp15.sctlr_el[1] & SCTLR_E0E) {
20845e28139SAmanieu d'Antras         cpsr |= CPSR_E;
20945e28139SAmanieu d'Antras     } else {
21045e28139SAmanieu d'Antras         cpsr &= ~CPSR_E;
21145e28139SAmanieu d'Antras     }
2125f764597SLaurent Vivier 
213a9f495b9SRichard Henderson     /* Our vdso default_sigreturn label is a table of entry points. */
214a9f495b9SRichard Henderson     retcode = default_sigreturn + (is_fdpic * 2 + is_rt) * 8;
215a9f495b9SRichard Henderson 
216a9f495b9SRichard Henderson     /*
217a9f495b9SRichard Henderson      * Put the sigreturn code on the stack no matter which return
218a9f495b9SRichard Henderson      * mechanism we use in order to remain ABI compliant.
219a9f495b9SRichard Henderson      * Because this is about ABI, always use the A32 instructions,
220a9f495b9SRichard Henderson      * despite the fact that our actual vdso trampoline is T16.
221a9f495b9SRichard Henderson      */
222e8fa7295SChristophe Lyon     if (is_fdpic) {
223a9f495b9SRichard Henderson         write_arm_fdpic_sigreturn(frame->retcode,
224a9f495b9SRichard Henderson                                   is_rt ? RT_SIGFRAME_RC3_OFFSET
225a9f495b9SRichard Henderson                                         : SIGFRAME_RC3_OFFSET);
226e8fa7295SChristophe Lyon     } else {
227a9f495b9SRichard Henderson         write_arm_sigreturn(frame->retcode,
228a9f495b9SRichard Henderson                             is_rt ? TARGET_NR_rt_sigreturn
229a9f495b9SRichard Henderson                                   : TARGET_NR_sigreturn);
2305f764597SLaurent Vivier     }
2315f764597SLaurent Vivier 
232a9f495b9SRichard Henderson     if (ka->sa_flags & TARGET_SA_RESTORER) {
233a9f495b9SRichard Henderson         if (is_fdpic) {
234a9f495b9SRichard Henderson             /* Place the function descriptor in slot 3. */
235a9f495b9SRichard Henderson             __put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]);
236a9f495b9SRichard Henderson         } else {
237a9f495b9SRichard Henderson             retcode = ka->sa_restorer;
238a9f495b9SRichard Henderson         }
2395f764597SLaurent Vivier     }
2405f764597SLaurent Vivier 
2415f764597SLaurent Vivier     env->regs[0] = usig;
242e8fa7295SChristophe Lyon     if (is_fdpic) {
243e8fa7295SChristophe Lyon         env->regs[9] = handler_fdpic_GOT;
244e8fa7295SChristophe Lyon     }
245b5d66e0dSRichard Henderson     env->regs[13] = sp_addr;
2465f764597SLaurent Vivier     env->regs[14] = retcode;
2475f764597SLaurent Vivier     env->regs[15] = handler & (thumb ? ~1 : ~3);
24845e28139SAmanieu d'Antras     cpsr_write(env, cpsr, CPSR_IT | CPSR_T | CPSR_E, CPSRWriteByInstr);
249e8fa7295SChristophe Lyon 
250e8fa7295SChristophe Lyon     return 0;
2515f764597SLaurent Vivier }
2525f764597SLaurent Vivier 
setup_sigframe_vfp(abi_ulong * regspace,CPUARMState * env)253b807a108SRichard Henderson static abi_ulong *setup_sigframe_vfp(abi_ulong *regspace, CPUARMState *env)
2545f764597SLaurent Vivier {
2555f764597SLaurent Vivier     int i;
2565f764597SLaurent Vivier     struct target_vfp_sigframe *vfpframe;
2575f764597SLaurent Vivier     vfpframe = (struct target_vfp_sigframe *)regspace;
2585f764597SLaurent Vivier     __put_user(TARGET_VFP_MAGIC, &vfpframe->magic);
2595f764597SLaurent Vivier     __put_user(sizeof(*vfpframe), &vfpframe->size);
2605f764597SLaurent Vivier     for (i = 0; i < 32; i++) {
2615f764597SLaurent Vivier         __put_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]);
2625f764597SLaurent Vivier     }
2635f764597SLaurent Vivier     __put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr);
2645f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc);
2655f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst);
2665f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2);
2675f764597SLaurent Vivier     return (abi_ulong*)(vfpframe+1);
2685f764597SLaurent Vivier }
2695f764597SLaurent Vivier 
setup_sigframe_iwmmxt(abi_ulong * regspace,CPUARMState * env)270b807a108SRichard Henderson static abi_ulong *setup_sigframe_iwmmxt(abi_ulong *regspace, CPUARMState *env)
2715f764597SLaurent Vivier {
2725f764597SLaurent Vivier     int i;
2735f764597SLaurent Vivier     struct target_iwmmxt_sigframe *iwmmxtframe;
2745f764597SLaurent Vivier     iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace;
2755f764597SLaurent Vivier     __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic);
2765f764597SLaurent Vivier     __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size);
2775f764597SLaurent Vivier     for (i = 0; i < 16; i++) {
2785f764597SLaurent Vivier         __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]);
2795f764597SLaurent Vivier     }
2805f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf);
2815f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf);
2825f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0);
2835f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1);
2845f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2);
2855f764597SLaurent Vivier     __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3);
2865f764597SLaurent Vivier     return (abi_ulong*)(iwmmxtframe+1);
2875f764597SLaurent Vivier }
2885f764597SLaurent Vivier 
setup_sigframe(struct target_ucontext * uc,target_sigset_t * set,CPUARMState * env)289b807a108SRichard Henderson static void setup_sigframe(struct target_ucontext *uc,
2905f764597SLaurent Vivier                            target_sigset_t *set, CPUARMState *env)
2915f764597SLaurent Vivier {
2925f764597SLaurent Vivier     struct target_sigaltstack stack;
2935f764597SLaurent Vivier     int i;
2945f764597SLaurent Vivier     abi_ulong *regspace;
2955f764597SLaurent Vivier 
2965f764597SLaurent Vivier     /* Clear all the bits of the ucontext we don't use.  */
297b807a108SRichard Henderson     memset(uc, 0, offsetof(struct target_ucontext, tuc_mcontext));
2985f764597SLaurent Vivier 
2995f764597SLaurent Vivier     memset(&stack, 0, sizeof(stack));
300465e237bSLaurent Vivier     target_save_altstack(&stack, env);
3015f764597SLaurent Vivier     memcpy(&uc->tuc_stack, &stack, sizeof(stack));
3025f764597SLaurent Vivier 
3035f764597SLaurent Vivier     setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]);
3045f764597SLaurent Vivier     /* Save coprocessor signal frame.  */
3055f764597SLaurent Vivier     regspace = uc->tuc_regspace;
3067fbc6a40SRichard Henderson     if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) {
307b807a108SRichard Henderson         regspace = setup_sigframe_vfp(regspace, env);
3085f764597SLaurent Vivier     }
3095f764597SLaurent Vivier     if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
310b807a108SRichard Henderson         regspace = setup_sigframe_iwmmxt(regspace, env);
3115f764597SLaurent Vivier     }
3125f764597SLaurent Vivier 
3135f764597SLaurent Vivier     /* Write terminating magic word */
3145f764597SLaurent Vivier     __put_user(0, regspace);
3155f764597SLaurent Vivier 
3165f764597SLaurent Vivier     for(i = 0; i < TARGET_NSIG_WORDS; i++) {
3175f764597SLaurent Vivier         __put_user(set->sig[i], &uc->tuc_sigmask.sig[i]);
3185f764597SLaurent Vivier     }
3195f764597SLaurent Vivier }
3205f764597SLaurent Vivier 
setup_frame(int usig,struct target_sigaction * ka,target_sigset_t * set,CPUARMState * regs)321b807a108SRichard Henderson void setup_frame(int usig, struct target_sigaction *ka,
3225f764597SLaurent Vivier                  target_sigset_t *set, CPUARMState *regs)
3235f764597SLaurent Vivier {
324b807a108SRichard Henderson     struct sigframe *frame;
3255f764597SLaurent Vivier     abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame));
3265f764597SLaurent Vivier 
3275f764597SLaurent Vivier     trace_user_setup_frame(regs, frame_addr);
3285f764597SLaurent Vivier     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
3295f764597SLaurent Vivier         goto sigsegv;
3305f764597SLaurent Vivier     }
3315f764597SLaurent Vivier 
332b807a108SRichard Henderson     setup_sigframe(&frame->uc, set, regs);
3335f764597SLaurent Vivier 
334b5d66e0dSRichard Henderson     if (setup_return(regs, ka, usig, frame, frame_addr)) {
335e8fa7295SChristophe Lyon         goto sigsegv;
336e8fa7295SChristophe Lyon     }
3375f764597SLaurent Vivier 
3385f764597SLaurent Vivier     unlock_user_struct(frame, frame_addr, 1);
3395f764597SLaurent Vivier     return;
3405f764597SLaurent Vivier sigsegv:
341e8fa7295SChristophe Lyon     unlock_user_struct(frame, frame_addr, 1);
3425f764597SLaurent Vivier     force_sigsegv(usig);
3435f764597SLaurent Vivier }
3445f764597SLaurent Vivier 
setup_rt_frame(int usig,struct target_sigaction * ka,target_siginfo_t * info,target_sigset_t * set,CPUARMState * env)3455f764597SLaurent Vivier void setup_rt_frame(int usig, struct target_sigaction *ka,
3465f764597SLaurent Vivier                     target_siginfo_t *info,
3475f764597SLaurent Vivier                     target_sigset_t *set, CPUARMState *env)
3485f764597SLaurent Vivier {
349b807a108SRichard Henderson     struct rt_sigframe *frame;
350b807a108SRichard Henderson     abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame));
351b807a108SRichard Henderson     abi_ulong info_addr, uc_addr;
352b807a108SRichard Henderson 
353b807a108SRichard Henderson     trace_user_setup_rt_frame(env, frame_addr);
354b807a108SRichard Henderson     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
355b807a108SRichard Henderson         goto sigsegv;
356b807a108SRichard Henderson     }
357b807a108SRichard Henderson 
358b807a108SRichard Henderson     info_addr = frame_addr + offsetof(struct rt_sigframe, info);
359b5d66e0dSRichard Henderson     uc_addr = frame_addr + offsetof(struct rt_sigframe, sig.uc);
3604d6d8a05SGustavo Romero     frame->info = *info;
361b807a108SRichard Henderson 
362b5d66e0dSRichard Henderson     setup_sigframe(&frame->sig.uc, set, env);
363b807a108SRichard Henderson 
364b5d66e0dSRichard Henderson     if (setup_return(env, ka, usig, &frame->sig, frame_addr)) {
365b807a108SRichard Henderson         goto sigsegv;
366b807a108SRichard Henderson     }
367b807a108SRichard Henderson 
368b807a108SRichard Henderson     env->regs[1] = info_addr;
369b807a108SRichard Henderson     env->regs[2] = uc_addr;
370b807a108SRichard Henderson 
371b807a108SRichard Henderson     unlock_user_struct(frame, frame_addr, 1);
372b807a108SRichard Henderson     return;
373b807a108SRichard Henderson sigsegv:
374b807a108SRichard Henderson     unlock_user_struct(frame, frame_addr, 1);
375b807a108SRichard Henderson     force_sigsegv(usig);
3765f764597SLaurent Vivier }
3775f764597SLaurent Vivier 
3785f764597SLaurent Vivier static int
restore_sigcontext(CPUARMState * env,struct target_sigcontext * sc)3795f764597SLaurent Vivier restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc)
3805f764597SLaurent Vivier {
3815f764597SLaurent Vivier     int err = 0;
3825f764597SLaurent Vivier     uint32_t cpsr;
3835f764597SLaurent Vivier 
3845f764597SLaurent Vivier     __get_user(env->regs[0], &sc->arm_r0);
3855f764597SLaurent Vivier     __get_user(env->regs[1], &sc->arm_r1);
3865f764597SLaurent Vivier     __get_user(env->regs[2], &sc->arm_r2);
3875f764597SLaurent Vivier     __get_user(env->regs[3], &sc->arm_r3);
3885f764597SLaurent Vivier     __get_user(env->regs[4], &sc->arm_r4);
3895f764597SLaurent Vivier     __get_user(env->regs[5], &sc->arm_r5);
3905f764597SLaurent Vivier     __get_user(env->regs[6], &sc->arm_r6);
3915f764597SLaurent Vivier     __get_user(env->regs[7], &sc->arm_r7);
3925f764597SLaurent Vivier     __get_user(env->regs[8], &sc->arm_r8);
3935f764597SLaurent Vivier     __get_user(env->regs[9], &sc->arm_r9);
3945f764597SLaurent Vivier     __get_user(env->regs[10], &sc->arm_r10);
3955f764597SLaurent Vivier     __get_user(env->regs[11], &sc->arm_fp);
3965f764597SLaurent Vivier     __get_user(env->regs[12], &sc->arm_ip);
3975f764597SLaurent Vivier     __get_user(env->regs[13], &sc->arm_sp);
3985f764597SLaurent Vivier     __get_user(env->regs[14], &sc->arm_lr);
3995f764597SLaurent Vivier     __get_user(env->regs[15], &sc->arm_pc);
4005f764597SLaurent Vivier     __get_user(cpsr, &sc->arm_cpsr);
4015f764597SLaurent Vivier     cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr);
4025f764597SLaurent Vivier 
4035f764597SLaurent Vivier     err |= !valid_user_regs(env);
4045f764597SLaurent Vivier 
4055f764597SLaurent Vivier     return err;
4065f764597SLaurent Vivier }
4075f764597SLaurent Vivier 
restore_sigframe_vfp(CPUARMState * env,abi_ulong * regspace)408b807a108SRichard Henderson static abi_ulong *restore_sigframe_vfp(CPUARMState *env, abi_ulong *regspace)
4095f764597SLaurent Vivier {
4105f764597SLaurent Vivier     int i;
4115f764597SLaurent Vivier     abi_ulong magic, sz;
4125f764597SLaurent Vivier     uint32_t fpscr, fpexc;
4135f764597SLaurent Vivier     struct target_vfp_sigframe *vfpframe;
4145f764597SLaurent Vivier     vfpframe = (struct target_vfp_sigframe *)regspace;
4155f764597SLaurent Vivier 
4165f764597SLaurent Vivier     __get_user(magic, &vfpframe->magic);
4175f764597SLaurent Vivier     __get_user(sz, &vfpframe->size);
4185f764597SLaurent Vivier     if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) {
4195f764597SLaurent Vivier         return 0;
4205f764597SLaurent Vivier     }
4215f764597SLaurent Vivier     for (i = 0; i < 32; i++) {
4225f764597SLaurent Vivier         __get_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]);
4235f764597SLaurent Vivier     }
4245f764597SLaurent Vivier     __get_user(fpscr, &vfpframe->ufp.fpscr);
4255f764597SLaurent Vivier     vfp_set_fpscr(env, fpscr);
4265f764597SLaurent Vivier     __get_user(fpexc, &vfpframe->ufp_exc.fpexc);
4275f764597SLaurent Vivier     /* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid
4285f764597SLaurent Vivier      * and the exception flag is cleared
4295f764597SLaurent Vivier      */
4305f764597SLaurent Vivier     fpexc |= (1 << 30);
4315f764597SLaurent Vivier     fpexc &= ~((1 << 31) | (1 << 28));
4325f764597SLaurent Vivier     env->vfp.xregs[ARM_VFP_FPEXC] = fpexc;
4335f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst);
4345f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2);
4355f764597SLaurent Vivier     return (abi_ulong*)(vfpframe + 1);
4365f764597SLaurent Vivier }
4375f764597SLaurent Vivier 
restore_sigframe_iwmmxt(CPUARMState * env,abi_ulong * regspace)438b807a108SRichard Henderson static abi_ulong *restore_sigframe_iwmmxt(CPUARMState *env,
4395f764597SLaurent Vivier                                           abi_ulong *regspace)
4405f764597SLaurent Vivier {
4415f764597SLaurent Vivier     int i;
4425f764597SLaurent Vivier     abi_ulong magic, sz;
4435f764597SLaurent Vivier     struct target_iwmmxt_sigframe *iwmmxtframe;
4445f764597SLaurent Vivier     iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace;
4455f764597SLaurent Vivier 
4465f764597SLaurent Vivier     __get_user(magic, &iwmmxtframe->magic);
4475f764597SLaurent Vivier     __get_user(sz, &iwmmxtframe->size);
4485f764597SLaurent Vivier     if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) {
4495f764597SLaurent Vivier         return 0;
4505f764597SLaurent Vivier     }
4515f764597SLaurent Vivier     for (i = 0; i < 16; i++) {
4525f764597SLaurent Vivier         __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]);
4535f764597SLaurent Vivier     }
4545f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf);
4555f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf);
4565f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0);
4575f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1);
4585f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2);
4595f764597SLaurent Vivier     __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3);
4605f764597SLaurent Vivier     return (abi_ulong*)(iwmmxtframe + 1);
4615f764597SLaurent Vivier }
4625f764597SLaurent Vivier 
do_sigframe_return(CPUARMState * env,target_ulong context_addr,struct target_ucontext * uc)463b807a108SRichard Henderson static int do_sigframe_return(CPUARMState *env,
4645f764597SLaurent Vivier                               target_ulong context_addr,
465b807a108SRichard Henderson                               struct target_ucontext *uc)
4665f764597SLaurent Vivier {
4675f764597SLaurent Vivier     sigset_t host_set;
4685f764597SLaurent Vivier     abi_ulong *regspace;
4695f764597SLaurent Vivier 
4705f764597SLaurent Vivier     target_to_host_sigset(&host_set, &uc->tuc_sigmask);
4715f764597SLaurent Vivier     set_sigmask(&host_set);
4725f764597SLaurent Vivier 
473b807a108SRichard Henderson     if (restore_sigcontext(env, &uc->tuc_mcontext)) {
4745f764597SLaurent Vivier         return 1;
475b807a108SRichard Henderson     }
4765f764597SLaurent Vivier 
4775f764597SLaurent Vivier     /* Restore coprocessor signal frame */
4785f764597SLaurent Vivier     regspace = uc->tuc_regspace;
4797fbc6a40SRichard Henderson     if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) {
480b807a108SRichard Henderson         regspace = restore_sigframe_vfp(env, regspace);
4815f764597SLaurent Vivier         if (!regspace) {
4825f764597SLaurent Vivier             return 1;
4835f764597SLaurent Vivier         }
4845f764597SLaurent Vivier     }
4855f764597SLaurent Vivier     if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
486b807a108SRichard Henderson         regspace = restore_sigframe_iwmmxt(env, regspace);
4875f764597SLaurent Vivier         if (!regspace) {
4885f764597SLaurent Vivier             return 1;
4895f764597SLaurent Vivier         }
4905f764597SLaurent Vivier     }
4915f764597SLaurent Vivier 
492ddc3e74dSRichard Henderson     target_restore_altstack(&uc->tuc_stack, env);
4935f764597SLaurent Vivier 
4945f764597SLaurent Vivier #if 0
4955f764597SLaurent Vivier     /* Send SIGTRAP if we're single-stepping */
4965f764597SLaurent Vivier     if (ptrace_cancel_bpt(current))
4975f764597SLaurent Vivier         send_sig(SIGTRAP, current, 1);
4985f764597SLaurent Vivier #endif
4995f764597SLaurent Vivier 
5005f764597SLaurent Vivier     return 0;
5015f764597SLaurent Vivier }
5025f764597SLaurent Vivier 
do_sigreturn(CPUARMState * env)503b807a108SRichard Henderson long do_sigreturn(CPUARMState *env)
5045f764597SLaurent Vivier {
5055f764597SLaurent Vivier     abi_ulong frame_addr;
506b807a108SRichard Henderson     struct sigframe *frame = NULL;
5075f764597SLaurent Vivier 
5085f764597SLaurent Vivier     /*
5095f764597SLaurent Vivier      * Since we stacked the signal on a 64-bit boundary,
5105f764597SLaurent Vivier      * then 'sp' should be word aligned here.  If it's
5115f764597SLaurent Vivier      * not, then the user is trying to mess with us.
5125f764597SLaurent Vivier      */
5135f764597SLaurent Vivier     frame_addr = env->regs[13];
5145f764597SLaurent Vivier     trace_user_do_sigreturn(env, frame_addr);
5155f764597SLaurent Vivier     if (frame_addr & 7) {
5165f764597SLaurent Vivier         goto badframe;
5175f764597SLaurent Vivier     }
5185f764597SLaurent Vivier 
5195f764597SLaurent Vivier     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
5205f764597SLaurent Vivier         goto badframe;
5215f764597SLaurent Vivier     }
5225f764597SLaurent Vivier 
523b807a108SRichard Henderson     if (do_sigframe_return(env,
524b807a108SRichard Henderson                            frame_addr + offsetof(struct sigframe, uc),
5255f764597SLaurent Vivier                            &frame->uc)) {
5265f764597SLaurent Vivier         goto badframe;
5275f764597SLaurent Vivier     }
5285f764597SLaurent Vivier 
5295f764597SLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
53057a0c938SRichard Henderson     return -QEMU_ESIGRETURN;
5315f764597SLaurent Vivier 
5325f764597SLaurent Vivier badframe:
5335f764597SLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
5345f764597SLaurent Vivier     force_sig(TARGET_SIGSEGV);
53557a0c938SRichard Henderson     return -QEMU_ESIGRETURN;
5365f764597SLaurent Vivier }
5375f764597SLaurent Vivier 
do_rt_sigreturn(CPUARMState * env)538b807a108SRichard Henderson long do_rt_sigreturn(CPUARMState *env)
5395f764597SLaurent Vivier {
5405f764597SLaurent Vivier     abi_ulong frame_addr;
541b807a108SRichard Henderson     struct rt_sigframe *frame = NULL;
5425f764597SLaurent Vivier 
5435f764597SLaurent Vivier     /*
5445f764597SLaurent Vivier      * Since we stacked the signal on a 64-bit boundary,
5455f764597SLaurent Vivier      * then 'sp' should be word aligned here.  If it's
5465f764597SLaurent Vivier      * not, then the user is trying to mess with us.
5475f764597SLaurent Vivier      */
5485f764597SLaurent Vivier     frame_addr = env->regs[13];
5495f764597SLaurent Vivier     trace_user_do_rt_sigreturn(env, frame_addr);
5505f764597SLaurent Vivier     if (frame_addr & 7) {
5515f764597SLaurent Vivier         goto badframe;
5525f764597SLaurent Vivier     }
5535f764597SLaurent Vivier 
5545f764597SLaurent Vivier     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
5555f764597SLaurent Vivier         goto badframe;
5565f764597SLaurent Vivier     }
5575f764597SLaurent Vivier 
558b807a108SRichard Henderson     if (do_sigframe_return(env,
559b5d66e0dSRichard Henderson                            frame_addr + offsetof(struct rt_sigframe, sig.uc),
560b5d66e0dSRichard Henderson                            &frame->sig.uc)) {
5615f764597SLaurent Vivier         goto badframe;
5625f764597SLaurent Vivier     }
5635f764597SLaurent Vivier 
5645f764597SLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
56557a0c938SRichard Henderson     return -QEMU_ESIGRETURN;
5665f764597SLaurent Vivier 
5675f764597SLaurent Vivier badframe:
5685f764597SLaurent Vivier     unlock_user_struct(frame, frame_addr, 0);
5695f764597SLaurent Vivier     force_sig(TARGET_SIGSEGV);
57057a0c938SRichard Henderson     return -QEMU_ESIGRETURN;
5715f764597SLaurent Vivier }
572b5d66e0dSRichard Henderson 
573b5d66e0dSRichard Henderson /*
574b5d66e0dSRichard Henderson  * EABI syscalls pass the number via r7.
575b5d66e0dSRichard Henderson  * Note that the kernel still adds the OABI syscall number to the trap,
576b5d66e0dSRichard Henderson  * presumably for backward ABI compatibility with unwinders.
577b5d66e0dSRichard Henderson  */
578b5d66e0dSRichard Henderson #define ARM_MOV_R7_IMM(X)       (0xe3a07000 | (X))
579b5d66e0dSRichard Henderson #define ARM_SWI_SYS(X)          (0xef000000 | (X) | ARM_SYSCALL_BASE)
580b5d66e0dSRichard Henderson 
581b5d66e0dSRichard Henderson #define THUMB_MOVS_R7_IMM(X)    (0x2700 | (X))
582b5d66e0dSRichard Henderson #define THUMB_SWI_SYS           0xdf00
583b5d66e0dSRichard Henderson 
write_arm_sigreturn(uint32_t * rc,int syscall)584b5d66e0dSRichard Henderson static void write_arm_sigreturn(uint32_t *rc, int syscall)
585b5d66e0dSRichard Henderson {
586b5d66e0dSRichard Henderson     __put_user(ARM_MOV_R7_IMM(syscall), rc);
587b5d66e0dSRichard Henderson     __put_user(ARM_SWI_SYS(syscall), rc + 1);
588b5d66e0dSRichard Henderson     /* Wrote 8 of 12 bytes */
589b5d66e0dSRichard Henderson }
590b5d66e0dSRichard Henderson 
write_thm_sigreturn(uint32_t * rc,int syscall)591b5d66e0dSRichard Henderson static void write_thm_sigreturn(uint32_t *rc, int syscall)
592b5d66e0dSRichard Henderson {
593b5d66e0dSRichard Henderson     __put_user(THUMB_SWI_SYS << 16 | THUMB_MOVS_R7_IMM(syscall), rc);
594b5d66e0dSRichard Henderson     /* Wrote 4 of 12 bytes */
595b5d66e0dSRichard Henderson }
596b5d66e0dSRichard Henderson 
597b5d66e0dSRichard Henderson /*
598b5d66e0dSRichard Henderson  * Stub needed to make sure the FD register (r9) contains the right value.
599b5d66e0dSRichard Henderson  * Use the same instruction sequence as the kernel.
600b5d66e0dSRichard Henderson  */
write_arm_fdpic_sigreturn(uint32_t * rc,int ofs)601b5d66e0dSRichard Henderson static void write_arm_fdpic_sigreturn(uint32_t *rc, int ofs)
602b5d66e0dSRichard Henderson {
603b5d66e0dSRichard Henderson     assert(ofs <= 0xfff);
604b5d66e0dSRichard Henderson     __put_user(0xe59d3000 | ofs, rc + 0);   /* ldr r3, [sp, #ofs] */
605b5d66e0dSRichard Henderson     __put_user(0xe8930908, rc + 1);         /* ldm r3, { r3, r9 } */
606b5d66e0dSRichard Henderson     __put_user(0xe12fff13, rc + 2);         /* bx  r3 */
607b5d66e0dSRichard Henderson     /* Wrote 12 of 12 bytes */
608b5d66e0dSRichard Henderson }
609b5d66e0dSRichard Henderson 
write_thm_fdpic_sigreturn(void * vrc,int ofs)610b5d66e0dSRichard Henderson static void write_thm_fdpic_sigreturn(void *vrc, int ofs)
611b5d66e0dSRichard Henderson {
612b5d66e0dSRichard Henderson     uint16_t *rc = vrc;
613b5d66e0dSRichard Henderson 
614b5d66e0dSRichard Henderson     assert((ofs & ~0x3fc) == 0);
615b5d66e0dSRichard Henderson     __put_user(0x9b00 | (ofs >> 2), rc + 0);      /* ldr r3, [sp, #ofs] */
616b5d66e0dSRichard Henderson     __put_user(0xcb0c, rc + 1);                   /* ldm r3, { r2, r3 } */
617b5d66e0dSRichard Henderson     __put_user(0x4699, rc + 2);                   /* mov r9, r3 */
618b5d66e0dSRichard Henderson     __put_user(0x4710, rc + 3);                   /* bx  r2 */
619b5d66e0dSRichard Henderson     /* Wrote 8 of 12 bytes */
620b5d66e0dSRichard Henderson }
621b5d66e0dSRichard Henderson 
setup_sigtramp(abi_ulong sigtramp_page)622b5d66e0dSRichard Henderson void setup_sigtramp(abi_ulong sigtramp_page)
623b5d66e0dSRichard Henderson {
624b5d66e0dSRichard Henderson     uint32_t total_size = 8 * RETCODE_BYTES;
625b5d66e0dSRichard Henderson     uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, total_size, 0);
626b5d66e0dSRichard Henderson 
627b5d66e0dSRichard Henderson     assert(tramp != NULL);
628b5d66e0dSRichard Henderson 
629b5d66e0dSRichard Henderson     default_sigreturn = sigtramp_page;
630b5d66e0dSRichard Henderson     write_arm_sigreturn(&tramp[0 * RETCODE_WORDS], TARGET_NR_sigreturn);
631b5d66e0dSRichard Henderson     write_thm_sigreturn(&tramp[1 * RETCODE_WORDS], TARGET_NR_sigreturn);
632b5d66e0dSRichard Henderson     write_arm_sigreturn(&tramp[2 * RETCODE_WORDS], TARGET_NR_rt_sigreturn);
633b5d66e0dSRichard Henderson     write_thm_sigreturn(&tramp[3 * RETCODE_WORDS], TARGET_NR_rt_sigreturn);
634b5d66e0dSRichard Henderson 
635b5d66e0dSRichard Henderson     sigreturn_fdpic_tramp = sigtramp_page + 4 * RETCODE_BYTES;
636b5d66e0dSRichard Henderson     write_arm_fdpic_sigreturn(tramp + 4 * RETCODE_WORDS,
637b5d66e0dSRichard Henderson                               offsetof(struct sigframe, retcode[3]));
638b5d66e0dSRichard Henderson     write_thm_fdpic_sigreturn(tramp + 5 * RETCODE_WORDS,
639b5d66e0dSRichard Henderson                                 offsetof(struct sigframe, retcode[3]));
640b5d66e0dSRichard Henderson     write_arm_fdpic_sigreturn(tramp + 6 * RETCODE_WORDS,
641b5d66e0dSRichard Henderson                               offsetof(struct rt_sigframe, sig.retcode[3]));
642b5d66e0dSRichard Henderson     write_thm_fdpic_sigreturn(tramp + 7 * RETCODE_WORDS,
643b5d66e0dSRichard Henderson                               offsetof(struct rt_sigframe, sig.retcode[3]));
644b5d66e0dSRichard Henderson 
645b5d66e0dSRichard Henderson     unlock_user(tramp, sigtramp_page, total_size);
646b5d66e0dSRichard Henderson }
647