VirtualBox

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

Last change on this file since 81696 was 81667, checked in by vboxsync, 5 years ago

Main: Added a portPath attribute to IUSBDevice. This will allow uniquely identifying a specific USB port in a system (see bugref:9518).

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