[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Caution: The following items are not part of the specification document, but are included for prospective operating system and boot loader writers.
4.1 Notes on PC | ||
4.2 BIOS device mapping techniques | ||
4.3 Example OS code | ||
4.4 Example boot loader code |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 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 (grub.info)Query System Address Map 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.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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.
4.2.1 Data comparison technique | ||
4.2.2 I/O restriction technique |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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:
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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:
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.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 (as.info)Top 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.
4.3.1 multiboot.h | ||
4.3.2 boot.S | ||
4.3.3 kernel.c | ||
4.3.4 Other Multiboot kernels |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 */ |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
In the file `boot.S':
/* 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 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
And, in the file `kernel.c':
/* 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++) 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; } } } } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Other useful information should be available in Multiboot kernels, such as GNU Mach and Fiasco http://os.inf.tu-dresden.de/fiasco/. And, it is worth mentioning the OSKit http://www.cs.utah.edu/projects/flux/oskit/, which provides a library supporting the specification.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The GNU GRUB (see (grub.info)Top 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:
See the webpage http://www.gnu.org/software/grub/grub.html, for more information.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by root on April, 6 2008 using texi2html 1.76.