VirtualBox

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

Last change on this file since 104159 was 99739, checked in by vboxsync, 13 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 212.5 KB
Line 
1/* $Id: DevPCNet.cpp 99739 2023-05-11 01:01:08Z 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-2023 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 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetR3MmioRead: unsupported op size: address=%RGp cb=%u\n", off, cb);
3817 }
3818 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIORead), a);
3819 }
3820 else
3821 memset(pv, 0, cb);
3822
3823 LogFlow(("#%d pcnetR3MmioRead: pvUser=%p:{%.*Rhxs} cb=%d off=%RGp rc=%Rrc\n",
3824 PCNET_INST_NR, pv, cb, pv, cb, off, VBOXSTRICTRC_VAL(rc)));
3825 return rc;
3826}
3827
3828
3829/**
3830 * @callback_method_impl{FNIOMMMIONEWWRITE}
3831 */
3832static DECLCALLBACK(VBOXSTRICTRC) pcnetR3MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3833{
3834 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3835 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
3836 VBOXSTRICTRC rc = VINF_SUCCESS;
3837 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3838 RT_NOREF_PV(pvUser);
3839
3840 /*
3841 * We have to check the range, because we're page aligning the MMIO stuff presently.
3842 */
3843 if (off < PCNET_PNPMMIO_SIZE)
3844 {
3845 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3846 switch (cb)
3847 {
3848 case 1: pcnetR3MmioWriteU8(pThis, off, *(uint8_t *)pv); break;
3849 case 2: rc = pcnetR3MmioWriteU16(pDevIns, pThis, pThisCC, off, *(uint16_t *)pv); break;
3850 case 4: rc = pcnetR3MmioWriteU32(pDevIns, pThis, pThisCC, off, *(uint32_t *)pv); break;
3851 default:
3852 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetR3MmioWrite: unsupported op size: address=%RGp cb=%u\n", off, cb);
3853 }
3854
3855 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3856 }
3857 LogFlow(("#%d pcnetR3MmioWrite: pvUser=%p:{%.*Rhxs} cb=%d off=%RGp rc=%Rrc\n",
3858 PCNET_INST_NR, pv, cb, pv, cb, off, VBOXSTRICTRC_VAL(rc)));
3859 return rc;
3860}
3861
3862
3863
3864/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
3865
3866/**
3867 * @callback_method_impl{FNTMTIMERDEV, Poll timer}
3868 */
3869static DECLCALLBACK(void) pcnetR3Timer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
3870{
3871 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3872 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
3873 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3874 Assert(hTimer == pThis->hTimerPoll); RT_NOREF(pvUser, hTimer);
3875
3876 STAM_PROFILE_ADV_START(&pThis->StatTimer, a);
3877 pcnetPollTimer(pDevIns, pThis, pThisCC);
3878 STAM_PROFILE_ADV_STOP(&pThis->StatTimer, a);
3879}
3880
3881
3882/**
3883 * @callback_method_impl{FNTMTIMERDEV,
3884 * Software interrupt timer callback function.}
3885 */
3886static DECLCALLBACK(void) pcnetR3TimerSoftInt(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
3887{
3888 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3889 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
3890 Assert(hTimer == pThis->hTimerSoftInt); RT_NOREF(pvUser, hTimer);
3891
3892 pThis->aCSR[7] |= 0x0800; /* STINT */
3893 pcnetUpdateIrq(pDevIns, pThis);
3894 PDMDevHlpTimerSetNano(pDevIns, pThis->hTimerSoftInt, 12800U * (pThis->aBCR[BCR_STVAL] & 0xffff));
3895}
3896
3897
3898/**
3899 * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
3900 *
3901 * This is only called when we restore a saved state and temporarily
3902 * disconnected the network link to inform the guest that network connections
3903 * should be considered lost.
3904 */
3905static DECLCALLBACK(void) pcnetR3TimerRestore(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
3906{
3907 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3908 Assert(hTimer == pThis->hTimerRestore); RT_NOREF(pvUser);
3909
3910 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
3911 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
3912
3913 rc = VERR_GENERAL_FAILURE;
3914
3915 /* 10 Mbps models (up to and including Am79C970A) have no MII and no way to get
3916 * an MII management auto-poll interrupt (MAPINT) indicating link state changes.
3917 * In some cases we want to make sure the guest really noticed the link going down;
3918 * the cLinkDownReported counter is incremented every time the guest did something
3919 * that might have made it notice the link loss, and we only bring the link back
3920 * up once we're reasonably certain the guest knows it was down.
3921 */
3922 if (pThis->cLinkDownReported <= PCNET_MAX_LINKDOWN_REPORTED)
3923 {
3924 rc = PDMDevHlpTimerSetMillies(pDevIns, hTimer, 1500);
3925 AssertRC(rc);
3926 }
3927 if (RT_FAILURE(rc))
3928 {
3929 pThis->fLinkTempDown = false;
3930 if (pThis->fLinkUp)
3931 {
3932 LogRel(("PCnet#%d: The link is back up again after the restore.\n", pDevIns->iInstance));
3933 Log(("#%d pcnetR3TimerRestore: Clearing ERR and CERR after load. cLinkDownReported=%d\n",
3934 pDevIns->iInstance, pThis->cLinkDownReported));
3935 pThis->aCSR[0] &= ~(RT_BIT(15) | RT_BIT(13)); /* ERR | CERR - probably not 100% correct either... */
3936 pThis->Led.Actual.s.fError = 0;
3937 }
3938 }
3939 else
3940 Log(("#%d pcnetR3TimerRestore: cLinkDownReported=%d, wait another 1500ms...\n",
3941 pDevIns->iInstance, pThis->cLinkDownReported));
3942
3943 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
3944}
3945
3946
3947/* -=-=-=-=-=- PCI Device Callbacks -=-=-=-=-=- */
3948
3949/**
3950 * @callback_method_impl{FNPCIIOREGIONMAP, For the PCnet I/O Ports.}
3951 */
3952static DECLCALLBACK(int) pcnetR3PciMapUnmapIoPorts(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
3953 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
3954{
3955 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3956 int rc;
3957 RT_NOREF(iRegion, cb, enmType, pPciDev);
3958
3959 Assert(pDevIns->apPciDevs[0] == pPciDev);
3960 Assert(enmType == PCI_ADDRESS_SPACE_IO);
3961 Assert(cb >= 0x20);
3962
3963 if (GCPhysAddress != NIL_RTGCPHYS)
3964 {
3965 RTIOPORT Port = (RTIOPORT)GCPhysAddress;
3966 rc = PDMDevHlpIoPortMap(pDevIns, pThis->hIoPortsPciAProm, Port);
3967 AssertRCReturn(rc, rc);
3968 rc = PDMDevHlpIoPortMap(pDevIns, pThis->hIoPortsPci, Port + 0x10);
3969 AssertRCReturn(rc, rc);
3970 pThis->IOPortBase = Port;
3971 }
3972 else
3973 {
3974 rc = PDMDevHlpIoPortUnmap(pDevIns, pThis->hIoPortsPciAProm);
3975 AssertRCReturn(rc, rc);
3976 rc = PDMDevHlpIoPortUnmap(pDevIns, pThis->hIoPortsPci);
3977 AssertRCReturn(rc, rc);
3978 pThis->IOPortBase = 0;
3979 }
3980
3981 return VINF_SUCCESS;
3982}
3983
3984
3985/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
3986
3987/**
3988 * @callback_method_impl{FNDBGFHANDLERDEV}
3989 */
3990static DECLCALLBACK(void) pcnetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3991{
3992 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
3993 bool fRcvRing = false;
3994 bool fXmtRing = false;
3995 bool fAPROM = false;
3996
3997 /*
3998 * Parse args.
3999 */
4000 if (pszArgs)
4001 {
4002 fRcvRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "rcv");
4003 fXmtRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "xmt");
4004 fAPROM = strstr(pszArgs, "verbose") || strstr(pszArgs, "aprom");
4005 }
4006
4007 /*
4008 * Show info.
4009 */
4010 const char *pcszModel;
4011 switch (pThis->uDevType)
4012 {
4013 case DEV_AM79C970A: pcszModel = "AM79C970A"; break;
4014 case DEV_AM79C973: pcszModel = "AM79C973"; break;
4015 case DEV_AM79C960: pcszModel = "AM79C960/NE2100"; break;
4016 case DEV_AM79C960_EB: pcszModel = "AM79C960/EtherBlaster"; break;
4017 default: pcszModel = "Unknown"; break;
4018 }
4019 pHlp->pfnPrintf(pHlp,
4020 "pcnet #%d: port=%RTiop", pDevIns->iInstance, pThis->IOPortBase);
4021 if (PCNET_IS_ISA(pThis))
4022 pHlp->pfnPrintf(pHlp, " irq=%RX32", pThis->uIsaIrq);
4023 else
4024 pHlp->pfnPrintf(pHlp, " mmio=%RX32", PDMDevHlpMmioGetMappingAddress(pDevIns, pThis->hMmioPci));
4025
4026 pHlp->pfnPrintf(pHlp,
4027 " mac-cfg=%RTmac %s%s%s\n",
4028 &pThis->MacConfigured, pcszModel, pDevIns->fRCEnabled ? " RC" : "", pDevIns->fR0Enabled ? " R0" : "");
4029
4030
4031 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
4032 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4033
4034 pHlp->pfnPrintf(pHlp,
4035 "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"
4036 " MISS=%d CERR=%d BABL=%d ERR=%d\n",
4037 pThis->aCSR[0],
4038 !!(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)),
4039 !!(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)),
4040 !!(pThis->aCSR[0] & RT_BIT(10)), !!(pThis->aCSR[0] & RT_BIT(11)), !!(pThis->aCSR[0] & RT_BIT(12)), !!(pThis->aCSR[0] & RT_BIT(13)),
4041 !!(pThis->aCSR[0] & RT_BIT(14)), !!(pThis->aCSR[0] & RT_BIT(15)));
4042
4043 pHlp->pfnPrintf(pHlp,
4044 "CSR1=%#06x:\n",
4045 pThis->aCSR[1]);
4046
4047 pHlp->pfnPrintf(pHlp,
4048 "CSR2=%#06x:\n",
4049 pThis->aCSR[2]);
4050
4051 pHlp->pfnPrintf(pHlp,
4052 "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",
4053 pThis->aCSR[3],
4054 !!(pThis->aCSR[3] & RT_BIT(2)), !!(pThis->aCSR[3] & RT_BIT(3)), !!(pThis->aCSR[3] & RT_BIT(4)), CSR_LAPPEN(pThis),
4055 CSR_DXSUFLO(pThis), !!(pThis->aCSR[3] & RT_BIT(8)), !!(pThis->aCSR[3] & RT_BIT(9)), !!(pThis->aCSR[3] & RT_BIT(10)),
4056 !!(pThis->aCSR[3] & RT_BIT(11)), !!(pThis->aCSR[3] & RT_BIT(12)), !!(pThis->aCSR[3] & RT_BIT(14)));
4057
4058 pHlp->pfnPrintf(pHlp,
4059 "CSR4=%#06x: JABM=%d JAB=%d TXSTRM=%d TXSTRT=%d RCVCOOM=%d RCVCCO=%d UINT=%d UINTCMD=%d\n"
4060 " MFCOM=%d MFCO=%d ASTRP_RCV=%d APAD_XMT=%d DPOLL=%d TIMER=%d EMAPLUS=%d EN124=%d\n",
4061 pThis->aCSR[4],
4062 !!(pThis->aCSR[4] & RT_BIT( 0)), !!(pThis->aCSR[4] & RT_BIT( 1)), !!(pThis->aCSR[4] & RT_BIT( 2)), !!(pThis->aCSR[4] & RT_BIT( 3)),
4063 !!(pThis->aCSR[4] & RT_BIT( 4)), !!(pThis->aCSR[4] & RT_BIT( 5)), !!(pThis->aCSR[4] & RT_BIT( 6)), !!(pThis->aCSR[4] & RT_BIT( 7)),
4064 !!(pThis->aCSR[4] & RT_BIT( 8)), !!(pThis->aCSR[4] & RT_BIT( 9)), !!(pThis->aCSR[4] & RT_BIT(10)), !!(pThis->aCSR[4] & RT_BIT(11)),
4065 !!(pThis->aCSR[4] & RT_BIT(12)), !!(pThis->aCSR[4] & RT_BIT(13)), !!(pThis->aCSR[4] & RT_BIT(14)), !!(pThis->aCSR[4] & RT_BIT(15)));
4066
4067 pHlp->pfnPrintf(pHlp,
4068 "CSR5=%#06x:\n",
4069 pThis->aCSR[5]);
4070
4071 pHlp->pfnPrintf(pHlp,
4072 "CSR6=%#06x: RLEN=%#x* TLEN=%#x* [* encoded]\n",
4073 pThis->aCSR[6],
4074 (pThis->aCSR[6] >> 8) & 0xf, (pThis->aCSR[6] >> 12) & 0xf);
4075
4076 pHlp->pfnPrintf(pHlp,
4077 "CSR8..11=%#06x,%#06x,%#06x,%#06x: LADRF=%#018llx\n",
4078 pThis->aCSR[8], pThis->aCSR[9], pThis->aCSR[10], pThis->aCSR[11],
4079 (uint64_t)(pThis->aCSR[ 8] & 0xffff)
4080 | (uint64_t)(pThis->aCSR[ 9] & 0xffff) << 16
4081 | (uint64_t)(pThis->aCSR[10] & 0xffff) << 32
4082 | (uint64_t)(pThis->aCSR[11] & 0xffff) << 48);
4083
4084 pHlp->pfnPrintf(pHlp,
4085 "CSR12..14=%#06x,%#06x,%#06x: PADR=%02x:%02x:%02x:%02x:%02x:%02x (Current MAC Address)\n",
4086 pThis->aCSR[12], pThis->aCSR[13], pThis->aCSR[14],
4087 pThis->aCSR[12] & 0xff,
4088 (pThis->aCSR[12] >> 8) & 0xff,
4089 pThis->aCSR[13] & 0xff,
4090 (pThis->aCSR[13] >> 8) & 0xff,
4091 pThis->aCSR[14] & 0xff,
4092 (pThis->aCSR[14] >> 8) & 0xff);
4093
4094 pHlp->pfnPrintf(pHlp,
4095 "CSR15=%#06x: DXR=%d DTX=%d LOOP=%d DXMTFCS=%d FCOLL=%d DRTY=%d INTL=%d PORTSEL=%d LTR=%d\n"
4096 " MENDECL=%d DAPC=%d DLNKTST=%d DRCVPV=%d DRCVBC=%d PROM=%d\n",
4097 pThis->aCSR[15],
4098 !!(pThis->aCSR[15] & RT_BIT( 0)), !!(pThis->aCSR[15] & RT_BIT( 1)), !!(pThis->aCSR[15] & RT_BIT( 2)), !!(pThis->aCSR[15] & RT_BIT( 3)),
4099 !!(pThis->aCSR[15] & RT_BIT( 4)), !!(pThis->aCSR[15] & RT_BIT( 5)), !!(pThis->aCSR[15] & RT_BIT( 6)), (pThis->aCSR[15] >> 7) & 3,
4100 !!(pThis->aCSR[15] & RT_BIT( 9)), !!(pThis->aCSR[15] & RT_BIT(10)), !!(pThis->aCSR[15] & RT_BIT(11)),
4101 !!(pThis->aCSR[15] & RT_BIT(12)), !!(pThis->aCSR[15] & RT_BIT(13)), !!(pThis->aCSR[15] & RT_BIT(14)), !!(pThis->aCSR[15] & RT_BIT(15)));
4102
4103 pHlp->pfnPrintf(pHlp,
4104 "CSR46=%#06x: POLL=%#06x (Poll Time Counter)\n",
4105 pThis->aCSR[46], pThis->aCSR[46] & 0xffff);
4106
4107 pHlp->pfnPrintf(pHlp,
4108 "CSR47=%#06x: POLLINT=%#06x (Poll Time Interval)\n",
4109 pThis->aCSR[47], pThis->aCSR[47] & 0xffff);
4110
4111 pHlp->pfnPrintf(pHlp,
4112 "CSR58=%#06x: SWSTYLE=%d %s SSIZE32=%d CSRPCNET=%d APERRENT=%d\n",
4113 pThis->aCSR[58],
4114 pThis->aCSR[58] & 0x7f,
4115 (pThis->aCSR[58] & 0x7f) == 0 ? "C-LANCE / PCnet-ISA"
4116 : (pThis->aCSR[58] & 0x7f) == 1 ? "ILACC"
4117 : (pThis->aCSR[58] & 0x7f) == 2 ? "PCnet-32"
4118 : (pThis->aCSR[58] & 0x7f) == 3 ? "PCnet-PCI II"
4119 : "!!reserved!!",
4120 !!(pThis->aCSR[58] & RT_BIT(8)), !!(pThis->aCSR[58] & RT_BIT(9)), !!(pThis->aCSR[58] & RT_BIT(10)));
4121
4122 pHlp->pfnPrintf(pHlp,
4123 "CSR112=%04RX32: MFC=%04x (Missed receive Frame Count)\n",
4124 pThis->aCSR[112], pThis->aCSR[112] & 0xffff);
4125
4126 pHlp->pfnPrintf(pHlp,
4127 "CSR122=%04RX32: RCVALGN=%04x (Receive Frame Align)\n",
4128 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(0)));
4129
4130 pHlp->pfnPrintf(pHlp,
4131 "CSR124=%04RX32: RPA=%04x (Runt Packet Accept)\n",
4132 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(3)));
4133
4134 if (pThis->uDevType == DEV_AM79C970A || pThis->uDevType == DEV_AM79C973)
4135 {
4136 /* Print the Burst and Bus Control Register; the DWIO bit is quite important. */
4137 pHlp->pfnPrintf(pHlp,
4138 "BCR18=%#04x: ROMTMG=%u MEMCMD=%u EXTREQ=%u\n"
4139 " DWIO=%u BREADE=%u BWRITE=%u\n",
4140 pThis->aBCR[18],
4141 (pThis->aBCR[18] >> 12) & 0xf, !!(pThis->aBCR[18] & RT_BIT(9)), !!(pThis->aBCR[18] & RT_BIT(8)),
4142 !!(pThis->aBCR[18] & RT_BIT(7)), !!(pThis->aBCR[18] & RT_BIT(6)), !!(pThis->aBCR[18] & RT_BIT(5)));
4143 }
4144
4145 if (pThis->uDevType == DEV_AM79C973)
4146 {
4147 /* Print a bit of the MII state. */
4148 pHlp->pfnPrintf(pHlp,
4149 "BCR32=%#06x: MIIILP=%d XPHYSP=%d XPHYFD=%d XPHYANE=%d XPHYRST=%d\n"
4150 " DANAS=%d APDW=%u APEP=%d FMDC=%u MIIPD=%d ANTST=%d\n",
4151 pThis->aBCR[32],
4152 !!(pThis->aBCR[32] & RT_BIT( 1)), !!(pThis->aBCR[32] & RT_BIT( 3)), !!(pThis->aBCR[32] & RT_BIT( 4)),
4153 !!(pThis->aBCR[32] & RT_BIT( 5)), !!(pThis->aBCR[32] & RT_BIT( 6)), !!(pThis->aBCR[32] & RT_BIT( 7)),
4154 (pThis->aBCR[32] >> 8) & 0x7,
4155 !!(pThis->aBCR[32] & RT_BIT(11)),
4156 (pThis->aBCR[32] >> 12) & 0x3,
4157 !!(pThis->aBCR[32] & RT_BIT(14)), !!(pThis->aBCR[32] & RT_BIT(15)));
4158 }
4159
4160 /*
4161 * Dump the receive ring.
4162 */
4163 pHlp->pfnPrintf(pHlp,
4164 "RCVRL=%04x RCVRC=%04x GCRDRA=%RX32 \n"
4165 "CRDA=%08RX32 CRBA=%08RX32 CRBC=%03x CRST=%04x\n"
4166 "NRDA=%08RX32 NRBA=%08RX32 NRBC=%03x NRST=%04x\n"
4167 "NNRDA=%08RX32\n"
4168 ,
4169 CSR_RCVRL(pThis), CSR_RCVRC(pThis), pThis->GCRDRA,
4170 CSR_CRDA(pThis), CSR_CRBA(pThis), CSR_CRBC(pThis), CSR_CRST(pThis),
4171 CSR_NRDA(pThis), CSR_NRBA(pThis), CSR_NRBC(pThis), CSR_NRST(pThis),
4172 CSR_NNRD(pThis));
4173 if (fRcvRing)
4174 {
4175 const unsigned cb = 1 << pThis->iLog2DescSize;
4176 RTGCPHYS32 GCPhys = pThis->GCRDRA;
4177 unsigned i = CSR_RCVRL(pThis);
4178 while (i-- > 0)
4179 {
4180 RMD rmd;
4181 pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, GCPhys), false);
4182 pHlp->pfnPrintf(pHlp,
4183 "%04x %RX32:%c%c RBADR=%08RX32 BCNT=%03x MCNT=%03x "
4184 "OWN=%d ERR=%d FRAM=%d OFLO=%d CRC=%d BUFF=%d STP=%d ENP=%d BPE=%d "
4185 "PAM=%d LAFM=%d BAM=%d RCC=%02x RPC=%02x ONES=%#x ZEROS=%d\n",
4186 i, GCPhys, i + 1 == CSR_RCVRC(pThis) ? '*' : ' ', GCPhys == CSR_CRDA(pThis) ? '*' : ' ',
4187 rmd.rmd0.rbadr, 4096 - rmd.rmd1.bcnt, rmd.rmd2.mcnt,
4188 rmd.rmd1.own, rmd.rmd1.err, rmd.rmd1.fram, rmd.rmd1.oflo, rmd.rmd1.crc, rmd.rmd1.buff,
4189 rmd.rmd1.stp, rmd.rmd1.enp, rmd.rmd1.bpe,
4190 rmd.rmd1.pam, rmd.rmd1.lafm, rmd.rmd1.bam, rmd.rmd2.rcc, rmd.rmd2.rpc,
4191 rmd.rmd1.ones, rmd.rmd2.zeros);
4192
4193 GCPhys += cb;
4194 }
4195 }
4196
4197 /*
4198 * Dump the transmit ring.
4199 */
4200 pHlp->pfnPrintf(pHlp,
4201 "XMTRL=%04x XMTRC=%04x GCTDRA=%08RX32 BADX=%08RX32\n"
4202 "PXDA=%08RX32 PXBC=%03x PXST=%04x\n"
4203 "CXDA=%08RX32 CXBA=%08RX32 CXBC=%03x CXST=%04x\n"
4204 "NXDA=%08RX32 NXBA=%08RX32 NXBC=%03x NXST=%04x\n"
4205 "NNXDA=%08RX32\n"
4206 ,
4207 CSR_XMTRL(pThis), CSR_XMTRC(pThis),
4208 pThis->GCTDRA, CSR_BADX(pThis),
4209 CSR_PXDA(pThis), CSR_PXBC(pThis), CSR_PXST(pThis),
4210 CSR_CXDA(pThis), CSR_CXBA(pThis), CSR_CXBC(pThis), CSR_CXST(pThis),
4211 CSR_NXDA(pThis), CSR_NXBA(pThis), CSR_NXBC(pThis), CSR_NXST(pThis),
4212 CSR_NNXD(pThis));
4213 if (fXmtRing)
4214 {
4215 const unsigned cb = 1 << pThis->iLog2DescSize;
4216 RTGCPHYS32 GCPhys = pThis->GCTDRA;
4217 unsigned i = CSR_XMTRL(pThis);
4218 while (i-- > 0)
4219 {
4220 TMD tmd;
4221 pcnetTmdLoadAll(pDevIns, pThis, &tmd, PHYSADDR(pThis, GCPhys));
4222 pHlp->pfnPrintf(pHlp,
4223 "%04x %RX32:%c%c TBADR=%08RX32 BCNT=%03x OWN=%d "
4224 "ERR=%d NOFCS=%d LTINT=%d ONE=%d DEF=%d STP=%d ENP=%d BPE=%d "
4225 "BUFF=%d UFLO=%d EXDEF=%d LCOL=%d LCAR=%d RTRY=%d TDR=%03x TRC=%#x ONES=%#x\n"
4226 ,
4227 i,
4228 GCPhys,
4229 i + 1 == CSR_XMTRC(pThis) ? '*' : ' ',
4230 GCPhys == CSR_CXDA(pThis) ? '*' : ' ',
4231 tmd.tmd0.tbadr,
4232 4096 - tmd.tmd1.bcnt,
4233 tmd.tmd1.own,
4234 tmd.tmd1.err,
4235 tmd.tmd1.nofcs,
4236 tmd.tmd1.ltint,
4237 tmd.tmd1.one,
4238 tmd.tmd1.def,
4239 tmd.tmd1.stp,
4240 tmd.tmd1.enp,
4241 tmd.tmd1.bpe,
4242 tmd.tmd2.buff,
4243 tmd.tmd2.uflo,
4244 tmd.tmd2.exdef,
4245 tmd.tmd2.lcol,
4246 tmd.tmd2.lcar,
4247 tmd.tmd2.rtry,
4248 tmd.tmd2.tdr,
4249 tmd.tmd2.trc,
4250 tmd.tmd1.ones);
4251
4252 GCPhys += cb;
4253 }
4254 }
4255
4256 /* Dump the Addres PROM (APROM). */
4257 if (fAPROM)
4258 {
4259 pHlp->pfnPrintf(pHlp,
4260 "Address PROM:\n %Rhxs\n", pThis->aPROM);
4261
4262
4263 }
4264
4265 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4266}
4267
4268
4269/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
4270
4271/**
4272 * Takes down the link temporarily if it's current status is up.
4273 *
4274 * This is used during restore and when replumbing the network link.
4275 *
4276 * The temporary link outage is supposed to indicate to the OS that all network
4277 * connections have been lost and that it for instance is appropriate to
4278 * renegotiate any DHCP lease.
4279 *
4280 * @param pThis The PCnet shared instance data.
4281 */
4282static void pcnetR3TempLinkDown(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
4283{
4284 if (pThis->fLinkUp)
4285 {
4286 pThis->fLinkTempDown = true;
4287 pThis->cLinkDownReported = 0;
4288 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4289 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4290 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
4291 AssertRC(rc);
4292 }
4293}
4294
4295
4296/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
4297
4298/**
4299 * Saves the configuration.
4300 *
4301 * @param pHlp The device helpers.
4302 * @param pThis The PCnet shared instance data.
4303 * @param pSSM The saved state handle.
4304 */
4305static void pcnetR3SaveConfig(PCPDMDEVHLPR3 pHlp, PPCNETSTATE pThis, PSSMHANDLE pSSM)
4306{
4307 pHlp->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4308 pHlp->pfnSSMPutU8(pSSM, pThis->uDevType);
4309 pHlp->pfnSSMPutU32(pSSM, pThis->u32LinkSpeed);
4310}
4311
4312
4313/**
4314 * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
4315 */
4316static DECLCALLBACK(int) pcnetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
4317{
4318 RT_NOREF(uPass);
4319 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4320 pcnetR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
4321 return VINF_SSM_DONT_CALL_AGAIN;
4322}
4323
4324
4325/**
4326 * @callback_method_impl{FNSSMDEVSAVEPREP,
4327 * Serializes the receive thread, it may be working inside the critsect.}
4328 */
4329static DECLCALLBACK(int) pcnetR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4330{
4331 RT_NOREF(pSSM);
4332 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4333
4334 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4335 AssertRCReturn(rc, rc);
4336 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4337 return VINF_SUCCESS;
4338}
4339
4340
4341/**
4342 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4343 */
4344static DECLCALLBACK(int) pcnetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4345{
4346 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4347 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4348
4349 pHlp->pfnSSMPutBool(pSSM, pThis->fLinkUp);
4350 pHlp->pfnSSMPutU32(pSSM, pThis->u32RAP);
4351 pHlp->pfnSSMPutS32(pSSM, pThis->iISR);
4352 pHlp->pfnSSMPutU32(pSSM, pThis->u32Lnkst);
4353 pHlp->pfnSSMPutBool(pSSM, false/* was ffPrivIfEnabled */); /* >= If version 0.9 */
4354 pHlp->pfnSSMPutBool(pSSM, pThis->fSignalRxMiss); /* >= If version 0.10 */
4355 pHlp->pfnSSMPutGCPhys32(pSSM, pThis->GCRDRA);
4356 pHlp->pfnSSMPutGCPhys32(pSSM, pThis->GCTDRA);
4357 pHlp->pfnSSMPutMem(pSSM, pThis->aPROM, sizeof(pThis->aPROM));
4358 pHlp->pfnSSMPutMem(pSSM, pThis->aCSR, sizeof(pThis->aCSR));
4359 pHlp->pfnSSMPutMem(pSSM, pThis->aBCR, sizeof(pThis->aBCR));
4360 pHlp->pfnSSMPutMem(pSSM, pThis->aMII, sizeof(pThis->aMII));
4361 pHlp->pfnSSMPutU16(pSSM, pThis->u16CSR0LastSeenByGuest);
4362 pHlp->pfnSSMPutU64(pSSM, pThis->u64LastPoll);
4363 pcnetR3SaveConfig(pHlp, pThis, pSSM);
4364
4365 int rc = VINF_SUCCESS;
4366 rc = PDMDevHlpTimerSave(pDevIns, pThis->hTimerPoll, pSSM);
4367 if (RT_FAILURE(rc))
4368 return rc;
4369 if (pThis->uDevType == DEV_AM79C973)
4370 rc = PDMDevHlpTimerSave(pDevIns, pThis->hTimerSoftInt, pSSM);
4371 return rc;
4372}
4373
4374
4375/**
4376 * @callback_method_impl{FNSSMDEVLOADPREP},
4377 * Serializes the receive thread, it may be working inside the critsect.}
4378 */
4379static DECLCALLBACK(int) pcnetR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4380{
4381 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4382 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4383
4384 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4385 AssertRCReturn(rc, rc);
4386
4387 uint32_t uVer = pHlp->pfnSSMHandleVersion(pSSM);
4388 if ( uVer < VBOX_FULL_VERSION_MAKE(4, 3, 6)
4389 || ( uVer >= VBOX_FULL_VERSION_MAKE(4, 3, 51)
4390 && uVer < VBOX_FULL_VERSION_MAKE(4, 3, 53)))
4391 {
4392 /* older saved states contain the shared memory region which was never used for ages. */
4393 void *pvSharedMMIOR3;
4394 rc = PDMDevHlpMmio2Create(pDevIns, pDevIns->apPciDevs[0], 2, _512K, 0, "PCnetSh", &pvSharedMMIOR3, &pThis->hMmio2Shared);
4395 if (RT_FAILURE(rc))
4396 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
4397 N_("Failed to allocate the dummy shmem region for the PCnet device"));
4398 }
4399
4400 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4401 return rc;
4402}
4403
4404
4405/**
4406 * @callback_method_impl{FNSSMDEVLOADEXEC}
4407 */
4408static DECLCALLBACK(int) pcnetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4409{
4410 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4411 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4412 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4413
4414 if ( SSM_VERSION_MAJOR_CHANGED(uVersion, PCNET_SAVEDSTATE_VERSION)
4415 || SSM_VERSION_MINOR(uVersion) < 7)
4416 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4417
4418 if (uPass == SSM_PASS_FINAL)
4419 {
4420 /* restore data */
4421 pHlp->pfnSSMGetBool(pSSM, &pThis->fLinkUp);
4422 int rc = pHlp->pfnSSMGetU32(pSSM, &pThis->u32RAP);
4423 AssertRCReturn(rc, rc);
4424 AssertLogRelMsgReturn(pThis->u32RAP < RT_ELEMENTS(pThis->aCSR), ("%#x\n", pThis->u32RAP), VERR_SSM_LOAD_CONFIG_MISMATCH);
4425 pHlp->pfnSSMGetS32(pSSM, &pThis->iISR);
4426 pHlp->pfnSSMGetU32(pSSM, &pThis->u32Lnkst);
4427 if ( SSM_VERSION_MAJOR(uVersion) > 0
4428 || SSM_VERSION_MINOR(uVersion) >= 9)
4429 {
4430 bool fPrivIfEnabled = false;
4431 pHlp->pfnSSMGetBool(pSSM, &fPrivIfEnabled);
4432 if (fPrivIfEnabled)
4433 {
4434 /* no longer implemented */
4435 LogRel(("PCnet#%d: Cannot enable private interface!\n", PCNET_INST_NR));
4436 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4437 }
4438 }
4439 if ( SSM_VERSION_MAJOR(uVersion) > 0
4440 || SSM_VERSION_MINOR(uVersion) >= 10)
4441 pHlp->pfnSSMGetBool(pSSM, &pThis->fSignalRxMiss);
4442 pHlp->pfnSSMGetGCPhys32(pSSM, &pThis->GCRDRA);
4443 pHlp->pfnSSMGetGCPhys32(pSSM, &pThis->GCTDRA);
4444 pHlp->pfnSSMGetMem(pSSM, &pThis->aPROM, sizeof(pThis->aPROM));
4445 pHlp->pfnSSMGetMem(pSSM, &pThis->aCSR, sizeof(pThis->aCSR));
4446 pHlp->pfnSSMGetMem(pSSM, &pThis->aBCR, sizeof(pThis->aBCR));
4447 pHlp->pfnSSMGetMem(pSSM, &pThis->aMII, sizeof(pThis->aMII));
4448 pHlp->pfnSSMGetU16(pSSM, &pThis->u16CSR0LastSeenByGuest);
4449 pHlp->pfnSSMGetU64(pSSM, &pThis->u64LastPoll);
4450 }
4451
4452 /* check config */
4453 RTMAC Mac;
4454 int rc = pHlp->pfnSSMGetMem(pSSM, &Mac, sizeof(Mac));
4455 AssertRCReturn(rc, rc);
4456 if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
4457 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4458 LogRel(("PCnet#%u: The mac address differs: config=%RTmac saved=%RTmac\n", PCNET_INST_NR, &pThis->MacConfigured, &Mac));
4459
4460 uint8_t uDevType;
4461 rc = pHlp->pfnSSMGetU8(pSSM, &uDevType);
4462 AssertRCReturn(rc, rc);
4463 if (pThis->uDevType != uDevType)
4464 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("The uDevType setting differs: config=%u saved=%u"), pThis->uDevType, uDevType);
4465
4466 uint32_t u32LinkSpeed;
4467 rc = pHlp->pfnSSMGetU32(pSSM, &u32LinkSpeed);
4468 AssertRCReturn(rc, rc);
4469 if ( pThis->u32LinkSpeed != u32LinkSpeed
4470 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4471 LogRel(("PCnet#%u: The mac link speed differs: config=%u saved=%u\n", PCNET_INST_NR, pThis->u32LinkSpeed, u32LinkSpeed));
4472
4473 if (uPass == SSM_PASS_FINAL)
4474 {
4475 /* restore timers and stuff */
4476 PDMDevHlpTimerLoad(pDevIns, pThis->hTimerPoll, pSSM);
4477 if (pThis->uDevType == DEV_AM79C973)
4478 {
4479 if ( SSM_VERSION_MAJOR(uVersion) > 0
4480 || SSM_VERSION_MINOR(uVersion) >= 8)
4481 PDMDevHlpTimerLoad(pDevIns, pThis->hTimerSoftInt, pSSM);
4482 }
4483
4484 pThis->iLog2DescSize = BCR_SWSTYLE(pThis)
4485 ? 4
4486 : 3;
4487 pThis->GCUpperPhys = BCR_SSIZE32(pThis)
4488 ? 0
4489 : (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
4490
4491 /* update promiscuous mode. */
4492 if (pThisCC->pDrv)
4493 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, CSR_PROM(pThis));
4494
4495 /* Indicate link down to the guest OS that all network connections have
4496 been lost, unless we've been teleported here. */
4497 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
4498 pcnetR3TempLinkDown(pDevIns, pThis);
4499 }
4500
4501 return VINF_SUCCESS;
4502}
4503
4504/**
4505 * @callback_method_impl{FNSSMDEVLOADDONE}
4506 */
4507static DECLCALLBACK(int) pcnetR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4508{
4509 RT_NOREF(pSSM);
4510 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4511 int rc = VINF_SUCCESS;
4512 if (pThis->hMmio2Shared != NIL_PGMMMIO2HANDLE)
4513 {
4514 /* drop this dummy region */
4515 rc = pDevIns->pHlpR3->pfnMmio2Destroy(pDevIns, pThis->hMmio2Shared);
4516 AssertLogRelRC(rc);
4517 pThis->hMmio2Shared = NIL_PGMMMIO2HANDLE;
4518 }
4519 return rc;
4520}
4521
4522/* -=-=-=-=-=- PCNETSTATE::INetworkDown -=-=-=-=-=- */
4523
4524/**
4525 * Check if the device/driver can receive data now.
4526 *
4527 * Worker for pcnetR3NetworkDown_WaitReceiveAvail(). This must be called before
4528 * the pfnRecieve() method is called.
4529 *
4530 * @returns VBox status code.
4531 * @param pDevIns The device instance.
4532 * @param pThis The PCnet shared instance data.
4533 */
4534static int pcnetR3CanReceive(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
4535{
4536 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4537 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
4538
4539 rc = VERR_NET_NO_BUFFER_SPACE;
4540
4541 if (RT_LIKELY(!CSR_DRX(pThis) && !CSR_STOP(pThis) && !CSR_SPND(pThis)))
4542 {
4543 if (HOST_IS_OWNER(CSR_CRST(pThis)) && pThis->GCRDRA)
4544 pcnetRdtePoll(pDevIns, pThis);
4545
4546 if (RT_UNLIKELY(HOST_IS_OWNER(CSR_CRST(pThis))))
4547 {
4548 /** @todo Notify the guest _now_. Will potentially increase the interrupt load */
4549 if (pThis->fSignalRxMiss)
4550 pThis->aCSR[0] |= 0x1000; /* Set MISS flag */
4551 }
4552 else
4553 rc = VINF_SUCCESS;
4554 }
4555
4556 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4557 return rc;
4558}
4559
4560
4561/**
4562 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
4563 */
4564static DECLCALLBACK(int) pcnetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
4565{
4566 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
4567 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4568 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4569
4570 int rc = pcnetR3CanReceive(pDevIns, pThis);
4571 if (RT_SUCCESS(rc))
4572 return VINF_SUCCESS;
4573 if (RT_UNLIKELY(cMillies == 0))
4574 return VERR_NET_NO_BUFFER_SPACE;
4575
4576 rc = VERR_INTERRUPTED;
4577 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
4578 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
4579 VMSTATE enmVMState;
4580 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
4581 || enmVMState == VMSTATE_RUNNING_LS))
4582 {
4583 int rc2 = pcnetR3CanReceive(pDevIns, pThis);
4584 if (RT_SUCCESS(rc2))
4585 {
4586 rc = VINF_SUCCESS;
4587 break;
4588 }
4589 LogFlow(("pcnetR3NetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", cMillies));
4590 /* Start the poll timer once which will remain active as long fMaybeOutOfSpace
4591 * is true -- even if (transmit) polling is disabled (CSR_DPOLL). */
4592 rc2 = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4593 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc2);
4594 pcnetPollTimerStart(pDevIns, pThis);
4595 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4596 PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventOutOfRxSpace, cMillies);
4597 }
4598 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
4599 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
4600
4601 return rc;
4602}
4603
4604
4605/**
4606 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
4607 */
4608static DECLCALLBACK(int) pcnetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
4609{
4610 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
4611 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4612 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4613
4614 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
4615 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4616 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
4617
4618 /*
4619 * Check for the max ethernet frame size, taking the IEEE 802.1Q (VLAN) tag into
4620 * account. Note that the CRC Checksum is optional.
4621 * Ethernet frames consist of a 14-byte header [+ 4-byte vlan tag] + a 1500-byte body [+ 4-byte CRC].
4622 */
4623 if (RT_LIKELY( cb <= 1518
4624 || ( cb <= 1522
4625 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN))))
4626 {
4627 bool fAddFCS = cb <= 1514
4628 || ( cb <= 1518
4629 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN));
4630 if (cb > 70) /* unqualified guess */
4631 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
4632 pcnetReceiveNoSync(pDevIns, pThis, pThisCC, (const uint8_t *)pvBuf, cb, fAddFCS, false);
4633 pThis->Led.Actual.s.fReading = 0;
4634 }
4635#ifdef LOG_ENABLED
4636 else
4637 {
4638 static bool s_fFirstBigFrameLoss = true;
4639 unsigned cbMaxFrame = ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN)
4640 ? 1522 : 1518;
4641 if (s_fFirstBigFrameLoss)
4642 {
4643 s_fFirstBigFrameLoss = false;
4644 Log(("PCnet#%d: Received giant frame %zu, max %u. (Further giants will be reported at level5.)\n",
4645 PCNET_INST_NR, cb, cbMaxFrame));
4646 }
4647 else
4648 Log5(("PCnet#%d: Received giant frame %zu bytes, max %u.\n",
4649 PCNET_INST_NR, cb, cbMaxFrame));
4650 }
4651#endif /* LOG_ENABLED */
4652
4653 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4654 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
4655
4656 return VINF_SUCCESS;
4657}
4658
4659
4660/**
4661 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
4662 */
4663static DECLCALLBACK(void) pcnetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
4664{
4665 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
4666 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4667 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4668 pcnetXmitPending(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
4669}
4670
4671
4672/* -=-=-=-=-=- PCNETSTATE::INetworkConfig -=-=-=-=-=- */
4673
4674/**
4675 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
4676 */
4677static DECLCALLBACK(int) pcnetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
4678{
4679 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
4680 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4681 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4682 memcpy(pMac, pThis->aPROM, sizeof(*pMac));
4683 return VINF_SUCCESS;
4684}
4685
4686
4687/**
4688 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
4689 */
4690static DECLCALLBACK(PDMNETWORKLINKSTATE) pcnetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
4691{
4692 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
4693 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4694 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4695 if (pThis->fLinkUp && !pThis->fLinkTempDown)
4696 return PDMNETWORKLINKSTATE_UP;
4697 if (!pThis->fLinkUp)
4698 return PDMNETWORKLINKSTATE_DOWN;
4699 if (pThis->fLinkTempDown)
4700 return PDMNETWORKLINKSTATE_DOWN_RESUME;
4701 AssertMsgFailed(("Invalid link state!\n"));
4702 return PDMNETWORKLINKSTATE_INVALID;
4703}
4704
4705
4706/**
4707 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
4708 */
4709static DECLCALLBACK(int) pcnetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
4710{
4711 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
4712 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4713 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4714 bool fLinkUp;
4715
4716 AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
4717 ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
4718
4719 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
4720 {
4721 pcnetR3TempLinkDown(pDevIns, pThis);
4722 /*
4723 * Note that we do not notify the driver about the link state change because
4724 * the change is only temporary and can be disregarded from the driver's
4725 * point of view (see @bugref{7057}).
4726 */
4727 return VINF_SUCCESS;
4728 }
4729 /* has the state changed? */
4730 fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
4731 if (pThis->fLinkUp != fLinkUp)
4732 {
4733 pThis->fLinkUp = fLinkUp;
4734 if (fLinkUp)
4735 {
4736 /* Connect with a configured delay. */
4737 pThis->fLinkTempDown = true;
4738 pThis->cLinkDownReported = 0;
4739 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4740 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4741 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
4742 AssertRC(rc);
4743 }
4744 else
4745 {
4746 /* disconnect */
4747 pThis->cLinkDownReported = 0;
4748 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4749 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4750 }
4751 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
4752 if (pThisCC->pDrv)
4753 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
4754 }
4755 return VINF_SUCCESS;
4756}
4757
4758
4759/* -=-=-=-=-=- PCNETSTATE::ILeds (LUN#0) -=-=-=-=-=- */
4760
4761/**
4762 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4763 */
4764static DECLCALLBACK(int) pcnetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4765{
4766 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, ILeds);
4767 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4768 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4769 if (iLUN == 0)
4770 {
4771 *ppLed = &pThis->Led;
4772 return VINF_SUCCESS;
4773 }
4774 return VERR_PDM_LUN_NOT_FOUND;
4775}
4776
4777
4778/* -=-=-=-=-=- PCNETSTATE::IBase (LUN#0) -=-=-=-=-=- */
4779
4780/**
4781 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4782 */
4783static DECLCALLBACK(void *) pcnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
4784{
4785 PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, IBase);
4786 Assert(&pThisCC->IBase == pInterface);
4787 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4788 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
4789 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
4790 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4791 return NULL;
4792}
4793
4794
4795/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
4796
4797/**
4798 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
4799 */
4800static DECLCALLBACK(void) pcnetR3PowerOff(PPDMDEVINS pDevIns)
4801{
4802 /* Poke thread waiting for buffer space. */
4803 pcnetWakeupReceive(pDevIns);
4804}
4805
4806
4807/**
4808 * @interface_method_impl{PDMDEVREG,pfnDetach}
4809 *
4810 * One port on the network card has been disconnected from the network.
4811 */
4812static DECLCALLBACK(void) pcnetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4813{
4814 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4815 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4816 Log(("#%d pcnetR3Detach:\n", PCNET_INST_NR));
4817 RT_NOREF(fFlags);
4818
4819 AssertLogRelReturnVoid(iLUN == 0);
4820
4821 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4822 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4823
4824 /** @todo r=pritesh still need to check if i missed
4825 * to clean something in this function
4826 */
4827
4828 /*
4829 * Zero some important members.
4830 */
4831 pThis->fDriverAttached = false;
4832 pThisCC->pDrvBase = NULL;
4833 pThisCC->pDrv = NULL;
4834 /// @todo figure this out
4835 //pThis->pDrvR0 = NIL_RTR0PTR;
4836 //pThis->pDrvRC = NIL_RTRCPTR;
4837
4838 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4839}
4840
4841
4842/**
4843 * @interface_method_impl{PDMDEVREG,pfnAttach}
4844 * One port on the network card has been connected to a network.
4845 */
4846static DECLCALLBACK(int) pcnetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4847{
4848 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4849 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4850 LogFlow(("#%d pcnetR3Attach:\n", PCNET_INST_NR));
4851 RT_NOREF(fFlags);
4852
4853 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
4854
4855 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4856 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
4857
4858 /*
4859 * Attach the driver.
4860 */
4861 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
4862 if (RT_SUCCESS(rc))
4863 {
4864 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
4865 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
4866 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
4867 pThis->fDriverAttached = true;
4868 /// @todo figoure out this
4869 //pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
4870 //pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
4871 }
4872 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
4873 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
4874 {
4875 /* This should never happen because this function is not called
4876 * if there is no driver to attach! */
4877 Log(("#%d No attached driver!\n", PCNET_INST_NR));
4878 }
4879
4880 /*
4881 * Temporary set the link down if it was up so that the guest
4882 * will know that we have change the configuration of the
4883 * network card
4884 */
4885 if (RT_SUCCESS(rc))
4886 pcnetR3TempLinkDown(pDevIns, pThis);
4887
4888 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4889 return rc;
4890
4891}
4892
4893
4894/**
4895 * @interface_method_impl{PDMDEVREG,pfnSuspend}
4896 */
4897static DECLCALLBACK(void) pcnetR3Suspend(PPDMDEVINS pDevIns)
4898{
4899 /* Poke thread waiting for buffer space. */
4900 pcnetWakeupReceive(pDevIns);
4901}
4902
4903
4904/**
4905 * @interface_method_impl{PDMDEVREG,pfnReset}
4906 */
4907static DECLCALLBACK(void) pcnetR3Reset(PPDMDEVINS pDevIns)
4908{
4909 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4910 if (pThis->fLinkTempDown)
4911 {
4912 pThis->cLinkDownReported = 0x10000;
4913 PDMDevHlpTimerStop(pDevIns, pThis->hTimerRestore);
4914 pcnetR3TimerRestore(pDevIns, pThis->hTimerRestore, pThis);
4915 }
4916
4917 /** @todo How to flush the queues? */
4918 pcnetR3HardReset(pDevIns, pThis);
4919}
4920
4921
4922/**
4923 * @interface_method_impl{PDMDEVREG,pfnRelocate}
4924 */
4925static DECLCALLBACK(void) pcnetR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4926{
4927 PPCNETSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PPCNETSTATERC);
4928 pThisRC->pDrv += offDelta;
4929}
4930
4931
4932/**
4933 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4934 */
4935static DECLCALLBACK(int) pcnetR3Destruct(PPDMDEVINS pDevIns)
4936{
4937 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
4938 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4939
4940 if (pThis->hEventOutOfRxSpace == NIL_SUPSEMEVENT)
4941 {
4942 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventOutOfRxSpace);
4943 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventOutOfRxSpace);
4944 pThis->hEventOutOfRxSpace = NIL_SUPSEMEVENT;
4945 }
4946
4947 if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->CritSect))
4948 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
4949 return VINF_SUCCESS;
4950}
4951
4952
4953/**
4954 * @interface_method_impl{PDMDEVREG,pfnConstruct}
4955 */
4956static DECLCALLBACK(int) pcnetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
4957{
4958 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
4959 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
4960 PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
4961 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4962 PPDMIBASE pBase;
4963 char szTmp[128];
4964 int rc;
4965
4966 Assert(RT_ELEMENTS(pThis->aBCR) == BCR_MAX_RAP);
4967 Assert(RT_ELEMENTS(pThis->aMII) == MII_MAX_REG);
4968 Assert(sizeof(pThis->abLoopBuf) == RT_ALIGN_Z(sizeof(pThis->abLoopBuf), 16));
4969
4970 /*
4971 * Init what's required to make the destructor safe.
4972 */
4973 pThis->iInstance = iInstance;
4974 pThis->hEventOutOfRxSpace = NIL_SUPSEMEVENT;
4975 pThis->hIoPortsPci = NIL_IOMIOPORTHANDLE;
4976 pThis->hIoPortsPciAProm = NIL_IOMIOPORTHANDLE;
4977 pThis->hIoPortsIsa = NIL_IOMIOPORTHANDLE;
4978 pThis->hIoPortsIsaAProm = NIL_IOMIOPORTHANDLE;
4979 pThis->hMmio2Shared = NIL_PGMMMIO2HANDLE;
4980 pThisCC->pDevIns = pDevIns;
4981
4982 /*
4983 * Validate configuration.
4984 */
4985 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
4986 "MAC|CableConnected|Am79C973|ChipType|Port|IRQ|LineSpeed|PrivIfEnabled|LinkUpDelay|StatNo",
4987 "");
4988 /*
4989 * Read the configuration.
4990 */
4991 rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4992 if (RT_FAILURE(rc))
4993 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"MAC\" value"));
4994 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
4995 if (RT_FAILURE(rc))
4996 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"CableConnected\" value"));
4997
4998 /*
4999 * Determine the model.
5000 */
5001 char szChipType[16];
5002 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "ChipType", &szChipType[0], sizeof(szChipType), "Am79C970A");
5003 if (RT_FAILURE(rc))
5004 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ChipType\" as string failed"));
5005
5006 if (!strcmp(szChipType, "Am79C970A"))
5007 pThis->uDevType = DEV_AM79C970A; /* 10 Mbps PCnet-PCI II. */
5008 else if (!strcmp(szChipType, "Am79C973"))
5009 pThis->uDevType = DEV_AM79C973; /* 10/100 Mbps PCnet-FAST III. */
5010 else if (!strcmp(szChipType, "Am79C960"))
5011 pThis->uDevType = DEV_AM79C960; /* 10 Mbps PCnet-ISA, NE2100/Am2100 compatible. */
5012 else if (!strcmp(szChipType, "Am79C960_EB"))
5013 {
5014 pThis->uDevType = DEV_AM79C960_EB; /* 10 Mbps PCnet-ISA, Racal InterLink NI6510 EtherBlaster compatible. */
5015 /* NI6510 drivers (at least Racal's and Linux) require the OUI to be InterLan's (Racal-Datacom).
5016 * Refuse loading if OUI doesn't match, because otherwise drivers won't load in the guest. */
5017 if (memcmp(&pThis->MacConfigured, "\x02\x07\x01", 3))
5018 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5019 N_("Configuration error: MAC address OUI for EtherBlaster must be 02 07 01"));
5020 }
5021 else
5022 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5023 N_("Configuration error: The \"ChipType\" value \"%s\" is unsupported"),
5024 szChipType);
5025
5026
5027 /*
5028 * Process the old model configuration. If present, it must take precedence for saved state compatibility.
5029 */
5030 bool fAm79C973;
5031 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Am79C973", &fAm79C973, false);
5032 if (RT_FAILURE(rc))
5033 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Am79C973\" value"));
5034 if (fAm79C973)
5035 pThis->uDevType = DEV_AM79C973;
5036
5037 /*
5038 * Process ISA configuration options. The defaults are chosen to be NE2100/Am2100 compatible.
5039 */
5040 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->IOPortBase, 0x300);
5041 if (RT_FAILURE(rc))
5042 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Port\" value"));
5043
5044 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, 3);
5045 if (RT_FAILURE(rc))
5046 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"IRQ\" value"));
5047
5048 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LineSpeed", &pThis->u32LinkSpeed, 1000000); /* 1GBit/s (in kbps units)*/
5049 if (RT_FAILURE(rc))
5050 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"LineSpeed\" value"));
5051
5052 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
5053 if (RT_FAILURE(rc))
5054 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
5055 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
5056 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
5057 LogRel(("PCnet#%d WARNING! Link up delay is set to %u seconds!\n", iInstance, pThis->cMsLinkUpDelay / 1000));
5058 Log(("#%d Link up delay is set to %u seconds\n", iInstance, pThis->cMsLinkUpDelay / 1000));
5059
5060 uint32_t uStatNo = iInstance;
5061 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
5062 if (RT_FAILURE(rc))
5063 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
5064
5065 /*
5066 * Initialize data (most of it anyway).
5067 */
5068 pThis->Led.u32Magic = PDMLED_MAGIC;
5069 /* IBase */
5070 pThisCC->IBase.pfnQueryInterface = pcnetQueryInterface;
5071 /* INeworkPort */
5072 pThisCC->INetworkDown.pfnWaitReceiveAvail = pcnetR3NetworkDown_WaitReceiveAvail;
5073 pThisCC->INetworkDown.pfnReceive = pcnetR3NetworkDown_Receive;
5074 pThisCC->INetworkDown.pfnXmitPending = pcnetR3NetworkDown_XmitPending;
5075 /* INetworkConfig */
5076 pThisCC->INetworkConfig.pfnGetMac = pcnetR3NetworkConfig_GetMac;
5077 pThisCC->INetworkConfig.pfnGetLinkState = pcnetR3NetworkConfig_GetLinkState;
5078 pThisCC->INetworkConfig.pfnSetLinkState = pcnetR3NetworkConfig_SetLinkState;
5079 /* ILeds */
5080 pThisCC->ILeds.pfnQueryStatusLed = pcnetQueryStatusLed;
5081
5082 /* PCI Device */
5083 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
5084 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
5085
5086 PDMPciDevSetVendorId(pPciDev, 0x1022);
5087 PDMPciDevSetDeviceId(pPciDev, 0x2000);
5088 PDMPciDevSetByte(pPciDev, 0x04, 0x07); /* command */
5089 PDMPciDevSetByte(pPciDev, 0x05, 0x00);
5090 PDMPciDevSetByte(pPciDev, 0x06, 0x80); /* status */
5091 PDMPciDevSetByte(pPciDev, 0x07, 0x02);
5092 PDMPciDevSetByte(pPciDev, 0x08, pThis->uDevType == DEV_AM79C973 ? 0x40 : 0x16); /* revision */
5093 PDMPciDevSetByte(pPciDev, 0x09, 0x00);
5094 PDMPciDevSetByte(pPciDev, 0x0a, 0x00); /* ethernet network controller */
5095 PDMPciDevSetByte(pPciDev, 0x0b, 0x02);
5096 PDMPciDevSetByte(pPciDev, 0x0e, 0x00); /* header_type */
5097 PDMPciDevSetByte(pPciDev, 0x10, 0x01); /* IO Base */
5098 PDMPciDevSetByte(pPciDev, 0x11, 0x00);
5099 PDMPciDevSetByte(pPciDev, 0x12, 0x00);
5100 PDMPciDevSetByte(pPciDev, 0x13, 0x00);
5101 PDMPciDevSetByte(pPciDev, 0x14, 0x00); /* MMIO Base */
5102 PDMPciDevSetByte(pPciDev, 0x15, 0x00);
5103 PDMPciDevSetByte(pPciDev, 0x16, 0x00);
5104 PDMPciDevSetByte(pPciDev, 0x17, 0x00);
5105
5106 /* subsystem and subvendor IDs */
5107 PDMPciDevSetByte(pPciDev, 0x2c, 0x22); /* subsystem vendor id */
5108 PDMPciDevSetByte(pPciDev, 0x2d, 0x10);
5109 PDMPciDevSetByte(pPciDev, 0x2e, 0x00); /* subsystem id */
5110 PDMPciDevSetByte(pPciDev, 0x2f, 0x20);
5111 PDMPciDevSetByte(pPciDev, 0x3d, 1); /* interrupt pin 0 */
5112 PDMPciDevSetByte(pPciDev, 0x3e, 0x06);
5113 PDMPciDevSetByte(pPciDev, 0x3f, 0xff);
5114
5115 /*
5116 * We use our own critical section (historical reasons).
5117 */
5118 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PCnet#%u", iInstance);
5119 AssertRCReturn(rc, rc);
5120 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5121 AssertRCReturn(rc, rc);
5122
5123 /*
5124 * Register the PCI device, its I/O regions, the timer and the saved state item.
5125 */
5126 Assert(PCNET_IS_PCI(pThis) != PCNET_IS_ISA(pThis)); /* IOPortBase is shared, so it's either one or the other! */
5127
5128 if (PCNET_IS_PCI(pThis))
5129 {
5130 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
5131 AssertRCReturn(rc, rc);
5132
5133 /* Region #0: I/O ports - two handlers: */
5134 rc = PDMDevHlpIoPortCreate(pDevIns, 0x10 /*cPorts*/, pPciDev, 0 /*iPciRegion*/,
5135 pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/,
5136 "PCnet APROM", NULL /*paExtDescs*/, &pThis->hIoPortsPciAProm);
5137 AssertRCReturn(rc, rc);
5138 rc = PDMDevHlpIoPortCreate(pDevIns, 0x10 /*cPorts*/, pPciDev, 0 /*iPciRegion*/,
5139 pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/,
5140 "PCnet", NULL /*paExtDescs*/, &pThis->hIoPortsPci);
5141 AssertRCReturn(rc, rc);
5142 rc = PDMDevHlpPCIIORegionRegisterIoCustom(pDevIns, 0, PCNET_IOPORT_SIZE, pcnetR3PciMapUnmapIoPorts);
5143 AssertRCReturn(rc, rc);
5144
5145 /* Region #1: MMIO */
5146 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 1 /*iPciRegion*/, PCNET_PNPMMIO_SIZE, PCI_ADDRESS_SPACE_MEM,
5147 pcnetR3MmioWrite, pcnetR3MmioRead, NULL /*pvUser*/,
5148 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
5149 "PCnet", &pThis->hMmioPci);
5150 AssertRCReturn(rc, rc);
5151 }
5152
5153 /*
5154 * Register ISA I/O ranges for PCnet-ISA.
5155 */
5156 if (PCNET_IS_ISA(pThis))
5157 {
5158 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, pcnetIoPortAPromWrite, pcnetIOPortAPromRead,
5159 "PCnet APROM", NULL /*paExtDesc*/, &pThis->hIoPortsIsaAProm);
5160 AssertRCReturn(rc, rc);
5161 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x10, 0x10 /*cPorts*/, pcnetIoPortWrite, pcnetIoPortRead,
5162 "PCnet", NULL /*paExtDesc*/, &pThis->hIoPortsIsa);
5163 AssertRCReturn(rc, rc);
5164 }
5165
5166
5167 /* Transmit descriptor polling timer */
5168 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3Timer, NULL, TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0,
5169 "PCnet Poll", &pThis->hTimerPoll);
5170 AssertRCReturn(rc, rc);
5171 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->hTimerPoll, &pThis->CritSect);
5172 AssertRCReturn(rc, rc);
5173
5174 if (pThis->uDevType == DEV_AM79C973)
5175 {
5176 /* Software Interrupt timer */
5177 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3TimerSoftInt, NULL,
5178 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "PCnet SoftInt", &pThis->hTimerSoftInt);
5179 AssertRCReturn(rc, rc);
5180 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->hTimerSoftInt, &pThis->CritSect);
5181 AssertRCReturn(rc, rc);
5182 }
5183 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3TimerRestore, pThis,
5184 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "PCnet Restore", &pThis->hTimerRestore);
5185 AssertRCReturn(rc, rc);
5186
5187 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCNET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
5188 NULL, pcnetR3LiveExec, NULL,
5189 pcnetR3SavePrep, pcnetR3SaveExec, NULL,
5190 pcnetR3LoadPrep, pcnetR3LoadExec, pcnetR3LoadDone);
5191 AssertRCReturn(rc, rc);
5192
5193 /*
5194 * Create the transmit queue.
5195 */
5196 rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "PCnet-Xmit", pcnetR3XmitTaskCallback, NULL /*pvUser*/, &pThis->hXmitTask);
5197 AssertRCReturn(rc, rc);
5198
5199 /*
5200 * Create the RX notifier semaphore.
5201 */
5202 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventOutOfRxSpace);
5203 AssertRCReturn(rc, rc);
5204
5205 /*
5206 * Attach status driver (optional).
5207 */
5208 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
5209 if (RT_SUCCESS(rc))
5210 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5211 else
5212 AssertMsgReturn( rc == VERR_PDM_NO_ATTACHED_DRIVER
5213 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME,
5214 ("Failed to attach to status driver. rc=%Rrc\n", rc),
5215 rc);
5216
5217 /*
5218 * Attach driver.
5219 */
5220 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
5221 if (RT_SUCCESS(rc))
5222 {
5223 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
5224 AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
5225 VERR_PDM_MISSING_INTERFACE_BELOW);
5226 pThis->fDriverAttached = true;
5227 /// @todo figure out this!
5228 //pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
5229 //pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
5230 }
5231 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
5232 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
5233 {
5234 /* No error! */
5235 Log(("No attached driver!\n"));
5236 }
5237 else
5238 return rc;
5239
5240 /*
5241 * Reset the device state. (Do after attaching.)
5242 */
5243 pcnetR3HardReset(pDevIns, pThis);
5244
5245 /*
5246 * Register the info item.
5247 */
5248 RTStrPrintf(szTmp, sizeof(szTmp), "pcnet%d", pDevIns->iInstance);
5249 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "PCNET info.", pcnetR3Info);
5250
5251 /*
5252 * Register statistics.
5253 * The /Public/ bits are official and used by session info in the GUI.
5254 */
5255 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
5256 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
5257 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
5258 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
5259 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
5260 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
5261
5262 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
5263 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
5264
5265#ifdef VBOX_WITH_STATISTICS
5266 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadRZ, STAMTYPE_PROFILE, "MMIO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in RZ");
5267 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadR3, STAMTYPE_PROFILE, "MMIO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in R3");
5268 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteRZ, STAMTYPE_PROFILE, "MMIO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in RZ");
5269 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteR3, STAMTYPE_PROFILE, "MMIO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in R3");
5270 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAPROMRead, STAMTYPE_PROFILE, "IO/APROMRead", STAMUNIT_TICKS_PER_CALL, "Profiling APROM reads");
5271 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAPROMWrite, STAMTYPE_PROFILE, "IO/APROMWrite", STAMUNIT_TICKS_PER_CALL, "Profiling APROM writes");
5272 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, "IO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ");
5273 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, "IO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3");
5274 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, "IO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ");
5275 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, "IO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3");
5276 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling Timer");
5277 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
5278 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
5279 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_PROFILE, "RxOverflowWakeup", STAMUNIT_TICKS_PER_OCCURENCE, "Nr of RX overflow wakeups");
5280 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCase1, STAMTYPE_COUNTER, "Transmit/Case1", STAMUNIT_OCCURENCES, "Single descriptor transmit");
5281 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCase2, STAMTYPE_COUNTER, "Transmit/Case2", STAMUNIT_OCCURENCES, "Multi descriptor transmit");
5282 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, "Transmit/TotalRZ", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ");
5283 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, "Transmit/TotalR3", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3");
5284 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, "Transmit/SendRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in RZ");
5285 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, "Transmit/SendR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in R3");
5286 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTdtePollRZ, STAMTYPE_PROFILE, "TdtePollRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in RZ");
5287 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTdtePollR3, STAMTYPE_PROFILE, "TdtePollR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in R3");
5288 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRdtePollRZ, STAMTYPE_PROFILE, "RdtePollRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in RZ");
5289 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRdtePollR3, STAMTYPE_PROFILE, "RdtePollR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in R3");
5290
5291 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTmdStoreRZ, STAMTYPE_PROFILE, "TmdStoreRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in RZ");
5292 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTmdStoreR3, STAMTYPE_PROFILE, "TmdStoreR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in R3");
5293
5294 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatXmitSkipCurrent, STAMTYPE_COUNTER, "Xmit/Skipped", STAMUNIT_OCCURENCES, "");
5295
5296 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, "UpdateIRQ", STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks");
5297 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPollTimer, STAMTYPE_PROFILE, "PollTimer", STAMUNIT_TICKS_PER_CALL, "Profiling poll timer");
5298 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMIIReads, STAMTYPE_COUNTER, "MIIReads", STAMUNIT_OCCURENCES, "Number of MII reads");
5299 unsigned i;
5300 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitFlush) - 1; i++)
5301 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5302 "", "XmitFlushIrq/%02u", i + 1);
5303 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5304 "", "XmitFlushIrq/%02u-inf", i + 1);
5305
5306 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitChainCounts) - 1; i++)
5307 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5308 "", "XmitChainCounts/%02u", i + 1);
5309 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
5310 "", "XmitChainCounts/%02u-inf", i + 1);
5311#endif /* VBOX_WITH_STATISTICS */
5312
5313 return VINF_SUCCESS;
5314}
5315
5316#else /* !IN_RING3 */
5317
5318/**
5319 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
5320 */
5321static DECLCALLBACK(int) pcnetRZConstruct(PPDMDEVINS pDevIns)
5322{
5323 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5324 PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
5325
5326 /* Critical section setup: */
5327 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5328 AssertRCReturn(rc, rc);
5329
5330 /* PCI I/O ports: */
5331 if (pThis->hIoPortsPciAProm != NIL_IOMIOPORTHANDLE)
5332 {
5333 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsPciAProm, pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/);
5334 AssertRCReturn(rc, rc);
5335 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsPci, pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/);
5336 AssertRCReturn(rc, rc);
5337 }
5338 else
5339 Assert(pThis->hIoPortsPci == NIL_IOMIOPORTHANDLE);
5340
5341 /** @todo PCI MMIO */
5342
5343 /* ISA I/O ports: */
5344 if (pThis->hIoPortsIsaAProm != NIL_IOMIOPORTHANDLE)
5345 {
5346 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsaAProm, pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/);
5347 AssertRCReturn(rc, rc);
5348 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsa, pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/);
5349 AssertRCReturn(rc, rc);
5350 }
5351 else
5352 Assert(pThis->hIoPortsIsa == NIL_IOMIOPORTHANDLE);
5353
5354 return VINF_SUCCESS;
5355}
5356
5357#endif /* !IN_RING3 */
5358
5359/**
5360 * The device registration structure.
5361 */
5362const PDMDEVREG g_DevicePCNet =
5363{
5364 /* .u32Version = */ PDM_DEVREG_VERSION,
5365 /* .uReserved0 = */ 0,
5366 /* .szName = */ "pcnet",
5367#ifdef PCNET_GC_ENABLED
5368 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
5369#else
5370 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS,
5371#endif
5372 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
5373 /* .cMaxInstances = */ ~0U,
5374 /* .uSharedVersion = */ 42,
5375 /* .cbInstanceShared = */ sizeof(PCNETSTATE),
5376 /* .cbInstanceCC = */ sizeof(PCNETSTATECC),
5377 /* .cbInstanceRC = */ sizeof(PCNETSTATERC),
5378 /* .cMaxPciDevices = */ 1,
5379 /* .cMaxMsixVectors = */ 0,
5380 /* .pszDescription = */ "AMD PCnet Ethernet controller.\n",
5381#if defined(IN_RING3)
5382 /* .pszRCMod = */ "VBoxDDRC.rc",
5383 /* .pszR0Mod = */ "VBoxDDR0.r0",
5384 /* .pfnConstruct = */ pcnetR3Construct,
5385 /* .pfnDestruct = */ pcnetR3Destruct,
5386 /* .pfnRelocate = */ pcnetR3Relocate,
5387 /* .pfnMemSetup = */ NULL,
5388 /* .pfnPowerOn = */ NULL,
5389 /* .pfnReset = */ pcnetR3Reset,
5390 /* .pfnSuspend = */ pcnetR3Suspend,
5391 /* .pfnResume = */ NULL,
5392 /* .pfnAttach = */ pcnetR3Attach,
5393 /* .pfnDetach = */ pcnetR3Detach,
5394 /* .pfnQueryInterface = */ NULL,
5395 /* .pfnInitComplete = */ NULL,
5396 /* .pfnPowerOff = */ pcnetR3PowerOff,
5397 /* .pfnSoftReset = */ NULL,
5398 /* .pfnReserved0 = */ NULL,
5399 /* .pfnReserved1 = */ NULL,
5400 /* .pfnReserved2 = */ NULL,
5401 /* .pfnReserved3 = */ NULL,
5402 /* .pfnReserved4 = */ NULL,
5403 /* .pfnReserved5 = */ NULL,
5404 /* .pfnReserved6 = */ NULL,
5405 /* .pfnReserved7 = */ NULL,
5406#elif defined(IN_RING0)
5407 /* .pfnEarlyConstruct = */ NULL,
5408 /* .pfnConstruct = */ pcnetRZConstruct,
5409 /* .pfnDestruct = */ NULL,
5410 /* .pfnFinalDestruct = */ NULL,
5411 /* .pfnRequest = */ NULL,
5412 /* .pfnReserved0 = */ NULL,
5413 /* .pfnReserved1 = */ NULL,
5414 /* .pfnReserved2 = */ NULL,
5415 /* .pfnReserved3 = */ NULL,
5416 /* .pfnReserved4 = */ NULL,
5417 /* .pfnReserved5 = */ NULL,
5418 /* .pfnReserved6 = */ NULL,
5419 /* .pfnReserved7 = */ NULL,
5420#elif defined(IN_RC)
5421 /* .pfnConstruct = */ pcnetRZConstruct,
5422 /* .pfnReserved0 = */ NULL,
5423 /* .pfnReserved1 = */ NULL,
5424 /* .pfnReserved2 = */ NULL,
5425 /* .pfnReserved3 = */ NULL,
5426 /* .pfnReserved4 = */ NULL,
5427 /* .pfnReserved5 = */ NULL,
5428 /* .pfnReserved6 = */ NULL,
5429 /* .pfnReserved7 = */ NULL,
5430#else
5431# error "Not in IN_RING3, IN_RING0 or IN_RC!"
5432#endif
5433 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
5434};
5435
5436#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5437
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use