硬盘启动盘,加载操作系统
模拟硬盘加载操作系统
环境:
VMware16
Ubuntu16.04
qemu
bochs 2.7
参考:
启动,BIOS,MBR
硬盘控制器主要端口寄存器
《操作系统真相还原》
1.系统开机流程
暂不构建中断向量表,直接加载MBR
2.BIOS 内存分布
MBR的位置在0x7c00,可用区域仅仅512B。如果在实模式下,要使内核大小超过512B,则需要使用0x500区域。该区域大小约为30KB。
因此使用MBR引导加载SETUP,使得内核大小超过512B。如下图所示:
- mbr.asm,是boot loader,用于将真正的OS内核,即setup.asm中的程序,读入内存
- setup.asm,是OS内核,存放要执行的代码
SETUP_MAIN_ADDR equ 0x500 ; setup地址
boot:
mov edi, SETUP_MAIN_ADDR
; 假设mbr在第一个扇区,setup在第二个扇区
mov ecx, 1 ; 第一个扇区
mov bl, 2 ; 读两个扇区
call read_hd ; 读硬盘,加载操作系统内核
jmp SETUP_MAIN_ADDR ; 跳转到setup
3.硬盘读取方式
常见的硬盘寻找方式有CHS和LBA逻辑块地址
本文使用LBA48寻址。
寄存器如下所示:
-
数据寄存器(Data Register):数据寄存器是最重要的硬盘寄存器之一,用于读写磁盘数据。
-
状态寄存器(Status Register):状态寄存器用于指示当前硬盘的状态,包括硬盘是否忙碌、是否有错误、是否准备就绪等等。
-
命令寄存器(Command Register):命令寄存器用于向硬盘发送指令和控制信息,例如读写命令、格式化命令、复位硬盘等等。
-
错误(Error)寄存器:错误寄存器在出现硬盘错误时由控制器更新,用于记录当前硬盘的错误状态,并提示操作系统或用户进行处理。
-
LBA寄存器(Logical Block Addressing Register):LBA寄存器用于存储逻辑块地址,它指定要读取或写入的硬盘扇区编号。
-
扇区数寄存器(Sector Count Register):扇区数寄存器用于指示要读取或写入的扇区数。
-
设备寄存器(Device Register):设备寄存器用于存储硬盘的设备地址和访问模式等信息。
读取硬盘扇区操作基本流程:
1.设置扇区数目寄存器(Sector Count Register)、柱面号低8位寄存器(Cylinder Low Register)、柱面号中8位寄存器(Cylinder Mid Register)、柱面号高8位寄存器(Cylinder High Register),以确定读写位置和范围。
; 设置读取的扇区数目
mov dx, 0x1f2 ; Sector count
mov al, bl
out dx, al
; 设置开始读取的扇区号
inc dx ; 0x1f3 LBA low 8bit
mov al, cl
out dx, al
inc dx ; 0x1f4 LBA mid 8bit
mov al, ch
out dx, al
inc dx ; 0x1f5 LBA high 8bit
shr ecx, 16
mov al, cl
out dx, al
2.设置设备寄存器(Device Register),用于存储硬盘的设备地址和访问模式等信息
; 0x1f6 8bit
; 0-3 位iba地址的24-27
; 4 0表示主盘 1表示从盘
; 5、7位固定为1
; 6 0表示CHS模式,1表示LAB模式
inc dx
shr ecx, 8
and cl, 0b0000_1111 ; 0~3 bit
mov al, 0b1110_0000 ; 4~7 bit
or al, cl
out dx, al
3.设置命令Command寄存器写入命令硬盘就开始工做:
- identify:0xEC,硬盘识别
- read sector:0x20,读扇区
- write sector:0x30,写扇区
; 0x1f7 8bit 命令或状态端口
inc dx
mov al, 0x20 ; 读扇区
out dx, al
4.检测硬盘状态
; 一直等待,直到硬盘的状态是:不繁忙,数据已准备好
; 即第7位为0,第3位为1,第0位为0
.wait_hd_prepare:
xchg bx, bx
mov dx, 0x1f7
.check_status:
in al, dx
and al, 0b1000_1000
cmp al, 0b0000_1000
jnz .check_status
ret
4.读取硬盘,一次读两个字节,读256次,共512B,即读一个扇区
read_hd_sector:
mov dx, 0x1f0 ; 0x1f0 Data
mov cx, 256
.read_word:
in ax, dx
mov [edi], ax
add edi, 2
loop .read_word
ret
4.测试效果
使用 bximage 工具创建一个大小为 16MB、每个扇区大小为 512 字节的纯二进制格式磁盘镜像文件
bximage -q -hd=16 -func=create -sectsize=512 -imgmode=flat $(BUILD)/$(HD_IMG_NAME)
使用dd命令写入磁盘镜像。
# 将编译输出目录下的 boot.o 文件写入到生成的磁盘镜像的第 0 个扇区中,实现将 boot.o 文件写入到操作系统启动扇区的功能
dd if=${BUILD}/boot/boot.o of=$(BUILD)/$(HD_IMG_NAME) bs=512 seek=0 count=1 conv=notrunc
# 将编译输出目录下的 setup.o 文件写入到生成的磁盘镜像的第 1 个扇区,实现向操作系统传递硬件和引导信息的功能。
dd if=${BUILD}/boot/setup.o of=$(BUILD)/$(HD_IMG_NAME) bs=512 seek=1 count=1 conv=notrunc
使用qemu加载测试:
qemu-system-i386 -m 32M -boot c -hda $(BUILD)/$(HD_IMG_NAME)
实现模拟硬盘加载操作系统,且完成硬盘扇区内容跳转。