VirtualBox

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

Last change on this file since 90330 was 88498, checked in by vboxsync, 4 years ago

DevPCNet: Added comments, removed logic that caused transmit to hang when link was down (see bugref:1613).

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