/* * mknbi.c - MaKe NetBoot Image for DOS * * Copyright (C) 1996-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: mknbi.c,v 1.8 2003/03/09 00:43:09 gkminix Exp $ */ #define NEED_BINARY 1 #include #include #include "mknbi.h" #ifndef _MKNBI_H_DOS_ #error Included wrong header file #endif /* * Values for keep command line option */ #define KEEP_MISSING 0 #define KEEP_NONE 1 #define KEEP_ALL 2 #define KEEP_UNDI 3 static char *ktypes[] = { "none", "all", "undi", NULL }; /* * Global variables */ int usehd = FALSE; /* Non-zero if ramdisk is hard disk */ int nohd = FALSE; /* Non-zero if hard disk accesses not allowed */ int norpl = FALSE; /* Non-zero if RPL mechanism not allowed */ int useint15 = FALSE; /* Non-zero if int 15h protection enabled */ int singlefat = FALSE; /* Non-zero if only one FAT for ramdisk */ char *volumename = NULL; /* volume name of ramdisk filesystem */ /* * Variables private to this module */ static char *batchname = NULL; /* name of system to batch process */ static char *outname = NULL; /* name of output file */ static char *rdname = NULL; /* name of ramdisk image file */ static char *keepname = NULL; /* name of module to keep */ static int rdsize = 0; /* size of ramdisk in kB */ static int outfile; /* file handle for output file */ static int rdimage; /* file handle for ramdisk image */ static int debug = FALSE; /* Use debugging module for testing */ static int keepflag = KEEP_MISSING; /* keep flag value for load header */ static int cur_rec_num = -1; /* Number of current load record */ static struct load_header header; /* Load header */ static struct load_record *cur_rec; /* Pointer to current load record */ /* * Command line options and arguments */ static struct cmdopt opts[] = { { "batch-sys", 'b', strval, {(char **)&batchname}, NULL, "name of system to process", "SYSTEM" }, { "outfile", 'o', strval, {(char **)&outname}, NULL, "name of boot image output file", "FILE" }, { "ramdisk-image", 'r', strval, {(char **)&rdname}, NULL, "ramdisk image source file or directory", "FILE|DIR" }, { "ramdisk-size", 's', intval, {(char **)&rdsize}, NULL, "size in kB of ramdisk image", "SIZE" }, { "no-hard-disk", 'n', boolval, {(char **)&nohd}, NULL, "do not allow hard disk accesses in client", NULL }, { "no-rpl", 'l', boolval, {(char **)&norpl}, NULL, "do not allow RPL memory protection", NULL }, { "use-int-15", 'i', boolval, {(char **)&useint15}, NULL, "use interrupt 15h ramdisk protection", NULL }, { "single-fat", 'F', boolval, {(char **)&singlefat}, NULL, "use only one FAT for ramdisk", NULL }, { "simulate-hard-disk", 'c', boolval, {(char **)&usehd}, NULL, "ramdisk simulates hard disk instead of floppy", NULL }, { "volume-name", 'V', strval, {(char **)&volumename}, NULL, "name of ramdisk volume", "STRING" }, { "keep", 'k', strval, {(char **)&keepname}, NULL, "name of module to keep after loading DOS:\n" " STRING syntax: none|all|undi", "STRING" }, { "rdimage", 0, nonopt, {(char **)&rdname}, NULL, "ramdisk image (if not given as option)", NULL }, { "debug", 't', boolval, {(char **)&debug}, NULL, "Use debugging initial loader", NULL }, { "outfile", 0, nonopt, {(char **)&outname}, NULL, "output file (if not given as option)", NULL }, { NULL, 0, noval, {NULL}, NULL, NULL, NULL } }; /* * Parameters in each section of database file */ static struct paramdef dbparams[] = { { "volume-name", par_string, NULL, {&volumename}}, { "outfile", par_string, NULL, {&outname}}, { "ramdisk-image", par_string, NULL, {&rdname}}, { "ramdisk-size", par_int, NULL, {(char **)&rdsize}}, { "no-hard-disk", par_bool, NULL, {(char **)&nohd}}, { "no-rpl", par_bool, NULL, {(char **)&norpl}}, { "use-int-15", par_bool, NULL, {(char **)&useint15}}, { "single-fat", par_bool, NULL, {(char **)&singlefat}}, { "simulate-hd", par_bool, NULL, {(char **)&usehd}}, { "debug", par_bool, NULL, {(char **)&debug}}, { "keep", par_enum, ktypes, {(char **)&keepflag}}, { NULL, par_null, NULL, {NULL}} }; /* * Write a buffer into the output file and update the load record */ static void putrec(recnum, src, size) int recnum; __u8 *src; int size; { unsigned long l; size_t isize; __u8 *buf; assert(cur_rec_num == recnum); isize = ((size / (SECTSIZE + 1)) + 1) * SECTSIZE; buf = (__u8 *)nbmalloc(isize); memcpy(buf, src, size); (void)nbwrite(buf, isize, outfile); free(buf); l = get_long(cur_rec->ilength) + isize; assign(cur_rec->ilength.low, htot(low_word(l))); assign(cur_rec->ilength.high, htot(high_word(l))); l = get_long(cur_rec->mlength) + isize; assign(cur_rec->mlength.low, htot(low_word(l))); assign(cur_rec->mlength.high, htot(high_word(l))); } /* * Initialize a load record */ static void initrec(recnum, segment, flags, vendor_size) int recnum; int segment; int flags; int vendor_size; { cur_rec_num++; assert(cur_rec_num == recnum); if (cur_rec_num > 0) cur_rec = (struct load_record *)((__u8 *)cur_rec + ((cur_rec->rlength << 2) & 0x3c) + ((cur_rec->rlength >> 2) & 0x3c)); cur_rec->rlength = (((sizeof(struct load_record) - sizeof(union vendor_data)) >> 2) | (((vendor_size + 3) & 0x3c) << 2)) & 0xff; cur_rec->rtag1 = (recnum + VENDOR_OFF) & 0xff; cur_rec->rflags = flags & 0xff; assign(cur_rec->address.low, htot(low_word((unsigned long) segment << 4))); assign(cur_rec->address.high, htot(high_word((unsigned long) segment << 4))); } /* * Process boot loader image */ static void do_loader(loadflags) int loadflags; { __u8 *dataptr; unsigned long lsize, tsize, dsize, msize; /* * Determine which of the two images to load, it's load size and * it's memory size, which is a word value stored at offset 0x0002 * of the image. */ dataptr = (debug ? firstd_data : first_data); lsize = (debug ? firstd_data_size : first_data_size); tsize = (int)(dataptr[0x0002] & 0x00FF) + ((int)(dataptr[0x0003] & 0x00FF) << 8) + 1; dsize = (int)(dataptr[0x0004] & 0x00FF) + ((int)(dataptr[0x0005] & 0x00FF) << 8) + 1; msize = roundup(tsize, 16) + dsize + 1; /* * Check that the size values are within range. We can use assert() * here because an error should never happen. The images are stored * within this program and can never change. */ assert(lsize <= LOADERLSIZE); assert(msize <= LOADERMSIZE && msize >= lsize); /* * Finally copy the image into the output file and set the load record * according to the sizes determined above. */ initrec(LOADERNUM, LOADERSEG, 0, sizeof(cur_rec->vendor_data.loadflags)); cur_rec->vendor_data.loadflags = loadflags & 0xff; putrec(LOADERNUM, dataptr, lsize); assign(cur_rec->mlength.low, htot(low_word(msize))); assign(cur_rec->mlength.high, htot(high_word(msize))); } /* * Process ramdisk image file */ static void do_ramdisk(geometry) struct disk_geometry *geometry; { static __u8 buf[SECTSIZE]; unsigned long l; int i; /* * Generate the load record which contains the disk geometry, and * then write the partition table, all hidden sectors and the new * boot record. */ initrec(RAMDISKNUM, 0, FLAG_EOF, sizeof(struct disk_geometry)); memcpy(&(cur_rec->vendor_data.geometry), geometry, sizeof(struct disk_geometry)); /* * Copy the ramdisk image from the temporary file into the final * output file. */ do { i = nbread(buf, SECTSIZE, rdimage); if (i > 0) putrec(RAMDISKNUM, buf, i); } while (i == SECTSIZE); if (get_long(cur_rec->ilength) > ((unsigned long)MAX_RDSIZE * 1024)) { prnerr0("ram disk image too large"); exit(EXIT_DOS_RDSIZE); } l = get_long(geometry->num_sectors) * SECTSIZE; assign(cur_rec->mlength.low, htot(low_word(l))); assign(cur_rec->mlength.high, htot(high_word(l))); assign(cur_rec->address.low, htot(low_word(RDADDR))); assign(cur_rec->address.high, htot(high_word(RDADDR))); } /* * Dump the load record information to stderr */ static void dump_header(lh) struct load_header *lh; { static char *s_tags[] = { /* LOADERNUM */ "primary boot loader", /* RAMDISKNUM */ "ramdisk image"}; static char *s_flags[]= { "absolute address", "after previous segment", "at end of memory", "before previos segment"}; struct load_record *lr; char *vendstr = NULL; int i, num = 0; i = (lh->hlength >> 2) & 0x3c; vendstr = (char *)nbmalloc(i + 2); while (i > 0) { i--; vendstr[i] = lh->dummy[i]; } printf("\nLoad record information:\n" " Magic number: 0x%08lX\n" " Length of header: %d bytes (standard) + %d bytes (vendor)\n" " Flags: 0x%08lX\n" " Location address: %04X:%04X\n" " Execute address: %04X:%04X\n" " Vendor data: %s\n" "\n", get_long(lh->magic), (lh->hlength << 2) & 0x3c, (lh->hlength >> 2) & 0x3c, (unsigned long)lh->hflags1 + ((unsigned long)lh->hflags2 << 8) + ((unsigned long)lh->hflags3 << 16), ttoh(getval(lh->locn.segment)), ttoh(getval(lh->locn.offset)), ttoh(getval(lh->execute.segment)), ttoh(getval(lh->execute.offset)), vendstr); i = ((lh->hlength >> 2) & 0x3c) + ((lh->hlength << 2) & 0x3c); lr = (struct load_record *)&(((__u8 *)lh)[i]); while (TRUE) { printf("Record #%d:\n" " Length of header: %d bytes (standard) + %d bytes (vendor)\n" " Vendor tag: 0x%02X (%s)\n" " Reserved flags: 0x%02X\n" " Flags: 0x%02X (%s%s)\n" " Load address: 0x%08lX%s\n" " Image length: 0x%08lX bytes\n" " Memory length: 0x%08lX bytes\n", ++num, (lr->rlength << 2) & 0x3c, (lr->rlength >> 2) & 0x3c, (int)lr->rtag1, lr->rtag1 < 16 || lr->rtag1-16 >= NUM_RECORDS ? "unknown" : s_tags[lr->rtag1-16], (int)lr->rtag2, (int)lr->rflags, s_flags[lr->rflags & 0x03], lr->rflags & FLAG_EOF ? ", last record" : "", get_long(lr->address), get_long(lr->address) >= 0x100000L && (lr->rflags & 0x03) == 0? " (high memory)" : "", get_long(lr->ilength), get_long(lr->mlength)); if (lr->rtag1-16 == RAMDISKNUM) { int heads = ttoh(getval(lr->vendor_data.geometry.num_heads)); int cyls = ttoh(getval(lr->vendor_data.geometry.cylinders)); int spt = ttoh(getval(lr->vendor_data.geometry.sect_per_track)); int diskid = ttoh(getval(lr->vendor_data.geometry.boot_drive)); printf(" Vendor data: " "%d cylinders; %d heads; %d sectors; disk id: 0x%02X\n" "\n", cyls, heads, spt, diskid); } else if (lr->rtag1-16 == LOADERNUM) { int flags = lr->vendor_data.loadflags; printf(" Vendor data: " "NO-RPL flag %s\n" " " "USE-INT-15 flag %s\n" "\n", flags & FLAG_NORPL ? "true" : "false", flags & FLAG_INT15 ? "true" : "false"); } else { printf(" Vendor data: %s\n" "\n", lr->rlength & 0xf0 ? "unknown" : "none"); } if (lr->rflags & FLAG_EOF) break; i = ((lr->rlength >> 2) & 0x3c) + ((lr->rlength << 2) & 0x3c); lr = (struct load_record *)&(((__u8 *)lr)[i]); } free(vendstr); } /* * Read system database */ static void getdb(name) char *name; { struct sectdef sect; char *namebuf; size_t len; /* Read one entry from database file */ len = strlen(name) + 11; namebuf = (char *)nbmalloc(len); sprintf(namebuf, "%s:mknbi-dos", name); sect.name = namebuf; sect.params = dbparams; sect.startsect = NULL; sect.endsect = NULL; readdb(§, dbname); /* Check that parameters are correct */ if (rdsize > MAX_RDSIZE) { prnerr2("ramdisk size must be < %d kB in section <%s>", MAX_RDSIZE, namebuf); exit(EXIT_DB); } if (rdname == NULL) { prnerr1("need ramdisk image file or directory name in section <%s>", namebuf); exit(EXIT_DB); } if (outname == NULL) { prnerr1("need output file name in section <%s>", namebuf); exit(EXIT_DB); } if ((volumename != NULL) && (strlen(volumename) > 11)) { prnerr1("volume name too long in section <%s>", namebuf); exit(EXIT_DB); } if (usehd & nohd) { prnerr1("can't use 'simulate-hd' and 'no-hard-disk' " "simultaneously in section <%s>", namebuf); exit(EXIT_DB); } free(namebuf); } /* * Main program */ int main(argc, argv) int argc; char **argv; { struct disk_geometry geometry; int vendor_size, loadflags; /* Parse options */ nbsetup(argc, argv, opts, NULL); if (keepname != NULL) { if (!strcmp(ktypes[KEEP_NONE - 1], keepname)) keepflag = KEEP_NONE; else if (!strcmp(ktypes[KEEP_ALL - 1], keepname)) keepflag = KEEP_ALL; else if (!strcmp(ktypes[KEEP_UNDI - 1], keepname)) keepflag = KEEP_UNDI; else { prnerr1("invalid keep argument %s", keepname); exit(EXIT_USAGE); } free(keepname); keepname = NULL; } /* Read configuration file */ if (batchname != NULL) getdb(batchname); if (rdsize > MAX_RDSIZE) { prnerr1("ramdisk size must be < %d kB", MAX_RDSIZE); exit(EXIT_USAGE); } if (rdname == NULL) { prnerr0("need ramdisk image file or directory name"); exit(EXIT_USAGE); } if (outname == NULL) { prnerr0("need output file name"); exit(EXIT_USAGE); } if ((volumename != NULL) && (strlen(volumename) > 11)) { prnerr0("volume name longer than 11 characters"); exit(EXIT_USAGE); } if (usehd & nohd) { prnerr0("options -h and -n can't be used simultaneously"); exit(EXIT_USAGE); } /* Prepare volume name */ if (volumename != NULL) { int i, len; char *buf = NULL; len = strlen(volumename); if (len > 0) { buf = (char *)nbmalloc(12); for (i = 0; i < 11; i++) { if (i < len) buf[i] = toupper(volumename[i]); else buf[i] = ' '; } buf[i] = '\0'; } free(volumename); volumename = buf; } /* Open the input and output files */ if ((outfile = creat(outname, 0644)) < 0) { prnerr1("unable to create output file %s", outname); exit(EXIT_DOS_IMGCREATE); } rdimage = openrd(rdname, rdsize, &geometry, &loadflags); if (verbose > 0) { printf("Ramdisk filename = %s\n", rdname); printf("Output file name = %s\n", outname); } /* Initialize the boot header */ vendor_size = ((strlen(VENDOR_ID) + sizeof(__u32) - 1) / sizeof(__u32)) * sizeof(__u32); memset(&header, 0, sizeof(header)); assign(header.magic.low, htot(low_word(HEADER_MAGIC))); assign(header.magic.high, htot(high_word(HEADER_MAGIC))); assign(header.locn.segment, htot(HEADERSEG)); assign(header.locn.offset, htot(0)); assign(header.execute.segment, htot(LOADERSEG)); assign(header.execute.offset, htot(0)); assign(header.bootsig, htot(BOOT_SIGNATURE)); header.hlength = ((__u8)((int)(header.dummy - (__u8 *)&header) / sizeof(__u32)) & 0x0f) | ((__u8)((vendor_size / sizeof(__u32)) << 4) & 0xf0); header.hflags1 = (keepflag == KEEP_ALL ? FLAG1_KEEP_ALL : (keepflag == KEEP_UNDI ? FLAG1_KEEP_UNDI : 0)); bytecpy(VENDOR_ID, header.dummy, strlen(VENDOR_ID)); (void)nbwrite((__u8 *)&header, sizeof(header), outfile); /* Initialize pointer to first load record */ cur_rec = (struct load_record *)&(header.dummy[vendor_size]); /* Process the boot loader record */ do_loader(loadflags); /* Process the ramdisk image */ do_ramdisk(&geometry); /* After writing out all this stuff, finally update the boot header */ if (lseek(outfile, 0, 0) != 0) { prnerr1("unable to seek to beginning of %s", outname); exit(EXIT_SEEK); } (void)nbwrite((__u8 *)&header, sizeof(header), outfile); close(outfile); /* If user asked for detailed output, parse the header and output all of */ /* the load record information */ if (verbose > 1) dump_header(&header); return(EXIT_SUCCESS); }