]> git.saurik.com Git - apple/xnu.git/blame - bsd/dev/arm/systemcalls.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / dev / arm / systemcalls.c
CommitLineData
5ba3f43e
A
1/*
2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
3 */
4
5#include <kern/task.h>
6#include <kern/thread.h>
7#include <kern/assert.h>
8#include <kern/clock.h>
9#include <kern/locks.h>
10#include <kern/sched_prim.h>
11#include <mach/machine/thread_status.h>
12#include <mach/thread_act.h>
d9a64523 13#include <machine/machine_routines.h>
5ba3f43e
A
14#include <arm/thread.h>
15#include <arm/proc_reg.h>
16#include <pexpert/pexpert.h>
17
18#include <sys/kernel.h>
19#include <sys/vm.h>
20#include <sys/proc_internal.h>
21#include <sys/syscall.h>
22#include <sys/systm.h>
23#include <sys/user.h>
24#include <sys/errno.h>
25#include <sys/kdebug.h>
26#include <sys/sysent.h>
27#include <sys/sysproto.h>
28#include <sys/kauth.h>
cb323159 29#include <sys/bitstring.h>
5ba3f43e
A
30
31#include <security/audit/audit.h>
32
cb323159
A
33#if CONFIG_MACF
34#include <security/mac_framework.h>
35#endif
36
5ba3f43e
A
37#if CONFIG_DTRACE
38extern int32_t dtrace_systrace_syscall(struct proc *, void *, int *);
39extern void dtrace_systrace_syscall_return(unsigned short, int, int *);
f427ee49 40#endif /* CONFIG_DTRACE */
5ba3f43e
A
41
42extern void
43unix_syscall(struct arm_saved_state * regs, thread_t thread_act,
f427ee49
A
44 struct uthread * uthread, struct proc * proc);
45
46static int arm_get_syscall_args(uthread_t, struct arm_saved_state *, const struct sysent *);
47static int arm_get_u32_syscall_args(uthread_t, arm_saved_state32_t *, const struct sysent *);
48static void arm_prepare_u32_syscall_return(const struct sysent *, arm_saved_state_t *, uthread_t, int);
49static void arm_prepare_syscall_return(const struct sysent *, struct arm_saved_state *, uthread_t, int);
50static unsigned short arm_get_syscall_number(struct arm_saved_state *);
51static void arm_trace_unix_syscall(int, struct arm_saved_state *);
52static void arm_clear_syscall_error(struct arm_saved_state *);
53#define save_r0 r[0]
54#define save_r1 r[1]
55#define save_r2 r[2]
56#define save_r3 r[3]
57#define save_r4 r[4]
58#define save_r5 r[5]
59#define save_r6 r[6]
60#define save_r7 r[7]
61#define save_r8 r[8]
62#define save_r9 r[9]
63#define save_r10 r[10]
64#define save_r11 r[11]
65#define save_r12 r[12]
66#define save_r13 r[13]
5ba3f43e
A
67
68#if COUNT_SYSCALLS
f427ee49
A
69__XNU_PRIVATE_EXTERN int do_count_syscalls = 1;
70__XNU_PRIVATE_EXTERN int syscalls_log[SYS_MAXSYSCALL];
5ba3f43e
A
71#endif
72
73#define code_is_kdebug_trace(code) (((code) == SYS_kdebug_trace) || \
f427ee49
A
74 ((code) == SYS_kdebug_trace64) || \
75 ((code) == SYS_kdebug_trace_string))
5ba3f43e
A
76
77/*
78 * Function: unix_syscall
79 *
80 * Inputs: regs - pointer to Process Control Block
81 *
82 * Outputs: none
83 */
84#ifdef __arm__
85__attribute__((noreturn))
86#endif
87void
88unix_syscall(
f427ee49
A
89 struct arm_saved_state * state,
90 __unused thread_t thread_act,
91 struct uthread * uthread,
92 struct proc * proc)
5ba3f43e 93{
f427ee49 94 const struct sysent *callp;
5ba3f43e 95 int error;
cb323159 96 unsigned short code, syscode;
f427ee49 97 pid_t pid;
5ba3f43e
A
98
99#if defined(__arm__)
100 assert(is_saved_state32(state));
101#endif
102
103 uthread_reset_proc_refcount(uthread);
104
105 code = arm_get_syscall_number(state);
106
f427ee49 107#define unix_syscall_kprintf(x...) /* kprintf("unix_syscall: " x) */
5ba3f43e 108
5ba3f43e
A
109 if (kdebug_enable && !code_is_kdebug_trace(code)) {
110 arm_trace_unix_syscall(code, state);
111 }
5ba3f43e 112
f427ee49 113 if ((uthread->uu_flag & UT_VFORK)) {
5ba3f43e 114 proc = current_proc();
f427ee49 115 }
5ba3f43e 116
f427ee49 117 syscode = (code < nsysent) ? code : SYS_invalid;
cb323159 118 callp = &sysent[syscode];
5ba3f43e
A
119
120 /*
121 * sy_narg is inaccurate on ARM if a 64 bit parameter is specified. Since user_addr_t
122 * is currently a 32 bit type, this is really a long word count. See rdar://problem/6104668.
123 */
124 if (callp->sy_narg != 0) {
125 if (arm_get_syscall_args(uthread, state, callp) != 0) {
126 /* Too many arguments, or something failed */
127 unix_syscall_kprintf("arm_get_syscall_args failed.\n");
128 callp = &sysent[SYS_invalid];
129 }
130 }
131
132 uthread->uu_flag |= UT_NOTCANCELPT;
133 uthread->syscall_code = code;
134
135 uthread->uu_rval[0] = 0;
136
137 /*
138 * r4 is volatile, if we set it to regs->save_r4 here the child
139 * will have parents r4 after execve
140 */
141 uthread->uu_rval[1] = 0;
142
143 error = 0;
144
145 /*
146 * ARM runtime will call cerror if the carry bit is set after a
147 * system call, so clear it here for the common case of success.
148 */
149 arm_clear_syscall_error(state);
150
151#if COUNT_SYSCALLS
152 if (do_count_syscalls > 0) {
153 syscalls_log[code]++;
154 }
155#endif
156 pid = proc_pid(proc);
157
158#ifdef JOE_DEBUG
159 uthread->uu_iocount = 0;
160 uthread->uu_vpindex = 0;
161#endif
162 unix_syscall_kprintf("code %d (pid %d - %s, tid %lld)\n", code,
f427ee49 163 pid, proc->p_comm, thread_tid(current_thread()));
5ba3f43e 164
cb323159
A
165#if CONFIG_MACF
166 if (__improbable(proc->syscall_filter_mask != NULL && !bitstr_test(proc->syscall_filter_mask, syscode))) {
167 error = mac_proc_check_syscall_unix(proc, syscode);
f427ee49 168 if (error) {
cb323159 169 goto skip_syscall;
f427ee49 170 }
cb323159
A
171 }
172#endif /* CONFIG_MACF */
173
5ba3f43e 174 AUDIT_SYSCALL_ENTER(code, proc, uthread);
f427ee49 175 error = (*(callp->sy_call))(proc, &uthread->uu_arg[0], &(uthread->uu_rval[0]));
5ba3f43e
A
176 AUDIT_SYSCALL_EXIT(code, proc, uthread, error);
177
cb323159
A
178#if CONFIG_MACF
179skip_syscall:
180#endif /* CONFIG_MACF */
181
f427ee49
A
182 unix_syscall_kprintf("code %d, error %d, results %x, %x (pid %d - %s, tid %lld)\n", code, error,
183 uthread->uu_rval[0], uthread->uu_rval[1],
184 pid, get_bsdtask_info(current_task()) ? proc->p_comm : "unknown", thread_tid(current_thread()));
5ba3f43e
A
185
186#ifdef JOE_DEBUG
187 if (uthread->uu_iocount) {
188 printf("system call returned with uu_iocount != 0");
189 }
190#endif
191#if CONFIG_DTRACE
192 uthread->t_dtrace_errno = error;
193#endif /* CONFIG_DTRACE */
194#if DEBUG || DEVELOPMENT
195 kern_allocation_name_t
196 prior __assert_only = thread_set_allocation_name(NULL);
197 assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior));
198#endif /* DEBUG || DEVELOPMENT */
199
200 arm_prepare_syscall_return(callp, state, uthread, error);
201
202 uthread->uu_flag &= ~UT_NOTCANCELPT;
0a7de745 203 uthread->syscall_code = 0;
5ba3f43e
A
204
205 if (uthread->uu_lowpri_window) {
206 /*
207 * task is marked as a low priority I/O type
208 * and the I/O we issued while in this system call
209 * collided with normal I/O operations... we'll
210 * delay in order to mitigate the impact of this
211 * task on the normal operation of the system
212 */
213 throttle_lowpri_io(1);
214 }
5ba3f43e 215 if (kdebug_enable && !code_is_kdebug_trace(code)) {
cb323159 216 KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END,
f427ee49 217 error, uthread->uu_rval[0], uthread->uu_rval[1], pid);
5ba3f43e 218 }
5ba3f43e
A
219
220#if PROC_REF_DEBUG
221 if (__improbable(uthread_get_proc_refcount(uthread) != 0)) {
222 panic("system call returned with uu_proc_refcount != 0");
223 }
224#endif
225
226#ifdef __arm__
227 thread_exception_return();
228#endif
229}
230
231void
232unix_syscall_return(int error)
233{
234 thread_t thread_act;
235 struct uthread *uthread;
236 struct proc *proc;
237 struct arm_saved_state *regs;
238 unsigned short code;
f427ee49 239 const struct sysent *callp;
5ba3f43e 240
f427ee49
A
241#define unix_syscall_return_kprintf(x...) /* kprintf("unix_syscall_retur
242 * n: " x) */
5ba3f43e
A
243
244 thread_act = current_thread();
245 proc = current_proc();
246 uthread = get_bsdthread_info(thread_act);
247
248 regs = find_user_regs(thread_act);
249 code = uthread->syscall_code;
250 callp = (code >= nsysent) ? &sysent[SYS_invalid] : &sysent[code];
251
252#if CONFIG_DTRACE
f427ee49 253 if (callp->sy_call == dtrace_systrace_syscall) {
5ba3f43e 254 dtrace_systrace_syscall_return( code, error, uthread->uu_rval );
f427ee49 255 }
5ba3f43e
A
256#endif /* CONFIG_DTRACE */
257#if DEBUG || DEVELOPMENT
258 kern_allocation_name_t
259 prior __assert_only = thread_set_allocation_name(NULL);
260 assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior));
261#endif /* DEBUG || DEVELOPMENT */
262
263 AUDIT_SYSCALL_EXIT(code, proc, uthread, error);
264
265 /*
266 * Get index into sysent table
267 */
268 arm_prepare_syscall_return(callp, regs, uthread, error);
269
270 uthread->uu_flag &= ~UT_NOTCANCELPT;
0a7de745 271 uthread->syscall_code = 0;
5ba3f43e
A
272
273 if (uthread->uu_lowpri_window) {
274 /*
275 * task is marked as a low priority I/O type
276 * and the I/O we issued while in this system call
277 * collided with normal I/O operations... we'll
278 * delay in order to mitigate the impact of this
279 * task on the normal operation of the system
280 */
281 throttle_lowpri_io(1);
282 }
5ba3f43e 283 if (kdebug_enable && !code_is_kdebug_trace(code)) {
cb323159
A
284 KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END,
285 error, uthread->uu_rval[0], uthread->uu_rval[1], proc->p_pid);
5ba3f43e 286 }
5ba3f43e
A
287
288 thread_exception_return();
289 /* NOTREACHED */
290}
291
292static void
f427ee49 293arm_prepare_u32_syscall_return(const struct sysent *callp, arm_saved_state_t *regs, uthread_t uthread, int error)
5ba3f43e 294{
d9a64523
A
295 assert(is_saved_state32(regs));
296
297 arm_saved_state32_t *ss32 = saved_state32(regs);
298
5ba3f43e 299 if (error == ERESTART) {
d9a64523 300 ss32->pc -= 4;
5ba3f43e
A
301 } else if (error != EJUSTRETURN) {
302 if (error) {
d9a64523
A
303 ss32->save_r0 = error;
304 ss32->save_r1 = 0;
5ba3f43e 305 /* set the carry bit to execute cerror routine */
d9a64523 306 ss32->cpsr |= PSR_CF;
5ba3f43e 307 unix_syscall_return_kprintf("error: setting carry to trigger cerror call\n");
f427ee49 308 } else { /* (not error) */
5ba3f43e
A
309 switch (callp->sy_return_type) {
310 case _SYSCALL_RET_INT_T:
311 case _SYSCALL_RET_UINT_T:
312 case _SYSCALL_RET_OFF_T:
313 case _SYSCALL_RET_ADDR_T:
314 case _SYSCALL_RET_SIZE_T:
315 case _SYSCALL_RET_SSIZE_T:
316 case _SYSCALL_RET_UINT64_T:
d9a64523
A
317 ss32->save_r0 = uthread->uu_rval[0];
318 ss32->save_r1 = uthread->uu_rval[1];
5ba3f43e
A
319 break;
320 case _SYSCALL_RET_NONE:
d9a64523
A
321 ss32->save_r0 = 0;
322 ss32->save_r1 = 0;
5ba3f43e
A
323 break;
324 default:
325 panic("unix_syscall: unknown return type");
326 break;
327 }
328 }
329 }
330 /* else (error == EJUSTRETURN) { nothing } */
5ba3f43e
A
331}
332
333static void
f427ee49 334arm_trace_u32_unix_syscall(int code, arm_saved_state32_t *regs)
5ba3f43e 335{
cb323159
A
336 bool indirect = (regs->save_r12 == 0);
337 if (indirect) {
338 KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START,
339 regs->save_r1, regs->save_r2, regs->save_r3, regs->save_r4);
340 } else {
341 KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START,
342 regs->save_r0, regs->save_r1, regs->save_r2, regs->save_r3);
343 }
5ba3f43e
A
344}
345
346static void
f427ee49 347arm_clear_u32_syscall_error(arm_saved_state32_t *regs)
5ba3f43e
A
348{
349 regs->cpsr &= ~PSR_CF;
f427ee49 350}
5ba3f43e
A
351
352#if defined(__arm__)
353
354static int
f427ee49 355arm_get_syscall_args(uthread_t uthread, struct arm_saved_state *state, const struct sysent *callp)
5ba3f43e
A
356{
357 assert(is_saved_state32(state));
358 return arm_get_u32_syscall_args(uthread, saved_state32(state), callp);
359}
360
361#if __arm__ && (__BIGGEST_ALIGNMENT__ > 4)
f427ee49 362/*
5ba3f43e 363 * For armv7k, the alignment constraints of the ABI mean we don't know how the userspace
f427ee49 364 * arguments are arranged without knowing the the prototype of the syscall. So we use mungers
5ba3f43e
A
365 * to marshal the userspace data into the uu_arg. This also means we need the same convention
366 * as mach syscalls. That means we use r8 to pass arguments in the BSD case as well.
367 */
368static int
f427ee49 369arm_get_u32_syscall_args(uthread_t uthread, arm_saved_state32_t *regs, const struct sysent *callp)
5ba3f43e
A
370{
371 sy_munge_t *munger;
372
373 /* This check is probably not very useful since these both come from build-time */
f427ee49 374 if (callp->sy_arg_bytes > sizeof(uthread->uu_arg)) {
5ba3f43e 375 return -1;
f427ee49 376 }
5ba3f43e
A
377
378 /* get the munger and use it to marshal in the data from userspace */
379 munger = callp->sy_arg_munge32;
f427ee49 380 if (munger == NULL || (callp->sy_arg_bytes == 0)) {
5ba3f43e 381 return 0;
f427ee49 382 }
5ba3f43e
A
383
384 return munger(regs, uthread->uu_arg);
385}
386#else
387/*
388 * For an AArch32 kernel, where we know that we have only AArch32 userland,
389 * we do not do any munging (which is a little confusing, as it is a contrast
390 * to the i386 kernel, where, like the x86_64 kernel, we always munge
391 * arguments from a 32-bit userland out to 64-bit.
392 */
393static int
f427ee49 394arm_get_u32_syscall_args(uthread_t uthread, arm_saved_state32_t *regs, const struct sysent *callp)
5ba3f43e
A
395{
396 int regparams;
397 int flavor = (regs->save_r12 == 0 ? 1 : 0);
f427ee49 398
5ba3f43e
A
399 regparams = (7 - flavor); /* Indirect value consumes a register */
400
f427ee49 401 assert((unsigned) callp->sy_arg_bytes <= sizeof(uthread->uu_arg));
5ba3f43e
A
402
403 if (callp->sy_arg_bytes <= (sizeof(uint32_t) * regparams)) {
404 /*
405 * Seven arguments or less are passed in registers.
406 */
407 memcpy(&uthread->uu_arg[0], &regs->r[flavor], callp->sy_arg_bytes);
408 } else if (callp->sy_arg_bytes <= sizeof(uthread->uu_arg)) {
409 /*
410 * In this case, we composite - take the first args from registers,
411 * the remainder from the stack (offset by the 7 regs therein).
412 */
413 unix_syscall_kprintf("%s: spillover...\n", __FUNCTION__);
f427ee49
A
414 memcpy(&uthread->uu_arg[0], &regs->r[flavor], regparams * sizeof(int));
415 if (copyin((user_addr_t)regs->sp + 7 * sizeof(int), (int *)&uthread->uu_arg[0] + regparams,
416 (callp->sy_arg_bytes - (sizeof(uint32_t) * regparams))) != 0) {
5ba3f43e
A
417 return -1;
418 }
419 } else {
420 return -1;
421 }
422
423 return 0;
424}
425#endif
426
f427ee49 427static unsigned short
5ba3f43e
A
428arm_get_syscall_number(struct arm_saved_state *regs)
429{
430 if (regs->save_r12 != 0) {
f427ee49 431 return (unsigned short)regs->save_r12;
5ba3f43e 432 } else {
f427ee49 433 return (unsigned short)regs->save_r0;
5ba3f43e
A
434 }
435}
436
437static void
f427ee49 438arm_prepare_syscall_return(const struct sysent *callp, struct arm_saved_state *state, uthread_t uthread, int error)
5ba3f43e
A
439{
440 assert(is_saved_state32(state));
441 arm_prepare_u32_syscall_return(callp, state, uthread, error);
442}
443
444static void
445arm_trace_unix_syscall(int code, struct arm_saved_state *state)
446{
447 assert(is_saved_state32(state));
448 arm_trace_u32_unix_syscall(code, saved_state32(state));
449}
450
451static void
f427ee49 452arm_clear_syscall_error(struct arm_saved_state * state)
5ba3f43e
A
453{
454 assert(is_saved_state32(state));
455 arm_clear_u32_syscall_error(saved_state32(state));
456}
457
458#elif defined(__arm64__)
f427ee49
A
459static void arm_prepare_u64_syscall_return(const struct sysent *, arm_saved_state_t *, uthread_t, int);
460static int arm_get_u64_syscall_args(uthread_t, arm_saved_state64_t *, const struct sysent *);
5ba3f43e
A
461
462static int
f427ee49 463arm_get_syscall_args(uthread_t uthread, struct arm_saved_state *state, const struct sysent *callp)
5ba3f43e
A
464{
465 if (is_saved_state32(state)) {
466 return arm_get_u32_syscall_args(uthread, saved_state32(state), callp);
467 } else {
468 return arm_get_u64_syscall_args(uthread, saved_state64(state), callp);
469 }
470}
471
472/*
f427ee49
A
473 * 64-bit: all arguments in registers. We're willing to use x9, a temporary
474 * register per the ABI, to pass an argument to the kernel for one case,
5ba3f43e
A
475 * an indirect syscall with 8 arguments. No munging required, as all arguments
476 * are in 64-bit wide registers already.
477 */
478static int
f427ee49 479arm_get_u64_syscall_args(uthread_t uthread, arm_saved_state64_t *regs, const struct sysent *callp)
5ba3f43e 480{
0a7de745 481 int indirect_offset;
f427ee49 482
d9a64523
A
483#if CONFIG_REQUIRES_U32_MUNGING
484 sy_munge_t *mungerp;
485#endif
486
5ba3f43e 487 indirect_offset = (regs->x[ARM64_SYSCALL_CODE_REG_NUM] == 0) ? 1 : 0;
5ba3f43e 488
f427ee49 489 /*
5ba3f43e
A
490 * Everything should fit in registers for now.
491 */
0a7de745 492 if (callp->sy_narg > (int)(sizeof(uthread->uu_arg) / sizeof(uthread->uu_arg[0]))) {
5ba3f43e
A
493 return -1;
494 }
495
496 memcpy(&uthread->uu_arg[0], &regs->x[indirect_offset], callp->sy_narg * sizeof(uint64_t));
d9a64523
A
497
498#if CONFIG_REQUIRES_U32_MUNGING
499 /*
500 * The indirect system call interface is vararg based. For armv7k, arm64_32,
501 * and arm64, this means we simply lay the values down on the stack, padded to
502 * a width multiple (4 bytes for armv7k and arm64_32, 8 bytes for arm64).
503 * The arm64(_32) stub for syscall will load this data into the registers and
504 * then trap. This gives us register state that corresponds to what we would
505 * expect from a armv7 task, so in this particular case we need to munge the
506 * arguments.
507 *
508 * TODO: Is there a cleaner way to do this check? What we're actually
509 * interested in is whether the task is arm64_32. We don't appear to guarantee
510 * that uu_proc is populated here, which is why this currently uses the
511 * thread_t.
512 */
513 mungerp = callp->sy_arg_munge32;
514 assert(uthread->uu_thread);
515
516 if (indirect_offset && !ml_thread_is64bit(uthread->uu_thread)) {
517 (*mungerp)(&uthread->uu_arg[0]);
518 }
519#endif
520
5ba3f43e
A
521 return 0;
522}
523/*
f427ee49 524 * When the kernel is running AArch64, munge arguments from 32-bit
5ba3f43e
A
525 * userland out to 64-bit.
526 *
527 * flavor == 1 indicates an indirect syscall.
528 */
529static int
f427ee49 530arm_get_u32_syscall_args(uthread_t uthread, arm_saved_state32_t *regs, const struct sysent *callp)
5ba3f43e
A
531{
532 int regparams;
533#if CONFIG_REQUIRES_U32_MUNGING
534 sy_munge_t *mungerp;
535#else
536#error U32 syscalls on ARM64 kernel requires munging
537#endif
538 int flavor = (regs->save_r12 == 0 ? 1 : 0);
539
540 regparams = (7 - flavor); /* Indirect value consumes a register */
541
f427ee49 542 assert((unsigned) callp->sy_arg_bytes <= sizeof(uthread->uu_arg));
5ba3f43e
A
543
544 if (callp->sy_arg_bytes <= (sizeof(uint32_t) * regparams)) {
545 /*
546 * Seven arguments or less are passed in registers.
547 */
548 memcpy(&uthread->uu_arg[0], &regs->r[flavor], callp->sy_arg_bytes);
549 } else if (callp->sy_arg_bytes <= sizeof(uthread->uu_arg)) {
550 /*
551 * In this case, we composite - take the first args from registers,
552 * the remainder from the stack (offset by the 7 regs therein).
553 */
554 unix_syscall_kprintf("%s: spillover...\n", __FUNCTION__);
f427ee49
A
555 memcpy(&uthread->uu_arg[0], &regs->r[flavor], regparams * sizeof(int));
556 if (copyin((user_addr_t)regs->sp + 7 * sizeof(int), (int *)&uthread->uu_arg[0] + regparams,
557 (callp->sy_arg_bytes - (sizeof(uint32_t) * regparams))) != 0) {
5ba3f43e
A
558 return -1;
559 }
560 } else {
561 return -1;
562 }
563
564#if CONFIG_REQUIRES_U32_MUNGING
565 /* Munge here */
566 mungerp = callp->sy_arg_munge32;
567 if (mungerp != NULL) {
568 (*mungerp)(&uthread->uu_arg[0]);
569 }
570#endif
571
572 return 0;
5ba3f43e
A
573}
574
f427ee49 575static unsigned short
5ba3f43e
A
576arm_get_syscall_number(struct arm_saved_state *state)
577{
578 if (is_saved_state32(state)) {
579 if (saved_state32(state)->save_r12 != 0) {
f427ee49
A
580 return (unsigned short)saved_state32(state)->save_r12;
581 } else {
582 return (unsigned short)saved_state32(state)->save_r0;
5ba3f43e
A
583 }
584 } else {
585 if (saved_state64(state)->x[ARM64_SYSCALL_CODE_REG_NUM] != 0) {
f427ee49
A
586 return (unsigned short)saved_state64(state)->x[ARM64_SYSCALL_CODE_REG_NUM];
587 } else {
588 return (unsigned short)saved_state64(state)->x[0];
5ba3f43e
A
589 }
590 }
5ba3f43e
A
591}
592
593static void
f427ee49 594arm_prepare_syscall_return(const struct sysent *callp, struct arm_saved_state *state, uthread_t uthread, int error)
5ba3f43e
A
595{
596 if (is_saved_state32(state)) {
d9a64523 597 arm_prepare_u32_syscall_return(callp, state, uthread, error);
5ba3f43e 598 } else {
d9a64523 599 arm_prepare_u64_syscall_return(callp, state, uthread, error);
5ba3f43e
A
600 }
601}
602
603static void
f427ee49 604arm_prepare_u64_syscall_return(const struct sysent *callp, arm_saved_state_t *regs, uthread_t uthread, int error)
5ba3f43e 605{
d9a64523
A
606 assert(is_saved_state64(regs));
607
608 arm_saved_state64_t *ss64 = saved_state64(regs);
609
5ba3f43e 610 if (error == ERESTART) {
cb323159 611 add_saved_state_pc(regs, -4);
5ba3f43e
A
612 } else if (error != EJUSTRETURN) {
613 if (error) {
d9a64523
A
614 ss64->x[0] = error;
615 ss64->x[1] = 0;
f427ee49 616 /*
5ba3f43e 617 * Set the carry bit to execute cerror routine.
f427ee49 618 * ARM64_TODO: should we have a separate definition?
5ba3f43e
A
619 * The bits are the same.
620 */
d9a64523 621 ss64->cpsr |= PSR_CF;
5ba3f43e 622 unix_syscall_return_kprintf("error: setting carry to trigger cerror call\n");
f427ee49 623 } else { /* (not error) */
5ba3f43e
A
624 switch (callp->sy_return_type) {
625 case _SYSCALL_RET_INT_T:
d9a64523
A
626 ss64->x[0] = uthread->uu_rval[0];
627 ss64->x[1] = uthread->uu_rval[1];
5ba3f43e
A
628 break;
629 case _SYSCALL_RET_UINT_T:
d9a64523
A
630 ss64->x[0] = (u_int)uthread->uu_rval[0];
631 ss64->x[1] = (u_int)uthread->uu_rval[1];
5ba3f43e
A
632 break;
633 case _SYSCALL_RET_OFF_T:
634 case _SYSCALL_RET_ADDR_T:
635 case _SYSCALL_RET_SIZE_T:
636 case _SYSCALL_RET_SSIZE_T:
637 case _SYSCALL_RET_UINT64_T:
d9a64523
A
638 ss64->x[0] = *((uint64_t *)(&uthread->uu_rval[0]));
639 ss64->x[1] = 0;
5ba3f43e
A
640 break;
641 case _SYSCALL_RET_NONE:
642 break;
643 default:
644 panic("unix_syscall: unknown return type");
645 break;
646 }
647 }
648 }
649 /* else (error == EJUSTRETURN) { nothing } */
5ba3f43e
A
650}
651static void
f427ee49 652arm_trace_u64_unix_syscall(int code, arm_saved_state64_t *regs)
5ba3f43e 653{
cb323159
A
654 bool indirect = (regs->x[ARM64_SYSCALL_CODE_REG_NUM] == 0);
655 if (indirect) {
656 KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START,
657 regs->x[1], regs->x[2], regs->x[3], regs->x[4]);
658 } else {
659 KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START,
660 regs->x[0], regs->x[1], regs->x[2], regs->x[3]);
661 }
5ba3f43e
A
662}
663
664static void
665arm_trace_unix_syscall(int code, struct arm_saved_state *state)
666{
667 if (is_saved_state32(state)) {
668 arm_trace_u32_unix_syscall(code, saved_state32(state));
669 } else {
670 arm_trace_u64_unix_syscall(code, saved_state64(state));
671 }
672}
673
674static void
675arm_clear_u64_syscall_error(arm_saved_state64_t *regs)
676{
f427ee49
A
677 /*
678 * ARM64_TODO: should we have a separate definition?
679 * The bits are the same.
5ba3f43e
A
680 */
681 regs->cpsr &= ~PSR_CF;
682}
683
684static void
f427ee49 685arm_clear_syscall_error(struct arm_saved_state * state)
5ba3f43e
A
686{
687 if (is_saved_state32(state)) {
688 arm_clear_u32_syscall_error(saved_state32(state));
689 } else {
690 arm_clear_u64_syscall_error(saved_state64(state));
691 }
692}
693
f427ee49 694#else
5ba3f43e
A
695#error Unknown architecture.
696#endif