VirtualBox

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

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

Devices: unused parameters.

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