1 /* $NetBSD: syscall.c,v 1.1 2014/08/10 05:47:37 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matt Thomas of 3am Software Foundry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "opt_multiprocessor.h" 33 /* DO NOT INCLUDE opt_compat_XXX.h */ 34 35 #include <sys/param.h> 36 #include <sys/cpu.h> 37 #include <sys/ktrace.h> 38 #include <sys/proc.h> 39 #include <sys/reboot.h> 40 #include <sys/systm.h> 41 #include <sys/syscallvar.h> 42 43 #include <uvm/uvm_extern.h> 44 45 #include <aarch64/locore.h> 46 47 #ifndef NARGREG 48 #define NARGREG 8 /* 8 args are in registers */ 49 #endif 50 #define MOREARGS(sp) ((const void *)(uintptr_t)(sp)) /* more args go here */ 51 #ifndef REGISTER_T 52 #define REGISTER_T register_t 53 #endif 54 55 #ifndef EMULNAME 56 #include <sys/syscall.h> 57 58 #define EMULNAME(x) (x) 59 #define EMULNAMEU(x) (x) 60 61 __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.1 2014/08/10 05:47:37 matt Exp $"); 62 63 void 64 cpu_spawn_return(struct lwp *l) 65 { 66 userret(l, l->l_md.md_utf); 67 } 68 69 void 70 child_return(void *arg) 71 { 72 struct lwp * const l = arg; 73 struct trapframe * const tf = l->l_md.md_utf; 74 75 tf->tf_reg[0] = 0; 76 tf->tf_reg[1] = 1; 77 tf->tf_spsr &= ~NZCV_C; 78 l->l_md.md_cpacr = CPACR_FPEN_NONE; 79 80 ktrsysret(SYS_fork, 0, 0); 81 /* Profiling? XXX */ 82 } 83 #endif 84 85 static void EMULNAME(syscall)(struct trapframe *); 86 87 void 88 EMULNAME(syscall)(struct trapframe *tf) 89 { 90 struct lwp * const l = curlwp; 91 struct proc * const p = l->l_proc; 92 register_t rval[2]; 93 register_t args[10]; 94 int error; 95 96 LWP_CACHE_CREDS(l, p); 97 98 curcpu()->ci_data.cpu_nsyscall++; 99 100 size_t code = tf->tf_esr & 0xffff; 101 register_t *params = tf->tf_reg; 102 size_t nargs = NARGREG; 103 104 switch (code) { 105 case EMULNAMEU(SYS_syscall): 106 #if !defined(COMPAT_LINUX) 107 case EMULNAMEU(SYS___syscall): 108 code = tf->tf_reg[17]; 109 #else 110 code = *params++; 111 nargs -= 1; 112 #endif 113 /* 114 * code is first argument, 115 * followed by actual args. 116 */ 117 break; 118 default: 119 break; 120 } 121 122 code &= EMULNAMEU(SYS_NSYSENT) - 1; 123 const struct sysent * const callp = p->p_emul->e_sysent + code; 124 125 if (__predict_false(callp->sy_narg > nargs)) { 126 const size_t diff = callp->sy_narg - nargs; 127 memcpy(args, params, nargs * sizeof(params[0])); 128 if (sizeof(params[0]) == sizeof(REGISTER_T)) { 129 error = copyin(MOREARGS(tf->tf_sp), &args[nargs], 130 diff * sizeof(register_t)); 131 if (error) 132 goto bad; 133 } else { 134 /* 135 * If the register_t used by the process isn't the 136 * as the one used by the kernel, we can't directly 137 * copy the arguments off the stack into args. We 138 * need to buffer them in a REGISTER_T array and 139 * then move them individually into args. 140 */ 141 REGISTER_T args32[10]; 142 error = copyin(MOREARGS(tf->tf_sp), args32, 143 diff * sizeof(REGISTER_T)); 144 if (error) 145 goto bad; 146 for (size_t i = 0; i < diff; i++) { 147 args[nargs + i] = args32[i]; 148 } 149 } 150 params = args; 151 } 152 153 #ifdef __AARCH64EB__ 154 #define SYCALL_ARG_64(a, b) (((register_t) (a) << 32) | (uint32_t)(b)) 155 #else 156 #define SYCALL_ARG_64(a, b) (((register_t) (b) << 32) | (uint32_t)(a)) 157 #endif 158 159 /* 160 * If the syscall used a different (smaller) register size, 161 * reconstruct the 64-bit arguments from two 32-bit registers. 162 */ 163 if (__predict_false(sizeof(register_t) != sizeof(REGISTER_T) 164 && SYCALL_NARGS64(callp) > 0)) { 165 for (size_t i = 0, j = 0; i < callp->sy_narg; i++, j++) { 166 if (SYCALL_ARG_64_P(callp, i)) { 167 register_t *inp = ¶ms[j]; 168 args[i] = SYCALL_ARG_64(inp[0], inp[1]); 169 j++; 170 } else if (i != j) { 171 args[i] = params[j]; 172 } 173 } 174 params = args; 175 } 176 177 error = sy_invoke(callp, l, params, rval, code); 178 179 if (__predict_true(error == 0)) { 180 if (__predict_false(sizeof(register_t) != sizeof(REGISTER_T) 181 && SYCALL_RET_64_P(callp))) { 182 #ifdef __AARCH64EB__ 183 tf->tf_reg[0] = (uint32_t) (rval[0] >> 32); 184 tf->tf_reg[1] = (uint32_t) (rval[0] >> 0); 185 #else 186 tf->tf_reg[0] = (uint32_t) (rval[0] >> 0); 187 tf->tf_reg[1] = (uint32_t) (rval[0] >> 32); 188 #endif 189 } else { 190 tf->tf_reg[0] = rval[0]; 191 tf->tf_reg[1] = rval[1]; 192 } 193 tf->tf_spsr &= ~NZCV_C; 194 } else { 195 switch (error) { 196 case ERESTART: 197 /* 198 * Set user's pc back to redo the system call. 199 */ 200 tf->tf_pc -= 4; 201 break; 202 case EJUSTRETURN: 203 /* nothing to do */ 204 break; 205 default: 206 bad: 207 #ifndef __HAVE_MINIMAL_EMUL 208 if (p->p_emul->e_errno) 209 error = p->p_emul->e_errno[error]; 210 #endif 211 tf->tf_reg[0] = error; 212 tf->tf_spsr |= NZCV_C; 213 break; 214 } 215 } 216 217 userret(l, tf); 218 } 219 220 void EMULNAME(syscall_intern)(struct proc *); 221 222 void 223 EMULNAME(syscall_intern)(struct proc *p) 224 { 225 226 p->p_md.md_syscall = EMULNAME(syscall); 227 } 228