/*
* mknbi.c - MaKe NetBoot Image for Linux
*
* Copyright (C) 1995-2003 Gero Kuhlmann <gero@gkminix.han.de>
* Copyright (C) 1996,1997 Gero Kuhlmann <gero@gkminix.han.de>
* and Markus Gutschke <gutschk@math.uni-muenster.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.9 2003/03/09 00:43:10 gkminix Exp $
*/
#define NEED_BINARY 1
#include <common.h>
#ifdef HAVE_INET
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <nblib.h>
#include "mknbi.h"
#ifndef _MKNBI_H_LINUX_
#error Included wrong header file
#endif
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 *nfsdir = NULL; /* Directory to mount as root via NFS */
static char *rootdev = NULL; /* Name of root device */
static char *kname = NULL; /* Name of kernel image file */
static char *append = NULL; /* String to append to cmd line end */
static char *addrs = NULL; /* String with various addresses */
static char *vgamode = NULL; /* Buffer for VGA mode string */
static char *rdmodebuf = NULL; /* Buffer for ramdisk mode string */
static int rdmode = RD_AUTO; /* Ramdisk load mode */
static int debug = FALSE; /* Use debugging module for testing */
static int kimage = -1; /* File handle for kernel image */
static int rdimage = -1; /* File handle for ramdisk image */
static int outfile; /* File handle for output file */
static unsigned long rdlocation; /* Memory location of ramdisk image */
static struct setup_header setup_hdr; /* Setup header of Linux kernel */
static int kernel_ver_major; /* Linux kernel major version number */
static int kernel_ver_minor; /* Linux kernel minor version number */
static int kernel_flags = 0; /* Flags for use by the Linux kernel */
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 */
static __u8 copyrec_buf[SECTSIZE];
/*
* 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", "FILE" },
{ "append", 'a', strval, {(char **)&append}, NULL,
"append string to end of kernel command line", "STRING" },
{ "root-dir", 'd', strval, {(char **)&nfsdir}, NULL,
"NFS mounted root dir:\n"
" STRING syntax: rom|ram|kernel|<dir>", "STRING" },
{ "ip-addrs", 'i', strval, {(char **)&addrs}, NULL,
"IP addresses for Linux kernel:\n"
" STRING syntax: rom|kernel"
#ifdef HAVE_INET
"|<IP-addrs>"
#endif
, "STRING" },
{ "kernel", 'k', strval, {(char **)&kname}, NULL,
"path to Linux kernel image", "FILE" },
{ "ramdisk-mode", 'l', strval, {(char **)&rdmodebuf}, NULL,
"loading mode for ramdisk:\n"
" STRING syntax: auto|eom|<fixed address>", "STRING" },
{ "vga-mode", 'm', strval, {(char **)&vgamode}, NULL,
"VGA mode when Linux kernel starts:\n"
" STRING syntax: normal|extended|ask|\n"
" default|<number>", "STRING" },
{ "debug", 't', boolval, {(char **)&debug}, NULL,
"Use debugging kernel loader", NULL },
{ "kernel", 0, nonopt, {(char **)&kname}, NULL,
"Linux kernel image (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}},
{ "kernel", par_string, NULL, {&kname}},
{ "ramdisk-image", par_string, NULL, {&rdname}},
{ "ramdisk-mode", par_string, NULL, {&rdmodebuf}},
{ "vga-mode", par_string, NULL, {&vgamode}},
{ "append", par_string, NULL, {&append}},
{ "root-dir", par_string, NULL, {&nfsdir}},
{ "ip-addrs", par_string, NULL, {&addrs}},
{ "debug", par_bool, NULL, {(char **)&debug}},
{ NULL, par_null, NULL, {NULL}}
};
/*
* Get setup header from Linux kernel
*/
static void get_setup()
{
int i;
char version_buf[128];
/* Seek to kernel header */
assert(kimage >= 0);
if (lseek(kimage, SECTSIZE, 0) != SECTSIZE) {
prnerr0("unable to seek to Linux kernel header");
exit(EXIT_SEEK);
}
/* Read kernel header */
i = nbread((char *)&setup_hdr, sizeof(setup_hdr), kimage);
if (i != sizeof(setup_hdr) ||
!bytecmp(SETUP_MAGIC, setup_hdr.magic, strlen(SETUP_MAGIC)) ||
ttoh(getval(setup_hdr.version)) < SETUP_VERSION) {
prnerr0("invalid linux kernel or kernel too old");
exit(EXIT_LINUX_INVKERN);
}
/* Seek to kernel version string */
i = ttoh(getval(setup_hdr.kernel_version));
if (i < 0 || i > 0x8000 || lseek(kimage, i + SECTSIZE, 0) != (i + SECTSIZE)) {
prnerr0("unable to seek to Linux kernel version string");
exit(EXIT_SEEK);
}
/* Read kernel version string */
i = nbread(version_buf, sizeof(version_buf) - 1, kimage);
version_buf[i] = '\0';
i = strlen(version_buf);
if (strlen(version_buf) < 3 ||
sscanf(version_buf, "%d.%d", &kernel_ver_major, &kernel_ver_minor) != 2) {
prnerr0("inalid Linux kernel version string");
exit(EXIT_LINUX_INVKERN);
}
/* Seek back to start of kernel file */
if (lseek(kimage, 0, 0) != 0) {
prnerr0("unable to seek to Linux kernel header");
exit(EXIT_SEEK);
}
/* Setup kernel flags according to kernel version and flags */
if (kernel_ver_major >= 2) {
if (kernel_ver_minor >= 2)
kernel_flags |= KRN_USE_IP;
if (kernel_ver_minor >= 4)
kernel_flags |= KRN_NFS_IP;
}
if (!(setup_hdr.loadflags & SETUP_HIGH))
kernel_flags |= KRN_LOW;
}
/*
* 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 kernel image file into the
* boot file
*/
static int copyrec(recnum, remaining, image, name)
int recnum;
long remaining;
int image;
char *name;
{
long size, bytes_read;
int i = 1;
assert(cur_rec_num == recnum);
for (bytes_read = 0L; remaining > 0L && i > 0;) {
size = (remaining > SECTSIZE) ? SECTSIZE : remaining;
if ((i = nbread(copyrec_buf, size, image))) {
putrec(recnum, copyrec_buf, i);
bytes_read += i;
remaining -= i;
}
}
return(bytes_read);
}
/*
* Initialize a load record
*/
static void initrec(recnum, segment, flags, vendor_size)
int recnum;
unsigned long segment;
int flags;
int vendor_size;
{
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(segment << 4)));
assign(cur_rec->address.high, htot(high_word(segment << 4)));
}
/*
* Process boot loader image
*/
static void do_loader()
{
__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);
dsize = (int)(dataptr[0x0004] & 0x00FF) +
((int)(dataptr[0x0005] & 0x00FF) << 8);
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 <= BOOTLLSIZE);
assert(msize <= BOOTLMSIZE && msize >= lsize);
/*
* Finally copy the image into the output file and set the load record
* according to the sizes determined above.
*/
initrec(BOOTLNUM, DEF_BOOTLSEG, 0, 0);
putrec(BOOTLNUM, dataptr, lsize);
assign(cur_rec->mlength.low, htot(low_word(msize)));
assign(cur_rec->mlength.high, htot(high_word(msize)));
}
/*
* Process command line image
*/
static void do_cmdline(cmdline)
char *cmdline;
{
size_t len;
__u8 *bufp;
/*
* Copy the command line into a temporary buffer. This is necessary in
* case our host uses a different character set as the Linux client.
* bytecpy() cares for the necessary character set conversion.
*/
len = strlen(cmdline);
bufp = (__u8 *)nbmalloc(len + 2);
bytecpy(cmdline, bufp, len);
bufp[len] = 0;
initrec(CMDLNUM, DEF_CMDLSEG, 0, 0);
putrec(CMDLNUM, bufp, len + 1);
assign(cur_rec->mlength.low, htot(low_word(CMDLMSIZE)));
assign(cur_rec->mlength.high, htot(high_word(CMDLMSIZE)));
free(bufp);
}
/*
* Process kernel image file
*/
static void do_kernel()
{
int flags, setup_sects;
unsigned long l;
/* Process the floppy boot loader code */
initrec(INITNUM, DEF_INITSEG, 0, 0);
if (copyrec(INITNUM, INITLSIZE, kimage, kname) != INITLSIZE) {
prnerr0("unexpected end of kernel image file");
exit(EXIT_LINUX_KERNEOF);
}
if (getval(*((__u16 *)&(copyrec_buf[BOOT_SIG_OFF]))) !=
htot(BOOT_SIGNATURE)){
prnerr0("could not find magic number in kernel boot sector");
exit(EXIT_LINUX_INVKERN);
}
setup_sects = copyrec_buf[BOOT_SETUP_SEGS];
if (setup_sects == 0)
/* See /usr/src/linux/Documentation/i386/boot.txt for explanation */
setup_sects = 4;
assign(cur_rec->mlength.low, htot(low_word(INITMSIZE)));
assign(cur_rec->mlength.high, htot(high_word(INITMSIZE)));
/* Process the setup code. It is always a multiple of SECTSIZE. */
initrec(SETUPNUM, DEF_SETUPSEG, 0, 0);
while (setup_sects--) {
if (copyrec(SETUPNUM, SECTSIZE, kimage, kname) != SECTSIZE) {
prnerr0("unexpected end of kernel image file");
exit(EXIT_LINUX_KERNEOF);
}
}
if (get_long(cur_rec->ilength) > SETUPLSIZE) {
prnerr0("setup code of kernel image too large");
exit(EXIT_LINUX_INVKERN);
}
assign(cur_rec->mlength.low, htot(low_word(SETUPMSIZE)));
assign(cur_rec->mlength.high, htot(high_word(SETUPMSIZE)));
/* Process the kernel */
flags = (rdimage >= 0) ? 0 : FLAG_EOF;
initrec(KERNELNUM, DEF_SYSSEG, flags, sizeof(cur_rec->vendor_data));
cur_rec->vendor_data.krnflags = kernel_flags & 0x00FF;
while (copyrec(KERNELNUM, SECTSIZE, kimage, kname) == SECTSIZE) ;
assign(cur_rec->mlength.low, cur_rec->ilength.low);
assign(cur_rec->mlength.high, cur_rec->ilength.high);
/* Process the ramdisk image */
if (rdimage >= 0) {
unsigned long imagestart = get_long(cur_rec->address) +
get_long(cur_rec->mlength);
/* Determine position of ram disk image */
if (imagestart < 0x100000L)
imagestart = 0x100000L;
if (rdmode != RD_EOM) {
if (rdmode == RD_AUTO)
rdlocation = imagestart;
else if (rdmode == RD_FIXED)
/* always align to 4kB page boundary */
rdlocation &= ~0xfffL;
if (rdlocation < imagestart) {
prnerr0("ramdisk location within linux kernel");
exit(EXIT_LINUX_RDLOC);
}
}
/* Process the ram disk image */
flags = (rdmode == RD_EOM ? FLAG_B1 : 0) | FLAG_EOF;
initrec(RAMDISKNUM, 0, flags, sizeof(cur_rec->vendor_data));
cur_rec->vendor_data.rdflags = rdmode & 0xff;
while (copyrec(RAMDISKNUM, SECTSIZE, rdimage, rdname) == SECTSIZE) ;
/* Memory length has to be a multiple of 4kB */
l = (get_long(cur_rec->ilength) + 0xfffL) & ~0xfffL;
assign(cur_rec->mlength.low, htot(low_word(l)));
assign(cur_rec->mlength.high, htot(high_word(l)));
if (rdmode != RD_EOM)
l = rdlocation;
assign(cur_rec->address.low, htot(low_word(l)));
assign(cur_rec->address.high, htot(high_word(l)));
}
}
/*
* Remove duplicate and contradictory entries from the commandline
*/
static void cleanup_cmdline(cmdline)
char *cmdline;
{
static char *entries[] = { " root=",
" ip=",
" nfsroot=",
" nfsaddrs=",
NULL };
char **cur_entry;
char *ptr1, *ptr2;
/*
* This code assumes, that the first entry in the command line is a word
* followed by a space character. This word must not contain the letter 'r'.
* This assumptions holds, because the standard command line adds the word
* "auto " to the beginning of the command line!
*/
assert(!strncmp(cmdline, "auto ", 5));
for (cur_entry = entries; *cur_entry; cur_entry++) {
while ((ptr1 = strstr(cmdline, *cur_entry)) != NULL &&
strstr(ptr1+1, *cur_entry) != NULL) {
ptr2 = strchr(ptr1+1, ' ');
memmove(ptr1+1, ptr2+1, strlen(ptr2) + 1);
}
}
again: /* Remove multiple occurrences of "ro" and "rw" */
for (ptr1 = strchr(cmdline, 'r'); ptr1 != NULL; ptr1 = strchr(ptr1+1, 'r')) {
if (ptr1[-1] == ' ' && (ptr1[1] == 'o' || ptr1[1] == 'w') &&
ptr1[2] == ' ') {
for (ptr2 = strchr(ptr1+3, 'r'); ptr2 != NULL;
ptr2 = strchr(ptr2+1, 'r')) {
if (ptr2[-1] == ' ' &&
(ptr2[1] == 'o' || ptr2[1] == 'w') &&
(ptr2[2] == ' ' || ptr2[2] == '\000')) {
memmove(ptr1, ptr1+3, strlen(ptr1+2) + 1);
goto again;
}
}
}
}
/* Remove the "auto" at the beginning if "noauto" found */
if ((ptr1 = strstr(cmdline, " noauto")) != NULL &&
(ptr1[7] == ' ' || ptr1[7] == '\000')) {
/* For this code we assume "auto " at the beginning of the cmd line */
memmove(cmdline, cmdline+5, (ptr1 - cmdline));
memmove(ptr1-5, ptr1+7, strlen(ptr1+7) + 1);
}
}
/*
* Dump load record vendor information
*/
static char *decode_vendor_info(lr)
struct load_record *lr;
{
static char *s_rd[] = { /* RD_AUTO */ "RD: auto positioning",
/* RD_EOM */ "RD: positioned by Boot-Rom",
/* RD_FIXED */ "RD: fixed memory location" };
if ((lr->rlength & 0xf0) == 0)
return("none");
else if ((lr->rtag1 - 16) == RAMDISKNUM)
return(lr->vendor_data.rdflags < 3 ?
s_rd[lr->vendor_data.rdflags] : "illegal");
else if ((lr->rtag1 - 16) == KERNELNUM)
return(lr->vendor_data.krnflags & KRN_USE_IP ?
"use IP string" : "use NFSADDRS string");
else
return("unknown");
}
/*
* Dump the load record information to stderr
*/
static void dump_header(lh)
struct load_header *lh;
{
static char *s_tags[] = { /* BOOTLNUM */ "primary boot loader",
/* CMDLNUM */ "command line",
/* INITNUM */ "floppy boot sector",
/* SETUPNUM */ "kernel setup",
/* KERNELNUM */ "kernel image",
/* 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("\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"
" Vendor data: %s\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),
decode_vendor_info(lr)
);
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-linux", name);
sect.name = namebuf;
sect.params = dbparams;
sect.startsect = NULL;
sect.endsect = NULL;
readdb(§, dbname);
/* Check that parameters are correct */
if (kname == NULL) {
prnerr1("need kernel image file name in section <%s>", namebuf);
exit(EXIT_DB);
}
if (outname == NULL) {
prnerr1("need output file name in section <%s>", namebuf);
exit(EXIT_DB);
}
free(namebuf);
}
/*
* Main program
*/
int main(argc, argv)
int argc;
char **argv;
{
size_t len;
char *cmdline;
char *nfsaddrs;
char *cp, *ip;
int vendor_size;
int i;
/* Initialize option argments */
copystr(&nfsdir, DFLT_DIR);
copystr(&rootdev, DFLT_DEV);
copystr(&kname, DFLT_IMAGE);
/* Parse options and read configuration file */
nbsetup(argc, argv, opts, NULL);
if (batchname != NULL)
getdb(batchname);
if (kname == NULL) {
prnerr0("need kernel image file name");
exit(EXIT_USAGE);
}
if (outname == NULL) {
prnerr0("need output file name");
exit(EXIT_USAGE);
}
/* Open the input and output files */
if ((kimage = open(kname, O_RDONLY | O_BINARY)) < 0) {
prnerr1("unable to open kernel file %s", kname);
exit(EXIT_LINUX_KERNOPEN);
}
if ((outfile = creat(outname, 0644)) < 0) {
prnerr1("unable to create output file %s", outname);
exit(EXIT_LINUX_IMGCREATE);
}
if (rdname != NULL && (rdimage = open(rdname, O_RDONLY | O_BINARY)) < 0) {
prnerr1("unable to open ramdisk image file %s", rdname);
exit(EXIT_LINUX_RDOPEN);
}
if (verbose > 0) {
printf("Kernel image file name = %s\n", kname);
printf("Output file name = %s\n", outname);
if (rdname != NULL)
printf("Ramdisk image file name = %s\n", rdname);
}
/* Read kernel setup header and get kernel version */
get_setup();
if (verbose > 0)
printf("Kernel version = %d.%d\n",
kernel_ver_major, kernel_ver_minor);
/* Decode ramdisk loading mode */
if (rdmodebuf == NULL || !strcmp("auto", rdmodebuf))
rdmode = RD_AUTO;
else if (!strcmp("eom", rdmodebuf))
rdmode = RD_EOM;
else {
rdmode = RD_FIXED;
if ((rdlocation = strtol(rdmodebuf, &cp, 0)) < 0x100000L) {
prnerr0("ramdisk image load location must be above 1M");
exit(EXIT_LINUX_RDLOC);
}
if (*cp) {
prnerr0("invalid ramdisk location");
exit(EXIT_LINUX_RDLOC);
}
}
/*
* Decode VGA mode string, the VGA mode value is only valid if the
* pointer to string buffer is not NULL.
*/
if (vgamode != NULL) {
if (!strcmp("normal", vgamode))
i = VGA_NORMAL;
else if (!strcmp("extended", vgamode))
i = VGA_EXTENDED;
else if (!strcmp("ask", vgamode))
i = VGA_ASK;
else if (!strcmp("default", vgamode))
i = VGA_DEFAULT;
else {
i = (int)strtol(vgamode, &cp, 0);
if (i < VGA_MIN || i > VGA_MAX || *cp) {
prnerr0("invalid VGA mode");
exit(EXIT_LINUX_VGAMODE);
}
}
if (i == VGA_DEFAULT)
cp = NULL;
else {
cp = (char *)nbmalloc(16);
sprintf(cp, "%d", i);
}
free(vgamode);
vgamode = cp;
}
/* Parse the IP address option */
#ifdef HAVE_INET
if (addrs != NULL && *addrs && strcmp(addrs, "rom") &&
strcmp(addrs, "kernel")) {
struct hostent *hp;
char *buf, *bp;
/* Allocate memory for 7 address strings, each with max 15 chars */
i = 0;
ip = addrs;
bp = buf = (char *)nbmalloc((MAX_ADDR_SIZE + 1) * 7);
while (ip != NULL && *ip) {
if ((cp = strchr(ip, ':')) != NULL) *cp++ = '\0';
if (strlen(ip) > 0) {
if ((hp = gethostbyname(ip)) == NULL) {
prnerr1("invalid hostname %s", ip);
exit(EXIT_HOSTNAME);
}
if (hp->h_length != sizeof(struct in_addr)) {
prnerr0("invalid host address type");
exit(EXIT_HOSTADDR);
}
/* Result from ntoa is smaller than MAX_ADDR_SIZE */
strcpy(bp, inet_ntoa(*((struct in_addr *)(hp->h_addr))));
bp += strlen(bp);
}
ip = cp;
if (i < 3) *bp++ = ':';
if (i >= 3) break;
i++;
}
for (; i < 4; i++)
*bp++ = ':';
/* Finally copy host, network interface and protocol name */
if (ip != NULL) {
if ((cp = strchr(ip, ':')) != NULL) *cp++ = '\0';
if ((i = strlen(ip)) > MAX_ADDR_SIZE) {
prnerr0("client name too long in IP address string");
exit(EXIT_LINUX_INVCLNTNAME);
}
strncpy(bp, ip, i);
bp += i;
if (cp != NULL) {
ip = cp;
if ((cp = strchr(ip, ':')) != NULL) *cp++ = '\0';
if ((i = strlen(ip)) != 4 ||
strncmp(ip, "eth", 3) ||
ip[3] < '0' || ip[3] > '9') {
prnerr0("invalid ethernet device name");
exit(EXIT_LINUX_INVETH);
}
*bp++ = ':';
strncpy(bp, ip, i);
bp += i;
if (cp != NULL) {
ip = cp;
if ((kernel_ver_major < 2 ||
(kernel_ver_major == 2 &&
kernel_ver_minor < 2)) ||
(strcmp(ip, "off") &&
strcmp(ip, "none") &&
strcmp(ip, "on") &&
strcmp(ip, "any") &&
strcmp(ip, "dhcp") &&
strcmp(ip, "bootp") &&
strcmp(ip, "rarp") &&
strcmp(ip, "both"))) {
prnerr0("invalid protocol name");
exit(EXIT_LINUX_INVPROTO);
}
*bp++ = ':';
strncpy(bp, ip, i);
bp += i;
}
}
}
*bp = '\0';
free(addrs);
addrs = buf;
}
#else
if (addrs != NULL && *addrs && strcmp(addrs, "rom") &&
strcmp(addrs, "kernel")) {
prnerr0("no INET support for -i option");
exit(EXIT_INET);
}
#endif
if (addrs == NULL)
copystr(&addrs, DFLT_ADDRS);
/* Determine root device for kernel */
if (!strncmp(nfsdir, "ram", 3) || !strcmp(nfsdir, "initrd")) {
if (rdname == NULL) {
prnerr0("boot from ramdisk but no image specified");
exit(EXIT_LINUX_NORD);
}
if (kernel_ver_major < 1 ||
(kernel_ver_major == 1 && kernel_ver_minor < 4)) {
prnerr0("kernel too old to boot from ramdisk");
exit(EXIT_LINUX_INVKERN);
}
if (rootdev != NULL)
free(rootdev);
rootdev = (char *)nbmalloc(strlen(nfsdir) + 16);
if (!strcmp(nfsdir, "initrd"))
sprintf(rootdev, "root=/dev/ram");
else {
for (i = 3; i < 6 && nfsdir[i] != '\0'; i++)
if (!isdigit(nfsdir[i]))
break;
if (nfsdir[i] != '\0') {
prnerr0("invalid ramdisk device name");
exit(EXIT_LINUX_INVRDDEV);
}
sprintf(rootdev, "root=/dev/%s", nfsdir);
}
/*
* If the nfsroot option is missing on the command line, the
* boot loader will add a default option, which might not be what
* we want. Instead, we want to have no nfsroot option at all
* on the command line, because we are using a ramdisk. We set
* nfsdir to a value of "kernel" which means to the boot loader
* to remove this option from the final command line.
*/
copystr(&nfsdir, "kernel");
} else if (!strncmp(nfsdir, "/dev/", 5)) {
if (rootdev != NULL)
free(rootdev);
rootdev = (char *)nbmalloc(strlen(nfsdir) + 7);
sprintf(rootdev, "root=%s", nfsdir);
copystr(&nfsdir, "kernel");
}
/* Construct command line to pass to the kernel */
if (kernel_flags & KRN_USE_IP) {
/*
* If we have to use "ip=" instead of "nfsaddrs=" on the kernel
* command line, this means we have a newer kernel. With the
* old kernel a missing "nfsaddrs=" means to let the kernel
* determine the IP addresses by itself. With the newer kernel
* versions a missing "ip=" option just leaves the interface
* uninitialized. We therefore have to use an empty IP address
* string in this case.
*/
if (addrs != NULL && !strcmp(addrs, "kernel"))
copystr(&addrs, KERNEL_ADDRS);
nfsaddrs = NFS_IP;
} else
nfsaddrs = NFS_ADDRS;
len = strlen(DFLT_CMDL) + strlen(rootdev) + 2;
if (append != NULL && *append)
len += strlen(append) + 1;
if (nfsdir != NULL && *nfsdir)
len += strlen(NFS_ROOT) + strlen(nfsdir) + 1;
if (addrs != NULL && *addrs)
len += strlen(nfsaddrs) + strlen(addrs) + 1;
if (vgamode != NULL && *vgamode)
len += strlen(VGA_ARG) + strlen(vgamode) + 1;
cmdline = (char *)nbmalloc(len + 2);
sprintf(cmdline, "%s %s", DFLT_CMDL, rootdev);
if (nfsdir != NULL && *nfsdir) {
cp = cmdline + strlen(cmdline);
sprintf(cp, " %s%s", NFS_ROOT, nfsdir);
}
if (addrs != NULL && *addrs) {
cp = cmdline + strlen(cmdline);
sprintf(cp, " %s%s", nfsaddrs, addrs);
}
if (vgamode != NULL && *vgamode) {
cp = cmdline + strlen(cmdline);
sprintf(cp, " %s%s", VGA_ARG, vgamode);
}
if (append != NULL && *append) {
cp = cmdline + strlen(cmdline);
sprintf(cp, " %s", append);
}
cleanup_cmdline(cmdline);
if (strlen(cmdline) > CMDLLSIZE) {
prnerr0("command line too long");
exit(EXIT_LINUX_CMDLSIZE);
}
if (verbose > 0)
printf("Kernel command line = \"%s\"\n", cmdline);
/* 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(DEF_HEADERSEG));
assign(header.locn.offset, htot(0));
assign(header.execute.segment, htot(DEF_BOOTLSEG));
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);
bytecpy(VENDOR_ID, header.dummy, strlen(VENDOR_ID));
(void)nbwrite((__u8 *)&header, sizeof(header), outfile);
/* Initialize the pointer to the first load record */
cur_rec = (struct load_record *)&(header.dummy[vendor_size]);
/* Process all load records. The order of these calls is important! */
do_loader();
do_cmdline(cmdline);
do_kernel();
/* After writing out all this stuff, finally update the boot header */
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 the 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