/*
* mknbi.c - MaKe NetBoot Image for DOS
*
* Copyright (C) 1996-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: mknbi.c,v 1.8 2003/03/09 00:43:09 gkminix Exp $
*/
#define NEED_BINARY 1
#include <common.h>
#include <nblib.h>
#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);
}
syntax highlighted by Code2HTML, v. 0.9.1