]>
Commit | Line | Data |
---|---|---|
376b8519 HD |
1 | /* |
2 | * QEMU Intel i82596 (Apricot) emulation | |
3 | * | |
4 | * Copyright (c) 2019 Helge Deller <deller@gmx.de> | |
5 | * This work is licensed under the GNU GPL license version 2 or later. | |
6 | * | |
7 | * This software was written to be compatible with the specification: | |
8 | * https://www.intel.com/assets/pdf/general/82596ca.pdf | |
9 | */ | |
10 | ||
11 | #include "qemu/osdep.h" | |
12 | #include "qemu/timer.h" | |
13 | #include "net/net.h" | |
14 | #include "net/eth.h" | |
376b8519 HD |
15 | #include "hw/irq.h" |
16 | #include "hw/qdev-properties.h" | |
17 | #include "migration/vmstate.h" | |
ae4994d2 | 18 | #include "exec/address-spaces.h" |
376b8519 HD |
19 | #include "qemu/module.h" |
20 | #include "trace.h" | |
21 | #include "i82596.h" | |
22 | #include <zlib.h> /* For crc32 */ | |
23 | ||
24 | #if defined(ENABLE_DEBUG) | |
25 | #define DBG(x) x | |
26 | #else | |
27 | #define DBG(x) do { } while (0) | |
28 | #endif | |
29 | ||
30 | #define USE_TIMER 0 | |
31 | ||
32 | #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) | |
33 | ||
34 | #define PKT_BUF_SZ 1536 | |
35 | #define MAX_MC_CNT 64 | |
36 | ||
37 | #define ISCP_BUSY 0x0001 | |
38 | ||
39 | #define I596_NULL ((uint32_t)0xffffffff) | |
40 | ||
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 */ | |
45 | ||
baba731b PMD |
46 | #define SCB_COMMAND_ACK_MASK \ |
47 | (SCB_STATUS_CX | SCB_STATUS_FR | SCB_STATUS_CNA | SCB_STATUS_RNR) | |
48 | ||
376b8519 HD |
49 | #define CU_IDLE 0 |
50 | #define CU_SUSPENDED 1 | |
51 | #define CU_ACTIVE 2 | |
52 | ||
53 | #define RX_IDLE 0 | |
54 | #define RX_SUSPENDED 1 | |
55 | #define RX_READY 4 | |
56 | ||
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. */ | |
60 | ||
61 | #define CMD_FLEX 0x0008 /* Enable flexible memory model */ | |
62 | ||
63 | enum commands { | |
64 | CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, | |
65 | CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7 | |
66 | }; | |
67 | ||
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 */ | |
72 | ||
73 | #define I596_EOF 0x8000 | |
74 | #define SIZE_MASK 0x3fff | |
75 | ||
376b8519 HD |
76 | /* various flags in the chip config registers */ |
77 | #define I596_PREFETCH (s->config[0] & 0x80) | |
78 | #define I596_PROMISC (s->config[8] & 0x01) | |
79 | #define I596_BC_DISABLE (s->config[8] & 0x02) /* broadcast disable */ | |
80 | #define I596_NOCRC_INS (s->config[8] & 0x08) | |
81 | #define I596_CRCINM (s->config[11] & 0x04) /* CRC appended */ | |
82 | #define I596_MC_ALL (s->config[11] & 0x20) | |
83 | #define I596_MULTIIA (s->config[13] & 0x40) | |
84 | ||
85 | ||
86 | static uint8_t get_byte(uint32_t addr) | |
87 | { | |
88 | return ldub_phys(&address_space_memory, addr); | |
89 | } | |
90 | ||
91 | static void set_byte(uint32_t addr, uint8_t c) | |
92 | { | |
93 | return stb_phys(&address_space_memory, addr, c); | |
94 | } | |
95 | ||
96 | static uint16_t get_uint16(uint32_t addr) | |
97 | { | |
98 | return lduw_be_phys(&address_space_memory, addr); | |
99 | } | |
100 | ||
101 | static void set_uint16(uint32_t addr, uint16_t w) | |
102 | { | |
103 | return stw_be_phys(&address_space_memory, addr, w); | |
104 | } | |
105 | ||
106 | static uint32_t get_uint32(uint32_t addr) | |
107 | { | |
108 | uint32_t lo = lduw_be_phys(&address_space_memory, addr); | |
109 | uint32_t hi = lduw_be_phys(&address_space_memory, addr + 2); | |
110 | return (hi << 16) | lo; | |
111 | } | |
112 | ||
113 | static void set_uint32(uint32_t addr, uint32_t val) | |
114 | { | |
115 | set_uint16(addr, (uint16_t) val); | |
116 | set_uint16(addr + 2, val >> 16); | |
117 | } | |
118 | ||
119 | ||
120 | struct qemu_ether_header { | |
121 | uint8_t ether_dhost[6]; | |
122 | uint8_t ether_shost[6]; | |
123 | uint16_t ether_type; | |
124 | }; | |
125 | ||
126 | #define PRINT_PKTHDR(txt, BUF) do { \ | |
127 | struct qemu_ether_header *hdr = (void *)(BUF); \ | |
128 | printf(txt ": packet dhost=" MAC_FMT ", shost=" MAC_FMT ", type=0x%04x\n",\ | |
129 | MAC_ARG(hdr->ether_dhost), MAC_ARG(hdr->ether_shost), \ | |
130 | be16_to_cpu(hdr->ether_type)); \ | |
131 | } while (0) | |
132 | ||
133 | static void i82596_transmit(I82596State *s, uint32_t addr) | |
134 | { | |
135 | uint32_t tdb_p; /* Transmit Buffer Descriptor */ | |
136 | ||
137 | /* TODO: Check flexible mode */ | |
138 | tdb_p = get_uint32(addr + 8); | |
139 | while (tdb_p != I596_NULL) { | |
140 | uint16_t size, len; | |
141 | uint32_t tba; | |
142 | ||
143 | size = get_uint16(tdb_p); | |
144 | len = size & SIZE_MASK; | |
145 | tba = get_uint32(tdb_p + 8); | |
146 | trace_i82596_transmit(len, tba); | |
147 | ||
148 | if (s->nic && len) { | |
149 | assert(len <= sizeof(s->tx_buffer)); | |
19f70347 PM |
150 | address_space_read(&address_space_memory, tba, |
151 | MEMTXATTRS_UNSPECIFIED, s->tx_buffer, len); | |
376b8519 HD |
152 | DBG(PRINT_PKTHDR("Send", &s->tx_buffer)); |
153 | DBG(printf("Sending %d bytes\n", len)); | |
154 | qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, len); | |
155 | } | |
156 | ||
157 | /* was this the last package? */ | |
158 | if (size & I596_EOF) { | |
159 | break; | |
160 | } | |
161 | ||
162 | /* get next buffer pointer */ | |
163 | tdb_p = get_uint32(tdb_p + 4); | |
164 | } | |
165 | } | |
166 | ||
167 | static void set_individual_address(I82596State *s, uint32_t addr) | |
168 | { | |
169 | NetClientState *nc; | |
170 | uint8_t *m; | |
171 | ||
172 | nc = qemu_get_queue(s->nic); | |
173 | m = s->conf.macaddr.a; | |
19f70347 PM |
174 | address_space_read(&address_space_memory, addr + 8, |
175 | MEMTXATTRS_UNSPECIFIED, m, ETH_ALEN); | |
376b8519 HD |
176 | qemu_format_nic_info_str(nc, m); |
177 | trace_i82596_new_mac(nc->info_str); | |
178 | } | |
179 | ||
180 | static void set_multicast_list(I82596State *s, uint32_t addr) | |
181 | { | |
182 | uint16_t mc_count, i; | |
183 | ||
184 | memset(&s->mult[0], 0, sizeof(s->mult)); | |
185 | mc_count = get_uint16(addr + 8) / ETH_ALEN; | |
186 | addr += 10; | |
187 | if (mc_count > MAX_MC_CNT) { | |
188 | mc_count = MAX_MC_CNT; | |
189 | } | |
190 | for (i = 0; i < mc_count; i++) { | |
191 | uint8_t multicast_addr[ETH_ALEN]; | |
19f70347 PM |
192 | address_space_read(&address_space_memory, addr + i * ETH_ALEN, |
193 | MEMTXATTRS_UNSPECIFIED, multicast_addr, ETH_ALEN); | |
376b8519 HD |
194 | DBG(printf("Add multicast entry " MAC_FMT "\n", |
195 | MAC_ARG(multicast_addr))); | |
196 | unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) & | |
197 | BITS(7, 2)) >> 2; | |
198 | assert(mcast_idx < 8 * sizeof(s->mult)); | |
199 | s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); | |
200 | } | |
201 | trace_i82596_set_multicast(mc_count); | |
202 | } | |
203 | ||
204 | void i82596_set_link_status(NetClientState *nc) | |
205 | { | |
206 | I82596State *d = qemu_get_nic_opaque(nc); | |
207 | ||
208 | d->lnkst = nc->link_down ? 0 : 0x8000; | |
209 | } | |
210 | ||
211 | static void update_scb_status(I82596State *s) | |
212 | { | |
213 | s->scb_status = (s->scb_status & 0xf000) | |
214 | | (s->cu_status << 8) | (s->rx_status << 4); | |
215 | set_uint16(s->scb, s->scb_status); | |
216 | } | |
217 | ||
218 | ||
219 | static void i82596_s_reset(I82596State *s) | |
220 | { | |
221 | trace_i82596_s_reset(s); | |
222 | s->scp = 0; | |
223 | s->scb_status = 0; | |
224 | s->cu_status = CU_IDLE; | |
225 | s->rx_status = RX_SUSPENDED; | |
226 | s->cmd_p = I596_NULL; | |
227 | s->lnkst = 0x8000; /* initial link state: up */ | |
228 | s->ca = s->ca_active = 0; | |
229 | s->send_irq = 0; | |
230 | } | |
231 | ||
232 | ||
233 | static void command_loop(I82596State *s) | |
234 | { | |
235 | uint16_t cmd; | |
236 | uint16_t status; | |
237 | uint8_t byte_cnt; | |
238 | ||
239 | DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s->cmd_p)); | |
240 | ||
241 | while (s->cmd_p != I596_NULL) { | |
242 | /* set status */ | |
243 | status = STAT_B; | |
244 | set_uint16(s->cmd_p, status); | |
245 | status = STAT_C | STAT_OK; /* update, but write later */ | |
246 | ||
247 | cmd = get_uint16(s->cmd_p + 2); | |
248 | DBG(printf("Running command %04x at %08x\n", cmd, s->cmd_p)); | |
249 | ||
250 | switch (cmd & 0x07) { | |
251 | case CmdNOp: | |
252 | break; | |
253 | case CmdSASetup: | |
254 | set_individual_address(s, s->cmd_p); | |
255 | break; | |
256 | case CmdConfigure: | |
257 | byte_cnt = get_byte(s->cmd_p + 8) & 0x0f; | |
258 | byte_cnt = MAX(byte_cnt, 4); | |
259 | byte_cnt = MIN(byte_cnt, sizeof(s->config)); | |
260 | /* copy byte_cnt max. */ | |
19f70347 PM |
261 | address_space_read(&address_space_memory, s->cmd_p + 8, |
262 | MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); | |
376b8519 HD |
263 | /* config byte according to page 35ff */ |
264 | s->config[2] &= 0x82; /* mask valid bits */ | |
265 | s->config[2] |= 0x40; | |
266 | s->config[7] &= 0xf7; /* clear zero bit */ | |
267 | assert(I596_NOCRC_INS == 0); /* do CRC insertion */ | |
268 | s->config[10] = MAX(s->config[10], 5); /* min frame length */ | |
269 | s->config[12] &= 0x40; /* only full duplex field valid */ | |
270 | s->config[13] |= 0x3f; /* set ones in byte 13 */ | |
271 | break; | |
272 | case CmdTDR: | |
273 | /* get signal LINK */ | |
274 | set_uint32(s->cmd_p + 8, s->lnkst); | |
275 | break; | |
276 | case CmdTx: | |
277 | i82596_transmit(s, s->cmd_p); | |
278 | break; | |
279 | case CmdMulticastList: | |
280 | set_multicast_list(s, s->cmd_p); | |
281 | break; | |
282 | case CmdDump: | |
283 | case CmdDiagnose: | |
284 | printf("FIXME Command %d !!\n", cmd & 7); | |
285 | assert(0); | |
286 | } | |
287 | ||
288 | /* update status */ | |
289 | set_uint16(s->cmd_p, status); | |
290 | ||
291 | s->cmd_p = get_uint32(s->cmd_p + 4); /* get link address */ | |
292 | DBG(printf("NEXT addr would be %08x\n", s->cmd_p)); | |
293 | if (s->cmd_p == 0) { | |
294 | s->cmd_p = I596_NULL; | |
295 | } | |
296 | ||
297 | /* Stop when last command of the list. */ | |
298 | if (cmd & CMD_EOL) { | |
299 | s->cmd_p = I596_NULL; | |
300 | } | |
301 | /* Suspend after doing cmd? */ | |
302 | if (cmd & CMD_SUSP) { | |
303 | s->cu_status = CU_SUSPENDED; | |
304 | printf("FIXME SUSPEND !!\n"); | |
305 | } | |
306 | /* Interrupt after doing cmd? */ | |
307 | if (cmd & CMD_INTR) { | |
308 | s->scb_status |= SCB_STATUS_CX; | |
309 | } else { | |
310 | s->scb_status &= ~SCB_STATUS_CX; | |
311 | } | |
312 | update_scb_status(s); | |
313 | ||
314 | /* Interrupt after doing cmd? */ | |
315 | if (cmd & CMD_INTR) { | |
316 | s->send_irq = 1; | |
317 | } | |
318 | ||
319 | if (s->cu_status != CU_ACTIVE) { | |
320 | break; | |
321 | } | |
322 | } | |
323 | DBG(printf("FINISHED COMMAND LOOP\n")); | |
324 | qemu_flush_queued_packets(qemu_get_queue(s->nic)); | |
325 | } | |
326 | ||
327 | static void i82596_flush_queue_timer(void *opaque) | |
328 | { | |
329 | I82596State *s = opaque; | |
330 | if (0) { | |
331 | timer_del(s->flush_queue_timer); | |
332 | qemu_flush_queued_packets(qemu_get_queue(s->nic)); | |
333 | timer_mod(s->flush_queue_timer, | |
334 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); | |
335 | } | |
336 | } | |
337 | ||
338 | static void examine_scb(I82596State *s) | |
339 | { | |
340 | uint16_t command, cuc, ruc; | |
341 | ||
342 | /* get the scb command word */ | |
343 | command = get_uint16(s->scb + 2); | |
344 | cuc = (command >> 8) & 0x7; | |
345 | ruc = (command >> 4) & 0x7; | |
346 | DBG(printf("MAIN COMMAND %04x cuc %02x ruc %02x\n", command, cuc, ruc)); | |
347 | /* and clear the scb command word */ | |
348 | set_uint16(s->scb + 2, 0); | |
349 | ||
baba731b | 350 | s->scb_status &= ~(command & SCB_COMMAND_ACK_MASK); |
376b8519 HD |
351 | |
352 | switch (cuc) { | |
353 | case 0: /* no change */ | |
354 | break; | |
355 | case 1: /* CUC_START */ | |
356 | s->cu_status = CU_ACTIVE; | |
357 | break; | |
358 | case 4: /* CUC_ABORT */ | |
359 | s->cu_status = CU_SUSPENDED; | |
360 | s->scb_status |= SCB_STATUS_CNA; /* CU left active state */ | |
361 | break; | |
362 | default: | |
363 | printf("WARNING: Unknown CUC %d!\n", cuc); | |
364 | } | |
365 | ||
366 | switch (ruc) { | |
367 | case 0: /* no change */ | |
368 | break; | |
369 | case 1: /* RX_START */ | |
370 | case 2: /* RX_RESUME */ | |
371 | s->rx_status = RX_IDLE; | |
372 | if (USE_TIMER) { | |
373 | timer_mod(s->flush_queue_timer, qemu_clock_get_ms( | |
374 | QEMU_CLOCK_VIRTUAL) + 1000); | |
375 | } | |
376 | break; | |
377 | case 3: /* RX_SUSPEND */ | |
378 | case 4: /* RX_ABORT */ | |
379 | s->rx_status = RX_SUSPENDED; | |
380 | s->scb_status |= SCB_STATUS_RNR; /* RU left active state */ | |
381 | break; | |
382 | default: | |
383 | printf("WARNING: Unknown RUC %d!\n", ruc); | |
384 | } | |
385 | ||
386 | if (command & 0x80) { /* reset bit set? */ | |
387 | i82596_s_reset(s); | |
388 | } | |
389 | ||
390 | /* execute commands from SCBL */ | |
391 | if (s->cu_status != CU_SUSPENDED) { | |
392 | if (s->cmd_p == I596_NULL) { | |
393 | s->cmd_p = get_uint32(s->scb + 4); | |
394 | } | |
395 | } | |
396 | ||
397 | /* update scb status */ | |
398 | update_scb_status(s); | |
399 | ||
400 | command_loop(s); | |
401 | } | |
402 | ||
403 | static void signal_ca(I82596State *s) | |
404 | { | |
405 | uint32_t iscp = 0; | |
406 | ||
407 | /* trace_i82596_channel_attention(s); */ | |
408 | if (s->scp) { | |
409 | /* CA after reset -> do init with new scp. */ | |
410 | s->sysbus = get_byte(s->scp + 3); /* big endian */ | |
411 | DBG(printf("SYSBUS = %08x\n", s->sysbus)); | |
412 | if (((s->sysbus >> 1) & 0x03) != 2) { | |
413 | printf("WARNING: NO LINEAR MODE !!\n"); | |
414 | } | |
415 | if ((s->sysbus >> 7)) { | |
416 | printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\n"); | |
417 | } | |
418 | iscp = get_uint32(s->scp + 8); | |
419 | s->scb = get_uint32(iscp + 4); | |
420 | set_byte(iscp + 1, 0); /* clear BUSY flag in iscp */ | |
421 | s->scp = 0; | |
422 | } | |
423 | ||
424 | s->ca++; /* count ca() */ | |
425 | if (!s->ca_active) { | |
426 | s->ca_active = 1; | |
427 | while (s->ca) { | |
428 | examine_scb(s); | |
429 | s->ca--; | |
430 | } | |
431 | s->ca_active = 0; | |
432 | } | |
433 | ||
434 | if (s->send_irq) { | |
435 | s->send_irq = 0; | |
436 | qemu_set_irq(s->irq, 1); | |
437 | } | |
438 | } | |
439 | ||
440 | void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val) | |
441 | { | |
442 | I82596State *s = opaque; | |
443 | /* printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); */ | |
444 | switch (addr) { | |
445 | case PORT_RESET: /* Reset */ | |
446 | i82596_s_reset(s); | |
447 | break; | |
448 | case PORT_ALTSCP: | |
449 | s->scp = val; | |
450 | break; | |
451 | case PORT_CA: | |
452 | signal_ca(s); | |
453 | break; | |
454 | } | |
455 | } | |
456 | ||
457 | uint32_t i82596_ioport_readw(void *opaque, uint32_t addr) | |
458 | { | |
459 | return -1; | |
460 | } | |
461 | ||
462 | void i82596_h_reset(void *opaque) | |
463 | { | |
464 | I82596State *s = opaque; | |
465 | ||
466 | i82596_s_reset(s); | |
467 | } | |
468 | ||
b8c4b67e | 469 | bool i82596_can_receive(NetClientState *nc) |
376b8519 HD |
470 | { |
471 | I82596State *s = qemu_get_nic_opaque(nc); | |
472 | ||
473 | if (s->rx_status == RX_SUSPENDED) { | |
b8c4b67e | 474 | return false; |
376b8519 HD |
475 | } |
476 | ||
477 | if (!s->lnkst) { | |
b8c4b67e | 478 | return false; |
376b8519 HD |
479 | } |
480 | ||
481 | if (USE_TIMER && !timer_pending(s->flush_queue_timer)) { | |
b8c4b67e | 482 | return true; |
376b8519 HD |
483 | } |
484 | ||
b8c4b67e | 485 | return true; |
376b8519 HD |
486 | } |
487 | ||
376b8519 HD |
488 | ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t sz) |
489 | { | |
490 | I82596State *s = qemu_get_nic_opaque(nc); | |
491 | uint32_t rfd_p; | |
492 | uint32_t rbd; | |
493 | uint16_t is_broadcast = 0; | |
a43790f2 PM |
494 | size_t len = sz; /* length of data for guest (including CRC) */ |
495 | size_t bufsz = sz; /* length of data in buf */ | |
376b8519 HD |
496 | uint32_t crc; |
497 | uint8_t *crc_ptr; | |
376b8519 HD |
498 | static const uint8_t broadcast_macaddr[6] = { |
499 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
500 | ||
501 | DBG(printf("i82596_receive() start\n")); | |
502 | ||
503 | if (USE_TIMER && timer_pending(s->flush_queue_timer)) { | |
504 | return 0; | |
505 | } | |
506 | ||
507 | /* first check if receiver is enabled */ | |
508 | if (s->rx_status == RX_SUSPENDED) { | |
509 | trace_i82596_receive_analysis(">>> Receiving suspended"); | |
510 | return -1; | |
511 | } | |
512 | ||
513 | if (!s->lnkst) { | |
514 | trace_i82596_receive_analysis(">>> Link down"); | |
515 | return -1; | |
516 | } | |
517 | ||
518 | /* Received frame smaller than configured "min frame len"? */ | |
519 | if (sz < s->config[10]) { | |
520 | printf("Received frame too small, %zu vs. %u bytes\n", | |
521 | sz, s->config[10]); | |
522 | return -1; | |
523 | } | |
524 | ||
525 | DBG(printf("Received %lu bytes\n", sz)); | |
526 | ||
527 | if (I596_PROMISC) { | |
528 | ||
529 | /* promiscuous: receive all */ | |
530 | trace_i82596_receive_analysis( | |
531 | ">>> packet received in promiscuous mode"); | |
532 | ||
533 | } else { | |
534 | ||
535 | if (!memcmp(buf, broadcast_macaddr, 6)) { | |
536 | /* broadcast address */ | |
537 | if (I596_BC_DISABLE) { | |
538 | trace_i82596_receive_analysis(">>> broadcast packet rejected"); | |
539 | ||
540 | return len; | |
541 | } | |
542 | ||
543 | trace_i82596_receive_analysis(">>> broadcast packet received"); | |
544 | is_broadcast = 1; | |
545 | ||
546 | } else if (buf[0] & 0x01) { | |
547 | /* multicast */ | |
548 | if (!I596_MC_ALL) { | |
549 | trace_i82596_receive_analysis(">>> multicast packet rejected"); | |
550 | ||
551 | return len; | |
552 | } | |
553 | ||
554 | int mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2; | |
555 | assert(mcast_idx < 8 * sizeof(s->mult)); | |
556 | ||
557 | if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { | |
558 | trace_i82596_receive_analysis(">>> multicast address mismatch"); | |
559 | ||
560 | return len; | |
561 | } | |
562 | ||
563 | trace_i82596_receive_analysis(">>> multicast packet received"); | |
564 | is_broadcast = 1; | |
565 | ||
566 | } else if (!memcmp(s->conf.macaddr.a, buf, 6)) { | |
567 | ||
568 | /* match */ | |
569 | trace_i82596_receive_analysis( | |
570 | ">>> physical address matching packet received"); | |
571 | ||
572 | } else { | |
573 | ||
574 | trace_i82596_receive_analysis(">>> unknown packet"); | |
575 | ||
576 | return len; | |
577 | } | |
578 | } | |
579 | ||
376b8519 HD |
580 | /* Calculate the ethernet checksum (4 bytes) */ |
581 | len += 4; | |
582 | crc = cpu_to_be32(crc32(~0, buf, sz)); | |
583 | crc_ptr = (uint8_t *) &crc; | |
584 | ||
585 | rfd_p = get_uint32(s->scb + 8); /* get Receive Frame Descriptor */ | |
586 | assert(rfd_p && rfd_p != I596_NULL); | |
587 | ||
588 | /* get first Receive Buffer Descriptor Address */ | |
589 | rbd = get_uint32(rfd_p + 8); | |
590 | assert(rbd && rbd != I596_NULL); | |
591 | ||
592 | trace_i82596_receive_packet(len); | |
593 | /* PRINT_PKTHDR("Receive", buf); */ | |
594 | ||
595 | while (len) { | |
596 | uint16_t command, status; | |
597 | uint32_t next_rfd; | |
598 | ||
599 | command = get_uint16(rfd_p + 2); | |
600 | assert(command & CMD_FLEX); /* assert Flex Mode */ | |
601 | /* get first Receive Buffer Descriptor Address */ | |
602 | rbd = get_uint32(rfd_p + 8); | |
603 | assert(get_uint16(rfd_p + 14) == 0); | |
604 | ||
605 | /* printf("Receive: rfd is %08x\n", rfd_p); */ | |
606 | ||
607 | while (len) { | |
608 | uint16_t buffer_size, num; | |
609 | uint32_t rba; | |
a43790f2 | 610 | size_t bufcount, crccount; |
376b8519 HD |
611 | |
612 | /* printf("Receive: rbd is %08x\n", rbd); */ | |
613 | buffer_size = get_uint16(rbd + 12); | |
614 | /* printf("buffer_size is 0x%x\n", buffer_size); */ | |
615 | assert(buffer_size != 0); | |
616 | ||
617 | num = buffer_size & SIZE_MASK; | |
618 | if (num > len) { | |
619 | num = len; | |
620 | } | |
621 | rba = get_uint32(rbd + 8); | |
622 | /* printf("rba is 0x%x\n", rba); */ | |
a43790f2 PM |
623 | /* |
624 | * Calculate how many bytes we want from buf[] and how many | |
625 | * from the CRC. | |
626 | */ | |
627 | if ((len - num) >= 4) { | |
628 | /* The whole guest buffer, we haven't hit the CRC yet */ | |
629 | bufcount = num; | |
630 | } else { | |
631 | /* All that's left of buf[] */ | |
632 | bufcount = len - 4; | |
633 | } | |
634 | crccount = num - bufcount; | |
635 | ||
636 | if (bufcount > 0) { | |
637 | /* Still some of the actual data buffer to transfer */ | |
638 | assert(bufsz >= bufcount); | |
639 | bufsz -= bufcount; | |
640 | address_space_write(&address_space_memory, rba, | |
641 | MEMTXATTRS_UNSPECIFIED, buf, bufcount); | |
642 | rba += bufcount; | |
643 | buf += bufcount; | |
644 | len -= bufcount; | |
645 | } | |
646 | ||
647 | /* Write as much of the CRC as fits */ | |
648 | if (crccount > 0) { | |
649 | address_space_write(&address_space_memory, rba, | |
650 | MEMTXATTRS_UNSPECIFIED, crc_ptr, crccount); | |
651 | rba += crccount; | |
652 | crc_ptr += crccount; | |
653 | len -= crccount; | |
376b8519 HD |
654 | } |
655 | ||
656 | num |= 0x4000; /* set F BIT */ | |
657 | if (len == 0) { | |
658 | num |= I596_EOF; /* set EOF BIT */ | |
659 | } | |
660 | set_uint16(rbd + 0, num); /* write actual count with flags */ | |
661 | ||
662 | /* get next rbd */ | |
663 | rbd = get_uint32(rbd + 4); | |
664 | /* printf("Next Receive: rbd is %08x\n", rbd); */ | |
665 | ||
666 | if (buffer_size & I596_EOF) /* last entry */ | |
667 | break; | |
668 | } | |
669 | ||
670 | /* Housekeeping, see pg. 18 */ | |
671 | next_rfd = get_uint32(rfd_p + 4); | |
672 | set_uint32(next_rfd + 8, rbd); | |
673 | ||
674 | status = STAT_C | STAT_OK | is_broadcast; | |
675 | set_uint16(rfd_p, status); | |
676 | ||
677 | if (command & CMD_SUSP) { /* suspend after command? */ | |
678 | s->rx_status = RX_SUSPENDED; | |
679 | s->scb_status |= SCB_STATUS_RNR; /* RU left active state */ | |
680 | break; | |
681 | } | |
682 | if (command & CMD_EOL) /* was it last Frame Descriptor? */ | |
683 | break; | |
684 | ||
685 | assert(len == 0); | |
686 | } | |
687 | ||
688 | assert(len == 0); | |
689 | ||
690 | s->scb_status |= SCB_STATUS_FR; /* set "RU finished receiving frame" bit. */ | |
691 | update_scb_status(s); | |
692 | ||
693 | /* send IRQ that we received data */ | |
694 | qemu_set_irq(s->irq, 1); | |
695 | /* s->send_irq = 1; */ | |
696 | ||
697 | if (0) { | |
698 | DBG(printf("Checking:\n")); | |
699 | rfd_p = get_uint32(s->scb + 8); /* get Receive Frame Descriptor */ | |
700 | DBG(printf("Next Receive: rfd is %08x\n", rfd_p)); | |
701 | rfd_p = get_uint32(rfd_p + 4); /* get Next Receive Frame Descriptor */ | |
702 | DBG(printf("Next Receive: rfd is %08x\n", rfd_p)); | |
703 | /* get first Receive Buffer Descriptor Address */ | |
704 | rbd = get_uint32(rfd_p + 8); | |
705 | DBG(printf("Next Receive: rbd is %08x\n", rbd)); | |
706 | } | |
707 | ||
708 | return sz; | |
709 | } | |
710 | ||
711 | ||
712 | const VMStateDescription vmstate_i82596 = { | |
713 | .name = "i82596", | |
714 | .version_id = 1, | |
715 | .minimum_version_id = 1, | |
1de81b42 | 716 | .fields = (const VMStateField[]) { |
376b8519 HD |
717 | VMSTATE_UINT16(lnkst, I82596State), |
718 | VMSTATE_TIMER_PTR(flush_queue_timer, I82596State), | |
719 | VMSTATE_END_OF_LIST() | |
720 | } | |
721 | }; | |
722 | ||
723 | void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *info) | |
724 | { | |
725 | if (s->conf.macaddr.a[0] == 0) { | |
726 | qemu_macaddr_default_if_unset(&s->conf.macaddr); | |
727 | } | |
728 | s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), | |
7d0fefdf | 729 | dev->id, &dev->mem_reentrancy_guard, s); |
376b8519 HD |
730 | qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); |
731 | ||
732 | if (USE_TIMER) { | |
733 | s->flush_queue_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, | |
734 | i82596_flush_queue_timer, s); | |
735 | } | |
736 | s->lnkst = 0x8000; /* initial link state: up */ | |
737 | } |