VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevDP8390.cpp@ 107876

Last change on this file since 107876 was 107853, checked in by vboxsync, 5 weeks ago

DevDP8390: Reduced Parfait warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 197.8 KB
Line 
1/* $Id: DevDP8390.cpp 107853 2025-01-18 16:36:25Z vboxsync $ */
2/** @file
3 * DevDP8390 - National Semiconductor DP8390-based Ethernet Adapter Emulation.
4 */
5
6/*
7 * Copyright (C) 2022-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_dev_dp8390 NatSemi DP8390-Based Ethernet NIC Emulation.
29 *
30 * This software was written based on the following documents:
31 *
32 * - National Semiconductor DP8390/NS32490 Network Interface Controller,
33 * 1986
34 * - National Semiconductor DP8390D/NS32490D NIC Network Interface
35 * Controller datasheet, July 1995
36 * - National Semiconductor Application Note 729, DP839EB-ATN IBM PC-AT
37 * Compatible DP83901 SNIC Serial Network Interface Controller
38 * Evaluation Board, 1993
39 * - National Semiconductor Application Note 842, The Design and Operation
40 * of a Low Cost, 8-Bit PC-XT Compatible Ethernet Adapter Using
41 * the DP83902, May 1993
42 * - National Semiconductor Application Note 858, Guide to Loopback Using
43 * the DP8390 Chip Set, October 1992
44 * - National Semiconductor Application Note 875, DP83905EB-AT AT/LANTIC
45 * Evaluation Board, June 1993
46 * - Western Digital WD83C584 Bus Interface Controller Device datasheet,
47 * October 29, 1990
48 * - Western Digital WD83C690 Ethernet LAN Controller datasheet,
49 * November 2, 1990
50 * - 3Com EtherLink II Adapter Technical Reference Manual,
51 * March 1991
52 *
53 * This emulation is compatible with drivers for:
54 * - Novell/Eagle/Anthem NE1000 (8-bit)
55 * - Novell/Eagle/Anthem NE2000 (16-bit)
56 * - Western Digital/SMC WD8003E (8-bit)
57 * - Western Digital/SMC WD8013EBT (16-bit)
58 * - 3Com EtherLink II 3C503 (8-bit)
59 *
60 *
61 * The National Semiconductor DP8390 was an early (circa 1986) low-cost
62 * Ethernet controller, typically accompanied by the DP8391 Serial Network
63 * Interface and the DP8392 Coaxial Transceiver Interface.
64 *
65 * Due to its relatively low cost, the DP8390 NIC was chosen for several
66 * very widespread early PC Ethernet designs, namely the Novell NE1000/NE2000,
67 * Western Digital (later SMC) WD8003 EtherCard Plus, and 3Com EtherLink II.
68 * The popularity of these cards, especially the NE2000, in turn spawned
69 * a bevy of compatible chips from National Semiconductor and many others.
70 *
71 * All common DP8390-based cards have onboard memory. The initial WD8003E and
72 * NE1000 cards have one 8Kx8 SRAM; 16-bit cards like WD8013E or NE2000 have
73 * two 8Kx8 SRAMs wired in 8Kx16 configuration to enable 16-bit wide transfers.
74 * The DP8390 can address up to 64K or local memory and uses "Local DMA"
75 * (similar to bus mastering) to access it. Some newer cards had 32K or more
76 * onboard RAM. Note that an NE2000 in 8-bit mode can only address 8K local
77 * memory, effectively reverting to an NE1000.
78 *
79 * The DP8390 uses "Remote DMA" to move data between local memory and the host
80 * system. Remote DMA is quite similar to 8237-style third party DMA, except
81 * the DMA controller is on the DP8390 chip in this case.
82 *
83 * The DP8390 has a control bit (DCR.WTS) which selects whether all DMA (both
84 * Local and Remote) transfers are 8-bit or 16-bit. Word-wide transfers can
85 * generally only be used on a 16-bit card in a 16-bit slot, because only then
86 * can the host drive 16-bit I/O cycles to the data ports. That is why
87 * an NE2000 in an 8-bit slot can only use half of its local RAM -- remote DMA
88 * simply cannot access half of the 8Kx16 SRAM.
89 *
90 * The DP8390 maps its internal registers as sixteen 8-bit wide I/O ports.
91 * There are four register pages, selectable through the Command Register (CR)
92 * which is accessible at offset 0 in all pages.
93 *
94 * The NE1000/NE2000 cards only use I/O and IRQ resources, not memory
95 * or DMA. In contrast, the Western Digital cards use memory-mapped buffers.
96 * Later AT/LANTIC (DP83905) based NE2000-compatible cards can optionally
97 * use memory as well. The 3Com EtherLink II (3C503) uses a custom gate array
98 * in addition to the DP8390 and can use programmed I/O, 8237 DMA, as well
99 * as optional direct memory mapping.
100 *
101 * Address decoding is typically incomplete, which causes the buffer RAM and
102 * possibly PROM to be aliased multiple times in the DP8390's address space.
103 *
104 * Buffer overflow handling is slightly tricky. The DP8390 assumes that if
105 * the receiver is enabled, there is space for at least one page (256 bytes).
106 * Once it fills up the page and advances the CURR pointer, the DP8390 checks
107 * whether CURR equals BNRY and if so, triggers an overflow condition. Note
108 * that after the NIC is initialized, CURR *will* normally equal BNRY, with
109 * both pointing at the beginning of the receive ring (PSTART). An overflow
110 * is only triggered when CURR equals BNRY right after advancing.
111 *
112 * The documentation of the Send Packet command mentions that when CRDA crosses
113 * the PSTOP register, the current remote DMA address (i.e. CRDA) is set to
114 * the PSTART value, which is rather convenient when reading received packets
115 * out of the ring buffer using remote DMA. The documentation does not mention
116 * that the same logic applies for all remote DMA reads, a feature that several
117 * NE1000/NE2000 drivers (packet drivers, Novell ODI) rely on. This is logical,
118 * because reading out of the receive ring buffer address range always implies
119 * reading received packets, and then the PSTOP->PSTART wraparound becomes
120 * desirable. It is unclear whether the same wraparound handling also applies
121 * for remote DMA writes within the receive ring buffer.
122 *
123 * The documentation is not very clear on how the CRDA register is managed.
124 * One might be led to believe that starting remote DMA copies the remote DMA
125 * start address (i.e. RSAR) to the CRDA register. However, the NE1000 ODI
126 * driver for OS/2 1.0 (NE1000.SYS from early 1988) relies on restarting remote
127 * DMA and continuing where it left off. The DP8390D datasheet only mentions
128 * this in a passing fashion at the end of the "Remote Write with High Speed
129 * Buses" section, saying that if a dummy remote read is executed before a
130 * remote write, RSAR can be set up for the dummy read such that the CRDA
131 * register contains the desired value for the following write.
132 *
133 * Conversely, it is not spelled out that writing RSAR also updates CRDA, but
134 * at least Novell's NE2000 ODI driver v2.12 is known to rely on this behavior
135 * and checks that a write to RSAR is reflected in CRDA.
136 *
137 * Loopback operation is limited in the DP8390. Because it is a half-duplex
138 * device, it cannot truly transmit and receive simultaneously. When loopback
139 * is in effect, the received data is *not* written into memory. Only the last
140 * few bytes of the packet are visible in the FIFO.
141 *
142 * Likewise due to its half-duplex nature, the CRC circuitry during loopback
143 * works either only on the transmit side (FCS is generated but not checked)
144 * or the receive side (FCS is checked but not generated).
145 *
146 * The loopback behavior is even stranger when DCR.WTS is set to enabled 16-bit
147 * DMA transfers. Even though the chip reads 16 bits at a time, only 8 bits are
148 * actually transmitted; the DCR.BOS bit determines whether the low or high
149 * 8 bits of each words are transmitted. As a consequence, the programmed length
150 * of the transmit is also halved.
151 *
152 * Because loopback operation is so different from normal send/receive, loopback
153 * packets are not run through the normal receive path and are treated specially
154 * instead. The WD and especially 3C503 diagnostics exercise the loopback
155 * functionality fairly thoroughly.
156 *
157 *
158 * NE1000 and NE2000
159 * -----------------
160 *
161 * Common NE1000/NE2000 configurations in Novell drivers:
162 * I/O Base = 300h, IRQ = 3 (default)
163 * I/O Base = 320h, IRQ = 2
164 * I/O Base = 340h, IRQ = 4
165 * I/O Base = 360h, IRQ = 5
166 * The I/O base can be set to 300h/320h/340h/360h; the IRQ to 2, 3, 4, 5.
167 * No memory or DMA is used.
168 *
169 * The NE1000/NE2000 adds a data register and a reset register to the I/O
170 * space. A PROM containing the node address is mapped into the DP8390's local
171 * address space.
172 *
173 * The mapping of the 32x8 PROM on an NE2000 card is quite non-obvious but
174 * fortunately well explained in the AN-729 Application Note. Address lines
175 * A4-A1 of the internal bus are connected to lines A3-A0 of the PROM
176 * (enabling 16 distinct bytes of the 32-byte PROM to be addressed). However,
177 * the negated EN16 signal, which is active when the NE2000 is in a 16-bit
178 * slot, is connected to the PROM's address line A4. That means an NE2000 in
179 * a 16-bit slot reads different PROM bytes than when the same card is in an
180 * 8-bit slot. The PROM is structured such that an NE2000 in an 8-bit slot
181 * reads a 'BB' signature (same as NE1000) at PROM offset 1Eh/1Fh, while
182 * an NE2000 in a 16-bit slot returns a 'WW' signature from PROM offset
183 * 0Eh/0Fh instead.
184 *
185 * The original NE1000 boards Assy. #950-054401 actually only had 6 bytes of
186 * MAC address in the PROM, the rest was unused (0FFh). Software supporting the
187 * NE1000 thus should not examine the PROM contents beyond the first 6 bytes.
188 *
189 * Novell's old OUI was 00:00:D8 but drivers are not known to check for it.
190 *
191 * Newer DP83905 AT/LANTIC based NE2000plus cards were optionally capable of
192 * using shared RAM in a manner very similar to the WD8003/WD8013.
193 *
194 *
195 * WD8003 and WD8013 EtherCard Plus
196 * --------------------------------
197 *
198 * Common WD8013 configurations:
199 * I/O Base = 280h, IRQ = 3, RAM D000-D3FF (default)
200 * I/O Base = 330h, IRQ = 10, RAM CC00-CFFF
201 * I/O Base = 240h, IRQ/RAM soft-configurable
202 * The I/O base can be set anywhere in the 2xxh-3xxh range in 20h increments.
203 * The IRQs available on a WD8013 are 2, 3, 4, 5, 7, 10, 11, 15. The shared
204 * RAM can be anywhere between 80000h (512K) to FFC000h (16M-16K) in 16K
205 * increments.
206 *
207 * The Western Digital WD8003E appeared at around the same time as Novell's
208 * NE1000 (1987). It is likewise a short 8-bit ISA card with 8Kx8 onboard
209 * SRAM. The major difference is that rather than using remote DMA to move
210 * data between the host and local RAM, the WD8003 directly mapps the onboard
211 * memory to the host's address space (often called shared memory). A later
212 * 16-bit WD8013 model used 8Kx16 SRAM, and there were follow-on WD8003 models
213 * with 16K or 32K local RAM.
214 *
215 * Instead of mapping the PROM into the DP8390's local address space, the
216 * WD8003/WD8013 exposes the node address through the I/O space; the DP8390's
217 * local address space only contains buffer RAM.
218 *
219 * The WD8003 cannot use remote DMA at all; the host must use shared memory.
220 * Remote DMA can be programmed but there is no way to trigger RDMA transfers.
221 *
222 * Western Digital's brand name for WD8003/WD8013 was EtherCard. Circa 1991,
223 * WD sold the networking business to SMC; SMC continued to sell and further
224 * develop the cards under the Elite brand name, also designated as the
225 * SMC8000 series.
226 *
227 * The original WD8003E/EBT/WT uses very simple glue logic around the DP8390
228 * and must be configured through jumpers. Newer WD8003EB/EP/EW/W/WC uses an
229 * interface chip (WD83C583, WD83C584, or later) with an EEPROM and can be
230 * configured through a software utility.
231 *
232 * Similarly the 16-bit WD8013EBT is configured only though jumpers, while
233 * the newer WD8013EB/W/EW/EWC/WC/EPC are software configurable.
234 *
235 * The "Board ID" byte (at offset 6 in the PROM) is used to distinguish
236 * between the various models.
237 *
238 * Newer WD cards use the WD83C690 controller rather than DP8390. The
239 * WD83C690 is close enough to DP8390 that old WD drivers should work with
240 * it, but it has a number of differences. It has no support for Remote DMA
241 * whatsoever, and does not implement multicast filtering.
242 *
243 * The WD83C690 also handles receive buffer overflows somewhat differently;
244 * the DP8390 never fills the last remaining buffer page, meaning that
245 * CURR=BNRY indicates an empty buffer while CURR=BNRY-1 means buffer full.
246 * The WD83C690 can fill all pages and decides whether it is full or empty
247 * based on whether CURR or BNRY was changed more recently.
248 *
249 * Old Western Digital utilities/drivers may require the card to have WD's
250 * old OUI of 00:00:0C and refuse to recognize the hardware otherwise.
251 *
252 * The emulation passes WD diagnostics with no errors (DIAGNOSE.EXE Ver 1.11,
253 * dated 12/12/1989).
254 *
255 *
256 * 3C503 EtherLink II
257 * ------------------
258 *
259 * Common 3C503 configurations in Novell drivers:
260 * I/O Base = 300h, IRQ = 3 (default)
261 * The I/O base can be set via jumpers to 2E0h, 2A0h, 280h, 250h, 350h, 330h,
262 * 310h, or 300h (default). The ROM/RAM can be optionally mapped to one of
263 * DC000-DFFFF, D8000-DBFFF, CC000-CFFFF, or C8000-CBFFF, again configured
264 * through jumpers. The available IRQs are 2, 3, 4, or 5, and DRQs 1, 2, or 3,
265 * both soft-configurable (no IRQ/DRQ jumpers).
266 *
267 * Yet another design based on the DP8390 was the 3Com 3C503 EtherLink II,
268 * available sometime in 1988. Unlike Novell and WD, 3Com added a custom
269 * host interface ASIC ("Gate Array") which handles all transfers to and from
270 * the 8Kx8 onboard SRAM. The 3C503 can map the card's local RAM directly
271 * into the host's address space, alternatively software can use either PIO
272 * or 8-bit DMA to transfer data.
273 *
274 * For reasons that are not entirely clear, 3Com decided that the Remote DMA
275 * implementation on the DP3890 (successfully used by the NE1000/NE2000) was
276 * too buggy and the Gate Array essentially duplicates the Remote DMA
277 * functionality, while also adding 8327 style DMA support (like the DP839EB
278 * had) and optional shared RAM.
279 *
280 * Just like the NE1000/NE2000 and WD8003/WD8013, the 3C503 exists in an
281 * 8-bit variant (EtherLink II) and a 16-bit variant (EtherLink II/16),
282 * although both types are called 3C503.
283 *
284 * Since the 3C503 does not require shared RAM to operate, 3Com decided to
285 * use a single memory mapping for both a boot ROM (if present) and shared
286 * RAM. It is possible to boot from the ROM utilizing PIO or DMA for data
287 * transfers, and later switch to shared RAM. However, 3Com needed to add
288 * a hack for warm boot; the Vector Pointer Registers (VPTR0/1/2) contain
289 * a 20-bit address and the Gate Array monitors the ISA bus for a read cycle
290 * to that address. When a read cycle from the VPTR address occurs, the
291 * memory mapping is switched from RAM to ROM. The VPTR registers are meant
292 * to be programmed with the warm boot vector (often F000:FFF0 or FFFF0h).
293 *
294 * Some UNIX 3C503 drivers may require the card to have 3Com's old OUI
295 * of 02:60:8C and refuse to detect the hardware otherwise. Likewise the
296 * 3C503 diagnostics fail if the OUI is not 3Com's.
297 *
298 * The emulation passes 3Com diagnostics with flying colors (3C503.EXE Version
299 * 1.5, dated 11/26/1991).
300 *
301 *
302 * Linux Drivers
303 *
304 * The DP8390 driver (shared by NE1000/NE2000, WD8003/WD8013, and 3C503 drivers)
305 * in Linux has severe bugs in the receive path. The driver clears receive
306 * interrupts *after* going through the receive ring; that causes it to race
307 * against the DP8390 chip and sometimes dismiss receive interrupts without
308 * handling them. The driver also only receives at most 9 packets at a time,
309 * which again can cause already received packets to be "hanging" in the receive
310 * queue without the driver processing them.
311 * In addition, prior to Linux 1.3.47, the driver incorrectly cleared the
312 * overflow warning interrupt after any receive, causing it to potentially
313 * miss overflow interrupts.
314 *
315 * The above bugs cause received packets to be lost or retransmitted by sender,
316 * causing major TCP/IP performance issues when the DP8390 receives packets
317 * very quickly. Other operating systems do not exhibit these bugs.
318 *
319 *
320 * BSD Drivers
321 *
322 * For reasons that are not obvious, BSD drivers have configuration defaults far
323 * off from the hardware defaults. For NE2000 (ne1), it is I/O base 300h and
324 * IRQ 10. For WD8003E (we0), it is I/O base 280h, IRQ 9, memory D0000-D1FFF.
325 * For 3C503 (ec0), it is I/O base 250h, IRQ 9, memory D8000-D9FFF (no DMA).
326 *
327 * The resource assigments are difficult to configure (sometimes impossible on
328 * installation CDs) and the high IRQs may clash with PCI devices.
329 *
330 */
331
332
333/*********************************************************************************************************************************
334* Header Files *
335*********************************************************************************************************************************/
336#define LOG_GROUP LOG_GROUP_DEV_DP8390
337#include <VBox/vmm/pdmdev.h>
338#include <VBox/vmm/pdmnetifs.h>
339#include <VBox/vmm/pgm.h>
340#include <VBox/version.h>
341#include <iprt/asm.h>
342#include <iprt/assert.h>
343#include <iprt/critsect.h>
344#include <iprt/net.h>
345#include <iprt/string.h>
346#include <iprt/time.h>
347#ifdef IN_RING3
348# include <iprt/mem.h>
349# include <iprt/semaphore.h>
350# include <iprt/uuid.h>
351#endif
352
353#include "VBoxDD.h"
354
355
356/*********************************************************************************************************************************
357* Defined Constants And Macros *
358*********************************************************************************************************************************/
359
360#define DPNIC_SAVEDSTATE_VERSION 1
361
362/** Maximum number of times we report a link down to the guest (failure to send frame) */
363#define DPNIC_MAX_LINKDOWN_REPORTED 3
364
365/** Maximum number of times we postpone restoring a link that is temporarily down. */
366#define DPNIC_MAX_LINKRST_POSTPONED 3
367
368/** Maximum frame size we handle */
369#define MAX_FRAME 1536
370
371/* Size of the local RAM. */
372#define DPNIC_MEM_SIZE 16384u
373
374#define DPNIC_MEM_MASK (DPNIC_MEM_SIZE - 1)
375
376/* Although it is a 16-bit adapter, the EtherLink II only supports 8-bit DMA
377 * and therefore DMA channels 1 to 3 are available.
378 */
379#define ELNKII_MIN_VALID_DMA 1
380#define ELNKII_MAX_VALID_DMA 3
381
382/* EtherLink II Gate Array revision. */
383#define ELNKII_GA_REV 1
384
385
386/*********************************************************************************************************************************
387* Structures and Typedefs *
388*********************************************************************************************************************************/
389
390
391/**
392 * Emulated device types.
393 */
394enum DP8390_DEVICE_TYPE
395{
396 DEV_NE1000 = 0, /* Novell NE1000 compatible (8-bit). */
397 DEV_NE2000 = 1, /* Novell NE2000 compatible (16-bit). */
398 DEV_WD8003 = 2, /* Western Digital WD8003 EtherCard Plus compatible (8-bit). */
399 DEV_WD8013 = 3, /* Western Digital WD8013 EtherCard Plus compatible (16-bit). */
400 DEV_3C503 = 4 /* 3Com 3C503 EtherLink II compatible. */
401};
402
403/** WD8003/WD80013 specific register offsets. */
404#define WDR_CTRL1 0 /* Control register 1. */
405#define WDR_ATDET 1 /* 16-bit slot detect. */
406#define WDR_IOBASE 2 /* I/O base register. */
407#define WDR_CTRL2 5 /* Control register 2. */
408#define WDR_JP 6 /* Jumper settings. */
409#define WDR_PROM 8 /* PROM offset in I/O space. */
410
411/** WD8013 Control Register 1. */
412typedef struct WD_CTRL1 {
413 uint8_t A13_18 : 6; /* Shared memory decoding A13-A18. */
414 uint8_t MEME : 1; /* Enable memory access. */
415 uint8_t RESET : 1; /* Reset NIC core. */
416} WD_CTRL1;
417AssertCompile(sizeof(WD_CTRL1) == sizeof(uint8_t));
418
419/** WD8013 Control Register 2. */
420typedef struct WD_CTRL2 {
421 uint8_t A19_23 : 5; /* Shared memory decoding A19-A23. */
422 uint8_t res : 1; /* Reserved. */
423 uint8_t MEMW : 1; /* Memory width (16-bit wide if set). */
424 uint8_t M16 : 1; /* Allow 16-bit host memory cycles if set. */
425} WD_CTRL2;
426AssertCompile(sizeof(WD_CTRL2) == sizeof(uint8_t));
427
428
429/** 3C503 EtherLink II specific register offsets. */
430#define GAR_PSTR 0
431#define GAR_PSPR 1
432#define GAR_DQTR 2
433#define GAR_R_BCFR 3
434#define GAR_R_PCFR 4
435#define GAR_GACFR 5
436#define GAR_GACR 6
437#define GAR_STREG 7
438#define GAR_IDCFR 8
439#define GAR_DAMSB 9
440#define GAR_DALSB 10
441#define GAR_VPTR2 11
442#define GAR_VPTR1 12
443#define GAR_VPTR0 13
444#define GAR_RFMSB 14
445#define GAR_RFLSB 15
446
447/** 3C503 EtherLink II Gate Array registers. */
448
449/** Gate Array DRQ Timer Register. */
450typedef struct EL_DQTR {
451 uint8_t tb : 5; /* Timer bits; should be multiple of 4. */
452 uint8_t res : 3; /* Reserved. */
453} GA_DQTR;
454AssertCompile(sizeof(GA_DQTR) == sizeof(uint8_t));
455
456/** Gate Array Configuration Register. */
457typedef struct EL_GACFR {
458 uint8_t mbs : 3; /* Memory Bank Select. */
459 uint8_t rsel : 1; /* RAM Select. */
460 uint8_t test : 1; /* Makes GA counters run at 10 MHz. */
461 uint8_t ows : 1; /* 0 Wait State for Gate Array. */
462 uint8_t tcm : 1; /* Terminal Count Mask for DMA (block interrupt if set). */
463 uint8_t nim : 1; /* NIC Interrupt Mask (block interrupt if set). */
464} GA_GACFR;
465AssertCompile(sizeof(GA_GACFR) == sizeof(uint8_t));
466
467/** Gate Array Configuration Register. */
468typedef struct EL_GACR {
469 uint8_t rst : 1; /* Hard reset GA/NIC. */
470 uint8_t xsel : 1; /* Transceiver Select. */
471 uint8_t ealo : 1; /* Window low 16 bytes of PROM to I/O space. */
472 uint8_t eahi : 1; /* Window high 16 bytes of PROM to I/O space. */
473 uint8_t share : 1; /* Enable interrupt sharing. */
474 uint8_t dbsel : 1; /* Double Buffer Select for FIFOs. */
475 uint8_t ddir : 1; /* DMA Direction (1=host to adapter). */
476 uint8_t start : 1; /* Start Gate Array DMA. */
477} GA_GACR;
478AssertCompile(sizeof(GA_GACR) == sizeof(uint8_t));
479
480/** Gate Array Status Register. */
481typedef struct EL_STREG {
482 uint8_t rev : 3; /* Gate Array Revision. */
483 uint8_t dip : 1; /* DMA In Progress. */
484 uint8_t dtc : 1; /* DMA Terminal Count. */
485 uint8_t oflw : 1; /* Data Overflow. */
486 uint8_t uflw : 1; /* Data Underflow. */
487 uint8_t dprdy : 1; /* Data Port Ready. */
488} GA_STREG;
489AssertCompile(sizeof(GA_STREG) == sizeof(uint8_t));
490
491/** Gate Array Interrupt/DMA Configuration. */
492typedef struct EL_IDCFR {
493 uint8_t drq1 : 1; /* Enable DRQ 1. */
494 uint8_t drq2 : 1; /* Enable DRQ 2. */
495 uint8_t drq3 : 1; /* Enable DRQ 3. */
496 uint8_t res : 1; /* Unused. */
497 uint8_t irq2 : 1; /* Enable IRQ 2. */
498 uint8_t irq3 : 1; /* Enable IRQ 3. */
499 uint8_t irq4 : 1; /* Enable IRQ 4. */
500 uint8_t irq5 : 1; /* Enable IRQ 5. */
501} GA_IDCFR;
502AssertCompile(sizeof(GA_IDCFR) == sizeof(uint8_t));
503
504/** Current DMA Address. */
505typedef struct EL_CDADR {
506 uint8_t cdadr_lsb; /* Current DMA Address LSB. */
507 uint8_t cdadr_msb; /* Current DMA Address MSB. */
508} GA_CDADR;
509AssertCompile(sizeof(GA_CDADR) == sizeof(uint16_t));
510
511/** 3C503 Gate Array state. */
512typedef struct EL_GA_s {
513 uint8_t PSTR; /* Page Start Register. */
514 uint8_t PSPR; /* Page Stop Register. */
515 union {
516 uint8_t DQTR; /* DRQ Timer Register. */
517 GA_DQTR dqtr;
518 };
519 uint8_t BCFR; /* Base Configuration Register (R/O). */
520 uint8_t PCFR; /* Boot PROM Configuration Register (R/O). */
521 union {
522 uint8_t GACFR;
523 GA_GACFR gacfr; /* Gate Array Configuration Register. */
524 };
525 union {
526 uint8_t GACR; /* Gate Array Control Register. */
527 GA_GACR gacr;
528 };
529 union {
530 uint8_t STREG; /* Gate Array Status Register (R/O). */
531 GA_STREG streg;
532 };
533 union {
534 uint8_t IDCFR; /* Interrupt/DMA Configuration Register. */
535 GA_IDCFR idcfr;
536 };
537 uint8_t DAMSB; /* DMA Address MSB. */
538 uint8_t DALSB; /* DMA Address LSB. */
539 uint8_t VPTR2; /* Vector Pointer 2. */
540 uint8_t VPTR1; /* Vector Pointer 1. */
541 uint8_t VPTR0; /* Vector Pointer 0. */
542 union {
543 uint16_t CDADR; /* Current DMA address (internal state). */
544 GA_CDADR cdadr;
545 };
546 bool fGaIrq; /* Gate Array IRQ (internal state). */
547} EL_GA, *PEL_GA;
548
549/** DP8390 core register offsets. */
550#define DPR_CR 0
551
552#define DPR_P0_R_CLDA0 1
553#define DPR_P0_W_PSTART 1
554#define DPR_P0_R_CLDA1 2
555#define DPR_P0_W_PSTOP 2
556#define DPR_P0_BNRY 3
557#define DPR_P0_R_TSR 4
558#define DPR_P0_W_TPSR 4
559#define DPR_P0_R_NCR 5
560#define DPR_P0_W_TBCR0 5
561#define DPR_P0_R_FIFO 6
562#define DPR_P0_W_TBCR1 6
563#define DPR_P0_ISR 7
564#define DPR_P0_R_CRDA0 8
565#define DPR_P0_W_RSAR0 8
566#define DPR_P0_R_CRDA1 9
567#define DPR_P0_W_RSAR1 9
568#define DPR_P0_W_RBCR0 10
569#define DPR_P0_W_RBCR1 11
570#define DPR_P0_R_RSR 12
571#define DPR_P0_W_RCR 12
572#define DPR_P0_R_CNTR0 13
573#define DPR_P0_W_TCR 13
574#define DPR_P0_R_CNTR1 14
575#define DPR_P0_W_DCR 14
576#define DPR_P0_R_CNTR2 15
577#define DPR_P0_W_IMR 15
578
579#define DPR_P1_CURR 7
580
581#define DPR_P2_R_PSTART 1
582#define DPR_P2_W_CLDA0 1
583#define DPR_P2_R_PSTOP 2
584#define DPR_P2_W_CLDA1 2
585#define DPR_P2_RNXTPP 3 /* Remote Next Packet Pointer. */
586#define DPR_P2_R_TPSR 4
587#define DPR_P2_LNXTPP 5 /* Local Next Packet Pointer. */
588#define DPR_P2_ADRCU 6 /* Address Counter (Upper). */
589#define DPR_P2_ADRCL 7 /* Address Counter (Lower). */
590#define DPR_P2_R_RCR 12
591#define DPR_P2_R_TCR 13
592#define DPR_P2_R_DCR 14
593#define DPR_P2_R_IMR 15
594
595
596/** DP8390 Packet Header. */
597typedef struct DP_PKT_HDR {
598 uint8_t rcv_stat; /* Receive Status. */
599 uint8_t next_ptr; /* Next Packet Pointer. */
600 uint16_t byte_cnt; /* Receive byte count. */
601} DP_PKT_HDR;
602
603/** Select values for CR.RD field. */
604#define DP_CR_RDMA_INVL 0 /* Invalid value. */
605#define DP_CR_RDMA_RD 1 /* Remote Read. */
606#define DP_CR_RDMA_WR 2 /* Remote Write. */
607#define DP_CR_RDMA_SP 3 /* Send Packet. */
608#define DP_CR_RDMA_ABRT 4 /* Abort Remote DMA. */
609
610/** DP8390 Command Register (CR). */
611typedef struct DP_CR {
612 uint8_t STP : 1; /* Stop. */
613 uint8_t STA : 1; /* Start. */
614 uint8_t TXP : 1; /* Transmit Packet. */
615 uint8_t RD : 3; /* Remote DMA Command. */
616 uint8_t PS : 2; /* Page Select. */
617} DP_CR;
618AssertCompile(sizeof(DP_CR) == sizeof(uint8_t));
619
620/** DP8390 Interrupt Status Register (ISR). */
621typedef struct DP_ISR {
622 uint8_t PRX : 1; /* Packet Received. */
623 uint8_t PTX : 1; /* Packet Transmitted. */
624 uint8_t RXE : 1; /* Receive Error. */
625 uint8_t TXE : 1; /* Transmit Error. */
626 uint8_t OVW : 1; /* Overwrite Warning (no receive buffers). */
627 uint8_t CNT : 1; /* Counter Overflow. */
628 uint8_t RDC : 1; /* Remote DMA Complete. */
629 uint8_t RST : 1; /* Reset Status. */
630} DP_ISR;
631AssertCompile(sizeof(DP_ISR) == sizeof(uint8_t));
632
633/** DP8390 Interrupt Mask Register (IMR). */
634typedef struct DP_IMR {
635 uint8_t PRXE : 1; /* Packet Received Interrupt Enable. */
636 uint8_t PTXE : 1; /* Packet Transmitted Interrupt Enable. */
637 uint8_t RXEE : 1; /* Receive Error Interrupt Enable. */
638 uint8_t TXEE : 1; /* Transmit Error Interrupt Enable. */
639 uint8_t OVWE : 1; /* Overwrite Warning Interrupt Enable. */
640 uint8_t CNTE : 1; /* Counter Overflow Interrupt Enable. */
641 uint8_t RDCE : 1; /* DMA Complete Interrupt Enable. */
642 uint8_t res : 1; /* Reserved. */
643} DP_IMR;
644AssertCompile(sizeof(DP_IMR) == sizeof(uint8_t));
645
646/** DP8390 Data Configuration Register (DCR). */
647typedef struct DP_DCR {
648 uint8_t WTS : 1; /* Word Transfer Select. */
649 uint8_t BOS : 1; /* Byte Order Select. */
650 uint8_t LAS : 1; /* Long Address Select. */
651 uint8_t LS : 1; /* Loopback Select. */
652 uint8_t ARM : 1; /* Auto-Initialize Remote. */
653 uint8_t FT : 2; /* Fifo Threshold Select. */
654 uint8_t res : 1; /* Reserved. */
655} DP_DCR;
656AssertCompile(sizeof(DP_DCR) == sizeof(uint8_t));
657
658/** Transmit Configuration Register (TCR). */
659typedef struct DP_TCR {
660 uint8_t CRC : 1; /* Inhibit CRC. */
661 uint8_t LB : 2; /* Loopback Control. */
662 uint8_t ATD : 1; /* Auto Transmit Disable. */
663 uint8_t OFST : 1; /* Collision Offset Enable. */
664 uint8_t res : 3; /* Reserved. */
665} DP_TCR;
666AssertCompile(sizeof(DP_TCR) == sizeof(uint8_t));
667
668/** Transmit Status Register (TSR). */
669typedef struct DP_TSR {
670 uint8_t PTX : 1; /* Packet Transmitted. */
671 uint8_t DFR : 1; /* Non-Deferred Transmission (reserved in DP83901A). */
672 uint8_t COL : 1; /* Transmit Collided. */
673 uint8_t ABT : 1; /* Transmit Aborted. */
674 uint8_t CRS : 1; /* Carrier Sense Lost. */
675 uint8_t FU : 1; /* FIFO Underrun. */
676 uint8_t CDH : 1; /* CD Heartbeat. */
677 uint8_t OWC : 1; /* Out of Window Collision. */
678} DP_TSR;
679AssertCompile(sizeof(DP_TSR) == sizeof(uint8_t));
680
681/** Receive Configuration Register (RCR). */
682typedef struct DP_RCR {
683 uint8_t SEP : 1; /* Save Errored Packets. */
684 uint8_t AR : 1; /* Accept Runt Packets. */
685 uint8_t AB : 1; /* Accept Broadcast. */
686 uint8_t AM : 1; /* Accept Multicast. */
687 uint8_t PRO : 1; /* Promiscuous Physical. */
688 uint8_t MON : 1; /* Monitor Mode. */
689 uint8_t res : 2; /* Reserved. */
690} DP_RCR;
691AssertCompile(sizeof(DP_RCR) == sizeof(uint8_t));
692
693/** Receive Status Register (RSR). */
694typedef struct DP_RSR {
695 uint8_t PRX : 1; /* Packet Received Intact. */
696 uint8_t CRC : 1; /* CRC Error. */
697 uint8_t FAE : 1; /* Frame Alignment Error. */
698 uint8_t FO : 1; /* FIFO Overrun. */
699 uint8_t MPA : 1; /* Missed Packet. */
700 uint8_t PHY : 1; /* Physical/Multicast Address. */
701 uint8_t DIS : 1; /* Receiver Disabled. */
702 uint8_t DFR : 1; /* Deferring. */
703} DP_RSR;
704AssertCompile(sizeof(DP_RSR) == sizeof(uint8_t));
705
706/** Transmit Byte Count Register. */
707typedef struct DP_TBCR {
708 uint8_t TBCR0;
709 uint8_t TBCR1;
710} DP_TBCR;
711AssertCompile(sizeof(DP_TBCR) == sizeof(uint16_t));
712
713/** Current Local DMA Address. */
714typedef struct DP_CLDA {
715 uint8_t CLDA0;
716 uint8_t CLDA1;
717} DP_CLDA;
718AssertCompile(sizeof(DP_CLDA) == sizeof(uint16_t));
719
720/** Remote Start Address Register. */
721typedef struct DP_RSAR {
722 uint8_t RSAR0;
723 uint8_t RSAR1;
724} DP_RSAR;
725AssertCompile(sizeof(DP_RSAR) == sizeof(uint16_t));
726
727/** Remote Byte Count Register. */
728typedef struct DP_RBCR {
729 uint8_t RBCR0;
730 uint8_t RBCR1;
731} DP_RBCR;
732AssertCompile(sizeof(DP_RBCR) == sizeof(uint16_t));
733
734/** Current Remote DMA Address. */
735typedef struct DP_CRDA {
736 uint8_t CRDA0;
737 uint8_t CRDA1;
738} DP_CRDA;
739AssertCompile(sizeof(DP_CRDA) == sizeof(uint16_t));
740
741/** Page 1 registers. */
742/* All registers read/write without side effects, unlike pages 0/2. */
743typedef struct DP_PG1 {
744 uint8_t dummy_cr;
745 uint8_t PAR[6]; /* Physical Address PAR0-PAR5. */
746 uint8_t dummy_curr; /* Current Page Register. */
747 uint8_t MAR[8]; /* Multicast Address Register MAR0-MAR7. */
748} DP_PG1;
749AssertCompile(sizeof(DP_PG1) == 16);
750
751/** DP8390 FIFO. Not all of the state is explicitly accessible. */
752typedef struct DP_FIFO {
753 uint8_t rp; /* Read pointer. */
754 uint8_t wp; /* Write pointer. */
755 uint8_t fifo[16]; /* 16 bytes of FIFO. */
756} DP_FIFO;
757
758/**
759 * Core DP8390 chip state.
760 */
761typedef struct DP8390CORE
762{
763 union {
764 uint8_t CR; /* Command Register. */
765 DP_CR cr;
766 };
767 union {
768 uint8_t DCR; /* Data Control Register. */
769 DP_DCR dcr;
770 };
771 /* Interrupt control. */
772 union {
773 uint8_t ISR; /* Interrupt Status Register. */
774 DP_ISR isr;
775 };
776 union {
777 uint8_t IMR; /* Interrupt Mask Register. */
778 DP_IMR imr;
779 };
780 /* Receive state. */
781 union {
782 uint8_t RCR; /* Receive Control Register. */
783 DP_RCR rcr;
784 };
785 union {
786 uint8_t RSR; /* Receive Status register. */
787 DP_RSR rsr;
788 };
789 /* Transmit State. */
790 union {
791 uint8_t TCR; /* Transmit Control Register. */
792 DP_TCR tcr;
793 };
794 union {
795 uint8_t TSR; /* Transmit Status register. */
796 DP_TSR tsr;
797 };
798 uint8_t NCR; /* Number of Collisions Register. */
799 /* Local DMA transmit state. */
800 uint8_t TPSR; /* Transmit Page Start. */
801 union {
802 uint16_t TBCR; /* Transmit Byte Count. */
803 DP_TBCR tbcr;
804 };
805 /* Local DMA receive state. */
806 union {
807 uint16_t CLDA; /* Current Local DMA Address. */
808 DP_CLDA clda;
809 };
810 uint8_t PSTART; /* Page Start. */
811 uint8_t PSTOP; /* Page Stop. */
812 uint8_t CURR; /* Current Page. */
813 uint8_t BNRY; /* Boundary Page. Also spelled BNDRY. */
814 /* Remote DMA state. */
815 union {
816 uint16_t RSAR; /* Remote Start Address Register. */
817 DP_RSAR rsar;
818 };
819 union {
820 uint16_t RBCR; /* Remote Byte Count Register. */
821 DP_RBCR rbcr;
822 };
823 union {
824 uint16_t CRDA; /* Current Remote DMA Address. */
825 DP_CRDA crda;
826 };
827 /* Miscellaneous state. */
828 uint8_t lnxtpp; /* Local Next Packet Pointer. */
829 uint8_t rnxtpp; /* Remote Next Packet Pointer. */
830 /* Tally counters. */
831 uint8_t CNTR0; /* Frame Alignment Errors. */
832 uint8_t CNTR1; /* CRC Errors. */
833 uint8_t CNTR2; /* Missed Packet Errors. */
834 union {
835 uint8_t PG1[sizeof(DP_PG1)];
836 DP_PG1 pg1; /* All Page 1 Registers. */
837 };
838 DP_FIFO fifo; /* The internal FIFO. */
839} DP8390CORE, *PDP8390CORE;
840
841/**
842 * DP8390-based card state.
843 */
844typedef struct DPNICSTATE
845{
846 /** Restore timer.
847 * This is used to disconnect and reconnect the link after a restore. */
848 TMTIMERHANDLE hTimerRestore;
849
850 /** Transmit signaller. */
851 PDMTASKHANDLE hXmitTask;
852 /** Receive ready signaller. */
853 PDMTASKHANDLE hCanRxTask;
854
855 /** Emulated device type. */
856 uint8_t uDevType;
857 /** State of the card's interrupt request signal. */
858 bool fNicIrqActive;
859
860 /** Core DP8390 chip state. */
861 DP8390CORE core;
862
863 /** WD80x3 Control Register 1. */
864 union {
865 uint8_t CTRL1;
866 WD_CTRL1 ctrl1;
867 };
868 /** WD80x3 Control Register 2. */
869 union {
870 uint8_t CTRL2;
871 WD_CTRL2 ctrl2;
872 };
873
874 /** 3C503 Gate Array state. */
875 EL_GA ga;
876 /** The 3C503 soft-configured ISA DMA channel. */
877 uint8_t uElIsaDma;
878
879 /** The PROM contents. 32 bytes addressable, R/O. */
880 uint8_t aPROM[32];
881
882 /** Shared RAM base. */
883 RTGCPHYS MemBase;
884 /** Shared RAM MMIO region handle. */
885 PGMMMIO2HANDLE hSharedMem;
886 /** Shared RAM size. */
887 RTGCPHYS cbMemSize;
888
889 /** Base port of the I/O space region. */
890 RTIOPORT IOPortBase;
891 /** The configured ISA IRQ. */
892 uint8_t uIsaIrq;
893 /** The configured ISA DMA channel. */
894 uint8_t uIsaDma;
895 /** If set the link is currently up. */
896 bool fLinkUp;
897 /** If set the link is temporarily down because of a saved state load. */
898 bool fLinkTempDown;
899 /** Number of times we've reported the link down. */
900 uint16_t cLinkDownReported;
901 /** Number of times we've postponed the link restore. */
902 uint16_t cLinkRestorePostponed;
903
904 /** The "hardware" MAC address. */
905 RTMAC MacConfigured;
906
907 /** Set if DPNICSTATER3::pDrv is not NULL. */
908 bool fDriverAttached;
909 /** The LED. */
910 PDMLED Led;
911 /** Status LUN: The LED ports. */
912 PDMILEDPORTS ILeds;
913 /** Partner of ILeds. */
914 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
915
916 /** Access critical section. */
917 PDMCRITSECT CritSect;
918 /** Event semaphore for blocking on receive. */
919 RTSEMEVENT hEventOutOfRxSpace;
920 /** We are waiting/about to start waiting for more receive buffers. */
921 bool volatile fMaybeOutOfSpace;
922
923 /* MS to wait before we enable the link. */
924 uint32_t cMsLinkUpDelay;
925 /** The device instance number (for logging). */
926 uint32_t iInstance;
927
928 STAMCOUNTER StatReceiveBytes;
929 STAMCOUNTER StatTransmitBytes;
930#ifdef VBOX_WITH_STATISTICS
931 STAMPROFILEADV StatIOReadRZ;
932 STAMPROFILEADV StatIOReadR3;
933 STAMPROFILEADV StatIOWriteRZ;
934 STAMPROFILEADV StatIOWriteR3;
935 STAMPROFILEADV StatReceive;
936 STAMPROFILEADV StatTransmitR3;
937 STAMPROFILEADV StatTransmitRZ;
938 STAMPROFILE StatTransmitSendR3;
939 STAMPROFILE StatTransmitSendRZ;
940 STAMPROFILE StatRxOverflow;
941 STAMCOUNTER StatRxOverflowWakeup;
942 STAMCOUNTER StatRxCanReceiveNow;
943 STAMCOUNTER StatRxCannotReceiveNow;
944 STAMPROFILEADV StatInterrupt;
945 STAMCOUNTER StatDropPktMonitor;
946 STAMCOUNTER StatDropPktRcvrDis;
947 STAMCOUNTER StatDropPktVeryShort;
948 STAMCOUNTER StatDropPktVMNotRunning;
949 STAMCOUNTER StatDropPktNoLink;
950 STAMCOUNTER StatDropPktNoMatch;
951 STAMCOUNTER StatDropPktNoBuffer;
952#endif /* VBOX_WITH_STATISTICS */
953
954 /** NIC-specific ISA I/O ports. */
955 IOMIOPORTHANDLE hIoPortsNic;
956
957 /** Common DP8390 core I/O ports. */
958 IOMIOPORTHANDLE hIoPortsCore;
959
960 /** The runt pad buffer (only really needs 60 bytes). */
961 uint8_t abRuntBuf[64];
962
963 /** The packet buffer. */
964 uint8_t abLocalRAM[DPNIC_MEM_SIZE];
965
966 /** The loopback transmit buffer (avoid stack allocations). */
967 uint8_t abLoopBuf[DPNIC_MEM_SIZE]; /// @todo Can this be smaller?
968} DPNICSTATE, *PDPNICSTATE;
969
970
971/**
972 * DP8390 state for ring-3.
973 *
974 * @implements PDMIBASE
975 * @implements PDMINETWORKDOWN
976 * @implements PDMINETWORKCONFIG
977 * @implements PDMILEDPORTS
978 */
979typedef struct DPNICSTATER3
980{
981 /** Pointer to the device instance. */
982 PPDMDEVINSR3 pDevIns;
983 /** Pointer to the connector of the attached network driver. */
984 PPDMINETWORKUPR3 pDrv;
985 /** Pointer to the attached network driver. */
986 R3PTRTYPE(PPDMIBASE) pDrvBase;
987 /** LUN\#0 + status LUN: The base interface. */
988 PDMIBASE IBase;
989 /** LUN\#0: The network port interface. */
990 PDMINETWORKDOWN INetworkDown;
991 /** LUN\#0: The network config port interface. */
992 PDMINETWORKCONFIG INetworkConfig;
993
994 /** Status LUN: The LED ports. */
995 PDMILEDPORTS ILeds;
996 /** Partner of ILeds. */
997 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
998} DPNICSTATER3;
999/** Pointer to a DP8390 state structure for ring-3. */
1000typedef DPNICSTATER3 *PDPNICSTATER3;
1001
1002
1003/**
1004 * DP8390 state for ring-0.
1005 */
1006typedef struct DPNICSTATER0
1007{
1008 /** Pointer to the connector of the attached network driver. */
1009 PPDMINETWORKUPR0 pDrv;
1010} DPNICSTATER0;
1011/** Pointer to a DP8390 state structure for ring-0. */
1012typedef DPNICSTATER0 *PDPNICSTATER0;
1013
1014
1015/**
1016 * DP8390 state for raw-mode.
1017 */
1018typedef struct DPNICSTATERC
1019{
1020 /** Pointer to the connector of the attached network driver. */
1021 PPDMINETWORKUPRC pDrv;
1022} DPNICSTATERC;
1023/** Pointer to a DP8390 state structure for raw-mode. */
1024typedef DPNICSTATERC *PDPNICSTATERC;
1025
1026
1027/** The DP8390 state structure for the current context. */
1028typedef CTX_SUFF(DPNICSTATE) DPNICSTATECC;
1029/** Pointer to a DP8390 state structure for the current
1030 * context. */
1031typedef CTX_SUFF(PDPNICSTATE) PDPNICSTATECC;
1032
1033
1034#ifndef VBOX_DEVICE_STRUCT_TESTCASE
1035
1036
1037/*********************************************************************************************************************************
1038* Internal Functions *
1039*********************************************************************************************************************************/
1040
1041static int dp8390CoreAsyncXmitLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PDPNICSTATECC pThisCC, bool fOnWorkerThread);
1042
1043/**
1044 * Checks if the link is up.
1045 * @returns true if the link is up.
1046 * @returns false if the link is down.
1047 */
1048DECLINLINE(bool) dp8390IsLinkUp(PDPNICSTATE pThis)
1049{
1050 return pThis->fDriverAttached && !pThis->fLinkTempDown && pThis->fLinkUp;
1051}
1052
1053
1054/* Table and macro borrowed from DevPCNet.cpp. */
1055#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
1056
1057/* generated using the AUTODIN II polynomial
1058 * x^32 + x^26 + x^23 + x^22 + x^16 +
1059 * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
1060 */
1061static const uint32_t crctab[256] =
1062{
1063 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
1064 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
1065 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
1066 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
1067 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
1068 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
1069 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
1070 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
1071 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
1072 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
1073 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
1074 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
1075 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
1076 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
1077 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
1078 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
1079 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
1080 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
1081 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
1082 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
1083 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
1084 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
1085 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
1086 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
1087 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
1088 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
1089 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
1090 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
1091 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
1092 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
1093 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
1094 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
1095 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
1096 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
1097 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
1098 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
1099 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
1100 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
1101 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
1102 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
1103 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
1104 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
1105 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
1106 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
1107 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
1108 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
1109 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
1110 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
1111 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
1112 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
1113 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
1114 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
1115 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
1116 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
1117 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
1118 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
1119 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
1120 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
1121 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
1122 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
1123 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
1124 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
1125 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
1126 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
1127};
1128
1129
1130#ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
1131#define ETHER_IS_MULTICAST(a) ((*(uint8_t *)(a)) & 1)
1132#endif
1133
1134
1135/**
1136 * Check if incoming frame matches the station address.
1137 */
1138DECLINLINE(int) padr_match(PDPNICSTATE pThis, const uint8_t *buf)
1139{
1140 RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
1141 int result;
1142
1143 /* Checks own address only; always enabled if receiver on. */
1144 result = !memcmp(hdr->DstMac.au8, pThis->core.pg1.PAR, 6);
1145
1146 return result;
1147}
1148
1149
1150/**
1151 * Check if incoming frame is an accepted broadcast frame.
1152 */
1153DECLINLINE(int) padr_bcast(PDPNICSTATE pThis, const uint8_t *buf)
1154{
1155 static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1156 RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
1157 int result = pThis->core.rcr.AB && !memcmp(hdr->DstMac.au8, aBCAST, 6);
1158 return result;
1159}
1160
1161
1162/**
1163 * Check if incoming frame is an accepted multicast frame.
1164 */
1165DECLINLINE(int) padr_mcast(PDPNICSTATE pThis, const uint8_t *buf, int *mcast_type)
1166{
1167 uint32_t crc = UINT32_MAX;
1168 RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
1169 int result = 0;
1170
1171 /* If multicast addresses are enabled, and the destination
1172 * address is in fact multicast, the address must be run through
1173 * the CRC generator and matched against the multicast filter
1174 * array.
1175 */
1176 if (pThis->core.rcr.AM && ETHER_IS_MULTICAST(hdr->DstMac.au8))
1177 {
1178 unsigned i;
1179 const uint8_t *p = buf;
1180 unsigned crc_frag, crc_rev;
1181 unsigned ma_bit_mask, ma_byte_idx;
1182
1183 /* Indicate to caller that the address is a multicast one, regardless
1184 * of whether it's accepted or not.
1185 */
1186 *mcast_type = 1;
1187
1188 for (i = 0; i < sizeof(hdr->DstMac); ++i)
1189 CRC(crc, *p++);
1190
1191 /* The top 6 bits of the CRC calculated from the destination address
1192 * becomes an index into the 64-bit multicast address register. Sadly
1193 * our CRC algorithm is bit-reversed (Ethernet shifts bits out MSB first)
1194 * so instead of the top 6 bits of the CRC we have to take the bottom 6
1195 * and reverse the bits.
1196 */
1197 crc_frag = crc & 63;
1198
1199 for (i = 0, crc_rev = 0; i < 6; ++i)
1200 crc_rev |= ((crc_frag >> i) & 1) * (0x20 >> i);
1201
1202 ma_bit_mask = 1 << (crc_rev & 7);
1203 ma_byte_idx = crc_rev / 8;
1204 Log3Func(("crc=%08X, crc_frag=%u, crc_rev=%u, ma_byte_idx=%u, ma_bit_mask=%02X\n", crc, crc_frag, crc_rev, ma_byte_idx, ma_bit_mask));
1205 Log3Func(("MAR: %02X:%02X:%02X:%02X %02X:%02X:%02X:%02X\n", pThis->core.pg1.MAR[0], pThis->core.pg1.MAR[1], pThis->core.pg1.MAR[2], pThis->core.pg1.MAR[3], pThis->core.pg1.MAR[4], pThis->core.pg1.MAR[5], pThis->core.pg1.MAR[6], pThis->core.pg1.MAR[7]));
1206
1207 /* The multicast filtering logic is fairly extensively
1208 * verified by EtherLink II diagnostics (3C503.EXE).
1209 */
1210 if (pThis->core.pg1.MAR[ma_byte_idx] & ma_bit_mask)
1211 {
1212 Log3Func(("Passed multicast filter\n"));
1213 result = 1;
1214 }
1215 }
1216
1217 return result;
1218}
1219
1220
1221/**
1222 * Check if incoming frame is an accepted promiscuous frame.
1223 */
1224DECLINLINE(int) padr_promi(PDPNICSTATE pThis, const uint8_t *buf)
1225{
1226 RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
1227 int result = pThis->core.rcr.PRO && !ETHER_IS_MULTICAST(hdr->DstMac.au8);
1228 return result;
1229}
1230
1231
1232/**
1233 * Update the device IRQ line based on internal state.
1234 */
1235static void dp8390CoreUpdateIrq(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
1236{
1237 bool fCoreIrqActive = false;
1238 bool fNicIrqActive = false;
1239
1240 STAM_PROFILE_ADV_START(&pThis->StatInterrupt, a);
1241
1242 /* Set the ISR.CNT bit based on the counter state (top counter bits ANDed together). */
1243 pThis->core.isr.CNT = (pThis->core.CNTR0 & pThis->core.CNTR1 & pThis->core.CNTR2) >> 7;
1244
1245 /* IRQ is active if a bit is set in ISR and the corresponding bit
1246 * is set in IMR. No additional internal state needed.
1247 */
1248 Assert(!pThis->core.imr.res);
1249 if (pThis->core.ISR & pThis->core.IMR)
1250 fCoreIrqActive = true;
1251
1252 /* The 3C503 has additional interrupt sources and control. For other device
1253 * types, the extras magically work out to be a no-op.
1254 */
1255 pThis->ga.fGaIrq = pThis->ga.streg.dtc && !pThis->ga.gacfr.tcm;
1256 fNicIrqActive = (fCoreIrqActive && !pThis->ga.gacfr.nim) || (pThis->ga.streg.dtc && !pThis->ga.gacfr.tcm);
1257
1258 Log2Func(("#%d set irq fNicIrqActive=%d (fCoreIrqActive=%d, fGaIrq=%d)\n", pThis->iInstance, fNicIrqActive, fCoreIrqActive, pThis->ga.fGaIrq));
1259
1260 /* The IRQ line typically does not change. */
1261 if (RT_UNLIKELY(fNicIrqActive != pThis->fNicIrqActive))
1262 {
1263 LogFunc(("#%d IRQ=%d, state=%d\n", pThis->iInstance, pThis->uIsaIrq, fNicIrqActive));
1264 /// @todo Handle IRQ 2/9 elsewhere
1265 PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq == 2 ? 9 : pThis->uIsaIrq, fNicIrqActive);
1266 pThis->fNicIrqActive = fNicIrqActive;
1267 }
1268 STAM_PROFILE_ADV_STOP(&pThis->StatInterrupt, a);
1269}
1270
1271
1272/**
1273 * Perform a software reset of the NIC.
1274 */
1275static void dp8390CoreReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
1276{
1277 LogFlowFunc(("#%d:\n", pThis->iInstance));
1278
1279 /* DP8390 or DP83901A datasheet, section 11.0. */
1280 pThis->core.cr.TXP = 0;
1281 pThis->core.cr.STA = 0;
1282 pThis->core.cr.STP = 1;
1283 pThis->core.cr.RD = DP_CR_RDMA_ABRT;
1284 pThis->core.isr.RST = 1;
1285 pThis->core.IMR = 0;
1286 pThis->core.dcr.LAS = 0;
1287 pThis->core.tcr.LB = 0;
1288
1289 /// @todo Check if this really happens on soft reset
1290 /* Clear the internal FIFO including r/w pointers. */
1291 memset(&pThis->core.fifo, 0, sizeof(pThis->core.fifo));
1292
1293 /* Make sure the IRQ line us updated. */
1294 dp8390CoreUpdateIrq(pDevIns, pThis);
1295}
1296
1297#ifdef IN_RING3
1298
1299static DECLCALLBACK(void) dp8390R3WakeupReceive(PPDMDEVINS pDevIns)
1300{
1301 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
1302 LogFlowFunc(("#%d\n", pThis->iInstance));
1303 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
1304 if (pThis->hEventOutOfRxSpace != NIL_RTSEMEVENT)
1305 RTSemEventSignal(pThis->hEventOutOfRxSpace);
1306}
1307
1308/**
1309 * @callback_method_impl{FNPDMTASKDEV,
1310 * Signal to R3 that NIC is ready to receive a packet.
1311 */
1312static DECLCALLBACK(void) dpNicR3CanRxTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
1313{
1314 RT_NOREF(pvUser);
1315 dp8390R3WakeupReceive(pDevIns);
1316}
1317
1318#endif /* IN_RING3 */
1319
1320/**
1321 * Read up to 256 bytes from a single page of local RAM.
1322 */
1323static void dpLocalRAMReadBuf(PDPNICSTATE pThis, uint16_t addr, unsigned cb, uint8_t *pDst)
1324{
1325 if ((RT_LOBYTE(addr) + cb) > 256)
1326 {
1327 LogFunc(("#%d: addr=%04X, cb=%X, cb!!\n", pThis->iInstance, addr, cb));
1328 cb = 256 - RT_LOBYTE(addr);
1329 }
1330
1331 /* A single page is always either entirely inside or outside local RAM. */
1332 if (pThis->uDevType == DEV_NE1000)
1333 {
1334 /* Only 14 bits of address are decoded. */
1335 addr &= 0x3fff;
1336 if (addr >= 0x2000)
1337 {
1338 /* Local RAM is mapped at 2000h-3FFFh. */
1339 addr -= 0x2000;
1340 memcpy(pDst, &pThis->abLocalRAM[addr], cb);
1341 }
1342 else
1343 LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1344 }
1345 else if (pThis->uDevType == DEV_NE2000)
1346 {
1347 /* Only 15 bits of address are decoded. */
1348 addr &= 0x7fff;
1349 if (addr >= 0x4000)
1350 {
1351 /* Local RAM is mapped at 4000h-7FFFh. */
1352 addr -= 0x4000;
1353 memcpy(pDst, &pThis->abLocalRAM[addr], cb);
1354 }
1355 else
1356 LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1357 }
1358 else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
1359 {
1360 /* Local RAM is mapped starting at address zero. */
1361 addr &= DPNIC_MEM_MASK;
1362 if (addr + cb <= DPNIC_MEM_SIZE)
1363 memcpy(pDst, &pThis->abLocalRAM[addr], cb);
1364 else
1365 LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1366 }
1367 else if (pThis->uDevType == DEV_3C503)
1368 {
1369 /* Only 14 bits of address are decoded. */
1370 /// @todo Is there any internal wrap-around in the 3C503 too?
1371 addr &= 0x3fff;
1372 if (addr >= 0x2000)
1373 {
1374 /* Local RAM is mapped at 2000h-3FFFh. */
1375 addr -= 0x2000;
1376 memcpy(pDst, &pThis->abLocalRAM[addr], cb);
1377 }
1378 else
1379 LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1380 }
1381 else
1382 {
1383 Assert(0);
1384 }
1385}
1386
1387
1388#ifdef IN_RING3
1389
1390/**
1391 * Write up to 256 bytes into a single page of local RAM.
1392 */
1393static void dpLocalRAMWriteBuf(PDPNICSTATE pThis, uint16_t addr, unsigned cb, const uint8_t *pSrc)
1394{
1395 if ((RT_LOBYTE(addr) + cb) > 256)
1396 {
1397 LogFunc(("#%d: addr=%04X, cb=%X, cb!!\n", pThis->iInstance, addr, cb));
1398 cb = 256 - RT_LOBYTE(addr);
1399 }
1400
1401 /* A single page is always either entirely inside or outside local RAM. */
1402 if (pThis->uDevType == DEV_NE1000)
1403 {
1404 /* Only 14 bits of address are decoded. */
1405 addr &= 0x3fff;
1406 if (addr >= 0x2000)
1407 {
1408 /* Local RAM is mapped at 2000h-3FFFh. */
1409 addr -= 0x2000;
1410 memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
1411 }
1412 else
1413 LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1414 }
1415 else if (pThis->uDevType == DEV_NE2000)
1416 {
1417 /* Only 14 bits of address are decoded. */
1418 addr &= 0x7fff;
1419 if (addr >= 0x4000)
1420 {
1421 /* Local RAM is mapped at 4000h-7FFFh. */
1422 addr -= 0x4000;
1423 memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
1424 }
1425 else
1426 LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1427 }
1428 else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
1429 {
1430 /* Local RAM is mapped starting at address zero. */
1431 addr &= DPNIC_MEM_MASK;
1432 if (addr + cb <= DPNIC_MEM_SIZE)
1433 memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
1434 else
1435 LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1436 }
1437 else if (pThis->uDevType == DEV_3C503)
1438 {
1439 /* Only 14 bits of address are decoded. */
1440 /// @todo Is there any internal wrap-around in the 3C503 too?
1441 addr &= 0x3fff;
1442 if (addr >= 0x2000)
1443 {
1444 /* Local RAM is mapped at 2000h-3FFFh. */
1445 addr -= 0x2000;
1446 memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
1447 }
1448 else
1449 LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
1450 }
1451 else
1452 {
1453 Assert(0);
1454 }
1455}
1456
1457
1458/**
1459 * Receive an arbitrarily long buffer into the receive ring starting at CLDA.
1460 * Update RSR, CLDA, and other state in the process.
1461 */
1462static void dp8390CoreReceiveBuf(PDPNICSTATE pThis, DP_RSR *pRsr, const uint8_t *src, unsigned cbLeft, bool fLast)
1463{
1464 LogFlow(("#%d: Initial CURR=%02X00 CLDA=%04X\n", pThis->iInstance, pThis->core.CURR, pThis->core.CLDA));
1465
1466 while (cbLeft)
1467 {
1468 unsigned cbWrite;
1469 unsigned cbPage;
1470
1471 /* Write at most up to the end of a page. */
1472 cbPage = cbWrite = 256 - pThis->core.clda.CLDA0;
1473 if (cbWrite > cbLeft)
1474 cbWrite = cbLeft;
1475 Log2Func(("#%d: cbLeft=%d CURR=%02X00 CLDA=%04X\n", pThis->iInstance, cbLeft, pThis->core.CURR, pThis->core.CLDA));
1476 dpLocalRAMWriteBuf(pThis, pThis->core.CLDA, cbWrite, src);
1477 src += cbWrite;
1478
1479 /* If this is the last fragment of a received frame, we need to
1480 * round CLDA up to the next page boundary to correctly evaluate
1481 * buffer overflows and the next pointer. Otherwise we just
1482 * add however much data we had so that we can continue writing
1483 * at the CLDA position.
1484 */
1485 if (fLast && (cbWrite == cbLeft))
1486 {
1487 Log3Func(("#%d: Round up: CLDA=%04X cbPage=%X\n", pThis->iInstance, pThis->core.CLDA, cbPage));
1488 pThis->core.CLDA += cbPage;
1489 }
1490 else
1491 pThis->core.CLDA += cbWrite;
1492
1493 Log3Func(("#%d: Final CURR=%02X00 CLDA=%04X\n", pThis->iInstance, pThis->core.CURR, pThis->core.CLDA));
1494 /* If at end of ring, wrap around. */
1495 if (pThis->core.clda.CLDA1 == pThis->core.PSTOP)
1496 pThis->core.clda.CLDA1 = pThis->core.PSTART;
1497
1498 /* Check for buffer overflow. */
1499 if (pThis->core.clda.CLDA1 == pThis->core.BNRY)
1500 {
1501 pThis->core.isr.OVW = 1;
1502 pThis->core.isr.RST = 1;
1503 pRsr->MPA = 1; /* Indicates to caller that receive was aborted. */
1504 STAM_COUNTER_INC(&pThis->StatDropPktNoBuffer);
1505 Log3Func(("#%d: PSTART=%02X00 PSTOP=%02X00 BNRY=%02X00 CURR=%02X00 -- overflow!\n", pThis->iInstance, pThis->core.PSTART, pThis->core.PSTOP, pThis->core.BNRY, pThis->core.CURR));
1506 break;
1507 }
1508 cbLeft -= cbWrite;
1509 }
1510}
1511
1512/**
1513 * Write incoming data into the packet buffer.
1514 */
1515static void dp8390CoreReceiveLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, const uint8_t *src, size_t cbToRecv)
1516{
1517 int is_padr = 0, is_bcast = 0, is_mcast = 0, is_prom = 0;
1518 int mc_type = 0;
1519
1520 /*
1521 * Drop all packets if the VM is not running yet/anymore.
1522 */
1523 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1524 if ( enmVMState != VMSTATE_RUNNING
1525 && enmVMState != VMSTATE_RUNNING_LS)
1526 {
1527 STAM_COUNTER_INC(&pThis->StatDropPktVMNotRunning);
1528 return;
1529 }
1530
1531 /*
1532 * Drop all packets if the cable is not connected.
1533 */
1534 if (RT_UNLIKELY(!dp8390IsLinkUp(pThis)))
1535 {
1536 STAM_COUNTER_INC(&pThis->StatDropPktNoLink);
1537 return;
1538 }
1539
1540 /*
1541 * Drop everything if NIC is not started or in reset.
1542 */
1543 if (RT_UNLIKELY(!pThis->core.cr.STA || pThis->core.cr.STP))
1544 {
1545 STAM_COUNTER_INC(&pThis->StatDropPktRcvrDis);
1546 return;
1547 }
1548
1549 /* Drop impossibly short packets. The DP8390 requires a packet to have
1550 * at least 8 bytes to even qualify as a runt. We can also assume that
1551 * there is a complete destination address at that point.
1552 */
1553 if (RT_UNLIKELY(cbToRecv < 8))
1554 {
1555 STAM_COUNTER_INC(&pThis->StatDropPktVeryShort);
1556 return;
1557 }
1558
1559 LogFlowFunc(("#%d: size on wire=%d\n", pThis->iInstance, cbToRecv));
1560
1561 /*
1562 * Perform address matching. Packets which do not pass any address
1563 * matching logic are ignored.
1564 */
1565 if ( (is_padr = padr_match(pThis, src))
1566 || (is_bcast = padr_bcast(pThis, src))
1567 || (is_mcast = padr_mcast(pThis, src, &mc_type))
1568 || (is_prom = padr_promi(pThis, src)))
1569 {
1570 union {
1571 uint8_t nRSR;
1572 DP_RSR nRsr;
1573 };
1574 uint32_t fcs = 0;
1575
1576 nRSR = 0;
1577 Log2Func(("#%d Packet passed address filter (is_padr=%d, is_bcast=%d, is_mcast=%d, is_prom=%d), size=%d\n", pThis->iInstance, is_padr, is_bcast, is_mcast, is_prom, cbToRecv));
1578
1579 if (is_bcast || mc_type)
1580 nRsr.PHY = 1;
1581
1582 /* In Monitor Mode, just increment the tally counter. */
1583 if (RT_UNLIKELY(pThis->core.rcr.MON))
1584 {
1585 STAM_COUNTER_INC(&pThis->StatDropPktMonitor);
1586 nRsr.MPA = 1;
1587 if (pThis->core.CNTR2 <= 192)
1588 pThis->core.CNTR2++; /* Relies on UpdateIrq to be run. */
1589 }
1590 else
1591 {
1592 /* Error detection: FCS and frame alignment errors cannot happen,
1593 * likewise FIFO overruns can't.
1594 * Runts are padded up to the required minimum. Note that the DP8390
1595 * documentation considers packets smaller than 64 bytes to be runts,
1596 * but that includes 32 bits of FCS.
1597 */
1598
1599 /* See if we need to pad, and how much. Note that if there's any
1600 * room left in the receive buffers, a runt will fit even after padding.
1601 */
1602 if (RT_UNLIKELY(cbToRecv < 60))
1603 {
1604 /// @todo This really is kind of stupid. We shouldn't be doing any
1605 /// padding here, it should be done by the sending side!
1606 memset(pThis->abRuntBuf, 0, sizeof(pThis->abRuntBuf));
1607 memcpy(pThis->abRuntBuf, src, cbToRecv);
1608 cbToRecv = 60;
1609 src = pThis->abRuntBuf;
1610 }
1611
1612 LogFlowFunc(("#%d: PSTART=%02X00 PSTOP=%02X00 BNRY=%02X00 CURR=%02X00\n", pThis->iInstance, pThis->core.PSTART, pThis->core.PSTOP, pThis->core.BNRY, pThis->core.CURR));
1613
1614 /* All packets that passed the address filter are copied to local RAM.
1615 * Since the DP8390 does not know how long the frame is until it detects
1616 * end of frame, it can only detect an out-of-buffer condition after
1617 * filling up all available space. It then reports an error and rewinds
1618 * back to where it was before.
1619 *
1620 * We do not limit the incoming frame size except by available buffer space. /// @todo Except we do??
1621 */
1622
1623 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbToRecv);
1624
1625 /* Copy incoming data to the packet buffer. Start by setting CLDA
1626 * to CURR + 4, leaving room for header.
1627 */
1628 pThis->core.CLDA = RT_MAKE_U16(4, pThis->core.CURR);
1629
1630 /* Receive the incoming frame. */
1631 Assert(cbToRecv < MAX_FRAME); /// @todo Can we actually do bigger?
1632 dp8390CoreReceiveBuf(pThis, &nRsr, src, (unsigned)cbToRecv, false);
1633 /// @todo Use the same method for runt padding?
1634
1635 /* If there was no overflow, add the FCS. */
1636 if (!nRsr.MPA)
1637 {
1638 fcs = 0xBADF00D; // Just fake it, does anyone care?
1639 dp8390CoreReceiveBuf(pThis, &nRsr, (uint8_t *)&fcs, sizeof(fcs), true);
1640 }
1641
1642 /* Error-free packets are considered intact. */
1643 if (!nRsr.CRC && !nRsr.FAE && !nRsr.FO && !nRsr.MPA)
1644 {
1645 nRsr.PRX = 1;
1646 pThis->core.isr.PRX = 1;
1647 }
1648 else
1649 pThis->core.isr.RXE = 1;
1650
1651 /* For 'intact' packets, write the packet header. */
1652 if (nRsr.PRX)
1653 {
1654 DP_PKT_HDR header;
1655
1656 /* Round up CLDA to the next page. */
1657 if (pThis->core.clda.CLDA0)
1658 pThis->core.CLDA = RT_MAKE_U16(0, pThis->core.clda.CLDA1 + 1);
1659
1660 /* If entire frame was successfully received, write the packet header at the old CURR. */
1661 header.rcv_stat = nRSR;
1662 header.next_ptr = pThis->core.clda.CLDA1;
1663 /// @todo big endian (WTS)
1664 header.byte_cnt = (uint16_t)cbToRecv + sizeof(fcs);
1665
1666 pThis->core.CLDA = RT_MAKE_U16(0, pThis->core.CURR);
1667 dpLocalRAMWriteBuf(pThis, pThis->core.CLDA, sizeof(header), (uint8_t *)&header);
1668 pThis->core.CLDA += sizeof(header);
1669
1670 pThis->core.CURR = header.next_ptr;
1671 }
1672 }
1673
1674 pThis->core.RSR = nRSR;
1675
1676 Log2Func(("Receive completed, size=%d, CURR=%02X00, RSR=%02X, ISR=%02X\n", cbToRecv, pThis->core.CURR, pThis->core.RSR, pThis->core.ISR));
1677 dp8390CoreUpdateIrq(pDevIns, pThis);
1678 }
1679 else
1680 {
1681 Log3Func(("#%d Packet did not pass address filter, size=%d\n", pThis->iInstance, cbToRecv));
1682 STAM_COUNTER_INC(&pThis->StatDropPktNoMatch);
1683 }
1684}
1685
1686#endif /* IN_RING3 */
1687
1688
1689/**
1690 * Transmit a packet from local memory.
1691 *
1692 * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
1693 *
1694 * @param pDevIns The device instance data.
1695 * @param pThis The device state data.
1696 * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
1697 */
1698static int dp8390CoreXmitPacket(PPDMDEVINS pDevIns, PDPNICSTATE pThis, bool fOnWorkerThread)
1699{
1700 PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
1701 RT_NOREF_PV(fOnWorkerThread);
1702 int rc;
1703
1704 /*
1705 * Grab the xmit lock of the driver as well as the DP8390 device state.
1706 */
1707 PPDMINETWORKUP pDrv = pThisCC->pDrv;
1708 if (pDrv)
1709 {
1710 rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
1711 if (RT_FAILURE(rc))
1712 return rc;
1713 }
1714 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
1715 if (RT_SUCCESS(rc))
1716 {
1717 /*
1718 * Do the transmitting.
1719 */
1720 int rc2 = dp8390CoreAsyncXmitLocked(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
1721 AssertReleaseRC(rc2);
1722
1723 /*
1724 * Release the locks.
1725 */
1726 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1727 }
1728 else
1729 AssertLogRelRC(rc);
1730 if (pDrv)
1731 pDrv->pfnEndXmit(pDrv);
1732
1733 return rc;
1734}
1735
1736
1737#ifdef IN_RING3
1738
1739/**
1740 * @callback_method_impl{FNPDMTASKDEV,
1741 * This is just a very simple way of delaying sending to R3.
1742 */
1743static DECLCALLBACK(void) dpNicR3XmitTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
1744{
1745 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
1746 NOREF(pvUser);
1747
1748 /*
1749 * Transmit if we can.
1750 */
1751 dp8390CoreXmitPacket(pDevIns, pThis, true /*fOnWorkerThread*/);
1752}
1753
1754#endif /* IN_RING3 */
1755
1756
1757/**
1758 * Allocates a scatter/gather buffer for a transfer.
1759 *
1760 * @returns See PPDMINETWORKUP::pfnAllocBuf.
1761 * @param pThis The device instance.
1762 * @param pThisCC The device state for current context.
1763 * @param cbMin The minimum buffer size.
1764 * @param fLoopback Set if we're in loopback mode.
1765 * @param pSgLoop Pointer to stack storage for the loopback SG.
1766 * @param ppSgBuf Where to return the SG buffer descriptor on success.
1767 * Always set.
1768 */
1769DECLINLINE(int) dp8390XmitAllocBuf(PDPNICSTATE pThis, PDPNICSTATECC pThisCC, size_t cbMin, bool fLoopback,
1770 PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
1771{
1772 int rc;
1773
1774 if (!fLoopback)
1775 {
1776 PPDMINETWORKUP pDrv = pThisCC->pDrv;
1777 if (RT_LIKELY(pDrv))
1778 {
1779 rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
1780 AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
1781 if (RT_FAILURE(rc))
1782 *ppSgBuf = NULL;
1783 }
1784 else
1785 {
1786 rc = VERR_NET_DOWN;
1787 *ppSgBuf = NULL;
1788 }
1789 }
1790 else
1791 {
1792 /* Fake loopback allocator. */
1793 pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
1794 pSgLoop->cbUsed = 0;
1795 pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
1796 pSgLoop->pvAllocator = pThis;
1797 pSgLoop->pvUser = NULL;
1798 pSgLoop->cSegs = 1;
1799 pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
1800 pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
1801 *ppSgBuf = pSgLoop;
1802 rc = VINF_SUCCESS;
1803 }
1804 return rc;
1805}
1806
1807
1808/**
1809 * Sends the scatter/gather buffer.
1810 *
1811 * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
1812 *
1813 * @returns See PDMINETWORKUP::pfnSendBuf.
1814 * @param pDevIns The device instance.
1815 * @param pThisCC The current context device state.
1816 * @param fLoopback Set if we're in loopback mode.
1817 * @param pSgBuf The SG to send.
1818 * @param fOnWorkerThread Set if we're being called on a work thread. Clear
1819 * if an EMT.
1820 */
1821DECLINLINE(int) dp8390CoreXmitSendBuf(PPDMDEVINS pDevIns, PDPNICSTATECC pThisCC, bool fLoopback, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
1822{
1823 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
1824 int rc;
1825 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
1826 if (!fLoopback)
1827 {
1828 STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
1829 if (pSgBuf->cbUsed > 70) /* unqualified guess */
1830 pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
1831
1832 PPDMINETWORKUP pDrv = pThisCC->pDrv;
1833 if (RT_LIKELY(pDrv))
1834 {
1835 rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
1836 AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
1837 }
1838 else
1839 rc = VERR_NET_DOWN;
1840
1841 pThis->Led.Actual.s.fWriting = 0;
1842 STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
1843 }
1844 else
1845 {
1846 PDP8390CORE pCore = &pThis->core;
1847 union {
1848 uint8_t nRSR;
1849 DP_RSR nRsr;
1850 };
1851 unsigned ofs;
1852 uint32_t fcs = UINT32_MAX;
1853
1854 nRSR = 0;
1855
1856 /* Loopback on the DP8390 is so strange that it must be handled specially. */
1857 Assert(pSgBuf->pvAllocator == (void *)pThis);
1858 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
1859
1860 LogFlowFunc(("#%d: loopback (DCR=%02X LB=%u TCR=%02X RCR=%02X, %u bytes)\n", pThis->iInstance, pCore->DCR, pCore->tcr.LB, pCore->TCR, pCore->RCR, pSgBuf->cbUsed));
1861 for (ofs = 0; ofs < pSgBuf->cbUsed; ofs += 16)
1862 Log((" %04X: %.*Rhxs\n", ofs, ofs + 16 < pSgBuf->cbUsed ? 16 : pSgBuf->cbUsed - ofs, &pThis->abLoopBuf[ofs]));
1863
1864 /* A packet shorter than 8 bytes is ignored by the receiving side. */
1865 if (pSgBuf->cbUsed < 8)
1866 return VINF_SUCCESS;
1867
1868 /* The loopback mode affects transmit status bits. */
1869 switch (pCore->tcr.LB)
1870 {
1871 case 1: /* Internal loopback within DP8390. */
1872 pCore->tsr.CDH = 1;
1873 pCore->tsr.CRS = 1;
1874 break;
1875 case 2: /* Loopback through serializer. */
1876 pCore->tsr.CDH = 1;
1877 break;
1878 case 3: /* External loopback. Requires a cable. */
1879 break;
1880 default:
1881 Assert(0);
1882 }
1883
1884 /* The CRC Inhibit controls whether transmit or receive path uses the
1885 * CRC circuitry. If transmit side uses CRC, receive always fails.
1886 * We always need to calculate the FCS because either the sending or
1887 * the receiving side uses it.
1888 */
1889 uint8_t *p;
1890 uint8_t *pktbuf = pThis->abLoopBuf; /// @todo Point into sgbuf instead?
1891 uint16_t pktlen = (uint16_t)pSgBuf->cbUsed;
1892 uint16_t fcslen = pktlen;
1893 uint8_t abFcs[4];
1894 bool fAddrMatched = true;
1895
1896 /* If the receiver side is calculating FCS, it needs to skip the last
1897 * bytes (which are the transmit-side FCS).
1898 */
1899 if (pCore->tcr.CRC && (pktlen > 4))
1900 fcslen -= 4;
1901
1902 p = pktbuf;
1903 while (p != &pktbuf[fcslen])
1904 CRC(fcs, *p++);
1905
1906 fcs = ~fcs;
1907 Log3Func(("FCS: %08X\n", fcs));
1908 for (ofs = 0; ofs < sizeof(abFcs); ++ofs)
1909 {
1910 abFcs[ofs] = (uint8_t)fcs;
1911 fcs >>= 8;
1912 }
1913
1914 /* The FIFO write pointer gets zeroed on each receive,
1915 * but the read pointer does not.
1916 */
1917 pCore->fifo.wp = 0;
1918
1919 if (pCore->tcr.CRC)
1920 {
1921 bool fGoodFcs = true;
1922 int is_padr = 0, is_bcast = 0, is_mcast = 0, is_prom = 0;
1923 int mc_type = 0;
1924
1925 /* Always put the first 8 bytes of the packet in the FIFO. */
1926 for (ofs = 0; ofs < 8; ++ofs)
1927 pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
1928
1929
1930 /* If the receiving side uses the CRC circuitry, it also performs
1931 * destination address matching.
1932 */
1933 if ( (is_padr = padr_match(pThis, pktbuf))
1934 || (is_bcast = padr_bcast(pThis, pktbuf))
1935 || (is_mcast = padr_mcast(pThis, pktbuf, &mc_type))
1936 || (is_prom = padr_promi(pThis, pktbuf)))
1937 {
1938 /* Receiving side checks the FCS. */
1939 fGoodFcs = !memcmp(&pktbuf[pktlen - 4], abFcs, sizeof(abFcs));
1940 Log2Func(("#%d: Address matched (is_padr=%d, is_bcast=%d, is_mcast=%d, is_prom=%d), checking FCS (fGoodFcs=%RTbool)\n", pThis->iInstance, is_padr, is_bcast, is_mcast, is_prom, fGoodFcs));
1941
1942 /* Now we have to update the FIFO. Since only 8 bytes are visible
1943 * in the FIFO after a receive, we can skip most of it.
1944 */
1945 for ( ; ofs < pktlen; ++ofs)
1946 pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
1947
1948 }
1949 else
1950 {
1951 nRsr.PRX = 1; /* Weird but true, for non-matching address only! */
1952 fAddrMatched = false;
1953 Log3Func(("#%d: Address NOT matched, ignoring FCS errors.\n", pThis->iInstance));
1954 }
1955
1956 /* The PHY bit is set when when an enabled broadcast packet is accepted,
1957 * but also when an enabled multicast packet arrives regardless of whether
1958 * it passes the MAR filter or not.
1959 */
1960 if (is_bcast || mc_type)
1961 nRsr.PHY = 1;
1962
1963 if (!fGoodFcs)
1964 nRsr.CRC = 1;
1965 }
1966 else
1967 {
1968 nRsr.CRC = 1; /* Always report CRC error if receiver isn't checking. */
1969
1970 /* Now we have to update the FIFO. Since only 8 bytes are visible
1971 * in the FIFO after a receive, we can skip most of it.
1972 */
1973 for (ofs = 0; ofs < pktlen; ++ofs)
1974 pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
1975
1976 /* Stuff the generated FCS in the FIFO. */
1977 for (ofs = 0; ofs < sizeof(abFcs); ++ofs)
1978 pCore->fifo.fifo[pCore->fifo.wp++ & 7] = abFcs[ofs];
1979 }
1980
1981 /* And now put the packet length in the FIFO. */
1982 if (fAddrMatched || 1)
1983 {
1984 pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_LOBYTE(pktlen);
1985 pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_HIBYTE(pktlen);
1986 pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_HIBYTE(pktlen); /* Yes, written twice! */
1987 }
1988
1989 Log(("FIFO: rp=%u, wp=%u\n", pCore->fifo.rp & 7, pCore->fifo.wp & 7));
1990 Log((" %Rhxs\n", &pCore->fifo.fifo));
1991
1992 if (nRsr.CRC)
1993 pCore->isr.RXE = 1;
1994 pCore->RSR = nRSR;
1995
1996 pThis->Led.Actual.s.fReading = 0;
1997
1998 /* Return success so that caller sets TSR.PTX and ISR.PTX. */
1999 rc = VINF_SUCCESS;
2000 }
2001 return rc;
2002}
2003
2004
2005/**
2006 * Reads the entire frame into the scatter gather buffer.
2007 */
2008DECLINLINE(void) dp8390CoreXmitRead(PPDMDEVINS pDevIns, const unsigned uLocalAddr, const unsigned cbFrame, PPDMSCATTERGATHER pSgBuf, bool fLoopback)
2009{
2010 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
2011 unsigned uOfs = 0;
2012 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
2013 Assert(pSgBuf->cbAvailable >= cbFrame);
2014
2015 pSgBuf->cbUsed = cbFrame;
2016
2017 LogFlowFunc(("#%d: uLocalAddr=%04X cbFrame=%d\n", pThis->iInstance, uLocalAddr, cbFrame));
2018 /* Have to figure out where the address is in local RAM. */
2019 if (pThis->uDevType == DEV_NE1000)
2020 {
2021 /* Only 14 bits of address are decoded. */
2022 uOfs = uLocalAddr & 0x3fff;
2023 if (uOfs >= 0x2000)
2024 {
2025 /* Local RAM is mapped at 2000h-3FFFh. */
2026 uOfs -= 0x2000;
2027 }
2028 else
2029 {
2030 /// @todo What are we supposed to do?!
2031 LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
2032 }
2033 }
2034 else if (pThis->uDevType == DEV_NE2000)
2035 {
2036 /* Only 15 bits of address are decoded. */
2037 uOfs = uLocalAddr & 0x7fff;
2038 if (uOfs >= 0x4000)
2039 {
2040 /* Local RAM is mapped at 4000h-7FFFh. */
2041 uOfs -= 0x4000;
2042 }
2043 else
2044 {
2045 /// @todo What are we supposed to do?!
2046 LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
2047 }
2048 }
2049 else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
2050 {
2051 /* Not much to do, WD was nice enough to put the RAM at the start of DP8390's address space. */
2052 uOfs = uLocalAddr & DPNIC_MEM_MASK;
2053 }
2054 else if (pThis->uDevType == DEV_3C503)
2055 {
2056 /* Only 14 bits of address are decoded. */
2057 uOfs = uLocalAddr & 0x3fff;
2058 if (uOfs >= 0x2000)
2059 {
2060 /* Local RAM is mapped at 2000h-3FFFh. */
2061 uOfs -= 0x2000;
2062 }
2063 else
2064 {
2065 /// @todo What are we supposed to do?!
2066 LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
2067 }
2068 }
2069 else
2070 {
2071 Assert(0);
2072 }
2073
2074 if (!fLoopback)
2075 {
2076 /* Fast path for normal transmit, ignores DCR.WTS. */
2077 if (uOfs + cbFrame <= sizeof(pThis->abLocalRAM))
2078 memcpy(pSgBuf->aSegs[0].pvSeg, &pThis->abLocalRAM[uOfs], cbFrame);
2079 else
2080 memset(pSgBuf->aSegs[0].pvSeg, 0xEE, cbFrame);
2081 }
2082 else
2083 {
2084 /* If DCR.WTS is set, only every other byte actually goes through loopback. */
2085 const uint8_t *src = &pThis->abLocalRAM[uOfs];
2086 uint8_t *dst = (uint8_t *)pSgBuf->aSegs[0].pvSeg;
2087 int cbDst = cbFrame;
2088 int step = 1 << pThis->core.dcr.WTS;
2089
2090 /* Depending on DCR.BOS, take either odd or even bytes when DCR.WTS is set. */
2091 if (pThis->core.dcr.WTS && !pThis->core.dcr.BOS)
2092 ++src;
2093
2094 while (cbDst-- && (src <= &pThis->abLocalRAM[DPNIC_MEM_SIZE]))
2095 {
2096 *dst++ = *src;
2097 src += step;
2098 }
2099
2100 /* The address should perhaps wrap around -- depends on card design. */
2101 if (cbDst != -1)
2102 {
2103 while (cbDst--)
2104 *dst++ = 0xEE;
2105 }
2106 Assert(cbDst == -1);
2107 }
2108}
2109
2110/**
2111 * Try to transmit a frame.
2112 */
2113static void dp8390CoreStartTransmit(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
2114{
2115 /*
2116 * Transmit the packet if possible, defer it if we cannot do it
2117 * in the current context.
2118 */
2119 pThis->core.TSR = 0; /* Clear transmit status. */
2120 pThis->core.NCR = 0; /* Clear collision counter. */
2121#if defined(IN_RING0) || defined(IN_RC)
2122 PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
2123 if (!pThisCC->pDrv)
2124 {
2125 int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hXmitTask);
2126 AssertRC(rc);
2127 }
2128 else
2129#endif
2130 {
2131 int rc = dp8390CoreXmitPacket(pDevIns, pThis, false /*fOnWorkerThread*/);
2132 if (rc == VERR_TRY_AGAIN)
2133 rc = VINF_SUCCESS;
2134 AssertRC(rc);
2135 }
2136}
2137
2138
2139/**
2140 * If a packet is waiting, poke the receiving machinery.
2141 *
2142 * @threads EMT.
2143 */
2144static void dp8390CoreKickReceive(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
2145{
2146 if (pThis->fMaybeOutOfSpace)
2147 {
2148 LogFlow(("Poking receive thread.\n"));
2149#ifdef IN_RING3
2150 dp8390R3WakeupReceive(pDevIns);
2151#else
2152 int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hCanRxTask);
2153 AssertRC(rc);
2154#endif
2155 }
2156}
2157
2158/**
2159 * Try transmitting a frame.
2160 *
2161 * @threads TX or EMT.
2162 */
2163static int dp8390CoreAsyncXmitLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PDPNICSTATECC pThisCC, bool fOnWorkerThread)
2164{
2165 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
2166
2167 /*
2168 * Just drop it if not transmitting. Can happen with delayed transmits
2169 * if transmit was disabled in the meantime.
2170 */
2171 if (RT_UNLIKELY(!pThis->core.cr.TXP))
2172 {
2173 LogFunc(("#%d: Nope, CR.TXP is off (fOnWorkerThread=%RTbool)\n", pThis->iInstance, fOnWorkerThread));
2174 return VINF_SUCCESS;
2175 }
2176
2177 /*
2178 * Blast out data from the packet buffer.
2179 */
2180 int rc;
2181 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
2182 do
2183 {
2184 /* Don't send anything when the link is down. */
2185 if (RT_UNLIKELY( !dp8390IsLinkUp(pThis)
2186 && pThis->cLinkDownReported > DPNIC_MAX_LINKDOWN_REPORTED)
2187 )
2188 break;
2189
2190 bool const fLoopback = pThis->core.tcr.LB != 0;
2191 PDMSCATTERGATHER SgLoop;
2192 PPDMSCATTERGATHER pSgBuf;
2193
2194 /*
2195 * Sending is easy peasy, there is by definition always
2196 * a complete packet on hand.
2197 */
2198 unsigned cb = pThis->core.TBCR; /* Packet size. */
2199 const int adr = RT_MAKE_U16(0, pThis->core.TPSR);
2200 LogFunc(("#%d: cb=%d, adr=%04X\n", pThis->iInstance, cb, adr));
2201
2202 if (RT_LIKELY(dp8390IsLinkUp(pThis) || fLoopback))
2203 {
2204 if (RT_LIKELY(cb <= MAX_FRAME))
2205 {
2206 /* Loopback fun! */
2207 if (RT_UNLIKELY(fLoopback && pThis->core.dcr.WTS))
2208 {
2209 cb /= 2;
2210 Log(("Loopback with DCR.WTS set -> cb=%d\n", cb));
2211 }
2212
2213 rc = dp8390XmitAllocBuf(pThis, pThisCC, cb, fLoopback, &SgLoop, &pSgBuf);
2214 if (RT_SUCCESS(rc))
2215 {
2216 dp8390CoreXmitRead(pDevIns, adr, cb, pSgBuf, fLoopback);
2217 rc = dp8390CoreXmitSendBuf(pDevIns, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
2218 Log2Func(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
2219 }
2220 else if (rc == VERR_TRY_AGAIN)
2221 {
2222 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2223 LogFunc(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
2224 return VINF_SUCCESS;
2225 }
2226 if (RT_SUCCESS(rc))
2227 {
2228 pThis->core.tsr.PTX = 1;
2229 pThis->core.isr.PTX = 1;
2230 }
2231 else
2232 {
2233 pThis->core.tsr.COL = 1; /* Pretend there was a collision. */
2234 pThis->core.isr.TXE = 1;
2235 }
2236 }
2237 else
2238 {
2239 /* Signal error, as this violates the Ethernet specs. Note that the DP8390
2240 * hardware does *not* limit the packet length.
2241 */
2242 LogRel(("DPNIC#%d: Attempt to transmit illegal giant frame (%u bytes) -> signaling error\n", pThis->iInstance, cb));
2243 pThis->core.tsr.OWC = 1; /* Pretend there was an out-of-window collision. */
2244 pThis->core.isr.TXE = 1;
2245 }
2246 }
2247 else
2248 {
2249 /* Signal a transmit error pretending there was a collision. */
2250 pThis->core.tsr.COL = 1;
2251 pThis->core.isr.TXE = 1;
2252 pThis->cLinkDownReported++;
2253 }
2254 /* Transmit officially done, update register state. */
2255 pThis->core.cr.TXP = 0;
2256 pThis->core.TBCR = 0;
2257 LogFlowFunc(("#%d: TSR=%02X, ISR=%02X\n", pThis->iInstance, pThis->core.TSR, pThis->core.ISR));
2258
2259 } while (0); /* No loop, because there isn't ever more than one packet to transmit. */
2260
2261 dp8390CoreUpdateIrq(pDevIns, pThis);
2262
2263 /* If there's anything waiting, this should be a good time to recheck. */
2264 dp8390CoreKickReceive(pDevIns, pThis);
2265
2266 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2267
2268 return VINF_SUCCESS;
2269}
2270
2271/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
2272
2273
2274static uint32_t dp8390CoreRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int ofs)
2275{
2276 uint8_t val;
2277
2278 /* The 3C503 can read the PROM instead of the DP8390 registers. */
2279 if (pThis->ga.gacr.ealo)
2280 return pThis->aPROM[ofs % 0xf];
2281 else if (pThis->ga.gacr.eahi)
2282 return pThis->aPROM[16 + (ofs % 0xf)];
2283
2284 /* Command Register exists in all pages. */
2285 if (ofs == DPR_CR)
2286 return pThis->core.CR;
2287
2288 if (pThis->core.cr.PS == 0)
2289 {
2290 switch (ofs)
2291 {
2292 case DPR_P0_R_CLDA0:
2293 return pThis->core.clda.CLDA0;
2294 case DPR_P0_R_CLDA1:
2295 return pThis->core.clda.CLDA1;
2296 case DPR_P0_BNRY:
2297 return pThis->core.BNRY;
2298 case DPR_P0_R_TSR:
2299 return pThis->core.TSR;
2300 case DPR_P0_R_NCR:
2301 return pThis->core.NCR;
2302 case DPR_P0_R_FIFO:
2303 return pThis->core.fifo.fifo[pThis->core.fifo.rp++ & 7]; /// @todo Abstract the mask somehow?
2304 case DPR_P0_ISR:
2305 return pThis->core.ISR;
2306 case DPR_P0_R_CRDA0:
2307 return pThis->core.crda.CRDA0;
2308 case DPR_P0_R_CRDA1:
2309 return pThis->core.crda.CRDA1;
2310 case DPR_P0_R_RSR:
2311 return pThis->core.RSR;
2312 case DPR_P0_R_CNTR0:
2313 val = pThis->core.CNTR0;
2314 pThis->core.CNTR0 = 0; /* Cleared by reading. */
2315 dp8390CoreUpdateIrq(pDevIns, pThis);
2316 return val;
2317 case DPR_P0_R_CNTR1:
2318 val = pThis->core.CNTR1;
2319 pThis->core.CNTR1 = 0; /* Cleared by reading. */
2320 dp8390CoreUpdateIrq(pDevIns, pThis);
2321 return val;
2322 case DPR_P0_R_CNTR2:
2323 val = pThis->core.CNTR2;
2324 pThis->core.CNTR2 = 0; /* Cleared by reading. */
2325 dp8390CoreUpdateIrq(pDevIns, pThis);
2326 return val;
2327 default:
2328 return 0; /// @todo or 0xFF? or something else?
2329 }
2330 }
2331 else if (pThis->core.cr.PS == 1)
2332 {
2333 /* Page 1 is easy, most registers are stored directly. */
2334 if (ofs == DPR_P1_CURR)
2335 return pThis->core.CURR;
2336 else
2337 return pThis->core.PG1[ofs];
2338 }
2339 else if (pThis->core.cr.PS == 2)
2340 {
2341 /* Page 2 is for diagnostics. Reads many registers that
2342 * are write-only in Page 0.
2343 */
2344 switch (ofs)
2345 {
2346 case DPR_P2_R_PSTART:
2347 return pThis->core.PSTART;
2348 case DPR_P2_R_PSTOP:
2349 return pThis->core.PSTOP;
2350 case DPR_P2_RNXTPP:
2351 return pThis->core.rnxtpp;
2352 case DPR_P2_R_TPSR:
2353 return pThis->core.TPSR;
2354 case DPR_P2_LNXTPP:
2355 return pThis->core.lnxtpp;
2356 case DPR_P2_ADRCU:
2357 case DPR_P2_ADRCL:
2358 return 0; /// @todo What's this?
2359 case DPR_P2_R_RCR:
2360 return pThis->core.RCR;
2361 case DPR_P2_R_TCR:
2362 return pThis->core.TCR;
2363 case DPR_P2_R_DCR:
2364 return pThis->core.DCR;
2365 case DPR_P2_R_IMR:
2366 return pThis->core.IMR;
2367 default:
2368 return 0; /// @todo Or 0xFF? Or something else?
2369 }
2370 }
2371 else
2372 {
2373 /* Page 3 is undocumented and unimplemented. */
2374 LogFunc(("Reading page 3 register: ofs=%X!\n", ofs));
2375 return 0;
2376 }
2377}
2378
2379
2380static int dp8390CoreWriteCR(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint8_t val)
2381{
2382 union {
2383 uint8_t nCR;
2384 DP_CR nCr;
2385 };
2386
2387 nCR = val;
2388 LogFlow(("val=%02X, old=%02X\n", val, pThis->core.CR));
2389 if (nCr.STP != pThis->core.cr.STP)
2390 {
2391 if (nCr.STP)
2392 {
2393 /* Stop the engine -- software reset. */
2394 pThis->core.cr.STP = 1;
2395 pThis->core.isr.RST = 1;
2396 }
2397 else
2398 {
2399 /* Clear the stop condition. */
2400 pThis->core.cr.STP = 0;
2401
2402 /* And possibly start up right away. */
2403 if (nCr.STA)
2404 pThis->core.cr.STA = 1;
2405
2406 /* The STA bit may have been set all along. */
2407 if (pThis->core.cr.STA)
2408 pThis->core.isr.RST = 0;
2409 }
2410
2411 /* Unblock receive thread if necessary, possibly drop any packets. */
2412 dp8390CoreKickReceive(pDevIns, pThis);
2413 }
2414 if (nCr.STA && !pThis->core.cr.STA)
2415 {
2416 /* Start the engine. It is not clearly documented but the STA bit is
2417 * sticky, and once it's set only a hard reset can clear it. Setting the
2418 * STP bit doesn't clear it.
2419 */
2420 pThis->core.cr.STA = 1;
2421 pThis->core.isr.RST = 0;
2422
2423 /* Unblock receive thread. */
2424 dp8390CoreKickReceive(pDevIns, pThis);
2425 }
2426 if (nCr.TXP && !pThis->core.cr.TXP)
2427 {
2428 /* Kick off a transmit. */
2429 pThis->core.cr.TXP = 1; /* Indicate transmit in progress. */
2430 dp8390CoreStartTransmit(pDevIns, pThis);
2431 }
2432
2433 /* It is not possible to write a zero (invalid value) to the RD bits. */
2434 if (nCr.RD == DP_CR_RDMA_INVL)
2435 nCr.RD = DP_CR_RDMA_ABRT;
2436
2437 if (nCr.RD != pThis->core.cr.RD)
2438 {
2439 /* Remote DMA state change. */
2440 if (nCr.RD & DP_CR_RDMA_ABRT)
2441 {
2442 /* Abort. */
2443 LogFunc(("RDMA Abort! RD=%d RSAR=%04X RBCR=%04X CRDA=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR, pThis->core.CRDA));
2444 }
2445 else if (nCr.RD == DP_CR_RDMA_SP)
2446 {
2447 DP_PKT_HDR header;
2448
2449 /* Read a packet header from memory at BNRY. */
2450 dpLocalRAMReadBuf(pThis, pThis->core.BNRY, sizeof(header), (uint8_t*)&header);
2451
2452 pThis->core.CRDA = RT_MAKE_U16(0, pThis->core.BNRY);
2453 pThis->core.RBCR = header.byte_cnt;
2454
2455 LogFunc(("RDMA SP: RD=%d RSAR=%04X RBCR=%04X CRDA=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR, pThis->core.CRDA));
2456 }
2457 else
2458 {
2459 /* Starting remote DMA read or write. */
2460 LogFunc(("RDMA: RD=%d RSAR=%04X RBCR=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR));
2461 }
2462 pThis->core.cr.RD = nCr.RD;
2463 /* NB: The current DMA address (CRDA) is not modified here. */
2464 }
2465 /* Set the page select bits. */
2466 pThis->core.cr.PS = nCr.PS;
2467
2468 return VINF_SUCCESS;
2469}
2470
2471static int dp8390CoreWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int ofs, uint8_t val)
2472{
2473 int rc = VINF_SUCCESS;
2474 bool fUpdateIRQ = false;
2475
2476 Log2Func(("#%d: page=%d reg=%X val=%02X\n", pThis->iInstance, pThis->core.cr.PS, ofs, val));
2477
2478 /* Command Register exists in all pages. */
2479 if (ofs == DPR_CR)
2480 {
2481 rc = dp8390CoreWriteCR(pDevIns, pThis, val);
2482 }
2483 else if (pThis->core.cr.PS == 0)
2484 {
2485 switch (ofs)
2486 {
2487 case DPR_P0_W_PSTART:
2488 pThis->core.PSTART = val;
2489 pThis->core.CURR = val;
2490 break;
2491 case DPR_P0_W_PSTOP:
2492 pThis->core.PSTOP = val;
2493 break;
2494 case DPR_P0_BNRY:
2495 if (pThis->core.BNRY != val)
2496 {
2497 pThis->core.BNRY = val;
2498 /* Probably made more room in receive ring. */
2499 dp8390CoreKickReceive(pDevIns, pThis);
2500 }
2501 break;
2502 case DPR_P0_W_TPSR:
2503 pThis->core.TPSR = val;
2504 break;
2505 case DPR_P0_W_TBCR0:
2506 pThis->core.tbcr.TBCR0 = val;
2507 break;
2508 case DPR_P0_W_TBCR1:
2509 pThis->core.tbcr.TBCR1 = val;
2510 break;
2511 case DPR_P0_ISR:
2512 /* Bits are cleared by writing 1 to them, except for bit 7 (RST). */
2513 pThis->core.ISR &= ~val | RT_BIT(7);
2514 fUpdateIRQ = true;
2515 break;
2516 case DPR_P0_W_RSAR0:
2517 /* NE2000 ODI driver v2.12 detects card presence by writing RSAR0
2518 * and checking if CRDA0 changes to the same value.
2519 */
2520 pThis->core.rsar.RSAR0 = val;
2521 pThis->core.crda.CRDA0 = val;
2522 break;
2523 case DPR_P0_W_RSAR1:
2524 pThis->core.rsar.RSAR1 = val;
2525 pThis->core.crda.CRDA1 = val;
2526 break;
2527 case DPR_P0_W_RBCR0:
2528 pThis->core.rbcr.RBCR0 = val;
2529 break;
2530 case DPR_P0_W_RBCR1:
2531 pThis->core.rbcr.RBCR1 = val;
2532 break;
2533 case DPR_P0_W_RCR:
2534 pThis->core.RCR = val;
2535 pThis->core.rsr.DIS = pThis->core.rcr.MON;
2536 break;
2537 case DPR_P0_W_TCR:
2538 pThis->core.TCR = val;
2539 break;
2540 case DPR_P0_W_DCR:
2541 pThis->core.DCR = val;
2542 break;
2543 case DPR_P0_W_IMR:
2544 pThis->core.IMR = val & 0x7f; /* Don't let the high bit get set. */
2545 fUpdateIRQ = true;
2546 break;
2547 default:
2548 Assert(0);
2549 break;
2550 }
2551 }
2552 else if (pThis->core.cr.PS == 1)
2553 {
2554 /* Page 1 is easy, most registers are stored directly. */
2555 if (ofs == DPR_P1_CURR)
2556 {
2557 pThis->core.CURR = val;
2558 }
2559 else
2560 pThis->core.PG1[ofs] = val;
2561 }
2562 else if (pThis->core.cr.PS == 2)
2563 {
2564 switch (ofs)
2565 {
2566 case DPR_P2_W_CLDA0:
2567 pThis->core.clda.CLDA0 = val;
2568 break;
2569 case DPR_P2_W_CLDA1:
2570 pThis->core.clda.CLDA1 = val;
2571 break;
2572 case DPR_P2_RNXTPP:
2573 pThis->core.rnxtpp = val;
2574 break;
2575 case DPR_P2_LNXTPP:
2576 pThis->core.lnxtpp = val;
2577 break;
2578 case DPR_P2_ADRCU:
2579 case DPR_P2_ADRCL:
2580 /// @todo What are these?
2581 break;
2582 default:
2583 LogFunc(("Writing unimplemented register: Page 2, offset=%d, val=%02X!\n", ofs, val));
2584 break;
2585 }
2586 }
2587 else
2588 {
2589 /* Page 3 is undocumented and unimplemented. */
2590 LogFunc(("Writing page 3 register: offset=%d, val=%02X!\n", ofs, val));
2591 }
2592
2593 if (fUpdateIRQ)
2594 dp8390CoreUpdateIrq(pDevIns, pThis);
2595
2596 return rc;
2597}
2598
2599
2600static void neLocalRAMWrite8(PDPNICSTATE pThis, uint16_t addr, uint8_t val)
2601{
2602 if (pThis->uDevType == DEV_NE1000)
2603 {
2604 /* Only 14 bits of address are decoded. */
2605 addr &= 0x3fff;
2606 if (addr >= 0x2000)
2607 {
2608 /* Local RAM is mapped at 2000h-3FFFh. */
2609 addr -= 0x2000;
2610 pThis->abLocalRAM[addr] = val;
2611 }
2612 }
2613 else if (pThis->uDevType == DEV_NE2000)
2614 {
2615 /* Only 15 bits of address are decoded. */
2616 addr &= 0x7fff;
2617 if (addr >= 0x4000)
2618 {
2619 /* Local RAM is mapped at 4000h-7FFFh. */
2620 addr -= 0x4000;
2621 pThis->abLocalRAM[addr] = val;
2622 }
2623 }
2624 else
2625 {
2626 Assert(0);
2627 }
2628}
2629
2630
2631static void neLocalRAMWrite16(PDPNICSTATE pThis, uint16_t addr, uint16_t val)
2632{
2633 if (pThis->uDevType == DEV_NE2000)
2634 {
2635 /* Only 14 bits of address are decoded, word aligned. */
2636 addr &= 0x7ffe;
2637 if (addr >= 0x4000)
2638 {
2639 /* Local RAM is mapped at 4000h-7FFFh. */
2640 addr -= 0x4000;
2641 pThis->abLocalRAM[addr+0] = RT_LOBYTE(val);
2642 pThis->abLocalRAM[addr+1] = RT_HIBYTE(val);
2643 }
2644 }
2645 else
2646 {
2647 Assert(0);
2648 }
2649}
2650
2651
2652static uint8_t neLocalRAMRead8(PDPNICSTATE pThis, uint16_t addr)
2653{
2654 uint8_t val = 0xff;
2655
2656 if (pThis->uDevType == DEV_NE1000)
2657 {
2658 /* Only 14 bits of address are decoded. */
2659 addr &= 0x3fff;
2660 if (addr >= 0x2000)
2661 {
2662 /* Local RAM is mapped at 2000h-3FFFh. */
2663 addr -= 0x2000;
2664 val = pThis->abLocalRAM[addr];
2665 }
2666 else
2667 {
2668 /* The PROM is mapped below 2000h, effectively only 4 bits decoded.
2669 * NE1000 emulation uses top 16 bytes of the PROM.
2670 */
2671 val = pThis->aPROM[(addr & 0x0f) + 16]; /// @todo Use a constant
2672 }
2673 }
2674 else if (pThis->uDevType == DEV_NE2000)
2675 {
2676 /* Only 15 bits of address are decoded. */
2677 addr &= 0x7fff;
2678 if (addr >= 0x4000)
2679 {
2680 /* Local RAM is mapped at 4000h-7FFFh. */
2681 addr -= 0x4000;
2682 val = pThis->abLocalRAM[addr];
2683 }
2684 else
2685 {
2686 /* The PROM is mapped below 4000h, effectively only 4 bits decoded.
2687 * Address bits 1:4 from the bus are connected to address pins 0:3
2688 * on the PROM.
2689 */
2690 val = pThis->aPROM[(addr & 0x1f) >> 1]; /// @todo use a constant
2691 }
2692 }
2693 else
2694 {
2695 Assert(0);
2696 }
2697 return val;
2698}
2699
2700
2701static uint16_t neLocalRAMRead16(PDPNICSTATE pThis, uint16_t addr)
2702{
2703 uint16_t val = 0xffff;
2704
2705 if (pThis->uDevType == DEV_NE2000)
2706 {
2707 /* Only 14 bits of address are decoded, word aligned. */
2708 addr &= 0x7ffe;
2709 if (addr >= 0x4000)
2710 {
2711 /* Local RAM is mapped at 4000h-7FFFh. */
2712 addr -= 0x4000;
2713 val = RT_MAKE_U16(pThis->abLocalRAM[addr], pThis->abLocalRAM[addr+1]);
2714 }
2715 else
2716 {
2717 uint8_t uPromByte;
2718
2719 /* The PROM is mapped below 4000h, effectively only 4 bits decoded.
2720 * Address bits 1:4 from the bus are connected to address pins 0:3
2721 * on the PROM.
2722 */
2723 uPromByte = pThis->aPROM[(addr & 0x1f) >> 1];
2724 val = RT_MAKE_U16(uPromByte, uPromByte);
2725 }
2726 }
2727 else
2728 {
2729 Assert(0);
2730 }
2731 return val;
2732}
2733
2734
2735static int neDataPortWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint16_t val)
2736{
2737 /* Remote Write; ignored if Remote DMA command is not 'Write'. */
2738 if (pThis->core.cr.RD == DP_CR_RDMA_WR)
2739 {
2740 /// @todo Also do nothing if DCR.LAS set?
2741 if (pThis->core.dcr.WTS)
2742 {
2743 Log3Func(("RDMA16 write %04X to local addr %04X\n", val, pThis->core.CRDA));
2744 neLocalRAMWrite16(pThis, pThis->core.CRDA, val);
2745 }
2746 else
2747 {
2748 Log3Func(("RDMA8 write %02X to local addr %04X\n", val, pThis->core.CRDA));
2749 neLocalRAMWrite8(pThis, pThis->core.CRDA, val);
2750 }
2751 pThis->core.CRDA += 1 << pThis->core.dcr.WTS;
2752 if ((pThis->core.crda.CRDA1 == pThis->core.PSTOP) && (pThis->core.PSTOP != pThis->core.PSTART))
2753 {
2754 LogFunc(("RDMA wrap / write!! (CRDA=%04X PSTOP=%02X00 PSTART=%02X00)\n", pThis->core.CRDA, pThis->core.PSTOP, pThis->core.PSTART));
2755 Assert(!pThis->core.crda.CRDA0); /// @todo Can misalignment actually happen?
2756 pThis->core.crda.CRDA1 = pThis->core.PSTART;
2757 }
2758 pThis->core.RBCR -= 1;
2759
2760 /* Carefully decrement if WTS set so we don't overshoot and miss EOP. */
2761 if (pThis->core.dcr.WTS && pThis->core.RBCR)
2762 pThis->core.RBCR -= 1;
2763
2764 if (!pThis->core.RBCR)
2765 {
2766 LogFunc(("RDMA EOP / write\n"));
2767 pThis->core.isr.RDC = 1;
2768 pThis->core.cr.RD = 0;
2769 dp8390CoreUpdateIrq(pDevIns, pThis);
2770 }
2771 }
2772 return VINF_SUCCESS;
2773}
2774
2775
2776static uint16_t neDataPortRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
2777{
2778 uint16_t val = 0x1234;
2779
2780 /* Remote Read; ignored if Remote DMA command is not 'Read'. */
2781 if (pThis->core.cr.RD == DP_CR_RDMA_RD)
2782 {
2783 /// @todo Also do nothing if DCR.LAS set?
2784 if (pThis->core.dcr.WTS)
2785 {
2786 val = neLocalRAMRead16(pThis, pThis->core.CRDA);
2787 Log3Func(("RDMA16 read from local addr %04X: %04X\n", pThis->core.CRDA, val));
2788 }
2789 else
2790 {
2791 val = neLocalRAMRead8(pThis, pThis->core.CRDA);
2792 Log3Func(("RDMA8 read from local addr %04X: %02X\n", pThis->core.CRDA, val));
2793 }
2794 pThis->core.CRDA += 1 << pThis->core.dcr.WTS;
2795 /// @todo explain that PSTOP=PSTART check is only to reduce logging/busywork
2796 if ((pThis->core.crda.CRDA1 == pThis->core.PSTOP) && (pThis->core.PSTOP != pThis->core.PSTART))
2797 {
2798 Log3Func(("RDMA wrap / read (CRDA=%04X PSTOP=%02X00 PSTART=%02X00)\n", pThis->core.CRDA, pThis->core.PSTOP, pThis->core.PSTART));
2799 Assert(!pThis->core.crda.CRDA0); /// @todo can misalignment happen?
2800 pThis->core.crda.CRDA1 = pThis->core.PSTART;
2801 }
2802 pThis->core.RBCR -= 1;
2803
2804 /* Carefully decrement if WTS set so we don't overshoot and miss EOP. */
2805 if (pThis->core.dcr.WTS && pThis->core.RBCR)
2806 pThis->core.RBCR -= 1;
2807
2808 if (!pThis->core.RBCR)
2809 {
2810 LogFunc(("RDMA EOP / read\n"));
2811 pThis->core.isr.RDC = 1;
2812 pThis->core.cr.RD = 0;
2813 dp8390CoreUpdateIrq(pDevIns, pThis);
2814 }
2815 }
2816 return val;
2817}
2818
2819
2820static int neResetPortWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
2821{
2822 LogFlowFunc(("\n"));
2823 dp8390CoreReset(pDevIns, pThis);
2824 return VINF_SUCCESS;
2825}
2826
2827
2828static int dpNeIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int addr, uint16_t val)
2829{
2830 int reg = addr & 0x0f;
2831 int rc = VINF_SUCCESS;
2832
2833 Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val));
2834
2835 /* The NE2000 has 8 bytes of data port followed by 8 bytes of reset port.
2836 * In contrast, the NE1000 has 4 bytes of data port followed by 4 bytes
2837 * of reset port, aliased twice within the 16-byte range.
2838 */
2839 if (pThis->uDevType == DEV_NE2000)
2840 reg >>= 1;
2841 if (reg & 0x04)
2842 rc = neResetPortWrite(pDevIns, pThis);
2843 else
2844 rc = neDataPortWrite(pDevIns, pThis, val);
2845
2846 return rc;
2847}
2848
2849
2850static uint16_t neIoRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int addr)
2851{
2852 uint16_t val = UINT16_MAX;
2853 int reg = addr & 0x0f;
2854
2855 /* The NE2000 has 8 bytes of data port followed by 8 bytes of reset port.
2856 * In contrast, the NE1000 has 4 bytes of data port followed by 4 bytes
2857 * of reset port, aliased twice within the 16-byte range.
2858 */
2859 if (pThis->uDevType == DEV_NE2000)
2860 reg >>= 1;
2861 if (reg & 0x04)
2862 val = 0x52; /// @todo Check what really happens
2863 else
2864 val = neDataPortRead(pDevIns, pThis);
2865
2866 Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val));
2867 return val;
2868}
2869
2870
2871static int wdIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int addr, uint8_t val)
2872{
2873 int reg = addr & 0xf;
2874 int rc = VINF_SUCCESS;
2875 union {
2876 uint8_t nCTRL1;
2877 WD_CTRL1 nCtrl1;
2878 };
2879 union {
2880 uint8_t nCTRL2;
2881 WD_CTRL2 nCtrl2;
2882 };
2883
2884 Log2Func(("#%d: addr=%#06x val=%#02x\n", pThis->iInstance, addr, val));
2885
2886 switch (reg)
2887 {
2888 case WDR_CTRL1:
2889 nCTRL1 = val;
2890 if (nCtrl1.MEME != pThis->ctrl1.MEME)
2891 {
2892 LogFunc(("CTRL1.MEME=%u\n", nCtrl1.MEME));
2893 pThis->ctrl1.MEME = nCtrl1.MEME;
2894 }
2895 if (nCtrl1.RESET)
2896 {
2897 dp8390CoreReset(pDevIns, pThis);
2898 pThis->CTRL1 = 0;
2899 }
2900 break;
2901 case WDR_CTRL2:
2902 /* NYI. */
2903 nCTRL2 = val;
2904 if (nCTRL2 != pThis->CTRL2)
2905 {
2906 LogFunc(("CTRL2=%02X, new=%02X\n", pThis->CTRL2, nCTRL2));
2907 pThis->CTRL2 = nCTRL2;
2908 }
2909 break;
2910 default:
2911 /* Most of the WD registers are read-only. */
2912 break;
2913 }
2914
2915 return rc;
2916}
2917
2918
2919static uint8_t wdIoRead(PDPNICSTATE pThis, int addr)
2920{
2921 uint8_t val = UINT8_MAX;
2922 int reg = addr & 0x0f;
2923
2924 if (reg >= WDR_PROM)
2925 {
2926 val = pThis->aPROM[reg & 7];
2927 }
2928 else
2929 {
2930 if (pThis->uDevType == DEV_WD8013)
2931 {
2932 switch (reg)
2933 {
2934 case WDR_CTRL1:
2935 val = pThis->CTRL1;
2936 break;
2937 case WDR_ATDET:
2938 val = 1 /* Always 1 for DEV_WD8013. */;
2939 break;
2940 case WDR_IOBASE:
2941 val = pThis->aPROM[WDR_IOBASE]; //val = pThis->IOPortBase >> 5;
2942 break;
2943 case WDR_CTRL2:
2944 val = pThis->CTRL2;
2945 break;
2946 case WDR_JP:
2947 val = 0xa0;
2948 break;
2949 default:
2950 val = 0x00; /// @todo What should it be really?
2951 break;
2952 }
2953 }
2954 else
2955 {
2956 /* Old WD adapters (including 8003E) aliased the PROM for
2957 * unimplemented control register reads.
2958 */
2959 switch (reg)
2960 {
2961 case WDR_CTRL2:
2962 val = 1; //pThis->CTRL2;
2963 break;
2964 case WDR_JP:
2965 val = 0xa0;
2966 break;
2967 default:
2968 val = pThis->aPROM[reg & 7];
2969 break;
2970 }
2971 }
2972
2973 }
2974
2975 Log2Func(("#%d: addr=%#04x val=%#04x\n", pThis->iInstance, addr, val));
2976 return val;
2977}
2978
2979
2980static uint8_t elGetIrqFromIdcfr(uint8_t val)
2981{
2982 union {
2983 uint8_t IDCFR;
2984 EL_IDCFR idcfr;
2985 };
2986 uint8_t irq = 0;
2987
2988 IDCFR = val;
2989
2990 /* Lowest set IRQ bit wins (might not match hardware).
2991 * NB: It is valid to not enable any IRQ line!
2992 */
2993 if (idcfr.irq2)
2994 irq = 2;
2995 else if (idcfr.irq3)
2996 irq = 3;
2997 else if (idcfr.irq4)
2998 irq = 4;
2999 else if (idcfr.irq5)
3000 irq = 5;
3001
3002 return irq;
3003}
3004
3005static uint8_t elGetDrqFromIdcfr(uint8_t val)
3006{
3007 union {
3008 uint8_t IDCFR;
3009 EL_IDCFR idcfr;
3010 };
3011 uint8_t drq = 0;
3012
3013 IDCFR = val;
3014
3015 /* Lowest set DRQ bit wins; it is valid to not set any. */
3016 if (idcfr.drq1)
3017 drq = 1;
3018 else if (idcfr.drq2)
3019 drq = 2;
3020 else if (idcfr.drq3)
3021 drq = 3;
3022
3023 return drq;
3024}
3025
3026static void elWriteIdcfr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
3027{
3028 uint8_t uOldIrq = pThis->uIsaIrq;
3029 uint8_t uNewIrq;
3030 uint8_t uOldDrq = pThis->uElIsaDma;
3031 uint8_t uNewDrq;
3032
3033 /* If the IRQ is currently active, have to switch it. */
3034 uNewIrq = elGetIrqFromIdcfr(val);
3035 if (uOldIrq != uNewIrq)
3036 {
3037 LogFunc(("#%d Switching IRQ=%d -> IRQ=%d\n", pThis->iInstance, uOldIrq, uNewIrq));
3038 if (pThis->fNicIrqActive)
3039 {
3040 /* This probably isn't supposed to happen. */
3041 LogFunc(("#%d Moving active IRQ!\n", pThis->iInstance));
3042 if (uOldIrq)
3043 PDMDevHlpISASetIrq(pDevIns, uOldIrq, 0);
3044 if (uNewIrq)
3045 PDMDevHlpISASetIrq(pDevIns, uNewIrq, 1);
3046 }
3047 pThis->uIsaIrq = uNewIrq;
3048 }
3049
3050 /* And now the same dance for DMA. */
3051 uNewDrq = elGetDrqFromIdcfr(val);
3052 if (uOldDrq != uNewDrq)
3053 {
3054 /// @todo We can't really move the DRQ, what can we do?
3055 LogFunc(("#%d Switching DRQ=%d -> DRQ=%d\n", pThis->iInstance, uOldDrq, uNewDrq));
3056 pThis->uElIsaDma = uNewDrq;
3057 }
3058
3059 pGa->IDCFR = val;
3060}
3061
3062
3063static void elWriteGacfr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
3064{
3065 union {
3066 uint8_t nGACFR;
3067 GA_GACFR nGacfr;
3068 };
3069
3070 nGACFR = val;
3071
3072 if (nGacfr.nim != pGa->gacfr.nim)
3073 {
3074 /// @todo Should we just run UpdateInterrupts?
3075 if (pThis->fNicIrqActive && !nGacfr.nim)
3076 {
3077 LogFunc(("#%d: Unmasking active IRQ!\n", pThis->iInstance));
3078 PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, 1);
3079 }
3080 else if (pThis->fNicIrqActive && nGacfr.nim)
3081 {
3082 LogFunc(("#%d: Masking active IRQ\n", pThis->iInstance));
3083 PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, 0);
3084 }
3085 }
3086
3087 /// @todo rsel/mbs bit change?
3088 if (nGacfr.rsel != pGa->gacfr.rsel)
3089 {
3090 LogFunc(("#%d: rsel=%u mbs=%u\n", pThis->iInstance, nGacfr.rsel, nGacfr.mbs));
3091 }
3092
3093 pGa->GACFR = nGACFR;
3094}
3095
3096
3097static void elSoftReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
3098{
3099 PEL_GA pGa = &pThis->ga;
3100
3101 LogFlow(("Resetting ASIC GA\n"));
3102 /* Most GA registers are zeroed. */
3103 pGa->PSTR = pGa->PSPR = 0;
3104 pGa->DQTR = 0;
3105 elWriteGacfr(pDevIns, pThis, pGa, 0);
3106 pGa->STREG = ELNKII_GA_REV;
3107 pGa->VPTR0 = pGa->VPTR1 = pGa->VPTR2 = 0;
3108 pGa->DALSB = pGa->DAMSB = 0;
3109 elWriteIdcfr(pDevIns, pThis, pGa, 0);
3110 pGa->GACR = 0x0B; /* Low bit set = in reset state. */
3111 pGa->fGaIrq = false;
3112
3113 /* Reset the NIC core. */
3114 dp8390CoreReset(pDevIns, pThis);
3115}
3116
3117
3118static int elWriteGacr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
3119{
3120 union {
3121 uint8_t nGACR;
3122 GA_GACR nGacr;
3123 };
3124
3125 nGACR = val;
3126
3127 if (nGacr.rst != pGa->gacr.rst)
3128 {
3129 /* When going out of reset, only clear the rst bit. 3C503 diagnostics checks for this. */
3130 if (nGacr.rst)
3131 elSoftReset(pDevIns, pThis);
3132 else
3133 pGa->gacr.rst = 0;
3134 }
3135 else
3136 {
3137#ifdef IN_RING0
3138 /* Force a trip to R3. */
3139 if (pThis->uElIsaDma == pThis->uIsaDma)
3140 return VINF_IOM_R3_IOPORT_WRITE;
3141#endif
3142
3143 /* Make the data registers "ready" as long as transfers are started. */
3144 if (nGacr.start)
3145 {
3146 pGa->cdadr.cdadr_lsb = pGa->DALSB;
3147 pGa->cdadr.cdadr_msb = pGa->DAMSB;
3148 LogFunc(("DMA started, ddir=%u, cdadr=%04X\n", pGa->gacr.ddir, pGa->CDADR));
3149 pGa->streg.dprdy = 1;
3150 pGa->streg.dip = 1;
3151 pGa->streg.dtc = 0;
3152 }
3153 else
3154 {
3155 pGa->streg.dprdy = 0;
3156 pGa->streg.dip = 0;
3157 }
3158
3159 /* Only do anything if the software configured DMA channel matches the emulation config. */
3160 if (pThis->uElIsaDma == pThis->uIsaDma)
3161 {
3162#ifdef IN_RING3
3163 PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, pGa->streg.dprdy);
3164 if (pGa->streg.dprdy)
3165 PDMDevHlpDMASchedule(pDevIns);
3166 LogFunc(("#%d: DREQ for channel %u set to %u\n", pThis->iInstance, pThis->uIsaDma, pGa->streg.dprdy));
3167#else
3168 /* Must not get here. */
3169 Assert(0);
3170#endif
3171 }
3172
3173 pGa->GACR = nGACR;
3174 LogFunc(("GACR=%02X ealo=%u eahi=%u\n", pGa->GACR, pGa->gacr.ealo, pGa->gacr.eahi));
3175 }
3176
3177 return VINF_SUCCESS;
3178}
3179
3180/* The Register File LSB (GAR_RFLSB) port supports 16-bit I/O access, but
3181 * we internally split everything up into 8-bit access anyway.
3182 * That goes for both reading and writing.
3183 */
3184static void elGaDataWrite(PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
3185{
3186 /* Data write; ignored if not started and in "download" mode. */
3187 if (pGa->gacr.start && pGa->gacr.ddir)
3188 {
3189 uint16_t addr = pGa->CDADR;
3190
3191 addr &= 0x3fff;
3192 if (addr >= 0x2000)
3193 {
3194 /* Local RAM is mapped at 2000h-3FFFh. */
3195 addr -= 0x2000;
3196 pThis->abLocalRAM[addr] = val;
3197 }
3198
3199 pGa->CDADR++;
3200 /// @todo Does this really apply to writes or only reads?
3201 if ((pGa->cdadr.cdadr_msb == pGa->PSPR) && (pGa->PSPR != pGa->PSTR))
3202 {
3203 LogFunc(("GA DMA wrap / write!! (cdadr=%04X PSPR=%02X00 PSTR=%02X00)\n", pGa->CDADR, pGa->PSPR, pGa->PSTR));
3204 pGa->cdadr.cdadr_msb = pGa->PSTR;
3205 }
3206 }
3207}
3208
3209
3210static uint8_t elGaDataRead(PDPNICSTATE pThis, PEL_GA pGa)
3211{
3212 uint8_t val = 0xcd;
3213
3214 /* Data read; ignored if not started and in "upload" mode. */
3215 if (pGa->gacr.start && !pGa->gacr.ddir)
3216 {
3217 uint16_t addr = pGa->CDADR;
3218
3219 addr &= 0x3fff;
3220 if (addr >= 0x2000)
3221 {
3222 /* Local RAM is mapped at 2000h-3FFFh. */
3223 addr -= 0x2000;
3224 val = pThis->abLocalRAM[addr];
3225 }
3226
3227 pGa->CDADR++;
3228 if ((pGa->cdadr.cdadr_msb == pGa->PSPR) && (pGa->PSPR != pGa->PSTR))
3229 {
3230 LogFunc(("GA DMA wrap / read!! (cdadr=%04X PSPR=%02X00 PSTR=%02X00)\n", pGa->CDADR, pGa->PSPR, pGa->PSTR));
3231 pGa->cdadr.cdadr_msb = pGa->PSTR;
3232 }
3233 }
3234 return val;
3235}
3236
3237
3238static int elGaIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int addr, uint8_t val)
3239{
3240 int reg = addr & 0xf;
3241 int rc = VINF_SUCCESS;
3242 PEL_GA pGa = &pThis->ga;
3243
3244 Log2Func(("#%d: addr=%#04x val=%#02x\n", pThis->iInstance, addr, val));
3245
3246 switch (reg)
3247 {
3248 case GAR_PSTR:
3249 pGa->PSTR = val;
3250 break;
3251 case GAR_PSPR:
3252 pGa->PSPR = val;
3253 break;
3254 case GAR_DQTR:
3255 pGa->DQTR = val;
3256 break;
3257 case GAR_GACFR:
3258 elWriteGacfr(pDevIns, pThis, pGa, val);
3259 break;
3260 case GAR_GACR:
3261 rc = elWriteGacr(pDevIns, pThis, pGa, val);
3262 break;
3263 case GAR_STREG:
3264 /* Writing anything to STREG clears ASIC interrupt. */
3265 pThis->ga.streg.dtc = 0;
3266 pThis->ga.fGaIrq = false;
3267 dp8390CoreUpdateIrq(pDevIns, pThis);
3268 break;
3269 case GAR_IDCFR:
3270 elWriteIdcfr(pDevIns, pThis, pGa, val);
3271 break;
3272 case GAR_DAMSB:
3273 pGa->DAMSB = val;
3274 break;
3275 case GAR_DALSB:
3276 pGa->DALSB = val;
3277 break;
3278 case GAR_VPTR2:
3279 pGa->VPTR2 = val;
3280 break;
3281 case GAR_VPTR1:
3282 pGa->VPTR1 = val;
3283 break;
3284 case GAR_VPTR0:
3285 pGa->VPTR0 = val;
3286 break;
3287 case GAR_RFMSB:
3288 case GAR_RFLSB:
3289 elGaDataWrite(pThis, pGa, val);
3290 break;
3291 case GAR_R_BCFR:
3292 case GAR_R_PCFR:
3293 /* Read-only registers, ignored. */
3294 break;
3295 default:
3296 Assert(0);
3297 break;
3298 }
3299
3300 return rc;
3301}
3302
3303
3304static uint8_t elGaIoRead(PDPNICSTATE pThis, int addr)
3305{
3306 uint8_t val = UINT8_MAX;
3307 int reg = addr & 0x0f;
3308 PEL_GA pGa = &pThis->ga;
3309
3310 switch (reg)
3311 {
3312 case GAR_PSTR:
3313 val = pGa->PSTR;
3314 break;
3315 case GAR_PSPR:
3316 val = pGa->PSPR;
3317 break;
3318 case GAR_DQTR:
3319 val = pGa->DQTR;
3320 break;
3321 case GAR_R_BCFR:
3322 val = pGa->BCFR;
3323 break;
3324 case GAR_R_PCFR:
3325 val = pGa->PCFR;
3326 break;
3327 case GAR_GACFR:
3328 val = pGa->GACFR;
3329 break;
3330 case GAR_GACR:
3331 val = pGa->GACR;
3332 break;
3333 case GAR_STREG:
3334 val = pGa->STREG;
3335 break;
3336 case GAR_IDCFR:
3337 val = pGa->IDCFR;
3338 break;
3339 case GAR_DAMSB:
3340 val = pGa->DAMSB;
3341 break;
3342 case GAR_DALSB:
3343 val = pGa->DALSB;
3344 break;
3345 case GAR_VPTR2:
3346 val = pGa->VPTR2;
3347 break;
3348 case GAR_VPTR1:
3349 val = pGa->VPTR1;
3350 break;
3351 case GAR_VPTR0:
3352 val = pGa->VPTR0;
3353 break;
3354 case GAR_RFMSB:
3355 case GAR_RFLSB:
3356 val = elGaDataRead(pThis, pGa);
3357 break;
3358 default:
3359 Assert(0);
3360 break;
3361 }
3362
3363 Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val));
3364 return val;
3365}
3366
3367
3368/**
3369 * @callback_method_impl{FNIOMIOPORTIN}
3370 */
3371static DECLCALLBACK(VBOXSTRICTRC)
3372neIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3373{
3374 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3375 int rc = VINF_SUCCESS;
3376 RTIOPORT reg = Port & 0xf;
3377 uint8_t u8Lo, u8Hi = 0;
3378 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
3379 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3380 RT_NOREF_PV(pvUser);
3381
3382 switch (cb)
3383 {
3384 case 1:
3385 *pu32 = neIoRead(pDevIns, pThis, Port);
3386 break;
3387 case 2:
3388 /* Manually split word access if necessary if it's an NE1000. Perhaps overkill. */
3389 if (pThis->uDevType == DEV_NE1000)
3390 {
3391 u8Lo = neIoRead(pDevIns, pThis, reg);
3392 if (reg < 0xf) // This logic is not entirely accurate (wraparound).
3393 u8Hi = neIoRead(pDevIns, pThis, reg + 1);
3394 *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
3395 }
3396 else
3397 *pu32 = neIoRead(pDevIns, pThis, reg);
3398 break;
3399 default:
3400 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3401 "neIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
3402 Port, cb);
3403 }
3404
3405 Log2Func(("#%d: NE Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
3406 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
3407 return rc;
3408}
3409
3410
3411/**
3412 * @callback_method_impl{FNIOMIOPORTIN}
3413 */
3414static DECLCALLBACK(VBOXSTRICTRC)
3415wdIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3416{
3417 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3418 int rc = VINF_SUCCESS;
3419 int reg = Port & 0xf;
3420 uint8_t u8Lo, u8Hi = 0;
3421 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
3422 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3423 RT_NOREF_PV(pvUser);
3424
3425 switch (cb)
3426 {
3427 case 1:
3428 *pu32 = wdIoRead(pThis, reg);
3429 break;
3430 case 2:
3431 /* Manually split word access. */
3432 u8Lo = wdIoRead(pThis, reg);
3433 if (reg < 0xf) // This logic is not entirely accurate (wraparound).
3434 u8Hi = wdIoRead(pThis, reg + 1);
3435 *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
3436 break;
3437 default:
3438 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3439 "wdIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
3440 Port, cb);
3441 }
3442
3443 Log2Func(("#%d: WD Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
3444 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
3445 return rc;
3446}
3447
3448
3449/**
3450 * @callback_method_impl{FNIOMIOPORTIN}
3451 */
3452static DECLCALLBACK(VBOXSTRICTRC)
3453elIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3454{
3455 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3456 int rc = VINF_SUCCESS;
3457 int reg = Port & 0xf;
3458 uint8_t u8Lo, u8Hi = 0;
3459 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
3460 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3461 RT_NOREF_PV(pvUser);
3462
3463 switch (cb)
3464 {
3465 case 1:
3466 *pu32 = elGaIoRead(pThis, Port);
3467 break;
3468 case 2:
3469 /* Manually split word access. */
3470 u8Lo = elGaIoRead(pThis, reg);
3471 if (reg < 0xf) // This logic is not entirely accurate (wraparound).
3472 u8Hi = elGaIoRead(pThis, reg + 1);
3473 *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
3474 break;
3475 default:
3476 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3477 "elIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
3478 Port, cb);
3479 }
3480
3481 Log2Func(("#%d: EL Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
3482 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
3483 return rc;
3484}
3485
3486
3487/**
3488 * @callback_method_impl{FNIOMIOPORTIN}
3489 */
3490static DECLCALLBACK(VBOXSTRICTRC)
3491dp8390CoreIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3492{
3493 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3494 int rc = VINF_SUCCESS;
3495 int reg = Port & 0xf;
3496 uint8_t u8Lo, u8Hi;
3497 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
3498 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3499 RT_NOREF_PV(pvUser);
3500
3501 switch (cb)
3502 {
3503 case 1:
3504 *pu32 = dp8390CoreRead(pDevIns, pThis, reg);
3505 break;
3506 case 2:
3507 /* Manually split word access. */
3508 u8Lo = dp8390CoreRead(pDevIns, pThis, reg + 0);
3509 /* This logic is not entirely accurate. */
3510 if (reg < 0xf)
3511 u8Hi = dp8390CoreRead(pDevIns, pThis, reg + 1);
3512 else
3513 u8Hi = 0;
3514 *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
3515 break;
3516 default:
3517 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3518 "dp8390CoreIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
3519 Port, cb);
3520 }
3521
3522 Log2Func(("#%d: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
3523 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
3524 return rc;
3525}
3526
3527
3528/**
3529 * @callback_method_impl{FNIOMIOPORTOUT}
3530 */
3531static DECLCALLBACK(VBOXSTRICTRC)
3532neIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3533{
3534 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3535 int rc = VINF_SUCCESS;
3536 int reg = Port & 0xf;
3537 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3538 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3539 RT_NOREF_PV(pvUser);
3540
3541 switch (cb)
3542 {
3543 case 1:
3544 rc = dpNeIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
3545 break;
3546 case 2:
3547 /* Manually split word access if necessary. */
3548 if (pThis->uDevType == DEV_NE2000)
3549 {
3550 rc = dpNeIoWrite(pDevIns, pThis, Port, RT_LOWORD(u32));
3551 }
3552 else
3553 {
3554 rc = dpNeIoWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(RT_LOWORD(u32)));
3555 if (RT_SUCCESS(rc) && (reg < 0xf))
3556 rc = dpNeIoWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(RT_LOWORD(u32)));
3557 }
3558 break;
3559 default:
3560 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3561 "neIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
3562 Port, cb);
3563 }
3564
3565 Log2Func(("#%d: NE Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
3566 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3567 return rc;
3568}
3569
3570
3571/**
3572 * @callback_method_impl{FNIOMIOPORTOUT}
3573 */
3574static DECLCALLBACK(VBOXSTRICTRC)
3575wdIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3576{
3577 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3578 int rc = VINF_SUCCESS;
3579 int reg = Port & 0xf;
3580 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3581 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3582 RT_NOREF_PV(pvUser);
3583
3584 switch (cb)
3585 {
3586 case 1:
3587 rc = wdIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
3588 break;
3589 case 2:
3590 /* Manually split word access. */
3591 rc = wdIoWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(RT_LOWORD(u32)));
3592 if (RT_SUCCESS(rc) && (reg < 0xf))
3593 rc = wdIoWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(RT_LOWORD(u32)));
3594 break;
3595 default:
3596 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3597 "wdIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
3598 Port, cb);
3599 }
3600
3601 Log2Func(("#%d: WD Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
3602 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3603 return rc;
3604}
3605
3606
3607/**
3608 * @callback_method_impl{FNIOMIOPORTOUT}
3609 */
3610static DECLCALLBACK(VBOXSTRICTRC)
3611elIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3612{
3613 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3614 int rc = VINF_SUCCESS;
3615 int reg = Port & 0xf;
3616 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3617 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3618 RT_NOREF_PV(pvUser);
3619
3620 switch (cb)
3621 {
3622 case 1:
3623 rc = elGaIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
3624 break;
3625 case 2:
3626 /* Manually split word access. */
3627 rc = elGaIoWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(RT_LOWORD(u32)));
3628 if (RT_SUCCESS(rc) && (reg < 0xf))
3629 rc = elGaIoWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(RT_LOWORD(u32)));
3630 break;
3631 default:
3632 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3633 "elIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
3634 Port, cb);
3635 }
3636
3637 Log2Func(("#%d: EL Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
3638 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3639 return rc;
3640}
3641
3642
3643/**
3644 * @callback_method_impl{FNIOMIOPORTOUT}
3645 */
3646static DECLCALLBACK(VBOXSTRICTRC)
3647dp8390CoreIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3648{
3649 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3650 int rc = VINF_SUCCESS;
3651 int reg = Port & 0xf;
3652 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3653 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3654 RT_NOREF_PV(pvUser);
3655
3656 switch (cb)
3657 {
3658 case 1:
3659 rc = dp8390CoreWrite(pDevIns, pThis, reg, RT_LOBYTE(u32));
3660 break;
3661 case 2:
3662 /* Manually split word access. */
3663 rc = dp8390CoreWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(RT_LOWORD(u32)));
3664 if (!RT_SUCCESS(rc))
3665 break;
3666 rc = dp8390CoreWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(RT_LOWORD(u32)));
3667 break;
3668 default:
3669 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
3670 "dp8390CoreIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
3671 Port, cb);
3672 }
3673
3674 Log2Func(("#%d: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
3675 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3676 return rc;
3677}
3678
3679
3680#if 0
3681/**
3682 * @callback_method_impl{FNIOMMMIONEWFILL,
3683 * Local RAM write hook\, to be called from IOM. This is the advanced version of
3684 * wdMemWrite function.}
3685 */
3686static DECLCALLBACK(VBOXSTRICTRC)
3687dpWdMmioFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3688{
3689 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3690 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3691
3692 !!
3693 return VINF_SUCCESS
3694}
3695#endif
3696
3697
3698/**
3699 * @callback_method_impl{FNIOMMMIONEWREAD,
3700 * Local RAM read hook\, to be called from IOM.}
3701 */
3702static DECLCALLBACK(VBOXSTRICTRC) wdMemRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3703{
3704 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3705 uint8_t *pbData = (uint8_t *)pv;
3706 NOREF(pvUser);
3707
3708// STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3709 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3710
3711 if (pThis->ctrl1.MEME)
3712 {
3713 Log3Func(("#%d: Reading %u bytes from address %X: [%.*Rhxs]\n", pThis->iInstance, cb, off, cb, &pThis->abLocalRAM[off & DPNIC_MEM_MASK]));
3714 while (cb-- > 0)
3715 *pbData++ = pThis->abLocalRAM[off++ & DPNIC_MEM_MASK];
3716 }
3717 else
3718 memset(pv, 0xff, cb);
3719
3720// STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3721 return VINF_SUCCESS;
3722}
3723
3724/**
3725 * @callback_method_impl{FNIOMMMIONEWWRITE,
3726 * Local RAM write hook\, to be called from IOM.}
3727 */
3728static DECLCALLBACK(VBOXSTRICTRC) wdMemWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3729{
3730 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3731 uint8_t const *pbSrc = (uint8_t const *)pv;
3732 NOREF(pvUser);
3733
3734// STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3735 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3736
3737 if (pThis->ctrl1.MEME)
3738 {
3739 Log3Func(("#%d: Writing %u bytes to address %X: [%.*Rhxs]\n", pThis->iInstance, cb, off, cb, pbSrc));
3740 while (cb-- > 0)
3741 pThis->abLocalRAM[off++ & DPNIC_MEM_MASK] = *pbSrc++;
3742 }
3743
3744// STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3745 return VINF_SUCCESS;
3746}
3747
3748
3749/**
3750 * @callback_method_impl{FNIOMMMIONEWREAD,
3751 * Local RAM read hook\, to be called from IOM.}
3752 */
3753static DECLCALLBACK(VBOXSTRICTRC) elMemRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3754{
3755 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3756 uint8_t *pbData = (uint8_t *)pv;
3757 NOREF(pvUser);
3758
3759 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3760
3761 if (pThis->ga.gacfr.rsel)
3762 {
3763 Log3Func(("#%d: Reading %u bytes from address %X\n", pThis->iInstance, cb, off));
3764 while (cb-- > 0)
3765 *pbData++ = pThis->abLocalRAM[off++ & DPNIC_MEM_MASK];
3766 }
3767 else
3768 {
3769 Log3Func(("#%d: Ignoring read of %u bytes from address %X\n", pThis->iInstance, cb, off));
3770 memset(pv, 0xff, cb);
3771 }
3772 return VINF_SUCCESS;
3773}
3774
3775
3776/**
3777 * @callback_method_impl{FNIOMMMIONEWWRITE,
3778 * Local RAM write hook\, to be called from IOM.}
3779 */
3780static DECLCALLBACK(VBOXSTRICTRC) elMemWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3781{
3782 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3783 uint8_t const *pbSrc = (uint8_t const *)pv;
3784 NOREF(pvUser);
3785
3786 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3787
3788 if (pThis->ga.gacfr.rsel)
3789 {
3790 Log3Func(("#%d: Writing %u bytes to address %X\n", pThis->iInstance, cb, off));
3791 while (cb-- > 0)
3792 pThis->abLocalRAM[off++ & DPNIC_MEM_MASK] = *pbSrc++;
3793 }
3794 else
3795 {
3796 Log3Func(("#%d: Ignoring write of %u bytes to address %X\n", pThis->iInstance, cb, off));
3797 }
3798 return VINF_SUCCESS;
3799}
3800
3801
3802#ifdef IN_RING3
3803
3804/* Shamelessly stolen from DevDMA.cpp */
3805
3806/* Test the decrement bit of mode register. */
3807#define IS_MODE_DEC(c) ((c) & 0x20)
3808/* Test the auto-init bit of mode register. */
3809#define IS_MODE_AI(c) ((c) & 0x10)
3810/* Extract the transfer type bits of mode register. */
3811#define GET_MODE_XTYP(c) (((c) & 0x0c) >> 2)
3812
3813/* DMA transfer modes. */
3814enum {
3815 DMODE_DEMAND, /* Demand transfer mode. */
3816 DMODE_SINGLE, /* Single transfer mode. */
3817 DMODE_BLOCK, /* Block transfer mode. */
3818 DMODE_CASCADE /* Cascade mode. */
3819};
3820
3821/* DMA transfer types. */
3822enum {
3823 DTYPE_VERIFY, /* Verify transfer type. */
3824 DTYPE_WRITE, /* Write transfer type. */
3825 DTYPE_READ, /* Read transfer type. */
3826 DTYPE_ILLEGAL /* Undefined. */
3827};
3828
3829static DECLCALLBACK(uint32_t) elnk3R3DMAXferHandler(PPDMDEVINS pDevIns, void *opaque,
3830 unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
3831{
3832 PDPNICSTATE pThis = (PDPNICSTATE)opaque;
3833 int dma_mode;
3834 int dma_type;
3835 uint16_t cbToXfer;
3836 uint32_t cbXferred = 0;
3837 uint16_t uDmaAddr;
3838 int rc;
3839
3840 /*
3841 * The 3C503 EtherLink II uses DMA as an alternative to shared RAM
3842 * or PIO. The Gate Array tracks its own current DMA address within
3843 * the adapter's local address space.
3844 */
3845 dma_mode = PDMDevHlpDMAGetChannelMode(pDevIns, pThis->uIsaDma);
3846 dma_type = GET_MODE_XTYP(dma_mode);
3847 uDmaAddr = pThis->ga.CDADR;
3848 cbToXfer = dma_len;
3849 LogFlowFunc(("dma_mode=%d, dma_type=%d, dma_pos=%u, dma_len=%u, cdadr=%04X\n", dma_mode, dma_type, dma_pos, dma_len, uDmaAddr));
3850
3851 /* Skip any accesses below local memory start. */
3852 if ((0x2000 > 0) && (uDmaAddr < 0x2000)) /// @todo Should keep track in variables
3853 {
3854 uint16_t cbToSkip = 0x2000 - uDmaAddr;
3855
3856 uDmaAddr += cbToSkip;
3857 /// @todo Should this write junk to host memory when reading from device?
3858 if (cbToSkip < cbToXfer)
3859 {
3860 cbToXfer -= cbToSkip;
3861 Assert(uDmaAddr == 0x2000);
3862 LogFunc(("DMA skipping %u bytes!\n", cbToSkip));
3863 }
3864 else
3865 {
3866 cbToXfer = 0; /* Transfer entirely below valid address range. */
3867 LogFunc(("DMA below valid address range!\n"));
3868 }
3869 }
3870
3871 if (cbToXfer)
3872 {
3873 uint16_t cbToSkip = 0;
3874
3875 /* Clip transfer size so it falls within local RAM. */
3876 if ((uDmaAddr - 0x2000 + cbToXfer) > (int)sizeof(pThis->abLocalRAM))
3877 {
3878 /* Calculate how much to skip anything at the end. */
3879 cbToSkip = sizeof(pThis->abLocalRAM) - (0x2000 - uDmaAddr + cbToXfer);
3880 LogFunc(("DMA above valid address range uDmaAddr=%04X cbToXfer=%u cbToSkip=%u!\n", uDmaAddr, cbToXfer, cbToSkip));
3881 cbToXfer -= cbToSkip;
3882 }
3883
3884 if (dma_type == DTYPE_WRITE)
3885 {
3886 /* Write transfer type. Reading from device, writing to memory. */
3887 if (!pThis->ga.gacr.ddir)
3888 {
3889 Log2Func(("DMAWriteMemory uDmaAddr=%04X cbToXfer=%u\n", uDmaAddr, cbToXfer));
3890 rc = PDMDevHlpDMAWriteMemory(pDevIns, nchan,
3891 &pThis->abLocalRAM[uDmaAddr - 0x2000],
3892 dma_pos, cbToXfer, &cbXferred);
3893 AssertMsgRC(rc, ("DMAWriteMemory -> %Rrc\n", rc));
3894 }
3895 else
3896 {
3897 // Do nothing, direction does not match.
3898 /// @todo Bug in DevDMA?
3899 LogFunc(("DTYPE_WRITE but GACR.ddir set, do nothing!\n"));
3900 }
3901 }
3902 else
3903 {
3904 /* Read of Verify transfer type. Reading from memory, writing to device. */
3905 if (pThis->ga.gacr.ddir)
3906 {
3907 Log2Func(("DMAReadMemory uDmaAddr=%04X cbToXfer=%u\n", uDmaAddr, cbToXfer));
3908 rc = PDMDevHlpDMAReadMemory(pDevIns, nchan,
3909 &pThis->abLocalRAM[uDmaAddr - 0x2000],
3910 dma_pos, cbToXfer, &cbXferred);
3911 AssertMsgRC(rc, ("DMAReadMemory -> %Rrc\n", rc));
3912 }
3913 else
3914 {
3915 // Do nothing, direction does not match.
3916 /// @todo Bug in DevDMA?
3917 LogFunc(("DTYPE_READ but GACR.ddir clear, do nothing!\n"));
3918 }
3919 }
3920
3921 /* NB: This might wrap. In theory it might wrap back to valid
3922 * memory but... just no.
3923 */
3924 /// @todo Actually... what would really happen?
3925 uDmaAddr += cbToXfer + cbToSkip;
3926 }
3927 Log2Func(("After DMA transfer: uDmaAddr=%04X, cbXferred=%u\n", uDmaAddr, cbXferred));
3928
3929 /* Advance the DMA address and see if transfer completed (it almost certainly did). */
3930 if (1)
3931 {
3932 Log2Func(("DMA completed\n"));
3933 PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, 0);
3934 pThis->ga.streg.dtc = 1;
3935 pThis->ga.fGaIrq = true;
3936 dp8390CoreUpdateIrq(pDevIns, pThis);
3937 }
3938 else
3939 {
3940 LogFunc(("DMA continuing: uDmaAddr=%04X, cbXferred=%u\n", uDmaAddr, cbXferred));
3941 PDMDevHlpDMASchedule(pDevIns);
3942 }
3943
3944 /* Returns the updated transfer count. */
3945 return dma_pos + dma_len;
3946}
3947
3948
3949/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
3950
3951/**
3952 * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
3953 *
3954 * This is only called when we restore a saved state and temporarily
3955 * disconnected the network link to inform the guest that network connections
3956 * should be considered lost.
3957 */
3958static DECLCALLBACK(void) dpNicR3TimerRestore(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
3959{
3960 RT_NOREF(pvUser);
3961 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
3962 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
3963 AssertReleaseRC(rc);
3964
3965 rc = VERR_GENERAL_FAILURE;
3966
3967 /* The DP8390 based cards have no concept of link state. Reporting collisions on all transmits
3968 * is the best approximation of a disconnected cable that we can do. Some drivers (3C503) warn
3969 * of possible disconnected cable, some don't. Many cards with DP8390 chips had permanently
3970 * attached cables (AUI or BNC) and their drivers do not expect cables to be disconnected and
3971 * re-connected at runtime. Guests which are waiting for a receive have no way to notice any
3972 * problem, therefore we only postpone restoring a link a couple of times, and then reconnect
3973 * regardless of whether the guest noticed anything or not.
3974 */
3975 if ( (pThis->cLinkDownReported <= DPNIC_MAX_LINKDOWN_REPORTED)
3976 && (pThis->cLinkRestorePostponed <= DPNIC_MAX_LINKRST_POSTPONED))
3977 rc = PDMDevHlpTimerSetMillies(pDevIns, hTimer, 1500);
3978 if (RT_FAILURE(rc))
3979 {
3980 pThis->fLinkTempDown = false;
3981 if (pThis->fLinkUp)
3982 {
3983 LogRel(("DPNIC#%d: The link is back up again after the restore.\n",
3984 pThis->iInstance));
3985 LogFunc(("#%d: cLinkDownReported=%d\n", pThis->iInstance, pThis->cLinkDownReported));
3986 pThis->Led.Actual.s.fError = 0;
3987 }
3988 }
3989 else
3990 {
3991 LogFunc(("#%d: cLinkDownReported=%d, cLinkRestorePostponed=%d, wait another 1500ms...\n",
3992 pThis->iInstance, pThis->cLinkDownReported, pThis->cLinkRestorePostponed));
3993 pThis->cLinkRestorePostponed++;
3994 }
3995
3996 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
3997}
3998
3999
4000/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
4001
4002/**
4003 * @callback_method_impl{FNDBGFHANDLERDEV}
4004 */
4005static DECLCALLBACK(void) dpNicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4006{
4007 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4008 bool fRecvBuffer = false;
4009 bool fSendBuffer = false;
4010 unsigned uFreePages;
4011 DP8390CORE *pCore = &pThis->core;
4012 const char *aszModels[] = {"NE1000", "NE2000", "WD8003E", "WD8013E", "3C503"};
4013
4014 /*
4015 * Parse args.
4016 */
4017 if (pszArgs)
4018 {
4019 fRecvBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "recvbuf");
4020 fSendBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "sendbuf");
4021 }
4022
4023 /*
4024 * Show device information.
4025 */
4026 pHlp->pfnPrintf(pHlp, "DPNIC #%d: %s port=%RTiop IRQ=%u",
4027 pThis->iInstance,
4028 aszModels[pThis->uDevType],
4029 pThis->IOPortBase,
4030 pThis->uIsaIrq);
4031 if (pThis->MemBase)
4032 pHlp->pfnPrintf(pHlp, " mem=%05X-%05X", pThis->MemBase, pThis->MemBase + pThis->cbMemSize - 1);
4033 if (pThis->uIsaDma)
4034 pHlp->pfnPrintf(pHlp, " DMA=%u", pThis->uIsaDma);
4035 pHlp->pfnPrintf(pHlp, " mac-cfg=%RTmac%s %s\n",
4036 &pThis->MacConfigured,
4037 pDevIns->fR0Enabled ? " RZ" : "",
4038 pThis->fDriverAttached ? "attached" : "unattached!");
4039
4040 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
4041 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4042
4043 pHlp->pfnPrintf(pHlp, "\nDP3890 NIC Core\n");
4044 pHlp->pfnPrintf(pHlp, " CR=%02X: %s%s%s RD=%d PS=%d\n", pCore->CR,
4045 pCore->cr.STP ? "STP " : "",
4046 pCore->cr.STA ? "STA " : "",
4047 pCore->cr.TXP ? "TXP " : "",
4048 pCore->cr.RD, pCore->cr.PS);
4049 pHlp->pfnPrintf(pHlp, " ISR=%02X: %s%s%s%s%s%s%s%s\n", pCore->ISR,
4050 pCore->isr.PRX ? "PRX " : "",
4051 pCore->isr.PTX ? "PTX " : "",
4052 pCore->isr.RXE ? "RXE " : "",
4053 pCore->isr.TXE ? "TXE " : "",
4054 pCore->isr.OVW ? "OVW " : "",
4055 pCore->isr.CNT ? "CNT " : "",
4056 pCore->isr.RDC ? "RDC " : "",
4057 pCore->isr.RST ? "RST " : "");
4058 pHlp->pfnPrintf(pHlp, " IMR=%02X: %s%s%s%s%s%s%s%s\n", pCore->IMR,
4059 pCore->imr.PRXE ? "PRXE " : "",
4060 pCore->imr.PTXE ? "PTXE " : "",
4061 pCore->imr.RXEE ? "RXEE " : "",
4062 pCore->imr.TXEE ? "TXEE " : "",
4063 pCore->imr.OVWE ? "OVWE " : "",
4064 pCore->imr.CNTE ? "CNTE " : "",
4065 pCore->imr.RDCE ? "RDCE " : "",
4066 pCore->imr.res ? "Reserved bit set!!" : "");
4067 pHlp->pfnPrintf(pHlp, " DCR=%02X: %s%s%s%s%sFT=%d %s\n", pCore->DCR,
4068 pCore->dcr.WTS ? "WTS " : "",
4069 pCore->dcr.BOS ? "BOS " : "",
4070 pCore->dcr.LAS ? "LAS " : "",
4071 pCore->dcr.LS ? "LS " : "",
4072 pCore->dcr.ARM ? "ARM " : "",
4073 pCore->dcr.FT,
4074 pCore->dcr.res ? "Reserved bit set!!" : "");
4075 pHlp->pfnPrintf(pHlp, " TCR=%02X: %sLB=%d %s%s\n", pCore->TCR,
4076 pCore->tcr.CRC ? "CRC " : "",
4077 pCore->tcr.LB,
4078 pCore->tcr.ATD ? "ATD " : "",
4079 pCore->tcr.OFST ? "OFST" : "");
4080 pHlp->pfnPrintf(pHlp, " TSR=%02X: %s%s%s%s%s%s%s%s\n", pCore->TSR,
4081 pCore->tsr.PTX ? "PTX " : "",
4082 pCore->tsr.DFR ? "DFR " : "",
4083 pCore->tsr.COL ? "COL " : "",
4084 pCore->tsr.ABT ? "ABT " : "",
4085 pCore->tsr.CRS ? "CRS " : "",
4086 pCore->tsr.FU ? "FU " : "",
4087 pCore->tsr.CDH ? "CDH " : "",
4088 pCore->tsr.OWC ? "OWC " : "");
4089 pHlp->pfnPrintf(pHlp, " RCR=%02X: %s%s%s%s%s%s\n", pCore->RCR,
4090 pCore->rcr.SEP ? "SEP " : "",
4091 pCore->rcr.AR ? "AR " : "",
4092 pCore->rcr.AB ? "AB " : "",
4093 pCore->rcr.AM ? "AM " : "",
4094 pCore->rcr.PRO ? "PRO " : "",
4095 pCore->rcr.MON ? "MON " : "");
4096 pHlp->pfnPrintf(pHlp, " RSR=%02X: %s%s%s%s%s%s%s%s\n", pCore->RSR,
4097 pCore->rsr.PRX ? "PRX " : "",
4098 pCore->rsr.CRC ? "CRC " : "",
4099 pCore->rsr.FAE ? "FAE " : "",
4100 pCore->rsr.FO ? "FO " : "",
4101 pCore->rsr.MPA ? "MPA " : "",
4102 pCore->rsr.PHY ? "PHY " : "",
4103 pCore->rsr.DIS ? "DIS " : "",
4104 pCore->rsr.DFR ? "DFR " : "");
4105 pHlp->pfnPrintf(pHlp, " ActIntSrc: %02X\n", pCore->ISR & pCore->IMR);
4106 pHlp->pfnPrintf(pHlp, " Receiving: %s%s%s%s%s%s\n",
4107 pCore->rcr.AB ? "Broadcast " : "",
4108 pCore->rcr.AM ? "Multicast " : "",
4109 pCore->rcr.PRO ? "Promiscuous " : "",
4110 pCore->rcr.MON ? "Monitor " : "",
4111 pCore->cr.STA ? "Started " : "Not started ",
4112 pCore->isr.RST ? "Reset!" : "");
4113
4114 /* Dump the currently programmed station address. */
4115 pHlp->pfnPrintf(pHlp, " MAC Addr : %RTmac\n", &pCore->pg1.PAR);
4116
4117 /* Dump the currently programmed multicast filter. */
4118 pHlp->pfnPrintf(pHlp, " Multicast: %02X:%02X:%02X:%02X %02X:%02X:%02X:%02X\n",
4119 pCore->pg1.MAR[0], pCore->pg1.MAR[1], pCore->pg1.MAR[2], pCore->pg1.MAR[3],
4120 pCore->pg1.MAR[4], pCore->pg1.MAR[5], pCore->pg1.MAR[6], pCore->pg1.MAR[7]);
4121
4122 /* Dump the DMA state. */
4123 pHlp->pfnPrintf(pHlp, " Local DMA : TPSR=%02X00 TBCR=%04X CLDA=%04X\n",
4124 pCore->TPSR, pCore->TBCR, pCore->CLDA);
4125 pHlp->pfnPrintf(pHlp, " : PSTART=%02X00 PSTOP=%02X00 CURR=%02X00 BNRY=%02X00\n",
4126 pCore->PSTART, pCore->PSTOP, pCore->CURR, pCore->BNRY);
4127 pHlp->pfnPrintf(pHlp, " Remote DMA: RSAR=%04X RBCR=%04X CRDA=%04X\n",
4128 pCore->RSAR, pCore->RBCR, pCore->CRDA);
4129
4130 /* Try to figure out how much available space there is in the receive ring. */
4131 if (pCore->BNRY <= pCore->CURR)
4132 uFreePages = pCore->PSTOP - pCore->PSTART - (pCore->CURR - pCore->BNRY);
4133 else
4134 uFreePages = pCore->BNRY - pCore->CURR;
4135 pHlp->pfnPrintf(pHlp, " Estimated %u free pages (%u bytes) in receive ring\n", uFreePages, uFreePages * 256);
4136
4137 if (pThis->fMaybeOutOfSpace)
4138 pHlp->pfnPrintf(pHlp, " Waiting for receive space\n");
4139 if (pThis->fLinkTempDown)
4140 {
4141 pHlp->pfnPrintf(pHlp, " Link down count %d\n", pThis->cLinkDownReported);
4142 pHlp->pfnPrintf(pHlp, " Postpone count %d\n", pThis->cLinkRestorePostponed);
4143 }
4144
4145 if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
4146 {
4147 /* Dump the WD specific registers. */
4148 pHlp->pfnPrintf(pHlp, "\nWD80x3 Control Registers\n");
4149 pHlp->pfnPrintf(pHlp, " CTRL1=%02X: %s%s A18-A13=%02X\n", pThis->CTRL1,
4150 pThis->ctrl1.RESET ? "RESET " : "",
4151 pThis->ctrl1.MEME ? "MEME " : "",
4152 pThis->ctrl1.A13_18);
4153 pHlp->pfnPrintf(pHlp, " CTRL2=%02X: %s%s A23-A19=%02X\n", pThis->CTRL2,
4154 pThis->ctrl2.M16 ? "M16 " : "",
4155 pThis->ctrl2.MEMW ? "MEMW " : "",
4156 pThis->ctrl2.A19_23);
4157 }
4158
4159 if (pThis->uDevType == DEV_3C503)
4160 {
4161 PEL_GA pGa = &pThis->ga;
4162
4163 /* Dump the Gate Array state. */
4164 pHlp->pfnPrintf(pHlp, "\n3C503 ASIC Gate Array\n");
4165 pHlp->pfnPrintf(pHlp, " PSTR=%02X00 PSPR=%02X00 cdadr=%04X\n",
4166 pGa->PSTR, pGa->PSTR, pGa->CDADR);
4167 pHlp->pfnPrintf(pHlp, " DQTR=%02X: tb=%d\n", pGa->DQTR,
4168 pGa->dqtr.tb);
4169 pHlp->pfnPrintf(pHlp, " BCFR=%02X PCFR=%02X\n",
4170 pGa->BCFR, pGa->PCFR);
4171 pHlp->pfnPrintf(pHlp, " GACFR=%02X: mbs=%d %s%s%s%s%s\n", pGa->GACFR,
4172 pGa->gacfr.mbs,
4173 pGa->gacfr.rsel ? "rsel " : "",
4174 pGa->gacfr.test ? "test " : "",
4175 pGa->gacfr.ows ? "ows " : "",
4176 pGa->gacfr.tcm ? "tcm " : "",
4177 pGa->gacfr.nim ? "nim " : "");
4178 pHlp->pfnPrintf(pHlp, " GACR=%02X: %s%s%s%s%s%s%s%s\n", pGa->GACR,
4179 pGa->gacr.rst ? "rst " : "",
4180 pGa->gacr.xsel ? "xsel " : "",
4181 pGa->gacr.ealo ? "ealo " : "",
4182 pGa->gacr.eahi ? "eahi " : "",
4183 pGa->gacr.share ? "share " : "",
4184 pGa->gacr.dbsel ? "dbsel " : "",
4185 pGa->gacr.ddir ? "ddir " : "",
4186 pGa->gacr.start ? "start " : "");
4187 pHlp->pfnPrintf(pHlp, " STREG=%02X: rev=%d %s%s%s%s%s\n", pGa->STREG,
4188 pGa->streg.rev,
4189 pGa->streg.dip ? "dip " : "",
4190 pGa->streg.dtc ? "dtc " : "",
4191 pGa->streg.oflw ? "oflw " : "",
4192 pGa->streg.uflw ? "uflw " : "",
4193 pGa->streg.dprdy ? "dprdy " : "");
4194 pHlp->pfnPrintf(pHlp, " IDCFR=%02X: %s%s%s%s%s%s%s\n", pGa->IDCFR,
4195 pGa->idcfr.drq1 ? "drq1 " : "",
4196 pGa->idcfr.drq2 ? "drq2 " : "",
4197 pGa->idcfr.drq3 ? "drq3 " : "",
4198 pGa->idcfr.irq2 ? "irq2 " : "",
4199 pGa->idcfr.irq3 ? "irq3 " : "",
4200 pGa->idcfr.irq4 ? "irq4 " : "",
4201 pGa->idcfr.irq5 ? "irq5 " : "");
4202 pHlp->pfnPrintf(pHlp, " DALSB=%02X DAMSB=%02X addr=%04X\n",
4203 pGa->DALSB, pGa->DAMSB,
4204 RT_MAKE_U16(pGa->DALSB, pGa->DAMSB));
4205 pHlp->pfnPrintf(pHlp, " VPTR0=%02X VPTR1=%02X VPTR2=%02X, VPTR=%X\n",
4206 pGa->VPTR0, pGa->VPTR1, pGa->VPTR2,
4207 (pGa->VPTR2 << 12) | (pGa->VPTR1 << 4) | (pGa->VPTR0 >> 4));
4208
4209
4210
4211 }
4212
4213 /* Dump the beginning of the send buffer (before it is sent). */
4214 uint8_t dump_buf[16];
4215 unsigned dump_beg, dump_end;
4216 if (fSendBuffer)
4217 {
4218 dump_beg = RT_MAKE_U16(0, pThis->core.TPSR);
4219 pHlp->pfnPrintf(pHlp, "Send buffer (start at %u based on TPSR):\n", dump_beg);
4220 dump_end = RT_MIN(dump_beg + 64, 64 * _1K - 16);
4221 for (unsigned ofs = dump_beg; ofs < dump_end; ofs += 16)
4222 {
4223 dpLocalRAMReadBuf(pThis, ofs, sizeof(dump_buf), dump_buf);
4224 pHlp->pfnPrintf(pHlp, " %04X: %Rhxs\n", ofs, dump_buf);
4225 }
4226 }
4227
4228 /* Dump the beginning of the receive buffer (before it is read). */
4229 if (fRecvBuffer)
4230 {
4231 dump_beg = pThis->core.CRDA;
4232 pHlp->pfnPrintf(pHlp, "Recv buffer (start at %u based on CRDA):\n", dump_beg);
4233 dump_end = RT_MIN(dump_beg + 64, 64 * _1K - 16);
4234 for (unsigned ofs = dump_beg; ofs < dump_end; ofs += 16)
4235 {
4236 dpLocalRAMReadBuf(pThis, ofs, sizeof(dump_buf), dump_buf);
4237 pHlp->pfnPrintf(pHlp, " %04X: %Rhxs\n", ofs, dump_buf);
4238 }
4239 }
4240
4241 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4242}
4243
4244
4245/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
4246
4247
4248static void dpNicR3HardReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
4249{
4250 LogFlowFunc(("#%d:\n", pThis->iInstance));
4251
4252 /* Initialize the PROM. Covers both NE1000 and NE2000. */
4253 Assert(sizeof(pThis->MacConfigured) == 6);
4254 memset(pThis->aPROM, 0, sizeof(pThis->aPROM));
4255 /* The first 6 bytes of PROM always contain the configured MAC address. */
4256 memcpy(&pThis->aPROM[0x00], &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4257
4258 if ((pThis->uDevType == DEV_NE1000) || (pThis->uDevType == DEV_NE2000))
4259 {
4260 /* The NE1000/NE2000 repeats the MAC address and also includes BB/WW signature. */
4261 memcpy(&pThis->aPROM[0x10], &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4262 pThis->aPROM[0x0E] = pThis->aPROM[0x0F] = 'W'; /* Word-wide. */
4263 pThis->aPROM[0x1E] = pThis->aPROM[0x1F] = 'B'; /* Byte-wide. */
4264 }
4265 else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
4266 {
4267 /* The WD8003/WD8013 only uses 8 bytes of the PROM. The 7th byte
4268 * contains a board ID and the last byte is a checksum calculated
4269 * such that a two's complement sum of the 8 bytes equals FFh.
4270 */
4271 int i;
4272 uint8_t sum;
4273
4274 /* The board ID is 2 for 8003S, 3 for 8003E, 4 for 8003WT, 5 for 8013EBT. */
4275 pThis->aPROM[0x06] = 3;
4276 if (pThis->uDevType == DEV_WD8013)
4277 pThis->aPROM[0x06] = 5;
4278
4279 for (i = 0, sum = 0; i < 7; ++i)
4280 sum += pThis->aPROM[i];
4281
4282 pThis->aPROM[0x07] = 0xff - sum;
4283 }
4284 else if (pThis->uDevType == DEV_3C503)
4285 {
4286 const uint16_t el_io_bases[] = { 0x2E0, 0x2A0, 0x280, 0x250, 0x350, 0x330, 0x310, 0x300, 0 };
4287 const uint32_t el_mem_bases[] = { 0xDC000, 0xD8000, 0xCC000, 0xC8000, 0 };
4288 int i;
4289
4290 /* Zap the Gate Array state. */
4291 memset(&pThis->ga, 0, sizeof(pThis->ga));
4292
4293 /* Find the BCFR value. */
4294 for (i = 0; el_io_bases[i]; ++i)
4295 {
4296 if (pThis->IOPortBase == el_io_bases[i])
4297 break;
4298 }
4299 /// @todo Make sure we somehow disallow values that a 3C503 can't do
4300 if (i < 8)
4301 pThis->ga.BCFR = 1 << i;
4302
4303 /* Find the PCFR value. */
4304 for (i = 0; el_mem_bases[i]; ++i)
4305 {
4306 if (pThis->MemBase == el_mem_bases[i])
4307 break;
4308 }
4309 /// @todo Make sure we somehow disallow values that a 3C503 can't do
4310 if (i < 4)
4311 pThis->ga.PCFR = RT_BIT(7) >> i;
4312 }
4313
4314 /* Clear the local RAM. */
4315 memset(pThis->abLocalRAM, 0, sizeof(pThis->abLocalRAM));
4316
4317 /* Wipe out all of the DP8390 core state. */
4318 memset(&pThis->core, 0, sizeof(pThis->core));
4319
4320 dp8390CoreReset(pDevIns, pThis);
4321}
4322
4323/**
4324 * Takes down the link temporarily if it's current status is up.
4325 *
4326 * This is used during restore and when replumbing the network link.
4327 *
4328 * The temporary link outage is supposed to indicate to the OS that all network
4329 * connections have been lost and that it for instance is appropriate to
4330 * renegotiate any DHCP lease.
4331 *
4332 * @param pDevIns The device instance data.
4333 * @param pThis The device state.
4334 */
4335static void dp8390TempLinkDown(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
4336{
4337 if (pThis->fLinkUp)
4338 {
4339 pThis->fLinkTempDown = true;
4340 pThis->cLinkDownReported = 0;
4341 pThis->cLinkRestorePostponed = 0;
4342 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4343 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
4344 AssertRC(rc);
4345 }
4346}
4347
4348
4349/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
4350
4351/**
4352 * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
4353 */
4354static DECLCALLBACK(int) dpNicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
4355{
4356 RT_NOREF(uPass);
4357 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4358 pDevIns->pHlpR3->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4359 return VINF_SSM_DONT_CALL_AGAIN;
4360}
4361
4362
4363/**
4364 * @callback_method_impl{FNSSMDEVSAVEPREP,
4365 * Serializes the receive thread, it may be working inside the critsect.}
4366 */
4367static DECLCALLBACK(int) dpNicSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4368{
4369 RT_NOREF(pSSM);
4370 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4371
4372 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4373 AssertRC(rc);
4374 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4375
4376 return VINF_SUCCESS;
4377}
4378
4379
4380/**
4381 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4382 */
4383static DECLCALLBACK(int) dpNicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4384{
4385 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4386 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4387
4388 /* Start with saving the generic bits. */
4389 pHlp->pfnSSMPutBool(pSSM, pThis->fLinkUp);
4390 pHlp->pfnSSMPutBool(pSSM, pThis->fNicIrqActive);
4391
4392 /* Continue with DP8390 core. */
4393 pHlp->pfnSSMPutU8(pSSM, pThis->core.CR);
4394 pHlp->pfnSSMPutU8(pSSM, pThis->core.DCR);
4395 pHlp->pfnSSMPutU8(pSSM, pThis->core.ISR);
4396 pHlp->pfnSSMPutU8(pSSM, pThis->core.IMR);
4397 pHlp->pfnSSMPutU8(pSSM, pThis->core.RCR);
4398 pHlp->pfnSSMPutU8(pSSM, pThis->core.RSR);
4399 pHlp->pfnSSMPutU8(pSSM, pThis->core.TCR);
4400 pHlp->pfnSSMPutU8(pSSM, pThis->core.TSR);
4401 pHlp->pfnSSMPutU8(pSSM, pThis->core.NCR);
4402 pHlp->pfnSSMPutU8(pSSM, pThis->core.TPSR);
4403 pHlp->pfnSSMPutU16(pSSM, pThis->core.TBCR);
4404 pHlp->pfnSSMPutU16(pSSM, pThis->core.CLDA);
4405 pHlp->pfnSSMPutU8(pSSM, pThis->core.PSTART);
4406 pHlp->pfnSSMPutU8(pSSM, pThis->core.PSTOP);
4407 pHlp->pfnSSMPutU8(pSSM, pThis->core.CURR);
4408 pHlp->pfnSSMPutU8(pSSM, pThis->core.BNRY);
4409 pHlp->pfnSSMPutU16(pSSM, pThis->core.RSAR);
4410 pHlp->pfnSSMPutU16(pSSM, pThis->core.RBCR);
4411 pHlp->pfnSSMPutU16(pSSM, pThis->core.CRDA);
4412 pHlp->pfnSSMPutU8(pSSM, pThis->core.lnxtpp);
4413 pHlp->pfnSSMPutU8(pSSM, pThis->core.rnxtpp);
4414 pHlp->pfnSSMPutU8(pSSM, pThis->core.CNTR0);
4415 pHlp->pfnSSMPutU8(pSSM, pThis->core.CNTR1);
4416 pHlp->pfnSSMPutU8(pSSM, pThis->core.CNTR2);
4417 pHlp->pfnSSMPutMem(pSSM, &pThis->core.pg1.PAR, sizeof(pThis->core.pg1.PAR));
4418 pHlp->pfnSSMPutMem(pSSM, &pThis->core.pg1.MAR, sizeof(pThis->core.pg1.MAR));
4419 pHlp->pfnSSMPutU8(pSSM, pThis->core.fifo.rp);
4420 pHlp->pfnSSMPutU8(pSSM, pThis->core.fifo.wp);
4421 pHlp->pfnSSMPutMem(pSSM, &pThis->core.fifo.fifo, sizeof(pThis->core.fifo.fifo));
4422
4423 /* Now the WD80x3 state. */
4424 pHlp->pfnSSMPutU8(pSSM, pThis->CTRL1);
4425 pHlp->pfnSSMPutU8(pSSM, pThis->CTRL2);
4426
4427 /* Finally the 3C503-specific state. */
4428 pHlp->pfnSSMPutU8(pSSM, pThis->ga.PSTR);
4429 pHlp->pfnSSMPutU8(pSSM, pThis->ga.PSPR);
4430 pHlp->pfnSSMPutU8(pSSM, pThis->ga.DQTR);
4431 pHlp->pfnSSMPutU8(pSSM, pThis->ga.BCFR);
4432 pHlp->pfnSSMPutU8(pSSM, pThis->ga.PCFR);
4433 pHlp->pfnSSMPutU8(pSSM, pThis->ga.GACFR);
4434 pHlp->pfnSSMPutU8(pSSM, pThis->ga.GACR);
4435 pHlp->pfnSSMPutU8(pSSM, pThis->ga.STREG);
4436 pHlp->pfnSSMPutU8(pSSM, pThis->ga.IDCFR);
4437 pHlp->pfnSSMPutU8(pSSM, pThis->ga.DAMSB);
4438 pHlp->pfnSSMPutU8(pSSM, pThis->ga.DALSB);
4439 pHlp->pfnSSMPutU8(pSSM, pThis->ga.VPTR2);
4440 pHlp->pfnSSMPutU8(pSSM, pThis->ga.VPTR1);
4441 pHlp->pfnSSMPutU8(pSSM, pThis->ga.VPTR0);
4442 pHlp->pfnSSMPutU16(pSSM, pThis->ga.CDADR);
4443 pHlp->pfnSSMPutBool(pSSM, pThis->ga.fGaIrq);
4444
4445 /* Save the configured MAC address. */
4446 pHlp->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4447
4448 return VINF_SUCCESS;
4449}
4450
4451
4452/**
4453 * @callback_method_impl{FNSSMDEVLOADPREP},
4454 * Serializes the receive thread, it may be working inside the critsect.}
4455 */
4456static DECLCALLBACK(int) dpNicLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4457{
4458 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4459 RT_NOREF(pSSM);
4460
4461 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4462 AssertRC(rc);
4463
4464 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4465
4466 return rc;
4467}
4468
4469
4470/**
4471 * @callback_method_impl{FNSSMDEVLOADEXEC}
4472 */
4473static DECLCALLBACK(int) dpNicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4474{
4475 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4476 PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
4477 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4478
4479 if (SSM_VERSION_MAJOR_CHANGED(uVersion, DPNIC_SAVEDSTATE_VERSION))
4480 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4481
4482 if (uPass == SSM_PASS_FINAL)
4483 {
4484 /* Restore data, first the generic bits. */
4485 pHlp->pfnSSMGetBool(pSSM, &pThis->fLinkUp);
4486 pHlp->pfnSSMGetBool(pSSM, &pThis->fNicIrqActive);
4487
4488 /* Now the DP8390 core. */
4489 pHlp->pfnSSMGetU8(pSSM, &pThis->core.CR);
4490 pHlp->pfnSSMGetU8(pSSM, &pThis->core.DCR);
4491 pHlp->pfnSSMGetU8(pSSM, &pThis->core.ISR);
4492 pHlp->pfnSSMGetU8(pSSM, &pThis->core.IMR);
4493 pHlp->pfnSSMGetU8(pSSM, &pThis->core.RCR);
4494 pHlp->pfnSSMGetU8(pSSM, &pThis->core.RSR);
4495 pHlp->pfnSSMGetU8(pSSM, &pThis->core.TCR);
4496 pHlp->pfnSSMGetU8(pSSM, &pThis->core.TSR);
4497 pHlp->pfnSSMGetU8(pSSM, &pThis->core.NCR);
4498 pHlp->pfnSSMGetU8(pSSM, &pThis->core.TPSR);
4499 pHlp->pfnSSMGetU16(pSSM, &pThis->core.TBCR);
4500 pHlp->pfnSSMGetU16(pSSM, &pThis->core.CLDA);
4501 pHlp->pfnSSMGetU8(pSSM, &pThis->core.PSTART);
4502 pHlp->pfnSSMGetU8(pSSM, &pThis->core.PSTOP);
4503 pHlp->pfnSSMGetU8(pSSM, &pThis->core.CURR);
4504 pHlp->pfnSSMGetU8(pSSM, &pThis->core.BNRY);
4505 pHlp->pfnSSMGetU16(pSSM, &pThis->core.RSAR);
4506 pHlp->pfnSSMGetU16(pSSM, &pThis->core.RBCR);
4507 pHlp->pfnSSMGetU16(pSSM, &pThis->core.CRDA);
4508 pHlp->pfnSSMGetU8(pSSM, &pThis->core.lnxtpp);
4509 pHlp->pfnSSMGetU8(pSSM, &pThis->core.rnxtpp);
4510 pHlp->pfnSSMGetU8(pSSM, &pThis->core.CNTR0);
4511 pHlp->pfnSSMGetU8(pSSM, &pThis->core.CNTR1);
4512 pHlp->pfnSSMGetU8(pSSM, &pThis->core.CNTR2);
4513 pHlp->pfnSSMGetMem(pSSM, &pThis->core.pg1.PAR, sizeof(pThis->core.pg1.PAR));
4514 pHlp->pfnSSMGetMem(pSSM, &pThis->core.pg1.MAR, sizeof(pThis->core.pg1.MAR));
4515 pHlp->pfnSSMGetU8(pSSM, &pThis->core.fifo.rp);
4516 pHlp->pfnSSMGetU8(pSSM, &pThis->core.fifo.wp);
4517 pHlp->pfnSSMGetMem(pSSM, &pThis->core.fifo.fifo, sizeof(pThis->core.fifo.fifo));
4518
4519 /* WD80x3-specific state. */
4520 pHlp->pfnSSMGetU8(pSSM, &pThis->CTRL1);
4521 pHlp->pfnSSMGetU8(pSSM, &pThis->CTRL2);
4522
4523 /* 3C503-specific state. */
4524 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.PSTR);
4525 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.PSPR);
4526 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.DQTR);
4527 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.BCFR);
4528 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.PCFR);
4529 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.GACFR);
4530 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.GACR);
4531 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.STREG);
4532 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.IDCFR);
4533 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.DAMSB);
4534 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.DALSB);
4535 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.VPTR2);
4536 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.VPTR1);
4537 pHlp->pfnSSMGetU8(pSSM, &pThis->ga.VPTR0);
4538 pHlp->pfnSSMGetU16(pSSM, &pThis->ga.CDADR);
4539 pHlp->pfnSSMGetBool(pSSM, &pThis->ga.fGaIrq);
4540
4541 /* Set IRQ and DMA based on IDCFR if this is a 3C503. */
4542 if (pThis->uDevType == DEV_3C503)
4543 {
4544 pThis->uIsaIrq = elGetIrqFromIdcfr(pThis->ga.IDCFR);
4545 pThis->uElIsaDma = elGetDrqFromIdcfr(pThis->ga.IDCFR);
4546 }
4547 }
4548
4549 /* check config */
4550 RTMAC Mac;
4551 int rc = pHlp->pfnSSMGetMem(pSSM, &Mac, sizeof(Mac));
4552 AssertRCReturn(rc, rc);
4553 if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
4554 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4555 LogRel(("DPNIC#%u: The mac address differs: config=%RTmac saved=%RTmac\n", pThis->iInstance, &pThis->MacConfigured, &Mac));
4556
4557 if (uPass == SSM_PASS_FINAL)
4558 {
4559 /* update promiscuous mode. */
4560 if (pThisCC->pDrv)
4561 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, 0 /* promiscuous enabled */);
4562
4563 /* Indicate link down to the guest OS that all network connections have
4564 been lost, unless we've been teleported here. */
4565 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
4566 dp8390TempLinkDown(pDevIns, pThis);
4567 }
4568
4569 return VINF_SUCCESS;
4570}
4571
4572
4573/* -=-=-=-=-=- DPNICSTATE::INetworkDown -=-=-=-=-=- */
4574
4575/**
4576 * Check if the device/driver can receive data now.
4577 *
4578 * Worker for dpNicNet_WaitReceiveAvail(). This must be called before
4579 * the pfnRecieve() method is called.
4580 *
4581 * @returns VBox status code.
4582 * @param pDevIns The device instance.
4583 * @param pThis The device instance data.
4584 */
4585static int dp8390CanReceive(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
4586{
4587 DP8390CORE *pCore = &pThis->core;
4588 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4589 AssertReleaseRC(rc);
4590
4591 rc = VINF_SUCCESS;
4592
4593 /*
4594 * The card has typically room for several full-size Ethernet frames but
4595 * the buffers can overflow. We cheat a bit and try to hold off when it
4596 * looks like there is temporarily not enough buffer spave.
4597 *
4598 * If the receiver is disabled, accept packets and drop them to avoid
4599 * pile-ups. If the receiver is enabled, take a closer look.
4600 */
4601 if (pCore->cr.STA && !pCore->cr.STP)
4602 {
4603 /* Receiver is enabled. Find out if we're low on buffer space.
4604 * But if the receive buffer isn't at least 4K big (16 pages),
4605 * don't bother. Typically there will be 5K or more in the
4606 * receive buffer.
4607 */
4608 if (pCore->PSTART + 16 <= pCore->PSTOP)
4609 {
4610 uint16_t free_pages;
4611
4612 /* Free space is between BNRY (host's read pointer) and CURR
4613 * (NIC's write pointer).
4614 */
4615 if (pCore->BNRY <= pCore->CURR)
4616 {
4617 /* Free space wraps around. This might technically give
4618 * the wrong answer if the buffer is empty (BNRY = CURR)
4619 * but in that case there's plenty of room anyway.
4620 */
4621 free_pages = pCore->PSTOP - pCore->PSTART - (pCore->CURR - pCore->BNRY);
4622 }
4623 else
4624 {
4625 /* Free space does not wrap. */
4626 free_pages = pCore->BNRY - pCore->CURR;
4627 }
4628 Log2Func(("#%d: %u free pages (%u bytes)\n", pThis->iInstance, free_pages, free_pages * 256));
4629
4630 /* Six pages (1,536 bytes) is enough for the longest standard Ethernet frame
4631 * (1522 bytes including FCS) plus packet header (4 bytes).
4632 */
4633 if (free_pages < 6)
4634 {
4635 rc = VERR_NET_NO_BUFFER_SPACE;
4636 Log2Func(("#%d: Buffer space low, returning %Rrc!\n", pThis->iInstance, rc));
4637 }
4638 }
4639 }
4640
4641 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4642 return rc;
4643}
4644
4645
4646/**
4647 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
4648 */
4649static DECLCALLBACK(int) dpNicNet_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
4650{
4651 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkDown);
4652 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4653 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4654
4655 int rc = dp8390CanReceive(pDevIns, pThis);
4656 if (RT_SUCCESS(rc))
4657 {
4658 STAM_COUNTER_INC(&pThis->StatRxCanReceiveNow);
4659 return VINF_SUCCESS;
4660 }
4661 if (RT_UNLIKELY(cMillies == 0))
4662 {
4663 STAM_COUNTER_INC(&pThis->StatRxCannotReceiveNow);
4664 return VINF_SUCCESS; //VERR_NET_NO_BUFFER_SPACE;
4665 }
4666
4667 rc = VERR_INTERRUPTED;
4668 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
4669 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
4670 VMSTATE enmVMState;
4671 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
4672 || enmVMState == VMSTATE_RUNNING_LS))
4673 {
4674 int rc2 = dp8390CanReceive(pDevIns, pThis);
4675 if (RT_SUCCESS(rc2))
4676 {
4677 rc = VINF_SUCCESS;
4678 break;
4679 }
4680 if (cMillies > 666)
4681 cMillies = 666;
4682 LogFlowFunc(("Waiting cMillies=%u...\n", cMillies));
4683
4684 /*rc2 = */RTSemEventWait(pThis->hEventOutOfRxSpace, cMillies);
4685//LogRelFunc(("RTSemEventWait: rc=%Rrc\n", rc2));
4686// if (rc2 == VERR_TIMEOUT)
4687// break;
4688 }
4689 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
4690 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
4691
4692 return rc;
4693}
4694
4695
4696/**
4697 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
4698 */
4699static DECLCALLBACK(int) dpNicNet_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
4700{
4701 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkDown);
4702 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4703 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4704 int rc;
4705
4706 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
4707 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4708 AssertReleaseRC(rc);
4709
4710 if (cb > 50) /* unqualified guess */
4711 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
4712 dp8390CoreReceiveLocked(pDevIns, pThis, (const uint8_t *)pvBuf, cb);
4713 pThis->Led.Actual.s.fReading = 0;
4714
4715 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4716 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
4717
4718 return VINF_SUCCESS;
4719}
4720
4721
4722/**
4723 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
4724 */
4725static DECLCALLBACK(void) dpNicNet_XmitPending(PPDMINETWORKDOWN pInterface)
4726{
4727 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkDown);
4728 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4729 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4730 dp8390CoreXmitPacket(pDevIns, pThis, true /*fOnWorkerThread*/);
4731}
4732
4733
4734/* -=-=-=-=-=- DPNICSTATE::INetworkConfig -=-=-=-=-=- */
4735
4736/**
4737 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
4738 */
4739static DECLCALLBACK(int) dpNicGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
4740{
4741 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkConfig);
4742 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4743 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4744
4745 LogFlowFunc(("#%d\n", pThis->iInstance));
4746 /// @todo This is broken!! We can't properly get the MAC address set by the guest
4747#if 0
4748 memcpy(pMac, pThis->core.pg1.PAR, sizeof(*pMac));
4749#else
4750 memcpy(pMac, pThis->aPROM, sizeof(*pMac));
4751#endif
4752 return VINF_SUCCESS;
4753}
4754
4755
4756/**
4757 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
4758 */
4759static DECLCALLBACK(PDMNETWORKLINKSTATE) dpNicGetLinkState(PPDMINETWORKCONFIG pInterface)
4760{
4761 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkConfig);
4762 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4763 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4764
4765 if (pThis->fLinkUp && !pThis->fLinkTempDown)
4766 return PDMNETWORKLINKSTATE_UP;
4767 if (!pThis->fLinkUp)
4768 return PDMNETWORKLINKSTATE_DOWN;
4769 if (pThis->fLinkTempDown)
4770 return PDMNETWORKLINKSTATE_DOWN_RESUME;
4771 AssertMsgFailed(("Invalid link state!\n"));
4772 return PDMNETWORKLINKSTATE_INVALID;
4773}
4774
4775
4776/**
4777 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
4778 */
4779static DECLCALLBACK(int) dpNicSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
4780{
4781 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkConfig);
4782 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4783 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4784 bool fLinkUp;
4785
4786 LogFlowFunc(("#%d\n", pThis->iInstance));
4787 AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
4788 ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
4789
4790 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
4791 {
4792 dp8390TempLinkDown(pDevIns, pThis);
4793 /*
4794 * Note that we do not notify the driver about the link state change because
4795 * the change is only temporary and can be disregarded from the driver's
4796 * point of view (see @bugref{7057}).
4797 */
4798 return VINF_SUCCESS;
4799 }
4800 /* has the state changed? */
4801 fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
4802 if (pThis->fLinkUp != fLinkUp)
4803 {
4804 pThis->fLinkUp = fLinkUp;
4805 if (fLinkUp)
4806 {
4807 /* Connect with a configured delay. */
4808 pThis->fLinkTempDown = true;
4809 pThis->cLinkDownReported = 0;
4810 pThis->cLinkRestorePostponed = 0;
4811 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4812 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
4813 AssertRC(rc);
4814 }
4815 else
4816 {
4817 /* Disconnect. */
4818 pThis->cLinkDownReported = 0;
4819 pThis->cLinkRestorePostponed = 0;
4820 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4821 }
4822 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
4823 if (pThisCC->pDrv)
4824 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
4825 }
4826 return VINF_SUCCESS;
4827}
4828
4829
4830/* -=-=-=-=-=- DPNICSTATE::ILeds (LUN#0) -=-=-=-=-=- */
4831
4832/**
4833 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4834 */
4835static DECLCALLBACK(int) dpNicQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4836{
4837 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, ILeds);
4838 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4839 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4840 if (iLUN == 0)
4841 {
4842 *ppLed = &pThis->Led;
4843 return VINF_SUCCESS;
4844 }
4845 return VERR_PDM_LUN_NOT_FOUND;
4846}
4847
4848
4849/* -=-=-=-=-=- DPNICSTATE::IBase (LUN#0) -=-=-=-=-=- */
4850
4851/**
4852 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4853 */
4854static DECLCALLBACK(void *) dpNicQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
4855{
4856 PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, IBase);
4857 Assert(&pThisCC->IBase == pInterface);
4858 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4859 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
4860 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
4861 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4862 return NULL;
4863}
4864
4865
4866/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
4867
4868/**
4869 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
4870 */
4871static DECLCALLBACK(void) dpNicR3PowerOff(PPDMDEVINS pDevIns)
4872{
4873 /* Poke thread waiting for buffer space. */
4874 dp8390R3WakeupReceive(pDevIns);
4875}
4876
4877
4878/**
4879 * @interface_method_impl{PDMDEVREG,pfnDetach}
4880 *
4881 * One port on the network card has been disconnected from the network.
4882 */
4883static DECLCALLBACK(void) dpNicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4884{
4885 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4886 PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
4887 RT_NOREF(fFlags);
4888 LogFlowFunc(("#%d\n", pThis->iInstance));
4889
4890 AssertLogRelReturnVoid(iLUN == 0);
4891
4892 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4893 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4894
4895 /*
4896 * Zero some important members.
4897 */
4898 pThis->fDriverAttached = false;
4899 pThisCC->pDrvBase = NULL;
4900 pThisCC->pDrv = NULL;
4901
4902 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4903}
4904
4905
4906/**
4907 * @interface_method_impl{PDMDEVREG,pfnAttach}
4908 * One port on the network card has been connected to a network.
4909 */
4910static DECLCALLBACK(int) dpNicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4911{
4912 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4913 PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
4914 RT_NOREF(fFlags);
4915 LogFlowFunc(("#%d\n", pThis->iInstance));
4916
4917 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
4918
4919 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4920 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4921
4922 /*
4923 * Attach the driver.
4924 */
4925 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
4926 if (RT_SUCCESS(rc))
4927 {
4928 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
4929 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
4930 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
4931 pThis->fDriverAttached = true;
4932 }
4933 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
4934 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
4935 {
4936 /* This should never happen because this function is not called
4937 * if there is no driver to attach! */
4938 LogFunc(("#%d No attached driver!\n", pThis->iInstance));
4939 }
4940
4941 /*
4942 * Temporarily drop the link if it was up so that the guest
4943 * will know that we have changed the configuration of the
4944 * network card
4945 */
4946 if (RT_SUCCESS(rc))
4947 dp8390TempLinkDown(pDevIns, pThis);
4948
4949 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4950 return rc;
4951}
4952
4953
4954/**
4955 * @interface_method_impl{PDMDEVREG,pfnSuspend}
4956 */
4957static DECLCALLBACK(void) dpNicR3Suspend(PPDMDEVINS pDevIns)
4958{
4959 /* Poke thread waiting for buffer space. */
4960 dp8390R3WakeupReceive(pDevIns);
4961}
4962
4963
4964/**
4965 * @interface_method_impl{PDMDEVREG,pfnReset}
4966 */
4967static DECLCALLBACK(void) dpNicR3Reset(PPDMDEVINS pDevIns)
4968{
4969 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
4970 LogFlowFunc(("#%d\n", pThis->iInstance));
4971 if (pThis->fLinkTempDown)
4972 {
4973 pThis->cLinkDownReported = 0x1000;
4974 pThis->cLinkRestorePostponed = 0x1000;
4975 PDMDevHlpTimerStop(pDevIns, pThis->hTimerRestore);
4976 dpNicR3TimerRestore(pDevIns, pThis->hTimerRestore, pThis);
4977 }
4978
4979 dpNicR3HardReset(pDevIns, pThis);
4980}
4981
4982
4983/**
4984 * @interface_method_impl{PDMDEVREG,pfnRelocate}
4985 */
4986static DECLCALLBACK(void) dpNicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4987{
4988 PDPNICSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PDPNICSTATERC);
4989 pThisRC->pDrv += offDelta;
4990}
4991
4992
4993/**
4994 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4995 */
4996static DECLCALLBACK(int) dpNicR3Destruct(PPDMDEVINS pDevIns)
4997{
4998 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
4999 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
5000
5001 if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->CritSect))
5002 {
5003 RTSemEventSignal(pThis->hEventOutOfRxSpace);
5004 RTSemEventDestroy(pThis->hEventOutOfRxSpace);
5005 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
5006 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
5007 }
5008 return VINF_SUCCESS;
5009}
5010
5011
5012/**
5013 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5014 */
5015static DECLCALLBACK(int) dpNicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5016{
5017 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5018 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
5019 PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
5020 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5021 PPDMIBASE pBase;
5022 char szTmp[128];
5023 int rc;
5024
5025 /*
5026 * Init what's required to make the destructor safe.
5027 */
5028 pThis->iInstance = iInstance;
5029 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
5030 pThis->hIoPortsNic = NIL_IOMIOPORTHANDLE;
5031 pThis->hIoPortsCore = NIL_IOMIOPORTHANDLE;
5032 pThisCC->pDevIns = pDevIns;
5033
5034 /*
5035 * Validate configuration.
5036 */
5037 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|Port|MemBase|IRQ|DMA|DeviceType|LinkUpDelay|LineSpeed", "");
5038
5039 /*
5040 * Read the configuration.
5041 */
5042 rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
5043 if (RT_FAILURE(rc))
5044 return PDMDEV_SET_ERROR(pDevIns, rc,
5045 N_("Configuration error: Failed to get the \"MAC\" value"));
5046 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
5047 if (RT_FAILURE(rc))
5048 return PDMDEV_SET_ERROR(pDevIns, rc,
5049 N_("Configuration error: Failed to get the \"CableConnected\" value"));
5050
5051 /*
5052 * Determine the model.
5053 */
5054 char szDeviceType[16];
5055 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "DeviceType", &szDeviceType[0], sizeof(szDeviceType), "NE2000");
5056 if (RT_FAILURE(rc))
5057 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ChipType\" as string failed"));
5058
5059 if (!strcmp(szDeviceType, "NE1000"))
5060 pThis->uDevType = DEV_NE1000; /* Novell NE1000. */
5061 else if (!strcmp(szDeviceType, "NE2000"))
5062 pThis->uDevType = DEV_NE2000; /* Novell NE2000. */
5063 else if (!strcmp(szDeviceType, "WD8003"))
5064 pThis->uDevType = DEV_WD8003; /* WD EtherCard Plus. */
5065 else if (!strcmp(szDeviceType, "WD8013"))
5066 pThis->uDevType = DEV_WD8013; /* WD EtherCard Plus 16. */
5067 else if (!strcmp(szDeviceType, "3C503"))
5068 pThis->uDevType = DEV_3C503; /* 3Com 3C503 EtherLink II. */
5069 else
5070 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5071 N_("Configuration error: The \"DeviceType\" value \"%s\" is unsupported"),
5072 szDeviceType);
5073
5074
5075 /*
5076 * Default resource assignments depend on the device type.
5077 */
5078 RTIOPORT uDefIoPort = 0; /* To be overridden. */
5079 uint8_t uDefIrq = 0;
5080 uint8_t uDefDma = 0; /* Default to no DMA. */
5081 unsigned uDefMemBase = 0; /* Default to no shared memory. */
5082
5083 if ((pThis->uDevType == DEV_NE1000) || (pThis->uDevType == DEV_NE2000))
5084 {
5085 uDefIoPort = 0x300;
5086 uDefIrq = 3;
5087 }
5088 else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
5089 {
5090 uDefIoPort = 0x280;
5091 uDefIrq = 3;
5092 uDefMemBase = 0xd0000;
5093 pThis->cbMemSize = _8K;
5094 if (pThis->uDevType == DEV_WD8013)
5095 pThis->cbMemSize = _16K;
5096 }
5097 else if (pThis->uDevType == DEV_3C503)
5098 {
5099 uDefIoPort = 0x300;
5100 uDefIrq = 3;
5101 uDefDma = 1;
5102 uDefMemBase = 0xdc000;
5103 pThis->cbMemSize = _8K;
5104 }
5105
5106 /*
5107 * Process ISA configuration options.
5108 */
5109 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->IOPortBase, uDefIoPort);
5110 if (RT_FAILURE(rc))
5111 return PDMDEV_SET_ERROR(pDevIns, rc,
5112 N_("Configuration error: Failed to get the \"Port\" value"));
5113
5114 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, uDefIrq);
5115 if (RT_FAILURE(rc))
5116 return PDMDEV_SET_ERROR(pDevIns, rc,
5117 N_("Configuration error: Failed to get the \"IRQ\" value"));
5118
5119 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA", &pThis->uIsaDma, uDefDma);
5120 if (RT_FAILURE(rc))
5121 return PDMDEV_SET_ERROR(pDevIns, rc,
5122 N_("Configuration error: Failed to get the \"DMA\" value"));
5123
5124 rc = pHlp->pfnCFGMQueryGCPtrDef(pCfg, "MemBase", &pThis->MemBase, uDefMemBase);
5125 if (RT_FAILURE(rc))
5126 return PDMDEV_SET_ERROR(pDevIns, rc,
5127 N_("Configuration error: Failed to get the \"MemBase\" value"));
5128
5129
5130
5131 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
5132 if (RT_FAILURE(rc))
5133 return PDMDEV_SET_ERROR(pDevIns, rc,
5134 N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
5135 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
5136 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
5137 {
5138 LogRel(("DPNIC#%d WARNING! Link up delay is set to %u seconds!\n",
5139 iInstance, pThis->cMsLinkUpDelay / 1000));
5140 }
5141 LogFunc(("#%d Link up delay is set to %u seconds\n",
5142 iInstance, pThis->cMsLinkUpDelay / 1000));
5143
5144
5145 /*
5146 * Initialize data (most of it anyway).
5147 */
5148 pThis->Led.u32Magic = PDMLED_MAGIC;
5149 /* IBase */
5150 pThisCC->IBase.pfnQueryInterface = dpNicQueryInterface;
5151 /* INetworkPort */
5152 pThisCC->INetworkDown.pfnWaitReceiveAvail = dpNicNet_WaitReceiveAvail;
5153 pThisCC->INetworkDown.pfnReceive = dpNicNet_Receive;
5154 pThisCC->INetworkDown.pfnXmitPending = dpNicNet_XmitPending;
5155 /* INetworkConfig */
5156 pThisCC->INetworkConfig.pfnGetMac = dpNicGetMac;
5157 pThisCC->INetworkConfig.pfnGetLinkState = dpNicGetLinkState;
5158 pThisCC->INetworkConfig.pfnSetLinkState = dpNicSetLinkState;
5159 /* ILeds */
5160 pThisCC->ILeds.pfnQueryStatusLed = dpNicQueryStatusLed;
5161
5162 pThis->hIoPortsCore = NIL_IOMIOPORTHANDLE;
5163 pThis->hIoPortsNic = NIL_IOMIOPORTHANDLE;
5164 pThis->hSharedMem = NIL_IOMMMIOHANDLE;
5165
5166 /*
5167 * We use our own critical section (historical reasons).
5168 */
5169 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "DPNIC#%u", iInstance);
5170 AssertRCReturn(rc, rc);
5171 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5172 AssertRCReturn(rc, rc);
5173
5174 rc = RTSemEventCreate(&pThis->hEventOutOfRxSpace);
5175 AssertRCReturn(rc, rc);
5176
5177 /*
5178 * Register ISA I/O ranges. This depends on the device type.
5179 */
5180 if ((pThis->uDevType == DEV_NE1000) || (pThis->uDevType == DEV_NE2000))
5181 {
5182 /* The NE1000 and NE2000 map the DP8390 at the beginning of the port range,
5183 * followed by the data/reset ports.
5184 */
5185 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, dp8390CoreIOPortWrite, dp8390CoreIOPortRead,
5186 "DP8390-Core", NULL /*paExtDesc*/, &pThis->hIoPortsCore);
5187 if (RT_FAILURE(rc))
5188 return rc;
5189 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x10, 0x10 /*cPorts*/, neIOPortWrite, neIOPortRead,
5190 "DPNIC-NE", NULL /*paExtDesc*/, &pThis->hIoPortsNic);
5191 if (RT_FAILURE(rc))
5192 return rc;
5193 }
5194 else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
5195 {
5196 /* The WD8003 and WD8013 map the DP8390 at the end of the port range
5197 * (16 bytes into it). The first 8 bytes of the range are largely unused
5198 * while the second 8 bytes map the PROM.
5199 */
5200 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, wdIOPortWrite, wdIOPortRead,
5201 "DPNIC-WD", NULL /*paExtDesc*/, &pThis->hIoPortsNic);
5202 if (RT_FAILURE(rc))
5203 return rc;
5204 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x10, 0x10 /*cPorts*/, dp8390CoreIOPortWrite, dp8390CoreIOPortRead,
5205 "DP8390-Core", NULL /*paExtDesc*/, &pThis->hIoPortsCore);
5206 if (RT_FAILURE(rc))
5207 return rc;
5208
5209 /*
5210 * Shared memory MMIO area. This is rather lame.
5211 */
5212 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, pThis->MemBase, pThis->cbMemSize,
5213 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
5214 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
5215 wdMemWrite, wdMemRead, NULL /*wdMmioFill*/, NULL /*pvUser*/,
5216 "DPNIC - WD Shared RAM", &pThis->hSharedMem);
5217 AssertRCReturn(rc, rc);
5218
5219 /* Hack to make WD drivers happy. */
5220 memcpy(&pThis->MacConfigured, "\x00\x00\xC0", 3);
5221 }
5222 else if (pThis->uDevType == DEV_3C503)
5223 {
5224 /* The 3C503 maps the DP8390 at the base I/O address, except the first
5225 * or second 16 bytes of PROM can be mapped into the same space. The
5226 * custom Gate Array is mapped at I/O base + 400h.
5227 */
5228 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, dp8390CoreIOPortWrite, dp8390CoreIOPortRead,
5229 "DP8390-Core", NULL /*paExtDesc*/, &pThis->hIoPortsCore);
5230 if (RT_FAILURE(rc))
5231 return rc;
5232
5233 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x400, 0x10 /*cPorts*/, elIOPortWrite, elIOPortRead,
5234 "DPNIC-EL", NULL /*paExtDesc*/, &pThis->hIoPortsNic);
5235 if (RT_FAILURE(rc))
5236 return rc;
5237
5238 /*
5239 * Shared memory MMIO area. The same lame thing.
5240 */
5241 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, pThis->MemBase, pThis->cbMemSize,
5242 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
5243 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
5244 elMemWrite, elMemRead, NULL /*elMmioFill*/, NULL /*pvUser*/,
5245 "DPNIC - 3C503 Shared RAM", &pThis->hSharedMem);
5246 AssertRCReturn(rc, rc);
5247
5248 /*
5249 * Register DMA channel.
5250 */
5251 if ((pThis->uIsaDma >= ELNKII_MIN_VALID_DMA) && (pThis->uIsaDma <= ELNKII_MAX_VALID_DMA))
5252 {
5253 rc = PDMDevHlpDMARegister(pDevIns, pThis->uIsaDma, elnk3R3DMAXferHandler, pThis);
5254 if (RT_FAILURE(rc))
5255 return rc;
5256 LogRel(("DPNIC#%d: Enabling 3C503 DMA channel %u\n", iInstance, pThis->uIsaDma));
5257 }
5258 else
5259 LogRel(("DPNIC#%d: Disabling 3C503 DMA\n", iInstance));
5260
5261 /* Hack to make 3C503 diagnostics happy. */
5262 memcpy(&pThis->MacConfigured, "\x02\x60\x8C", 3);
5263 }
5264
5265
5266 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, dpNicR3TimerRestore, NULL, TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
5267 "DPNIC Link Restore Timer", &pThis->hTimerRestore);
5268 if (RT_FAILURE(rc))
5269 return rc;
5270
5271 rc = PDMDevHlpSSMRegisterEx(pDevIns, DPNIC_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
5272 NULL, dpNicLiveExec, NULL,
5273 dpNicSavePrep, dpNicSaveExec, NULL,
5274 dpNicLoadPrep, dpNicLoadExec, NULL);
5275 if (RT_FAILURE(rc))
5276 return rc;
5277
5278 /*
5279 * Create the transmit notifier signaller.
5280 */
5281 rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "DPNIC-Xmit", dpNicR3XmitTaskCallback, NULL /*pvUser*/, &pThis->hXmitTask);
5282 if (RT_FAILURE(rc))
5283 return rc;
5284
5285 /*
5286 * Create the RX notifier signaller.
5287 */
5288 rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "DPNIC-Rcv", dpNicR3CanRxTaskCallback, NULL /*pvUser*/, &pThis->hCanRxTask);
5289 if (RT_FAILURE(rc))
5290 return rc;
5291
5292 /*
5293 * Register the info item.
5294 */
5295 RTStrPrintf(szTmp, sizeof(szTmp), "dpnic%d", pThis->iInstance);
5296 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "dpnic info", dpNicR3Info);
5297
5298 /*
5299 * Attach status driver (optional).
5300 */
5301 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
5302 if (RT_SUCCESS(rc))
5303 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5304 else if ( rc != VERR_PDM_NO_ATTACHED_DRIVER
5305 && rc != VERR_PDM_CFG_MISSING_DRIVER_NAME)
5306 {
5307 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5308 return rc;
5309 }
5310
5311 /*
5312 * Attach driver.
5313 */
5314 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
5315 if (RT_SUCCESS(rc))
5316 {
5317 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
5318 AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
5319 VERR_PDM_MISSING_INTERFACE_BELOW);
5320 pThis->fDriverAttached = true;
5321 }
5322 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
5323 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
5324 {
5325 /* No error! */
5326 LogFunc(("No attached driver!\n"));
5327 }
5328 else
5329 return rc;
5330
5331 /*
5332 * Reset the device state. (Do after attaching.)
5333 */
5334 dpNicR3HardReset(pDevIns, pThis);
5335
5336 /*
5337 * Register statistics counters.
5338 */
5339 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Public/Net/DPNIC%u/BytesReceived", iInstance);
5340 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Public/Net/DPNIC%u/BytesTransmitted", iInstance);
5341
5342 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/DPNIC%d/ReceiveBytes", iInstance);
5343 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/DPNIC%d/TransmitBytes", iInstance);
5344
5345#ifdef VBOX_WITH_STATISTICS
5346 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ", "/Devices/DPNIC%d/IO/ReadRZ", iInstance);
5347 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3", "/Devices/DPNIC%d/IO/ReadR3", iInstance);
5348 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ", "/Devices/DPNIC%d/IO/WriteRZ", iInstance);
5349 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3", "/Devices/DPNIC%d/IO/WriteR3", iInstance);
5350 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/DPNIC%d/Receive", iInstance);
5351 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/DPNIC%d/RxOverflow", iInstance);
5352 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES , "Nr of RX overflow wakeups", "/Devices/DPNIC%d/RxOverflowWakeup", iInstance);
5353 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxCanReceiveNow, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES , "Can receive immediately", "/Devices/DPNIC%d/RxCanReceiveNow", iInstance);
5354 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxCannotReceiveNow, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES , "Cannot receive, not waiting", "/Devices/DPNIC%d/RxCannotReceiveNow", iInstance);
5355 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ", "/Devices/DPNIC%d/Transmit/TotalRZ", iInstance);
5356 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3", "/Devices/DPNIC%d/Transmit/TotalR3", iInstance);
5357 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in RZ", "/Devices/DPNIC%d/Transmit/SendRZ", iInstance);
5358 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in R3", "/Devices/DPNIC%d/Transmit/SendR3", iInstance);
5359
5360 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks", "/Devices/DPNIC%d/UpdateIRQ", iInstance);
5361
5362 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktMonitor, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, monitor mode", "/Devices/DPNIC%d/DropPktMonitor", iInstance);
5363 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktRcvrDis, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, receiver not enabled", "/Devices/DPNIC%d/DropPktRcvrDis", iInstance);
5364 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktVeryShort, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet less than 8 bytes long", "/Devices/DPNIC%d/DropPktVeryShort", iInstance);
5365 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktVMNotRunning,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, VM not running", "/Devices/DPNIC%d/DropPktVMNotRunning", iInstance);
5366 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoLink, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, no link", "/Devices/DPNIC%d/DropPktNoLink", iInstance);
5367 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoMatch, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, address match reject", "/Devices/DPNIC%d/DropPktNoMatch", iInstance);
5368 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoBuffer, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, DP8390 buffer overflow", "/Devices/DPNIC%d/DropPktNoBuffer", iInstance);
5369#endif /* VBOX_WITH_STATISTICS */
5370
5371 return VINF_SUCCESS;
5372}
5373
5374#else
5375
5376/**
5377 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
5378 */
5379static DECLCALLBACK(int) dpNicRZConstruct(PPDMDEVINS pDevIns)
5380{
5381 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5382 PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
5383
5384 /* Critical section setup: */
5385 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5386 AssertRCReturn(rc, rc);
5387
5388 /* NIC-specific ISA I/O ports: */
5389 if (pThis->hIoPortsNic != NIL_IOMIOPORTHANDLE)
5390 {
5391 switch (pThis->uDevType)
5392 {
5393 case DEV_NE1000:
5394 case DEV_NE2000:
5395 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNic, neIOPortWrite, neIOPortRead, NULL /*pvUser*/);
5396 AssertRCReturn(rc, rc);
5397 break;
5398 case DEV_WD8003:
5399 case DEV_WD8013:
5400 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNic, wdIOPortWrite, wdIOPortRead, NULL /*pvUser*/);
5401 AssertRCReturn(rc, rc);
5402 break;
5403 case DEV_3C503:
5404 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNic, elIOPortWrite, elIOPortRead, NULL /*pvUser*/);
5405 AssertRCReturn(rc, rc);
5406 break;
5407 default:
5408 /* Must not happen. */
5409 return VERR_INTERNAL_ERROR;
5410 }
5411 }
5412
5413 /* Common DP8390 core I/O ports: */
5414 if (pThis->hIoPortsCore != NIL_IOMIOPORTHANDLE)
5415 {
5416 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsCore, dp8390CoreIOPortWrite, dp8390CoreIOPortRead, NULL /*pvUser*/);
5417 AssertRCReturn(rc, rc);
5418 }
5419
5420 /* Shared RAM, if used: */
5421 if (pThis->hSharedMem != NIL_IOMMMIOHANDLE)
5422 {
5423 AssertRCReturn(rc, rc);
5424 switch (pThis->uDevType)
5425 {
5426 case DEV_WD8003:
5427 case DEV_WD8013:
5428 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hSharedMem, wdMemWrite, wdMemRead, NULL /*pvUser*/);
5429 AssertRCReturn(rc, rc);
5430 break;
5431 case DEV_3C503:
5432 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hSharedMem, elMemWrite, elMemRead, NULL /*pvUser*/);
5433 AssertRCReturn(rc, rc);
5434 break;
5435 case DEV_NE1000:
5436 case DEV_NE2000:
5437 default:
5438 /* Must not happen. */
5439 return VERR_INTERNAL_ERROR;
5440 }
5441 }
5442
5443 return VINF_SUCCESS;
5444}
5445
5446#endif /* IN_RING3 */
5447
5448/**
5449 * The device registration structure.
5450 */
5451const PDMDEVREG g_DeviceDP8390 =
5452{
5453 /* .u32Version = */ PDM_DEVREG_VERSION,
5454 /* .uReserved0 = */ 0,
5455 /* .szName = */ "dp8390",
5456 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
5457 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
5458 /* .cMaxInstances = */ ~0U,
5459 /* .uSharedVersion = */ 42,
5460 /* .cbInstanceShared = */ sizeof(DPNICSTATE),
5461 /* .cbInstanceCC = */ sizeof(DPNICSTATECC),
5462 /* .cbInstanceRC = */ sizeof(DPNICSTATERC),
5463 /* .cMaxPciDevices = */ 0,
5464 /* .cMaxMsixVectors = */ 0,
5465 /* .pszDescription = */ "National Semiconductor DP8390 based adapter.\n",
5466#if defined(IN_RING3)
5467 /* .pszRCMod = */ "VBoxDDRC.rc",
5468 /* .pszR0Mod = */ "VBoxDDR0.r0",
5469 /* .pfnConstruct = */ dpNicR3Construct,
5470 /* .pfnDestruct = */ dpNicR3Destruct,
5471 /* .pfnRelocate = */ dpNicR3Relocate,
5472 /* .pfnMemSetup = */ NULL,
5473 /* .pfnPowerOn = */ NULL,
5474 /* .pfnReset = */ dpNicR3Reset,
5475 /* .pfnSuspend = */ dpNicR3Suspend,
5476 /* .pfnResume = */ NULL,
5477 /* .pfnAttach = */ dpNicR3Attach,
5478 /* .pfnDetach = */ dpNicR3Detach,
5479 /* .pfnQueryInterface = */ NULL,
5480 /* .pfnInitComplete = */ NULL,
5481 /* .pfnPowerOff = */ dpNicR3PowerOff,
5482 /* .pfnSoftReset = */ NULL,
5483 /* .pfnReserved0 = */ NULL,
5484 /* .pfnReserved1 = */ NULL,
5485 /* .pfnReserved2 = */ NULL,
5486 /* .pfnReserved3 = */ NULL,
5487 /* .pfnReserved4 = */ NULL,
5488 /* .pfnReserved5 = */ NULL,
5489 /* .pfnReserved6 = */ NULL,
5490 /* .pfnReserved7 = */ NULL,
5491#elif defined(IN_RING0)
5492 /* .pfnEarlyConstruct = */ NULL,
5493 /* .pfnConstruct = */ dpNicRZConstruct,
5494 /* .pfnDestruct = */ NULL,
5495 /* .pfnFinalDestruct = */ NULL,
5496 /* .pfnRequest = */ NULL,
5497 /* .pfnReserved0 = */ NULL,
5498 /* .pfnReserved1 = */ NULL,
5499 /* .pfnReserved2 = */ NULL,
5500 /* .pfnReserved3 = */ NULL,
5501 /* .pfnReserved4 = */ NULL,
5502 /* .pfnReserved5 = */ NULL,
5503 /* .pfnReserved6 = */ NULL,
5504 /* .pfnReserved7 = */ NULL,
5505#elif defined(IN_RC)
5506 /* .pfnConstruct = */ NULL,
5507 /* .pfnReserved0 = */ NULL,
5508 /* .pfnReserved1 = */ NULL,
5509 /* .pfnReserved2 = */ NULL,
5510 /* .pfnReserved3 = */ NULL,
5511 /* .pfnReserved4 = */ NULL,
5512 /* .pfnReserved5 = */ NULL,
5513 /* .pfnReserved6 = */ NULL,
5514 /* .pfnReserved7 = */ NULL,
5515#else
5516# error "Not in IN_RING3, IN_RING0 or IN_RC!"
5517#endif
5518 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
5519};
5520
5521#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette