/*
 * Copyright 1991-1998 by Open Software Foundation, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */
/*
 * MkLinux
 */
/* CMU_HIST */
/*
 * Revision 2.3  92/08/03  17:16:40  jfriedl
 * 	Hacked boot_load_program and read_emulator_symbols to print
 * 	the location of unix and emulator symbols.  This is to make
 * 	sure startup loads the emulator text from the same place as
 * 	the emulator symbol table.
 * 	[92/08/02            cmaeda]
 * 
 * Revision 2.2  92/01/03  19:57:44  dbg
 *      Close startup file after loading it.
 * 	[91/09/25            dbg]
 * 
 * 	Move outside of kernel.
 * 	[91/09/04            dbg]
 * 
 * Revision 2.14  91/09/04  11:27:54  jsb
 * 	Fixed a printf in read_symtab_from_file (following failure of
 * 	get_symtab) by not printing unset variable ``result''.
 * 	[91/09/01  07:48:45  jsb]
 * 
 * Revision 2.13  91/08/28  11:09:49  jsb
 * 	Fixed to check vm_allocate return codes, esp in read_symtab_from_file.
 * 	[91/08/26            rpd]
 * 
 * Revision 2.12  91/07/31  17:25:29  dbg
 * 	Replace blkclr with bzero.
 * 	[91/07/26            dbg]
 * 
 * 	Moved read_symtab_from_file here and made independent of load
 * 	format.
 * 	[91/06/25            dbg]
 * 
 * 
 * Revision 2.11  91/05/14  15:23:41  mrt
 * 	Correcting copyright
 * 
 * Revision 2.10  91/03/16  14:41:37  rpd
 * 	Fixed read_emulator_symbols to not panic if there is no emulator.
 * 	[91/03/13            rpd]
 * 
 * Revision 2.9  91/02/05  17:01:45  mrt
 * 	Changed to new copyright
 * 	[91/01/28  14:55:09  mrt]
 * 
 * Revision 2.8  90/10/25  14:42:05  rwd
 * 	Modified boot_load_program and read_emulator_symbols
 * 	to allocate arrays from heap, not stack.
 * 	[90/10/23            rpd]
 * 
 * Revision 2.7  90/08/27  21:46:02  dbg
 * 	Reduce lint.
 * 	[90/08/13            dbg]
 * 
 * 	Use new error codes.  Use new file_io package.
 * 	[90/07/16            dbg]
 * 
 * Revision 2.6  90/06/02  14:45:31  rpd
 * 	Added read_emulator_symbols.
 * 	[90/05/11  17:08:19  rpd]
 * 
 * 	From jsb: to support debugging, read in the server's symbol table.
 * 	[90/04/23            rpd]
 * 	Converted to new IPC.
 * 	[90/03/26  21:31:33  rpd]
 * 
 * Revision 2.5  90/01/11  11:41:20  dbg
 * 	Use bootstrap-task print routines.
 * 	[89/12/20            dbg]
 * 
 * Revision 2.4  89/11/14  10:28:13  dbg
 * 	Restore read-only protection for text segment.
 * 	[89/11/01            dbg]
 * 
 * Revision 2.3  89/09/08  11:22:13  dbg
 * 	Use new open_file routine.  Change 'struct inode' to
 * 	'struct file' for new stand-alone IO package.
 * 	[89/08/31            dbg]
 * 
 * 26-May-89  Randall Dean (rwd) at Carnegie-Mellon University
 *	Fixed separate text/data case.  Added copyright to version
 *	generated by dbg
 *
 */
/* CMU_ENDHIST */
/* 
 * Mach Operating System
 * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

#include "bootstrap.h"
#include <stdarg.h>
#include <ddb/nlist.h>

int load_program_file(struct file *, task_port_t, thread_port_t, objfmt_t);
void read_symtab_from_file(struct file *, mach_port_t, task_port_t,
			   const char *, objfmt_t);

/*
 * Loads the file into the user space of the specified task,
 * and sets up the starting thread's registers.
 */

boolean_t	load_protect_text = TRUE;

int
load_program_file(register struct file	*fp,
		  task_port_t		user_task,
		  thread_port_t		user_thread,
		  objfmt_t		ofmt)
{
	struct loader_info	*lp = &ofmt->info;
	kern_return_t		result;
	vm_offset_t		text_page_start,
				text_page_end,
				data_page_start,
				data_page_end,
				area_start,
	                        zero_page_start;

#if 0
	if (!file_is_executable(fp)) {
		BOOTSTRAP_IO_LOCK();
		printf("server not executable\n");
		BOOTSTRAP_IO_UNLOCK();
		return (EX_NOT_EXECUTABLE);
	}
#endif

	/*
         * Map page 0 so that the program doesn't get it as
	 * the result of a vm_allocate() or whatever.
	 */
	zero_page_start = 0;
	(void) vm_map(user_task, &zero_page_start, vm_page_size,
			0, FALSE, MEMORY_OBJECT_NULL, 0, TRUE,
			VM_PROT_NONE, VM_PROT_NONE, VM_INHERIT_COPY);

	/*
	 * Allocate space.
	 */
	text_page_start = trunc_page(lp->text_start);
	text_page_end   = round_page(lp->text_start + lp->text_size);
	data_page_start = trunc_page(lp->data_start);
	data_page_end   = round_page(lp->data_start + lp->data_size)
		+ round_page(lp->bss_size);

	if (text_page_end >= data_page_start) {
	    /*
	     * One contiguous area for text and data.
	     */
	    result = vm_allocate(mach_task_self(),
				&area_start,
				(vm_size_t)(data_page_end - text_page_start),
				TRUE);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("allocate text+data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = read_file(fp,
				lp->text_offset,
				area_start + (lp->text_start - text_page_start),
				lp->text_size);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("read text into text+data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = read_file(fp,
				lp->data_offset,
				area_start + (lp->data_start - text_page_start),
				lp->data_size);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("read data into text+data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    memset((char *)(area_start + (lp->data_start + lp->data_size - text_page_start)),
		   0,
		   lp->bss_size);

	    result = vm_allocate(user_task,
				&text_page_start,
				(vm_size_t)(data_page_end - text_page_start),
				FALSE);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("allocate text+data (%x,%x) failed\n", text_page_start,
		       data_page_end);
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = vm_write(user_task,
				text_page_start,
				(pointer_t)area_start,
				(vm_size_t)(data_page_end - text_page_start));
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("write text+data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = vm_deallocate(mach_task_self(),
				area_start,
				(vm_size_t)(data_page_end - text_page_start));
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("deallocate text+data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	}
	else {
	    /*
	     * Separated text and data areas.
	     */
	    /*
	     * First read in the text
	     */
	    result = vm_allocate(mach_task_self(),
				&area_start,
				(vm_size_t)(text_page_end - text_page_start), TRUE);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("allocate text buffer failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = read_file(fp,
			       lp->text_offset,
			       area_start + (lp->text_start - text_page_start),
			       lp->text_size);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("read text failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = vm_allocate(user_task,
				&text_page_start,
				(vm_size_t)(text_page_end - text_page_start),
				FALSE);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("allocate text (%x,%x) failed\n", text_page_start,
		       data_page_end);
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = vm_write(user_task,
				text_page_start,
				(pointer_t)area_start,
				(vm_size_t)(text_page_end - text_page_start));
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("write text failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = vm_deallocate(mach_task_self(),
				area_start,
				(vm_size_t)(text_page_end - text_page_start));
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("deallocate text failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    /*
	     * Then the data
	     */
	    result = vm_allocate(mach_task_self(),
				&area_start,
				(vm_size_t)(data_page_end - data_page_start),
				TRUE);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("allocate data buffer failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = read_file(fp,
			       lp->data_offset,
			       area_start + (lp->data_start - data_page_start),
			       lp->data_size);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("read data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    memset((char *)(area_start + (lp->data_start + lp->data_size - data_page_start)),
		   0,
		   lp->bss_size);

	    result = vm_allocate(user_task,
				&data_page_start,
				(vm_size_t)(data_page_end - data_page_start),
				FALSE);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("allocate data (%x,%x) failed\n", data_page_start,
		       data_page_end);
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = vm_write(user_task,
				data_page_start,
				(pointer_t)area_start,
				(vm_size_t)(data_page_end - data_page_start));
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("write data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	    result = vm_deallocate(mach_task_self(),
				area_start,
				(vm_size_t)(data_page_end - data_page_start));
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("deallocate data failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }

	}
	/*
	 * Protect the text.
	 */
	if (load_protect_text) {
	    result = vm_protect(user_task,
			    text_page_start,
			    (vm_size_t)(trunc_page(lp->text_start+lp->text_size) - text_page_start),
			    FALSE,
			    VM_PROT_READ|VM_PROT_EXECUTE);
	    if (result) {
		BOOTSTRAP_IO_LOCK();
		printf("protect text failed\n");
		BOOTSTRAP_IO_UNLOCK();
		return (result);
	    }
	}

	return (KERN_SUCCESS);
}

#define	isspace(c)	((c) == ' ' || (c) == '\t')

/*VARARGS4*/
int
boot_load_program(mach_port_t master_host_port,
		  mach_port_t master_device_port,
		  task_port_t user_task,
		  thread_port_t user_thread,
		  unsigned int server_flags,
		  const char *symtab_name,
		  const char *file_name,
		  unsigned long mapend)
{
	int		arg_len;
	kern_return_t	result;
	struct file	file;
	struct objfmt	ofmt;

	/*
	 * Open the file
	 */

	memset((char *)&ofmt, 0, sizeof (ofmt));
	memset((char *)&file, 0, sizeof(file));
	result = open_file(master_device_port, file_name, &file);
	if (result != 0) {
	    BOOTSTRAP_IO_LOCK();
	    printf("openi %d\n", result);
	    BOOTSTRAP_IO_UNLOCK();
	    return (result);
	}
#ifdef DEBUG
	BOOTSTRAP_IO_LOCK();
	printf("bootstrap: opened <%s>\n", file_name);
	BOOTSTRAP_IO_UNLOCK();
#endif

	/*
	 * Read in the header and get the file pointers.
	 */
	result = ex_get_header(&file, &ofmt);
	if (result) {
	    BOOTSTRAP_IO_LOCK();
	    printf("boot_load_program: failed to read file header.\n");
	    BOOTSTRAP_IO_UNLOCK();
	    return (result);
        }

	/*
	 * Allocate space for:
	 *    dummy 0 argument count
	 *    dummy 0 pointer to arguments
	 *    dummy 0 pointer to environment variables
	 *    and align to integer boundary
	 */
	arg_len = sizeof(int) + 2 * sizeof(char *);
	arg_len = (arg_len + (sizeof(int) - 1)) & ~(sizeof(int)-1);

	/*
	 * Load the file
	 */
	result = load_program_file(&file, user_task, user_thread, &ofmt);
	if (result) {
	    BOOTSTRAP_IO_LOCK();
	    printf("load_program_file failed - %d\n", result);
	    BOOTSTRAP_IO_UNLOCK();
	    return (result);
	}

	set_regs(master_host_port, user_task,
	    user_thread, &ofmt.info, mapend, arg_len);

	/*
	 * Read symbols from file
	 */
	(*ofmt.fmt->symload)(&file, master_host_port, user_task,
			     symtab_name, &ofmt);
	/*
	 * Close the file.
	 */
	close_file(&file);
	return (0);
}

int
ex_get_header(struct file *fp, objfmt_t ofmt)
{
    int i;
    objfmt_switch_t fmt;
    char buf[HEADER_MAX], *bufptr = buf;
    int result;

    result = read_file(fp, 0, (vm_offset_t)buf, sizeof(buf));
    if (result)
	return (result);

    for (i = 0; formats[i] != 0; i++) {
	fmt = formats[i];
	if (!(*fmt->recog)(fp, ofmt, &bufptr))
	    continue;
	ofmt->fmt = fmt;
	result = (*fmt->load)(fp, ofmt, bufptr);
	if (bufptr != buf)
	    free(bufptr);
	return result;
    }
    return EX_NOT_EXECUTABLE;
}
