1313#include "net/net.h"
1414#include "hw/irq.h"
1515#include "hw/net/smc91c111.h"
16+ #include "hw/registerfields.h"
1617#include "hw/qdev-properties.h"
1718#include "qapi/error.h"
1819#include "qemu/log.h"
2223
2324/* Number of 2k memory pages available. */
2425#define NUM_PACKETS 4
26+ /*
27+ * Maximum size of a data frame, including the leading status word
28+ * and byte count fields and the trailing CRC, last data byte
29+ * and control byte (per figure 8-1 in the Microchip Technology
30+ * LAN91C111 datasheet).
31+ */
32+ #define MAX_PACKET_SIZE 2048
2533
2634#define TYPE_SMC91C111 "smc91c111"
2735OBJECT_DECLARE_SIMPLE_TYPE (smc91c111_state , SMC91C111 )
@@ -51,7 +59,7 @@ struct smc91c111_state {
5159 int tx_fifo_done_len ;
5260 int tx_fifo_done [NUM_PACKETS ];
5361 /* Packet buffer memory. */
54- uint8_t data [NUM_PACKETS ][2048 ];
62+ uint8_t data [NUM_PACKETS ][MAX_PACKET_SIZE ];
5563 uint8_t int_level ;
5664 uint8_t int_mask ;
5765 MemoryRegion mmio ;
@@ -79,7 +87,8 @@ static const VMStateDescription vmstate_smc91c111 = {
7987 VMSTATE_INT32_ARRAY (rx_fifo , smc91c111_state , NUM_PACKETS ),
8088 VMSTATE_INT32 (tx_fifo_done_len , smc91c111_state ),
8189 VMSTATE_INT32_ARRAY (tx_fifo_done , smc91c111_state , NUM_PACKETS ),
82- VMSTATE_BUFFER_UNSAFE (data , smc91c111_state , 0 , NUM_PACKETS * 2048 ),
90+ VMSTATE_BUFFER_UNSAFE (data , smc91c111_state , 0 ,
91+ NUM_PACKETS * MAX_PACKET_SIZE ),
8392 VMSTATE_UINT8 (int_level , smc91c111_state ),
8493 VMSTATE_UINT8 (int_mask , smc91c111_state ),
8594 VMSTATE_END_OF_LIST ()
@@ -118,6 +127,18 @@ static const VMStateDescription vmstate_smc91c111 = {
118127#define RS_TOOSHORT 0x0400
119128#define RS_MULTICAST 0x0001
120129
130+ FIELD (PTR , PTR , 0 , 11 )
131+ FIELD (PTR , NOT_EMPTY , 11 , 1 )
132+ FIELD (PTR , RESERVED , 12 , 1 )
133+ FIELD (PTR , READ , 13 , 1 )
134+ FIELD (PTR , AUTOINCR , 14 , 1 )
135+ FIELD (PTR , RCV , 15 , 1 )
136+
137+ static inline bool packetnum_valid (int packet_num )
138+ {
139+ return packet_num >= 0 && packet_num < NUM_PACKETS ;
140+ }
141+
121142/* Update interrupt status. */
122143static void smc91c111_update (smc91c111_state * s )
123144{
@@ -218,12 +239,33 @@ static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
218239/* Release the memory allocated to a packet. */
219240static void smc91c111_release_packet (smc91c111_state * s , int packet )
220241{
242+ if (!packetnum_valid (packet )) {
243+ /*
244+ * Data sheet doesn't document behaviour in this guest error
245+ * case, and there is no error status register to report it.
246+ * Log and ignore the attempt.
247+ */
248+ qemu_log_mask (LOG_GUEST_ERROR ,
249+ "smc91c111: attempt to release invalid packet %d\n" ,
250+ packet );
251+ return ;
252+ }
221253 s -> allocated &= ~(1 << packet );
222254 if (s -> tx_alloc == 0x80 )
223255 smc91c111_tx_alloc (s );
224256 smc91c111_flush_queued_packets (s );
225257}
226258
259+ static void smc91c111_complete_tx_packet (smc91c111_state * s , int packetnum )
260+ {
261+ if (s -> ctr & CTR_AUTO_RELEASE ) {
262+ /* Race? */
263+ smc91c111_release_packet (s , packetnum );
264+ } else if (s -> tx_fifo_done_len < NUM_PACKETS ) {
265+ s -> tx_fifo_done [s -> tx_fifo_done_len ++ ] = packetnum ;
266+ }
267+ }
268+
227269/* Flush the TX FIFO. */
228270static void smc91c111_do_tx (smc91c111_state * s )
229271{
@@ -239,12 +281,25 @@ static void smc91c111_do_tx(smc91c111_state *s)
239281 return ;
240282 for (i = 0 ; i < s -> tx_fifo_len ; i ++ ) {
241283 packetnum = s -> tx_fifo [i ];
284+ /* queue_tx checked the packet number was valid */
285+ assert (packetnum_valid (packetnum ));
242286 p = & s -> data [packetnum ][0 ];
243287 /* Set status word. */
244288 * (p ++ ) = 0x01 ;
245289 * (p ++ ) = 0x40 ;
246290 len = * (p ++ );
247291 len |= ((int )* (p ++ )) << 8 ;
292+ if (len > MAX_PACKET_SIZE ) {
293+ /*
294+ * Datasheet doesn't say what to do here, and there is no
295+ * relevant tx error condition listed. Log, and drop the packet.
296+ */
297+ qemu_log_mask (LOG_GUEST_ERROR ,
298+ "smc91c111: tx packet with bad length %d, dropping\n" ,
299+ len );
300+ smc91c111_complete_tx_packet (s , packetnum );
301+ continue ;
302+ }
248303 len -= 6 ;
249304 control = p [len + 1 ];
250305 if (control & 0x20 )
@@ -273,11 +328,7 @@ static void smc91c111_do_tx(smc91c111_state *s)
273328 }
274329 }
275330#endif
276- if (s -> ctr & CTR_AUTO_RELEASE )
277- /* Race? */
278- smc91c111_release_packet (s , packetnum );
279- else if (s -> tx_fifo_done_len < NUM_PACKETS )
280- s -> tx_fifo_done [s -> tx_fifo_done_len ++ ] = packetnum ;
331+ smc91c111_complete_tx_packet (s , packetnum );
281332 qemu_send_packet (qemu_get_queue (s -> nic ), p , len );
282333 }
283334 s -> tx_fifo_len = 0 ;
@@ -287,6 +338,17 @@ static void smc91c111_do_tx(smc91c111_state *s)
287338/* Add a packet to the TX FIFO. */
288339static void smc91c111_queue_tx (smc91c111_state * s , int packet )
289340{
341+ if (!packetnum_valid (packet )) {
342+ /*
343+ * Datasheet doesn't document behaviour in this error case, and
344+ * there's no error status register we could report it in.
345+ * Log and ignore.
346+ */
347+ qemu_log_mask (LOG_GUEST_ERROR ,
348+ "smc91c111: attempt to queue invalid packet %d\n" ,
349+ packet );
350+ return ;
351+ }
290352 if (s -> tx_fifo_len == NUM_PACKETS )
291353 return ;
292354 s -> tx_fifo [s -> tx_fifo_len ++ ] = packet ;
@@ -318,6 +380,49 @@ static void smc91c111_reset(DeviceState *dev)
318380#define SET_LOW (name , val ) s->name = (s->name & 0xff00) | val
319381#define SET_HIGH (name , val ) s->name = (s->name & 0xff) | (val << 8)
320382
383+ /*
384+ * The pointer register's pointer is an 11 bit value (so it exactly
385+ * indexes a 2048-byte data frame). Add the specified offset to it,
386+ * wrapping around at the 2048 byte mark, and return the resulting
387+ * wrapped value. There are flag bits in the top part of the register,
388+ * but we can ignore them here as the mask will mask them out.
389+ */
390+ static int ptr_reg_add (smc91c111_state * s , int offset )
391+ {
392+ return (s -> ptr + offset ) & R_PTR_PTR_MASK ;
393+ }
394+
395+ /*
396+ * For an access to the Data Register at @offset, return the
397+ * required offset into the packet's data frame. This will
398+ * perform the pointer register autoincrement if required, and
399+ * guarantees to return an in-bounds offset.
400+ */
401+ static int data_reg_ptr (smc91c111_state * s , int offset )
402+ {
403+ int p ;
404+
405+ if (s -> ptr & R_PTR_AUTOINCR_MASK ) {
406+ /*
407+ * Autoincrement: use the current pointer value, and
408+ * increment the pointer register's pointer field.
409+ */
410+ p = FIELD_EX32 (s -> ptr , PTR , PTR );
411+ s -> ptr = FIELD_DP32 (s -> ptr , PTR , PTR , ptr_reg_add (s , 1 ));
412+ } else {
413+ /*
414+ * No autoincrement: register offset determines which
415+ * byte we're addressing. Setting the pointer to the top
416+ * of the data buffer and then using the pointer wrapping
417+ * to read the bottom byte of the buffer is not something
418+ * sensible guest software will do, but the datasheet
419+ * doesn't say what the behaviour is, so we don't forbid it.
420+ */
421+ p = ptr_reg_add (s , offset & 3 );
422+ }
423+ return p ;
424+ }
425+
321426static void smc91c111_writeb (void * opaque , hwaddr offset ,
322427 uint32_t value )
323428{
@@ -457,12 +562,14 @@ static void smc91c111_writeb(void *opaque, hwaddr offset,
457562 n = s -> rx_fifo [0 ];
458563 else
459564 n = s -> packet_num ;
460- p = s -> ptr & 0x07ff ;
461- if (s -> ptr & 0x4000 ) {
462- s -> ptr = (s -> ptr & 0xf800 ) | ((s -> ptr + 1 ) & 0x7ff );
463- } else {
464- p += (offset & 3 );
565+ if (!packetnum_valid (n )) {
566+ /* Datasheet doesn't document what to do here */
567+ qemu_log_mask (LOG_GUEST_ERROR ,
568+ "smc91c111: attempt to write data to invalid packet %d\n" ,
569+ n );
570+ return ;
465571 }
572+ p = data_reg_ptr (s , offset );
466573 s -> data [n ][p ] = value ;
467574 }
468575 return ;
@@ -605,12 +712,14 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
605712 n = s -> rx_fifo [0 ];
606713 else
607714 n = s -> packet_num ;
608- p = s -> ptr & 0x07ff ;
609- if (s -> ptr & 0x4000 ) {
610- s -> ptr = (s -> ptr & 0xf800 ) | ((s -> ptr + 1 ) & 0x07ff );
611- } else {
612- p += (offset & 3 );
715+ if (!packetnum_valid (n )) {
716+ /* Datasheet doesn't document what to do here */
717+ qemu_log_mask (LOG_GUEST_ERROR ,
718+ "smc91c111: attempt to read data from invalid packet %d\n" ,
719+ n );
720+ return 0 ;
613721 }
722+ p = data_reg_ptr (s , offset );
614723 return s -> data [n ][p ];
615724 }
616725 case 12 : /* Interrupt status. */
@@ -706,13 +815,16 @@ static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t
706815 if (crc )
707816 packetsize += 4 ;
708817 /* TODO: Flag overrun and receive errors. */
709- if (packetsize > 2048 )
818+ if (packetsize > MAX_PACKET_SIZE ) {
710819 return -1 ;
820+ }
711821 packetnum = smc91c111_allocate_packet (s );
712822 if (packetnum == 0x80 )
713823 return -1 ;
714824 s -> rx_fifo [s -> rx_fifo_len ++ ] = packetnum ;
715825
826+ /* allocate_packet() will not hand us back an invalid packet number */
827+ assert (packetnum_valid (packetnum ));
716828 p = & s -> data [packetnum ][0 ];
717829 /* ??? Multicast packets? */
718830 status = 0 ;
0 commit comments