Makefile | 20 +++- buildimage.c | 15 ++- mbootpack.c | 311 ++++++++++++++++++++++++++--------------------------------- mbootpack.h | 2 +- 4 files changed, 162 insertions(+), 186 deletions(-) diff --git a/Makefile b/Makefile index aba82f3..2096ffd 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,10 @@ # PROG := mbootpack +MANPAGE := $(PROG).man OBJS := mbootpack.o buildimage.o DEPS := mbootpack.o.d buildimage.o.d +VERSION := 0.6a # # Tools etc. @@ -25,17 +27,31 @@ CFLAGS += -Wmissing-prototypes CFLAGS += -pipe -O3 DEPFLAGS = -Wp,-MD,$(@F).d +ifneq ($(VERSION),) +DEFS += -DVERSION=\"$(VERSION)\" +endif + +ifneq ($(WITH_ZLIB),) +DEFS += -DHAVE_ZLIB +ZLIB = -lz +else +ZLIB = +endif + # # Rules # -all: $(PROG) +all: $(PROG) $(MANPAGE) gdb: $(PROG) $(GDB) $< $(PROG): $(OBJS) - $(CC) -o $@ $(filter-out %.a, $^) + $(CC) $(CFLAGS) $(DEFS) -o $@ $(filter-out %.a, $^) $(ZLIB) + +$(MANPAGE): $(PROG) + help2man -n "packages a multiboot kernel and modules as a single file" -s 1 -N ./$< > $@ clean: FRC $(RM) mbootpack *.o *.d bootsect setup bzimage_header.c diff --git a/buildimage.c b/buildimage.c index 033dcf8..7f3e054 100644 --- a/buildimage.c +++ b/buildimage.c @@ -38,7 +38,6 @@ #include #include #include -#include #include "mbootpack.h" #include "mb_header.h" @@ -84,14 +83,14 @@ address_t place_mbi(long int size) address_t start; start = 0xa000 - size; if (start < 0x9000 + sizeof(bzimage_bootsect) + sizeof(bzimage_setup)) { - printf("Fatal: command-lines too long: need %i, have %i bytes\n", + printf("Fatal: command-lines too long: need %ld, have %ld bytes\n", size, 0x1000 - (sizeof(bzimage_bootsect) + sizeof(bzimage_setup))); exit(1); } if (!quiet) { - printf("Placed MBI and strings (%p+%p)\n", - start, size); + printf("Placed MBI and strings (%lx+%lx)\n", + (unsigned long)start, size); } return start; } @@ -110,7 +109,7 @@ void make_bzImage(section_t *sections, /* Patch the kernel and mbi addresses into the setup code */ *(address_t *)(bzimage_setup + BZ_ENTRY_OFFSET) = entry; *(address_t *)(bzimage_setup + BZ_MBI_OFFSET) = mbi; - if (!quiet) printf("Kernel entry is %p, MBI is %p.\n", entry, mbi); + if (!quiet) printf("Kernel entry is %lx, MBI is %lx.\n", (unsigned long)entry, (unsigned long)mbi); /* If the high-memory sections don't start at 1MB, shuffle them down * in the file and record how they should be relocated at boot time. */ @@ -122,8 +121,8 @@ void make_bzImage(section_t *sections, if (highmem_size != 0) { *(address_t *)(bzimage_setup + BZ_HIGH_START_OFFSET) = highmem_start; *(address_t *)(bzimage_setup + BZ_HIGH_SIZE_OFFSET) = highmem_size; - if (!quiet) printf("High sections: %p bytes at %p.\n", - highmem_size, highmem_start); + if (!quiet) printf("High sections: %lx bytes at %lx.\n", + (unsigned long)highmem_size, (unsigned long)highmem_start); } else highmem_start = HIGHMEM_START; @@ -144,7 +143,7 @@ void make_bzImage(section_t *sections, exit(1); } - if (!quiet) printf("Wrote bzImage header: %i + %i bytes.\n", + if (!quiet) printf("Wrote bzImage header: %zu + %zu bytes.\n", sizeof(bzimage_bootsect), sizeof(bzimage_setup)); /* Sorted list of sections below 1MB: write them out */ diff --git a/mbootpack.c b/mbootpack.c index eb0aa0f..16e44f3 100644 --- a/mbootpack.c +++ b/mbootpack.c @@ -43,7 +43,17 @@ #include #include #include -#include + +#ifdef HAVE_ZLIB +#include +#else +#define gzFile FILE* +#define gzopen fopen +#define gzclose fclose +#define gzeof feof +#define gzseek fseek +#define gzread(f, buf, len) fread(buf, 1, len, fp) +#endif /* From GNU GRUB */ #include "mb_header.h" @@ -112,72 +122,79 @@ static section_t *sections = NULL; static section_t *last_section = NULL; static address_t next_free_space = 0; -static void usage(void) -/* If we don't understand the command-line options */ -{ - printf( -"Usage: mbpack [OPTIONS] kernel-image\n\n" -" -h --help Print this text.\n" +static const char usage[] = +"Usage: mbootpack [OPTIONS] kernel-image\n\n" +"Takes a multiboot kernel and modules (e.g. a Xen VMM, linux kernel and initrd), and packages them up as a single file that looks like a bzImage linux kernel.\n\n" " -q --quiet Only output errors and warnings.\n" " -t --truncate Clip module paths in command-lines.\n" " -o --output=filename Output to filename (default \"bzImage\").\n" " -c --command-line=STRING Set the kernel command line (DEPRECATED!).\n" " -m --module=\"MOD arg1 arg2...\" Load module MOD with arguments \"arg1...\"\n" " (can be used multiple times).\n" -"\n"); - exit(1); -} +" -h --help Display this help and exit.\n" +#ifdef VERSION +" -V --version Display version number and exit.\n" +#endif +"\n"; /* Return a pointer into the string, skipping over all leading directories. */ static char *clip_path(char *str) { - const char *p, *last = NULL; + char *p, *last = NULL; for (p = str; *p && *p != ' '; p++) if (*p == '/') last = p; return (last ? last + 1 : str); } +static _Noreturn void fatal(const char *fmt, ...) +{ + va_list args; + + fputs("Fatal: ", stderr); + fprintf(stderr, fmt, args); + exit(1); +} + +static _Noreturn void fatal_err(const char *fmt, ...) +{ + va_list args; + + fatal(fmt, args, strerror(errno)); +} + static void place_kernel_section(address_t start, long int size) /* Place the kernel in memory, checking for the memory hole. */ { - if (start + size + 0x1000 - 0x100000 >= 0x2000000) { + if (start + size + 0x1000 - 0x100000 >= 0x2000000) /* Arbitrary limit: output file would be too big to TFTP. * This is really to catch insane load addresses: Xen 2.0.5 * comes in a version with symbols, which asks to be loaded * at Xen's *virtual* address (nearly 4GB). */ - printf("Fatal: kernel section loads too high: %#x+%#x\n", start); - exit(1); - } + fatal("kernel section loads too high: %#lx+%#lx\n", (unsigned long)start, size + 0x1000 - 0x100000); if (start >= MEM_HOLE_END) { /* Above the memory hole: easy */ next_free_space = MAX(next_free_space, start + size); if (!quiet) { - printf("Placed kernel section (%p+%p)\n", start, size); + printf("Placed kernel section (%lx+%lx)\n", (unsigned long)start, size); } return; } - if (start >= MEM_HOLE_START) { + if (start >= MEM_HOLE_START) /* In the memory hole. Not so good */ - printf("Fatal: kernel load address (%p) is in the memory hole.\n", - start); - exit(1); - } + fatal("kernel load address (%lx) is in the memory hole.\n", (unsigned long)start); - if (start + size > MEM_HOLE_START) { + if (start + size > MEM_HOLE_START) /* Too big for low memory */ - printf("Fatal: kernel (%p+%p) runs into the memory hole.\n", - start, size); - exit(1); - } + fatal("kernel (%lx+%lx) runs into the memory hole.\n", (unsigned long)start, size); /* Kernel loads below the memory hole */ next_free_space = MAX(next_free_space, start + size); if (!quiet) { - printf("Placed kernel section (%p+%p)\n", start, size); + printf("Placed kernel section (%lx+%lx)\n", (unsigned long)start, size); } } @@ -199,8 +216,7 @@ static address_t place_section(long int size, int align) next_free_space = start + size; if (!quiet) { - printf("Placed section (%p+%p), align=%p\n", - start, size, align); + printf("Placed section (%lx+%lx), align=%d\n", (unsigned long)start, size, align); } return start; } @@ -216,7 +232,7 @@ static address_t load_kernel(const char *filename) address_t start; size_t len; long int size, loadsize; - FILE *fp; + gzFile fp; char *buffer; section_t *sec, *s; Elf32_Ehdr *ehdr; @@ -227,44 +243,37 @@ static address_t load_kernel(const char *filename) static char headerbuf[HEADERBUF_SIZE]; /* Stat and open the file */ - if (stat(filename, &sb) != 0) { - printf("Fatal: cannot stat %s: %s\n", filename, strerror(errno)); - exit(1); - } - if ((fp = fopen(filename, "r")) == NULL) { - printf("Fatal: cannot open %s: %s\n", filename, strerror(errno)); - exit(1); - } + if (stat(filename, &sb) != 0) + fatal("cannot stat %s: %s\n", filename, strerror(errno)); + if ((fp = gzopen(filename, "r")) == NULL) + fatal("cannot open %s: %s\n", filename, strerror(errno)); /* Load the first 8k of the file */ - if (fseek(fp, 0, SEEK_SET) < 0) { - printf("Fatal: seek error in %s: %s\n", filename, strerror(errno)); - exit(1); - } - if ((len = fread(headerbuf, 1, HEADERBUF_SIZE, fp)) - < HEADERBUF_SIZE) + if (gzseek(fp, 0, SEEK_SET) < 0) + fatal("seek error in %s: %s\n", filename, strerror(errno)); + len = gzread(fp, headerbuf, sizeof(headerbuf)); + if (len < sizeof(headerbuf)) { - if (feof(fp)) /* Short file */ + if (gzeof(fp)) /* Short file */ { - if (len < 12) { - printf("Fatal: %s is too short to be a multiboot file.", - filename); - exit(1); - } - } else { - printf("Fatal: read error in %s: %s\n", filename, strerror(errno)); - exit(1); - } + if (len < 12) + fatal("%s is too short to be a multiboot file.", filename); + } else + fatal("read error in %s: %s\n", filename, strerror(errno)); } +#ifndef HAVE_ZLIB /* Sanity-check: is this file compressed? */ if ((headerbuf[0] == '\037' && (headerbuf[1] == '\235' /* .Z */ || headerbuf[1] == '\213' /* .gz */)) || (headerbuf[0] == 'B' && headerbuf[1] == 'Z') /* .bz[2] */) { - printf("Warning: %s looks like a compressed file.\n" - " You should uncompress it first!\n", filename); + fprintf(stderr, + "Warning: %s looks like a compressed file.\n" + " You should uncompress it first!\n", + filename); } +#endif /* Now look for a multiboot header */ for (i = 0; i <= MIN(len - 12, MULTIBOOT_SEARCH - 12); i += 4) @@ -276,20 +285,16 @@ static address_t load_kernel(const char *filename) /* Not a multiboot header */ continue; } - if (mbh->flags & MULTIBOOT_UNSUPPORTED) { + if (mbh->flags & MULTIBOOT_UNSUPPORTED) /* Requires options we don't support */ - printf("Fatal: found a multiboot header, but it " - "requires multiboot options that I\n" - "don't understand. Sorry.\n"); - exit(1); - } + fatal("found a multiboot header, but it requires multiboot options that I don't\n" + "understand. Sorry."); if (mbh->flags & MULTIBOOT_VIDEO_MODE) { /* Asked for screen mode information */ /* XXX carry on regardless */ - printf("Warning: found a multiboot header which asks " - "for screen mode information.\n" - " This kernel will NOT be given valid" - "screen mode information at boot time.\n"); + fputs("Warning: found a multiboot header which asks for screen mode information.\n" + " This kernel will NOT be given valid screen mode information at boot\n" + " time.", stderr); } /* This kernel will do: place and load it */ @@ -312,37 +317,25 @@ static address_t load_kernel(const char *filename) else size = loadsize; - if (loadsize > size) { - printf("Fatal: can't load %i bytes of kernel into %i bytes " - "of memory.\n", loadsize, size); - exit(1); - } + if (loadsize > size) + fatal("can't load %ld bytes of kernel into %ld bytes of memory.\n", loadsize, size); /* Does it fit where it wants to be? */ place_kernel_section(start, size); /* Load the kernel */ - if ((buffer = malloc(size)) == NULL) { - printf("Fatal: malloc() for kernel load failed: %s\n", - strerror(errno)); - exit(1); - } - if ((fread(buffer, loadsize, 1, fp)) != 1) { - printf("Fatal: cannot read %s: %s\n", - filename, strerror(errno)); - exit(1); - } - fclose(fp); + if ((buffer = malloc(size)) == NULL) + fatal("malloc() for kernel load failed: %s\n", strerror(errno)); + if ((gzread(fp, buffer, loadsize)) != loadsize) + fatal("cannot read %s: %s\n", filename, strerror(errno)); + gzclose(fp); /* Clear the kernel BSS */ memset(buffer + loadsize, 0, size - loadsize); /* Start off the linked list of sections */ - if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) { - printf("Fatal: malloc() for section_t failed: %s\n", - strerror(errno)); - exit(1); - } + if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) + fatal("malloc() for section_t failed: %s\n", strerror(errno)); sec->buffer = buffer; sec->start = start; sec->size = size; @@ -363,25 +356,14 @@ static address_t load_kernel(const char *filename) || ehdr->e_ident[EI_DATA] != ELFDATA2LSB || ehdr->e_ident[EI_CLASS] != ELFCLASS32 || ehdr->e_machine != EM_386) - { - printf("Fatal: kernel has neither ELF32/x86 nor multiboot load" - " headers.\n"); - exit(1); - } - if (ehdr->e_phoff + ehdr->e_phnum*sizeof(*phdr) > HEADERBUF_SIZE) { + fatal("kernel has neither ELF32/x86 nor multiboot load headers."); + if (ehdr->e_phoff + ehdr->e_phnum*sizeof(*phdr) > HEADERBUF_SIZE) /* Don't expect this will happen with sane kernels */ - printf("Fatal: too much ELF for me. Try increasing " - "HEADERBUF_SIZE in mbootpack.\n"); - exit(1); - } - if (ehdr->e_phoff + ehdr->e_phnum*sizeof (*phdr) > len) { - printf("Fatal: malformed ELF header overruns EOF.\n"); - exit(1); - } - if (ehdr->e_phnum <= 0) { - printf("Fatal: ELF kernel has no program headers.\n"); - exit(1); - } + fatal("too much ELF for me. Try increasing HEADERBUF_SIZE in mbootpack."); + if (ehdr->e_phoff + ehdr->e_phnum*sizeof (*phdr) > len) + fatal("malformed ELF header overruns EOF."); + if (ehdr->e_phnum <= 0) + fatal("ELF kernel has no program headers."); if(!quiet) printf("Loading %s using ELF header.\n", filename); @@ -389,7 +371,7 @@ static address_t load_kernel(const char *filename) if (ehdr->e_type != ET_EXEC || ehdr->e_version != EV_CURRENT || ehdr->e_phentsize != sizeof (Elf32_Phdr)) { - printf("Warning: funny-looking ELF header.\n"); + fputs("Warning: funny-looking ELF header.\n", stderr); } phdr = (Elf32_Phdr *)(headerbuf + ehdr->e_phoff); @@ -407,38 +389,26 @@ static address_t load_kernel(const char *filename) * have one */ if (size == 0) continue; - if ((buffer = malloc(size)) == NULL) { - printf("Fatal: malloc() for kernel load failed: %s\n", - strerror(errno)); - exit(1); - } + if ((buffer = malloc(size)) == NULL) + fatal_err("malloc() for kernel load failed"); /* Place the section where it wants to be */ place_kernel_section(start, size); /* Load section from file */ if (loadsize > 0) { - if (fseek(fp, phdr[i].p_offset, SEEK_SET) != 0) { - printf("Fatal: seek failed in %s\n", - strerror(errno)); - exit(1); - } - if ((fread(buffer, loadsize, 1, fp)) != 1) { - printf("Fatal: cannot read %s: %s\n", - filename, strerror(errno)); - exit(1); - } + if (gzseek(fp, phdr[i].p_offset, SEEK_SET) < 0) + fatal("seek failed in %s\n", strerror(errno)); + if ((gzread(fp, buffer, loadsize)) != loadsize) + fatal_err("cannot read", filename); } /* Clear the rest of the buffer */ memset(buffer + loadsize, 0, size - loadsize); /* Add this section to the list (keeping it ordered) */ - if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) { - printf("Fatal: malloc() for section_t failed: %s\n", - strerror(errno)); - exit(1); - } + if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) + fatal_err("malloc() for section_t failed"); sec->buffer = buffer; sec->start = start; sec->size = size; @@ -480,8 +450,7 @@ static address_t load_kernel(const char *filename) } /* This is not a multiboot kernel */ - printf("Fatal: %s is not a multiboot kernel.\n", filename); - exit(1); + fatal("%s is not a multiboot kernel.\n", filename); } @@ -501,7 +470,7 @@ int main(int argc, char **argv) long int size, mod_command_line_space, command_line_len; int modules, opt, mbi_reloc_offset, truncate; - static const char short_options[] = "hc:m:o:qtM"; + static const char short_options[] = "hc:m:o:qtMV"; static const struct option options[] = { { "help", 0, 0, 'h' }, { "command-line", 1, 0, 'c' }, @@ -510,6 +479,9 @@ int main(int argc, char **argv) { "output", 1, 0, 'o' }, { "quiet", 0, 0, 'q' }, { "truncate", 0, 0, 't' }, +#ifdef VERSION + { "version", 0, 0, 'V' }, +#endif { 0, 0, 0, 0 }, }; @@ -541,14 +513,26 @@ int main(int argc, char **argv) case 't': truncate = 1; break; +#ifdef VERSION + case 'V': + puts("mbootpack " VERSION); + return 0; +#endif case 'h': case '?': + puts(usage); + return 0; default: - usage(); + fputs(usage, stderr); + return 1; } } imagename = argv[optind]; - if (!imagename || strlen(imagename) == 0) usage(); + if (!imagename || strlen(imagename) == 0) + { + fputs(usage, stderr); + return 1; + } command_line_len = strlen(command_line) + strlen(imagename) + 2; /* Leave space to overwrite the command-line at boot time */ command_line_len = MAX(command_line_len, CMD_LINE_SPACE); @@ -570,17 +554,11 @@ int main(int argc, char **argv) /* Locate this section after the setup sectors, in *low* memory */ start = place_mbi(size); - if ((buffer = malloc(size)) == NULL) { - printf("Fatal: malloc() for boot metadata failed: %s\n", - strerror(errno)); - exit(1); - } + if ((buffer = malloc(size)) == NULL) + fatal_err("malloc() for boot metadata failed"); - if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) { - printf("Fatal: malloc() for section_t failed: %s\n", - strerror(errno)); - exit(1); - } + if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) + fatal_err("malloc() for section_t failed"); sec->buffer = buffer; sec->start = start; sec->size = size; @@ -601,7 +579,7 @@ int main(int argc, char **argv) p += command_line_len; /* Bootloader ID */ - sprintf(p, version_string); + strcpy(p, version_string); mbi->boot_loader_name = ((address_t)p) + mbi_reloc_offset; p += strlen(version_string) + 1; @@ -638,32 +616,20 @@ int main(int argc, char **argv) } /* Find space for it */ - if (stat(mod_filename, &sb) != 0) { - printf("Fatal: cannot stat %s: %s\n", - mod_filename, strerror(errno)); - exit(1); - } + if (stat(mod_filename, &sb) != 0) + fatal_err("cannot stat", mod_filename); size = sb.st_size; start = place_section(size, X86_PAGE_SIZE); /* XXX should be place_section(size, 4) if the MBH hasn't got * XXX MULTIBOOT_PAGE_ALIGN set, but that breaks Xen */ /* Load it */ - if ((buffer = malloc(sb.st_size)) == NULL) { - printf("Fatal: malloc failed for module load: %s\n", - strerror(errno)); - exit(1); - } - if ((fp = fopen(mod_filename, "r")) == NULL) { - printf("Fatal: cannot open %s: %s\n", - mod_filename, strerror(errno)); - exit(1); - } - if ((fread(buffer, sb.st_size, 1, fp)) != 1) { - printf("Fatal: cannot read %s: %s\n", - mod_filename, strerror(errno)); - exit(1); - } + if ((buffer = malloc(sb.st_size)) == NULL) + fatal_err("malloc failed for module load"); + if ((fp = fopen(mod_filename, "r")) == NULL) + fatal_err("cannot open", mod_filename); + if ((fread(buffer, sb.st_size, 1, fp)) != 1) + fatal_err("cannot read", mod_filename); fclose(fp); /* Sanity-check: is this file compressed? */ @@ -671,8 +637,7 @@ int main(int argc, char **argv) (buffer[1] == '\235' /* .Z */ || buffer[1] == '\213' /* .gz */)) || (buffer[0] == 'B' && buffer[1] == 'Z') /* .bz[2] */) { - printf("Warning: %s looks like a compressed file.\n", - mod_filename); + fprintf(stderr, "Warning: %s looks like a compressed file.\n", mod_filename); } if (!quiet) printf("Loaded module from %s\n", mod_filename); @@ -690,15 +655,12 @@ int main(int argc, char **argv) /* Store the module command line */ if (truncate) mod_command_line = clip_path(mod_command_line); - sprintf(mod_clp, "%s", mod_command_line); + strcpy(mod_clp, mod_command_line); mod_clp += strlen(mod_clp) + 1; /* Add the section to the list */ - if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) { - printf("Fatal: malloc() for section_t failed: %s\n", - strerror(errno)); - exit(1); - } + if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) + fatal_err("malloc() for section_t failed"); sec->buffer = buffer; sec->start = start; sec->size = size; @@ -713,10 +675,8 @@ int main(int argc, char **argv) /* Everything is placed and loaded. Now we package it all up * as a bzImage */ - if ((fp = fopen(out_filename, "w")) == NULL) { - printf("Fatal: cannot open %s: %s\n", out_filename, strerror(errno)); - exit(1); - } + if ((fp = fopen(out_filename, "w")) == NULL) + fatal_err("cannot open", out_filename); make_bzImage(sections, kernel_entry, ((address_t)mbi) + mbi_reloc_offset, @@ -724,7 +684,8 @@ int main(int argc, char **argv) fclose(fp); /* Success! */ - if(!quiet) printf("Finished.\n"); + if(!quiet) + puts("Finished."); return 0; } diff --git a/mbootpack.h b/mbootpack.h index 361efaf..9f719b5 100644 --- a/mbootpack.h +++ b/mbootpack.h @@ -37,7 +37,7 @@ extern int quiet; /* Types */ -typedef uint32_t address_t; +typedef uintptr_t address_t; typedef struct section_t { char *buffer;