VirtualBox

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

Last change on this file since 52934 was 51155, checked in by vboxsync, 11 years ago

DevPCNet: Correct CSR0 state on reset.

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

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