VirtualBox

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

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

PCnet: Dump BCR18 (DWIO) in info handler, too.

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