/*
* netdrv.c - Handle different network drivers
*
* Copyright (C) 1998-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: netdrv.c,v 1.7 2003/01/25 23:29:42 gkminix Exp $
*/
#define NEED_BINARY 1
#include <common.h>
#include <nblib.h>
#include "makerom.h"
/*
* Definitions local for this module
*
* Note for MAXPROGSIZE/MAXUNDISIZE: The bootrom copies the program at one
* piece, and can therefore only handle max. 64kB. However, since the starting
* address within the decompressed image might not start at a segment boundary
* and we can't allow segment overruns, the maximum allowable size for any
* program file is 65535 - 16, e.g. 65519, minus one byte for safety.
*/
#define COMSIG 0x4B47 /* Signature for DOS program header */
#define EXESIG 0x5A4D /* Signature of DOS EXE files */
#define MAXARGSIZE 126 /* Max length of DOS argument line */
#define MAXPROGSIZE 65518L /* Max size of DOS program */
#define MAXUNDISIZE 65518L /* Max size of UNDI driver */
#define MAXPISIZE 16424L /* Max size of PROTOCOL.INI image */
#define MAXCOMPARA 0x0FFFL /* Max paragraphs for COM program */
#define COMSTACKPARA (1024L / 16L) /* Paragraph no. for COM stack */
#define MINPROGPARA 80 /* Min program size is paragraphs */
#define PSPSIZE 256L /* Size of DOS PSP */
#define PSPPARA (PSPSIZE / 16L) /* Size of DOS PSP in paragraphs */
#define DOSBASEPARA 6L /* DOS base paragraphs required */
#define EXESECTSIZE 512 /* Size of one sector in EXE file */
#define EXESIGOFS 0x0000 /* Offset to EXE file signature */
#define EXELASTSECT 0x0002 /* Number of bytes in last EXE sector */
#define EXEFSIZE 0x0004 /* Number of sectors in EXE file */
#define EXEHEADSIZE 0x0008 /* Offset to size of EXE file header */
#define EXEMINPARA 0x000A /* Required number of paragraphs */
#define EXEHDRSIZE 0x001A /* Minimum size of EXE header */
#define UNDISIGNATURE "UNDI" /* UNDI file signature */
#define UNDISIGOFS 0x0000 /* Offset to UNDI file signature */
#define UNDITEXT 0x0004 /* Size of text segment in bytes */
#define UNDIDATA 0x0006 /* Size of data segment in bytes */
#define UNDIBSS 0x0008 /* Size of BSS segment in bytes */
#define UNDIHDRSIZE 0x000E /* Size of UNDI file header */
/*
* Variables local to this module
*/
static unsigned long dosminpara; /* Minimum no. of DOS paragraphs */
static unsigned long dosmaxpara; /* Maximum no. of DOS paragraphs */
/*
* Determine required paragraphs for a program compressed with PKLITE.
*/
static unsigned long chk_pklite(fname, progpara, buf)
char *fname;
unsigned long progpara;
__u8 *buf;
{
unsigned long sizepara;
sizepara = (ttoh(getval(*((__u16 *)(&buf[0x0001])))) + 15L) / 16L;
if (sizepara < progpara) {
/* PKLITE sometimes gives wrong sizes */
prnerr1("Warning: %s", fname);
prnerr0("Invalid size in file compressed with PKLITE.");
prnerr0("Continuing anyway with some hopefully safe defaults. You should");
prnerr0("better decompress the file before using it for a bootrom.");
sizepara = MAXCOMPARA;
}
return(sizepara);
}
/*
* Some DOS executables are packed or compressed. Most decompressors require
* a little amount of memory behind the loaded program. This is only neces-
* sary for COM programs, since EXE programs know in advance how large they
* have to be for decompression.
* The only compressor I know of so far is PKLITE. All other compressors like
* tinyprog or lzexe can only compress EXE type files, or generate EXE even
* when compressing COM programs.
*/
static struct compstruct {
unsigned char *name;
unsigned char *idstring;
unsigned int idoffset;
unsigned long (*getsize)__P((char *, unsigned long, __u8 *));
} complist[] = {
{ "PKLITE", "PKLITE", 0x0030, &chk_pklite },
{ NULL, NULL, 0, NULL }
};
/*
* Copy DOS program into output file
*/
static unsigned long copy_prog(fname, args, minsize, maxsize, outfile)
char *fname;
char *args;
long minsize;
long maxsize;
int outfile;
{
__u8 inbuf[BLKSIZE];
__u8 firstbuf[BLKSIZE];
unsigned long progsize;
unsigned long progpara;
unsigned long dospara;
unsigned long writecnt = 0L;
unsigned int inbuflen, firstbuflen;
int i, len, infile;
__u16 *u16p;
/*
* Each DOS program requires a header which looks like:
*
* Offset
* 0000 - Two byte magic cookie
* 0002 - Length of command line in bytes
* 0003 - Command line (not terminated by zero)
* var - Length of program image in bytes
* var+2 - Amount of memory to allocate in paragraphs
* var+4 - Program image
*
* Setup everything up to the command line.
*/
memset(firstbuf, 0, sizeof(firstbuf));
firstbuf[0] = (__u8)(COMSIG & 0xff);
firstbuf[1] = (__u8)((COMSIG >> 8) & 0xff);
if (args != NULL) {
if ((len = strlen(args)) > MAXARGSIZE - 1) {
prnerr0("DOS program argument too large");
exit(EXIT_MAKEROM_ARGSIZE);
}
firstbuf[2] = (__u8)((len + 1) & 0xff);
firstbuf[3] = (__u8)0x20; /* cmd line has to start with a blank */
for (firstbuflen = 4, i = 0;
firstbuflen < sizeof(firstbuf) && i < len && args[i];
firstbuflen++, i++)
firstbuf[firstbuflen] = (__u8)(args[i] & 0xff);
} else {
firstbuf[2] = 0;
firstbuflen = 3;
}
/* Now open the DOS program image file and determine it's size. */
progsize = filesize(fname);
if ((infile = open(fname, O_RDONLY | O_BINARY)) < 0) {
prnerr1("unable to open DOS program %s", fname);
exit(EXIT_MAKEROM_OPENDRV);
}
/*
* Read first sector of DOS program image and check it's type. In case of
* a COM program, we set the number of required memory paragraphs to the
* total size of the program. Otherwise, for an EXE program we can take
* this value out of the EXE file header.
*/
inbuflen = nbread((unsigned char *)inbuf, sizeof(inbuf), infile);
if (ttoh(getval(*((__u16 *)(&inbuf[EXESIGOFS])))) == EXESIG) {
unsigned long exehlen = 0L;
unsigned long exesize, l;
/* Determine length of EXE file header */
if (inbuflen >= EXEHDRSIZE)
exehlen = ttoh(getval(*((__u16 *)(&inbuf[EXEHEADSIZE])))) * 16;
if (exehlen < EXEHDRSIZE) {
prnerr1("invalid EXE header in file %s", fname);
exit(EXIT_MAKEROM_INVEXE);
}
/* Determine total amount of memory required for EXE program */
l = ttoh(getval(*((__u16 *)(&inbuf[EXELASTSECT]))));
exesize = (ttoh(getval(*((__u16 *)(&inbuf[EXEFSIZE])))) - 1) * EXESECTSIZE;
exesize += ttoh(getval(*((__u16 *)(&inbuf[EXEMINPARA])))) * 16;
exesize += (l == 0L ? EXESECTSIZE : l);
if ((exesize + exehlen) > MAXPROGSIZE) {
prnerr1("DOS program %s requires too much memory", fname);
exit(EXIT_MAKEROM_INVEXE);
}
/* Skip any possible debugging information in EXE file */
if (progsize > (exesize + exehlen))
progsize = exesize + exehlen;
progpara = ((exesize + 15L) / 16L) + PSPPARA;
} else {
unsigned long reqmem;
int compr;
/* Check amount of memory required to load the file */
if (progsize > (MAXPROGSIZE - PSPSIZE)) {
prnerr1("DOS program %s too large", fname);
exit(EXIT_MAKEROM_DOSSIZE);
}
progpara = ((progsize + 15L) / 16L) + PSPPARA + COMSTACKPARA;
/* Check if the program has been compressed */
for (compr = 0; complist[compr].name != NULL; compr++) {
if (bytecmp(complist[compr].idstring,
&inbuf[complist[compr].idoffset],
strlen(complist[compr].idstring))) {
if (complist[compr].getsize == NULL) {
prnerr2("program %s is compressed with %s",
fname, complist[compr].name);
prnerr0("it has to be decompressed before it can be used");
exit(EXIT_MAKEROM_PROGCOMP);
}
progpara = (*complist[compr].getsize)(fname, progpara,
inbuf);
break;
}
}
/*
* With COM programs it's fairly difficult to determine the amount
* of memory required for loading and running it (in contrast to
* EXE programs which have this information in their header). The
* code above just guesses some minimum value. In case the user
* has specified a different value with the maxsize parameter, use
* that instead. Also note that some programs might require more
* memory when running than for just loading them. In order to care
* for this, we use the larger of minsize and maxsize for the actual
* amount of memory to allocate.
* If the user has not specified a maxsize parameter, we take the
* size guessed above and round it up to the maximum value.
*/
if (maxsize < 0L && (minsize < 0 || minsize < (progpara * 16L)))
reqmem = progpara;
else if (minsize < 0L || minsize < maxsize)
reqmem = (maxsize + 15L) / 16L;
else
reqmem = (minsize + 15L) / 16L;
if (reqmem > progpara)
progpara = reqmem;
/* Check amount of paragraphs to allocate */
if (progpara > MAXCOMPARA) {
prnerr1("DOS program %s requires too much memory", fname);
exit(EXIT_MAKEROM_DOSSIZE);
}
}
/* Minimum program size - smaller values get rejected by the bootrom */
if (progpara < MINPROGPARA)
progpara = MINPROGPARA;
/* Now set the program header in the output file */
u16p = (__u16 *)(&firstbuf[firstbuflen]);
assign(*u16p, htot(low_word(progsize)));
u16p = (__u16 *)(&firstbuf[firstbuflen + 2]);
assign(*u16p, htot(low_word(progpara)));
firstbuflen += 4;
/* Determine the minimum and maximum amount of memory required by program */
if (maxsize < 0L)
maxsize = progpara * 16L;
else if ((maxsize + 15L) / 16L < progpara) {
prnerr1("Warning: %s", fname);
prnerr0("Given maxsize value probably too small, but using it anyway.");
if (verbose > 1)
prnerr2("maxsize = %ld, progsize = %ld",
maxsize, progpara * 16L);
}
if (minsize < 0L)
minsize = maxsize;
if (minsize > maxsize)
maxsize = minsize;
/*
* Determine the amount of memory required for the DOS simulator. When the
* first program loads, it has to have at least maxsize bytes of memory
* available. When it terminates, it will occupy minsize bytes of memory.
* Therefore, for the next program the total amount of DOS memory has to
* be at least the number of paragraphs required so far by the preceding
* programs, plus it's own maximum value. At the end, dosmaxpara is the
* number of paragraphs required by the DOS simulator.
*/
dospara = dosminpara + ((maxsize + 15L) / 16L) + 1;
if (dospara > dosmaxpara)
dosmaxpara = dospara;
dosminpara += ((minsize + 15L) / 16L) + 1;
/* Now first write the program header and then the program image */
writecnt += nbwrite(firstbuf, firstbuflen, outfile);
while (TRUE) {
if (inbuflen >= progsize) {
writecnt += nbwrite(inbuf, progsize, outfile);
break;
}
progsize -= inbuflen;
writecnt += nbwrite(inbuf, inbuflen, outfile);
if ((inbuflen = nbread(inbuf, BLKSIZE, infile)) == 0) {
prnerr1("unexepected end of DOS program %s", fname);
exit(EXIT_MAKEROM_EXEEOF);
}
}
close(infile);
return(writecnt);
}
/*
* Write DOS programs into output file
*/
static unsigned long dodosprog(pdp, outfile)
struct progdef *pdp;
int outfile;
{
unsigned long writecnt = 0L;
int i;
for (i = 0; i < pdp->prognum; i++) {
writecnt += copy_prog(pdp->prognames[i], pdp->progargs[i],
pdp->minsizes[i], pdp->maxsizes[i], outfile);
if (verbose > 2)
printf("End offset of prog %d: %lu\n", i, writecnt);
}
return(writecnt);
}
/*
* Write packet driver programs into output file
*/
static unsigned long dopktdrv(np, parareq, outfile)
struct netdrvdef *np;
unsigned long *parareq;
int outfile;
{
struct pktdrvdef *pdp = &(np->driverdefs.pd);
struct i_long il;
unsigned long writecnt = 0L;
/* Setup minimum amount of paragraphs required for DOS simulator */
dosminpara = dosmaxpara = DOSBASEPARA;
/* Write the number of DOS programs into output file */
assign(il.low, htot(low_word(pdp->progs.prognum)));
writecnt += nbwrite((__u8 *)&(il.low), sizeof(il.low), outfile);
/* Write all DOS programs into output file */
writecnt += dodosprog(&(pdp->progs), outfile);
*parareq += dosmaxpara;
return(writecnt);
}
/*
* Write NDIS driver programs into output file
*/
static unsigned long dondisdrv(np, parareq, outfile)
struct netdrvdef *np;
unsigned long *parareq;
int outfile;
{
struct ndisdef *ndp = &(np->driverdefs.ndis);
struct i_long il;
unsigned long writecnt = 0L;
/* Write the size and image of protocol.ini into the output file */
if (ndp->protinisize > MAXPISIZE) {
prnerr0("PROTOCOL.INI image too large");
exit(EXIT_MAKEROM_PISIZE);
}
assign(il.low, htot(low_word(ndp->protinisize)));
writecnt += nbwrite((__u8 *)&(il.low), sizeof(il.low), outfile);
writecnt += nbwrite((__u8 *)(ndp->protocolini), ndp->protinisize, outfile);
/* Setup minimum amount of paragraphs required for DOS simulator */
dosminpara = dosmaxpara = DOSBASEPARA + ((ndp->protinisize + 15L) / 16L) + 1;
/* Next write the number of DOS programs into output file */
assign(il.low, htot(low_word(ndp->progs.prognum)));
writecnt += nbwrite((__u8 *)&(il.low), sizeof(il.low), outfile);
/* Write all DOS programs into output file */
writecnt += dodosprog(&(ndp->progs), outfile);
*parareq += dosmaxpara;
return(writecnt);
}
/*
* Write UNDI driver into output file
*/
static unsigned long doundidrv(np, parareq, outfile)
struct netdrvdef *np;
unsigned long *parareq;
int outfile;
{
struct undidef *up = &(np->driverdefs.undi);
struct stat sbuf;
unsigned long undisize;
unsigned long writecnt = 0L;
__u8 inbuf[BLKSIZE];
int infile, inbuflen;
/* Open the UNDI driver image file */
if ((infile = open(up->name, O_RDONLY | O_BINARY)) < 0) {
prnerr1("unable to open UNDI driver%s", up->name);
exit(EXIT_MAKEROM_OPENDRV);
}
if (fstat(infile, &sbuf) < 0) {
prnerr1("unable to stat UNDI driver %s", up->name);
exit(EXIT_STAT);
}
if (sbuf.st_size > MAXUNDISIZE) {
prnerr1("UNDI driver %s too large", up->name);
exit(EXIT_MAKEROM_UNDISIZE);
}
/*
* Read the first sector of the UNDI driver and check that it has a
* correct header.
*/
inbuflen = nbread((__u8 *)inbuf, BLKSIZE, infile);
if (inbuflen < UNDIHDRSIZE ||
!bytecmp(UNDISIGNATURE, inbuf, strlen(UNDISIGNATURE))) {
prnerr1("invalid UNDI driver header in file %s", up->name);
exit(EXIT_MAKEROM_INVDRV);
}
undisize = ttoh(getval(*((__u16 *)(&inbuf[UNDITEXT]))));
undisize += ttoh(getval(*((__u16 *)(&inbuf[UNDIBSS]))));
if (undisize > MAXUNDISIZE) {
prnerr1("UNDI driver %s too large", up->name);
exit(EXIT_MAKEROM_UNDISIZE);
}
*parareq += ((undisize + 15L) / 16L) + 1;
/* Write the driver image into the output file */
while (inbuflen > 0) {
writecnt += nbwrite((__u8 *)inbuf, inbuflen, outfile);
inbuflen = nbread((__u8 *)inbuf, BLKSIZE, infile);
}
close(infile);
return(writecnt);
}
/*
* Copy the network driver into the output file
*/
unsigned long donetdrv(np, parareq, outfile)
struct netdrvdef *np;
unsigned long *parareq;
int outfile;
{
switch (np->drivertype) {
case DRVTYPE_PD:
return(dopktdrv(np, parareq, outfile));
case DRVTYPE_NDIS:
return(dondisdrv(np, parareq, outfile));
case DRVTYPE_UNDI:
return(doundidrv(np, parareq, outfile));
}
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1