/*
 * @OSF_FREE_FREE_COPYRIGHT@
 * 
 */
/*
 * HISTORY
 * $Log: serv_init.c,v $
 * Revision 1.1.2.7  1997/02/27  11:11:06  bruel
 * 	HPPA support
 * 	[1997/02/27  11:08:08  bruel]
 *
 * Revision 1.1.2.6  1996/11/18  18:16:15  barbou
 * 	Initialize the notification module.
 * 	[1996/11/18  18:00:59  barbou]
 * 
 * Revision 1.1.2.5  1996/11/05  12:20:29  barbou
 * 	Added "cthread_red_zone_size" (1 page) for DEBUG configurations.
 * 	[96/11/05            barbou]
 * 
 * Revision 1.1.2.4  1996/10/09  09:35:44  barbou
 * 	Added osfmach3_halt_system.
 * 	Added support for the "-s" boot flag.
 * 	[96/10/04            barbou]
 * 
 * Revision 1.1.2.3  1996/09/27  23:04:14  barbou
 * 	Merged up to linux-2.0.21.
 * 	[1996/09/27  22:14:04  barbou]
 * 
 * Revision 1.1.2.2  1996/09/18  14:25:22  barbou
 * 	Don't call parse_root_device in osfmach3_start_init: the devices haven't
 * 	been initialized yet.
 * 	[96/09/17            barbou]
 * 
 * Revision 1.1.2.1  1996/09/09  17:19:00  barbou
 * 	Call sys_setup in the context of the "init" task. Do the
 * 	appropriate segment registers incantations to tell it to
 * 	copy data from our own address space.
 * 	[96/09/07            barbou]
 * 
 * 	Use task[smp_num_cpus] for init's task.
 * 	[1996/09/06  23:07:52  barbou]
 * 
 * 	Call sys_setup after forking init, so that init gets pid 1.
 * 	[96/09/07            barbou]
 * 
 * 	Terminate the startup thread after start_kernel().
 * 	[96/09/06            barbou]
 * 
 * 	Adapted for clone().
 * 	[1996/09/05  17:26:56  barbou]
 * 
 * 	Created.
 * 	[1996/09/02  10:14:00  barbou]
 * 
 * $EndLog$
 */

#include <linux/autoconf.h>

#include <mach/exc_server.h>
#include <mach/std_types.h>
#include <mach/port.h>
#include <mach/mach_traps.h>
#include <mach/host_info.h>
#include <mach/host_reboot.h>
#include <mach/mach_host.h>
#include <mach/mach_interface.h>
#include <mach/bootstrap.h>

#include <osfmach3/mach_init.h>
#include <osfmach3/mach3_debug.h>
#include <osfmach3/uniproc.h>
#include <osfmach3/console.h>
#include <osfmach3/parent_server.h>
#include <osfmach3/serv_port.h>
#include <osfmach3/server_thread.h>
#include <osfmach3/block_dev.h>

#include <asm/segment.h>
#include <asm/page.h>
#include <asm/param.h>
#include <asm/processor.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/mm.h>

#include <asm/pgtable.h>

extern void	uniproc_preemption_init(void);
extern char	*getenv(char *var);
extern void	start_kernel(void);
extern void	subsystem_init(void);
extern void	mach_trap_init(void);
extern void	server_thread_init(void);
extern void	osfmach3_notify_init(void);
extern void	ux_server_init(void);
extern void	dev_to_object_init(void);
extern void	device_reply_hdlr(void);
extern void	init_mapped_time(void);
extern void	jiffies_thread_init(void);
extern void	user_memory_init(void);
extern void	inode_pager_init(void);
extern void	fake_interrupt_init(void);
extern void	osfmach3_console_init(void);

void		setup_server(int argc, char **argv);
void		ports_init(void);
void		print_versions(void);
void		gen_init(int argc, char **argv);

boolean_t use_activations = FALSE;
extern boolean_t in_kernel;		/* exported by libcthreads */
extern boolean_t map_user_mem_to_copy;

mach_port_t	privileged_host_port;
mach_port_t	host_port;
mach_port_t	security_port;
mach_port_t	root_ledger_wired;
mach_port_t	root_ledger_paged;
mach_port_t	device_server_port;
mach_port_t	default_pager_port;
mach_port_t	default_processor_set;
mach_port_t	default_processor_set_name;
mach_port_t	exc_subsystem_port;

security_token_t	server_security_token;

char		*rootdev_name = "boot_device";

vm_size_t	osfmach3_mem_size;

vm_size_t	page_size;
vm_size_t	page_mask;
int		page_shift;

int	parent_server = 0;		/* Running under another server ? */

char	osfmach3_kernel_version[KERNEL_VERSION_MAX];

/*
 * Size of the server threads' stacks size.
 * This variable is imported and used by the cthreads library.
 *
 * IMPORTANT NOTE: one needs to use the -maxonstack MIG option when
 * building libmach. Otherwise, stacks of 64K or over are necessary !
 */
#ifdef	i386
vm_size_t	cthread_stack_size = 16 * 1024;
#endif	/* i386 */
#ifdef	PPC
vm_size_t	cthread_stack_size = 16 * 1024;
#endif	/* PPC */
#ifdef	hp_pa
vm_size_t	cthread_stack_size = 32 * 1024;
#endif	/* hp_pa */

#ifdef	CONFIG_OSFMACH3_DEBUG
vm_size_t	cthread_red_zone_size = PAGE_SIZE;
#endif	/* CONFIG_OSFMACH3_DEBUG */

host_priority_info_data_t host_pri_info;


void
main(
	int	argc,
	char	**argv)
{
	struct server_thread_priv_data	priv_data;

	server_thread_set_priv_data(cthread_self(), &priv_data);
	set_current_task(&init_task);
	uniproc_enter();
	uniproc_preemption_init();

	/*
	 * Initialize the server.
	 */
	setup_server(argc, argv);

	/*
	 * Linux-specific code starts here.
	 */
	start_kernel();

	uniproc_exit();
	cthread_detach(cthread_self());
	cthread_exit((void *) 0);
}


void
setup_server(
	int	argc,
	char	**argv)
{
	kern_return_t		kr;
	mach_msg_type_number_t	count;

	ports_init();

	count = HOST_PRIORITY_INFO_COUNT;
	kr= host_info(mach_host_self(), HOST_PRIORITY_INFO,
		      (host_info_t) &host_pri_info, &count);
	if (kr != KERN_SUCCESS)
		panic("host_info HOST_PRIORITY_INFO");

	gen_init(argc, argv);  /* generic "machine-dependent" initialization */
	print_versions();
	if (use_activations)
		subsystem_init();

	/*
	 * Make the server unswappable (but it's still pageable).
	 */
	kr = task_swappable(privileged_host_port, mach_task_self(), FALSE);
	if (kr != KERN_SUCCESS) {
		MACH3_DEBUG(1, kr, ("setup_server: task_swappable"));
	}

	/*
	 * Initialize module for handling server threads.  Must be
	 * done very early, because even our wired threads are managed
	 * by this module.
	 */
	server_thread_init();
	ux_server_init();
	mach_trap_init();
	osfmach3_notify_init();

	if (parent_server) {
		/*
		 * Catch signals if we're a regular application
		 * of a parent server.
		 */
		parent_server_catchall_signals();
	}

	fake_interrupt_init();
	init_mapped_time();
	jiffies_thread_init();
	dev_to_object_init();
	device_reply_hdlr();
	user_memory_init();
	inode_pager_init();
}



/*
 * Print Mach kernel version and server version.
 */
void
print_versions(void)
{
	if (host_kernel_version(mach_host_self(),&osfmach3_kernel_version[0])
	    != KERN_SUCCESS) {
		printk("Warning: print_versions: can't get micro-kernel version.\n");
	} else {
		printk("%s", osfmach3_kernel_version);	/* MK version */
	}
}


void
ports_init(void)
{
	mach_port_t	bootstrap_port;
	kern_return_t	result;
	u_int		security_token_size;

	/*
	 * Get our bootstrap port
	 */
	result = task_get_special_port(mach_task_self(),
				       TASK_BOOTSTRAP_PORT,
				       &bootstrap_port);
	if (result != KERN_SUCCESS)
		panic("get bootstrap port %d", result);

	/*
	 * Ask for the privileged ports
	 */
	result = bootstrap_ports(bootstrap_port,
				 &privileged_host_port,
				 &device_server_port,
				 &root_ledger_wired,
				 &root_ledger_paged,
				 &security_port);
	if (result != KERN_SUCCESS)
		panic("bootstrap privileged ports %d", result);

	/*
	 * Get default_pager port
	 */
	result = vm_set_default_memory_manager(privileged_host_port,
					       &default_pager_port);
	if (result != KERN_SUCCESS)
                panic("getting default pager port");

	host_port = mach_host_self();

	/*
	 * Lookup our default processor-set name/control ports.
	 */
	(void) processor_set_default(mach_host_self(),
				     &default_processor_set_name);
	(void) host_processor_set_priv(privileged_host_port,
				       default_processor_set_name,
				       &default_processor_set);


	/*
	 * Get the security token for device operations
	 */
	security_token_size = TASK_SECURITY_TOKEN_COUNT;
	result = task_info(mach_task_self(), TASK_SECURITY_TOKEN,
			   (task_info_t) &server_security_token,
			   &security_token_size);
	if (result != KERN_SUCCESS)
		panic("server security token %d", result);
}


void
subsystem_init(void)
{
	kern_return_t		result;
	rpc_subsystem_t		serv_subsystem;
	mach_msg_type_number_t	serv_subsystem_size;
	extern struct rpc_subsystem serv_callback_subsystem;

	/*
	 * Combine the exception_raise subsystem with the callback
	 * subsystem, so that a single port can be used for RPC to
	 * both of them.
	 */
	serv_subsystem =
		mach_subsystem_join((rpc_subsystem_t) &catch_exc_subsystem,
				    (rpc_subsystem_t) &serv_callback_subsystem,
				    &serv_subsystem_size,
				    (void *(*)(int)) cthread_malloc);

	/*
	 * Register subsystems.
	 */
	result = mach_subsystem_create(mach_task_self(),
				       (user_subsystem_t) serv_subsystem,
				       serv_subsystem_size,
				       &exc_subsystem_port);
	if (result != KERN_SUCCESS) {
		MACH3_DEBUG(0, result, ("ports_init: mach_subsystem_create"));
		printk("subsystem_init: continuing without activations\n");
		use_activations = FALSE;
	}
}


void
insert_args(
	char	*args,
	int	*argcp,
	char	***argvp)
{
	int pass, nargs, argc = 0, i;
	char *p, **argv;
	kern_return_t kr;

	/* Two passes over the argument string.  The first counts the
	   arguments so we know how big an argv[] to allocate; the second
	   fills in the pointers in this argv[].  */
	for (pass = 1; pass <= 2; pass++) {
		nargs = 0;
		p = args;
		while (1) {
			while (*p == ' ') p++;
			if (*p == '\0')
				break;
			if (pass == 2)
				argv[nargs] = p;
			nargs++;
			while (*p != ' ' && *p != '\0') p++;
			if (pass == 2 && *p == ' ')
				*p++ = '\0';
		}
		if (pass == 1) {
			/* Allocate a new argv[] that will contain the
			   inserted arguments followed by the remaining
			   original arguments.  After this argv[], we will
			   put a copy of `args' so that we can replace
			   spaces by '\0's in this copy.  We can't use
			   MALLOC yet so vm_allocate directly.  Since
			   pointers to these args can be stashed away
			   (e.g. in bootparam), we never deallocate this
			   memory.  */
			argc = *argcp + nargs;
			i = (argc + 1) * sizeof *argv + p - args + sizeof '\0';
			kr = vm_allocate(mach_task_self(),
					 (vm_address_t *) &argv, i, TRUE);
			if (kr != KERN_SUCCESS) {
				MACH3_DEBUG(1, kr, ("insert_args: allocate"));
				panic("insert_args");
			}
			for (i = 0; i < *argcp; i++)
				argv[i + nargs] = (*argvp)[i];
			argv[argc] = NULL;
			p = (char *) &argv[argc + 1];
			args = strcpy(p, args);
		}
	}
	*argcp = argc;
	*argvp = argv;
}

extern char **server_command_line_p;
boolean_t single_user = FALSE;


void
old_get_config_info(
	int	argc,
	char	**argv)
{
	char		*args;

	if (in_kernel) {
		use_activations = TRUE;
	}

	/*
	 * Parse the arguments.
	 */

	/*
	 * Arg 0 is program name
	 */
	argv++, argc--;

	/*
	 * There may be additional args in our environment
	 */
	args = getenv("startup_flags");

	/*
	 * Process flag arguments:
	 */
	while (1) {
		register char *cp;
		register char c;

		if (argc == 0 || argv[0][0] != '-') {
			if (args != NULL) {
				insert_args(args, &argc, &argv);
				args = NULL;
				continue;
			}
			break;
		}
		cp = argv[0];
		while ((c = *++cp) != '\0') {
			switch (c) {
			    case 'h':
				osfmach3_console_init();	/* XXX */
				Debugger("inline");
				break;
			    case 'l':
				parent_server_set_type(PARENT_SERVER_LINUX);
				goto flag_2;
			    case 'o':
				parent_server_set_type(PARENT_SERVER_OSF1);
				goto flag_2;
			    case '2':	 
flag_2:
				parent_server_get_mach_privilege();
				if (parent_server != 1) {
					parent_server = 1;
					printk("The server is not running standalone. PID = %d.\n",
					       parent_server_getpid());
				}
				break;
			    case 'A':
				/* Set option for using short-circuited
				 * RPC in syscalls.  Check for {0,1}
				 * argument (use 1, if not specified):
				 */
				if (argv[1] && argv[1][0] != '-') {
					use_activations =
						(argv[1][0] != '0');
					argv++; argc--;
				} else
					use_activations = TRUE;

				if (use_activations) {
					if (! in_kernel) {
						printk("\nWARNING: user mode: "
						    "-A ignored\n");
						use_activations = FALSE;
					}
				}
				break;

			    case 'k':
			    case 'S':
				if (!in_kernel)
					printk("WARNING: Server not "
					       "kernel-loaded but -%c found\n",
					       c);
				if (argv[1] && argv[1][0] != '-')
					argv++, argc--;
					/* Skip over the submap base/size arg */
				break;

			    case 's':
				single_user = TRUE;
				break;

			    case 'c':	/* Linux-like command line */
				if (argv[1]) {
					server_command_line_p = &argv[1];
					argv++; argc--;
				}
				break;
			    default:
				printk("Warning: invalid flag `-%c'\n", c);
				break;
			}
		}
		argv++, argc--;
	}

	/*
	 * First non-flag arg is root device name.  If this is a first
	 * server the microkernel will pass us this even if we say -a,
	 * so we skip it even in that case.
	 */
	if (argc > 0) {
		rootdev_name = argv[0];
		argv++, argc--;
	}

	if (in_kernel) {
		printk("*** SERVER IS KERNEL-LOADED ***\n");
	}
	if (use_activations) {
		printk("*** USING ACTIVATIONS ***\n");
	}
}

kernel_boot_info_t kernel_boot_info;


void
get_config_info(
	int	argc,
	char	**argv)
{
	boolean_t	found_name_arg;
	char		*bp;
	int		len;
	kern_return_t	kr;
	char 		*(local_argv[50]);

	if (argc > 1) {
		old_get_config_info(argc, argv);
		return;
	}

	kr = host_get_boot_info(privileged_host_port,
				kernel_boot_info);
	if (kr != KERN_SUCCESS || *kernel_boot_info == 0) {
		old_get_config_info(argc, argv);
		return;
	}
	printk("Boot info: %s\n", kernel_boot_info);
	/*
	 * Process the arguments: the format of the boot info is
	 * 	rootdev [-howto] [kernel_name[:server_name]] [server args]
	 *
	 * We translate this into the syntax expected by old_get_config_info,
	 * i.e. the syntax that is used to invoke a child server as a
	 * process under the main server:
	 *
	 * 	server_name [-howto] [server args] rootdev
	 */

	/* We will construct argc and argv as for a child server: */
	argc = 1;	/* Leave room for argv[0], to be filled in later */
	argv = local_argv;
	bp = kernel_boot_info;

	/* Remove any trailing new-line from kernel_boot_info: */
	len = strlen(bp);
	if (bp[len - 1] == '\n')
		bp[len - 1] = '\0';

	rootdev_name = bp;	/* This will be last entry in argv */
	found_name_arg = FALSE;

	/* Process the blank-separated words of the string into null-terminated
	 * argv entries.  At top of each iteration, we are positioned at
	 * start of the previously processed word (or at the end of string).
	 */
	do {
		/* Find the end of the previous word and null terminate it: */
		while (*bp && *bp != ' ' && *bp != '\n')
			bp++;

		if (*bp == '\n') {
			*bp++ = 0;
			break;
		}
		else if (*bp)
			*bp++ = 0;
		else
			break;

		/* Find the start of the next word, or end-of-line: */
		while (*bp == ' ')
			bp++;

		if (*bp == '\n') {
			*bp++ = 0;
			break;
		}
		else if (!*bp)
			break;

		/* Is it the kernel:server argument? */
		if (*bp != '-' && !found_name_arg) {
			found_name_arg = TRUE;
			/* Skip over kernel name to server name: */
			while (*bp && *bp != ' ' && *bp != ':')
				bp++;
			if (*bp == ':')
				argv[0] = ++bp;
			else
				argv[0] = "[No Server Name Found]";
		} else {
			argv[argc++] = bp;
		}
	} while (1);

	argv[argc++] = rootdev_name;
	argv[argc] = 0;

	old_get_config_info(argc, argv);
}


void
set_rootdev(void)
{
}


void
parse_root_device(const char *rootdev_name)
{
	static char		name[GETS_MAX];
	int kr;

	if (ROOT_DEV != 0) {
		/*
		 * root device has already been specified via
		 * a "root=..." argument in the command line.
		 */
		return;
	}

	while (1) {
		if (rootdev_name == NULL) {
			printk("root device? ");
			rootdev_name = gets(name);
			if (rootdev_name == NULL)
				panic("set_rootdev: gets");
		}
		kr = gen_disk_name_to_dev(rootdev_name, &ROOT_DEV);
		if (kr == 0 && ROOT_DEV != NODEV)
			break;
		printk("Bad Mach root device name: %s\n", rootdev_name);
		rootdev_name = NULL;
	}
	printk("Mach root device %s: major=%d minor=%d\n", rootdev_name,
	       MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
}


void
size_memory(void)
{
	kern_return_t rc;
	int host_buff[HOST_BASIC_INFO_COUNT];
	mach_msg_type_number_t host_buff_size = HOST_BASIC_INFO_COUNT;
	vm_statistics_data_t vm_stat;

	rc = host_info(mach_host_self(), HOST_BASIC_INFO,
		       host_buff, &host_buff_size);
	if (rc != KERN_SUCCESS) {
		printk("host_info() returned 0x%x.\n", rc);
		panic("size_memory: host_info failed");
	}

	osfmach3_mem_size = ((struct host_basic_info *) host_buff)->memory_size;

        host_buff_size = HOST_VM_INFO_COUNT;
	rc = host_statistics(privileged_host_port, HOST_VM_INFO,
			     (host_info_t) &vm_stat, &host_buff_size);
	if (rc != KERN_SUCCESS) {
		printk("vm_statistics() returned 0x%x.\n", rc);
		panic("size_memory: vm_statistics failed");
	}

        (void) host_page_size(mach_host_self(), &page_size);

	page_mask = page_size - 1;

	if ((page_mask & page_size) != 0)
		panic("size_memory: page size not a power of two");

	for (page_shift = 0; ; page_shift++)
		if ((1 << page_shift) == page_size)
			break;
}


void
gen_init(
	int	argc,
	char	**argv)
{
	extern boolean_t	in_kernel;
	boolean_t		page_0_was_allocated;
	kern_return_t		rc;

	size_memory();

	serv_port_register(mach_task_self(), &init_task);
	init_task.osfmach3.task->mach_task_port = mach_task_self();

	/*
	 * Protect our page 0, so that all null pointers references
	 * will fail. We may have to undo the existing protection...
	 */
	if (!in_kernel &&
	    ((rc = vm_protect(mach_task_self(), 0, PAGE_SIZE, TRUE,
			VM_PROT_READ)) != KERN_SUCCESS ||
	     (rc = vm_protect(mach_task_self(), 0, PAGE_SIZE, FALSE,
			VM_PROT_NONE)) != KERN_SUCCESS)) {
		vm_offset_t page_0 = (vm_offset_t)0;	/* page 0 in the map */
		/*
		 * vm_deallocate returns success even for non-mapped storage.
		 * Trust the vm_protect rc instead.
		 */
		(void)vm_deallocate(mach_task_self(), 0, PAGE_SIZE);
                page_0_was_allocated = (rc != KERN_INVALID_ADDRESS); /* (known non-SUCCESS) */
		rc = vm_map(mach_task_self(), &page_0, PAGE_SIZE,
			0, FALSE, MEMORY_OBJECT_NULL, 0, FALSE,
			VM_PROT_NONE, VM_PROT_READ, VM_INHERIT_NONE);
	} else {
		rc = KERN_SUCCESS;
		page_0_was_allocated = FALSE;
        }

	/*
	 * Get arguments and config info
	 */
	get_config_info(argc, argv);
	if (rc != KERN_SUCCESS)
		printk("\aWARNING: failed to reserve page 0. vm_map() returned 0x%x\n", rc);

	if (page_0_was_allocated) {
		printk("\aWARNING: existing page 0 deallocated.\n");
	}

	osfmach3_console_init();
}


void
osfmach3_start_init(
	char	**argv_init,
	char	**envp_init)
{
	extern int	sys_setup(void);
	int		ret;
	kern_return_t	kr;
	mach_port_t	child_thread;
	struct vm_area_struct *mpnt;
	unsigned long	p1, p2, argv, envp;
	int 		argc, envc, i;
	char		*cp;
	struct pt_regs	*user_regs;
	extern char	*rootdev_name;
	unsigned long	old_fs;

	/*
	 * Create a new empty task.
	 */
	ret = do_fork(CLONING_INIT, 0, NULL);
	if (ret != 1) {
		if (ret > 0)
			panic("fork init: got pid %d instead of 1.\n", ret);
		panic("can't fork init: error %d", -ret);
	}

	/*
	 * Act as "init" now.
	 */
	uniproc_switch_to(current, task[smp_num_cpus]);

	/*
	 * This will mount the root file system, amongst other things.
	 */
	old_fs = get_fs();
	set_fs(get_ds());
	sys_setup();
	set_fs(old_fs);

	/*
	 * Load /mach_servers/mach_init into the new task.
	 */
	child_thread = current->osfmach3.thread->mach_thread_port;

	/*
	 * Put the arguments in the empty user task.
	 */
	mpnt = (struct vm_area_struct *)kmalloc(sizeof (*mpnt), GFP_KERNEL);
	if (mpnt == NULL) {
		panic("can't allocate vm_area for init args");
	}
	mpnt->vm_mm = current->mm;
	mpnt->vm_start = PAGE_SIZE;
	mpnt->vm_end = PAGE_SIZE + ARG_MAX;
	mpnt->vm_page_prot = PAGE_COPY;
	mpnt->vm_flags = VM_READ|VM_WRITE;
	mpnt->vm_inode = NULL;
	mpnt->vm_offset = 0;
	mpnt->vm_ops = NULL;
	insert_vm_struct(current->mm, mpnt);

	p1 = mpnt->vm_start;
	for (argc = 0; argv_init[argc] != NULL; argc++);
	for (envc = 0; envp_init[envc] != NULL; envc++);
	p2 = p1 + ((argc+1 + envc+1) * sizeof (char *));

	argv = p1;
	for (i = 0; i < argc; i++) {
		put_fs_long(p2, (long *) p1);
		for (cp = argv_init[i]; *cp; cp++) {
			put_fs_byte(*cp, (char *) p2);
			p2 += sizeof (char);
		}
		put_fs_byte('\0', (char *) p2);
		p2 += sizeof (char);
		p1 += sizeof (char *);
	}
	put_fs_long(0, (long *) p1);
	p1 += sizeof (char *);

	envp = p1;
	for (i = 0; i < envc; i++) {
		put_fs_long(p2, (long *) p1);
		for (cp = envp_init[i]; *cp; cp++) {
			put_fs_byte(*cp, (char *) p2);
			p2 += sizeof (char);
		}
		put_fs_byte('\0', (char *) p2);
		p2 += sizeof (char);
		p1 += sizeof (char *);
	}
	put_fs_long(0, (long *) p1);
	p1 += sizeof (char *);

	/*
	 * The fake exec.
	 */
	ret = do_execve("/mach_servers/mach_init",
			(char **) argv, (char **) envp,
			current->osfmach3.thread->regs_ptr);
	if (ret) {
		panic("can't exec /mach_servers/mach_init: error %d", -ret);
	}
	user_regs = current->osfmach3.thread->regs_ptr;

	uniproc_switch_to(current, task[0]);

	/*
	 * Launch /mach_servers/mach_init.
	 */
	kr = osfmach3_thread_set_state(child_thread,
				       user_regs);
	if (kr != KERN_SUCCESS) {
		MACH3_DEBUG(0, kr, ("osfmach3_start_init: "
				    "osfmach3_thread_set_state(init=0x%x)",
				    child_thread));
		panic("can't set init threads's state");
	}
	kr = thread_resume(child_thread);
	if (kr != KERN_SUCCESS) {
		MACH3_DEBUG(0, kr, ("osfmach3_start_init: "
				    "thread_resume(init=0x%x)",
				    child_thread));
		panic("can't resume init's thread");
	}
}

boolean_t server_halted = FALSE;


void
osfmach3_halt_system(
	boolean_t reset)
{
	kern_return_t kr;

	server_halted = TRUE;	/* for the jiffies thread */
	if (parent_server) {
		parent_server_release_console();
		parent_server_exit(0);
	} else {
		kr = host_reboot(privileged_host_port,
				 reset ? 0 : HOST_REBOOT_HALT);
		if (kr != KERN_SUCCESS) {
			MACH3_DEBUG(0, kr,
				    ("osfmach3_halt_system(%d): host_reboot",
				     reset));
		}
	}
	/* in case it didn't work ... */
	printk("osfmach3_halt_system: entering endless loop...\n");
	for (;;);
}