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"
15 #include "sysemu/sysemu.h"
17 #include "hw/qdev-properties.h"
18 #include "migration/vmstate.h"
19 #include "qemu/module.h"
22 #include <zlib.h> /* For crc32 */
24 #if defined(ENABLE_DEBUG)
27 #define DBG(x) do { } while (0)
32 #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
34 #define PKT_BUF_SZ 1536
37 #define ISCP_BUSY 0x0001
39 #define I596_NULL ((uint32_t)0xffffffff)
41 #define SCB_STATUS_CX 0x8000 /* CU finished command with I bit */
42 #define SCB_STATUS_FR 0x4000 /* RU finished receiving a frame */
43 #define SCB_STATUS_CNA 0x2000 /* CU left active state */
44 #define SCB_STATUS_RNR 0x1000 /* RU left active state */
46 #define SCB_COMMAND_ACK_MASK \
47 (SCB_STATUS_CX | SCB_STATUS_FR | SCB_STATUS_CNA | SCB_STATUS_RNR)
50 #define CU_SUSPENDED 1
54 #define RX_SUSPENDED 1
57 #define CMD_EOL 0x8000 /* The last command of the list, stop. */
58 #define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
59 #define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
61 #define CMD_FLEX 0x0008 /* Enable flexible memory model */
64 CmdNOp
= 0, CmdSASetup
= 1, CmdConfigure
= 2, CmdMulticastList
= 3,
65 CmdTx
= 4, CmdTDR
= 5, CmdDump
= 6, CmdDiagnose
= 7
68 #define STAT_C 0x8000 /* Set to 0 after execution */
69 #define STAT_B 0x4000 /* Command being executed */
70 #define STAT_OK 0x2000 /* Command executed ok */
71 #define STAT_A 0x1000 /* Command aborted */
73 #define I596_EOF 0x8000
74 #define SIZE_MASK 0x3fff
76 #define ETHER_TYPE_LEN 2
77 #define VLAN_TCI_LEN 2
78 #define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN)
80 /* various flags in the chip config registers */
81 #define I596_PREFETCH (s->config[0] & 0x80)
82 #define I596_PROMISC (s->config[8] & 0x01)
83 #define I596_BC_DISABLE (s->config[8] & 0x02) /* broadcast disable */
84 #define I596_NOCRC_INS (s->config[8] & 0x08)
85 #define I596_CRCINM (s->config[11] & 0x04) /* CRC appended */
86 #define I596_MC_ALL (s->config[11] & 0x20)
87 #define I596_MULTIIA (s->config[13] & 0x40)
90 static uint8_t get_byte(uint32_t addr
)
92 return ldub_phys(&address_space_memory
, addr
);
95 static void set_byte(uint32_t addr
, uint8_t c
)
97 return stb_phys(&address_space_memory
, addr
, c
);
100 static uint16_t get_uint16(uint32_t addr
)
102 return lduw_be_phys(&address_space_memory
, addr
);
105 static void set_uint16(uint32_t addr
, uint16_t w
)
107 return stw_be_phys(&address_space_memory
, addr
, w
);
110 static uint32_t get_uint32(uint32_t addr
)
112 uint32_t lo
= lduw_be_phys(&address_space_memory
, addr
);
113 uint32_t hi
= lduw_be_phys(&address_space_memory
, addr
+ 2);
114 return (hi
<< 16) | lo
;
117 static void set_uint32(uint32_t addr
, uint32_t val
)
119 set_uint16(addr
, (uint16_t) val
);
120 set_uint16(addr
+ 2, val
>> 16);
124 struct qemu_ether_header
{
125 uint8_t ether_dhost
[6];
126 uint8_t ether_shost
[6];
130 #define PRINT_PKTHDR(txt, BUF) do { \
131 struct qemu_ether_header *hdr = (void *)(BUF); \
132 printf(txt ": packet dhost=" MAC_FMT ", shost=" MAC_FMT ", type=0x%04x\n",\
133 MAC_ARG(hdr->ether_dhost), MAC_ARG(hdr->ether_shost), \
134 be16_to_cpu(hdr->ether_type)); \
137 static void i82596_transmit(I82596State
*s
, uint32_t addr
)
139 uint32_t tdb_p
; /* Transmit Buffer Descriptor */
141 /* TODO: Check flexible mode */
142 tdb_p
= get_uint32(addr
+ 8);
143 while (tdb_p
!= I596_NULL
) {
147 size
= get_uint16(tdb_p
);
148 len
= size
& SIZE_MASK
;
149 tba
= get_uint32(tdb_p
+ 8);
150 trace_i82596_transmit(len
, tba
);
153 assert(len
<= sizeof(s
->tx_buffer
));
154 address_space_read(&address_space_memory
, tba
,
155 MEMTXATTRS_UNSPECIFIED
, s
->tx_buffer
, len
);
156 DBG(PRINT_PKTHDR("Send", &s
->tx_buffer
));
157 DBG(printf("Sending %d bytes\n", len
));
158 qemu_send_packet(qemu_get_queue(s
->nic
), s
->tx_buffer
, len
);
161 /* was this the last package? */
162 if (size
& I596_EOF
) {
166 /* get next buffer pointer */
167 tdb_p
= get_uint32(tdb_p
+ 4);
171 static void set_individual_address(I82596State
*s
, uint32_t addr
)
176 nc
= qemu_get_queue(s
->nic
);
177 m
= s
->conf
.macaddr
.a
;
178 address_space_read(&address_space_memory
, addr
+ 8,
179 MEMTXATTRS_UNSPECIFIED
, m
, ETH_ALEN
);
180 qemu_format_nic_info_str(nc
, m
);
181 trace_i82596_new_mac(nc
->info_str
);
184 static void set_multicast_list(I82596State
*s
, uint32_t addr
)
186 uint16_t mc_count
, i
;
188 memset(&s
->mult
[0], 0, sizeof(s
->mult
));
189 mc_count
= get_uint16(addr
+ 8) / ETH_ALEN
;
191 if (mc_count
> MAX_MC_CNT
) {
192 mc_count
= MAX_MC_CNT
;
194 for (i
= 0; i
< mc_count
; i
++) {
195 uint8_t multicast_addr
[ETH_ALEN
];
196 address_space_read(&address_space_memory
, addr
+ i
* ETH_ALEN
,
197 MEMTXATTRS_UNSPECIFIED
, multicast_addr
, ETH_ALEN
);
198 DBG(printf("Add multicast entry " MAC_FMT
"\n",
199 MAC_ARG(multicast_addr
)));
200 unsigned mcast_idx
= (net_crc32(multicast_addr
, ETH_ALEN
) &
202 assert(mcast_idx
< 8 * sizeof(s
->mult
));
203 s
->mult
[mcast_idx
>> 3] |= (1 << (mcast_idx
& 7));
205 trace_i82596_set_multicast(mc_count
);
208 void i82596_set_link_status(NetClientState
*nc
)
210 I82596State
*d
= qemu_get_nic_opaque(nc
);
212 d
->lnkst
= nc
->link_down
? 0 : 0x8000;
215 static void update_scb_status(I82596State
*s
)
217 s
->scb_status
= (s
->scb_status
& 0xf000)
218 | (s
->cu_status
<< 8) | (s
->rx_status
<< 4);
219 set_uint16(s
->scb
, s
->scb_status
);
223 static void i82596_s_reset(I82596State
*s
)
225 trace_i82596_s_reset(s
);
228 s
->cu_status
= CU_IDLE
;
229 s
->rx_status
= RX_SUSPENDED
;
230 s
->cmd_p
= I596_NULL
;
231 s
->lnkst
= 0x8000; /* initial link state: up */
232 s
->ca
= s
->ca_active
= 0;
237 static void command_loop(I82596State
*s
)
243 DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s
->cmd_p
));
245 while (s
->cmd_p
!= I596_NULL
) {
248 set_uint16(s
->cmd_p
, status
);
249 status
= STAT_C
| STAT_OK
; /* update, but write later */
251 cmd
= get_uint16(s
->cmd_p
+ 2);
252 DBG(printf("Running command %04x at %08x\n", cmd
, s
->cmd_p
));
254 switch (cmd
& 0x07) {
258 set_individual_address(s
, s
->cmd_p
);
261 byte_cnt
= get_byte(s
->cmd_p
+ 8) & 0x0f;
262 byte_cnt
= MAX(byte_cnt
, 4);
263 byte_cnt
= MIN(byte_cnt
, sizeof(s
->config
));
264 /* copy byte_cnt max. */
265 address_space_read(&address_space_memory
, s
->cmd_p
+ 8,
266 MEMTXATTRS_UNSPECIFIED
, s
->config
, byte_cnt
);
267 /* config byte according to page 35ff */
268 s
->config
[2] &= 0x82; /* mask valid bits */
269 s
->config
[2] |= 0x40;
270 s
->config
[7] &= 0xf7; /* clear zero bit */
271 assert(I596_NOCRC_INS
== 0); /* do CRC insertion */
272 s
->config
[10] = MAX(s
->config
[10], 5); /* min frame length */
273 s
->config
[12] &= 0x40; /* only full duplex field valid */
274 s
->config
[13] |= 0x3f; /* set ones in byte 13 */
277 /* get signal LINK */
278 set_uint32(s
->cmd_p
+ 8, s
->lnkst
);
281 i82596_transmit(s
, s
->cmd_p
);
283 case CmdMulticastList
:
284 set_multicast_list(s
, s
->cmd_p
);
288 printf("FIXME Command %d !!\n", cmd
& 7);
293 set_uint16(s
->cmd_p
, status
);
295 s
->cmd_p
= get_uint32(s
->cmd_p
+ 4); /* get link address */
296 DBG(printf("NEXT addr would be %08x\n", s
->cmd_p
));
298 s
->cmd_p
= I596_NULL
;
301 /* Stop when last command of the list. */
303 s
->cmd_p
= I596_NULL
;
305 /* Suspend after doing cmd? */
306 if (cmd
& CMD_SUSP
) {
307 s
->cu_status
= CU_SUSPENDED
;
308 printf("FIXME SUSPEND !!\n");
310 /* Interrupt after doing cmd? */
311 if (cmd
& CMD_INTR
) {
312 s
->scb_status
|= SCB_STATUS_CX
;
314 s
->scb_status
&= ~SCB_STATUS_CX
;
316 update_scb_status(s
);
318 /* Interrupt after doing cmd? */
319 if (cmd
& CMD_INTR
) {
323 if (s
->cu_status
!= CU_ACTIVE
) {
327 DBG(printf("FINISHED COMMAND LOOP\n"));
328 qemu_flush_queued_packets(qemu_get_queue(s
->nic
));
331 static void i82596_flush_queue_timer(void *opaque
)
333 I82596State
*s
= opaque
;
335 timer_del(s
->flush_queue_timer
);
336 qemu_flush_queued_packets(qemu_get_queue(s
->nic
));
337 timer_mod(s
->flush_queue_timer
,
338 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) + 1000);
342 static void examine_scb(I82596State
*s
)
344 uint16_t command
, cuc
, ruc
;
346 /* get the scb command word */
347 command
= get_uint16(s
->scb
+ 2);
348 cuc
= (command
>> 8) & 0x7;
349 ruc
= (command
>> 4) & 0x7;
350 DBG(printf("MAIN COMMAND %04x cuc %02x ruc %02x\n", command
, cuc
, ruc
));
351 /* and clear the scb command word */
352 set_uint16(s
->scb
+ 2, 0);
354 s
->scb_status
&= ~(command
& SCB_COMMAND_ACK_MASK
);
357 case 0: /* no change */
359 case 1: /* CUC_START */
360 s
->cu_status
= CU_ACTIVE
;
362 case 4: /* CUC_ABORT */
363 s
->cu_status
= CU_SUSPENDED
;
364 s
->scb_status
|= SCB_STATUS_CNA
; /* CU left active state */
367 printf("WARNING: Unknown CUC %d!\n", cuc
);
371 case 0: /* no change */
373 case 1: /* RX_START */
374 case 2: /* RX_RESUME */
375 s
->rx_status
= RX_IDLE
;
377 timer_mod(s
->flush_queue_timer
, qemu_clock_get_ms(
378 QEMU_CLOCK_VIRTUAL
) + 1000);
381 case 3: /* RX_SUSPEND */
382 case 4: /* RX_ABORT */
383 s
->rx_status
= RX_SUSPENDED
;
384 s
->scb_status
|= SCB_STATUS_RNR
; /* RU left active state */
387 printf("WARNING: Unknown RUC %d!\n", ruc
);
390 if (command
& 0x80) { /* reset bit set? */
394 /* execute commands from SCBL */
395 if (s
->cu_status
!= CU_SUSPENDED
) {
396 if (s
->cmd_p
== I596_NULL
) {
397 s
->cmd_p
= get_uint32(s
->scb
+ 4);
401 /* update scb status */
402 update_scb_status(s
);
407 static void signal_ca(I82596State
*s
)
411 /* trace_i82596_channel_attention(s); */
413 /* CA after reset -> do init with new scp. */
414 s
->sysbus
= get_byte(s
->scp
+ 3); /* big endian */
415 DBG(printf("SYSBUS = %08x\n", s
->sysbus
));
416 if (((s
->sysbus
>> 1) & 0x03) != 2) {
417 printf("WARNING: NO LINEAR MODE !!\n");
419 if ((s
->sysbus
>> 7)) {
420 printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\n");
422 iscp
= get_uint32(s
->scp
+ 8);
423 s
->scb
= get_uint32(iscp
+ 4);
424 set_byte(iscp
+ 1, 0); /* clear BUSY flag in iscp */
428 s
->ca
++; /* count ca() */
440 qemu_set_irq(s
->irq
, 1);
444 void i82596_ioport_writew(void *opaque
, uint32_t addr
, uint32_t val
)
446 I82596State
*s
= opaque
;
447 /* printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); */
449 case PORT_RESET
: /* Reset */
461 uint32_t i82596_ioport_readw(void *opaque
, uint32_t addr
)
466 void i82596_h_reset(void *opaque
)
468 I82596State
*s
= opaque
;
473 bool i82596_can_receive(NetClientState
*nc
)
475 I82596State
*s
= qemu_get_nic_opaque(nc
);
477 if (s
->rx_status
== RX_SUSPENDED
) {
485 if (USE_TIMER
&& !timer_pending(s
->flush_queue_timer
)) {
492 #define MIN_BUF_SIZE 60
494 ssize_t
i82596_receive(NetClientState
*nc
, const uint8_t *buf
, size_t sz
)
496 I82596State
*s
= qemu_get_nic_opaque(nc
);
499 uint16_t is_broadcast
= 0;
500 size_t len
= sz
; /* length of data for guest (including CRC) */
501 size_t bufsz
= sz
; /* length of data in buf */
504 uint8_t buf1
[MIN_BUF_SIZE
+ VLAN_HLEN
];
505 static const uint8_t broadcast_macaddr
[6] = {
506 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
508 DBG(printf("i82596_receive() start\n"));
510 if (USE_TIMER
&& timer_pending(s
->flush_queue_timer
)) {
514 /* first check if receiver is enabled */
515 if (s
->rx_status
== RX_SUSPENDED
) {
516 trace_i82596_receive_analysis(">>> Receiving suspended");
521 trace_i82596_receive_analysis(">>> Link down");
525 /* Received frame smaller than configured "min frame len"? */
526 if (sz
< s
->config
[10]) {
527 printf("Received frame too small, %zu vs. %u bytes\n",
532 DBG(printf("Received %lu bytes\n", sz
));
536 /* promiscuous: receive all */
537 trace_i82596_receive_analysis(
538 ">>> packet received in promiscuous mode");
542 if (!memcmp(buf
, broadcast_macaddr
, 6)) {
543 /* broadcast address */
544 if (I596_BC_DISABLE
) {
545 trace_i82596_receive_analysis(">>> broadcast packet rejected");
550 trace_i82596_receive_analysis(">>> broadcast packet received");
553 } else if (buf
[0] & 0x01) {
556 trace_i82596_receive_analysis(">>> multicast packet rejected");
561 int mcast_idx
= (net_crc32(buf
, ETH_ALEN
) & BITS(7, 2)) >> 2;
562 assert(mcast_idx
< 8 * sizeof(s
->mult
));
564 if (!(s
->mult
[mcast_idx
>> 3] & (1 << (mcast_idx
& 7)))) {
565 trace_i82596_receive_analysis(">>> multicast address mismatch");
570 trace_i82596_receive_analysis(">>> multicast packet received");
573 } else if (!memcmp(s
->conf
.macaddr
.a
, buf
, 6)) {
576 trace_i82596_receive_analysis(
577 ">>> physical address matching packet received");
581 trace_i82596_receive_analysis(">>> unknown packet");
587 /* if too small buffer, then expand it */
588 if (len
< MIN_BUF_SIZE
+ VLAN_HLEN
) {
589 memcpy(buf1
, buf
, len
);
590 memset(buf1
+ len
, 0, MIN_BUF_SIZE
+ VLAN_HLEN
- len
);
592 if (len
< MIN_BUF_SIZE
) {
598 /* Calculate the ethernet checksum (4 bytes) */
600 crc
= cpu_to_be32(crc32(~0, buf
, sz
));
601 crc_ptr
= (uint8_t *) &crc
;
603 rfd_p
= get_uint32(s
->scb
+ 8); /* get Receive Frame Descriptor */
604 assert(rfd_p
&& rfd_p
!= I596_NULL
);
606 /* get first Receive Buffer Descriptor Address */
607 rbd
= get_uint32(rfd_p
+ 8);
608 assert(rbd
&& rbd
!= I596_NULL
);
610 trace_i82596_receive_packet(len
);
611 /* PRINT_PKTHDR("Receive", buf); */
614 uint16_t command
, status
;
617 command
= get_uint16(rfd_p
+ 2);
618 assert(command
& CMD_FLEX
); /* assert Flex Mode */
619 /* get first Receive Buffer Descriptor Address */
620 rbd
= get_uint32(rfd_p
+ 8);
621 assert(get_uint16(rfd_p
+ 14) == 0);
623 /* printf("Receive: rfd is %08x\n", rfd_p); */
626 uint16_t buffer_size
, num
;
628 size_t bufcount
, crccount
;
630 /* printf("Receive: rbd is %08x\n", rbd); */
631 buffer_size
= get_uint16(rbd
+ 12);
632 /* printf("buffer_size is 0x%x\n", buffer_size); */
633 assert(buffer_size
!= 0);
635 num
= buffer_size
& SIZE_MASK
;
639 rba
= get_uint32(rbd
+ 8);
640 /* printf("rba is 0x%x\n", rba); */
642 * Calculate how many bytes we want from buf[] and how many
645 if ((len
- num
) >= 4) {
646 /* The whole guest buffer, we haven't hit the CRC yet */
649 /* All that's left of buf[] */
652 crccount
= num
- bufcount
;
655 /* Still some of the actual data buffer to transfer */
656 assert(bufsz
>= bufcount
);
658 address_space_write(&address_space_memory
, rba
,
659 MEMTXATTRS_UNSPECIFIED
, buf
, bufcount
);
665 /* Write as much of the CRC as fits */
667 address_space_write(&address_space_memory
, rba
,
668 MEMTXATTRS_UNSPECIFIED
, crc_ptr
, crccount
);
674 num
|= 0x4000; /* set F BIT */
676 num
|= I596_EOF
; /* set EOF BIT */
678 set_uint16(rbd
+ 0, num
); /* write actual count with flags */
681 rbd
= get_uint32(rbd
+ 4);
682 /* printf("Next Receive: rbd is %08x\n", rbd); */
684 if (buffer_size
& I596_EOF
) /* last entry */
688 /* Housekeeping, see pg. 18 */
689 next_rfd
= get_uint32(rfd_p
+ 4);
690 set_uint32(next_rfd
+ 8, rbd
);
692 status
= STAT_C
| STAT_OK
| is_broadcast
;
693 set_uint16(rfd_p
, status
);
695 if (command
& CMD_SUSP
) { /* suspend after command? */
696 s
->rx_status
= RX_SUSPENDED
;
697 s
->scb_status
|= SCB_STATUS_RNR
; /* RU left active state */
700 if (command
& CMD_EOL
) /* was it last Frame Descriptor? */
708 s
->scb_status
|= SCB_STATUS_FR
; /* set "RU finished receiving frame" bit. */
709 update_scb_status(s
);
711 /* send IRQ that we received data */
712 qemu_set_irq(s
->irq
, 1);
713 /* s->send_irq = 1; */
716 DBG(printf("Checking:\n"));
717 rfd_p
= get_uint32(s
->scb
+ 8); /* get Receive Frame Descriptor */
718 DBG(printf("Next Receive: rfd is %08x\n", rfd_p
));
719 rfd_p
= get_uint32(rfd_p
+ 4); /* get Next Receive Frame Descriptor */
720 DBG(printf("Next Receive: rfd is %08x\n", rfd_p
));
721 /* get first Receive Buffer Descriptor Address */
722 rbd
= get_uint32(rfd_p
+ 8);
723 DBG(printf("Next Receive: rbd is %08x\n", rbd
));
730 const VMStateDescription vmstate_i82596
= {
733 .minimum_version_id
= 1,
734 .fields
= (VMStateField
[]) {
735 VMSTATE_UINT16(lnkst
, I82596State
),
736 VMSTATE_TIMER_PTR(flush_queue_timer
, I82596State
),
737 VMSTATE_END_OF_LIST()
741 void i82596_common_init(DeviceState
*dev
, I82596State
*s
, NetClientInfo
*info
)
743 if (s
->conf
.macaddr
.a
[0] == 0) {
744 qemu_macaddr_default_if_unset(&s
->conf
.macaddr
);
746 s
->nic
= qemu_new_nic(info
, &s
->conf
, object_get_typename(OBJECT(dev
)),
748 qemu_format_nic_info_str(qemu_get_queue(s
->nic
), s
->conf
.macaddr
.a
);
751 s
->flush_queue_timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
,
752 i82596_flush_queue_timer
, s
);
754 s
->lnkst
= 0x8000; /* initial link state: up */