/* * mknbi.c - MaKe NetBoot Image for Menu Generation Language * * Copyright (C) 1997-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.4 2003/01/25 23:29:44 gkminix Exp $ */ #define NEED_BINARY 1 #include "mknbi.h" #ifndef _MKNBI_H_MGL_ #error Included wrong header file #endif /* Define optimization codes for reading the database file */ #define CONF_NOOPT 0 #define CONF_OPT86 1 #define CONF_OPT186 2 #define CONF_OPT386 3 static char *optlst[] = { "86", "186", "386", NULL }; /* * Local and global variables */ static char *batchname = NULL; /* name of system to batch process */ static char *outname = NULL; /* Name of output file */ static char *mglname = NULL; /* Name of mgl source file */ static int outfile; /* File handle for output file */ static int opt86 = FALSE; /* temporary flags for optimization */ static int opt186 = FALSE; static int opt386 = FALSE; static int confopt = CONF_NOOPT; int is186 = TRUE; /* Use 186+ optimizations */ int is386 = TRUE; /* Use 386+ optimizations */ int debug = FALSE; /* Include debugging info */ int noheader = FALSE; /* Don't include a boot image 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" }, { "infile", 'i', strval, {(char **)&mglname}, NULL, "name of MGL source file", "FILE" }, { "outfile", 'o', strval, {(char **)&outname}, NULL, "name of boot image output file", "FILE" }, { "debug", 'd', boolval, {(char **)&debug}, NULL, "enable debugging code", NULL }, { "noheader", 'c', boolval, {(char **)&noheader}, NULL, "disable generation of boot image header", NULL }, { "opt86", '0', boolval, {(char **)&opt86}, NULL, "don't use optimization", NULL }, { "opt186", '1', boolval, {(char **)&opt186}, NULL, "use optimization for 186 processor", NULL }, { "opt386", '2', boolval, {(char **)&opt386}, NULL, "use optimization for 386 processor", NULL }, { "infile", 0, nonopt, {(char **)&mglname}, NULL, "name of MGL source (if not given as option)", 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[] = { { "outfile", par_string, NULL, {&outname}}, { "infile", par_string, NULL, {&mglname}}, { "debug", par_bool, NULL, {(char **)&debug}}, { "noheader", par_bool, NULL, {(char **)&noheader}}, { "optimize", par_enum, optlst, {(char **)&confopt}}, { 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; long 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))); } /* * Copy a certain number of bytes from the compiled image file into the * output file */ static void copyrec(recnum, image) int recnum; int image; { __u8 copyrec_buf[SECTSIZE]; int i = 1; assert(cur_rec_num == recnum); while ((i = nbread(copyrec_buf, SECTSIZE, image)) > 0) putrec(recnum, copyrec_buf, i); } /* * 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)) >> 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))); } /* * Dump the load record information to stderr */ static void dump_header(lh) struct load_header *lh; { static char *s_tags[] = { /* PROGNUM */ "compiled program"}; 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("\n" "Load 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" "\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 ? " (high memory)" : "", get_long(lr->ilength), get_long(lr->mlength)); 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-mgl", name); sect.name = namebuf; sect.params = dbparams; sect.startsect = NULL; sect.endsect = NULL; readdb(§, dbname); /* Check that parameters are correct */ if (outname == NULL) { prnerr1("need output file name in section <%s>", namebuf); exit(EXIT_DB); } /* Convert optimization level */ switch (confopt) { case CONF_OPT86: opt86 = TRUE; opt186 = FALSE; opt386 = FALSE; break; case CONF_OPT186: opt86 = FALSE; opt186 = TRUE; opt386 = FALSE; break; case CONF_OPT386: default: opt86 = FALSE; opt186 = FALSE; opt386 = TRUE; break; } free(namebuf); } /* * Main program */ int main(argc, argv) int argc; char **argv; { int vendor_size; int tmpfile; /* Parse options and read configuration file */ nbsetup(argc, argv, opts, NULL); if (batchname != NULL) getdb(batchname); if (outname == NULL) { prnerr0("need output file name"); exit(EXIT_USAGE); } /* Analyze level of optimization. If nothing specified, use default */ if (opt386) { is186 = TRUE; is386 = TRUE; } else if (opt186) { is186 = TRUE; is386 = FALSE; } else if(opt86) { is186 = FALSE; is386 = FALSE; } /* Open the output file */ if ((outfile = creat(outname, 0644)) < 0) { prnerr1("unable to create output file %s", outname); exit(EXIT_MGL_IMGCREATE); } if (verbose > 0) { if (mglname == NULL) printf("Source file name = \n"); else printf("Source file name = %s\n", mglname); 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(PROGSEG)); 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 = HEADER_RETFLAG; bytecpy(VENDOR_ID, header.dummy, strlen(VENDOR_ID)); if (!noheader) (void)nbwrite((__u8 *)&header, sizeof(header), outfile); /* Initialize pointer to first load record */ cur_rec = (struct load_record *)&(header.dummy[vendor_size]); /* Compile the source file */ initrec(PROGNUM, PROGSEG, FLAG_EOF, 0); tmpfile = gencode(mglname); copyrec(PROGNUM, tmpfile); assign(cur_rec->mlength.low, htot(low_word(PROGMSIZE))); assign(cur_rec->mlength.high, htot(high_word(PROGMSIZE))); /* After writing out all this stuff, finally update the boot header */ if (!noheader) { if (lseek(outfile, 0, 0) != 0) { prnerr0("unable to seek to beginning of output file"); exit(EXIT_SEEK); } (void)nbwrite((__u8 *)&header, sizeof(header), 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); }