警告:以下内容不是该规范文档的一部分,但为了未来的操作系统及启动载入器作者,而包括进来。
考虑在Multiboot信息结构体中‘flags’参数的第0位,如果讨论中的启动载入器使用旧的BIOS接口,或最新的不可用(参考关于第6位的描述),那么可能报告一个15或63M的内存最大值。强烈建议启动载入器执行一个详细的内存探测。
至于在Multiboot信息结构体中‘flags’参数的第1位,它顶多被识别为,在一个操作系统上,确定哪个BIOS设备映射到哪个设备驱动不是无关紧要的。对各种操作系统,许多拼凑的处理已经被使用,而不是解决这个问题,它们中的绝大多数在许多条件下会崩溃。为了鼓励这个问题的通用的解决方案,有两种BIOS设备映射技术(参考4.2节【BIOS设备映射技术】,12页)。
考虑在Multiboot信息结构体中‘flags’参数的第6位,重要的是注意到在这里使用的数据结构(由‘BaseAddrLow’开始)是由INT 15h, AX=E820h — 查询系统地址映射(Query System Address Map)调用返回的数据。更多信息参考GRUB手册的“查询系统地址映射”一节。这里的接口是为了允许一个启动载入器,不需要修改就可以与BIOS接口的合理扩展一起工作,根据需要传递任何由操作系统解释的额外数据。
这两个技术应该可以用于任何PC操作系统,而且都不要求驱动本身提供特殊支持。本节将被展开为详细的解释,特别对于I/O限制技术(restriction technique)。
通用的规则是,数据比较技术是快速、肮脏的解决方案。在绝大多数时间,它能够工作,但不能覆盖所有的基点,而且相对简单。
I/O限制技术要复杂得多,但它有在所有条件下解决这个问题的潜力,再加上,当剩下的BIOS设备不都是有操作系统驱动时,也允许访问它们。
在激活任意设备驱动之前,在可以被唯一识别的每个硬盘上,从相似的扇区收集足够数据。
在激活该设备驱动之后,比较来自使用操作系统驱动的设备的数据。有希望足够提供这样的一个映射。
问题:
1. 某些BIOS设备上的数据可能是相同的(因此从BIOS读入驱动的部分应该有放弃的机制)。
2. 可能有从BIOS无法访问的额外设备,它与该BIOS使用的某些设备相同(因此也应该能在这种情形下放弃)。
这个第一步可能不是必要的,但首先为写入PC ram的设备驱动构建写时拷贝(copy-on-write)映射。为随后构建的BIOS虚拟机保留原始拷贝。
对于每个在线的设备驱动,确定哪个BIOS设备变成不可访问,通过:
1. 构建一个干净的BIOS虚拟机。
2. 把由这个设备驱动所要求的I/O区域的I/O许可映射设置为不许可(不能读写)。
3. 访问每个设备
4. 记录哪个设备成功了,哪些尝试访问受限I/O区域(足够幸运的话,这将是一个xor的情形)。
对于每个设备驱动, 给定由它归纳的BIOS设备(在这个列表中不应该有缺失),应该很容易确定在控制器上有哪些设备。
通常,给定BIOS号,从每个控制器你至少有2个硬盘,它们几乎总是从控制器上逻辑号最低的设备算起。
在这个发布版本中,包括了Multiboot内核‘kernel’。这个内核只是在屏幕上打印出Multiboot信息结构体,因此你可以使用这个内核来测试一个Multiboot兼容的驱动载入器,并作为如何实现一个Multiboot内核的参考。在Multiboot源代码发布版的‘doc’目录下可以找到源代码。
内核‘kernel’仅包含了3个文件:‘boot.S’,‘kernel.c’及‘multiboot.h’。汇编源代码‘boot.S’以GAS(参考在The GNU assembler中的“GNU assembler”)写就,并包含了Multiboot信息结构体来遵循规范。当一个Multiboot兼容的驱动载入器载入并执行它时,它初始化栈指针及EFLAGS,然后调用在‘kernel.c’中定义的函数cmain。如果cmain返回到这个被调用者,它显示一个消息来通知用户它的停止状态,并永久停止,直到你按下reset键。文件‘kernel.c’包含函数cmain,它检查由启动载入器传入的魔数是否有效等等,及某些在屏幕上打印消息的函数。文件‘multiboot.h’定义了一些宏,比如用于Multiboot头的魔数,Multiboot头结构体及Multiboot信息结构体。
这是在‘multiboot.h’中的源代码:
/*multiboot.h - Multiboot header file. */
/*Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc.
*
*Permission is hereby granted, free of charge, to any person obtaining a copy
* ofthis software and associated documentation files (the "Software"), to
*deal in the Software without restriction, including without limitation the
*rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
*sell copies of the Software, and to permit persons to whom the Software is
*furnished to do so, subject to the following conditions:
*
*The above copyright notice and this permission notice shall be included in
*all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUTWARRANTY OFANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THEWARRANTIES OFMERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NOEVENT SHALL ANY
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OROTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/*How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 8192
/*The magic field should contain this. */
#define MULTIBOOT_HEADER_MAGIC0x1BADB002
/*This should be in %eax. */
#define MULTIBOOT_BOOTLOADER_MAGIC0x2BADB002
/*The bits in the required part of flags field we don’t support. */
#define MULTIBOOT_UNSUPPORTED0x0000fffc
/*Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/*Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN0x00000004
/* Flagsset in the ‘flags’ member of the multiboot header. */
/*Align all boot modules on i386 page (4KB) boundaries. */
#define MULTIBOOT_PAGE_ALIGN0x00000001
/*Must pass memory information to OS. */
#define MULTIBOOT_MEMORY_INFO0x00000002
/*Must pass video information to OS. */
#define MULTIBOOT_VIDEO_MODE0x00000004
/*This flag indicates the use of the address fields in the header. */
#define MULTIBOOT_AOUT_KLUDGE0x00010000
/*Flags to be set in the ‘flags’ member of the multiboot info structure. */
/*is there basic lower/upper memory information? */
#define MULTIBOOT_INFO_MEMORY0x00000001
/*is there a boot device set? */
#define MULTIBOOT_INFO_BOOTDEV0x00000002
/*is the command-line defined? */
#define MULTIBOOT_INFO_CMDLINE0x00000004
/*are there modules to do something with? */
#define MULTIBOOT_INFO_MODS 0x00000008
/*These next two are mutually exclusive */
/*is there a symbol table loaded? */
#define MULTIBOOT_INFO_AOUT_SYMS0x00000010
/*is there an ELF section header table? */
#define MULTIBOOT_INFO_ELF_SHDR0X00000020
/*is there a full memory map? */
#define MULTIBOOT_INFO_MEM_MAP0x00000040
/*Is there drive info? */
#define MULTIBOOT_INFO_DRIVE_INFO0x00000080
/*Is there a config table? */
#define MULTIBOOT_INFO_CONFIG_TABLE0x00000100
/*Is there a boot loader name? */
#defineMULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
/*Is there a APM table? */
#define MULTIBOOT_INFO_APM_TABLE0x00000400
/*Is there video information? */
#define MULTIBOOT_INFO_VIDEO_INFO0x00000800
#ifndef ASM_FILE
typedef unsigned shortmultiboot_uint16_t;
typedef unsigned intmultiboot_uint32_t;
typedef unsigned long longmultiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT MAGIC - see above. */
multiboot_uint32_t magic;
/* Feature flags. */
multiboot_uint32_t flags;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
/* These are only valid if MULTIBOOT AOUT KLUDGE is set. */
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
multiboot_uint32_t entry_addr;
/* These are only valid if MULTIBOOT VIDEO MODE is set. */
multiboot_uint32_t mode_type;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
/*The symbol table for a.out. */
struct multiboot_aout_symbol_table
{
multiboot_uint32_t tabsize;
multiboot_uint32_t strsize;
multiboot_uint32_t addr;
multiboot_uint32_t reserved;
};
typedefstructmultiboot_aout_symbol_table multiboot_aout_symbol_table_t;
/*The section header table for ELF. */
structmultiboot_elf_section_header_table
{
multiboot_uint32_t num;
multiboot_uint32_t size;
multiboot_uint32_t addr;
multiboot_uint32_t shndx;
};
typedefstructmultiboot_elf_section_header_table multiboot_elf_section_header_table_t;
struct multiboot_info
{
/* Multiboot info version number */
multiboot_uint32_t flags;
/* Available memory from BIOS */
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
/* "root" partition */
multiboot_uint32_t boot_device;
/* Kernel command line */
multiboot_uint32_t cmdline;
/* Boot-Module list */
multiboot_uint32_t mods_count;
multiboot_uint32_t mods_addr;
union
{
multiboot_aout_symbol_table_t aout_sym;
multiboot_elf_section_header_table_telf_sec;
} u;
/* Memory Mapping buffer */
multiboot_uint32_t mmap_length;
multiboot_uint32_t mmap_addr;
/* Drive Info buffer */
multiboot_uint32_t drives_length;
multiboot_uint32_t drives_addr;
/* ROM configuration table */
multiboot_uint32_t config_table;
/* Boot Loader Name */
multiboot_uint32_t boot_loader_name;
/* APM table */
multiboot_uint32_t apm_table;
/* Video */
multiboot_uint32_t vbe_control_info;
multiboot_uint32_t vbe_mode_info;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
};
typedefstructmultiboot_info multiboot_info_t;
struct multiboot_mmap_entry
{
multiboot_uint32_t size;
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
multiboot_uint32_t type;
}__attribute__((packed));
typedefstructmultiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_mod_list
{
/* the memory used goes from bytes ‘mod start’to ‘mod end-1’ inclusive*/
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
/* Module command line */
multiboot_uint32_t cmdline;
/* padding to take it to 16 bytes (must bezero) */
multiboot_uint32_t pad;
};
typedefstructmultiboot_mod_list multiboot_module_t;
#endif /* ! ASM FILE */
#endif /* ! MULTIBOOT HEADER */
在文件‘boot.S’中:
/*boot.S - bootstrap the kernel */
/*Copyright (C) 1999, 2001 Free Software Foundation, Inc.
This program is free software; you canredistribute it and/or modify
it under the terms of the GNU GeneralPublic License as published by
the Free Software Foundation; eitherversion 2 of the License, or
(atyour option) any later version.
This program is distributed in the hopethat it will be useful,
but WITHOUT ANY WARRANTY; without even theimplied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULARPURPOSE. See the
GNU General Public License for moredetails.
You should have received a copy of the GNUGeneral Public License
along with this program; if not, write tothe Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
#define ASM_FILE 1
#include <multiboot.h>
/* Csymbol format. HAVE ASM USCORE is defined by configure. */
#ifdef HAVE_ASM_USCORE
# define EXT_C(sym) _ ## sym
#else
# define EXT_C(sym) sym
#endif
/*The size of our stack (16KB). */
#define STACK_SIZE 0x4000
/*The flags for the Multiboot header. */
#ifdef __ELF__
# define MULTIBOOT_HEADER_FLAGS 0x00000003
#else
# define MULTIBOOT_HEADER_FLAGS 0x00010003
#endif
.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 Multibootinformation 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’里:
/*kernel.c - the C part of the kernel */
/*Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you canredistribute it and/or modify
it under the terms of the GNU GeneralPublic License as published by
the Free Software Foundation; eitherversion 2 of the License, or
(atyour option) any later version.
This program is distributed in the hopethat it will be useful,
but WITHOUT ANY WARRANTY; without even theimplied warranty of
MERCHANTABILITY or FITNESS FOR APARTICULAR PURPOSE. See the
GNU General Public License for moredetails.
You should have received a copy of the GNUGeneral Public License
along with this program; if not, write tothe 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. */
staticint xpos;
/*Save the Y position. */
static int ypos;
/*Point to the video memory. */
static volatile unsigned char *video;
/*Forward declarations. */
voidcmain (unsigned long magic, unsigned long addr);
static void cls (void);
static void itoa (char *buf, intbase, int d);
static void putchar (int c);
voidprintf (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 aMultiboot-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 informationstructure. */
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))
{
multiboot_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 = (multiboot_module_t *)mbi->mods_addr;
i < mbi->mods_count;
i++, mod++)
printf (" mod_start = 0x%x, mod_end =0x%x, cmdline = %s\n",
(unsigned)mod->mod_start,
(unsigned) mod->mod_end,
(char *) mod->cmdline);
}
/* Bits 4 and 5 are mutually exclusive! */
if (CHECK_FLAG (mbi->flags, 4)&& CHECK_FLAG (mbi->flags, 5))
{
printf ("Both bits 4 and 5 areset.\n");
return;
}
/* Is the symbol table of a.out valid? */
if (CHECK_FLAG (mbi->flags, 4))
{
multiboot_aout_symbol_table_t*multiboot_aout_sym = &(mbi->u.aout_sym);
printf("multiboot_aout_symbol_table: tabsize = 0x%0x, "
"strsize = 0x%x, addr= 0x%x\n",
(unsigned) multiboot_aout_sym->tabsize,
(unsigned)multiboot_aout_sym->strsize,
(unsigned)multiboot_aout_sym->addr);
}
/* Is the section header table of ELF valid? */
if (CHECK_FLAG (mbi->flags, 5))
{
multiboot_elf_section_header_table_t*multiboot_elf_sec = &(mbi->u.elf_sec);
printf ("multiboot_elf_sec: num= %u, size = 0x%x,"
" addr = 0x%x, shndx =0x%x\n",
(unsigned)multiboot_elf_sec->num, (unsigned) multiboot_elf_sec->size,
(unsigned)multiboot_elf_sec->addr, (unsigned) multiboot_elf_sec->shndx);
}
/* Are mmap * valid? */
if (CHECK_FLAG (mbi->flags, 6))
{
multiboot_memory_map_t *mmap;
printf ("mmap_addr = 0x%x,mmap_length = 0x%x\n",
(unsigned) mbi->mmap_addr,(unsigned) mbi->mmap_length);
for (mmap = (multiboot_memory_map_t *)mbi->mmap_addr;
(unsigned long) mmap <mbi->mmap_addr + mbi->mmap_length;
mmap =(multiboot_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,
mmap->addr>> 32,
mmap->addr& 0xffffffff,
mmap->len>> 32,
mmap->len& 0xffffffff,
(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 ifBASE 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;
}
}
}
}
其它有用的信息可以在Multiboot内核中得到,比如GNU Mach及Fiascohttp://os.inf.tu-dresden.de/fiasco/。及值得注意的OSKit http://www.cs.utah.edu/projects/flux/oskit/,它提供了一个支持这个规范的库。
GNU GRUB(参考GRUB手册中的“GRUB”一节)项目是一个Multiboot兼容的启动载入器,支持所有在这个规范中所要求及许多可选的特性。源代码可从GNU网站下下载。