VirtualBox

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

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

DevPCNet: ASTRP_RCV bit is only for 802.3 frames, not Ethernet frames.
Data sheet mentions it elsewhere, but not in the documentation for the
bit itself. The code has always been incorrect, but r77259 broke it
further. The casualty was OpenStep, which now gets its network back.

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

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