VirtualBox

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

Last change on this file since 76051 was 76051, checked in by vboxsync, 6 years ago

PCnet: Corrected most instances of wrong capitalization.

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