VirtualBox

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

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

PDM,Devices: Moving the PDMPCIDEV structures into the PDMDEVINS allocation. Preps for extending the config space to 4KB. bugref:9218

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