读取磁盘:LBA方式

Posted mlzrq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读取磁盘:LBA方式相关的知识,希望对你有一定的参考价值。

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

IDE通道1,读写0x1f0-0x1f7号端口

IDE通道2,读写0x170-0x17f号端口

CHS方式:

写0x1f1: 0

写0x1f2: 要读的扇区数

写0x1f3: 扇区号W

写0x1f4: 柱面的低8位

写0x1f5: 柱面的高8位

写0x1f6: 7~5位,101,第4位0表示主盘,1表示从盘,3~0位,磁头号

写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: 7~5位,111,第4位0表示主盘,1表示从盘,3~0位,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: 7~5位,010,第4位0表示主盘,1表示从盘,3~0位,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)

运行结果

技术分享图片















以上是关于读取磁盘:LBA方式的主要内容,如果未能解决你的问题,请参考以下文章

文件系统块如何转换为 lba?

CHS 到 LBA 的映射 -(磁盘存储)

在引导加载程序中使用 int 13h ah=42h 读取 LBA 硬盘

LBA和CHS转换(转)

硬盘寻址 CHS LBA

是否可以在 Android 设备中访问 SD 卡的特定 LBA