为什么我从 LBA 到 CHS 的转换不起作用?

问题描述 投票:0回答:1

我正在为 x86 BIOS 开发引导加载程序。在我的第一阶段引导加载程序(MBR)中,我需要从磁盘读取2880个扇区(或更多),然后跳转到放置在磁盘第二个扇区的第二阶段引导加载程序。第二阶段将使用 FAT16 加载内核文件,我稍后将实现它。

该函数适用于低于 65 的 LBA 值,这足以加载用 C 编写的第二阶段。

我已将 BPB_SecPerTrk 定义为 18,将 BPB_NumHeads 定义为 2

这是我的汇编函数代码:

; convert LBA to CHS
; input: si = LBA
; output: ch = cylinder, dh = head, cl = sector
lba_to_chs:
    push bx                         ; save bx
    mov ax, si                      ; load LBA address into ax

    ; Calculate sectors (CL)
    xor dx, dx                      ; clear dx
    div word [BPB_SecPerTrk]        ; ax = LBA / SPT, dx = LBA % SPT
    mov cl, dl                      ; cl = (LBA % SPT) + 1 (sector)
    inc cl                          ; increment cl by 1

    ; Calculate head (DH)
    xor dx, dx                      ; clear dx
    div word [BPB_NumHeads]         ; ax = LBA / (SPT * NumHeads), dx = (LBA / SPT) % NumHeads
    mov dh, dl                      ; dh = (LBA / SPT) % NumHeads (head)

    ; Calculate cylinder (CH)
    mov ch, al                      ; ch = ax (cylinder number, lower 8 bits)
    mov al, ah                      ; al = ah (upper 8 bits of cylinder number)
    shl al, 6                       ; shift upper 2 bits of cylinder to higher bits
    or ch, al                       ; combine them with lower 8 bits of ch

    pop bx                          ; restore bx
    ret

这里也是负责磁盘初始化的函数。

disk_init_lba:
    pusha
    ; check if lba extension is supperted
    mov ah, 0x41                    ; check extensions
    mov bx, 0x55AA                  ; magic number
    mov dl, 0x80                    ; disk number
    int 0x13                        ; call BIOS
    stc                             ; DEBUG: implicitly disable reading disk using int 0x13 extensions
    jc .lba_ext_not_sup             ; if carry flag is set, jump to error handler
    jmp .read_lba_ext               ; if not, jump to read disk using LBA
.read_lba_ext:
    mov si, DAPACK                  ; load DAP address to si
    mov ah, 0x42                    ; extended read function
    mov dl, 0x80                    ; disk number
    int 0x13                        ; call BIOS
    jc .fail                        ; if carry flag is set, jump to error handler
    jmp .ok                         ; if not, jump to success handler
.lba_ext_not_sup:
    call print_disk_lba_sup_fail    ; print failure message
    jmp .read_lba_via_chs           ; jump to read disk using CHS
.read_lba_via_chs:
    clc                             ; clear carry flag if for some reason it was set
    xor si, si                      ; LBA = 0
    xor di, di                      ; set di to 0
    mov bx, START_STAGE1            ; buffer for sector
    jmp .loop                       ; jump to loop
.loop:
    inc si                          ; increment LBA
    add bx, 0x200                   ; next sector buffer
    call lba_to_chs                 ; convert LBA to CHS
    mov ah, 0x02                    ; read disk BIOS function
    mov al, 0x01                    ; number of sectors to read
    mov dl, 0x80                    ; disk number 0
    int 0x13                        ; call BIOS
    jc .retry                       ; if carry flag is set, jump to error handler
    ; FIXME: reading LBAs above 65
    ; TODO: read up to 1.44 MB (2879 sectors)
    cmp si, 65                      ; check if we read enough sectors to fill 1.44 MB
    jle .loop                       ; if true read next sector
    jmp .ok                         ; if not, jump to success handler
.retry:
    inc di                          ; increment di
    cmp di, 3                       ; check if we tried 3 times
    jne .loop                       ; if not, retry
    jmp .fail                       ; if yes, jump to error handler
.fail:
    call print_disk_read_fail       ; print failure message
    jmp .exit                       ; jump to exit
.ok:
    call print_disk_read_ok         ; print success message
    jmp .exit                       ; jump to exit
.exit:
    popa
    ret

我认为这与代码中某些地方的值溢出有关。

assembly x86 kernel bootloader bios
1个回答
0
投票

lba_to_chs

中的错误

@ecm 发现柱面编号的 2 个最高有效位属于 CL 寄存器(位 6 和 7),因此将

or ch, al
更改为
or cl, al
,或者考虑使用下一个较短版本的转换代码:

; IN (si) OUT (cx,dh) MOD (ax,dl)
lba_to_chs:
    mov  ax, si                      ; LBA
    xor  dx, dx
    div  word [BPB_SecPerTrk]
    mov  cx, dx
    inc  cx                          ; Sector
    cwd
    div  word [BPB_NumHeads]
    mov  dh, dl                      ; Head
    shl  ah, 6
    xchg al, ah
    or   cx, ax                      ; Cylinder
    ret

disk_init_lba

中的错误

需要重复磁盘操作的情况并不罕见。这就是 DI 寄存器中重试次数为 3 的原因。然而,您所做的是允许所有扇区一起重试 3 次,而您实际上应该允许每个扇区重试几次 但更糟糕的是,您根本
不重试同一个扇区! “增量 LBA”和“下一个扇区缓冲区”操作在重试时不得干预。

clc ; clear carry flag if for some reason it was set xor si, si ; LBA = 0

不需要这个

clc

,后面的
xor si, si
无论如何都会清除进位标志。

disk_init_lba: pusha ... .read_lba_via_chs: xor si, si ; LBA = 0 mov bx, 0x7C00 ; buffer for sector .NextSector: inc si ; increment LBA add bx, 0x200 ; next sector buffer mov di, 3 ; Tries per sector .NextTry: call lba_to_chs ; convert LBA to CHS mov ax, 0x0201 ; read 1 sector mov dl, 0x80 ; disk number int 0x13 ; call BIOS jc .retry ; FIXME: reading LBAs above 65 ; TODO: read up to 1.44 MB (2879 sectors) cmp si, 65 ; check if we read enough sectors jbe .NextSector jmp .ok ; if not, jump to success handler .retry: dec di ; more tries? jnz .NextTry ; yes .fail: call print_disk_read_fail ; print failure message jmp .exit .ok: call print_disk_read_ok ; print success message .exit: popa ret

; FIXME:读取 65 以上的 LBA

不要修改 BX,而是保持其固定并将 ES 段寄存器前进 32 (512 / 16)。

disk_init_lba: pusha push es ... .read_lba_via_chs: xor si, si ; LBA = 0 mov bx, 0x7E00 ; buffer for sector .NextSector: inc si ; increment LBA mov ax, es ; next sector buffer ES:BX add ax, 32 mov es, ax mov di, 3 ; Tries per sector .NextTry: call lba_to_chs ; convert LBA to CHS mov ax, 0x0201 ; read 1 sector mov dl, 0x80 ; disk number int 0x13 ; call BIOS jc .retry cmp si, 65 ; check if we read enough sectors jbe .NextSector jmp .ok .retry: dec di ; more tries? jnz .NextTry ; yes .fail: call print_disk_read_fail ; print failure message jmp .exit .ok: call print_disk_read_ok ; print success message .exit: pop es popa ret

; TODO:最多读取 1.44 MB(2879 个扇区)

您不能希望读取那么多字节。您当前的代码仅限于使用传统内存,因此最多略小于 655360 字节是可行的...

我想说,对于你的第二阶段来说已经足够了。

© www.soinside.com 2019 - 2024. All rights reserved.