BIOS INT 13H(从驱动器读取扇区)出现问题



描述:

在我努力创建一个简单的独立程序的过程中,我在第一个扇区中编写了一个简单引导加载程序。它的目的是将程序加载到内存中。为此,我使用了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设置结束,表示故障。

在引导加载程序中阅读优秀的答案;t跳转到内核代码我已经修复了可能导致问题的未指定DS值的潜在问题。如果我在int 0x13调用之前停止并转储CPU状态,我会在两个VirtualBoxe:上获得一致的状态

00:00:05.930849 eax=00000280 ebx=00007e00 ecx=00000002 edx=00000000 esi=00000000 edi=0000fff0

00:00:05.9300857 eip=00007cc8 esp=00007bf9 ebp=00007bff iopl=0 nv up ei pl nz na po nc

00:00:05.9300864 cs={0000基数=0000000000000000限制=0000ffff标志=0000009b}dr0=00000000 dr1=00000000

00:00:05.9300877 ds={0000基数=0000000000000000限制=0000ffff标志=00000093}dr2=00000000 dr3=00000000

00:00:05.9300884 es={0000基数=0000000000000000限制=0000ffff标志=00000093}dr4=00000000 dr5=00000000

00:00:05.930991 fs={0000基数=0000000000000000限制=0000ffff标志=00000093}dr6=ffff0f0 dr7=00000400

00:00:05.930998 gs={0000基数=0000000000000000限制=0000ffff标志=00000093}cr0=00000010 cr2=00000000

00:00:05.930904 ss={0000基数=0000000000000000限制=0000ffff标志=00000093}cr3=00000000 cr4=00000000

00:00:05.930910 gdtr=00000000000 fe89f:0047 idtr=0000000000000000:ffff-eflags=00200246

分析所有值,我只能得出结论,中断的所有输入参数都设置正确。转储后的状态具有CF设置和错误代码:

00:00:08.8984877 eax=00000900 ebx=00000000 ecx=00000002 edx=00000000 esi=00000000 edi=0000fff0

00:00:08.8984887 eip=00007cca esp=00007bf9 ebp=00007bff iopl=0 nv上行ei-pl nz na po-cy

00:00:08.8984896 cs={0000基数=0000000000000000限制=0000ffff标志=0000009b}dr0=00000000 dr1=00000000

00:00:08.8984909 ds={0000基数=0000000000000000限制=0000ffff标志=00000093}dr2=00000000 dr3=00000000

00:00:08.8984917 es={07e0基数=000000000000 7e00限制=0000ffff标志=00000093}dr4=00000000 dr5=00000000

00:00:08.8984925 fs={0000基数=0000000000000000限制=0000ffff标志=00000093}dr6=ffff0f0 dr7=00000400

00:00:08.8984934 gs={0000基数=0000000000000000限制=0000ffff标志=00000093}cr0=00000010 cr2=00000000

00:00:08.8984941 ss={0000基数=0000000000000000限制=0000ffff标志=00000093}cr3=00000000 cr4=00000000

00:00:08.8984948 gdtr=00000000000 fe89f:0047 idtr=0000000000000000:ffff-eflags=00200247

注意到错误代码AH=9数据边界错误(尝试在64K边界或>80h扇区上进行DMA),导致https://en.wikipedia.org/wiki/INT_13H作出此声明的地方:

Buffer的寻址应确保整个缓冲区位于给定段内,即(BX+size_of_Buffer)<=10000小时。

这将解释我最初的问题,因此我进行了另一个修复,以设置es=0x7e0bx=0。这是上面显示的代码的状态。然而,即使是此代码也会在上述状态下失败。

进一步的测试表明,我可以成功读取多达65个扇区,但有66个或更多扇区失败。由于这是一个奇怪的数字,我计算了第65个扇区的末尾:0xffff。因此,这个问题变得更加令人困惑。

问题:

我的es=0x7e0bx=0解决方案是否应该避免分段交叉(据我所知)?

如果是这样的话,为什么它似乎是跨越线性地址的问题?

或者可以跨越段,但不能跨越线性地址中的0xffff标记?

谢谢你的帮助。

问题是您"试图通过64K边界进行DMA"。事实上,目标缓冲区的范围从物理地址0x07E00到0x17E00,在0x10000处跨越64K边界。(这与段边界无关,所以使用什么段和偏移值来获得0x07E00物理地址并不重要。)

这与最初的IBM PC是如何廉价设计的有关。他们没有使用带有16位总线和16位支持芯片的16位8086,而是使用了带有8位总线的更便宜的16位8098 CPU,该CPU可以与更便宜的8位支持芯片一起使用。特别是,他们选择的DMA控制器只能寻址64K的内存,这是DMA控制器设计用于8位CPU的典型寻址限制。他们通过一个单独的芯片来实现这一点,该芯片提供DMA地址的前四位,允许8088的整个1024K地址空间被寻址。(在IBM PC AT上,它被扩展为提供高8位,这样就可以访问整个80286 16M地址空间。)

不幸的是,这意味着DMA地址的高四位在DMA操作期间是固定的,这有效地将1024K地址空间划分为十六个64K页。任何执行DMA操作的尝试,如果跨越64K边界,从一个页面到下一个页面,都将绕过页面的开头。虽然BIOS可以通过将读取划分为两个单独的读取(每个64K页面一个)来解决这个问题,但它只是返回一个错误。

请注意,这通常只是软盘访问的问题,因为硬盘接口通常不使用IBMPCDMA控制器。

正如Jester和Michael Petch在评论中所说,由于在跨越轨道和圆柱体边界时也存在类似的潜在边界问题,解决这个问题的最佳方法是一次只读取一个扇区。作为一个简单的解决方案,您只需移动缓冲区,使其从物理地址0x10000开始,但您可能会发现,在现实世界中的系统中,您只能读取轨道上的剩余扇区。

最新更新