VirtualBox

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

Last change on this file since 90447 was 90447, checked in by vboxsync, 3 years ago

Dev*: Checked up all the PDMDevHlpCritSectEnter calls to make sure the status code is checked. bugref:6695

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

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