Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 1 | The smc91c111.c driver appears to have several issues. The can_receive() |
| 2 | function can return that the driver is ready when rx_fifo has not been |
| 3 | freed yet. There is also no sanity check of rx_fifo() in _receive() which |
| 4 | can lead to corruption of the rx_fifo array. |
| 5 | |
| 6 | release_packet() can also call qemu_flush_queued_packets() before rx_fifo |
| 7 | has been cleaned up, resulting in cases where packets are submitted |
| 8 | for which there is not yet any space. |
| 9 | |
| 10 | This patch therefore: |
| 11 | |
| 12 | * fixes the logic in can_receive() |
| 13 | * adds logic to receive() as a sanity check |
| 14 | * moves the flush() calls to the correct places where data is ready |
| 15 | to be received |
| 16 | |
| 17 | Upstream-Status: Pending [discussion in progress on mailing list] |
| 18 | RP 2015/9/7 |
| 19 | |
| 20 | Index: qemu-2.4.0/hw/net/smc91c111.c |
| 21 | =================================================================== |
| 22 | --- qemu-2.4.0.orig/hw/net/smc91c111.c |
| 23 | +++ qemu-2.4.0/hw/net/smc91c111.c |
| 24 | @@ -185,7 +185,6 @@ static void smc91c111_release_packet(smc |
| 25 | s->allocated &= ~(1 << packet); |
| 26 | if (s->tx_alloc == 0x80) |
| 27 | smc91c111_tx_alloc(s); |
| 28 | - qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| 29 | } |
| 30 | |
| 31 | /* Flush the TX FIFO. */ |
| 32 | @@ -237,9 +236,11 @@ static void smc91c111_do_tx(smc91c111_st |
| 33 | } |
| 34 | } |
| 35 | #endif |
| 36 | - if (s->ctr & CTR_AUTO_RELEASE) |
| 37 | + if (s->ctr & CTR_AUTO_RELEASE) { |
| 38 | /* Race? */ |
| 39 | smc91c111_release_packet(s, packetnum); |
| 40 | + qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| 41 | + } |
| 42 | else if (s->tx_fifo_done_len < NUM_PACKETS) |
| 43 | s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; |
| 44 | qemu_send_packet(qemu_get_queue(s->nic), p, len); |
| 45 | @@ -379,9 +380,11 @@ static void smc91c111_writeb(void *opaqu |
| 46 | smc91c111_release_packet(s, s->rx_fifo[0]); |
| 47 | } |
| 48 | smc91c111_pop_rx_fifo(s); |
| 49 | + qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| 50 | break; |
| 51 | case 5: /* Release. */ |
| 52 | smc91c111_release_packet(s, s->packet_num); |
| 53 | + qemu_flush_queued_packets(qemu_get_queue(s->nic)); |
| 54 | break; |
| 55 | case 6: /* Add to TX FIFO. */ |
| 56 | smc91c111_queue_tx(s, s->packet_num); |
| 57 | @@ -642,7 +642,7 @@ static int smc91c111_can_receive(NetClie |
| 58 | |
| 59 | if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) |
| 60 | return 1; |
| 61 | - if (s->allocated == (1 << NUM_PACKETS) - 1) |
| 62 | + if ((s->allocated == (1 << NUM_PACKETS) - 1) || (s->rx_fifo_len == NUM_PACKETS)) |
| 63 | return 0; |
| 64 | return 1; |
| 65 | } |
| 66 | @@ -671,6 +671,8 @@ static ssize_t smc91c111_receive(NetClie |
| 67 | /* TODO: Flag overrun and receive errors. */ |
| 68 | if (packetsize > 2048) |
| 69 | return -1; |
| 70 | + if (s->rx_fifo_len == NUM_PACKETS) |
| 71 | + return -1; |
| 72 | packetnum = smc91c111_allocate_packet(s); |
| 73 | if (packetnum == 0x80) |
| 74 | return -1; |