2 * QEMU Intel i82596 (Apricot) emulation
4 * Copyright (c) 2019 Helge Deller <deller@gmx.de>
5 * This work is licensed under the GNU GPL license version 2 or later.
7 * This software was written to be compatible with the specification:
8 * https://www.intel.com/assets/pdf/general/82596ca.pdf
11 #include "qemu/osdep.h"
12 #include "qemu/timer.h"
16 #include "hw/qdev-properties.h"
17 #include "migration/vmstate.h"
18 #include "qemu/module.h"
21 #include <zlib.h> /* For crc32 */
23 #if defined(ENABLE_DEBUG)
26 #define DBG(x) do { } while (0)
31 #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
33 #define PKT_BUF_SZ 1536
36 #define ISCP_BUSY 0x0001
38 #define I596_NULL ((uint32_t)0xffffffff)
40 #define SCB_STATUS_CX 0x8000 /* CU finished command with I bit */
41 #define SCB_STATUS_FR 0x4000 /* RU finished receiving a frame */
42 #define SCB_STATUS_CNA 0x2000 /* CU left active state */
43 #define SCB_STATUS_RNR 0x1000 /* RU left active state */
45 #define SCB_COMMAND_ACK_MASK \
46 (SCB_STATUS_CX | SCB_STATUS_FR | SCB_STATUS_CNA | SCB_STATUS_RNR)
49 #define CU_SUSPENDED 1
53 #define RX_SUSPENDED 1
56 #define CMD_EOL 0x8000 /* The last command of the list, stop. */
57 #define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
58 #define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
60 #define CMD_FLEX 0x0008 /* Enable flexible memory model */
63 CmdNOp
= 0, CmdSASetup
= 1, CmdConfigure
= 2, CmdMulticastList
= 3,
64 CmdTx
= 4, CmdTDR
= 5, CmdDump
= 6, CmdDiagnose
= 7
67 #define STAT_C 0x8000 /* Set to 0 after execution */
68 #define STAT_B 0x4000 /* Command being executed */
69 #define STAT_OK 0x2000 /* Command executed ok */
70 #define STAT_A 0x1000 /* Command aborted */
72 #define I596_EOF 0x8000
73 #define SIZE_MASK 0x3fff
75 #define ETHER_TYPE_LEN 2
76 #define VLAN_TCI_LEN 2
77 #define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN)
79 /* various flags in the chip config registers */
80 #define I596_PREFETCH (s->config[0] & 0x80)
81 #define I596_PROMISC (s->config[8] & 0x01)
82 #define I596_BC_DISABLE (s->config[8] & 0x02) /* broadcast disable */
83 #define I596_NOCRC_INS (s->config[8] & 0x08)
84 #define I596_CRCINM (s->config[11] & 0x04) /* CRC appended */
85 #define I596_MC_ALL (s->config[11] & 0x20)
86 #define I596_MULTIIA (s->config[13] & 0x40)
89 static uint8_t get_byte(uint32_t addr
)
91 return ldub_phys(&address_space_memory
, addr
);
94 static void set_byte(uint32_t addr
, uint8_t c
)
96 return stb_phys(&address_space_memory
, addr
, c
);
99 static uint16_t get_uint16(uint32_t addr
)
101 return lduw_be_phys(&address_space_memory
, addr
);
104 static void set_uint16(uint32_t addr
, uint16_t w
)
106 return stw_be_phys(&address_space_memory
, addr
, w
);
109 static uint32_t get_uint32(uint32_t addr
)
111 uint32_t lo
= lduw_be_phys(&address_space_memory
, addr
);
112 uint32_t hi
= lduw_be_phys(&address_space_memory
, addr
+ 2);
113 return (hi
<< 16) | lo
;
116 static void set_uint32(uint32_t addr
, uint32_t val
)
118 set_uint16(addr
, (uint16_t) val
);
119 set_uint16(addr
+ 2, val
>> 16);
123 struct qemu_ether_header
{
124 uint8_t ether_dhost
[6];
125 uint8_t ether_shost
[6];
129 #define PRINT_PKTHDR(txt, BUF) do { \
130 struct qemu_ether_header *hdr = (void *)(BUF); \
131 printf(txt ": packet dhost=" MAC_FMT ", shost=" MAC_FMT ", type=0x%04x\n",\
132 MAC_ARG(hdr->ether_dhost), MAC_ARG(hdr->ether_shost), \
133 be16_to_cpu(hdr->ether_type)); \
136 static void i82596_transmit(I82596State
*s
, uint32_t addr
)
138 uint32_t tdb_p
; /* Transmit Buffer Descriptor */
140 /* TODO: Check flexible mode */
141 tdb_p
= get_uint32(addr
+ 8);
142 while (tdb_p
!= I596_NULL
) {
146 size
= get_uint16(tdb_p
);
147 len
= size
& SIZE_MASK
;
148 tba
= get_uint32(tdb_p
+ 8);
149 trace_i82596_transmit(len
, tba
);
152 assert(len
<= sizeof(s
->tx_buffer
));
153 address_space_read(&address_space_memory
, tba
,
154 MEMTXATTRS_UNSPECIFIED
, s
->tx_buffer
, len
);
155 DBG(PRINT_PKTHDR("Send", &s
->tx_buffer
));
156 DBG(printf("Sending %d bytes\n", len
));
157 qemu_send_packet(qemu_get_queue(s
->nic
), s
->tx_buffer
, len
);
160 /* was this the last package? */
161 if (size
& I596_EOF
) {
165 /* get next buffer pointer */
166 tdb_p
= get_uint32(tdb_p
+ 4);
170 static void set_individual_address(I82596State
*s
, uint32_t addr
)
175 nc
= qemu_get_queue(s
->nic
);
176 m
= s
->conf
.macaddr
.a
;
177 address_space_read(&address_space_memory
, addr
+ 8,
178 MEMTXATTRS_UNSPECIFIED
, m
, ETH_ALEN
);
179 qemu_format_nic_info_str(nc
, m
);
180 trace_i82596_new_mac(nc
->info_str
);
183 static void set_multicast_list(I82596State
*s
, uint32_t addr
)
185 uint16_t mc_count
, i
;
187 memset(&s
->mult
[0], 0, sizeof(s
->mult
));
188 mc_count
= get_uint16(addr
+ 8) / ETH_ALEN
;
190 if (mc_count
> MAX_MC_CNT
) {
191 mc_count
= MAX_MC_CNT
;
193 for (i
= 0; i
< mc_count
; i
++) {
194 uint8_t multicast_addr
[ETH_ALEN
];
195 address_space_read(&address_space_memory
, addr
+ i
* ETH_ALEN
,
196 MEMTXATTRS_UNSPECIFIED
, multicast_addr
, ETH_ALEN
);
197 DBG(printf("Add multicast entry " MAC_FMT
"\n",
198 MAC_ARG(multicast_addr
)));
199 unsigned mcast_idx
= (net_crc32(multicast_addr
, ETH_ALEN
) &
201 assert(mcast_idx
< 8 * sizeof(s
->mult
));
202 s
->mult
[mcast_idx
>> 3] |= (1 << (mcast_idx
& 7));
204 trace_i82596_set_multicast(mc_count
);
207 void i82596_set_link_status(NetClientState
*nc
)
209 I82596State
*d
= qemu_get_nic_opaque(nc
);
211 d
->lnkst
= nc
->link_down
? 0 : 0x8000;
214 static void update_scb_status(I82596State
*s
)
216 s
->scb_status
= (s
->scb_status
& 0xf000)
217 | (s
->cu_status
<< 8) | (s
->rx_status
<< 4);
218 set_uint16(s
->scb
, s
->scb_status
);
222 static void i82596_s_reset(I82596State
*s
)
224 trace_i82596_s_reset(s
);
227 s
->cu_status
= CU_IDLE
;
228 s
->rx_status
= RX_SUSPENDED
;
229 s
->cmd_p
= I596_NULL
;
230 s
->lnkst
= 0x8000; /* initial link state: up */
231 s
->ca
= s
->ca_active
= 0;
236 static void command_loop(I82596State
*s
)
242 DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s
->cmd_p
));
244 while (s
->cmd_p
!= I596_NULL
) {
247 set_uint16(s
->cmd_p
, status
);
248 status
= STAT_C
| STAT_OK
; /* update, but write later */
250 cmd
= get_uint16(s
->cmd_p
+ 2);
251 DBG(printf("Running command %04x at %08x\n", cmd
, s
->cmd_p
));
253 switch (cmd
& 0x07) {
257 set_individual_address(s
, s
->cmd_p
);
260 byte_cnt
= get_byte(s
->cmd_p
+ 8) & 0x0f;
261 byte_cnt
= MAX(byte_cnt
, 4);
262 byte_cnt
= MIN(byte_cnt
, sizeof(s
->config
));
263 /* copy byte_cnt max. */
264 address_space_read(&address_space_memory
, s
->cmd_p
+ 8,
265 MEMTXATTRS_UNSPECIFIED
, s
->config
, byte_cnt
);
266 /* config byte according to page 35ff */
267 s
->config
[2] &= 0x82; /* mask valid bits */
268 s
->config
[2] |= 0x40;
269 s
->config
[7] &= 0xf7; /* clear zero bit */
270 assert(I596_NOCRC_INS
== 0); /* do CRC insertion */
271 s
->config
[10] = MAX(s
->config
[10], 5); /* min frame length */
272 s
->config
[12] &= 0x40; /* only full duplex field valid */
273 s
->config
[13] |= 0x3f; /* set ones in byte 13 */
276 /* get signal LINK */
277 set_uint32(s
->cmd_p
+ 8, s
->lnkst
);
280 i82596_transmit(s
, s
->cmd_p
);
282 case CmdMulticastList
:
283 set_multicast_list(s
, s
->cmd_p
);
287 printf("FIXME Command %d !!\n", cmd
& 7);
292 set_uint16(s
->cmd_p
, status
);
294 s
->cmd_p
= get_uint32(s
->cmd_p
+ 4); /* get link address */
295 DBG(printf("NEXT addr would be %08x\n", s
->cmd_p
));
297 s
->cmd_p
= I596_NULL
;
300 /* Stop when last command of the list. */
302 s
->cmd_p
= I596_NULL
;
304 /* Suspend after doing cmd? */
305 if (cmd
& CMD_SUSP
) {
306 s
->cu_status
= CU_SUSPENDED
;
307 printf("FIXME SUSPEND !!\n");
309 /* Interrupt after doing cmd? */
310 if (cmd
& CMD_INTR
) {
311 s
->scb_status
|= SCB_STATUS_CX
;
313 s
->scb_status
&= ~SCB_STATUS_CX
;
315 update_scb_status(s
);
317 /* Interrupt after doing cmd? */
318 if (cmd
& CMD_INTR
) {
322 if (s
->cu_status
!= CU_ACTIVE
) {
326 DBG(printf("FINISHED COMMAND LOOP\n"));
327 qemu_flush_queued_packets(qemu_get_queue(s
->nic
));
330 static void i82596_flush_queue_timer(void *opaque
)
332 I82596State
*s
= opaque
;
334 timer_del(s
->flush_queue_timer
);
335 qemu_flush_queued_packets(qemu_get_queue(s
->nic
));
336 timer_mod(s
->flush_queue_timer
,
337 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) + 1000);
341 static void examine_scb(I82596State
*s
)
343 uint16_t command
, cuc
, ruc
;
345 /* get the scb command word */
346 command
= get_uint16(s
->scb
+ 2);
347 cuc
= (command
>> 8) & 0x7;
348 ruc
= (command
>> 4) & 0x7;
349 DBG(printf("MAIN COMMAND %04x cuc %02x ruc %02x\n", command
, cuc
, ruc
));
350 /* and clear the scb command word */
351 set_uint16(s
->scb
+ 2, 0);
353 s
->scb_status
&= ~(command
& SCB_COMMAND_ACK_MASK
);
356 case 0: /* no change */
358 case 1: /* CUC_START */
359 s
->cu_status
= CU_ACTIVE
;
361 case 4: /* CUC_ABORT */
362 s
->cu_status
= CU_SUSPENDED
;
363 s
->scb_status
|= SCB_STATUS_CNA
; /* CU left active state */
366 printf("WARNING: Unknown CUC %d!\n", cuc
);
370 case 0: /* no change */
372 case 1: /* RX_START */
373 case 2: /* RX_RESUME */
374 s
->rx_status
= RX_IDLE
;
376 timer_mod(s
->flush_queue_timer
, qemu_clock_get_ms(
377 QEMU_CLOCK_VIRTUAL
) + 1000);
380 case 3: /* RX_SUSPEND */
381 case 4: /* RX_ABORT */
382 s
->rx_status
= RX_SUSPENDED
;
383 s
->scb_status
|= SCB_STATUS_RNR
; /* RU left active state */
386 printf("WARNING: Unknown RUC %d!\n", ruc
);
389 if (command
& 0x80) { /* reset bit set? */
393 /* execute commands from SCBL */
394 if (s
->cu_status
!= CU_SUSPENDED
) {
395 if (s
->cmd_p
== I596_NULL
) {
396 s
->cmd_p
= get_uint32(s
->scb
+ 4);
400 /* update scb status */
401 update_scb_status(s
);
406 static void signal_ca(I82596State
*s
)
410 /* trace_i82596_channel_attention(s); */
412 /* CA after reset -> do init with new scp. */
413 s
->sysbus
= get_byte(s
->scp
+ 3); /* big endian */
414 DBG(printf("SYSBUS = %08x\n", s
->sysbus
));
415 if (((s
->sysbus
>> 1) & 0x03) != 2) {
416 printf("WARNING: NO LINEAR MODE !!\n");
418 if ((s
->sysbus
>> 7)) {
419 printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\n");
421 iscp
= get_uint32(s
->scp
+ 8);
422 s
->scb
= get_uint32(iscp
+ 4);
423 set_byte(iscp
+ 1, 0); /* clear BUSY flag in iscp */
427 s
->ca
++; /* count ca() */
439 qemu_set_irq(s
->irq
, 1);
443 void i82596_ioport_writew(void *opaque
, uint32_t addr
, uint32_t val
)
445 I82596State
*s
= opaque
;
446 /* printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); */
448 case PORT_RESET
: /* Reset */
460 uint32_t i82596_ioport_readw(void *opaque
, uint32_t addr
)
465 void i82596_h_reset(void *opaque
)
467 I82596State
*s
= opaque
;
472 bool i82596_can_receive(NetClientState
*nc
)
474 I82596State
*s
= qemu_get_nic_opaque(nc
);
476 if (s
->rx_status
== RX_SUSPENDED
) {
484 if (USE_TIMER
&& !timer_pending(s
->flush_queue_timer
)) {
491 #define MIN_BUF_SIZE 60
493 ssize_t
i82596_receive(NetClientState
*nc
, const uint8_t *buf
, size_t sz
)
495 I82596State
*s
= qemu_get_nic_opaque(nc
);
498 uint16_t is_broadcast
= 0;
499 size_t len
= sz
; /* length of data for guest (including CRC) */
500 size_t bufsz
= sz
; /* length of data in buf */
503 uint8_t buf1
[MIN_BUF_SIZE
+ VLAN_HLEN
];
504 static const uint8_t broadcast_macaddr
[6] = {
505 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
507 DBG(printf("i82596_receive() start\n"));
509 if (USE_TIMER
&& timer_pending(s
->flush_queue_timer
)) {
513 /* first check if receiver is enabled */
514 if (s
->rx_status
== RX_SUSPENDED
) {
515 trace_i82596_receive_analysis(">>> Receiving suspended");
520 trace_i82596_receive_analysis(">>> Link down");
524 /* Received frame smaller than configured "min frame len"? */
525 if (sz
< s
->config
[10]) {
526 printf("Received frame too small, %zu vs. %u bytes\n",
531 DBG(printf("Received %lu bytes\n", sz
));
535 /* promiscuous: receive all */
536 trace_i82596_receive_analysis(
537 ">>> packet received in promiscuous mode");
541 if (!memcmp(buf
, broadcast_macaddr
, 6)) {
542 /* broadcast address */
543 if (I596_BC_DISABLE
) {
544 trace_i82596_receive_analysis(">>> broadcast packet rejected");
549 trace_i82596_receive_analysis(">>> broadcast packet received");
552 } else if (buf
[0] & 0x01) {
555 trace_i82596_receive_analysis(">>> multicast packet rejected");
560 int mcast_idx
= (net_crc32(buf
, ETH_ALEN
) & BITS(7, 2)) >> 2;
561 assert(mcast_idx
< 8 * sizeof(s
->mult
));
563 if (!(s
->mult
[mcast_idx
>> 3] & (1 << (mcast_idx
& 7)))) {
564 trace_i82596_receive_analysis(">>> multicast address mismatch");
569 trace_i82596_receive_analysis(">>> multicast packet received");
572 } else if (!memcmp(s
->conf
.macaddr
.a
, buf
, 6)) {
575 trace_i82596_receive_analysis(
576 ">>> physical address matching packet received");
580 trace_i82596_receive_analysis(">>> unknown packet");
586 /* if too small buffer, then expand it */
587 if (len
< MIN_BUF_SIZE
+ VLAN_HLEN
) {
588 memcpy(buf1
, buf
, len
);
589 memset(buf1
+ len
, 0, MIN_BUF_SIZE
+ VLAN_HLEN
- len
);
591 if (len
< MIN_BUF_SIZE
) {
597 /* Calculate the ethernet checksum (4 bytes) */
599 crc
= cpu_to_be32(crc32(~0, buf
, sz
));
600 crc_ptr
= (uint8_t *) &crc
;
602 rfd_p
= get_uint32(s
->scb
+ 8); /* get Receive Frame Descriptor */
603 assert(rfd_p
&& rfd_p
!= I596_NULL
);
605 /* get first Receive Buffer Descriptor Address */
606 rbd
= get_uint32(rfd_p
+ 8);
607 assert(rbd
&& rbd
!= I596_NULL
);
609 trace_i82596_receive_packet(len
);
610 /* PRINT_PKTHDR("Receive", buf); */
613 uint16_t command
, status
;
616 command
= get_uint16(rfd_p
+ 2);
617 assert(command
& CMD_FLEX
); /* assert Flex Mode */
618 /* get first Receive Buffer Descriptor Address */
619 rbd
= get_uint32(rfd_p
+ 8);
620 assert(get_uint16(rfd_p
+ 14) == 0);
622 /* printf("Receive: rfd is %08x\n", rfd_p); */
625 uint16_t buffer_size
, num
;
627 size_t bufcount
, crccount
;
629 /* printf("Receive: rbd is %08x\n", rbd); */
630 buffer_size
= get_uint16(rbd
+ 12);
631 /* printf("buffer_size is 0x%x\n", buffer_size); */
632 assert(buffer_size
!= 0);
634 num
= buffer_size
& SIZE_MASK
;
638 rba
= get_uint32(rbd
+ 8);
639 /* printf("rba is 0x%x\n", rba); */
641 * Calculate how many bytes we want from buf[] and how many
644 if ((len
- num
) >= 4) {
645 /* The whole guest buffer, we haven't hit the CRC yet */
648 /* All that's left of buf[] */
651 crccount
= num
- bufcount
;
654 /* Still some of the actual data buffer to transfer */
655 assert(bufsz
>= bufcount
);
657 address_space_write(&address_space_memory
, rba
,
658 MEMTXATTRS_UNSPECIFIED
, buf
, bufcount
);
664 /* Write as much of the CRC as fits */
666 address_space_write(&address_space_memory
, rba
,
667 MEMTXATTRS_UNSPECIFIED
, crc_ptr
, crccount
);
673 num
|= 0x4000; /* set F BIT */
675 num
|= I596_EOF
; /* set EOF BIT */
677 set_uint16(rbd
+ 0, num
); /* write actual count with flags */
680 rbd
= get_uint32(rbd
+ 4);
681 /* printf("Next Receive: rbd is %08x\n", rbd); */
683 if (buffer_size
& I596_EOF
) /* last entry */
687 /* Housekeeping, see pg. 18 */
688 next_rfd
= get_uint32(rfd_p
+ 4);
689 set_uint32(next_rfd
+ 8, rbd
);
691 status
= STAT_C
| STAT_OK
| is_broadcast
;
692 set_uint16(rfd_p
, status
);
694 if (command
& CMD_SUSP
) { /* suspend after command? */
695 s
->rx_status
= RX_SUSPENDED
;
696 s
->scb_status
|= SCB_STATUS_RNR
; /* RU left active state */
699 if (command
& CMD_EOL
) /* was it last Frame Descriptor? */
707 s
->scb_status
|= SCB_STATUS_FR
; /* set "RU finished receiving frame" bit. */
708 update_scb_status(s
);
710 /* send IRQ that we received data */
711 qemu_set_irq(s
->irq
, 1);
712 /* s->send_irq = 1; */
715 DBG(printf("Checking:\n"));
716 rfd_p
= get_uint32(s
->scb
+ 8); /* get Receive Frame Descriptor */
717 DBG(printf("Next Receive: rfd is %08x\n", rfd_p
));
718 rfd_p
= get_uint32(rfd_p
+ 4); /* get Next Receive Frame Descriptor */
719 DBG(printf("Next Receive: rfd is %08x\n", rfd_p
));
720 /* get first Receive Buffer Descriptor Address */
721 rbd
= get_uint32(rfd_p
+ 8);
722 DBG(printf("Next Receive: rbd is %08x\n", rbd
));
729 const VMStateDescription vmstate_i82596
= {
732 .minimum_version_id
= 1,
733 .fields
= (VMStateField
[]) {
734 VMSTATE_UINT16(lnkst
, I82596State
),
735 VMSTATE_TIMER_PTR(flush_queue_timer
, I82596State
),
736 VMSTATE_END_OF_LIST()
740 void i82596_common_init(DeviceState
*dev
, I82596State
*s
, NetClientInfo
*info
)
742 if (s
->conf
.macaddr
.a
[0] == 0) {
743 qemu_macaddr_default_if_unset(&s
->conf
.macaddr
);
745 s
->nic
= qemu_new_nic(info
, &s
->conf
, object_get_typename(OBJECT(dev
)),
747 qemu_format_nic_info_str(qemu_get_queue(s
->nic
), s
->conf
.macaddr
.a
);
750 s
->flush_queue_timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
,
751 i82596_flush_queue_timer
, s
);
753 s
->lnkst
= 0x8000; /* initial link state: up */