VirtualBox

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

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

more warnings.

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