/*
 *	SCCS id	@(#)boot.c	2.1 (Berkeley)	8/5/83
 */

/*
 * Patch to boot from ra(0,0)unix instead of xp(0,0)unix
 *
 * Peter Klapper, 08.10.2021 
 */

#include	<sys/param.h>
#include	<sys/seg.h>
#include	<sys/ino.h>
#include	<sys/inode.h>
#include	<sys/filsys.h>
#include	<sys/dir.h>
#include	<sys/reboot.h>
#include	<sys/koverlay.h>
#include	<a.out.h>
#include	"../saio.h"

#ifdef RB_DEFNAME
#undef RB_DEFNAME
#define RB_DEFNAME	"ra(0,0)unix"
#endif

#undef	btoc
#define	KB	* 1024L

#define	KISD0	((u_short *) 0172300)
#define	KISD3	((u_short *) 0172306)
#define	KDSD0	((u_short *) 0172320)
#undef	KISA0
#define	KISA0	((u_short *) 0172340)
#define	KISA3	((u_short *) 0172346)
#define	KDSA0	((u_short *) 0172360)

#define	SEG_DATA	01
#define	SEG_TEXT	02
#define	SEG_OVLY	04

extern	int	cputype;
extern	bool_t	ksep;			/* Is kernel mode currently separated */
extern	bool_t	sep_id;			/* Does the cpu support separate I/D? */
extern	int	bootopts, bootdev, checkword;
char	module[]	= "Boot";	/* This program's name (used by trap) */
char	line[100]	= RB_DEFNAME;
bool_t	overlaid	= 0;
u_short	pdrproto[16 + NOVL]	= {0};
struct	exec	exec;
struct	ovlhdr	ovlhdr;
unsigned	btoc();

struct	loadmap	{
	int	seg_type;
	long	seg_len;
};
struct	loadtable	{
	short	lt_magic;
	struct	loadmap	*lt_map;
};

struct	loadmap	load407[]	=	{
	SEG_DATA,	56 KB,
	0,		0  KB
	};
struct	loadmap	load410[]	=	{
	SEG_TEXT,	48 KB,
	SEG_DATA,	56 KB,
	0,		0  KB
	};
struct	loadmap	load411[]	=	{
	SEG_DATA,	56 KB,
	SEG_TEXT,	64 KB,
	0,		0  KB
	};
struct	loadmap	load430[]	=	{
	SEG_TEXT,	16 KB,			/* minumum, 8 KB + 1 */
	SEG_OVLY,	8  KB,
	SEG_DATA,	24 KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	0,		0  KB
	};
struct	loadmap	load431[]	=	{
	SEG_DATA,	56 KB,			/* minumum, 48 KB + 1 */
	SEG_TEXT,	56 KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	SEG_OVLY,	8  KB,
	0,		0  KB
	};

struct	loadtable	loadtable[]	=	{
	A_MAGIC1,	load407,
	A_MAGIC2,	load410,
	A_MAGIC3,	load411,
	A_MAGIC5,	load430,
	A_MAGIC6,	load431
	};

main ()
{
	int i, j;
	int retry	= 0;
	struct	loadtable	*setup ();

	segflag	= 2;			/* device drivers care about this */

	printf ("\n%d%s\n", cputype, module);
#ifdef  UCB_AUTOBOOT
	/*
	 *	The machine language will have gotten the bootopts
	 *	if we're an autoboot and will pass them along.
	 *	If r2 (checkword) was the complement of bootopts,
	 *	this is an automatic reboot, otherwise do it the hard way.
	 */
	if (checkword != ~bootopts)
#endif
		bootopts	= RB_SINGLE | RB_ASKNAME;
	do	{
		if (bootopts & RB_ASKNAME)	{
			printf (": ");
			gets (line);
		}
		else
			printf (": %s\n", line);
		if (line[0] == '\0')	{
			printf (": %s\n", RB_DEFNAME);
			i	= open (RB_DEFNAME, 0);
		}
		else
			i	= open (line, 0);
		j = -1;
		if (i >= 0)	{
			j	= checkunix (i, setup (i));
			(void) close (i);
			}
		if (++retry > 2)
			bootopts	= RB_SINGLE | RB_ASKNAME;
	}	while (j < 0);

}

struct	loadtable	*
setup (io)
register io;
{
	register	i;

	exec.a_magic	= getw (io);
	exec.a_text	= (unsigned) getw (io);
	exec.a_data	= (unsigned) getw (io);
	exec.a_bss	= (unsigned) getw (io);

	/*
	 *	Space over the remainder of the exec header.  We do this
	 *	instead of seeking because the input might be a tape which
	 *	doesn't know how to seek.
	 */
	getw (io); getw (io); getw (io); getw (io);

	/*
	 *	If overlaid, get overlay header.
	 */
	if (exec.a_magic == A_MAGIC5 || exec.a_magic == A_MAGIC6)	{
		overlaid++;
		ovlhdr.max_ovl	= getw (io);
		for (i = 0; i < NOVL; i++)
			ovlhdr.ov_siz[i]	= (unsigned) getw (io);
		}
	
	for (i = 0; i < sizeof (loadtable) / sizeof (struct loadtable); i++)
		if (loadtable[i].lt_magic == exec.a_magic)
			return (&loadtable[i]);

	printf ("Bad magic number 0%o\n", exec.a_magic);
	return ((struct loadtable *) NULL);
}


checkunix (io, lt)
struct	loadtable	*lt;
{
	char	*segname;
	register	ovseg, segtype;
	register unsigned		seglen;
	struct	loadmap	*lm	= lt->lt_map;

	if (lt == (struct loadtable *) NULL)
		return (-1);

	/*
	 *	Check and set I & D space requirements.
	 */
	if (exec.a_magic == A_MAGIC3 || exec.a_magic == A_MAGIC6)
		if (!sep_id)	{
			printf ("Cannot load separate I & D object files\n");
			return (-1);
			}
		else
			setsep ();
	else
		if (sep_id)
			setnosep ();

	/*
	 *	Check the sizes of each segment.
	 */
	ovseg	= 0;
	while (segtype = lm->seg_type)	{
		switch (segtype)	{
			case SEG_TEXT:
				/*
				 *	Round text size to nearest page.
				 */
				if (exec.a_magic == A_MAGIC2)
					seglen	= ctob (stoc (ctos (btoc (exec.a_text))));
				else
					seglen	= exec.a_text;
				segname	= "Text";
				break;

			case SEG_DATA:
				seglen	= exec.a_data + exec.a_bss;
				segname	= "Data";
				if (exec.a_magic == A_MAGIC1)
					seglen	+= exec.a_text;
				else
					/*
					 *	Force a complaint if the file
					 *	won't fit.  It's here instead
					 *	of in the SEG_TEXT case above
					 *	because it's more likely to be
					 *	a data overflow problem.
					 */
					if (exec.a_magic == A_MAGIC2)
						seglen	+= ctob (stoc (ctos (btoc (exec.a_text))));
				break;

			case SEG_OVLY:
				seglen	= ovlhdr.ov_siz[ovseg];
				segname	= "Overlay";
				ovseg++;
				break;

			default:
				/*
				 *	This ``cannot happen.''
				 */
				printf ("Unknown segment type in load table:  %d\n", segtype);
				return (-1);
				/*NOTREACHED*/
			}

		seglen	= ctob (btoc (seglen));
		if (((long) seglen) > lm->seg_len)	{
			if (segtype == SEG_OVLY)
				printf ("%s %d too large by %D bytes", segname, ovseg, lm->seg_len - ((long) seglen));
			else
				printf ("%s too large by %D bytes", segname, lm->seg_len - ((long) seglen));
			return (-1);
			}
		if (segtype == SEG_TEXT)
		    switch (exec.a_magic) {
			case A_MAGIC5:
			    if (seglen <= 8 KB) {
				printf("Base segment too small, 8K minimum\n");
				return(-1);
			    }
			    break;
			case A_MAGIC6:
			    if (seglen <= 48 KB) {
				printf("Base segment too small, 48K minimum\n");
				return(-1);
			    }
			    break;
			default:
			    break;
		}

		lm++;
		}
	/*
	 * 431's (overlaid separate I/D) could be larger than
	 * 128 KB; relocate the bootstrap to 192K.
	 */
	if (exec.a_magic == A_MAGIC6)
		if (reloc() < 0) {
			printf("Not enough memory\n");
			return(-1);
		}

	copyunix (io, lt);
	setregs (lt);
	return (0);
}

copyunix (io, lt)
register	io;
struct	loadtable	*lt;
{
	int	i;
	bool_t	donedata = 0;
	register	addr;
	register unsigned	seglen;
	off_t	segoff;
	int	segtype;
	int	nseg, phys, ovseg;
	struct	loadmap	*lm	= lt->lt_map;

	/*
	 *	Load the segments and set up prototype PDRs.
	 */
	nseg	= 0;
	phys	= 0;
	ovseg	= 0;
	lm	= lt->lt_map;
	while (segtype = lm++->seg_type)	{

		segoff	= (off_t) N_TXTOFF(exec);

		switch (segtype)	{
			case SEG_TEXT:
				seglen	= exec.a_text;
				break;

			case SEG_DATA:
				seglen	= exec.a_data;
				if (exec.a_magic != A_MAGIC1)	{
					segoff	+= (off_t) exec.a_text;
					if (overlaid)
						for (i = 0; i < NOVL; i++)
							segoff	+= (off_t) ovlhdr.ov_siz[i];
					}
				else
					seglen	+= exec.a_text;
				donedata++;
				break;

			case SEG_OVLY:
				seglen	= ovlhdr.ov_siz[ovseg];
				segoff	+= exec.a_text;
				for (i = 0; i < ovseg; i++)
					segoff	+= (off_t) ovlhdr.ov_siz[i];
				ovseg++;
				break;

			}

		if (!seglen)
			continue;
		setseg (phys);
		if (exec.a_magic != A_MAGIC1)
			(void) lseek (io, segoff, 0);
		for (addr = 0; addr < seglen; addr += 2)
			mtpi (getw (io), addr);

		if (segtype == SEG_DATA)	{
			clrseg (addr, exec.a_bss);
			seglen	+= exec.a_bss;
			}

		pdrproto[nseg++]	= btoc (seglen);
		if (!donedata)
			seglen	= ctob (stoc (ctos (btoc (seglen))));
		phys	+= btoc (seglen);
		}
}

/*
 *	Set the real segmentation registers.
 */
setregs (lt)
struct	loadtable	*lt;
{
	register	i;
	register u_short	*par_base, *pdr_base;
	bool_t	donedata = 0;
	int	phys, segtype;
	int	nseg, ntextpgs, novlypgs, npages, pagelen;
	struct	loadmap	*lm	= lt->lt_map;

	nseg	= 0;
	phys	= 0;
	ntextpgs	= 0;
	novlypgs	= 0;

	setseg (0);
	if (exec.a_magic == A_MAGIC1)
		return;

	/*
	 *	First deny access to all except I/O page.
	 */
	par_base	= KISA0;
	pdr_base	= KISD0;
	for (i = 0; i < (ksep ?  8 : 7); i++)	{
		*par_base++	= 0;
		*pdr_base++	= NOACC;
		}
	if (ksep)	{
		par_base	= KDSA0;
		pdr_base	= KDSD0;
		for (i = 0; i < 7; i++)	{
			*par_base++	= 0;
			*pdr_base++	= NOACC;
			}
		}
	
	if (overlaid)	{
		/*
		 *	We must write the prototype overlay register table.
		 *	N.B.:  we assume that
		 *		the table lies in the first 8k of kernel virtual
		 *		space, and
		 *
		 *		the appropriate page lies at physical 0.
		 */
		if (ksep)
			*KDSD0	=	((128 -1) << 8) | RW;
		else
			*KISD0	=	((128 -1) << 8) | RW;
		par_base	= &(((u_short *) OVLY_TABLE_BASE)[0]);
		pdr_base	= &(((u_short *) OVLY_TABLE_BASE)[1 + NOVL]);
		for (i = 0; i < NOVL; i++)	{
			mtpd (0, par_base++);
			mtpd (NOACC, pdr_base++);
			}
		}

	/*
	 *	Now set all registers which should be nonzero.
	 */
	lm	= lt->lt_map;
	while (segtype = lm++->seg_type)	{
		if (!(npages = ctos (pdrproto[nseg])))
			continue;

		switch (segtype)	{
			case SEG_TEXT:
				/*
				 *	Text always starts at KI0;
				 */
				par_base	= KISA0;
				pdr_base	= KISD0;
				ntextpgs	+= npages;
				break;

			case SEG_DATA:
				if (overlaid)
					if (ksep)	{
						par_base	= I_DATA_PAR_BASE;
						pdr_base	= I_DATA_PDR_BASE;
						}
					else
						{
						par_base	= N_DATA_PAR_BASE;
						pdr_base	= N_DATA_PDR_BASE;
						}
				else
					if (ksep)	{
						par_base	= KDSA0;
						pdr_base	= KDSD0;
						}
					else
						{
						par_base	= &KISA0[ntextpgs + novlypgs];
						pdr_base	= &KISD0[ntextpgs + novlypgs];
						}
				donedata++;
				break;

			case SEG_OVLY:
				par_base	= &(((u_short *) OVLY_TABLE_BASE)[1 + novlypgs]);
				pdr_base	= &(((u_short *) OVLY_TABLE_BASE)[1 + NOVL + 1 + novlypgs]);
				novlypgs	+= npages;
				break;

			}

		for (i = 0; i < npages; i++)	{
			pagelen	= MIN (btoc ((int) (8 KB)), pdrproto[nseg]);
			if (segtype == SEG_OVLY)	{
				mtpd (phys, par_base);
				mtpd (((pagelen - 1) << 8) | RO, pdr_base);
				}
			else
				{
				*par_base	= phys;
				if (segtype == SEG_TEXT)
					if (ksep)
						*pdr_base	= ((pagelen - 1) << 8) | RO;
					else
						/*
						 *	Nonseparate kernels will
						 *	write into text page 0
						 *	when first booted.
						 */
						if (i == 0)
							*pdr_base	= ((pagelen - 1) << 8) | RW;
						else
							*pdr_base	= ((pagelen - 1) << 8) | RO;
				else
					*pdr_base	= ((pagelen - 1) << 8) | RW;
				}
			par_base++, pdr_base++;
			if (donedata)
				phys	+= pagelen;
			else
				phys	+= stoc (ctos (pagelen));
			pdrproto[nseg]	-= pagelen;
			}

		nseg++;
		}

	/*
	 *	Phys now contains the address of the start of
	 *	free memory.  We set K[ID]6 now or systrap to
	 *	kernel mode will clobber text at 0140000.
	 */
	if (ksep)	{
		KDSA0[6]	= phys;
		KDSD0[6]	= (stoc(1) - 1) << 8 | RW;
		}
	else
		{
		KISA0[6]	= phys;
		KISD0[6]	= (stoc(1) - 1) << 8 | RW;
		}

	if (overlaid)
		mtpd (phys, &(((u_short *) OVLY_TABLE_BASE)[1 + NOVL + 1 + NOVL]));
}

unsigned
btoc (nclicks)
unsigned nclicks;
{
	return ((unsigned) (((((long) nclicks) + ((long) 63)) >> 6)));
}
