如何用grub引导你的内核

xiaoee posted @ 2010年3月20日 05:53 in Operating System with tags OS grub boot loader kernel , 13695 阅读

以下内容以GNU GRUB 0.97为标准,并且是从U盘引导。

1.     安装grubU盘。

安装grubU盘最方便的方法就是在linux系统下使用grub安装程序。

具体步骤如下:

a)        首先找到U盘所在设备(这里假设为/dev/sdb1),然后挂载:

mkdir /mnt/sdb1
mount /dev/sdb1 /mnt/sdb1

b)       创建相关目录

mkdir /mnt/sdb1/boot
mkdir /mnt/sdb1/boot/grub

c)        找到文件stage1, stage2, *_stage1_5的位置

find /usr/ -name statge

d)       复制以上文件到grub 目录(假设上面的文件在/usr/share/grub/ 目录下):

cp /usr/share/grub /mnt/sdb1/boot/grub
cp /usr/share/grub /mnt/sdb1/boot/grub

如果U盘是用FAT文件格式格式化的则复制fat_stage1_5,这里用的是FAT32

cp /usr/share/grub /mnt/sdb1/boot/grub

e)        运行grub安装程序:

sudo grub

接着如下操作:(其中设备号要具体确定)

好了现在U盘上已经装好了grub,引导配置写在grub.conf中。

2.     grub引导 内核

grub有两种不同的引导机制,一种是直接 载入系统内核,另一种是通过chainloader载入另一个bootloader来引导自己的系统。可以被bootloader原生支持的内核一般要符合多重引导规范(Multiboot Specification)。当然为了方便grub也支持直接引导linuxFreeBSD等。

我们要用grub来引导自己的内核,当然我们不希望自己写一个bootloader再让grubchainload,所以我们得支持多重引导规范。

 

  一个简单的内核实例(grub0.97源文件/docs/下面有三个文件multiboot.hloader.skernel.c,下面作了简化):

loader.asm:这里使用nasm语法

global loader           ; making entry point visible to linker
extern kmain            ; kmain is defined elsewhere

; setting up the Multiboot header - see GRUB docs for details

MODULEALIGN equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO   equ  1<<1                   ; provide memory map
FLAGS     equ  MODULEALIGN | MEMINFO  ; this is the Multiboot 'flag' field
MAGIC       equ   0x1BADB002           ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum required 

section .text
align 4
MultiBootHeader:
   dd MAGIC
   dd FLAGS
   dd CHECKSUM
; reserve initial kernel stack space

STACKSIZE equ 0x4000                  ; that's 16k.
loader:
   mov esp, stack+STACKSIZE           ; set up the stack
   finit                              ; initialize FPU
   push eax                           ; pass Multiboot magic number
   push ebx                           ; pass Multiboot info structure
   call  kmain                       ; call kernel proper
   cli
hang:
   hlt                                ; halt machine should kernel return
   jmp  hang
section .bss
align 32
stack:
   resb STACKSIZE      ; reserve 16k stack on a quadword boundary

编译方法:

nasm -f elf -o loader.o loader.asm
kernel.c

void kmain( void* mbd, unsigned int magic )
{
   if ( magic != 0x2BADB002 )
   {
      /* Something went not according to specs. Print an error */
      /* message and halt, but do *not* rely on the multiboot */
      /* data structure. */
   }
   unsigned char *videoram = (unsigned char *) 0xb8000;
   char str[]="hello world";
   int i;
   for (i=0;i<sizeof(str);i++) {
       videoram[i*2] = str[i]; /* character 'A' */
       videoram[1] = 0x07; /* forground, background color. */
   }
   /* Write your kernel here. */
}

编译方法

gcc -o kernel.o -c kernel.c

为了方便我们使用一个链接文件

linker.ld:

ENTRY (loader)
SECTIONS{
    . = 0x00100000;
    .text :{
        *(.text)
    }
    .rodata ALIGN (0x1000) : {
        *(.rodata)
    }
 
    .data ALIGN (0x1000) : {
        *(.data)
    }
    .bss : {
        sbss = .;
        *(COMMON)
        *(.bss)
        ebss = .;
    }
}

内核必须被加载到1M以上的内存。

最后链接:

ld –T linker.ld  -o kernel.bin loader.o kernel.o

U盘启动:

root (hd0,0)
kernel /boot/kernel.bin
boot

然后可以看到在屏幕的左上角打卬出了hello world

3.     grub引导完内核后的机器状态

1.EAX必须包含魔数;0X2BADB002,这个值告诉操作系 统它是由兼容multibootbootloader引导的。

2.EBX 必须包含bootloader提供的多重引导信息结构的32位物理地址。

我们可以在进入kmain之前把这两个参数通过压栈传过去, 像上面loader.asm中做的一样。

3.CS指向一个以0为基址,4GB1为界限的代码段描述符

4.DSSSESFSGS指向一个以0为基址,4GB1为界限的数据段描述符。

5.A20地址线被打开

6.CR0PE位置位,PG位清除,即保护模式开启,分页关闭。

7.EFLAGSVM位必须清除,IF位必须清除。

8.GDTR这个时候可能不合法,所以在设置成指向正确的GDT之前,即使段寄存器像上面一样设置好了也不要用。

9.中断被关闭。

所以要让自己的内核可以被grub引导,其实只要添加一个multiboot header(参看loader.s开头部分);之后我们就可以根据加载后grub提供的环境,利用eax传过来的参数检测是否被正确加载,利用ebx传过来的参数得到内存以及加载等各种信息。由于A20地址线已经打开,保护模式也打开,我们 只需要正确设置GDTR和各种段寄存器就行了。我们甚至就可以在上面的kmain函数的基础上,根据我们的需要开始编写自己的内核了。因此grub使得编写操作系统的起步就变得相当容 易。

 

4.     参考资料

1.the grub manual

2.grub multiboot

3.wiki.osdev.org

4. http://wiki.osdev.org/GRUB

5. http://wiki.osdev.org/Bare_bones#Booting_the_kernel

 

5.     其它

关于josgrub引导:(MIT的一个6.828课程的实验操作系统)

其它jos是支持multiboot的,看看jos/kern/entry.S,发现开始已经定义了multiboot header

再看看jos/kern/Makefrag,可以发现有一个grub的目标,正好是用来生成grub引导用的内核jos-grub

关于Orange’sgrub引导:(<<Orange's 一个操作系统的实现>>书中的系统)

        其实只要在oranges/kernel/kernel.asm中添加multiboot headerGDT的定义,把切换GDT改到加载刚定义的GDT


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter