=== modified file 'conf/common.rmk' --- conf/common.rmk 2010-07-06 18:27:55 +0000 +++ conf/common.rmk 2010-08-10 14:06:23 +0000 @@ -165,6 +165,11 @@ grub_editenv_SOURCES = gnulib/progname.c util/grub-editenv.c lib/envblk.c util/misc.c kern/emu/misc.c kern/emu/mm.c kern/misc.c kern/err.c CLEANFILES += grub-editenv +# for grub-mbchk +bin_UTILITIES += grub-mbchk +grub_mbchk_SOURCES = gnulib/progname.c util/grub-mbchk.c kern/emu/misc.c util/misc.c +CLEANFILES += grub-mbchk + # Needed for genmk.rb to work ifeq (0,1) bin_UTILITIES += grub-macho2img grub-pe2elf === added file 'docs/man/grub-mbchk.h2m' --- docs/man/grub-mbchk.h2m 1970-01-01 00:00:00 +0000 +++ docs/man/grub-mbchk.h2m 2010-08-10 13:31:55 +0000 @@ -0,0 +1,2 @@ +[NAME] +grub-mbchk \- check the format of a Multiboot kernel === added file 'util/grub-mbchk.c' --- util/grub-mbchk.c 1970-01-01 00:00:00 +0000 +++ util/grub-mbchk.c 2010-08-10 15:49:40 +0000 @@ -0,0 +1,260 @@ +/* grub-mbchk.c - a simple checker for the format of a Multiboot kernel */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2001,2002,2010 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "progname.h" + +static int quiet = 0; +static char *optstring = "hvq"; +static struct option options[] = +{ + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"quiet", no_argument, 0, 'q'}, + {0, 0, 0, 0} +}; + +static void +usage (int status) +{ + if (status) + fprintf (stderr, "Try ``%s --help'' for more information.\n", program_name); + else + printf ("\ +Usage: %s [OPTION]... [FILENAME]...\n\ +Check if the format of FILENAME complies with the Multiboot Specification.\n\ +\n\ +\nOptions:\n\ + -q, --quiet suppress all normal output\n\ + -h, --help display this help and exit\n\ + -V, --version output version information and exit.\n\ +\n\ +If not given explicitly, FILENAME defaults to standard input.\n\ +\n\ +Report bugs to <%s>.\n", +program_name, PACKAGE_BUGREPORT); + exit (status); +} + +static int +check_multiboot (const char *filename, FILE *fp) +{ + struct multiboot_header *mbh = 0; + unsigned int i; + char buf[8192]; + + memset(buf, 0, 8192); + + if (fread (buf, 1, 8192, fp) == 0) + { + fprintf (stderr, "%s: Read error.\n", filename); + return 0; + } + + for (i = 0; i < 8192 - sizeof (struct multiboot_header); i++) + { + multiboot_uint32_t magic = *((multiboot_uint32_t *) (buf + i)); + + if (magic == MULTIBOOT_HEADER_MAGIC) + { + mbh = (struct multiboot_header *) (buf + i); + break; + } + } + + if (! mbh) + { + fprintf (stderr, "%s: No Multiboot header.\n", filename); + return 0; + } + + if (! quiet) + printf ("%s: The Multiboot header is found at the offset %d.\n", + filename, i); + + /* Check for the checksum. */ + if (mbh->magic + mbh->flags + mbh->checksum != 0) + { + fprintf (stderr, + "%s: Bad checksum (0x%x).\n", + filename, mbh->checksum); + return 0; + } + + /* Reserved flags must be zero. */ + if (mbh->flags & ~0x00010003) + { + fprintf (stderr, + "%s: Non-zero is found in reserved flags (0x%x).\n", + filename, mbh->flags); + return 0; + } + + if (! quiet) + { + printf ("%s: Page alignment is turned %s.\n", + filename, (mbh->flags & 0x1)? "on" : "off"); + printf ("%s: Memory information is turned %s.\n", + filename, (mbh->flags & 0x2)? "on" : "off"); + printf ("%s: Address fields is turned %s.\n", + filename, (mbh->flags & 0x10000)? "on" : "off"); + } + + /* Check for the address fields. */ + if (mbh->flags & 0x10000) + { + if (mbh->header_addr < mbh->load_addr) + { + fprintf (stderr, + "%s: header_addr is less than " + "load_addr (0x%x > 0x%x).\n", + filename, mbh->header_addr, mbh->load_addr); + return 0; + } + + if (mbh->load_end_addr && mbh->load_addr >= mbh->load_end_addr) + { + fprintf (stderr, + "%s: load_addr is not less than load_end_addr" + " (0x%x >= 0x%x).\n", + filename, mbh->load_addr, mbh->load_end_addr); + return 0; + } + + if (mbh->bss_end_addr && mbh->load_end_addr > mbh->bss_end_addr) + { + fprintf (stderr, + "%s: load_end_addr is greater than bss_end_addr" + " (0x%x > 0x%x).\n", + filename, mbh->load_end_addr, mbh->bss_end_addr); + return 0; + } + + if (mbh->load_addr > mbh->entry_addr) + { + fprintf (stderr, + "%s: load_addr is greater than entry_addr" + " (0x%x > 0x%x).\n", + filename, mbh->load_addr, mbh->entry_addr); + return 0; + } + + /* FIXME: It is better to check if the entry address is within the + file, especially when the load end address is zero. */ + if (mbh->load_end_addr && mbh->load_end_addr <= mbh->entry_addr) + { + fprintf (stderr, + "%s: load_end_addr is not greater than entry_addr" + " (0x%x <= 0x%x).\n", + filename, mbh->load_end_addr, mbh->entry_addr); + return 0; + } + + /* This is a GRUB-specific limitation. */ + if (mbh->load_addr < 0x100000) + { + fprintf (stderr, + "%s: Cannot be loaded at less than 1MB by GRUB" + " (0x%x).\n", + filename, mbh->load_addr); + return 0; + } + } + + if (! quiet) + printf ("%s: All checks passed.\n", filename); + + return 1; +} + +int +main (int argc, char *argv[]) +{ + int c; + + set_program_name (argv[0]); + + grub_util_init_nls (); + + do + { + c = getopt_long (argc, argv, optstring, options, 0); + switch (c) + { + case EOF: + break; + + case 'h': + usage (0); + break; + + case 'V': + printf ("%s (GNU GRUB " VERSION ")\n", program_name); + exit (0); + break; + + case 'q': + quiet = 1; + break; + + default: + usage (1); + break; + } + } + while (c != EOF); + + if (optind < argc) + { + while (optind < argc) + { + FILE *fp; + + fp = fopen (argv[optind], "r"); + if (! fp) + { + fprintf (stderr, "%s: No such file.\n", argv[optind]); + exit (1); + } + + if (! check_multiboot (argv[optind], fp)) + exit (1); + + fclose (fp); + optind++; + } + } + else + { + if (! check_multiboot ("", stdin)) + exit (1); + } + return 0; +}