VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use