/* * mknbi.c - MaKe NetBoot Image for Linux * * Copyright (C) 1995-2003 Gero Kuhlmann * Copyright (C) 1996,1997 Gero Kuhlmann * and Markus Gutschke * * 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 #ifdef HAVE_INET #include #include #include #endif #include #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|", "STRING" }, { "ip-addrs", 'i', strval, {(char **)&addrs}, NULL, "IP addresses for Linux kernel:\n" " STRING syntax: rom|kernel" #ifdef HAVE_INET "|" #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|", "STRING" }, { "vga-mode", 'm', strval, {(char **)&vgamode}, NULL, "VGA mode when Linux kernel starts:\n" " STRING syntax: normal|extended|ask|\n" " default|", "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); }