1 /* $NetBSD: syscall.c,v 1.6 2024/11/22 20:01:04 skrll 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 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.6 2024/11/22 20:01:04 skrll Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/cpu.h> 36 #include <sys/endian.h> 37 #include <sys/proc.h> 38 #include <sys/signal.h> 39 #include <sys/systm.h> 40 41 #include <riscv/frame.h> 42 #include <riscv/userret.h> 43 44 #ifndef EMULNAME 45 #define EMULNAME(x) (x) 46 #include <sys/syscall.h> 47 #include <sys/syscallvar.h> 48 #endif 49 50 #ifndef SYSCALL_SHIFT 51 #define SYSCALL_SHIFT 0 52 #endif 53 54 void EMULNAME(syscall_intern)(struct proc *); 55 static void EMULNAME(syscall)(struct trapframe *); 56 57 __CTASSERT(EMULNAME(SYS_MAXSYSARGS) <= 8); 58 59 void 60 EMULNAME(syscall_intern)(struct proc *p) 61 { 62 p->p_md.md_syscall = EMULNAME(syscall); 63 } 64 65 #define INSN_SIZE 4 66 /* 67 * Process a system call. 68 * 69 * System calls are strange beasts. They are passed the syscall number 70 * in t6, and the arguments in the registers (as normal). 71 * The return value (if any) in a0 and possibly a1. The instruction 72 * directly after the syscall is excepted to contain a jump instruction 73 * for an error handler. If the syscall completes with no error, the PC 74 * will be advanced past that instruction. 75 */ 76 77 void 78 EMULNAME(syscall)(struct trapframe *tf) 79 { 80 struct lwp * const l = curlwp; 81 struct proc * const p = l->l_proc; 82 register_t *args = &tf->tf_a0; 83 register_t retval[2]; 84 const struct sysent *callp; 85 int code, error; 86 #ifdef _LP64 87 const bool pk32_p = (p->p_flag & PK_32) != 0; 88 register_t copyargs[EMULNAME(SYS_MAXSYSARGS)]; 89 #endif 90 91 curcpu()->ci_data.cpu_nsyscall++; 92 93 callp = p->p_emul->e_sysent; 94 code = tf->tf_t6 - SYSCALL_SHIFT; 95 96 /* 97 * Userland should have taken care of moving everything to their 98 * usual place so these code's should never get to the kernel. 99 */ 100 if (code == SYS_syscall || code == SYS___syscall) { 101 #ifdef RISCV_SYSCALL_DEBUG 102 printf("syscall: _syscall code %#"PRIxREGISTER"\n", tf->tf_a0); 103 #endif 104 /* XXX 32-bit vs 64-bit handling?? */ 105 code = *args; /* syscall num is first arg */ 106 args++; /* shuffle args */ 107 } 108 109 if (code >= p->p_emul->e_nsysent) 110 callp += p->p_emul->e_nosys; 111 else 112 callp += code; 113 114 #ifdef _LP64 115 const size_t nargs = callp->sy_narg; 116 /* 117 * If there are no 64bit arguments, we still need "sanitize" the 118 * 32-bit arguments in case they try to slip through a 64-bit pointer. 119 * and all arguments were in 120 * registers, just use the trapframe for the source of arguments 121 */ 122 if (pk32_p) { 123 size_t narg64 = SYCALL_NARGS64(callp); 124 unsigned int arg64mask = SYCALL_ARG_64_MASK(callp); 125 bool doing_arg64 = false; 126 register_t *args32 = args; 127 128 /* 129 * All arguments are 32bits wide and if we have 64bit arguments 130 * then use two 32bit registers to construct a 64bit argument. 131 * We remarshall them into 64bit slots but we don't want to 132 * disturb the original arguments in case we get restarted. 133 */ 134 if (SYCALL_NARGS64(callp) > 0) { 135 args = copyargs; 136 } 137 138 /* 139 * Copy all the arguments to copyargs, starting with the ones 140 * in registers. Using the hints in the 64bit argmask, 141 * we marshall the passed 32bit values into 64bit slots. If we 142 * encounter a 64 bit argument, we grab two adjacent 32bit 143 * values and synthesize the 64bit argument. 144 */ 145 for (size_t i = 0; i < nargs + narg64; ) { 146 register_t arg = *args32++; 147 if (__predict_true((arg64mask & 1) == 0)) { 148 /* 149 * Just copy it with sign extension on 150 */ 151 args[i++] = (int32_t) arg; 152 arg64mask >>= 1; 153 continue; 154 } 155 /* 156 * 64bit arg. grab the low 32 bits, discard the high. 157 */ 158 arg = (uint32_t)arg; 159 if (!doing_arg64) { 160 /* 161 * Pick up the 1st word of a 64bit arg. 162 * If lowword == 1 then highword == 0, 163 * so this is the highword and thus 164 * shifted left by 32, otherwise 165 * lowword == 0 and highword == 1 so 166 * it isn't shifted at all. Remember 167 * we still need another word. 168 */ 169 doing_arg64 = true; 170 args[i] = arg << (_QUAD_LOWWORD*32); 171 narg64--; /* one less 64bit arg */ 172 } else { 173 /* 174 * Pick up the 2nd word of a 64bit arg. 175 * if highword == 1, it's shifted left 176 * by 32, otherwise lowword == 1 and 177 * highword == 0 so it isn't shifted at 178 * all. And now head to the next argument. 179 */ 180 doing_arg64 = false; 181 args[i++] |= arg << (_QUAD_HIGHWORD*32); 182 arg64mask >>= 1; 183 } 184 } 185 } 186 #endif 187 188 #ifdef RISCV_SYSCALL_DEBUG 189 if (p->p_emul->e_syscallnames) 190 printf("syscall %s:", p->p_emul->e_syscallnames[code]); 191 else 192 printf("syscall %u:", code); 193 if (nargs == 0) 194 printf(" <no args>"); 195 else for (size_t j = 0; j < nargs; j++) { 196 printf(" [%s%zu]=%#"PRIxREGISTER, 197 SYCALL_ARG_64_P(callp, j) ? "+" : "", 198 j, args[j]); 199 } 200 printf("\n"); 201 #endif 202 /* 203 * Assume success and fixup pc to point after the ecall and jump 204 * to cerror. fork, and friends, expect this. 205 */ 206 tf->tf_pc += 207 INSN_SIZE + // ecall 208 INSN_SIZE * 2; // jump to cerror (or nops) 209 210 error = sy_invoke(callp, l, args, retval, code); 211 212 switch (error) { 213 case 0: 214 #ifdef _LP64 215 #if 0 216 if (pk32_p && SYCALL_RET_64_P(callp)) { 217 218 /* 219 * If this is from O32 and it's a 64bit quantity, 220 * split it into 2 32bit values in adjacent registers. 221 */ 222 register_t tmp = retval[0]; 223 tf->tf_reg[_X_A0 + _QUAD_LOWWORD] = (int32_t) tmp; 224 tf->tf_reg[_X_A0 + _QUAD_HIGHWORD] = tmp >> 32; 225 } 226 #endif 227 #endif 228 229 tf->tf_a0 = retval[0]; 230 tf->tf_a1 = retval[1]; 231 232 #ifdef RISCV_SYSCALL_DEBUG 233 if (p->p_emul->e_syscallnames) 234 printf("syscall %s:", p->p_emul->e_syscallnames[code]); 235 else 236 printf("syscall %u:", code); 237 printf(" return a0=%#"PRIxREGISTER" a1=%#"PRIxREGISTER" to" 238 " %#"PRIxREGISTER "\n", 239 tf->tf_a0, tf->tf_a1, tf->tf_pc); 240 #endif 241 break; 242 case ERESTART: 243 tf->tf_pc -= 244 INSN_SIZE + // ecall 245 INSN_SIZE * 2; // jump to cerror (or nops) 246 break; 247 case EJUSTRETURN: 248 break; 249 default: 250 if (p->p_emul->e_errno) 251 error = p->p_emul->e_errno[error]; 252 tf->tf_a0 = error; 253 tf->tf_pc -= INSN_SIZE * 2; // jump to cerror. 254 #ifdef RISCV_SYSCALL_DEBUG 255 if (p->p_emul->e_syscallnames) 256 printf("syscall %s:", p->p_emul->e_syscallnames[code]); 257 else 258 printf("syscall %u:", code); 259 printf(" return error=%d to %#" PRIxREGISTER "\n", error, tf->tf_pc); 260 #endif 261 break; 262 } 263 264 KASSERT(l->l_blcnt == 0); 265 KASSERT(curcpu()->ci_biglock_count == 0); 266 267 userret(l); 268 } 269