/* 
 * Mach Operating System
 * Copyright (c) 1991,1990 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 
 * 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 the
 * rights to redistribute these changes.
 */
/*
 * HISTORY
 * $Log:	build_boot.c,v $
 * Revision 2.3  92/01/03  20:28:35  dbg
 * 	Remove '-sequent' switch.  Recognize Sequent from
 * 	real-to-protected bootstrap text following a.out header.
 * 	[91/08/30            dbg]
 * 
 * 	Added code to glue the kernel and the boot file together, for
 * 	all machines.
 * 	[91/05/29            dbg]
 * 
 * Revision 2.2  91/05/08  13:09:36  dbg
 * 	Created.
 * 	[91/02/26            dbg]
 * 
 */

/*
 * Build a pure-kernel boot file from a kernel and a bootstrap loader.
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>

#include <mach/machine/vm_types.h>
#include <mach/boot_info.h>

char *	kernel_name;
char *	bootstrap_name;
char *	boot_file_name = "mach.boot";

int	kern_file;
int	boot_file;
int	out_file;

int	kern_symbols = 1;
int	boot_symbols = 1;

usage()
{
	printf("usage: build_boot [ -o boot_file ] kernel bootstrap\n");
	exit(1);
}

main(argc, argv)
	int	argc;
	char	**argv;
{
	argc--, argv++;	/* skip program name */

	if (argc == 0)
	    usage();

	/*
	 * Parse switches.
	 */
	while (argc > 0 && **argv == '-') {
	    char *s;

	    s = *argv;
	    if (s[1] == 'o') {
		/* output file name */
		argc--, argv++;
		if (argc == 0)
		    usage();
		boot_file_name = *argv;
		argc--, argv++;
	    }
	    else {
		printf("unknown switch: %s\n", s);
		exit(1);
	    }
	}

	if (argc != 2)
	    usage();

	kernel_name = argv[0];
	bootstrap_name = argv[1];

	kern_file = check_and_open(kernel_name);
	boot_file = check_and_open(bootstrap_name);

	out_file = creat(boot_file_name, 0777);	/* XXX mode */
	if (out_file < 0) {
	    perror(boot_file_name);
	    exit(2);
	}

	build_boot();

	close(out_file);
	close(kern_file);
	close(boot_file);

	exit(0);
}

int
check_and_open(fname)
	char *fname;
{
	int f;
	struct stat statb;

	if (stat(fname, &statb) < 0) {
	    perror(fname);
	    exit(2);
	}
	if ((statb.st_mode & S_IFMT) != S_IFREG ||
	    (statb.st_mode & (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))) == 0) {
		printf("build_boot: %s: not an executable file\n",
			fname);
		exit(2);
	}

	f = open(fname, O_RDONLY, 0);
	if (f < 0) {
	    perror(fname);
	    exit(2);
	}
	return (f);
}

/*
 * Create the boot file.
 */
build_boot()
{
	/*
	 * The symbol table is read from the file.
	 * Machine-dependent code can specify an
	 * optional header to be prefixed to the
	 * symbol table, containing information
	 * that cannot directly be read from the file.
	 */
#define	HEADER_MAX	(64*sizeof(int))

	vm_offset_t	sym_off;
	vm_size_t	sym_size;
	char		sym_header[HEADER_MAX];
	vm_size_t	sym_header_size = HEADER_MAX;

	struct loader_info	kern_header;
	struct loader_info	boot_header;
	struct loader_info	boot_header_out;
	struct boot_info	boot_info;

	off_t	off;
	off_t	boot_info_off;
	off_t	boot_image_off;

	/*
	 * Read in the kernel header.
	 */
	if (!ex_get_header(kern_file, 1, &kern_header,
			sym_header, &sym_header_size)) {
	    printf("%s: not an executable file\n", kernel_name);
	    exit(4);
	}

	/*
	 * Copy its text and data to the text section of the output file.
	 */
	lseek(out_file, exec_header_size(), L_SET);

	lseek(kern_file, kern_header.text_offset, L_SET);
	file_copy(out_file, kern_file, kern_header.text_size);

	lseek(kern_file, kern_header.data_offset, L_SET);
	file_copy(out_file, kern_file, kern_header.data_size);

	/*
	 * Allocate the boot_info block.
	 */
	boot_info_off = lseek(out_file, (off_t) 0, L_INCR);
	(void) lseek(out_file, (off_t) sizeof(struct boot_info), L_INCR);

	/*
	 * Find the kernel symbol table.
	 */
	if (kern_symbols && kern_header.sym_size != 0) {
	    /*
	     * Copy the header to the output file.
	     */
	    if (sym_header_size)
		write(out_file, sym_header, sym_header_size);

	    /*
	     * Copy the symbol table to the output file.
	     */
	    lseek(kern_file, kern_header.sym_offset, L_SET);
	    file_copy(out_file, kern_file, kern_header.sym_size);

	    /*
	     * Round to an integer boundary in the file.
	     */
	    sym_size = sym_header_size + kern_header.sym_size;
	    if (sym_size % sizeof(int) != 0) {
		int	pad;
		int	zeros = 0;

		pad = sizeof(int) - (sym_size % sizeof(int));
		write(out_file, (char *)&zeros, pad);
		sym_size += pad;
	    }
		
	    /*
	     * Remember the symbol table size.
	     */
	    boot_info.sym_size = sym_size;
	}
	else {
	    boot_info.sym_size = 0;
	}

	/*
	 * Remember the start of the bootstrap image.
	 */
	boot_image_off = lseek(out_file, (off_t) 0, L_INCR);

	/*
	 * Read the header for the bootstrap file.
	 */
	if (!ex_get_header(boot_file, 0, &boot_header,
			sym_header, &sym_header_size)) {
	    printf("%s: not an executable file\n", bootstrap_name);
	    exit(4);
	}

	/*
	 * Copy the text
	 */
	lseek(boot_file, boot_header.text_offset, L_SET);
	file_copy(out_file, boot_file, boot_header.text_size);

	/*
	 * And the data
	 */
	lseek(boot_file, boot_header.data_offset, L_SET);
	file_copy(out_file, boot_file, boot_header.data_size);

	/*
	 * Symbols for boot program
	 */
	if (boot_symbols && boot_header.sym_size != 0) {
	    /*
	     * Copy the header to the output file.
	     */
	    if (sym_header_size)
		write(out_file, sym_header, sym_header_size);

	    /*
	     * Copy the symbol table to the output file.
	     */
	    lseek(boot_file, boot_header.sym_offset, L_SET);
	    file_copy(out_file, boot_file, boot_header.sym_size);

	    /*
	     * Round to an integer boundary in the file.
	     */
	    sym_size = sym_header_size + boot_header.sym_size;
	    if (sym_size % sizeof(int) != 0) {
		int	pad;
		int	zeros = 0;

		pad = sizeof(int) - (sym_size % sizeof(int));
		write(out_file, (char *)&zeros, pad);
		sym_size += pad;
	    }
		
	    /*
	     * Remember the symbol table size.
	     */
	    boot_header.sym_size = sym_size;
	}
	else {
	    boot_header.sym_size = 0;
	}

	/*
	 * Save the size of the boot image.
	 */
	off = lseek(out_file, (off_t) 0, L_INCR);
	boot_info.boot_size = off - boot_image_off;

	/*
	 * Write out a modified copy of the boot header.
	 * Offsets are relative to the end of the boot header.
	 */
	boot_header_out = boot_header;
	boot_header_out.text_offset = 0;
	boot_header_out.data_offset = boot_header_out.text_offset
					+ boot_header.text_size;
	boot_header_out.sym_offset  = boot_header_out.data_offset
					+ boot_header.data_size;

	write(out_file, (char *)&boot_header_out, sizeof(boot_header_out));

	boot_info.load_info_size = sizeof(boot_header_out);

	/*
	 * Remember the end of the file.
	 */
	off = lseek(out_file, (off_t) 0, L_INCR);

	/*
	 * Go back to the start of the file and write out
	 * the bootable file header.
	 */
	lseek(out_file, (off_t) 0, L_SET);
	write_exec_header(out_file, &kern_header, off);

	/*
	 * Write out the boot_info block.
	 */
	lseek(out_file, boot_info_off, L_SET);
	write(out_file, (char *)&boot_info, sizeof(boot_info));

}

check_read(f, addr, size)
	int	f;
	char *	addr;
	int	size;
{
	if (read(f, addr, size) != size) {
	    perror("read");
	    exit(6);
	}
}

/*
 * Copy N bytes from in_file to out_file
 */
file_copy(out_f, in_f, size)
	int	out_f;
	int	in_f;
	int	size;
{
	char	buf[4096];

	while (size >= sizeof(buf)) {
	    check_read(in_f, buf, sizeof(buf));
	    write(out_f, buf, sizeof(buf));
	    size -= sizeof(buf);
	}
	if (size > 0) {
	    check_read(in_f, buf, size);
	    write(out_f, buf, size);
	}
}

