VirtualBox

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

Last change on this file since 55904 was 55904, checked in by vboxsync, 10 years ago

PGM: Added a pVCpu parameter to all physical handler callouts and also a PGMACCESSORIGIN parameter to the ring-3 one. Fixed virtual handler callout mix up from previous commit.

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

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