VirtualBox

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

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

warning

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