LBA簡介
磁盤讀取發展
IO操作讀取硬盤的三種方式:
-
chs方式 :小於8G (8064MB)
-
LBA28方式:小於137GB
-
LBA48方式:小於144,000,000 GB
LBA方式訪問使用了data寄存器,LBA寄存器(總共3個),device寄存器,command寄存器來完成的。
LBA28和LBA48方式:
LBA28方式使用28位來描述一個扇區地址,最大支持128GB的硬磁盤容量。
LBA28的寄存器
寄存器 | 端口 | 作用 |
---|---|---|
data寄存器 | 0x1F0 | 已經讀取或寫入的數據,大小為兩個字節(16位數據) 每次讀取1個word,反復循環,直到讀完所有數據 |
features寄存器 | 0x1F1 | 讀取時的錯誤信息 寫入時的額外參數 |
sector count寄存器 | 0x1F2 | 指定讀取或寫入的扇區數 |
LBA low寄存器 | 0x1F3 | lba地址的低8位 |
LBA mid寄存器 | 0x1F4 | lba地址的中8位 |
LBA high寄存器 | 0x1F5 | lba地址的高8位 |
device寄存器 | 0x1F6 | lba地址的前4位(占用device寄存器的低4位) 主盤值為0(占用device寄存器的第5位) 第6位值為1 LBA模式為1,CHS模式為0(占用device寄存器的第7位) 第8位值為1 |
command寄存器 | 0x1F7 | 讀取,寫入的命令,返回磁盤狀態 1 讀取扇區:0x20 磁盤識別:0xEC |
IDE通道1,讀寫0x1f0-0x1f7號端口
IDE通道2,讀寫0x170-0x17f號端口
CHS方式:
寫0x1f1: 0
寫0x1f2: 要讀的扇區數
寫0x1f3: 扇區號W
寫0x1f4: 柱面的低8位
寫0x1f5: 柱面的高8位
寫0x1f6: 75位,101,第4位0表示主盤,1表示從盤,30位,磁頭號
寫0x1f7: 0x20為讀, 0x30為寫
讀0x1f7: 第4位為0表示讀寫完成,否則要一直循環等待
讀0x1f0: 每次讀取1個word,反復循環,直到讀完所有數據
24-bit LBA方式:
寫0x1f1: 0
寫0x1f2: 要讀的扇區數
寫0x1f3: LBA參數的0~7位
寫0x1f4: LBA參數的8~15位
寫0x1f5: LBA參數的16~23位
寫0x1f6: 75位,111,第4位0表示主盤,1表示從盤,30位,LBA參數的24~27位
寫0x1f7: 0x20為讀, 0x30為寫
讀0x1f7: 第4位為0表示讀寫完成,否則要一直循環等待
讀0x1f0: 每次讀取1個word,反復循環,直到讀完所有數據
48-bit LBA方式:
寫兩次0x1f1端口: 0
寫兩次0x1f2端口: 第一次要讀的扇區數的高8位,第二次低8位
寫0x1f3: LBA參數的24~31位
寫0x1f3: LBA參數的0~7位
寫0x1f4: LBA參數的32~39位
寫0x1f4: LBA參數的8~15位
寫0x1f5: LBA參數的40~47位
寫0x1f5: LBA參數的16~23位
寫0x1f6: 75位,010,第4位0表示主盤,1表示從盤,30位,0
寫0x1f7: 0x24為讀, 0x34為寫
LBA和CHS的的對應關系
雖然LBA和CHS的兩種定位方式不同,但其實兩者間還是有一個轉換關系的。
讀取硬盤
1)sector count寄存器寄存器寫入讀取的扇區數
2)LBA low寄存器,LBA mid寄存器,LBA high寄存器寫入lba地址
3)device寄存器寫入lba地址和讀取模式
4)command寄存器寫入寫入命令
5)讀取兩個字節數據,多次循環直到讀取完扇區數據。
代碼
boot.asm
引導文件,初始化屏幕后,讀取硬盤並加載4個扇區到內存位置[0x90000]處。然后跳轉到0x90000處執行指令。
;Rats OS
;Tab=4
[bits 16]
org 0x7c00 ;指明程序的偏移的基地址
;----------- loader const ------------------
LOADER_SECTOR_LBA equ 0x1 ;第2個邏輯扇區開始
LOADER_SECTOR_COUNT equ 9 ;讀取9個扇區
LOADER_BASE_ADDR equ 0x9000 ;內存地址0x9000
;-------------------------------------------
;引導扇區代碼
jmp Entry
db 0x90
db "RATSBOOT" ;啟動區的名稱可以是任意的字符串(8字節)
;程序核心內容
Entry:
;------------------
;初始化寄存器
mov ax,0
mov ss,ax
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov gs,ax
mov sp,0x7c00
;------------------
;清屏
mov ah,0x06 ;清除屏幕
mov al,0
mov cx,0
mov dx,0xffff
mov bh,0x17 ;屬性為藍底白字
int 0x10
mov ah,0x02 ;光標位置初始化
mov dx,0
mov bh,0
mov dh,0x0
mov dl,0x0
int 0x10
;------------------
;讀取硬盤1-10扇區
mov ebx,LOADER_SECTOR_LBA ;LBA扇區號
mov cx,LOADER_SECTOR_COUNT ;讀取扇區數
mov di,LOADER_BASE_ADDR ;寫入內存地址
call Func_ReadLBA16
jmp LOADER_BASE_ADDR
; ------------------------------------------------------------------------
; 讀取磁盤:Func_ReadLBA16
; 參數:
; ebx 扇區邏輯號
; cx 讀入的扇區數,8位
; di 讀取后的寫入內存地址
; ------------------------------------------------------------------------
Func_ReadLBA16:
;設置讀取的扇區數
mov al,cl
mov dx,0x1F2
out dx,al
;設置lba地址
;設置低8位
mov al,bl
mov dx,0x1F3
out dx,al
;設置中8位
shr ebx,8
mov al,bl
mov dx,0x1F4
out dx,al
;設置高8位
shr ebx,8
mov al,bl
mov dx,0x1F5
out dx,al
;設置高4位和device
shr ebx,8
and bl,0x0F
or bl,0xE0
mov al,bl
mov dx,0x1F6
out dx,al
;設置commond
mov al,0x20
mov dx,0x1F7
out dx,al
.check_status:;檢查磁盤狀態
nop
in al,dx
and al,0x88 ;第4位為1表示硬盤准備好數據傳輸,第7位為1表示硬盤忙
cmp al,0x08
jnz .check_status ;磁盤數據沒准備好,繼續循環檢查
;設置循環次數到cx
mov ax,cx ;乘法ax存放目標操作數
mov dx,256
mul dx
mov cx,ax ;循環次數 = 扇區數 x 512 / 2
mov bx,di
mov dx,0x1F0
.read_data:
in ax,dx ;讀取數據
mov [bx],ax ;復制數據到內存
add bx,2 ;讀取完成,內存地址后移2個字節
loop .read_data
ret
FillSector:
resb 510-($-$$) ;處理當前行$至結束(1FE)的填充
db 0x55, 0xaa
loader.asm
被引導扇區加載到0x90000位置,執行輸出hello in loader
文字
;Rats OS
;Tab=4
[bits 16]
section loader vstart=LOADER_BASE_ADDR ;指明程序的偏移的基地址
;----------- loader const ------------------
LOADER_BASE_ADDR equ 0x9000 ;內存地址0x9000
;---------------------------------------
jmp Entry
;程序核心內容
Entry:
;---------------------------
;輸出字符串
mov si,HelloMsg ;將HelloMsg的地址放入si
mov dh,0 ;設置顯示行
mov dl,0 ;設置顯示列
call Func_Sprint ;調用函數
jmp $ ;讓CPU掛起,等待指令
; ------------------------------------------------------------------------
; 顯示字符串函數:Func_Sprint
; 參數:
; si = 字符串開始地址,
; dh = 第N行,0開始
; dl = 第N列,0開始
; ------------------------------------------------------------------------
Func_Sprint:
mov cx,0 ;BIOS中斷參數:顯示字符串長度
mov bx,si
.len:;獲取字符串長度
mov al,[bx] ;讀取1個字節到al
inc bx ;讀取下個字節
cmp al,0 ;是否以0結束
je .sprint
inc cx ;計數器
jmp .len
.sprint:;顯示字符串
mov bx,si
mov bp,bx
mov bx,ds
mov es,bx ;BIOS中斷參數:計算[ES:BP]為顯示字符串開始地址
mov ah,0x13 ;BIOS中斷參數:中斷模式
mov al,0x01 ;BIOS中斷參數:輸出方式
mov bh,0x0 ;BIOS中斷參數:指定分頁為0
mov bl,0x1F ;BIOS中斷參數:顯示屬性,指定白色文字
int 0x10 ;調用BIOS中斷操作顯卡。輸出字符串
ret
; ------------------------------------------------------------------------
;准備顯示字符串
HelloMsg: db "hello in loader!",0
times 512-($-$$) db 0 ; 處理當前行$至結束(1FE)的填充
運行
創建Makefile文件,並執行make命令
# tools
PLATFORM=Linux
NASM=nasm
QEMU=qemu-system-x86_64
QEMU-IMG=qemu-img
BOCHS=bochs
BX-IMG=bximage
# args
boot=boot
build=build
target: prepare img
$(BOCHS) -f bochsrc.me
img: $(build)/ratsos.img
@echo "build img completed"
$(build)/ratsos.img:$(build)/boot.bin $(build)/loader.bin
$(BX-IMG) -hd -mode=flat -size=32 -q $(build)/ratsos.img
sleep 1
dd if=$(build)/boot.bin of=$(build)/ratsos.img bs=512 count=1 conv=notrunc
dd if=$(build)/loader.bin of=$(build)/ratsos.img bs=512 count=1 seek=1 conv=notrunc
$(build)/%.bin: $(boot)/%.asm
$(NASM) -f bin -o $(build)/$*.bin $(boot)/$*.asm
prepare: $(build)
@echo "prepare dir $(build)"
ifeq ($(build), $(wildcard $(build)))
@echo "build directory exist..."
else
mkdir -p $(build)
endif
clean:
@echo "clean dir $(build)"
rm -rf $(build)/*
platform:
@echo $(PLATFORM)
運行結果