软盘读取 (AH=0x2, int 0x13) 未完成。

3
在我的引导程序的第二阶段,我正在尝试将虚拟软盘中的一些扇区加载到bochs中的内存中,但在调用int 0x13后,该例程没有返回。
我相信我第二阶段的相关代码如下:
bootsys_start:
    mov %cs, %ax
    mov %ax, %ds

    /*
     * Remap IRQs. Interrupts have been disabled in the
     * bootloader already.
     */

    mov i8259A_ICW1($i8259A_IC4), %al
    out %al, i8259A_ICW1_ADDR($i8259A_MASTER)
    out %al, i8259A_ICW1_ADDR($i8259A_SLAVE)

    mov i8259A_ICW2($USER_INT_START), %al
    out %al, i8259A_ICW2_ADDR($i8259A_MASTER)
    mov i8259A_ICW2($USER_INT_START + 8), %al
    out %al, i8259A_ICW2_ADDR($i8259A_SLAVE)

    mov i8259A_ICW3($0x4), %al
    out %al, i8259A_ICW3_ADDR($i8259A_MASTER)
    mov i8259A_ICW3($0x2), %al
    out %al, i8259A_ICW3_ADDR($i8259A_SLAVE)

    mov i8259A_ICW4($i8259A_uPM & i8259A_x86), %al
    out %al, i8259A_ICW4_ADDR($i8259A_MASTER)
    out %al, i8259A_ICW4_ADDR($i8259A_SLAVE)

    call mm_detect

    /* Load the kernel now. */

    xor %bp, %bp
1:
    mov $KERNEL_ORG >> 0x4, %ax
    mov %ax, %es
    mov $KERNEL_ORG & 0xf, %bx
    mov $0x200 | KERNEL_SECTORS, %ax
    mov $(KERNEL_C << 0x8) | KERNEL_S, %cx
    mov $(KERNEL_H << 0x8) | FLOPPY_DRV, %dx
    int $0x13 /* <--- This int 0x13 doesn't seem to return */
    jnc 1f
    cmp $0x2, %bp
    je floppy_err
    inc %bp
    xor %ah, %ah
    int $0x13
    jmp 1b

所有的代码都可以在我的Github存储库中找到。要构建,请使用make all,然后使用命令bochsBOCHS上运行。

我首先要做的是确认我是否真正正确地获得了所有参数。在bochs的shell中,r会产生以下结果:

CPU0:
rax: 00000000_534d0201 rcx: 00000000_00000005
rdx: 00000000_534d0000 rbx: 00000000_00000000
rsp: 00000000_00007700 rbp: 00000000_00000000
rsi: 00000000_000e0005 rdi: 00000000_00000316
r8 : 00000000_00000000 r9 : 00000000_00000000
r10: 00000000_00000000 r11: 00000000_00000000
r12: 00000000_00000000 r13: 00000000_00000000
r14: 00000000_00000000 r15: 00000000_00000000
rip: 00000000_00000036
eflags 0x00007046: id vip vif ac vm rf NT IOPL=3 of df if tf sf ZF af PF cf

ah = 0x2(例程ID),al = 0x1(扇区数),ch = 0x0(柱面号低字节),cl = 0x5(扇区号和柱面号高两位),dh = 0x0(磁头号),dl = 0x0(驱动器号)。

sreg 打印为 es

es:0x0000

并且 bx = 0x0,所以扇区被加载到 0x0:0x0,正如我所期望的那样。


我尝试了几件事:

  1. 加载到物理地址为0x600

    我认为在执行BIOS中断例程期间覆盖IVT或BDA可能不是一个好主意,所以我尝试将扇区加载到0x600es = 0x60bx = 0x0)(我知道BDA只有256字节大小)。结果相同。

  2. 加载磁盘上的第一个扇区

    也许读取第五个扇区超出了边界或其他什么问题?使用int 0x13读取我的第二阶段的代码按预期工作。我的第二阶段中的int 0x13类似,因此我希望它能够工作。作为测试,我修改了我的第二阶段以读取扇区1,但仍然不起作用。

  3. 清零eax的高位部分

    我想也许BIOS例程确实存在错误,某种方式使用了eax而不是ax。我尝试清零eax的高16位部分...但没有效果。

正如我之前所说,我已经将一些扇区从磁盘加载到内存中。在int 0x13之前,GPRs的内容如下(使用bochs shell中的r获得):

CPU0:
rax: 00000000_00000203 rcx: 00000000_00090002
rdx: 00000000_00000000 rbx: 00000000_00000000
rsp: 00000000_00007700 rbp: 00000000_00000000
rsi: 00000000_000e7cdd rdi: 00000000_000000e2
r8 : 00000000_00000000 r9 : 00000000_00000000
r10: 00000000_00000000 r11: 00000000_00000000
r12: 00000000_00000000 r13: 00000000_00000000
r14: 00000000_00000000 r15: 00000000_00000000
rip: 00000000_00007c59
eflags 0x00007046: id vip vif ac vm rf NT IOPL=3 of df if tf sf ZF af PF cf

sreg 产生的是 es:0x8f60,这是在 EBDA 前动态计算出来的地址。

比较两者,我没有看到任何可能影响中断例程功能的显著差异,因此问题不可能是通过寄存器传递的参数。

有人有其他建议吗?


1
你尝试过使用 [mcve] 来隔离问题吗?特别是考虑到在第一种情况下 IP 是 36h,在第二种情况下是 7c59h,你如何设置段寄存器。 - Margaret Bloom
1
@Downvoter 无论如何,你的完整问题或多或少是“我以前可以加载一个扇区,现在不能了”。既然你已经排除了无状态原因(要加载的扇区、要加载的地址等),唯一合乎逻辑的解释就是其他事情发生了变化。但是如果没有提供完整的 [MCVE],我们就无法知道。 - davmac
@MargaretBloom 我运行了 ax=0xe820, int 0x15 (全面内存映射),第一个条目是从 0x00x9fc00,后者等于 EBDA 起始地址。该条目类型为 0x1,表示可供操作系统使用的内存。我正在加载三个扇区,如我对 0x8f600 的回答所述,远低于 EBDA。该区域必须空闲以供使用。 - cadaniluk
2
问题可能是因为您已经重新映射了8259上的IRQ到BIOS不期望的设置。在进入保护模式后重新映射IRQ。此时,您将不再使用BIOS例程,可以随心所欲地操作。BIOS可能使用软盘驱动器IRQ6(和系统定时器IRQ0),但由于它们被重新映射,它永远无法命中正确的IVT。 - Michael Petch
1
@Downvoter明白了,但是当你发布一个问题,人们开始要求提供MCVE,并且你的回答是(a)我会做一个,但是加载第二级引导程序工作正常,然后(b)我不知道从哪里开始 - 那么可能存在一些关于MCVE是什么以及为什么需要它的误解。通常情况下,如果你没有全力尝试解决问题,请不要发布问题。是的,有人可能会猜到解决方案(“在调用int 0x13之前重新路由IRQs吗?”);这并不意味着你可以/应该跳过这些必要的工作。 - davmac
显示剩余10条评论
1个回答

7

你的软盘读取代码Int 13h/AH=02h存在一些问题:

  1. 您在问题中已经指出了一个错误。在实模式下读取0x0000:0x0000之上的扇区是不好的做法。这将覆盖中断向量表(IVT)。从0x0000:0x0000到0x0040:0x0000的区域是IVT;从0x0040:0x0000到0x0060:0x0000的区域是BIOS数据区(BDA)。应该将BDA视为实模式BIOS例程可能使用的临时存储区。

    要解决这个问题,请将其加载到安全位置,如0x0060:0x0000(物理地址0x00600)。

    一旦进入保护模式,0x00000000到0x00000600之间的区域可以用于其他用途。注意:不要将扩展BIOS数据区(EBDA)内存区域用作通用内存,因为系统管理模式(SMM)和高级配置和电源接口(ACPI)可能会对其进行写入。

  2. 您的代码重新映射了8259A以准备进入保护模式。在这样做时,IRQ被重新映射到IVT的不同部分。Int 13h例程可能依赖于中断触发和BIOS中断例程执行需要软盘读取的工作。IRQ0(系统计时器)和IRQ6(软盘控制器)是可能的。如果将8259A的基址重新映射到其他位置,则BIOS安装的中断例程将不会执行。这可能会导致意外的行为,包括Int 13h永远不返回。

    要解决这个问题,建议在进入保护模式后重新映射8259A PIC的基址。此时您可能已经完成了BIOS中断处理,因此这不应该是一个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接