xref: /netbsd/sys/arch/mips/mips/syscall.c (revision 6550d01e)
1 /*	$NetBSD: syscall.c,v 1.42 2010/12/20 00:25:38 matt 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  *
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 /*
33  * Copyright (c) 1992, 1993
34  *	The Regents of the University of California.  All rights reserved.
35  *
36  * This code is derived from software contributed to Berkeley by
37  * the Systems Programming Group of the University of Utah Computer
38  * Science Department and Ralph Campbell.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  * from: Utah Hdr: trap.c 1.32 91/04/06
65  *
66  *	@(#)trap.c	8.5 (Berkeley) 1/11/94
67  */
68 /*
69  * Copyright (c) 1988 University of Utah.
70  *
71  * This code is derived from software contributed to Berkeley by
72  * the Systems Programming Group of the University of Utah Computer
73  * Science Department and Ralph Campbell.
74  *
75  * Redistribution and use in source and binary forms, with or without
76  * modification, are permitted provided that the following conditions
77  * are met:
78  * 1. Redistributions of source code must retain the above copyright
79  *    notice, this list of conditions and the following disclaimer.
80  * 2. Redistributions in binary form must reproduce the above copyright
81  *    notice, this list of conditions and the following disclaimer in the
82  *    documentation and/or other materials provided with the distribution.
83  * 3. All advertising materials mentioning features or use of this software
84  *    must display the following acknowledgement:
85  *	This product includes software developed by the University of
86  *	California, Berkeley and its contributors.
87  * 4. Neither the name of the University nor the names of its contributors
88  *    may be used to endorse or promote products derived from this software
89  *    without specific prior written permission.
90  *
91  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
92  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
93  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
94  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
95  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
96  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
97  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
98  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
99  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
100  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
101  * SUCH DAMAGE.
102  *
103  * from: Utah Hdr: trap.c 1.32 91/04/06
104  *
105  *	@(#)trap.c	8.5 (Berkeley) 1/11/94
106  */
107 
108 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
109 
110 __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.42 2010/12/20 00:25:38 matt Exp $");
111 
112 #if defined(_KERNEL_OPT)
113 #include "opt_sa.h"
114 #endif
115 
116 #include <sys/param.h>
117 #include <sys/systm.h>
118 #include <sys/endian.h>
119 #include <sys/proc.h>
120 #include <sys/signal.h>
121 #include <sys/syscall.h>
122 #include <sys/syscallvar.h>
123 #include <sys/sa.h>
124 #include <sys/savar.h>
125 
126 #include <uvm/uvm_extern.h>
127 
128 #include <machine/cpu.h>
129 #include <mips/trap.h>
130 #include <mips/reg.h>
131 #include <mips/regnum.h>			/* symbolic register indices */
132 #include <mips/userret.h>
133 
134 #ifndef EMULNAME
135 #define EMULNAME(x)	(x)
136 #endif
137 
138 #ifndef SYSCALL_SHIFT
139 #define SYSCALL_SHIFT 0
140 #endif
141 
142 void	EMULNAME(syscall_intern)(struct proc *);
143 static void EMULNAME(syscall)(struct lwp *, uint32_t, uint32_t, vaddr_t);
144 
145 register_t MachEmulateBranch(struct frame *, register_t, u_int, int);
146 
147 void
148 EMULNAME(syscall_intern)(struct proc *p)
149 {
150 	p->p_md.md_syscall = EMULNAME(syscall);
151 }
152 
153 /*
154  * Process a system call.
155  *
156  * System calls are strange beasts.  They are passed the syscall number
157  * in v0, and the arguments in the registers (as normal).  They return
158  * an error flag in a3 (if a3 != 0 on return, the syscall had an error),
159  * and the return value (if any) in v0 and possibly v1.
160  */
161 
162 void
163 EMULNAME(syscall)(struct lwp *l, u_int status, u_int cause, vaddr_t opc)
164 {
165 	struct proc *p = l->l_proc;
166 	struct frame *frame = l->l_md.md_regs;
167 	mips_reg_t *fargs = &frame->f_regs[_R_A0];
168 	register_t *args = NULL;
169 	register_t copyargs[2+SYS_MAXSYSARGS];
170 	mips_reg_t saved_v0;
171 	vaddr_t usp;
172 	size_t nargs;
173 	const struct sysent *callp;
174 	int code, error;
175 #if defined(__mips_o32)
176 	const int abi = _MIPS_BSD_API_O32;
177 	KASSERTMSG(p->p_md.md_abi == abi,
178 	    ("pid %d(%p): md_abi(%d) != abi(%d)",
179 	    p->p_pid, p, p->p_md.md_abi, abi));
180 	size_t nregs = 4;
181 #else
182 	const int abi = p->p_md.md_abi;
183 	size_t nregs = _MIPS_SIM_NEWABI_P(abi) ? 8 : 4;
184 	size_t i;
185 #endif
186 
187 	LWP_CACHE_CREDS(l, p);
188 
189 	curcpu()->ci_data.cpu_nsyscall++;
190 
191 	if (cause & MIPS_CR_BR_DELAY)
192 		frame->f_regs[_R_PC] = MachEmulateBranch(frame, opc, 0, 0);
193 	else
194 		frame->f_regs[_R_PC] = opc + sizeof(uint32_t);
195 
196 	callp = p->p_emul->e_sysent;
197 	saved_v0 = code = frame->f_regs[_R_V0];
198 
199 	code -= SYSCALL_SHIFT;
200 
201 #ifdef KERN_SA
202 	if (__predict_false((l->l_savp)
203             && (l->l_savp->savp_pflags & SAVP_FLAG_DELIVERING)))
204 		l->l_savp->savp_pflags &= ~SAVP_FLAG_DELIVERING;
205 #endif
206 
207 	if (code == SYS_syscall
208 	    || (code == SYS___syscall && abi != _MIPS_BSD_API_O32)) {
209 		/*
210 		 * Code is first argument, followed by actual args.
211 		 */
212 		code = *fargs++ - SYSCALL_SHIFT;
213 		nregs--;
214 	} else if (code == SYS___syscall) {
215 		/*
216 		 * Like syscall, but code is a quad, so as to maintain
217 		 * quad alignment for the rest of the arguments.
218 		 */
219 		code = fargs[_QUAD_LOWWORD] - SYSCALL_SHIFT;
220 		fargs += 2;
221 		nregs -= 2;
222 	}
223 
224 	if (code >= p->p_emul->e_nsysent)
225 		callp += p->p_emul->e_nosys;
226 	else
227 		callp += code;
228 
229 	nargs = callp->sy_narg;
230 	frame->f_regs[_R_V0] = 0;
231 #if !defined(__mips_o32)
232 	if (abi != _MIPS_BSD_API_O32) {
233 #endif
234 		CTASSERT(sizeof(copyargs[0]) == sizeof(fargs[0]));
235 		if (nargs <= nregs) {
236 			/*
237 			 * Just use the frame for the source of arguments
238 			 */
239 			args = fargs;
240 		} else {
241 			const size_t nsaved = _MIPS_SIM_NEWABI_P(abi) ? 0 : 4;
242 			KASSERT(nargs <= __arraycount(copyargs));
243 			args = copyargs;
244 			/*
245 			 * Copy the arguments passed via register from the
246 			 * trap frame to our argument array
247 			 */
248 			memcpy(copyargs, fargs, nregs * sizeof(register_t));
249 			/*
250 			 * Start copying args skipping the register slots
251 			 * slots on the stack.
252 			 */
253 			usp = frame->f_regs[_R_SP] + nsaved*sizeof(register_t);
254 			error = copyin((register_t *)usp, &copyargs[nregs],
255 			    (nargs - nregs) * sizeof(copyargs[0]));
256 			if (error)
257 				goto bad;
258 		}
259 #if !defined(__mips_o32)
260 	} else do {
261 		/*
262 		 * The only difference between O32 and N32 is the calling
263 		 * sequence.  If you make O32
264 		 */
265 		int32_t copy32args[SYS_MAXSYSARGS];
266 		int32_t *cargs = copy32args;
267 		unsigned int arg64mask = SYCALL_ARG_64_MASK(callp);
268 		bool doing_arg64;
269 		size_t narg64 = SYCALL_NARGS64(callp);
270 		/*
271 		 * All arguments are 32bits wide and 64bit arguments use
272 		 * two 32bit registers or stack slots.  We need to remarshall
273 		 * them into 64bit slots
274 		 */
275 		args = copyargs;
276 		CTASSERT(sizeof(copy32args[0]) != sizeof(fargs[0]));
277 
278 		/*
279 		 * If there are no 64bit arguments and all arguments were in
280 		 * registers, just use the frame for the source of arguments
281 		 */
282 		if (nargs <= nregs && narg64 == 0) {
283 			args = fargs;
284 			break;
285 		}
286 
287 		if (nregs <= nargs + narg64) {
288 			/*
289 			 * Grab the non-register arguments from the stack
290 			 * after skipping the slots for the 4 register passed
291 			 * arguments.
292 			 */
293 			usp = frame->f_regs[_R_SP] + 4*sizeof(int32_t);
294 			error = copyin((int32_t *)usp, copy32args,
295 			    (nargs + narg64 - nregs) * sizeof(copy32args[0]));
296 			if (error)
297 				goto bad;
298 		}
299 		/*
300 		 * Copy all the arguments to copyargs, starting with the ones
301 		 * in registers.  Using the hints in the 64bit argmask,
302 		 * we marshall the passed 32bit values into 64bit slots.  If we
303 		 * encounter a 64 bit argument, we grab two adjacent 32bit
304 		 * values and synthesize the 64bit argument.
305 		 */
306 		for (i = 0, doing_arg64 = false; i < nargs + narg64;) {
307 			register_t arg;
308 			if (nregs > 0) {
309 				arg = (int32_t) *fargs++;
310 				nregs--;
311 			} else {
312 				arg = *cargs++;
313 			}
314 			if (__predict_true((arg64mask & 1) == 0)) {
315 				/*
316 				 * Just copy it with sign extension on
317 				 */
318 				copyargs[i++] = (int32_t) arg;
319 				arg64mask >>= 1;
320 				continue;
321 			}
322 			/*
323 			 * 64bit arg.  grab the low 32 bits, discard the high.
324 			 */
325 			arg = (uint32_t)arg;
326 			if (!doing_arg64) {
327 				/*
328 				 * Pick up the 1st word of a 64bit arg.
329 				 * If lowword == 1 then highword == 0,
330 				 * so this is the highword and thus
331 				 * shifted left by 32, otherwise
332 				 * lowword == 0 and highword == 1 so
333 				 * it isn't shifted at all.  Remember
334 				 * we still need another word.
335 				 */
336 				doing_arg64 = true;
337 				copyargs[i] = arg << (_QUAD_LOWWORD*32);
338 				narg64--;	/* one less 64bit arg */
339 			} else {
340 				/*
341 				 * Pick up the 2nd word of a 64bit arg.
342 				 * if highword == 1, it's shifted left
343 				 * by 32, otherwise lowword == 1 and
344 				 * highword == 0 so it isn't shifted at
345 				 * all.  And now head to the next argument.
346 				 */
347 				doing_arg64 = false;
348 				copyargs[i++] |= arg << (_QUAD_HIGHWORD*32);
349 				arg64mask >>= 1;
350 			}
351 		}
352 	} while (/*CONSTCOND*/ 0);	/* avoid a goto */
353 #endif
354 
355 #ifdef MIPS_SYSCALL_DEBUG
356 	if (p->p_emul->e_syscallnames)
357 		printf("syscall %s:", p->p_emul->e_syscallnames[code]);
358 	else
359 		printf("syscall %u:", code);
360 	if (nargs == 0)
361 		printf(" <no args>");
362 	else for (size_t j = 0; j < nargs; j++) {
363 		if (j == nregs) printf(" *");
364 		printf(" [%s%zu]=%#"PRIxREGISTER,
365 		    SYCALL_ARG_64_P(callp, j) ? "+" : "",
366 		    j, args[j]);
367 	}
368 	printf("\n");
369 #endif
370 
371 	if (__predict_false(p->p_trace_enabled)
372 	    && (error = trace_enter(code, args, nargs)) != 0)
373 		goto out;
374 
375 	error = (*callp->sy_call)(l, args, &frame->f_regs[_R_V0]);
376 
377     out:
378 	switch (error) {
379 	case 0:
380 #if !defined(__mips_o32)
381 		if (abi == _MIPS_BSD_API_O32 && SYCALL_RET_64_P(callp)) {
382 			/*
383 			 * If this is from O32 and it's a 64bit quantity,
384 			 * split it into 2 32bit values in adjacent registers.
385 			 */
386 			mips_reg_t tmp = frame->f_regs[_R_V0];
387 			frame->f_regs[_R_V0 + _QUAD_LOWWORD] = (int32_t) tmp;
388 			frame->f_regs[_R_V0 + _QUAD_HIGHWORD] = tmp >> 32;
389 		}
390 #endif
391 #ifdef MIPS_SYSCALL_DEBUG
392 		if (p->p_emul->e_syscallnames)
393 			printf("syscall %s:", p->p_emul->e_syscallnames[code]);
394 		else
395 			printf("syscall %u:", code);
396 		printf(" return v0=%#"PRIxREGISTER" v1=%#"PRIxREGISTER"\n",
397 		    frame->f_regs[_R_V0], frame->f_regs[_R_V1]);
398 #endif
399 		frame->f_regs[_R_A3] = 0;
400 		break;
401 	case ERESTART:
402 		frame->f_regs[_R_V0] = saved_v0; /* restore syscall code */
403 		frame->f_regs[_R_PC] = opc;
404 		break;
405 	case EJUSTRETURN:
406 		break;	/* nothing to do */
407 	default:
408 	bad:
409 		if (p->p_emul->e_errno)
410 			error = p->p_emul->e_errno[error];
411 		frame->f_regs[_R_V0] = error;
412 		frame->f_regs[_R_A3] = 1;
413 #ifdef MIPS_SYSCALL_DEBUG
414 		if (p->p_emul->e_syscallnames)
415 			printf("syscall %s:", p->p_emul->e_syscallnames[code]);
416 		else
417 			printf("syscall %u:", code);
418 		printf(" return error=%d\n", error);
419 #endif
420 		break;
421 	}
422 
423 	if (__predict_false(p->p_trace_enabled))
424 		trace_exit(code, &frame->f_regs[_R_V0], error);
425 
426 	userret(l);
427 }
428