个人认为 BootLoader 本身是很无趣的,和操作系统的关系也并不大,更多是在和硬件的关系上。如果只是想研究操作系统的话,BootLoader 本身能看懂就可以了。
BIOS 引导程序是整个系统启动的时候做的第一件事,BIOS自检结束后会根据启动选项设置(这里指软驱启动)去选择启动设备,即检测软盘的第0磁头第0磁道第1扇区,是否以数值0x55和0xaa两字节作为结尾。如果是,那么BIOS就认为这个扇区是一个Boot Sector(引导扇区),进而把此扇区的数据复制到物理内存地址 0x7c00
处
最简单的 Boot 引导 #
那我们第一步显然实现最简单的一个引导程序。
org 0x7c00
BaseOfStack equ 0x7c00
Label_Start:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
;======= clear screen
mov ax, 0600h
mov bx, 0700h
mov cx, 0
mov dx, 0184fh
int 10h
;======= set focus
mov ax, 0200h
mov bx, 0000h
mov dx, 0000h
int 10h
;======= display on screen : Start Booting......
mov ax, 1301h
mov bx, 000fh
mov dx, 0000h
mov cx, 10
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartBootMessage
int 10h
;======= reset floppy
xor ah, ah
xor dl, dl
int 13h
jmp $
StartBootMessage: db "Start Boot"
;======= fill zero until whole sector
times 510 - ($ - $$) db 0
dw 0xaa55
然后我们创建一个 FD(软盘)
的文件系统的文件。之后构建这个 bin
$ nasm boot.asm -o boot.bin
然后将这个数据写入软盘
$ dd if=boot.bin of=boot.img bs=512 count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 8.6105e-05 s, 5.9 MB/s
然后我们使用 bochs 模拟器将其跑起来
本地启动配置如下
megs: 148
floppya: 1_44=boot.img, status=inserted
boot: floppy
floppy_bootsig_check: disabled=0
log: bochsout.txt
mouse: enabled=0
# 当前版本的 ubuntu:22.04 使用 latest 报错
romimage: file=/usr/share/bochs/BIOS-bochs-legacy
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
加载 Loader 程序 #
说到加载 Loader
程序,最理想的方法自然是从文件系统中把 Loader
程序加载到内存里。考虑到代码的易实现性,本操作系统将选用逻辑简单的 FAT12
文件系统来装载 Loader
程序和内核程序。
首先定义了 fat12.inc
针对 fat12
文件系统的基础定义
org 0x7c00
BaseOfStack equ 0x7c00
BaseOfLoader equ 0x1000
OffsetOfLoader equ 0x00
RootDirSectors equ 14
SectorNumOfRootDirStart equ 19
SectorNumOfFAT1Start equ 1
SectorBalance equ 17
jmp short Label_Start
nop
BS_OEMName db 'MINEboot'
BPB_BytesPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xf0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db 'boot loader'
BS_FileSysType db 'FAT12 '
随即准备一个读取函数
;======= read one sector from floppy
Func_ReadOneSector:
push bp
mov bp, sp
sub esp, 2
mov byte [bp - 2], cl
push bx
mov bl, [BPB_SecPerTrk]
div bl
inc ah
mov cl, ah
mov dh, al
shr al, 1
mov ch, al
and dh, 1
pop bx
mov dl, [BS_DrvNum]
Label_Go_On_Reading:
mov ah, 2
mov al, byte [bp - 2]
int 13h
jc Label_Go_On_Reading
add esp, 2
pop bp
ret
INT 13h,AH=02h 功能:读取磁盘扇区。
- AL=读入的扇区数(必须非0)
- CH=磁道号(柱面号)的低8位
- CL=扇区号1
63(bit 05),磁道号(柱面号)的高2位(bit6~7, 只对硬盘有效) - DH=磁头号;DL=驱动器号(如果操作的是硬盘驱动器,bit 7必须被置位)
- ES:BX=>数据缓冲区。
紧接着就是独缺 Loader.bin
文件了
;======= search loader.bin
mov word [SectorNo], SectorNumOfRootDirStart
Lable_Search_In_Root_Dir_Begin:
cmp word [RootDirSizeForLoop], 0
jz Label_No_LoaderBin
dec word [RootDirSizeForLoop]
mov ax, 00h
mov es, ax
mov bx, 8000h
mov ax, [SectorNo]
mov cl, 1
call Func_ReadOneSector
mov si, LoaderFileName
mov di, 8000h
cld
mov dx, 10h
Label_Search_For_LoaderBin:
cmp dx, 0
jz Label_Goto_Next_Sector_In_Root_Dir
dec dx
mov cx, 11
Label_Cmp_FileName:
cmp cx, 0
jz Label_FileName_Found
dec cx
lodsb
cmp al, byte [es:di]
jz Label_Go_On
jmp Label_Different
Label_Go_On:
inc di
jmp Label_Cmp_FileName
Label_Different:
and di, 0ffe0h
add di, 20h
mov si, LoaderFileName
jmp Label_Search_For_LoaderBin
Label_Goto_Next_Sector_In_Root_Dir:
add word [SectorNo], 1
jmp Lable_Search_In_Root_Dir_Begin
执行 Loader 程序 #
将实现Boot引导程序的最后一步,那就是跳转至Loader引导加载程序处,向其移交处理器的控制权。代码清单3-13完成了向Loader引导加载程序移交执行权的工作,即跳转至物理地址0x10000处执行loader.bin程序。
Label_File_Loaded:
jmp BaseOfLoader:OffsetOfLoader
准备一个最简单的 Loader
程序试试
org 10000h
mov ax, cs
mov ds, ax
mov es, ax
mov ax, 0x00
mov ss, ax
mov sp, 0x7c00
;======= display on screen : Start Loader......
mov ax, 1301h
mov bx, 000fh
mov dx, 0200h ;row 2
mov cx, 12
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartLoaderMessage
int 10h
jmp $
;======= display messages
StartLoaderMessage: db "Start Loader"