In reference to bit 0 of the `flags' parameter in the Multiboot
information structure, if the bootloader in question uses older
BIOS interfaces, or the newest ones are not available (see
description about bit 6), then a maximum of either 15 or 63 megabytes of
memory may be reported. It is highly recommended that boot
loaders perform a thorough memory probe.
In reference to bit 1 of the `flags' parameter in the Multiboot
information structure, it is recognized that determination of which
BIOS drive maps to which device driver in an operating system is
non-trivial, at best. Many kludges have been made to various operating
systems instead of solving this problem, most of them breaking under
many conditions. To encourage the use of general-purpose solutions to
this problem, there are 2 BIOS device mapping techniques
(see section 4.2 BIOS device mapping techniques).
In reference to bit 6 of the `flags' parameter in the Multiboot
information structure, it is important to note that the data structure
used there (starting with `BaseAddrLow') is the data returned by
the INT 15h, AX=E820h -- Query System Address Map call. See See section `Query System Address Map' in The GRUB Manual, for more information. The interface here is meant to allow a
boot loader to work unmodified with any reasonable extensions of the
BIOS interface, passing along any extra data to be interpreted by
the operating system as desired.
Both of these techniques should be usable from any PC operating system,
and neither require any special support in the drivers themselves. This
section will be flushed out into detailed explanations, particularly for
the I/O restriction technique.
The general rule is that the data comparison technique is the quick and
dirty solution. It works most of the time, but doesn't cover all the
bases, and is relatively simple.
The I/O restriction technique is much more complex, but it has potential
to solve the problem under all conditions, plus allow access of the
remaining BIOS devices when not all of them have operating system
drivers.
Before activating any of the device drivers, gather enough data
from similar sectors on each of the disks such that each one can be
uniquely identified.
After activating the device drivers, compare data from the drives using
the operating system drivers. This should hopefully be sufficient to
provide such a mapping.
Problems:
The data on some BIOS devices might be identical (so the part
reading the drives from the BIOS should have some mechanism to give
up).
There might be extra drives not accessible from the BIOS which are
identical to some drive used by the BIOS (so it should be capable
of giving up there as well).
This first step may be unnecessary, but first create copy-on-write
mappings for the device drivers writing into PC RAM. Keep the
original copies for the clean BIOS virtual machine to be
created later.
For each device driver brought online, determine which BIOS devices
become inaccessible by:
Create a clean BIOS virtual machine.
Set the I/O permission map for the I/O area claimed by the device driver
to no permissions (neither read nor write).
Access each device.
Record which devices succeed, and those which try to access the
restricted I/O areas (hopefully, this will be an xor
situation).
For each device driver, given how many of the BIOS devices were
subsumed by it (there should be no gaps in this list), it should be easy
to determine which devices on the controller these are.
In general, you have at most 2 disks from each controller given
BIOS numbers, but they pretty much always count from the lowest
logically numbered devices on the controller.
In this distribution, the example Multiboot kernel `kernel' is
included. The kernel just prints out the Multiboot information structure
on the screen, so you can make use of the kernel to test a
Multiboot-compliant boot loader and for reference to how to implement a
Multiboot kernel. The source files can be found under the directory
`docs' in the GRUB distribution.
The kernel `kernel' consists of only three files: `boot.S',
`kernel.c' and `multiboot.h'. The assembly source
`boot.S' is written in GAS (see section `GNU assembler' in The GNU assembler), and contains the Multiboot information structure to
comply with the specification. When a Multiboot-compliant boot loader
loads and execute it, it initialize the stack pointer and EFLAGS,
and then call the function cmain defined in `kernel.c'. If
cmain returns to the callee, then it shows a message to inform
the user of the halt state and stops forever until you push the reset
key. The file `kernel.c' contains the function cmain,
which checks if the magic number passed by the boot loader is valid and
so on, and some functions to print messages on the screen. The file
`multiboot.h' defines some macros, such as the magic number for the
Multiboot header, the Multiboot header structure and the Multiboot
information structure.
This is the source code in the file `multiboot.h':
/* multiboot.h - the header for Multiboot */
/* Copyright (C) 1999, 2001 Free Software Foundation, Inc.
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
(at your option) 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. */
/* Macros. */
/* The magic number for the Multiboot header. */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
/* The flags for the Multiboot header. */
#ifdef __ELF__
# define MULTIBOOT_HEADER_FLAGS 0x00000003
#else
# define MULTIBOOT_HEADER_FLAGS 0x00010003
#endif
/* The magic number passed by a Multiboot-compliant boot loader. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
/* The size of our stack (16KB). */
#define STACK_SIZE 0x4000
/* C symbol format. HAVE_ASM_USCORE is defined by configure. */
#ifdef HAVE_ASM_USCORE
# define EXT_C(sym) _ ## sym
#else
# define EXT_C(sym) sym
#endif
#ifndef ASM
/* Do not include here in boot.S. */
/* Types. */
/* The Multiboot header. */
typedef struct multiboot_header
{
unsigned long magic;
unsigned long flags;
unsigned long checksum;
unsigned long header_addr;
unsigned long load_addr;
unsigned long load_end_addr;
unsigned long bss_end_addr;
unsigned long entry_addr;
} multiboot_header_t;
/* The symbol table for a.out. */
typedef struct aout_symbol_table
{
unsigned long tabsize;
unsigned long strsize;
unsigned long addr;
unsigned long reserved;
} aout_symbol_table_t;
/* The section header table for ELF. */
typedef struct elf_section_header_table
{
unsigned long num;
unsigned long size;
unsigned long addr;
unsigned long shndx;
} elf_section_header_table_t;
/* The Multiboot information. */
typedef struct multiboot_info
{
unsigned long flags;
unsigned long mem_lower;
unsigned long mem_upper;
unsigned long boot_device;
unsigned long cmdline;
unsigned long mods_count;
unsigned long mods_addr;
union
{
aout_symbol_table_t aout_sym;
elf_section_header_table_t elf_sec;
} u;
unsigned long mmap_length;
unsigned long mmap_addr;
} multiboot_info_t;
/* The module structure. */
typedef struct module
{
unsigned long mod_start;
unsigned long mod_end;
unsigned long string;
unsigned long reserved;
} module_t;
/* The memory map. Be careful that the offset 0 is base_addr_low
but no size. */
typedef struct memory_map
{
unsigned long size;
unsigned long base_addr_low;
unsigned long base_addr_high;
unsigned long length_low;
unsigned long length_high;
unsigned long type;
} memory_map_t;
#endif /* ! ASM */
/* boot.S - bootstrap the kernel */
/* Copyright (C) 1999, 2001 Free Software Foundation, Inc.
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
(at your option) 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. */
#define ASM 1
#include <multiboot.h>
.text
.globl start, _start
start:
_start:
jmp multiboot_entry
/* Align 32 bits boundary. */
.align 4
/* Multiboot header. */
multiboot_header:
/* magic */
.long MULTIBOOT_HEADER_MAGIC
/* flags */
.long MULTIBOOT_HEADER_FLAGS
/* checksum */
.long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
#ifndef __ELF__
/* header_addr */
.long multiboot_header
/* load_addr */
.long _start
/* load_end_addr */
.long _edata
/* bss_end_addr */
.long _end
/* entry_addr */
.long multiboot_entry
#endif /* ! __ELF__ */
multiboot_entry:
/* Initialize the stack pointer. */
movl $(stack + STACK_SIZE), %esp
/* Reset EFLAGS. */
pushl $0
popf
/* Push the pointer to the Multiboot information structure. */
pushl %ebx
/* Push the magic value. */
pushl %eax
/* Now enter the C main function... */
call EXT_C(cmain)
/* Halt. */
pushl $halt_message
call EXT_C(printf)
loop: hlt
jmp loop
halt_message:
.asciz "Halted."
/* Our stack area. */
.comm stack, STACK_SIZE
/* kernel.c - the C part of the kernel */
/* Copyright (C) 1999 Free Software Foundation, Inc.
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
(at your option) 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. */
#include <multiboot.h>
/* Macros. */
/* Check if the bit BIT in FLAGS is set. */
#define CHECK_FLAG(flags,bit) ((flags) & (1 << (bit)))
/* Some screen stuff. */
/* The number of columns. */
#define COLUMNS 80
/* The number of lines. */
#define LINES 24
/* The attribute of an character. */
#define ATTRIBUTE 7
/* The video memory address. */
#define VIDEO 0xB8000
/* Variables. */
/* Save the X position. */
static int xpos;
/* Save the Y position. */
static int ypos;
/* Point to the video memory. */
static volatile unsigned char *video;
/* Forward declarations. */
void cmain (unsigned long magic, unsigned long addr);
static void cls (void);
static void itoa (char *buf, int base, int d);
static void putchar (int c);
void printf (const char *format, ...);
/* Check if MAGIC is valid and print the Multiboot information structure
pointed by ADDR. */
void
cmain (unsigned long magic, unsigned long addr)
{
multiboot_info_t *mbi;
/* Clear the screen. */
cls ();
/* Am I booted by a Multiboot-compliant boot loader? */
if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
{
printf ("Invalid magic number: 0x%x\n", (unsigned) magic);
return;
}
/* Set MBI to the address of the Multiboot information structure. */
mbi = (multiboot_info_t *) addr;
/* Print out the flags. */
printf ("flags = 0x%x\n", (unsigned) mbi->flags);
/* Are mem_* valid? */
if (CHECK_FLAG (mbi->flags, 0))
printf ("mem_lower = %uKB, mem_upper = %uKB\n",
(unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);
/* Is boot_device valid? */
if (CHECK_FLAG (mbi->flags, 1))
printf ("boot_device = 0x%x\n", (unsigned) mbi->boot_device);
/* Is the command line passed? */
if (CHECK_FLAG (mbi->flags, 2))
printf ("cmdline = %s\n", (char *) mbi->cmdline);
/* Are mods_* valid? */
if (CHECK_FLAG (mbi->flags, 3))
{
module_t *mod;
int i;
printf ("mods_count = %d, mods_addr = 0x%x\n",
(int) mbi->mods_count, (int) mbi->mods_addr);
for (i = 0, mod = (module_t *) mbi->mods_addr;
i < mbi->mods_count;
i++, mod += sizeof (module_t))
printf (" mod_start = 0x%x, mod_end = 0x%x, string = %s\n",
(unsigned) mod->mod_start,
(unsigned) mod->mod_end,
(char *) mod->string);
}
/* Bits 4 and 5 are mutually exclusive! */
if (CHECK_FLAG (mbi->flags, 4) && CHECK_FLAG (mbi->flags, 5))
{
printf ("Both bits 4 and 5 are set.\n");
return;
}
/* Is the symbol table of a.out valid? */
if (CHECK_FLAG (mbi->flags, 4))
{
aout_symbol_table_t *aout_sym = &(mbi->u.aout_sym);
printf ("aout_symbol_table: tabsize = 0x%0x, "
"strsize = 0x%x, addr = 0x%x\n",
(unsigned) aout_sym->tabsize,
(unsigned) aout_sym->strsize,
(unsigned) aout_sym->addr);
}
/* Is the section header table of ELF valid? */
if (CHECK_FLAG (mbi->flags, 5))
{
elf_section_header_table_t *elf_sec = &(mbi->u.elf_sec);
printf ("elf_sec: num = %u, size = 0x%x,"
" addr = 0x%x, shndx = 0x%x\n",
(unsigned) elf_sec->num, (unsigned) elf_sec->size,
(unsigned) elf_sec->addr, (unsigned) elf_sec->shndx);
}
/* Are mmap_* valid? */
if (CHECK_FLAG (mbi->flags, 6))
{
memory_map_t *mmap;
printf ("mmap_addr = 0x%x, mmap_length = 0x%x\n",
(unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
for (mmap = (memory_map_t *) mbi->mmap_addr;
(unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
mmap = (memory_map_t *) ((unsigned long) mmap
+ mmap->size + sizeof (mmap->size)))
printf (" size = 0x%x, base_addr = 0x%x%x,"
" length = 0x%x%x, type = 0x%x\n",
(unsigned) mmap->size,
(unsigned) mmap->base_addr_high,
(unsigned) mmap->base_addr_low,
(unsigned) mmap->length_high,
(unsigned) mmap->length_low,
(unsigned) mmap->type);
}
}
/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
static void
cls (void)
{
int i;
video = (unsigned char *) VIDEO;
for (i = 0; i < COLUMNS * LINES * 2; i++)
*(video + i) = 0;
xpos = 0;
ypos = 0;
}
/* Convert the integer D to a string and save the string in BUF. If
BASE is equal to 'd', interpret that D is decimal, and if BASE is
equal to 'x', interpret that D is hexadecimal. */
static void
itoa (char *buf, int base, int d)
{
char *p = buf;
char *p1, *p2;
unsigned long ud = d;
int divisor = 10;
/* If %d is specified and D is minus, put `-' in the head. */
if (base == 'd' && d < 0)
{
*p++ = '-';
buf++;
ud = -d;
}
else if (base == 'x')
divisor = 16;
/* Divide UD by DIVISOR until UD == 0. */
do
{
int remainder = ud % divisor;
*p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
}
while (ud /= divisor);
/* Terminate BUF. */
*p = 0;
/* Reverse BUF. */
p1 = buf;
p2 = p - 1;
while (p1 < p2)
{
char tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2--;
}
}
/* Put the character C on the screen. */
static void
putchar (int c)
{
if (c == '\n' || c == '\r')
{
newline:
xpos = 0;
ypos++;
if (ypos >= LINES)
ypos = 0;
return;
}
*(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
*(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;
xpos++;
if (xpos >= COLUMNS)
goto newline;
}
/* Format a string and print it on the screen, just like the libc
function printf. */
void
printf (const char *format, ...)
{
char **arg = (char **) &format;
int c;
char buf[20];
arg++;
while ((c = *format++) != 0)
{
if (c != '%')
putchar (c);
else
{
char *p;
c = *format++;
switch (c)
{
case 'd':
case 'u':
case 'x':
itoa (buf, c, *((int *) arg++));
p = buf;
goto string;
break;
case 's':
p = *arg++;
if (! p)
p = "(null)";
string:
while (*p)
putchar (*p++);
break;
default:
putchar (*((int *) arg++));
break;
}
}
}
}
The GNU GRUB (see section `GRUB' in The GRUB manual) project
is a full Multiboot-compliant boot loader, supporting all required and
optional features present in this specification. A public release has
not been made, but the test release is available from: