Home | History | Annotate | Download | only in util
      1 /* mbchk - a simple checker for the format of a Multiboot kernel */
      2 /*
      3  *  Copyright (C) 1999,2001,2002  Free Software Foundation, Inc.
      4  *
      5  *  This program is free software; you can redistribute it and/or modify
      6  *  it under the terms of the GNU General Public License as published by
      7  *  the Free Software Foundation; either version 2 of the License, or
      8  *  (at your option) any later version.
      9  *
     10  *  This program is distributed in the hope that it will be useful,
     11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *  GNU General Public License for more details.
     14  *
     15  *  You should have received a copy of the GNU General Public License
     16  *  along with this program; if not, write to the Free Software
     17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 #include <config.h>
     21 
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <getopt.h>
     25 #include <multiboot.h>
     26 
     27 static int quiet = 0;
     28 static char *optstring = "hvq";
     29 static struct option longopts[] =
     30 {
     31   {"help", no_argument, 0, 'h'},
     32   {"version", no_argument, 0, 'v'},
     33   {"quiet", no_argument, 0, 'q'},
     34   {0}
     35 };
     36 
     37 static void
     38 usage (int status)
     39 {
     40   if (status)
     41     fprintf (stderr, "Try ``mbchk --help'' for more information.\n");
     42   else
     43     printf ("Usage: mbchk [OPTION]... [FILE]...\n"
     44 	    "Check if the format of FILE complies with the Multiboot Specification.\n"
     45 	    "\n"
     46 	    "-q, --quiet                suppress all normal output\n"
     47 	    "-h, --help                 display this help and exit\n"
     48 	    "-v, --version              output version information and exit.\n"
     49 	    "\n"
     50 	    "Report bugs to <bug-grub (at) gnu.org>.\n");
     51 
     52   exit (status);
     53 }
     54 
     55 static int
     56 check_multiboot (const char *filename, FILE *fp)
     57 {
     58   multiboot_header_t *mbh = 0;
     59   int i;
     60   char buf[8192];
     61 
     62   if (fread (buf, 1, 8192, fp) < 0)
     63     {
     64       fprintf (stderr, "%s: Read error.\n", filename);
     65       return 0;
     66     }
     67 
     68   for (i = 0; i < 8192 - sizeof (multiboot_header_t); i++)
     69     {
     70       unsigned long magic = *((unsigned long *) (buf + i));
     71 
     72       if (magic == MULTIBOOT_HEADER_MAGIC)
     73 	{
     74 	  mbh = (multiboot_header_t *) (buf + i);
     75 	  break;
     76 	}
     77     }
     78 
     79   if (! mbh)
     80     {
     81       fprintf (stderr, "%s: No Multiboot header.\n", filename);
     82       return 0;
     83     }
     84 
     85   if (! quiet)
     86     printf ("%s: The Multiboot header is found at the offset %d.\n",
     87 	    filename, i);
     88 
     89   /* Check for the checksum.  */
     90   if (mbh->magic + mbh->flags + mbh->checksum != 0)
     91     {
     92       fprintf (stderr,
     93 	       "%s: Bad checksum (0x%lx).\n",
     94 	       filename, mbh->checksum);
     95       return 0;
     96     }
     97 
     98   /* Reserved flags must be zero.  */
     99   if (mbh->flags & ~0x00010003)
    100     {
    101       fprintf (stderr,
    102 	       "%s: Non-zero is found in reserved flags (0x%lx).\n",
    103 	       filename, mbh->flags);
    104       return 0;
    105     }
    106 
    107   if (! quiet)
    108     {
    109       printf ("%s: Page alignment is turned %s.\n",
    110 	      filename, (mbh->flags & 0x1)? "on" : "off");
    111       printf ("%s: Memory information is turned %s.\n",
    112 	      filename, (mbh->flags & 0x2)? "on" : "off");
    113       printf ("%s: Address fields is turned %s.\n",
    114 	      filename, (mbh->flags & 0x10000)? "on" : "off");
    115     }
    116 
    117   /* Check for the address fields.  */
    118   if (mbh->flags & 0x10000)
    119     {
    120       if (mbh->header_addr < mbh->load_addr)
    121 	{
    122 	  fprintf (stderr,
    123 		   "%s: header_addr is less than "
    124 		   "load_addr (0x%lx > 0x%lx).\n",
    125 		   filename, mbh->header_addr, mbh->load_addr);
    126 	  return 0;
    127 	}
    128 
    129       if (mbh->load_end_addr && mbh->load_addr >= mbh->load_end_addr)
    130 	{
    131 	  fprintf (stderr,
    132 		   "%s: load_addr is not less than load_end_addr"
    133 		   " (0x%lx >= 0x%lx).\n",
    134 		   filename, mbh->load_addr, mbh->load_end_addr);
    135 	  return 0;
    136 	}
    137 
    138       if (mbh->bss_end_addr && mbh->load_end_addr > mbh->bss_end_addr)
    139 	{
    140 	  fprintf (stderr,
    141 		   "%s: load_end_addr is greater than bss_end_addr"
    142 		   " (0x%lx > 0x%lx).\n",
    143 		   filename, mbh->load_end_addr, mbh->bss_end_addr);
    144 	  return 0;
    145 	}
    146 
    147       if (mbh->load_addr > mbh->entry_addr)
    148 	{
    149 	  fprintf (stderr,
    150 		   "%s: load_addr is greater than entry_addr"
    151 		   " (0x%lx > 0x%lx).\n",
    152 		   filename, mbh->load_addr, mbh->entry_addr);
    153 	  return 0;
    154 	}
    155 
    156       /* FIXME: It is better to check if the entry address is within the
    157 	 file, especially when the load end address is zero.  */
    158       if (mbh->load_end_addr && mbh->load_end_addr <= mbh->entry_addr)
    159 	{
    160 	  fprintf (stderr,
    161 		   "%s: load_end_addr is not greater than entry_addr"
    162 		   " (0x%lx <= 0x%lx).\n",
    163 		   filename, mbh->load_end_addr, mbh->entry_addr);
    164 	  return 0;
    165 	}
    166 
    167       /* This is a GRUB-specific limitation.  */
    168       if (mbh->load_addr < 0x100000)
    169 	{
    170 	  fprintf (stderr,
    171 		   "%s: Cannot be loaded at less than 1MB by GRUB"
    172 		   " (0x%lx).\n",
    173 		   filename, mbh->load_addr);
    174 	  return 0;
    175 	}
    176     }
    177 
    178   if (! quiet)
    179     printf ("%s: All checks passed.\n", filename);
    180 
    181   return 1;
    182 }
    183 
    184 int
    185 main (int argc, char *argv[])
    186 {
    187   int c;
    188 
    189   do
    190     {
    191       c = getopt_long (argc, argv, optstring, longopts, 0);
    192       switch (c)
    193 	{
    194 	case EOF:
    195 	  break;
    196 
    197 	case 'h':
    198 	  usage (0);
    199 	  break;
    200 
    201 	case 'v':
    202 	  printf ("mbchk (GNU GRUB " VERSION ")\n");
    203 	  exit (0);
    204 	  break;
    205 
    206 	case 'q':
    207 	  quiet = 1;
    208 	  break;
    209 
    210 	default:
    211 	  usage (1);
    212 	  break;
    213 	}
    214     }
    215   while (c != EOF);
    216 
    217   if (optind < argc)
    218     {
    219       while (optind < argc)
    220 	{
    221 	  FILE *fp;
    222 
    223 	  fp = fopen (argv[optind], "r");
    224 	  if (! fp)
    225 	    {
    226 	      fprintf (stderr, "%s: No such file.\n", argv[optind]);
    227 	      exit (1);
    228 	    }
    229 
    230 	  if (! check_multiboot (argv[optind], fp))
    231 	    exit (1);
    232 
    233 	  fclose (fp);
    234 	  optind++;
    235 	}
    236     }
    237   else
    238     {
    239       if (! check_multiboot ("<stdin>", stdin))
    240 	exit (1);
    241     }
    242 
    243   return 0;
    244 }
    245