在i386平台上,Linux内*使用一个相当 杂的启动约定。这是历史多个方面进
的结果。既有早期对内*本身应为可 动影像的要求、杂乱的PC内*模型的 *,
也有实模式下的DOS作为主流操作系统 遽然*亡引起的PC工业界变化的原*
现在,有四个版本的Linux/i386启动协议:
Old kernels: 只支持zImage/Image,一些老版本甚至不支持命令行。

Protocol 2.00 (Kernel 1.3.73) 增*了对bzImage和initrd-作为boot loader
和内*之间通讯的*准途径-的支持,setup.S被处理为可以重
定位的,尽管仍然假设使用*统的setup area。

Protocol 2.01 (Kernel 1.3.76)增*了heap越界*告。

Protocol 2.02 (Kernel 2.4.0-test3-pre3)新的命令行协议。更低的内*上
限。不覆盖(overwrite)*统的setup area,这*


Linux 启动协议 v20070523


作者:H. Peter Anvin [hpa@zytor.com]
译者:Wick [wickmaycry@gmail.com]

- *为译者注
- 尊重他人劳动,分享社会成果。转载 保留文*出处和文*所有链接。
----------
本文*原作者H. Peter Anvin是瑞典人,作为SYSLINUX的作者,还 责了klibc和
NASM*自
由软件项目。
文*对*在的启动协议进行简要概括 对Linux内*分析的起*有很大的帮助 读
者也可以
借*理解到纷繁变换的协议给内*代* 带来的稍许变化。
由于本人水平有限,如若读者对文* 汇翻译有任何意见或者不同见解,敬 指*。
----------

在i386平台下,Linux内*使用了一个相 复杂的启动协议。这要部分归咎于历
原*,*
为早期的内*需要被做成一个可启动 像,其他原*还包括,复杂的计算机 *模
型,由于
实模式DOS作为主流操作系统而改变了 算机工业的预期发展**。

当前共有以下Linux/i386启动协议*在着:

旧版内*:

仅仅支持zImage/Image。一些早期的内*甚至不支持命令 行。

2.00版协议:



(内*版本 1.3.73)*入了bzImage和initrd的支持,也 有了一种*规
化的方法来实现启动装载器(* boot loader)和内*间的通信。setup.S
建*了一块可移动,但是仍旧可写的 *统的安装程序*载区域。

2.01版协议:

(内*版本 1.3.76)*入一系列大量的溢出*告。

2.02版协议:





(内*版本 2.4.0-test3-pre3)这是新的命令行协议。它降低了 规的
内*使用上限(* 见以下MEMORY LAYOUT内*布局介绍)。没有覆盖*统
的安装程序区域,**对于那些使用 来自SMM或者32位BIOS入口地址的
EBDA(* Extended BIOS Data Area,拓展BIOS数据域)的系统,这*做
可以使得启动更*安全。

2.03版协议:


(内*版本 2.4.18-pre1)明确定义了高端的initrd地址可以 启动装载器使
用。

2.04版协议:

(内*版本 2.6.14)将syssize域拓展到四个*节。

2.05版协议:


(内*版本 2.6.20)使得保护模式下的内*部分可 重新定位(* 可被移
动) 。同时引入了relocable_kernel和kernel_alignm ent域。

2.06版协议:


(内*版本 2.6.22)*入了一个新域(* cmdline_size),以*储启动
命令行的大小。




**** 内*布局

使用了Image或者zImage的启动*载器(the kernel loader),其在*统上内*布局
大致如下
图所示:

| |
0A0000 +------------------------+
| Reserved for BIOS | 未使用,被BIOS EBDA保留
09A000 +------------------------+
| Command line |
| Stack/heap | 被实模式下的内*代*所使用
098000 +------------------------+
| kernel setup | 实模式下的内*代*
090200 +------------------------+
| kernel boot sector | 历史遗留下来的内*启动扇区
090000 +------------------------+
| Protected-mode kernel | 内*镜像的主要部分
010000 +------------------------+
| Boot loader | - 启动扇区的入口位置 0000:7c00
001000 +------------------------+
| Reserved for MBR/BIOS |
000800 +------------------------+
| Typically used by MBR |
000600 +------------------------+
| BIOS use only |
000000 +------------------------+

----------
* 以上的特殊名词翻译

Stack/heap *区/*区
kernel setup 内*安装程序
Boot loader 启动装载器
----------
当使用bzImage的时候,保护模式下的内 *部分就被重定位到0x100000(高端内
*),而实
模式下的内*块(包括启动扇区,安 程序,和**部分)就可以自由定位
0x10000和低
段内*区域的任意位置。不幸的是, 2.00和2.01版启动协议*,0x90000以上的
内*区域
仍旧被内*所使用;2.02版启动协议就 决了这个问题。

人们总想让内*上限——启动装载器 触的低端内*的最高点——尽可能的 ,越低越
好,这是
*为一些新的BIOS已经开始分配相当大 内*区域,这片区域被称为拓展BIOS 据区
(EBDA),位于低端内*的顶端。启动 载器会使用BIOS的0x21号**来判*有
少低端内
*可以使用。

不幸的是,如果INT 12h报告的内*大小太少,启动装载器 没有任何用处,*时
只能向用
户报告错误。*而这个启动*载器* 要求被设计成更小的尺寸。对于zImage 者旧式
bzImage版本内*,它们可能会需要在0x90 000段写入数据,这时启动装载器应该
保不会*
用0x9a000以上内*;太多的BIOS*坏了以 上原则(* 指BIOS分配太多区域用作
EBDA,而导
致低端内*空闲空间过小)。

而现在的bzImage内*已经使用2.02(包括 2.02)以后的启动协议,也将建议使用
一下内*
布局方式:


~ ~
| Protected-mode kernel |
100000 +------------------------+
| I/O memory hole |
0A0000 +------------------------+
| Reserved for BIOS | 尽可能预留更多空闲空间
~ ~
| Command line | (也可置于 X+10000 *记下)
X+10000 +------------------------+
| Stack/heap | 被实模式下的内*代*所使用
X+08000 +------------------------+
| Kernel setup | 实模式下的内*代*
| Kernel boot sector | 历史遗留下来的内*启动扇区
X +------------------------+
| Boot loader | - 启动扇区的入口位置 0000:7c00
001000 +------------------------+
| Reserved for MBR/BIOS |
000800 +------------------------+
| Typically used by MBR |
000600 +------------------------+
| BIOS use only |
000000 +------------------------+

.... X位置应和boot loader设计允许的上限一*低。


**** 实模式部分的内*头部

下面的段落*,还有任何关于内*启 工序的描述*,"一个扇区"意指512个*
节。他和实
际使用的底层媒体*的(* 扇区概念)是独立的,不同的。

装载Linux内*的第一*,应该是装载实 模式部分的内*代*(包括启动扇区 安
装程序代
*),然后检查接着的头部是否位于0x 01f1(偏移量)。尽管启动装载器只会 择
装载前两
个扇区(1K)并且接着检查启动扇区的 大小,但是实模式代*合计可以达到32 K。

头部的定义可以如一下情况:

偏移/大小 协议 命名 意义
01F1/1 ALL(1 setup_sects 安装程序的大小(单位:扇区)
01F2/2 ALL root_flags 如果设置,那么root分区将会被*载为 读
01F4/4 2.04+(2 syssize 32位代*段尺寸(用两个16位*长表示
01F8/2 ALL ram_size 不可用!-仅被bootsect.S使用
01FA/2 ALL vid_mode 视频模式控制
01FC/2 ALL root_dev 默认的*目录所在的设备号
01FE/2 ALL boot_flag 0xAA*术数*(* boot sector结束*志)
0200/2 2.00+ jump JUMP指令
0202/4 2.00+
header *术*名“HdrS”
0206/2 2.00+ version 支持的启动协议版本号
0208/4 2.00+ realmode_swtch 启动装载器的hook(见下文)
020C/2 2.00+ start_sys 低端地址*的系统位置(0x1000),已 废弃
020E/2 2.00+ kernel_version 指向内*版本*符串的指针
0210/1 2.00+ type_of_loader 启动装载器的*志符
0211/1 2.00+
loadflags 启动协议的选项*志位
0212/2 2.00+ setup_move_size 将要移动的(* 安装程序)大小(和hook并用)
0214/4 2.00+ code32_start 启动装载器的hook(见下文)
0218/4 2.00+ ramdisk_image initrd*载位置(由启动装载器给定)
021C/4 2.00+ ramdisk_size initrd尺寸(由启动装载器给定)
0220/4 2.00+ bootsect_kludge 不可用! - 仅被bootsect.S使用
0224/2 2.01+ heap_end_ptr 安装程序末尾之后的空闲位置
0226/2 N/A pad1 未使用
0228/4 2.02+ cmd_line_ptr 32位指针,指向内*命令行
022C/4 2.03+ initrd_addr_max 合法的最高位initrd位置
0230/4 2.05+ kernel_alignment kernel需要的物理地址对齐值
0234/1 2.05+ relocatable_kernel 显示kernel是否可以重定位
0235/3 N/A pad2 未使用
0238/4 2.06+ cmdline_size 内*命令行最大尺寸

(1) 考虑到向后兼容性,如果setup_sects域是 0,那么他的真实值就是4。

(2) 对于2.04以前的协议,syssize前两个*节 并未使用,这意味着bzImage的大小
*法确定。

如果在偏移0x202的位置*法找到*术数 *“HdrS”(0x53726448),那么这个启动协
议版本可
被归结至“旧版内*”。装载一个旧 本内*,就需要假设以下参数:

Image type = zImage
不支持initrd
实模式部分的内*将被装载到0x90000

否则,(* 紧接“HdrS”的)“version”域就会包含 启动协议版本。
例如,协议版本2.01,“version”域的值 为“0x0201”。当在头部设定*域后, *必
须确认已
经设定了*协议版本使用到的其他域



**** 详述头部域的细节情况

对于这里的每个域,有些是内*和启 *载器的信息(将在以下*记为“Read ”类
型),有些
则是将会被启动*载器填写(*记为 Write”),而又有些可能会被启动* 器读
取后又更
改(*记为“Modify”)。

通用的启动*载器,其填写的域被* 为(“obligatory”)。将内**载到非 *准
化地址的
启动*载器,其填写的域被*记为( reloc”);而其余的的启动*载器就 忽略
这些域。

所有域的*节顺序为从小到大排列( 竟这是x86)。


Field name(域的名称): setup_secs
Type(类型值,* 以上有描述): read
Offset/size(偏移/*节数): 0x1f1/1
Protocol(适用协议): ALL

用扇区数表示的安装程序代*大小。 果*域为0,那么真实值即是4。实模 下的
代*由
引导扇区(通常为一个扇区大小)和 装程序代*组成。


Field name: root_flags
Type: modify (optional)
Offset/size: 0x1f2/2
Protocol: ALL

如果这个域非0,*分区将默认为只读 在这里并不赞成使用*域,用命令行 的“ro”和
“rw”选项代替吧。


Field name: syssize
Type: read
Offset/size: 0x1f4/4 (protocol 2.04+) 0x1f4/2 (protocol ALL)
Protocol: 2.04+

保护模式下的内*代*大小,使用两 16位表示。由于2.04以前的协议**域 有
两*节
长度(* 16位),**如果LOAD_HIGH*志被设定, 内*大小就不会被一味的信任。


Field name: ram_size
Type: kernel internal
Offset/size: 0x1f8/2
Protocol: ALL

*域已经被废弃。


Field name: vid_mode
Type: modify (obligatory)
Offset/size: 0x1fa/2

请查看 特殊命令行选项 *节。


Field name: root_dev
Type: modify (optional)
Offset/size: 0x1fc/2
Protocol: ALL

默认的*目录分区所在的设备号。不 议使用*域,使用命令行的“root=” 替代它。


Field name: boot_flag
Type: read
Offset/size: 0x1fe/2
Protocol: ALL

域的值为0xAA55,这是最接近旧版Linux内 *的一个*术数*。


Field name: jump
Type: read
Offset/size: 0x200/2
Protocol: 2.00+

包含一个x86跳转指令,是一个带符号 相对于0x202的偏移量,接着是0xEB。这
以用来
决定头部的大小。

Field name: header
Type: read
Offset/size: 0x202/4
Protocol: 2.00+

域的值为“HdrS”(0x53726448).


Field name: version
Type: read
Offset/size: 0x206/2
Protocol: 2.00+

启动协议的版本号,使用了(整数部 <<8)+ 小数部分的*式。
比如:0x0204代表2.04版本,0x0a11代表了 想的10.17版本。


Field name: readmode_swtch
Type: modify (optional)
Offset/size: 0x208/4
Protocol: 2.00+

启动装载器的hook(见以下 高级的启动装载器hook)。


Field name: start_sys
Type: read
Offset/size: 0x20c/4
Protocol: 2.00+

低端内**的系统位置(0x1000)。已 弃。


Field name: kernel_version
Type: read
Offset/size: 0x20e/2
Protocol: 2.00+

如果*项非0,那么它就是一个指针, 值为以空值(NULL)结尾的,可识别 内
*版本
号的*符串的地址,再减掉0x200。它用 来向用户显示内*的版本。这个值应 于
(0x200
* setup_sects)。

例如,如果*项被设定为0x1c00,内* 本号的*符串就可能在内*文件*偏
0x1e00的
位置找到。当且仅当“setup_sects”值大 于*于15的时候,这个数值合法。如下 所示:

0x1c00 < 15 * 0x200 (= 0x1e00) 但是
0x1c00 >= 14 * 0x200 (= 0x1c00)

0x1c00 >> 9 = 14,**setup_secs最小值是15。


Field name: type_of_loader
Type: write (obligatory)
Offset/size: 0x210/1
Protocol: 2.00+

如果*的启动*载器有一个已经给定 id号(见下表),在这里就可输入0xTV 其
*,T
是一个启动*载器的*志符,而V是一 版本号。否则,就在这里输入0xff。

可选的启动*载器的id:
0 LILO (0x00为pre-2.00装载器所保留)
1 Loadlin
2 bootsect-loader (0x20,其他0x2V都被保留)
3 SYSLINUX
4 EtherBoot
5 ELILO
7 GRuB
8 U-BOOT
9 Xen
A Gujin
B Qemu

如果*有一个已用(* 且未被列出)的启动装载器的ID,请联 系
[hpa@zytor.com]


Field name: loadflags
Type: modify (obligatory)
Offset/size: 0x211/1
Protocol: 2.00+

*域是一个比特掩*。

Bit 0 (read): LOADED_HIGH
- 如果是0,保护模式部分的代*将被* 到0x10000。
- 如果是1, 保护模式部分的代*将被*载到0x100000 。

Bit 7 (write): CAN_USE_HEAP
将*项设置1,可以指明heap_end_ptr定义 效。如果*项清空,一些安装程序的
功能将会被禁用。


Field name: setup_move_size
Type: modify (obligatory)
Offset/size: 0x212/2
Protocol: 2.00-2.01

当使用2.00或者2.01版协议时,如果实模 式部分的内*没有被装载到0x90000,那
他将在
*载流程*的*后部分移动至那里。 果除了实模式代*本身以外,*有另 的数
据(例
如,内*命令行)想移动,那么请填 *域。

这块单元是以启动装载器作为开头的 *个*节。

如果*的启动协议版本高过2.02(包括2.0 2),或者实模式代*已经被装载到
0x90000,那
么*项可以忽略。


Field name: code32_start
Type: modify (optional, reloc)
Offset/size: 0x214/4
Protocol: 2.00+

跳转到保护模式的地址。默认下,这 该是内*所*载的位置,且*项被启 *载
器来判
*合适的*载地址。

*域可被用作以下用途:

1. 用作启动装载器的hook
2. 如果一个启动装载器没有安装hook,却 将可移动内**载到非*准化的地
址,那么
他必须修改*域来指向要*载的地址 置。



Field name: ramdisk_image
Type: write (obligatory)
Offset/size: 0x218/4
Protocol: 2.00+

用作初始化的ramdisk或者ramfs的32位线性 地址。如果没有初始化的
ramdisk/ramfs,请
将*位置0。


Field name: ramdisk_size
Type: write (obligatory)
Offset/size: 0x21c/4
Protocol: 2.00+

用作初始化的ramdisk或者ramfs的大小。 果没有初始化的ramdisk/ramfs,请
将*位置0。


Field name: bootsect_kludge
Type: kernel internal
Offset/size: 0x220/4
Protocol: 2.00+

*域已废弃。


Field name: heap_end_ptr
Type: write (obligatory)
Offset/size: 0x224/2
Protocol: 2.01+

将*域设定为安装程序使用的*区/*区的位置(指相对于实模式代*的 移),
再减去
0x200。


Field name: cmd_line_ptr
Type: write (obligatory)
Offset/size: 0x228/4
Protocol: 2.02+

将*域设定为内*命令行的线性地址
内*命令行可以被放置在安装程序* 的末尾和0xA0000之间的任意位置;它不 必
设在与
实模式代*相同的64K段*。

即便*的启动*载器不支持命令行, 可填写,这时*可以将*地址指向一 空
串。如果
*域被置0,内*会假定*的启动装载 不支持2.02以上协议。


Field name: initrd_addr_max
Type: read
Offset/size: 0x22c/4
Protocol: 2.03+

*的初始化ramdisk/ramfs内容可用的最高地址。2.02和更老 启动协议并没有定
义*域,
且他的最大地址是0x37FFFFFF。(这个地 被定义为最高的安全地址,所以,如 果*的
ramdisk*好是131072*节大小,而且*域 0x37FFFFFF,*就可以从0x37FE0000
启动*
的ramdisk)。


Field name: kernel_alignment
Type: read (reloc)
Offset/size: 0x230/4
Protocol: 2.05+

内*所需要的对齐位(当relocatable_kerne l = true时可用)。


Field name: relocatable_kernel
Type: read (reloc)
Offset/size: 0x234/1
Protocol: 2.05+

如果*项非0,则保护模式部分的内* 以被*载到满足kernel_alignment的任意
位置。
在*载完成后,启动装载器会将code32_s tart设定为指向*载的代*位置,或者
向启动
*载器的hook。


Field name: cmdline_size
Type: read
Offset/size: 0x238/4
Protocol: 2.06+

除去终*符0后的命令行最大长度。意 是,命令行可以包含最大cmdline_size个
*符数。
在2.05或更早启动协议版本里,这个最 值是255。


**** 内*命令行

如今内*命令行已经成为了启动*载 和内*通信的重要手段。一些命令行 项同
*与启动
装载器自身有关,见如下“特殊的命 行选项”。

内*命令行是一个以空值结尾的*符 。他的最大长度被保*到cmdline_size域
在2.06版
协议以前,最大长度是255个*符。太 的*符串会被内*自动截*。

如果启动协议是2.02或者更新版本,内 *命令行的地址将由头部的cmd_line_ptr域
给定(见
下文)。这个地址可以是安装程序的* 区与0xA0000之间任意地址。

如果启动协议版本低于2.02,就可使用 下的协议来读取内*命令行:

在偏移0x0020(*)位置,“cmd_line_magic ”,填入*术数* 0xA33F

在偏移0x0022(*)位置,“cmd_line_offse t”,填入内*命令行(相对于实模式
部分的内*)的偏移地址。

内*命令行必须包含在setup_move_size覆 的内*范围内,所以*可能需要修改
这个域。


**** 实模式代*的内*布局

实模式代*需要建立一个*区/*区,也要为内*命令行分配相应内* 。这些都需
要在实模
式代*可达的低1M空间内完成。

要注意:现在的机器都拥有一个相当 的拓展BIOS数据区(EBDA)。**建议 量
使用低1M空
间*尽可能小的空间。

不幸的是,在下面的情况下就必须使 0x90000内*段:

- 装载一个zImage内* ((loadflags & 0x01) == 0)
- 装载一个使用2.01或更早版本协议的内 *。

-> 在2.00和2.01版启动协议*,实模式代* 会被*载到另一个地址,但也会被
再次重新分配到0x90000。对于“旧版” *,实模式代*必须被*载到
0x90000。

当*载到0x90000时,避免使用高于0x9a000 的内*。

对于2.02或者更高版本启动协议,命令 不必和实模式下安装程序代*放在同 一个
64K段*;
**允许*区/*区拥有完整的64K段,且可以在其** 放命令行。

内*命令行既不该被*放在实模式代* 以下空间,也不应该放置在高端内* *。

**** 启动设置示例

*为是一个设置示例,我们可以假定* 在以下实模式内*段的布局:

当装载到低于0x90000的地址时,使用整 段:


0x0000-0x7fff 实模式部分的内*
0x8000-0xdfff *区/*区
0xe000-0xffff 内*命令行

当装载到0x90000处,或者*使用了2.01或 者更早版本的协议:

0x0000-0x7fff 实模式部分的内*
0x8000-0x97ff *区/*区
0x9800-0x9fff 内*命令行

这*一个启动装载器应该如*填写头 *的以下域:

C 代*

1. unsigned long base_ptr; /* base address for real-mode segment */
2.
3. if ( setup_sects == 0 ) {
4. setup_sects = 4;
5. }
6.
7. if ( protocol >= 0x0200 ) {
8. type_of_loader = [type code]; /* constant */
9. if ( loading_initrd ) {
10. ramdisk_image = [intrd_address]; /* constant */
11. ramdisk_size = [intrd_size]; /* constant */
12. }
13.
14. if ( protocol >= 0x0202 && loadflags & 0x01 )
15. heap_end = 0xe000;
16. else
17. heap_end = 0x9800;
18.
19. if ( protocol >= 0x0201 ) {
20. heap_end_ptr = heap_end - 0x200;
21. loadflags |= 0x80; /* CAN_USE_HEAP */
22. }
23.
24. if ( protocol >= 0x0202 ) {
25. cmd_line_ptr = base_ptr + heap_end;
26. strcpy(cmd_line_ptr, cmdline);
27. } else {
28. cmd_line_magic = 0xA33F;
29. cmd_line_offset = heap_end;
30. setup_move_size = heap_end + strlen(cmdline)+1;
31. strcpy(base_ptr+cmd_line_offset, cmdline);
32. }
33. } else {
34. /* Very old kernel */
35.
36. heap_end = 0x9800;
37.
38. cmd_line_magic = 0xA33F;
39. cmd_line_offset = heap_end;
40.
41. /* A very old kernel MUST have its real-mode code
42. loaded at 0x90000 */
43.
44. if ( base_ptr != 0x90000 ) {
45. /* Copy the real-mode kernel */
46. memcpy(0x90000, base_ptr, (setup_sects+1)*512);
47. base_ptr = 0x90000; /* Relocated */
48. }
49.
50. strcpy(0x90000+cmd_line_offset, cmdline);
51.
52. /* It is recommended to clear memory up to the 32K mark */
53. memset(0x90000 + (setup_sects+1)*512, 0,
54. (64-(setup_sects+1))*512);
55. }



**** 装载内*的剩余部分

32位内*代*从内*文件偏移(setup_sects +1)*512的位置开始(同*,如果
setup_sects*
于0,其真实值就是4)。
如果是Image/zImage内*,*段代*应该*载到0x10000 而bzImage内*则应该*载到
0x100000位置。

如果使用2.00或更高协议,而且已经设 loadflags的0x01位(LOAD_HIGH),那么
内*就被
认作bzImage内*。

C 代*

1. is_bzImage = (protocol >= 0x0200) && (loadflags & 0x01);
2. load_address = is_bzImage ? 0x100000 : 0x10000;



注意Image/zImage可能在达到512K大小,这*就会使 到整个0x10000-0x90000内
*范围。
这意味着,迫切需要这些内*将实模 部分装载到0x90000。bzImage内*则提供
更多的灵
活性。


**** 特殊的命令行选项

如果用户键入了启动装载器支持的命 行参数,那么用户可能期望以下命令 选项
可以工作。
即便并非所有选项对内*都有意义, 们也不该直接被*除。启动装载器的 者如
果需要给
装载器自身提供另外的命令行参数, 应该在Documentation/kernel-
parameters.txt注册
这些选项,来确保他们不会和当前的 者即将使用的内*选项相冲突。

vga=
这里的不是一个整数(在C*言表示法 *,应是十进制,八进制或者十*进
制其*之一),就是“normal”(0xFFFF ,“ext”(0xFFFE),“ask”(0xFFFD) *
的一个。这个值应被填入vid_mode域,* 为他会在命令行被解析前被内*使用

mem=
是用C*言表示法定义的整形,后面可 追*(大小写不敏感的)K,M,G,
T,P或者E(代表<< 10, << 20, << 30, << 40, << 50或者 << 60)。这就指明了
内*文件在内**的末尾。它影响了in itrd可能*放的位置,*为initrd应该被
置在内*末尾的附近。要注意:这个 项同时作用于内*和启动装载器!

initrd=
指定装载的initrd,显然是和启动装载 是独立的文件,而且一些启动装载
器(比如LILO)甚至不需要这个选项。

另外,有些启动装载器在额定的命令 上添*了以下参数:

BOOT_IMAGE=
要*载的启动镜像。同*,也是和bootl oader独立的。

auto
内*不需要用户外界干预自行启动。

如果这些参数被启动装载器添*,强 建议将它们放置在用户指定的或者设 项指
定的命令
行之前。否则,“init=/bin/sh”跟上auto会让人产生*义。

**** 启动内*

跳转到内*入口地址,内*就开始运 了。其入口处于相对于实模式内*部 的段
地址偏移
0x20的位置。这意思是:如果*将实模 内**载到0x90000,内*入口就应该
9020:0000。

在入口处,我们将ds,es,ss一起指向 模式内*代*的起始位置(如果代* *载到
0x90000,那这个位置应该是0x9000),sp *器应该指向*区的顶端,而且还要
禁用**。
*外,为了防*内*出现bug,建议启 装载器作出设置: fs = gs = ds = es = ss。

对于以上的举例,我们这*来实现:

C 代*

1. /* Note: in the case of the "old" kernel protocol, base_ptr must
2. be == 0x90000 at this point; see the previous sample code */
3.
4. seg = base_ptr >> 4;
5.
6. cli(); /* Enter with interrupts disabled! */
7.
8. /* Set up the real-mode kernel stack */
9. _SS = seg;
10. _SP = heap_end;
11.
12. _DS = _ES = _FS = _GS = seg;
13. jmp_far(seg+0x20, 0); /* Run the kernel */


如果启动扇区访问的是一个软驱,建 在内*运行前关*马达。*为内*启 会保
持关**
*状态,这*就*法关*驱动器马达 尤其当装载的内*需要*载软驱的时 (*
*时的软
件驱动器状态未知,*法使用)。


**** 高级的启动装载器hook
如果启动装载器在一个不利的环境下 行(比如在DOS下运行的LOADIN),就有
能不去遵
从*准的内**射,这*的启动装载 可能使用到了如下的hook。如果是启用 了
hook,它
会在适当的时间被内*触发。hook应该 认作绝对最后的手段来使用!

醒目:所有的hook在启用时都需要保*% esp,%ebp,%esi和%edi。

realmode_swtch:
在即将进入保护模式之前出发的一个16 位实模式段外*程序。默认下这*的 序应
该禁用NMI,所以*的程序也应如*。

code32_start:
在刚刚跃迁到保护模式的时候要跳转 的32位平面模式(flat-mode)例程,但是
*程序应在内*解压缩之前执行。除 CS外其他段地址都不能被确认保*( 在的
内*保*,而老版本没有);*应该 己将他们设置为BOOT_DS。
在完成hook之后,应该在*的启动装载 覆盖hook之前跳转到*域*(* 指
code32_start)(如果条件允许,就执行 定位)。