VirtualBox

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

Last change on this file since 91022 was 91022, checked in by vboxsync, 3 years ago

DevPCnet: Handle AMD drivers which disable PCI bus mastering around setting CSR0.INIT (see bugref:10088).

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