13
13
#include "net/net.h"
14
14
#include "hw/irq.h"
15
15
#include "hw/net/smc91c111.h"
16
+ #include "hw/registerfields.h"
16
17
#include "hw/qdev-properties.h"
17
18
#include "qapi/error.h"
18
19
#include "qemu/log.h"
22
23
23
24
/* Number of 2k memory pages available. */
24
25
#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
25
33
26
34
#define TYPE_SMC91C111 "smc91c111"
27
35
OBJECT_DECLARE_SIMPLE_TYPE (smc91c111_state , SMC91C111 )
@@ -51,7 +59,7 @@ struct smc91c111_state {
51
59
int tx_fifo_done_len ;
52
60
int tx_fifo_done [NUM_PACKETS ];
53
61
/* Packet buffer memory. */
54
- uint8_t data [NUM_PACKETS ][2048 ];
62
+ uint8_t data [NUM_PACKETS ][MAX_PACKET_SIZE ];
55
63
uint8_t int_level ;
56
64
uint8_t int_mask ;
57
65
MemoryRegion mmio ;
@@ -79,7 +87,8 @@ static const VMStateDescription vmstate_smc91c111 = {
79
87
VMSTATE_INT32_ARRAY (rx_fifo , smc91c111_state , NUM_PACKETS ),
80
88
VMSTATE_INT32 (tx_fifo_done_len , smc91c111_state ),
81
89
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 ),
83
92
VMSTATE_UINT8 (int_level , smc91c111_state ),
84
93
VMSTATE_UINT8 (int_mask , smc91c111_state ),
85
94
VMSTATE_END_OF_LIST ()
@@ -118,6 +127,18 @@ static const VMStateDescription vmstate_smc91c111 = {
118
127
#define RS_TOOSHORT 0x0400
119
128
#define RS_MULTICAST 0x0001
120
129
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
+
121
142
/* Update interrupt status. */
122
143
static void smc91c111_update (smc91c111_state * s )
123
144
{
@@ -218,12 +239,33 @@ static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
218
239
/* Release the memory allocated to a packet. */
219
240
static void smc91c111_release_packet (smc91c111_state * s , int packet )
220
241
{
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
+ }
221
253
s -> allocated &= ~(1 << packet );
222
254
if (s -> tx_alloc == 0x80 )
223
255
smc91c111_tx_alloc (s );
224
256
smc91c111_flush_queued_packets (s );
225
257
}
226
258
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
+
227
269
/* Flush the TX FIFO. */
228
270
static void smc91c111_do_tx (smc91c111_state * s )
229
271
{
@@ -239,12 +281,25 @@ static void smc91c111_do_tx(smc91c111_state *s)
239
281
return ;
240
282
for (i = 0 ; i < s -> tx_fifo_len ; i ++ ) {
241
283
packetnum = s -> tx_fifo [i ];
284
+ /* queue_tx checked the packet number was valid */
285
+ assert (packetnum_valid (packetnum ));
242
286
p = & s -> data [packetnum ][0 ];
243
287
/* Set status word. */
244
288
* (p ++ ) = 0x01 ;
245
289
* (p ++ ) = 0x40 ;
246
290
len = * (p ++ );
247
291
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
+ }
248
303
len -= 6 ;
249
304
control = p [len + 1 ];
250
305
if (control & 0x20 )
@@ -273,11 +328,7 @@ static void smc91c111_do_tx(smc91c111_state *s)
273
328
}
274
329
}
275
330
#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 );
281
332
qemu_send_packet (qemu_get_queue (s -> nic ), p , len );
282
333
}
283
334
s -> tx_fifo_len = 0 ;
@@ -287,6 +338,17 @@ static void smc91c111_do_tx(smc91c111_state *s)
287
338
/* Add a packet to the TX FIFO. */
288
339
static void smc91c111_queue_tx (smc91c111_state * s , int packet )
289
340
{
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
+ }
290
352
if (s -> tx_fifo_len == NUM_PACKETS )
291
353
return ;
292
354
s -> tx_fifo [s -> tx_fifo_len ++ ] = packet ;
@@ -318,6 +380,49 @@ static void smc91c111_reset(DeviceState *dev)
318
380
#define SET_LOW (name , val ) s->name = (s->name & 0xff00) | val
319
381
#define SET_HIGH (name , val ) s->name = (s->name & 0xff) | (val << 8)
320
382
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
+
321
426
static void smc91c111_writeb (void * opaque , hwaddr offset ,
322
427
uint32_t value )
323
428
{
@@ -457,12 +562,14 @@ static void smc91c111_writeb(void *opaque, hwaddr offset,
457
562
n = s -> rx_fifo [0 ];
458
563
else
459
564
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 ;
465
571
}
572
+ p = data_reg_ptr (s , offset );
466
573
s -> data [n ][p ] = value ;
467
574
}
468
575
return ;
@@ -605,12 +712,14 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
605
712
n = s -> rx_fifo [0 ];
606
713
else
607
714
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 ;
613
721
}
722
+ p = data_reg_ptr (s , offset );
614
723
return s -> data [n ][p ];
615
724
}
616
725
case 12 : /* Interrupt status. */
@@ -706,13 +815,16 @@ static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t
706
815
if (crc )
707
816
packetsize += 4 ;
708
817
/* TODO: Flag overrun and receive errors. */
709
- if (packetsize > 2048 )
818
+ if (packetsize > MAX_PACKET_SIZE ) {
710
819
return -1 ;
820
+ }
711
821
packetnum = smc91c111_allocate_packet (s );
712
822
if (packetnum == 0x80 )
713
823
return -1 ;
714
824
s -> rx_fifo [s -> rx_fifo_len ++ ] = packetnum ;
715
825
826
+ /* allocate_packet() will not hand us back an invalid packet number */
827
+ assert (packetnum_valid (packetnum ));
716
828
p = & s -> data [packetnum ][0 ];
717
829
/* ??? Multicast packets? */
718
830
status = 0 ;
0 commit comments