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