
/*
  NAME:   System Startup - Level 1 Bootloader
  
  AUTHOR: Tom Perrine
  VERS:   $Header: lev1boot.c,v 1.16 87/05/29 14:37:40 Perrine Exp $
  PRIV:   none (runs on bare machine)
  
  
  This program is written in C to keep it small and efficient, and to
  facilitate its use of the processor registers.

  It is loaded by the level 0 bootloader.  Its function is to read in
  the desired kernel image and initial process image, and transfer
  control to the kernel (which then transfers control to the initial
  process).
  
  The type of the boot disk is specified in the arguments, passed in
  registers from the level 0 boot. The difference is in get_abs_block
  which determines which disk is being accessed.
  
  A special C runtime startup file is used (see the make file).
  */


#include <a.out.h>
#include "ktypes.h"
#include "filesys.h"


#define KILL		025	/* line kill char - ^U	*/
#define ERASE		010	/* char erase, backspace - ^H */
#define REDRAW		022	/* redisplay line - ^R */
#define NL		012	/* newline, linefeed */

#define ABORT           2

#define KER_LOC         0x0200           /* kernel starts at location 0x200 physical to leave room for the RPB */
#define INIT_LOC        0x200000         /* starting location for the initial
					  * process = 2 Mbytes		    */

#define	V1		0x1	/* version 1 - this version passes 6 arguments
				including the VFLAGS and the physical location
				of the initial process */
#define V2		0x2	/* version 2 - added a seventh arg - the size
				(in bytes) of the Initial Process */

#define	VFLAGS		V1|V2
	/* this must be the OR of the preceeding Version flags */


#define PGSIZE 512		/* VAX page size */


char l1what[]   = "$Header: lev1boot.c,v 1.16 87/05/29 14:37:40 Perrine Exp $";

	/* drive register offsets */
#define	REGBASE 0x400		/* offset of drive 0 reg from mbabase */
#define ROFFSET 0x80		/* offset between register sets */

unsigned int	DISKADDR;	/* used also by getblock */
unsigned int	MBABASE;	/* used also by getblock*/


struct bootstruct
{
  char	sUNIT[2];		/* disk unit number */
  char	sEXT[2];		/* extent number */
  char	sKERPATH[32];		/* kernel path */
  char	sINITPATH[32];		/* initial process path */
};

	/* this struct holds things that will be passed to the Kernel
	   (via the stack)
	*/
struct		/* ALL addresses are PHYSICAL! */
{
  unsigned int kbtext;		/* beginning of Kernel text */
  unsigned int ketext;		/* end of Kernel text (data follows) */
  unsigned int kend;		/* end of Kernel */
  unsigned int IPsz;		/* size of the initial process (in bytes) */
} argstruct;



int	UNIT;				/* disk unit number */
int	EXT	=	5;		/* (default) extent number */
char	KERPATH[32];	/* kernel path */
char	INITPATH[32];	/* initial process path */


/*
 *	The default pathname of the file in which to find the above
 *	structured data, the 'default boot data file'.
 */
char	BOOTFILE[32]	=	"/boot.default";


extern cardinal32 SCB[];	/* defined in scb.c */


extern char *strcpy();
extern char *end;



main()
{
  register int driveno, mba_base, RPBaddr;	/* from level 0 boot */
  		/* r11, r10, r9 */

  struct filep            kfp, ifp, bfp;
  long			kernelentry;

#ifdef lint
  driveno = 0;			/* keep lint happy */
  mba_base = 0;
  RPBaddr = 0;
  
#endif


  printf("\n\nKSOS-32 LEVEL 1 BOOTLOADER $Revision: 1.16 $\n\n");
#ifdef DEBUG
  printf("driveno= %X, mba_base= %X, RPBaddr= 0x%X\n",driveno,mba_base,RPBaddr);
#endif
  UNIT=driveno;
  MBABASE = mba_base;
  
  printf("Defaults: unit = %d  extent = %d  boot file = %s\n", UNIT,EXT,BOOTFILE);
  
  /* determine address of drive registers from unit and MBA base */
  DISKADDR = mba_base + REGBASE + (ROFFSET * UNIT);

#ifdef DEBUG
  printf("DISKADDR = %X   _end = %X\n",DISKADDR, &end);
#endif

  /* Build the System Control Block */
  initSCB();
  
  if (find("boot file: ", UNIT, EXT, BOOTFILE, &bfp) < 0)
    /* if can't find boot control file, try a manual boot */
    for (;;)
      {
	if (find("\nkernel: ", UNIT, EXT, KERPATH, &kfp) < 0)
	  continue;
	
	if ((kernelentry=load_image(&kfp, KER_LOC)) < 0)
	  {
	    printf("Can't load kernel\n");
	    continue;
	  }
	
	if (find("initial process: ", UNIT, EXT, INITPATH, &ifp) < 0)
	  continue;
	
	if (load_image(&ifp, INIT_LOC) < 0)
	  {
	    printf("Can't load initial process\n");
	    continue;
	  }
	
	break;	/* if loaded everything, get out of the loop */
	
      }	/* of manual boot */
  else	/* autoboot */
    {
      readbootfile(&bfp);
      if (openit(UNIT,EXT,KERPATH,&kfp) < 0)
	abort("Can't find kernel.\n");
      

      if ((kernelentry=load_image(&kfp, KER_LOC)) < 0)
	abort("Can't load kernel.\n");


      if (openit(UNIT,EXT,INITPATH,&ifp) < 0)
	abort("Can' find initial process.\n");


      if (load_image(&ifp, INIT_LOC) < 0)
	abort("Can't load initial process\n");

    }

  printf("\n");
  /* exit to kernel (see kb1crt0) */
  (*((int (*)()) kernelentry))(VFLAGS,
	    argstruct.kbtext,argstruct.ketext,argstruct.kend,
	    RPBaddr,INIT_LOC,argstruct.IPsz);
  
}


find(prompt, dunit, dext, dname, fp)
	char            *prompt, *dname;
	int             dunit, dext;
	struct filep    *fp;
{
	char            name[64], numb[8];

	printf(prompt);
	get_string(name, sizeof name);

	if (name[0] == 0)
		(void) strcpy(name, dname);
	else
	{
		printf("  extent: ");
		get_string(numb, sizeof numb);

		if (numb[0])
		{
			dext = atoi(numb);

			printf("  unit: ");
			get_string(numb, sizeof numb);

			if (numb[0])
				dunit = atoi(numb);
		}
	}

#ifdef DEBUG
printf("DEBUG:find:dunit %d, dext %d,name <%s>\n",dunit,dext,name);
#endif
	return (openit(dunit,dext,name,fp));

	

}


load_image(fp, start)
	struct filep            *fp;
	unsigned int            start;  /* PHYSICAL starting address */
{
  char *addr;
  register int i;
  struct exec header;

	int                     magic;

  resetread();			/* this reset all read static info -
				 see read.c for details and important limitations!!! */

  if (read(fp,(char *)&header,(sizeof(struct exec)))!= (sizeof(struct exec)))
    {
      printf("Can't read first block of file\n");
      return (-1);
    }

/* kernel must be 410, initial process 410?????? */

/* use this code if they are to be different...
  magic = (start == KER_LOC) ? 0410 : 0410;
*/
  magic = 0410;

  if (header.a_magic != magic)
    {
	printf("Bad magic word: 0%o\n",header.a_magic);
	return (-1);
    }

  printf("Loading the ");
  if (start == KER_LOC)
     printf("Kernel");
  else
      printf("Initial Process");

  printf(" at 0x%X",start);

  printf(": %d+%d+%d\n",header.a_text,header.a_data,header.a_bss);


  if (read(fp,(char*)start,header.a_text) != header.a_text)
    goto short_read;

  addr = (char *)header.a_text + start;
  if (magic == 0410)
    while ((int)addr & 0x3ff)	/* clear and round to next 1K boundary */
      *addr++ = 0;

  if (start == KER_LOC)		/* if loading the kernel... */
    {				/* save some useful PHYSICAL addresses */
      argstruct.kbtext = start;
      argstruct.ketext = (unsigned int)addr;
    }

#ifdef DEBUG
  printf("loading data at %X\n",addr);
#endif

  if (read(fp,addr,header.a_data) != header.a_data)
    goto short_read;

  addr += header.a_data;

#ifdef DEBUG
printf("cleared from 0x%X to",addr);
#endif

  for (i=0; i< header.a_bss;i++) /* clear bss */
     *addr++ = 0;
  while ((int)addr & 0x1ff)	/* clear and round to next page */
    *addr++ = 0;

#ifdef DEBUG
printf(" 0x%X\n",addr);
#endif

#ifdef DEBUG
  printf("entry virtual address: %X",header.a_entry);
#endif
  header.a_entry &= 0x7fffffff;	/* get rid of SY0 mapping */
  header.a_entry += start;	/* account for load place */
#ifdef DEBUG
  printf(", physical: %X\n",header.a_entry);
#endif

  if (start == KER_LOC)		/* if loading the kernel... */
    {				/* save some useful PHYSICAL addresses */
      argstruct.kend   = (unsigned int)addr;
    }
  else
    {
      argstruct.IPsz = (unsigned int)addr - INIT_LOC;
    }
  

  return((cardinal32)header.a_entry);

short_read:
  printf("Short Read: Fatal Error - exiting!\n");
	return(-1);
}



get_string(str, len)
	register char           *str;
	register int            len;
{
	register int            i;
	char			*savep;		/* save start spot */
	int			savel;		/* save start len */

	/*
	 * Read characters until NL or str is full.
	 */

	savel = len;
	savep = str;

	for (i = 0, len--; i < len; i++, str++)
	{
		*str = (char) getchar();

		if (*str == KILL)
			{
			i = 0;
			len = savel;
			str = savep;
			printf("^U");
			continue;
			}
		else if (*str == ERASE)
			{
			  putchar('/');
			  
			putchar(*(str-1));
			putchar('/');
			i--;
			i--;
			str--;
			str--;
			continue;
			}
		else if (*str == REDRAW)
		  {
		    *str = '\0';
		    printf("\n%s",savep);
		    str--;
		    i--;
		  }
		
		else  if (*str == NL)
			{
			*str = '\0';
			return;
			}
	}

	*str = '\0';
	return;
}


abort(msg)
	char                    *msg;
{
	printf(msg);

	exit(ABORT);
}


readbootfile(bfp)
	struct filep	*bfp;
{
	int		i;

	char my_buffer[BLKSZ];
	char *BUFFER = &my_buffer[0];


	printf("Booting from: ");
	if (get_file_block(bfp,0L,BUFFER) < 0)
	{
		printf("Can't read first block of file.\n");
		return (-1);
	}

	((struct bootstruct *)BUFFER)->sUNIT[1] = '\0';	/* pad for atoi */
	UNIT	= atoi(((struct bootstruct *)BUFFER) -> sUNIT);

	((struct bootstruct *)BUFFER)->sEXT[1] = '\0';		/* pad for atoi */
	EXT	= atoi(((struct bootstruct *)BUFFER) -> sEXT);

	printf("UNIT=%d ",UNIT);
	printf("EXT=%d ",EXT);
	/* convert trailing blanks into nulls */
	for (i=sizeof KERPATH -1; ( i >= 0 && ((struct bootstruct *)BUFFER)->sKERPATH[i] == ' '); i--)
		((struct bootstruct *)BUFFER)-> sKERPATH[i] = '\0';
	for (i=sizeof INITPATH -1;(i >= 0 && ((struct bootstruct *)BUFFER)->sINITPATH[i] == ' '); i--)
		((struct bootstruct *)BUFFER)-> sINITPATH[i] = '\0';

	(void) strcpy(KERPATH, ((struct bootstruct *)BUFFER)-> sKERPATH);
	(void) strcpy(INITPATH, ((struct bootstruct *)BUFFER)-> sINITPATH);
	printf("Kernel:<%s> ",KERPATH);
	printf("Initial process:<%s>\n",INITPATH);
	return(0);
}



openit(unit,ext,name,fp)
	int		unit,	/* disk unit */
			ext;	/* extent */
	char		*name;	/* file name */
	struct filep	*fp;	/* file pointer */
{
	struct extp	ex;	/* extent pointer */
	if (extopen(unit, ext, &ex) < 0)
	{
		printf("Can't open unit %d extent %d\n", unit, ext);
		return (-1);
	}

	if (fileopen(&ex, name, fp) < 0)
	{
		printf("Can't open %s\n", name);
		return (-1);
	}

	return(0);
}


exit(foo)
     int foo;
{

  printf("exit(%d)\n",foo);

  for (;;)
      ;				/* spin */
  
}


dummyHandler(arg1,arg2,arg3)
     int arg1,arg2,arg3;
{
  printf("dummyHandler: %d:%d:%d\n");
}
