/* $NetBSD: syscall.c,v 1.25 2005/12/11 12:18:46 christos Exp $ */ /* * Copyright (C) 2002 Matt Thomas * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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 "opt_altivec.h" #include "opt_ktrace.h" #include "opt_systrace.h" #include "opt_multiprocessor.h" /* DO NOT INCLUDE opt_compat_XXX.h */ /* If needed, they will be included by file that includes this one */ #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #ifdef SYSTRACE #include #endif #include #include #include #include #define FIRSTARG 3 /* first argument is in reg 3 */ #define NARGREG 8 /* 8 args are in registers */ #define MOREARGS(sp) ((caddr_t)((uintptr_t)(sp) + 8)) /* more args go here */ #ifndef EMULNAME #include #define EMULNAME(x) (x) #define EMULNAMEU(x) (x) __KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.25 2005/12/11 12:18:46 christos Exp $"); void child_return(void *arg) { struct lwp * const l = arg; #ifdef KTRACE struct proc * const p = l->l_proc; #endif struct trapframe * const tf = trapframe(l); KERNEL_PROC_UNLOCK(l); tf->fixreg[FIRSTARG] = 0; tf->fixreg[FIRSTARG + 1] = 1; tf->cr &= ~0x10000000; tf->srr1 &= ~(PSL_FP|PSL_VEC); /* Disable FP & AltiVec, as we can't be them. */ l->l_addr->u_pcb.pcb_fpcpu = NULL; #ifdef KTRACE if (KTRPOINT(p, KTR_SYSRET)) { KERNEL_PROC_LOCK(l); ktrsysret(l, SYS_fork, 0, 0); KERNEL_PROC_UNLOCK(l); } #endif /* Profiling? XXX */ curcpu()->ci_schedstate.spc_curpriority = l->l_priority; } #endif static void EMULNAME(syscall_plain)(struct trapframe *); void EMULNAME(syscall_plain)(struct trapframe *frame) { struct lwp *l = curlwp; struct proc *p = l->l_proc; const struct sysent *callp; size_t argsize; register_t code; register_t *params, rval[2]; register_t args[10]; int error; int n; curcpu()->ci_ev_scalls.ev_count++; code = frame->fixreg[0]; params = frame->fixreg + FIRSTARG; n = NARGREG; #ifdef COMPAT_MACH if ((callp = mach_syscall_dispatch(&code)) == NULL) #endif /* COMPAT_MACH */ { switch (code) { case EMULNAMEU(SYS_syscall): /* * code is first argument, * followed by actual args. */ code = *params++; n -= 1; break; #if !defined(COMPAT_LINUX) case EMULNAMEU(SYS___syscall): params++; code = *params++; n -= 2; break; #endif default: break; } callp = p->p_emul->e_sysent + (code & (EMULNAMEU(SYS_NSYSENT)-1)); } argsize = callp->sy_argsize; if (argsize > n * sizeof(register_t)) { memcpy(args, params, n * sizeof(register_t)); KERNEL_PROC_LOCK(l); error = copyin(MOREARGS(frame->fixreg[1]), args + n, argsize - n * sizeof(register_t)); KERNEL_PROC_UNLOCK(l); if (error) goto bad; params = args; } rval[0] = 0; rval[1] = 0; if ((callp->sy_flags & SYCALL_MPSAFE) == 0) { KERNEL_PROC_LOCK(l); } error = (*callp->sy_call)(l, params, rval); if ((callp->sy_flags & SYCALL_MPSAFE) == 0) { KERNEL_PROC_UNLOCK(l); } switch (error) { case 0: frame->fixreg[FIRSTARG] = rval[0]; frame->fixreg[FIRSTARG + 1] = rval[1]; frame->cr &= ~0x10000000; #ifdef COMPAT_MACH /* * For regular system calls, on success, * the next instruction is skipped */ if ((frame->fixreg[0] < p->p_emul->e_nsysent) && (frame->fixreg[0] >= 0)) frame->srr0 += 4; #endif /* COMPAT_MACH */ break; case ERESTART: /* * Set user's pc back to redo the system call. */ frame->srr0 -= 4; break; case EJUSTRETURN: /* nothing to do */ break; default: bad: if (p->p_emul->e_errno) error = p->p_emul->e_errno[error]; frame->fixreg[FIRSTARG] = error; frame->cr |= 0x10000000; break; } userret(l, frame); } #if defined(KTRACE) || defined(SYSTRACE) static void EMULNAME(syscall_fancy)(struct trapframe *); void EMULNAME(syscall_fancy)(struct trapframe *frame) { struct lwp *l = curlwp; struct proc *p = l->l_proc; const struct sysent *callp; size_t argsize; register_t code; register_t realcode; register_t *params, rval[2]; register_t args[10]; int error; int n; KERNEL_PROC_LOCK(l); curcpu()->ci_ev_scalls.ev_count++; code = frame->fixreg[0]; params = frame->fixreg + FIRSTARG; n = NARGREG; realcode = code; #ifdef COMPAT_MACH if ((callp = mach_syscall_dispatch(&code)) == NULL) #endif /* COMPAT_MACH */ { switch (code) { case EMULNAMEU(SYS_syscall): /* * code is first argument, * followed by actual args. */ code = *params++; n -= 1; break; #if !defined(COMPAT_LINUX) case EMULNAMEU(SYS___syscall): params++; code = *params++; n -= 2; break; #endif default: break; } code &= EMULNAMEU(SYS_NSYSENT) - 1; callp = p->p_emul->e_sysent + code; realcode = code; } argsize = callp->sy_argsize; if (argsize > n * sizeof(register_t)) { memcpy(args, params, n * sizeof(register_t)); error = copyin(MOREARGS(frame->fixreg[1]), args + n, argsize - n * sizeof(register_t)); if (error) goto bad; params = args; } if ((error = trace_enter(l, code, realcode, callp - code, params)) != 0) goto out; rval[0] = 0; rval[1] = 0; error = (*callp->sy_call)(l, params, rval); out: switch (error) { case 0: frame->fixreg[FIRSTARG] = rval[0]; frame->fixreg[FIRSTARG + 1] = rval[1]; frame->cr &= ~0x10000000; #ifdef COMPAT_MACH /* * For regular system calls, on success, * the next instruction is skipped */ if ((frame->fixreg[0] < p->p_emul->e_nsysent) && (frame->fixreg[0] >= 0)) frame->srr0 += 4; #endif /* COMPAT_MACH */ break; case ERESTART: /* * Set user's pc back to redo the system call. */ frame->srr0 -= 4; break; case EJUSTRETURN: /* nothing to do */ break; default: bad: if (p->p_emul->e_errno) error = p->p_emul->e_errno[error]; frame->fixreg[FIRSTARG] = error; frame->cr |= 0x10000000; break; } KERNEL_PROC_UNLOCK(l); trace_exit(l, realcode, params, rval, error); userret(l, frame); } #endif /* KTRACE || SYSTRACE */ void EMULNAME(syscall_intern)(struct proc *); void EMULNAME(syscall_intern)(struct proc *p) { #ifdef KTRACE if (p->p_traceflag & (KTRFAC_SYSCALL | KTRFAC_SYSRET)) { p->p_md.md_syscall = EMULNAME(syscall_fancy); return; } #endif #ifdef SYSTRACE if (ISSET(p->p_flag, P_SYSTRACE)) { p->p_md.md_syscall = EMULNAME(syscall_fancy); return; } #endif p->p_md.md_syscall = EMULNAME(syscall_plain); }