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