Boot 引导

个人认为 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

image

加载 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 0~5),磁道号(柱面号)的高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"
comments powered by Disqus