VirtualBox

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

Last change on this file since 33000 was 32967, checked in by vboxsync, 14 years ago

DevPCNet: comment

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

© 2023 Oracle
ContactPrivacy policyTerms of Use