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], dl
。 DS 中的段很重要。如果 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
一些背景:
我正在开发一个基本的引导加载程序,它使用 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
中。这会导致磁盘读取例程在某些硬件上失败。
我有一个包含
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], dl
。 DS 中的段很重要。如果 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