1 /* $NetBSD: aarch32_syscall.c,v 1.9 2024/02/07 04:20:26 msaitoh Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Ryo Shimizu 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: aarch32_syscall.c,v 1.9 2024/02/07 04:20:26 msaitoh Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/ktrace.h> 34 #include <sys/proc.h> 35 #include <sys/syscallvar.h> 36 #include <uvm/uvm_extern.h> 37 38 #include <aarch64/armreg.h> 39 #include <aarch64/frame.h> 40 #include <aarch64/machdep.h> 41 #include <aarch64/userret.h> 42 43 #ifndef EMULNAME 44 #error EMULNAME is not defined 45 #endif 46 47 #ifndef NARGREG 48 #define NARGREG 4 /* 4 args are in registers */ 49 #endif 50 51 static void EMULNAME(syscall)(struct trapframe *); 52 53 union args { 54 register_t a64[EMULNAMEU(SYS_MAXSYSARGS)]; 55 register32_t a32[EMULNAMEU(SYS_MAXSYSARGS)]; 56 }; 57 58 void 59 EMULNAME(syscall)(struct trapframe *tf) 60 { 61 struct lwp * const l = curlwp; 62 struct proc * const p = l->l_proc; 63 const struct sysent *callp; 64 union args args64buf, args32buf; 65 register_t rval[2]; 66 register32_t *args32 = args32buf.a32; 67 int error, i; 68 bool do_trace, thumbmode; 69 70 curcpu()->ci_data.cpu_nsyscall++; /* XXX unsafe curcpu() */ 71 72 thumbmode = (tf->tf_spsr & SPSR_A32_T) ? true : false; 73 #ifdef SYSCALL_CODE_REG 74 /* 75 * mov.w r<SYSCALL_CODE_REG>, #<syscall_no> 76 * svc #<SYSCALL_CODE_REG_SVC> 77 */ 78 #ifdef SYSCALL_CODE_REG_SVC 79 if ((tf->tf_esr & 0xffff) != SYSCALL_CODE_REG_SVC) { 80 error = EINVAL; 81 goto bad; 82 } 83 #endif 84 uint32_t code = tf->tf_reg[SYSCALL_CODE_REG]; 85 #if (SYSCALL_CODE_REG == 0) 86 int regstart = 1; /* args start from r1 */ 87 int nargs_reg = NARGREG - 1; /* number of argument in registers */ 88 #else 89 int regstart = 0; /* args start from r0 */ 90 int nargs_reg = NARGREG; /* number of argument in registers */ 91 #endif 92 #else /* SYSCALL_CODE_REG */ 93 uint32_t code = tf->tf_esr & 0xffff; /* XXX: 16-23bits are omitted */ 94 if (thumbmode) { 95 if (code != 255) { 96 do_trapsignal(l, SIGILL, ILL_ILLTRP, 97 (void *)(tf->tf_pc - 2), tf->tf_esr); 98 error = EINVAL; 99 goto bad; 100 } 101 code = tf->tf_reg[0]; 102 tf->tf_reg[0] = tf->tf_reg[12]; /* orig $r0 is saved to $ip */ 103 } 104 int regstart = 0; /* args start from r0 */ 105 int nargs_reg = NARGREG; /* number of argument in registers */ 106 #endif /* SYSCALL_CODE_REG */ 107 108 #ifdef SYSCALL_CODE_REMAP 109 code = SYSCALL_CODE_REMAP(code); 110 #endif 111 112 code %= EMULNAMEU(SYS_NSYSENT); 113 callp = p->p_emul->e_sysent + code; 114 #ifndef SYSCALL_NO_INDIRECT 115 if (__predict_false(callp->sy_flags & SYCALL_INDIRECT)) { 116 int off = 1; 117 #ifdef NETBSD32_SYS_netbsd32____syscall /* XXX ugly: apply only for NETBSD32 */ 118 /* 119 * For __syscall(2), 1st argument is quad_t, which is 120 * stored in r0 and r1. 121 */ 122 if (code == NETBSD32_SYS_netbsd32____syscall) 123 off = 2; 124 #endif 125 nargs_reg -= off; 126 regstart = off; /* args start from r1 or r2 */ 127 #ifdef __AARCH64EB__ 128 if (off == 2) 129 code = tf->tf_reg[1]; 130 else 131 #endif 132 code = tf->tf_reg[0]; 133 code %= EMULNAMEU(SYS_NSYSENT); 134 callp = p->p_emul->e_sysent + code; 135 136 /* don't allow nested syscall */ 137 if (__predict_false(callp->sy_flags & SYCALL_INDIRECT)) { 138 error = EINVAL; 139 goto bad; 140 } 141 } 142 #endif /* SYSCALL_NO_INDIRECT */ 143 144 /* number of argument to fetch from sp */ 145 KASSERT(callp->sy_narg <= EMULNAMEU(SYS_MAXSYSARGS)); 146 int nargs_sp = callp->sy_narg - nargs_reg; 147 148 /* fetch arguments from tf and sp, and store to args32buf[] */ 149 for (i = 0; i < nargs_reg; i++) 150 *args32++ = (uint32_t)tf->tf_reg[regstart++]; 151 if (nargs_sp > 0) { 152 error = copyin( 153 (void*)(uintptr_t)(uint32_t)tf->tf_reg[13], /* sp = r13 */ 154 args32, nargs_sp * sizeof(register32_t)); 155 if (error) 156 goto bad; 157 } 158 159 rval[0] = 0; 160 rval[1] = tf->tf_reg[1]; 161 #if 0 162 error = sy_invoke(callp, l, args32buf.a32, rval, code); 163 #else 164 /* 165 * XXX: trace_enter()/trace_exit() called from sy_invoke() expects 166 * 64bit args, but sy_invoke doesn't take care of it. 167 * therefore call trace_enter(), sy_call(), trace_exit() manually. 168 */ 169 #ifdef KDTRACE_HOOKS 170 #define KDTRACE_ENTRY(a) (a) 171 #else 172 #define KDTRACE_ENTRY(a) (0) 173 #endif 174 do_trace = p->p_trace_enabled && 175 ((callp->sy_flags & SYCALL_INDIRECT) == 0); 176 if (__predict_false(do_trace || 177 KDTRACE_ENTRY(callp->sy_entry) || 178 KDTRACE_ENTRY(callp->sy_return))) { 179 /* build 64bit args for trace_enter()/trace_exit() */ 180 int nargs = callp->sy_narg; 181 for (i = 0; i < nargs; i++) 182 args64buf.a64[i] = args32buf.a32[i]; 183 } 184 185 if (__predict_false(do_trace || KDTRACE_ENTRY(callp->sy_entry))) 186 error = trace_enter(code, callp, args64buf.a64); 187 188 if (error == 0) 189 error = sy_call(callp, l, args32buf.a32, rval); 190 191 if (__predict_false(do_trace || KDTRACE_ENTRY(callp->sy_return))) 192 trace_exit(code, callp, args64buf.a64, rval, error); 193 #endif 194 195 if (__predict_true(error == 0)) { 196 tf->tf_reg[0] = rval[0]; 197 #ifndef SYSCALL_NO_RVAL1 198 tf->tf_reg[1] = rval[1]; 199 #endif 200 tf->tf_spsr &= ~NZCV_C; 201 } else { 202 switch (error) { 203 case ERESTART: 204 /* redo system call insn */ 205 if (thumbmode) 206 tf->tf_pc -= 2; 207 else 208 tf->tf_pc -= 4; 209 break; 210 case EJUSTRETURN: 211 /* nothing to do */ 212 break; 213 default: 214 bad: 215 #ifndef __HAVE_MINIMAL_EMUL 216 if (p->p_emul->e_errno) 217 error = p->p_emul->e_errno[error]; 218 #elif defined(SYSCALL_EMUL_ERRNO) 219 error = SYSCALL_EMUL_ERRNO(error); 220 #endif 221 tf->tf_reg[0] = error; 222 tf->tf_spsr |= NZCV_C; 223 break; 224 } 225 } 226 227 userret(l); 228 } 229 230 void EMULNAME(syscall_intern)(struct proc *); 231 232 void 233 EMULNAME(syscall_intern)(struct proc *p) 234 { 235 p->p_md.md_syscall = EMULNAME(syscall); 236 } 237