xref: /netbsd/sys/arch/powerpc/powerpc/syscall.c (revision c4a72b64)
1 /*	$NetBSD: syscall.c,v 1.16 2002/11/29 11:56:36 manu 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 
40 #include <sys/param.h>
41 #include <sys/proc.h>
42 #include <sys/reboot.h>
43 #include <sys/systm.h>
44 #include <sys/user.h>
45 #ifdef KTRACE
46 #include <sys/ktrace.h>
47 #endif
48 #ifdef SYSTRACE
49 #include <sys/systrace.h>
50 #endif
51 
52 #include <uvm/uvm_extern.h>
53 
54 #include <powerpc/userret.h>
55 #include <machine/cpu.h>
56 #include <machine/frame.h>
57 
58 #define	FIRSTARG	3		/* first argument is in reg 3 */
59 #define	NARGREG		8		/* 8 args are in registers */
60 #define	MOREARGS(sp)	((caddr_t)((uintptr_t)(sp) + 8)) /* more args go here */
61 
62 #ifndef EMULNAME
63 #include <sys/syscall.h>
64 
65 #define EMULNAME(x)	(x)
66 #define EMULNAMEU(x)	(x)
67 
68 __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.16 2002/11/29 11:56:36 manu Exp $");
69 
70 void
71 child_return(void *arg)
72 {
73 	struct proc * const p = arg;
74 	struct trapframe * const tf = trapframe(p);
75 
76 	KERNEL_PROC_UNLOCK(p);
77 
78 	tf->fixreg[FIRSTARG] = 0;
79 	tf->fixreg[FIRSTARG + 1] = 1;
80 	tf->cr &= ~0x10000000;
81 	tf->srr1 &= ~(PSL_FP|PSL_VEC);	/* Disable FP & AltiVec, as we can't
82 					   be them. */
83 	p->p_addr->u_pcb.pcb_fpcpu = NULL;
84 #ifdef	KTRACE
85 	if (KTRPOINT(p, KTR_SYSRET)) {
86 		KERNEL_PROC_LOCK(p);
87 		ktrsysret(p, SYS_fork, 0, 0);
88 		KERNEL_PROC_UNLOCK(p);
89 	}
90 #endif
91 	/* Profiling?							XXX */
92 	curcpu()->ci_schedstate.spc_curpriority = p->p_priority;
93 }
94 #endif
95 
96 static void EMULNAME(syscall_plain)(struct trapframe *);
97 
98 void
99 EMULNAME(syscall_plain)(struct trapframe *frame)
100 {
101 	struct proc *p = curproc;
102 	const struct sysent *callp;
103 	size_t argsize;
104 	register_t code;
105 	register_t *params, rval[2];
106 	register_t args[10];
107 	int error;
108 	int n;
109 
110 	curcpu()->ci_ev_scalls.ev_count++;
111 
112 	code = frame->fixreg[0];
113 	params = frame->fixreg + FIRSTARG;
114 	n = NARGREG;
115 
116 #ifdef COMPAT_MACH
117 	if ((callp = mach_syscall_dispatch(&code)) == NULL)
118 #endif /* COMPAT_MACH */
119 	{
120 		switch (code) {
121 		case EMULNAMEU(SYS_syscall):
122 			/*
123 			 * code is first argument,
124 			 * followed by actual args.
125 			 */
126 			code = *params++;
127 			n -= 1;
128 			break;
129 #if !defined(COMPAT_LINUX)
130 		case EMULNAMEU(SYS___syscall):
131 			params++;
132 			code = *params++;
133 			n -= 2;
134 			break;
135 #endif
136 		default:
137 			break;
138 		}
139 
140 		callp = p->p_emul->e_sysent +
141 		    (code & (EMULNAMEU(SYS_NSYSENT)-1));
142 	}
143 
144 	argsize = callp->sy_argsize;
145 
146 	if (argsize > n * sizeof(register_t)) {
147 		memcpy(args, params, n * sizeof(register_t));
148 		KERNEL_PROC_LOCK(p);
149 		error = copyin(MOREARGS(frame->fixreg[1]),
150 		       args + n,
151 		       argsize - n * sizeof(register_t));
152 		KERNEL_PROC_UNLOCK(p);
153 		if (error)
154 			goto syscall_bad;
155 		params = args;
156 	}
157 
158 	rval[0] = 0;
159 	rval[1] = 0;
160 
161 	if ((callp->sy_flags & SYCALL_MPSAFE) == 0) {
162 		KERNEL_PROC_LOCK(p);
163 	}
164 
165 	error = (*callp->sy_call)(p, params, rval);
166 
167 	if ((callp->sy_flags & SYCALL_MPSAFE) == 0) {
168 		KERNEL_PROC_UNLOCK(p);
169 	}
170 
171 	switch (error) {
172 	case 0:
173 		frame->fixreg[FIRSTARG] = rval[0];
174 		frame->fixreg[FIRSTARG + 1] = rval[1];
175 		frame->cr &= ~0x10000000;
176 #ifdef COMPAT_MACH
177 		/*
178 		 * For regular system calls, on success,
179 		 * the next instruction is skipped
180 		 */
181 		if ((frame->fixreg[0] < p->p_emul->e_nsysent)
182 		    && (frame->fixreg[0] >= 0))
183 			frame->srr0 += 4;
184 #endif /* COMPAT_MACH */
185 		break;
186 	case ERESTART:
187 		/*
188 		 * Set user's pc back to redo the system call.
189 		 */
190 		frame->srr0 -= 4;
191 		break;
192 	case EJUSTRETURN:
193 		/* nothing to do */
194 		break;
195 	default:
196 syscall_bad:
197 		if (p->p_emul->e_errno)
198 			error = p->p_emul->e_errno[error];
199 		frame->fixreg[FIRSTARG] = error;
200 		frame->cr |= 0x10000000;
201 		break;
202 	}
203 	userret(p, frame);
204 }
205 
206 #if defined(KTRACE) || defined(SYSTRACE)
207 static void EMULNAME(syscall_fancy)(struct trapframe *);
208 
209 void
210 EMULNAME(syscall_fancy)(struct trapframe *frame)
211 {
212 	struct proc *p = curproc;
213 	const struct sysent *callp;
214 	size_t argsize;
215 	register_t code;
216 	register_t realcode;
217 	register_t *params, rval[2];
218 	register_t args[10];
219 	int error;
220 	int n;
221 
222 	KERNEL_PROC_LOCK(p);
223 	curcpu()->ci_ev_scalls.ev_count++;
224 
225 	code = frame->fixreg[0];
226 	params = frame->fixreg + FIRSTARG;
227 	n = NARGREG;
228 
229 	realcode = code;
230 #ifdef COMPAT_MACH
231 	if ((callp = mach_syscall_dispatch(&code)) == NULL)
232 #endif /* COMPAT_MACH */
233 	{
234 		switch (code) {
235 		case EMULNAMEU(SYS_syscall):
236 			/*
237 			 * code is first argument,
238 			 * followed by actual args.
239 			 */
240 			code = *params++;
241 			n -= 1;
242 			break;
243 #if !defined(COMPAT_LINUX)
244 		case EMULNAMEU(SYS___syscall):
245 			params++;
246 			code = *params++;
247 			n -= 2;
248 			break;
249 #endif
250 		default:
251 			break;
252 		}
253 
254 		code &= EMULNAMEU(SYS_NSYSENT) - 1;
255 		callp = p->p_emul->e_sysent + code;
256 		realcode = code;
257 	}
258 
259 	argsize = callp->sy_argsize;
260 
261 	if (argsize > n * sizeof(register_t)) {
262 		memcpy(args, params, n * sizeof(register_t));
263 		error = copyin(MOREARGS(frame->fixreg[1]),
264 		       args + n,
265 		       argsize - n * sizeof(register_t));
266 		if (error)
267 			goto syscall_bad;
268 		params = args;
269 	}
270 
271 	if ((error = trace_enter(p, code, realcode, params, rval)) != 0)
272 		goto syscall_bad;
273 
274 	rval[0] = 0;
275 	rval[1] = 0;
276 
277 	error = (*callp->sy_call)(p, params, rval);
278 	switch (error) {
279 	case 0:
280 		frame->fixreg[FIRSTARG] = rval[0];
281 		frame->fixreg[FIRSTARG + 1] = rval[1];
282 		frame->cr &= ~0x10000000;
283 #ifdef COMPAT_MACH
284 		/*
285 		 * For regular system calls, on success,
286 		 * the next instruction is skipped
287 		 */
288 		if ((frame->fixreg[0] < p->p_emul->e_nsysent)
289 		    && (frame->fixreg[0] >= 0))
290 			frame->srr0 += 4;
291 #endif /* COMPAT_MACH */
292 		break;
293 	case ERESTART:
294 		/*
295 		 * Set user's pc back to redo the system call.
296 		 */
297 		frame->srr0 -= 4;
298 		break;
299 	case EJUSTRETURN:
300 		/* nothing to do */
301 		break;
302 	default:
303 syscall_bad:
304 		if (p->p_emul->e_errno)
305 			error = p->p_emul->e_errno[error];
306 		frame->fixreg[FIRSTARG] = error;
307 		frame->cr |= 0x10000000;
308 		break;
309 	}
310 	KERNEL_PROC_UNLOCK(p);
311 	trace_exit(p, realcode, params, rval, error);
312 	userret(p, frame);
313 }
314 #endif /* KTRACE || SYSTRACE */
315 
316 void EMULNAME(syscall_intern)(struct proc *);
317 
318 void
319 EMULNAME(syscall_intern)(struct proc *p)
320 {
321 #ifdef KTRACE
322 	if (p->p_traceflag & (KTRFAC_SYSCALL | KTRFAC_SYSRET)) {
323 		p->p_md.md_syscall = EMULNAME(syscall_fancy);
324 		return;
325 	}
326 #endif
327 #ifdef SYSTRACE
328 	if (ISSET(p->p_flag, P_SYSTRACE)) {
329 		p->p_md.md_syscall = EMULNAME(syscall_fancy);
330 		return;
331 	}
332 #endif
333 	p->p_md.md_syscall = EMULNAME(syscall_plain);
334 }
335 
336