xref: /netbsd/sys/arch/mips/mips/syscall.c (revision c4a72b64)
1 /*	$NetBSD: syscall.c,v 1.13 2002/11/30 10:52:16 jdolecek Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe and by Charles M. Hannum.
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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright (c) 1988 University of Utah.
41  * Copyright (c) 1992, 1993
42  *	The Regents of the University of California.  All rights reserved.
43  *
44  * This code is derived from software contributed to Berkeley by
45  * the Systems Programming Group of the University of Utah Computer
46  * Science Department and Ralph Campbell.
47  *
48  * Redistribution and use in source and binary forms, with or without
49  * modification, are permitted provided that the following conditions
50  * are met:
51  * 1. Redistributions of source code must retain the above copyright
52  *    notice, this list of conditions and the following disclaimer.
53  * 2. Redistributions in binary form must reproduce the above copyright
54  *    notice, this list of conditions and the following disclaimer in the
55  *    documentation and/or other materials provided with the distribution.
56  * 3. All advertising materials mentioning features or use of this software
57  *    must display the following acknowledgement:
58  *	This product includes software developed by the University of
59  *	California, Berkeley and its contributors.
60  * 4. Neither the name of the University nor the names of its contributors
61  *    may be used to endorse or promote products derived from this software
62  *    without specific prior written permission.
63  *
64  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
65  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
66  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
67  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
68  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
69  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
70  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
71  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
72  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
73  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
74  * SUCH DAMAGE.
75  *
76  * from: Utah Hdr: trap.c 1.32 91/04/06
77  *
78  *	@(#)trap.c	8.5 (Berkeley) 1/11/94
79  */
80 
81 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
82 
83 __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.13 2002/11/30 10:52:16 jdolecek Exp $");
84 
85 #if defined(_KERNEL_OPT)
86 #include "opt_ktrace.h"
87 #include "opt_systrace.h"
88 #include "opt_syscall_debug.h"
89 #endif
90 
91 #include <sys/param.h>
92 #include <sys/systm.h>
93 #include <sys/proc.h>
94 #include <sys/user.h>
95 #include <sys/signal.h>
96 #ifdef KTRACE
97 #include <sys/ktrace.h>
98 #endif
99 #ifdef SYSTRACE
100 #include <sys/systrace.h>
101 #endif
102 #include <sys/syscall.h>
103 
104 #include <uvm/uvm_extern.h>
105 
106 #include <machine/cpu.h>
107 #include <mips/trap.h>
108 #include <mips/reg.h>
109 #include <mips/regnum.h>			/* symbolic register indices */
110 #include <mips/userret.h>
111 
112 #ifndef EMULNAME
113 #define EMULNAME(x)	(x)
114 #endif
115 
116 #ifndef SYSCALL_SHIFT
117 #define SYSCALL_SHIFT 0
118 #endif
119 
120 void	EMULNAME(syscall_intern)(struct proc *);
121 void	EMULNAME(syscall_plain)(struct proc *, u_int, u_int, u_int);
122 void	EMULNAME(syscall_fancy)(struct proc *, u_int, u_int, u_int);
123 
124 vaddr_t MachEmulateBranch(struct frame *, vaddr_t, u_int, int);
125 
126 #define DELAYBRANCH(x) ((int)(x)<0)
127 
128 void
129 EMULNAME(syscall_intern)(struct proc *p)
130 {
131 #ifdef KTRACE
132 	if (p->p_traceflag & (KTRFAC_SYSCALL | KTRFAC_SYSRET)) {
133 		p->p_md.md_syscall = EMULNAME(syscall_fancy);
134 		return;
135 	}
136 #endif
137 #ifdef SYSTRACE
138 	if (ISSET(p->p_flag, P_SYSTRACE)) {
139 		p->p_md.md_syscall = EMULNAME(syscall_fancy);
140 		return;
141 	}
142 #endif
143 	p->p_md.md_syscall = EMULNAME(syscall_plain);
144 }
145 
146 /*
147  * Process a system call.
148  *
149  * System calls are strange beasts.  They are passed the syscall number
150  * in v0, and the arguments in the registers (as normal).  They return
151  * an error flag in a3 (if a3 != 0 on return, the syscall had an error),
152  * and the return value (if any) in v0 and possibly v1.
153  *
154  * XXX Needs to be heavily rototilled for N32 or LP64 support.
155  */
156 
157 void
158 EMULNAME(syscall_plain)(struct proc *p, u_int status, u_int cause, u_int opc)
159 {
160 	struct frame *frame = (struct frame *)p->p_md.md_regs;
161 	register_t *args, copyargs[8];
162 	register_t *rval;
163 #if _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
164 	register_t copyrval[2];
165 #endif
166 	mips_reg_t ov0;
167 	size_t numsys, nsaved, nargs;
168 	const struct sysent *callp;
169 	int code, error;
170 
171 	uvmexp.syscalls++;
172 
173 	if (DELAYBRANCH(cause))
174 		frame->f_regs[PC] = MachEmulateBranch(frame, opc, 0, 0);
175 	else
176 		frame->f_regs[PC] = opc + sizeof(int);
177 
178 	callp = p->p_emul->e_sysent;
179 	numsys = p->p_emul->e_nsysent;
180 	ov0 = code = frame->f_regs[V0] - SYSCALL_SHIFT;
181 
182 	switch (code) {
183 	case SYS_syscall:
184 	case SYS___syscall:
185 		args = copyargs;
186 		if (code == SYS_syscall) {
187 			/*
188 			 * Code is first argument, followed by actual args.
189 			 */
190 			code = frame->f_regs[A0] - SYSCALL_SHIFT;
191 			args[0] = frame->f_regs[A1];
192 			args[1] = frame->f_regs[A2];
193 			args[2] = frame->f_regs[A3];
194 			nsaved = 3;
195 		} else {
196 			/*
197 			 * Like syscall, but code is a quad, so as to maintain
198 			 * quad alignment for the rest of the arguments.
199 			 */
200 			code = frame->f_regs[A0 + _QUAD_LOWWORD]
201 			    - SYSCALL_SHIFT;
202 			args[0] = frame->f_regs[A2];
203 			args[1] = frame->f_regs[A3];
204 			nsaved = 2;
205 		}
206 
207 		if (code >= p->p_emul->e_nsysent)
208 			callp += p->p_emul->e_nosys;
209 		else
210 			callp += code;
211 		nargs = callp->sy_argsize / sizeof(register_t);
212 
213 		if (nargs > nsaved) {
214 			error = copyin(
215 			    ((register_t *)(vaddr_t)frame->f_regs[SP] + 4),
216 			    (args + nsaved),
217 			    (nargs - nsaved) * sizeof(register_t));
218 			if (error)
219 				goto bad;
220 		}
221 		break;
222 
223 	default:
224 		if (code >= p->p_emul->e_nsysent)
225 			callp += p->p_emul->e_nosys;
226 		else
227 			callp += code;
228 		nargs = callp->sy_narg;
229 
230 		if (nargs < 5) {
231 #if !defined(_MIPS_BSD_API) || _MIPS_BSD_API == _MIPS_BSD_API_LP32
232 			args = (register_t *)&frame->f_regs[A0];
233 #elif _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
234 			args = copyargs;
235 			args[0] = frame->f_regs[A0];
236 			args[1] = frame->f_regs[A1];
237 			args[2] = frame->f_regs[A2];
238 			args[3] = frame->f_regs[A3];
239 #else
240 # error syscall not implemented for current MIPS ABI
241 #endif
242 		} else {
243 			args = copyargs;
244 			error = copyin(
245 			    ((register_t *)(vaddr_t)frame->f_regs[SP] + 4),
246 			    (&copyargs[4]),
247 			    (nargs - 4) * sizeof(register_t));
248 			if (error)
249 				goto bad;
250 			args[0] = frame->f_regs[A0];
251 			args[1] = frame->f_regs[A1];
252 			args[2] = frame->f_regs[A2];
253 			args[3] = frame->f_regs[A3];
254 		}
255 		break;
256 	}
257 
258 #ifdef SYSCALL_DEBUG
259 	scdebug_call(p, code, (register_t *)args);
260 #endif
261 
262 #if !defined(_MIPS_BSD_API) || _MIPS_BSD_API == _MIPS_BSD_API_LP32
263 	rval = (register_t *)&frame->f_regs[V0];
264 	rval[0] = 0;
265 	/* rval[1] already has V1 */
266 #elif _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
267 	rval = copyrval;
268 	rval[0] = 0;
269 	rval[1] = frame->f_regs[V1];
270 #endif
271 
272 	error = (*callp->sy_call)(p, args, rval);
273 
274 	switch (error) {
275 	case 0:
276 #if _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
277 		frame->f_regs[V0] = rval[0];
278 		frame->f_regs[V1] = rval[1];
279 #endif
280 		frame->f_regs[A3] = 0;
281 		break;
282 	case ERESTART:
283 		frame->f_regs[V0] = ov0;	/* restore syscall code */
284 		frame->f_regs[PC] = opc;
285 		break;
286 	case EJUSTRETURN:
287 		break;	/* nothing to do */
288 	default:
289 	bad:
290 		if (p->p_emul->e_errno)
291 			error = p->p_emul->e_errno[error];
292 		frame->f_regs[V0] = error;
293 		frame->f_regs[A3] = 1;
294 		break;
295 	}
296 
297 #ifdef SYSCALL_DEBUG
298 	scdebug_ret(p, code, error, rval);
299 #endif
300 
301 	userret(p);
302 }
303 
304 void
305 EMULNAME(syscall_fancy)(struct proc *p, u_int status, u_int cause, u_int opc)
306 {
307 	struct frame *frame = (struct frame *)p->p_md.md_regs;
308 	register_t *args, copyargs[8];
309 	register_t *rval;
310 #if _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
311 	register_t copyrval[2];
312 #endif
313 	mips_reg_t ov0;
314 	size_t numsys, nsaved, nargs;
315 	const struct sysent *callp;
316 	int code, error;
317 
318 	uvmexp.syscalls++;
319 
320 	if (DELAYBRANCH(cause))
321 		frame->f_regs[PC] = MachEmulateBranch(frame, opc, 0, 0);
322 	else
323 		frame->f_regs[PC] = opc + sizeof(int);
324 
325 	callp = p->p_emul->e_sysent;
326 	numsys = p->p_emul->e_nsysent;
327 	ov0 = code = frame->f_regs[V0] - SYSCALL_SHIFT;
328 
329 	switch (code) {
330 	case SYS_syscall:
331 	case SYS___syscall:
332 		args = copyargs;
333 		if (code == SYS_syscall) {
334 			/*
335 			 * Code is first argument, followed by actual args.
336 			 */
337 			code = frame->f_regs[A0] - SYSCALL_SHIFT;
338 			args[0] = frame->f_regs[A1];
339 			args[1] = frame->f_regs[A2];
340 			args[2] = frame->f_regs[A3];
341 			nsaved = 3;
342 		} else {
343 			/*
344 			 * Like syscall, but code is a quad, so as to maintain
345 			 * quad alignment for the rest of the arguments.
346 			 */
347 			code = frame->f_regs[A0 + _QUAD_LOWWORD]
348 			    - SYSCALL_SHIFT;
349 			args[0] = frame->f_regs[A2];
350 			args[1] = frame->f_regs[A3];
351 			nsaved = 2;
352 		}
353 
354 		if (code >= p->p_emul->e_nsysent)
355 			callp += p->p_emul->e_nosys;
356 		else
357 			callp += code;
358 		nargs = callp->sy_argsize / sizeof(register_t);
359 
360 		if (nargs > nsaved) {
361 			error = copyin(
362 			    ((register_t *)(vaddr_t)frame->f_regs[SP] + 4),
363 			    (args + nsaved),
364 			    (nargs - nsaved) * sizeof(register_t));
365 			if (error)
366 				goto bad;
367 		}
368 		break;
369 
370 	default:
371 		if (code >= p->p_emul->e_nsysent)
372 			callp += p->p_emul->e_nosys;
373 		else
374 			callp += code;
375 		nargs = callp->sy_narg;
376 
377 		if (nargs < 5) {
378 #if !defined(_MIPS_BSD_API) || _MIPS_BSD_API == _MIPS_BSD_API_LP32
379 			args = (register_t *)&frame->f_regs[A0];
380 #elif _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
381 			args = copyargs;
382 			args[0] = frame->f_regs[A0];
383 			args[1] = frame->f_regs[A1];
384 			args[2] = frame->f_regs[A2];
385 			args[3] = frame->f_regs[A3];
386 #else
387 # error syscall not implemented for current MIPS ABI
388 #endif
389 		} else {
390 			args = copyargs;
391 			error = copyin(
392 			    ((register_t *)(vaddr_t)frame->f_regs[SP] + 4),
393 			    (&copyargs[4]),
394 			    (nargs - 4) * sizeof(register_t));
395 			if (error)
396 				goto bad;
397 			args[0] = frame->f_regs[A0];
398 			args[1] = frame->f_regs[A1];
399 			args[2] = frame->f_regs[A2];
400 			args[3] = frame->f_regs[A3];
401 		}
402 		break;
403 	}
404 
405 	if ((error = trace_enter(p, code, code, args, rval)) != 0)
406 		goto bad;
407 
408 #if !defined(_MIPS_BSD_API) || _MIPS_BSD_API == _MIPS_BSD_API_LP32
409 	rval = (register_t *)&frame->f_regs[V0];
410 	rval[0] = 0;
411 	/* rval[1] already has V1 */
412 #elif _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
413 	rval = copyrval;
414 	rval[0] = 0;
415 	rval[1] = frame->f_regs[V1];
416 #endif
417 
418 	error = (*callp->sy_call)(p, args, rval);
419 
420 	switch (error) {
421 	case 0:
422 #if _MIPS_BSD_API == _MIPS_BSD_API_LP32_64CLEAN
423 		frame->f_regs[V0] = rval[0];
424 		frame->f_regs[V1] = rval[1];
425 #endif
426 		frame->f_regs[A3] = 0;
427 		break;
428 	case ERESTART:
429 		frame->f_regs[V0] = ov0;	/* restore syscall code */
430 		frame->f_regs[PC] = opc;
431 		break;
432 	case EJUSTRETURN:
433 		break;	/* nothing to do */
434 	default:
435 	bad:
436 		if (p->p_emul->e_errno)
437 			error = p->p_emul->e_errno[error];
438 		frame->f_regs[V0] = error;
439 		frame->f_regs[A3] = 1;
440 		break;
441 	}
442 
443 	trace_exit(p, code, args, rval, error);
444 
445 	userret(p);
446 }
447