/*
 *	mkboot [-addr] file dev
 *
 * Copy <file> as a bootstrappable program onto device <dev> at
 * block address <addr> (default 9600).
 * Set up sector 0 of the device to look like an OS/MT volume
 * descriptor for the LSU.
 *
 * Make a fake OS/MT directory and LIB prefix for the 3200-series LSU.
 */

#include <sys/param.h>
#include <sys/filsys.h>


/*
 * OS/32 MT Structure definitions
 */

struct os_lib {		/* OS/32 Loader Information Block */
	char	l_segt,		/* segment type */
		l_nlib,		/* number of LIBs */
		l_nlu,		/* max number of logical units */
		l_nasn,		/* max number of assigns */
		l_mpri,		/* max priority */
		l_ipri,		/* initial priority */
		l_preg,		/* pure segmentation register */
		l_ncom,		/* number of task common segs */
		l_nlsg,		/* number of library segs */
		l_tatr;		/* task common attributes */
	short	l_opt;		/* task options */
	int	l_segs,		/* size of impure (sectors) */
		l_fw2,		/* num entries ?? */
		l_fw3,		/* max system space ?? */
		l_itsw[2],	/* initial TSW */
		l_psiz,		/* size of pure (sectors) */
		l_prn,		/* sector offset of pure */
		l_segn;		/* last word of segment name ?? */
	char	l_dumn[16];	/* dump file or dummy name ?? */
	int	l_his,		/* patch history pointer */
		l_fill0[4];	/*	-	*/
	char	l_date[8],	/* TET date */
		l_time[8];	/* TET time */
	short	l_nioq,		/* max queued I/O requests */
		l_fill1;	/*	-	*/
	int	l_ctop,		/* last halfword in partition */
		l_utop;		/* impure size (bytes) */
};

struct os_dir {	/* OS/32 Directory Entry */
	char	d_fnm[8],	/* file name */
		d_ext[3],	/* extension */
		d_act;		/* account */
	int	d_flba,		/* first sector address */
		d_llba;		/* last sector address */
	short	d_keys,		/* write/read keys */
		d_lrcl;		/* logical record length */
	int	d_date,		/* date allocated */
		d_luse;		/* date last assigned */
	short	d_wcnt,		/* write count */
		d_rcnt;		/* read count */
	char	d_atrb,		/* attributes */
		d_bksz,		/* block size */
		d_inbs,		/* index block size */
		d_mefx;		/* mef number of extents ?? */
	int	d_csec,		/* current sector or number of records */
		d_pswd;		/* password ?? */
};

struct os_dirblk {		/* OS/32 Directory Block */
	int		db_next;	/* sector address of next dirblk */
	struct os_dir	db_dir[5];	/* directory entries */
};

struct os_vd {			/* OS/32 Volume Descriptor */
	char	v_vol[4];	/* volume name */
	int	v_atrb,		/* attributes ?? */
		v_fdp,		/* sector address of first directory block */
		v_osp,		/* sector address of OS image */
		v_oss,		/* size of OS image (sectors) */
		v_map,		/* sector address of bit map */
		v_ila,		/* inverted list address ?? */
		v_sdp;		/* secondary directory pointer */
};

struct filsys		fs;
struct os_lib		lib;
struct os_dirblk	dirblk;
#define dir		dirblk.db_dir[0]
struct os_vd		vd;

char	buff[256];	/* sector copy buffer */
long	saddr = 9600*2;	/* sector address for bootstrap stuff */
int	nsec;		/* length (sectors) of bootstrap program */

main(argc, argv)
char **argv;
{
	register dev, file, len;
	register char *p;

	if (--argc && *(p = *++argv) == '-') {
		if ((saddr = 2*atoi(++p)) <= 0)
			error("Illegal block number");
		argc--;
		argv++;
	}
	if (argc < 2)
		error("Usage: mkboot [ - baddr ] file dev");

	if ((file = open(*argv, 0)) < 0)
		error("Can't open %s", *argv);
	if ((dev = open(*++argv, 2)) < 0)
		error("Can't write on %s", *argv);

	/*
	 * Make sure we aren't overwriting file system
	 */
	lseek(dev, 512L, 0);
	if (read(dev, &fs, sizeof(fs)) != sizeof(fs))
		error("Superblock read error");
	if (saddr < 2*fs.s_fsize)
		error("Are you trying to clobber the file system??");

	/*
	 * Skip a.out header
	 */
	lseek(file, 32L, 0);
	lseek(dev, (saddr+2) * 256, 0);

	/*
	 * Copy file & count blocks copied
	 */
	while ((len = read(file, buff, 256)) > 0) {
		if (write(dev, buff, len) != len)
			error("Write error");
		nsec++;
	}
	if (len < 0)
		error("Read error");

	/*
	 * Round up size to ensure that it's bigger than the LSU program
	 * (to avoid P-E LSU relocation bug)
	 */
	if (nsec < 32)
		nsec = 32;

	/*
	 * Set up a pseudo OS/MT volume descriptor for the LSU
	 */
	strncpy(vd.v_vol, "UNIX", 4);
	vd.v_fdp = saddr;
	vd.v_osp = saddr + 2;
	vd.v_oss = nsec;

	lseek(dev, 0L, 0);
	if (wrsec(dev, &vd, sizeof vd))
		error("Volume label write error");

	/*
	 * Make directory block
	 */
	strncpy(dir.d_fnm, "BOOT    ", 8);
	strncpy(dir.d_ext, "   ", 3);
	dir.d_flba = saddr + 1;
	dir.d_llba = saddr + 1 + nsec + 1;
	dir.d_atrb = 0x13;
	dir.d_csec = nsec + 2;

	lseek(dev, saddr * 256, 0);
	if (wrsec(dev, &dirblk, sizeof dirblk))
		error("Directory block write error");

	/*
	 * Make loader information block
	 */
	lib.l_segt = 1;
	lib.l_nlib = 1;
	lib.l_nlu = 15;
	lib.l_mpri = lib.l_ipri = 128;
	lib.l_segs = nsec;
	lib.l_fw3 = 0xfffff;
	lib.l_itsw[1] = 0x60;
	lib.l_ctop = (nsec + 1) * 256 - 2;
	lib.l_utop = nsec * 256;

	lseek(dev, (saddr+1) * 256, 0);
	if (wrsec(dev, &lib, sizeof lib))
		error("Loader information block write error");
}

/*
 * Write len bytes from addr to fd, null-padding to 256-byte sector
 */
wrsec(fd, addr, len)
char *addr;
{
	register n;
	static char zeroes[256];

	if (write(fd, addr, len) != len)
		return(1);
	if ((n = 256-len) > 0)
		if (write(fd, zeroes, n) != n)
			return(1);
	return(0);
}

error(s, x)
{
	printf(s, x);
	printf("\n");
	exit(1);
}
