/*
 * passes.c  -  Compress kernel image and concatenate it with a boot loader
 *
 * Copyright (C) 1997-2003 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  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
 *  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.
 *
 * $Id: passes.c,v 1.5 2003/03/09 00:43:08 gkminix Exp $
 */

#define NEED_TIME 1
#define NEED_BINARY 1
#include <common.h>
#include <nblib.h>
#include <memory.h>
#include "makerom.h"



/*
 * Definitions local to this module
 */
#define PNPHDROFS	ROMPNPPTROFS	/* Offset to ptr to PnP expansion hdr */
#define PNPEXPLENOFS	0x0005		/* Offset to length of expansion hdr */
#define PNPEXPCHKSUM	0x0009		/* Offset to PnP header checksum */
#define PNPDEVIDOFS	0x000A		/* Offset to PnP device ID */
#define PNPINDICATOR	0x0015		/* Offset to PnP device indicator */
#define PNPDDIM		0x80		/* Bitmask if DDIM is supported */
#define PNPSIG		"$PnP"		/* Signature of PnP expansion header */

#define PCIHDROFS	ROMPCIPTROFS	/* Offset to ptr to PCI structure */
#define PCIVENDIDOFS	0x0004		/* Offset to vendor ID */
#define PCIDEVIDOFS	0x0006		/* Offset to device ID */
#define PCIHDRLENOFS	0x000A		/* Offset to length of PCI structure */
#define PCIIMGLENOFS	0x0010		/* Offset to image length */
#define PCISIG		"PCIR"		/* Signature of PCI data structure */

#define ROMSIG		0xAA55		/* ROM signature */
#define ROMVECT18	0x18		/* ROM interrupt vector 18h */
#define ROMVECT19	0x19		/* ROM interrupt vector 19h */

#define KERNROMID	"$BC$"		/* Kernel ROM ID signature */
#define NETROMID	"UNDI"		/* Network driver ROM ID signature */
#define NBROMID		"netboot"	/* Netboot rom ID signature */

#define MINROMSIZE	8192				/* Minimum ROM size */
#define MAXROMSIZE	((FLOPEND - FLOPMEM) * 16L)	/* Maximum ROM size */
#define MAXOUTSIZE	((DECOMPEND - DECOMPMEM) * 16L)	/* Maximum kern size */



/*
 * Table of bus type strings
 */
static char *bustypestr[] = {
	"ISAR", "EISA", "MCAR", "PCIR", "VESA", "PCCR"
};



/*
 * Convert a hex character into a binary number
 */
static int gethex(c)
char c;
{
  if (isdigit(c))
	return(c - '0');
  else if (c >= 'a' && c <= 'f')
	return(c - 'a' + 10);
  else if (c >= 'A' && c <= 'F')
	return(c - 'A' + 10);
  prnerr0("invalid PnP ID string");
  exit(EXIT_MAKEROM_INVPNPID);
}



/*
 * Set a PnP device ID
 */
static void setpnpid(buf, cp)
__u8 *buf;
char *cp;
{
  int i;

  for (i = 0; i < 3; i++)
	if (!isalpha(cp[i]))
		break;
  if (i != 3 || strlen(cp) != PNPIDLEN) {
	prnerr1("invalid PnP ID string %s", cp);
	exit(EXIT_MAKEROM_INVPNPID);
  }

  i  = ((totarget(toupper(*(cp++))) - 0x40) & 0x1f) << 2;
  i |= ((totarget(toupper(*cp)) - 0x40) & 0x1f) >> 3;
  *(buf++) = i & 0x7f;
  i  = ((totarget(toupper(*(cp++))) - 0x40) & 0x1f) << 5;
  i |= ((totarget(toupper(*(cp++))) - 0x40) & 0x1f);
  *(buf++) = i & 0xff;
  i  = (gethex(*(cp++)) & 0x0f) << 4;
  i |= (gethex(*(cp++)) & 0x0f);
  *(buf++) = i & 0xff;
  i  = (gethex(*(cp++)) & 0x0f) << 4;
  i |= (gethex(*(cp++)) & 0x0f);
  *(buf++) = i & 0xff;
}



/*
 * Pass 1 - concatenate the kernel image with the network driver
 */
static unsigned long pass1(bp, tempfile)
struct bootdef *bp;
int tempfile;
{
  __u8 kernbuf[BLKSIZE];
  __u8 netbuf[BLKSIZE];
  __u8 firstnetbuf[BLKSIZE];
  unsigned long netdrvofs;
  unsigned long writecnt;
  unsigned long krnlength;
  unsigned long netlength;
  unsigned long romidofs;
  unsigned long kerntotsize, nettotsize, totsize;
  unsigned int kernbuflen, netbuflen;
  unsigned int firstnetbuflen;
  int kernfile, netfile;
  int romidlen, romidchksum, i;
  __u16 *u16p;

  /* Open the kernel input file */
  if ((kernfile = open(bp->kernelname, O_RDONLY | O_BINARY)) < 0) {
	prnerr1("unable to open kernel file %s", bp->kernelname);
	exit(EXIT_MAKEROM_OPENKERN);
  }

  /* Open the network driver interface input file */
  if ((netfile = open(bp->netdrv.name, O_RDONLY | O_BINARY)) < 0) {
	prnerr1("unable to open network driver interface file %s",
							bp->netdrv.name);
	exit(EXIT_MAKEROM_OPENNET);
  }

  /*
   * Read the first kernel block and determine the actual size of the rom kernel
   * without the BSS segment. Also check the version number.
   */
  kernbuflen = BLKSIZE;
  if (nbread(kernbuf, kernbuflen, kernfile) != kernbuflen) {
	prnerr1("unexpected end of file %s", bp->kernelname);
	exit(EXIT_MAKEROM_READKERN);
  }
  if (kernbuf[KRNMAJOROFS] != (__u8)VER_MAJOR ||
      kernbuf[KRNMINOROFS] != (__u8)VER_MINOR) {
	prnerr0("invalid kernel version number");
	exit(EXIT_MAKEROM_INVKERN);
  }
  krnlength  = (ttoh(getval(*((__u16 *)(&kernbuf[KRNTEXTOFS])))) + 15L) & 0xfff0L;
  krnlength += ttoh(getval(*((__u16 *)(&kernbuf[KRNDATAOFS]))));

  /*
   * Check that the kernel ROM ID structure is correct, determine it's
   * checksum and get the total memory size required for the kernel.
   */
  romidofs = ttoh(getval(*((__u16 *)(&kernbuf[KRNROMIDOFS]))));
  if (romidofs + ROMID_LENGTH > kernbuflen)
	romidlen = kernbuflen;
  else
	romidlen = kernbuf[romidofs + ROMID_LENGTH] & 0xff;
  if (romidlen < ROMID_MIN_SIZE || romidofs + romidlen > kernbuflen) {
	prnerr0("invalid ROM ID structure in kernel file");
	exit(EXIT_MAKEROM_INVKERN);
  }
  if (!bytecmp(KERNROMID, &kernbuf[romidofs + ROMID_SIG], strlen(KERNROMID))) {
	prnerr1("file %s is not a netboot kernel", bp->kernelname);
	exit(EXIT_MAKEROM_INVKERN);
  }
  romidchksum = 0;
  for (i = 0; i < romidlen; i++)
	romidchksum += kernbuf[romidofs + i] & 0xff;
  kernbuf[romidofs + ROMID_CHKSUM] = (__u8)((0 - romidchksum) & 0xff);
  kerntotsize  = (ttoh(getval(*((__u16 *)(&kernbuf[romidofs + ROMID_STKSIZE])))) + 15L) & 0xfff0L;
  kerntotsize += (ttoh(getval(*((__u16 *)(&kernbuf[romidofs + ROMID_DATASIZE])))) + 15L) & 0xfff0L;
  kerntotsize += (ttoh(getval(*((__u16 *)(&kernbuf[romidofs + ROMID_CODESIZE])))) + 15L) & 0xfff0L;
  kerntotsize /= 16L;

  /* Set the total number of kernel paragraphs into kernel image */
  u16p = (__u16 *)(&kernbuf[KRNSIZEOFS]);
  assign(*u16p, htot(kerntotsize & 0xffff));

  /*
   * Read the first network driver interface block and determine the
   * actual size of the interface without the BSS segment. Also check
   * the version number.
   */
  netbuflen = BLKSIZE;
  if (nbread(netbuf, netbuflen, netfile) != netbuflen) {
	prnerr1("unexpected end of file %s", bp->netdrv.name);
	exit(EXIT_MAKEROM_READNET);
  }
  if (netbuf[NETMAJOROFS] != (__u8)VER_MAJOR ||
      netbuf[NETMINOROFS] != (__u8)VER_MINOR) {
	prnerr0("invalid network driver interface version number");
	exit(EXIT_MAKEROM_INVDRV);
  }
  netlength  = (ttoh(getval(*((__u16 *)(&netbuf[NETTEXTOFS])))) + 15L) & 0xfff0L;
  netlength += ttoh(getval(*((__u16 *)(&netbuf[NETDATAOFS]))));

  /*
   * Check that the network driver ROM ID structure is correct and determine
   * it's checksum. The total number of paragraphs required for the network
   * driver can only be determined after all drivers have been written. We
   * also setup the bus type string in the ROM ID structure.
   */
  romidofs = ttoh(getval(*((__u16 *)(&netbuf[NETROMIDOFS]))));
  if (romidofs + ROMID_LENGTH > netbuflen)
	romidlen = netbuflen;
  else
	romidlen = netbuf[romidofs + ROMID_LENGTH] & 0xff;
  if (romidlen < ROMID_MIN_SIZE || romidofs + romidlen > netbuflen) {
	prnerr0("invalid ROM ID structure in network driver interface file");
	exit(EXIT_MAKEROM_INVDRV);
  }
  if (!bytecmp(NETROMID, &netbuf[romidofs + ROMID_SIG], strlen(NETROMID))) {
	prnerr1("file %s is not a network driver interface", bp->netdrv.name);
	exit(EXIT_MAKEROM_INVDRV);
  }
  bytecpy(bustypestr[bp->bus_type - 1],
			&netbuf[romidofs + ROMID_BUSTYPE], BUSTYPE_SIZE);
  romidchksum = 0;
  for (i = 0; i < romidlen; i++)
	romidchksum += netbuf[romidofs + i] & 0xff;
  netbuf[romidofs + ROMID_CHKSUM] = (__u8)((0 - romidchksum) & 0xff);
  nettotsize  = (ttoh(getval(*((__u16 *)(&netbuf[romidofs + ROMID_STKSIZE])))) + 15L) & 0xfff0L;
  nettotsize += (ttoh(getval(*((__u16 *)(&netbuf[romidofs + ROMID_DATASIZE])))) + 15L) & 0xfff0L;
  nettotsize += (ttoh(getval(*((__u16 *)(&netbuf[romidofs + ROMID_CODESIZE])))) + 15L) & 0xfff0L;
  nettotsize /= 16L;

  /* Set the bus type into network driver image */
  netbuf[NETBUSTYPEOFS] = (__u8)((bp->bus_type - 1) & 0xff);

  /*
   * Now copy the kernel image file into the output file. Only copy the
   * relevant bytes excluding the BSS segment, which has only zero bytes
   * anyway.
   */
  writecnt = 0L;
  krnlength -= kernbuflen;
  while (TRUE) {
	writecnt += nbwrite(kernbuf, kernbuflen, tempfile);
	if (krnlength <= 0) break;
	kernbuflen = nbread(kernbuf, BLKSIZE, kernfile);
	if (kernbuflen == 0) {
		prnerr0("unexpected end of kernel image file");
		exit(EXIT_MAKEROM_KERNEOF);
	}
	if (kernbuflen > krnlength)
		kernbuflen = krnlength;
	krnlength -= kernbuflen;
  }
  close(kernfile);
  if (verbose > 2)
	printf("End position of kernel:         %lu\n", writecnt);

  /*
   * Copy the first network driver interface buffer into a temporary
   * space so that we can restore it after we know the total network
   * driver size.
   */
  memcpy(firstnetbuf, netbuf, sizeof(netbuf));
  firstnetbuflen = netbuflen;
  netdrvofs = writecnt;

  /*
   * Next copy the network driver interface file into the output file in the
   * same way as the kernel image has been copied.
   */
  netlength -= netbuflen;
  while (TRUE) {
	writecnt += nbwrite(netbuf, netbuflen, tempfile);
	if (netlength <= 0) break;
	netbuflen = nbread(netbuf, BLKSIZE, netfile);
	if (netbuflen == 0) {
		prnerr0("unexpected end of network interface file");
		exit(EXIT_MAKEROM_DRVEOF);
	}
	if (netbuflen > netlength)
		netbuflen = netlength;
	netlength -= netbuflen;
  }
  close(netfile);
  if (verbose > 2)
	printf("End position of interface file: %lu\n", writecnt);

  /*
   * Postprocess the network driver. This usually means to copy the
   * driver (like the packet driver) into the output file.
   */
  writecnt += donetdrv(&(bp->netdrv), &nettotsize, tempfile);
  if (verbose > 2) {
	printf("End position of bootrom image:  %lu\n", writecnt);
	printf("Paragraphs for network driver:  %lu (0x%lX)\n",
						nettotsize, nettotsize);
  }

  /* Check that the output file size is not too large */
  if (writecnt > MAXOUTSIZE) {
	prnerr1("size of output file exceeds %lu kB", MAXOUTSIZE / 1024L);
	exit(EXIT_MAKEROM_SIZE);
  }

  /*
   * Compute the total size required for the bootrom and check that it
   * doesn't exceed a limit.
   */
  totsize = kerntotsize + nettotsize;
  if (totsize > (MEMEND - OSENDMEM)) {
	prnerr1("size of bootrom run image exceeds %lu kB",
					(MEMEND - OSENDMEM) / (1024L / 16L));
	exit(EXIT_MAKEROM_SIZE);
  }

  /*
   * Set the total network driver size into the first network driver
   * interface buffer and rewrite it into the output file.
   */
  u16p = (__u16 *)(&firstnetbuf[NETSIZEOFS]);
  assign(*u16p, htot(nettotsize & 0xffff));
  if (lseek(tempfile, netdrvofs, 0) < 0) {
	prnerr0("unable to seek output file");
	exit(EXIT_SEEK);
  }
  (void)nbwrite(firstnetbuf, firstnetbuflen, tempfile);

  /* Rewind to beginning of output file for the following pass 2 */
  if (lseek(tempfile, 0L, 0) < 0) {
	prnerr0("unable to seek to beginning of output file");
	exit(EXIT_SEEK);
  }
  if (verbose > 2)
	printf("\n\n");

  return(totsize);
}



/*
 * Pass 2 - compress kernel image and concatenate it with the loader
 */
static void pass2(bp, totsize, kernfile, loader)
struct bootdef *bp;
unsigned long totsize;
int kernfile;
int loader;
{
  struct loaderdef *lp = &(bp->loaders[loader]);
  __u8 inbuf[BLKSIZE];			/* Input file buffer */
  __u8 firstbuf[BLKSIZE];		/* Buffer for first sector of ROM */
  unsigned long firstofs;		/* Offset to first ROM sector */
  unsigned long writecnt;		/* number of bytes in output file */
  unsigned int len, firstlen;
  unsigned long ldsize;			/* Size of rom image loader */
  unsigned long pci_ofs;		/* Offset to PCI header */
  unsigned long pnp_ofs;		/* Offset to PnP header */
  unsigned long serno;			/* Rom serial number */
  unsigned long romvector;		/* Rom interrupt vector */
  unsigned long romsize;		/* Physical size of ROM */
  unsigned long romlength;		/* Size of bootrom kernel image */
  unsigned char romsizchar;
  int i, dorom, bootflags;
  int infile, outfile;
  char *tmpfname;
  __u16 *u16p;

  /*
   * Check for the ROM signature to determine whether we are going to build
   * a ROM or a floppy image.
   */
  if ((infile = open(lp->name, O_RDONLY | O_BINARY)) < 0) {
	prnerr1("unable to open loader file %s", lp->name);
	exit(EXIT_MAKEROM_OPENLDR);
  }
  len = nbread(inbuf, BLKSIZE, infile);
  dorom = (ttoh(getval(*((__u16 *)(&inbuf[0])))) == ROMSIG);
  if (!dorom && (lp->outtype == OUT_FLASH || lp->outtype == OUT_FC)) {
	/* Don't produce a flash header for floppy images */
	prnerr0("unable to produce floppy image with flash header");
	exit(EXIT_MAKEROM_INVLDR);
  }

  /*
   * Open the output file. If we have to produce anything different than raw
   * binary we have to initially use a temporary file.
   */
  if (lp->outtype != OUT_BINARY) {
	if ((tmpfname = tempnam(NULL, "mkrom")) == NULL) {
		prnerr0("unable to generate temporary file name");
		exit(EXIT_TEMPNAME);
	}
	if ((outfile = open(tmpfname, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
								0644)) < 0) {
		prnerr1("unable to open temporary file %s", tmpfname);
		exit(EXIT_CREATE);
	}
	if (verbose < 2)
		unlink(tmpfname);
  } else {
	if ((outfile = open(lp->outname,
			O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0644)) < 0) {
		prnerr1("unable to open output file %s", lp->outname);
		exit(EXIT_CREATE);
	}
  }

  /*
   * Now copy the specified bootrom loader into the output file. If
   * it's a floppy loader, save the first sector of the loader, and
   * read the next sector which is then the first ROM sector. Also
   * set the offset to that first ROM sector within the output file
   * to later allow rewriting it.
   */
  firstofs = 0L;
  if (!dorom) {
	if (len != BLKSIZE) {
		prnerr1("unexpected end of loader image %s", lp->name);
		exit(EXIT_MAKEROM_INVLDR);
	}
	(void)nbwrite(inbuf, len, outfile);
	firstofs = BLKSIZE;
	len = nbread(inbuf, BLKSIZE, infile);
  }

  /* Check for correct version */
  if (inbuf[ROMMAJOROFS] != (__u8)VER_MAJOR ||
      inbuf[ROMMINOROFS] != (__u8)VER_MINOR) {
	prnerr0("invalid loader version number");
	exit(EXIT_MAKEROM_INVLDR);
  }

  /* Check that we really have a netboot loader */
  if (!bytecmp(NBROMID, &inbuf[ROMIDOFS], strlen(NBROMID))) {
	prnerr1("file %s is not a netboot loader image", lp->name);
	exit(EXIT_MAKEROM_INVLDR);
  }

  /* Compute the serial number from the version number and the current time */
  serno = (unsigned long)((time(NULL) + (VER_MAJOR * 100 + VER_MINOR)) & 0x7fffffffL);
  u16p = (__u16 *)(&inbuf[ROMSEROFS + 0]);
  assign(*u16p, htot(serno & 0xffff));
  u16p = (__u16 *)(&inbuf[ROMSEROFS + 2]);
  assign(*u16p, htot((serno >> 16) & 0xffff));

  /* Compute the ROM interrupt vector - always int 18h for floppy loader */
  romvector = ((dorom ?
		(lp->useint18 ? ROMVECT18 : ROMVECT19) :
		ROMVECT18) * 4) & 0xffff;
  u16p = (__u16 *)(&inbuf[ROMVECTOFS]);
  assign(*u16p, htot(romvector & 0xffff));

  /* Set the bootrom flags */
  bootflags = 0;
  if (lp->bootask) {
	bootflags |= ROMFLG_ASKNET;
	if (lp->asktime > 0) {
		if (lp->asktime < 2)
			bootflags|= 0x02 & ROMFLG_ASKTIME;
		else
			/* The time is set in 2 second intervalls */
			bootflags|= ((lp->asktime / 2) << 1) & ROMFLG_ASKTIME;
	}
  }
  inbuf[ROMBOOTFLGOFS] = (__u8)(bootflags & 0xff);

  /* Write total number of paragraphs required for the bootrom */
  u16p = (__u16 *)(&inbuf[ROMTOTSIZEOFS]);
  assign(*u16p, htot(totsize & 0xffff));

  /* Determine offset to PCI data structure and set vendor/device ID */
  pci_ofs = ttoh(getval(*((__u16 *)(&inbuf[PCIHDROFS]))));
  if (pci_ofs) {
	unsigned long pci_len;

	pci_len = ttoh(getval(*((__u16 *)(&inbuf[pci_ofs + PCIHDRLENOFS]))));
	if (pci_ofs + pci_len > len ||
	    !bytecmp(PCISIG, &inbuf[pci_ofs], strlen(PCISIG))) {
		prnerr0("invalid PCI header");
		exit(EXIT_MAKEROM_INVLDR);
	}
	if (bp->bus_type == BUSTYPE_PCI) {
		u16p = (__u16 *)(&inbuf[pci_ofs + PCIVENDIDOFS]);
		assign(*u16p, htot(bp->pci_vendid & 0xffff));
		u16p = (__u16 *)(&inbuf[pci_ofs + PCIDEVIDOFS]);
		assign(*u16p, htot(bp->pci_devid & 0xffff));
	}
  }

  /* If we don't have a PCI card, set offset to PCI structure to zero */
  if (!lp->cardinst || bp->bus_type != BUSTYPE_PCI) {
	inbuf[PCIHDROFS + 0] = (__u8)0;
	inbuf[PCIHDROFS + 1] = (__u8)0;
  }

  /* Compute the PnP expansion header checksum */
  pnp_ofs = ttoh(getval(*((__u16 *)(&inbuf[PNPHDROFS]))));
  if (pnp_ofs) {
	int pnp_chksum = 0;
	unsigned long pnp_len;

	/* Check if PnP header is correct */
	pnp_len = ttoh(getval(*((__u16 *)
				(&inbuf[pnp_ofs + PNPEXPLENOFS])))) * 16;
	if (pnp_ofs + pnp_len > len ||
	    !bytecmp(PNPSIG, &inbuf[pnp_ofs], strlen(PNPSIG))) {
		prnerr0("invalid PnP header");
		exit(EXIT_MAKEROM_INVLDR);
	}
	/* Generate PnP vendor and device ID */
	if (bp->bus_type != BUSTYPE_PCI && bp->pnp_devid != NULL)
		setpnpid(&inbuf[pnp_ofs + PNPDEVIDOFS], bp->pnp_devid);
	/* Compute new checksum of PnP header */
	for (i = 0; i < pnp_len; i++) {
		pnp_chksum += inbuf[pnp_ofs + i];
		pnp_chksum &= 0xff;
	}
	inbuf[pnp_ofs + PNPEXPCHKSUM] = (__u8)((0 - pnp_chksum) & 0xff);
  }

  /* Save the first ROM loader sector for later reference. */
  memcpy(firstbuf, inbuf, sizeof(inbuf));
  firstlen = len;

  /* Always have to set the checksum to zero before writing the ROM code */
  write_chksum = 0;

  /* Write the loader image into the output file */
  writecnt = 0;
  while (TRUE) {
	writecnt += nbwrite(inbuf, len, outfile);
	if ((len = nbread(inbuf, BLKSIZE, infile)) == 0)
		break;
  }
  close(infile);

  /* The kernel image has to start at a paragraph boundary */
  if ((writecnt & 0x0f) != 0) {
	memset(inbuf, 0, 16);
	writecnt += nbwrite(inbuf, 16 - (writecnt & 0x0f), outfile);
  }
  ldsize = writecnt;

  /* Next copy the kernel image into the output file */
  romlength = freeze(kernfile, outfile);
  writecnt += romlength;
  if (writecnt > MAXROMSIZE) {
	prnerr1("bootrom code cannot exceed %lu kB", MAXROMSIZE / 1024L);
	exit(EXIT_MAKEROM_SIZE);
  }

  /*
   * If we got a real ROM here, determine the next available ROM size and
   * fill the output file up to that amount with FF bytes. The last byte
   * has to remain for the checksum. Then update the size of the physical
   * ROM at the beginning of the output file, and write the size of the
   * bootrom kernel image.
   */

  /* Determine required ROM size. */
  if (lp->outsize) {
	unsigned long ul = (unsigned long)(lp->outsize) * 1024L;

	if (ul < writecnt + 3 || ul > MAXROMSIZE) {
		prnerr1("invalid ROM output size %d kb given", lp->outsize);
		exit(EXIT_MAKEROM_SIZE);
	}
	romsize = ul; 
  } else for (romsize = MINROMSIZE; romsize < writecnt + 3; )
	romsize <<= 1;

  /* Compute the missing size values and the final checksum */
  romsizchar = (romsize >> 9) & 0xff;		/* Divide by 512 */
  write_chksum += romsizchar;
  if (pci_ofs)
	write_chksum += romsizchar;
  for (i = 0; i < 4; i++)
	write_chksum += (romlength >> (8 * i)) & 0xff;

  /*
   * Copy 0xFF byte buffer and checksum to output file (only for real ROM).
   * For some network cards (notably 3Com), the last two bytes of the ROM
   * code have to be 0x80.
   */
  if (dorom) {
	memset(inbuf, 0xff, sizeof(inbuf));
	while (TRUE) {
		/* Check if this is the last block */
		if (romsize - writecnt <= BLKSIZE + 3) {
			len = (int)(romsize - writecnt);
			assert(len >= 3);
			writecnt += nbwrite(inbuf, len - 3, outfile);
			write_chksum += 0x80 * 2;
			inbuf[0] = (__u8)((0 - (write_chksum & 0xff)) & 0xff);
			inbuf[1] = 0x80;
			inbuf[2] = 0x80;
			writecnt += nbwrite(inbuf, 3, outfile);
			break;
		}
		writecnt += nbwrite((unsigned char *)inbuf, BLKSIZE, outfile);
	}
  }

  /* Put the physical ROM length into the output file */
  firstbuf[ROMLENOFS] = (__u8)romsizchar;

  /* Put the image length into the PCI data structure */
  if (pci_ofs)
	firstbuf[pci_ofs + PCIIMGLENOFS] = (__u8)romsizchar;

  /* Put the length of the compressed kernel image into the output file */
  u16p = (__u16 *)(&firstbuf[ROMCOMPSIZEOFS + 0]);
  assign(*u16p, htot(romlength & 0xffff));
  u16p = (__u16 *)(&firstbuf[ROMCOMPSIZEOFS + 2]);
  assign(*u16p, htot((romlength >> 16) & 0xffff));

  /* Finally rewrite the first sector of the loader */
  if (lseek(outfile, firstofs, 0) < 0) {
	prnerr1("unable to seek %s", lp->outname);
	exit(EXIT_SEEK);
  }
  (void)nbwrite(firstbuf, firstlen, outfile);

  /* Rewind to beginning of kernel file for another pass 2 */
  if (lseek(kernfile, 0L, 0) < 0) {
	prnerr0("unable to seek to beginning of kernel file");
	exit(EXIT_SEEK);
  }

  /* Generate the final output file format */
  if (lp->outtype != OUT_BINARY) {
	if (lseek(outfile, 0L, 0) < 0) {
		prnerr0("unable to seek to beginning of temporary file");
		exit(EXIT_SEEK);
	}
	if (lp->outtype == OUT_FLASH || lp->outtype == OUT_FC)
		makeflash(lp->outname, outfile, ldsize);
	else
		makehex(lp->outname, outfile, lp->outtype);
  }
  close(outfile);
}



/*
 * Generate bootrom output files by successively calling both passes
 */
void dopasses(bp)
struct bootdef *bp;
{
  char *tmpfname;
  unsigned long totsize;
  int i, tmpfile;

  if ((tmpfname = tempnam(NULL, "mkrom")) == NULL) {
	prnerr0("unable to generate temporary file name");
	exit(EXIT_TEMPNAME);
  }
  if ((tmpfile = open(tmpfname, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
								0644)) < 0) {
	prnerr1("unable to open temporary file %s", tmpfname);
	exit(EXIT_CREATE);
  }
  if (verbose < 2)
	unlink(tmpfname);

  totsize = pass1(bp, tmpfile);
  for (i = 0; i < bp->loadernum; i++)
	pass2(bp, totsize, tmpfile, i);

  close(tmpfile);
}



syntax highlighted by Code2HTML, v. 0.9.1