VirtualBox

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

Last change on this file since 76768 was 76766, checked in by vboxsync, 6 years ago

PCnet: Improved loopback mode emulation.

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