| 1 | /* |
| 2 | * arm signal functions |
| 3 | * |
| 4 | * Copyright (c) 2013 Stacey D. Son |
| 5 | * |
| 6 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 7 | */ |
| 8 | #include "qemu/osdep.h" |
| 9 | #include "qemu.h" |
| 10 | |
| 11 | /* |
| 12 | * Compare to arm/arm/machdep.c sendsig() |
| 13 | * Assumes that target stack frame memory is locked. |
| 14 | */ |
| 15 | abi_long set_sigtramp_args(CPUARMState *env, int sig, |
| 16 | struct target_sigframe *frame, |
| 17 | abi_ulong frame_addr, |
| 18 | struct target_sigaction *ka) |
| 19 | { |
| 20 | /* |
| 21 | * Arguments to signal handler: |
| 22 | * r0 = signal number |
| 23 | * r1 = siginfo pointer |
| 24 | * r2 = ucontext pointer |
| 25 | * r5 = ucontext pointer |
| 26 | * pc = signal handler pointer |
| 27 | * sp = sigframe struct pointer |
| 28 | * lr = sigtramp at base of user stack |
| 29 | */ |
| 30 | |
| 31 | env->regs[0] = sig; |
| 32 | env->regs[1] = frame_addr + |
| 33 | offsetof(struct target_sigframe, sf_si); |
| 34 | env->regs[2] = frame_addr + |
| 35 | offsetof(struct target_sigframe, sf_uc); |
| 36 | |
| 37 | /* the trampoline uses r5 as the uc address */ |
| 38 | env->regs[5] = frame_addr + |
| 39 | offsetof(struct target_sigframe, sf_uc); |
| 40 | env->regs[TARGET_REG_PC] = ka->_sa_handler & ~1; |
| 41 | env->regs[TARGET_REG_SP] = frame_addr; |
| 42 | env->regs[TARGET_REG_LR] = TARGET_PS_STRINGS - TARGET_SZSIGCODE; |
| 43 | /* |
| 44 | * Low bit indicates whether or not we're entering thumb mode. |
| 45 | */ |
| 46 | cpsr_write(env, (ka->_sa_handler & 1) * CPSR_T, CPSR_T, CPSRWriteByInstr); |
| 47 | |
| 48 | return 0; |
| 49 | } |
| 50 | |
| 51 | static abi_long get_vfpcontext(CPUARMState *env, abi_ulong frame_addr, |
| 52 | struct target_sigframe *frame) |
| 53 | { |
| 54 | /* see sendsig and get_vfpcontext in sys/arm/arm/exec_machdep.c */ |
| 55 | target_mcontext_vfp_t *vfp = &frame->sf_vfp; |
| 56 | target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; |
| 57 | |
| 58 | /* Assumes that mcp and vfp are locked */ |
| 59 | for (int i = 0; i < 32; i++) { |
| 60 | vfp->mcv_reg[i] = tswap64(*aa32_vfp_dreg(env, i)); |
| 61 | } |
| 62 | vfp->mcv_fpscr = tswap32(vfp_get_fpscr(env)); |
| 63 | mcp->mc_vfp_size = tswap32(sizeof(*vfp)); |
| 64 | mcp->mc_vfp_ptr = tswap32(frame_addr + ((uintptr_t)vfp - (uintptr_t)frame)); |
| 65 | return 0; |
| 66 | } |
| 67 | |
| 68 | /* |
| 69 | * Compare to arm/arm/exec_machdep.c get_mcontext() |
| 70 | * Assumes that the memory is locked if mcp points to user memory. |
| 71 | */ |
| 72 | abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags) |
| 73 | { |
| 74 | uint32_t *gr = mcp->__gregs; |
| 75 | |
| 76 | gr[TARGET_REG_CPSR] = tswap32(cpsr_read(env)); |
| 77 | if (flags & TARGET_MC_GET_CLEAR_RET) { |
| 78 | gr[TARGET_REG_R0] = 0; |
| 79 | gr[TARGET_REG_CPSR] &= ~CPSR_C; |
| 80 | } else { |
| 81 | gr[TARGET_REG_R0] = tswap32(env->regs[0]); |
| 82 | } |
| 83 | |
| 84 | gr[TARGET_REG_R1] = tswap32(env->regs[1]); |
| 85 | gr[TARGET_REG_R2] = tswap32(env->regs[2]); |
| 86 | gr[TARGET_REG_R3] = tswap32(env->regs[3]); |
| 87 | gr[TARGET_REG_R4] = tswap32(env->regs[4]); |
| 88 | gr[TARGET_REG_R5] = tswap32(env->regs[5]); |
| 89 | gr[TARGET_REG_R6] = tswap32(env->regs[6]); |
| 90 | gr[TARGET_REG_R7] = tswap32(env->regs[7]); |
| 91 | gr[TARGET_REG_R8] = tswap32(env->regs[8]); |
| 92 | gr[TARGET_REG_R9] = tswap32(env->regs[9]); |
| 93 | gr[TARGET_REG_R10] = tswap32(env->regs[10]); |
| 94 | gr[TARGET_REG_R11] = tswap32(env->regs[11]); |
| 95 | gr[TARGET_REG_R12] = tswap32(env->regs[12]); |
| 96 | |
| 97 | gr[TARGET_REG_SP] = tswap32(env->regs[13]); |
| 98 | gr[TARGET_REG_LR] = tswap32(env->regs[14]); |
| 99 | gr[TARGET_REG_PC] = tswap32(env->regs[15]); |
| 100 | |
| 101 | /* |
| 102 | * FreeBSD's get_mcontext doesn't save VFP info, but sets the pointer and |
| 103 | * size to zero. Applications that need the VFP state use |
| 104 | * sysarch(ARM_GET_VFPSTATE) and are expected to adjust mcontext after that. |
| 105 | */ |
| 106 | mcp->mc_vfp_size = 0; |
| 107 | mcp->mc_vfp_ptr = 0; |
| 108 | memset(&mcp->mc_spare, 0, sizeof(mcp->mc_spare)); |
| 109 | |
| 110 | return 0; |
| 111 | } |
| 112 | |
| 113 | /* |
| 114 | * Compare to arm/arm/exec_machdep.c sendsig() |
| 115 | * Assumes that the memory is locked if frame points to user memory. |
| 116 | */ |
| 117 | abi_long setup_sigframe_arch(CPUARMState *env, abi_ulong frame_addr, |
| 118 | struct target_sigframe *frame, int flags) |
| 119 | { |
| 120 | target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; |
| 121 | |
| 122 | get_mcontext(env, mcp, flags); |
| 123 | get_vfpcontext(env, frame_addr, frame); |
| 124 | return 0; |
| 125 | } |
| 126 | |
| 127 | /* Compare to arm/arm/exec_machdep.c set_mcontext() */ |
| 128 | abi_long set_mcontext(CPUARMState *env, target_mcontext_t *mcp, int srflag) |
| 129 | { |
| 130 | int err = 0; |
| 131 | const uint32_t *gr = mcp->__gregs; |
| 132 | uint32_t cpsr, ccpsr = cpsr_read(env); |
| 133 | uint32_t fpscr, mask; |
| 134 | |
| 135 | cpsr = tswap32(gr[TARGET_REG_CPSR]); |
| 136 | /* |
| 137 | * Only allow certain bits to change, reject attempted changes to non-user |
| 138 | * bits. In addition, make sure we're headed for user mode and none of the |
| 139 | * interrupt bits are set. |
| 140 | */ |
| 141 | if ((ccpsr & ~CPSR_USER) != (cpsr & ~CPSR_USER)) { |
| 142 | return -TARGET_EINVAL; |
| 143 | } |
| 144 | if ((cpsr & CPSR_M) != ARM_CPU_MODE_USR || |
| 145 | (cpsr & (CPSR_I | CPSR_F)) != 0) { |
| 146 | return -TARGET_EINVAL; |
| 147 | } |
| 148 | |
| 149 | /* |
| 150 | * The movs pc,lr instruction that implements the return to userland masks |
| 151 | * these bits out. |
| 152 | */ |
| 153 | mask = cpsr & CPSR_T ? 0x1 : 0x3; |
| 154 | |
| 155 | /* |
| 156 | * Make sure that we either have no vfp, or it's the correct size. |
| 157 | * FreeBSD just ignores it, though, so maybe we'll need to adjust |
| 158 | * things below instead. |
| 159 | */ |
| 160 | if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(target_mcontext_vfp_t)) { |
| 161 | return -TARGET_EINVAL; |
| 162 | } |
| 163 | |
| 164 | env->regs[0] = tswap32(gr[TARGET_REG_R0]); |
| 165 | env->regs[1] = tswap32(gr[TARGET_REG_R1]); |
| 166 | env->regs[2] = tswap32(gr[TARGET_REG_R2]); |
| 167 | env->regs[3] = tswap32(gr[TARGET_REG_R3]); |
| 168 | env->regs[4] = tswap32(gr[TARGET_REG_R4]); |
| 169 | env->regs[5] = tswap32(gr[TARGET_REG_R5]); |
| 170 | env->regs[6] = tswap32(gr[TARGET_REG_R6]); |
| 171 | env->regs[7] = tswap32(gr[TARGET_REG_R7]); |
| 172 | env->regs[8] = tswap32(gr[TARGET_REG_R8]); |
| 173 | env->regs[9] = tswap32(gr[TARGET_REG_R9]); |
| 174 | env->regs[10] = tswap32(gr[TARGET_REG_R10]); |
| 175 | env->regs[11] = tswap32(gr[TARGET_REG_R11]); |
| 176 | env->regs[12] = tswap32(gr[TARGET_REG_R12]); |
| 177 | |
| 178 | env->regs[13] = tswap32(gr[TARGET_REG_SP]); |
| 179 | env->regs[14] = tswap32(gr[TARGET_REG_LR]); |
| 180 | env->regs[15] = tswap32(gr[TARGET_REG_PC] & ~mask); |
| 181 | if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr != 0) { |
| 182 | /* see set_vfpcontext in sys/arm/arm/exec_machdep.c */ |
| 183 | target_mcontext_vfp_t *vfp; |
| 184 | |
| 185 | vfp = lock_user(VERIFY_READ, mcp->mc_vfp_ptr, sizeof(*vfp), 1); |
| 186 | for (int i = 0; i < 32; i++) { |
| 187 | __get_user(*aa32_vfp_dreg(env, i), &vfp->mcv_reg[i]); |
| 188 | } |
| 189 | __get_user(fpscr, &vfp->mcv_fpscr); |
| 190 | vfp_set_fpscr(env, fpscr); |
| 191 | unlock_user(vfp, mcp->mc_vfp_ptr, sizeof(target_ucontext_t)); |
| 192 | |
| 193 | /* |
| 194 | * linux-user sets fpexc, fpinst and fpinst2, but these aren't in |
| 195 | * FreeBSD's mcontext, what to do? |
| 196 | */ |
| 197 | } |
| 198 | cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); |
| 199 | |
| 200 | return err; |
| 201 | } |
| 202 | |
| 203 | /* Compare to arm/arm/machdep.c sys_sigreturn() */ |
| 204 | abi_long get_ucontext_sigreturn(CPUARMState *env, abi_ulong target_sf, |
| 205 | abi_ulong *target_uc) |
| 206 | { |
| 207 | *target_uc = target_sf; |
| 208 | |
| 209 | return 0; |
| 210 | } |