/*	boot.c	6.1	83/07/29	*/

#include	"../s32/setjmp.h"
#include	"../h/param.h"
#include	"../h/inode.h"
#include	"../h/fs.h"
#include	"../h/vm.h"
#include	<a.out.h>
#include	"../salib/saio.h"
#include	"../h/reboot.h"
#include	"../s32/cpu.h"

#define	PROMNAMELEN	4

extern int *nofault;

/*
 * Boot program... arguments passed in determine
 * whether boot stops to ask for system name and which device
 * boot comes from.
 */

char	devname[][2] = {
	'r','d',	/* 0 = rd */
	'i','d',	/* 1 = is */
};

char line[100] = "xx(0,0)vmunix";
char def_boot[] = "d(0,6)vmunix";

extern short autoboot_area;
extern char bootline_area[];
extern u_long chipType;
char sys_debug = 0;	/* Enable system-wide debugging statements. */

int	retry = 0;
unsigned	sum = 0;		/* kernel checksum */
char	defaultstring[PROMNAMELEN+1];	/* store the "boot-from" string here */
jmp_buf	restart;			/* Handy rabbithole to pop out of */

main(romval, defaultfile)
	char *defaultfile;
{
	register howto, devtype;	/* this is very important.
		do not procede unless you fully understand this comment:
		howto=r11, devtype=r10 */
	register char *ptr1, *ptr2;
	int io;
	extern char version[];

	romval &= 0xff; /* not sure about crm yet */

#ifdef lint
	howto = 0; devtype = 0;
#endif
	printf("%s", version);
	/*
	 * Check that the format of the string the monitor 
	 * passed us is legit.  VRM, GRM and SRM may waffle; CRM
	 * may screw up, too, in various circumstances.
	 */
	if (checkformat(defaultfile) < 0)
		/* 
		 * "defaultstring" is where the string is stored 
		 * after validity checking.
		 */
		defaultstring[0] = '\0';
	else
		defaultstring[PROMNAMELEN] = '\0'; /* null-terminate */

	if (setjmp(restart))
	{
		/*
		 * We will wind up here in case of unexpected exceptions...
		 *
		 * We don't want to autoboot after 
		 * weirdness dumps us here.
		 */
		romval = 0;
		printf("\n(boot code restarting)\n");
	}

	printf("%sboot from %s\n",
		romval == 2?"auto":"", 
		(*defaultstring) ? defaultstring : "");
#ifdef JUSTASK
	howto = RB_ASKNAME|RB_SINGLE;
#else
	devtype = 0;		/* For now only boot from Rimfire */
	if (romval == 2)	/* Autoboot */
		howto = 0;
	else			/* Boot to single user */
		howto = RB_ASKNAME;
	if ((howto&RB_ASKNAME)==0) {	/* Autoboot */
		if (devtype>=0 && devtype<sizeof(devname)/2	/* Check */
		    && devname[devtype][0]) {			/* devtype */
		/* 
		 * Convert prom syntax to unix syntax .
		 *
		 * If the machine is stupid enough to have grody old VRM
		 * and it passes us a garbage defaultfile, force the 
		 * defaultfile to be DEFBOOT.
		 */
		convert(line, defaultstring);
		} else
			howto = RB_SINGLE|RB_ASKNAME;	/* Force manual boot */
	}
#endif
	for (;;) {
		if (howto & RB_ASKNAME) {		/* If manual boot */
			printf("M%x: ", chipType);	/* Print prompt */
			gets(line);			/* Get response */
		} else
			printf("M%x: %s\n", chipType, line);	/* Autoboot */
	if (line[0] == '\0')	/* Hit return default to vmunix */
	 {
		/* convert prom syntax to unix syntax */
		convert(line, defaultstring);
		printf("M%x: %s\n", chipType, line);	/* Print message */
	 }
	else if (line[0] == '.')	/* If '.' add tail to default*/
	 {
		char templine[64];

		strcpy(templine, line);
		/* convert prom syntax to unix syntax */
		convert(line, defaultstring);
		strcat(line, templine);
		printf("M%x: %s\n", chipType, line);	/* Print message */
	 }
	else if (line[0] == 'j' && line[1] == '\0')
	 {
		/*
		 * Undocumented trap door to Hell 
		 * for those who don't brush their teeth...
		 */
		asm ("jmp	0x20000");
	 }
	else if (line[0] != '\0' && line[1] == '\0')
	 {
		strcpy (&(line[1]), def_boot);		/* Copy boot line */
		printf("M%x: %s\n", chipType, line);	/* Print message */
	 }
	 else
		printf("\n");
/*
 *	Special code hack to still support the name 'rim'
 */
		if (line[0] == 'r' && line [1] == 'i' && line [2] == 'm') {
			for (line[1]='d',ptr1= &line[2],ptr2= &line[3]; *ptr2;) 
				*ptr1++ = *ptr2++;
			*ptr1 = 0;
		}
		/*
		** Now that line is cool, we save it in bootline_area
		** for our friend vmunix:
		*/
		strncpy(bootline_area, line, 79);
		bootline_area[79] = '\0';
		if (sys_debug)
			printf("We will tell unix we came from %s!\n",
				bootline_area);

		io = open(line, 0);	/* Open and initialize the device */
		if (io >= 0)		/* Open succeeded, device ready */
			copyunix(howto, io);	/* Load vmunix */
		if (++retry > 2)	/* Increment retry on autoboot error */
			howto = RB_SINGLE|RB_ASKNAME;	/* Force manual boot */
	}
}

long
convert(uname, pname) /* convert prom syntax to unix syntax */
	char *uname, *pname;
{
	register long pnameval = (long)pname;
	register char *un = uname;
	register char *pn = pname;
	long ctlr, minornumber, tempnumber, diskspercont, unit;

	/*
	 * Because of validity check "checkformat", we no longer need to 
	 * check the origin of the string we are converting.  So, the check:
	 *	(pnameval < 0x18000) || (pnameval > 0x18800)
	 * is no longer necessary.  It's ugly anyways.
	 * 
	 * Earlier, we truncated the string if checkformat didn't like it.
	 */
	if (! *pname)  
	{
		/* 
		 * Address of string is out of range,
		 * or string is otherwise bogus (like not composed of 
		 * printing characters). "Checkformat" said so when we 
		 * first started the boot code.
		 */
		printf("OLD STYLE BOOT PROMS\n", pnameval);
		strcpy(uname, "rd(0,6)vmunix"); /* ICCH */
		return 1;
	}

	*un++ = *pn++ + 'a' - 'A'; /* convert to lower case */
	*un++ = *pn++ + 'a' - 'A'; /* convert to lower case */
	*un++ = '(';
	ctlr = *pn++ - '0';
	unit = *pn++ - '0';

	tempnumber = minornumber = 16 * ctlr + unit;
	if (minornumber > 99) {
		*un++ = '0' + (tempnumber / 100);
		tempnumber %= 100;
	}
	if (minornumber > 9) {
		*un++ = '0' + (tempnumber / 10);
		tempnumber %= 10;
	}
	*un++ = '0' + tempnumber;
	strcpy(un, ",6)vmunix");

	return 0;
}

/*
 * Check that the given string is made up of real live ASCII characters.
 */
checkformat(promstring)
char *promstring;
{
	register int i;
	register char c;

	nofault = restart;

	if (!setjmp(restart))
	{
		for (i=0; i<PROMNAMELEN; promstring++, i++)
		{
			c = *promstring;
			/*
			 * 32 is ascii for ' ';
			 * 126 is ascii for highest printable character ('~').
			 */
			if ((c < 32) || (c > 126))
				return (-1);
			else
				defaultstring[i] = c;
		}
	} 
	else
	{
#ifdef DEBUG
		printf("Bus error examining boot string.\n");
#endif DEBUG
		return(0);
	}
}

unsigned
checksum(prevsum, addr, len)
	unsigned register prevsum;
	register char *addr;
	register int len;
{

	while (len--) {
		if (prevsum&01)
			prevsum = (prevsum>>1) + 0x8000;
		else
			prevsum >>= 1;
		prevsum += *addr++;
		prevsum &= 0xFFFF;
	}

	return(prevsum);
}



/*ARGSUSED*/
copyunix(howto, io)
register howto, io;
{
	struct exec x;
	register int i;
	char *addr;
#ifdef DoSum
	char *firstaddr;
#endif DoSum

	i = read(io, (char *)&x, sizeof x);	/* Read the exec header */

	if (i != sizeof x ||	/* Check for read error && valid magic # */
	    (x.a_magic != 0407 && x.a_magic != 0413 && x.a_magic != 0410))
		_stop("Bad format\n");
	if (x.a_magic == 0413 && lseek(io, 0x400, 0) == -1) /* Align for */
		goto shread;		/* start of text */
	addr = (char *)0x20000;		/* Load starting at 128K */
	printf("%d bytes text ", x.a_text);	/* Print text size */
	if (read(io, addr, x.a_text) != x.a_text)	/* Read the text */
		goto shread;
#ifdef DoSum
	firstaddr = addr;
#endif DoSum

	addr += x.a_text;	/* Increment the address */
	if (x.a_magic == 0413 || x.a_magic == 0410)	/* Pad it out */
		while ((int)addr & CLOFSET)
			*addr++ = 0;
	printf("+ %d bytes data ", x.a_data);	/* Print data size */
	if (read(io, addr, x.a_data) != x.a_data)	/* Read the data */
		goto shread;
#ifdef DoSum
	sum = checksum(0, (char *)&x, sizeof x);
	sum = checksum(sum, firstaddr, x.a_text);
	sum = checksum(sum, addr, x.a_data);
#endif DoSum
	
	addr += x.a_data;	/* Increment the address */
	printf("+ %d bytes bss", x.a_bss);	/* Print bss size */
#ifdef CLR_BSS		/* Clear bss if kernel doesn't, which it does */
	x.a_bss += 128*512;	/* slop */
	for (i = 0; i < x.a_bss; i++)
		*addr++ = 0;
	printf(" cleared");
#endif
	x.a_entry &= 0x7fffffff;	/* Mask the entry point. WHY ?? */
	printf(", start 0x%x\n", x.a_entry);	/* Print the entry point */

#ifdef DoSum
	printf("checksum %u\n", sum);
#endif DoSum

	if (howto == 0)		/* Autoboot */
		strcpy (&autoboot_area, "a");	/* So that the parameter is not
						   scrogged by the kernel */
	else			/* Manual boot */
		strcpy (&autoboot_area, "");

	/* Jump start: */
	(*((int (*)()) x.a_entry))(&autoboot_area, bootline_area);
	_stop("Danger, Will Robinson\n"); /* Should never get here, ha ha */
shread:
	_stop("Short read\n");	/* File system problem */
}
