COM1 IRQ Not Triggering Every Time
Posted: Sun Mar 24, 2024 2:31 pm
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:
And I'm sending data from my host via:
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
serial.c
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: Select all
QEMU_FLAGS = -cdrom $(ISO_TARGET) -display cocoa,full-screen=on -monitor stdio -serial pty
Code: Select all
echo -n h > /dev/ttys002
Here is the relevant code:
irq.c
Code: Select all
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;
}
}
Code: Select all
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();
}
}
}