【发布时间】: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(气缸盖扇区)。我在这个答案中讨论了这种转换:stackoverflow.com/a/45495410/3857942。在这个答案中:stackoverflow.com/a/54894586/3857942 我提供了一个
lba_to_chs函数,我每次循环 1 个扇区以作为引导加载程序的一部分从磁盘读取内核。 -
如果您一次读取 1 个扇区,您永远不必担心多磁道读取和跨越柱面边界。
标签: assembly x86 virtualbox bootloader bios