/* Startup and request thread server loop
   Copyright (C) 1991, 1992 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 2, 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 "ufs.h"
#include "fs.h"

#include "bootdefs.h"
#include "ioudefs.h"
#include "fsudefs.h"
#include <mach/mach_traps.h>
#include <mach/mig_errors.h>
#include <mach/error.h>
#include <device/device.h>
#include <stdio.h>

static int nreqthreads;
static struct mutex reqthreadlock;

static void ufs_request_thread (void);
static void map_time ();
static void kickoff_ufs (char *);
static void get_privports ();

extern mach_port_t mach_task_self_; /* XXX */
extern vm_size_t vm_page_size;	/* XXX */
mach_port_t __mach_task_self_;
vm_size_t __vm_page_size;

void
main()
{
  volatile static int hold = 1;

  /* XXX */
  if (!__mach_task_self_)
    __mach_task_self_ = mach_task_self_;
  else
    mach_task_self_ = __mach_task_self_;
  
  /* XXX */
  if (!__vm_page_size)
    __vm_page_size = vm_page_size; 
  else
    vm_page_size = __vm_page_size;
  
  get_privports ();
  ufs_control_port = MACH_PORT_NULL;
  ufs_realnode = MACH_PORT_NULL;
  dotdot_file = MACH_PORT_NULL;
  readonly = 1;

  kickoff_ufs("hd0a");
}

static  struct imsg		/* XXX -rm */
    {
      mach_msg_header_t	hdr;
      mach_msg_type_t port_desc_1;
      mach_port_t port_1;
      mach_msg_type_t port_desc_2;
      mach_port_t port_2;
    } imsg;

static void			/* XXX -rm */
explain_port (mach_msg_type_t desc, mach_port_t port)
{
  printf ("\tname %d, size %d, number %d, inline %d, long %d, dealloc %d\n"
	  "\t\t%p\n",
	  desc.msgt_name,
	  desc.msgt_size,
	  desc.msgt_number,
	  desc.msgt_inline,
	  desc.msgt_longform,
	  desc.msgt_deallocate,
	  port);
}

static void			/* XXX -rm */
explain_imsg (void)
{
  printf ("bits %#.8x, size %u, remote %p, local %p, seqno %u, id %d\n",
	  imsg.hdr.msgh_bits,
	  imsg.hdr.msgh_size,
	  imsg.hdr.msgh_remote_port,
	  imsg.hdr.msgh_local_port,
	  imsg.hdr.msgh_seqno,
	  imsg.hdr.msgh_id);
  explain_port (imsg.port_desc_1, imsg.port_1);
  explain_port (imsg.port_desc_2, imsg.port_2);
}

static void
get_privports ()
{
  mach_port_t reply;
  error_t result;
  mach_port_t boot_port;
  mach_port_t host_priv;

  task_get_special_port (mach_task_self (), TASK_BOOTSTRAP_PORT, &boot_port);

  reply = mach_reply_port ();
  imsg.hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
				       MACH_MSG_TYPE_MAKE_SEND_ONCE);
  imsg.hdr.msgh_size = 0;
  imsg.hdr.msgh_remote_port = boot_port;
  imsg.hdr.msgh_local_port = reply;
  imsg.hdr.msgh_kind = MACH_MSGH_KIND_NORMAL;
  imsg.hdr.msgh_id = 999999;
  
  result = mach_msg (&imsg.hdr,
		     MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT,
		     sizeof imsg.hdr, sizeof imsg, reply, 500,
		     MACH_PORT_NULL);
  host_priv = imsg.port_1;
  master_device_port = imsg.port_2;
 
  default_pager = MACH_PORT_NULL;
  vm_set_default_memory_manager (host_priv, &default_pager);
  mach_port_deallocate (mach_task_self (), boot_port);
  mach_port_deallocate (mach_task_self (), host_priv);
}

void
test_routine ()
{
  struct inode *rip;
  error_t err;
  char buf[1024], *bp;
  char pathbuf[1024];
  u_int buflen;
  mach_port_t rootpt, pwpt;
  retry_type retry;

  err = iget (2, &rip);
  if (err)
    panic_with_error ("iget", err);
  printf ("1 (gotten root)\n");

  rootpt = convert_protid_to_port
    (make_protid (rip, FS_LOOKUP_READ | FS_LOOKUP_EXEC, 0));
  mach_port_insert_right (mach_task_self (), rootpt, rootpt,
			  MACH_MSG_TYPE_MAKE_SEND);
  iput (rip);

  /* ===== */

  err = U_dir_pathtrans (rootpt, "RFS/.LOCALROOT/etc/passwd",
			 FS_LOOKUP_READ, 0, &retry, pathbuf, &pwpt);
  printf ("done\n");
  if (err)
    panic_with_error ("/etc/passwd", err);

  do
    {
      bp = buf;
      buflen = 10;
      err = U_io_read (pwpt, &bp, &buflen, -1, 10);
      if (err)
	panic_with_error ("/etc/passwd read", err);
      bp[buflen] = '\0';
      printf ("%s", bp);
      if (bp != buf)
	vm_deallocate (mach_task_self (), (vm_address_t) bp, buflen);
    }
  while (buflen);
}
  

/* Kick off all the stuff we need to get going.  When we get here,
   nothing is initialized except ufs_control_port, ufs_realnode, 
   readonly, dotdot_file, and default_pager.  */
static void
kickoff_ufs (char *devname)
{
  device_t con;
  int err;

  mutex_init (&bootlock);
  condition_init (&bootsync);
  
  /* open the devices */
  (void) device_open (master_device_port, D_WRITE, "console", &con);
#if 0
  set_console_port (con);
#else
  stdout = mach_open_devstream (con, "w");
#endif

  init_exceptions ();

  err = device_open (master_device_port, (readonly ? 0 : D_WRITE) | D_READ,
		     devname, &ufs_device);
  if (err)
    panic_with_error ("disk open", err);

  /* Create the portset */
  mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET,
		      &ufs_portset);
  
  /* Map the time */
  map_time ();
  
  /* Read the superblock */
  dev_read_sync (SBLOCK, (void **)&sblock, SBSIZE);
  
  nextgennumber = 0;

  /* init various subsystems */
  inode_init ();
  pager_init ();
  
  /* Become the first request thread */
  mutex_init (&reqthreadlock);
  nreqthreads = 0;
  cthread_detach (cthread_fork ((cthread_fn_t) ufs_request_thread,
				(any_t) 0));
  
  explain_imsg();

  test_routine ();
}

static void
map_time ()
{
  memory_object_t memobj;
  device_t timedev;
  
  (void) device_open (master_device_port, 0, "time", &timedev);
  (void) device_map (timedev, VM_PROT_READ, 0, sizeof (time_value_t),
		     &memobj, 0);
  (void) vm_map (mach_task_self (), (u_int *)&time, sizeof (time_value_t), 0,
		 1, memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE);
  (void) mach_port_deallocate (mach_task_self (), timedev);
  (void) mach_port_deallocate (mach_task_self (), memobj);
}
  

static int
request_server (mach_msg_header_t *inp,
		mach_msg_header_t *outp)
{
  int fs_server (mach_msg_header_t *, mach_msg_header_t *); /* XXX */
  int io_server (mach_msg_header_t *, mach_msg_header_t *); /* XXX */
  int fsys_server (mach_msg_header_t *, mach_msg_header_t *); /* XXX */
  int status;
  
  mutex_lock (&reqthreadlock);
  if (!nreqthreads)
    panic ("request server");
  if (!--nreqthreads)
    cthread_detach (cthread_fork ((cthread_fn_t) ufs_request_thread, 
				  (any_t) 0));
  mutex_unlock (&reqthreadlock);

  status = (io_server (inp, outp)
	    || fs_server (inp, outp)
	    || seqnos_memory_object_server (inp, outp)
	    || device_reply_server (inp, outp)
	    || seqnos_notify_server (inp, outp)
	    || fsys_server (inp, outp));

  mutex_lock (&reqthreadlock);
  nreqthreads++;
  mutex_unlock (&reqthreadlock);

  return status;
}

static void
ufs_request_thread (void)
{
  error_t error;
  error_t mach_msg_server (int (*)(mach_msg_header_t *, mach_msg_header_t *),
			   int, mach_port_t); /* XXX */

  printf ("new req thd\n");
  mutex_lock (&reqthreadlock);
  nreqthreads++;
  mutex_unlock (&reqthreadlock);

  error = mach_msg_server (request_server, __vm_page_size * 2, ufs_portset);
  panic_with_error ("mach_msg_server for requests", error);
}


volatile void
panic (char *s)
{
  volatile int a, b;
  printf ("panic: %s\n", s);
  
  a = b / 0;
  for (;;);
}

volatile void
panic_with_error (char *s, error_t error_code)
{
  volatile int a, b;
  
  printf ("panic: %s: %s (0x%x = %d/%d/%d)\n", s, mach_error_string (error_code),
	  (int) error_code, err_get_system (error_code), 
	  err_get_sub (error_code), 
	  err_get_code (error_code));
  a = b / 0;
  for (;;);
}
