VirtualBox

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

Last change on this file since 80923 was 80923, checked in by vboxsync, 5 years ago

Dev/Net: @bugref:9556 Lower interrupts on reset

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