VirtualBox

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

Last change on this file since 59063 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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

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