/* Second stage boot loader
   
   Copyright (C) 1996 Pete A. Zaitcev
   		 1996 Maurizio Plaza
   		 1996 David S. Miller
   		 1996 Miguel de Icaza
   		 1996 Jakub Jelinek
   
   This program 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 of the License, or
   (at your option) any later version.
   
   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "silo.h"
#include <linux/elf.h>

/* This has to be first initialized variable in main.c */

extern char silo_conf[256];
extern char silo_conf_part;

extern int start;

int useconf = 0;
struct linux_nodeops *prom_nodeops;
int prom_root_node;
struct linux_romvec *romvec;
enum arch architecture;
static int timer_status = 0;
static char *initrd_start;
static int initrd_size;
static int initrd_defpart;
static char *initrd_defdevice;
static char *initrd_string;
static int initrd_prompt;
static int pause_after;
static char *pause_message = "Press ENTER to continue.";
static int show_arguments = 0;
static char given_bootargs [512];
static int given_bootargs_by_user = 0;
static char sol_params [512];
static int fill_reboot_cmd = 0;
static char other_device [512];
int other_part = -1;
int solaris = 0;
int other = 0;
char *password = 0;

void maintabfunc (void)
{
    cfg_print_images ();
    printf ("boot: %s", cbuff);
}

void parse_name (char *imagename, int defpart, char **device, int *part, char **kname)
{
    static char parsebuf[1024];
    
    strcpy (parsebuf, imagename);
    imagename = parsebuf;
    *device = 0;
    *kname = 0;
    if (!strncmp (imagename, "!cd", 3)) {
        *kname = imagename;
        *part = 0;
        return;
    }
    if (romvec->pv_romvers < 2) {
        static char v0_buffer[20];
        *kname = v0_device (imagename);
        if (*kname) {
            memcpy (v0_buffer, imagename, *kname - imagename + 1);
            v0_buffer [*kname - imagename + 1] = 0;
            (*kname)++;
            *device = v0_buffer;
        }  else
            *kname = imagename;
    } else {
    	*kname = strchr (imagename, ';');
    	if (!*kname)
	    *kname = imagename;
    	else {
	    **kname = 0;
	    (*kname)++;
	    *device = imagename;
        }
    }
    /* Range */
    if (**kname >= '0' && **kname <= '8' && (*kname)[1] == '[') {
	*part = **kname - '0';
	(*kname)++;
	return;
    }
    if (**kname == '[') {
	*part = 0;
	return;
    }
    /* Path */
    if (**kname >= '1' && **kname <= '8') {
    	if ((*kname)[1] == '/' || !(*kname)[1]) {
	    *part = **kname - '0';
	    if (!(*kname)[1]) {
	    	*kname = 0;
	    } else
	        (*kname)++;
	}
    } else if (**kname == '/') {
    	if (defpart != -2) {
	    if (defpart != -1)
	    	*part = defpart;
	    else
	    	*part = 1;
	}
    } else
	*kname = 0;
}

void check_initrd (char *label, int defpart, char *defdevice)
{
    char *p;
    
    initrd_string = cfg_get_strg (label, "initrd");
    if (initrd_string) {
	initrd_defpart = defpart;
	initrd_defdevice = defdevice;
	initrd_size = 0;
	p = cfg_get_strg (label, "initrd-size");
	if (p) initrd_size = atoi (p);
	initrd_prompt = cfg_get_flag (label, "initrd-prompt");
    }
}

void dig_into_params (char *params)
{
    char *p, *q;
    char *last_root = 0;

    p = params;    
    for (;;) {
        while (*p == ' ') p++;
        if (!*p) break;
        if (!strncmp (p, "initrd", 6)) {
    	    p += 6;
    	    if (*p == '=') {
    	        for (q = p++; *q && *q != ' '; q++);
    	        if (p != q) {
    	    	    initrd_string = malloc (q - p + 1);
    	    	    memcpy (initrd_string, p, q - p);
    	    	    initrd_string [q - p] = 0;
    	    	    memset (p - 7, ' ', q - p + 7);
    	        }
    	    } else if (!strncmp (p, "-prompt", 7)) {
    	        initrd_prompt = 1;
    	        memset (p - 6, ' ', 7 + 6);
    	    } else if (!strncmp (p, "-size=", 6)) {
    	        int i;
    	        p += 6;
    	        i = atoi (p);
    	        if (i > 0) {
    	            initrd_size = i;
    	            for (q = p; *q >= '0' && *q <= '9'; q++);
    	            memset (p - 12, ' ', q - p + 12);
    	        }
    	    }
        } else if (!strncmp (p, "pause-after", 11)) {
    	    pause_after = 1;
    	    memset (p, ' ', 11);
        } else if (!strncmp (p, "show-arguments", 14)) {
            show_arguments = 1;
            memset (p, ' ', 14);
        } else if (!strncmp (p, "root=", 5)) {
            if (last_root) {
                while (*last_root != ' ') *last_root++ = ' ';
            }
            last_root = p;
        }
        while (*p && *p != ' ') p++;
    }
}

void halt (struct linux_romvec *promvec)
{
    close ();
    if (timer_status >= 1)
        close_timer (promvec);
    (*promvec->pv_halt) ();
}

void check_password(char *str)
{
    int i;
    
    for (i = 0; i < 3; i++) {
	printf ("\n%sassword: ", str);
	passwdbuff[0] = 0;
	cmdedit ((void (*)(void)) 0, 1);
	printf ("\n");
	if (!strncmp (password, passwdbuff))
	    return;
	if (i < 2)
	    printf ("Wrong password. Please try again...");
    }
    printf ("Seems like you don't know the access password...\n");
    halt(romvec);
}

int get_params (struct linux_romvec *promvec, char **device, int *part, char **kname, char **params)
{
    int defpart = -1;
    char *defdevice = 0;
    char *p, *q;
    int c;
    char *imagename = 0, *label = 0;
    int timeout = -1, beg = 0, end;
    static char buffer[2048], buffer2[2048];
    static int first = 1;
    int from_prom = 0;
    int from_siloconf = 0;
    static int no_prom_args = 0;
    int tabbedout = 0;
    char *bootblock = 0;
    
    other = 0;
    fill_reboot_cmd = 0;
    solaris = 0;
    initrd_string = 0;
    pause_after = 0;
    cmdinit ();
    *params = "";
    if (useconf) {
	defdevice = cfg_get_strg (0, "device");
	p = cfg_get_strg (0, "partition");
	if (p && *p >= '1' && *p <= '8' && !p[1])
	    defpart = *p - '0';
	if (first) {
	    first = 0;
	    p = cfg_get_strg (0, "timeout");
	    if (p && *p)
	        timeout = atoi (p);
	    if (no_prom_args) p = 0;
	    else p = get_bootargs (promvec, 0);
	    if (p) while (*p == ' ') p++;
	    if (p && *p) {
	        for (q = p; *q && *q != ' '; q++);
	        if (*q == ' ') {
	            *q++ = 0;
	            while (*q == ' ') q++;
	            if (*q) *params = q;
	        }
	        imagename = p;
	        from_prom = 1;
	        printf ("\n");
	    } else if (!timeout) {
	    	printf ("boot: ");
	        c = nbgetchar (promvec);
		if (c != -1 && c != '\n' && c != '\r') {
		    if (c == '\t') {
		        maintabfunc ();
		        tabbedout = 1;
		    } else if (c >= ' ') {
		        cbuff[0] = c;
		        cbuff[1] = 0;
		    }
	        } else {
		    imagename = cfg_get_default ();
		    printf ("\n");
		}
	    } else if (timeout != -1) {
		if (!timer_status) {
		    if (!init_timer (promvec)) timer_status = 1;
		    else timer_status = -1;
		} else
		    timer_status++;
	        if (timer_status <= 0) {
	            printf ("\nYour timeout %d.%ds will never expire, since counter couldn't"
	            	    "\nbe initialized\nboot: ", timeout/10, timeout % 10);
	        } else {
                    printf ("boot: ");
	            reset_ticks ();
	            c = nbgetchar (promvec);
	            if (c == -1) {
	            	beg = get_ticks ();
		        end = beg + 10 * timeout;
	            	do {
		            c = nbgetchar (promvec);
		            if (c != -1)
		                break;
	                } while (get_ticks () <= end);
	            }
		    if (c != -1 && c != '\n' && c != '\r') {
		        if (c == '\t') {
		            maintabfunc ();
		            tabbedout = 1;
		        } else if (c >= ' ') {
		            cbuff[0] = c;
		            cbuff[1] = 0;
		        }
	            } else {
		        imagename = cfg_get_default ();
		        printf ("\n");
		    }
		    if (timer_status >= 1) {
		        timer_status--;
		        close_timer (promvec);
		    }
	        }
	    }
	}
	if (!imagename) {
	    if ((!*cbuff || (timeout > 0 && timer_status < 0)) && !tabbedout)
                printf ("boot: ");
	    cmdedit (maintabfunc, 0);
	    strcpy (given_bootargs, cbuff);
	    given_bootargs_by_user = 1;
	    printf ("\n");
	    if (!*cbuff)
		imagename = cfg_get_default ();
	    else {
		p = strchr (cbuff, ' ');
		if (!p)
		    *params = "";
		else {
		    *p++ = 0;
		    while (*p && *p <= ' ')
			p++;
		    *params = p;
		}
		imagename = cbuff;
	    }
	}
	p = cfg_get_strg (imagename, "image");
	if (p && *p) {
	    label = imagename;
	    imagename = p;
	} else
	    label = 0;
	p = cfg_get_strg (0, "pause-message");
	if (p) pause_message = p;
	if (label) {
	    if (**params && password)
		check_password ("To specify image arguments you need to enter p");
	    from_siloconf = 1;
	    defdevice = cfg_get_strg (label, "device");
	    p = cfg_get_strg (label, "partition");
	    if (p && *p >= '1' && *p <= '8' && !p[1])
		defpart = *p - '0';
	    *buffer = 0;
	    q = buffer;
	    if (cfg_get_flag (label, "fill-reboot-cmd"))
	        fill_reboot_cmd = 1;
	    if (cfg_get_strg (label, "other"))
	    	other = 1;
	    else if (cfg_get_flag (label, "solaris"))
	    	solaris = 1;
	    p = cfg_get_strg (label, "literal");
	    if (!p && other)
	    	p = cfg_get_strg (label, "append");
	    if (p) {
		strcpy (q, p);
		q = strchr (q, 0);
		if (**params) {
		    if (*p)
		        *q++ = ' ';
		    strcpy (q, *params);
		}
	    } else if (!solaris && !other) {
		p = cfg_get_strg (label, "root");
		if (p) {
		    strcpy (q, "root=");
		    strcpy (q + 5, p);
		    q = strchr (q, 0);
		    *q++ = ' ';
		}
		if (cfg_get_flag (label, "read-only")) {
		    strcpy (q, "ro ");
		    q += 3;
		}
		if (cfg_get_flag (label, "read-write")) {
		    strcpy (q, "rw ");
		    q += 3;
		}
		p = cfg_get_strg (label, "ramdisk");
		if (p) {
		    strcpy (q, "ramdisk=");
		    strcpy (q + 8, p);
		    q = strchr (q, 0);
		    *q++ = ' ';
		}
		p = cfg_get_strg (label, "append");
		if (p) {
		    strcpy (q, p);
		    q = strchr (q, 0);
		    *q++ = ' ';
		}
		check_initrd (label, defpart, defdevice);
		if (**params)
			strcpy (q, *params);
	    }
	    if (other) {
	        char *oth_device = 0;
	        char *oth_kname = 0;
	        
	        other_part = -1;
    		parse_name (imagename, -1, &oth_device, &other_part, &oth_kname);
    		if (other_part == -1 || oth_kname) {
    		    printf ("Wrong syntax for other= parameter. The parameter should be\n");
    		    if (romvec->pv_romvers < 2)
    		        printf ("either a single number 1-8 (ie. partition of the current disk)\n"
    		                "or sd(X,Y,Z)N (ie. some other disk plus partition number)\n"
    		                "e.g. sd(0,3,2)4\n");
    		    else
    		        printf ("either a single number 1-8 (ie. partition of the current disk)\n"
    		                "or /prom/path/name;N (ie. some other disk plus partition number)\n"
    		                "e.g. /iommu/sbus/espdma/esp/sd@3,0;3 \n");
    		    *kname = 0;
    		    return 0;
    		}
    		if (!oth_device) {
    		    if (defdevice)
    		        oth_device = defdevice;
    		    else
    		        oth_device = bootdevice;
    		}
    		strcpy (other_device, oth_device);
	    	p = cfg_get_strg (label, "bootblock");
	    	if (p)
	    	    imagename = p;
	    	else {
	    	    static char bufx[8];
	    	    
	    	    strcpy (bufx, "x[1-16]");
	    	    *bufx = other_part + '0';
	    	    defdevice = other_device;
	    	    defpart = other_part;
	    	}
	    }
	    pause_after = cfg_get_flag (label, "pause-after");
	    p = cfg_get_strg (label, "pause-message");
	    if (p) pause_message = p;
	    *params = buffer;
	}
    } else {
        if (first) {
            first = 0;
	    if (no_prom_args) p = 0;
	    else p = get_bootargs (promvec, 0);
	    if (p) while (*p == ' ') p++;
	    if (p && *p) {
	        for (q = p; *q && *q != ' '; q++);
	        if (*q == ' ') {
	            *q++ = 0;
	            while (*q == ' ') q++;
	            if (*q) *params = q;
	        }
	        imagename = p;
	        from_prom = 1;
	        printf ("\n");
	    }
	}
	if (!imagename) {
            printf ("boot: ");
	    cmdedit ((void (*)(void)) 0, 0);
	    strcpy (given_bootargs, cbuff);
	    given_bootargs_by_user = 1;
	    printf ("\n");
	    p = strchr (cbuff, ' ');
	    if (!p)
	        *params = "";
	    else {
	        *p = 0;
	        p++;
	        while (*p && *p <= ' ')
		    p++;
	        *params = p;
	    }
	    imagename = cbuff;
	}
    }
    if (!strcmp (imagename, "halt"))
	return 1;
    if (!label && password)
    	check_password ("To boot a custom image you need to type here your p");
    if (!strcmp (imagename, "xxdebug"))
	return 2;
    if (!strcmp (imagename, "help")) {
        if (promvec->pv_romvers < 2)
            printf ("You have to type image name as {XY(...)}partno/path or {XY(...)}partno[mm-nn],\n"
                    "where XY(...) is the optional v0 prom device name (if partition number is specified,\n"
                    "it should be Sun partition number (zero based) of any partition starting at cyl. 0)\n"
                    "({} means that it is optional) and partno is a Linux partition number from 1 to 8.\n"
                    "You can specify a path into filesystem (ext2fs, ufs) - has to start with / - or\n"
                    "[mm-nn] as range of phys. blocks (512B).\n");
	else
	    printf ("You have to type image name as [prom_path;]partno/path, where partno is a\n"
	    	    "number from 1 to 8. If partno is not specified, either default from silo.conf\n"
	    	    "(partition=X) or 1 will be used. Instead of /path you can type [mm-nn] to\n"
	    	    "specify a range of phys. blocks (512B)\n");
	printf ("If you use silo.conf and have some image= sections there, you can type\n"
                "its label or alias name instead of the above described image names.\n"
                "Pressing just enter will load default image with default arguments. Special\n"
                "image names `halt' and `help' can be used to fall back to PROM or display\n"
                "help. All three types of image names can be followed by additional arguments.\n"
                "Examples:\n");
        if (promvec->pv_romvers < 2)
            printf ("  /boot/vmlinux.gz root=/dev/sda4\n"
                    "  2/boot/mykernel.gz root=/dev/sda2\n"
                    "  sd(0,6,2)5/boot/old.b\n"
                    "  sd(1,2,0)[1-16] root=/dev/sda4\n");
        else
            printf ("  /iommu/sbus/espdma/esp/sd@3,0;4/boot/vmlinux.gz root=/dev/sda4\n"
                    "  1/boot/old.b\n"
                    "  /sbus/espdma/esp/sd@0,0;3[1-16] root=/dev/sda4\n");
        printf ("  linux root=/dev/sda4\n"
                "  live\n");
    	*kname = 0;
        return 0;
    }
    strcpy (buffer2, imagename);
    *part = -2;
    parse_name (imagename, -2, device, part, kname);
    if (!*device)
	*device = defdevice;
    if (*kname && *part == -2) {
        if (defpart != -1)
            *part = defpart;
        else
            *part = 1;
    }
    if (!*kname) {
    	if (*part != -2) {
	    other_part = *part;
	    if (!*device)
	    	strcpy (other_device, bootdevice);
	    else
	    	strcpy (other_device, *device);
	    p = strstr (*params, "bootblock=");
	    if (p && (p == *params || p[-1] == ' ') && p[10] && p[10] != ' ') {
	        char tmp[512], *q;
	        
	        q = tmp; 
	        p += 10;
	        while (*p && *p != ' ')
	            *q++ = *p++;
	        *q = 0;
	        parse_name (tmp, defpart, device, part, kname);
	        if (*kname) {
	            p = strstr (*params, "bootblock=");
	            memset (p, ' ', q - tmp + 10);
	            other = 1;
	            return 0;
	        } else {
	            printf ("Syntax of your bootblock= parameter is wrong. Please see help\n");
	        }
	    } else {
	    	other = 1;
	    	*kname = strdup ("[1-16]");
	    	return 0;
	    }
    	} else {
            if (defpart != -1)
                *part = defpart;
            else
                *part = 1;
    	}
        if (from_prom) {
            int options_node, len;
            int is_from_prom = 0;
            char *v = "";
            char buffer3[2048];
            
    	    if ((options_node = prom_searchsiblings (prom_getchild (prom_root_node), "options")) != 0) {
    	    	if (promvec->pv_romvers >= 2) {
		    prom_getstring (options_node, v = "boot-file", buffer3, 2048);
		    len = prom_getproplen (options_node, v);
		    if (len < 0) len = 0;
		    buffer3[len] = 0;
		    if (!strcmp (buffer3, *promvec->pv_v2bootargs.bootargs))
		        is_from_prom = 1;
		    else {
		        prom_getstring (options_node, v = "diag-file", buffer3, 2048);
		        len = prom_getproplen (options_node, v);
		        if (len < 0) len = 0;
		        buffer3[len] = 0;
		        if (!strcmp (buffer3, *promvec->pv_v2bootargs.bootargs))
		            is_from_prom = 1;
		    }
		} else {
		    prom_getstring (options_node, v = "boot-from", buffer3, 2048);
		    len = prom_getproplen (options_node, v);
		    if (len < 0) len = 0;
		    buffer3[len] = 0;
		    if (!strcmp (buffer3, ((*(promvec->pv_v0bootargs))->argv)[0]))
		    	is_from_prom = 1;
		}
	    }
	    if (is_from_prom) {
	    	first = 1;
	    	no_prom_args = 1;
	    	*given_bootargs = 0;
	    	given_bootargs_by_user = 1;
	    	printf ("You have `%s' string in your %s variable.\n"
	    		"This string doesn't contain valid arguments to SILO.\n"
	    		"Consider doing setenv %s. Anyway, SILO will continue as\n"
	    		"if there were no arguments in %s.\n", buffer3, v, v, v);
	    	return 0;
	    }
        }
    	printf ("Your imagename `%s' and arguments `%s' have either wrong syntax,\n"
    	        "or describe a label which is not present in silo.conf\n"
    	        "Type `help' at the boot: prompt if you need it and then try again.\n", buffer2, *params ? *params : "");
    } else if (!solaris) {
    	p = strstr (*params, "solaris");
    	if (p && (p == *params || p[-1] == ' ') && (!p[7] || p[7] == ' ')) {
    	    memset (p, ' ', 7);
    	    solaris = 1;
    	} else if (!strcmp (*kname, "/kernel/unix"))
    	    solaris = 1;
    	if (!fill_reboot_cmd) {
    	    p = strstr (*params, "fill-reboot-cmd");
    	    if (p && (p == *params || p[-1] == ' ') && (!p[15] || p[15] == ' ')) {
    	    	memset (p, ' ', 15);
    	    	fill_reboot_cmd = 1;
    	    }
    	}
    }
    return 0;
}

void initrd_lenfunc (int len, char **filebuffer, char **filelimit)
{
    initrd_start = memory_find ((len + 16383) & ~16383);
    if (!initrd_start) {
        fatal ("You do not have enough continuous available memory for such initial ramdisk.");
        halt (romvec);
    }
    initrd_size = len;
    printf ("Loading initial ramdisk....\n");
    *filebuffer = initrd_start;
    *filelimit = initrd_start + ((len + 16383) & ~16383);
}

/* Here we are launched */
int bootmain (struct linux_romvec *promvec)
{
    unsigned off;
    int len, image_len;
    char *kname, *params, *device;
    int part;
    union {
	char *b;
	struct aout_hdr *a;
	Elf32_Ehdr *e;
    } hp;
    int isfile, fileok = 0;
    char *p;
    unsigned int ret_offset;
    char *params_device = 0;

    romvec = promvec;

    prom_nodeops = promvec->pv_nodeops;
    prom_root_node = prom_getsibling (0);
    get_idprom();
    prom_ranges_init ();
    architecture = get_architecture (promvec);
    strcpy (given_bootargs, get_bootargs (promvec, 1));

    printf (" ");
   
    if (diskinit (promvec) == -1)
	(*promvec->pv_halt) ();

/* Here should be code #ifdef TFTP, that will handle loading of silo.conf via tftp and this code should be on the other side #ifndef TFTP */
    if (*silo_conf && silo_conf_part >= 1 && silo_conf_part <= 8) {
	int len;
	solaris = 0;
	fileok = load_file (0, silo_conf_part, silo_conf, (unsigned char *) 0x4000, (unsigned char *) &start, &len, 1, 0);
	if (!fileok || (unsigned) len >= 65535)
	    printf ("\nCouldn't load %s\n", silo_conf);
	else {
	    if (!cfg_parse (silo_conf, (unsigned char *) 0x4000, len)) {
		char *p, *q;
		int len = 0;
		int defpart = -1;
		int i;
		p = cfg_get_strg (0, "message");
		if (p) {
		    q = cfg_get_strg (0, "partition");
		    if (q && *q >= '1' && *q <= '8' && !q[1])
			defpart = *q - '0';
		    parse_name (p, defpart, &device, &part, &kname);
		    if (kname) {
			if (!device)
			    device = cfg_get_strg (0, "device");
			solaris = 0;
			if (load_file (device, part, kname, (unsigned char *) 0x4000, (unsigned char *) &start, &len, 1, 0)) {
			    *(unsigned char *) (0x4000 + len) = 0;
			    printf ("\n");
			    print_message ((char *) 0x4000);
			}
		    }
		}
		useconf = 1;
		password = cfg_get_strg (0, "password");
		if (password && !cfg_get_flag (0, "restricted")) {
		    check_password ("P");
		    password = 0;
		}
	    } else {
	        printf ("Syntax error in %s\nPlease check the file for obvious bugs, and if you think it is correct, get"
	                "latest version of SILO. If problems still survive, contact the author\n", silo_conf);
	    }
	}
    } else
	printf ("\n");
    if (!useconf)
	printf ("No config file loaded, you can boot just from this command line\n"
	"Type [prompath;]part/path_to_image [parameters] on the prompt\n"
	  "E.g. /iommu/sbus/espdma/esp/sd@3,0;4/vmlinux root=/dev/sda4\n"
		"or 2/vmlinux.live (to load vmlinux.live from 2nd partition of boot disk)\n");

    isfile = 0;			/* RC = 0 invalid file or not an executable */
    while (!isfile) {
	switch (get_params (promvec, &device, &part, &kname, &params)) {
	case 1:
	    halt (promvec);
	case 2:
	    (*promvec->pv_abort) ();
	    continue;
	}
	if (!kname)
	    continue;

	if (solaris) {
	    char *p = seed_part_into_device ((!device || !*device) ? bootdevice : device, part);
	    strcpy (sol_params, p);
	    params_device = strchr (sol_params, 0) + 1;
	    strcpy (params_device, kname);
	    if (params && *params) {
	        strcat (params_device, " ");
	        strcat (params_device, params);
	    }
	}
	    
	fileok = load_file (device, part, kname, (unsigned char *) 0x4000, (unsigned char *) &start, &image_len, 1, 0);
	
	ret_offset = 0x4000;

	if (fileok) {
	    /* By this point the first sector is loaded (and the rest of */
	    /* the kernel) so we check if it is an executable file, either */
	    /* an a.out or an elf binary */

	    hp.b = (char *) 0x4000;
	    if (hp.a->magic == 0x01030107) {
	    	if (solaris) {
	    	    printf ("\nYour Solaris `ufsboot' is not an ELF image. Try again.\n");
	    	    continue;
	    	}
		off = sizeof (struct aout_hdr);
		if (image_len > hp.a->ltext + hp.a->ldata)
		    len = hp.a->ltext + hp.a->ldata;
		else
		    len = image_len;
		isfile = 1;
	    } else {
		Elf32_Phdr *p;
		if (hp.e->e_ident[EI_MAG0] == ELFMAG0 &&
		    hp.e->e_ident[EI_MAG1] == ELFMAG1 &&
		    hp.e->e_ident[EI_MAG2] == ELFMAG2 &&
		    hp.e->e_ident[EI_MAG3] == ELFMAG3) {
		    unsigned int e_entry;
		    
		    if (hp.e->e_ident[EI_CLASS] != ELFCLASS32) {
			fatal ("Image is not a 32bit ELF");
			halt (promvec);
		    }
		    if (hp.e->e_ident[EI_DATA] != ELFDATA2MSB) {
			fatal ("Image is not a MSB ELF");
			halt (promvec);
		    }
		    p = (Elf32_Phdr *) (hp.b + hp.e->e_phoff);
		    if (p->p_type != PT_LOAD) {
			fatal ("Cannot find a loadable segment in your ELF image");
			halt (promvec);
		    }
		    if (solaris) {
		        int i;
		        
		        for (i = 0; i < hp.e->e_phnum; i++, p++) {
		            if (p->p_vaddr < 0x4000 + image_len || p->p_vaddr + p->p_memsz >= 0x280000) {
		            	fatal("Unable to handle your Solaris `ufsboot' bootloader.");
		            	halt (promvec);
		            }
		            memcpy ((char *)p->p_vaddr, (char *)(0x4000 + p->p_offset), p->p_filesz);
		            if (p->p_filesz < p->p_memsz)
		            	memset ((char *)(p->p_vaddr + p->p_filesz), 0, p->p_memsz - p->p_filesz);
		        }
		        isfile = 1;
		        ret_offset = hp.e->e_entry;
		    } else {
		    	off = p->p_offset + hp.e->e_entry - p->p_vaddr;
		    	len = p->p_filesz;
		    	if (len > image_len) len = image_len;
		    	isfile = 1;
		    }
		} else
		    printf ("\nUnknown %s image format\n", kname);
	    }
	} else
	    printf ("\nImage not found.... try again\n");
    }

    if (solaris) {
    	params = params_device;
    	params_device = sol_params;
    } else if (!other) {
    	params_device = 0;
        memcpy ((unsigned char *) 0x4000, ((unsigned char *) 0x4000) + off, len);
        p = find_linux_HdrS (image_len);
        if (p) {
            if (fill_reboot_cmd && *(unsigned short *)(p + 8) >= 0x201) { /* ie. uses reboot_command */
                char *q = (char *)(*(unsigned int *)(p + 24) - 0xF0000000), *r;
                extern char bootdevice[];
                
                if (q >= (char *)0x4000 && q <= (char *)0x300000) {
                    if (given_bootargs_by_user) {
                        if (strlen (bootdevice) <= 254) {
			    strcpy (q, bootdevice);
			    r = strchr (q, 0);
			    if (strlen (given_bootargs) < 255 - (r - q)) {
			        *r++ = ' ';
			        strcpy (r, given_bootargs);
			    }
		        }
                    } else if (strlen (given_bootargs) <= 255)
                        strcpy (q, given_bootargs);
                }
            }
            dig_into_params (params);
            if (initrd_string) {
            	char *q, *r, *initrd_device, *initrd_kname, *initrd_limit, *initrd_cur, c;
		char *string;
            	int initrd_partno, len, statusok = 0;
            	
            	q = strchr (initrd_string, '|');
            	if (q && !initrd_size) {
            	    fatal ("When more than one initial ramdisk piece is specified, you have to give\n"
            	           "a non-zero initrd-size option which is no shorter than sum of all pieces\n"
            	           "lengths. Try again...\n");
            	    halt (promvec);
            	}
            	if (q) {
    		    initrd_start = memory_find ((initrd_size + 16383) & ~16383);
    		    if (!initrd_start) {
        		fatal ("You do not have enough continuous available memory for such initial ramdisk.");
        		halt (promvec);
    		    }
    		    string = strdup (initrd_string);
    		    q = strchr (string, '|');
    		    r = string;
    		    initrd_cur = initrd_start;
    		    initrd_limit = initrd_start + ((initrd_size + 16383) & ~16383);
    		    printf ("Loading parts of initial ramdisk...\n");
    		    for (;;) {
    		    	c = *q;
    		    	*q = 0;
			parse_name (r, initrd_defpart, &initrd_device, &initrd_partno, &initrd_kname);
			if (!initrd_kname) break;
			if (!initrd_device) initrd_device = initrd_defdevice;
			if (!load_file (initrd_device, initrd_partno, initrd_kname, initrd_cur, initrd_limit, &len, 0, 0)) break;
			if (!c) {
			    statusok = 1;
			    break;
			}
			initrd_cur += len;
			r = q + 1;
			q = strchr (r, '|');
			if (!q) q = strchr (r, 0);
			if (initrd_prompt) {
			    close ();
			    printf ("Insert next media and press ENTER");
			    dpin ();
			    printf ("\n");
			}
    		    }
    		    free (string);
    		    if (statusok) {
	            	*(unsigned int *)(p + 16) = ((unsigned int)initrd_start | 0xf0000000);
	            	*(unsigned int *)(p + 20) = initrd_size;
    		    } else
    		        printf ("Error: initial ramdisk loading failed. No initrd will be used.\n");
            	} else {
		    parse_name (initrd_string, initrd_defpart, &initrd_device, &initrd_partno, &initrd_kname);
		    if (initrd_kname) {
		        if (!initrd_device) initrd_device = initrd_defdevice;
	        	if (load_file (initrd_device, initrd_partno, initrd_kname, (unsigned char *) 0x300000, (unsigned char *) 0x380000, 0, 0, initrd_lenfunc)) {
	            	    *(unsigned int *)(p + 16) = ((unsigned int)initrd_start | 0xf0000000);
	            	    *(unsigned int *)(p + 20) = initrd_size;
	        	}
		    }
		}
	    }
        }
    } else {
        char *p;
        memcpy ((unsigned char *) 0x4000, ((unsigned char *) 0x4000) + off, len);
        p = seed_part_into_device (other_device, other_part);
        strcpy (other_device, p);
        params_device = other_device;
    }
    close ();
    if (timer_status >= 1)
        close_timer (promvec);
    set_bootargs (promvec, params, params_device);
    if (show_arguments) {
    	show_bootargs (promvec);
    	pause_after = 1;
    }
    if (pause_after) {
        printf ("%s", pause_message);
        dpin ();
        printf ("\n");
    }
    return ret_offset;
}