GRUB multiboot

http://hi.baidu.com/bznr/blog/item/a641b745c5e4b726cffca3b7.html

 

Multiboot的研究(grub)
2006-10-10 16:41

1.1 Multiboot 规范的背景

每一个操作系统一般都有自己的 boot loader ,由于这些 boot loader 安装与启动的用户接口都不同,所以要在同一台机器上使用多个操作系统不是容易的事情。该规范的目的定义了 boot loader 与操作系统之间统一的接口,任何遵循此规范的 boot loader 可以引导任何遵循此规范的操作系统。

 

1.2 Multiboot 的实现

规范主要分成 3 个部分:

首先是关于操作系统的部分。遵循 Multiboot 规范的操作系统必须包括多重引导头文件( Multiboot header.h )。这个文件必须在操作系统的第一个 8192 字节内,这样 Grub 中通过:

#define MULTIBOOT_SEARCH          8192

#define MULTIBOOT_FOUND(addr, len) /

  (! ((addr) & 0x3) /

   && (len) >= 12 /

   && *((int *) (addr)) == MULTIBOOT_MAGIC /

   && ! (*((unsigned *) (addr)) + *((unsigned *) (addr + 4)) /

        + *((unsigned *) (addr + 8))) /

   && (! (MULTIBOOT_AOUT_KLUDGE & *((int *) (addr + 4))) || (len) >= 32) /

   && (! (MULTIBOOT_VIDEO_MODE & *((int *) (addr + 4))) || (len) >= 48))

寻找该文件中关于操作系统的信息。文件中包括 ’magic’ ’flags’ ’checksum’ ’header_addr’ ’load_addr’ ’load_end_addr’ ’bss_end_addr’ ’entry_addr’ ’mode_type’ ’ width’ ‘height’ ‘depth 等字段。

其中 magic 用来标识多重引导头,值必须是 0x1BADB002

flagg 32 位)表示操作系统需要 boot loader 具有的特性。

head_addr 包含多重引导头的开始地址-在那里去寻找 magic 值的物理地址。这个字段用来同步 OS Image 偏移和物理地址之间的映射。

其他字段各具其作用,总之是向 grub 传递操作系统的相关信息和要求。

然后是 Grub 引导操作系统时必须设置机器状态为指定状态,例如:

在在 asm.S 中使用函数 multi_boot() 将魔数 0x2BADB002 放入 eax 中,来告诉操作系统目前是由兼容 Multiboot boot loader 在进行引导;将 0x8 放入 ebx 中, 0x8 boot loader grub 提供多重引导信息结构( struct multiboot_info )的 32 位物理地址。总之, grub 按照规范来设置机器状态。

最后是 grub 初始化多和填充重引导信息结构( struct multiboot_info )中的结构变量,通过多重引导信息结构, grub 可以与操作系统交换重要的信息,此工作在 common.c 中进行。

struct multiboot_info mbi 定义了结构体变量 mbi ,然后在 init_bios_info (void) 函数中对其进行初始化和填充:

调用 asm.S 中的 get_memsize() 得到 mbi.mem_lower (常规内存大小)和 mbi.mem_upper (扩展内存大小)

gateA20(1) 使 gateA20 由端口 92 或芯片组的特定程序控制,使系统速度更快。

调用 get_code_end() 得到 mbi.mmap_addr (表示含有由 BIOS 提供的 memory map 的缓冲区的地址),得到 mbi.mmap_length (表示含有由 BIOS 提供的 memory map 的缓冲区的长度), memory map 列出所有可以用作正常用途的 RAM

然后得到驱动( drive )的信息,只有多重启动内核要求时才进行此操作,因为会降低启动速度。信息由 mbi_drives_length(addr number mode) 表示。

mbi.config_table = get_rom_config_table () 得到 ROM 配置表。

mbi.boot_loader_name = (unsigned long) "GNU GRUB " VERSION; 表示 boot_loader 的名字(即 grub )和 grub 的版本。

mbi.apm_table 通过 get_apm_info () 得到 AMP (高级电源管理)表的信息。

Grub 按照操作系统多重引导头文件中 flag 各个位的设置,将操作系统需要的信息传递给操作系统。

 

1.3 Multiboot 流程

多重引导信息结构初始化工作由 asm.S 模块运行时调用 init_bios_info (void) 函数完成。

builtins.c 文件中通过判断内核的类型是 Multiboot ,调用 multi_boot ((int) entry_addr, (int) &mbi)


http://www.oldlinux.org/oldlinux/viewthread.php?tid=6177

编写操作系统之使用GRUB

[这个贴子最后由sololxy在 2006/04/24 03:21am 第 2 次编辑]

9_454.rar (18.26 KB)

使用GRUB
                                        翻译   :  solo_lxy
                                        E-mail :  solo_lxy@126.com
                                        QQ     :  36727308
                                        MSN    :  solo_lxy@hotmail.com
(GRUB的英文主页可以在http://www.gnu.org/software/grub/grub.en.html 上找到)
        本教程是由Chris Giese贡献的,可以到其网站(这个地址已经无效了 )参看英文版。
=========================================================
                         参考
=========================================================
这些都是我在使用GRUB时经验积累,可以在alt.os.development(我也没上去,郁闷拉)上找到相关帖子的原版:
Subject: Re: generic bootloader question
Newsgroups: alt.os.development
From: "Marv"
Date: Sat, 7 Apr 2001 23:35:20 +0100
References: <9antu8$glc$1@uni00nw.unity.ncsu.edu >
Message-ID: <986682856.680474@dionysos>
Subject: Re: Grub multiboot example
Newsgroups: alt.os.development
From: "Marv"
Date: Mon, 4 Jun 2001 17:21:17 +0100
References: <4a400d54.0106040458.5140872b@posting.google.com >
Message-ID:
Subject: Re: Grub multiboot example
Newsgroups: alt.os.development
From: "Mike Wimpy"
Date: Thu, 7 Jun 2001 22:17:51 -0700
References: <4a400d54.0106040458.5140872b@posting.google.com > <3B1CDA6D.154ADD9D@127.0.0.1 > <3B1CDAF9.300ED5E6@127.0.0.1 > <3B204A02.14A11876@cisco.com > <3b2053e5_2@news.pacifier.com > <3B205CC8.1685DD4C@cisco.com >
Message-ID: <3b205ff8_2@news.pacifier.com >
Subject: Re: grub coff (solved it!)
Newsgroups: alt.os.development
From: "Mark & Candice White"
Date: Sun, 16 Sep 2001 10:57:34 GMT
References:
Message-ID:

获取GRUB
原代码地址:ftp://alpha.gnu.org/gnu/grub/grub-0.90.tar.gz
可执行程序:ftp://alpha.gnu.org/gnu/grub/grub-0.90-i386-pc.tar.gz
DOS和Windows用户所需要的PARTCOPY和RAWRITE工具可以在以下网址下载:
        http://www.execpc.com/~geezer/johnfine/index.htm#zero
        http://uranus.it.swin.edu.au/~jn/linux/rawwrite.htm
        http://www.tux.org/pub/dos/rawrite/
建立GRUB
类Unix环境可以在shell上执行以下命令:
> ./configure
> make
(…我想应该是这样的…)
(实在不行可以参看源代码中的Readme、FAQ或INSTALL文件)
DOS和Windows用户请参看源代码中的安装文档。(作者并没有给出相关方法,我也不想在这上面花时间,因为我不怎么喜欢在DO或Windows上做这样的事情)
在没有文件系统的软盘上安装GRUB
1.        获取GRUB的二进制文件(stage1和stage2)
2.        合并stage1、stage2到一个文件:
(DOS)          > copy  /b stage1 + stage2 boot
(UNIX)        > cat stage1 stage2 > boot
3.        直接将boot文件写进软盘:
(DOS)          > rewrite boot a:
或者            > partcopy boot 0 168000 –f0
(UNIX)        > cat stage1 stage2 > boot
使用partcopy时它可能会报告说boot文件大小不到0x168000字节,不过不要紧。
在已经安装了文件系统的软盘上安装GRUB
1.        按照上面的介绍制作一个带有GRUB可以启动的软盘。
2.        将stage1和stage2拷贝到另外一张安装了文件系统的软盘上,注意该软盘的文件系统必须是GRUB可以识别的。为了使用GRUB的 setup命令,stage1和stage2必须放在安装了文件系统的软盘的/boot/grub目录下,具体操作如下:
    (DOS)  > mkdir a:/boot
              > mkdir a:/boot/grub
              > copy stage1 a:/boot/grub
              > copy stage2 a:/boot/grub
(UNIX) > mount /dev/fd0 /mnt
          > mkdir /mnt/boot
          > mkdir /mnt/boot/grub
> cp stage1 /mnt/boot/grub
                  > cp stage2 /mnt/boot/grub
3. 在第二个软盘上安装了GRUB后,就不要再修改、移动、删除或经过碎片整理了。一旦被修改,这个软盘就不能在用于启动了!为了防止修改,可以让这个文件变成只读的:
        (DOS)   attrib +r +s stage2
(UNIX)  chmod a-w stage2
   以上的DOS命令使得stage2变成一个只读的系统文件。这样可以防止碎片整理。
注意:stage1文件此时已经被拷贝至启动扇区。如果这个文件在GRUB安装后被删除,则该软盘还是可以启动的,这与stage2不同。
3.        用不带文件系统的软盘启动电脑。当GRUB提示符出现后,取出软盘,插入拷贝了stage1和stage2的带有文件系统的第二张软盘。
4.        如果stage1和stage2已经在/boot/grub目录中,那么可以简单的键入如下命令安装GRUB:
       setup (fd0)
这条命令显然同下面的命令等效:
       install /boot/grub/stage1 d (fd0) /boot/grub/stage2 p
               /boot/grub/menu.lst
如果stage1和stage2在其他位置,如在子文件夹/foo中,那么按照如下的方式安装:
       install = (fd0)/foo/stage1 (fd0) (fd0)/foo/stage2 0x800 p
                 (fd0)/foo/menu.lst
这样带有文件系统的这张软盘就可以作为启动了盘了。
xxx - 从第二张软盘上启动,拷贝一个新的stage2并修改其访问属性,然后重新执行setup或install,这样可行吗?(xxx – GRUB并不是一个shell -- 它不能拷贝文件或是列出文件夹 – 不是吗?)
xxx – 安装语法:
           0x8000,这个值被写入软盘的驱动扇区,它指示stage2被加载到内存后的首地址。
       P,修改stage2,告诉内核stage2所在的分区(我想是这样的)。
       (fd0)/boot/grub/menu.lst,修改stage2,告诉GRUB到哪儿去加载menu.lst(启动
菜单)配置文件。
           
制作多启动内核
        无论你的内核是什么样的文件格式,你的内核必须要有多启动头(multiboot header)。这个头必须是4字节对齐,并且必须在内核的前8k字节以前。
注意:一个.text段前8K的地址不必是某个开始文件的前8K.
ELF内核
        GRUB可以直接识别ELF文件格式。如果你的内核是ELF内核,可以使用下面简单的多启动头:
MULTIBOOT_PAGE_ALIGN           equ 1<<0
MULTIBOOT_MEMORY_INFO          equ 1<<1
MULTIBOOT_HEADER_MAGIC         equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS         equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
CHECKSUM                       equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
; 多启动头( NASM 语法)
ALIGN 4
DD MULTIBOOT_HEADER_MAGIC
DD MULTIBOOT_HEADER_FLAGS
DD CHECKSUM
将上面的代码放在靠近内核启动代码的开始处,然后建立内核。在建立的内核以后,使用GRUB的mbchk功能函数测试内核是否能用于多启动
内核的加载地址
        GRUB从ELF内核文件中读取物理地址(加载地址;LMA)。这个值必须大于或等于1M,并且小于真实的物理RAM的大小。
        如果加载地址在1M以下,会发生#7号错误:
                Loading below 1MB is not suppored
注意:这是GRUB的限制,并不是多启动机制的限制。
如果你使用了像0xC0000000这样的地址,显然是溢出了,同样会得到#7号错误。
注意:mbchk并不会检查这样的错误。
        一般情况下,物理地址就是虚拟地址VMA,这可以在连接脚本或连接器的命令行中设置(“ld –Ttext=0x100000 …”)。如果你的连接器版本支持的话,物理地址和虚拟地址可以用“AT”在连接脚本里独立的指定:
        OUTPUT_FORMAT("elf32-i386"
        ENTRY(entry)
        virt = 0xC0000000; /* 3 G */
        phys = 0x100000;  /* 1 M */
        SECTIONS
        {   
            .text virt : AT(phys)
            {   code = .;
                *(.text)
                . = ALIGN(4096);
            }
            .data :  AT(phys + (data - code))
            {   data = .;
                *(.data)
                . = ALIGN(4096);
            }
            .bss :  AT(phys + (bss - code))
            {   bss = .;
                *(.bss)
                *(COMMON)
                . = ALIGN(4096); }
                end = .;
            }
        }
连接完成后,使用objdump –h查看地址是否正确。
DJGPP的COFF文件格式和其他文件格式
        DJGPP用户可以使用下面这个工具制作ELF文件。
                http://www.multimania.com/placr/binutils.html
(xxx – 这个服务器一般很难打开的,如果你愿意可以给它作个镜像。我无能为力,因为我的磁盘空间不够)
        我推荐建立标准的COFF文件,然后这样做:
                Objcopy-elf –O elf32-i386 krnl.cof kenl.elf
        如果这样不行的话,你可以使用“AOUT KLUDGE”让GRUB加载COFF文件。这需要在多启动头的后面添加额外的域(field),像下面这样:
    MULTIBOOT_PAGE_ALIGN           equ 1<<0
    MULTIBOOT_MEMORY_INFO          equ 1<<1
    MULTIBOOT_AOUT_KLUDGE          equ 1<<16
    MULTIBOOT_HEADER_MAGIC         equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS             equ MULTIBOOT_PAGE_ALIGN| MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
   CHECKSUM                        equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
    ; The Multiboot header
    ALIGN 4
    mboot:
        DD MULTIBOOT_HEADER_MAGIC
        DD MULTIBOOT_HEADER_FLAGS
        DD CHECKSUM
        ;如果设置了在MULTIBOOT_HEADER_FLAGS
        ;中设置了MULTIBOOT_AOUT_KLUDGE
        DD mboot         ; these are PHYSICAL addresses
        DD code          ; start of kernel .text (code) section
        DD edata         ; end of kernel .data section
        DD end           ; end of kernel BSS
        DD start                 ; kernel entry point (initial EIP)
注意:如果是二进制或其他文件格式,则同样需要“AOUT KLUDGE”。(xxx – 没有测试过,也许是正确的)。
启动内核
1.        确定你的内核在GRUB方便找到的位置。不必非得在软盘上(靠,这个真COOL)。
2.        从GRUB软盘上启动。
3.        (可选的)告诉GRUB,它的根文件夹所使用的设备。
             root (hd0,1)
这将挂载(mount)第一块硬盘的第二个主分区,以其作为做为根文件夹。
4.        告诉GRUB你的内核在哪儿:
            kernel  /krnl.elf
如果你没有指定根设备,就必须在路径名上显式指定:
            kernel  (hd0,1)/krnl.elf
5.        如果GRUB没有出现任何错误,那么就可以直接启动了:
            boot
利用GRUB向你的内核传递信息
启动后,在32位内核进入点,机器状态如下:
1.        CS指向基地址为0x00000000,限长为4G – 1的代码段描述符。
2.        DS,SS,ES,FS和GS指向基地址为0x00000000,限长为4G – 1的数据段描述符。
3.        A20地址线已经打开。
4.        页机制被禁止。
5.        中断被禁止。
6.        EAX = 0x2BADB002
7.        系统信息和启动信息块的线性地址保存在 EBX中(相当于一个指针)。
                /* 多启动信息结构 */
        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;
        在调用main函数之前将EBX的值压入堆栈,就可以使用C代码来访问以上结构中的相关信息了,示例代码如下:
汇编启动代码:
                …
                push ebx
                call main ; “call main” for Linux/ELF
                …
C语言代码:
                #include <…>
                int main(multiboot_info_t* boot_info)
                {
                        if( boot_info->flags & 2)
                        {
                                Kprintf(“the command line is:’%s’/n”,
                         (char*) boot_info->cmdline);
                        }
                        …
                }
制作启动菜单
示例1:
        # Entry 0:
        title   WildMagnolia
        root    (fd0)
        kernel  /boot/kernel.elf
        module  /boot/mod_a
        module  /boot/mod_b
示例2:
        #
        # Sample boot menu configuration file
        #
        #  default - boot the first entry.
        default 0
        # if have problem - boot the second entry.
        fallback 1
        # after 30 sec boot default.
        timeout 30
        # GNU Hurd
        title          GNU/Hurd
        root           (hd0,0)
        kernel         /boot/gnumach.gz root=hd0s1
        module         /boot/serverboot.gz
        # Linux - boot ot second HDD
        title          GNU/Linux
        kernel        (hd1,0)/vmlinuz root=/dev/hdb1
        # booting Mach - get kernel from floppy
        title          Utah Mach4 multiboot
        root           (hd0,2)
        pause          Insert the diskette now!!
        kernel         (fd0)/boot/kernel root=hd0s3
        module        (fd0)/boot/bootstrap
        # booting OS/2
        title         OS/2
        root          (hd0,1)
        makeactive
        # chainload OS/2 bootloader from the first sector
        chainloader +1
        # For booting Windows NT or Windows95
        title         Windows NT / Windows 95 boot menu
        root     (hd0,0)
        makeactive
        chainloader +1
        # za boot na DOS ako Windows NT e instaliran
        # chainload /bootsect.dos
        # Colors change :0).
        title                 Change the colors
        color                 light-green/brown blink-red/blue

为内核加载模块(module)
=======================================================================
xxx
=======================================================================
=======================================================================
Other
=======================================================================
(看来作者并没有写全这些,真遗憾!
- gzip-compressed kernels?用gzip压缩的内核如何处理呢?
- does GRUB understand kernel file formats other than ELF?
- GRUB除了可以处理ELF内核以外还可以处理其他的吗?
by K.J.
Yes, you may also compile your kernel as a COFF file or an AOUT file.
K.J回答说你可以把你的内核编译成COFF或AOUT文件。(纯二进制文件一样是可以处理的,我正在写的一个内核就是使用纯二进制的,它可以让我更清楚的知道我的代码在内存的什么位置,这样对我来说更了然入胸。)

(在使用GRUB的过程中也出现了不少问题,有些到现在还没搞得太清楚,那位大虾在行的话不妨让我请教请教,谢谢了! 转贴请注明原文和翻译的文章,也不
枉我敲了两个小时的中文,谢谢 )


[ 本帖最后由 redgrid 于 2007-6-4 15:35 编辑 ]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值