学习操作系统原理最好的方法是自己写一个简单的操作系统。
本讲我们设计一个简单的读硬盘实验。通过一定的方法使硬盘第二个扇区的前3个字节依次为1、2、3,最后3个字节依次为3、2、1,中间的506个字节全为0。然后通过读硬盘程序将硬盘第二个扇区的数据读取到内存0x7e00-0x7fff的地方,也就是内存中MBR之后的512个字节。最后通过QEMU+DGB调试的方式来查看内存中0x7e00-0x7fff的数据,是否与硬盘第二个扇区中的数据一致,如果一致说明读硬盘成功。
本讲代码文件共2个:
- data1.asm
- boot1.asm
下面我们开始实验。
一、设置硬盘第二个扇区中的数据
data1.asm代码如下:
db 1
db 2
db 3
times 506 db 0
db 3
db 2
db 1
data1.asm就是生成512字节的数据,前3个字节依次是1、2、3,最后3个字节依次为3、2、1,中间的506个字节全为0。
在PowerShell中输入如下命令:
nasm data1.asm -o data1.bin
hexdump data1.bin -C
上述命令是将data1.asm通过汇编器生成了二进制文件data1.bin,然后通过hexdump命令查看data1.bin是否正确。截图如下:
从上面截图可以看到,生成的data.bin文件共512字节,其中前3个字节依次为1、2、3,最后3个字节依次为3、2、1,中间的506个字节全为0。下面将data1.bin文件写入到虚拟硬盘的第二个扇区中。
在PowerShell中输入如下命令:
dd conv=notrunc if=data1.bin of=/media/VMShare/GrapeOS.img seek=1
hexdump /media/VMShare/GrapeOS.img -C
上面dd命令中的参数seek=1意思是在写入GrapeOS.img时,跳过1个写入块,写入块默认大小为512字节,也就将data1.bin写入到虚拟硬盘GrapeOS.img的第二个扇区中。截图如下:
从上面截图中可以看到,虚拟硬盘第二个扇区中的数据达到了我们的实验要求。
二、读硬盘程序
boot1.asm的代码如下:
;定义常量
DISK_BUFFER equ 0x7e00 ;读硬盘临时存放数据用的缓存区,放到boot程序之后。
org 0x7c00
;初始化段寄存器
mov ax,cs
mov ds,ax ;ds指向与cs相同的段
mov esi,1 ;读取硬盘的第2个扇区
mov di,DISK_BUFFER
call func_read_one_sector
stop:
hlt
jmp stop
;读取硬盘1个扇区(主硬盘控制器主盘)
;输入参数:esi,ds:di。
;esi LBA扇区号
;ds:di 将数据写入到的内存起始地址
;输出参数:无。
func_read_one_sector:
;第1步:检查硬盘控制器状态
mov dx,0x1f7
.not_ready1:
nop ;nop相当于稍息 hlt相当于睡觉
in al,dx ;读0x1f7端口
and al,0xc0 ;第7位为1表示硬盘忙,第6位为1表示硬盘控制器已准备好,正在等待指令。
cmp al,0x40 ;当第7位为0,且第6位为1,则进入下一个步。
jne .not_ready1 ;若未准备好,则继续判断。
;第2步:设置要读取的扇区数
mov dx,0x1f2
mov al,1
out dx,al ;读取1个扇区
;第3步:将LBA地址存入0x1f3-0x1f6
mov eax,esi
;LBA地址7-0位写入端口0x1f3
mov dx,0x1f3
out dx,al
;LBA地址15-8位写入端口写入0x1f4
shr eax,8
mov dx,0x1f4
out dx,al
;LBA地址23-16位写入端口0x1f5
shr eax,8
mov dx,0x1f5
out dx,al
;第4步:设置device端口
shr eax,8
and al,0x0f ;LBA第24-27位
or al,0xe0 ;设置7-4位为1110,表示LBA模式,主盘
mov dx,0x1f6
out dx,al
;第5步:向0x1f7端口写入读命令0x20
mov dx,0x1f7
mov al,0x20
out dx,al
;第6步:检测硬盘状态
.not_ready2:
nop ;nop相当于稍息 hlt相当于睡觉
in al,dx ;读0x1f7端口
and al,0x88 ;第7位为1表示硬盘忙,第3位为1表示硬盘控制器已准备好数据传输。
cmp al,0x08 ;当第7位为0,且第3位为1,进入下一步。
jne .not_ready2 ;若未准备好,则继续判断。
;第7步:从0x1f0端口读数据
mov cx,256 ;每次读取2字节,一个扇区需要读256次。
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [di],ax
add di,2
loop .go_on_read
ret
times 510-($-$$) db 0
db 0x55,0xaa
上面的代码主要就是读硬盘函数func_read_one_sector,总共分7个步骤,与上讲中的读硬盘操作步骤一致。结合代码注释和之前讲的知识,大家应该是可以看懂的。
下面我们将编译boot1.asm,并将生成的二进制文件写入到虚拟硬盘的第一个扇区。在PowerShell中输入如下命令:
nasm boot1.asm -o boot1.bin
dd conv=notrunc if=boot1.bin of=/media/VMShare/GrapeOS.img
截图如下:
在Windows的cmd命令行中启动QEMU调试模式:
qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img -S -s
截图如下:
在PowerShell中启动GDB:
[root@CentOS7 Lesson20]# gdb
(gdb) target remote 192.168.10.102:1234
(gdb) b *0x7c00
(gdb) c
Continuing.
Breakpoint 1, 0x00007c00 in ?? ()
(gdb) x /512xb 0x7e00
截图如下:
从截图中可以看到,此时程序停在了断点0x7c00处,此时内存中0x7e00-0x7fff中的数据都是0。
我们输入命令c
,让程序继续运行几秒钟,然后Ctrl+C,让程序暂停。此时读硬盘程序应该已经执行完了,我们再来查看一下内存0x7e00-0x7fff中的数据。截图如下:
从截图中可以看到,此时内存中0x7e00-0x7fff的数据和硬盘第二扇区中的数据一致,说明读取成功,实验完毕。
本讲视频版地址:https://www.bilibili.com/video/BV1JY411z7VT/
配套的代码与资料在:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS操作系统交流QQ群:643474045