xref: /netbsd/sys/arch/powerpc/powerpc/syscall.c (revision 6550d01e)
1 /*	$NetBSD: syscall.c,v 1.46 2011/01/18 01:02:55 matt 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_multiprocessor.h"
37 #include "opt_sa.h"
38 /* DO NOT INCLUDE opt_compat_XXX.h */
39 /* If needed, they will be included by file that includes this one */
40 
41 #include <sys/param.h>
42 #include <sys/cpu.h>
43 #include <sys/ktrace.h>
44 #include <sys/proc.h>
45 #include <sys/reboot.h>
46 #include <sys/systm.h>
47 #include <sys/sa.h>
48 #include <sys/savar.h>
49 #include <sys/syscallvar.h>
50 
51 #include <uvm/uvm_extern.h>
52 
53 #include <powerpc/frame.h>
54 #include <powerpc/pcb.h>
55 #include <powerpc/userret.h>
56 
57 #define	FIRSTARG	3		/* first argument is in reg 3 */
58 #define	NARGREG		8		/* 8 args are in registers */
59 #define	MOREARGS(sp)	((void *)((uintptr_t)(sp) + 8)) /* more args go here */
60 
61 #ifndef EMULNAME
62 #include <sys/syscall.h>
63 
64 #define EMULNAME(x)	(x)
65 #define EMULNAMEU(x)	(x)
66 
67 __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.46 2011/01/18 01:02:55 matt Exp $");
68 
69 void
70 child_return(void *arg)
71 {
72 	struct lwp * const l = arg;
73 	struct trapframe * const tf = trapframe(l);
74 
75 	tf->tf_fixreg[FIRSTARG] = 0;
76 	tf->tf_fixreg[FIRSTARG + 1] = 1;
77 	tf->tf_cr &= ~0x10000000;
78 	tf->tf_srr1 &= ~(PSL_FP|PSL_VEC); /* Disable FP & AltiVec, as we can't
79 					   be them. */
80 	ktrsysret(SYS_fork, 0, 0);
81 	/* Profiling?							XXX */
82 }
83 #endif
84 
85 static void EMULNAME(syscall_plain)(struct trapframe *);
86 
87 #include <powerpc/spr.h>
88 
89 void
90 EMULNAME(syscall_plain)(struct trapframe *tf)
91 {
92 	struct lwp *l = curlwp;
93 	struct proc *p = l->l_proc;
94 	const struct sysent *callp;
95 	size_t argsize;
96 	register_t code;
97 	register_t *params, rval[2];
98 	register_t args[10];
99 	int error;
100 	int n;
101 
102 	LWP_CACHE_CREDS(l, p);
103 	curcpu()->ci_ev_scalls.ev_count++;
104 	curcpu()->ci_data.cpu_nsyscall++;
105 
106 	code = tf->tf_fixreg[0];
107 	params = tf->tf_fixreg + FIRSTARG;
108 	n = NARGREG;
109 
110 #ifdef KERN_SA
111 	if (__predict_false((l->l_savp)
112             && (l->l_savp->savp_pflags & SAVP_FLAG_DELIVERING)))
113 		l->l_savp->savp_pflags &= ~SAVP_FLAG_DELIVERING;
114 #endif
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 		error = copyin(MOREARGS(tf->tf_fixreg[1]),
149 		    args + n,
150 		    argsize - n * sizeof(register_t));
151 		if (error)
152 			goto bad;
153 		params = args;
154 	}
155 
156 	rval[0] = 0;
157 	rval[1] = 0;
158 
159 	error = sy_call(callp, l, params, rval);
160 
161 	switch (error) {
162 	case 0:
163 		tf->tf_fixreg[FIRSTARG] = rval[0];
164 		tf->tf_fixreg[FIRSTARG + 1] = rval[1];
165 		tf->tf_cr &= ~0x10000000;
166 #ifdef COMPAT_MACH
167 		/*
168 		 * For regular system calls, on success,
169 		 * the next instruction is skipped
170 		 */
171 		if ((tf->tf_fixreg[0] < p->p_emul->e_nsysent)
172 		    && (tf->tf_fixreg[0] >= 0))
173 			tf->tf_srr0 += 4;
174 #endif /* COMPAT_MACH */
175 		break;
176 	case ERESTART:
177 		/*
178 		 * Set user's pc back to redo the system call.
179 		 */
180 		tf->tf_srr0 -= 4;
181 		break;
182 	case EJUSTRETURN:
183 		/* nothing to do */
184 		break;
185 	default:
186 	bad:
187 		if (p->p_emul->e_errno)
188 			error = p->p_emul->e_errno[error];
189 		tf->tf_fixreg[FIRSTARG] = error;
190 		tf->tf_cr |= 0x10000000;
191 		break;
192 	}
193 
194 	userret(l, tf);
195 }
196 
197 static void EMULNAME(syscall_fancy)(struct trapframe *);
198 
199 void
200 EMULNAME(syscall_fancy)(struct trapframe *tf)
201 {
202 	struct lwp *l = curlwp;
203 	struct proc *p = l->l_proc;
204 	const struct sysent *callp;
205 	size_t argsize;
206 	register_t code;
207 	register_t realcode;
208 	register_t *params, rval[2];
209 	register_t args[10];
210 	int error;
211 	int n;
212 
213 	LWP_CACHE_CREDS(l, p);
214 
215 	curcpu()->ci_ev_scalls.ev_count++;
216 
217 	code = tf->tf_fixreg[0];
218 	params = tf->tf_fixreg + FIRSTARG;
219 	n = NARGREG;
220 
221 #ifdef KERN_SA
222 	if (__predict_false((l->l_savp)
223             && (l->l_savp->savp_pflags & SAVP_FLAG_DELIVERING)))
224 		l->l_savp->savp_pflags &= ~SAVP_FLAG_DELIVERING;
225 #endif
226 
227 	realcode = code;
228 #ifdef COMPAT_MACH
229 	if ((callp = mach_syscall_dispatch(&code)) == NULL)
230 #endif /* COMPAT_MACH */
231 	{
232 		switch (code) {
233 		case EMULNAMEU(SYS_syscall):
234 			/*
235 			 * code is first argument,
236 			 * followed by actual args.
237 			 */
238 			code = *params++;
239 			n -= 1;
240 			break;
241 #if !defined(COMPAT_LINUX)
242 		case EMULNAMEU(SYS___syscall):
243 			params++;
244 			code = *params++;
245 			n -= 2;
246 			break;
247 #endif
248 		default:
249 			break;
250 		}
251 
252 		code &= EMULNAMEU(SYS_NSYSENT) - 1;
253 		callp = p->p_emul->e_sysent + code;
254 		realcode = code;
255 	}
256 
257 	argsize = callp->sy_argsize;
258 
259 	if (argsize > n * sizeof(register_t)) {
260 		memcpy(args, params, n * sizeof(register_t));
261 		error = copyin(MOREARGS(tf->tf_fixreg[1]),
262 		       args + n,
263 		       argsize - n * sizeof(register_t));
264 		if (error)
265 			goto bad;
266 		params = args;
267 	}
268 
269 	if ((error = trace_enter(realcode, params, callp->sy_narg)) != 0)
270 		goto out;
271 
272 	rval[0] = 0;
273 	rval[1] = 0;
274 
275 	error = sy_call(callp, l, params, rval);
276 out:
277 	switch (error) {
278 	case 0:
279 		tf->tf_fixreg[FIRSTARG] = rval[0];
280 		tf->tf_fixreg[FIRSTARG + 1] = rval[1];
281 		tf->tf_cr &= ~0x10000000;
282 #ifdef COMPAT_MACH
283 		/*
284 		 * For regular system calls, on success,
285 		 * the next instruction is skipped
286 		 */
287 		if ((tf->tf_fixreg[0] < p->p_emul->e_nsysent)
288 		    && (tf->tf_fixreg[0] >= 0))
289 			tf->tf_srr0 += 4;
290 #endif /* COMPAT_MACH */
291 		break;
292 	case ERESTART:
293 		/*
294 		 * Set user's pc back to redo the system call.
295 		 */
296 		tf->tf_srr0 -= 4;
297 		break;
298 	case EJUSTRETURN:
299 		/* nothing to do */
300 		break;
301 	default:
302 	bad:
303 		if (p->p_emul->e_errno)
304 			error = p->p_emul->e_errno[error];
305 		tf->tf_fixreg[FIRSTARG] = error;
306 		tf->tf_cr |= 0x10000000;
307 		break;
308 	}
309 	trace_exit(realcode, rval, error);
310 	userret(l, tf);
311 }
312 
313 void EMULNAME(syscall_intern)(struct proc *);
314 
315 void
316 EMULNAME(syscall_intern)(struct proc *p)
317 {
318 
319 	if (trace_is_enabled(p))
320 		p->p_md.md_syscall = EMULNAME(syscall_fancy);
321 	else
322 		p->p_md.md_syscall = EMULNAME(syscall_plain);
323 }
324