syscall.c revision 1.25
1/*	$NetBSD: syscall.c,v 1.25 2005/12/11 12:18:46 christos Exp $	*/
2
3/*
4 * Copyright (C) 2002 Matt Thomas
5 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
6 * Copyright (C) 1995, 1996 TooLs GmbH.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by TooLs GmbH.
20 * 4. The name of TooLs GmbH may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include "opt_altivec.h"
36#include "opt_ktrace.h"
37#include "opt_systrace.h"
38#include "opt_multiprocessor.h"
39/* DO NOT INCLUDE opt_compat_XXX.h */
40/* If needed, they will be included by file that includes this one */
41
42#include <sys/param.h>
43#include <sys/proc.h>
44#include <sys/reboot.h>
45#include <sys/systm.h>
46#include <sys/user.h>
47#include <sys/sa.h>
48#include <sys/savar.h>
49#ifdef KTRACE
50#include <sys/ktrace.h>
51#endif
52#ifdef SYSTRACE
53#include <sys/systrace.h>
54#endif
55
56#include <uvm/uvm_extern.h>
57
58#include <powerpc/userret.h>
59#include <machine/cpu.h>
60#include <machine/frame.h>
61
62#define	FIRSTARG	3		/* first argument is in reg 3 */
63#define	NARGREG		8		/* 8 args are in registers */
64#define	MOREARGS(sp)	((caddr_t)((uintptr_t)(sp) + 8)) /* more args go here */
65
66#ifndef EMULNAME
67#include <sys/syscall.h>
68
69#define EMULNAME(x)	(x)
70#define EMULNAMEU(x)	(x)
71
72__KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.25 2005/12/11 12:18:46 christos Exp $");
73
74void
75child_return(void *arg)
76{
77	struct lwp * const l = arg;
78#ifdef KTRACE
79	struct proc * const p = l->l_proc;
80#endif
81	struct trapframe * const tf = trapframe(l);
82
83	KERNEL_PROC_UNLOCK(l);
84
85	tf->fixreg[FIRSTARG] = 0;
86	tf->fixreg[FIRSTARG + 1] = 1;
87	tf->cr &= ~0x10000000;
88	tf->srr1 &= ~(PSL_FP|PSL_VEC);	/* Disable FP & AltiVec, as we can't
89					   be them. */
90	l->l_addr->u_pcb.pcb_fpcpu = NULL;
91#ifdef	KTRACE
92	if (KTRPOINT(p, KTR_SYSRET)) {
93		KERNEL_PROC_LOCK(l);
94		ktrsysret(l, SYS_fork, 0, 0);
95		KERNEL_PROC_UNLOCK(l);
96	}
97#endif
98	/* Profiling?							XXX */
99	curcpu()->ci_schedstate.spc_curpriority = l->l_priority;
100}
101#endif
102
103static void EMULNAME(syscall_plain)(struct trapframe *);
104
105void
106EMULNAME(syscall_plain)(struct trapframe *frame)
107{
108	struct lwp *l = curlwp;
109	struct proc *p = l->l_proc;
110	const struct sysent *callp;
111	size_t argsize;
112	register_t code;
113	register_t *params, rval[2];
114	register_t args[10];
115	int error;
116	int n;
117
118	curcpu()->ci_ev_scalls.ev_count++;
119
120	code = frame->fixreg[0];
121	params = frame->fixreg + FIRSTARG;
122	n = NARGREG;
123
124#ifdef COMPAT_MACH
125	if ((callp = mach_syscall_dispatch(&code)) == NULL)
126#endif /* COMPAT_MACH */
127	{
128		switch (code) {
129		case EMULNAMEU(SYS_syscall):
130			/*
131			 * code is first argument,
132			 * followed by actual args.
133			 */
134			code = *params++;
135			n -= 1;
136			break;
137#if !defined(COMPAT_LINUX)
138		case EMULNAMEU(SYS___syscall):
139			params++;
140			code = *params++;
141			n -= 2;
142			break;
143#endif
144		default:
145			break;
146		}
147
148		callp = p->p_emul->e_sysent +
149		    (code & (EMULNAMEU(SYS_NSYSENT)-1));
150	}
151
152	argsize = callp->sy_argsize;
153
154	if (argsize > n * sizeof(register_t)) {
155		memcpy(args, params, n * sizeof(register_t));
156		KERNEL_PROC_LOCK(l);
157		error = copyin(MOREARGS(frame->fixreg[1]),
158		       args + n,
159		       argsize - n * sizeof(register_t));
160		KERNEL_PROC_UNLOCK(l);
161		if (error)
162			goto bad;
163		params = args;
164	}
165
166	rval[0] = 0;
167	rval[1] = 0;
168
169	if ((callp->sy_flags & SYCALL_MPSAFE) == 0) {
170		KERNEL_PROC_LOCK(l);
171	}
172
173	error = (*callp->sy_call)(l, params, rval);
174
175	if ((callp->sy_flags & SYCALL_MPSAFE) == 0) {
176		KERNEL_PROC_UNLOCK(l);
177	}
178	switch (error) {
179	case 0:
180		frame->fixreg[FIRSTARG] = rval[0];
181		frame->fixreg[FIRSTARG + 1] = rval[1];
182		frame->cr &= ~0x10000000;
183#ifdef COMPAT_MACH
184		/*
185		 * For regular system calls, on success,
186		 * the next instruction is skipped
187		 */
188		if ((frame->fixreg[0] < p->p_emul->e_nsysent)
189		    && (frame->fixreg[0] >= 0))
190			frame->srr0 += 4;
191#endif /* COMPAT_MACH */
192		break;
193	case ERESTART:
194		/*
195		 * Set user's pc back to redo the system call.
196		 */
197		frame->srr0 -= 4;
198		break;
199	case EJUSTRETURN:
200		/* nothing to do */
201		break;
202	default:
203	bad:
204		if (p->p_emul->e_errno)
205			error = p->p_emul->e_errno[error];
206		frame->fixreg[FIRSTARG] = error;
207		frame->cr |= 0x10000000;
208		break;
209	}
210	userret(l, frame);
211}
212
213#if defined(KTRACE) || defined(SYSTRACE)
214static void EMULNAME(syscall_fancy)(struct trapframe *);
215
216void
217EMULNAME(syscall_fancy)(struct trapframe *frame)
218{
219	struct lwp *l = curlwp;
220	struct proc *p = l->l_proc;
221	const struct sysent *callp;
222	size_t argsize;
223	register_t code;
224	register_t realcode;
225	register_t *params, rval[2];
226	register_t args[10];
227	int error;
228	int n;
229
230	KERNEL_PROC_LOCK(l);
231	curcpu()->ci_ev_scalls.ev_count++;
232
233	code = frame->fixreg[0];
234	params = frame->fixreg + FIRSTARG;
235	n = NARGREG;
236
237	realcode = code;
238#ifdef COMPAT_MACH
239	if ((callp = mach_syscall_dispatch(&code)) == NULL)
240#endif /* COMPAT_MACH */
241	{
242		switch (code) {
243		case EMULNAMEU(SYS_syscall):
244			/*
245			 * code is first argument,
246			 * followed by actual args.
247			 */
248			code = *params++;
249			n -= 1;
250			break;
251#if !defined(COMPAT_LINUX)
252		case EMULNAMEU(SYS___syscall):
253			params++;
254			code = *params++;
255			n -= 2;
256			break;
257#endif
258		default:
259			break;
260		}
261
262		code &= EMULNAMEU(SYS_NSYSENT) - 1;
263		callp = p->p_emul->e_sysent + code;
264		realcode = code;
265	}
266
267	argsize = callp->sy_argsize;
268
269	if (argsize > n * sizeof(register_t)) {
270		memcpy(args, params, n * sizeof(register_t));
271		error = copyin(MOREARGS(frame->fixreg[1]),
272		       args + n,
273		       argsize - n * sizeof(register_t));
274		if (error)
275			goto bad;
276		params = args;
277	}
278
279	if ((error = trace_enter(l, code, realcode, callp - code, params)) != 0)
280		goto out;
281
282	rval[0] = 0;
283	rval[1] = 0;
284
285	error = (*callp->sy_call)(l, params, rval);
286out:
287	switch (error) {
288	case 0:
289		frame->fixreg[FIRSTARG] = rval[0];
290		frame->fixreg[FIRSTARG + 1] = rval[1];
291		frame->cr &= ~0x10000000;
292#ifdef COMPAT_MACH
293		/*
294		 * For regular system calls, on success,
295		 * the next instruction is skipped
296		 */
297		if ((frame->fixreg[0] < p->p_emul->e_nsysent)
298		    && (frame->fixreg[0] >= 0))
299			frame->srr0 += 4;
300#endif /* COMPAT_MACH */
301		break;
302	case ERESTART:
303		/*
304		 * Set user's pc back to redo the system call.
305		 */
306		frame->srr0 -= 4;
307		break;
308	case EJUSTRETURN:
309		/* nothing to do */
310		break;
311	default:
312	bad:
313		if (p->p_emul->e_errno)
314			error = p->p_emul->e_errno[error];
315		frame->fixreg[FIRSTARG] = error;
316		frame->cr |= 0x10000000;
317		break;
318	}
319	KERNEL_PROC_UNLOCK(l);
320	trace_exit(l, realcode, params, rval, error);
321	userret(l, frame);
322}
323#endif /* KTRACE || SYSTRACE */
324
325void EMULNAME(syscall_intern)(struct proc *);
326
327void
328EMULNAME(syscall_intern)(struct proc *p)
329{
330#ifdef KTRACE
331	if (p->p_traceflag & (KTRFAC_SYSCALL | KTRFAC_SYSRET)) {
332		p->p_md.md_syscall = EMULNAME(syscall_fancy);
333		return;
334	}
335#endif
336#ifdef SYSTRACE
337	if (ISSET(p->p_flag, P_SYSTRACE)) {
338		p->p_md.md_syscall = EMULNAME(syscall_fancy);
339		return;
340	}
341#endif
342	p->p_md.md_syscall = EMULNAME(syscall_plain);
343}
344