VirtualBox

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

Last change on this file since 37608 was 37466, checked in by vboxsync, 14 years ago

VMM,Devices: Automatically use a per-device lock instead of the giant IOM lock. With exception of the PIC, APIC, IOAPIC and PCI buses which are all using the PDM crit sect, there should be no calls between devices. So, this change should be relatively safe.

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