IRQ 6 软盘控制器中断未触发
IRQ 6 floppy disk controller interrupt not triggered
出于某种原因,IRQ 6 从未在我的 Qemu、Bochs、VMWare 或 VirtualBox 模拟器中触发。我需要某种类型的虚拟软盘驱动器吗?这是我的 IRq6 处理程序:
void i86_flpy_irq (struct regs *r) {
//! irq fired
_FloppyDiskIRQ = 1;
printf("IRQ 6 HIT");
}
它从不说 "IRQ 6 HIT",不仅如此,在我的 irq6 安装函数中,我在内核中调用:
void flpydsk_install (int irq) {
//! install irq handler
install_handler_irq (irq, i86_flpy_irq);
//! initialize the DMA for FDC
flpydsk_initialize_dma ();
//! reset the fdc
flpydsk_reset ();
//! set drive information
//flpydsk_drive_data (13, 1, 0xf, true);
}
如您所见,我调用了一个重置软盘驱动器控制器的函数。让我们看看那个函数:
void flpydsk_reset () {
uint32_t st0, cyl;
_FloppyDiskIRQ = 0;
//! reset the controller
flpydsk_disable_controller ();
flpydsk_enable_controller ();
//flpydsk_wait_irq ();
printf("STARTED WAITING");
while(_FloppyDiskIRQ == 0);
_FloppyDiskIRQ = 0;
printf("ENDED WAITING FOR IRQ");
//! send CHECK_INT/SENSE INTERRUPT command to all drives
for (int i=0; i<4; i++)
flpydsk_check_int (&st0,&cyl);
//! transfer speed 500kb/s
flpydsk_write_ccr (0);
//! pass mechanical drive info. steprate=3ms, unload time=240ms, load time=16ms
flpydsk_drive_data (3,16,240,true);
//! calibrate the disk
flpydsk_calibrate ( _CurrentDrive );
}
你可以在上面看到,我通过检查 _FloppyDiskIRQ
是否为 1 来等待 IRQ 完成,这是我在它命中时设置的。我还注意到 this 常见错误。代码永远不会通过 while()
循环,因为 IRQ6 永远不会触发。是否有一个原因?我如何修复它以便 IRQ 6 可以触发?我在想我必须向我的模拟器添加一些东西(比如我为我的 ATA 控制器添加了一个虚拟硬盘)。
我还通过打印 IRQ 进行了测试,截至目前只有 0、1、12(没有 6)...
extern "C" void irq_handler(struct regs *r)
{
/* This is a blank function pointer */
regs_func handler;
/* Find out if we have a custom handler to run for this
* IRQ, and then finally, run it */
handler = irq_routines[r->int_no];
if (handler)
{
handler(r);
}
printf("%d,",r->int_no);
//irq_taskmanager->Schedule((CPUState*)r);
/* If the IDT entry that was invoked was greater than 40
* (meaning IRQ8 - 15), then we need to send an EOI to
* the slave controller */
if (r->int_no >= 8)
{
p8b_irq.out(0x20,0xA0);
}
/* In either case, we need to send an EOI to the master
* interrupt controller too */
p8b_irq.out(0x20, 0x20);
}
该问题与您显示的代码无关。如果您要使用 QEMU 和 GDB 进行调试或使用 BOCHS 及其内部调试器并允许您的代码到 运行 直到进入无限循环,您可能会发现问题的根本原因是什么。
主要问题不是虚拟或真实硬件有问题。你的内核有一个错误。如果您为硬件设备(在本例中为软盘控制器)正确启用了 IRQ,但没有得到预期的中断,则有以下几种可能性:
- 您将错误的中断处理程序连接到 IRQ
- 您没有在 PIC 上启用您期望的 IRQ
- 您尚未在 CPU 上启用中断。
- 在设备(软盘控制器)上启用中断的代码不正确
调试器会告诉您的关键信息是 中断启用 标志在 EFLAGS 时已清除 flpydsk_reset
到达。这意味着 CPU 不接受外部中断。如果 interrupts enabled 标志被无意中关闭,或者您从未打开它们,就会发生这种情况。
其次,您是否注意到在等待软盘中断时您的鼠标指针没有移动并且计时器没有更新?这与软盘中断 (IRQ 6) 没有被触发的原因相同。
快速浏览一下您的 kernel.c++
文件就会发现问题所在:
isr.install_isrs();
irq.install_irqs();
Timer timer;
timer.install_timer();
KBD kbd;
kbd.install_kbd_driver();
MOUSE mouse;
mouse.install_mouse_driver();
flpydsk_install(6);
__asm__ __volatile__ ("sti");
您安装了多个驱动程序(keyboard/mouse 等),但您在 CPU 到达之前发出 CLI 指令的状态下启动这一段代码。您的 flpydisk_install
函数最终要求启用中断,但您发出 STI 指令 AFTER 调用 flpydisk_install
。在 flpydsk_reset
之前发出 STI 之前,您将处于无限循环中,等待不会发生的中断。
一个快速的肮脏修复是在调用 flpydsk_install
之前执行 __asm__ __volatile__ ("sti");
。我可能会编写一些代码来启用 8259A PIC 上的中断,因为每个需要中断的设备都需要它们。
带有快速修复的修订代码如下所示:
isr.install_isrs();
irq.install_irqs();
Timer timer;
timer.install_timer();
KBD kbd;
kbd.install_kbd_driver();
MOUSE mouse;
mouse.install_mouse_driver();
__asm__ __volatile__ ("sti");
flpydsk_install(6);
现在 STI 启用 CPU 的中断,IRQ6 及其中断处理程序应该被触发,允许 flpydsk_reset
退出其循环。
Virtual Box软盘控制器
一旦您使用 STI 启用对 CPU 的中断,VirtualBox 将要求您添加一个软盘控制器到虚拟机。您不一定需要虚拟软盘,至少需要软盘控制器。如果没有虚拟软盘控制器,通过端口向不存在的硬件发送命令将不会启用 IRQ6。
我相信默认情况下 BOCHS 和 QEMU 将模拟存在的软盘控制器,即使没有安装虚拟软盘进入虚拟机。
使用您的内核设置 QEMU 调试
由于您的文件 BoneOS.bin
是作为 ELF 可执行文件构建的,并且因为您的代码不会在模式(实模式或 64 位长模式)之间切换,所以设置调试相当容易。您的 Makefile
有 QEMU 的 start-debug
秘诀。我已将其修改为以下内容:
start-debug:
qemu-system-i386 -S -s -kernel BoneOS.bin -m 1G -serial file:qemu-serial.log \
-serial stdio -usb -device usb-host,hostbus=2,hostaddr=1 -no-reboot &
gdb BoneOS.bin \
-ex 'target remote localhost:1234' \
-ex 'break kernelMain' \
-ex 'layout src' \
-ex 'layout reg' \
-ex 'continue'
第一个 QEMU 命令设置 QEMU 在启动时停止并等待到 GDB 的远程连接。第二个命令将 GDB 连接到 QEMU,在 kernelMain
处中断(主 C++ 条目指向您的代码),显示源代码和寄存器。调试符号来自BoneOS.bin
.
您可以查找关于实际使用 GDB 调试器的一般教程。
如果您安装了 DDD 软件包,您可以使用图形调试器。可以这样启动:
start-debug:
qemu-system-i386 -S -s -kernel BoneOS.bin -m 1G -serial file:qemu-serial.log \
-serial stdio -usb -device usb-host,hostbus=2,hostaddr=1 -no-reboot &
ddd BoneOS.bin \
--eval-command="target remote localhost:1234" \
--eval-command="break kernelMain"
通过调整 CodeBlocks[= 的调试器选项,可以使用 Codeblocks 进行远程 QEMU 调试96=] 项目。
我遇到了同样的问题。我设法在每次中断服务执行后通过 remapping/reinitializing PIC 解决了它。
我认为这是一个糟糕的解决方案,但暂时有效。
我遇到的问题是,我在键盘中断后调用软盘读取例程,但没有向 PIC 发送 EOI(中断结束)命令。
所以在调用软盘读取例程之前发送 EOI 解决了这个问题。
您找到更好的解决方案了吗?
出于某种原因,IRQ 6 从未在我的 Qemu、Bochs、VMWare 或 VirtualBox 模拟器中触发。我需要某种类型的虚拟软盘驱动器吗?这是我的 IRq6 处理程序:
void i86_flpy_irq (struct regs *r) {
//! irq fired
_FloppyDiskIRQ = 1;
printf("IRQ 6 HIT");
}
它从不说 "IRQ 6 HIT",不仅如此,在我的 irq6 安装函数中,我在内核中调用:
void flpydsk_install (int irq) {
//! install irq handler
install_handler_irq (irq, i86_flpy_irq);
//! initialize the DMA for FDC
flpydsk_initialize_dma ();
//! reset the fdc
flpydsk_reset ();
//! set drive information
//flpydsk_drive_data (13, 1, 0xf, true);
}
如您所见,我调用了一个重置软盘驱动器控制器的函数。让我们看看那个函数:
void flpydsk_reset () {
uint32_t st0, cyl;
_FloppyDiskIRQ = 0;
//! reset the controller
flpydsk_disable_controller ();
flpydsk_enable_controller ();
//flpydsk_wait_irq ();
printf("STARTED WAITING");
while(_FloppyDiskIRQ == 0);
_FloppyDiskIRQ = 0;
printf("ENDED WAITING FOR IRQ");
//! send CHECK_INT/SENSE INTERRUPT command to all drives
for (int i=0; i<4; i++)
flpydsk_check_int (&st0,&cyl);
//! transfer speed 500kb/s
flpydsk_write_ccr (0);
//! pass mechanical drive info. steprate=3ms, unload time=240ms, load time=16ms
flpydsk_drive_data (3,16,240,true);
//! calibrate the disk
flpydsk_calibrate ( _CurrentDrive );
}
你可以在上面看到,我通过检查 _FloppyDiskIRQ
是否为 1 来等待 IRQ 完成,这是我在它命中时设置的。我还注意到 this 常见错误。代码永远不会通过 while()
循环,因为 IRQ6 永远不会触发。是否有一个原因?我如何修复它以便 IRQ 6 可以触发?我在想我必须向我的模拟器添加一些东西(比如我为我的 ATA 控制器添加了一个虚拟硬盘)。
我还通过打印 IRQ 进行了测试,截至目前只有 0、1、12(没有 6)...
extern "C" void irq_handler(struct regs *r)
{
/* This is a blank function pointer */
regs_func handler;
/* Find out if we have a custom handler to run for this
* IRQ, and then finally, run it */
handler = irq_routines[r->int_no];
if (handler)
{
handler(r);
}
printf("%d,",r->int_no);
//irq_taskmanager->Schedule((CPUState*)r);
/* If the IDT entry that was invoked was greater than 40
* (meaning IRQ8 - 15), then we need to send an EOI to
* the slave controller */
if (r->int_no >= 8)
{
p8b_irq.out(0x20,0xA0);
}
/* In either case, we need to send an EOI to the master
* interrupt controller too */
p8b_irq.out(0x20, 0x20);
}
该问题与您显示的代码无关。如果您要使用 QEMU 和 GDB 进行调试或使用 BOCHS 及其内部调试器并允许您的代码到 运行 直到进入无限循环,您可能会发现问题的根本原因是什么。
主要问题不是虚拟或真实硬件有问题。你的内核有一个错误。如果您为硬件设备(在本例中为软盘控制器)正确启用了 IRQ,但没有得到预期的中断,则有以下几种可能性:
- 您将错误的中断处理程序连接到 IRQ
- 您没有在 PIC 上启用您期望的 IRQ
- 您尚未在 CPU 上启用中断。
- 在设备(软盘控制器)上启用中断的代码不正确
调试器会告诉您的关键信息是 中断启用 标志在 EFLAGS 时已清除 flpydsk_reset
到达。这意味着 CPU 不接受外部中断。如果 interrupts enabled 标志被无意中关闭,或者您从未打开它们,就会发生这种情况。
其次,您是否注意到在等待软盘中断时您的鼠标指针没有移动并且计时器没有更新?这与软盘中断 (IRQ 6) 没有被触发的原因相同。
快速浏览一下您的 kernel.c++
文件就会发现问题所在:
isr.install_isrs();
irq.install_irqs();
Timer timer;
timer.install_timer();
KBD kbd;
kbd.install_kbd_driver();
MOUSE mouse;
mouse.install_mouse_driver();
flpydsk_install(6);
__asm__ __volatile__ ("sti");
您安装了多个驱动程序(keyboard/mouse 等),但您在 CPU 到达之前发出 CLI 指令的状态下启动这一段代码。您的 flpydisk_install
函数最终要求启用中断,但您发出 STI 指令 AFTER 调用 flpydisk_install
。在 flpydsk_reset
之前发出 STI 之前,您将处于无限循环中,等待不会发生的中断。
一个快速的肮脏修复是在调用 flpydsk_install
之前执行 __asm__ __volatile__ ("sti");
。我可能会编写一些代码来启用 8259A PIC 上的中断,因为每个需要中断的设备都需要它们。
带有快速修复的修订代码如下所示:
isr.install_isrs();
irq.install_irqs();
Timer timer;
timer.install_timer();
KBD kbd;
kbd.install_kbd_driver();
MOUSE mouse;
mouse.install_mouse_driver();
__asm__ __volatile__ ("sti");
flpydsk_install(6);
现在 STI 启用 CPU 的中断,IRQ6 及其中断处理程序应该被触发,允许 flpydsk_reset
退出其循环。
Virtual Box软盘控制器
一旦您使用 STI 启用对 CPU 的中断,VirtualBox 将要求您添加一个软盘控制器到虚拟机。您不一定需要虚拟软盘,至少需要软盘控制器。如果没有虚拟软盘控制器,通过端口向不存在的硬件发送命令将不会启用 IRQ6。
我相信默认情况下 BOCHS 和 QEMU 将模拟存在的软盘控制器,即使没有安装虚拟软盘进入虚拟机。
使用您的内核设置 QEMU 调试
由于您的文件 BoneOS.bin
是作为 ELF 可执行文件构建的,并且因为您的代码不会在模式(实模式或 64 位长模式)之间切换,所以设置调试相当容易。您的 Makefile
有 QEMU 的 start-debug
秘诀。我已将其修改为以下内容:
start-debug:
qemu-system-i386 -S -s -kernel BoneOS.bin -m 1G -serial file:qemu-serial.log \
-serial stdio -usb -device usb-host,hostbus=2,hostaddr=1 -no-reboot &
gdb BoneOS.bin \
-ex 'target remote localhost:1234' \
-ex 'break kernelMain' \
-ex 'layout src' \
-ex 'layout reg' \
-ex 'continue'
第一个 QEMU 命令设置 QEMU 在启动时停止并等待到 GDB 的远程连接。第二个命令将 GDB 连接到 QEMU,在 kernelMain
处中断(主 C++ 条目指向您的代码),显示源代码和寄存器。调试符号来自BoneOS.bin
.
您可以查找关于实际使用 GDB 调试器的一般教程。
如果您安装了 DDD 软件包,您可以使用图形调试器。可以这样启动:
start-debug:
qemu-system-i386 -S -s -kernel BoneOS.bin -m 1G -serial file:qemu-serial.log \
-serial stdio -usb -device usb-host,hostbus=2,hostaddr=1 -no-reboot &
ddd BoneOS.bin \
--eval-command="target remote localhost:1234" \
--eval-command="break kernelMain"
通过调整 CodeBlocks[= 的调试器选项,可以使用 Codeblocks 进行远程 QEMU 调试96=] 项目。
我遇到了同样的问题。我设法在每次中断服务执行后通过 remapping/reinitializing PIC 解决了它。 我认为这是一个糟糕的解决方案,但暂时有效。 我遇到的问题是,我在键盘中断后调用软盘读取例程,但没有向 PIC 发送 EOI(中断结束)命令。 所以在调用软盘读取例程之前发送 EOI 解决了这个问题。 您找到更好的解决方案了吗?