/* * netdrv.c - Handle different network drivers * * Copyright (C) 1998-2003 Gero Kuhlmann * * 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 #include #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); }