xref: /netbsd-src/sys/arch/aarch64/aarch64/aarch32_syscall.c (revision 90313c06e62e910bf0d1bb24faa9d17dcefd0ab6)
1 /*	$NetBSD: aarch32_syscall.c,v 1.9 2024/02/07 04:20:26 msaitoh Exp $	*/
2 
3 /*
4  * Copyright (c) 2018 Ryo Shimizu
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: aarch32_syscall.c,v 1.9 2024/02/07 04:20:26 msaitoh Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/ktrace.h>
34 #include <sys/proc.h>
35 #include <sys/syscallvar.h>
36 #include <uvm/uvm_extern.h>
37 
38 #include <aarch64/armreg.h>
39 #include <aarch64/frame.h>
40 #include <aarch64/machdep.h>
41 #include <aarch64/userret.h>
42 
43 #ifndef EMULNAME
44 #error EMULNAME is not defined
45 #endif
46 
47 #ifndef NARGREG
48 #define NARGREG		4		/* 4 args are in registers */
49 #endif
50 
51 static void EMULNAME(syscall)(struct trapframe *);
52 
53 union args {
54 	register_t a64[EMULNAMEU(SYS_MAXSYSARGS)];
55 	register32_t a32[EMULNAMEU(SYS_MAXSYSARGS)];
56 };
57 
58 void
EMULNAME(syscall)59 EMULNAME(syscall)(struct trapframe *tf)
60 {
61 	struct lwp * const l = curlwp;
62 	struct proc * const p = l->l_proc;
63 	const struct sysent *callp;
64 	union args args64buf, args32buf;
65 	register_t rval[2];
66 	register32_t *args32 = args32buf.a32;
67 	int error, i;
68 	bool do_trace, thumbmode;
69 
70 	curcpu()->ci_data.cpu_nsyscall++; /* XXX unsafe curcpu() */
71 
72 	thumbmode = (tf->tf_spsr & SPSR_A32_T) ? true : false;
73 #ifdef SYSCALL_CODE_REG
74 	/*
75 	 * mov.w r<SYSCALL_CODE_REG>, #<syscall_no>
76 	 * svc #<SYSCALL_CODE_REG_SVC>
77 	 */
78 #ifdef SYSCALL_CODE_REG_SVC
79 	if ((tf->tf_esr & 0xffff) != SYSCALL_CODE_REG_SVC) {
80 		error = EINVAL;
81 		goto bad;
82 	}
83 #endif
84 	uint32_t code = tf->tf_reg[SYSCALL_CODE_REG];
85 #if (SYSCALL_CODE_REG == 0)
86 	int regstart = 1;		/* args start from r1 */
87 	int nargs_reg = NARGREG - 1;	/* number of argument in registers */
88 #else
89 	int regstart = 0;		/* args start from r0 */
90 	int nargs_reg = NARGREG;	/* number of argument in registers */
91 #endif
92 #else /* SYSCALL_CODE_REG */
93 	uint32_t code = tf->tf_esr & 0xffff;	/* XXX: 16-23bits are omitted */
94 	if (thumbmode) {
95 		if (code != 255) {
96 			do_trapsignal(l, SIGILL, ILL_ILLTRP,
97 			    (void *)(tf->tf_pc - 2), tf->tf_esr);
98 			error = EINVAL;
99 			goto bad;
100 		}
101 		code = tf->tf_reg[0];
102 		tf->tf_reg[0] = tf->tf_reg[12];	/* orig $r0 is saved to $ip */
103 	}
104 	int regstart = 0;		/* args start from r0 */
105 	int nargs_reg = NARGREG;	/* number of argument in registers */
106 #endif /* SYSCALL_CODE_REG */
107 
108 #ifdef SYSCALL_CODE_REMAP
109 	code = SYSCALL_CODE_REMAP(code);
110 #endif
111 
112 	code %= EMULNAMEU(SYS_NSYSENT);
113 	callp = p->p_emul->e_sysent + code;
114 #ifndef SYSCALL_NO_INDIRECT
115 	if (__predict_false(callp->sy_flags & SYCALL_INDIRECT)) {
116 		int off = 1;
117 #ifdef NETBSD32_SYS_netbsd32____syscall /* XXX ugly: apply only for NETBSD32 */
118 		/*
119 		 * For __syscall(2), 1st argument is quad_t, which is
120 		 * stored in r0 and r1.
121 		 */
122 		if (code == NETBSD32_SYS_netbsd32____syscall)
123 			off = 2;
124 #endif
125 		nargs_reg -= off;
126 		regstart = off;	/* args start from r1 or r2 */
127 #ifdef __AARCH64EB__
128 		if (off == 2)
129 			code = tf->tf_reg[1];
130 		else
131 #endif
132 			code = tf->tf_reg[0];
133 		code %= EMULNAMEU(SYS_NSYSENT);
134 		callp = p->p_emul->e_sysent + code;
135 
136 		/* don't allow nested syscall */
137 		if (__predict_false(callp->sy_flags & SYCALL_INDIRECT)) {
138 			error = EINVAL;
139 			goto bad;
140 		}
141 	}
142 #endif /* SYSCALL_NO_INDIRECT */
143 
144 	/* number of argument to fetch from sp */
145 	KASSERT(callp->sy_narg <= EMULNAMEU(SYS_MAXSYSARGS));
146 	int nargs_sp = callp->sy_narg - nargs_reg;
147 
148 	/* fetch arguments from tf and sp, and store to args32buf[] */
149 	for (i = 0; i < nargs_reg; i++)
150 		*args32++ = (uint32_t)tf->tf_reg[regstart++];
151 	if (nargs_sp > 0) {
152 		error = copyin(
153 		    (void*)(uintptr_t)(uint32_t)tf->tf_reg[13],	/* sp = r13 */
154 		    args32, nargs_sp * sizeof(register32_t));
155 		if (error)
156 			goto bad;
157 	}
158 
159 	rval[0] = 0;
160 	rval[1] = tf->tf_reg[1];
161 #if 0
162 	error = sy_invoke(callp, l, args32buf.a32, rval, code);
163 #else
164 	/*
165 	 * XXX: trace_enter()/trace_exit() called from sy_invoke() expects
166 	 *      64bit args, but sy_invoke doesn't take care of it.
167 	 *      therefore call trace_enter(), sy_call(), trace_exit() manually.
168 	 */
169 #ifdef KDTRACE_HOOKS
170 #define KDTRACE_ENTRY(a)	(a)
171 #else
172 #define KDTRACE_ENTRY(a)	(0)
173 #endif
174 	do_trace = p->p_trace_enabled &&
175 	    ((callp->sy_flags & SYCALL_INDIRECT) == 0);
176 	if (__predict_false(do_trace ||
177 	    KDTRACE_ENTRY(callp->sy_entry) ||
178 	    KDTRACE_ENTRY(callp->sy_return))) {
179 		/* build 64bit args for trace_enter()/trace_exit() */
180 		int nargs = callp->sy_narg;
181 		for (i = 0; i < nargs; i++)
182 			args64buf.a64[i] = args32buf.a32[i];
183 	}
184 
185 	if (__predict_false(do_trace || KDTRACE_ENTRY(callp->sy_entry)))
186 		error = trace_enter(code, callp, args64buf.a64);
187 
188 	if (error == 0)
189 		error = sy_call(callp, l, args32buf.a32, rval);
190 
191 	if (__predict_false(do_trace || KDTRACE_ENTRY(callp->sy_return)))
192 		trace_exit(code, callp, args64buf.a64, rval, error);
193 #endif
194 
195 	if (__predict_true(error == 0)) {
196 		tf->tf_reg[0] = rval[0];
197 #ifndef SYSCALL_NO_RVAL1
198 		tf->tf_reg[1] = rval[1];
199 #endif
200 		tf->tf_spsr &= ~NZCV_C;
201 	} else {
202 		switch (error) {
203 		case ERESTART:
204 			/* redo system call insn */
205 			if (thumbmode)
206 				tf->tf_pc -= 2;
207 			else
208 				tf->tf_pc -= 4;
209 			break;
210 		case EJUSTRETURN:
211 			/* nothing to do */
212 			break;
213 		default:
214 		bad:
215 #ifndef __HAVE_MINIMAL_EMUL
216 			if (p->p_emul->e_errno)
217 				error = p->p_emul->e_errno[error];
218 #elif defined(SYSCALL_EMUL_ERRNO)
219 			error = SYSCALL_EMUL_ERRNO(error);
220 #endif
221 			tf->tf_reg[0] = error;
222 			tf->tf_spsr |= NZCV_C;
223 			break;
224 		}
225 	}
226 
227 	userret(l);
228 }
229 
230 void EMULNAME(syscall_intern)(struct proc *);
231 
232 void
EMULNAME(syscall_intern)233 EMULNAME(syscall_intern)(struct proc *p)
234 {
235 	p->p_md.md_syscall = EMULNAME(syscall);
236 }
237