VirtualBox

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

Last change on this file since 49498 was 49414, checked in by vboxsync, 11 years ago

Network/Adapters: Fix wrong link state transition upon host resume (#7057)

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