Home | History | Annotate | Line # | Download | only in aarch64
      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
     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
    233 EMULNAME(syscall_intern)(struct proc *p)
    234 {
    235 	p->p_md.md_syscall = EMULNAME(syscall);
    236 }
    237