Bootloader
Mar 27, 2024 20:30 · 1304 words · 3 minute read
什么是 Bootloader
Bootloader 是一类用于在计算机开机时加载操作系统的小软件。而对处理器来说,和操作系统一样,Bootloader 也只不过是它盲目执行的一串代码。
x86 系统(IBM 兼容机)开机后 CPU 执行的第一个程序(就是那个蓝色的界面),是 BIOS(Basic Input and Output System)。这个初始化程序提前被烧录在了一个 ROM(只读存储器)中。
x86 系统刚开机时 CPU 以实模式运行,20 位地址总线只能寻址 1M,将 1M 内存空间最上面的 0xF0000 - 0xFFFFF 64K(第一个段)映射给 ROM。刚上电时第一条指令指向 0xFFFF0(CS 设置为 0xFFFF;IP 设置为 0x0000,0xFFFF « 4 + 0x0000),就会执行 ROM 中的代码,BIOS 开始检查硬件。如果一切就绪,BIOS 接着会去加载二级 Bootloader。
保护模式则能够寻址 4G。
0xAA55
装载着操作系统的磁盘的前 512 字节被称作 Bootsector(启动扇区)或者 MBR(Master Boot Record),这是一个约定。有效的启动扇区最后两个字节必须是 0xAA55,BIOS 就会认为这块磁盘是可启动的。
理想状态下,启动扇区会放置另一个 Bootloader,比如 GRUB。BIOS 在结束前将启动扇区中的内容加载到内存的 0x7C00 位置,通过 JMP 将控制权交给这部分代码。
下面给一个简单的 Bootloader 源码,GRUB 则要复杂得多:
org 7C00h
jmp short Start ;Jump over the data (the 'short' keyword makes the jmp instruction smaller)
Msg: db "Hello World! "
EndMsg:
Start: mov bx, 000Fh ;Page 0, colour attribute 15 (white) for the int 10 calls below
mov cx, 1 ;We will want to write 1 character
xor dx, dx ;Start at top left corner
mov ds, dx ;Ensure ds = 0 (to let us load the message)
cld ;Ensure direction flag is cleared (for LODSB)
Print: mov si, Msg ;Loads the address of the first byte of the message, 7C02h in this case
;PC BIOS Interrupt 10 Subfunction 2 - Set cursor position
;AH = 2
Char: mov ah, 2 ;BH = page, DH = row, DL = column
int 10h
lodsb ;Load a byte of the message into AL.
;Remember that DS is 0 and SI holds the
;offset of one of the bytes of the message.
;PC BIOS Interrupt 10 Subfunction 9 - Write character and colour
;AH = 9
mov ah, 9 ;BH = page, AL = character, BL = attribute, CX = character count
int 10h
inc dl ;Advance cursor
cmp dl, 80 ;Wrap around edge of screen if necessary
jne Skip
xor dl, dl
inc dh
cmp dh, 25 ;Wrap around bottom of screen if necessary
jne Skip
xor dh, dh
Skip: cmp si, EndMsg ;If we're not at end of message,
jne Char ;continue loading characters
jmp Print ;otherwise restart from the beginning of the message
times 0200h - 2 - ($ - $$) db 0 ;Zerofill up to 510 bytes
dw 0AA55h ;Boot Sector signature
;OPTIONAL:
;To zerofill up to the size of a standard 1.44MB, 3.5" floppy disk
;times 1474560 - ($ - $$) db 0
-
将上面代码写入 floppy.asm 文件并编译成 Bootloader 镜像
$ nasm -f bin -o floppy.img floppy.asm
-
把 Bootloader 镜像写入磁盘
dd if=floppy.img of=/dev/vdb
GRUB
我们常用 grub2 来修改操作系统启动时的内核参数:
vim /etc/default/grub
修改内核参数grub2-mkconfig -o /boot/grub2/grub.cfg
生成 grub2 配置grub2-install /dev/vda
将 grub2 生成的 Bootloader 镜像 boot.img(不超过 512B)写入磁盘的启动扇区(MBR),和上面的dd
命令效果差不多
但 512 字节的程序实在太小了,能做的事有限,所以 GRUB 分成两个阶段:
- 阶段 2 的镜像位置(所在的第一个扇区)已经写死在了 boot.img Bootloader 程序中,就由它来加载
- 有着 grub2 全部功能的完整镜像 core.img
GRUB 各种镜像文件参考:https://www.gnu.org/software/grub/manual/grub/html_node/Images.html
当加载到 grub2 内核 kernel.img(不是 Linux 内核)前,1M 内存空间实在不够用了,在这里就要释放 32 位寻址(4GB)的能力,从实模式切换到保护模式(打开 Gate A20)。kernel.img 是压缩过的,下面就要对其解压并加载到内存中(这时候 4GB 就非常大了),然后跳转运行 kernel.img 的代码。
linux16 命令表示加载指定的操作系统内核文件,并传递内核启动参数。选定操作系统后,就会读取并加载 Linux 内核镜像到内存中,并跳转运行内核。