VirtualBox

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

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

PCnet: Print MMIO base for PCI models, IRQ for ISA models.

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