[BACK]Return to aarch32_syscall.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / arch / aarch64 / aarch64

File: [cvs.NetBSD.org] / src / sys / arch / aarch64 / aarch64 / aarch32_syscall.c (download)

Revision 1.4, Sat May 15 11:38:26 2021 UTC (2 years, 11 months ago) by rin
Branch: MAIN
Changes since 1.3: +20 -5 lines

Fix __syscall(2) for COMPAT_NETBSD32 on aarch64{,eb}.

The 1st argument for __syscall(2) is quad_t, which is stored in r0 and r1.

Now, tests/lib/libc/t_syscall:mmap___syscall passes for COMPAT_NETBSD32.

/*	$NetBSD: aarch32_syscall.c,v 1.4 2021/05/15 11:38:26 rin Exp $	*/

/*
 * Copyright (c) 2018 Ryo Shimizu <ryo@nerv.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: aarch32_syscall.c,v 1.4 2021/05/15 11:38:26 rin Exp $");

#include <sys/param.h>
#include <sys/ktrace.h>
#include <sys/proc.h>
#include <sys/syscallvar.h>
#include <uvm/uvm_extern.h>

#include <aarch64/armreg.h>
#include <aarch64/frame.h>
#include <aarch64/machdep.h>
#include <aarch64/userret.h>

#ifndef EMULNAME
#error EMULNAME is not defined
#endif

#ifndef NARGREG
#define NARGREG		4		/* 4 args are in registers */
#endif

static void EMULNAME(syscall)(struct trapframe *);

union args {
	register_t a64[EMULNAMEU(SYS_MAXSYSARGS)];
	register32_t a32[EMULNAMEU(SYS_MAXSYSARGS)];
};

void
EMULNAME(syscall)(struct trapframe *tf)
{
	struct lwp * const l = curlwp;
	struct proc * const p = l->l_proc;
	const struct sysent *callp;
	union args args64buf, args32buf;
	register_t rval[2];
	register32_t *args32 = args32buf.a32;
	int error, i;
	bool do_trace, thumbmode;

	LWP_CACHE_CREDS(l, p);

	curcpu()->ci_data.cpu_nsyscall++;

	uint32_t code = tf->tf_esr & 0xffff;	/* XXX: 16-23bits are omitted */
	thumbmode = (tf->tf_spsr & SPSR_A32_T) ? true : false;
	if (thumbmode) {
		if (code != 255) {
			do_trapsignal(l, SIGILL, ILL_ILLTRP,
			    (void *)(tf->tf_pc - 2), tf->tf_esr);
			error = EINVAL;
			goto bad;
		}
		code = tf->tf_reg[0];
		tf->tf_reg[0] = tf->tf_reg[12];	/* orig $r0 is saved to $ip */
	}

	int nargs_reg = NARGREG;	/* number of argument in registers */
	int regstart = 0;		/* args start from r0 */


	code %= EMULNAMEU(SYS_NSYSENT);
	callp = p->p_emul->e_sysent + code;
	if (__predict_false(callp->sy_flags & SYCALL_INDIRECT)) {
		int off = 1;
#ifdef NETBSD32_SYS_netbsd32____syscall /* XXX ugly: apply only for NETBSD32 */
		/*
		 * For __syscall(2), 1st argument is quad_t, which is
		 * stored in r0 and r1.
		 */
		if (code == NETBSD32_SYS_netbsd32____syscall)
			off = 2;
#endif
		nargs_reg -= off;
		regstart = off;	/* args start from r1 or r2 */
#ifdef __AARCH64EB__
		if (off == 2)
			code = tf->tf_reg[1];
		else
#endif
			code = tf->tf_reg[0];
		code %= EMULNAMEU(SYS_NSYSENT);
		callp = p->p_emul->e_sysent + code;

		/* don't allow nested syscall */
		if (__predict_false(callp->sy_flags & SYCALL_INDIRECT)) {
			error = EINVAL;
			goto bad;
		}
	}

	/* number of argument to fetch from sp */
	KASSERT(callp->sy_narg <= EMULNAMEU(SYS_MAXSYSARGS));
	int nargs_sp = callp->sy_narg - nargs_reg;

	/* fetch arguments from tf and sp, and store to args32buf[] */
	for (i = 0; i < nargs_reg; i++)
		*args32++ = (uint32_t)tf->tf_reg[regstart++];
	if (nargs_sp > 0) {
		error = copyin(
		    (void*)(uintptr_t)(uint32_t)tf->tf_reg[13],	/* sp = r13 */
		    args32, nargs_sp * sizeof(register32_t));
		if (error)
			goto bad;
	}

	rval[0] = rval[1] = 0;

#if 0
	error = sy_invoke(callp, l, args32buf.a32, rval, code);
#else
	/*
	 * XXX: trace_enter()/trace_exit() called from sy_invoke() expects
	 *      64bit args, but sy_invoke doesn't take care of it.
	 *      therefore call trace_enter(), sy_call(), trace_exit() manually.
	 */
#ifdef KDTRACE_HOOKS
#define KDTRACE_ENTRY(a)	(a)
#else
#define KDTRACE_ENTRY(a)	(0)
#endif
	do_trace = p->p_trace_enabled &&
	    ((callp->sy_flags & SYCALL_INDIRECT) == 0);
	if (__predict_false(do_trace ||
	    KDTRACE_ENTRY(callp->sy_entry) || KDTRACE_ENTRY(callp->sy_return))) {
		/* build 64bit args for trace_enter()/trace_exit() */
		int nargs = callp->sy_narg;
		for (i = 0; i < nargs; i++)
			args64buf.a64[i] = args32buf.a32[i];
	}

	if (__predict_false(do_trace || KDTRACE_ENTRY(callp->sy_entry)))
		error = trace_enter(code, callp, args64buf.a64);

	if (error == 0)
		error = sy_call(callp, l, args32buf.a32, rval);

	if (__predict_false(do_trace || KDTRACE_ENTRY(callp->sy_return)))
		trace_exit(code, callp, args64buf.a64, rval, error);
#endif

	if (__predict_true(error == 0)) {
		tf->tf_reg[0] = rval[0];
		tf->tf_reg[1] = rval[1];
		tf->tf_spsr &= ~NZCV_C;
	} else {
		switch (error) {
		case ERESTART:
			/* redo system call insn */
			if (thumbmode)
				tf->tf_pc -= 2;
			else
				tf->tf_pc -= 4;
			break;
		case EJUSTRETURN:
			/* nothing to do */
			break;
		default:
		bad:
#ifndef __HAVE_MINIMAL_EMUL
			if (p->p_emul->e_errno)
				error = p->p_emul->e_errno[error];
#endif
			tf->tf_reg[0] = error;
			tf->tf_spsr |= NZCV_C;
			break;
		}
	}

	userret(l);
}

void EMULNAME(syscall_intern)(struct proc *);

void
EMULNAME(syscall_intern)(struct proc *p)
{
	p->p_md.md_syscall = EMULNAME(syscall);
}