/*
 *  linux/abi/emulate.c
 *
 *  Copyright (C) 1993  Linus Torvalds
 *
 *   Modified by Eric Youngdale to include all ibcs syscalls.
 *   Re-written by Drew Sullivan to handle lots more of the syscalls correctly.
 */

/*
 * Emulate.c contains the entry point for the 'lcall 7,xxx' handler.
 */
#include <ibcs/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <ibcs/unistd.h>
#include <linux/segment.h>
#include <linux/ptrace.h>
#include <linux/config.h>

#include <asm/segment.h>
#include <asm/system.h>
#include <linux/fs.h>
#include <linux/sys.h>

#define IBCS_TRACE	1	/* set to zero to disable tracing */
/*
 *  These are defined to enable their inclsion in the branch table
 *  defined below.
 */

#ifdef __cplusplus
extern "C" {
#endif
extern int ibcs_stat(int, int);
extern int ibcs_lstat(int, int);
extern int ibcs_fstat(int, int);
extern int ibcs_statfs(int, int);
extern int ibcs_fstatfs(int, int);
extern int ibcs_ioctl(int, int, int);

extern int ibcs_getpid(struct pt_regs * regs);
extern int ibcs_getuid(struct pt_regs * regs);
extern int ibcs_getgid(struct pt_regs * regs);
extern int ibcs_pipe(struct pt_regs * regs);
extern int ibcs_fork(struct pt_regs * regs);

static void plist(char *args, int *list);
#ifdef __cplusplus
}
#endif

/*
 *  ELF and COFF use the IBCS format. If configured for either one then
 *  enable the ibcs support.
 */

#ifdef CONFIG_BINFMT_ELF
#define CONFIG_IBCS
#endif

#ifdef CONFIG_BINFMT_COFF
#define CONFIG_IBCS
#endif

#ifndef CONFIG_IBCS

/* This is a special stub that we use *only* if ibcs is not configured into
   the kernel */

#ifdef __cplusplus
extern "C"
#endif
void iABI_emulate(struct pt_regs * regs)
{
	printk("lcall 7,xxx: eax = %08x\n",regs->eax);
}
#else

/* This lookup table is used to determine how many arguments to pull from
   the user mode stack, and how many to pass to the kernel routine. */

/* This table is used to convert the errno numbers returned by linux
   into the iBCS numbers */

#define EBADE EINVAL
#define EBADR EINVAL
#define EXFULL EINVAL
#define ENOANO EINVAL
#define EBADRQC EINVAL
#define EBADSLT EINVAL
#define EDEADLOCK EINVAL
#define EBFONT EINVAL
#define ELIBACC EINVAL
#define ELIBBAD EINVAL
#define ELIBSCN EINVAL
#define ELIBMAX EINVAL
#define ELIBEXEC EINVAL
#define EILSEQ EINVAL
#define ENOTSOCK EINVAL
#define EDESTADDRREQ EINVAL
#define EMSGSIZE EINVAL
#define EPROTOTYPE EINVAL
#define ENOPROTOOPT EINVAL
#define EPROTONOSUPPORT EINVAL
#define ESOCKTNOSUPPORT EINVAL
#define EOPNOTSUPP EINVAL
#define EPFNOSUPPORT EINVAL
#define EAFNOSUPPORT EINVAL
#define EADDRINUSE EINVAL
#define EADDRNOTAVAIL EINVAL
#define ENETDOWN EINVAL
#define ENETUNREACH EINVAL
#define ENETRESET EINVAL
#define ECONNABORTED EINVAL
#define ECONNRESET EINVAL
#define ENOBUFS EINVAL
#define EISCONN EINVAL
#define ENOTCONN EINVAL
#define ESHUTDOWN EINVAL
#define ETOOMANYREFS EINVAL
#define ETIMEDOUT EINVAL
#define ECONNREFUSED EINVAL
#define EHOSTDOWN EINVAL
#define EHOSTUNREACH EINVAL
#define EALREADY EINVAL
#define EINPROGRESS EINVAL
#define EUCLEAN EINVAL
#define ENOTNAM EINVAL
#define ENAVAIL EINVAL
#define EISNAM EINVAL
#define EREMOTEIO EINVAL
#define EBLIN EINVAL
#define EINIT EINVAL
#define REMDEV EINVAL

static unsigned char errno_cvt[255] = {
  0,
  EPERM, ENOENT, ESRCH, EINTR, EIO, ENXIO, E2BIG, ENOEXEC, EBADF, ECHILD,
  EAGAIN, ENOMEM, EACCES, EFAULT, ENOTBLK, EBUSY, EEXIST, EXDEV, ENODEV,
  ENOTDIR, EISDIR, EINVAL, ENFILE, EMFILE, ENOTTY, ETXTBSY, EFBIG, ENOSPC,
  ESPIPE, EROFS, EMLINK, EPIPE, EDOM, ERANGE, EDEADLK, ENAMETOOLONG, ENOLCK,
  ENOSYS, ENOTEMPTY, ELOOP, EWOULDBLOCK, ENOMSG, EIDRM, ECHRNG, EL2NSYNC,
  EL3HLT, EL3RST, ELNRNG, EUNATCH, ENOCSI, EL2HLT, EBADE, EBADR, EXFULL,
  ENOANO, EBADRQC, EBADSLT, EDEADLOCK, EBFONT, ENOSTR, ENODATA, ETIME, ENOSR,
  ENONET, ENOPKG, EREMOTE, ENOLINK, EADV, ESRMNT, ECOMM, EPROTO, EMULTIHOP,
  EDOTDOT, EBADMSG, EOVERFLOW, ENOTUNIQ, EBADFD, EREMCHG, ELIBACC, ELIBBAD,
  ELIBSCN, ELIBMAX, ELIBEXEC, EILSEQ, ERESTART, ESTRPIPE, EUSERS, ENOTSOCK,
  EDESTADDRREQ, EMSGSIZE, EPROTOTYPE, ENOPROTOOPT, EPROTONOSUPPORT,
  ESOCKTNOSUPPORT, EOPNOTSUPP, EPFNOSUPPORT, EAFNOSUPPORT, EADDRINUSE,
  EADDRNOTAVAIL, ENETDOWN, ENETUNREACH, ENETRESET, ECONNABORTED, ECONNRESET,
  ENOBUFS, EISCONN, ENOTCONN, ESHUTDOWN, ETOOMANYREFS, ETIMEDOUT, ECONNREFUSED,
  EHOSTDOWN, EHOSTUNREACH, EALREADY, EINPROGRESS, ESTALE, EUCLEAN, ENOTNAM,
  ENAVAIL, EISNAM, EREMOTEIO
};

/* This table contains the appropriate kernel routines that can be run
   to perform the syscalls in question.  If an entry is we don't know
   how to handle it yet.
   Spl means that we need to do special processing for this syscall
	(see ibcs_wait or ibcs_getpid)
*/

#define Spl	-1	/* pass the regs structure down */
#define Ukn	-2	/* no code to handle this case yet */

static struct IBCS_function {
	fn_ptr	kfunc;	/* function to call (sys_... or ibcs_...) */
	int	nargs;	/* number of args to kfunc or Ukn or Spl */
	char *	name;	/* name of function */
	char *	args;	/* how to print the arg list (see plist) */
} IBCS_functions[] = {
	{ 0,		Ukn,	"syscall",	"",	},	/*    0 */
	{ sys_exit,	1,	"exit",		"i",	},	/*    1 */
	{ ibcs_fork,	Spl,	"fork",		"",	},	/*    2 */
	{ sys_read,	3,	"read",		"ipi",	},	/*    3 */
	{ sys_write,	3,	"write",	"ipi",	},	/*    4 */
	{ sys_open,	3,	"open",		"sxo",	},	/*    5 */
	{ sys_close,	1,	"close",	"i",	},	/*    6 */
	{ ibcs_wait,	Spl,	"wait",		"",	},	/*    7 */
	{ sys_creat,	2,	"creat",	"so",	},	/*    8 */
	{ sys_link,	2,	"link",		"ss",	},	/*    9 */

	{ sys_unlink,	1,	"unlink",	"ss",	},	/*   10 */
	{ 0,		Spl,	"exec",		"",	},	/*   11 */
	{ sys_chdir,	1,	"chdir",	"s",	},	/*   12 */
	{ sys_time,	1,	"time",		"x",	},	/*   13 */
	{ sys_mknod,	3,	"mknod",	"xxx",	},	/*   14 */
	{ sys_chmod,	2,	"chmod",	"xx",	},	/*   15 */
	{ sys_chown,	3,	"chown",	"xxx",	},	/*   16 */
	{ sys_brk,	1,	"brk/break",	"x",	},	/*   17 */
	{ ibcs_stat,	2,	"stat",		"sx",	},	/*   18 */
	{ sys_lseek,	3,	"seek/lseek",	"iii",	},	/*   19 */

	{ ibcs_getpid,	Spl,	"getpid",	"",	},	/*   20 */
	{ 0,		Ukn,	"mount",	"",	},	/*   21 */
	{ sys_umount,	1,	"umount",	"x",	},	/*   22 */
	{ sys_setuid,	1,	"setuid",	"i",	},	/*   23 */
	{ ibcs_getuid,	Spl,	"getuid",	"",	},	/*   24 */
	{ sys_stime,	1,	"stime",	"x",	},	/*   25 */
	{ sys_ptrace,	4,	"ptrace",	"xxxx",	},	/*   26 */
	{ sys_alarm,	1,	"alarm",	"x",	},	/*   27 */
	{ ibcs_fstat,	2,	"fstat",	"xx",	},	/*   28 */
	{ sys_pause,	0,	"pause",	"",	},	/*   29 */

	{ sys_utime,	2,	"utime",	"xx",	},	/*   30 */
	{ 0,		Ukn,	"stty",		"",	},	/*   31 */
	{ 0,		Ukn,	"gtty",		"",	},	/*   32 */
	{ sys_access,	2,	"access",	"xx",	},	/*   33 */
	{ sys_nice,	1,	"nice",		"x",	},	/*   34 */
	{ ibcs_statfs,	2,	"statfs",	"xx",	},	/*   35 */
	{ sys_sync,	0,	"sync",		"",	},	/*   36 */
	{ sys_kill,	2,	"kill",		"xx",	},	/*   37 */
	{ ibcs_fstatfs,	2,	"fstatfs",	"xx",	},	/*   38 */
	{ sys_getpgrp,	0,	"setpgrp",	"",	},	/*   39 */

	{ 0,		Ukn,	"cxenix",	"",	},	/*   40 */
	{ sys_dup,	1,	"dup",		"x",	},	/*   41 */
	{ ibcs_pipe,	Spl,	"pipe",		"",	},	/*   42 */
	{ sys_times,	1,	"times",	"x",	},	/*   43 */
	{ sys_profil,	4,	"prof",		"xxxx",	},	/*   44 */
	{ 0,		Ukn,	"lock/plock",	"",	},	/*   45 */
	{ sys_setgid,	1,	"setgid",	"x",	},	/*   46 */
	{ ibcs_getgid,	Spl,	"getgid",	"",	},	/*   47 */
	{ sys_signal,	2,	"signal",	"xx",	},	/*   48 */
	{ 0,		Ukn,	"msgsys",	"",	},	/*   49 */

	{ 0,		Ukn,	"sysi86/sys3b",	"",	},	/*   50 */
	{ sys_acct,	1,	"acct/sysacct",	"x",	},	/*   51 */
	{ 0,		3,	"shmsys",	"xxx",	},	/*   52 */
	{ 0,		Ukn,	"semsys",	"",	},	/*   53 */
	{ ibcs_ioctl,	3,	"ioctl",	"xxx",	},	/*   54 */
	{ 0,		3,	"uadmin",	"xxx",	},	/*   55 */
	{ 0,		Ukn,	"?",		"",	},	/*   56 */
	{ sys_olduname,	1,	"utsys",	"x",	},	/*   57 */
	{ 0,		Ukn,	"?",		"",	},	/*   58 */
	{ sys_execve,	3,	"exece",	"xxx",	},	/*   59 */

	{ sys_umask,	1,	"umask",	"x",	},	/*   60 */
	{ sys_chroot,	1,	"chroot",	"x",	},	/*   61 */
	{ sys_fcntl,	3,	"fcntl",	"xxx",	},	/*   62 */
	{ sys_ulimit,	2,	"ulimit",	"xx",	},	/*   63 */
	{ 0,		Ukn,	"?",		"",	},	/*   64 */
	{ 0,		Ukn,	"?",		"",	},	/*   65 */
	{ 0,		Ukn,	"?",		"",	},	/*   66 */
	{ 0,		Ukn,	"?",		"",	},	/*   67 */
	{ 0,		Ukn,	"?",		"",	},	/*   68 */
	{ 0,		Ukn,	"?",		"",	},	/*   69 */

	{ 0,		Ukn,	"advfs",	"",	},	/*   70 */
	{ 0,		Ukn,	"unadvfs",	"",	},	/*   71 */
	{ 0,		Ukn,	"rmount",	"",	},	/*   72 */
	{ 0,		Ukn,	"rumount",	"",	},	/*   73 */
	{ 0,		Ukn,	"rfstart",	"",	},	/*   74 */
	{ 0,		Ukn,	"?",		"",	},	/*   75 */
	{ 0,		Ukn,	"rdebug",	"",	},	/*   76 */
	{ 0,		Ukn,	"rfstop",	"",	},	/*   77 */
	{ 0,		Ukn,	"rfsys",	"",	},	/*   78 */
	{ sys_rmdir,	1,	"rmdir",	"s",	},	/*   79 */

	{ sys_mkdir,	2,	"mkdir",	"sx",	},	/*   80 */
	{ sys_readdir,	3,	"getdents",	"xxx",	},	/*   81 */
	{ 0,		Ukn,	"sysfs",	"",	},	/*   82 */
	{ 0,		Ukn,	"getmsg",	"",	},	/*   83 */
	{ 0,		Ukn,	"putmsg",	"",	},	/*   84 */
	{ 0,		Ukn,	"poll",		"",	},	/*   85 */
	{ 0,		Ukn,	"?",		"",	},	/*   86 */
	{ 0,		Ukn,	"?",		"",	},	/*   87 */
	{ 0,		Ukn,	"?",		"",	},	/*   88 */
	{ 0,		Ukn,	"?",		"",	},	/*   89 */

	{ sys_symlink,	2,	"symlink",	"ss",	},	/*   90 */
	{ ibcs_lstat,	2,	"lstat",	"sx",	},	/*   91 */
	{ sys_readlink,	2,	"readlink",	"sx",	},	/*   92 */
	{ 0,		Ukn,	"?",		"",	},	/*   93 */
	{ 0,		Ukn,	"?",		"",	},	/*   94 */
	{ 0,		Ukn,	"?",		"",	},	/*   95 */
	{ 0,		Ukn,	"?",		"",	},	/*   96 */
	{ 0,		Ukn,	"?",		"",	},	/*   97 */
	{ 0,		Ukn,	"?",		"",	},	/*   98 */
	{ 0,		Ukn,	"?",		"",	},	/*   99 */

	{ 0,		Ukn,	"?",		"",	},	/*  100 */
	{ 0,		Ukn,	"?",		"",	},	/*  101 */
	{ 0,		Ukn,	"?",		"",	},	/*  102 */
	{ 0,		Ukn,	"?",		"",	},	/*  103 */
	{ 0,		Ukn,	"?",		"",	},	/*  104 */
	{ 0,		Ukn,	"?",		"",	},	/*  105 */
	{ 0,		Ukn,	"?",		"",	},	/*  106 */
	{ 0,		Ukn,	"?",		"",	},	/*  107 */
	{ 0,		Ukn,	"?",		"",	},	/*  108 */
	{ 0,		Ukn,	"?",		"",	},	/*  109 */

	{ 0,		Ukn,	"?",		"",	},	/*  110 */
	{ 0,		Ukn,	"?",		"",	},	/*  111 */
	{ 0,		Ukn,	"?",		"",	},	/*  112 */
	{ 0,		Ukn,	"?",		"",	},	/*  113 */
	{ 0,		Ukn,	"?",		"",	},	/*  114 */
	{ 0,		Ukn,	"?",		"",	},	/*  115 */
	{ 0,		Ukn,	"?",		"",	},	/*  116 */
	{ 0,		Ukn,	"?",		"",	},	/*  117 */
	{ 0,		Ukn,	"?",		"",	},	/*  118 */
	{ 0,		Ukn,	"?",		"",	},	/*  119 */

	{ 0,		Ukn,	"?",		"",	},	/*  120 */
	{ 0,		Ukn,	"?",		"",	},	/*  121 */
	{ 0,		Ukn,	"?",		"",	},	/*  122 */
	{ 0,		Ukn,	"?",		"",	},	/*  123 */
	{ 0,		Ukn,	"?",		"",	},	/*  124 */
	{ 0,		Ukn,	"?",		"",	},	/*  125 */
	{ 0,		Ukn,	"?",		"",	},	/*  126 */
	{ 0,		Ukn,	"?",		"",	},	/*  127 */
};

#ifdef __cplusplus
extern "C" 
#endif
void iABI_emulate(struct pt_regs * regs)
{
  int i;
  int args[8];
  int rvalue;
  struct IBCS_function *p = &IBCS_functions[regs->eax]; 

  for(i=0; i < p->nargs; i++)
    args[i] = get_fs_long(((unsigned long *) regs->esp) + (i+1));

#if IBCS_TRACE
  printk("%s(", p->name); plist(p->args, args); printk("): ");
#endif
  rvalue = 0;
  if (p->kfunc) {
    switch(p->nargs) {
    case Spl:
      rvalue = (*p->kfunc)(regs);
      break;
    case 0:
      rvalue = (*p->kfunc)();
      break;
    case 1:
      rvalue = (*p->kfunc)(args[0]);
      break;
    case 2:
      rvalue = (*p->kfunc)(args[0], args[1]);
      break;
    case 3:
      rvalue = (*p->kfunc)(args[0], args[1], args[2]);
      break;
    }
  } else  {
    printk("Unsupported iBSC2 function %d(%s)", regs->eax, p->name);
  }
  
  /* The errno numbers need to be translated in a second table */
  
  if (rvalue >= 0) {
    regs->eflags &= ~1; /* Clear carry flag */
    regs->eax = rvalue;
#if IBCS_TRACE
  printk("%d {%d}\n", regs->eax, regs->edx);
#endif
  } else {
    regs->eflags |= 1; /* Set carry flag */
    if (rvalue > -255) 
      regs->eax = errno_cvt[-rvalue];
    else
      regs->eax = EINVAL;
#if IBCS_TRACE
    printk("Error %d\n", regs->eax);
#endif
  }
}

/* Here we handle syscalls that need a little more special treatment */
int ibcs_fork(struct pt_regs * regs) {
	int rvalue;

	rvalue = sys_fork(regs->ebx, regs->ecx, 1,
		regs->esi, regs->edi, regs->ebp, regs->eax, regs->ds,
		regs->es, regs->fs, regs->gs, regs->orig_eax,
		regs->eip, regs->cs, regs->eflags, regs->esp, regs->ss);
	regs->edx = 0;
	return rvalue;
}

int ibcs_pipe(struct pt_regs * regs) {
	long filedes[2];
	int old_fs = get_fs();
	int rvalue;

	set_fs(get_ds());
	rvalue = sys_pipe(&filedes);
	set_fs(old_fs);
	if (rvalue == 0) {
		regs->eax = filedes[0];
		regs->edx = filedes[1];
	}
	return rvalue;
}

/* note the double value return in eax and edx */
int ibcs_getpid(struct pt_regs * regs) {
	regs->eax = sys_getpid();
	regs->edx = sys_getppid();

	return 0;
}

int ibcs_getuid(struct pt_regs * regs) {
	regs->eax = sys_getuid();
	regs->edx = sys_geteuid();

	return 0;
}

int ibcs_getgid(struct pt_regs * regs) {
	regs->eax = sys_getgid();
	regs->edx = sys_getegid();

	return 0;
}

/* I don't know the correct mask for eflags to check for ZF,PF,SF,OF
   so this code won't work as is */
int ibcs_wait(struct pt_regs * regs) {
	int	pid, loc, opt;

	/* if ZF,PF,SF,and OF are set then it is waitpid */
	if (regs->eflags & 0x0000) {	/* ***BUG*** we need a mask here */
		pid = get_fs_long(((unsigned long *) regs->esp) + 1);
		loc = get_fs_long(((unsigned long *) regs->esp) + 2);
		opt = get_fs_long(((unsigned long *) regs->esp) + 3);

		result = sys_waitpid(pid, loc, opt);
	} else {
		loc = get_fs_long(((unsigned long *) regs->esp) + 1);
		result = sys_wait(-1, loc, 0L);
	}
	return 0;
}

#if IBCS_TRACE
/*
 * plist is used by the trace code to show the arg list
 */
static void plist(char *args, int *list) {
	int error;
	char * tmp;

	while (*args) {
		switch(*args++) {
		case 'i': printk("%d", *list++);		break;
		case 'o': printk("%o", *list++);		break;
		case 'x': printk("%x", *list++);		break;
		case 's': 
			error = getname(*list++,&tmp);
			if (!error) {
				printk("%s", tmp);	
				putname(tmp);
			}
			break;

		case 'p': 
			error = getname(*list, &tmp);
			if (!error) {
				printk("%x[", *list);
				plist("xxxx", tmp);
				printk("]");
				putname(tmp);
			}
			++list;
			break;

		}
		if (*args) printk(", ");
	}
}
#endif
#endif
