學習操作系統原理最好的方法是自己寫一個簡單的操作系統。
本講我們設計一個簡單的讀硬盤實驗。通過一定的方法使硬盤第二個扇區的前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