]>
Commit | Line | Data |
---|---|---|
34ea023d SS |
1 | /* |
2 | * QEMU TULIP Emulation | |
3 | * | |
4 | * Copyright (c) 2019 Sven Schnelle <[email protected]> | |
5 | * | |
6 | * This work is licensed under the GNU GPL license version 2 or later. | |
7 | */ | |
8 | ||
9 | #include "qemu/osdep.h" | |
10 | #include "qemu/log.h" | |
11 | #include "hw/irq.h" | |
12 | #include "hw/pci/pci.h" | |
13 | #include "hw/qdev-properties.h" | |
14 | #include "hw/nvram/eeprom93xx.h" | |
15 | #include "migration/vmstate.h" | |
16 | #include "sysemu/sysemu.h" | |
17 | #include "tulip.h" | |
18 | #include "trace.h" | |
19 | #include "net/eth.h" | |
20 | ||
21 | typedef struct TULIPState { | |
22 | PCIDevice dev; | |
23 | MemoryRegion io; | |
24 | MemoryRegion memory; | |
25 | NICConf c; | |
26 | qemu_irq irq; | |
27 | NICState *nic; | |
28 | eeprom_t *eeprom; | |
29 | uint32_t csr[16]; | |
30 | ||
31 | /* state for MII */ | |
32 | uint32_t old_csr9; | |
33 | uint32_t mii_word; | |
34 | uint32_t mii_bitcnt; | |
35 | ||
36 | hwaddr current_rx_desc; | |
37 | hwaddr current_tx_desc; | |
38 | ||
39 | uint8_t rx_frame[2048]; | |
40 | uint8_t tx_frame[2048]; | |
41 | uint16_t tx_frame_len; | |
42 | uint16_t rx_frame_len; | |
43 | uint16_t rx_frame_size; | |
44 | ||
45 | uint32_t rx_status; | |
46 | uint8_t filter[16][6]; | |
47 | } TULIPState; | |
48 | ||
49 | static const VMStateDescription vmstate_pci_tulip = { | |
50 | .name = "tulip", | |
51 | .fields = (VMStateField[]) { | |
52 | VMSTATE_PCI_DEVICE(dev, TULIPState), | |
53 | VMSTATE_UINT32_ARRAY(csr, TULIPState, 16), | |
54 | VMSTATE_UINT32(old_csr9, TULIPState), | |
55 | VMSTATE_UINT32(mii_word, TULIPState), | |
56 | VMSTATE_UINT32(mii_bitcnt, TULIPState), | |
57 | VMSTATE_UINT64(current_rx_desc, TULIPState), | |
58 | VMSTATE_UINT64(current_tx_desc, TULIPState), | |
59 | VMSTATE_BUFFER(rx_frame, TULIPState), | |
60 | VMSTATE_BUFFER(tx_frame, TULIPState), | |
61 | VMSTATE_UINT16(rx_frame_len, TULIPState), | |
62 | VMSTATE_UINT16(tx_frame_len, TULIPState), | |
63 | VMSTATE_UINT16(rx_frame_size, TULIPState), | |
64 | VMSTATE_UINT32(rx_status, TULIPState), | |
65 | VMSTATE_UINT8_2DARRAY(filter, TULIPState, 16, 6), | |
66 | VMSTATE_END_OF_LIST() | |
67 | } | |
68 | }; | |
69 | ||
70 | static void tulip_desc_read(TULIPState *s, hwaddr p, | |
71 | struct tulip_descriptor *desc) | |
72 | { | |
73 | if (s->csr[0] & CSR0_DBO) { | |
74 | desc->status = ldl_be_pci_dma(&s->dev, p); | |
75 | desc->control = ldl_be_pci_dma(&s->dev, p + 4); | |
76 | desc->buf_addr1 = ldl_be_pci_dma(&s->dev, p + 8); | |
77 | desc->buf_addr2 = ldl_be_pci_dma(&s->dev, p + 12); | |
78 | } else { | |
79 | desc->status = ldl_le_pci_dma(&s->dev, p); | |
80 | desc->control = ldl_le_pci_dma(&s->dev, p + 4); | |
81 | desc->buf_addr1 = ldl_le_pci_dma(&s->dev, p + 8); | |
82 | desc->buf_addr2 = ldl_le_pci_dma(&s->dev, p + 12); | |
83 | } | |
84 | } | |
85 | ||
86 | static void tulip_desc_write(TULIPState *s, hwaddr p, | |
87 | struct tulip_descriptor *desc) | |
88 | { | |
89 | if (s->csr[0] & CSR0_DBO) { | |
90 | stl_be_pci_dma(&s->dev, p, desc->status); | |
91 | stl_be_pci_dma(&s->dev, p + 4, desc->control); | |
92 | stl_be_pci_dma(&s->dev, p + 8, desc->buf_addr1); | |
93 | stl_be_pci_dma(&s->dev, p + 12, desc->buf_addr2); | |
94 | } else { | |
95 | stl_le_pci_dma(&s->dev, p, desc->status); | |
96 | stl_le_pci_dma(&s->dev, p + 4, desc->control); | |
97 | stl_le_pci_dma(&s->dev, p + 8, desc->buf_addr1); | |
98 | stl_le_pci_dma(&s->dev, p + 12, desc->buf_addr2); | |
99 | } | |
100 | } | |
101 | ||
102 | static void tulip_update_int(TULIPState *s) | |
103 | { | |
104 | uint32_t ie = s->csr[5] & s->csr[7]; | |
105 | bool assert = false; | |
106 | ||
107 | s->csr[5] &= ~(CSR5_AIS | CSR5_NIS); | |
108 | ||
109 | if (ie & (CSR5_TI | CSR5_TU | CSR5_RI | CSR5_GTE | CSR5_ERI)) { | |
110 | s->csr[5] |= CSR5_NIS; | |
111 | } | |
112 | ||
113 | if (ie & (CSR5_LC | CSR5_GPI | CSR5_FBE | CSR5_LNF | CSR5_ETI | CSR5_RWT | | |
114 | CSR5_RPS | CSR5_RU | CSR5_UNF | CSR5_LNP_ANC | CSR5_TJT | | |
115 | CSR5_TPS)) { | |
116 | s->csr[5] |= CSR5_AIS; | |
117 | } | |
118 | ||
119 | assert = s->csr[5] & s->csr[7] & (CSR5_AIS | CSR5_NIS); | |
120 | trace_tulip_irq(s->csr[5], s->csr[7], assert ? "assert" : "deassert"); | |
121 | qemu_set_irq(s->irq, assert); | |
122 | } | |
123 | ||
124 | static bool tulip_rx_stopped(TULIPState *s) | |
125 | { | |
126 | return ((s->csr[5] >> CSR5_RS_SHIFT) & CSR5_RS_MASK) == CSR5_RS_STOPPED; | |
127 | } | |
128 | ||
129 | static void tulip_dump_tx_descriptor(TULIPState *s, | |
130 | struct tulip_descriptor *desc) | |
131 | { | |
132 | trace_tulip_descriptor("TX ", s->current_tx_desc, | |
133 | desc->status, desc->control >> 22, | |
134 | desc->control & 0x7ff, (desc->control >> 11) & 0x7ff, | |
135 | desc->buf_addr1, desc->buf_addr2); | |
136 | } | |
137 | ||
138 | static void tulip_dump_rx_descriptor(TULIPState *s, | |
139 | struct tulip_descriptor *desc) | |
140 | { | |
141 | trace_tulip_descriptor("RX ", s->current_rx_desc, | |
142 | desc->status, desc->control >> 22, | |
143 | desc->control & 0x7ff, (desc->control >> 11) & 0x7ff, | |
144 | desc->buf_addr1, desc->buf_addr2); | |
145 | } | |
146 | ||
147 | static void tulip_next_rx_descriptor(TULIPState *s, | |
148 | struct tulip_descriptor *desc) | |
149 | { | |
150 | if (desc->control & RDES1_RER) { | |
151 | s->current_rx_desc = s->csr[3]; | |
152 | } else if (desc->control & RDES1_RCH) { | |
153 | s->current_rx_desc = desc->buf_addr2; | |
154 | } else { | |
155 | s->current_rx_desc += sizeof(struct tulip_descriptor) + | |
156 | (((s->csr[0] >> CSR0_DSL_SHIFT) & CSR0_DSL_MASK) << 2); | |
157 | } | |
158 | s->current_rx_desc &= ~3ULL; | |
159 | } | |
160 | ||
161 | static void tulip_copy_rx_bytes(TULIPState *s, struct tulip_descriptor *desc) | |
162 | { | |
163 | int len1 = (desc->control >> RDES1_BUF1_SIZE_SHIFT) & RDES1_BUF1_SIZE_MASK; | |
164 | int len2 = (desc->control >> RDES1_BUF2_SIZE_SHIFT) & RDES1_BUF2_SIZE_MASK; | |
165 | int len; | |
166 | ||
167 | if (s->rx_frame_len && len1) { | |
168 | if (s->rx_frame_len > len1) { | |
169 | len = len1; | |
170 | } else { | |
171 | len = s->rx_frame_len; | |
172 | } | |
8ffb7265 | 173 | |
34ea023d SS |
174 | pci_dma_write(&s->dev, desc->buf_addr1, s->rx_frame + |
175 | (s->rx_frame_size - s->rx_frame_len), len); | |
176 | s->rx_frame_len -= len; | |
177 | } | |
178 | ||
179 | if (s->rx_frame_len && len2) { | |
180 | if (s->rx_frame_len > len2) { | |
181 | len = len2; | |
182 | } else { | |
183 | len = s->rx_frame_len; | |
184 | } | |
8ffb7265 | 185 | |
34ea023d SS |
186 | pci_dma_write(&s->dev, desc->buf_addr2, s->rx_frame + |
187 | (s->rx_frame_size - s->rx_frame_len), len); | |
188 | s->rx_frame_len -= len; | |
189 | } | |
190 | } | |
191 | ||
192 | static bool tulip_filter_address(TULIPState *s, const uint8_t *addr) | |
193 | { | |
194 | static const char broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
195 | bool ret = false; | |
196 | int i; | |
197 | ||
198 | for (i = 0; i < 16 && ret == false; i++) { | |
199 | if (!memcmp(&s->filter[i], addr, ETH_ALEN)) { | |
200 | ret = true; | |
201 | } | |
202 | } | |
203 | ||
204 | if (!memcmp(addr, broadcast, ETH_ALEN)) { | |
205 | return true; | |
206 | } | |
207 | ||
208 | if (s->csr[6] & (CSR6_PR | CSR6_RA)) { | |
209 | /* Promiscuous mode enabled */ | |
210 | s->rx_status |= RDES0_FF; | |
211 | return true; | |
212 | } | |
213 | ||
214 | if ((s->csr[6] & CSR6_PM) && (addr[0] & 1)) { | |
215 | /* Pass all Multicast enabled */ | |
216 | s->rx_status |= RDES0_MF; | |
217 | return true; | |
218 | } | |
219 | ||
220 | if (s->csr[6] & CSR6_IF) { | |
221 | ret ^= true; | |
222 | } | |
223 | return ret; | |
224 | } | |
225 | ||
226 | static ssize_t tulip_receive(TULIPState *s, const uint8_t *buf, size_t size) | |
227 | { | |
228 | struct tulip_descriptor desc; | |
229 | ||
230 | trace_tulip_receive(buf, size); | |
231 | ||
8ffb7265 PP |
232 | if (size < 14 || size > sizeof(s->rx_frame) - 4 |
233 | || s->rx_frame_len || tulip_rx_stopped(s)) { | |
34ea023d SS |
234 | return 0; |
235 | } | |
236 | ||
237 | if (!tulip_filter_address(s, buf)) { | |
238 | return size; | |
239 | } | |
240 | ||
241 | do { | |
242 | tulip_desc_read(s, s->current_rx_desc, &desc); | |
243 | tulip_dump_rx_descriptor(s, &desc); | |
244 | ||
245 | if (!(desc.status & RDES0_OWN)) { | |
246 | s->csr[5] |= CSR5_RU; | |
247 | tulip_update_int(s); | |
248 | return s->rx_frame_size - s->rx_frame_len; | |
249 | } | |
250 | desc.status = 0; | |
251 | ||
252 | if (!s->rx_frame_len) { | |
253 | s->rx_frame_size = size + 4; | |
254 | s->rx_status = RDES0_LS | | |
255 | ((s->rx_frame_size & RDES0_FL_MASK) << RDES0_FL_SHIFT); | |
256 | desc.status |= RDES0_FS; | |
257 | memcpy(s->rx_frame, buf, size); | |
258 | s->rx_frame_len = s->rx_frame_size; | |
259 | } | |
260 | ||
261 | tulip_copy_rx_bytes(s, &desc); | |
262 | ||
263 | if (!s->rx_frame_len) { | |
264 | desc.status |= s->rx_status; | |
265 | s->csr[5] |= CSR5_RI; | |
266 | tulip_update_int(s); | |
267 | } | |
268 | tulip_dump_rx_descriptor(s, &desc); | |
269 | tulip_desc_write(s, s->current_rx_desc, &desc); | |
270 | tulip_next_rx_descriptor(s, &desc); | |
271 | } while (s->rx_frame_len); | |
272 | return size; | |
273 | } | |
274 | ||
275 | static ssize_t tulip_receive_nc(NetClientState *nc, | |
276 | const uint8_t *buf, size_t size) | |
277 | { | |
278 | return tulip_receive(qemu_get_nic_opaque(nc), buf, size); | |
279 | } | |
280 | ||
34ea023d SS |
281 | static NetClientInfo net_tulip_info = { |
282 | .type = NET_CLIENT_DRIVER_NIC, | |
283 | .size = sizeof(NICState), | |
284 | .receive = tulip_receive_nc, | |
285 | }; | |
286 | ||
287 | static const char *tulip_reg_name(const hwaddr addr) | |
288 | { | |
289 | switch (addr) { | |
290 | case CSR(0): | |
291 | return "CSR0"; | |
292 | ||
293 | case CSR(1): | |
294 | return "CSR1"; | |
295 | ||
296 | case CSR(2): | |
297 | return "CSR2"; | |
298 | ||
299 | case CSR(3): | |
300 | return "CSR3"; | |
301 | ||
302 | case CSR(4): | |
303 | return "CSR4"; | |
304 | ||
305 | case CSR(5): | |
306 | return "CSR5"; | |
307 | ||
308 | case CSR(6): | |
309 | return "CSR6"; | |
310 | ||
311 | case CSR(7): | |
312 | return "CSR7"; | |
313 | ||
314 | case CSR(8): | |
315 | return "CSR8"; | |
316 | ||
317 | case CSR(9): | |
318 | return "CSR9"; | |
319 | ||
320 | case CSR(10): | |
321 | return "CSR10"; | |
322 | ||
323 | case CSR(11): | |
324 | return "CSR11"; | |
325 | ||
326 | case CSR(12): | |
327 | return "CSR12"; | |
328 | ||
329 | case CSR(13): | |
330 | return "CSR13"; | |
331 | ||
332 | case CSR(14): | |
333 | return "CSR14"; | |
334 | ||
335 | case CSR(15): | |
336 | return "CSR15"; | |
337 | ||
338 | default: | |
339 | break; | |
340 | } | |
341 | return ""; | |
342 | } | |
343 | ||
344 | static const char *tulip_rx_state_name(int state) | |
345 | { | |
346 | switch (state) { | |
347 | case CSR5_RS_STOPPED: | |
348 | return "STOPPED"; | |
349 | ||
350 | case CSR5_RS_RUNNING_FETCH: | |
351 | return "RUNNING/FETCH"; | |
352 | ||
353 | case CSR5_RS_RUNNING_CHECK_EOR: | |
354 | return "RUNNING/CHECK EOR"; | |
355 | ||
356 | case CSR5_RS_RUNNING_WAIT_RECEIVE: | |
357 | return "WAIT RECEIVE"; | |
358 | ||
359 | case CSR5_RS_SUSPENDED: | |
360 | return "SUSPENDED"; | |
361 | ||
362 | case CSR5_RS_RUNNING_CLOSE: | |
363 | return "RUNNING/CLOSE"; | |
364 | ||
365 | case CSR5_RS_RUNNING_FLUSH: | |
366 | return "RUNNING/FLUSH"; | |
367 | ||
368 | case CSR5_RS_RUNNING_QUEUE: | |
369 | return "RUNNING/QUEUE"; | |
370 | ||
371 | default: | |
372 | break; | |
373 | } | |
374 | return ""; | |
375 | } | |
376 | ||
377 | static const char *tulip_tx_state_name(int state) | |
378 | { | |
379 | switch (state) { | |
380 | case CSR5_TS_STOPPED: | |
381 | return "STOPPED"; | |
382 | ||
383 | case CSR5_TS_RUNNING_FETCH: | |
384 | return "RUNNING/FETCH"; | |
385 | ||
386 | case CSR5_TS_RUNNING_WAIT_EOT: | |
387 | return "RUNNING/WAIT EOT"; | |
388 | ||
389 | case CSR5_TS_RUNNING_READ_BUF: | |
390 | return "RUNNING/READ BUF"; | |
391 | ||
392 | case CSR5_TS_RUNNING_SETUP: | |
393 | return "RUNNING/SETUP"; | |
394 | ||
395 | case CSR5_TS_SUSPENDED: | |
396 | return "SUSPENDED"; | |
397 | ||
398 | case CSR5_TS_RUNNING_CLOSE: | |
399 | return "RUNNING/CLOSE"; | |
400 | ||
401 | default: | |
402 | break; | |
403 | } | |
404 | return ""; | |
405 | } | |
406 | ||
407 | static void tulip_update_rs(TULIPState *s, int state) | |
408 | { | |
409 | s->csr[5] &= ~(CSR5_RS_MASK << CSR5_RS_SHIFT); | |
410 | s->csr[5] |= (state & CSR5_RS_MASK) << CSR5_RS_SHIFT; | |
411 | trace_tulip_rx_state(tulip_rx_state_name(state)); | |
412 | } | |
413 | ||
414 | static uint16_t tulip_mdi_default[] = { | |
415 | /* MDI Registers 0 - 6, 7 */ | |
416 | 0x3100, 0xf02c, 0x7810, 0x0000, 0x0501, 0x4181, 0x0000, 0x0000, | |
417 | /* MDI Registers 8 - 15 */ | |
418 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, | |
419 | /* MDI Registers 16 - 31 */ | |
420 | 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, | |
421 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, | |
422 | }; | |
423 | ||
424 | /* Readonly mask for MDI (PHY) registers */ | |
425 | static const uint16_t tulip_mdi_mask[] = { | |
426 | 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000, | |
427 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, | |
428 | 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, | |
429 | 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, | |
430 | }; | |
431 | ||
432 | static uint16_t tulip_mii_read(TULIPState *s, int phy, int reg) | |
433 | { | |
434 | uint16_t ret = 0; | |
435 | if (phy == 1) { | |
436 | ret = tulip_mdi_default[reg]; | |
437 | } | |
438 | trace_tulip_mii_read(phy, reg, ret); | |
439 | return ret; | |
440 | } | |
441 | ||
442 | static void tulip_mii_write(TULIPState *s, int phy, int reg, uint16_t data) | |
443 | { | |
444 | trace_tulip_mii_write(phy, reg, data); | |
445 | ||
446 | if (phy != 1) { | |
447 | return; | |
448 | } | |
449 | ||
450 | tulip_mdi_default[reg] &= ~tulip_mdi_mask[reg]; | |
451 | tulip_mdi_default[reg] |= (data & tulip_mdi_mask[reg]); | |
452 | } | |
453 | ||
454 | static void tulip_mii(TULIPState *s) | |
455 | { | |
456 | uint32_t changed = s->old_csr9 ^ s->csr[9]; | |
457 | uint16_t data; | |
458 | int op, phy, reg; | |
459 | ||
460 | if (!(changed & CSR9_MDC)) { | |
461 | return; | |
462 | } | |
463 | ||
464 | if (!(s->csr[9] & CSR9_MDC)) { | |
465 | return; | |
466 | } | |
467 | ||
468 | s->mii_bitcnt++; | |
469 | s->mii_word <<= 1; | |
470 | ||
471 | if (s->csr[9] & CSR9_MDO && (s->mii_bitcnt < 16 || | |
472 | !(s->csr[9] & CSR9_MII))) { | |
473 | /* write op or address bits */ | |
474 | s->mii_word |= 1; | |
475 | } | |
476 | ||
477 | if (s->mii_bitcnt >= 16 && (s->csr[9] & CSR9_MII)) { | |
478 | if (s->mii_word & 0x8000) { | |
479 | s->csr[9] |= CSR9_MDI; | |
480 | } else { | |
481 | s->csr[9] &= ~CSR9_MDI; | |
482 | } | |
483 | } | |
484 | ||
485 | if (s->mii_word == 0xffffffff) { | |
486 | s->mii_bitcnt = 0; | |
487 | } else if (s->mii_bitcnt == 16) { | |
488 | op = (s->mii_word >> 12) & 0x0f; | |
489 | phy = (s->mii_word >> 7) & 0x1f; | |
490 | reg = (s->mii_word >> 2) & 0x1f; | |
491 | ||
492 | if (op == 6) { | |
493 | s->mii_word = tulip_mii_read(s, phy, reg); | |
494 | } | |
495 | } else if (s->mii_bitcnt == 32) { | |
496 | op = (s->mii_word >> 28) & 0x0f; | |
497 | phy = (s->mii_word >> 23) & 0x1f; | |
498 | reg = (s->mii_word >> 18) & 0x1f; | |
499 | data = s->mii_word & 0xffff; | |
500 | ||
501 | if (op == 5) { | |
502 | tulip_mii_write(s, phy, reg, data); | |
503 | } | |
504 | } | |
505 | } | |
506 | ||
507 | static uint32_t tulip_csr9_read(TULIPState *s) | |
508 | { | |
509 | if (s->csr[9] & CSR9_SR) { | |
510 | if (eeprom93xx_read(s->eeprom)) { | |
511 | s->csr[9] |= CSR9_SR_DO; | |
512 | } else { | |
513 | s->csr[9] &= ~CSR9_SR_DO; | |
514 | } | |
515 | } | |
516 | ||
517 | tulip_mii(s); | |
518 | return s->csr[9]; | |
519 | } | |
520 | ||
521 | static void tulip_update_ts(TULIPState *s, int state) | |
522 | { | |
523 | s->csr[5] &= ~(CSR5_TS_MASK << CSR5_TS_SHIFT); | |
524 | s->csr[5] |= (state & CSR5_TS_MASK) << CSR5_TS_SHIFT; | |
525 | trace_tulip_tx_state(tulip_tx_state_name(state)); | |
526 | } | |
527 | ||
528 | static uint64_t tulip_read(void *opaque, hwaddr addr, | |
529 | unsigned size) | |
530 | { | |
531 | TULIPState *s = opaque; | |
532 | uint64_t data = 0; | |
533 | ||
534 | switch (addr) { | |
535 | case CSR(9): | |
536 | data = tulip_csr9_read(s); | |
537 | break; | |
538 | ||
539 | case CSR(12): | |
540 | /* Fake autocompletion complete until we have PHY emulation */ | |
541 | data = 5 << CSR12_ANS_SHIFT; | |
542 | break; | |
543 | ||
544 | default: | |
545 | if (addr & 7) { | |
546 | qemu_log_mask(LOG_GUEST_ERROR, "%s: read access at unknown address" | |
547 | " 0x%"PRIx64"\n", __func__, addr); | |
548 | } else { | |
549 | data = s->csr[addr >> 3]; | |
550 | } | |
551 | break; | |
552 | } | |
553 | trace_tulip_reg_read(addr, tulip_reg_name(addr), size, data); | |
554 | return data; | |
555 | } | |
556 | ||
557 | static void tulip_tx(TULIPState *s, struct tulip_descriptor *desc) | |
558 | { | |
559 | if (s->tx_frame_len) { | |
560 | if ((s->csr[6] >> CSR6_OM_SHIFT) & CSR6_OM_MASK) { | |
561 | /* Internal or external Loopback */ | |
562 | tulip_receive(s, s->tx_frame, s->tx_frame_len); | |
8ffb7265 | 563 | } else if (s->tx_frame_len <= sizeof(s->tx_frame)) { |
34ea023d SS |
564 | qemu_send_packet(qemu_get_queue(s->nic), |
565 | s->tx_frame, s->tx_frame_len); | |
566 | } | |
567 | } | |
568 | ||
569 | if (desc->control & TDES1_IC) { | |
570 | s->csr[5] |= CSR5_TI; | |
571 | tulip_update_int(s); | |
572 | } | |
573 | } | |
574 | ||
8ffb7265 | 575 | static int tulip_copy_tx_buffers(TULIPState *s, struct tulip_descriptor *desc) |
34ea023d SS |
576 | { |
577 | int len1 = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK; | |
578 | int len2 = (desc->control >> TDES1_BUF2_SIZE_SHIFT) & TDES1_BUF2_SIZE_MASK; | |
579 | ||
8ffb7265 | 580 | if (s->tx_frame_len + len1 > sizeof(s->tx_frame)) { |
97d7fb5a PMD |
581 | qemu_log_mask(LOG_GUEST_ERROR, |
582 | "%s: descriptor overflow (ofs: %u, len:%d, size:%zu)\n", | |
583 | __func__, s->tx_frame_len, len1, sizeof(s->tx_frame)); | |
8ffb7265 PP |
584 | return -1; |
585 | } | |
34ea023d SS |
586 | if (len1) { |
587 | pci_dma_read(&s->dev, desc->buf_addr1, | |
588 | s->tx_frame + s->tx_frame_len, len1); | |
589 | s->tx_frame_len += len1; | |
590 | } | |
591 | ||
8ffb7265 | 592 | if (s->tx_frame_len + len2 > sizeof(s->tx_frame)) { |
97d7fb5a PMD |
593 | qemu_log_mask(LOG_GUEST_ERROR, |
594 | "%s: descriptor overflow (ofs: %u, len:%d, size:%zu)\n", | |
595 | __func__, s->tx_frame_len, len2, sizeof(s->tx_frame)); | |
8ffb7265 PP |
596 | return -1; |
597 | } | |
34ea023d SS |
598 | if (len2) { |
599 | pci_dma_read(&s->dev, desc->buf_addr2, | |
600 | s->tx_frame + s->tx_frame_len, len2); | |
601 | s->tx_frame_len += len2; | |
602 | } | |
603 | desc->status = (len1 + len2) ? 0 : 0x7fffffff; | |
8ffb7265 PP |
604 | |
605 | return 0; | |
34ea023d SS |
606 | } |
607 | ||
608 | static void tulip_setup_filter_addr(TULIPState *s, uint8_t *buf, int n) | |
609 | { | |
610 | int offset = n * 12; | |
611 | ||
612 | s->filter[n][0] = buf[offset]; | |
613 | s->filter[n][1] = buf[offset + 1]; | |
614 | ||
615 | s->filter[n][2] = buf[offset + 4]; | |
616 | s->filter[n][3] = buf[offset + 5]; | |
617 | ||
618 | s->filter[n][4] = buf[offset + 8]; | |
619 | s->filter[n][5] = buf[offset + 9]; | |
620 | ||
621 | trace_tulip_setup_filter(n, s->filter[n][5], s->filter[n][4], | |
622 | s->filter[n][3], s->filter[n][2], s->filter[n][1], s->filter[n][0]); | |
623 | } | |
624 | ||
625 | static void tulip_setup_frame(TULIPState *s, | |
626 | struct tulip_descriptor *desc) | |
627 | { | |
628 | uint8_t buf[4096]; | |
629 | int len = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK; | |
630 | int i; | |
631 | ||
632 | trace_tulip_setup_frame(); | |
633 | ||
634 | if (len == 192) { | |
635 | pci_dma_read(&s->dev, desc->buf_addr1, buf, len); | |
636 | for (i = 0; i < 16; i++) { | |
637 | tulip_setup_filter_addr(s, buf, i); | |
638 | } | |
639 | } | |
640 | ||
641 | desc->status = 0x7fffffff; | |
642 | ||
643 | if (desc->control & TDES1_IC) { | |
644 | s->csr[5] |= CSR5_TI; | |
645 | tulip_update_int(s); | |
646 | } | |
647 | } | |
648 | ||
649 | static void tulip_next_tx_descriptor(TULIPState *s, | |
650 | struct tulip_descriptor *desc) | |
651 | { | |
652 | if (desc->control & TDES1_TER) { | |
653 | s->current_tx_desc = s->csr[4]; | |
654 | } else if (desc->control & TDES1_TCH) { | |
655 | s->current_tx_desc = desc->buf_addr2; | |
656 | } else { | |
657 | s->current_tx_desc += sizeof(struct tulip_descriptor) + | |
658 | (((s->csr[0] >> CSR0_DSL_SHIFT) & CSR0_DSL_MASK) << 2); | |
659 | } | |
660 | s->current_tx_desc &= ~3ULL; | |
661 | } | |
662 | ||
663 | static uint32_t tulip_ts(TULIPState *s) | |
664 | { | |
665 | return (s->csr[5] >> CSR5_TS_SHIFT) & CSR5_TS_MASK; | |
666 | } | |
667 | ||
668 | static void tulip_xmit_list_update(TULIPState *s) | |
669 | { | |
8ffb7265 PP |
670 | #define TULIP_DESC_MAX 128 |
671 | uint8_t i = 0; | |
34ea023d SS |
672 | struct tulip_descriptor desc; |
673 | ||
674 | if (tulip_ts(s) != CSR5_TS_SUSPENDED) { | |
675 | return; | |
676 | } | |
677 | ||
8ffb7265 | 678 | for (i = 0; i < TULIP_DESC_MAX; i++) { |
34ea023d SS |
679 | tulip_desc_read(s, s->current_tx_desc, &desc); |
680 | tulip_dump_tx_descriptor(s, &desc); | |
681 | ||
682 | if (!(desc.status & TDES0_OWN)) { | |
683 | tulip_update_ts(s, CSR5_TS_SUSPENDED); | |
684 | s->csr[5] |= CSR5_TU; | |
685 | tulip_update_int(s); | |
686 | return; | |
687 | } | |
688 | ||
689 | if (desc.control & TDES1_SET) { | |
690 | tulip_setup_frame(s, &desc); | |
691 | } else { | |
692 | if (desc.control & TDES1_FS) { | |
693 | s->tx_frame_len = 0; | |
694 | } | |
695 | ||
8ffb7265 PP |
696 | if (!tulip_copy_tx_buffers(s, &desc)) { |
697 | if (desc.control & TDES1_LS) { | |
698 | tulip_tx(s, &desc); | |
699 | } | |
34ea023d SS |
700 | } |
701 | } | |
702 | tulip_desc_write(s, s->current_tx_desc, &desc); | |
703 | tulip_next_tx_descriptor(s, &desc); | |
704 | } | |
705 | } | |
706 | ||
707 | static void tulip_csr9_write(TULIPState *s, uint32_t old_val, | |
708 | uint32_t new_val) | |
709 | { | |
710 | if (new_val & CSR9_SR) { | |
711 | eeprom93xx_write(s->eeprom, | |
712 | !!(new_val & CSR9_SR_CS), | |
713 | !!(new_val & CSR9_SR_SK), | |
714 | !!(new_val & CSR9_SR_DI)); | |
715 | } | |
716 | } | |
717 | ||
718 | static void tulip_reset(TULIPState *s) | |
719 | { | |
720 | trace_tulip_reset(); | |
721 | ||
722 | s->csr[0] = 0xfe000000; | |
723 | s->csr[1] = 0xffffffff; | |
724 | s->csr[2] = 0xffffffff; | |
725 | s->csr[5] = 0xf0000000; | |
726 | s->csr[6] = 0x32000040; | |
727 | s->csr[7] = 0xf3fe0000; | |
728 | s->csr[8] = 0xe0000000; | |
729 | s->csr[9] = 0xfff483ff; | |
730 | s->csr[11] = 0xfffe0000; | |
731 | s->csr[12] = 0x000000c6; | |
732 | s->csr[13] = 0xffff0000; | |
733 | s->csr[14] = 0xffffffff; | |
734 | s->csr[15] = 0x8ff00000; | |
735 | } | |
736 | ||
737 | static void tulip_qdev_reset(DeviceState *dev) | |
738 | { | |
739 | PCIDevice *d = PCI_DEVICE(dev); | |
740 | TULIPState *s = TULIP(d); | |
741 | ||
742 | tulip_reset(s); | |
743 | } | |
744 | ||
745 | static void tulip_write(void *opaque, hwaddr addr, | |
746 | uint64_t data, unsigned size) | |
747 | { | |
748 | TULIPState *s = opaque; | |
749 | trace_tulip_reg_write(addr, tulip_reg_name(addr), size, data); | |
750 | ||
751 | switch (addr) { | |
752 | case CSR(0): | |
753 | s->csr[0] = data; | |
754 | if (data & CSR0_SWR) { | |
755 | tulip_reset(s); | |
756 | tulip_update_int(s); | |
757 | } | |
758 | break; | |
759 | ||
760 | case CSR(1): | |
761 | tulip_xmit_list_update(s); | |
762 | break; | |
763 | ||
764 | case CSR(2): | |
765 | qemu_flush_queued_packets(qemu_get_queue(s->nic)); | |
766 | break; | |
767 | ||
768 | case CSR(3): | |
769 | s->csr[3] = data & ~3ULL; | |
770 | s->current_rx_desc = s->csr[3]; | |
771 | qemu_flush_queued_packets(qemu_get_queue(s->nic)); | |
772 | break; | |
773 | ||
774 | case CSR(4): | |
775 | s->csr[4] = data & ~3ULL; | |
776 | s->current_tx_desc = s->csr[4]; | |
777 | tulip_xmit_list_update(s); | |
778 | break; | |
779 | ||
780 | case CSR(5): | |
781 | /* Status register, write clears bit */ | |
782 | s->csr[5] &= ~(data & (CSR5_TI | CSR5_TPS | CSR5_TU | CSR5_TJT | | |
783 | CSR5_LNP_ANC | CSR5_UNF | CSR5_RI | CSR5_RU | | |
784 | CSR5_RPS | CSR5_RWT | CSR5_ETI | CSR5_GTE | | |
785 | CSR5_LNF | CSR5_FBE | CSR5_ERI | CSR5_AIS | | |
786 | CSR5_NIS | CSR5_GPI | CSR5_LC)); | |
787 | tulip_update_int(s); | |
788 | break; | |
789 | ||
790 | case CSR(6): | |
791 | s->csr[6] = data; | |
792 | if (s->csr[6] & CSR6_SR) { | |
793 | tulip_update_rs(s, CSR5_RS_RUNNING_WAIT_RECEIVE); | |
794 | qemu_flush_queued_packets(qemu_get_queue(s->nic)); | |
795 | } else { | |
796 | tulip_update_rs(s, CSR5_RS_STOPPED); | |
797 | } | |
798 | ||
799 | if (s->csr[6] & CSR6_ST) { | |
800 | tulip_update_ts(s, CSR5_TS_SUSPENDED); | |
801 | tulip_xmit_list_update(s); | |
802 | } else { | |
803 | tulip_update_ts(s, CSR5_TS_STOPPED); | |
804 | } | |
805 | break; | |
806 | ||
807 | case CSR(7): | |
808 | s->csr[7] = data; | |
809 | tulip_update_int(s); | |
810 | break; | |
811 | ||
812 | case CSR(8): | |
813 | s->csr[9] = data; | |
814 | break; | |
815 | ||
816 | case CSR(9): | |
817 | tulip_csr9_write(s, s->csr[9], data); | |
818 | /* don't clear MII read data */ | |
819 | s->csr[9] &= CSR9_MDI; | |
820 | s->csr[9] |= (data & ~CSR9_MDI); | |
821 | tulip_mii(s); | |
822 | s->old_csr9 = s->csr[9]; | |
823 | break; | |
824 | ||
825 | case CSR(10): | |
826 | s->csr[10] = data; | |
827 | break; | |
828 | ||
829 | case CSR(11): | |
830 | s->csr[11] = data; | |
831 | break; | |
832 | ||
833 | case CSR(12): | |
834 | /* SIA Status register, some bits are cleared by writing 1 */ | |
835 | s->csr[12] &= ~(data & (CSR12_MRA | CSR12_TRA | CSR12_ARA)); | |
836 | break; | |
837 | ||
838 | case CSR(13): | |
839 | s->csr[13] = data; | |
840 | break; | |
841 | ||
842 | case CSR(14): | |
843 | s->csr[14] = data; | |
844 | break; | |
845 | ||
846 | case CSR(15): | |
847 | s->csr[15] = data; | |
848 | break; | |
849 | ||
850 | default: | |
851 | qemu_log_mask(LOG_GUEST_ERROR, "%s: write to CSR at unknown address " | |
852 | "0x%"PRIx64"\n", __func__, addr); | |
853 | break; | |
854 | } | |
855 | } | |
856 | ||
857 | static const MemoryRegionOps tulip_ops = { | |
858 | .read = tulip_read, | |
859 | .write = tulip_write, | |
860 | .endianness = DEVICE_LITTLE_ENDIAN, | |
861 | .impl = { | |
862 | .min_access_size = 4, | |
863 | .max_access_size = 4, | |
864 | }, | |
865 | }; | |
866 | ||
867 | static void tulip_idblock_crc(TULIPState *s, uint16_t *srom) | |
868 | { | |
869 | int word, n; | |
870 | int bit; | |
871 | unsigned char bitval, crc; | |
872 | const int len = 9; | |
873 | n = 0; | |
874 | crc = -1; | |
875 | ||
876 | for (word = 0; word < len; word++) { | |
877 | for (bit = 15; bit >= 0; bit--) { | |
878 | if ((word == (len - 1)) && (bit == 7)) { | |
879 | /* | |
880 | * Insert the correct CRC result into input data stream | |
881 | * in place. | |
882 | */ | |
883 | srom[len - 1] = (srom[len - 1] & 0xff00) | (unsigned short)crc; | |
884 | break; | |
885 | } | |
886 | n++; | |
887 | bitval = ((srom[word] >> bit) & 1) ^ ((crc >> 7) & 1); | |
888 | crc = crc << 1; | |
889 | if (bitval == 1) { | |
890 | crc ^= 6; | |
891 | crc |= 0x01; | |
892 | } | |
893 | } | |
894 | } | |
895 | } | |
896 | ||
897 | static uint16_t tulip_srom_crc(TULIPState *s, uint8_t *eeprom, size_t len) | |
898 | { | |
899 | unsigned long crc = 0xffffffff; | |
900 | unsigned long flippedcrc = 0; | |
901 | unsigned char currentbyte; | |
902 | unsigned int msb, bit, i; | |
903 | ||
904 | for (i = 0; i < len; i++) { | |
905 | currentbyte = eeprom[i]; | |
906 | for (bit = 0; bit < 8; bit++) { | |
907 | msb = (crc >> 31) & 1; | |
908 | crc <<= 1; | |
909 | if (msb ^ (currentbyte & 1)) { | |
910 | crc ^= 0x04c11db6; | |
911 | crc |= 0x00000001; | |
912 | } | |
913 | currentbyte >>= 1; | |
914 | } | |
915 | } | |
916 | ||
917 | for (i = 0; i < 32; i++) { | |
918 | flippedcrc <<= 1; | |
919 | bit = crc & 1; | |
920 | crc >>= 1; | |
921 | flippedcrc += bit; | |
922 | } | |
923 | return (flippedcrc ^ 0xffffffff) & 0xffff; | |
924 | } | |
925 | ||
926 | static const uint8_t eeprom_default[128] = { | |
927 | 0x3c, 0x10, 0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, | |
928 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
929 | 0x56, 0x08, 0x04, 0x01, 0x00, 0x80, 0x48, 0xb3, | |
930 | 0x0e, 0xa7, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x08, | |
931 | 0x01, 0x8d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x78, | |
932 | 0xe0, 0x01, 0x00, 0x50, 0x00, 0x18, 0x00, 0x00, | |
933 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
934 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
935 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
936 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
937 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
938 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x6b, | |
939 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, | |
940 | 0x48, 0xb3, 0x0e, 0xa7, 0x40, 0x00, 0x00, 0x00, | |
941 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
942 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
943 | }; | |
944 | ||
945 | static void tulip_fill_eeprom(TULIPState *s) | |
946 | { | |
947 | uint16_t *eeprom = eeprom93xx_data(s->eeprom); | |
948 | memcpy(eeprom, eeprom_default, 128); | |
949 | ||
950 | /* patch in our mac address */ | |
951 | eeprom[10] = cpu_to_le16(s->c.macaddr.a[0] | (s->c.macaddr.a[1] << 8)); | |
952 | eeprom[11] = cpu_to_le16(s->c.macaddr.a[2] | (s->c.macaddr.a[3] << 8)); | |
953 | eeprom[12] = cpu_to_le16(s->c.macaddr.a[4] | (s->c.macaddr.a[5] << 8)); | |
954 | tulip_idblock_crc(s, eeprom); | |
955 | eeprom[63] = cpu_to_le16(tulip_srom_crc(s, (uint8_t *)eeprom, 126)); | |
956 | } | |
957 | ||
958 | static void pci_tulip_realize(PCIDevice *pci_dev, Error **errp) | |
959 | { | |
960 | TULIPState *s = DO_UPCAST(TULIPState, dev, pci_dev); | |
961 | uint8_t *pci_conf; | |
962 | ||
963 | pci_conf = s->dev.config; | |
964 | pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ | |
965 | ||
966 | s->eeprom = eeprom93xx_new(&pci_dev->qdev, 64); | |
967 | tulip_fill_eeprom(s); | |
968 | ||
969 | memory_region_init_io(&s->io, OBJECT(&s->dev), &tulip_ops, s, | |
970 | "tulip-io", 128); | |
971 | ||
972 | memory_region_init_io(&s->memory, OBJECT(&s->dev), &tulip_ops, s, | |
973 | "tulip-mem", 128); | |
974 | ||
975 | pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); | |
976 | pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->memory); | |
977 | ||
978 | s->irq = pci_allocate_irq(&s->dev); | |
979 | ||
980 | qemu_macaddr_default_if_unset(&s->c.macaddr); | |
981 | ||
982 | s->nic = qemu_new_nic(&net_tulip_info, &s->c, | |
983 | object_get_typename(OBJECT(pci_dev)), | |
984 | pci_dev->qdev.id, s); | |
985 | qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); | |
986 | } | |
987 | ||
988 | static void pci_tulip_exit(PCIDevice *pci_dev) | |
989 | { | |
990 | TULIPState *s = DO_UPCAST(TULIPState, dev, pci_dev); | |
991 | ||
992 | qemu_del_nic(s->nic); | |
993 | qemu_free_irq(s->irq); | |
994 | eeprom93xx_free(&pci_dev->qdev, s->eeprom); | |
995 | } | |
996 | ||
997 | static void tulip_instance_init(Object *obj) | |
998 | { | |
999 | PCIDevice *pci_dev = PCI_DEVICE(obj); | |
1000 | TULIPState *d = DO_UPCAST(TULIPState, dev, pci_dev); | |
1001 | ||
1002 | device_add_bootindex_property(obj, &d->c.bootindex, | |
1003 | "bootindex", "/ethernet-phy@0", | |
40c2281c | 1004 | &pci_dev->qdev); |
34ea023d SS |
1005 | } |
1006 | ||
1007 | static Property tulip_properties[] = { | |
1008 | DEFINE_NIC_PROPERTIES(TULIPState, c), | |
1009 | DEFINE_PROP_END_OF_LIST(), | |
1010 | }; | |
1011 | ||
1012 | static void tulip_class_init(ObjectClass *klass, void *data) | |
1013 | { | |
1014 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1015 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
1016 | ||
1017 | k->realize = pci_tulip_realize; | |
1018 | k->exit = pci_tulip_exit; | |
1019 | k->vendor_id = PCI_VENDOR_ID_DEC; | |
1020 | k->device_id = PCI_DEVICE_ID_DEC_21143; | |
1021 | k->subsystem_vendor_id = 0x103c; | |
1022 | k->subsystem_id = 0x104f; | |
1023 | k->class_id = PCI_CLASS_NETWORK_ETHERNET; | |
1024 | dc->vmsd = &vmstate_pci_tulip; | |
4f67d30b | 1025 | device_class_set_props(dc, tulip_properties); |
34ea023d SS |
1026 | dc->reset = tulip_qdev_reset; |
1027 | set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); | |
1028 | } | |
1029 | ||
1030 | static const TypeInfo tulip_info = { | |
1031 | .name = TYPE_TULIP, | |
1032 | .parent = TYPE_PCI_DEVICE, | |
1033 | .instance_size = sizeof(TULIPState), | |
1034 | .class_init = tulip_class_init, | |
1035 | .instance_init = tulip_instance_init, | |
1036 | .interfaces = (InterfaceInfo[]) { | |
1037 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, | |
1038 | { }, | |
1039 | }, | |
1040 | }; | |
1041 | ||
1042 | static void tulip_register_types(void) | |
1043 | { | |
1044 | type_register_static(&tulip_info); | |
1045 | } | |
1046 | ||
1047 | type_init(tulip_register_types) |