VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevPCNet.cpp@ 106273

Last change on this file since 106273 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 212.6 KB
Line 
1/* $Id: DevPCNet.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * DevPCNet - AMD PCnet-PCI II / PCnet-FAST III (Am79C970A / Am79C973) Ethernet Controller Emulation.
4 *
5 * This software was written to be compatible with the specifications:
6 * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
7 * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
8 * and
9 * AMD Am79C973/Am79C975 PCnet-FAST III Single-Chip 10/100 Mbps PCI Ethernet Controller datasheet
10 * AMD publication# 20510 Rev:E Amendment/0 Issue Date: August 2000
11 * and
12 * AMD Am79C960 PCnet-ISA Single-Chip Ethernet Controller datasheet
13 * AMD publication# 16907 Rev:B Amendment/0 Issue Date: May 1994
14 */
15
16/*
17 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
18 *
19 * This file is part of VirtualBox base platform packages, as
20 * available from https://www.virtualbox.org.
21 *
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License
24 * as published by the Free Software Foundation, in version 3 of the
25 * License.
26 *
27 * This program is distributed in the hope that it will be useful, but
28 * WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 * General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, see <https://www.gnu.org/licenses>.
34 *
35 * SPDX-License-Identifier: GPL-3.0-only
36 * --------------------------------------------------------------------
37 *
38 * This code is based on:
39 *
40 * AMD PCnet-PCI II (Am79C970A) emulation
41 *
42 * Copyright (c) 2004 Antony T Curtis
43 *
44 * Permission is hereby granted, free of charge, to any person obtaining a copy
45 * of this software and associated documentation files (the "Software"), to deal
46 * in the Software without restriction, including without limitation the rights
47 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48 * copies of the Software, and to permit persons to whom the Software is
49 * furnished to do so, subject to the following conditions:
50 *
51 * The above copyright notice and this permission notice shall be included in
52 * all copies or substantial portions of the Software.
53 *
54 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
57 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
59 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
60 * THE SOFTWARE.
61 */
62
63
64/*********************************************************************************************************************************
65* Header Files *
66*********************************************************************************************************************************/
67#define LOG_GROUP LOG_GROUP_DEV_PCNET
68#include <VBox/vmm/pdmdev.h>
69#include <VBox/vmm/pdmnetifs.h>
70#include <VBox/vmm/pgm.h>
71#include <VBox/version.h>
72#include <iprt/asm.h>
73#include <iprt/assert.h>
74#include <iprt/critsect.h>
75#include <iprt/net.h>
76#include <iprt/string.h>
77#include <iprt/time.h>
78#ifdef IN_RING3
79# include <iprt/mem.h>
80# include <iprt/semaphore.h>
81# include <iprt/uuid.h>
82#endif
83
84#include "VBoxDD.h"
85
86
87/*********************************************************************************************************************************
88* Defined Constants And Macros *
89*********************************************************************************************************************************/
90
91/* Enable to handle frequent io reads in the guest context (recommended) */
92#define PCNET_GC_ENABLED
93
94/* PCNET_DEBUG_IO - Log6 */
95/* PCNET_DEBUG_BCR - Log7 */
96/* PCNET_DEBUG_CSR - Log8 */
97/* PCNET_DEBUG_RMD - Log9 */
98/* PCNET_DEBUG_TMD - Log10 */
99/* PCNET_DEBUG_MATCH - Log11 */
100/* PCNET_DEBUG_MII - Log12 */
101
102#define PCNET_IOPORT_SIZE 0x20
103#define PCNET_PNPMMIO_SIZE 0x20
104
105#define PCNET_SAVEDSTATE_VERSION 10
106
107#define BCR_MAX_RAP 50
108#define MII_MAX_REG 32
109#define CSR_MAX_REG 128
110
111/** Maximum number of times we report a link down to the guest (failure to send frame) */
112#define PCNET_MAX_LINKDOWN_REPORTED 3
113
114/** Maximum frame size we handle */
115#define MAX_FRAME 1536
116
117#define PCNET_INST_NR (pThis->iInstance)
118
119/** @name Bus configuration registers
120 * @{ */
121#define BCR_MSRDA 0
122#define BCR_MSWRA 1
123#define BCR_MC 2
124#define BCR_RESERVED3 3
125#define BCR_LNKST 4
126#define BCR_LED1 5
127#define BCR_LED2 6
128#define BCR_LED3 7
129#define BCR_RESERVED8 8
130#define BCR_FDC 9
131/* 10 - 15 = reserved */
132#define BCR_IOBASEL 16 /* Reserved */
133#define BCR_IOBASEU 16 /* Reserved */
134#define BCR_BSBC 18
135#define BCR_EECAS 19
136#define BCR_SWS 20
137#define BCR_INTCON 21 /* Reserved */
138#define BCR_PLAT 22
139#define BCR_PCISVID 23
140#define BCR_PCISID 24
141#define BCR_SRAMSIZ 25
142#define BCR_SRAMB 26
143#define BCR_SRAMIC 27
144#define BCR_EBADDRL 28
145#define BCR_EBADDRU 29
146#define BCR_EBD 30
147#define BCR_STVAL 31
148#define BCR_MIICAS 32
149#define BCR_MIIADDR 33
150#define BCR_MIIMDR 34
151#define BCR_PCIVID 35
152#define BCR_PMC_A 36
153#define BCR_DATA0 37
154#define BCR_DATA1 38
155#define BCR_DATA2 39
156#define BCR_DATA3 40
157#define BCR_DATA4 41
158#define BCR_DATA5 42
159#define BCR_DATA6 43
160#define BCR_DATA7 44
161#define BCR_PMR1 45
162#define BCR_PMR2 46
163#define BCR_PMR3 47
164/** @} */
165
166/** @name Bus configuration sub register accessors.
167 * @{ */
168#define BCR_DWIO(S) !!((S)->aBCR[BCR_BSBC] & 0x0080)
169#define BCR_SSIZE32(S) !!((S)->aBCR[BCR_SWS ] & 0x0100)
170#define BCR_SWSTYLE(S) ((S)->aBCR[BCR_SWS ] & 0x00FF)
171/** @} */
172
173/** @name CSR subregister accessors.
174 * @{ */
175#define CSR_INIT(S) !!((S)->aCSR[0] & 0x0001) /**< Init assertion */
176#define CSR_STRT(S) !!((S)->aCSR[0] & 0x0002) /**< Start assertion */
177#define CSR_STOP(S) !!((S)->aCSR[0] & 0x0004) /**< Stop assertion */
178#define CSR_TDMD(S) !!((S)->aCSR[0] & 0x0008) /**< Transmit demand. (perform xmit poll now (readable, settable, not clearable) */
179#define CSR_TXON(S) !!((S)->aCSR[0] & 0x0010) /**< Transmit on (readonly) */
180#define CSR_RXON(S) !!((S)->aCSR[0] & 0x0020) /**< Receive On */
181#define CSR_INEA(S) !!((S)->aCSR[0] & 0x0040) /**< Interrupt Enable */
182#define CSR_LAPPEN(S) !!((S)->aCSR[3] & 0x0020) /**< Look Ahead Packet Processing Enable */
183#define CSR_DXSUFLO(S) !!((S)->aCSR[3] & 0x0040) /**< Disable Transmit Stop on Underflow error */
184#define CSR_ASTRP_RCV(S) !!((S)->aCSR[4] & 0x0400) /**< Auto Strip Receive */
185#define CSR_DPOLL(S) !!((S)->aCSR[4] & 0x1000) /**< Disable Transmit Polling */
186#define CSR_SPND(S) !!((S)->aCSR[5] & 0x0001) /**< Suspend */
187#define CSR_LTINTEN(S) !!((S)->aCSR[5] & 0x4000) /**< Last Transmit Interrupt Enable */
188#define CSR_TOKINTD(S) !!((S)->aCSR[5] & 0x8000) /**< Transmit OK Interrupt Disable */
189
190#define CSR_STINT !!((S)->aCSR[7] & 0x0800) /**< Software Timer Interrupt */
191#define CSR_STINTE !!((S)->aCSR[7] & 0x0400) /**< Software Timer Interrupt Enable */
192
193#define CSR_DRX(S) !!((S)->aCSR[15] & 0x0001) /**< Disable Receiver */
194#define CSR_DTX(S) !!((S)->aCSR[15] & 0x0002) /**< Disable Transmit */
195#define CSR_LOOP(S) !!((S)->aCSR[15] & 0x0004) /**< Loopback Enable */
196#define CSR_DRCVPA(S) !!((S)->aCSR[15] & 0x2000) /**< Disable Receive Physical Address */
197#define CSR_DRCVBC(S) !!((S)->aCSR[15] & 0x4000) /**< Disable Receive Broadcast */
198#define CSR_PROM(S) !!((S)->aCSR[15] & 0x8000) /**< Promiscuous Mode */
199/** @} */
200
201#if defined(RT_BIG_ENDIAN) || !defined(RT_LITTLE_ENDIAN)
202# error fix macros (and more in this file) for big-endian machines
203#endif
204
205/** @name CSR register accessors.
206 * @{ */
207#define CSR_IADR(S) (*(uint32_t*)((S)->aCSR + 1)) /**< Initialization Block Address */
208#define CSR_CRBA(S) (*(uint32_t*)((S)->aCSR + 18)) /**< Current Receive Buffer Address */
209#define CSR_CXBA(S) (*(uint32_t*)((S)->aCSR + 20)) /**< Current Transmit Buffer Address */
210#define CSR_NRBA(S) (*(uint32_t*)((S)->aCSR + 22)) /**< Next Receive Buffer Address */
211#define CSR_BADR(S) (*(uint32_t*)((S)->aCSR + 24)) /**< Base Address of Receive Ring */
212#define CSR_NRDA(S) (*(uint32_t*)((S)->aCSR + 26)) /**< Next Receive Descriptor Address */
213#define CSR_CRDA(S) (*(uint32_t*)((S)->aCSR + 28)) /**< Current Receive Descriptor Address */
214#define CSR_BADX(S) (*(uint32_t*)((S)->aCSR + 30)) /**< Base Address of Transmit Descriptor */
215#define CSR_NXDA(S) (*(uint32_t*)((S)->aCSR + 32)) /**< Next Transmit Descriptor Address */
216#define CSR_CXDA(S) (*(uint32_t*)((S)->aCSR + 34)) /**< Current Transmit Descriptor Address */
217#define CSR_NNRD(S) (*(uint32_t*)((S)->aCSR + 36)) /**< Next Next Receive Descriptor Address */
218#define CSR_NNXD(S) (*(uint32_t*)((S)->aCSR + 38)) /**< Next Next Transmit Descriptor Address */
219#define CSR_CRBC(S) ((S)->aCSR[40]) /**< Current Receive Byte Count */
220#define CSR_CRST(S) ((S)->aCSR[41]) /**< Current Receive Status */
221#define CSR_CXBC(S) ((S)->aCSR[42]) /**< Current Transmit Byte Count */
222#define CSR_CXST(S) ((S)->aCSR[43]) /**< Current transmit status */
223#define CSR_NRBC(S) ((S)->aCSR[44]) /**< Next Receive Byte Count */
224#define CSR_NRST(S) ((S)->aCSR[45]) /**< Next Receive Status */
225#define CSR_POLL(S) ((S)->aCSR[46]) /**< Transmit Poll Time Counter */
226#define CSR_PINT(S) ((S)->aCSR[47]) /**< Transmit Polling Interval */
227#define CSR_PXDA(S) (*(uint32_t*)((S)->aCSR + 60)) /**< Previous Transmit Descriptor Address*/
228#define CSR_PXBC(S) ((S)->aCSR[62]) /**< Previous Transmit Byte Count */
229#define CSR_PXST(S) ((S)->aCSR[63]) /**< Previous Transmit Status */
230#define CSR_NXBA(S) (*(uint32_t*)((S)->aCSR + 64)) /**< Next Transmit Buffer Address */
231#define CSR_NXBC(S) ((S)->aCSR[66]) /**< Next Transmit Byte Count */
232#define CSR_NXST(S) ((S)->aCSR[67]) /**< Next Transmit Status */
233#define CSR_RCVRC(S) ((S)->aCSR[72]) /**< Receive Descriptor Ring Counter */
234#define CSR_XMTRC(S) ((S)->aCSR[74]) /**< Transmit Descriptor Ring Counter */
235#define CSR_RCVRL(S) ((S)->aCSR[76]) /**< Receive Descriptor Ring Length */
236#define CSR_XMTRL(S) ((S)->aCSR[78]) /**< Transmit Descriptor Ring Length */
237#define CSR_MISSC(S) ((S)->aCSR[112]) /**< Missed Frame Count */
238/** @} */
239
240/** @name Version for the PCnet/FAST III 79C973 card
241 * @{ */
242#define CSR_VERSION_LOW_79C973 0x5003 /* the lower two bits are always 11b for AMD (JEDEC) */
243#define CSR_VERSION_LOW_79C970A 0x1003
244#define CSR_VERSION_LOW_79C960 0x3003
245#define CSR_VERSION_HIGH 0x0262
246/** @} */
247
248/** Calculates the full physical address. */
249#define PHYSADDR(S,A) ((A) | (S)->GCUpperPhys)
250
251
252/*********************************************************************************************************************************
253* Structures and Typedefs *
254*********************************************************************************************************************************/
255
256/**
257 * Emulated device types.
258 */
259enum PCNET_DEVICE_TYPE
260{
261 DEV_AM79C970A = 0, /* PCnet-PCI II (PCI, 10 Mbps). */
262 DEV_AM79C973 = 1, /* PCnet-FAST III (PCI, 10/100 Mbps). */
263 DEV_AM79C960 = 2, /* PCnet-ISA (ISA, 10 Mbps, NE2100/NE1500T compatible) */
264 DEV_AM79C960_EB = 3 /* PCnet-ISA (ISA, 10 Mbps, Racal InterLan EtherBlaster compatible) */
265};
266
267#define PCNET_IS_PCI(pcnet) ((pcnet->uDevType == DEV_AM79C970A) || (pcnet->uDevType == DEV_AM79C973))
268#define PCNET_IS_ISA(pcnet) ((pcnet->uDevType == DEV_AM79C960) || (pcnet->uDevType == DEV_AM79C960_EB))
269
270/*
271 * Note: Old adapters, including the original NE2100/NE1500T, used the LANCE (Am7990) or
272 * possibly C-LANCE (Am79C90) chips. Those only had the RAP/RDP register and any PROMs were
273 * decoded by external circuitry; as a consequence, different vendors used different layouts.
274 * Several different variants are known:
275 *
276 * 1) Racal Interlan NI6510 (before PCnet-based EtherBlaster variants):
277 * - RDP at iobase + 0x00 (16-bit)
278 * - RAP at iobase + 0x02 (16-bit)
279 * - reset at iobase + 0x04 (8-bit)
280 * - config at iobase + 0x05 (8-bit)
281 * - PROM at iobase + 0x08
282 *
283 * 2) BICC Data Networks ISOLAN 4110-2 (ISA) / 4110-3 (MCA):
284 * - PROM at iobase + 0x00
285 * - reset at iobase + 0x04
286 * - config at iobase + 0x05
287 * - RDP at iobase + 0x0c (16-bit)
288 * - RAP at iobase + 0x0e (16-bit)
289 *
290 * 3) Novell NE2100/NE1500T, AMD Am2100, all PCnet chips:
291 * - PROM at iobase + 0x00
292 * - RDP at iobase + 0x10 (16-bit)
293 * - RAP at iobase + 0x12 (16-bit)
294 * - reset at iobase + 0x14 (16-bit)
295 * - BDP/IDP at iobase + 0x16 (16-bit)
296 *
297 * There were also non-busmastering LANCE adapters, such as the BICC 4110-1, DEC DEPCA,
298 * or NTI 16. Those are uninteresting.
299 *
300 * Newer adapters based on the integrated PCnet-ISA (Am79C960) and later have a fixed
301 * register layout compatible with the NE2100. However, they may still require different
302 * drivers. At least two different incompatible drivers exist for PCnet-based cards:
303 * Those from AMD and those from Racal InterLan for their EtherBlaster cards. The PROM
304 * on EtherBlaster cards uses a different signature ('RD' instead of 'WW') and Racal's
305 * drivers insist on the MAC address having the Racal-Datacom OUI (02 07 01).
306 */
307
308/**
309 * PCNET state.
310 *
311 * @implements PDMIBASE
312 * @implements PDMINETWORKDOWN
313 * @implements PDMINETWORKCONFIG
314 * @implements PDMILEDPORTS
315 */
316typedef struct PCNETSTATE
317{
318 /** Software Interrupt timer - R3. */
319 TMTIMERHANDLE hTimerSoftInt;
320 /** Poll timer - R3. */
321 TMTIMERHANDLE hTimerPoll;
322 /** Restore timer.
323 * This is used to disconnect and reconnect the link after a restore. */
324 TMTIMERHANDLE hTimerRestore;
325
326 /** Transmit signaller. */
327 PDMTASKHANDLE hXmitTask;
328
329 /** Register Address Pointer */
330 uint32_t u32RAP;
331 /** Internal interrupt service */
332 int32_t iISR;
333 /** ??? */
334 uint32_t u32Lnkst;
335 /** Address of the RX descriptor table (ring). Loaded at init. */
336 RTGCPHYS32 GCRDRA;
337 /** Address of the TX descriptor table (ring). Loaded at init. */
338 RTGCPHYS32 GCTDRA;
339 uint8_t aPROM[16];
340 uint16_t aCSR[CSR_MAX_REG];
341 uint16_t aBCR[BCR_MAX_RAP];
342 uint16_t aMII[MII_MAX_REG];
343
344 /** Holds the bits which were really seen by the guest. Relevant are bits
345 * 8..14 (IDON, TINT, RINT, MERR, MISS, CERR, BABL). We don't allow the
346 * guest to clear any of these bits (by writing a ONE) before a bit was
347 * seen by the guest. */
348 uint16_t u16CSR0LastSeenByGuest;
349 /** The configured IRQ for ISA operation. */
350 uint8_t uIsaIrq;
351 /** Alignment padding. */
352 uint8_t Alignment2[1+4];
353
354 /** Last time we polled the queues */
355 uint64_t u64LastPoll;
356
357 /** Size of a RX/TX descriptor (8 or 16 bytes according to SWSTYLE */
358 int32_t iLog2DescSize;
359 /** Bits 16..23 in 16-bit mode */
360 RTGCPHYS32 GCUpperPhys;
361
362 /** Base port of the I/O space region. */
363 RTIOPORT IOPortBase;
364 /** If set the link is currently up. */
365 bool fLinkUp;
366 /** If set the link is temporarily down because of a saved state load. */
367 bool fLinkTempDown;
368
369 /** Number of times we've reported the link down. */
370 uint32_t cLinkDownReported;
371 /** The configured MAC address. */
372 RTMAC MacConfigured;
373 /** Set if PCNETSTATER3::pDrvR3 is not NULL. */
374 bool fDriverAttached;
375 /** Alignment padding. */
376 uint8_t bAlignment3;
377
378 /** The LED. */
379 PDMLED Led;
380
381 /** Access critical section. */
382 PDMCRITSECT CritSect;
383 /** Event semaphore for blocking on receive. */
384 SUPSEMEVENT hEventOutOfRxSpace;
385 /** We are waiting/about to start waiting for more receive buffers. */
386 bool volatile fMaybeOutOfSpace;
387 /** True if we signal the guest that RX packets are missing. */
388 bool fSignalRxMiss;
389 /** Alignment padding. */
390 uint8_t Alignment4[HC_ARCH_BITS == 64 ? 2 : 6];
391
392 /** Error counter for bad receive descriptors. */
393 uint32_t uCntBadRMD;
394 /** Emulated device type. */
395 uint8_t uDevType;
396 bool afAlignment5[3];
397 /** Link speed to be reported through CSR68. */
398 uint32_t u32LinkSpeed;
399 /** MS to wait before we enable the link. */
400 uint32_t cMsLinkUpDelay;
401 /** The device instance number (for logging). */
402 uint32_t iInstance;
403
404 /** PCI Region \#0: I/O ports offset 0x10-0x1f. */
405 IOMIOPORTHANDLE hIoPortsPci;
406 /** PCI Region \#0: I/O ports offset 0x00-0x0f. */
407 IOMIOPORTHANDLE hIoPortsPciAProm;
408 /** PCI Region \#1: MMIO alternative to the I/O ports in region \#0. */
409 IOMMMIOHANDLE hMmioPci;
410
411 /** ISA I/O ports offset 0x10-0x1f. */
412 IOMIOPORTHANDLE hIoPortsIsa;
413 /** ISA I/O ports offset 0x00-0x0f. */
414 IOMIOPORTHANDLE hIoPortsIsaAProm;
415
416 /** Backwards compatible shared memory region during state loading (before 4.3.6). */
417 PGMMMIO2HANDLE hMmio2Shared;
418
419 /** The loopback transmit buffer (avoid stack allocations). */
420 uint8_t abLoopBuf[4096];
421 /** The recv buffer. */
422 uint8_t abRecvBuf[4096];
423
424 STAMCOUNTER StatReceiveBytes;
425 STAMCOUNTER StatTransmitBytes;
426#ifdef VBOX_WITH_STATISTICS
427 STAMPROFILEADV StatMMIOReadRZ;
428 STAMPROFILEADV StatMMIOReadR3;
429 STAMPROFILEADV StatMMIOWriteRZ;
430 STAMPROFILEADV StatMMIOWriteR3;
431 STAMPROFILEADV StatAPROMRead;
432 STAMPROFILEADV StatAPROMWrite;
433 STAMPROFILEADV StatIOReadRZ;
434 STAMPROFILEADV StatIOReadR3;
435 STAMPROFILEADV StatIOWriteRZ;
436 STAMPROFILEADV StatIOWriteR3;
437 STAMPROFILEADV StatTimer;
438 STAMPROFILEADV StatReceive;
439 STAMPROFILEADV StatTransmitR3;
440 STAMPROFILEADV StatTransmitRZ;
441 STAMCOUNTER StatTransmitCase1;
442 STAMCOUNTER StatTransmitCase2;
443 STAMPROFILE StatTransmitSendR3;
444 STAMPROFILE StatTransmitSendRZ;
445 STAMPROFILEADV StatTdtePollRZ;
446 STAMPROFILEADV StatTdtePollR3;
447 STAMPROFILEADV StatTmdStoreRZ;
448 STAMPROFILEADV StatTmdStoreR3;
449 STAMPROFILEADV StatRdtePollR3;
450 STAMPROFILEADV StatRdtePollRZ;
451 STAMPROFILE StatRxOverflow;
452 STAMCOUNTER StatRxOverflowWakeup;
453 STAMCOUNTER aStatXmitFlush[16];
454 STAMCOUNTER aStatXmitChainCounts[16];
455 STAMCOUNTER StatXmitSkipCurrent;
456 STAMPROFILEADV StatInterrupt;
457 STAMPROFILEADV StatPollTimer;
458 STAMCOUNTER StatMIIReads;
459#endif /* VBOX_WITH_STATISTICS */
460} PCNETSTATE;
461/** Pointer to a shared PCnet state structure. */
462typedef PCNETSTATE *PPCNETSTATE;
463
464
465/**
466 * PCNET state for ring-3.
467 *
468 * @implements PDMIBASE
469 * @implements PDMINETWORKDOWN
470 * @implements PDMINETWORKCONFIG
471 * @implements PDMILEDPORTS
472 */
473typedef struct PCNETSTATER3
474{
475 /** Pointer to the device instance. */
476 PPDMDEVINSR3 pDevIns;
477 /** Pointer to the connector of the attached network driver. */
478 PPDMINETWORKUPR3 pDrv;
479 /** Pointer to the attached network driver. */
480 R3PTRTYPE(PPDMIBASE) pDrvBase;
481 /** LUN\#0 + status LUN: The base interface. */
482 PDMIBASE IBase;
483 /** LUN\#0: The network port interface. */
484 PDMINETWORKDOWN INetworkDown;
485 /** LUN\#0: The network config port interface. */
486 PDMINETWORKCONFIG INetworkConfig;
487
488 /** Status LUN: The LED ports. */
489 PDMILEDPORTS ILeds;
490 /** Partner of ILeds. */
491 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
492} PCNETSTATER3;
493/** Pointer to a PCnet state structure for ring-3. */
494typedef PCNETSTATER3 *PPCNETSTATER3;
495
496
497/**
498 * PCNET state for ring-0.
499 */
500typedef struct PCNETSTATER0
501{
502 /** Pointer to the connector of the attached network driver. */
503 PPDMINETWORKUPR0 pDrv;
504} PCNETSTATER0;
505/** Pointer to a PCnet state structure for ring-0. */
506typedef PCNETSTATER0 *PPCNETSTATER0;
507
508
509/**
510 * PCNET state for raw-mode.
511 */
512typedef struct PCNETSTATERC
513{
514 /** Pointer to the connector of the attached network driver. */
515 PPDMINETWORKUPRC pDrv;
516} PCNETSTATERC;
517/** Pointer to a PCnet state structure for raw-mode. */
518typedef PCNETSTATERC *PPCNETSTATERC;
519
520
521/** The PCnet state structure for the current context. */
522typedef CTX_SUFF(PCNETSTATE) PCNETSTATECC;
523/** Pointer to a PCnet state structure for the current context. */
524typedef CTX_SUFF(PPCNETSTATE) PPCNETSTATECC;
525
526
527/** @todo All structs: big endian? */
528
529struct INITBLK16
530{
531 uint16_t mode; /**< copied into csr15 */
532 uint16_t padr1; /**< MAC 0..15 */
533 uint16_t padr2; /**< MAC 16..32 */
534 uint16_t padr3; /**< MAC 33..47 */
535 uint16_t ladrf1; /**< logical address filter 0..15 */
536 uint16_t ladrf2; /**< logical address filter 16..31 */
537 uint16_t ladrf3; /**< logical address filter 32..47 */
538 uint16_t ladrf4; /**< logical address filter 48..63 */
539 uint32_t rdra:24; /**< address of receive descriptor ring */
540 uint32_t res1:5; /**< reserved */
541 uint32_t rlen:3; /**< number of receive descriptor ring entries */
542 uint32_t tdra:24; /**< address of transmit descriptor ring */
543 uint32_t res2:5; /**< reserved */
544 uint32_t tlen:3; /**< number of transmit descriptor ring entries */
545};
546AssertCompileSize(INITBLK16, 24);
547
548/** bird: I've changed the type for the bitfields. They should only be 16-bit all together.
549 * frank: I've changed the bitfiled types to uint32_t to prevent compiler warnings. */
550struct INITBLK32
551{
552 uint16_t mode; /**< copied into csr15 */
553 uint16_t res1:4; /**< reserved */
554 uint16_t rlen:4; /**< number of receive descriptor ring entries */
555 uint16_t res2:4; /**< reserved */
556 uint16_t tlen:4; /**< number of transmit descriptor ring entries */
557 uint16_t padr1; /**< MAC 0..15 */
558 uint16_t padr2; /**< MAC 16..31 */
559 uint16_t padr3; /**< MAC 32..47 */
560 uint16_t res3; /**< reserved */
561 uint16_t ladrf1; /**< logical address filter 0..15 */
562 uint16_t ladrf2; /**< logical address filter 16..31 */
563 uint16_t ladrf3; /**< logical address filter 32..47 */
564 uint16_t ladrf4; /**< logical address filter 48..63 */
565 uint32_t rdra; /**< address of receive descriptor ring */
566 uint32_t tdra; /**< address of transmit descriptor ring */
567};
568AssertCompileSize(INITBLK32, 28);
569
570/** Transmit Message Descriptor */
571typedef struct TMD
572{
573 struct
574 {
575 uint32_t tbadr; /**< transmit buffer address */
576 } tmd0;
577 struct
578 {
579 uint32_t bcnt:12; /**< buffer byte count (two's complement) */
580 uint32_t ones:4; /**< must be 1111b */
581 uint32_t res:7; /**< reserved */
582 uint32_t bpe:1; /**< bus parity error */
583 uint32_t enp:1; /**< end of packet */
584 uint32_t stp:1; /**< start of packet */
585 uint32_t def:1; /**< deferred */
586 uint32_t one:1; /**< exactly one retry was needed to transmit a frame */
587 uint32_t ltint:1; /**< suppress interrupts after successful transmission */
588 uint32_t nofcs:1; /**< when set, the state of DXMTFCS is ignored and
589 transmitter FCS generation is activated. */
590 uint32_t err:1; /**< error occurred */
591 uint32_t own:1; /**< 0=owned by guest driver, 1=owned by controller */
592 } tmd1;
593 struct
594 {
595 uint32_t trc:4; /**< transmit retry count */
596 uint32_t res:12; /**< reserved */
597 uint32_t tdr:10; /**< time domain reflectometry */
598 uint32_t rtry:1; /**< retry error */
599 uint32_t lcar:1; /**< loss of carrier */
600 uint32_t lcol:1; /**< late collision */
601 uint32_t exdef:1; /**< excessive deferral */
602 uint32_t uflo:1; /**< underflow error */
603 uint32_t buff:1; /**< out of buffers (ENP not found) */
604 } tmd2;
605 struct
606 {
607 uint32_t res; /**< reserved for user defined space */
608 } tmd3;
609} TMD;
610AssertCompileSize(TMD, 16);
611
612/** Receive Message Descriptor */
613typedef struct RMD
614{
615 struct
616 {
617 uint32_t rbadr; /**< receive buffer address */
618 } rmd0;
619 struct
620 {
621 uint32_t bcnt:12; /**< buffer byte count (two's complement) */
622 uint32_t ones:4; /**< must be 1111b */
623 uint32_t res:4; /**< reserved */
624 uint32_t bam:1; /**< broadcast address match */
625 uint32_t lafm:1; /**< logical filter address match */
626 uint32_t pam:1; /**< physical address match */
627 uint32_t bpe:1; /**< bus parity error */
628 uint32_t enp:1; /**< end of packet */
629 uint32_t stp:1; /**< start of packet */
630 uint32_t buff:1; /**< buffer error */
631 uint32_t crc:1; /**< crc error on incoming frame */
632 uint32_t oflo:1; /**< overflow error (lost all or part of incoming frame) */
633 uint32_t fram:1; /**< frame error */
634 uint32_t err:1; /**< error occurred */
635 uint32_t own:1; /**< 0=owned by guest driver, 1=owned by controller */
636 } rmd1;
637 struct
638 {
639 uint32_t mcnt:12; /**< message byte count */
640 uint32_t zeros:4; /**< 0000b */
641 uint32_t rpc:8; /**< receive frame tag */
642 uint32_t rcc:8; /**< receive frame tag + reserved */
643 } rmd2;
644 struct
645 {
646 uint32_t res; /**< reserved for user defined space */
647 } rmd3;
648} RMD;
649AssertCompileSize(RMD, 16);
650
651
652#ifndef VBOX_DEVICE_STRUCT_TESTCASE
653
654
655/*********************************************************************************************************************************
656* Internal Functions *
657*********************************************************************************************************************************/
658static void pcnetPollRxTx(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC);
659static void pcnetPollTimer(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC);
660static void pcnetUpdateIrq(PPDMDEVINS pDevIns, PPCNETSTATE pThis);
661static uint32_t pcnetBCRReadU16(PPCNETSTATE pThis, uint32_t u32RAP);
662static VBOXSTRICTRC pcnetBCRWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, uint32_t u32RAP, uint32_t val);
663static void pcnetPollTimerStart(PPDMDEVINS pDevIns, PPCNETSTATE pThis);
664static int pcnetXmitPending(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fOnWorkerThread);
665
666#define PRINT_TMD(T) Log10(( \
667 "TMD0 : TBADR=%#010x\n" \
668 "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, " \
669 "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n" \
670 " BPE=%d, BCNT=%d\n" \
671 "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, " \
672 "LCA=%d, RTR=%d,\n" \
673 " TDR=%d, TRC=%d\n", \
674 (T)->tmd0.tbadr, \
675 (T)->tmd1.own, (T)->tmd1.err, (T)->tmd1.nofcs, \
676 (T)->tmd1.ltint, (T)->tmd1.one, (T)->tmd1.def, \
677 (T)->tmd1.stp, (T)->tmd1.enp, (T)->tmd1.bpe, \
678 4096-(T)->tmd1.bcnt, \
679 (T)->tmd2.buff, (T)->tmd2.uflo, (T)->tmd2.exdef,\
680 (T)->tmd2.lcol, (T)->tmd2.lcar, (T)->tmd2.rtry, \
681 (T)->tmd2.tdr, (T)->tmd2.trc))
682
683#define PRINT_RMD(R) Log9(( \
684 "RMD0 : RBADR=%#010x\n" \
685 "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, " \
686 "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n " \
687 "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
688 "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n", \
689 (R)->rmd0.rbadr, \
690 (R)->rmd1.own, (R)->rmd1.err, (R)->rmd1.fram, \
691 (R)->rmd1.oflo, (R)->rmd1.crc, (R)->rmd1.buff, \
692 (R)->rmd1.stp, (R)->rmd1.enp, (R)->rmd1.bpe, \
693 (R)->rmd1.pam, (R)->rmd1.lafm, (R)->rmd1.bam, \
694 (R)->rmd1.ones, 4096-(R)->rmd1.bcnt, \
695 (R)->rmd2.rcc, (R)->rmd2.rpc, (R)->rmd2.mcnt, \
696 (R)->rmd2.zeros))
697
698
699
700/**
701 * Checks if the link is up.
702 * @returns true if the link is up.
703 * @returns false if the link is down.
704 */
705DECLINLINE(bool) pcnetIsLinkUp(PPCNETSTATE pThis)
706{
707 return pThis->fDriverAttached && !pThis->fLinkTempDown && pThis->fLinkUp;
708}
709
710/**
711 * Memory write helper to handle PCI/ISA differences.
712 *
713 * @param pDevIns The device instance.
714 * @param pThis Pointer to the PCNet device instance
715 * @param GCPhys Guest physical memory address
716 * @param pvBuf Host side buffer address
717 * @param cbWrite Number of bytes to write
718 */
719static void pcnetPhysWrite(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite)
720{
721 if (!PCNET_IS_ISA(pThis))
722 PDMDevHlpPCIPhysWrite(pDevIns, GCPhys, pvBuf, cbWrite);
723 else
724 PDMDevHlpPhysWrite(pDevIns, GCPhys, pvBuf, cbWrite);
725}
726
727
728/**
729 * Memory read helper to handle PCI/ISA differences.
730 *
731 * @param pDevIns The device instance.
732 * @param pThis Pointer to the PCNet device instance.
733 * @param GCPhys Guest physical memory address.
734 * @param pvBuf Host side buffer address.
735 * @param cbRead Number of bytes to read.
736 */
737static void pcnetPhysRead(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS GCPhys, void *pvBuf, size_t cbRead)
738{
739 if (!PCNET_IS_ISA(pThis))
740 PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pvBuf, cbRead);
741 else
742 PDMDevHlpPhysRead(pDevIns, GCPhys, pvBuf, cbRead);
743}
744
745
746/**
747 * Load transmit message descriptor (TMD) if we own it.
748 * Makes sure we read the OWN bit first, which requires issuing two reads if
749 * the OWN bit is laid out in the second (D)WORD in memory.
750 *
751 * @param pDevIns The device instance.
752 * @param pThis adapter private data
753 * @param addr physical address of the descriptor
754 * @return true if we own the descriptor, false otherwise
755 */
756DECLINLINE(bool) pcnetTmdTryLoad(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd, RTGCPHYS32 addr)
757{
758 /* Convert the in-memory format to the internal layout which corresponds to SWSTYLE=2.
759 * Do not touch tmd if the OWN bit is not set (i.e. we don't own the descriptor).
760 */
761 if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
762 {
763 uint16_t xda[4]; /* Corresponds to memory layout, last word unused. */
764
765 /* For SWSTYLE=0, the OWN bit is in the second WORD we need and must be read before the first WORD. */
766 pcnetPhysRead(pDevIns, pThis, addr + sizeof(uint16_t), (void*)&xda[1], 2 * sizeof(uint16_t));
767 if (!(xda[1] & RT_BIT(15)))
768 return false;
769 pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda[0], sizeof(uint16_t));
770 ((uint32_t *)tmd)[0] = (uint32_t)xda[0] | ((uint32_t)(xda[1] & 0x00ff) << 16); /* TMD0, buffer address. */
771 ((uint32_t *)tmd)[1] = (uint32_t)xda[2] | ((uint32_t)(xda[1] & 0xff00) << 16); /* TMD1, buffer size and control bits. */
772 ((uint32_t *)tmd)[2] = 0; /* TMD2, status bits, set on error but not read. */
773 ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, never accessed. */
774 }
775 else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
776 {
777 /* For SWSTYLE=2, the OWN bit is in the second DWORD we need and must be read first. */
778 uint32_t xda[2];
779 pcnetPhysRead(pDevIns, pThis, addr + sizeof(uint32_t), (void*)&xda[1], sizeof(uint32_t));
780 if (!(xda[1] & RT_BIT(31)))
781 return false;
782 pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda[0], sizeof(uint32_t));
783 ((uint32_t *)tmd)[0] = xda[0]; /* TMD0, buffer address. */
784 ((uint32_t *)tmd)[1] = xda[1]; /* TMD1, buffer size and control bits. */
785 ((uint32_t *)tmd)[2] = 0; /* TMD2, status bits, set on error but not read. */
786 ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, never accessed. */
787 }
788 else
789 {
790 /* For SWSTYLE=3, the OWN bit is in the first DWORD we need, therefore a single read suffices. */
791 uint32_t xda[2];
792 pcnetPhysRead(pDevIns, pThis, addr + sizeof(uint32_t), (void*)&xda, sizeof(xda));
793 if (!(xda[0] & RT_BIT(31)))
794 return false;
795 ((uint32_t *)tmd)[0] = xda[1]; /* TMD0, buffer address. */
796 ((uint32_t *)tmd)[1] = xda[0]; /* TMD1, buffer size and control bits. */
797 ((uint32_t *)tmd)[2] = 0; /* TMD2, status bits, set on error but not read. */
798 ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, never accessed. */
799 }
800
801 return !!tmd->tmd1.own;
802}
803
804#if defined(IN_RING3) || defined(LOG_ENABLED)
805
806/**
807 * Loads an entire transmit message descriptor. Used for logging/debugging.
808 *
809 * @param pDevIns The device instance.
810 * @param pThis Adapter private data
811 * @param addr Physical address of the descriptor
812 */
813DECLINLINE(void) pcnetTmdLoadAll(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd, RTGCPHYS32 addr)
814{
815 /* Convert the in-memory format to the internal layout which corresponds to SWSTYLE=2. */
816 if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
817 {
818 uint16_t xda[4];
819
820 /* For SWSTYLE=0, we have to do a bit of work. */
821 pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda, sizeof(xda));
822 ((uint32_t *)tmd)[0] = (uint32_t)xda[0] | ((uint32_t)(xda[1] & 0x00ff) << 16); /* TMD0, buffer address. */
823 ((uint32_t *)tmd)[1] = (uint32_t)xda[2] | ((uint32_t)(xda[1] & 0xff00) << 16); /* TMD1, buffer size and control bits. */
824 ((uint32_t *)tmd)[2] = (uint32_t)xda[3] << 16; /* TMD2, status bits. */
825 ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, not present for SWSTYLE=1. */
826 }
827 else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
828 {
829 /* For SWSTYLE=2, simply read the TMD as is. */
830 pcnetPhysRead(pDevIns, pThis, addr, (void*)tmd, sizeof(*tmd));
831 }
832 else
833 {
834 /* For SWSTYLE=3, swap the first and third DWORD around. */
835 uint32_t xda[4];
836 pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda, sizeof(xda));
837 ((uint32_t *)tmd)[0] = xda[2]; /* TMD0, buffer address. */
838 ((uint32_t *)tmd)[1] = xda[1]; /* TMD1, buffer size and control bits. */
839 ((uint32_t *)tmd)[2] = xda[0]; /* TMD2, status bits. */
840 ((uint32_t *)tmd)[3] = xda[3]; /* TMD3, user data. */
841 }
842}
843
844#endif
845
846/**
847 * Store transmit message descriptor and hand it over to the host (the VM guest).
848 * Make sure the cleared OWN bit gets written last.
849 */
850DECLINLINE(void) pcnetTmdStorePassHost(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd, RTGCPHYS32 addr)
851{
852 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTmdStore), a);
853 if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
854 {
855 uint16_t xda[4]; /* Corresponds to memory layout, only two words used. */
856
857 /* For SWSTYLE=0, write the status word first, then the word containing the OWN bit. */
858 xda[1] = ((((uint32_t *)tmd)[0] >> 16) & 0xff) | ((((uint32_t *)tmd)[1]>>16) & 0xff00);
859 xda[1] &= ~RT_BIT(15);
860 xda[3] = ((uint32_t *)tmd)[2] >> 16;
861 /** @todo The WORD containing status bits may not need to be written unless the ERR bit is set. */
862 pcnetPhysWrite(pDevIns, pThis, addr + 3 * sizeof(uint16_t), (void*)&xda[3], sizeof(uint16_t));
863 pcnetPhysWrite(pDevIns, pThis, addr + 1 * sizeof(uint16_t), (void*)&xda[1], sizeof(uint16_t));
864 }
865 else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
866 {
867 /* For SWSTYLE=0, write TMD2 first, then TMD1. */
868 /** @todo The DWORD containing status bits may not need to be written unless the ERR bit is set. */
869 pcnetPhysWrite(pDevIns, pThis, addr + 2 * sizeof(uint32_t), (uint32_t*)tmd + 2, sizeof(uint32_t));
870 ((uint32_t*)tmd)[1] &= ~RT_BIT(31);
871 pcnetPhysWrite(pDevIns, pThis, addr + 1 * sizeof(uint32_t), (uint32_t*)tmd + 1, sizeof(uint32_t));
872 }
873 else
874 {
875 uint32_t xda[2];
876
877 /* For SWSTYLE=3, two DWORDs can be written in one go because the OWN bit is last. */
878 xda[0] = ((uint32_t *)tmd)[2];
879 xda[1] = ((uint32_t *)tmd)[1];
880 xda[1] &= ~RT_BIT(31);
881 pcnetPhysWrite(pDevIns, pThis, addr, (void*)&xda, sizeof(xda));
882 }
883 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTmdStore), a);
884}
885
886/**
887 * Load receive message descriptor
888 *
889 * Make sure we read the own flag first.
890 *
891 * @param pDevIns The device instance.
892 * @param pThis Adapter private data.
893 * @param addr Physical address of the descriptor.
894 * @param fRetIfNotOwn Return immediately after reading the own flag if we
895 * don't own the descriptor.
896 * @returns true if we own the descriptor, false otherwise
897 */
898DECLINLINE(bool) pcnetRmdLoad(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RMD *rmd, RTGCPHYS32 addr, bool fRetIfNotOwn)
899{
900 uint8_t ownbyte;
901
902 if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
903 {
904 uint16_t rda[4];
905 pcnetPhysRead(pDevIns, pThis, addr+3, &ownbyte, 1);
906 if (!(ownbyte & 0x80) && fRetIfNotOwn)
907 return false;
908 pcnetPhysRead(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
909 ((uint32_t *)rmd)[0] = (uint32_t)rda[0] | ((rda[1] & 0x00ff) << 16);
910 ((uint32_t *)rmd)[1] = (uint32_t)rda[2] | ((rda[1] & 0xff00) << 16);
911 ((uint32_t *)rmd)[2] = (uint32_t)rda[3];
912 ((uint32_t *)rmd)[3] = 0;
913 }
914 else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
915 {
916 pcnetPhysRead(pDevIns, pThis, addr+7, &ownbyte, 1);
917 if (!(ownbyte & 0x80) && fRetIfNotOwn)
918 return false;
919 pcnetPhysRead(pDevIns, pThis, addr, (void*)rmd, 16);
920 }
921 else
922 {
923 uint32_t rda[4];
924 pcnetPhysRead(pDevIns, pThis, addr+7, &ownbyte, 1);
925 if (!(ownbyte & 0x80) && fRetIfNotOwn)
926 return false;
927 pcnetPhysRead(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
928 ((uint32_t *)rmd)[0] = rda[2];
929 ((uint32_t *)rmd)[1] = rda[1];
930 ((uint32_t *)rmd)[2] = rda[0];
931 ((uint32_t *)rmd)[3] = rda[3];
932 }
933 /* Double check the own bit; guest drivers might be buggy and lock prefixes in the recompiler are ignored by other threads. */
934#ifdef DEBUG
935 if (rmd->rmd1.own == 1 && !(ownbyte & 0x80))
936 Log(("pcnetRmdLoad: own bit flipped while reading!!\n"));
937#endif
938 if (!(ownbyte & 0x80))
939 rmd->rmd1.own = 0;
940
941 return !!rmd->rmd1.own;
942}
943
944
945/**
946 * Store receive message descriptor and hand it over to the host (the VM guest).
947 * Make sure that all data are transmitted before we clear the own flag.
948 */
949DECLINLINE(void) pcnetRmdStorePassHost(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RMD *rmd, RTGCPHYS32 addr)
950{
951 if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
952 {
953 uint16_t rda[4];
954 rda[0] = ((uint32_t *)rmd)[0] & 0xffff;
955 rda[1] = ((((uint32_t *)rmd)[0]>>16) & 0xff) | ((((uint32_t *)rmd)[1]>>16) & 0xff00);
956 rda[2] = ((uint32_t *)rmd)[1] & 0xffff;
957 rda[3] = ((uint32_t *)rmd)[2] & 0xffff;
958 rda[1] |= 0x8000;
959 pcnetPhysWrite(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
960 rda[1] &= ~0x8000;
961 pcnetPhysWrite(pDevIns, pThis, addr+3, (uint8_t*)rda + 3, 1);
962 }
963 else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
964 {
965 ((uint32_t*)rmd)[1] |= 0x80000000;
966 pcnetPhysWrite(pDevIns, pThis, addr, (void*)rmd, 12);
967 ((uint32_t*)rmd)[1] &= ~0x80000000;
968 pcnetPhysWrite(pDevIns, pThis, addr+7, (uint8_t*)rmd + 7, 1);
969 }
970 else
971 {
972 uint32_t rda[3];
973 rda[0] = ((uint32_t *)rmd)[2];
974 rda[1] = ((uint32_t *)rmd)[1];
975 rda[2] = ((uint32_t *)rmd)[0];
976 rda[1] |= 0x80000000;
977 pcnetPhysWrite(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
978 rda[1] &= ~0x80000000;
979 pcnetPhysWrite(pDevIns, pThis, addr+7, (uint8_t*)rda + 7, 1);
980 }
981}
982
983#ifdef IN_RING3
984/**
985 * Read+Write a TX/RX descriptor to prevent PDMDevHlpPCIPhysWrite() allocating
986 * pages later when we shouldn't schedule to EMT. Temporarily hack.
987 */
988static void pcnetDescTouch(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS32 addr)
989{
990 uint8_t aBuf[16];
991 size_t cbDesc;
992 if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
993 cbDesc = 8;
994 else
995 cbDesc = 16;
996 pcnetPhysRead(pDevIns, pThis, addr, aBuf, cbDesc);
997 pcnetPhysWrite(pDevIns, pThis, addr, aBuf, cbDesc);
998}
999#endif /* IN_RING3 */
1000
1001/** Checks if it's a bad (as in invalid) RMD.*/
1002#define IS_RMD_BAD(rmd) ((rmd).rmd1.ones != 15)
1003
1004/** The network card is the owner of the RDTE/TDTE, actually it is this driver */
1005#define CARD_IS_OWNER(desc) (((desc) & 0x8000))
1006
1007/** The host is the owner of the RDTE/TDTE -- actually the VM guest. */
1008#define HOST_IS_OWNER(desc) (!((desc) & 0x8000))
1009
1010#ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
1011#define ETHER_IS_MULTICAST(a) ((*(uint8_t *)(a)) & 1)
1012#endif
1013
1014#define ETHER_ADDR_LEN ETH_ALEN
1015#define ETH_ALEN 6
1016#pragma pack(1)
1017struct ether_header /** @todo Use RTNETETHERHDR */
1018{
1019 uint8_t ether_dhost[ETH_ALEN]; /**< destination ethernet address */
1020 uint8_t ether_shost[ETH_ALEN]; /**< source ethernet address */
1021 uint16_t ether_type; /**< packet type ID field */
1022};
1023#pragma pack()
1024
1025#define PRINT_PKTHDR(BUF) do { \
1026 struct ether_header *hdr = (struct ether_header *)(BUF); \
1027 Log12(("#%d packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, shost=%02x:%02x:%02x:%02x:%02x:%02x, type=%#06x (bcast=%d)\n", \
1028 PCNET_INST_NR, \
1029 hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \
1030 hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \
1031 hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \
1032 hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \
1033 htons(hdr->ether_type), \
1034 !!ETHER_IS_MULTICAST(hdr->ether_dhost))); \
1035} while (0)
1036
1037
1038#define MULTICAST_FILTER_LEN 8
1039
1040DECLINLINE(uint32_t) lnc_mchash(const uint8_t *ether_addr)
1041{
1042#define LNC_POLYNOMIAL 0xEDB88320UL
1043 uint32_t crc = 0xFFFFFFFF;
1044 int idx, bit;
1045 uint8_t data;
1046
1047 for (idx = 0; idx < ETHER_ADDR_LEN; idx++)
1048 {
1049 for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++)
1050 {
1051 crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0);
1052 data >>= 1;
1053 }
1054 }
1055 return crc;
1056#undef LNC_POLYNOMIAL
1057}
1058
1059#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
1060
1061/* generated using the AUTODIN II polynomial
1062 * x^32 + x^26 + x^23 + x^22 + x^16 +
1063 * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
1064 */
1065static const uint32_t crctab[256] =
1066{
1067 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
1068 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
1069 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
1070 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
1071 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
1072 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
1073 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
1074 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
1075 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
1076 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
1077 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
1078 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
1079 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
1080 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
1081 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
1082 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
1083 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
1084 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
1085 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
1086 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
1087 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
1088 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
1089 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
1090 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
1091 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
1092 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
1093 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
1094 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
1095 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
1096 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
1097 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
1098 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
1099 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
1100 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
1101 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
1102 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
1103 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
1104 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
1105 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
1106 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
1107 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
1108 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
1109 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
1110 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
1111 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
1112 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
1113 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
1114 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
1115 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
1116 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
1117 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
1118 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
1119 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
1120 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
1121 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
1122 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
1123 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
1124 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
1125 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
1126 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
1127 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
1128 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
1129 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
1130 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
1131};
1132
1133DECLINLINE(int) padr_match(PPCNETSTATE pThis, const uint8_t *buf, size_t size)
1134{
1135 struct ether_header *hdr = (struct ether_header *)buf;
1136 int result;
1137#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
1138 result = !CSR_DRCVPA(pThis) && !memcmp(hdr->ether_dhost, pThis->aCSR + 12, 6);
1139#else
1140 uint8_t padr[6];
1141 padr[0] = pThis->aCSR[12] & 0xff;
1142 padr[1] = pThis->aCSR[12] >> 8;
1143 padr[2] = pThis->aCSR[13] & 0xff;
1144 padr[3] = pThis->aCSR[13] >> 8;
1145 padr[4] = pThis->aCSR[14] & 0xff;
1146 padr[5] = pThis->aCSR[14] >> 8;
1147 result = !CSR_DRCVPA(pThis) && !memcmp(hdr->ether_dhost, padr, 6);
1148#endif
1149
1150 Log11(("#%d packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, padr=%02x:%02x:%02x:%02x:%02x:%02x => %d\n", PCNET_INST_NR,
1151 hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2],
1152 hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5],
1153 pThis->aCSR[12] & 0xff, pThis->aCSR[12] >> 8,
1154 pThis->aCSR[13] & 0xff, pThis->aCSR[13] >> 8,
1155 pThis->aCSR[14] & 0xff, pThis->aCSR[14] >> 8, result));
1156 RT_NOREF_PV(size);
1157 return result;
1158}
1159
1160DECLINLINE(int) padr_bcast(PPCNETSTATE pThis, const uint8_t *buf, size_t size)
1161{
1162 static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1163 struct ether_header *hdr = (struct ether_header *)buf;
1164 int result = !CSR_DRCVBC(pThis) && !memcmp(hdr->ether_dhost, aBCAST, 6);
1165 Log11(("#%d padr_bcast result=%d\n", PCNET_INST_NR, result));
1166 RT_NOREF_PV(size);
1167 return result;
1168}
1169
1170static int ladr_match(PPCNETSTATE pThis, const uint8_t *buf, size_t size)
1171{
1172 struct ether_header *hdr = (struct ether_header *)buf;
1173 if (RT_UNLIKELY(hdr->ether_dhost[0] & 0x01) && ((uint64_t *)&pThis->aCSR[8])[0] != 0LL)
1174 {
1175 int index;
1176#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
1177 index = lnc_mchash(hdr->ether_dhost) >> 26;
1178 return ((uint8_t*)(pThis->aCSR + 8))[index >> 3] & (1 << (index & 7));
1179#else
1180 uint8_t ladr[8];
1181 ladr[0] = pThis->aCSR[8] & 0xff;
1182 ladr[1] = pThis->aCSR[8] >> 8;
1183 ladr[2] = pThis->aCSR[9] & 0xff;
1184 ladr[3] = pThis->aCSR[9] >> 8;
1185 ladr[4] = pThis->aCSR[10] & 0xff;
1186 ladr[5] = pThis->aCSR[10] >> 8;
1187 ladr[6] = pThis->aCSR[11] & 0xff;
1188 ladr[7] = pThis->aCSR[11] >> 8;
1189 index = lnc_mchash(hdr->ether_dhost) >> 26;
1190 return (ladr[index >> 3] & (1 << (index & 7)));
1191#endif
1192 }
1193 RT_NOREF_PV(size);
1194 return 0;
1195}
1196
1197
1198/**
1199 * Get the receive descriptor ring address with a given index.
1200 */
1201DECLINLINE(RTGCPHYS32) pcnetRdraAddr(PPCNETSTATE pThis, int idx)
1202{
1203 return pThis->GCRDRA + ((CSR_RCVRL(pThis) - idx) << pThis->iLog2DescSize);
1204}
1205
1206/**
1207 * Get the transmit descriptor ring address with a given index.
1208 */
1209DECLINLINE(RTGCPHYS32) pcnetTdraAddr(PPCNETSTATE pThis, int idx)
1210{
1211 return pThis->GCTDRA + ((CSR_XMTRL(pThis) - idx) << pThis->iLog2DescSize);
1212}
1213
1214
1215#undef htonl
1216#define htonl(x) ASMByteSwapU32(x)
1217#undef htons
1218#define htons(x) ( (((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8) )
1219
1220
1221static void pcnetSoftReset(PPCNETSTATE pThis)
1222{
1223 Log(("#%d pcnetSoftReset:\n", PCNET_INST_NR));
1224
1225 pThis->u32Lnkst = 0x40;
1226 pThis->GCRDRA = 0;
1227 pThis->GCTDRA = 0;
1228 pThis->u32RAP = 0;
1229
1230 pThis->aCSR[0] = 0x0004;
1231 pThis->aCSR[3] = 0x0000;
1232 pThis->aCSR[4] = 0x0115;
1233 pThis->aCSR[5] = 0x0000;
1234 pThis->aCSR[6] = 0x0000;
1235 pThis->aCSR[8] = 0;
1236 pThis->aCSR[9] = 0;
1237 pThis->aCSR[10] = 0;
1238 pThis->aCSR[11] = 0;
1239 pThis->aCSR[12] = RT_LE2H_U16(((uint16_t *)&pThis->aPROM[0])[0]);
1240 pThis->aCSR[13] = RT_LE2H_U16(((uint16_t *)&pThis->aPROM[0])[1]);
1241 pThis->aCSR[14] = RT_LE2H_U16(((uint16_t *)&pThis->aPROM[0])[2]);
1242 pThis->aCSR[15] &= 0x21c4;
1243 CSR_RCVRC(pThis) = 1;
1244 CSR_XMTRC(pThis) = 1;
1245 CSR_RCVRL(pThis) = 1;
1246 CSR_XMTRL(pThis) = 1;
1247 pThis->aCSR[80] = 0x1410;
1248 switch (pThis->uDevType)
1249 {
1250 default:
1251 case DEV_AM79C970A:
1252 pThis->aCSR[88] = CSR_VERSION_LOW_79C970A;
1253 pThis->aCSR[89] = CSR_VERSION_HIGH;
1254 break;
1255 case DEV_AM79C973:
1256 pThis->aCSR[88] = CSR_VERSION_LOW_79C973;
1257 pThis->aCSR[89] = CSR_VERSION_HIGH;
1258 break;
1259 case DEV_AM79C960:
1260 case DEV_AM79C960_EB:
1261 pThis->aCSR[88] = CSR_VERSION_LOW_79C960;
1262 pThis->aCSR[89] = 0x0000;
1263 break;
1264 }
1265 pThis->aCSR[94] = 0x0000;
1266 pThis->aCSR[100] = 0x0200;
1267 pThis->aCSR[103] = 0x0105;
1268 CSR_MISSC(pThis) = 0;
1269 pThis->aCSR[114] = 0x0000;
1270 pThis->aCSR[122] = 0x0000;
1271 pThis->aCSR[124] = 0x0000;
1272}
1273
1274/**
1275 * Check if we have to send an interrupt to the guest. An interrupt can occur on
1276 * - csr0 (written quite often)
1277 * - csr4 (only written by pcnetSoftReset(), pcnetStop() or by the guest driver)
1278 * - csr5 (only written by pcnetSoftReset(), pcnetStop or by the driver guest)
1279 */
1280static void pcnetUpdateIrq(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
1281{
1282 int iISR = 0;
1283 uint16_t csr0 = pThis->aCSR[0];
1284
1285 csr0 &= ~0x0080; /* clear INTR */
1286
1287 STAM_PROFILE_ADV_START(&pThis->StatInterrupt, a);
1288
1289 /* Linux guests set csr4=0x0915
1290 * W2k guests set csr3=0x4940 (disable BABL, MERR, IDON, DXSUFLO */
1291
1292#if 1
1293 if ( ( (csr0 & ~pThis->aCSR[3]) & 0x5f00)
1294 || (((pThis->aCSR[4]>>1) & ~pThis->aCSR[4]) & 0x0115)
1295 || (((pThis->aCSR[5]>>1) & pThis->aCSR[5]) & 0x0048))
1296#else
1297 if ( ( !(pThis->aCSR[3] & 0x4000) && !!(csr0 & 0x4000)) /* BABL */
1298 ||( !(pThis->aCSR[3] & 0x1000) && !!(csr0 & 0x1000)) /* MISS */
1299 ||( !(pThis->aCSR[3] & 0x0100) && !!(csr0 & 0x0100)) /* IDON */
1300 ||( !(pThis->aCSR[3] & 0x0200) && !!(csr0 & 0x0200)) /* TINT */
1301 ||( !(pThis->aCSR[3] & 0x0400) && !!(csr0 & 0x0400)) /* RINT */
1302 ||( !(pThis->aCSR[3] & 0x0800) && !!(csr0 & 0x0800)) /* MERR */
1303 ||( !(pThis->aCSR[4] & 0x0001) && !!(pThis->aCSR[4] & 0x0002)) /* JAB */
1304 ||( !(pThis->aCSR[4] & 0x0004) && !!(pThis->aCSR[4] & 0x0008)) /* TXSTRT */
1305 ||( !(pThis->aCSR[4] & 0x0010) && !!(pThis->aCSR[4] & 0x0020)) /* RCVO */
1306 ||( !(pThis->aCSR[4] & 0x0100) && !!(pThis->aCSR[4] & 0x0200)) /* MFCO */
1307 ||(!!(pThis->aCSR[5] & 0x0040) && !!(pThis->aCSR[5] & 0x0080)) /* EXDINT */
1308 ||(!!(pThis->aCSR[5] & 0x0008) && !!(pThis->aCSR[5] & 0x0010)) /* MPINT */)
1309#endif
1310 {
1311 iISR = !!(csr0 & 0x0040); /* CSR_INEA */
1312 csr0 |= 0x0080; /* set INTR */
1313 }
1314
1315#ifdef VBOX
1316 if (pThis->aCSR[4] & 0x0080) /* UINTCMD */
1317 {
1318 pThis->aCSR[4] &= ~0x0080; /* clear UINTCMD */
1319 pThis->aCSR[4] |= 0x0040; /* set UINT */
1320 Log(("#%d user int\n", PCNET_INST_NR));
1321 }
1322 if (pThis->aCSR[4] & csr0 & 0x0040 /* CSR_INEA */)
1323 {
1324 csr0 |= 0x0080; /* set INTR */
1325 iISR = 1;
1326 }
1327#else /* !VBOX */
1328 if (!!(pThis->aCSR[4] & 0x0080) && CSR_INEA(pThis)) /* UINTCMD */
1329 {
1330 pThis->aCSR[4] &= ~0x0080;
1331 pThis->aCSR[4] |= 0x0040; /* set UINT */
1332 csr0 |= 0x0080; /* set INTR */
1333 iISR = 1;
1334 Log(("#%d user int\n", PCNET_INST_NR));
1335 }
1336#endif /* !VBOX */
1337
1338#if 1
1339 if (((pThis->aCSR[5]>>1) & pThis->aCSR[5]) & 0x0500)
1340#else
1341 if ( (!!(pThis->aCSR[5] & 0x0400) && !!(pThis->aCSR[5] & 0x0800)) /* SINT */
1342 ||(!!(pThis->aCSR[5] & 0x0100) && !!(pThis->aCSR[5] & 0x0200)) /* SLPINT */)
1343#endif
1344 {
1345 iISR = 1;
1346 csr0 |= 0x0080; /* INTR */
1347 }
1348
1349 if ((pThis->aCSR[7] & 0x0C00) == 0x0C00) /* STINT + STINTE */
1350 iISR = 1;
1351
1352 pThis->aCSR[0] = csr0;
1353
1354 Log2(("#%d set irq iISR=%d\n", PCNET_INST_NR, iISR));
1355
1356 /* normal path is to _not_ change the IRQ status */
1357 if (RT_UNLIKELY(iISR != pThis->iISR))
1358 {
1359 if (!PCNET_IS_ISA(pThis))
1360 {
1361 Log(("#%d INTA=%d\n", PCNET_INST_NR, iISR));
1362 PDMDevHlpPCISetIrq(pDevIns, 0, iISR);
1363 }
1364 else
1365 {
1366 Log(("#%d IRQ=%d, state=%d\n", PCNET_INST_NR, pThis->uIsaIrq, iISR));
1367 PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, iISR);
1368 }
1369 pThis->iISR = iISR;
1370 }
1371 STAM_PROFILE_ADV_STOP(&pThis->StatInterrupt, a);
1372}
1373
1374#ifdef IN_RING3
1375
1376static void pcnetR3Init(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
1377{
1378 Log(("#%d pcnetR3Init: init_addr=%#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_IADR(pThis))));
1379
1380 /* If intialization was invoked with PCI bus mastering disabled, it's not going to
1381 * go very well. Better report an error.
1382 */
1383 if (PCNET_IS_PCI(pThis))
1384 {
1385 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
1386 uint8_t uCmd = PDMPciDevGetByte(pPciDev, 0x04);
1387
1388 if (!(uCmd & 4))
1389 {
1390 pThis->aCSR[0] |= 0x0801; /* Set the MERR bit instead of IDON. */
1391 LogRel(("PCnet#%d: Warning: Initialization failed due to disabled PCI bus mastering.\n", PCNET_INST_NR));
1392 return;
1393 }
1394 }
1395
1396 /** @todo Documentation says that RCVRL and XMTRL are stored as two's complement!
1397 * Software is allowed to write these registers directly. */
1398# define PCNET_INIT() do { \
1399 pcnetPhysRead(pDevIns, pThis, PHYSADDR(pThis, CSR_IADR(pThis)), \
1400 (uint8_t *)&initblk, sizeof(initblk)); \
1401 pThis->aCSR[15] = RT_LE2H_U16(initblk.mode); \
1402 CSR_RCVRL(pThis) = (initblk.rlen < 9) ? (1 << initblk.rlen) : 512; \
1403 CSR_XMTRL(pThis) = (initblk.tlen < 9) ? (1 << initblk.tlen) : 512; \
1404 pThis->aCSR[ 6] = (initblk.tlen << 12) | (initblk.rlen << 8); \
1405 pThis->aCSR[ 8] = RT_LE2H_U16(initblk.ladrf1); \
1406 pThis->aCSR[ 9] = RT_LE2H_U16(initblk.ladrf2); \
1407 pThis->aCSR[10] = RT_LE2H_U16(initblk.ladrf3); \
1408 pThis->aCSR[11] = RT_LE2H_U16(initblk.ladrf4); \
1409 pThis->aCSR[12] = RT_LE2H_U16(initblk.padr1); \
1410 pThis->aCSR[13] = RT_LE2H_U16(initblk.padr2); \
1411 pThis->aCSR[14] = RT_LE2H_U16(initblk.padr3); \
1412 pThis->GCRDRA = PHYSADDR(pThis, initblk.rdra); \
1413 pThis->GCTDRA = PHYSADDR(pThis, initblk.tdra); \
1414 } while (0)
1415
1416 if (BCR_SSIZE32(pThis))
1417 {
1418 struct INITBLK32 initblk;
1419 pThis->GCUpperPhys = 0;
1420 PCNET_INIT();
1421 Log(("#%d initblk.rlen=%#04x, initblk.tlen=%#04x\n",
1422 PCNET_INST_NR, initblk.rlen, initblk.tlen));
1423 }
1424 else
1425 {
1426 struct INITBLK16 initblk;
1427 pThis->GCUpperPhys = (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
1428 PCNET_INIT();
1429 Log(("#%d initblk.rlen=%#04x, initblk.tlen=%#04x\n",
1430 PCNET_INST_NR, initblk.rlen, initblk.tlen));
1431 }
1432
1433# undef PCNET_INIT
1434
1435 size_t cbRxBuffers = 0;
1436 for (int i = CSR_RCVRL(pThis); i >= 1; i--)
1437 {
1438 RMD rmd;
1439 RTGCPHYS32 rdaddr = PHYSADDR(pThis, pcnetRdraAddr(pThis, i));
1440
1441 pcnetDescTouch(pDevIns, pThis, rdaddr);
1442 /* At this time it is not guaranteed that the buffers are already initialized. */
1443 if (pcnetRmdLoad(pDevIns, pThis, &rmd, rdaddr, false))
1444 {
1445 uint32_t cbBuf = 4096U-rmd.rmd1.bcnt;
1446 cbRxBuffers += cbBuf;
1447 }
1448 }
1449
1450 for (int i = CSR_XMTRL(pThis); i >= 1; i--)
1451 {
1452 RTGCPHYS32 tdaddr = PHYSADDR(pThis, pcnetTdraAddr(pThis, i));
1453
1454 pcnetDescTouch(pDevIns, pThis, tdaddr);
1455 }
1456
1457 /*
1458 * Heuristics: The Solaris pcn driver allocates too few RX buffers (128 buffers of a
1459 * size of 128 bytes are 16KB in summary) leading to frequent RX buffer overflows. In
1460 * that case we don't signal RX overflows through the CSR0_MISS flag as the driver
1461 * re-initializes the device on every miss. Other guests use at least 32 buffers of
1462 * usually 1536 bytes and should therefore not run into condition. If they are still
1463 * short in RX buffers we notify this condition.
1464 */
1465 pThis->fSignalRxMiss = (cbRxBuffers == 0 || cbRxBuffers >= 32*_1K);
1466
1467 if (pThisCC->pDrv)
1468 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, CSR_PROM(pThis));
1469
1470 CSR_RCVRC(pThis) = CSR_RCVRL(pThis);
1471 CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
1472
1473 /* Reset cached RX and TX states */
1474 CSR_CRST(pThis) = CSR_CRBC(pThis) = CSR_NRST(pThis) = CSR_NRBC(pThis) = 0;
1475 CSR_CXST(pThis) = CSR_CXBC(pThis) = CSR_NXST(pThis) = CSR_NXBC(pThis) = 0;
1476
1477 LogRel(("PCnet#%d: Init: SWSTYLE=%d GCRDRA=%#010x[%d] GCTDRA=%#010x[%d]%s\n",
1478 PCNET_INST_NR, BCR_SWSTYLE(pThis),
1479 pThis->GCRDRA, CSR_RCVRL(pThis), pThis->GCTDRA, CSR_XMTRL(pThis),
1480 !pThis->fSignalRxMiss ? " (CSR0_MISS disabled)" : ""));
1481
1482 if (pThis->GCRDRA & (pThis->iLog2DescSize - 1))
1483 LogRel(("PCnet#%d: Warning: Misaligned RDRA\n", PCNET_INST_NR));
1484 if (pThis->GCTDRA & (pThis->iLog2DescSize - 1))
1485 LogRel(("PCnet#%d: Warning: Misaligned TDRA\n", PCNET_INST_NR));
1486
1487 pThis->aCSR[0] |= 0x0101; /* Initialization done */
1488 pThis->aCSR[0] &= ~0x0004; /* clear STOP bit */
1489}
1490
1491#endif /* IN_RING3 */
1492
1493/**
1494 * Start RX/TX operation.
1495 */
1496static void pcnetStart(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
1497{
1498 Log(("#%d pcnetStart:\n", PCNET_INST_NR));
1499
1500 /* Reset any cached RX/TX descriptor state. */
1501 CSR_CRDA(pThis) = CSR_CRBA(pThis) = CSR_NRDA(pThis) = CSR_NRBA(pThis) = 0;
1502 CSR_CRBC(pThis) = CSR_NRBC(pThis) = CSR_CRST(pThis) = 0;
1503
1504 if (!CSR_DTX(pThis))
1505 pThis->aCSR[0] |= 0x0010; /* set TXON */
1506 if (!CSR_DRX(pThis))
1507 pThis->aCSR[0] |= 0x0020; /* set RXON */
1508 pThis->aCSR[0] &= ~0x0004; /* clear STOP bit */
1509 pThis->aCSR[0] |= 0x0002; /* STRT */
1510
1511 pcnetPollTimerStart(pDevIns, pThis); /* start timer if it was stopped */
1512}
1513
1514/**
1515 * Stop RX/TX operation.
1516 */
1517static void pcnetStop(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
1518{
1519 Log(("#%d pcnetStop:\n", PCNET_INST_NR));
1520 pThis->aCSR[0] = 0x0004;
1521 pThis->aCSR[4] &= ~0x02c2;
1522 pThis->aCSR[5] &= ~0x0011;
1523 pcnetPollTimer(pDevIns, pThis, pThisCC);
1524}
1525
1526/**
1527 * Wakes up a receive thread stuck waiting for buffers.
1528 */
1529static void pcnetWakeupReceive(PPDMDEVINS pDevIns)
1530{
1531 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
1532 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
1533 if (pThis->hEventOutOfRxSpace != NIL_SUPSEMEVENT)
1534 {
1535 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventOutOfRxSpace);
1536 AssertRC(rc);
1537 }
1538}
1539
1540/**
1541 * Poll Receive Descriptor Table Entry and cache the results in the appropriate registers.
1542 *
1543 * @note Once a descriptor belongs to the network card (this driver), it
1544 * cannot be changed by the host (the guest driver) anymore. Well, it
1545 * could but the results are undefined by definition.
1546 *
1547 * @param pDevIns The device instance.
1548 * @param pThis The shared PCnet state data.
1549 * @param fSkipCurrent if true, don't scan the current RDTE.
1550 */
1551static void pcnetRdtePoll(PPDMDEVINS pDevIns, PPCNETSTATE pThis, bool fSkipCurrent=false)
1552{
1553 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
1554 /* assume lack of a next receive descriptor */
1555 CSR_NRST(pThis) = 0;
1556
1557 if (RT_LIKELY(pThis->GCRDRA))
1558 {
1559 /*
1560 * The current receive message descriptor.
1561 */
1562 RMD rmd;
1563 int i = CSR_RCVRC(pThis);
1564 RTGCPHYS32 addr;
1565
1566 if (i < 1)
1567 i = CSR_RCVRL(pThis);
1568
1569 if (!fSkipCurrent)
1570 {
1571 addr = pcnetRdraAddr(pThis, i);
1572 CSR_CRDA(pThis) = CSR_CRBA(pThis) = 0;
1573 CSR_CRBC(pThis) = CSR_CRST(pThis) = 0;
1574 if (!pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, addr), true))
1575 {
1576 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
1577 return;
1578 }
1579 if (RT_LIKELY(!IS_RMD_BAD(rmd)))
1580 {
1581 CSR_CRDA(pThis) = addr; /* Receive Descriptor Address */
1582 CSR_CRBA(pThis) = rmd.rmd0.rbadr; /* Receive Buffer Address */
1583 CSR_CRBC(pThis) = rmd.rmd1.bcnt; /* Receive Byte Count */
1584 CSR_CRST(pThis) = ((uint32_t *)&rmd)[1] >> 16; /* Receive Status */
1585 if (pThis->fMaybeOutOfSpace)
1586 pcnetWakeupReceive(pDevIns);
1587 }
1588 else
1589 {
1590 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
1591 /* This is not problematic since we don't own the descriptor
1592 * We actually do own it, otherwise pcnetRmdLoad would have returned false.
1593 * Don't flood the release log with errors.
1594 */
1595 if (++pThis->uCntBadRMD < 50)
1596 LogRel(("PCnet#%d: BAD RMD ENTRIES AT %#010x (i=%d)\n",
1597 PCNET_INST_NR, addr, i));
1598 return;
1599 }
1600 }
1601
1602 /*
1603 * The next descriptor.
1604 */
1605 if (--i < 1)
1606 i = CSR_RCVRL(pThis);
1607 addr = pcnetRdraAddr(pThis, i);
1608 CSR_NRDA(pThis) = CSR_NRBA(pThis) = 0;
1609 CSR_NRBC(pThis) = 0;
1610 if (!pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, addr), true))
1611 {
1612 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
1613 return;
1614 }
1615 if (RT_LIKELY(!IS_RMD_BAD(rmd)))
1616 {
1617 CSR_NRDA(pThis) = addr; /* Receive Descriptor Address */
1618 CSR_NRBA(pThis) = rmd.rmd0.rbadr; /* Receive Buffer Address */
1619 CSR_NRBC(pThis) = rmd.rmd1.bcnt; /* Receive Byte Count */
1620 CSR_NRST(pThis) = ((uint32_t *)&rmd)[1] >> 16; /* Receive Status */
1621 }
1622 else
1623 {
1624 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
1625 /* This is not problematic since we don't own the descriptor
1626 * We actually do own it, otherwise pcnetRmdLoad would have returned false.
1627 * Don't flood the release log with errors.
1628 */
1629 if (++pThis->uCntBadRMD < 50)
1630 LogRel(("PCnet#%d: BAD RMD ENTRIES + AT %#010x (i=%d)\n",
1631 PCNET_INST_NR, addr, i));
1632 return;
1633 }
1634
1635 /**
1636 * @todo NNRD
1637 */
1638 }
1639 else
1640 {
1641 CSR_CRDA(pThis) = CSR_CRBA(pThis) = CSR_NRDA(pThis) = CSR_NRBA(pThis) = 0;
1642 CSR_CRBC(pThis) = CSR_NRBC(pThis) = CSR_CRST(pThis) = 0;
1643 }
1644 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
1645}
1646
1647/**
1648 * Poll Transmit Descriptor Table Entry
1649 * @return true if transmit descriptors available
1650 */
1651static int pcnetTdtePoll(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd)
1652{
1653 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
1654 if (RT_LIKELY(pThis->GCTDRA))
1655 {
1656 RTGCPHYS32 cxda = pcnetTdraAddr(pThis, CSR_XMTRC(pThis));
1657
1658 if (!pcnetTmdTryLoad(pDevIns, pThis, tmd, PHYSADDR(pThis, cxda)))
1659 {
1660 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
1661 return 0;
1662 }
1663
1664 if (RT_UNLIKELY(tmd->tmd1.ones != 15))
1665 {
1666 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
1667 LogRel(("PCnet#%d: BAD TMD XDA=%#010x\n",
1668 PCNET_INST_NR, PHYSADDR(pThis, cxda)));
1669 return 0;
1670 }
1671
1672 /* previous xmit descriptor */
1673 CSR_PXDA(pThis) = CSR_CXDA(pThis);
1674 CSR_PXBC(pThis) = CSR_CXBC(pThis);
1675 CSR_PXST(pThis) = CSR_CXST(pThis);
1676
1677 /* set current transmit descriptor. */
1678 CSR_CXDA(pThis) = cxda;
1679 CSR_CXBC(pThis) = tmd->tmd1.bcnt;
1680 CSR_CXST(pThis) = ((uint32_t *)tmd)[1] >> 16;
1681 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
1682 return CARD_IS_OWNER(CSR_CXST(pThis));
1683 }
1684 else
1685 {
1686 /** @todo consistency with previous receive descriptor */
1687 CSR_CXDA(pThis) = 0;
1688 CSR_CXBC(pThis) = CSR_CXST(pThis) = 0;
1689 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
1690 return 0;
1691 }
1692}
1693
1694
1695/**
1696 * Write data into guest receive buffers.
1697 */
1698static void pcnetReceiveNoSync(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC,
1699 const uint8_t *buf, size_t cbToRecv, bool fAddFCS, bool fLoopback)
1700{
1701 int is_padr = 0;
1702 int is_bcast = 0;
1703 int is_ladr = 0;
1704 unsigned iRxDesc;
1705 int cbPacket;
1706
1707 if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis) || !cbToRecv))
1708 return;
1709
1710 /*
1711 * Drop packets if the VM is not running yet/anymore.
1712 */
1713 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1714 if ( enmVMState != VMSTATE_RUNNING
1715 && enmVMState != VMSTATE_RUNNING_LS)
1716 return;
1717
1718 /*
1719 * Drop packets if the cable is not connected
1720 */
1721 if (!pcnetIsLinkUp(pThis))
1722 return;
1723
1724 Log(("#%d pcnetReceiveNoSync: size=%d\n", PCNET_INST_NR, cbToRecv));
1725
1726 /*
1727 * Perform address matching.
1728 */
1729 if ( CSR_PROM(pThis)
1730 || (is_padr = padr_match(pThis, buf, cbToRecv))
1731 || (is_bcast = padr_bcast(pThis, buf, cbToRecv))
1732 || (is_ladr = ladr_match(pThis, buf, cbToRecv)))
1733 {
1734 if (HOST_IS_OWNER(CSR_CRST(pThis)))
1735 pcnetRdtePoll(pDevIns, pThis);
1736 if (RT_UNLIKELY(HOST_IS_OWNER(CSR_CRST(pThis))))
1737 {
1738 /* Not owned by controller. This should not be possible as
1739 * we already called pcnetR3CanReceive(). */
1740 LogRel(("PCnet#%d: no buffer: RCVRC=%d\n", PCNET_INST_NR, CSR_RCVRC(pThis)));
1741 /* Dump the status of all RX descriptors */
1742 const unsigned cb = 1 << pThis->iLog2DescSize;
1743 RTGCPHYS32 GCPhys = pThis->GCRDRA;
1744 iRxDesc = CSR_RCVRL(pThis);
1745 while (iRxDesc-- > 0)
1746 {
1747 RMD rmd;
1748 pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, GCPhys), false);
1749 LogRel((" %#010x\n", rmd.rmd1));
1750 GCPhys += cb;
1751 }
1752 pThis->aCSR[0] |= 0x1000; /* Set MISS flag */
1753 CSR_MISSC(pThis)++;
1754 }
1755 else
1756 {
1757 PCRTNETETHERHDR pEth = (PCRTNETETHERHDR)buf;
1758 bool fStrip = false;
1759 size_t len_802_3;
1760 uint8_t *src = &pThis->abRecvBuf[8];
1761 RTGCPHYS32 crda = CSR_CRDA(pThis);
1762 RTGCPHYS32 next_crda;
1763 RMD rmd, next_rmd;
1764
1765 /*
1766 * Ethernet framing considers these two octets to be
1767 * payload type; 802.3 framing considers them to be
1768 * payload length. IEEE 802.3x-1997 restricts Ethernet
1769 * type to be greater than or equal to 1536 (0x0600), so
1770 * that both framings can coexist on the wire.
1771 *
1772 * NB: CSR_ASTRP_RCV bit affects only 802.3 frames!
1773 */
1774 len_802_3 = RT_BE2H_U16(pEth->EtherType);
1775 if (len_802_3 < 46 && CSR_ASTRP_RCV(pThis))
1776 {
1777 cbToRecv = RT_MIN(sizeof(RTNETETHERHDR) + len_802_3, cbToRecv);
1778 fStrip = true;
1779 fAddFCS = false;
1780 }
1781
1782 memcpy(src, buf, cbToRecv);
1783
1784 if (!fStrip) {
1785 /* In loopback mode, Runt Packed Accept is always enabled internally;
1786 * don't do any padding because guest may be looping back very short packets.
1787 */
1788 if (!fLoopback)
1789 while (cbToRecv < 60)
1790 src[cbToRecv++] = 0;
1791
1792 if (fAddFCS)
1793 {
1794 uint32_t fcs = UINT32_MAX;
1795 uint8_t *p = src;
1796
1797 while (p != &src[cbToRecv])
1798 CRC(fcs, *p++);
1799
1800 /* FCS at the end of the packet */
1801 ((uint32_t *)&src[cbToRecv])[0] = htonl(fcs);
1802 cbToRecv += 4;
1803 }
1804 }
1805
1806 cbPacket = (int)cbToRecv; Assert((size_t)cbPacket == cbToRecv);
1807
1808#ifdef LOG_ENABLED
1809 PRINT_PKTHDR(buf);
1810#endif
1811
1812 pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, crda), false);
1813 /*if (!CSR_LAPPEN(pThis))*/
1814 rmd.rmd1.stp = 1;
1815
1816 size_t cbBuf = RT_MIN(4096 - (size_t)rmd.rmd1.bcnt, cbToRecv);
1817 RTGCPHYS32 rbadr = PHYSADDR(pThis, rmd.rmd0.rbadr);
1818
1819 /* save the old value to check if it was changed as long as we didn't
1820 * hold the critical section */
1821 iRxDesc = CSR_RCVRC(pThis);
1822
1823 /* We have to leave the critical section here or we risk deadlocking
1824 * with EMT when the write is to an unallocated page or has an access
1825 * handler associated with it.
1826 *
1827 * This shouldn't be a problem because:
1828 * - any modification to the RX descriptor by the driver is
1829 * forbidden as long as it is owned by the device
1830 * - we don't cache any register state beyond this point
1831 */
1832 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1833 pcnetPhysWrite(pDevIns, pThis, rbadr, src, cbBuf);
1834 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
1835 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
1836
1837 /* RX disabled in the meantime? If so, abort RX. */
1838 if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis)))
1839 return;
1840
1841 /* Was the register modified in the meantime? If so, don't touch the
1842 * register but still update the RX descriptor. */
1843 if (RT_LIKELY(iRxDesc == CSR_RCVRC(pThis)))
1844 {
1845 if (iRxDesc-- < 2)
1846 iRxDesc = CSR_RCVRL(pThis);
1847 CSR_RCVRC(pThis) = iRxDesc;
1848 }
1849 else
1850 iRxDesc = CSR_RCVRC(pThis);
1851
1852 src += cbBuf;
1853 cbToRecv -= cbBuf;
1854
1855 while (cbToRecv > 0)
1856 {
1857 /* Read the entire next descriptor as we're likely to need it. */
1858 next_crda = pcnetRdraAddr(pThis, iRxDesc);
1859
1860 /* Check next descriptor's own bit. If we don't own it, we have
1861 * to quit and write error status into the last descriptor we own.
1862 */
1863 if (!pcnetRmdLoad(pDevIns, pThis, &next_rmd, PHYSADDR(pThis, next_crda), true))
1864 break;
1865
1866 /* Write back current descriptor, clear the own bit. */
1867 pcnetRmdStorePassHost(pDevIns, pThis, &rmd, PHYSADDR(pThis, crda));
1868
1869 /* Switch to the next descriptor */
1870 crda = next_crda;
1871 rmd = next_rmd;
1872
1873 cbBuf = RT_MIN(4096 - (size_t)rmd.rmd1.bcnt, cbToRecv);
1874 RTGCPHYS32 rbadr2 = PHYSADDR(pThis, rmd.rmd0.rbadr);
1875
1876 /* We have to leave the critical section here or we risk deadlocking
1877 * with EMT when the write is to an unallocated page or has an access
1878 * handler associated with it. See above for additional comments. */
1879 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1880 pcnetPhysWrite(pDevIns, pThis, rbadr2, src, cbBuf);
1881 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
1882 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
1883
1884 /* RX disabled in the meantime? If so, abort RX. */
1885 if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis)))
1886 return;
1887
1888 /* Was the register modified in the meantime? If so, don't touch the
1889 * register but still update the RX descriptor. */
1890 if (RT_LIKELY(iRxDesc == CSR_RCVRC(pThis)))
1891 {
1892 if (iRxDesc-- < 2)
1893 iRxDesc = CSR_RCVRL(pThis);
1894 CSR_RCVRC(pThis) = iRxDesc;
1895 }
1896 else
1897 iRxDesc = CSR_RCVRC(pThis);
1898
1899 src += cbBuf;
1900 cbToRecv -= cbBuf;
1901 }
1902
1903 if (RT_LIKELY(cbToRecv == 0))
1904 {
1905 rmd.rmd1.enp = 1;
1906 rmd.rmd1.pam = !CSR_PROM(pThis) && is_padr;
1907 rmd.rmd1.lafm = !CSR_PROM(pThis) && is_ladr;
1908 rmd.rmd1.bam = !CSR_PROM(pThis) && is_bcast;
1909 rmd.rmd2.mcnt = cbPacket;
1910 rmd.rmd2.zeros = 0;
1911
1912 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbPacket);
1913 }
1914 else
1915 {
1916 Log(("#%d: Overflow by %ubytes\n", PCNET_INST_NR, cbToRecv));
1917 rmd.rmd1.oflo = 1;
1918 rmd.rmd1.buff = 1;
1919 rmd.rmd1.err = 1;
1920 }
1921
1922 /* write back, clear the own bit */
1923 pcnetRmdStorePassHost(pDevIns, pThis, &rmd, PHYSADDR(pThis, crda));
1924
1925 pThis->aCSR[0] |= 0x0400;
1926
1927 Log(("#%d RCVRC=%d CRDA=%#010x\n", PCNET_INST_NR, CSR_RCVRC(pThis), PHYSADDR(pThis, CSR_CRDA(pThis))));
1928#ifdef LOG_ENABLED
1929 PRINT_RMD(&rmd);
1930#endif
1931
1932 /* guest driver is owner: force repoll of current and next RDTEs */
1933 CSR_CRST(pThis) = 0;
1934 }
1935 }
1936
1937 /* see description of TXDPOLL:
1938 * ``transmit polling will take place following receive activities'' */
1939 if (!fLoopback)
1940 pcnetPollRxTx(pDevIns, pThis, pThisCC);
1941 pcnetUpdateIrq(pDevIns, pThis);
1942}
1943
1944
1945#ifdef IN_RING3
1946/**
1947 * @callback_method_impl{FNPDMTASKDEV,
1948 * This is just a very simple way of delaying sending to R3.}
1949 */
1950static DECLCALLBACK(void) pcnetR3XmitTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
1951{
1952 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
1953 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
1954 NOREF(pvUser);
1955
1956 /*
1957 * Transmit as much as we can.
1958 */
1959 pcnetXmitPending(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
1960}
1961#endif /* IN_RING3 */
1962
1963
1964/**
1965 * Allocates a scatter/gather buffer for a transfer.
1966 *
1967 * @returns See PPDMINETWORKUP::pfnAllocBuf.
1968 * @param pThis The shared PCNet state data.
1969 * @param cbMin The minimum buffer size.
1970 * @param fLoopback Set if we're in loopback mode.
1971 * @param pSgLoop Pointer to stack storage for the loopback SG.
1972 * @param ppSgBuf Where to return the SG buffer descriptor on success.
1973 * Always set.
1974 */
1975DECLINLINE(int) pcnetXmitAllocBuf(PPCNETSTATE pThis, PPCNETSTATECC pThisCC, size_t cbMin, bool fLoopback,
1976 PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
1977{
1978 int rc;
1979
1980 if (RT_UNLIKELY(fLoopback)) /* hope that loopback mode is rare */
1981 {
1982 pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
1983 pSgLoop->cbUsed = 0;
1984 pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
1985 pSgLoop->pvAllocator = pThis;
1986 pSgLoop->pvUser = NULL;
1987 pSgLoop->cSegs = 1;
1988 pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
1989 pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
1990 *ppSgBuf = pSgLoop;
1991 rc = VINF_SUCCESS;
1992 }
1993 else
1994 {
1995 PPDMINETWORKUP pDrv = pThisCC->pDrv;
1996 if (RT_LIKELY(pDrv))
1997 {
1998 rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
1999 AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
2000 if (RT_FAILURE(rc))
2001 *ppSgBuf = NULL;
2002 }
2003 else
2004 {
2005 rc = VERR_NET_DOWN;
2006 *ppSgBuf = NULL;
2007 }
2008 }
2009 return rc;
2010}
2011
2012
2013/**
2014 * Frees an unsent buffer.
2015 *
2016 * @param pThisCC The PCNet state data for the current context.
2017 * @param fLoopback Set if we're in loopback mode.
2018 * @param pSgBuf The SG to free. Can be NULL.
2019 */
2020DECLINLINE(void) pcnetXmitFreeBuf(PPCNETSTATECC pThisCC, bool fLoopback, PPDMSCATTERGATHER pSgBuf)
2021{
2022 if (pSgBuf)
2023 {
2024 if (RT_UNLIKELY(fLoopback))
2025 pSgBuf->pvAllocator = NULL;
2026 else
2027 {
2028 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2029 if (RT_LIKELY(pDrv))
2030 pDrv->pfnFreeBuf(pDrv, pSgBuf);
2031 }
2032 }
2033}
2034
2035
2036/**
2037 * Sends the scatter/gather buffer.
2038 *
2039 * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
2040 *
2041 * @returns See PDMINETWORKUP::pfnSendBuf.
2042 * @param pDevIns The device instance.
2043 * @param pThis The shared PCNet state data.
2044 * @param pThisCC The PCNet state data for the current context.
2045 * @param fLoopback Set if we're in loopback mode.
2046 * @param pSgBuf The SG to send.
2047 * @param fOnWorkerThread Set if we're being called on a work thread. Clear
2048 * if an EMT.
2049 */
2050DECLINLINE(int) pcnetXmitSendBuf(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fLoopback,
2051 PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
2052{
2053 int rc;
2054 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
2055 if (RT_UNLIKELY(fLoopback)) /* hope that loopback mode is rare */
2056 {
2057 Assert(pSgBuf->pvAllocator == (void *)pThis);
2058 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
2059 if (HOST_IS_OWNER(CSR_CRST(pThis)))
2060 pcnetRdtePoll(pDevIns, pThis);
2061
2062 pcnetReceiveNoSync(pDevIns, pThis, pThisCC, pThis->abLoopBuf, pSgBuf->cbUsed, true /* fAddFCS */, fLoopback);
2063 pThis->Led.Actual.s.fReading = 0;
2064 rc = VINF_SUCCESS;
2065 }
2066 else
2067 {
2068 /** @todo We used to leave the critsect here, not sure if that's necessary any
2069 * longer. If we could avoid that we could cache a bit more info in
2070 * the loop and make it part of the driver<->device contract, saving
2071 * critsect mess down in DrvIntNet. */
2072 STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
2073 if (pSgBuf->cbUsed > 70) /* unqualified guess */
2074 pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
2075
2076 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2077 if (RT_LIKELY(pDrv))
2078 {
2079 rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
2080 AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
2081 }
2082 else
2083 rc = VERR_NET_DOWN;
2084
2085 pThis->Led.Actual.s.fWriting = 0;
2086 STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
2087 }
2088 return rc;
2089}
2090
2091
2092/**
2093 * pcnetXmitRead1st worker that handles the unlikely + slower segmented code
2094 * path.
2095 */
2096static void pcnetXmitRead1stSlow(PPDMDEVINS pDevIns, RTGCPHYS32 GCPhysFrame, unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
2097{
2098 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
2099 pSgBuf->cbUsed = cbFrame;
2100 for (uint32_t iSeg = 0; ; iSeg++)
2101 {
2102 if (iSeg >= pSgBuf->cSegs)
2103 {
2104 AssertFailed();
2105 LogRelMax(10, ("PCnet: pcnetXmitRead1stSlow: segment overflow -> ignoring\n"));
2106 return;
2107 }
2108
2109 uint32_t cbRead = (uint32_t)RT_MIN(cbFrame, pSgBuf->aSegs[iSeg].cbSeg);
2110 pcnetPhysRead(pDevIns, pThis, GCPhysFrame, pSgBuf->aSegs[iSeg].pvSeg, cbRead);
2111 cbFrame -= cbRead;
2112 if (!cbFrame)
2113 return;
2114 GCPhysFrame += cbRead;
2115 }
2116}
2117
2118
2119/**
2120 * pcnetXmitSgReadMore worker that handles the unlikely + slower segmented code
2121 * path.
2122 */
2123static void pcnetXmitReadMoreSlow(PPDMDEVINS pDevIns, RTGCPHYS32 GCPhysFrame, unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
2124{
2125 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
2126
2127 /* Find the segment which we'll put the next byte into. */
2128 size_t off = pSgBuf->cbUsed;
2129 size_t offSeg = 0;
2130 uint32_t iSeg = 0;
2131 while (offSeg + pSgBuf->aSegs[iSeg].cbSeg <= off)
2132 {
2133 offSeg += pSgBuf->aSegs[iSeg].cbSeg;
2134 iSeg++;
2135 if (iSeg >= pSgBuf->cSegs)
2136 {
2137 AssertFailed();
2138 LogRelMax(10, ("PCnet: pcnetXmitReadMoreSlow: segment overflow #1 -> ignoring\n"));
2139 return;
2140 }
2141 }
2142
2143 /* Commit before we start copying so we can decrement cbFrame. */
2144 pSgBuf->cbUsed = off + cbFrame;
2145
2146 /* Deal with the first segment if we at an offset into it. */
2147 if (off != offSeg)
2148 {
2149 size_t offIntoSeg = off - offSeg;
2150 uint32_t cbRead = (uint32_t)RT_MIN(pSgBuf->aSegs[iSeg].cbSeg - offIntoSeg, cbFrame);
2151 pcnetPhysRead(pDevIns, pThis, GCPhysFrame,
2152 (uint8_t *)pSgBuf->aSegs[iSeg].pvSeg + offIntoSeg, cbRead);
2153 cbFrame -= cbRead;
2154 if (!cbFrame)
2155 return;
2156 GCPhysFrame += cbRead;
2157 iSeg++;
2158 }
2159
2160 /* For the remainder, we've got whole segments. */
2161 for (;; iSeg++)
2162 {
2163 if (iSeg >= pSgBuf->cSegs)
2164 {
2165 AssertFailed();
2166 LogRelMax(10, ("PCnet: pcnetXmitReadMoreSlow: segment overflow #2 -> ignoring\n"));
2167 return;
2168 }
2169
2170 uint32_t cbRead = (uint32_t)RT_MIN(pSgBuf->aSegs[iSeg].cbSeg, cbFrame);
2171 pcnetPhysRead(pDevIns, pThis, GCPhysFrame, pSgBuf->aSegs[iSeg].pvSeg, cbRead);
2172 cbFrame -= cbRead;
2173 if (!cbFrame)
2174 return;
2175 GCPhysFrame += cbFrame;
2176 }
2177}
2178
2179
2180/**
2181 * Reads the first part of a frame into the scatter gather buffer.
2182 */
2183DECLINLINE(void) pcnetXmitRead1st(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS32 GCPhysFrame, const unsigned cbFrame,
2184 PPDMSCATTERGATHER pSgBuf)
2185{
2186 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect)); RT_NOREF(pThis);
2187 Assert(pSgBuf->cbAvailable >= cbFrame);
2188
2189 if (RT_LIKELY(pSgBuf->aSegs[0].cbSeg >= cbFrame)) /* justification: all drivers returns a single segment atm. */
2190 {
2191 pSgBuf->cbUsed = cbFrame;
2192 pcnetPhysRead(pDevIns, pThis, GCPhysFrame, pSgBuf->aSegs[0].pvSeg, cbFrame);
2193 }
2194 else
2195 pcnetXmitRead1stSlow(pDevIns, GCPhysFrame, cbFrame, pSgBuf);
2196}
2197
2198/**
2199 * Reads more into the current frame.
2200 */
2201DECLINLINE(void) pcnetXmitReadMore(PPDMDEVINS pDevIns, RTGCPHYS32 GCPhysFrame, const unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
2202{
2203 size_t off = pSgBuf->cbUsed;
2204 Assert(pSgBuf->cbAvailable >= cbFrame + off);
2205
2206 if (RT_LIKELY(pSgBuf->aSegs[0].cbSeg >= cbFrame + off))
2207 {
2208 pSgBuf->cbUsed = cbFrame + off;
2209 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
2210 pcnetPhysRead(pDevIns, pThis, GCPhysFrame, (uint8_t *)pSgBuf->aSegs[0].pvSeg + off, cbFrame);
2211 }
2212 else
2213 pcnetXmitReadMoreSlow(pDevIns, GCPhysFrame, cbFrame, pSgBuf);
2214}
2215
2216
2217/**
2218 * Fails a TMD with a link down error.
2219 */
2220static void pcnetXmitFailTMDLinkDown(PPCNETSTATE pThis, TMD *pTmd)
2221{
2222 /* make carrier error - hope this is correct. */
2223 pThis->cLinkDownReported++;
2224 pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
2225 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
2226 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
2227 Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
2228 PCNET_INST_NR, pThis->aBCR[BCR_SWS]));
2229}
2230
2231/**
2232 * Fails a TMD with a generic error.
2233 */
2234static void pcnetXmitFailTMDGeneric(PPCNETSTATE pThis, TMD *pTmd)
2235{
2236 /* make carrier error - hope this is correct. */
2237 pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
2238 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
2239 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
2240 Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
2241 PCNET_INST_NR, pThis->aBCR[BCR_SWS]));
2242}
2243
2244
2245/**
2246 * Try to transmit frames
2247 */
2248static void pcnetTransmit(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
2249{
2250 if (RT_UNLIKELY(!CSR_TXON(pThis)))
2251 {
2252 pThis->aCSR[0] &= ~0x0008; /* Clear TDMD */
2253 return;
2254 }
2255
2256 /*
2257 * Check the current transmit descriptors.
2258 */
2259 TMD tmd;
2260 if (!pcnetTdtePoll(pDevIns, pThis, &tmd))
2261 return;
2262
2263 /*
2264 * Clear TDMD.
2265 */
2266 pThis->aCSR[0] &= ~0x0008;
2267
2268 /*
2269 * Transmit pending packets if possible, defer it if we cannot do it
2270 * in the current context.
2271 */
2272#if defined(IN_RING0) || defined(IN_RC)
2273 if (!pThisCC->pDrv)
2274 {
2275 int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hXmitTask);
2276 AssertRC(rc);
2277 }
2278 else
2279#endif
2280 {
2281 int rc = pcnetXmitPending(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
2282 if (rc == VERR_TRY_AGAIN)
2283 rc = VINF_SUCCESS;
2284 AssertRC(rc);
2285 }
2286}
2287
2288
2289/**
2290 * Actually try transmit frames.
2291 *
2292 * @threads TX or EMT.
2293 */
2294static int pcnetAsyncTransmit(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fOnWorkerThread)
2295{
2296 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
2297
2298 /*
2299 * Just cleared transmit demand if the transmitter is off.
2300 */
2301 if (RT_UNLIKELY(!CSR_TXON(pThis)))
2302 {
2303 pThis->aCSR[0] &= ~0x0008; /* Clear TDMD */
2304 return VINF_SUCCESS;
2305 }
2306
2307 /*
2308 * Iterate the transmit descriptors.
2309 */
2310 int rc;
2311 unsigned cFlushIrq = 0;
2312 int cMax = 32;
2313 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
2314 do
2315 {
2316#ifdef VBOX_WITH_STATISTICS
2317 unsigned cBuffers = 1;
2318#endif
2319 TMD tmd;
2320 if (!pcnetTdtePoll(pDevIns, pThis, &tmd))
2321 break;
2322
2323#ifdef LOG_ENABLED
2324 Log10(("#%d TMDLOAD %#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_CXDA(pThis))));
2325 PRINT_TMD(&tmd);
2326#endif
2327 bool const fLoopback = CSR_LOOP(pThis);
2328 PDMSCATTERGATHER SgLoop;
2329 PPDMSCATTERGATHER pSgBuf;
2330
2331 /*
2332 * The typical case - a complete packet.
2333 */
2334 if (tmd.tmd1.stp && tmd.tmd1.enp)
2335 {
2336 const unsigned cb = 4096 - tmd.tmd1.bcnt;
2337 Log(("#%d pcnetAsyncTransmit: stp&enp: cb=%d xmtrc=%#x\n", PCNET_INST_NR, cb, CSR_XMTRC(pThis)));
2338 STAM_COUNTER_INC(&pThis->StatTransmitCase1);
2339
2340 if (RT_LIKELY(pcnetIsLinkUp(pThis) || fLoopback))
2341 {
2342 /* From the manual: ``A zero length buffer is acceptable as
2343 * long as it is not the last buffer in a chain (STP = 0 and
2344 * ENP = 1).'' That means that the first buffer might have a
2345 * zero length if it is not the last one in the chain. */
2346 if (RT_LIKELY(cb <= MAX_FRAME))
2347 {
2348 rc = pcnetXmitAllocBuf(pThis, pThisCC, cb, fLoopback, &SgLoop, &pSgBuf);
2349 if (RT_SUCCESS(rc))
2350 {
2351 pcnetXmitRead1st(pDevIns, pThis, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
2352 rc = pcnetXmitSendBuf(pDevIns, pThis, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
2353 }
2354 else if (rc == VERR_TRY_AGAIN)
2355 {
2356 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2357 return VINF_SUCCESS;
2358 }
2359 if (RT_FAILURE(rc))
2360 pcnetXmitFailTMDLinkDown(pThis, &tmd);
2361 }
2362 else if (cb == 4096)
2363 {
2364 /* The Windows NT4 pcnet driver sometimes marks the first
2365 * unused descriptor as owned by us. Ignore that (by
2366 * passing it back). Do not update the ring counter in this
2367 * case (otherwise that driver becomes even more confused,
2368 * which causes transmit to stall for about 10 seconds).
2369 * This is just a workaround, not a final solution. */
2370 /* r=frank: IMHO this is the correct implementation. The
2371 * manual says: ``If the OWN bit is set and the buffer
2372 * length is 0, the OWN bit will be cleared. In the C-LANCE
2373 * the buffer length of 0 is interpreted as a 4096-byte
2374 * buffer.'' */
2375 /* r=michaln: Perhaps not quite right. The C-LANCE (Am79C90)
2376 * datasheet explains that the old LANCE (Am7990) ignored
2377 * the top four bits next to BCNT and a count of 0 was
2378 * interpreted as 4096. In the C-LANCE, that is still the
2379 * case if the top bits are all ones. If all 16 bits are
2380 * zero, the C-LANCE interprets it as zero-length transmit
2381 * buffer. It's not entirely clear if the later models
2382 * (PCnet-ISA, PCnet-PCI) behave like the C-LANCE or not.
2383 * It is possible that the actual behavior of the C-LANCE
2384 * and later hardware is that the buffer lengths are *16-bit*
2385 * two's complement numbers between 0 and 4096. AMD's drivers
2386 * in fact generally treat the length as a 16-bit quantity. */
2387 LogRel(("PCnet#%d: pcnetAsyncTransmit: illegal 4kb frame -> ignoring\n", PCNET_INST_NR));
2388 pcnetTmdStorePassHost(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
2389 break;
2390 }
2391 else
2392 {
2393 /* Signal error, as this violates the Ethernet specs. */
2394 /** @todo check if the correct error is generated. */
2395 LogRel(("PCnet#%d: pcnetAsyncTransmit: illegal 4kb frame -> signalling error\n", PCNET_INST_NR));
2396
2397 pcnetXmitFailTMDGeneric(pThis, &tmd);
2398 }
2399 }
2400 else
2401 pcnetXmitFailTMDLinkDown(pThis, &tmd);
2402
2403 /* Write back the TMD and pass it to the host (clear own bit). */
2404 pcnetTmdStorePassHost(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
2405
2406 /* advance the ring counter register */
2407 if (CSR_XMTRC(pThis) < 2)
2408 CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
2409 else
2410 CSR_XMTRC(pThis)--;
2411 }
2412 else if (tmd.tmd1.stp)
2413 {
2414 STAM_COUNTER_INC(&pThis->StatTransmitCase2);
2415
2416 /*
2417 * Read TMDs until end-of-packet or tdte poll fails (underflow).
2418 *
2419 * We allocate a maximum sized buffer here since we do not wish to
2420 * waste time finding out how much space we actually need even if
2421 * we could reliably do that on SMP guests.
2422 */
2423 unsigned cb = 4096 - tmd.tmd1.bcnt;
2424 rc = pcnetXmitAllocBuf(pThis, pThisCC, RT_MAX(MAX_FRAME, cb), fLoopback, &SgLoop, &pSgBuf);
2425 if (rc == VERR_TRY_AGAIN)
2426 {
2427 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2428 return VINF_SUCCESS;
2429 }
2430
2431 bool fDropFrame = RT_FAILURE(rc);
2432 if (!fDropFrame)
2433 pcnetXmitRead1st(pDevIns, pThis, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
2434
2435 for (;;)
2436 {
2437 /*
2438 * Advance the ring counter register and check the next tmd.
2439 */
2440#ifdef LOG_ENABLED
2441 const uint32_t iStart = CSR_XMTRC(pThis);
2442#endif
2443 const uint32_t GCPhysPrevTmd = PHYSADDR(pThis, CSR_CXDA(pThis));
2444 if (CSR_XMTRC(pThis) < 2)
2445 CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
2446 else
2447 CSR_XMTRC(pThis)--;
2448
2449 TMD tmdNext;
2450 if (!pcnetTdtePoll(pDevIns, pThis, &tmdNext))
2451 {
2452 /*
2453 * Underflow!
2454 */
2455 tmd.tmd2.buff = tmd.tmd2.uflo = tmd.tmd1.err = 1;
2456 pThis->aCSR[0] |= 0x0200; /* set TINT */
2457 /* Don't allow the guest to clear TINT before reading it */
2458 pThis->u16CSR0LastSeenByGuest &= ~0x0200;
2459 if (!CSR_DXSUFLO(pThis)) /* stop on xmit underflow */
2460 pThis->aCSR[0] &= ~0x0010; /* clear TXON */
2461 pcnetTmdStorePassHost(pDevIns, pThis, &tmd, GCPhysPrevTmd);
2462 AssertMsgFailed(("pcnetAsyncTransmit: Underflow!!!\n"));
2463 pcnetXmitFreeBuf(pThisCC, fLoopback, pSgBuf);
2464 break;
2465 }
2466
2467 /* release & save the previous tmd, pass it to the host */
2468 pcnetTmdStorePassHost(pDevIns, pThis, &tmd, GCPhysPrevTmd);
2469
2470 /*
2471 * The next tmd is already loaded.
2472 */
2473#ifdef VBOX_WITH_STATISTICS
2474 cBuffers++;
2475#endif
2476 tmd = tmdNext;
2477 cb = 4096 - tmd.tmd1.bcnt;
2478 if ( !fDropFrame
2479 && pSgBuf->cbUsed + cb <= pSgBuf->cbAvailable)
2480 pcnetXmitReadMore(pDevIns, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
2481 else
2482 {
2483 AssertMsg(fDropFrame, ("pcnetAsyncTransmit: Frame is too big!!! %d bytes\n", pSgBuf->cbUsed + cb));
2484 fDropFrame = true;
2485 }
2486
2487 /*
2488 * Done already?
2489 */
2490 if (tmd.tmd1.enp)
2491 {
2492 Log(("#%d pcnetAsyncTransmit: stp: cb=%d xmtrc=%#x-%#x\n", PCNET_INST_NR,
2493 pSgBuf ? pSgBuf->cbUsed : 0, iStart, CSR_XMTRC(pThis)));
2494 if (!fDropFrame && (pcnetIsLinkUp(pThis) || fLoopback))
2495 {
2496 rc = pcnetXmitSendBuf(pDevIns, pThis, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
2497 fDropFrame = RT_FAILURE(rc);
2498 }
2499 else
2500 pcnetXmitFreeBuf(pThisCC, fLoopback, pSgBuf);
2501 if (fDropFrame)
2502 pcnetXmitFailTMDLinkDown(pThis, &tmd);
2503
2504 /* Write back the TMD, pass it to the host */
2505 pcnetTmdStorePassHost(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
2506
2507 /* advance the ring counter register */
2508 if (CSR_XMTRC(pThis) < 2)
2509 CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
2510 else
2511 CSR_XMTRC(pThis)--;
2512 break;
2513 }
2514 } /* the loop */
2515 }
2516 else
2517 {
2518 /*
2519 * We underflowed in a previous transfer, or the driver is giving us shit.
2520 * Simply stop the transmitting for now.
2521 */
2522 /** @todo according to the specs we're supposed to clear the own bit and move on to the next one. */
2523 Log(("#%d pcnetAsyncTransmit: guest is giving us shit!\n", PCNET_INST_NR));
2524 break;
2525 }
2526 /* Update TDMD, TXSTRT and TINT. */
2527 pThis->aCSR[0] &= ~0x0008; /* clear TDMD */
2528
2529 pThis->aCSR[4] |= 0x0008; /* set TXSTRT */
2530 if ( !CSR_TOKINTD(pThis) /* Transmit OK Interrupt Disable, no infl. on errors. */
2531 || (CSR_LTINTEN(pThis) && tmd.tmd1.ltint)
2532 || tmd.tmd1.err)
2533 {
2534 cFlushIrq++;
2535 }
2536
2537 /** @todo should we continue after an error (tmd.tmd1.err) or not? */
2538
2539 STAM_COUNTER_INC(&pThis->aStatXmitChainCounts[RT_MIN(cBuffers,
2540 RT_ELEMENTS(pThis->aStatXmitChainCounts)) - 1]);
2541 if (--cMax == 0)
2542 break;
2543 } while (CSR_TXON(pThis)); /* transfer on */
2544
2545 if (cFlushIrq)
2546 {
2547 STAM_COUNTER_INC(&pThis->aStatXmitFlush[RT_MIN(cFlushIrq, RT_ELEMENTS(pThis->aStatXmitFlush)) - 1]);
2548 /* The WinXP PCnet driver has apparently a bug: It sets CSR0.TDMD _before_
2549 * it clears CSR0.TINT. This can lead to a race where the driver clears
2550 * CSR0.TINT right after it was set by the device. The driver waits until
2551 * CSR0.TINT is set again but this will never happen. So prevent clearing
2552 * this bit as long as the driver didn't read it. See @bugref{5288}. */
2553 pThis->aCSR[0] |= 0x0200; /* set TINT */
2554 /* Don't allow the guest to clear TINT before reading it */
2555 pThis->u16CSR0LastSeenByGuest &= ~0x0200;
2556 pcnetUpdateIrq(pDevIns, pThis);
2557 }
2558
2559 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2560
2561 return VINF_SUCCESS;
2562}
2563
2564
2565/**
2566 * Transmit pending descriptors.
2567 *
2568 * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
2569 *
2570 * @param pDevIns The device instance.
2571 * @param pThis The PCnet shared instance data.
2572 * @param pThisCC The PCNet state data for the current context.
2573 * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
2574 */
2575static int pcnetXmitPending(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fOnWorkerThread)
2576{
2577 RT_NOREF_PV(fOnWorkerThread);
2578 int rc;
2579
2580 /*
2581 * Grab the xmit lock of the driver as well as the PCnet device state.
2582 */
2583 PPDMINETWORKUP pDrv = pThisCC->pDrv;
2584 if (pDrv)
2585 {
2586 rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
2587 if (RT_FAILURE(rc))
2588 return rc;
2589 }
2590 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
2591 if (RT_SUCCESS(rc))
2592 {
2593 /** @todo check if we're supposed to suspend now. */
2594 /*
2595 * Do the transmitting.
2596 */
2597 int rc2 = pcnetAsyncTransmit(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
2598 AssertReleaseRC(rc2);
2599
2600 /*
2601 * Release the locks.
2602 */
2603 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
2604 }
2605 else
2606 AssertLogRelRC(rc);
2607 if (pDrv)
2608 pDrv->pfnEndXmit(pDrv);
2609
2610 return rc;
2611}
2612
2613
2614/**
2615 * Poll for changes in RX and TX descriptor rings.
2616 */
2617static void pcnetPollRxTx(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
2618{
2619 if (CSR_RXON(pThis))
2620 {
2621 /*
2622 * The second case is important for pcnetWaitReceiveAvail(): If CSR_CRST(pThis) was
2623 * true but pcnetR3CanReceive() returned false for some other reason we need to check
2624 * _now_ if we have to wakeup pcnetWaitReceiveAvail().
2625 */
2626 if ( HOST_IS_OWNER(CSR_CRST(pThis)) /* only poll RDTEs if none available or ... */
2627 || pThis->fMaybeOutOfSpace) /* ... for waking up pcnetWaitReceiveAvail() */
2628 pcnetRdtePoll(pDevIns, pThis);
2629 }
2630
2631 if (CSR_TDMD(pThis) || (CSR_TXON(pThis) && !CSR_DPOLL(pThis)))
2632 pcnetTransmit(pDevIns, pThis, pThisCC);
2633}
2634
2635
2636/**
2637 * Start the poller timer.
2638 * Poll timer interval is fixed to 500Hz. Don't stop it.
2639 * @thread EMT, TAP.
2640 */
2641static void pcnetPollTimerStart(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
2642{
2643 PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerPoll, 2);
2644}
2645
2646
2647/**
2648 * Update the poller timer.
2649 * @thread EMT.
2650 */
2651static void pcnetPollTimer(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
2652{
2653 STAM_PROFILE_ADV_START(&pThis->StatPollTimer, a);
2654
2655#ifdef LOG_ENABLED
2656 TMD dummy;
2657 if (CSR_STOP(pThis) || CSR_SPND(pThis))
2658 Log2(("#%d pcnetPollTimer time=%#010llx CSR_STOP=%d CSR_SPND=%d\n",
2659 PCNET_INST_NR, RTTimeMilliTS(), CSR_STOP(pThis), CSR_SPND(pThis)));
2660 else
2661 Log2(("#%d pcnetPollTimer time=%#010llx TDMD=%d TXON=%d POLL=%d TDTE=%d TDRA=%#x\n",
2662 PCNET_INST_NR, RTTimeMilliTS(), CSR_TDMD(pThis), CSR_TXON(pThis),
2663 !CSR_DPOLL(pThis), pcnetTdtePoll(pDevIns, pThis, &dummy), pThis->GCTDRA));
2664 Log2(("#%d pcnetPollTimer: CSR_CXDA=%#x CSR_XMTRL=%d CSR_XMTRC=%d\n",
2665 PCNET_INST_NR, CSR_CXDA(pThis), CSR_XMTRL(pThis), CSR_XMTRC(pThis)));
2666#endif
2667#ifdef LOG_ENABLED
2668 if (CSR_CXDA(pThis))
2669 {
2670 TMD tmd;
2671 pcnetTmdLoadAll(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
2672 Log10(("#%d pcnetPollTimer: TMDLOAD %#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_CXDA(pThis))));
2673 PRINT_TMD(&tmd);
2674 }
2675#endif
2676 if (CSR_TDMD(pThis))
2677 pcnetTransmit(pDevIns, pThis, pThisCC);
2678
2679 pcnetUpdateIrq(pDevIns, pThis);
2680
2681 /* If the receive thread is waiting for new descriptors, poll TX/RX even if polling
2682 * disabled. We wouldn't need to poll for new TX descriptors in that case but it will
2683 * not hurt as waiting for RX descriptors should happen very seldom */
2684 if (RT_LIKELY( !CSR_STOP(pThis)
2685 && !CSR_SPND(pThis)
2686 && ( !CSR_DPOLL(pThis)
2687 || pThis->fMaybeOutOfSpace)))
2688 {
2689 /* We ensure that we poll at least every 2ms (500Hz) but not more often than
2690 * 5000 times per second. This way we completely prevent the overhead from
2691 * heavy reprogramming the timer which turned out to be very CPU-intensive.
2692 * The drawback is that csr46 and csr47 are not updated properly anymore
2693 * but so far I have not seen any guest depending on these values. The 2ms
2694 * interval is the default polling interval of the PCnet card (65536/33MHz). */
2695 uint64_t u64Now = PDMDevHlpTimerGet(pDevIns, pThis->hTimerPoll);
2696 if (RT_UNLIKELY(u64Now - pThis->u64LastPoll > 200000))
2697 {
2698 pThis->u64LastPoll = u64Now;
2699 pcnetPollRxTx(pDevIns, pThis, pThisCC);
2700 }
2701 if (!PDMDevHlpTimerIsActive(pDevIns, pThis->hTimerPoll))
2702 pcnetPollTimerStart(pDevIns, pThis);
2703 }
2704 STAM_PROFILE_ADV_STOP(&pThis->StatPollTimer, a);
2705}
2706
2707
2708static VBOXSTRICTRC pcnetCSRWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t u32RAP, uint32_t val)
2709{
2710 VBOXSTRICTRC rc = VINF_SUCCESS;
2711 Log8(("#%d pcnetCSRWriteU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
2712 switch (u32RAP)
2713 {
2714 case 0:
2715 {
2716 uint16_t csr0 = pThis->aCSR[0];
2717 /* Clear any interrupt flags.
2718 * Don't clear an interrupt flag which was not seen by the guest yet. */
2719 csr0 &= ~(val & 0x7f00 & pThis->u16CSR0LastSeenByGuest);
2720 csr0 = (csr0 & ~0x0040) | (val & 0x0048);
2721 val = (val & 0x007f) | (csr0 & 0x7f00);
2722
2723 /* Iff STOP, STRT and INIT are set, clear STRT and INIT */
2724 if ((val & 7) == 7)
2725 val &= ~3;
2726
2727 Log(("#%d CSR0: old=%#06x new=%#06x\n", PCNET_INST_NR, pThis->aCSR[0], csr0));
2728
2729#ifndef IN_RING3
2730 if (!(csr0 & 0x0001/*init*/) && (val & 1))
2731 {
2732 Log(("#%d pcnetCSRWriteU16: pcnetR3Init requested => HC\n", PCNET_INST_NR));
2733 return VINF_IOM_R3_IOPORT_WRITE;
2734 }
2735#endif
2736 pThis->aCSR[0] = csr0;
2737
2738 if (!CSR_STOP(pThis) && (val & 4))
2739 pcnetStop(pDevIns, pThis, pThisCC);
2740
2741#ifdef IN_RING3
2742 if (!CSR_INIT(pThis) && (val & 1))
2743 {
2744 bool fDelayInit = false;
2745
2746 /* Many PCnet drivers disable PCI bus mastering before setting the INIT bit and
2747 * then immediately enable it back again. This is done to work around a silicon
2748 * bug that could cause a PCI bus hang under some circumstances. The bug only
2749 * existed in the early PCI chips (Am79C970 PCnet-PCI) but many drivers apply the
2750 * workaround to all PCnet PCI models. Drivers written by AMD generally do this
2751 * (DOS, Windows, OS/2). PCnet drivers in Windows 2000 and XP are new enough to
2752 * not apply the workaround to our emulated PCnet-PCI II (Am79C970A) and
2753 * PCnet-FAST III (Am79C973).
2754 *
2755 * The AMDPCnet32 drivers for NeXTSTEP/OpenStep (notably OS 4.2) completely fail
2756 * unless we delay the initialization until after bus mastering is re-enabled.
2757 */
2758 if (PCNET_IS_PCI(pThis))
2759 {
2760 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
2761 uint8_t uCmd = PDMPciDevGetByte(pPciDev, 0x04);
2762
2763 /* Recognize situation with PCI bus mastering disabled and setting
2764 * INIT bit without also setting STRT.
2765 */
2766 if (!(uCmd & 4) && !(val & 2))
2767 fDelayInit = true;
2768 }
2769
2770 if (!fDelayInit)
2771 pcnetR3Init(pDevIns, pThis, pThisCC);
2772 else
2773 {
2774 LogRel(("PCnet#%d: Delaying INIT due to disabled PCI bus mastering\n", PCNET_INST_NR));
2775 pThis->aCSR[0] |= 0x0001; /* Set INIT and MERR bits. */
2776 pThis->aCSR[6] = 1; /* Set a flag in read-only CSR6. */
2777 }
2778 }
2779#endif
2780
2781 if (!CSR_STRT(pThis) && (val & 2))
2782 pcnetStart(pDevIns, pThis);
2783
2784 if (CSR_TDMD(pThis))
2785 pcnetTransmit(pDevIns, pThis, pThisCC);
2786
2787 return rc;
2788 }
2789 case 2: /* IADRH */
2790 if (PCNET_IS_ISA(pThis))
2791 val &= 0x00ff; /* Upper 8 bits ignored on ISA chips. */
2792 RT_FALL_THRU();
2793 case 1: /* IADRL */
2794 case 8: /* LADRF 0..15 */
2795 case 9: /* LADRF 16..31 */
2796 case 10: /* LADRF 32..47 */
2797 case 11: /* LADRF 48..63 */
2798 case 12: /* PADR 0..15 */
2799 case 13: /* PADR 16..31 */
2800 case 14: /* PADR 32..47 */
2801 case 18: /* CRBAL */
2802 case 19: /* CRBAU */
2803 case 20: /* CXBAL */
2804 case 21: /* CXBAU */
2805 case 22: /* NRBAL */
2806 case 23: /* NRBAU */
2807 case 26: /* NRDAL */
2808 case 27: /* NRDAU */
2809 case 28: /* CRDAL */
2810 case 29: /* CRDAU */
2811 case 32: /* NXDAL */
2812 case 33: /* NXDAU */
2813 case 34: /* CXDAL */
2814 case 35: /* CXDAU */
2815 case 36: /* NNRDL */
2816 case 37: /* NNRDU */
2817 case 38: /* NNXDL */
2818 case 39: /* NNXDU */
2819 case 40: /* CRBCL */
2820 case 41: /* CRBCU */
2821 case 42: /* CXBCL */
2822 case 43: /* CXBCU */
2823 case 44: /* NRBCL */
2824 case 45: /* NRBCU */
2825 case 46: /* POLL */
2826 case 47: /* POLLINT */
2827 case 72: /* RCVRC */
2828 case 74: /* XMTRC */
2829 case 112: /* MISSC */
2830 if (CSR_STOP(pThis) || CSR_SPND(pThis))
2831 break;
2832 else
2833 {
2834 Log(("#%d: WRITE CSR%d, %#06x, ignoring!!\n", PCNET_INST_NR, u32RAP, val));
2835 return rc;
2836 }
2837 case 3: /* Interrupt Mask and Deferral Control */
2838 break;
2839 case 4: /* Test and Features Control */
2840 pThis->aCSR[4] &= ~(val & 0x026a);
2841 val &= ~0x026a;
2842 val |= pThis->aCSR[4] & 0x026a;
2843 break;
2844 case 5: /* Extended Control and Interrupt 1 */
2845 pThis->aCSR[5] &= ~(val & 0x0a90);
2846 val &= ~0x0a90;
2847 val |= pThis->aCSR[5] & 0x0a90;
2848 break;
2849 case 7: /* Extended Control and Interrupt 2 */
2850 {
2851 uint16_t csr7 = pThis->aCSR[7];
2852 csr7 &= ~0x0400 ;
2853 csr7 &= ~(val & 0x0800);
2854 csr7 |= (val & 0x0400);
2855 pThis->aCSR[7] = csr7;
2856 return rc;
2857 }
2858 case 15: /* Mode */
2859 if ((pThis->aCSR[15] & 0x8000) != (uint16_t)(val & 0x8000) && pThis->fDriverAttached)
2860 {
2861 Log(("#%d: promiscuous mode changed to %d\n", PCNET_INST_NR, !!(val & 0x8000)));
2862#ifndef IN_RING3
2863 return VINF_IOM_R3_IOPORT_WRITE;
2864#else
2865 /* check for promiscuous mode change */
2866 if (pThisCC->pDrv)
2867 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, !!(val & 0x8000));
2868#endif
2869 }
2870 break;
2871 case 16: /* IADRL */
2872 return pcnetCSRWriteU16(pDevIns, pThis, pThisCC, 1, val);
2873 case 17: /* IADRH */
2874 return pcnetCSRWriteU16(pDevIns, pThis, pThisCC, 2, val);
2875
2876 /*
2877 * 24 and 25 are the Base Address of Receive Descriptor.
2878 * We combine and mirror these in GCRDRA.
2879 */
2880 case 24: /* BADRL */
2881 case 25: /* BADRU */
2882 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
2883 {
2884 Log(("#%d: WRITE CSR%d, %#06x, ignoring!!\n", PCNET_INST_NR, u32RAP, val));
2885 return rc;
2886 }
2887 if (u32RAP == 24)
2888 pThis->GCRDRA = (pThis->GCRDRA & 0xffff0000) | (val & 0x0000ffff);
2889 else
2890 pThis->GCRDRA = (pThis->GCRDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
2891 Log(("#%d: WRITE CSR%d, %#06x => GCRDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCRDRA));
2892 if (pThis->GCRDRA & (pThis->iLog2DescSize - 1))
2893 LogRel(("PCnet#%d: Warning: Misaligned RDRA (GCRDRA=%#010x)\n", PCNET_INST_NR, pThis->GCRDRA));
2894 break;
2895
2896 /*
2897 * 30 & 31 are the Base Address of Transmit Descriptor.
2898 * We combine and mirrorthese in GCTDRA.
2899 */
2900 case 30: /* BADXL */
2901 case 31: /* BADXU */
2902 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
2903 {
2904 Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
2905 return rc;
2906 }
2907 if (u32RAP == 30)
2908 pThis->GCTDRA = (pThis->GCTDRA & 0xffff0000) | (val & 0x0000ffff);
2909 else
2910 pThis->GCTDRA = (pThis->GCTDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
2911 Log(("#%d: WRITE CSR%d, %#06x => GCTDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCTDRA));
2912 if (pThis->GCTDRA & (pThis->iLog2DescSize - 1))
2913 LogRel(("PCnet#%d: Warning: Misaligned TDRA (GCTDRA=%#010x)\n", PCNET_INST_NR, pThis->GCTDRA));
2914 break;
2915
2916 case 58: /* Software Style */
2917 rc = pcnetBCRWriteU16(pDevIns, pThis, BCR_SWS, val);
2918 break;
2919
2920 /*
2921 * Registers 76 and 78 aren't stored correctly (see todos), but I'm don't dare
2922 * try fix that right now. So, as a quick hack for 'alt init' I'll just correct them here.
2923 */
2924 case 76: /* RCVRL */ /** @todo call pcnetR3UpdateRingHandlers */
2925 /** @todo receive ring length is stored in two's complement! */
2926 case 78: /* XMTRL */ /** @todo call pcnetR3UpdateRingHandlers */
2927 /** @todo transmit ring length is stored in two's complement! */
2928 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
2929 {
2930 Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
2931 return rc;
2932 }
2933 Log(("#%d: WRITE CSR%d, %#06x (hacked %#06x) (alt init)\n", PCNET_INST_NR,
2934 u32RAP, val, 1 + ~(uint16_t)val));
2935 val = 1 + ~(uint16_t)val;
2936
2937 /*
2938 * HACK ALERT! Set the counter registers too.
2939 */
2940 pThis->aCSR[u32RAP - 4] = val;
2941 break;
2942
2943 default:
2944 return rc;
2945 }
2946 pThis->aCSR[u32RAP] = val;
2947 return rc;
2948}
2949
2950/**
2951 * Encode a 32-bit link speed into a custom 16-bit floating-point value
2952 */
2953static uint32_t pcnetLinkSpd(uint32_t speed)
2954{
2955 unsigned exp = 0;
2956
2957 while (speed & 0xFFFFE000)
2958 {
2959 speed /= 10;
2960 ++exp;
2961 }
2962 return (exp << 13) | speed;
2963}
2964
2965static VBOXSTRICTRC pcnetCSRReadU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t u32RAP, uint32_t *pu32)
2966{
2967 uint32_t val;
2968 switch (u32RAP)
2969 {
2970 case 0:
2971 /* Check if delayed initialization needs to run. */
2972 if (RT_UNLIKELY(pThis->aCSR[6] == 1))
2973 {
2974#ifndef IN_RING3
2975 return VINF_IOM_R3_IOPORT_READ;
2976#else
2977 /* This is the second half of delayed initialization required
2978 * to work around guest drivers that temporarily disable PCI bus
2979 * mastering around setting the INIT bit in CSR0.
2980 * See pcnetCSRWriteU16() for details.
2981 */
2982 pcnetR3Init(pDevIns, pThis, pThisCC);
2983 Assert(pThis->aCSR[6] != 1);
2984#endif
2985 }
2986 pcnetUpdateIrq(pDevIns, pThis);
2987 val = pThis->aCSR[0];
2988 val |= (val & 0x7800) ? 0x8000 : 0;
2989 pThis->u16CSR0LastSeenByGuest = val;
2990 break;
2991 case 16:
2992 return pcnetCSRReadU16(pDevIns, pThis, pThisCC, 1, pu32);
2993 case 17:
2994 return pcnetCSRReadU16(pDevIns, pThis, pThisCC, 2, pu32);
2995 case 58:
2996 *pu32 = pcnetBCRReadU16(pThis, BCR_SWS);
2997 return VINF_SUCCESS;
2998 case 68: /* Custom register to pass link speed to driver */
2999 *pu32 = pcnetLinkSpd(pThis->u32LinkSpeed);
3000 return VINF_SUCCESS;
3001 case 88:
3002 val = pThis->aCSR[89];
3003 val <<= 16;
3004 val |= pThis->aCSR[88];
3005 break;
3006 default:
3007 val = pThis->aCSR[u32RAP];
3008 }
3009 *pu32 = val;
3010 Log8(("#%d pcnetCSRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3011 return VINF_SUCCESS;
3012}
3013
3014static VBOXSTRICTRC pcnetBCRWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, uint32_t u32RAP, uint32_t val)
3015{
3016 u32RAP &= 0x7f;
3017 Log7(("#%d pcnetBCRWriteU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3018 switch (u32RAP)
3019 {
3020 case BCR_SWS:
3021 if (!(CSR_STOP(pThis) || CSR_SPND(pThis)))
3022 return VINF_SUCCESS;
3023 val &= ~0x0300;
3024 switch (val & 0x00ff)
3025 {
3026 default:
3027 Log(("#%d Bad SWSTYLE=%#04x\n", PCNET_INST_NR, val & 0xff));
3028 RT_FALL_THRU();
3029 case 0:
3030 val |= 0x0200; /* 16 bit */
3031 pThis->iLog2DescSize = 3;
3032 pThis->GCUpperPhys = (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
3033 break;
3034 case 1:
3035 val |= 0x0100; /* 32 bit */
3036 pThis->iLog2DescSize = 4;
3037 pThis->GCUpperPhys = 0;
3038 break;
3039 case 2:
3040 case 3:
3041 val |= 0x0300; /* 32 bit */
3042 pThis->iLog2DescSize = 4;
3043 pThis->GCUpperPhys = 0;
3044 break;
3045 }
3046 Log(("#%d BCR_SWS=%#06x\n", PCNET_INST_NR, val));
3047 pThis->aCSR[58] = val;
3048 RT_FALL_THRU();
3049 case BCR_LNKST:
3050 case BCR_LED1:
3051 case BCR_LED2:
3052 case BCR_LED3:
3053 case BCR_MC:
3054 case BCR_FDC:
3055 case BCR_BSBC:
3056 case BCR_EECAS:
3057 case BCR_PLAT:
3058 case BCR_MIICAS:
3059 case BCR_MIIADDR:
3060 pThis->aBCR[u32RAP] = val;
3061 break;
3062
3063 case BCR_STVAL:
3064 val &= 0xffff;
3065 pThis->aBCR[BCR_STVAL] = val;
3066 if (pThis->uDevType == DEV_AM79C973)
3067 PDMDevHlpTimerSetNano(pDevIns, pThis->hTimerSoftInt, 12800U * val);
3068 break;
3069
3070 case BCR_MIIMDR:
3071 pThis->aMII[pThis->aBCR[BCR_MIIADDR] & 0x1f] = val;
3072 Log12(("#%d pcnet: mii write %d <- %#x\n", PCNET_INST_NR, pThis->aBCR[BCR_MIIADDR] & 0x1f, val));
3073 break;
3074
3075 default:
3076 break;
3077 }
3078 return VINF_SUCCESS;
3079}
3080
3081static uint32_t pcnetMIIReadU16(PPCNETSTATE pThis, uint32_t miiaddr)
3082{
3083 uint32_t val;
3084 bool autoneg, duplex, fast, isolate;
3085 STAM_COUNTER_INC(&pThis->StatMIIReads);
3086
3087 /* If the DANAS (BCR32.7) bit is set, the MAC does not do any
3088 * auto-negotiation and the PHY must be set up explicitly. DANAS
3089 * effectively disables most other BCR32 bits.
3090 */
3091 if (pThis->aBCR[BCR_MIICAS] & 0x80)
3092 {
3093 /* PHY controls auto-negotiation. */
3094 autoneg = duplex = fast = 1;
3095 }
3096 else
3097 {
3098 /* BCR32 controls auto-negotiation. */
3099 autoneg = (pThis->aBCR[BCR_MIICAS] & 0x20) != 0;
3100 duplex = (pThis->aBCR[BCR_MIICAS] & 0x10) != 0;
3101 fast = (pThis->aBCR[BCR_MIICAS] & 0x08) != 0;
3102 }
3103
3104 /* Electrically isolating the PHY mostly disables it. */
3105 isolate = (pThis->aMII[0] & RT_BIT(10)) != 0;
3106
3107 switch (miiaddr)
3108 {
3109 case 0:
3110 /* MII basic mode control register. */
3111 val = 0;
3112 if (autoneg)
3113 val |= 0x1000; /* Enable auto negotiation. */
3114 if (fast)
3115 val |= 0x2000; /* 100 Mbps */
3116 if (duplex) /* Full duplex forced */
3117 val |= 0x0100; /* Full duplex */
3118 if (isolate) /* PHY electrically isolated. */
3119 val |= 0x0400; /* Isolated */
3120 break;
3121
3122 case 1:
3123 /* MII basic mode status register. */
3124 val = 0x7800 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
3125 | 0x0040 /* Mgmt frame preamble not required. */
3126 | 0x0020 /* Auto-negotiation complete. */
3127 | 0x0008 /* Able to do auto-negotiation. */
3128 | 0x0004 /* Link up. */
3129 | 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
3130 if (!pcnetIsLinkUp(pThis) || isolate) {
3131 val &= ~(0x0020 | 0x0004);
3132 pThis->cLinkDownReported++;
3133 }
3134 if (!autoneg) {
3135 /* Auto-negotiation disabled. */
3136 val &= ~(0x0020 | 0x0008);
3137 if (duplex)
3138 /* Full duplex forced. */
3139 val &= ~0x2800;
3140 else
3141 /* Half duplex forced. */
3142 val &= ~0x5000;
3143
3144 if (fast)
3145 /* 100 Mbps forced */
3146 val &= ~0x1800;
3147 else
3148 /* 10 Mbps forced */
3149 val &= ~0x6000;
3150 }
3151 break;
3152
3153 case 2:
3154 /* PHY identifier 1. */
3155 val = 0x22; /* Am79C874/AC101 PHY */
3156 break;
3157
3158 case 3:
3159 /* PHY identifier 2. */
3160 val = 0x561b; /* Am79C874/AC101 PHY */
3161 break;
3162
3163 case 4:
3164 /* Advertisement control register. */
3165 val = 0x01e0 /* Try 100mbps FD/HD and 10mbps FD/HD. */
3166#if 0
3167 // Advertising flow control is a) not the default, and b) confuses
3168 // the link speed detection routine in Windows PCnet driver
3169 | 0x0400 /* Try flow control. */
3170#endif
3171 | 0x0001; /* CSMA selector. */
3172 break;
3173
3174 case 5:
3175 /* Link partner ability register. */
3176 if (pcnetIsLinkUp(pThis) && !isolate)
3177 val = 0x8000 /* Next page bit. */
3178 | 0x4000 /* Link partner acked us. */
3179 | 0x0400 /* Can do flow control. */
3180 | 0x01e0 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
3181 | 0x0001; /* Use CSMA selector. */
3182 else
3183 {
3184 val = 0;
3185 pThis->cLinkDownReported++;
3186 }
3187 break;
3188
3189 case 6:
3190 /* Auto negotiation expansion register. */
3191 if (pcnetIsLinkUp(pThis) && !isolate)
3192 val = 0x0008 /* Link partner supports npage. */
3193 | 0x0004 /* Enable npage words. */
3194 | 0x0001; /* Can do N-way auto-negotiation. */
3195 else
3196 {
3197 val = 0;
3198 pThis->cLinkDownReported++;
3199 }
3200 break;
3201
3202 case 18:
3203 /* Diagnostic Register (FreeBSD pcn/ac101 driver reads this). */
3204 if (pcnetIsLinkUp(pThis) && !isolate)
3205 {
3206 val = 0x0100 /* Receive PLL locked. */
3207 | 0x0200; /* Signal detected. */
3208
3209 if (autoneg)
3210 {
3211 val |= 0x0400 /* 100Mbps rate. */
3212 | 0x0800; /* Full duplex. */
3213 }
3214 else
3215 {
3216 if (fast)
3217 val |= 0x0400; /* 100Mbps rate. */
3218 if (duplex)
3219 val |= 0x0800; /* Full duplex. */
3220 }
3221 }
3222 else
3223 {
3224 val = 0;
3225 pThis->cLinkDownReported++;
3226 }
3227 break;
3228
3229 default:
3230 val = 0;
3231 break;
3232 }
3233
3234 Log12(("#%d pcnet: mii read %d -> %#x\n", PCNET_INST_NR, miiaddr, val));
3235 return val;
3236}
3237
3238static uint32_t pcnetBCRReadU16(PPCNETSTATE pThis, uint32_t u32RAP)
3239{
3240 uint32_t val;
3241 u32RAP &= 0x7f;
3242 switch (u32RAP)
3243 {
3244 case BCR_LNKST:
3245 case BCR_LED1:
3246 case BCR_LED2:
3247 case BCR_LED3:
3248 val = pThis->aBCR[u32RAP] & ~0x8000;
3249 /* Clear LNKSTE if we're not connected or if we've just loaded a VM state. */
3250 if (!pThis->fDriverAttached || pThis->fLinkTempDown || !pThis->fLinkUp)
3251 {
3252 if (u32RAP == 4)
3253 pThis->cLinkDownReported++;
3254 val &= ~0x40;
3255 }
3256 /* AMD NDIS 5.0 driver programs BCR4 to indicate link state and polls
3257 * the LED bit (bit 15) to determine current link status.
3258 */
3259 val |= (val & 0x017f & pThis->u32Lnkst) ? 0x8000 : 0;
3260 break;
3261
3262 case BCR_MIIMDR:
3263 if ((pThis->uDevType == DEV_AM79C973) && (pThis->aBCR[BCR_MIIADDR] >> 5 & 0x1f) == 0)
3264 {
3265 uint32_t miiaddr = pThis->aBCR[BCR_MIIADDR] & 0x1f;
3266 val = pcnetMIIReadU16(pThis, miiaddr);
3267 }
3268 else
3269 val = 0xffff;
3270 break;
3271
3272 default:
3273 val = u32RAP < BCR_MAX_RAP ? pThis->aBCR[u32RAP] : 0;
3274 break;
3275 }
3276 Log7(("#%d pcnetBCRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3277 return val;
3278}
3279
3280#ifdef IN_RING3 /* move down */
3281static void pcnetR3HardReset(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
3282{
3283 int i;
3284 uint16_t checksum;
3285
3286 /* Lower any raised interrupts, see @bugref(9556) */
3287 if (RT_UNLIKELY(pThis->iISR))
3288 {
3289 pThis->iISR = 0;
3290 if (!PCNET_IS_ISA(pThis))
3291 {
3292 Log(("#%d INTA=%d\n", PCNET_INST_NR, pThis->iISR));
3293 PDMDevHlpPCISetIrq(pDevIns, 0, pThis->iISR);
3294 }
3295 else
3296 {
3297 Log(("#%d IRQ=%d, state=%d\n", PCNET_INST_NR, pThis->uIsaIrq, pThis->iISR));
3298 PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, pThis->iISR);
3299 }
3300 }
3301 /* Initialize the PROM */
3302 Assert(sizeof(pThis->MacConfigured) == 6);
3303 memcpy(pThis->aPROM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
3304 pThis->aPROM[ 8] = 0x00;
3305 pThis->aPROM[12] = pThis->aPROM[13] = 0x00;
3306 if (pThis->uDevType == DEV_AM79C960_EB)
3307 {
3308 pThis->aPROM[14] = 0x52;
3309 pThis->aPROM[15] = 0x44; /* NI6510 EtherBlaster 'RD' signature. */
3310 }
3311 else
3312 pThis->aPROM[14] = pThis->aPROM[15] = 0x57; /* NE2100 'WW' signature. */
3313
3314 /* 0x00/0xFF=ISA, 0x01=PnP, 0x10=VLB, 0x11=PCI */
3315 if (PCNET_IS_PCI(pThis))
3316 pThis->aPROM[ 9] = 0x11;
3317 else
3318 pThis->aPROM[ 9] = 0x00;
3319
3320 for (i = 0, checksum = 0; i < 16; i++)
3321 checksum += pThis->aPROM[i];
3322 *(uint16_t *)&pThis->aPROM[12] = RT_H2LE_U16(checksum);
3323
3324 /* Many of the BCR values would normally be read from the EEPROM. */
3325 pThis->aBCR[BCR_MSRDA] = 0x0005;
3326 pThis->aBCR[BCR_MSWRA] = 0x0005;
3327 pThis->aBCR[BCR_MC ] = 0x0002;
3328 pThis->aBCR[BCR_LNKST] = 0x00c0;
3329 pThis->aBCR[BCR_LED1 ] = 0x0084;
3330 pThis->aBCR[BCR_LED2 ] = 0x0088;
3331 pThis->aBCR[BCR_LED3 ] = 0x0090;
3332 /* For ISA PnP cards, BCR8 reports IRQ/DMA (e.g. 0x0035 means IRQ 3, DMA 5). */
3333 pThis->aBCR[BCR_FDC ] = 0x0000;
3334 pThis->aBCR[BCR_BSBC ] = 0x9001;
3335 pThis->aBCR[BCR_EECAS] = 0x0002;
3336 pThis->aBCR[BCR_STVAL] = 0xffff;
3337 pThis->aCSR[58 ] = /* CSR58 is an alias for BCR20 */
3338 pThis->aBCR[BCR_SWS ] = 0x0200;
3339 pThis->iLog2DescSize = 3;
3340 pThis->aBCR[BCR_PLAT ] = 0xff06;
3341 pThis->aBCR[BCR_MIICAS ] = 0x20; /* Auto-negotiation on. */
3342 pThis->aBCR[BCR_MIIADDR ] = 0; /* Internal PHY on Am79C973 would be (0x1e << 5) */
3343 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
3344 pThis->aBCR[BCR_PCIVID] = PCIDevGetVendorId(pPciDev);
3345 pThis->aBCR[BCR_PCISID] = PCIDevGetSubSystemId(pPciDev);
3346 pThis->aBCR[BCR_PCISVID] = PCIDevGetSubSystemVendorId(pPciDev);
3347
3348 /* Reset the error counter. */
3349 pThis->uCntBadRMD = 0;
3350
3351 pcnetSoftReset(pThis);
3352}
3353#endif /* IN_RING3 */
3354
3355
3356/* -=-=-=-=-=- APROM I/O Port access -=-=-=-=-=- */
3357
3358static void pcnetAPROMWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3359{
3360 addr &= 0x0f;
3361 val &= 0xff;
3362 Log(("#%d pcnetAPROMWriteU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3363 /* Check APROMWE bit to enable write access */
3364 if (pcnetBCRReadU16(pThis, 2) & 0x80)
3365 pThis->aPROM[addr] = val;
3366}
3367
3368static uint32_t pcnetAPROMReadU8(PPCNETSTATE pThis, uint32_t addr)
3369{
3370 uint32_t val = pThis->aPROM[addr &= 0x0f];
3371 Log(("#%d pcnetAPROMReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3372 return val;
3373}
3374
3375/**
3376 * @callback_method_impl{FNIOMIOPORTIN, APROM}
3377 */
3378static DECLCALLBACK(VBOXSTRICTRC)
3379pcnetIOPortAPromRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3380{
3381 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3382 VBOXSTRICTRC rc = VINF_SUCCESS;
3383 STAM_PROFILE_ADV_START(&pThis->StatAPROMRead, a);
3384 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3385 RT_NOREF_PV(pvUser);
3386
3387 /* FreeBSD is accessing in dwords. */
3388 if (cb == 1)
3389 *pu32 = pcnetAPROMReadU8(pThis, offPort);
3390 else if (cb == 2 && !BCR_DWIO(pThis))
3391 *pu32 = pcnetAPROMReadU8(pThis, offPort)
3392 | (pcnetAPROMReadU8(pThis, offPort + 1) << 8);
3393 else if (cb == 4 && BCR_DWIO(pThis))
3394 *pu32 = pcnetAPROMReadU8(pThis, offPort)
3395 | (pcnetAPROMReadU8(pThis, offPort + 1) << 8)
3396 | (pcnetAPROMReadU8(pThis, offPort + 2) << 16)
3397 | (pcnetAPROMReadU8(pThis, offPort + 3) << 24);
3398 else
3399 {
3400 Log(("#%d pcnetIOPortAPromRead: offPort=%RTiop cb=%d BCR_DWIO !!\n", PCNET_INST_NR, offPort, cb));
3401 rc = VERR_IOM_IOPORT_UNUSED;
3402 }
3403
3404 STAM_PROFILE_ADV_STOP(&pThis->StatAPROMRead, a);
3405 LogFlow(("#%d pcnetIOPortAPromRead: offPort=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n",
3406 PCNET_INST_NR, offPort, *pu32, cb, VBOXSTRICTRC_VAL(rc)));
3407 return rc;
3408}
3409
3410
3411/**
3412 * @callback_method_impl{FNIOMIOPORTOUT, APROM}
3413 */
3414static DECLCALLBACK(VBOXSTRICTRC)
3415pcnetIoPortAPromWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3416{
3417 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3418 VBOXSTRICTRC rc = VINF_SUCCESS;
3419 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3420 RT_NOREF_PV(pvUser);
3421
3422 if (cb == 1)
3423 {
3424 STAM_PROFILE_ADV_START(&pThis->StatAPROMWrite, a);
3425 pcnetAPROMWriteU8(pThis, offPort, u32);
3426 STAM_PROFILE_ADV_STOP(&pThis->StatAPROMWrite, a);
3427 }
3428 else
3429 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32);
3430
3431 LogFlow(("#%d pcnetIoPortAPromWrite: offPort=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n",
3432 PCNET_INST_NR, offPort, u32, cb, VBOXSTRICTRC_VAL(rc)));
3433 return rc;
3434}
3435
3436
3437/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
3438
3439
3440static VBOXSTRICTRC pcnetIoPortWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3441{
3442 RT_NOREF1(val);
3443 Log6(("#%d pcnetIoPortWriteU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3444 if (RT_LIKELY(!BCR_DWIO(pThis)))
3445 {
3446 switch (addr & 0x0f)
3447 {
3448 case 0x04: /* RESET */
3449 break;
3450 }
3451 }
3452 else
3453 Log(("#%d pcnetIoPortWriteU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3454
3455 return VINF_SUCCESS;
3456}
3457
3458static VBOXSTRICTRC pcnetIoPortReadU8(PPDMDEVINS pDevIns, PPCNETSTATE pThis, uint32_t addr, uint32_t *val)
3459{
3460 *val = UINT32_MAX;
3461
3462 if (RT_LIKELY(!BCR_DWIO(pThis)))
3463 {
3464 switch (addr & 0x0f)
3465 {
3466 case 0x04: /* RESET */
3467 pcnetSoftReset(pThis);
3468 *val = 0;
3469 break;
3470 }
3471 }
3472 else
3473 Log(("#%d pcnetIoPortReadU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, *val & 0xff));
3474
3475 pcnetUpdateIrq(pDevIns, pThis);
3476
3477 Log6(("#%d pcnetIoPortReadU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, *val & 0xff));
3478 return VINF_SUCCESS;
3479}
3480
3481static VBOXSTRICTRC pcnetIoPortWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t val)
3482{
3483 VBOXSTRICTRC rc = VINF_SUCCESS;
3484
3485 Log6(("#%d pcnetIoPortWriteU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3486 if (RT_LIKELY(!BCR_DWIO(pThis)))
3487 {
3488 switch (addr & 0x0f)
3489 {
3490 case 0x00: /* RDP */
3491 pcnetPollTimer(pDevIns, pThis, pThisCC);
3492 rc = pcnetCSRWriteU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val);
3493 pcnetUpdateIrq(pDevIns, pThis);
3494 break;
3495 case 0x02: /* RAP */
3496 pThis->u32RAP = val & 0x7f;
3497 break;
3498 case 0x06: /* BDP */
3499 rc = pcnetBCRWriteU16(pDevIns, pThis, pThis->u32RAP, val);
3500 break;
3501 }
3502 }
3503 else
3504 Log(("#%d pcnetIoPortWriteU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3505
3506 return rc;
3507}
3508
3509static VBOXSTRICTRC pcnetIoPortReadU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t *val)
3510{
3511 VBOXSTRICTRC rc = VINF_SUCCESS;
3512
3513 *val = ~0U;
3514
3515 if (RT_LIKELY(!BCR_DWIO(pThis)))
3516 {
3517 switch (addr & 0x0f)
3518 {
3519 case 0x00: /* RDP */
3520 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3521 /** Polling is then useless here and possibly expensive. */
3522 if (!CSR_DPOLL(pThis))
3523 pcnetPollTimer(pDevIns, pThis, pThisCC);
3524
3525 rc = pcnetCSRReadU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val);
3526 if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3527 goto skip_update_irq;
3528 break;
3529 case 0x02: /* RAP */
3530 *val = pThis->u32RAP;
3531 goto skip_update_irq;
3532 case 0x04: /* RESET */
3533 pcnetSoftReset(pThis);
3534 *val = 0;
3535 break;
3536 case 0x06: /* BDP */
3537 *val = pcnetBCRReadU16(pThis, pThis->u32RAP);
3538 break;
3539 }
3540 }
3541 else
3542 Log(("#%d pcnetIoPortReadU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, *val & 0xffff));
3543
3544 pcnetUpdateIrq(pDevIns, pThis);
3545
3546skip_update_irq:
3547 Log6(("#%d pcnetIoPortReadU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, *val & 0xffff));
3548 return rc;
3549}
3550
3551static VBOXSTRICTRC pcnetIoPortWriteU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t val)
3552{
3553 VBOXSTRICTRC rc = VINF_SUCCESS;
3554
3555 Log6(("#%d pcnetIoPortWriteU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
3556 if (RT_LIKELY(BCR_DWIO(pThis)))
3557 {
3558 switch (addr & 0x0f)
3559 {
3560 case 0x00: /* RDP */
3561 pcnetPollTimer(pDevIns, pThis, pThisCC);
3562 rc = pcnetCSRWriteU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val & 0xffff);
3563 pcnetUpdateIrq(pDevIns, pThis);
3564 break;
3565 case 0x04: /* RAP */
3566 pThis->u32RAP = val & 0x7f;
3567 break;
3568 case 0x0c: /* BDP */
3569 rc = pcnetBCRWriteU16(pDevIns, pThis, pThis->u32RAP, val & 0xffff);
3570 break;
3571 }
3572 }
3573 else if ((addr & 0x0f) == 0)
3574 {
3575 /* switch device to dword I/O mode */
3576 pcnetBCRWriteU16(pDevIns, pThis, BCR_BSBC, pcnetBCRReadU16(pThis, BCR_BSBC) | 0x0080);
3577 Log6(("device switched into dword i/o mode\n"));
3578 }
3579 else
3580 Log(("#%d pcnetIoPortWriteU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3581
3582 return rc;
3583}
3584
3585static VBOXSTRICTRC pcnetIoPortReadU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t *val)
3586{
3587 VBOXSTRICTRC rc = VINF_SUCCESS;
3588
3589 *val = ~0U;
3590
3591 if (RT_LIKELY(BCR_DWIO(pThis)))
3592 {
3593 switch (addr & 0x0f)
3594 {
3595 case 0x00: /* RDP */
3596 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3597 /** Polling is then useless here and possibly expensive. */
3598 if (!CSR_DPOLL(pThis))
3599 pcnetPollTimer(pDevIns, pThis, pThisCC);
3600
3601 rc = pcnetCSRReadU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val);
3602 if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3603 goto skip_update_irq;
3604 break;
3605 case 0x04: /* RAP */
3606 *val = pThis->u32RAP;
3607 goto skip_update_irq;
3608 case 0x08: /* RESET */
3609 pcnetSoftReset(pThis);
3610 *val = 0;
3611 break;
3612 case 0x0c: /* BDP */
3613 *val = pcnetBCRReadU16(pThis, pThis->u32RAP);
3614 break;
3615 }
3616 }
3617 else
3618 Log(("#%d pcnetIoPortReadU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, *val));
3619 pcnetUpdateIrq(pDevIns, pThis);
3620
3621skip_update_irq:
3622 Log6(("#%d pcnetIoPortReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, *val));
3623 return rc;
3624}
3625
3626
3627/**
3628 * @callback_method_impl{FNIOMIOPORTIN}
3629 */
3630static DECLCALLBACK(VBOXSTRICTRC) pcnetIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3631{
3632 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3633 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
3634 VBOXSTRICTRC rc;
3635 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
3636 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3637 RT_NOREF_PV(pvUser);
3638
3639 switch (cb)
3640 {
3641 case 1: rc = pcnetIoPortReadU8(pDevIns, pThis, offPort, pu32); break;
3642 case 2: rc = pcnetIoPortReadU16(pDevIns, pThis, pThisCC, offPort, pu32); break;
3643 case 4: rc = pcnetIoPortReadU32(pDevIns, pThis, pThisCC, offPort, pu32); break;
3644 default:
3645 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetIoPortRead: unsupported op size: offset=%#10x cb=%u\n", offPort, cb);
3646 }
3647
3648 Log2(("#%d pcnetIoPortRead: offPort=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, offPort, *pu32, cb, VBOXSTRICTRC_VAL(rc)));
3649 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
3650 return rc;
3651}
3652
3653
3654/**
3655 * @callback_method_impl{FNIOMIOPORTOUT}
3656 */
3657static DECLCALLBACK(VBOXSTRICTRC) pcnetIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3658{
3659 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3660 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
3661 VBOXSTRICTRC rc;
3662 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3663 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3664 RT_NOREF_PV(pvUser);
3665
3666 switch (cb)
3667 {
3668 case 1: rc = pcnetIoPortWriteU8(pThis, offPort, u32); break;
3669 case 2: rc = pcnetIoPortWriteU16(pDevIns, pThis, pThisCC, offPort, u32); break;
3670 case 4: rc = pcnetIoPortWriteU32(pDevIns, pThis, pThisCC, offPort, u32); break;
3671 default:
3672 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetIoPortWrite: unsupported op size: offset=%#10x cb=%u\n", offPort, cb);
3673 }
3674
3675 Log2(("#%d pcnetIoPortWrite: offPort=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, offPort, u32, cb, VBOXSTRICTRC_VAL(rc)));
3676 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3677 return rc;
3678}
3679
3680
3681/* -=-=-=-=-=- MMIO -=-=-=-=-=- */
3682
3683#ifdef IN_RING3
3684
3685static void pcnetR3MmioWriteU8(PPCNETSTATE pThis, RTGCPHYS off, uint32_t val)
3686{
3687 Log6(("#%d pcnetR3MmioWriteU8: off=%#010x val=%#04x\n", PCNET_INST_NR, off, val));
3688 if (!(off & 0x10))
3689 pcnetAPROMWriteU8(pThis, off, val);
3690}
3691
3692static VBOXSTRICTRC pcnetR3MmioReadU8(PPCNETSTATE pThis, RTGCPHYS addr, uint8_t *val)
3693{
3694 *val = 0xff;
3695 if (!(addr & 0x10))
3696 *val = pcnetAPROMReadU8(pThis, addr);
3697 Log6(("#%d pcnetR3MmioReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, *val));
3698 return VINF_SUCCESS;
3699}
3700
3701static VBOXSTRICTRC pcnetR3MmioWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS off, uint32_t val)
3702{
3703 VBOXSTRICTRC rcStrict;
3704 Log6(("#%d pcnetR3MmioWriteU16: off=%#010x val=%#06x\n", PCNET_INST_NR, off, val));
3705 if (off & 0x10)
3706 {
3707 rcStrict = pcnetIoPortWriteU16(pDevIns, pThis, pThisCC, off & 0x0f, val);
3708 if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
3709 rcStrict = VINF_IOM_R3_MMIO_WRITE;
3710 }
3711 else
3712 {
3713 pcnetAPROMWriteU8(pThis, off, val );
3714 pcnetAPROMWriteU8(pThis, off + 1, val >> 8);
3715 rcStrict = VINF_SUCCESS;
3716 }
3717 return rcStrict;
3718}
3719
3720static VBOXSTRICTRC pcnetR3MmioReadU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS addr, uint16_t *val)
3721{
3722 VBOXSTRICTRC rcStrict;
3723 uint32_t val32 = ~0U;
3724
3725 if (addr & 0x10)
3726 {
3727 rcStrict = pcnetIoPortReadU16(pDevIns, pThis, pThisCC, addr & 0x0f, &val32);
3728 if (rcStrict == VINF_IOM_R3_IOPORT_READ)
3729 rcStrict = VINF_IOM_R3_MMIO_READ;
3730 }
3731 else
3732 {
3733 val32 = pcnetAPROMReadU8(pThis, addr+1);
3734 val32 <<= 8;
3735 val32 |= pcnetAPROMReadU8(pThis, addr);
3736 rcStrict = VINF_SUCCESS;
3737 }
3738 *val = (uint16_t)val32;
3739 Log6(("#%d pcnetR3MmioReadU16: addr=%#010x val = %#06x\n", PCNET_INST_NR, addr, *val));
3740 return rcStrict;
3741}
3742
3743static VBOXSTRICTRC pcnetR3MmioWriteU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS off, uint32_t val)
3744{
3745 VBOXSTRICTRC rcStrict;
3746 Log6(("#%d pcnetR3MmioWriteU32: off=%#010x val=%#010x\n", PCNET_INST_NR, off, val));
3747 if (off & 0x10)
3748 {
3749 rcStrict = pcnetIoPortWriteU32(pDevIns, pThis, pThisCC, off & 0x0f, val);
3750 if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
3751 rcStrict = VINF_IOM_R3_MMIO_WRITE;
3752 }
3753 else
3754 {
3755 pcnetAPROMWriteU8(pThis, off, val );
3756 pcnetAPROMWriteU8(pThis, off + 1, val >> 8);
3757 pcnetAPROMWriteU8(pThis, off + 2, val >> 16);
3758 pcnetAPROMWriteU8(pThis, off + 3, val >> 24);
3759 rcStrict = VINF_SUCCESS;
3760 }
3761 return rcStrict;
3762}
3763
3764static VBOXSTRICTRC pcnetR3MmioReadU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS addr, uint32_t *val)
3765{
3766 VBOXSTRICTRC rcStrict;
3767
3768 if (addr & 0x10)
3769 {
3770 rcStrict = pcnetIoPortReadU32(pDevIns, pThis, pThisCC, addr & 0x0f, val);
3771 if (rcStrict == VINF_IOM_R3_IOPORT_READ)
3772 rcStrict = VINF_IOM_R3_MMIO_READ;
3773 }
3774 else
3775 {
3776 uint32_t val32;
3777
3778 val32 = pcnetAPROMReadU8(pThis, addr+3);
3779 val32 <<= 8;
3780 val32 |= pcnetAPROMReadU8(pThis, addr+2);
3781 val32 <<= 8;
3782 val32 |= pcnetAPROMReadU8(pThis, addr+1);
3783 val32 <<= 8;
3784 val32 |= pcnetAPROMReadU8(pThis, addr );
3785 *val = val32;
3786 rcStrict = VINF_SUCCESS;
3787 }
3788 Log6(("#%d pcnetR3MmioReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, *val));
3789 return rcStrict;
3790}
3791
3792
3793/**
3794 * @callback_method_impl{FNIOMMMIONEWREAD}
3795 */
3796static DECLCALLBACK(VBOXSTRICTRC) pcnetR3MmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3797{
3798 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3799 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
3800 VBOXSTRICTRC rc = VINF_SUCCESS;
3801 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3802 RT_NOREF_PV(pvUser);
3803
3804 /*
3805 * We have to check the range, because we're page aligning the MMIO.
3806 */
3807 if (off < PCNET_PNPMMIO_SIZE)
3808 {
3809 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIORead), a);
3810 switch (cb)
3811 {
3812 case 1: rc = pcnetR3MmioReadU8 (pThis, off, (uint8_t *)pv); break;
3813 case 2: rc = pcnetR3MmioReadU16(pDevIns, pThis, pThisCC, off, (uint16_t *)pv); break;
3814 case 4: rc = pcnetR3MmioReadU32(pDevIns, pThis, pThisCC, off, (uint32_t *)pv); break;
3815 default:
3816 memset(pv, 0, cb);
3817 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetR3MmioRead: unsupported op size: address=%RGp cb=%u\n", off, cb);
3818 break;
3819 }
3820 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIORead), a);
3821 }
3822 else
3823 memset(pv, 0, cb);
3824
3825 LogFlow(("#%d pcnetR3MmioRead: pvUser=%p:{%.*Rhxs} cb=%d off=%RGp rc=%Rrc\n",
3826 PCNET_INST_NR, pv, cb, pv, cb, off, VBOXSTRICTRC_VAL(rc)));
3827 return rc;
3828}
3829
3830
3831/**
3832 * @callback_method_impl{FNIOMMMIONEWWRITE}
3833 */
3834static DECLCALLBACK(VBOXSTRICTRC) pcnetR3MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3835{
3836 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3837 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
3838 VBOXSTRICTRC rc = VINF_SUCCESS;
3839 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3840 RT_NOREF_PV(pvUser);
3841
3842 /*
3843 * We have to check the range, because we're page aligning the MMIO stuff presently.
3844 */
3845 if (off < PCNET_PNPMMIO_SIZE)
3846 {
3847 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3848 switch (cb)
3849 {
3850 case 1: pcnetR3MmioWriteU8(pThis, off, *(uint8_t *)pv); break;
3851 case 2: rc = pcnetR3MmioWriteU16(pDevIns, pThis, pThisCC, off, *(uint16_t *)pv); break;
3852 case 4: rc = pcnetR3MmioWriteU32(pDevIns, pThis, pThisCC, off, *(uint32_t *)pv); break;
3853 default:
3854 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetR3MmioWrite: unsupported op size: address=%RGp cb=%u\n", off, cb);
3855 }
3856
3857 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3858 }
3859 LogFlow(("#%d pcnetR3MmioWrite: pvUser=%p:{%.*Rhxs} cb=%d off=%RGp rc=%Rrc\n",
3860 PCNET_INST_NR, pv, cb, pv, cb, off, VBOXSTRICTRC_VAL(rc)));
3861 return rc;
3862}
3863
3864
3865
3866/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
3867
3868/**
3869 * @callback_method_impl{FNTMTIMERDEV, Poll timer}
3870 */
3871static DECLCALLBACK(void) pcnetR3Timer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
3872{
3873 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3874 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
3875 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3876 Assert(hTimer == pThis->hTimerPoll); RT_NOREF(pvUser, hTimer);
3877
3878 STAM_PROFILE_ADV_START(&pThis->StatTimer, a);
3879 pcnetPollTimer(pDevIns, pThis, pThisCC);
3880 STAM_PROFILE_ADV_STOP(&pThis->StatTimer, a);
3881}
3882
3883
3884/**
3885 * @callback_method_impl{FNTMTIMERDEV,
3886 * Software interrupt timer callback function.}
3887 */
3888static DECLCALLBACK(void) pcnetR3TimerSoftInt(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
3889{
3890 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3891 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3892 Assert(hTimer == pThis->hTimerSoftInt); RT_NOREF(pvUser, hTimer);
3893
3894 pThis->aCSR[7] |= 0x0800; /* STINT */
3895 pcnetUpdateIrq(pDevIns, pThis);
3896 PDMDevHlpTimerSetNano(pDevIns, pThis->hTimerSoftInt, 12800U * (pThis->aBCR[BCR_STVAL] & 0xffff));
3897}
3898
3899
3900/**
3901 * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
3902 *
3903 * This is only called when we restore a saved state and temporarily
3904 * disconnected the network link to inform the guest that network connections
3905 * should be considered lost.
3906 */
3907static DECLCALLBACK(void) pcnetR3TimerRestore(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
3908{
3909 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3910 Assert(hTimer == pThis->hTimerRestore); RT_NOREF(pvUser);
3911
3912 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
3913 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
3914
3915 rc = VERR_GENERAL_FAILURE;
3916
3917 /* 10 Mbps models (up to and including Am79C970A) have no MII and no way to get
3918 * an MII management auto-poll interrupt (MAPINT) indicating link state changes.
3919 * In some cases we want to make sure the guest really noticed the link going down;
3920 * the cLinkDownReported counter is incremented every time the guest did something
3921 * that might have made it notice the link loss, and we only bring the link back
3922 * up once we're reasonably certain the guest knows it was down.
3923 */
3924 if (pThis->cLinkDownReported <= PCNET_MAX_LINKDOWN_REPORTED)
3925 {
3926 rc = PDMDevHlpTimerSetMillies(pDevIns, hTimer, 1500);
3927 AssertRC(rc);
3928 }
3929 if (RT_FAILURE(rc))
3930 {
3931 pThis->fLinkTempDown = false;
3932 if (pThis->fLinkUp)
3933 {
3934 LogRel(("PCnet#%d: The link is back up again after the restore.\n", pDevIns->iInstance));
3935 Log(("#%d pcnetR3TimerRestore: Clearing ERR and CERR after load. cLinkDownReported=%d\n",
3936 pDevIns->iInstance, pThis->cLinkDownReported));
3937 pThis->aCSR[0] &= ~(RT_BIT(15) | RT_BIT(13)); /* ERR | CERR - probably not 100% correct either... */
3938 pThis->Led.Actual.s.fError = 0;
3939 }
3940 }
3941 else
3942 Log(("#%d pcnetR3TimerRestore: cLinkDownReported=%d, wait another 1500ms...\n",
3943 pDevIns->iInstance, pThis->cLinkDownReported));
3944
3945 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
3946}
3947
3948
3949/* -=-=-=-=-=- PCI Device Callbacks -=-=-=-=-=- */
3950
3951/**
3952 * @callback_method_impl{FNPCIIOREGIONMAP, For the PCnet I/O Ports.}
3953 */
3954static DECLCALLBACK(int) pcnetR3PciMapUnmapIoPorts(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
3955 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
3956{
3957 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3958 int rc;
3959 RT_NOREF(iRegion, cb, enmType, pPciDev);
3960
3961 Assert(pDevIns->apPciDevs[0] == pPciDev);
3962 Assert(enmType == PCI_ADDRESS_SPACE_IO);
3963 Assert(cb >= 0x20);
3964
3965 if (GCPhysAddress != NIL_RTGCPHYS)
3966 {
3967 RTIOPORT Port = (RTIOPORT)GCPhysAddress;
3968 rc = PDMDevHlpIoPortMap(pDevIns, pThis->hIoPortsPciAProm, Port);
3969 AssertRCReturn(rc, rc);
3970 rc = PDMDevHlpIoPortMap(pDevIns, pThis->hIoPortsPci, Port + 0x10);
3971 AssertRCReturn(rc, rc);
3972 pThis->IOPortBase = Port;
3973 }
3974 else
3975 {
3976 rc = PDMDevHlpIoPortUnmap(pDevIns, pThis->hIoPortsPciAProm);
3977 AssertRCReturn(rc, rc);
3978 rc = PDMDevHlpIoPortUnmap(pDevIns, pThis->hIoPortsPci);
3979 AssertRCReturn(rc, rc);
3980 pThis->IOPortBase = 0;
3981 }
3982
3983 return VINF_SUCCESS;
3984}
3985
3986
3987/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
3988
3989/**
3990 * @callback_method_impl{FNDBGFHANDLERDEV}
3991 */
3992static DECLCALLBACK(void) pcnetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3993{
3994 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3995 bool fRcvRing = false;
3996 bool fXmtRing = false;
3997 bool fAPROM = false;
3998
3999 /*
4000 * Parse args.
4001 */
4002 if (pszArgs)
4003 {
4004 fRcvRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "rcv");
4005 fXmtRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "xmt");
4006 fAPROM = strstr(pszArgs, "verbose") || strstr(pszArgs, "aprom");
4007 }
4008
4009 /*
4010 * Show info.
4011 */
4012 const char *pcszModel;
4013 switch (pThis->uDevType)
4014 {
4015 case DEV_AM79C970A: pcszModel = "AM79C970A"; break;
4016 case DEV_AM79C973: pcszModel = "AM79C973"; break;
4017 case DEV_AM79C960: pcszModel = "AM79C960/NE2100"; break;
4018 case DEV_AM79C960_EB: pcszModel = "AM79C960/EtherBlaster"; break;
4019 default: pcszModel = "Unknown"; break;
4020 }
4021 pHlp->pfnPrintf(pHlp,
4022 "pcnet #%d: port=%RTiop", pDevIns->iInstance, pThis->IOPortBase);
4023 if (PCNET_IS_ISA(pThis))
4024 pHlp->pfnPrintf(pHlp, " irq=%RX32", pThis->uIsaIrq);
4025 else
4026 pHlp->pfnPrintf(pHlp, " mmio=%RX32", PDMDevHlpMmioGetMappingAddress(pDevIns, pThis->hMmioPci));
4027
4028 pHlp->pfnPrintf(pHlp,
4029 " mac-cfg=%RTmac %s%s%s\n",
4030 &pThis->MacConfigured, pcszModel, pDevIns->fRCEnabled ? " RC" : "", pDevIns->fR0Enabled ? " R0" : "");
4031
4032
4033 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
4034 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4035
4036 pHlp->pfnPrintf(pHlp,
4037 "CSR0=%#06x: INIT=%d STRT=%d STOP=%d TDMD=%d TXON=%d RXON=%d IENA=%d INTR=%d IDON=%d TINT=%d RINT=%d MERR=%d\n"
4038 " MISS=%d CERR=%d BABL=%d ERR=%d\n",
4039 pThis->aCSR[0],
4040 !!(pThis->aCSR[0] & RT_BIT(0)), !!(pThis->aCSR[0] & RT_BIT(1)), !!(pThis->aCSR[0] & RT_BIT(2)), !!(pThis->aCSR[0] & RT_BIT(3)), !!(pThis->aCSR[0] & RT_BIT(4)),
4041 !!(pThis->aCSR[0] & RT_BIT(5)), !!(pThis->aCSR[0] & RT_BIT(6)), !!(pThis->aCSR[0] & RT_BIT(7)), !!(pThis->aCSR[0] & RT_BIT(8)), !!(pThis->aCSR[0] & RT_BIT(9)),
4042 !!(pThis->aCSR[0] & RT_BIT(10)), !!(pThis->aCSR[0] & RT_BIT(11)), !!(pThis->aCSR[0] & RT_BIT(12)), !!(pThis->aCSR[0] & RT_BIT(13)),
4043 !!(pThis->aCSR[0] & RT_BIT(14)), !!(pThis->aCSR[0] & RT_BIT(15)));
4044
4045 pHlp->pfnPrintf(pHlp,
4046 "CSR1=%#06x:\n",
4047 pThis->aCSR[1]);
4048
4049 pHlp->pfnPrintf(pHlp,
4050 "CSR2=%#06x:\n",
4051 pThis->aCSR[2]);
4052
4053 pHlp->pfnPrintf(pHlp,
4054 "CSR3=%#06x: BSWP=%d EMBA=%d DXMT2PD=%d LAPPEN=%d DXSUFLO=%d IDONM=%d TINTM=%d RINTM=%d MERRM=%d MISSM=%d BABLM=%d\n",
4055 pThis->aCSR[3],
4056 !!(pThis->aCSR[3] & RT_BIT(2)), !!(pThis->aCSR[3] & RT_BIT(3)), !!(pThis->aCSR[3] & RT_BIT(4)), CSR_LAPPEN(pThis),
4057 CSR_DXSUFLO(pThis), !!(pThis->aCSR[3] & RT_BIT(8)), !!(pThis->aCSR[3] & RT_BIT(9)), !!(pThis->aCSR[3] & RT_BIT(10)),
4058 !!(pThis->aCSR[3] & RT_BIT(11)), !!(pThis->aCSR[3] & RT_BIT(12)), !!(pThis->aCSR[3] & RT_BIT(14)));
4059
4060 pHlp->pfnPrintf(pHlp,
4061 "CSR4=%#06x: JABM=%d JAB=%d TXSTRM=%d TXSTRT=%d RCVCOOM=%d RCVCCO=%d UINT=%d UINTCMD=%d\n"
4062 " MFCOM=%d MFCO=%d ASTRP_RCV=%d APAD_XMT=%d DPOLL=%d TIMER=%d EMAPLUS=%d EN124=%d\n",
4063 pThis->aCSR[4],
4064 !!(pThis->aCSR[4] & RT_BIT( 0)), !!(pThis->aCSR[4] & RT_BIT( 1)), !!(pThis->aCSR[4] & RT_BIT( 2)), !!(pThis->aCSR[4] & RT_BIT( 3)),
4065 !!(pThis->aCSR[4] & RT_BIT( 4)), !!(pThis->aCSR[4] & RT_BIT( 5)), !!(pThis->aCSR[4] & RT_BIT( 6)), !!(pThis->aCSR[4] & RT_BIT( 7)),
4066 !!(pThis->aCSR[4] & RT_BIT( 8)), !!(pThis->aCSR[4] & RT_BIT( 9)), !!(pThis->aCSR[4] & RT_BIT(10)), !!(pThis->aCSR[4] & RT_BIT(11)),
4067 !!(pThis->aCSR[4] & RT_BIT(12)), !!(pThis->aCSR[4] & RT_BIT(13)), !!(pThis->aCSR[4] & RT_BIT(14)), !!(pThis->aCSR[4] & RT_BIT(15)));
4068
4069 pHlp->pfnPrintf(pHlp,
4070 "CSR5=%#06x:\n",
4071 pThis->aCSR[5]);
4072
4073 pHlp->pfnPrintf(pHlp,
4074 "CSR6=%#06x: RLEN=%#x* TLEN=%#x* [* encoded]\n",
4075 pThis->aCSR[6],
4076 (pThis->aCSR[6] >> 8) & 0xf, (pThis->aCSR[6] >> 12) & 0xf);
4077
4078 pHlp->pfnPrintf(pHlp,
4079 "CSR8..11=%#06x,%#06x,%#06x,%#06x: LADRF=%#018llx\n",
4080 pThis->aCSR[8], pThis->aCSR[9], pThis->aCSR[10], pThis->aCSR[11],
4081 (uint64_t)(pThis->aCSR[ 8] & 0xffff)
4082 | (uint64_t)(pThis->aCSR[ 9] & 0xffff) << 16
4083 | (uint64_t)(pThis->aCSR[10] & 0xffff) << 32
4084 | (uint64_t)(pThis->aCSR[11] & 0xffff) << 48);
4085
4086 pHlp->pfnPrintf(pHlp,
4087 "CSR12..14=%#06x,%#06x,%#06x: PADR=%02x:%02x:%02x:%02x:%02x:%02x (Current MAC Address)\n",
4088 pThis->aCSR[12], pThis->aCSR[13], pThis->aCSR[14],
4089 pThis->aCSR[12] & 0xff,
4090 (pThis->aCSR[12] >> 8) & 0xff,
4091 pThis->aCSR[13] & 0xff,
4092 (pThis->aCSR[13] >> 8) & 0xff,
4093 pThis->aCSR[14] & 0xff,
4094 (pThis->aCSR[14] >> 8) & 0xff);
4095
4096 pHlp->pfnPrintf(pHlp,
4097 "CSR15=%#06x: DXR=%d DTX=%d LOOP=%d DXMTFCS=%d FCOLL=%d DRTY=%d INTL=%d PORTSEL=%d LTR=%d\n"
4098 " MENDECL=%d DAPC=%d DLNKTST=%d DRCVPV=%d DRCVBC=%d PROM=%d\n",
4099 pThis->aCSR[15],
4100 !!(pThis->aCSR[15] & RT_BIT( 0)), !!(pThis->aCSR[15] & RT_BIT( 1)), !!(pThis->aCSR[15] & RT_BIT( 2)), !!(pThis->aCSR[15] & RT_BIT( 3)),
4101 !!(pThis->aCSR[15] & RT_BIT( 4)), !!(pThis->aCSR[15] & RT_BIT( 5)), !!(pThis->aCSR[15] & RT_BIT( 6)), (pThis->aCSR[15] >> 7) & 3,
4102 !!(pThis->aCSR[15] & RT_BIT( 9)), !!(pThis->aCSR[15] & RT_BIT(10)), !!(pThis->aCSR[15] & RT_BIT(11)),
4103 !!(pThis->aCSR[15] & RT_BIT(12)), !!(pThis->aCSR[15] & RT_BIT(13)), !!(pThis->aCSR[15] & RT_BIT(14)), !!(pThis->aCSR[15] & RT_BIT(15)));
4104
4105 pHlp->pfnPrintf(pHlp,
4106 "CSR46=%#06x: POLL=%#06x (Poll Time Counter)\n",
4107 pThis->aCSR[46], pThis->aCSR[46] & 0xffff);
4108
4109 pHlp->pfnPrintf(pHlp,
4110 "CSR47=%#06x: POLLINT=%#06x (Poll Time Interval)\n",
4111 pThis->aCSR[47], pThis->aCSR[47] & 0xffff);
4112
4113 pHlp->pfnPrintf(pHlp,
4114 "CSR58=%#06x: SWSTYLE=%d %s SSIZE32=%d CSRPCNET=%d APERRENT=%d\n",
4115 pThis->aCSR[58],
4116 pThis->aCSR[58] & 0x7f,
4117 (pThis->aCSR[58] & 0x7f) == 0 ? "C-LANCE / PCnet-ISA"
4118 : (pThis->aCSR[58] & 0x7f) == 1 ? "ILACC"
4119 : (pThis->aCSR[58] & 0x7f) == 2 ? "PCnet-32"
4120 : (pThis->aCSR[58] & 0x7f) == 3 ? "PCnet-PCI II"
4121 : "!!reserved!!",
4122 !!(pThis->aCSR[58] & RT_BIT(8)), !!(pThis->aCSR[58] & RT_BIT(9)), !!(pThis->aCSR[58] & RT_BIT(10)));
4123
4124 pHlp->pfnPrintf(pHlp,
4125 "CSR112=%04RX32: MFC=%04x (Missed receive Frame Count)\n",
4126 pThis->aCSR[112], pThis->aCSR[112] & 0xffff);
4127
4128 pHlp->pfnPrintf(pHlp,
4129 "CSR122=%04RX32: RCVALGN=%04x (Receive Frame Align)\n",
4130 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(0)));
4131
4132 pHlp->pfnPrintf(pHlp,
4133 "CSR124=%04RX32: RPA=%04x (Runt Packet Accept)\n",
4134 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(3)));
4135
4136 if (pThis->uDevType == DEV_AM79C970A || pThis->uDevType == DEV_AM79C973)
4137 {
4138 /* Print the Burst and Bus Control Register; the DWIO bit is quite important. */
4139 pHlp->pfnPrintf(pHlp,
4140 "BCR18=%#04x: ROMTMG=%u MEMCMD=%u EXTREQ=%u\n"
4141 " DWIO=%u BREADE=%u BWRITE=%u\n",
4142 pThis->aBCR[18],
4143 (pThis->aBCR[18] >> 12) & 0xf, !!(pThis->aBCR[18] & RT_BIT(9)), !!(pThis->aBCR[18] & RT_BIT(8)),
4144 !!(pThis->aBCR[18] & RT_BIT(7)), !!(pThis->aBCR[18] & RT_BIT(6)), !!(pThis->aBCR[18] & RT_BIT(5)));
4145 }
4146
4147 if (pThis->uDevType == DEV_AM79C973)
4148 {
4149 /* Print a bit of the MII state. */
4150 pHlp->pfnPrintf(pHlp,
4151 "BCR32=%#06x: MIIILP=%d XPHYSP=%d XPHYFD=%d XPHYANE=%d XPHYRST=%d\n"
4152 " DANAS=%d APDW=%u APEP=%d FMDC=%u MIIPD=%d ANTST=%d\n",
4153 pThis->aBCR[32],
4154 !!(pThis->aBCR[32] & RT_BIT( 1)), !!(pThis->aBCR[32] & RT_BIT( 3)), !!(pThis->aBCR[32] & RT_BIT( 4)),
4155 !!(pThis->aBCR[32] & RT_BIT( 5)), !!(pThis->aBCR[32] & RT_BIT( 6)), !!(pThis->aBCR[32] & RT_BIT( 7)),
4156 (pThis->aBCR[32] >> 8) & 0x7,
4157 !!(pThis->aBCR[32] & RT_BIT(11)),
4158 (pThis->aBCR[32] >> 12) & 0x3,
4159 !!(pThis->aBCR[32] & RT_BIT(14)), !!(pThis->aBCR[32] & RT_BIT(15)));
4160 }
4161
4162 /*
4163 * Dump the receive ring.
4164 */
4165 pHlp->pfnPrintf(pHlp,
4166 "RCVRL=%04x RCVRC=%04x GCRDRA=%RX32 \n"
4167 "CRDA=%08RX32 CRBA=%08RX32 CRBC=%03x CRST=%04x\n"
4168 "NRDA=%08RX32 NRBA=%08RX32 NRBC=%03x NRST=%04x\n"
4169 "NNRDA=%08RX32\n"
4170 ,
4171 CSR_RCVRL(pThis), CSR_RCVRC(pThis), pThis->GCRDRA,
4172 CSR_CRDA(pThis), CSR_CRBA(pThis), CSR_CRBC(pThis), CSR_CRST(pThis),
4173 CSR_NRDA(pThis), CSR_NRBA(pThis), CSR_NRBC(pThis), CSR_NRST(pThis),
4174 CSR_NNRD(pThis));
4175 if (fRcvRing)
4176 {
4177 const unsigned cb = 1 << pThis->iLog2DescSize;
4178 RTGCPHYS32 GCPhys = pThis->GCRDRA;
4179 unsigned i = CSR_RCVRL(pThis);
4180 while (i-- > 0)
4181 {
4182 RMD rmd;
4183 pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, GCPhys), false);
4184 pHlp->pfnPrintf(pHlp,
4185 "%04x %RX32:%c%c RBADR=%08RX32 BCNT=%03x MCNT=%03x "
4186 "OWN=%d ERR=%d FRAM=%d OFLO=%d CRC=%d BUFF=%d STP=%d ENP=%d BPE=%d "
4187 "PAM=%d LAFM=%d BAM=%d RCC=%02x RPC=%02x ONES=%#x ZEROS=%d\n",
4188 i, GCPhys, i + 1 == CSR_RCVRC(pThis) ? '*' : ' ', GCPhys == CSR_CRDA(pThis) ? '*' : ' ',
4189 rmd.rmd0.rbadr, 4096 - rmd.rmd1.bcnt, rmd.rmd2.mcnt,
4190 rmd.rmd1.own, rmd.rmd1.err, rmd.rmd1.fram, rmd.rmd1.oflo, rmd.rmd1.crc, rmd.rmd1.buff,
4191 rmd.rmd1.stp, rmd.rmd1.enp, rmd.rmd1.bpe,
4192 rmd.rmd1.pam, rmd.rmd1.lafm, rmd.rmd1.bam, rmd.rmd2.rcc, rmd.rmd2.rpc,
4193 rmd.rmd1.ones, rmd.rmd2.zeros);
4194
4195 GCPhys += cb;
4196 }
4197 }
4198
4199 /*
4200 * Dump the transmit ring.
4201 */
4202 pHlp->pfnPrintf(pHlp,
4203 "XMTRL=%04x XMTRC=%04x GCTDRA=%08RX32 BADX=%08RX32\n"
4204 "PXDA=%08RX32 PXBC=%03x PXST=%04x\n"
4205 "CXDA=%08RX32 CXBA=%08RX32 CXBC=%03x CXST=%04x\n"
4206 "NXDA=%08RX32 NXBA=%08RX32 NXBC=%03x NXST=%04x\n"
4207 "NNXDA=%08RX32\n"
4208 ,
4209 CSR_XMTRL(pThis), CSR_XMTRC(pThis),
4210 pThis->GCTDRA, CSR_BADX(pThis),
4211 CSR_PXDA(pThis), CSR_PXBC(pThis), CSR_PXST(pThis),
4212 CSR_CXDA(pThis), CSR_CXBA(pThis), CSR_CXBC(pThis), CSR_CXST(pThis),
4213 CSR_NXDA(pThis), CSR_NXBA(pThis), CSR_NXBC(pThis), CSR_NXST(pThis),
4214 CSR_NNXD(pThis));
4215 if (fXmtRing)
4216 {
4217 const unsigned cb = 1 << pThis->iLog2DescSize;
4218 RTGCPHYS32 GCPhys = pThis->GCTDRA;
4219 unsigned i = CSR_XMTRL(pThis);
4220 while (i-- > 0)
4221 {
4222 TMD tmd;
4223 pcnetTmdLoadAll(pDevIns, pThis, &tmd, PHYSADDR(pThis, GCPhys));
4224 pHlp->pfnPrintf(pHlp,
4225 "%04x %RX32:%c%c TBADR=%08RX32 BCNT=%03x OWN=%d "
4226 "ERR=%d NOFCS=%d LTINT=%d ONE=%d DEF=%d STP=%d ENP=%d BPE=%d "
4227 "BUFF=%d UFLO=%d EXDEF=%d LCOL=%d LCAR=%d RTRY=%d TDR=%03x TRC=%#x ONES=%#x\n"
4228 ,
4229 i,
4230 GCPhys,
4231 i + 1 == CSR_XMTRC(pThis) ? '*' : ' ',
4232 GCPhys == CSR_CXDA(pThis) ? '*' : ' ',
4233 tmd.tmd0.tbadr,
4234 4096 - tmd.tmd1.bcnt,
4235 tmd.tmd1.own,
4236 tmd.tmd1.err,
4237 tmd.tmd1.nofcs,
4238 tmd.tmd1.ltint,
4239 tmd.tmd1.one,
4240 tmd.tmd1.def,
4241 tmd.tmd1.stp,
4242 tmd.tmd1.enp,
4243 tmd.tmd1.bpe,
4244 tmd.tmd2.buff,
4245 tmd.tmd2.uflo,
4246 tmd.tmd2.exdef,
4247 tmd.tmd2.lcol,
4248 tmd.tmd2.lcar,
4249 tmd.tmd2.rtry,
4250 tmd.tmd2.tdr,
4251 tmd.tmd2.trc,
4252 tmd.tmd1.ones);
4253
4254 GCPhys += cb;
4255 }
4256 }
4257
4258 /* Dump the Addres PROM (APROM). */
4259 if (fAPROM)
4260 {
4261 pHlp->pfnPrintf(pHlp,
4262 "Address PROM:\n %Rhxs\n", pThis->aPROM);
4263
4264
4265 }
4266
4267 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4268}
4269
4270
4271/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
4272
4273/**
4274 * Takes down the link temporarily if it's current status is up.
4275 *
4276 * This is used during restore and when replumbing the network link.
4277 *
4278 * The temporary link outage is supposed to indicate to the OS that all network
4279 * connections have been lost and that it for instance is appropriate to
4280 * renegotiate any DHCP lease.
4281 *
4282 * @param pThis The PCnet shared instance data.
4283 */
4284static void pcnetR3TempLinkDown(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
4285{
4286 if (pThis->fLinkUp)
4287 {
4288 pThis->fLinkTempDown = true;
4289 pThis->cLinkDownReported = 0;
4290 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4291 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4292 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
4293 AssertRC(rc);
4294 }
4295}
4296
4297
4298/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
4299
4300/**
4301 * Saves the configuration.
4302 *
4303 * @param pHlp The device helpers.
4304 * @param pThis The PCnet shared instance data.
4305 * @param pSSM The saved state handle.
4306 */
4307static void pcnetR3SaveConfig(PCPDMDEVHLPR3 pHlp, PPCNETSTATE pThis, PSSMHANDLE pSSM)
4308{
4309 pHlp->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4310 pHlp->pfnSSMPutU8(pSSM, pThis->uDevType);
4311 pHlp->pfnSSMPutU32(pSSM, pThis->u32LinkSpeed);
4312}
4313
4314
4315/**
4316 * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
4317 */
4318static DECLCALLBACK(int) pcnetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
4319{
4320 RT_NOREF(uPass);
4321 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4322 pcnetR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
4323 return VINF_SSM_DONT_CALL_AGAIN;
4324}
4325
4326
4327/**
4328 * @callback_method_impl{FNSSMDEVSAVEPREP,
4329 * Serializes the receive thread, it may be working inside the critsect.}
4330 */
4331static DECLCALLBACK(int) pcnetR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4332{
4333 RT_NOREF(pSSM);
4334 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4335
4336 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4337 AssertRCReturn(rc, rc);
4338 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4339 return VINF_SUCCESS;
4340}
4341
4342
4343/**
4344 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4345 */
4346static DECLCALLBACK(int) pcnetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4347{
4348 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4349 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4350
4351 pHlp->pfnSSMPutBool(pSSM, pThis->fLinkUp);
4352 pHlp->pfnSSMPutU32(pSSM, pThis->u32RAP);
4353 pHlp->pfnSSMPutS32(pSSM, pThis->iISR);
4354 pHlp->pfnSSMPutU32(pSSM, pThis->u32Lnkst);
4355 pHlp->pfnSSMPutBool(pSSM, false/* was ffPrivIfEnabled */); /* >= If version 0.9 */
4356 pHlp->pfnSSMPutBool(pSSM, pThis->fSignalRxMiss); /* >= If version 0.10 */
4357 pHlp->pfnSSMPutGCPhys32(pSSM, pThis->GCRDRA);
4358 pHlp->pfnSSMPutGCPhys32(pSSM, pThis->GCTDRA);
4359 pHlp->pfnSSMPutMem(pSSM, pThis->aPROM, sizeof(pThis->aPROM));
4360 pHlp->pfnSSMPutMem(pSSM, pThis->aCSR, sizeof(pThis->aCSR));
4361 pHlp->pfnSSMPutMem(pSSM, pThis->aBCR, sizeof(pThis->aBCR));
4362 pHlp->pfnSSMPutMem(pSSM, pThis->aMII, sizeof(pThis->aMII));
4363 pHlp->pfnSSMPutU16(pSSM, pThis->u16CSR0LastSeenByGuest);
4364 pHlp->pfnSSMPutU64(pSSM, pThis->u64LastPoll);
4365 pcnetR3SaveConfig(pHlp, pThis, pSSM);
4366
4367 int rc = VINF_SUCCESS;
4368 rc = PDMDevHlpTimerSave(pDevIns, pThis->hTimerPoll, pSSM);
4369 if (RT_FAILURE(rc))
4370 return rc;
4371 if (pThis->uDevType == DEV_AM79C973)
4372 rc = PDMDevHlpTimerSave(pDevIns, pThis->hTimerSoftInt, pSSM);
4373 return rc;
4374}
4375
4376
4377/**
4378 * @callback_method_impl{FNSSMDEVLOADPREP},
4379 * Serializes the receive thread, it may be working inside the critsect.}
4380 */
4381static DECLCALLBACK(int) pcnetR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4382{
4383 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4384 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4385
4386 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4387 AssertRCReturn(rc, rc);
4388
4389 uint32_t uVer = pHlp->pfnSSMHandleVersion(pSSM);
4390 if ( uVer < VBOX_FULL_VERSION_MAKE(4, 3, 6)
4391 || ( uVer >= VBOX_FULL_VERSION_MAKE(4, 3, 51)
4392 && uVer < VBOX_FULL_VERSION_MAKE(4, 3, 53)))
4393 {
4394 /* older saved states contain the shared memory region which was never used for ages. */
4395 void *pvSharedMMIOR3;
4396 rc = PDMDevHlpMmio2Create(pDevIns, pDevIns->apPciDevs[0], 2 << 16, _512K, 0, "PCnetSh",
4397 &pvSharedMMIOR3, &pThis->hMmio2Shared);
4398 if (RT_FAILURE(rc))
4399 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
4400 N_("Failed to allocate the dummy shmem region for the PCnet device"));
4401 }
4402
4403 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4404 return rc;
4405}
4406
4407
4408/**
4409 * @callback_method_impl{FNSSMDEVLOADEXEC}
4410 */
4411static DECLCALLBACK(int) pcnetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4412{
4413 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4414 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4415 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4416
4417 if ( SSM_VERSION_MAJOR_CHANGED(uVersion, PCNET_SAVEDSTATE_VERSION)
4418 || SSM_VERSION_MINOR(uVersion) < 7)
4419 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4420
4421 if (uPass == SSM_PASS_FINAL)
4422 {
4423 /* restore data */
4424 pHlp->pfnSSMGetBool(pSSM, &pThis->fLinkUp);
4425 int rc = pHlp->pfnSSMGetU32(pSSM, &pThis->u32RAP);
4426 AssertRCReturn(rc, rc);
4427 AssertLogRelMsgReturn(pThis->u32RAP < RT_ELEMENTS(pThis->aCSR), ("%#x\n", pThis->u32RAP), VERR_SSM_LOAD_CONFIG_MISMATCH);
4428 pHlp->pfnSSMGetS32(pSSM, &pThis->iISR);
4429 pHlp->pfnSSMGetU32(pSSM, &pThis->u32Lnkst);
4430 if ( SSM_VERSION_MAJOR(uVersion) > 0
4431 || SSM_VERSION_MINOR(uVersion) >= 9)
4432 {
4433 bool fPrivIfEnabled = false;
4434 pHlp->pfnSSMGetBool(pSSM, &fPrivIfEnabled);
4435 if (fPrivIfEnabled)
4436 {
4437 /* no longer implemented */
4438 LogRel(("PCnet#%d: Cannot enable private interface!\n", PCNET_INST_NR));
4439 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4440 }
4441 }
4442 if ( SSM_VERSION_MAJOR(uVersion) > 0
4443 || SSM_VERSION_MINOR(uVersion) >= 10)
4444 pHlp->pfnSSMGetBool(pSSM, &pThis->fSignalRxMiss);
4445 pHlp->pfnSSMGetGCPhys32(pSSM, &pThis->GCRDRA);
4446 pHlp->pfnSSMGetGCPhys32(pSSM, &pThis->GCTDRA);
4447 pHlp->pfnSSMGetMem(pSSM, &pThis->aPROM, sizeof(pThis->aPROM));
4448 pHlp->pfnSSMGetMem(pSSM, &pThis->aCSR, sizeof(pThis->aCSR));
4449 pHlp->pfnSSMGetMem(pSSM, &pThis->aBCR, sizeof(pThis->aBCR));
4450 pHlp->pfnSSMGetMem(pSSM, &pThis->aMII, sizeof(pThis->aMII));
4451 pHlp->pfnSSMGetU16(pSSM, &pThis->u16CSR0LastSeenByGuest);
4452 pHlp->pfnSSMGetU64(pSSM, &pThis->u64LastPoll);
4453 }
4454
4455 /* check config */
4456 RTMAC Mac;
4457 int rc = pHlp->pfnSSMGetMem(pSSM, &Mac, sizeof(Mac));
4458 AssertRCReturn(rc, rc);
4459 if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
4460 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4461 LogRel(("PCnet#%u: The mac address differs: config=%RTmac saved=%RTmac\n", PCNET_INST_NR, &pThis->MacConfigured, &Mac));
4462
4463 uint8_t uDevType;
4464 rc = pHlp->pfnSSMGetU8(pSSM, &uDevType);
4465 AssertRCReturn(rc, rc);
4466 if (pThis->uDevType != uDevType)
4467 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("The uDevType setting differs: config=%u saved=%u"), pThis->uDevType, uDevType);
4468
4469 uint32_t u32LinkSpeed;
4470 rc = pHlp->pfnSSMGetU32(pSSM, &u32LinkSpeed);
4471 AssertRCReturn(rc, rc);
4472 if ( pThis->u32LinkSpeed != u32LinkSpeed
4473 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4474 LogRel(("PCnet#%u: The mac link speed differs: config=%u saved=%u\n", PCNET_INST_NR, pThis->u32LinkSpeed, u32LinkSpeed));
4475
4476 if (uPass == SSM_PASS_FINAL)
4477 {
4478 /* restore timers and stuff */
4479 PDMDevHlpTimerLoad(pDevIns, pThis->hTimerPoll, pSSM);
4480 if (pThis->uDevType == DEV_AM79C973)
4481 {
4482 if ( SSM_VERSION_MAJOR(uVersion) > 0
4483 || SSM_VERSION_MINOR(uVersion) >= 8)
4484 PDMDevHlpTimerLoad(pDevIns, pThis->hTimerSoftInt, pSSM);
4485 }
4486
4487 pThis->iLog2DescSize = BCR_SWSTYLE(pThis)
4488 ? 4
4489 : 3;
4490 pThis->GCUpperPhys = BCR_SSIZE32(pThis)
4491 ? 0
4492 : (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
4493
4494 /* update promiscuous mode. */
4495 if (pThisCC->pDrv)
4496 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, CSR_PROM(pThis));
4497
4498 /* Indicate link down to the guest OS that all network connections have
4499 been lost, unless we've been teleported here. */
4500 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
4501 pcnetR3TempLinkDown(pDevIns, pThis);
4502 }
4503
4504 return VINF_SUCCESS;
4505}
4506
4507/**
4508 * @callback_method_impl{FNSSMDEVLOADDONE}
4509 */
4510static DECLCALLBACK(int) pcnetR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4511{
4512 RT_NOREF(pSSM);
4513 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4514 int rc = VINF_SUCCESS;
4515 if (pThis->hMmio2Shared != NIL_PGMMMIO2HANDLE)
4516 {
4517 /* drop this dummy region */
4518 rc = pDevIns->pHlpR3->pfnMmio2Destroy(pDevIns, pThis->hMmio2Shared);
4519 AssertLogRelRC(rc);
4520 pThis->hMmio2Shared = NIL_PGMMMIO2HANDLE;
4521 }
4522 return rc;
4523}
4524
4525/* -=-=-=-=-=- PCNETSTATE::INetworkDown -=-=-=-=-=- */
4526
4527/**
4528 * Check if the device/driver can receive data now.
4529 *
4530 * Worker for pcnetR3NetworkDown_WaitReceiveAvail(). This must be called before
4531 * the pfnRecieve() method is called.
4532 *
4533 * @returns VBox status code.
4534 * @param pDevIns The device instance.
4535 * @param pThis The PCnet shared instance data.
4536 */
4537static int pcnetR3CanReceive(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
4538{
4539 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4540 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
4541
4542 rc = VERR_NET_NO_BUFFER_SPACE;
4543
4544 if (RT_LIKELY(!CSR_DRX(pThis) && !CSR_STOP(pThis) && !CSR_SPND(pThis)))
4545 {
4546 if (HOST_IS_OWNER(CSR_CRST(pThis)) && pThis->GCRDRA)
4547 pcnetRdtePoll(pDevIns, pThis);
4548
4549 if (RT_UNLIKELY(HOST_IS_OWNER(CSR_CRST(pThis))))
4550 {
4551 /** @todo Notify the guest _now_. Will potentially increase the interrupt load */
4552 if (pThis->fSignalRxMiss)
4553 pThis->aCSR[0] |= 0x1000; /* Set MISS flag */
4554 }
4555 else
4556 rc = VINF_SUCCESS;
4557 }
4558
4559 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4560 return rc;
4561}
4562
4563
4564/**
4565 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
4566 */
4567static DECLCALLBACK(int) pcnetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
4568{
4569 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
4570 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4571 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4572
4573 int rc = pcnetR3CanReceive(pDevIns, pThis);
4574 if (RT_SUCCESS(rc))
4575 return VINF_SUCCESS;
4576 if (RT_UNLIKELY(cMillies == 0))
4577 return VERR_NET_NO_BUFFER_SPACE;
4578
4579 rc = VERR_INTERRUPTED;
4580 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
4581 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
4582 VMSTATE enmVMState;
4583 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
4584 || enmVMState == VMSTATE_RUNNING_LS))
4585 {
4586 int rc2 = pcnetR3CanReceive(pDevIns, pThis);
4587 if (RT_SUCCESS(rc2))
4588 {
4589 rc = VINF_SUCCESS;
4590 break;
4591 }
4592 LogFlow(("pcnetR3NetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", cMillies));
4593 /* Start the poll timer once which will remain active as long fMaybeOutOfSpace
4594 * is true -- even if (transmit) polling is disabled (CSR_DPOLL). */
4595 rc2 = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4596 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc2);
4597 pcnetPollTimerStart(pDevIns, pThis);
4598 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4599 PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventOutOfRxSpace, cMillies);
4600 }
4601 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
4602 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
4603
4604 return rc;
4605}
4606
4607
4608/**
4609 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
4610 */
4611static DECLCALLBACK(int) pcnetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
4612{
4613 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
4614 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4615 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4616
4617 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
4618 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4619 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
4620
4621 /*
4622 * Check for the max ethernet frame size, taking the IEEE 802.1Q (VLAN) tag into
4623 * account. Note that the CRC Checksum is optional.
4624 * Ethernet frames consist of a 14-byte header [+ 4-byte vlan tag] + a 1500-byte body [+ 4-byte CRC].
4625 */
4626 if (RT_LIKELY( cb <= 1518
4627 || ( cb <= 1522
4628 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN))))
4629 {
4630 bool fAddFCS = cb <= 1514
4631 || ( cb <= 1518
4632 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN));
4633 if (cb > 70) /* unqualified guess */
4634 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
4635 pcnetReceiveNoSync(pDevIns, pThis, pThisCC, (const uint8_t *)pvBuf, cb, fAddFCS, false);
4636 pThis->Led.Actual.s.fReading = 0;
4637 }
4638#ifdef LOG_ENABLED
4639 else
4640 {
4641 static bool s_fFirstBigFrameLoss = true;
4642 unsigned cbMaxFrame = ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN)
4643 ? 1522 : 1518;
4644 if (s_fFirstBigFrameLoss)
4645 {
4646 s_fFirstBigFrameLoss = false;
4647 Log(("PCnet#%d: Received giant frame %zu, max %u. (Further giants will be reported at level5.)\n",
4648 PCNET_INST_NR, cb, cbMaxFrame));
4649 }
4650 else
4651 Log5(("PCnet#%d: Received giant frame %zu bytes, max %u.\n",
4652 PCNET_INST_NR, cb, cbMaxFrame));
4653 }
4654#endif /* LOG_ENABLED */
4655
4656 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4657 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
4658
4659 return VINF_SUCCESS;
4660}
4661
4662
4663/**
4664 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
4665 */
4666static DECLCALLBACK(void) pcnetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
4667{
4668 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
4669 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4670 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4671 pcnetXmitPending(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
4672}
4673
4674
4675/* -=-=-=-=-=- PCNETSTATE::INetworkConfig -=-=-=-=-=- */
4676
4677/**
4678 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
4679 */
4680static DECLCALLBACK(int) pcnetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
4681{
4682 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
4683 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4684 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4685 memcpy(pMac, pThis->aPROM, sizeof(*pMac));
4686 return VINF_SUCCESS;
4687}
4688
4689
4690/**
4691 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
4692 */
4693static DECLCALLBACK(PDMNETWORKLINKSTATE) pcnetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
4694{
4695 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
4696 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4697 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4698 if (pThis->fLinkUp && !pThis->fLinkTempDown)
4699 return PDMNETWORKLINKSTATE_UP;
4700 if (!pThis->fLinkUp)
4701 return PDMNETWORKLINKSTATE_DOWN;
4702 if (pThis->fLinkTempDown)
4703 return PDMNETWORKLINKSTATE_DOWN_RESUME;
4704 AssertMsgFailed(("Invalid link state!\n"));
4705 return PDMNETWORKLINKSTATE_INVALID;
4706}
4707
4708
4709/**
4710 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
4711 */
4712static DECLCALLBACK(int) pcnetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
4713{
4714 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
4715 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4716 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4717 bool fLinkUp;
4718
4719 AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
4720 ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
4721
4722 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
4723 {
4724 pcnetR3TempLinkDown(pDevIns, pThis);
4725 /*
4726 * Note that we do not notify the driver about the link state change because
4727 * the change is only temporary and can be disregarded from the driver's
4728 * point of view (see @bugref{7057}).
4729 */
4730 return VINF_SUCCESS;
4731 }
4732 /* has the state changed? */
4733 fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
4734 if (pThis->fLinkUp != fLinkUp)
4735 {
4736 pThis->fLinkUp = fLinkUp;
4737 if (fLinkUp)
4738 {
4739 /* Connect with a configured delay. */
4740 pThis->fLinkTempDown = true;
4741 pThis->cLinkDownReported = 0;
4742 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4743 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4744 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
4745 AssertRC(rc);
4746 }
4747 else
4748 {
4749 /* disconnect */
4750 pThis->cLinkDownReported = 0;
4751 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4752 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4753 }
4754 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
4755 if (pThisCC->pDrv)
4756 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
4757 }
4758 return VINF_SUCCESS;
4759}
4760
4761
4762/* -=-=-=-=-=- PCNETSTATE::ILeds (LUN#0) -=-=-=-=-=- */
4763
4764/**
4765 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4766 */
4767static DECLCALLBACK(int) pcnetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4768{
4769 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, ILeds);
4770 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4771 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4772 if (iLUN == 0)
4773 {
4774 *ppLed = &pThis->Led;
4775 return VINF_SUCCESS;
4776 }
4777 return VERR_PDM_LUN_NOT_FOUND;
4778}
4779
4780
4781/* -=-=-=-=-=- PCNETSTATE::IBase (LUN#0) -=-=-=-=-=- */
4782
4783/**
4784 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4785 */
4786static DECLCALLBACK(void *) pcnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
4787{
4788 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, IBase);
4789 Assert(&pThisCC->IBase == pInterface);
4790 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4791 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
4792 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
4793 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4794 return NULL;
4795}
4796
4797
4798/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
4799
4800/**
4801 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
4802 */
4803static DECLCALLBACK(void) pcnetR3PowerOff(PPDMDEVINS pDevIns)
4804{
4805 /* Poke thread waiting for buffer space. */
4806 pcnetWakeupReceive(pDevIns);
4807}
4808
4809
4810/**
4811 * @interface_method_impl{PDMDEVREG,pfnDetach}
4812 *
4813 * One port on the network card has been disconnected from the network.
4814 */
4815static DECLCALLBACK(void) pcnetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4816{
4817 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4818 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4819 Log(("#%d pcnetR3Detach:\n", PCNET_INST_NR));
4820 RT_NOREF(fFlags);
4821
4822 AssertLogRelReturnVoid(iLUN == 0);
4823
4824 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4825 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4826
4827 /** @todo r=pritesh still need to check if i missed
4828 * to clean something in this function
4829 */
4830
4831 /*
4832 * Zero some important members.
4833 */
4834 pThis->fDriverAttached = false;
4835 pThisCC->pDrvBase = NULL;
4836 pThisCC->pDrv = NULL;
4837 /// @todo figure this out
4838 //pThis->pDrvR0 = NIL_RTR0PTR;
4839 //pThis->pDrvRC = NIL_RTRCPTR;
4840
4841 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4842}
4843
4844
4845/**
4846 * @interface_method_impl{PDMDEVREG,pfnAttach}
4847 * One port on the network card has been connected to a network.
4848 */
4849static DECLCALLBACK(int) pcnetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4850{
4851 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4852 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4853 LogFlow(("#%d pcnetR3Attach:\n", PCNET_INST_NR));
4854 RT_NOREF(fFlags);
4855
4856 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
4857
4858 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4859 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4860
4861 /*
4862 * Attach the driver.
4863 */
4864 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
4865 if (RT_SUCCESS(rc))
4866 {
4867 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
4868 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
4869 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
4870 pThis->fDriverAttached = true;
4871 /// @todo figoure out this
4872 //pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
4873 //pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
4874 }
4875 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
4876 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
4877 {
4878 /* This should never happen because this function is not called
4879 * if there is no driver to attach! */
4880 Log(("#%d No attached driver!\n", PCNET_INST_NR));
4881 }
4882
4883 /*
4884 * Temporary set the link down if it was up so that the guest
4885 * will know that we have change the configuration of the
4886 * network card
4887 */
4888 if (RT_SUCCESS(rc))
4889 pcnetR3TempLinkDown(pDevIns, pThis);
4890
4891 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4892 return rc;
4893
4894}
4895
4896
4897/**
4898 * @interface_method_impl{PDMDEVREG,pfnSuspend}
4899 */
4900static DECLCALLBACK(void) pcnetR3Suspend(PPDMDEVINS pDevIns)
4901{
4902 /* Poke thread waiting for buffer space. */
4903 pcnetWakeupReceive(pDevIns);
4904}
4905
4906
4907/**
4908 * @interface_method_impl{PDMDEVREG,pfnReset}
4909 */
4910static DECLCALLBACK(void) pcnetR3Reset(PPDMDEVINS pDevIns)
4911{
4912 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4913 if (pThis->fLinkTempDown)
4914 {
4915 pThis->cLinkDownReported = 0x10000;
4916 PDMDevHlpTimerStop(pDevIns, pThis->hTimerRestore);
4917 pcnetR3TimerRestore(pDevIns, pThis->hTimerRestore, pThis);
4918 }
4919
4920 /** @todo How to flush the queues? */
4921 pcnetR3HardReset(pDevIns, pThis);
4922}
4923
4924
4925/**
4926 * @interface_method_impl{PDMDEVREG,pfnRelocate}
4927 */
4928static DECLCALLBACK(void) pcnetR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4929{
4930 PPCNETSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PPCNETSTATERC);
4931 pThisRC->pDrv += offDelta;
4932}
4933
4934
4935/**
4936 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4937 */
4938static DECLCALLBACK(int) pcnetR3Destruct(PPDMDEVINS pDevIns)
4939{
4940 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
4941 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4942
4943 if (pThis->hEventOutOfRxSpace == NIL_SUPSEMEVENT)
4944 {
4945 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventOutOfRxSpace);
4946 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventOutOfRxSpace);
4947 pThis->hEventOutOfRxSpace = NIL_SUPSEMEVENT;
4948 }
4949
4950 if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->CritSect))
4951 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
4952 return VINF_SUCCESS;
4953}
4954
4955
4956/**
4957 * @interface_method_impl{PDMDEVREG,pfnConstruct}
4958 */
4959static DECLCALLBACK(int) pcnetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
4960{
4961 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
4962 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4963 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4964 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4965 PPDMIBASE pBase;
4966 char szTmp[128];
4967 int rc;
4968
4969 Assert(RT_ELEMENTS(pThis->aBCR) == BCR_MAX_RAP);
4970 Assert(RT_ELEMENTS(pThis->aMII) == MII_MAX_REG);
4971 Assert(sizeof(pThis->abLoopBuf) == RT_ALIGN_Z(sizeof(pThis->abLoopBuf), 16));
4972
4973 /*
4974 * Init what's required to make the destructor safe.
4975 */
4976 pThis->iInstance = iInstance;
4977 pThis->hEventOutOfRxSpace = NIL_SUPSEMEVENT;
4978 pThis->hIoPortsPci = NIL_IOMIOPORTHANDLE;
4979 pThis->hIoPortsPciAProm = NIL_IOMIOPORTHANDLE;
4980 pThis->hIoPortsIsa = NIL_IOMIOPORTHANDLE;
4981 pThis->hIoPortsIsaAProm = NIL_IOMIOPORTHANDLE;
4982 pThis->hMmio2Shared = NIL_PGMMMIO2HANDLE;
4983 pThisCC->pDevIns = pDevIns;
4984
4985 /*
4986 * Validate configuration.
4987 */
4988 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
4989 "MAC|CableConnected|Am79C973|ChipType|Port|IRQ|LineSpeed|PrivIfEnabled|LinkUpDelay|StatNo",
4990 "");
4991 /*
4992 * Read the configuration.
4993 */
4994 rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4995 if (RT_FAILURE(rc))
4996 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"MAC\" value"));
4997 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
4998 if (RT_FAILURE(rc))
4999 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"CableConnected\" value"));
5000
5001 /*
5002 * Determine the model.
5003 */
5004 char szChipType[16];
5005 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "ChipType", &szChipType[0], sizeof(szChipType), "Am79C970A");
5006 if (RT_FAILURE(rc))
5007 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ChipType\" as string failed"));
5008
5009 if (!strcmp(szChipType, "Am79C970A"))
5010 pThis->uDevType = DEV_AM79C970A; /* 10 Mbps PCnet-PCI II. */
5011 else if (!strcmp(szChipType, "Am79C973"))
5012 pThis->uDevType = DEV_AM79C973; /* 10/100 Mbps PCnet-FAST III. */
5013 else if (!strcmp(szChipType, "Am79C960"))
5014 pThis->uDevType = DEV_AM79C960; /* 10 Mbps PCnet-ISA, NE2100/Am2100 compatible. */
5015 else if (!strcmp(szChipType, "Am79C960_EB"))
5016 {
5017 pThis->uDevType = DEV_AM79C960_EB; /* 10 Mbps PCnet-ISA, Racal InterLink NI6510 EtherBlaster compatible. */
5018 /* NI6510 drivers (at least Racal's and Linux) require the OUI to be InterLan's (Racal-Datacom).
5019 * Refuse loading if OUI doesn't match, because otherwise drivers won't load in the guest. */
5020 if (memcmp(&pThis->MacConfigured, "\x02\x07\x01", 3))
5021 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5022 N_("Configuration error: MAC address OUI for EtherBlaster must be 02 07 01"));
5023 }
5024 else
5025 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5026 N_("Configuration error: The \"ChipType\" value \"%s\" is unsupported"),
5027 szChipType);
5028
5029
5030 /*
5031 * Process the old model configuration. If present, it must take precedence for saved state compatibility.
5032 */
5033 bool fAm79C973;
5034 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Am79C973", &fAm79C973, false);
5035 if (RT_FAILURE(rc))
5036 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Am79C973\" value"));
5037 if (fAm79C973)
5038 pThis->uDevType = DEV_AM79C973;
5039
5040 /*
5041 * Process ISA configuration options. The defaults are chosen to be NE2100/Am2100 compatible.
5042 */
5043 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->IOPortBase, 0x300);
5044 if (RT_FAILURE(rc))
5045 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Port\" value"));
5046
5047 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, 3);
5048 if (RT_FAILURE(rc))
5049 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"IRQ\" value"));
5050
5051 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LineSpeed", &pThis->u32LinkSpeed, 1000000); /* 1GBit/s (in kbps units)*/
5052 if (RT_FAILURE(rc))
5053 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"LineSpeed\" value"));
5054
5055 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
5056 if (RT_FAILURE(rc))
5057 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
5058 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
5059 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
5060 LogRel(("PCnet#%d WARNING! Link up delay is set to %u seconds!\n", iInstance, pThis->cMsLinkUpDelay / 1000));
5061 Log(("#%d Link up delay is set to %u seconds\n", iInstance, pThis->cMsLinkUpDelay / 1000));
5062
5063 uint32_t uStatNo = iInstance;
5064 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
5065 if (RT_FAILURE(rc))
5066 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
5067
5068 /*
5069 * Initialize data (most of it anyway).
5070 */
5071 pThis->Led.u32Magic = PDMLED_MAGIC;
5072 /* IBase */
5073 pThisCC->IBase.pfnQueryInterface = pcnetQueryInterface;
5074 /* INeworkPort */
5075 pThisCC->INetworkDown.pfnWaitReceiveAvail = pcnetR3NetworkDown_WaitReceiveAvail;
5076 pThisCC->INetworkDown.pfnReceive = pcnetR3NetworkDown_Receive;
5077 pThisCC->INetworkDown.pfnXmitPending = pcnetR3NetworkDown_XmitPending;
5078 /* INetworkConfig */
5079 pThisCC->INetworkConfig.pfnGetMac = pcnetR3NetworkConfig_GetMac;
5080 pThisCC->INetworkConfig.pfnGetLinkState = pcnetR3NetworkConfig_GetLinkState;
5081 pThisCC->INetworkConfig.pfnSetLinkState = pcnetR3NetworkConfig_SetLinkState;
5082 /* ILeds */
5083 pThisCC->ILeds.pfnQueryStatusLed = pcnetQueryStatusLed;
5084
5085 /* PCI Device */
5086 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
5087 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
5088
5089 PDMPciDevSetVendorId(pPciDev, 0x1022);
5090 PDMPciDevSetDeviceId(pPciDev, 0x2000);
5091 PDMPciDevSetByte(pPciDev, 0x04, 0x07); /* command */
5092 PDMPciDevSetByte(pPciDev, 0x05, 0x00);
5093 PDMPciDevSetByte(pPciDev, 0x06, 0x80); /* status */
5094 PDMPciDevSetByte(pPciDev, 0x07, 0x02);
5095 PDMPciDevSetByte(pPciDev, 0x08, pThis->uDevType == DEV_AM79C973 ? 0x40 : 0x16); /* revision */
5096 PDMPciDevSetByte(pPciDev, 0x09, 0x00);
5097 PDMPciDevSetByte(pPciDev, 0x0a, 0x00); /* ethernet network controller */
5098 PDMPciDevSetByte(pPciDev, 0x0b, 0x02);
5099 PDMPciDevSetByte(pPciDev, 0x0e, 0x00); /* header_type */
5100 PDMPciDevSetByte(pPciDev, 0x10, 0x01); /* IO Base */
5101 PDMPciDevSetByte(pPciDev, 0x11, 0x00);
5102 PDMPciDevSetByte(pPciDev, 0x12, 0x00);
5103 PDMPciDevSetByte(pPciDev, 0x13, 0x00);
5104 PDMPciDevSetByte(pPciDev, 0x14, 0x00); /* MMIO Base */
5105 PDMPciDevSetByte(pPciDev, 0x15, 0x00);
5106 PDMPciDevSetByte(pPciDev, 0x16, 0x00);
5107 PDMPciDevSetByte(pPciDev, 0x17, 0x00);
5108
5109 /* subsystem and subvendor IDs */
5110 PDMPciDevSetByte(pPciDev, 0x2c, 0x22); /* subsystem vendor id */
5111 PDMPciDevSetByte(pPciDev, 0x2d, 0x10);
5112 PDMPciDevSetByte(pPciDev, 0x2e, 0x00); /* subsystem id */
5113 PDMPciDevSetByte(pPciDev, 0x2f, 0x20);
5114 PDMPciDevSetByte(pPciDev, 0x3d, 1); /* interrupt pin 0 */
5115 PDMPciDevSetByte(pPciDev, 0x3e, 0x06);
5116 PDMPciDevSetByte(pPciDev, 0x3f, 0xff);
5117
5118 /*
5119 * We use our own critical section (historical reasons).
5120 */
5121 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PCnet#%u", iInstance);
5122 AssertRCReturn(rc, rc);
5123 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5124 AssertRCReturn(rc, rc);
5125
5126 /*
5127 * Register the PCI device, its I/O regions, the timer and the saved state item.
5128 */
5129 Assert(PCNET_IS_PCI(pThis) != PCNET_IS_ISA(pThis)); /* IOPortBase is shared, so it's either one or the other! */
5130
5131 if (PCNET_IS_PCI(pThis))
5132 {
5133 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
5134 AssertRCReturn(rc, rc);
5135
5136 /* Region #0: I/O ports - two handlers: */
5137 rc = PDMDevHlpIoPortCreate(pDevIns, 0x10 /*cPorts*/, pPciDev, 0 /*iPciRegion*/,
5138 pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/,
5139 "PCnet APROM", NULL /*paExtDescs*/, &pThis->hIoPortsPciAProm);
5140 AssertRCReturn(rc, rc);
5141 rc = PDMDevHlpIoPortCreate(pDevIns, 0x10 /*cPorts*/, pPciDev, 0 /*iPciRegion*/,
5142 pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/,
5143 "PCnet", NULL /*paExtDescs*/, &pThis->hIoPortsPci);
5144 AssertRCReturn(rc, rc);
5145 rc = PDMDevHlpPCIIORegionRegisterIoCustom(pDevIns, 0, PCNET_IOPORT_SIZE, pcnetR3PciMapUnmapIoPorts);
5146 AssertRCReturn(rc, rc);
5147
5148 /* Region #1: MMIO */
5149 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 1 /*iPciRegion*/, PCNET_PNPMMIO_SIZE, PCI_ADDRESS_SPACE_MEM,
5150 pcnetR3MmioWrite, pcnetR3MmioRead, NULL /*pvUser*/,
5151 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
5152 "PCnet", &pThis->hMmioPci);
5153 AssertRCReturn(rc, rc);
5154 }
5155
5156 /*
5157 * Register ISA I/O ranges for PCnet-ISA.
5158 */
5159 if (PCNET_IS_ISA(pThis))
5160 {
5161 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, pcnetIoPortAPromWrite, pcnetIOPortAPromRead,
5162 "PCnet APROM", NULL /*paExtDesc*/, &pThis->hIoPortsIsaAProm);
5163 AssertRCReturn(rc, rc);
5164 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x10, 0x10 /*cPorts*/, pcnetIoPortWrite, pcnetIoPortRead,
5165 "PCnet", NULL /*paExtDesc*/, &pThis->hIoPortsIsa);
5166 AssertRCReturn(rc, rc);
5167 }
5168
5169
5170 /* Transmit descriptor polling timer */
5171 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3Timer, NULL, TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0,
5172 "PCnet Poll", &pThis->hTimerPoll);
5173 AssertRCReturn(rc, rc);
5174 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->hTimerPoll, &pThis->CritSect);
5175 AssertRCReturn(rc, rc);
5176
5177 if (pThis->uDevType == DEV_AM79C973)
5178 {
5179 /* Software Interrupt timer */
5180 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3TimerSoftInt, NULL,
5181 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "PCnet SoftInt", &pThis->hTimerSoftInt);
5182 AssertRCReturn(rc, rc);
5183 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->hTimerSoftInt, &pThis->CritSect);
5184 AssertRCReturn(rc, rc);
5185 }
5186 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3TimerRestore, pThis,
5187 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "PCnet Restore", &pThis->hTimerRestore);
5188 AssertRCReturn(rc, rc);
5189
5190 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCNET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
5191 NULL, pcnetR3LiveExec, NULL,
5192 pcnetR3SavePrep, pcnetR3SaveExec, NULL,
5193 pcnetR3LoadPrep, pcnetR3LoadExec, pcnetR3LoadDone);
5194 AssertRCReturn(rc, rc);
5195
5196 /*
5197 * Create the transmit queue.
5198 */
5199 rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "PCnet-Xmit", pcnetR3XmitTaskCallback, NULL /*pvUser*/, &pThis->hXmitTask);
5200 AssertRCReturn(rc, rc);
5201
5202 /*
5203 * Create the RX notifier semaphore.
5204 */
5205 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventOutOfRxSpace);
5206 AssertRCReturn(rc, rc);
5207
5208 /*
5209 * Attach status driver (optional).
5210 */
5211 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
5212 if (RT_SUCCESS(rc))
5213 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5214 else
5215 AssertMsgReturn( rc == VERR_PDM_NO_ATTACHED_DRIVER
5216 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME,
5217 ("Failed to attach to status driver. rc=%Rrc\n", rc),
5218 rc);
5219
5220 /*
5221 * Attach driver.
5222 */
5223 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
5224 if (RT_SUCCESS(rc))
5225 {
5226 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
5227 AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
5228 VERR_PDM_MISSING_INTERFACE_BELOW);
5229 pThis->fDriverAttached = true;
5230 /// @todo figure out this!
5231 //pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
5232 //pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
5233 }
5234 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
5235 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
5236 {
5237 /* No error! */
5238 Log(("No attached driver!\n"));
5239 }
5240 else
5241 return rc;
5242
5243 /*
5244 * Reset the device state. (Do after attaching.)
5245 */
5246 pcnetR3HardReset(pDevIns, pThis);
5247
5248 /*
5249 * Register the info item.
5250 */
5251 RTStrPrintf(szTmp, sizeof(szTmp), "pcnet%d", pDevIns->iInstance);
5252 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "PCNET info.", pcnetR3Info);
5253
5254 /*
5255 * Register statistics.
5256 * The /Public/ bits are official and used by session info in the GUI.
5257 */
5258 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
5259 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
5260 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
5261 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
5262 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
5263 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
5264
5265 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
5266 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
5267
5268#ifdef VBOX_WITH_STATISTICS
5269 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadRZ, STAMTYPE_PROFILE, "MMIO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in RZ");
5270 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadR3, STAMTYPE_PROFILE, "MMIO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in R3");
5271 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteRZ, STAMTYPE_PROFILE, "MMIO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in RZ");
5272 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteR3, STAMTYPE_PROFILE, "MMIO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in R3");
5273 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAPROMRead, STAMTYPE_PROFILE, "IO/APROMRead", STAMUNIT_TICKS_PER_CALL, "Profiling APROM reads");
5274 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAPROMWrite, STAMTYPE_PROFILE, "IO/APROMWrite", STAMUNIT_TICKS_PER_CALL, "Profiling APROM writes");
5275 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, "IO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ");
5276 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, "IO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3");
5277 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, "IO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ");
5278 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, "IO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3");
5279 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling Timer");
5280 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
5281 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
5282 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_PROFILE, "RxOverflowWakeup", STAMUNIT_TICKS_PER_OCCURENCE, "Nr of RX overflow wakeups");
5283 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCase1, STAMTYPE_COUNTER, "Transmit/Case1", STAMUNIT_OCCURENCES, "Single descriptor transmit");
5284 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCase2, STAMTYPE_COUNTER, "Transmit/Case2", STAMUNIT_OCCURENCES, "Multi descriptor transmit");
5285 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, "Transmit/TotalRZ", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ");
5286 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, "Transmit/TotalR3", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3");
5287 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, "Transmit/SendRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in RZ");
5288 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, "Transmit/SendR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in R3");
5289 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTdtePollRZ, STAMTYPE_PROFILE, "TdtePollRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in RZ");
5290 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTdtePollR3, STAMTYPE_PROFILE, "TdtePollR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in R3");
5291 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRdtePollRZ, STAMTYPE_PROFILE, "RdtePollRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in RZ");
5292 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRdtePollR3, STAMTYPE_PROFILE, "RdtePollR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in R3");
5293
5294 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTmdStoreRZ, STAMTYPE_PROFILE, "TmdStoreRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in RZ");
5295 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTmdStoreR3, STAMTYPE_PROFILE, "TmdStoreR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in R3");
5296
5297 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatXmitSkipCurrent, STAMTYPE_COUNTER, "Xmit/Skipped", STAMUNIT_OCCURENCES, "");
5298
5299 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, "UpdateIRQ", STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks");
5300 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPollTimer, STAMTYPE_PROFILE, "PollTimer", STAMUNIT_TICKS_PER_CALL, "Profiling poll timer");
5301 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMIIReads, STAMTYPE_COUNTER, "MIIReads", STAMUNIT_OCCURENCES, "Number of MII reads");
5302 unsigned i;
5303 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitFlush) - 1; i++)
5304 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5305 "", "XmitFlushIrq/%02u", i + 1);
5306 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5307 "", "XmitFlushIrq/%02u-inf", i + 1);
5308
5309 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitChainCounts) - 1; i++)
5310 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5311 "", "XmitChainCounts/%02u", i + 1);
5312 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5313 "", "XmitChainCounts/%02u-inf", i + 1);
5314#endif /* VBOX_WITH_STATISTICS */
5315
5316 return VINF_SUCCESS;
5317}
5318
5319#else /* !IN_RING3 */
5320
5321/**
5322 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
5323 */
5324static DECLCALLBACK(int) pcnetRZConstruct(PPDMDEVINS pDevIns)
5325{
5326 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5327 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
5328
5329 /* Critical section setup: */
5330 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5331 AssertRCReturn(rc, rc);
5332
5333 /* PCI I/O ports: */
5334 if (pThis->hIoPortsPciAProm != NIL_IOMIOPORTHANDLE)
5335 {
5336 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsPciAProm, pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/);
5337 AssertRCReturn(rc, rc);
5338 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsPci, pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/);
5339 AssertRCReturn(rc, rc);
5340 }
5341 else
5342 Assert(pThis->hIoPortsPci == NIL_IOMIOPORTHANDLE);
5343
5344 /** @todo PCI MMIO */
5345
5346 /* ISA I/O ports: */
5347 if (pThis->hIoPortsIsaAProm != NIL_IOMIOPORTHANDLE)
5348 {
5349 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsaAProm, pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/);
5350 AssertRCReturn(rc, rc);
5351 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsa, pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/);
5352 AssertRCReturn(rc, rc);
5353 }
5354 else
5355 Assert(pThis->hIoPortsIsa == NIL_IOMIOPORTHANDLE);
5356
5357 return VINF_SUCCESS;
5358}
5359
5360#endif /* !IN_RING3 */
5361
5362/**
5363 * The device registration structure.
5364 */
5365const PDMDEVREG g_DevicePCNet =
5366{
5367 /* .u32Version = */ PDM_DEVREG_VERSION,
5368 /* .uReserved0 = */ 0,
5369 /* .szName = */ "pcnet",
5370#ifdef PCNET_GC_ENABLED
5371 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
5372#else
5373 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS,
5374#endif
5375 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
5376 /* .cMaxInstances = */ ~0U,
5377 /* .uSharedVersion = */ 42,
5378 /* .cbInstanceShared = */ sizeof(PCNETSTATE),
5379 /* .cbInstanceCC = */ sizeof(PCNETSTATECC),
5380 /* .cbInstanceRC = */ sizeof(PCNETSTATERC),
5381 /* .cMaxPciDevices = */ 1,
5382 /* .cMaxMsixVectors = */ 0,
5383 /* .pszDescription = */ "AMD PCnet Ethernet controller.\n",
5384#if defined(IN_RING3)
5385 /* .pszRCMod = */ "VBoxDDRC.rc",
5386 /* .pszR0Mod = */ "VBoxDDR0.r0",
5387 /* .pfnConstruct = */ pcnetR3Construct,
5388 /* .pfnDestruct = */ pcnetR3Destruct,
5389 /* .pfnRelocate = */ pcnetR3Relocate,
5390 /* .pfnMemSetup = */ NULL,
5391 /* .pfnPowerOn = */ NULL,
5392 /* .pfnReset = */ pcnetR3Reset,
5393 /* .pfnSuspend = */ pcnetR3Suspend,
5394 /* .pfnResume = */ NULL,
5395 /* .pfnAttach = */ pcnetR3Attach,
5396 /* .pfnDetach = */ pcnetR3Detach,
5397 /* .pfnQueryInterface = */ NULL,
5398 /* .pfnInitComplete = */ NULL,
5399 /* .pfnPowerOff = */ pcnetR3PowerOff,
5400 /* .pfnSoftReset = */ NULL,
5401 /* .pfnReserved0 = */ NULL,
5402 /* .pfnReserved1 = */ NULL,
5403 /* .pfnReserved2 = */ NULL,
5404 /* .pfnReserved3 = */ NULL,
5405 /* .pfnReserved4 = */ NULL,
5406 /* .pfnReserved5 = */ NULL,
5407 /* .pfnReserved6 = */ NULL,
5408 /* .pfnReserved7 = */ NULL,
5409#elif defined(IN_RING0)
5410 /* .pfnEarlyConstruct = */ NULL,
5411 /* .pfnConstruct = */ pcnetRZConstruct,
5412 /* .pfnDestruct = */ NULL,
5413 /* .pfnFinalDestruct = */ NULL,
5414 /* .pfnRequest = */ NULL,
5415 /* .pfnReserved0 = */ NULL,
5416 /* .pfnReserved1 = */ NULL,
5417 /* .pfnReserved2 = */ NULL,
5418 /* .pfnReserved3 = */ NULL,
5419 /* .pfnReserved4 = */ NULL,
5420 /* .pfnReserved5 = */ NULL,
5421 /* .pfnReserved6 = */ NULL,
5422 /* .pfnReserved7 = */ NULL,
5423#elif defined(IN_RC)
5424 /* .pfnConstruct = */ pcnetRZConstruct,
5425 /* .pfnReserved0 = */ NULL,
5426 /* .pfnReserved1 = */ NULL,
5427 /* .pfnReserved2 = */ NULL,
5428 /* .pfnReserved3 = */ NULL,
5429 /* .pfnReserved4 = */ NULL,
5430 /* .pfnReserved5 = */ NULL,
5431 /* .pfnReserved6 = */ NULL,
5432 /* .pfnReserved7 = */ NULL,
5433#else
5434# error "Not in IN_RING3, IN_RING0 or IN_RC!"
5435#endif
5436 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
5437};
5438
5439#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5440
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