VirtualBox

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

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

Network: Do not bring up links for "not attached" adapters.

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