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 */
47 #define CU_SUSPENDED 1
51 #define RX_SUSPENDED 1
54 #define CMD_EOL 0x8000 /* The last command of the list, stop. */
55 #define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
56 #define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
58 #define CMD_FLEX 0x0008 /* Enable flexible memory model */
61 CmdNOp
= 0, CmdSASetup
= 1, CmdConfigure
= 2, CmdMulticastList
= 3,
62 CmdTx
= 4, CmdTDR
= 5, CmdDump
= 6, CmdDiagnose
= 7
65 #define STAT_C 0x8000 /* Set to 0 after execution */
66 #define STAT_B 0x4000 /* Command being executed */
67 #define STAT_OK 0x2000 /* Command executed ok */
68 #define STAT_A 0x1000 /* Command aborted */
70 #define I596_EOF 0x8000
71 #define SIZE_MASK 0x3fff
73 #define ETHER_TYPE_LEN 2
74 #define VLAN_TCI_LEN 2
75 #define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN)
77 /* various flags in the chip config registers */
78 #define I596_PREFETCH (s->config[0] & 0x80)
79 #define I596_PROMISC (s->config[8] & 0x01)
80 #define I596_BC_DISABLE (s->config[8] & 0x02) /* broadcast disable */
81 #define I596_NOCRC_INS (s->config[8] & 0x08)
82 #define I596_CRCINM (s->config[11] & 0x04) /* CRC appended */
83 #define I596_MC_ALL (s->config[11] & 0x20)
84 #define I596_MULTIIA (s->config[13] & 0x40)
87 static uint8_t get_byte(uint32_t addr
)
89 return ldub_phys(&address_space_memory
, addr
);
92 static void set_byte(uint32_t addr
, uint8_t c
)
94 return stb_phys(&address_space_memory
, addr
, c
);
97 static uint16_t get_uint16(uint32_t addr
)
99 return lduw_be_phys(&address_space_memory
, addr
);
102 static void set_uint16(uint32_t addr
, uint16_t w
)
104 return stw_be_phys(&address_space_memory
, addr
, w
);
107 static uint32_t get_uint32(uint32_t addr
)
109 uint32_t lo
= lduw_be_phys(&address_space_memory
, addr
);
110 uint32_t hi
= lduw_be_phys(&address_space_memory
, addr
+ 2);
111 return (hi
<< 16) | lo
;
114 static void set_uint32(uint32_t addr
, uint32_t val
)
116 set_uint16(addr
, (uint16_t) val
);
117 set_uint16(addr
+ 2, val
>> 16);
121 struct qemu_ether_header
{
122 uint8_t ether_dhost
[6];
123 uint8_t ether_shost
[6];
127 #define PRINT_PKTHDR(txt, BUF) do { \
128 struct qemu_ether_header *hdr = (void *)(BUF); \
129 printf(txt ": packet dhost=" MAC_FMT ", shost=" MAC_FMT ", type=0x%04x\n",\
130 MAC_ARG(hdr->ether_dhost), MAC_ARG(hdr->ether_shost), \
131 be16_to_cpu(hdr->ether_type)); \
134 static void i82596_transmit(I82596State
*s
, uint32_t addr
)
136 uint32_t tdb_p
; /* Transmit Buffer Descriptor */
138 /* TODO: Check flexible mode */
139 tdb_p
= get_uint32(addr
+ 8);
140 while (tdb_p
!= I596_NULL
) {
144 size
= get_uint16(tdb_p
);
145 len
= size
& SIZE_MASK
;
146 tba
= get_uint32(tdb_p
+ 8);
147 trace_i82596_transmit(len
, tba
);
150 assert(len
<= sizeof(s
->tx_buffer
));
151 address_space_read(&address_space_memory
, tba
,
152 MEMTXATTRS_UNSPECIFIED
, s
->tx_buffer
, len
);
153 DBG(PRINT_PKTHDR("Send", &s
->tx_buffer
));
154 DBG(printf("Sending %d bytes\n", len
));
155 qemu_send_packet(qemu_get_queue(s
->nic
), s
->tx_buffer
, len
);
158 /* was this the last package? */
159 if (size
& I596_EOF
) {
163 /* get next buffer pointer */
164 tdb_p
= get_uint32(tdb_p
+ 4);
168 static void set_individual_address(I82596State
*s
, uint32_t addr
)
173 nc
= qemu_get_queue(s
->nic
);
174 m
= s
->conf
.macaddr
.a
;
175 address_space_read(&address_space_memory
, addr
+ 8,
176 MEMTXATTRS_UNSPECIFIED
, m
, ETH_ALEN
);
177 qemu_format_nic_info_str(nc
, m
);
178 trace_i82596_new_mac(nc
->info_str
);
181 static void set_multicast_list(I82596State
*s
, uint32_t addr
)
183 uint16_t mc_count
, i
;
185 memset(&s
->mult
[0], 0, sizeof(s
->mult
));
186 mc_count
= get_uint16(addr
+ 8) / ETH_ALEN
;
188 if (mc_count
> MAX_MC_CNT
) {
189 mc_count
= MAX_MC_CNT
;
191 for (i
= 0; i
< mc_count
; i
++) {
192 uint8_t multicast_addr
[ETH_ALEN
];
193 address_space_read(&address_space_memory
, addr
+ i
* ETH_ALEN
,
194 MEMTXATTRS_UNSPECIFIED
, multicast_addr
, ETH_ALEN
);
195 DBG(printf("Add multicast entry " MAC_FMT
"\n",
196 MAC_ARG(multicast_addr
)));
197 unsigned mcast_idx
= (net_crc32(multicast_addr
, ETH_ALEN
) &
199 assert(mcast_idx
< 8 * sizeof(s
->mult
));
200 s
->mult
[mcast_idx
>> 3] |= (1 << (mcast_idx
& 7));
202 trace_i82596_set_multicast(mc_count
);
205 void i82596_set_link_status(NetClientState
*nc
)
207 I82596State
*d
= qemu_get_nic_opaque(nc
);
209 d
->lnkst
= nc
->link_down
? 0 : 0x8000;
212 static void update_scb_status(I82596State
*s
)
214 s
->scb_status
= (s
->scb_status
& 0xf000)
215 | (s
->cu_status
<< 8) | (s
->rx_status
<< 4);
216 set_uint16(s
->scb
, s
->scb_status
);
220 static void i82596_s_reset(I82596State
*s
)
222 trace_i82596_s_reset(s
);
225 s
->cu_status
= CU_IDLE
;
226 s
->rx_status
= RX_SUSPENDED
;
227 s
->cmd_p
= I596_NULL
;
228 s
->lnkst
= 0x8000; /* initial link state: up */
229 s
->ca
= s
->ca_active
= 0;
234 static void command_loop(I82596State
*s
)
240 DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s
->cmd_p
));
242 while (s
->cmd_p
!= I596_NULL
) {
245 set_uint16(s
->cmd_p
, status
);
246 status
= STAT_C
| STAT_OK
; /* update, but write later */
248 cmd
= get_uint16(s
->cmd_p
+ 2);
249 DBG(printf("Running command %04x at %08x\n", cmd
, s
->cmd_p
));
251 switch (cmd
& 0x07) {
255 set_individual_address(s
, s
->cmd_p
);
258 byte_cnt
= get_byte(s
->cmd_p
+ 8) & 0x0f;
259 byte_cnt
= MAX(byte_cnt
, 4);
260 byte_cnt
= MIN(byte_cnt
, sizeof(s
->config
));
261 /* copy byte_cnt max. */
262 address_space_read(&address_space_memory
, s
->cmd_p
+ 8,
263 MEMTXATTRS_UNSPECIFIED
, s
->config
, byte_cnt
);
264 /* config byte according to page 35ff */
265 s
->config
[2] &= 0x82; /* mask valid bits */
266 s
->config
[2] |= 0x40;
267 s
->config
[7] &= 0xf7; /* clear zero bit */
268 assert(I596_NOCRC_INS
== 0); /* do CRC insertion */
269 s
->config
[10] = MAX(s
->config
[10], 5); /* min frame length */
270 s
->config
[12] &= 0x40; /* only full duplex field valid */
271 s
->config
[13] |= 0x3f; /* set ones in byte 13 */
274 /* get signal LINK */
275 set_uint32(s
->cmd_p
+ 8, s
->lnkst
);
278 i82596_transmit(s
, s
->cmd_p
);
280 case CmdMulticastList
:
281 set_multicast_list(s
, s
->cmd_p
);
285 printf("FIXME Command %d !!\n", cmd
& 7);
290 set_uint16(s
->cmd_p
, status
);
292 s
->cmd_p
= get_uint32(s
->cmd_p
+ 4); /* get link address */
293 DBG(printf("NEXT addr would be %08x\n", s
->cmd_p
));
295 s
->cmd_p
= I596_NULL
;
298 /* Stop when last command of the list. */
300 s
->cmd_p
= I596_NULL
;
302 /* Suspend after doing cmd? */
303 if (cmd
& CMD_SUSP
) {
304 s
->cu_status
= CU_SUSPENDED
;
305 printf("FIXME SUSPEND !!\n");
307 /* Interrupt after doing cmd? */
308 if (cmd
& CMD_INTR
) {
309 s
->scb_status
|= SCB_STATUS_CX
;
311 s
->scb_status
&= ~SCB_STATUS_CX
;
313 update_scb_status(s
);
315 /* Interrupt after doing cmd? */
316 if (cmd
& CMD_INTR
) {
320 if (s
->cu_status
!= CU_ACTIVE
) {
324 DBG(printf("FINISHED COMMAND LOOP\n"));
325 qemu_flush_queued_packets(qemu_get_queue(s
->nic
));
328 static void i82596_flush_queue_timer(void *opaque
)
330 I82596State
*s
= opaque
;
332 timer_del(s
->flush_queue_timer
);
333 qemu_flush_queued_packets(qemu_get_queue(s
->nic
));
334 timer_mod(s
->flush_queue_timer
,
335 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) + 1000);
339 static void examine_scb(I82596State
*s
)
341 uint16_t command
, cuc
, ruc
;
343 /* get the scb command word */
344 command
= get_uint16(s
->scb
+ 2);
345 cuc
= (command
>> 8) & 0x7;
346 ruc
= (command
>> 4) & 0x7;
347 DBG(printf("MAIN COMMAND %04x cuc %02x ruc %02x\n", command
, cuc
, ruc
));
348 /* and clear the scb command word */
349 set_uint16(s
->scb
+ 2, 0);
351 if (command
& BIT(31)) /* ACK-CX */
352 s
->scb_status
&= ~SCB_STATUS_CX
;
353 if (command
& BIT(30)) /*ACK-FR */
354 s
->scb_status
&= ~SCB_STATUS_FR
;
355 if (command
& BIT(29)) /*ACK-CNA */
356 s
->scb_status
&= ~SCB_STATUS_CNA
;
357 if (command
& BIT(28)) /*ACK-RNR */
358 s
->scb_status
&= ~SCB_STATUS_RNR
;
361 case 0: /* no change */
363 case 1: /* CUC_START */
364 s
->cu_status
= CU_ACTIVE
;
366 case 4: /* CUC_ABORT */
367 s
->cu_status
= CU_SUSPENDED
;
368 s
->scb_status
|= SCB_STATUS_CNA
; /* CU left active state */
371 printf("WARNING: Unknown CUC %d!\n", cuc
);
375 case 0: /* no change */
377 case 1: /* RX_START */
378 case 2: /* RX_RESUME */
379 s
->rx_status
= RX_IDLE
;
381 timer_mod(s
->flush_queue_timer
, qemu_clock_get_ms(
382 QEMU_CLOCK_VIRTUAL
) + 1000);
385 case 3: /* RX_SUSPEND */
386 case 4: /* RX_ABORT */
387 s
->rx_status
= RX_SUSPENDED
;
388 s
->scb_status
|= SCB_STATUS_RNR
; /* RU left active state */
391 printf("WARNING: Unknown RUC %d!\n", ruc
);
394 if (command
& 0x80) { /* reset bit set? */
398 /* execute commands from SCBL */
399 if (s
->cu_status
!= CU_SUSPENDED
) {
400 if (s
->cmd_p
== I596_NULL
) {
401 s
->cmd_p
= get_uint32(s
->scb
+ 4);
405 /* update scb status */
406 update_scb_status(s
);
411 static void signal_ca(I82596State
*s
)
415 /* trace_i82596_channel_attention(s); */
417 /* CA after reset -> do init with new scp. */
418 s
->sysbus
= get_byte(s
->scp
+ 3); /* big endian */
419 DBG(printf("SYSBUS = %08x\n", s
->sysbus
));
420 if (((s
->sysbus
>> 1) & 0x03) != 2) {
421 printf("WARNING: NO LINEAR MODE !!\n");
423 if ((s
->sysbus
>> 7)) {
424 printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\n");
426 iscp
= get_uint32(s
->scp
+ 8);
427 s
->scb
= get_uint32(iscp
+ 4);
428 set_byte(iscp
+ 1, 0); /* clear BUSY flag in iscp */
432 s
->ca
++; /* count ca() */
444 qemu_set_irq(s
->irq
, 1);
448 void i82596_ioport_writew(void *opaque
, uint32_t addr
, uint32_t val
)
450 I82596State
*s
= opaque
;
451 /* printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); */
453 case PORT_RESET
: /* Reset */
465 uint32_t i82596_ioport_readw(void *opaque
, uint32_t addr
)
470 void i82596_h_reset(void *opaque
)
472 I82596State
*s
= opaque
;
477 int i82596_can_receive(NetClientState
*nc
)
479 I82596State
*s
= qemu_get_nic_opaque(nc
);
481 if (s
->rx_status
== RX_SUSPENDED
) {
489 if (USE_TIMER
&& !timer_pending(s
->flush_queue_timer
)) {
496 #define MIN_BUF_SIZE 60
498 ssize_t
i82596_receive(NetClientState
*nc
, const uint8_t *buf
, size_t sz
)
500 I82596State
*s
= qemu_get_nic_opaque(nc
);
503 uint16_t is_broadcast
= 0;
507 uint8_t buf1
[MIN_BUF_SIZE
+ VLAN_HLEN
];
508 static const uint8_t broadcast_macaddr
[6] = {
509 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
511 DBG(printf("i82596_receive() start\n"));
513 if (USE_TIMER
&& timer_pending(s
->flush_queue_timer
)) {
517 /* first check if receiver is enabled */
518 if (s
->rx_status
== RX_SUSPENDED
) {
519 trace_i82596_receive_analysis(">>> Receiving suspended");
524 trace_i82596_receive_analysis(">>> Link down");
528 /* Received frame smaller than configured "min frame len"? */
529 if (sz
< s
->config
[10]) {
530 printf("Received frame too small, %zu vs. %u bytes\n",
535 DBG(printf("Received %lu bytes\n", sz
));
539 /* promiscuous: receive all */
540 trace_i82596_receive_analysis(
541 ">>> packet received in promiscuous mode");
545 if (!memcmp(buf
, broadcast_macaddr
, 6)) {
546 /* broadcast address */
547 if (I596_BC_DISABLE
) {
548 trace_i82596_receive_analysis(">>> broadcast packet rejected");
553 trace_i82596_receive_analysis(">>> broadcast packet received");
556 } else if (buf
[0] & 0x01) {
559 trace_i82596_receive_analysis(">>> multicast packet rejected");
564 int mcast_idx
= (net_crc32(buf
, ETH_ALEN
) & BITS(7, 2)) >> 2;
565 assert(mcast_idx
< 8 * sizeof(s
->mult
));
567 if (!(s
->mult
[mcast_idx
>> 3] & (1 << (mcast_idx
& 7)))) {
568 trace_i82596_receive_analysis(">>> multicast address mismatch");
573 trace_i82596_receive_analysis(">>> multicast packet received");
576 } else if (!memcmp(s
->conf
.macaddr
.a
, buf
, 6)) {
579 trace_i82596_receive_analysis(
580 ">>> physical address matching packet received");
584 trace_i82596_receive_analysis(">>> unknown packet");
590 /* if too small buffer, then expand it */
591 if (len
< MIN_BUF_SIZE
+ VLAN_HLEN
) {
592 memcpy(buf1
, buf
, len
);
593 memset(buf1
+ len
, 0, MIN_BUF_SIZE
+ VLAN_HLEN
- len
);
595 if (len
< MIN_BUF_SIZE
) {
600 /* Calculate the ethernet checksum (4 bytes) */
602 crc
= cpu_to_be32(crc32(~0, buf
, sz
));
603 crc_ptr
= (uint8_t *) &crc
;
605 rfd_p
= get_uint32(s
->scb
+ 8); /* get Receive Frame Descriptor */
606 assert(rfd_p
&& rfd_p
!= I596_NULL
);
608 /* get first Receive Buffer Descriptor Address */
609 rbd
= get_uint32(rfd_p
+ 8);
610 assert(rbd
&& rbd
!= I596_NULL
);
612 trace_i82596_receive_packet(len
);
613 /* PRINT_PKTHDR("Receive", buf); */
616 uint16_t command
, status
;
619 command
= get_uint16(rfd_p
+ 2);
620 assert(command
& CMD_FLEX
); /* assert Flex Mode */
621 /* get first Receive Buffer Descriptor Address */
622 rbd
= get_uint32(rfd_p
+ 8);
623 assert(get_uint16(rfd_p
+ 14) == 0);
625 /* printf("Receive: rfd is %08x\n", rfd_p); */
628 uint16_t buffer_size
, num
;
631 /* printf("Receive: rbd is %08x\n", rbd); */
632 buffer_size
= get_uint16(rbd
+ 12);
633 /* printf("buffer_size is 0x%x\n", buffer_size); */
634 assert(buffer_size
!= 0);
636 num
= buffer_size
& SIZE_MASK
;
640 rba
= get_uint32(rbd
+ 8);
641 /* printf("rba is 0x%x\n", rba); */
642 address_space_write(&address_space_memory
, rba
,
643 MEMTXATTRS_UNSPECIFIED
, buf
, num
);
647 if (len
== 0) { /* copy crc */
648 address_space_write(&address_space_memory
, rba
- 4,
649 MEMTXATTRS_UNSPECIFIED
, crc_ptr
, 4);
652 num
|= 0x4000; /* set F BIT */
654 num
|= I596_EOF
; /* set EOF BIT */
656 set_uint16(rbd
+ 0, num
); /* write actual count with flags */
659 rbd
= get_uint32(rbd
+ 4);
660 /* printf("Next Receive: rbd is %08x\n", rbd); */
662 if (buffer_size
& I596_EOF
) /* last entry */
666 /* Housekeeping, see pg. 18 */
667 next_rfd
= get_uint32(rfd_p
+ 4);
668 set_uint32(next_rfd
+ 8, rbd
);
670 status
= STAT_C
| STAT_OK
| is_broadcast
;
671 set_uint16(rfd_p
, status
);
673 if (command
& CMD_SUSP
) { /* suspend after command? */
674 s
->rx_status
= RX_SUSPENDED
;
675 s
->scb_status
|= SCB_STATUS_RNR
; /* RU left active state */
678 if (command
& CMD_EOL
) /* was it last Frame Descriptor? */
686 s
->scb_status
|= SCB_STATUS_FR
; /* set "RU finished receiving frame" bit. */
687 update_scb_status(s
);
689 /* send IRQ that we received data */
690 qemu_set_irq(s
->irq
, 1);
691 /* s->send_irq = 1; */
694 DBG(printf("Checking:\n"));
695 rfd_p
= get_uint32(s
->scb
+ 8); /* get Receive Frame Descriptor */
696 DBG(printf("Next Receive: rfd is %08x\n", rfd_p
));
697 rfd_p
= get_uint32(rfd_p
+ 4); /* get Next Receive Frame Descriptor */
698 DBG(printf("Next Receive: rfd is %08x\n", rfd_p
));
699 /* get first Receive Buffer Descriptor Address */
700 rbd
= get_uint32(rfd_p
+ 8);
701 DBG(printf("Next Receive: rbd is %08x\n", rbd
));
708 const VMStateDescription vmstate_i82596
= {
711 .minimum_version_id
= 1,
712 .fields
= (VMStateField
[]) {
713 VMSTATE_UINT16(lnkst
, I82596State
),
714 VMSTATE_TIMER_PTR(flush_queue_timer
, I82596State
),
715 VMSTATE_END_OF_LIST()
719 void i82596_common_init(DeviceState
*dev
, I82596State
*s
, NetClientInfo
*info
)
721 if (s
->conf
.macaddr
.a
[0] == 0) {
722 qemu_macaddr_default_if_unset(&s
->conf
.macaddr
);
724 s
->nic
= qemu_new_nic(info
, &s
->conf
, object_get_typename(OBJECT(dev
)),
726 qemu_format_nic_info_str(qemu_get_queue(s
->nic
), s
->conf
.macaddr
.a
);
729 s
->flush_queue_timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
,
730 i82596_flush_queue_timer
, s
);
732 s
->lnkst
= 0x8000; /* initial link state: up */