]>
Commit | Line | Data |
---|---|---|
376b8519 HD |
1 | /* |
2 | * QEMU Intel i82596 (Apricot) emulation | |
3 | * | |
4 | * Copyright (c) 2019 Helge Deller <[email protected]> | |
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" | |
15 | #include "sysemu/sysemu.h" | |
16 | #include "hw/irq.h" | |
17 | #include "hw/qdev-properties.h" | |
18 | #include "migration/vmstate.h" | |
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 | ||
76 | #define ETHER_TYPE_LEN 2 | |
77 | #define VLAN_TCI_LEN 2 | |
78 | #define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN) | |
79 | ||
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) | |
88 | ||
89 | ||
90 | static uint8_t get_byte(uint32_t addr) | |
91 | { | |
92 | return ldub_phys(&address_space_memory, addr); | |
93 | } | |
94 | ||
95 | static void set_byte(uint32_t addr, uint8_t c) | |
96 | { | |
97 | return stb_phys(&address_space_memory, addr, c); | |
98 | } | |
99 | ||
100 | static uint16_t get_uint16(uint32_t addr) | |
101 | { | |
102 | return lduw_be_phys(&address_space_memory, addr); | |
103 | } | |
104 | ||
105 | static void set_uint16(uint32_t addr, uint16_t w) | |
106 | { | |
107 | return stw_be_phys(&address_space_memory, addr, w); | |
108 | } | |
109 | ||
110 | static uint32_t get_uint32(uint32_t addr) | |
111 | { | |
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; | |
115 | } | |
116 | ||
117 | static void set_uint32(uint32_t addr, uint32_t val) | |
118 | { | |
119 | set_uint16(addr, (uint16_t) val); | |
120 | set_uint16(addr + 2, val >> 16); | |
121 | } | |
122 | ||
123 | ||
124 | struct qemu_ether_header { | |
125 | uint8_t ether_dhost[6]; | |
126 | uint8_t ether_shost[6]; | |
127 | uint16_t ether_type; | |
128 | }; | |
129 | ||
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)); \ | |
135 | } while (0) | |
136 | ||
137 | static void i82596_transmit(I82596State *s, uint32_t addr) | |
138 | { | |
139 | uint32_t tdb_p; /* Transmit Buffer Descriptor */ | |
140 | ||
141 | /* TODO: Check flexible mode */ | |
142 | tdb_p = get_uint32(addr + 8); | |
143 | while (tdb_p != I596_NULL) { | |
144 | uint16_t size, len; | |
145 | uint32_t tba; | |
146 | ||
147 | size = get_uint16(tdb_p); | |
148 | len = size & SIZE_MASK; | |
149 | tba = get_uint32(tdb_p + 8); | |
150 | trace_i82596_transmit(len, tba); | |
151 | ||
152 | if (s->nic && len) { | |
153 | assert(len <= sizeof(s->tx_buffer)); | |
19f70347 PM |
154 | address_space_read(&address_space_memory, tba, |
155 | MEMTXATTRS_UNSPECIFIED, s->tx_buffer, len); | |
376b8519 HD |
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); | |
159 | } | |
160 | ||
161 | /* was this the last package? */ | |
162 | if (size & I596_EOF) { | |
163 | break; | |
164 | } | |
165 | ||
166 | /* get next buffer pointer */ | |
167 | tdb_p = get_uint32(tdb_p + 4); | |
168 | } | |
169 | } | |
170 | ||
171 | static void set_individual_address(I82596State *s, uint32_t addr) | |
172 | { | |
173 | NetClientState *nc; | |
174 | uint8_t *m; | |
175 | ||
176 | nc = qemu_get_queue(s->nic); | |
177 | m = s->conf.macaddr.a; | |
19f70347 PM |
178 | address_space_read(&address_space_memory, addr + 8, |
179 | MEMTXATTRS_UNSPECIFIED, m, ETH_ALEN); | |
376b8519 HD |
180 | qemu_format_nic_info_str(nc, m); |
181 | trace_i82596_new_mac(nc->info_str); | |
182 | } | |
183 | ||
184 | static void set_multicast_list(I82596State *s, uint32_t addr) | |
185 | { | |
186 | uint16_t mc_count, i; | |
187 | ||
188 | memset(&s->mult[0], 0, sizeof(s->mult)); | |
189 | mc_count = get_uint16(addr + 8) / ETH_ALEN; | |
190 | addr += 10; | |
191 | if (mc_count > MAX_MC_CNT) { | |
192 | mc_count = MAX_MC_CNT; | |
193 | } | |
194 | for (i = 0; i < mc_count; i++) { | |
195 | uint8_t multicast_addr[ETH_ALEN]; | |
19f70347 PM |
196 | address_space_read(&address_space_memory, addr + i * ETH_ALEN, |
197 | MEMTXATTRS_UNSPECIFIED, multicast_addr, ETH_ALEN); | |
376b8519 HD |
198 | DBG(printf("Add multicast entry " MAC_FMT "\n", |
199 | MAC_ARG(multicast_addr))); | |
200 | unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) & | |
201 | BITS(7, 2)) >> 2; | |
202 | assert(mcast_idx < 8 * sizeof(s->mult)); | |
203 | s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); | |
204 | } | |
205 | trace_i82596_set_multicast(mc_count); | |
206 | } | |
207 | ||
208 | void i82596_set_link_status(NetClientState *nc) | |
209 | { | |
210 | I82596State *d = qemu_get_nic_opaque(nc); | |
211 | ||
212 | d->lnkst = nc->link_down ? 0 : 0x8000; | |
213 | } | |
214 | ||
215 | static void update_scb_status(I82596State *s) | |
216 | { | |
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); | |
220 | } | |
221 | ||
222 | ||
223 | static void i82596_s_reset(I82596State *s) | |
224 | { | |
225 | trace_i82596_s_reset(s); | |
226 | s->scp = 0; | |
227 | s->scb_status = 0; | |
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; | |
233 | s->send_irq = 0; | |
234 | } | |
235 | ||
236 | ||
237 | static void command_loop(I82596State *s) | |
238 | { | |
239 | uint16_t cmd; | |
240 | uint16_t status; | |
241 | uint8_t byte_cnt; | |
242 | ||
243 | DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s->cmd_p)); | |
244 | ||
245 | while (s->cmd_p != I596_NULL) { | |
246 | /* set status */ | |
247 | status = STAT_B; | |
248 | set_uint16(s->cmd_p, status); | |
249 | status = STAT_C | STAT_OK; /* update, but write later */ | |
250 | ||
251 | cmd = get_uint16(s->cmd_p + 2); | |
252 | DBG(printf("Running command %04x at %08x\n", cmd, s->cmd_p)); | |
253 | ||
254 | switch (cmd & 0x07) { | |
255 | case CmdNOp: | |
256 | break; | |
257 | case CmdSASetup: | |
258 | set_individual_address(s, s->cmd_p); | |
259 | break; | |
260 | case CmdConfigure: | |
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. */ | |
19f70347 PM |
265 | address_space_read(&address_space_memory, s->cmd_p + 8, |
266 | MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); | |
376b8519 HD |
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 */ | |
275 | break; | |
276 | case CmdTDR: | |
277 | /* get signal LINK */ | |
278 | set_uint32(s->cmd_p + 8, s->lnkst); | |
279 | break; | |
280 | case CmdTx: | |
281 | i82596_transmit(s, s->cmd_p); | |
282 | break; | |
283 | case CmdMulticastList: | |
284 | set_multicast_list(s, s->cmd_p); | |
285 | break; | |
286 | case CmdDump: | |
287 | case CmdDiagnose: | |
288 | printf("FIXME Command %d !!\n", cmd & 7); | |
289 | assert(0); | |
290 | } | |
291 | ||
292 | /* update status */ | |
293 | set_uint16(s->cmd_p, status); | |
294 | ||
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)); | |
297 | if (s->cmd_p == 0) { | |
298 | s->cmd_p = I596_NULL; | |
299 | } | |
300 | ||
301 | /* Stop when last command of the list. */ | |
302 | if (cmd & CMD_EOL) { | |
303 | s->cmd_p = I596_NULL; | |
304 | } | |
305 | /* Suspend after doing cmd? */ | |
306 | if (cmd & CMD_SUSP) { | |
307 | s->cu_status = CU_SUSPENDED; | |
308 | printf("FIXME SUSPEND !!\n"); | |
309 | } | |
310 | /* Interrupt after doing cmd? */ | |
311 | if (cmd & CMD_INTR) { | |
312 | s->scb_status |= SCB_STATUS_CX; | |
313 | } else { | |
314 | s->scb_status &= ~SCB_STATUS_CX; | |
315 | } | |
316 | update_scb_status(s); | |
317 | ||
318 | /* Interrupt after doing cmd? */ | |
319 | if (cmd & CMD_INTR) { | |
320 | s->send_irq = 1; | |
321 | } | |
322 | ||
323 | if (s->cu_status != CU_ACTIVE) { | |
324 | break; | |
325 | } | |
326 | } | |
327 | DBG(printf("FINISHED COMMAND LOOP\n")); | |
328 | qemu_flush_queued_packets(qemu_get_queue(s->nic)); | |
329 | } | |
330 | ||
331 | static void i82596_flush_queue_timer(void *opaque) | |
332 | { | |
333 | I82596State *s = opaque; | |
334 | if (0) { | |
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); | |
339 | } | |
340 | } | |
341 | ||
342 | static void examine_scb(I82596State *s) | |
343 | { | |
344 | uint16_t command, cuc, ruc; | |
345 | ||
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); | |
353 | ||
baba731b | 354 | s->scb_status &= ~(command & SCB_COMMAND_ACK_MASK); |
376b8519 HD |
355 | |
356 | switch (cuc) { | |
357 | case 0: /* no change */ | |
358 | break; | |
359 | case 1: /* CUC_START */ | |
360 | s->cu_status = CU_ACTIVE; | |
361 | break; | |
362 | case 4: /* CUC_ABORT */ | |
363 | s->cu_status = CU_SUSPENDED; | |
364 | s->scb_status |= SCB_STATUS_CNA; /* CU left active state */ | |
365 | break; | |
366 | default: | |
367 | printf("WARNING: Unknown CUC %d!\n", cuc); | |
368 | } | |
369 | ||
370 | switch (ruc) { | |
371 | case 0: /* no change */ | |
372 | break; | |
373 | case 1: /* RX_START */ | |
374 | case 2: /* RX_RESUME */ | |
375 | s->rx_status = RX_IDLE; | |
376 | if (USE_TIMER) { | |
377 | timer_mod(s->flush_queue_timer, qemu_clock_get_ms( | |
378 | QEMU_CLOCK_VIRTUAL) + 1000); | |
379 | } | |
380 | break; | |
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 */ | |
385 | break; | |
386 | default: | |
387 | printf("WARNING: Unknown RUC %d!\n", ruc); | |
388 | } | |
389 | ||
390 | if (command & 0x80) { /* reset bit set? */ | |
391 | i82596_s_reset(s); | |
392 | } | |
393 | ||
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); | |
398 | } | |
399 | } | |
400 | ||
401 | /* update scb status */ | |
402 | update_scb_status(s); | |
403 | ||
404 | command_loop(s); | |
405 | } | |
406 | ||
407 | static void signal_ca(I82596State *s) | |
408 | { | |
409 | uint32_t iscp = 0; | |
410 | ||
411 | /* trace_i82596_channel_attention(s); */ | |
412 | if (s->scp) { | |
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"); | |
418 | } | |
419 | if ((s->sysbus >> 7)) { | |
420 | printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\n"); | |
421 | } | |
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 */ | |
425 | s->scp = 0; | |
426 | } | |
427 | ||
428 | s->ca++; /* count ca() */ | |
429 | if (!s->ca_active) { | |
430 | s->ca_active = 1; | |
431 | while (s->ca) { | |
432 | examine_scb(s); | |
433 | s->ca--; | |
434 | } | |
435 | s->ca_active = 0; | |
436 | } | |
437 | ||
438 | if (s->send_irq) { | |
439 | s->send_irq = 0; | |
440 | qemu_set_irq(s->irq, 1); | |
441 | } | |
442 | } | |
443 | ||
444 | void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val) | |
445 | { | |
446 | I82596State *s = opaque; | |
447 | /* printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); */ | |
448 | switch (addr) { | |
449 | case PORT_RESET: /* Reset */ | |
450 | i82596_s_reset(s); | |
451 | break; | |
452 | case PORT_ALTSCP: | |
453 | s->scp = val; | |
454 | break; | |
455 | case PORT_CA: | |
456 | signal_ca(s); | |
457 | break; | |
458 | } | |
459 | } | |
460 | ||
461 | uint32_t i82596_ioport_readw(void *opaque, uint32_t addr) | |
462 | { | |
463 | return -1; | |
464 | } | |
465 | ||
466 | void i82596_h_reset(void *opaque) | |
467 | { | |
468 | I82596State *s = opaque; | |
469 | ||
470 | i82596_s_reset(s); | |
471 | } | |
472 | ||
b8c4b67e | 473 | bool i82596_can_receive(NetClientState *nc) |
376b8519 HD |
474 | { |
475 | I82596State *s = qemu_get_nic_opaque(nc); | |
476 | ||
477 | if (s->rx_status == RX_SUSPENDED) { | |
b8c4b67e | 478 | return false; |
376b8519 HD |
479 | } |
480 | ||
481 | if (!s->lnkst) { | |
b8c4b67e | 482 | return false; |
376b8519 HD |
483 | } |
484 | ||
485 | if (USE_TIMER && !timer_pending(s->flush_queue_timer)) { | |
b8c4b67e | 486 | return true; |
376b8519 HD |
487 | } |
488 | ||
b8c4b67e | 489 | return true; |
376b8519 HD |
490 | } |
491 | ||
492 | #define MIN_BUF_SIZE 60 | |
493 | ||
494 | ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t sz) | |
495 | { | |
496 | I82596State *s = qemu_get_nic_opaque(nc); | |
497 | uint32_t rfd_p; | |
498 | uint32_t rbd; | |
499 | uint16_t is_broadcast = 0; | |
a43790f2 PM |
500 | size_t len = sz; /* length of data for guest (including CRC) */ |
501 | size_t bufsz = sz; /* length of data in buf */ | |
376b8519 HD |
502 | uint32_t crc; |
503 | uint8_t *crc_ptr; | |
504 | uint8_t buf1[MIN_BUF_SIZE + VLAN_HLEN]; | |
505 | static const uint8_t broadcast_macaddr[6] = { | |
506 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
507 | ||
508 | DBG(printf("i82596_receive() start\n")); | |
509 | ||
510 | if (USE_TIMER && timer_pending(s->flush_queue_timer)) { | |
511 | return 0; | |
512 | } | |
513 | ||
514 | /* first check if receiver is enabled */ | |
515 | if (s->rx_status == RX_SUSPENDED) { | |
516 | trace_i82596_receive_analysis(">>> Receiving suspended"); | |
517 | return -1; | |
518 | } | |
519 | ||
520 | if (!s->lnkst) { | |
521 | trace_i82596_receive_analysis(">>> Link down"); | |
522 | return -1; | |
523 | } | |
524 | ||
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", | |
528 | sz, s->config[10]); | |
529 | return -1; | |
530 | } | |
531 | ||
532 | DBG(printf("Received %lu bytes\n", sz)); | |
533 | ||
534 | if (I596_PROMISC) { | |
535 | ||
536 | /* promiscuous: receive all */ | |
537 | trace_i82596_receive_analysis( | |
538 | ">>> packet received in promiscuous mode"); | |
539 | ||
540 | } else { | |
541 | ||
542 | if (!memcmp(buf, broadcast_macaddr, 6)) { | |
543 | /* broadcast address */ | |
544 | if (I596_BC_DISABLE) { | |
545 | trace_i82596_receive_analysis(">>> broadcast packet rejected"); | |
546 | ||
547 | return len; | |
548 | } | |
549 | ||
550 | trace_i82596_receive_analysis(">>> broadcast packet received"); | |
551 | is_broadcast = 1; | |
552 | ||
553 | } else if (buf[0] & 0x01) { | |
554 | /* multicast */ | |
555 | if (!I596_MC_ALL) { | |
556 | trace_i82596_receive_analysis(">>> multicast packet rejected"); | |
557 | ||
558 | return len; | |
559 | } | |
560 | ||
561 | int mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2; | |
562 | assert(mcast_idx < 8 * sizeof(s->mult)); | |
563 | ||
564 | if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { | |
565 | trace_i82596_receive_analysis(">>> multicast address mismatch"); | |
566 | ||
567 | return len; | |
568 | } | |
569 | ||
570 | trace_i82596_receive_analysis(">>> multicast packet received"); | |
571 | is_broadcast = 1; | |
572 | ||
573 | } else if (!memcmp(s->conf.macaddr.a, buf, 6)) { | |
574 | ||
575 | /* match */ | |
576 | trace_i82596_receive_analysis( | |
577 | ">>> physical address matching packet received"); | |
578 | ||
579 | } else { | |
580 | ||
581 | trace_i82596_receive_analysis(">>> unknown packet"); | |
582 | ||
583 | return len; | |
584 | } | |
585 | } | |
586 | ||
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); | |
591 | buf = buf1; | |
592 | if (len < MIN_BUF_SIZE) { | |
593 | len = MIN_BUF_SIZE; | |
594 | } | |
a43790f2 | 595 | bufsz = len; |
376b8519 HD |
596 | } |
597 | ||
598 | /* Calculate the ethernet checksum (4 bytes) */ | |
599 | len += 4; | |
600 | crc = cpu_to_be32(crc32(~0, buf, sz)); | |
601 | crc_ptr = (uint8_t *) &crc; | |
602 | ||
603 | rfd_p = get_uint32(s->scb + 8); /* get Receive Frame Descriptor */ | |
604 | assert(rfd_p && rfd_p != I596_NULL); | |
605 | ||
606 | /* get first Receive Buffer Descriptor Address */ | |
607 | rbd = get_uint32(rfd_p + 8); | |
608 | assert(rbd && rbd != I596_NULL); | |
609 | ||
610 | trace_i82596_receive_packet(len); | |
611 | /* PRINT_PKTHDR("Receive", buf); */ | |
612 | ||
613 | while (len) { | |
614 | uint16_t command, status; | |
615 | uint32_t next_rfd; | |
616 | ||
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); | |
622 | ||
623 | /* printf("Receive: rfd is %08x\n", rfd_p); */ | |
624 | ||
625 | while (len) { | |
626 | uint16_t buffer_size, num; | |
627 | uint32_t rba; | |
a43790f2 | 628 | size_t bufcount, crccount; |
376b8519 HD |
629 | |
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); | |
634 | ||
635 | num = buffer_size & SIZE_MASK; | |
636 | if (num > len) { | |
637 | num = len; | |
638 | } | |
639 | rba = get_uint32(rbd + 8); | |
640 | /* printf("rba is 0x%x\n", rba); */ | |
a43790f2 PM |
641 | /* |
642 | * Calculate how many bytes we want from buf[] and how many | |
643 | * from the CRC. | |
644 | */ | |
645 | if ((len - num) >= 4) { | |
646 | /* The whole guest buffer, we haven't hit the CRC yet */ | |
647 | bufcount = num; | |
648 | } else { | |
649 | /* All that's left of buf[] */ | |
650 | bufcount = len - 4; | |
651 | } | |
652 | crccount = num - bufcount; | |
653 | ||
654 | if (bufcount > 0) { | |
655 | /* Still some of the actual data buffer to transfer */ | |
656 | assert(bufsz >= bufcount); | |
657 | bufsz -= bufcount; | |
658 | address_space_write(&address_space_memory, rba, | |
659 | MEMTXATTRS_UNSPECIFIED, buf, bufcount); | |
660 | rba += bufcount; | |
661 | buf += bufcount; | |
662 | len -= bufcount; | |
663 | } | |
664 | ||
665 | /* Write as much of the CRC as fits */ | |
666 | if (crccount > 0) { | |
667 | address_space_write(&address_space_memory, rba, | |
668 | MEMTXATTRS_UNSPECIFIED, crc_ptr, crccount); | |
669 | rba += crccount; | |
670 | crc_ptr += crccount; | |
671 | len -= crccount; | |
376b8519 HD |
672 | } |
673 | ||
674 | num |= 0x4000; /* set F BIT */ | |
675 | if (len == 0) { | |
676 | num |= I596_EOF; /* set EOF BIT */ | |
677 | } | |
678 | set_uint16(rbd + 0, num); /* write actual count with flags */ | |
679 | ||
680 | /* get next rbd */ | |
681 | rbd = get_uint32(rbd + 4); | |
682 | /* printf("Next Receive: rbd is %08x\n", rbd); */ | |
683 | ||
684 | if (buffer_size & I596_EOF) /* last entry */ | |
685 | break; | |
686 | } | |
687 | ||
688 | /* Housekeeping, see pg. 18 */ | |
689 | next_rfd = get_uint32(rfd_p + 4); | |
690 | set_uint32(next_rfd + 8, rbd); | |
691 | ||
692 | status = STAT_C | STAT_OK | is_broadcast; | |
693 | set_uint16(rfd_p, status); | |
694 | ||
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 */ | |
698 | break; | |
699 | } | |
700 | if (command & CMD_EOL) /* was it last Frame Descriptor? */ | |
701 | break; | |
702 | ||
703 | assert(len == 0); | |
704 | } | |
705 | ||
706 | assert(len == 0); | |
707 | ||
708 | s->scb_status |= SCB_STATUS_FR; /* set "RU finished receiving frame" bit. */ | |
709 | update_scb_status(s); | |
710 | ||
711 | /* send IRQ that we received data */ | |
712 | qemu_set_irq(s->irq, 1); | |
713 | /* s->send_irq = 1; */ | |
714 | ||
715 | if (0) { | |
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)); | |
724 | } | |
725 | ||
726 | return sz; | |
727 | } | |
728 | ||
729 | ||
730 | const VMStateDescription vmstate_i82596 = { | |
731 | .name = "i82596", | |
732 | .version_id = 1, | |
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() | |
738 | } | |
739 | }; | |
740 | ||
741 | void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *info) | |
742 | { | |
743 | if (s->conf.macaddr.a[0] == 0) { | |
744 | qemu_macaddr_default_if_unset(&s->conf.macaddr); | |
745 | } | |
746 | s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), | |
747 | dev->id, s); | |
748 | qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); | |
749 | ||
750 | if (USE_TIMER) { | |
751 | s->flush_queue_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, | |
752 | i82596_flush_queue_timer, s); | |
753 | } | |
754 | s->lnkst = 0x8000; /* initial link state: up */ | |
755 | } |