| The smc91c111.c driver appears to have several issues. The can_receive() |
| function can return that the driver is ready when rx_fifo has not been |
| freed yet. There is also no sanity check of rx_fifo() in _receive() which |
| can lead to corruption of the rx_fifo array. |
| |
| release_packet() can also call qemu_flush_queued_packets() before rx_fifo |
| has been cleaned up, resulting in cases where packets are submitted |
| for which there is not yet any space. |
| |
| This patch therefore: |
| |
| * fixes the logic in can_receive() |
| * adds logic to receive() as a sanity check |
| * moves the flush() calls to the correct places where data is ready |
| to be received |
| |
| Upstream-Status: Pending [discussion in progress on mailing list] |
| RP 2015/9/7 |
| |
| Index: qemu-2.4.0/hw/net/smc91c111.c |
| =================================================================== |
| --- qemu-2.4.0.orig/hw/net/smc91c111.c |
| +++ qemu-2.4.0/hw/net/smc91c111.c |
| @@ -185,7 +185,6 @@ static void smc91c111_release_packet(smc |
| s->allocated &= ~(1 << packet); |
| if (s->tx_alloc == 0x80) |
| smc91c111_tx_alloc(s); |
| - qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| } |
| |
| /* Flush the TX FIFO. */ |
| @@ -237,9 +236,11 @@ static void smc91c111_do_tx(smc91c111_st |
| } |
| } |
| #endif |
| - if (s->ctr & CTR_AUTO_RELEASE) |
| + if (s->ctr & CTR_AUTO_RELEASE) { |
| /* Race? */ |
| smc91c111_release_packet(s, packetnum); |
| + qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| + } |
| else if (s->tx_fifo_done_len < NUM_PACKETS) |
| s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; |
| qemu_send_packet(qemu_get_queue(s->nic), p, len); |
| @@ -379,9 +380,11 @@ static void smc91c111_writeb(void *opaqu |
| smc91c111_release_packet(s, s->rx_fifo[0]); |
| } |
| smc91c111_pop_rx_fifo(s); |
| + qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| break; |
| case 5: /* Release. */ |
| smc91c111_release_packet(s, s->packet_num); |
| + qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| break; |
| case 6: /* Add to TX FIFO. */ |
| smc91c111_queue_tx(s, s->packet_num); |
| @@ -642,7 +642,7 @@ static int smc91c111_can_receive(NetClie |
| |
| if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) |
| return 1; |
| - if (s->allocated == (1 << NUM_PACKETS) - 1) |
| + if ((s->allocated == (1 << NUM_PACKETS) - 1) || (s->rx_fifo_len == NUM_PACKETS)) |
| return 0; |
| return 1; |
| } |
| @@ -671,6 +671,8 @@ static ssize_t smc91c111_receive(NetClie |
| /* TODO: Flag overrun and receive errors. */ |
| if (packetsize > 2048) |
| return -1; |
| + if (s->rx_fifo_len == NUM_PACKETS) |
| + return -1; |
| packetnum = smc91c111_allocate_packet(s); |
| if (packetnum == 0x80) |
| return -1; |