BIOS INT 13H 问题(从驱动器读取扇区)
Posted
技术标签:
【中文标题】BIOS INT 13H 问题(从驱动器读取扇区)【英文标题】:Problem with BIOS INT 13H (Read Sectors From Drive) 【发布时间】:2020-02-22 04:22:52 【问题描述】:说明:
为了创建一个简单的独立程序,我在第一个扇区中编写了一个简单的引导加载程序。其目的是将程序加载到内存中。为此,我使用 AH=2 的 INT 13h。代码是:
disk_load:
push dx ; Store DX on stack so later we can recall how many sectors were requested to be read,
; even if it is altered in the meantime.
mov ah, 0x02 ; Bios read sector.
mov al, dh ; Read DH sectors.
mov ch, 0x00 ; Select cylinder 0.
mov dh, 0x00 ; Select head 0.
mov cl, 0x02 ; Start reading from second sector (i.e. after the boot sector).
int 0x13 ; BIOS interrupt.
; <!----here
pop dx
ret
load_software:
mov bx, 0x7e0
mov es, bx
xor bx, bx
mov dh, 66
mov dl, [BOOT_DRIVE]
call disk_load
我在 VirtualBox 5.2.8 中进行了所有操作,并且运行良好。将所有内容移至具有 VirtualBox 6.0.14 的第二台机器上,实验失败。中断结束并设置 CF,表示失败。
阅读Boot loader doesn't jump to kernel code 中的出色答案我已经修复了可能导致问题的未指定 DS 值的潜在问题。如果我在 int 0x13
调用之前停止并转储 CPU 状态,我会在两个 VirtualBox 上获得一致的状态:
00:00:05.930849 eax=00000280 ebx=00007e00 ecx=00000002 edx=00000000 esi=00000000 edi=0000fff0
00:00:05.930857 eip=00007cc8 esp=00007bf9 ebp=00007bff iopl=0 nv up ei pl nz na po nc
00:00:05.930864 cs=0000 base=0000000000000000 limit=0000ffff flags=0000009b dr0=00000000 dr1=00000000
00:00:05.930877 ds=0000 base=0000000000000000 limit=0000ffff flags=00000093 dr2=00000000 dr3=00000000
00:00:05.930884 es=0000 base=0000000000000000 limit=0000ffff 标志=00000093 dr4=00000000 dr5=00000000
00:00:05.930891 fs=0000 base=0000000000000000 limit=0000ffff 标志=00000093 dr6=ffff0ff0 dr7=00000400
00:00:05.930898 gs=0000 base=0000000000000000 limit=0000ffff flags=00000093 cr0=00000010 cr2=00000000
00:00:05.930904 ss=0000 base=0000000000000000 limit=0000ffff flags=00000093 cr3=00000000 cr4=00000000
00:00:05.930910 gdtr=00000000000fe89f:0047 idtr=0000000000000000:ffff eflags=00200246
解析所有值我只能得出结论,中断的所有输入参数都设置正确。转储后的状态有CF设置和错误代码:
00:00:08.984877 eax=00000900 ebx=00000000 ecx=00000002 edx=00000000 esi=00000000 edi=0000fff0
00:00:08.984887 eip=00007cca esp=00007bf9 ebp=00007bff iopl=0 nv up ei pl nz na po cy
00:00:08.984896 cs=0000 base=0000000000000000 limit=0000ffff flags=0000009b dr0=00000000 dr1=00000000
00:00:08.984909 ds=0000 base=0000000000000000 limit=0000ffff flags=00000093 dr2=00000000 dr3=00000000
00:00:08.984917 es=07e0 base=0000000000007e00 limit=0000ffff 标志=00000093 dr4=00000000 dr5=00000000
00:00:08.984925 fs=0000 base=0000000000000000 limit=0000ffff 标志=00000093 dr6=ffff0ff0 dr7=00000400
00:00:08.984934 gs=0000 base=0000000000000000 limit=0000ffff flags=00000093 cr0=00000010 cr2=00000000
00:00:08.984941 ss=0000 base=0000000000000000 limit=0000ffff 标志=00000093 cr3=00000000 cr4=00000000
00:00:08.984948 gdtr=00000000000fe89f:0047 idtr=0000000000000000:ffff eflags=00200247
注意错误代码 AH=9
数据边界错误(尝试 DMA 跨越 64K 边界或 >80h 扇区) 将我带到 https://en.wikipedia.org/wiki/INT_13H 作出此声明的地方:
Buffer 的寻址应该保证完整的缓冲区在给定的段内,即 ( BX + size_of_buffer )
这可以解释我最初的问题,所以我做了另一个修复来设置es=0x7e0
和bx=0
。这是上面显示的代码的状态。但是,即使此代码也因上述状态而失败。
进一步测试表明,我可以成功读取多达 65 个扇区,但 66 个或更多扇区失败。作为一个奇怪的数字,我计算了第 65 个扇区的结尾:0xffff。所以问题变得有点混乱了。
问题:
我的es=0x7e0
和bx=0
解决方案是否应该避免段交叉(据我所知)?
如果是这样,为什么它似乎是跨越线性地址的问题?
或者可以跨段,但不能跨线性地址中的 0xffff 标记?
感谢您的帮助。
【问题讨论】:
文档似乎不清楚。 BIOS 版本也可能有问题。无论如何,您可能应该在循环中逐个读取扇区以避免出现问题。 您可以按照 Jester 的建议进行操作。一次循环一个扇区并将它们读入内存。您可以使用逻辑块寻址来执行此操作,并将 LBA 转换为 CHS(气缸盖扇区)。我在这个答案中讨论了这种转换:***.com/a/45495410/3857942。在这个答案中:***.com/a/54894586/3857942 我提供了一个lba_to_chs
函数,我每次循环 1 个扇区以作为引导加载程序的一部分从磁盘读取内核。
如果您一次读取 1 个扇区,您永远不必担心多磁道读取和跨越柱面边界。
【参考方案1】:
问题在于您“尝试 DMA 跨越 64K 边界”。事实上,您的目标缓冲区从物理地址 0x07E00 跨越到 0x17E00,它在 0x10000 处跨越了 64K 边界。 (这与段边界无关,因此您使用什么段和偏移值到达 0x07E00 物理地址无关紧要。)
之所以如此,是因为最初的 IBM PC 的设计成本很低。他们没有使用带有 16 位总线和 16 位支持芯片的 16 位 8086,而是使用更便宜的带有 8 位总线的 16 位 8088 CPU,可以与更便宜的 8 位支持芯片一起使用。特别是the DMA controller they chose 只能寻址 64K 内存,这是 DMA 控制器设计用于的 8 位 CPU 的典型寻址限制。他们通过一个单独的芯片使其工作,该芯片提供 DMA 地址的高四位,允许对 8088 的全部 1024K 地址空间进行寻址。 (在 IBM PC AT 上,这被扩展为提供高 8 位,以便可以访问整个 80286 16M 地址空间。)
不幸的是,这意味着 DMA 地址的高四位在 DMA 操作期间是固定的,这有效地将 1024K 地址空间划分为 16 个 64K 页。任何尝试执行从一个页面到下一个页面、跨越 64K 边界的 DMA 操作的尝试都将绕到页面的开头。虽然 BIOS 可以通过将读取分成两个单独的读取来解决这个问题,每个 64K 页一个,它只是返回一个错误。
请注意,这通常只是软盘访问的问题,因为硬盘接口通常不使用 IBM PC DMA 控制器。
由于在跨越磁道和柱面边界时也存在这样的潜在边界问题,解决此问题的最佳方法是一次只读取一个扇区,就像 Jester 和 Michael Petch 在 cmets 中所说的那样。作为一项简单的工作,您可以移动缓冲区,使其从物理地址 0x10000 开始,但您可能会发现在现实世界的系统中,您只能读取轨道上的剩余扇区。
【讨论】:
谢谢!这解释了我观察到的很多东西。使用上面的建议,我结束了循环所需的扇区并单独加载每个扇区。以上是关于BIOS INT 13H 问题(从驱动器读取扇区)的主要内容,如果未能解决你的问题,请参考以下文章
从没有 BIOS 服务的 INT 13 的软盘加载 OS 映像