diff --git a/hw/serial.c b/hw/serial.c index b1bd0ff..c902792 100644 --- a/hw/serial.c +++ b/hw/serial.c @@ -73,6 +73,15 @@ #define UART_LSR_OE 0x02 /* Overrun error indicator */ #define UART_LSR_DR 0x01 /* Receiver data ready */ +/* + * It's common for an IRQ handler to keep reading the RBR until + * the LSR indicates that the FIFO is empty, expecting that the + * CPU is vastly faster than the serial line. This can cause + * overruns or error indications if the FIFO never empties, so + * give the target OS a breather every so often. + */ +#define MAX_BURST 512 + struct SerialState { uint16_t divider; uint8_t rbr; /* receive register */ @@ -91,8 +100,14 @@ struct SerialState { int last_break_enable; target_phys_addr_t base; int it_shift; + int burst_len; }; +static void serial_clear_burst(SerialState *s) +{ + s->burst_len = 0; +} + static void serial_update_irq(SerialState *s) { if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) { @@ -114,6 +129,8 @@ static void serial_update_parameters(SerialState *s) int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; + serial_clear_burst(s); + if (s->lcr & 0x08) { if (s->lcr & 0x10) parity = 'E'; @@ -221,9 +238,12 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr) ret = s->divider & 0xff; } else { ret = s->rbr; - s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); - serial_update_irq(s); - qemu_chr_accept_input(s->chr); + if (s->burst_len < MAX_BURST) { + s->burst_len++; + s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); + serial_update_irq(s); + qemu_chr_accept_input(s->chr); + } } break; case 1: @@ -235,6 +255,7 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr) break; case 2: ret = s->iir; + serial_clear_burst(s); /* reset THR pending bit */ if ((ret & 0x7) == UART_IIR_THRI) s->thr_ipending = 0; @@ -248,6 +269,10 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr) break; case 5: ret = s->lsr; + if (s->burst_len >= MAX_BURST) + ret &= ~(UART_LSR_DR|UART_LSR_BI); + if (!(ret & UART_LSR_DR)) + serial_clear_burst(s); break; case 6: if (s->mcr & UART_MCR_LOOP) {