前面我们讲到,MBR 代码只有 512 字节,干不了太多事情。因此我们设计了 loader 程序,在 loader 中完成 kernel 的加载。
所以,在 MBR 中我们要完成对 loader 的加载。
为了项目结构清晰,我将工程目录划分如下
$ tree
.
├── boot
│ ├── Makefile
│ └── mbr.S
├── loader
│ ├── loader.S
│ └── Makefile
└── Makefile
2 directories, 5 files
完整工程代码:链接
boot 延续使用 mbr.S,在前面的工程中,我们在 boot 阶段没干什么事情,仅仅原地跳转。今天我们要让 boot 完成对 loader 的加载,并且跳转到 loader 执行。
我们计划将 loader 加载到内存的 0x8000 处,
mbr.S
.code16 # 让汇编器将程序汇编成 16 位的代码(因为 CPU 上电后处于 16 位的实模式)
.global _start
_start:
# 加载 loader 到内存
mov $0x8000, %bx # 要加载到的内存地址
mov $0x0002, %cx # ch:磁道号(0x00),cl:起始扇区号(0x02),(扇区 0x01 为 MBR)
mov $0x02, %ah # ah:读磁盘命令
mov $1, %al # al:读取的扇区数量,必须小于128,暂时设置成 1 个扇区
mov $0x0080, %dx # dh:磁头号,dl:驱动器号0x80(磁盘1)
int $0x13
# 跳转到 loader
mov $0x8000, %bx
jmp %bx
jmp .
.org 0x1fe
.byte 0x55, 0xAA
简单起见,我们上面代码直接使用 BIOS 提供的软中断来完成对硬盘的读取,参考《BIOS int 13H中断介绍》。
我们先写一个简例
loader.S
.code16
.global _start
_start:
jmp _start
代码执行完 int $0x13 后,就已经将磁盘上的 loader 代码拷贝到了内存的 0x8000 处,从左侧的 监视 窗口可以看到,0x8000 处的数据已经是 0xeb、0xfe 了,和磁盘镜像文件 os.img 的第二个扇区中的内容一致。
在 jmp %bx 处按 F10 单步,就会跳转到内存 0x8000 处执行(左侧 eip = 0x8000),也就是对应 loader 的代码 _start 处。