For some reason, IRQ 6 never hits in my Qemu, Bochs, VMWare, or VirtualBox emulators. Do I need some type of virtual Floppy Drive or something? Here is my IRq6 handler:
void i86_flpy_irq (struct regs *r) {
//! irq fired
_FloppyDiskIRQ = 1;
printf("IRQ 6 HIT");
}
It never says "IRQ 6 HIT", and not only that, in my install function for my irq6 where I call in kernel:
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);
}
As you can see, I call a function that resets the Floppy Drive controller. Let's look at that function:
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 );
}
You can see above, I wait for an IRQ to finish by checking if _FloppyDiskIRQ
is 1, which I set when it hits. I have also taken note of this common bug. The code never passes that while()
loop since an IRQ6 never fires. Is there a reason for this? How do I fix it so an IRQ 6 can fire? I'm thinking I have to add something to my emulator (like I added a virtual hard drive for my ATA controller).
I also tested by printing the IRQ's that fire which are only 0,1,12 as of now (no 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);
}
Full source code: https://github.com/amanuel2/OS_MIRROR
The issue isn't related to the code you have shown. If you were to use QEMU and GDB to debug or BOCHS with its internal debugger and allowed your code to run until it gets to the infinite loop, you'd likely discover what the root cause of the problem is.
The main problem isn't that the virtual or real hardware is buggy. Your kernel has a bug. If you have properly enabled IRQs for the hardware device (floppy controller in this case) and you don't get an expected interrupt there are some possibilities:
The key piece of information a debugger would have told you was that the interrupt enabled flag in EFLAGS was clear by the time flpydsk_reset
was reached. This means that the CPU isn't accepting external interrupts. This will occur if somehow the interrupts enabled flag was inadvertently turned off, or you never turned them on.
Secondly, did you notice that while waiting for the floppy disk interrupt that your mouse pointer didn't move and the timer didn't update? That is for the same reason the floppy disk interrupt (IRQ 6) didn't get triggered.
A quick glance of your kernel.c++
file will reveal the problem:
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");
You install a number of drivers (keyboard/mouse etc) but you start in a state where the CPU was issued a CLI instruction prior to reaching this segment of code. Your flpydisk_install
function ultimately requires that interrupts are enabled but you issue the STI instruction AFTER the call to flpydisk_install
. Until you issue STI before flpydsk_reset
you will remain in an infinite loop waiting for an interrupt that won't occur.
One quick dirty fix is to do __asm__ __volatile__ ("sti");
before the call to flpydsk_install
. I would have probably coded things to enable interrupts on the 8259A PIC as they are needed by each device requiring an interrupt.
The revised code with the quick fix would look like this:
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);
Now that STI enables interrupts to the CPU, IRQ6 and its interrupt handler should be triggered, allowing flpydsk_reset
to exit its loop.
Once you enable interrupts to the CPU with STI, VirtualBox will require that you add a floppy disk controller to the virtual machine. You don't necessarily need a virtual floppy, just the floppy controller at a minimum. Without a virtual floppy controller present sending commands to non-existent hardware via ports will not enable IRQ6.
I believe that by default BOCHS and QEMU will simulate a floppy disk controller being present even if there are no virtual floppy disks mounted into the virtual machine.
Since your file BoneOS.bin
is built as an ELF executable, and because your code doesn't switch between modes (real mode or 64-bit long mode) setting up debugging is reasonably easy. Your Makefile
had a start-debug
recipe for QEMU. I have modified it to the following:
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'
The first QEMU command sets up QEMU to halt when started and to wait for a remote connection to GDB. The second command connects GDB to QEMU, breaks on kernelMain
(the main C++ entry point to your code), displays the source code and the registers. The symbols for debugging come from BoneOS.bin
.
You can look for general tutorials on actually using the GDB debugger from that point.
If you have the DDD package installed you can use a graphical debugger. It can be launched this way:
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"
It is possible to use Codeblocks for remote QEMU debugging by adjusting the debugger options for a CodeBlocks project.