VirtualBox

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

Last change on this file since 28275 was 28275, checked in by vboxsync, 15 years ago

Devices/Network: Call pfnBeginXmit and pfnEndXmit.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette