xref: /netbsd-src/sys/arch/aarch64/aarch64/syscall.c (revision f14316bcbc544b96a93e884bc5c2b15fd60e22ae)
1 /*	$NetBSD: syscall.c,v 1.1 2014/08/10 05:47:37 matt Exp $	*/
2 
3 /*-
4  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matt Thomas of 3am Software Foundry.
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 #include "opt_multiprocessor.h"
33 /* DO NOT INCLUDE opt_compat_XXX.h */
34 
35 #include <sys/param.h>
36 #include <sys/cpu.h>
37 #include <sys/ktrace.h>
38 #include <sys/proc.h>
39 #include <sys/reboot.h>
40 #include <sys/systm.h>
41 #include <sys/syscallvar.h>
42 
43 #include <uvm/uvm_extern.h>
44 
45 #include <aarch64/locore.h>
46 
47 #ifndef NARGREG
48 #define	NARGREG		8		/* 8 args are in registers */
49 #endif
50 #define	MOREARGS(sp)	((const void *)(uintptr_t)(sp)) /* more args go here */
51 #ifndef REGISTER_T
52 #define REGISTER_T	register_t
53 #endif
54 
55 #ifndef EMULNAME
56 #include <sys/syscall.h>
57 
58 #define EMULNAME(x)	(x)
59 #define EMULNAMEU(x)	(x)
60 
61 __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.1 2014/08/10 05:47:37 matt Exp $");
62 
63 void
64 cpu_spawn_return(struct lwp *l)
65 {
66 	userret(l, l->l_md.md_utf);
67 }
68 
69 void
70 child_return(void *arg)
71 {
72 	struct lwp * const l = arg;
73 	struct trapframe * const tf = l->l_md.md_utf;
74 
75 	tf->tf_reg[0] = 0;
76 	tf->tf_reg[1] = 1;
77 	tf->tf_spsr &= ~NZCV_C;
78 	l->l_md.md_cpacr = CPACR_FPEN_NONE;
79 
80 	ktrsysret(SYS_fork, 0, 0);
81 	/* Profiling? XXX */
82 }
83 #endif
84 
85 static void EMULNAME(syscall)(struct trapframe *);
86 
87 void
88 EMULNAME(syscall)(struct trapframe *tf)
89 {
90 	struct lwp * const l = curlwp;
91 	struct proc * const p = l->l_proc;
92 	register_t rval[2];
93 	register_t args[10];
94 	int error;
95 
96 	LWP_CACHE_CREDS(l, p);
97 
98 	curcpu()->ci_data.cpu_nsyscall++;
99 
100 	size_t code = tf->tf_esr & 0xffff;
101 	register_t *params = tf->tf_reg;
102 	size_t nargs = NARGREG;
103 
104 	switch (code) {
105 	case EMULNAMEU(SYS_syscall):
106 #if !defined(COMPAT_LINUX)
107 	case EMULNAMEU(SYS___syscall):
108 		code = tf->tf_reg[17];
109 #else
110 		code = *params++;
111 		nargs -= 1;
112 #endif
113 		/*
114 		 * code is first argument,
115 		 * followed by actual args.
116 		 */
117 		break;
118 	default:
119 		break;
120 	}
121 
122 	code &= EMULNAMEU(SYS_NSYSENT) - 1;
123 	const struct sysent * const callp = p->p_emul->e_sysent + code;
124 
125 	if (__predict_false(callp->sy_narg > nargs)) {
126 		const size_t diff = callp->sy_narg - nargs;
127 		memcpy(args, params, nargs * sizeof(params[0]));
128 		if (sizeof(params[0]) == sizeof(REGISTER_T)) {
129 			error = copyin(MOREARGS(tf->tf_sp), &args[nargs],
130 			    diff * sizeof(register_t));
131 			if (error)
132 				goto bad;
133 		} else {
134 			/*
135 			 * If the register_t used by the process isn't the
136 			 * as the one used by the kernel, we can't directly
137 			 * copy the arguments off the stack into args.  We
138 			 * need to buffer them in a REGISTER_T array and
139 			 * then move them individually into args.
140 			 */
141 			REGISTER_T args32[10];
142 			error = copyin(MOREARGS(tf->tf_sp), args32,
143 			    diff * sizeof(REGISTER_T));
144 			if (error)
145 				goto bad;
146 			for (size_t i = 0; i < diff; i++) {
147 				args[nargs + i] = args32[i];
148 			}
149 		}
150 		params = args;
151 	}
152 
153 #ifdef __AARCH64EB__
154 #define SYCALL_ARG_64(a, b)	(((register_t) (a) << 32) | (uint32_t)(b))
155 #else
156 #define SYCALL_ARG_64(a, b)	(((register_t) (b) << 32) | (uint32_t)(a))
157 #endif
158 
159 	/*
160 	 * If the syscall used a different (smaller) register size,
161 	 * reconstruct the 64-bit arguments from two 32-bit registers.
162 	 */
163 	if (__predict_false(sizeof(register_t) != sizeof(REGISTER_T)
164 			    && SYCALL_NARGS64(callp) > 0)) {
165 		for (size_t i = 0, j = 0; i < callp->sy_narg; i++, j++) {
166 			if (SYCALL_ARG_64_P(callp, i)) {
167 				register_t *inp = &params[j];
168 				args[i] = SYCALL_ARG_64(inp[0], inp[1]);
169 				j++;
170 			} else if (i != j) {
171 				args[i] = params[j];
172 			}
173 		}
174 		params = args;
175 	}
176 
177 	error = sy_invoke(callp, l, params, rval, code);
178 
179 	if (__predict_true(error == 0)) {
180 		if (__predict_false(sizeof(register_t) != sizeof(REGISTER_T)
181 				    && SYCALL_RET_64_P(callp))) {
182 #ifdef __AARCH64EB__
183 			tf->tf_reg[0] = (uint32_t) (rval[0] >> 32);
184 			tf->tf_reg[1] = (uint32_t) (rval[0] >>  0);
185 #else
186 			tf->tf_reg[0] = (uint32_t) (rval[0] >>  0);
187 			tf->tf_reg[1] = (uint32_t) (rval[0] >> 32);
188 #endif
189 		} else {
190 			tf->tf_reg[0] = rval[0];
191 			tf->tf_reg[1] = rval[1];
192 		}
193 		tf->tf_spsr &= ~NZCV_C;
194 	} else {
195 		switch (error) {
196 		case ERESTART:
197 			/*
198 			 * Set user's pc back to redo the system call.
199 			 */
200 			tf->tf_pc -= 4;
201 			break;
202 		case EJUSTRETURN:
203 			/* nothing to do */
204 			break;
205 		default:
206 		bad:
207 #ifndef __HAVE_MINIMAL_EMUL
208 			if (p->p_emul->e_errno)
209 				error = p->p_emul->e_errno[error];
210 #endif
211 			tf->tf_reg[0] = error;
212 			tf->tf_spsr |= NZCV_C;
213 			break;
214 		}
215 	}
216 
217 	userret(l, tf);
218 }
219 
220 void EMULNAME(syscall_intern)(struct proc *);
221 
222 void
223 EMULNAME(syscall_intern)(struct proc *p)
224 {
225 
226 	p->p_md.md_syscall = EMULNAME(syscall);
227 }
228