/* Machine dependencies in the emulator for i386
   Copyright (C) 1991 Free Software Foundation

This file is part of the GNU Hurd.

The GNU Hurd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

The GNU Hurd is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with the GNU Hurd; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Written by Michael I. Bushnell.  */

#include <mach/mach_traps.h>	/* must be first */
#include <mach.h>
#include <cthreads.h>
#include <mach/error.h>
#include <mach/exception.h>
#include <mach/exc.h>
#include <gnu/posix_errors.h>
#include <gnu/types.h>
#include <gnu/param.h>
#include <gnu/errno.h>
#include <gnu/signal.h>
#include <gnu/wait.h>
#include <lib/libhurd.h>

#include "fs.h"
#include "io.h"
#include "socket.h"
#include "process.h"

#include <i386/eflags.h>
#include <i386/alix_machdep.h>
#include "alix.h"
#include "alix_sysent.h"
#include "alix_proc.h"

/* Signal codes for signals arising from hardware */
static int sigcodes[NSIG];

/* These are offsets from the argument to syscall for each of the
   various registers, and correspond to the order of pushes in 
   i386/alix_vector.s (syscall_vector).  */

#define EDX	0
#define EBX	1
#define EBP	2
#define ESI	3
#define EDI	4
#define ECX	5
#define EAX	6
#define EFL	7
#define EIP	8
#define ESP	9		/* Stack pointer of syscall user */

/* number of regs on stack */
#define NREGS 10

vm_size_t vm_page_size;
mach_port_t mach_task_self_ = MACH_PORT_NULL;

static mach_port_t initial_reply_port;

/* called from assembly language emulation vector */
void
syscall (int *regs)
{
  int callno;
  int ret1, ret2;
  int *args;
  int *uargs;
  int err;
  struct sysent *se;
  struct systab *st;

  callno = regs[EAX];		/* system call number goes in eax */

  /* skip return address in syscall stub */
  uargs = (int *)(regs[ESP] + sizeof (int));

  
  while (callno == 0)
    {
      /* Indirect system call.  */
      err = copyin (uargs, &callno, sizeof (int));
      if (err)
	goto error_out;
      uargs++;
    }
  
  se = 0;
  for (st = systab; st->tab; st++)
    if (callno >= st->min && callno <= st->max)
      {
	se = &st->tab[callno - st->min];
	break;
      }
  if (!se || !se->vector)
    {
      post_sigsys ();
      err = EINVAL;
      goto error_out;
    }

  args = alloca (se->nargs * sizeof (int));

  err = copyin (uargs, args, se->nargs * sizeof (int));
  if (err)
    goto error_out;

  ret1 = ret2 = 0;
  
  if (se->modregs)
    err = (*(syscallreg_t *)se->vector) (args, &ret1, &ret2, regs);
  else
    err = (*(syscall_t *)se->vector)(args, &ret1, &ret2);

 error_out:
  switch (err)
    {
    case NORMRETURN:
      regs[EAX] = ret1;
      regs[EDX] = ret2;
      regs[EFL] &= ~EFL_CF;
      break;
      
    default:
      regs[EAX] = err;
      regs[EFL] |= EFL_CF;
      break;
      
    case JUSTRETURN:
      /* Nothing.  */
      break;
      
    case RESTART:
      /* Back up instruction pointer before syscall instruction */
      regs[EIP] -= 7;
      break;
    }

  check_sigs (regs);
}



void
fetch_state (thread_t thread, 
	     int *state)
{
  u_int tssize = i386_THREAD_STATE_COUNT;
  
  thread_get_state (thread, i386_THREAD_STATE, state, &tssize);
  assert (tssize == i386_THREAD_STATE_COUNT);
}

int
thread_in_emulator (int *state)
{
  struct i386_thread_state *ts;
  
  ts = (struct i386_thread_state *) state;
  
  if (ts->eip > EMUL_MINADDR && ts->eip < EMUL_MAXADDR
      && (ts->eip < (int)trampoline || ts->eip > (int)endtrampoline))
    return 1;
  else
    return 0;
}

void
abort_thread_rpc (thread_t thread,
		  int *state,
		  int restart)
{
  struct i386_thread_state *ts;
  struct stackbase *base;

  ts = (struct i386_thread_state *) state;
  base = (struct stackbase *) (ts->uesp & STACK_MASK);
  
  if (base->in_intr_call)
    {
      base->call_intred = (restart ? RESTART : POSIX_EINTR);
      thread_abort (thread);
      switch (base->intr_type)
	{
	case IO_INTR:
	  io_interrupt (base->intr_port);
	  break;
	case WAIT_INTR:
	  proc_waitintr (base->intr_port);
	  break;
	}
    }
}


  
void
regs_to_sigcontext (int *regs,
		    struct sigcontext *scp)
{
  /* Segment registers??? XXX */
  scp->sc_edi = regs[EDI];
  scp->sc_esi = regs[ESI];
  scp->sc_ebp = regs[EBP];

  scp->sc_ebx = regs[EBX];
  scp->sc_edx = regs[EDX];
  scp->sc_ecx = regs[ECX];
  scp->sc_eax = regs[EAX];

  scp->sc_eip = regs[EIP];
  scp->sc_uesp = regs[ESP];
  scp->sc_efl = regs[EFL];
}

void
threadstate_to_sigcontext (struct i386_thread_state *ts,
			   struct sigcontext *scp)
{
  /* Segment registers??? XXX */
  scp->sc_edi = ts->edi;
  scp->sc_esi = ts->esi;
  scp->sc_ebp = ts->ebp;

  scp->sc_ebx = ts->ebx;
  scp->sc_edx = ts->edx;
  scp->sc_ecx = ts->ecx;
  scp->sc_eax = ts->eax;
  
  scp->sc_eip = ts->eip;
  scp->sc_uesp = ts->uesp;
  scp->sc_efl = ts->efl;
}

int
push_sigstuff (int oldsp,
	       struct sigcontext *scp,
	       int handler,
	       int signo,
	       int flags,
	       int omask,
	       struct sigstack *ss)
{
  int code;
  void *sigsp;
  struct 
    {
      int handler;
      int number;
      int code;
      int sigcaddr1;
      int sigcaddr2;
      struct sigcontext ctx;
    }
  stackframe;
  
  code = sigcodes[signo];
  sigcodes[signo] = 0;
  
  scp->sc_onstack = ss->ss_onstack;
  scp->sc_mask = omask;
  
  if (flags & SA_ONSTACK && !ss->ss_onstack)
    {
      sigsp = ss->ss_sp;
      ss->ss_onstack = 1;
    }
  else
    sigsp = (void *)oldsp;

  /* Push signal context onto stack */
  sigsp -= sizeof stackframe;
  stackframe.handler = handler;
  stackframe.number = signo;
  stackframe.code = code;
  bcopy (scp, &stackframe.ctx, sizeof (struct sigcontext));
  stackframe.sigcaddr1 = (int) (sigsp + 
				(int) &stackframe.ctx - (int) &stackframe);
  stackframe.sigcaddr2 = stackframe.sigcaddr1;

  if (copyout (&stackframe, sigsp, sizeof stackframe))
    return 0;
  
  return (int)sigsp;
}
  
/* Called from check_sigs when the current syscall should return to a
   signal handler */
void
set_sig_handler (int *regs, 
		 int signo,
		 int handler,
		 int flags,
		 int omask,
		 struct sigstack *ss)
{
  int sigsp;			/* where we push stuff */
  struct sigcontext scp;

  regs_to_sigcontext (regs, &scp);

  sigsp = push_sigstuff (regs[ESP], &scp, handler, signo, flags, omask, ss);

  if (sigsp)
    {
      regs[EIP] = (int) trampoline;
      regs[ESP] = sigsp;
    }
  else
    {
      /* At this point, we are hosed.  BSD exits with SIGILL here.  If
	 it's good enough for them, it's good enough for me. */
      abort_rpcs (0);
      proc_exit (procserver,
		 W_EXITCODE (0, SIGILL)
		 | (write_corefile () ? WCOREFLAG : 0));
      task_terminate (mach_task_self ());
    }
}


/* Called by signal thread when a thread running in user space (and
   currently suspended) should be "adjusted" to execute a signal handler.
   */
void
zap_thread_sighandler (thread_t thread,
		       int *state,
		       int signo,
		       int handler,
		       int flags,
		       int oldmask,
		       struct sigstack *ss)
{
  struct i386_thread_state *ts;
  int sigsp;
  struct sigcontext scp;

  thread_abort (thread);

  ts = (struct i386_thread_state *)state;
  
  threadstate_to_sigcontext (ts, &scp);

  sigsp = push_sigstuff (ts->uesp, &scp, handler, signo, flags, oldmask, ss);
  
  if (sigsp)
    {
      ts->uesp = sigsp;
      ts->eip = (int) trampoline;
      thread_set_state (thread, i386_THREAD_STATE, (int *)ts,
			i386_THREAD_STATE_COUNT);
    }
  else
    {
      abort_rpcs (0);
      proc_exit (procserver,
		 W_EXITCODE (0, SIGILL)
		 | (write_corefile () ? WCOREFLAG : 0));
      task_terminate (mach_task_self ());
    }
}

/* Called by microkernel when a thread gets an exception */
int
catch_exception_raise (mach_port_t port,
		       thread_t thread,
		       task_t task,
		       int exception,
		       int code,
		       int subcode)
{
  int signo, sigcode;
  struct stackbase *base;
  struct i386_thread_state ts;
  u_int tssize = i386_THREAD_STATE_COUNT;

  /* Verify various bits */
  if (port != sigthread_port || task != mach_task_self ())
    return KERN_SUCCESS;	/* ??? */
    
  switch (exception)
    {
    default:
      signo = SIGIOT;
      sigcode = exception;
      break;
      
    case EXC_BAD_ACCESS:
      /* XXX detect copy of mapped I/O region and handle differently. */
      thread_get_state (thread, i386_THREAD_STATE, (int *)&ts, &tssize);
      assert (tssize == i386_THREAD_STATE_COUNT);
      base = (struct stackbase *)(ts.uesp % STACK_MASK);
      if (base->in_ucopy && subcode >= (int) base->ucopy_ubase
	  && subcode < (int) base->ucopy_ubase + base->ucopy_len)
	{
	  ts.eip = base->ucopy_pc;
	  ts.uesp = base->ucopy_sp;
	  ts.eax = EFAULT;
	  base->in_ucopy = 0;
	  thread_set_state (thread, i386_THREAD_STATE, (int *)&ts, tssize);
	  return KERN_SUCCESS;
	}

      if (code == KERN_PROTECTION_FAILURE)
	signo = SIGSEGV;
      else
	signo = SIGBUS;
      sigcode = subcode;
      break;

    case EXC_BAD_INSTRUCTION:
      signo = SIGILL;
      if (code == EXC_I386_INVOP)
	sigcode = ILL_INVOPR_FAULT;
      else if (code == EXC_I386_STKFLT)
	sigcode = ILL_STACK_FAULT;
      else
	sigcode = 0;
      break;
      
    case EXC_ARITHMETIC:
      switch (code)
	{
	case EXC_I386_DIV:	/* integer divide by zero */
	  signo = SIGFPE;
	  sigcode = FPE_INTDIV_FAULT;
	  break;
	  
	case EXC_I386_INTO:	/* integer overflow */
	  signo = SIGFPE;
	  sigcode = FPE_INTOVF_TRAP;
	  break;

	  /* These aren't anywhere documented or used in Mach 3.0 */
	case EXC_I386_NOEXT:
	case EXC_I386_EXTOVR:
	default:
	  signo = SIGFPE;
	  sigcode = 0;
	  break;

	case EXC_I386_EXTERR:
	  /* Subcode is the fp_status word saved by the hardware. */
	  /* Give an error code corresponding to the first bit set. */
	  if (subcode & FPS_IE)
	    {
	      signo = SIGILL;
	      sigcode = ILL_FPEOPR_FAULT;
	    }
	  else if (subcode & FPS_DE)
	    {
	      signo = SIGFPE;
	      sigcode = FPE_FLTDNR_FAULT;
	    }
	  else if (subcode & FPS_ZE)
	    {
	      signo = SIGFPE;
	      sigcode = FPE_FLTDIV_FAULT;
	    }
	  else if (subcode & FPS_OE)
	    {
	      signo = SIGFPE;
	      sigcode = FPE_FLTOVF_FAULT;
	    }
	  else if (subcode & FPS_UE)
	    {
	      signo = SIGFPE;
	      sigcode = FPE_FLTUND_FAULT;
	    }
	  else if (subcode & FPS_PE)
	    {
	      signo = SIGFPE;
	      sigcode = FPE_FLTINX_FAULT;
	    }
	  else
	    {
	      signo = SIGFPE;
	      sigcode = 0;
	    }
	  break;

	  /* These two can only be arithmetic exceptions if we 
	     are in V86 mode, which sounds like emulation to me.
	     (see Mach 3.0 i386/trap.c)  */
	case EXC_I386_EMERR:
	  signo = SIGFPE;
	  sigcode = FPE_EMERR_FAULT;
	  break;
	case EXC_I386_BOUND:
	  signo = SIGFPE;
	  sigcode = FPE_EMBND_FAULT;
	  break;
	}
      break;

    case EXC_EMULATION:		
      /* 3.0 doesn't give this one, why, I don't know */
      signo = SIGEMT;
      sigcode = 0;
      break;

    case EXC_SOFTWARE:
      /* The only time we get this in Mach 3.0 is for an out of bounds
	 trap.  */
      if (code == EXC_I386_BOUND)
	{
	  signo = SIGFPE;
	  sigcode = FPE_SUBRNG_FAULT;
	}
      else
	{
	  signo = SIGEMT;
	  sigcode = 0;
	}
      break;
      
    case EXC_BREAKPOINT:
      signo = SIGTRAP;
      if (code == EXC_I386_SGL)
	sigcode = DBG_SINGLE_TRAP;
      else if (code == EXC_I386_BPT)
	sigcode = DBG_BRKPNT_FAULT;
      else
	sigcode = 0;
      break;
    }
  
  /* The above icky switch has set signo and sigcode appropriately.  
     Now we can do the real work.  */
  
  sigcodes[signo] = sigcode;
  
  internal_sig_post (thread, signo);
  return KERN_SUCCESS;
}


#ifdef COMPAT_43
int
wait_compat43 (void *ap, int *ret1, int *ret2, int *uregs)
{
#define EFL_ALLCC (EFL_CF|EFL_PF|EFL_ZF|EFL_SF)
  if ((uregs[EFL] & EFL_ALLCC) == EFL_ALLCC)
    return wait3 (uregs[ECX], uregs[EDX], ret1, ret2);
  else
    return wait0 (ret1, ret2);
}
#endif

/* SYSCALL: atomically set registers and modify signal state */
int
sigreturn (void *ap, int *ret1, int *ret2, int *uregs)
{
  struct 
    {
      struct sigcontext *scp;
    }
  *args = ap;
  struct sigcontext scp;
  int err;
  struct sigstate *ss;
  
  err = copyin (args->scp, &scp, sizeof (struct sigcontext));
  if (err)
    return err;

  ss = thread_to_sigstate (mach_thread_self ());
  ss->mask = scp.sc_mask;
  ss->sigstack.ss_onstack = scp.sc_onstack;
  mutex_unlock (&ss->lock);

  /* Segment registers??? XXX */
  uregs[EDI] = scp.sc_edi;
  uregs[ESI] = scp.sc_esi;
  uregs[EBP] = scp.sc_ebp;

  uregs[EBX] = scp.sc_ebx;
  uregs[EDX] = scp.sc_edx;
  uregs[ECX] = scp.sc_ecx;
  uregs[EAX] = scp.sc_eax;
  
  uregs[EIP] = scp.sc_eip;
  uregs[EFL] = scp.sc_efl;  /* Verify flags??? XXX */
  uregs[ESP] = scp.sc_uesp;
  
  return JUSTRETURN;
}



/* Start initial user thread running.  */
void
start_user_thread (int user_eip,
		   int user_esp)
{
  struct i386_thread_state ts;
  u_int tssize = i386_THREAD_STATE_COUNT;
  thread_t thread;
  
  thread_create (mach_task_self (), &thread);
  thread_get_state (thread, i386_THREAD_STATE, (int *)&ts, &tssize);
  assert (tssize == i386_THREAD_STATE_COUNT);
  
  ts.uesp = user_esp;
  ts.eip = user_eip;
  thread_set_state (thread, i386_THREAD_STATE, (int *)&ts, tssize);
  thread_resume (thread);
}

/* Allocate a stack and a MiG reply port */
/* Cannot have heavy locals; might be running on very short stack. */
void *
stack_alloc ()
{
  struct stackbase *base;
  error_t err;
  
  base = 0;
  err = vm_map (mach_task_self (), (u_int *)&base, STACK_SIZE, 
		STACK_MASK, 1, MEMORY_OBJECT_NULL, 0, 0, 
		VM_PROT_READ|VM_PROT_WRITE, 
		VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);

  base->reply_port = mach_reply_port ();
  base->in_intr_call = 0;
  base->call_intred = NORMRETURN;
  
  return base + STACK_SIZE - sizeof (int);
}

/* XXX */
void
mig_init ()
{
}

mach_port_t
mig_get_reply_port ()
{
  int sp;
  struct stackbase *base;

  asm ("movl %%esp,%0" : "=r" (sp));
  if ((sp & INT_STACK_MASK) == INT_STACK_SIZE)
    {
      if (!initial_reply_port)
	initial_reply_port = mach_reply_port ();
      return initial_reply_port;
    }
  else
    {
      base = (struct stackbase *)(sp & STACK_MASK);
      return base->reply_port;
    }
}

/* Called when there might be guck on the reply port */
void
mig_dealloc_reply_port ()
{
  int sp;
  struct stackbase *base;
  
  asm ("movl %%esp,%0" : "=r" (sp));
  if ((sp & INT_STACK_MASK) == INT_STACK_SIZE)
    {
      if (initial_reply_port)
	mach_port_mod_refs (mach_task_self (), initial_reply_port, 
			    MACH_PORT_RIGHT_RECEIVE, -1);
      initial_reply_port = mach_reply_port ();
    }
  else
    {
      base = (struct stackbase *) (sp & STACK_MASK);
      if (base->reply_port)
	mach_port_mod_refs (mach_task_self (), base->reply_port,
			    MACH_PORT_RIGHT_RECEIVE, -1);
      base->reply_port = mach_reply_port ();
    }
}

/* Set the new user thread up.  This thread should return the user
   fork system call.  */
void
set_newuser_thread (task_t task,
		    thread_t thread, 
		    int *regs,
		    int newpid)
{
  int base;
  int stackend;
  int local_regloc, user_regloc;
  pointer_t lastpage;
  u_int size;
  struct i386_thread_state ts;
  u_int tssize = i386_THREAD_STATE_COUNT;
    
  /* Create a stack for the user thread */
  base = 0;
  vm_map (task, (u_int *)&base, STACK_SIZE, STACK_MASK, 1, 
	  MEMORY_OBJECT_NULL, 0, 0, VM_PROT_READ|VM_PROT_WRITE, 
	  VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
  stackend = base + STACK_SIZE;

  /* Push return state onto user's stack */

  vm_read (task, stackend - vm_page_size, vm_page_size, &lastpage, &size);
  assert (size == vm_page_size);

  local_regloc = lastpage + vm_page_size - sizeof (int) * NREGS;
  user_regloc = stackend - sizeof (int) * NREGS;

  bcopy (regs, &local_regloc, sizeof (int) * NREGS);
  regs = (int *)local_regloc;
  
  regs[EAX] = newpid;		/* process id */
  regs[EDX] = 1;		/* return 1 in child */
  regs[EFL] &= ~EFL_CF;		/* no error */
  
  vm_write (task, stackend - vm_page_size, vm_page_size, lastpage);
  vm_deallocate (mach_task_self (), lastpage, vm_page_size);

  /* Set user state to be at fork init point */
  thread_get_state (thread, i386_THREAD_STATE, (int *)&ts, &tssize);
  assert (tssize == i386_THREAD_STATE_COUNT);
  ts.uesp = user_regloc;
  ts.eip = (int)fork_init_point;
  ts.ebx = (int)&((struct stackbase *)base)->reply_port;
  thread_set_state (thread, i386_THREAD_STATE, (int *)&ts, tssize);
}

/* Set the new emulator thread up in a child (forked) process.  */
void
set_newemul_thread (task_t task,
		    thread_t thread)
{
  struct i386_thread_state ts;
  u_int tssize = i386_THREAD_STATE_COUNT;
  
  thread_get_state (thread, i386_THREAD_STATE, (int *)&ts, &tssize);
  assert (tssize == i386_THREAD_STATE_COUNT);
  ts.eip = (int)emul_thd_init_point;
  thread_set_state (thread, i386_THREAD_STATE, (int *)&ts, tssize);
}
  

/* Stubs for the interruptible RPC's */

#if 0
#define INTR_CALL_N(type, name, decl, call)				    \
error_t									    \
name decl								    \
{									    \
  int sp;								    \
  struct stackbase *base;						    \
  error_t err;								    \
  extern error_t name ## _rpc ();					    \
									    \
  asm ("movl %%esp,%0" : "=r" (sp));					    \
  base = (struct stackbase *)(sp & STACK_MASK);				    \
 retry:									    \
  base->call_intred = NORMRETURN;					    \
  base->intr_port = a;							    \
  base->intr_type = type;						    \
  base->in_intr_call = 1;						    \
  if (err = rpc_check_sigs ())						    \
    {									    \
      base->in_intr_call = 0;						    \
      return err;							    \
    }									    \

  if (err == POSIX_EINTR || err == MACH_RCV_INTERRUPTED 		    \
      || err == MACH_SEND_INTERRUPTED)					    \
    {									    \
      /* If we get EINTR, but nobody's set call_intred, then the interrupt  \
	 wasn't intended for us, and we can continue.  If we get a RECV     \
	 intr, and it isn't accidental, then go back to the loop to pick    \
	 up the return from the RPC (which should arrive "quickly").  */    \
      if ((err == POSIX_EINTR && base->call_intred == NORMRETURN)	    \
	  || (err == MACH_RCV_INTERRUPTED				    \
	      && base->call_intred != NORMRETURN))			    \
	goto retry;							    \
      err = base->call_intred;						    \
    }									    \
  base->in_intr_call = 0;						    \
  return err;								    \
}

#define T(l) typeof (t ## l) l

#define INTR_CALL_2(type, name, ta, tb) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b), (a,b))

#define INTR_CALL_3(type, name, ta, tb, tc) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c), (a,b,c))

#define INTR_CALL_5(type, name, ta, tb, tc, td, te) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c, \
			   typeof (td) d,typeof (te) e), (a,b,c,d,e))

#define INTR_CALL_6(type, name, ta, tb, tc, td, te, tf) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c, \
			   typeof (td) d,typeof (te) e,typeof (tf) f), \
	      (a,b,c,d,e,f))

#define INTR_CALL_7(type, name, ta, tb, tc, td, te, tf, tg) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c, \
			   typeof (td) d,typeof (te) e,typeof (tf) f, \
			   typeof (tg) g), \
	      (a,b,c,d,e,f,g))

#define INTR_CALL_8(type, name, ta, tb, tc, td, te, tf, tg, th) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c, \
			   typeof (td) d,typeof (te) e,typeof (tf) f, \
			   typeof (tg) g,typeof (th) h), \
	      (a,b,c,d,e,f,g,h))

#define INTR_CALL_9(type, name, ta, tb, tc, td, te, tf, tg, th, ti) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c, \
			   typeof (td) d,typeof (te) e,typeof (tf) f, \
			   typeof (tg) g,typeof (th) h,typeof (ti) i), \
	      (a,b,c,d,e,f,g,h,i))

#define INTR_CALL_10(type, name, ta, tb, tc, td, te, tf, tg, th, ti, tj) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c, \
			   typeof (td) d,typeof (te) e,typeof (tf) f, \
			   typeof (tg) g,typeof (th) h,typeof (ti) i, \
			   typeof (tj) j), \
	      (a,b,c,d,e,f,g,h,i,j))

#define INTR_CALL_11(type, name, ta, tb, tc, td, te, tf, tg, th, ti, tj, tk) \
  INTR_CALL_N(type, name, (typeof (ta) a,typeof (tb) b,typeof (tc) c, \
			   typeof (td) d,typeof (te) e,typeof (tf) f, \
			   typeof (tg) g,typeof (th) h,typeof (ti) i, \
			   typeof (tj) j,typeof (tk) k), \
	      (a,b,c,d,e,f,g,h,i,j,k))


INTR_CALL_8(IO_INTR, io_write_inband, io_t, int, int, int, inband_data_t,
	    u_int, int, int *)

INTR_CALL_8(IO_INTR, io_write_outofband, io_t, int, int, int,
	    outofband_data_t, u_int, int, int *)

INTR_CALL_8(IO_INTR, io_read_inband, io_t, int, int, int, inband_data_t,
	    u_int *, int, int)

INTR_CALL_8(IO_INTR, io_read_outofband, io_t, int, int, int, 
	    outofband_data_t *, u_int *, int, int)

INTR_CALL_5(IO_INTR, io_ioctl_x, io_t, int, int, int, int)

INTR_CALL_7(IO_INTR, io_ioctl_r, io_t, int, int, int, int, 
	    ioctl_data_t, u_int *)

INTR_CALL_7(IO_INTR, io_ioctl_w, io_t, int, int, int, int, 
	    ioctl_data_t, u_int)

INTR_CALL_9(IO_INTR, io_ioctl_rw, io_t, int, int, int, int, 
	    ioctl_data_t, u_int, ioctl_data_t, u_int *)

INTR_CALL_2(IO_INTR, io_get_it, io_t, memory_object_t)

INTR_CALL_2(IO_INTR, io_readsleep, io_t, memory_object_t)

INTR_CALL_6(WAIT_INTR, proc_wait, process_t, pid_t, int *, int,
	    rusage_t *, pid_t *)

INTR_CALL_3(IO_INTR, socket_accept, socket_t, socket_t *, addr_port_t *)

INTR_CALL_2(IO_INTR, socket_connect, socket_t, addr_port_t)

INTR_CALL_10(IO_INTR, socket_send_inband, socket_t, addr_port_t, 
	    int, inband_data_t, u_int, fd_array_t, u_int, 
	    inband_data_t, u_int, int *)

INTR_CALL_10(IO_INTR, socket_send_outofband, socket_t, addr_port_t, int,
	    outofband_data_t, u_int, fd_array_t, u_int,
	    inband_data_t, u_int, int *)

INTR_CALL_11(IO_INTR, socket_recv_inband, socket_t, addr_port_t *,
	    int, inband_data_t, u_int *, fd_array_t, u_int *,
	    inband_data_t, u_int *, int *, int)

INTR_CALL_11(IO_INTR, socket_recv_outofband, socket_t, addr_port_t *,
	    int, outofband_data_t *, u_int *, fd_array_t, u_int *,
	    inband_data_t, u_int *, int *, int)

INTR_CALL_2(IO_INTR, file_lock, file_t, int)

INTR_CALL_9(IO_INTR, dir_pathtrans, file_t, filename_t, u_int, int, 
	    mode_t, retry_type *, filename_t, u_int *, file_t *)
#endif
