VirtualBox

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

Last change on this file since 44516 was 41864, checked in by vboxsync, 13 years ago

PCNet,NetShaper: PCNet exact packet len counting + wrong lock order fix in NetShaper (#5582)

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