在引导加载程序的第二阶段,我试图将虚拟软盘上的某些扇区加载到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
(驱动器号)。sreg
为es
打印:es:0x0000
和
bx = 0x0
,因此按我的预期将扇区加载到0x0:0x0
。我尝试了几件事:
加载到物理地址
0x600
我以为在执行BIOS中断例程期间覆盖IVT或BDA可能不是一个好主意,因此我尝试将扇区加载到
0x600
(es = 0x60
,bx = 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中断,因此这不应该成为问题。