在引导加载程序的第二阶段,我试图将虚拟软盘上的某些扇区加载到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 repository中找到。要构建,只需使用make all,然后使用命令bochs与BOCHS一起运行

我所做的第一件事是验证我是否正确设置了所有参数。 Bochs外壳中的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(驱动器号)。
sreges打印:
es:0x0000

bx = 0x0,因此按我的预期将扇区加载到0x0:0x0

我尝试了几件事:

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

将第一个扇区加载到磁盘上
也许阅读第五部分是某种程度上的界限?使用int 0x13读取我的第二阶段的代码按预期工作。在我的第二阶段中,int 0x13与之类似,因此我希望它能够正常工作。作为测试,我将第二阶段更改为读取扇区1,但仍然无法正常工作。

归零eax的上部
我发现BIOS例程中确实存在错误,并且以某种方式使用了eax而不是ax。我尝试将eax的高16位部分归零...无济于事。


如前所述,我已经将某些扇区从磁盘加载到内存中。 int 0x13之前的GPR内容如下(在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之前的动态计算地址。
将两者进行比较,我看不出可能会影响中断例程功能的显着差异,因此问题不应该是通过寄存器传递的参数。
有人对做什么有其他建议吗?

最佳答案

Int 13h/AH=02h软盘读取代码的几个问题:


您已经在问题中确定的这一个人。当您在实模式下运行时,读取0x0000:0x0000上方的扇区是个坏主意。这将破坏中断向量表(IVT)。从0x0000:0x0000到0x0040:0x0000的区域是IVT; BIOS Data Area(BDA)是0x0040:0x0000到0x0060:0x0000的区域。应将BDA视为实模式BIOS例程可能使用的临时区域。

要修复加载,请在安全的位置(例如0x0060:0x0000(物理地址0x00600))进行加载。

进入保护模式后,可以回收0x00000000和0x00000600之间的区域以用于其他用途。注意:请勿将Extended BIOS Data Area(EBDA)存储区用作通用存储器,因为System Management Mode(SMM)和Advanced Configuration and Power Interface(ACPI)可能会对其进行写入。
您的代码将重新映射8259A,以准备进入保护模式。这样,IRQ被重新映射到IVT的不同部分。 Int 13h例程可能依赖于中断来触发,而BIOS中断例程则需要执行软盘读取所需的工作。可以使用IRQ0(系统定时器)和IRQ6(软盘控制器)。如果在其他地方重新映射8259A的基础,BIOS将不会执行该中断例程。这可能会导致意外的行为,包括Int 13h再也不会返回。

要解决此问题,建议您在保护模式下重新映射8259A PIC的基座。到那时,您可能已经完成了BIOS中断,因此这不应该成为问题。

08-16 02:46