动手学操作系统(二、编写MBR主引导记录)
文章目录
- 动手学操作系统(二、编写MBR主引导记录)
- 1. 实模式和保护模式
- 2. BIOS与MBR
- 3. MBR程序
- Reference
在之前的学习内容中,我们已经实现了基本的仿真环境bochs的搭建,还首次使用硬盘引导启动了bochs,虽然我们只卡在了启动的界面,但是我们已经顺利的搭建起整个流程了。
1. 实模式和保护模式
实模式(Real Mode)和保护模式(Protected Mode)是 x86 架构的两种不同的工作模式,主要用于管理内存和处理器的功能。实模式是 x86 处理器在启动时默认的运行模式。
它最初是为 8086 处理器设计的,在这种模式下,处理器可以直接访问所有内存、I/O 设备和硬件中断。实模式提供了一种简单的内存管理机制,适用于早期的操作系统,如 MS-DOS。保护模式是 80286 及其后续处理器引入的一种高级工作模式,提供了更强的内存管理和保护机制。
它支持现代操作系统的许多功能,如虚拟内存、多任务处理和内存保护。
实模式和保护的模式的寻址也存在着区别,如下图所示
2. BIOS与MBR
BIOS(Base Input & Output System)是基本输入输出系统,是计算机启动时执行的固件,用于初始化和测试硬件组件,并引导操作系统。BIOS 是计算机最基本的固件之一,通常存储在主板上的只读存储器(ROM)芯片中。
UEFI BIOS(Unified Extensible Firmware Interface),UEFI 是 BIOS 的现代替代品,提供更多功能和更好的用户界面。支持大容量硬盘(超过 2 TB)、安全启动、图形用户界面等。UEFI 具有更灵活的引导管理功能和更好的安全性。
BIOS 是计算机启动时执行的固件,负责硬件初始化和引导操作系统。虽然传统 BIOS 已经使用多年,但随着计算机硬件和软件的发展,UEFI 逐渐取代了传统 BIOS,提供了更多的功能和更好的用户体验。但是我们在这个教程中还是关注于BIOS本身。
MBR(Main Boot Record)是主引导记录,它存在于整个硬盘最开始的扇区,即0盘0道1扇区,这个扇区被称为MBR引导扇区。MBR的功能有
引导加载:当计算机启动时,BIOS/UEFI 读取硬盘的第一个扇区(MBR),并执行主引导程序。主引导程序随后根据分区表信息找到并加载操作系统的引导程序。
管理分区:MBR 包含硬盘分区表的信息,定义了硬盘上最多四个主分区或三个主分区加一个扩展分区(扩展分区可以进一步包含多个逻辑分区)。
我们需要了解的是,在实模式下的1MB内存布局
顶部0xF0000~0xFFFFF
这部分64KB的内存就是ROM(Read Only Memory),里面存放的就算BIOS代码。这块ROM内存是掉电不会清除的,所以当系统被启动的时候,ROM里存放的BIOS首先被加载执行。
BIOS的运行过程是这样的,在开机的一瞬间,CPU的cs:ip
寄存器被强制初始化为0xF000: 0xFFF0
,由于开机的时候处于实模式,按照实模式的寻址方式段基地址要乘以16,也就是右移4位,然后再加上段内偏移地址,则计算得这个实际的物理地址为0xFFFF0
,这个地址便是BIOS程序的入口地址了。从地址0xFFFF0~0xFFFFF
只有16B的空间,这说明BIOS真正的执行脚本并不存储在这里,开机后执行的第一条语句一定是跳转语句jmp
,其实执行的第一条语句是
jmp far f000:e05b
跳转到了0xfe05b
出,说明这里才是BIOS代码真正开始的地方。接下来就是BIOS不断进行检测内存,显卡等外设信息,然后初始化硬件的过程了。
最后的一点任务,校验启动盘中位于0盘0道1扇区(也就是LBA地址为0的扇区)的内容,如果此扇区末尾的两个字节分别是魔数0x55
和0xaa
,BIOS便认为此扇区中确实存在可执行的程序,并把这个扇区的内容加载到物理地址0x7c00
的内存中,一个扇区的大小我们在上一节中设置硬盘扇区大小中设置过了,是512B。然后通过命令
jmp 0:0x7c00
跳转到0x7c00
继续执行MBR程序了。
1.为什么魔数必须是
0x55
和0xaa
?
因为这是按照统一的标准设定的,大家都遵守这个BIOS标准。
2.为什么最后一定跳转到0x7c00
?
这也是大家默认的一个规定
MBR的大小必须是512B,这是为了保证0x55
和0xaa
这两个魔数恰好出现在该扇区的最后两个字节处,即510
和511
字节处,我们使用bochs模拟的是x86平台是小端字节序,所以最后两个字节的内容是0xaa55
,其实就是这两个魔数合起来。
3. MBR程序
我的工程管理路径如下
其中src
用于存放脚本文件,bin
用于存放编译的结果,test
用于存放一些别的测试脚本和结果。
在路径~/d2los/src
中,编写程序mbr.S
cd ~/d2los/src
gedit ./mbr.S
然后写入以下内容
; ~/d2los/src/mbr.S
; MBR主引导程序
;------------------------------------------------------------
SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
; 清屏利用 0x06号功能, 上卷全部行, 实现清屏
; -----------------------------------------------------------
; INT 0x10 功能号: 0x06 功能描述: 上卷窗口
; ------------------------------------------------------
; 输入:
; AH = 功能号 0x06
; AL = 上卷的行数(如果为0, 表示全部)
; BH = 上卷行属性
; (CL, CH) = 窗口左上角的(X, Y)位置
; (DL, DH) = 窗口右下角的(X, Y)位置
; 无返回值
mov ax, 0x600
mov bx, 0x700
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0x184f ; 右下角: (80, 25),
; VGA文本模式中, 一行只能容纳80个字符, 共25行.
; 下标从0开始, 所以0x18=24, 0x4f=79
int 0x10 ; int 0x10
; 获取光标位置
; -----------------------------------------------------------
; .get_cursor 获取当前光标位置, 在光标位置处打印字符.
mov ah, 3 ; 输入: 3号子功能是获取光标位置, 需要存入ah寄存器
mov bh, 0 ; bh寄存器存储的是要获取光标的页号
int 0x10 ; 输出: ch=光标起始行, cl=光标结束行
; dh=光标所在行号, dl=光标所在列号
; 获取光标位置结束
; -----------------------------------------------------------
; 打印字符串
; -----------------------------------------------------------
; 还是用10h中断, 不过这次是调用13号子功能打印字符串
mov ax, message
mov bp, ax ; es:bp 为字符串起始地址, es此时同cs一致,
; 开始时已经为sreg初始化
; 光标位置要用到dx寄存器中的内容, cx中的光标位置可忽略
mov cx, 5 ; cx 为字符串长度, 不包括结束符0的字符个数
mov ax, 0x1301 ; 子功能号13是显示字符串及属性, 要存入ah寄存器,
; al设置显示字符方式 ah=01: 显示字符串,光标跟随移动
mov bx, 0x2 ; bh存储要显示的页号, 此时是第0页,
; bl中是字符属性, 属性黑底绿字(bl = 02h)
int 0x10 ; 执行BIOS 0x10 号中断
; 打印字符串结束
; -----------------------------------------------------------
jmp $ ; 使程序停在此处
message db "1 MBR"
times 510-($-$$) db 0
db 0x55,0xaa
然后编译
cd ~/d2los
nasm -o bin/mbr.bin src/mbr.S
我们就在目录~/d2los/bin
中生成了编译文件mbr.bin
,然后我们需要将这个二进制文件装在到我们为bochs虚拟机指定的虚拟硬盘hardisk60MB.img
(上一节创建的)中,使用dd
命令
cd ~/d2los
dd if=./bin/mbr.bin of=~/bochs/bin/hardisk60MB.img bs=512 count=1 conv=notrunc
然后运行bochs
~/bochs/bin/bochs -f ~/bochs/bin/bochsrc.disk
然后出现了我们的打印结果
Reference
[1]《一个64位操作系统的设计与实现》
[2]《操作系统真象还原》