OSDev.org https://forum.osdev.org/ |
|
COM1 IRQ Not Triggering Every Time https://forum.osdev.org/viewtopic.php?f=1&t=57184 |
Page 1 of 1 |
Author: | mttarry [ Sun Mar 24, 2024 2:31 pm ] | ||||
Post subject: | COM1 IRQ Not Triggering Every Time | ||||
When I send serial data via QEMU's virtual serial port from my host PC to my guest OS, I am seeing that my IRQ4 for COM1 does not trigger every time. On average it will trigger about 50% of the time. I can't figure out how I can determine whether it is QEMU errata or an issue with how I've setup my PIC. The fact that it triggers some of the time at least tells me that I have my interrupts mapped correctly, and that the IRQ handler is in the right place. I'm looking for any advice that might help me determine why IRQ4 is not triggering for each byte of serial data that is sent. I have not tested this on hardware yet because I don't have a serial cable at the moment. Thank you in advance for any help you can provide. These are my QEMU flags: Code: QEMU_FLAGS = -cdrom $(ISO_TARGET) -display cocoa,full-screen=on -monitor stdio -serial pty And I'm sending data from my host via: Code: echo -n h > /dev/ttys002 About 50% of the time I issue this command from my host, an IRQ is not triggered on my kernel. Here is the relevant code: irq.c Code: void irq_remap(void) { // ICW1 outb(PIC1_CMD, ICW1_ICW4 | ICW1_INIT); outb(PIC2_CMD, ICW1_ICW4 | ICW1_INIT); // ICW2 outb(PIC1_DATA, ICW2_PIC1_BASE); outb(PIC2_DATA, ICW2_PIC2_BASE); // ICW3 outb(PIC1_DATA, ICW3_PIC1_IRQ); // master PIC at IRQ4 outb(PIC2_DATA, ICW3_PIC2_IRQ); // slave PIC at IRQ2 // ICW4 outb(PIC1_DATA, ICW4_8086); outb(PIC2_DATA, ICW4_8086); // Unmask IRQ4 -- Todo: handle this in separate logic outb(PIC1_DATA, 0xEF); outb(PIC2_DATA, 0xFF); } static uint16_t pic_get_irq_reg(int ocw3) { outb(PIC1_CMD, ocw3); outb(PIC2_CMD, ocw3); return (inb(PIC2_CMD) << 8) | inb(PIC1_CMD); } uint16_t pic_get_irr(void) { return pic_get_irq_reg(PIC_OCW3_IRR); } uint16_t pic_get_isr(void) { return pic_get_irq_reg(PIC_OCW3_ISR); } void pic_endof_int(uint8_t irq) { if (irq >= 8) outb(PIC2_CMD, PIC_EOI); outb(PIC1_CMD, PIC_EOI); } void irq_handler() { int irq = BIT_INDEX(pic_get_isr()); kprintf("."); if (irq_handlers[irq] != NULL) { irq_handlers[irq](); } outb(PIC1_DATA, 0xEF); outb(PIC2_DATA, 0xFF); pic_endof_int(irq); } void irq_set_gates() { for (int i = IRQ_VEC_START; i < IRQ_VEC_END; ++i) { idt_set_desc(i, &irq_stub, IDTENTRY_KERNEL_INT); } } void irq_install() { irq_remap(); irq_set_gates(); } void register_irq_handler(int irq_num, void* handler) { if (irq_num >= 0 && irq_num < MAX_IRQS) { irq_handlers[irq_num] = handler; } } serial.c Code: int serial_init(void) {
outb(SERIAL_PORT + IER_OFFSET, 0x01); // enable interrupts outb(SERIAL_PORT + LCR_OFFSET, 0x80); // set DLAB outb(SERIAL_PORT + DIVISOR_LSB_OFFSET, 0x01); // set LSB of divisor outb(SERIAL_PORT + DIVISOR_MSB_OFFSET, 0x00); // set MSB of divisor outb(SERIAL_PORT + LCR_OFFSET, 0x00); // clear DLAB //outb(SERIAL_PORT + FCR_OFFSET, 0xC7); // enable FIFO, clear buffers, 14-byte threshold outb(SERIAL_PORT + FCR_OFFSET, 0x00); // disable FIFO outb(SERIAL_PORT + LCR_OFFSET, 0x03); // 8-bit, no parity, 1 stop bit outb(SERIAL_PORT + MCR_OFFSET, 0x1E); // loopback mode, test the serial chip outb(SERIAL_PORT, 0xAB); if (inb(SERIAL_PORT) != 0xAB) { return 1; } // serial is not faulty, set in normal operation mode outb(SERIAL_PORT + MCR_OFFSET, 0x0F); // interrupt enabled, RTS and DSR set register_irq_handler(SERIAL_PORT_IRQ, serial_irq_handler); return 0; } static void serial_handle_receive_byte() { uint8_t recb = inb(SERIAL_PORT); kprintf("%c", recb); } static void serial_irq_handler() { // Determine if we are reading/writing UART uint8_t lsr = inb(SERIAL_PORT + LSR_OFFSET); uint8_t iir = inb(SERIAL_PORT + IIR_OFFSET); // Check if data is ready to be extracted from the UART if (lsr & 1) { serial_handle_receive_byte(); } } }
|
Author: | Octocontrabass [ Sun Mar 24, 2024 9:49 pm ] |
Post subject: | Re: COM1 IRQ Not Triggering Every Time |
mttarry wrote: Code: outb(SERIAL_PORT + DIVISOR_LSB_OFFSET, 0x01); // set LSB of divisor outb(SERIAL_PORT + DIVISOR_MSB_OFFSET, 0x00); // set MSB of divisor Have you tried a bigger divisor? Maybe it's just too fast without the FIFO. |
Author: | mttarry [ Mon Mar 25, 2024 4:10 pm ] |
Post subject: | Re: COM1 IRQ Not Triggering Every Time |
Octocontrabass wrote: mttarry wrote: Code: outb(SERIAL_PORT + DIVISOR_LSB_OFFSET, 0x01); // set LSB of divisor outb(SERIAL_PORT + DIVISOR_MSB_OFFSET, 0x00); // set MSB of divisor Have you tried a bigger divisor? Maybe it's just too fast without the FIFO. I just tried increasing the divisor ( tried values 0x01-0xFF), and still see the same behavior. I also repeated that with the FIFO being enabled. Out of 20 runs of Code: echo -n "hi" > /dev/ttys002 , the IRQ4 will trigger anywhere between 8-14 times. At this point I'm thinking that maybe it's something to do with the MacOS port of qemu-system-i386, but any other ideas would be greatly appreciated.
|
Author: | Octocontrabass [ Mon Mar 25, 2024 6:24 pm ] |
Post subject: | Re: COM1 IRQ Not Triggering Every Time |
The only other thing that caught my eye is that you're reading the interrupt controller's in-service register to check which interrupt you're handling. Have you tried passing the interrupt vector from your interrupt stub instead? |
Author: | mttarry [ Mon Mar 25, 2024 6:44 pm ] |
Post subject: | Re: COM1 IRQ Not Triggering Every Time |
I've just identified that with QEMU -serial stdio, my COM1 IRQ fires 100% of the time and I can retrieve the serial data that way. For some reason the -serial pty option, which opens a virtual serial device file on my host, causes the interrupt not to fire all the time. |
Page 1 of 1 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |