/* Machine dependent support for exec
   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.  */

/* The following routine has to be defined by this file:
   exec_read_header
*/

#include <mach.h>
#include <hurd/hurd_types.h>
#include <gnu/errno.h>
#include <mach/error.h>
#include <gnu/posix_errors.h>
#include "exec.h"
#include <mach/i386/vm_param.h>
#include <lib/libhurd.h>

error_t
exec_read_header (void *filedata,
		  struct loader_info *lp)
{
  struct i386_exec *x = filedata;
  error_t err;
  
  switch (N_MAGIC (*x))
    {
    case OMAGIC:
      lp->text_start = 0;
      lp->text_size = 0;
      lp->text_offset = 0;
      lp->data_start = 0x10000;
      lp->data_size = x->a_text + x->a_data;
      lp->data_offset = sizeof (struct i386_exec);
      lp->bss_size = x->a_bss;
      break;
      
    case NMAGIC:
      lp->text_start = 0x10000;
      lp->text_size = x->a_text;
      lp->text_offset = sizeof (struct i386_exec);
      lp->data_start = lp->text_start + lp->text_size;
      lp->data_size = x->a_data;
      lp->data_offset = lp->text_offset + lp->text_size;
      lp->bss_size = x->a_bss;
      break;
      
    case ZMAGIC:
      lp->text_start = 0x10000;
      lp->text_size = sizeof (struct i386_exec) + x->a_text;
      lp->text_offset = 0;
      lp->data_start = lp->text_start + lp->text_size;
      lp->data_size = x->a_data;
      lp->data_offset = lp->text_offset + lp->text_size;
      lp->bss_size = x->a_bss;
      break;
      
    default:
      return POSIX_ENOEXEC;
    }
  lp->entry_1 = x->a_entry;
  lp->entry_2 = 0;
  return 0;
}

#if 0
void
exec_setargs (task_t task,
	      char *args,
	      u_int argvlen,
	      char *envs,
	      u_int envplen,
	      int argc,
	      int envc,
	      int *outsave)
{
  int i;
  vm_offset_t u_arg_start;	/* user start of arg list block */
  vm_offset_t l_arg_start;	/* local start of arg list block */
  vm_offset_t u_arg_page_start;	/* user start of args' page-aligned */
  vm_size_t arg_page_size;	/* page aligned size fo args */
  vm_offset_t l_arg_page_start;	/* local start of args, page-aligned */
  int arg_len;

  char **l_ap;			/* local arglist address */
  char *u_cp;			/* user argument string address */
  char *l_cp;			/* local argument string address */
  char *xp;
  int xlen;
  
  /* Calculate the size of the argument list.
     Add space for:
       argc
       pointers to arguments
       trailing 0 pointer
       pointers to environment variables
       trailing 0 pointer
       and align to integer boundar
   */
  arg_len = argvlen + envplen;
  arg_len += sizeof (int) + (2 + argc + envc) * sizeof (char *);
  arg_len = (arg_len + (sizeof (int) - 1)) & ~(sizeof (int) - 1);

  /* Get address of argument list in user space */
  u_arg_start = (VM_MAX_ADDRESS - arg_len) & ~(sizeof (int) - 1);
  
  /* Round to page boundaries, and allocate local copy */
  u_arg_page_start = trunc_page (u_arg_start);
  arg_page_size = (vm_size_t) (round_page (u_arg_start + arg_len)
			       - u_arg_page_start);
  
  vm_allocate (mach_task_self (), &l_arg_page_start, arg_page_size, 1);
  
  /* Set up address corresponding to user pointers in the kernel block */
  l_arg_start = l_arg_page_start + (u_arg_start - u_arg_page_start);
  l_ap = (char **) l_arg_start;
  
  /* Start the strings after the arg-count and pointers */
  u_cp = (char *)u_arg_start 
    + (argc + envc) * sizeof (char *)
      + 2 * sizeof (char *)
	+ sizeof (int);
  l_cp = (char *)l_arg_start 
    + (argc + envc) * sizeof (char *)
      + 2 * sizeof (char *)
	+ sizeof (int);
  
  /* First the argument count */
  *l_ap++ = (char *)argc;
  
  /* Then the strings and string pointers for each argument */
  xp = args;
  for (i = 0; i < argc; i++)
    {
      *l_ap++ = u_cp + (xp - args);
      xp += strlen (xp) + 1;
    }
  *l_ap++ = 0;			/* end of argv */
  xlen = xp - args;
  xp = envs;
  for (i = 0; i < envc; i++)
    {
      *l_ap++ = u_cp + (xp - envs + xlen);
      xp += strlen (xp) + 1;
    }
  *l_ap++ = 0;			/* end of envp */
  bcopy (args, l_cp, argvlen);
  bcopy (envs, l_cp + argvlen, envplen);
  
  /* Now write all of this to user space */
  vm_write (task, u_arg_page_start, l_arg_page_start, arg_page_size);
  vm_deallocate (mach_task_self (), l_arg_page_start, arg_page_size);

  *outsave = (int)u_arg_start;
}

void
exec_setregs (thread_t thread,
	      int entry_1,
	      int entry_2,
	      int outsave)
{
  struct i386_thread_state ts;
  u_int tssize = i386_THREAD_STATE_COUNT;
  
  thread_get_state (thread, i386_THREAD_STATE, &ts, &tssize);
  assert (tssize == i386_THREAD_STATE_COUNT);
  
  ts.uesp = outsave;
  ts.eip = entry_1;
  thread_set_state (thread, i386_THREAD_STATE, &ts, tssize);
}
#endif
