USB 硬盘仿真导致磁盘读取失败(BIOS int 13)?

USB hard disk emulation cause a disk read to fail (BIOS int 13)?

一些背景:

我正在开发一个基本的引导加载程序,它使用 BIOS INT 13h AH=02h 中断将辅助引导加载程序读入内存。我已经在模拟器(Virtualbox、Qemu 和 Bochs)中使用它了。

随后,我在我的引导加载程序中添加了一个 BPB(BIOS 参数块),制作了一个 bootable USB,并在我的真机上使用 USB Floppy Emulation(我在配置中设置)对其进行了测试我的真实机器的 BIOS 屏幕)。它就像一个魅力。

在我自己的机器上测试引导加载程序后,我在另一台更新的机器上测试了它。这台新计算机在其 BIOS 配置中没有软盘仿真选项,因此无法从 USB 驱动器启动。因此,在 this osdev wikipage 之后,我在 MBR 的末尾添加了一个分区 -table 以便较新的机器可以从 USB 启动。

问题:

添加分区 table 代码后,引导加载程序无法将辅助引导加载程序加载到内存中,BIOS INT 13h 失败。我不知道为什么会发生这种情况,因为我没有更改任何实际的引导加载程序代码。我刚刚添加了 64 位 MBR 分区 table 并且立即将数据读入内存失败。

BPB(BIOS参数块)&磁盘访问例程

bits    16 
org 0x7C00

jmp start
nop
;------------------------------------------;
;  Standard BIOS Parameter Block, "BPB".   ;
;------------------------------------------;
     bpbOEM         db  'MSDOS5.0'
     bpbSectSize    dw  512
     bpbClustSize   db  1
     bpbReservedSe  dw  1
     bpbFats        db  2
     bpbRootSize    dw  224
     bpbTotalSect   dw  2880
     bpbMedia       db  240
     bpbFatSize     dw  9
     bpbTrackSect   dw  18
     bpbHeads       dw  2
     bpbHiddenSect  dd  0
     bpbLargeSect   dd  0
     ;---------------------------------;
     ;  extended BPB for FAT12/FAT16   ;
     ;---------------------------------;
     bpbDriveNo     db  0
     bpbReserved    db  0
     bpbSignature   db  41            
     bpbID          dd  1
     bpbVolumeLabel db  'BOOT FLOPPY'
     bpbFileSystem  db  'FAT12   '

drive_n: db 0
start: 
    mov [drive_n], dl

    ; setup segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; setup stack
    cli
    mov ss, ax
    mov sp, 0x7C00   ; stack will grow downward to lower adresses
    sti

    ; write start string
    mov si, start_str    ; start_str = pointer to "Bootloader Found..."
    call write_str       ; routine that prints string in si register to screen 

    ; read bootstrapper into memory
    mov dl, [drive_n]; drive number
    mov dh, 0x00    ; head (base = 0)
    mov ch, 0x00    ; track /cylinder = 0
    mov cl, 0x02    ; (1= bootloader, 2=start of bootstrapper
    mov bx, 0x7E00  ; location to load bootstrapper 
    mov si, 0x04    ; number of attempts

    ; attempt read 4 times 
  read_floppy:
    ; reset floppy disk
    xor ax, ax
    int 0x13

    ; check if attempts to read remain, if not, hlt system (jmp to fail_read)
    test    si, si
    je  fail_read   ; *** This jump happens only on real machines with 
    dec si          ; USB hard drive emulation ***

    ; attempt read 
    mov ah, 0x02    ; select read
    mov al, 0x0F    ; num sectors
    int     0x13
    jc  read_floppy

    ...             ; continue onward happily! (without any errors)

MBR 分区Table

; 0x1b4
db "12345678", 0x0, 0x0     ; 10 byte unique id

; 0x1be         ; Partition 1 -- create one big partition that spans the whole disk (2880 sectors, 1.44mb)
db 0x80         ; boot indicator flag = on

; start sector
db 0            ; starting head = 0
db 0b00000001   ; cyilinder = 0, sector = 1 (2 cylinder high bits, and sector. 00 000001 = high bits db 0x00)
db 0            ; 7-0 bits of cylinder (insgesamt 9 bits) 

; filesystem type
db 1            ; filesystem type = fat12

; end sector = 2880th sector (because a floppy disk is 1.44mb)
db 1            ; ending head = 1
db 18           ; cyilinder = 79, sector = 18 (2 cylinder high bits, and sector. 00 000001 = high bits db 0x00)
db 79           ; 7-0 bits of cylinder (insgesamt 9 bits) 

dd 0            ; 32 bit value of number of sectors between MBR and partition
dd 2880         ; 32 bit value of total number of sectors

; 0x1ce         ; Partition 2
times 16 db 0

; 0x1de         ; Partition 3
times 16 db 0

; 0x1ee         ; Parititon 4
times 16 db 0

; 0x1fe         ; Signature
dw  0xAA55

问题

当且仅当BIOS中启用了USB硬盘驱动器仿真时,导致读取磁盘失败的原因是什么?我已经尝试更改分区 table 和 BPB,但似乎没有任何效果。我打赌这与计算机处理软盘和硬盘驱动器信息的方式不同有关,但很难找到这方面的任何信息。

如有任何帮助,我们将不胜感激。我不打算让这个问题这么长;它只是积累。

TL;DR:在某些情况下,引导驱动器未正确存储在标签 drive_n 中。这会导致磁盘读取例程在某些硬件上失败。


我有一个包含 通用集的 Whosebug 答案。一个重要提示是:

When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register.

用更相关的代码更新您的问题后,问题变得明显:

drive_n: db 0
start: 
    mov [drive_n], dl

    ; setup segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; setup stack
    cli
    mov ss, ax
    mov sp, 0x7C00   ; stack will grow downward to lower adresses
    sti

问题是 mov [drive_n], dl 是在设置段寄存器之前完成的。 mov [drive_n], dl 等同于 mov [ds:drive_n], dlDS 中的段很重要。如果 BIOS 使用非 0x0000 的 DS 段将控制权转移到您的引导加载程序,则 mov [drive_n], dl 会将驱动器编号写入您不期望的内存位置。

如果 DS 的值不为零并且引导驱动器不是 0x00,则很可能会失败。如果真正的引导驱动器存储在错误的内存位置,将使用存储在 drive_n 标签中的初始值。在你的情况下是 0x00.

在大多数情况下,你很幸运它起作用了。这个问题的解决方法很简单。确保在 DL 的值写入内存 设置段寄存器(最值得注意的是 DS ).代码应如下所示:

drive_n: db 0
start: 
    ; setup segments
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; setup stack
    cli
    mov ss, ax
    mov sp, 0x7C00   ; stack will grow downward to lower adresses
    sti

    mov [drive_n], dl