VirtualBox

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

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

PCnet: Removed redundant I/O port base, added APROM dumping, tweaked PCnet-ISA emulation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 209.7 KB
Line 
1/* $Id: DevPCNet.cpp 76764 2019-01-11 07:46:37Z 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)
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 while (cbToRecv < 60)
1962 src[cbToRecv++] = 0;
1963
1964 if (fAddFCS)
1965 {
1966 uint32_t fcs = UINT32_MAX;
1967 uint8_t *p = src;
1968
1969 while (p != &src[cbToRecv])
1970 CRC(fcs, *p++);
1971
1972 /* FCS at the end of the packet */
1973 ((uint32_t *)&src[cbToRecv])[0] = htonl(fcs);
1974 cbToRecv += 4;
1975 }
1976 }
1977
1978 cbPacket = (int)cbToRecv; Assert((size_t)cbPacket == cbToRecv);
1979
1980#ifdef PCNET_DEBUG_MATCH
1981 PRINT_PKTHDR(buf);
1982#endif
1983
1984 pcnetRmdLoad(pThis, &rmd, PHYSADDR(pThis, crda), false);
1985 /*if (!CSR_LAPPEN(pThis))*/
1986 rmd.rmd1.stp = 1;
1987
1988 size_t cbBuf = RT_MIN(4096 - (size_t)rmd.rmd1.bcnt, cbToRecv);
1989 RTGCPHYS32 rbadr = PHYSADDR(pThis, rmd.rmd0.rbadr);
1990
1991 /* save the old value to check if it was changed as long as we didn't
1992 * hold the critical section */
1993 iRxDesc = CSR_RCVRC(pThis);
1994
1995 /* We have to leave the critical section here or we risk deadlocking
1996 * with EMT when the write is to an unallocated page or has an access
1997 * handler associated with it.
1998 *
1999 * This shouldn't be a problem because:
2000 * - any modification to the RX descriptor by the driver is
2001 * forbidden as long as it is owned by the device
2002 * - we don't cache any register state beyond this point
2003 */
2004 PDMCritSectLeave(&pThis->CritSect);
2005 pcnetPhysWrite(pThis, rbadr, src, cbBuf);
2006 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
2007 AssertReleaseRC(rc);
2008
2009 /* RX disabled in the meantime? If so, abort RX. */
2010 if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis)))
2011 return;
2012
2013 /* Was the register modified in the meantime? If so, don't touch the
2014 * register but still update the RX descriptor. */
2015 if (RT_LIKELY(iRxDesc == CSR_RCVRC(pThis)))
2016 {
2017 if (iRxDesc-- < 2)
2018 iRxDesc = CSR_RCVRL(pThis);
2019 CSR_RCVRC(pThis) = iRxDesc;
2020 }
2021 else
2022 iRxDesc = CSR_RCVRC(pThis);
2023
2024 src += cbBuf;
2025 cbToRecv -= cbBuf;
2026
2027 while (cbToRecv > 0)
2028 {
2029 /* Read the entire next descriptor as we're likely to need it. */
2030 next_crda = pcnetRdraAddr(pThis, iRxDesc);
2031
2032 /* Check next descriptor's own bit. If we don't own it, we have
2033 * to quit and write error status into the last descriptor we own.
2034 */
2035 if (!pcnetRmdLoad(pThis, &next_rmd, PHYSADDR(pThis, next_crda), true))
2036 break;
2037
2038 /* Write back current descriptor, clear the own bit. */
2039 pcnetRmdStorePassHost(pThis, &rmd, PHYSADDR(pThis, crda));
2040
2041 /* Switch to the next descriptor */
2042 crda = next_crda;
2043 rmd = next_rmd;
2044
2045 cbBuf = RT_MIN(4096 - (size_t)rmd.rmd1.bcnt, cbToRecv);
2046 RTGCPHYS32 rbadr2 = PHYSADDR(pThis, rmd.rmd0.rbadr);
2047
2048 /* We have to leave the critical section here or we risk deadlocking
2049 * with EMT when the write is to an unallocated page or has an access
2050 * handler associated with it. See above for additional comments. */
2051 PDMCritSectLeave(&pThis->CritSect);
2052 pcnetPhysWrite(pThis, rbadr2, src, cbBuf);
2053 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
2054 AssertReleaseRC(rc);
2055
2056 /* RX disabled in the meantime? If so, abort RX. */
2057 if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis)))
2058 return;
2059
2060 /* Was the register modified in the meantime? If so, don't touch the
2061 * register but still update the RX descriptor. */
2062 if (RT_LIKELY(iRxDesc == CSR_RCVRC(pThis)))
2063 {
2064 if (iRxDesc-- < 2)
2065 iRxDesc = CSR_RCVRL(pThis);
2066 CSR_RCVRC(pThis) = iRxDesc;
2067 }
2068 else
2069 iRxDesc = CSR_RCVRC(pThis);
2070
2071 src += cbBuf;
2072 cbToRecv -= cbBuf;
2073 }
2074
2075 if (RT_LIKELY(cbToRecv == 0))
2076 {
2077 rmd.rmd1.enp = 1;
2078 rmd.rmd1.pam = !CSR_PROM(pThis) && is_padr;
2079 rmd.rmd1.lafm = !CSR_PROM(pThis) && is_ladr;
2080 rmd.rmd1.bam = !CSR_PROM(pThis) && is_bcast;
2081 rmd.rmd2.mcnt = cbPacket;
2082
2083 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbPacket);
2084 }
2085 else
2086 {
2087 Log(("#%d: Overflow by %ubytes\n", PCNET_INST_NR, cbToRecv));
2088 rmd.rmd1.oflo = 1;
2089 rmd.rmd1.buff = 1;
2090 rmd.rmd1.err = 1;
2091 }
2092
2093 /* write back, clear the own bit */
2094 pcnetRmdStorePassHost(pThis, &rmd, PHYSADDR(pThis, crda));
2095
2096 pThis->aCSR[0] |= 0x0400;
2097
2098 Log(("#%d RCVRC=%d CRDA=%#010x\n", PCNET_INST_NR,
2099 CSR_RCVRC(pThis), PHYSADDR(pThis, CSR_CRDA(pThis))));
2100#ifdef PCNET_DEBUG_RMD
2101 PRINT_RMD(&rmd);
2102#endif
2103
2104 /* guest driver is owner: force repoll of current and next RDTEs */
2105 CSR_CRST(pThis) = 0;
2106 }
2107 }
2108
2109 /* see description of TXDPOLL:
2110 * ``transmit polling will take place following receive activities'' */
2111 pcnetPollRxTx(pThis);
2112 pcnetUpdateIrq(pThis);
2113}
2114
2115
2116#ifdef IN_RING3
2117/**
2118 * Transmit queue consumer
2119 * This is just a very simple way of delaying sending to R3.
2120 *
2121 * @returns Success indicator.
2122 * If false the item will not be removed and the flushing will stop.
2123 * @param pDevIns The device instance.
2124 * @param pItem The item to consume. Upon return this item will be freed.
2125 */
2126static DECLCALLBACK(bool) pcnetXmitQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
2127{
2128 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
2129 NOREF(pItem);
2130
2131 /*
2132 * Transmit as much as we can.
2133 */
2134 pcnetXmitPending(pThis, true /*fOnWorkerThread*/);
2135
2136 return true;
2137}
2138#endif /* IN_RING3 */
2139
2140
2141/**
2142 * Allocates a scatter/gather buffer for a transfer.
2143 *
2144 * @returns See PPDMINETWORKUP::pfnAllocBuf.
2145 * @param pThis The device instance.
2146 * @param cbMin The minimum buffer size.
2147 * @param fLoopback Set if we're in loopback mode.
2148 * @param pSgLoop Pointer to stack storage for the loopback SG.
2149 * @param ppSgBuf Where to return the SG buffer descriptor on success.
2150 * Always set.
2151 */
2152DECLINLINE(int) pcnetXmitAllocBuf(PPCNETSTATE pThis, size_t cbMin, bool fLoopback,
2153 PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
2154{
2155 int rc;
2156
2157 if (RT_UNLIKELY(fLoopback)) /* hope that loopback mode is rare */
2158 {
2159 pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
2160 pSgLoop->cbUsed = 0;
2161 pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
2162 pSgLoop->pvAllocator = pThis;
2163 pSgLoop->pvUser = NULL;
2164 pSgLoop->cSegs = 1;
2165 pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
2166 pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
2167 *ppSgBuf = pSgLoop;
2168 rc = VINF_SUCCESS;
2169 }
2170 else
2171 {
2172 PPDMINETWORKUP pDrv = pThis->CTX_SUFF(pDrv);
2173 if (RT_LIKELY(pDrv))
2174 {
2175 rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
2176 AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
2177 if (RT_FAILURE(rc))
2178 *ppSgBuf = NULL;
2179 }
2180 else
2181 {
2182 rc = VERR_NET_DOWN;
2183 *ppSgBuf = NULL;
2184 }
2185 }
2186 return rc;
2187}
2188
2189
2190/**
2191 * Frees an unsent buffer.
2192 *
2193 * @param pThis The device instance.
2194 * @param fLoopback Set if we're in loopback mode.
2195 * @param pSgBuf The SG to free. Can be NULL.
2196 */
2197DECLINLINE(void) pcnetXmitFreeBuf(PPCNETSTATE pThis, bool fLoopback, PPDMSCATTERGATHER pSgBuf)
2198{
2199 if (pSgBuf)
2200 {
2201 if (RT_UNLIKELY(fLoopback))
2202 pSgBuf->pvAllocator = NULL;
2203 else
2204 {
2205 PPDMINETWORKUP pDrv = pThis->CTX_SUFF(pDrv);
2206 if (RT_LIKELY(pDrv))
2207 pDrv->pfnFreeBuf(pDrv, pSgBuf);
2208 }
2209 }
2210}
2211
2212
2213/**
2214 * Sends the scatter/gather buffer.
2215 *
2216 * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
2217 *
2218 * @returns See PDMINETWORKUP::pfnSendBuf.
2219 * @param pThis The device instance.
2220 * @param fLoopback Set if we're in loopback mode.
2221 * @param pSgBuf The SG to send.
2222 * @param fOnWorkerThread Set if we're being called on a work thread. Clear
2223 * if an EMT.
2224 */
2225DECLINLINE(int) pcnetXmitSendBuf(PPCNETSTATE pThis, bool fLoopback, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
2226{
2227 int rc;
2228 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
2229 if (RT_UNLIKELY(fLoopback)) /* hope that loopback mode is rare */
2230 {
2231 Assert(pSgBuf->pvAllocator == (void *)pThis);
2232 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
2233 if (HOST_IS_OWNER(CSR_CRST(pThis)))
2234 pcnetRdtePoll(pThis);
2235
2236 pcnetReceiveNoSync(pThis, pThis->abLoopBuf, pSgBuf->cbUsed, true /* fAddFCS */);
2237 pThis->Led.Actual.s.fReading = 0;
2238 rc = VINF_SUCCESS;
2239 }
2240 else
2241 {
2242 /** @todo We used to leave the critsect here, not sure if that's necessary any
2243 * longer. If we could avoid that we could cache a bit more info in
2244 * the loop and make it part of the driver<->device contract, saving
2245 * critsect mess down in DrvIntNet. */
2246 STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
2247 if (pSgBuf->cbUsed > 70) /* unqualified guess */
2248 pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
2249
2250 PPDMINETWORKUP pDrv = pThis->CTX_SUFF(pDrv);
2251 if (RT_LIKELY(pDrv))
2252 {
2253 rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
2254 AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
2255 }
2256 else
2257 rc = VERR_NET_DOWN;
2258
2259 pThis->Led.Actual.s.fWriting = 0;
2260 STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
2261 }
2262 return rc;
2263}
2264
2265
2266/**
2267 * pcnetXmitRead1st worker that handles the unlikely + slower segmented code
2268 * path.
2269 */
2270static void pcnetXmitRead1stSlow(PPCNETSTATE pThis, RTGCPHYS32 GCPhysFrame, unsigned cbFrame,
2271 PPDMSCATTERGATHER pSgBuf)
2272{
2273 AssertFailed(); /* This path is not supposed to be taken atm */
2274
2275 pSgBuf->cbUsed = cbFrame;
2276 for (uint32_t iSeg = 0; ; iSeg++)
2277 {
2278 Assert(iSeg < pSgBuf->cSegs);
2279 uint32_t cbRead = (uint32_t)RT_MIN(cbFrame, pSgBuf->aSegs[iSeg].cbSeg);
2280 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysFrame, pSgBuf->aSegs[iSeg].pvSeg, cbRead);
2281 cbFrame -= cbRead;
2282 if (!cbFrame)
2283 return;
2284 GCPhysFrame += cbRead;
2285 }
2286}
2287
2288
2289/**
2290 * pcnetXmitSgReadMore worker that handles the unlikely + slower segmented code
2291 * path.
2292 */
2293static void pcnetXmitReadMoreSlow(PPCNETSTATE pThis, RTGCPHYS32 GCPhysFrame, unsigned cbFrame,
2294 PPDMSCATTERGATHER pSgBuf)
2295{
2296 AssertFailed(); /* This path is not supposed to be taken atm */
2297
2298 /* Find the segment which we'll put the next byte into. */
2299 size_t off = pSgBuf->cbUsed;
2300 size_t offSeg = 0;
2301 uint32_t iSeg = 0;
2302 while (offSeg + pSgBuf->aSegs[iSeg].cbSeg <= off)
2303 {
2304 offSeg += pSgBuf->aSegs[iSeg].cbSeg;
2305 iSeg++;
2306 Assert(iSeg < pSgBuf->cSegs);
2307 }
2308
2309 /* Commit before we start copying so we can decrement cbFrame. */
2310 pSgBuf->cbUsed = off + cbFrame;
2311
2312 /* Deal with the first segment if we at an offset into it. */
2313 if (off != offSeg)
2314 {
2315 size_t offIntoSeg = off - offSeg;
2316 uint32_t cbRead = (uint32_t)RT_MIN(pSgBuf->aSegs[iSeg].cbSeg - offIntoSeg, cbFrame);
2317 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysFrame,
2318 (uint8_t *)pSgBuf->aSegs[iSeg].pvSeg + offIntoSeg, cbRead);
2319 cbFrame -= cbRead;
2320 if (!cbFrame)
2321 return;
2322 GCPhysFrame += cbRead;
2323 iSeg++;
2324 }
2325
2326 /* For the remainder, we've got whole segments. */
2327 for (;; iSeg++)
2328 {
2329 Assert(iSeg < pSgBuf->cSegs);
2330
2331 uint32_t cbRead = (uint32_t)RT_MIN(pSgBuf->aSegs[iSeg].cbSeg, cbFrame);
2332 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysFrame, pSgBuf->aSegs[iSeg].pvSeg, cbRead);
2333 cbFrame -= cbRead;
2334 if (!cbFrame)
2335 return;
2336 GCPhysFrame += cbFrame;
2337 }
2338}
2339
2340
2341/**
2342 * Reads the first part of a frame into the scatter gather buffer.
2343 */
2344DECLINLINE(void) pcnetXmitRead1st(PPCNETSTATE pThis, RTGCPHYS32 GCPhysFrame, const unsigned cbFrame,
2345 PPDMSCATTERGATHER pSgBuf)
2346{
2347 Assert(PDMCritSectIsOwner(&pThis->CritSect));
2348 Assert(pSgBuf->cbAvailable >= cbFrame);
2349
2350 if (RT_LIKELY(pSgBuf->aSegs[0].cbSeg >= cbFrame)) /* justification: all drivers returns a single segment atm. */
2351 {
2352 pSgBuf->cbUsed = cbFrame;
2353 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysFrame, pSgBuf->aSegs[0].pvSeg, cbFrame);
2354 }
2355 else
2356 pcnetXmitRead1stSlow(pThis, GCPhysFrame, cbFrame, pSgBuf);
2357}
2358
2359/**
2360 * Reads more into the current frame.
2361 */
2362DECLINLINE(void) pcnetXmitReadMore(PPCNETSTATE pThis, RTGCPHYS32 GCPhysFrame, const unsigned cbFrame,
2363 PPDMSCATTERGATHER pSgBuf)
2364{
2365 size_t off = pSgBuf->cbUsed;
2366 Assert(pSgBuf->cbAvailable >= cbFrame + off);
2367
2368 if (RT_LIKELY(pSgBuf->aSegs[0].cbSeg >= cbFrame + off))
2369 {
2370 pSgBuf->cbUsed = cbFrame + off;
2371 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysFrame,
2372 (uint8_t *)pSgBuf->aSegs[0].pvSeg + off, cbFrame);
2373 }
2374 else
2375 pcnetXmitReadMoreSlow(pThis, GCPhysFrame, cbFrame, pSgBuf);
2376}
2377
2378
2379/**
2380 * Fails a TMD with a link down error.
2381 */
2382static void pcnetXmitFailTMDLinkDown(PPCNETSTATE pThis, TMD *pTmd)
2383{
2384 /* make carrier error - hope this is correct. */
2385 pThis->cLinkDownReported++;
2386 pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
2387 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
2388 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
2389 Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
2390 PCNET_INST_NR, pThis->aBCR[BCR_SWS]));
2391}
2392
2393/**
2394 * Fails a TMD with a generic error.
2395 */
2396static void pcnetXmitFailTMDGeneric(PPCNETSTATE pThis, TMD *pTmd)
2397{
2398 /* make carrier error - hope this is correct. */
2399 pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
2400 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
2401 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
2402 Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
2403 PCNET_INST_NR, pThis->aBCR[BCR_SWS]));
2404}
2405
2406
2407/**
2408 * Try to transmit frames
2409 */
2410static void pcnetTransmit(PPCNETSTATE pThis)
2411{
2412 if (RT_UNLIKELY(!CSR_TXON(pThis)))
2413 {
2414 pThis->aCSR[0] &= ~0x0008; /* Clear TDMD */
2415 return;
2416 }
2417
2418 /*
2419 * Check the current transmit descriptors.
2420 */
2421 TMD tmd;
2422 if (!pcnetTdtePoll(pThis, &tmd))
2423 return;
2424
2425 /*
2426 * Clear TDMD.
2427 */
2428 pThis->aCSR[0] &= ~0x0008;
2429
2430 /*
2431 * Transmit pending packets if possible, defer it if we cannot do it
2432 * in the current context.
2433 */
2434#if defined(IN_RING0) || defined(IN_RC)
2435 if (!pThis->CTX_SUFF(pDrv))
2436 {
2437 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pThis->CTX_SUFF(pXmitQueue));
2438 if (RT_UNLIKELY(pItem))
2439 PDMQueueInsert(pThis->CTX_SUFF(pXmitQueue), pItem);
2440 }
2441 else
2442#endif
2443 {
2444 int rc = pcnetXmitPending(pThis, false /*fOnWorkerThread*/);
2445 if (rc == VERR_TRY_AGAIN)
2446 rc = VINF_SUCCESS;
2447 AssertRC(rc);
2448 }
2449}
2450
2451
2452/**
2453 * Actually try transmit frames.
2454 *
2455 * @threads TX or EMT.
2456 */
2457static int pcnetAsyncTransmit(PPCNETSTATE pThis, bool fOnWorkerThread)
2458{
2459 Assert(PDMCritSectIsOwner(&pThis->CritSect));
2460
2461 /*
2462 * Just cleared transmit demand if the transmitter is off.
2463 */
2464 if (RT_UNLIKELY(!CSR_TXON(pThis)))
2465 {
2466 pThis->aCSR[0] &= ~0x0008; /* Clear TDMD */
2467 return VINF_SUCCESS;
2468 }
2469
2470 /*
2471 * Iterate the transmit descriptors.
2472 */
2473 int rc;
2474 unsigned cFlushIrq = 0;
2475 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
2476 do
2477 {
2478#ifdef VBOX_WITH_STATISTICS
2479 unsigned cBuffers = 1;
2480#endif
2481 TMD tmd;
2482 if (!pcnetTdtePoll(pThis, &tmd))
2483 break;
2484
2485 /* Don't continue sending packets when the link is down. */
2486 if (RT_UNLIKELY( !pcnetIsLinkUp(pThis)
2487 && pThis->cLinkDownReported > PCNET_MAX_LINKDOWN_REPORTED)
2488 )
2489 break;
2490
2491#ifdef PCNET_DEBUG_TMD
2492 Log2(("#%d TMDLOAD %#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_CXDA(pThis))));
2493 PRINT_TMD(&tmd);
2494#endif
2495 bool const fLoopback = CSR_LOOP(pThis);
2496 PDMSCATTERGATHER SgLoop;
2497 PPDMSCATTERGATHER pSgBuf;
2498
2499 /*
2500 * The typical case - a complete packet.
2501 */
2502 if (tmd.tmd1.stp && tmd.tmd1.enp)
2503 {
2504 const unsigned cb = 4096 - tmd.tmd1.bcnt;
2505 Log(("#%d pcnetAsyncTransmit: stp&enp: cb=%d xmtrc=%#x\n", PCNET_INST_NR, cb, CSR_XMTRC(pThis)));
2506 STAM_COUNTER_INC(&pThis->StatTransmitCase1);
2507
2508 if (RT_LIKELY(pcnetIsLinkUp(pThis) || fLoopback))
2509 {
2510 /* From the manual: ``A zero length buffer is acceptable as
2511 * long as it is not the last buffer in a chain (STP = 0 and
2512 * ENP = 1).'' That means that the first buffer might have a
2513 * zero length if it is not the last one in the chain. */
2514 if (RT_LIKELY(cb <= MAX_FRAME))
2515 {
2516 rc = pcnetXmitAllocBuf(pThis, cb, fLoopback, &SgLoop, &pSgBuf);
2517 if (RT_SUCCESS(rc))
2518 {
2519 pcnetXmitRead1st(pThis, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
2520 rc = pcnetXmitSendBuf(pThis, fLoopback, pSgBuf, fOnWorkerThread);
2521 }
2522 else if (rc == VERR_TRY_AGAIN)
2523 {
2524 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2525 return VINF_SUCCESS;
2526 }
2527 if (RT_FAILURE(rc))
2528 pcnetXmitFailTMDLinkDown(pThis, &tmd);
2529 }
2530 else if (cb == 4096)
2531 {
2532 /* The Windows NT4 pcnet driver sometimes marks the first
2533 * unused descriptor as owned by us. Ignore that (by
2534 * passing it back). Do not update the ring counter in this
2535 * case (otherwise that driver becomes even more confused,
2536 * which causes transmit to stall for about 10 seconds).
2537 * This is just a workaround, not a final solution. */
2538 /* r=frank: IMHO this is the correct implementation. The
2539 * manual says: ``If the OWN bit is set and the buffer
2540 * length is 0, the OWN bit will be cleared. In the C-LANCE
2541 * the buffer length of 0 is interpreted as a 4096-byte
2542 * buffer.'' */
2543 /* r=michaln: Perhaps not quite right. The C-LANCE (Am79C90)
2544 * datasheet explains that the old LANCE (Am7990) ignored
2545 * the top four bits next to BCNT and a count of 0 was
2546 * interpreted as 4096. In the C-LANCE, that is still the
2547 * case if the top bits are all ones. If all 16 bits are
2548 * zero, the C-LANCE interprets it as zero-length transmit
2549 * buffer. It's not entirely clear if the later models
2550 * (PCnet-ISA, PCnet-PCI) behave like the C-LANCE or not.
2551 * It is possible that the actual behavior of the C-LANCE
2552 * and later hardware is that the buffer lengths are *16-bit*
2553 * two's complement numbers between 0 and 4096. AMD's drivers
2554 * in fact generally treat the length as a 16-bit quantity. */
2555 LogRel(("PCnet#%d: pcnetAsyncTransmit: illegal 4kb frame -> ignoring\n", PCNET_INST_NR));
2556 pcnetTmdStorePassHost(pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
2557 break;
2558 }
2559 else
2560 {
2561 /* Signal error, as this violates the Ethernet specs. */
2562 /** @todo check if the correct error is generated. */
2563 LogRel(("PCnet#%d: pcnetAsyncTransmit: illegal 4kb frame -> signalling error\n", PCNET_INST_NR));
2564
2565 pcnetXmitFailTMDGeneric(pThis, &tmd);
2566 }
2567 }
2568 else
2569 pcnetXmitFailTMDLinkDown(pThis, &tmd);
2570
2571 /* Write back the TMD and pass it to the host (clear own bit). */
2572 pcnetTmdStorePassHost(pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
2573
2574 /* advance the ring counter register */
2575 if (CSR_XMTRC(pThis) < 2)
2576 CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
2577 else
2578 CSR_XMTRC(pThis)--;
2579 }
2580 else if (tmd.tmd1.stp)
2581 {
2582 STAM_COUNTER_INC(&pThis->StatTransmitCase2);
2583
2584 /*
2585 * Read TMDs until end-of-packet or tdte poll fails (underflow).
2586 *
2587 * We allocate a maximum sized buffer here since we do not wish to
2588 * waste time finding out how much space we actually need even if
2589 * we could reliably do that on SMP guests.
2590 */
2591 unsigned cb = 4096 - tmd.tmd1.bcnt;
2592 rc = pcnetXmitAllocBuf(pThis, pcnetCalcPacketLen(pThis, cb), fLoopback, &SgLoop, &pSgBuf);
2593 if (rc == VERR_TRY_AGAIN)
2594 {
2595 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2596 return VINF_SUCCESS;
2597 }
2598
2599 bool fDropFrame = RT_FAILURE(rc);
2600 if (!fDropFrame)
2601 pcnetXmitRead1st(pThis, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
2602
2603 for (;;)
2604 {
2605 /*
2606 * Advance the ring counter register and check the next tmd.
2607 */
2608#ifdef LOG_ENABLED
2609 const uint32_t iStart = CSR_XMTRC(pThis);
2610#endif
2611 const uint32_t GCPhysPrevTmd = PHYSADDR(pThis, CSR_CXDA(pThis));
2612 if (CSR_XMTRC(pThis) < 2)
2613 CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
2614 else
2615 CSR_XMTRC(pThis)--;
2616
2617 TMD dummy;
2618 if (!pcnetTdtePoll(pThis, &dummy))
2619 {
2620 /*
2621 * Underflow!
2622 */
2623 tmd.tmd2.buff = tmd.tmd2.uflo = tmd.tmd1.err = 1;
2624 pThis->aCSR[0] |= 0x0200; /* set TINT */
2625 /* Don't allow the guest to clear TINT before reading it */
2626 pThis->u16CSR0LastSeenByGuest &= ~0x0200;
2627 if (!CSR_DXSUFLO(pThis)) /* stop on xmit underflow */
2628 pThis->aCSR[0] &= ~0x0010; /* clear TXON */
2629 pcnetTmdStorePassHost(pThis, &tmd, GCPhysPrevTmd);
2630 AssertMsgFailed(("pcnetAsyncTransmit: Underflow!!!\n"));
2631 pcnetXmitFreeBuf(pThis, fLoopback, pSgBuf);
2632 break;
2633 }
2634
2635 /* release & save the previous tmd, pass it to the host */
2636 pcnetTmdStorePassHost(pThis, &tmd, GCPhysPrevTmd);
2637
2638 /*
2639 * The next tmd.
2640 */
2641#ifdef VBOX_WITH_STATISTICS
2642 cBuffers++;
2643#endif
2644 pcnetTmdLoad(pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)), false);
2645 cb = 4096 - tmd.tmd1.bcnt;
2646 if ( !fDropFrame
2647 && pSgBuf->cbUsed + cb <= MAX_FRAME) /** @todo this used to be ... + cb < MAX_FRAME. */
2648 pcnetXmitReadMore(pThis, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
2649 else
2650 {
2651 AssertMsg(fDropFrame, ("pcnetAsyncTransmit: Frame is too big!!! %d bytes\n", pSgBuf->cbUsed + cb));
2652 fDropFrame = true;
2653 }
2654
2655 /*
2656 * Done already?
2657 */
2658 if (tmd.tmd1.enp)
2659 {
2660 Log(("#%d pcnetAsyncTransmit: stp: cb=%d xmtrc=%#x-%#x\n", PCNET_INST_NR,
2661 pSgBuf ? pSgBuf->cbUsed : 0, iStart, CSR_XMTRC(pThis)));
2662 if (!fDropFrame && (pcnetIsLinkUp(pThis) || fLoopback))
2663 {
2664 rc = pcnetXmitSendBuf(pThis, fLoopback, pSgBuf, fOnWorkerThread);
2665 fDropFrame = RT_FAILURE(rc);
2666 }
2667 else
2668 pcnetXmitFreeBuf(pThis, fLoopback, pSgBuf);
2669 if (fDropFrame)
2670 pcnetXmitFailTMDLinkDown(pThis, &tmd);
2671
2672 /* Write back the TMD, pass it to the host */
2673 pcnetTmdStorePassHost(pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
2674
2675 /* advance the ring counter register */
2676 if (CSR_XMTRC(pThis) < 2)
2677 CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
2678 else
2679 CSR_XMTRC(pThis)--;
2680 break;
2681 }
2682 } /* the loop */
2683 }
2684 else
2685 {
2686 /*
2687 * We underflowed in a previous transfer, or the driver is giving us shit.
2688 * Simply stop the transmitting for now.
2689 */
2690 /** @todo according to the specs we're supposed to clear the own bit and move on to the next one. */
2691 Log(("#%d pcnetAsyncTransmit: guest is giving us shit!\n", PCNET_INST_NR));
2692 break;
2693 }
2694 /* Update TDMD, TXSTRT and TINT. */
2695 pThis->aCSR[0] &= ~0x0008; /* clear TDMD */
2696
2697 pThis->aCSR[4] |= 0x0008; /* set TXSTRT */
2698 if ( !CSR_TOKINTD(pThis) /* Transmit OK Interrupt Disable, no infl. on errors. */
2699 || (CSR_LTINTEN(pThis) && tmd.tmd1.ltint)
2700 || tmd.tmd1.err)
2701 {
2702 cFlushIrq++;
2703 }
2704
2705 /** @todo should we continue after an error (tmd.tmd1.err) or not? */
2706
2707 STAM_COUNTER_INC(&pThis->aStatXmitChainCounts[RT_MIN(cBuffers,
2708 RT_ELEMENTS(pThis->aStatXmitChainCounts)) - 1]);
2709 } while (CSR_TXON(pThis)); /* transfer on */
2710
2711 if (cFlushIrq)
2712 {
2713 STAM_COUNTER_INC(&pThis->aStatXmitFlush[RT_MIN(cFlushIrq, RT_ELEMENTS(pThis->aStatXmitFlush)) - 1]);
2714 /* The WinXP PCnet driver has apparently a bug: It sets CSR0.TDMD _before_
2715 * it clears CSR0.TINT. This can lead to a race where the driver clears
2716 * CSR0.TINT right after it was set by the device. The driver waits until
2717 * CSR0.TINT is set again but this will never happen. So prevent clearing
2718 * this bit as long as the driver didn't read it. See @bugref{5288}. */
2719 pThis->aCSR[0] |= 0x0200; /* set TINT */
2720 /* Don't allow the guest to clear TINT before reading it */
2721 pThis->u16CSR0LastSeenByGuest &= ~0x0200;
2722 pcnetUpdateIrq(pThis);
2723 }
2724
2725 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
2726
2727 return VINF_SUCCESS;
2728}
2729
2730
2731/**
2732 * Transmit pending descriptors.
2733 *
2734 * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
2735 *
2736 * @param pThis The PCnet instance data.
2737 * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
2738 */
2739static int pcnetXmitPending(PPCNETSTATE pThis, bool fOnWorkerThread)
2740{
2741 RT_NOREF_PV(fOnWorkerThread);
2742 int rc;
2743
2744 /*
2745 * Grab the xmit lock of the driver as well as the E1K device state.
2746 */
2747 PPDMINETWORKUP pDrv = pThis->CTX_SUFF(pDrv);
2748 if (pDrv)
2749 {
2750 rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
2751 if (RT_FAILURE(rc))
2752 return rc;
2753 }
2754 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
2755 if (RT_SUCCESS(rc))
2756 {
2757 /** @todo check if we're supposed to suspend now. */
2758 /*
2759 * Do the transmitting.
2760 */
2761 int rc2 = pcnetAsyncTransmit(pThis, false /*fOnWorkerThread*/);
2762 AssertReleaseRC(rc2);
2763
2764 /*
2765 * Release the locks.
2766 */
2767 PDMCritSectLeave(&pThis->CritSect);
2768 }
2769 else
2770 AssertLogRelRC(rc);
2771 if (pDrv)
2772 pDrv->pfnEndXmit(pDrv);
2773
2774 return rc;
2775}
2776
2777
2778/**
2779 * Poll for changes in RX and TX descriptor rings.
2780 */
2781static void pcnetPollRxTx(PPCNETSTATE pThis)
2782{
2783 if (CSR_RXON(pThis))
2784 {
2785 /*
2786 * The second case is important for pcnetWaitReceiveAvail(): If CSR_CRST(pThis) was
2787 * true but pcnetCanReceive() returned false for some other reason we need to check
2788 * _now_ if we have to wakeup pcnetWaitReceiveAvail().
2789 */
2790 if ( HOST_IS_OWNER(CSR_CRST(pThis)) /* only poll RDTEs if none available or ... */
2791 || pThis->fMaybeOutOfSpace) /* ... for waking up pcnetWaitReceiveAvail() */
2792 pcnetRdtePoll(pThis);
2793 }
2794
2795 if (CSR_TDMD(pThis) || (CSR_TXON(pThis) && !CSR_DPOLL(pThis)))
2796 pcnetTransmit(pThis);
2797}
2798
2799
2800#ifndef PCNET_NO_POLLING
2801/**
2802 * Start the poller timer.
2803 * Poll timer interval is fixed to 500Hz. Don't stop it.
2804 * @thread EMT, TAP.
2805 */
2806static void pcnetPollTimerStart(PPCNETSTATE pThis)
2807{
2808 TMTimerSetMillies(pThis->CTX_SUFF(pTimerPoll), 2);
2809}
2810#endif
2811
2812
2813/**
2814 * Update the poller timer.
2815 * @thread EMT.
2816 */
2817static void pcnetPollTimer(PPCNETSTATE pThis)
2818{
2819 STAM_PROFILE_ADV_START(&pThis->StatPollTimer, a);
2820
2821#ifdef LOG_ENABLED
2822 TMD dummy;
2823 if (CSR_STOP(pThis) || CSR_SPND(pThis))
2824 Log2(("#%d pcnetPollTimer time=%#010llx CSR_STOP=%d CSR_SPND=%d\n",
2825 PCNET_INST_NR, RTTimeMilliTS(), CSR_STOP(pThis), CSR_SPND(pThis)));
2826 else
2827 Log2(("#%d pcnetPollTimer time=%#010llx TDMD=%d TXON=%d POLL=%d TDTE=%d TDRA=%#x\n",
2828 PCNET_INST_NR, RTTimeMilliTS(), CSR_TDMD(pThis), CSR_TXON(pThis),
2829 !CSR_DPOLL(pThis), pcnetTdtePoll(pThis, &dummy), pThis->GCTDRA));
2830 Log2(("#%d pcnetPollTimer: CSR_CXDA=%#x CSR_XMTRL=%d CSR_XMTRC=%d\n",
2831 PCNET_INST_NR, CSR_CXDA(pThis), CSR_XMTRL(pThis), CSR_XMTRC(pThis)));
2832#endif
2833#ifdef PCNET_DEBUG_TMD
2834 if (CSR_CXDA(pThis))
2835 {
2836 TMD tmd;
2837 pcnetTmdLoad(pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)), false);
2838 Log2(("#%d pcnetPollTimer: TMDLOAD %#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_CXDA(pThis))));
2839 PRINT_TMD(&tmd);
2840 }
2841#endif
2842 if (CSR_TDMD(pThis))
2843 pcnetTransmit(pThis);
2844
2845 pcnetUpdateIrq(pThis);
2846
2847 /* If the receive thread is waiting for new descriptors, poll TX/RX even if polling
2848 * disabled. We wouldn't need to poll for new TX descriptors in that case but it will
2849 * not hurt as waiting for RX descriptors should happen very seldom */
2850 if (RT_LIKELY( !CSR_STOP(pThis)
2851 && !CSR_SPND(pThis)
2852 && ( !CSR_DPOLL(pThis)
2853 || pThis->fMaybeOutOfSpace)))
2854 {
2855 /* We ensure that we poll at least every 2ms (500Hz) but not more often than
2856 * 5000 times per second. This way we completely prevent the overhead from
2857 * heavy reprogramming the timer which turned out to be very CPU-intensive.
2858 * The drawback is that csr46 and csr47 are not updated properly anymore
2859 * but so far I have not seen any guest depending on these values. The 2ms
2860 * interval is the default polling interval of the PCnet card (65536/33MHz). */
2861#ifdef PCNET_NO_POLLING
2862 pcnetPollRxTx(pThis);
2863#else
2864 uint64_t u64Now = TMTimerGet(pThis->CTX_SUFF(pTimerPoll));
2865 if (RT_UNLIKELY(u64Now - pThis->u64LastPoll > 200000))
2866 {
2867 pThis->u64LastPoll = u64Now;
2868 pcnetPollRxTx(pThis);
2869 }
2870 if (!TMTimerIsActive(pThis->CTX_SUFF(pTimerPoll)))
2871 pcnetPollTimerStart(pThis);
2872#endif
2873 }
2874 STAM_PROFILE_ADV_STOP(&pThis->StatPollTimer, a);
2875}
2876
2877
2878static int pcnetCSRWriteU16(PPCNETSTATE pThis, uint32_t u32RAP, uint32_t val)
2879{
2880 int rc = VINF_SUCCESS;
2881#ifdef PCNET_DEBUG_CSR
2882 Log(("#%d pcnetCSRWriteU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
2883#endif
2884 switch (u32RAP)
2885 {
2886 case 0:
2887 {
2888 uint16_t csr0 = pThis->aCSR[0];
2889 /* Clear any interrupt flags.
2890 * Don't clear an interrupt flag which was not seen by the guest yet. */
2891 csr0 &= ~(val & 0x7f00 & pThis->u16CSR0LastSeenByGuest);
2892 csr0 = (csr0 & ~0x0040) | (val & 0x0048);
2893 val = (val & 0x007f) | (csr0 & 0x7f00);
2894
2895 /* Iff STOP, STRT and INIT are set, clear STRT and INIT */
2896 if ((val & 7) == 7)
2897 val &= ~3;
2898
2899 Log(("#%d CSR0: old=%#06x new=%#06x\n", PCNET_INST_NR, pThis->aCSR[0], csr0));
2900
2901#ifndef IN_RING3
2902 if (!(csr0 & 0x0001/*init*/) && (val & 1))
2903 {
2904 Log(("#%d pcnetCSRWriteU16: pcnetInit requested => HC\n", PCNET_INST_NR));
2905 return VINF_IOM_R3_IOPORT_WRITE;
2906 }
2907#endif
2908 pThis->aCSR[0] = csr0;
2909
2910 if (!CSR_STOP(pThis) && (val & 4))
2911 pcnetStop(pThis);
2912
2913#ifdef IN_RING3
2914 if (!CSR_INIT(pThis) && (val & 1))
2915 pcnetInit(pThis);
2916#endif
2917
2918 if (!CSR_STRT(pThis) && (val & 2))
2919 pcnetStart(pThis);
2920
2921 if (CSR_TDMD(pThis))
2922 pcnetTransmit(pThis);
2923
2924 return rc;
2925 }
2926 case 1: /* IADRL */
2927 case 2: /* IADRH */
2928 case 8: /* LADRF 0..15 */
2929 case 9: /* LADRF 16..31 */
2930 case 10: /* LADRF 32..47 */
2931 case 11: /* LADRF 48..63 */
2932 case 12: /* PADR 0..15 */
2933 case 13: /* PADR 16..31 */
2934 case 14: /* PADR 32..47 */
2935 case 18: /* CRBAL */
2936 case 19: /* CRBAU */
2937 case 20: /* CXBAL */
2938 case 21: /* CXBAU */
2939 case 22: /* NRBAL */
2940 case 23: /* NRBAU */
2941 case 26: /* NRDAL */
2942 case 27: /* NRDAU */
2943 case 28: /* CRDAL */
2944 case 29: /* CRDAU */
2945 case 32: /* NXDAL */
2946 case 33: /* NXDAU */
2947 case 34: /* CXDAL */
2948 case 35: /* CXDAU */
2949 case 36: /* NNRDL */
2950 case 37: /* NNRDU */
2951 case 38: /* NNXDL */
2952 case 39: /* NNXDU */
2953 case 40: /* CRBCL */
2954 case 41: /* CRBCU */
2955 case 42: /* CXBCL */
2956 case 43: /* CXBCU */
2957 case 44: /* NRBCL */
2958 case 45: /* NRBCU */
2959 case 46: /* POLL */
2960 case 47: /* POLLINT */
2961 case 72: /* RCVRC */
2962 case 74: /* XMTRC */
2963 case 112: /* MISSC */
2964 if (CSR_STOP(pThis) || CSR_SPND(pThis))
2965 break;
2966 case 3: /* Interrupt Mask and Deferral Control */
2967 break;
2968 case 4: /* Test and Features Control */
2969 pThis->aCSR[4] &= ~(val & 0x026a);
2970 val &= ~0x026a;
2971 val |= pThis->aCSR[4] & 0x026a;
2972 break;
2973 case 5: /* Extended Control and Interrupt 1 */
2974 pThis->aCSR[5] &= ~(val & 0x0a90);
2975 val &= ~0x0a90;
2976 val |= pThis->aCSR[5] & 0x0a90;
2977 break;
2978 case 7: /* Extended Control and Interrupt 2 */
2979 {
2980 uint16_t csr7 = pThis->aCSR[7];
2981 csr7 &= ~0x0400 ;
2982 csr7 &= ~(val & 0x0800);
2983 csr7 |= (val & 0x0400);
2984 pThis->aCSR[7] = csr7;
2985 return rc;
2986 }
2987 case 15: /* Mode */
2988 if ((pThis->aCSR[15] & 0x8000) != (uint16_t)(val & 0x8000) && pThis->pDrvR3)
2989 {
2990 Log(("#%d: promiscuous mode changed to %d\n", PCNET_INST_NR, !!(val & 0x8000)));
2991#ifndef IN_RING3
2992 return VINF_IOM_R3_IOPORT_WRITE;
2993#else
2994 /* check for promiscuous mode change */
2995 if (pThis->pDrvR3)
2996 pThis->pDrvR3->pfnSetPromiscuousMode(pThis->pDrvR3, !!(val & 0x8000));
2997#endif
2998 }
2999 break;
3000 case 16: /* IADRL */
3001 return pcnetCSRWriteU16(pThis, 1, val);
3002 case 17: /* IADRH */
3003 return pcnetCSRWriteU16(pThis, 2, val);
3004
3005 /*
3006 * 24 and 25 are the Base Address of Receive Descriptor.
3007 * We combine and mirror these in GCRDRA.
3008 */
3009 case 24: /* BADRL */
3010 case 25: /* BADRU */
3011 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
3012 {
3013 Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
3014 return rc;
3015 }
3016 if (u32RAP == 24)
3017 pThis->GCRDRA = (pThis->GCRDRA & 0xffff0000) | (val & 0x0000ffff);
3018 else
3019 pThis->GCRDRA = (pThis->GCRDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
3020 Log(("#%d: WRITE CSR%d, %#06x => GCRDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCRDRA));
3021 break;
3022
3023 /*
3024 * 30 & 31 are the Base Address of Transmit Descriptor.
3025 * We combine and mirrorthese in GCTDRA.
3026 */
3027 case 30: /* BADXL */
3028 case 31: /* BADXU */
3029 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
3030 {
3031 Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
3032 return rc;
3033 }
3034 if (u32RAP == 30)
3035 pThis->GCTDRA = (pThis->GCTDRA & 0xffff0000) | (val & 0x0000ffff);
3036 else
3037 pThis->GCTDRA = (pThis->GCTDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
3038 Log(("#%d: WRITE CSR%d, %#06x => GCTDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCTDRA));
3039 break;
3040
3041 case 58: /* Software Style */
3042 rc = pcnetBCRWriteU16(pThis, BCR_SWS, val);
3043 break;
3044
3045 /*
3046 * Registers 76 and 78 aren't stored correctly (see todos), but I'm don't dare
3047 * try fix that right now. So, as a quick hack for 'alt init' I'll just correct them here.
3048 */
3049 case 76: /* RCVRL */ /** @todo call pcnetUpdateRingHandlers */
3050 /** @todo receive ring length is stored in two's complement! */
3051 case 78: /* XMTRL */ /** @todo call pcnetUpdateRingHandlers */
3052 /** @todo transmit ring length is stored in two's complement! */
3053 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
3054 {
3055 Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
3056 return rc;
3057 }
3058 Log(("#%d: WRITE CSR%d, %#06x (hacked %#06x) (alt init)\n", PCNET_INST_NR,
3059 u32RAP, val, 1 + ~(uint16_t)val));
3060 val = 1 + ~(uint16_t)val;
3061
3062 /*
3063 * HACK ALERT! Set the counter registers too.
3064 */
3065 pThis->aCSR[u32RAP - 4] = val;
3066 break;
3067
3068 default:
3069 return rc;
3070 }
3071 pThis->aCSR[u32RAP] = val;
3072 return rc;
3073}
3074
3075/**
3076 * Encode a 32-bit link speed into a custom 16-bit floating-point value
3077 */
3078static uint32_t pcnetLinkSpd(uint32_t speed)
3079{
3080 unsigned exp = 0;
3081
3082 while (speed & 0xFFFFE000)
3083 {
3084 speed /= 10;
3085 ++exp;
3086 }
3087 return (exp << 13) | speed;
3088}
3089
3090static uint32_t pcnetCSRReadU16(PPCNETSTATE pThis, uint32_t u32RAP)
3091{
3092 uint32_t val;
3093 switch (u32RAP)
3094 {
3095 case 0:
3096 pcnetUpdateIrq(pThis);
3097 val = pThis->aCSR[0];
3098 val |= (val & 0x7800) ? 0x8000 : 0;
3099 pThis->u16CSR0LastSeenByGuest = val;
3100 break;
3101 case 16:
3102 return pcnetCSRReadU16(pThis, 1);
3103 case 17:
3104 return pcnetCSRReadU16(pThis, 2);
3105 case 58:
3106 return pcnetBCRReadU16(pThis, BCR_SWS);
3107 case 68: /* Custom register to pass link speed to driver */
3108 return pcnetLinkSpd(pThis->u32LinkSpeed);
3109 case 88:
3110 val = pThis->aCSR[89];
3111 val <<= 16;
3112 val |= pThis->aCSR[88];
3113 break;
3114 default:
3115 val = pThis->aCSR[u32RAP];
3116 }
3117#ifdef PCNET_DEBUG_CSR
3118 Log(("#%d pcnetCSRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3119#endif
3120 return val;
3121}
3122
3123static int pcnetBCRWriteU16(PPCNETSTATE pThis, uint32_t u32RAP, uint32_t val)
3124{
3125 int rc = VINF_SUCCESS;
3126 u32RAP &= 0x7f;
3127#ifdef PCNET_DEBUG_BCR
3128 Log2(("#%d pcnetBCRWriteU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3129#endif
3130 switch (u32RAP)
3131 {
3132 case BCR_SWS:
3133 if (!(CSR_STOP(pThis) || CSR_SPND(pThis)))
3134 return rc;
3135 val &= ~0x0300;
3136 switch (val & 0x00ff)
3137 {
3138 default:
3139 Log(("#%d Bad SWSTYLE=%#04x\n", PCNET_INST_NR, val & 0xff));
3140 RT_FALL_THRU();
3141 case 0:
3142 val |= 0x0200; /* 16 bit */
3143 pThis->iLog2DescSize = 3;
3144 pThis->GCUpperPhys = (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
3145 break;
3146 case 1:
3147 val |= 0x0100; /* 32 bit */
3148 pThis->iLog2DescSize = 4;
3149 pThis->GCUpperPhys = 0;
3150 break;
3151 case 2:
3152 case 3:
3153 val |= 0x0300; /* 32 bit */
3154 pThis->iLog2DescSize = 4;
3155 pThis->GCUpperPhys = 0;
3156 break;
3157 }
3158 Log(("#%d BCR_SWS=%#06x\n", PCNET_INST_NR, val));
3159 pThis->aCSR[58] = val;
3160 RT_FALL_THRU();
3161 case BCR_LNKST:
3162 case BCR_LED1:
3163 case BCR_LED2:
3164 case BCR_LED3:
3165 case BCR_MC:
3166 case BCR_FDC:
3167 case BCR_BSBC:
3168 case BCR_EECAS:
3169 case BCR_PLAT:
3170 case BCR_MIICAS:
3171 case BCR_MIIADDR:
3172 pThis->aBCR[u32RAP] = val;
3173 break;
3174
3175 case BCR_STVAL:
3176 val &= 0xffff;
3177 pThis->aBCR[BCR_STVAL] = val;
3178 if (pThis->uDevType == DEV_AM79C973)
3179 TMTimerSetNano(pThis->CTX_SUFF(pTimerSoftInt), 12800U * val);
3180 break;
3181
3182 case BCR_MIIMDR:
3183 pThis->aMII[pThis->aBCR[BCR_MIIADDR] & 0x1f] = val;
3184#ifdef PCNET_DEBUG_MII
3185 Log(("#%d pcnet: mii write %d <- %#x\n", PCNET_INST_NR, pThis->aBCR[BCR_MIIADDR] & 0x1f, val));
3186#endif
3187 break;
3188
3189 default:
3190 break;
3191 }
3192 return rc;
3193}
3194
3195static uint32_t pcnetMIIReadU16(PPCNETSTATE pThis, uint32_t miiaddr)
3196{
3197 uint32_t val;
3198 bool autoneg, duplex, fast;
3199 STAM_COUNTER_INC(&pThis->StatMIIReads);
3200
3201 autoneg = (pThis->aBCR[BCR_MIICAS] & 0x20) != 0;
3202 duplex = (pThis->aBCR[BCR_MIICAS] & 0x10) != 0;
3203 fast = (pThis->aBCR[BCR_MIICAS] & 0x08) != 0;
3204
3205 switch (miiaddr)
3206 {
3207 case 0:
3208 /* MII basic mode control register. */
3209 val = 0;
3210 if (autoneg)
3211 val |= 0x1000; /* Enable auto negotiation. */
3212 if (fast)
3213 val |= 0x2000; /* 100 Mbps */
3214 if (duplex) /* Full duplex forced */
3215 val |= 0x0100; /* Full duplex */
3216 break;
3217
3218 case 1:
3219 /* MII basic mode status register. */
3220 val = 0x7800 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
3221 | 0x0040 /* Mgmt frame preamble not required. */
3222 | 0x0020 /* Auto-negotiation complete. */
3223 | 0x0008 /* Able to do auto-negotiation. */
3224 | 0x0004 /* Link up. */
3225 | 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
3226 if (!pThis->fLinkUp || pThis->fLinkTempDown) {
3227 val &= ~(0x0020 | 0x0004);
3228 pThis->cLinkDownReported++;
3229 }
3230 if (!autoneg) {
3231 /* Auto-negotiation disabled. */
3232 val &= ~(0x0020 | 0x0008);
3233 if (duplex)
3234 /* Full duplex forced. */
3235 val &= ~0x2800;
3236 else
3237 /* Half duplex forced. */
3238 val &= ~0x5000;
3239
3240 if (fast)
3241 /* 100 Mbps forced */
3242 val &= ~0x1800;
3243 else
3244 /* 10 Mbps forced */
3245 val &= ~0x6000;
3246 }
3247 break;
3248
3249 case 2:
3250 /* PHY identifier 1. */
3251 val = 0x22; /* Am79C874 PHY */
3252 break;
3253
3254 case 3:
3255 /* PHY identifier 2. */
3256 val = 0x561b; /* Am79C874 PHY */
3257 break;
3258
3259 case 4:
3260 /* Advertisement control register. */
3261 val = 0x01e0 /* Try 100mbps FD/HD and 10mbps FD/HD. */
3262#if 0
3263 // Advertising flow control is a) not the default, and b) confuses
3264 // the link speed detection routine in Windows PCnet driver
3265 | 0x0400 /* Try flow control. */
3266#endif
3267 | 0x0001; /* CSMA selector. */
3268 break;
3269
3270 case 5:
3271 /* Link partner ability register. */
3272 if (pThis->fLinkUp && !pThis->fLinkTempDown)
3273 val = 0x8000 /* Next page bit. */
3274 | 0x4000 /* Link partner acked us. */
3275 | 0x0400 /* Can do flow control. */
3276 | 0x01e0 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
3277 | 0x0001; /* Use CSMA selector. */
3278 else
3279 {
3280 val = 0;
3281 pThis->cLinkDownReported++;
3282 }
3283 break;
3284
3285 case 6:
3286 /* Auto negotiation expansion register. */
3287 if (pThis->fLinkUp && !pThis->fLinkTempDown)
3288 val = 0x0008 /* Link partner supports npage. */
3289 | 0x0004 /* Enable npage words. */
3290 | 0x0001; /* Can do N-way auto-negotiation. */
3291 else
3292 {
3293 val = 0;
3294 pThis->cLinkDownReported++;
3295 }
3296 break;
3297
3298 default:
3299 val = 0;
3300 break;
3301 }
3302
3303#ifdef PCNET_DEBUG_MII
3304 Log(("#%d pcnet: mii read %d -> %#x\n", PCNET_INST_NR, miiaddr, val));
3305#endif
3306 return val;
3307}
3308
3309static uint32_t pcnetBCRReadU16(PPCNETSTATE pThis, uint32_t u32RAP)
3310{
3311 uint32_t val;
3312 u32RAP &= 0x7f;
3313 switch (u32RAP)
3314 {
3315 case BCR_LNKST:
3316 case BCR_LED1:
3317 case BCR_LED2:
3318 case BCR_LED3:
3319 val = pThis->aBCR[u32RAP] & ~0x8000;
3320 /* Clear LNKSTE if we're not connected or if we've just loaded a VM state. */
3321 if (!pThis->pDrvR3 || pThis->fLinkTempDown || !pThis->fLinkUp)
3322 {
3323 if (u32RAP == 4)
3324 pThis->cLinkDownReported++;
3325 val &= ~0x40;
3326 }
3327 val |= (val & 0x017f & pThis->u32Lnkst) ? 0x8000 : 0;
3328 break;
3329
3330 case BCR_MIIMDR:
3331 if ((pThis->uDevType == DEV_AM79C973) && (pThis->aBCR[BCR_MIIADDR] >> 5 & 0x1f) == 0)
3332 {
3333 uint32_t miiaddr = pThis->aBCR[BCR_MIIADDR] & 0x1f;
3334 val = pcnetMIIReadU16(pThis, miiaddr);
3335 }
3336 else
3337 val = 0xffff;
3338 break;
3339
3340 default:
3341 val = u32RAP < BCR_MAX_RAP ? pThis->aBCR[u32RAP] : 0;
3342 break;
3343 }
3344#ifdef PCNET_DEBUG_BCR
3345 Log2(("#%d pcnetBCRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3346#endif
3347 return val;
3348}
3349
3350#ifdef IN_RING3 /* move down */
3351static void pcnetR3HardReset(PPCNETSTATE pThis)
3352{
3353 int i;
3354 uint16_t checksum;
3355
3356 /* Initialize the PROM */
3357 Assert(sizeof(pThis->MacConfigured) == 6);
3358 memcpy(pThis->aPROM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
3359 pThis->aPROM[ 8] = 0x00;
3360 pThis->aPROM[12] = pThis->aPROM[13] = 0x00;
3361 if (pThis->uDevType == DEV_AM79C960_EB)
3362 {
3363 pThis->aPROM[14] = 0x52;
3364 pThis->aPROM[15] = 0x44; /* NI6510 EtherBlaster 'RD' signature. */
3365 }
3366 else
3367 pThis->aPROM[14] = pThis->aPROM[15] = 0x57; /* NE2100 'WW' signature. */
3368
3369 /* 0x00/0xFF=ISA, 0x01=PnP, 0x10=VLB, 0x11=PCI */
3370 if (PCNET_IS_PCI(pThis))
3371 pThis->aPROM[ 9] = 0x11;
3372 else
3373 pThis->aPROM[ 9] = 0x00;
3374
3375 for (i = 0, checksum = 0; i < 16; i++)
3376 checksum += pThis->aPROM[i];
3377 *(uint16_t *)&pThis->aPROM[12] = RT_H2LE_U16(checksum);
3378
3379 pThis->aBCR[BCR_MSRDA] = 0x0005;
3380 pThis->aBCR[BCR_MSWRA] = 0x0005;
3381 pThis->aBCR[BCR_MC ] = 0x0002;
3382 pThis->aBCR[BCR_LNKST] = 0x00c0;
3383 pThis->aBCR[BCR_LED1 ] = 0x0084;
3384 pThis->aBCR[BCR_LED2 ] = 0x0088;
3385 pThis->aBCR[BCR_LED3 ] = 0x0090;
3386 pThis->aBCR[BCR_FDC ] = 0x0000;
3387 pThis->aBCR[BCR_BSBC ] = 0x9001;
3388 pThis->aBCR[BCR_EECAS] = 0x0002;
3389 pThis->aBCR[BCR_STVAL] = 0xffff;
3390 pThis->aCSR[58 ] = /* CSR58 is an alias for BCR20 */
3391 pThis->aBCR[BCR_SWS ] = 0x0200;
3392 pThis->iLog2DescSize = 3;
3393 pThis->aBCR[BCR_PLAT ] = 0xff06;
3394 pThis->aBCR[BCR_MIIADDR ] = 0; /* Internal PHY on Am79C973 would be (0x1e << 5) */
3395 pThis->aBCR[BCR_PCIVID] = PCIDevGetVendorId(&pThis->PciDev);
3396 pThis->aBCR[BCR_PCISID] = PCIDevGetSubSystemId(&pThis->PciDev);
3397 pThis->aBCR[BCR_PCISVID] = PCIDevGetSubSystemVendorId(&pThis->PciDev);
3398
3399 /* Reset the error counter. */
3400 pThis->uCntBadRMD = 0;
3401
3402 pcnetSoftReset(pThis);
3403}
3404#endif /* IN_RING3 */
3405
3406
3407/* -=-=-=-=-=- APROM I/O Port access -=-=-=-=-=- */
3408
3409static void pcnetAPROMWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3410{
3411 addr &= 0x0f;
3412 val &= 0xff;
3413 Log(("#%d pcnetAPROMWriteU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3414 /* Check APROMWE bit to enable write access */
3415 if (pcnetBCRReadU16(pThis, 2) & 0x80)
3416 pThis->aPROM[addr] = val;
3417}
3418
3419static uint32_t pcnetAPROMReadU8(PPCNETSTATE pThis, uint32_t addr)
3420{
3421 uint32_t val = pThis->aPROM[addr &= 0x0f];
3422 Log(("#%d pcnetAPROMReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3423 return val;
3424}
3425
3426/**
3427 * @callback_method_impl{FNIOMIOPORTIN, APROM}
3428 */
3429PDMBOTHCBDECL(int) pcnetIOPortAPromRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3430{
3431 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3432 int rc = VINF_SUCCESS;
3433 STAM_PROFILE_ADV_START(&pThis->StatAPROMRead, a);
3434 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3435 RT_NOREF_PV(pvUser);
3436
3437 /* FreeBSD is accessing in dwords. */
3438 if (cb == 1)
3439 *pu32 = pcnetAPROMReadU8(pThis, Port);
3440 else if (cb == 2 && !BCR_DWIO(pThis))
3441 *pu32 = pcnetAPROMReadU8(pThis, Port)
3442 | (pcnetAPROMReadU8(pThis, Port + 1) << 8);
3443 else if (cb == 4 && BCR_DWIO(pThis))
3444 *pu32 = pcnetAPROMReadU8(pThis, Port)
3445 | (pcnetAPROMReadU8(pThis, Port + 1) << 8)
3446 | (pcnetAPROMReadU8(pThis, Port + 2) << 16)
3447 | (pcnetAPROMReadU8(pThis, Port + 3) << 24);
3448 else
3449 {
3450 Log(("#%d pcnetIOPortAPromRead: Port=%RTiop cb=%d BCR_DWIO !!\n", PCNET_INST_NR, Port, cb));
3451 rc = VERR_IOM_IOPORT_UNUSED;
3452 }
3453
3454 STAM_PROFILE_ADV_STOP(&pThis->StatAPROMRead, a);
3455 LogFlow(("#%d pcnetIOPortAPromRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, *pu32, cb, rc));
3456 return rc;
3457}
3458
3459
3460/**
3461 * @callback_method_impl{FNIOMIOPORTOUT, APROM}
3462 */
3463PDMBOTHCBDECL(int) pcnetIOPortAPromWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3464{
3465 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3466 int rc = VINF_SUCCESS;
3467 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3468 RT_NOREF_PV(pvUser);
3469
3470 if (cb == 1)
3471 {
3472 STAM_PROFILE_ADV_START(&pThis->StatAPROMWrite, a);
3473 pcnetAPROMWriteU8(pThis, Port, u32);
3474 STAM_PROFILE_ADV_STOP(&pThis->StatAPROMWrite, a);
3475 }
3476 else
3477 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d u32=%#x\n", Port, cb, u32);
3478
3479 LogFlow(("#%d pcnetIOPortAPromWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, u32, cb, rc));
3480 return rc;
3481}
3482
3483
3484/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
3485
3486
3487static int pcnetIoportWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3488{
3489 RT_NOREF1(val);
3490#ifdef PCNET_DEBUG_IO
3491 Log2(("#%d pcnetIoportWriteU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3492#endif
3493 if (RT_LIKELY(!BCR_DWIO(pThis)))
3494 {
3495 switch (addr & 0x0f)
3496 {
3497 case 0x04: /* RESET */
3498 break;
3499 }
3500 }
3501 else
3502 Log(("#%d pcnetIoportWriteU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3503
3504 return VINF_SUCCESS;
3505}
3506
3507static uint32_t pcnetIoportReadU8(PPCNETSTATE pThis, uint32_t addr, int *pRC)
3508{
3509 uint32_t val = UINT32_MAX;
3510
3511 *pRC = VINF_SUCCESS;
3512
3513 if (RT_LIKELY(!BCR_DWIO(pThis)))
3514 {
3515 switch (addr & 0x0f)
3516 {
3517 case 0x04: /* RESET */
3518 pcnetSoftReset(pThis);
3519 val = 0;
3520 break;
3521 }
3522 }
3523 else
3524 Log(("#%d pcnetIoportReadU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val & 0xff));
3525
3526 pcnetUpdateIrq(pThis);
3527
3528#ifdef PCNET_DEBUG_IO
3529 Log2(("#%d pcnetIoportReadU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val & 0xff));
3530#endif
3531 return val;
3532}
3533
3534static int pcnetIoportWriteU16(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3535{
3536 int rc = VINF_SUCCESS;
3537
3538#ifdef PCNET_DEBUG_IO
3539 Log2(("#%d pcnetIoportWriteU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3540#endif
3541 if (RT_LIKELY(!BCR_DWIO(pThis)))
3542 {
3543 switch (addr & 0x0f)
3544 {
3545 case 0x00: /* RDP */
3546 pcnetPollTimer(pThis);
3547 rc = pcnetCSRWriteU16(pThis, pThis->u32RAP, val);
3548 pcnetUpdateIrq(pThis);
3549 break;
3550 case 0x02: /* RAP */
3551 pThis->u32RAP = val & 0x7f;
3552 break;
3553 case 0x06: /* BDP */
3554 rc = pcnetBCRWriteU16(pThis, pThis->u32RAP, val);
3555 break;
3556 }
3557 }
3558 else
3559 Log(("#%d pcnetIoportWriteU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3560
3561 return rc;
3562}
3563
3564static uint32_t pcnetIoportReadU16(PPCNETSTATE pThis, uint32_t addr, int *pRC)
3565{
3566 uint32_t val = ~0U;
3567
3568 *pRC = VINF_SUCCESS;
3569
3570 if (RT_LIKELY(!BCR_DWIO(pThis)))
3571 {
3572 switch (addr & 0x0f)
3573 {
3574 case 0x00: /* RDP */
3575 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3576 /** Polling is then useless here and possibly expensive. */
3577 if (!CSR_DPOLL(pThis))
3578 pcnetPollTimer(pThis);
3579
3580 val = pcnetCSRReadU16(pThis, pThis->u32RAP);
3581 if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3582 goto skip_update_irq;
3583 break;
3584 case 0x02: /* RAP */
3585 val = pThis->u32RAP;
3586 goto skip_update_irq;
3587 case 0x04: /* RESET */
3588 pcnetSoftReset(pThis);
3589 val = 0;
3590 break;
3591 case 0x06: /* BDP */
3592 val = pcnetBCRReadU16(pThis, pThis->u32RAP);
3593 break;
3594 }
3595 }
3596 else
3597 Log(("#%d pcnetIoportReadU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val & 0xffff));
3598
3599 pcnetUpdateIrq(pThis);
3600
3601skip_update_irq:
3602#ifdef PCNET_DEBUG_IO
3603 Log2(("#%d pcnetIoportReadU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val & 0xffff));
3604#endif
3605 return val;
3606}
3607
3608static int pcnetIoportWriteU32(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3609{
3610 int rc = VINF_SUCCESS;
3611
3612#ifdef PCNET_DEBUG_IO
3613 Log2(("#%d pcnetIoportWriteU32: addr=%#010x val=%#010x\n", PCNET_INST_NR,
3614 addr, val));
3615#endif
3616 if (RT_LIKELY(BCR_DWIO(pThis)))
3617 {
3618 switch (addr & 0x0f)
3619 {
3620 case 0x00: /* RDP */
3621 pcnetPollTimer(pThis);
3622 rc = pcnetCSRWriteU16(pThis, pThis->u32RAP, val & 0xffff);
3623 pcnetUpdateIrq(pThis);
3624 break;
3625 case 0x04: /* RAP */
3626 pThis->u32RAP = val & 0x7f;
3627 break;
3628 case 0x0c: /* BDP */
3629 rc = pcnetBCRWriteU16(pThis, pThis->u32RAP, val & 0xffff);
3630 break;
3631 }
3632 }
3633 else if ((addr & 0x0f) == 0)
3634 {
3635 /* switch device to dword I/O mode */
3636 pcnetBCRWriteU16(pThis, BCR_BSBC, pcnetBCRReadU16(pThis, BCR_BSBC) | 0x0080);
3637#ifdef PCNET_DEBUG_IO
3638 Log2(("device switched into dword i/o mode\n"));
3639#endif
3640 }
3641 else
3642 Log(("#%d pcnetIoportWriteU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3643
3644 return rc;
3645}
3646
3647static uint32_t pcnetIoportReadU32(PPCNETSTATE pThis, uint32_t addr, int *pRC)
3648{
3649 uint32_t val = ~0U;
3650
3651 *pRC = VINF_SUCCESS;
3652
3653 if (RT_LIKELY(BCR_DWIO(pThis)))
3654 {
3655 switch (addr & 0x0f)
3656 {
3657 case 0x00: /* RDP */
3658 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3659 /** Polling is then useless here and possibly expensive. */
3660 if (!CSR_DPOLL(pThis))
3661 pcnetPollTimer(pThis);
3662
3663 val = pcnetCSRReadU16(pThis, pThis->u32RAP);
3664 if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3665 goto skip_update_irq;
3666 break;
3667 case 0x04: /* RAP */
3668 val = pThis->u32RAP;
3669 goto skip_update_irq;
3670 case 0x08: /* RESET */
3671 pcnetSoftReset(pThis);
3672 val = 0;
3673 break;
3674 case 0x0c: /* BDP */
3675 val = pcnetBCRReadU16(pThis, pThis->u32RAP);
3676 break;
3677 }
3678 }
3679 else
3680 Log(("#%d pcnetIoportReadU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3681 pcnetUpdateIrq(pThis);
3682
3683skip_update_irq:
3684#ifdef PCNET_DEBUG_IO
3685 Log2(("#%d pcnetIoportReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
3686#endif
3687 return val;
3688}
3689
3690
3691/**
3692 * @callback_method_impl{FNIOMIOPORTIN}
3693 */
3694PDMBOTHCBDECL(int) pcnetIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3695{
3696 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3697 int rc = VINF_SUCCESS;
3698 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
3699 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3700 RT_NOREF_PV(pvUser);
3701
3702 switch (cb)
3703 {
3704 case 1: *pu32 = pcnetIoportReadU8(pThis, Port, &rc); break;
3705 case 2: *pu32 = pcnetIoportReadU16(pThis, Port, &rc); break;
3706 case 4: *pu32 = pcnetIoportReadU32(pThis, Port, &rc); break;
3707 default:
3708 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3709 "pcnetIOPortRead: unsupported op size: offset=%#10x cb=%u\n",
3710 Port, cb);
3711 }
3712
3713 Log2(("#%d pcnetIOPortRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, *pu32, cb, rc));
3714 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
3715 return rc;
3716}
3717
3718
3719/**
3720 * @callback_method_impl{FNIOMIOPORTOUT}
3721 */
3722PDMBOTHCBDECL(int) pcnetIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3723{
3724 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3725 int rc = VINF_SUCCESS;
3726 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3727 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3728 RT_NOREF_PV(pvUser);
3729
3730 switch (cb)
3731 {
3732 case 1: rc = pcnetIoportWriteU8(pThis, Port, u32); break;
3733 case 2: rc = pcnetIoportWriteU16(pThis, Port, u32); break;
3734 case 4: rc = pcnetIoportWriteU32(pThis, Port, u32); break;
3735 default:
3736 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3737 "pcnetIOPortWrite: unsupported op size: offset=%#10x cb=%u\n",
3738 Port, cb);
3739 }
3740
3741 Log2(("#%d pcnetIOPortWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, u32, cb, rc));
3742 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3743 return rc;
3744}
3745
3746
3747/* -=-=-=-=-=- MMIO -=-=-=-=-=- */
3748
3749static void pcnetMMIOWriteU8(PPCNETSTATE pThis, RTGCPHYS addr, uint32_t val)
3750{
3751#ifdef PCNET_DEBUG_IO
3752 Log2(("#%d pcnetMMIOWriteU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3753#endif
3754 if (!(addr & 0x10))
3755 pcnetAPROMWriteU8(pThis, addr, val);
3756}
3757
3758static uint32_t pcnetMMIOReadU8(PPCNETSTATE pThis, RTGCPHYS addr)
3759{
3760 uint32_t val = ~0U;
3761 if (!(addr & 0x10))
3762 val = pcnetAPROMReadU8(pThis, addr);
3763#ifdef PCNET_DEBUG_IO
3764 Log2(("#%d pcnetMMIOReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val & 0xff));
3765#endif
3766 return val;
3767}
3768
3769static void pcnetMMIOWriteU16(PPCNETSTATE pThis, RTGCPHYS addr, uint32_t val)
3770{
3771#ifdef PCNET_DEBUG_IO
3772 Log2(("#%d pcnetMMIOWriteU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3773#endif
3774 if (addr & 0x10)
3775 pcnetIoportWriteU16(pThis, addr & 0x0f, val);
3776 else
3777 {
3778 pcnetAPROMWriteU8(pThis, addr, val );
3779 pcnetAPROMWriteU8(pThis, addr+1, val >> 8);
3780 }
3781}
3782
3783static uint32_t pcnetMMIOReadU16(PPCNETSTATE pThis, RTGCPHYS addr)
3784{
3785 uint32_t val = ~0U;
3786 int rc;
3787
3788 if (addr & 0x10)
3789 val = pcnetIoportReadU16(pThis, addr & 0x0f, &rc);
3790 else
3791 {
3792 val = pcnetAPROMReadU8(pThis, addr+1);
3793 val <<= 8;
3794 val |= pcnetAPROMReadU8(pThis, addr);
3795 }
3796#ifdef PCNET_DEBUG_IO
3797 Log2(("#%d pcnetMMIOReadU16: addr=%#010x val = %#06x\n", PCNET_INST_NR, addr, val & 0xffff));
3798#endif
3799 return val;
3800}
3801
3802static void pcnetMMIOWriteU32(PPCNETSTATE pThis, RTGCPHYS addr, uint32_t val)
3803{
3804#ifdef PCNET_DEBUG_IO
3805 Log2(("#%d pcnetMMIOWriteU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
3806#endif
3807 if (addr & 0x10)
3808 pcnetIoportWriteU32(pThis, addr & 0x0f, val);
3809 else
3810 {
3811 pcnetAPROMWriteU8(pThis, addr, val );
3812 pcnetAPROMWriteU8(pThis, addr+1, val >> 8);
3813 pcnetAPROMWriteU8(pThis, addr+2, val >> 16);
3814 pcnetAPROMWriteU8(pThis, addr+3, val >> 24);
3815 }
3816}
3817
3818static uint32_t pcnetMMIOReadU32(PPCNETSTATE pThis, RTGCPHYS addr)
3819{
3820 uint32_t val;
3821 int rc;
3822
3823 if (addr & 0x10)
3824 val = pcnetIoportReadU32(pThis, addr & 0x0f, &rc);
3825 else
3826 {
3827 val = pcnetAPROMReadU8(pThis, addr+3);
3828 val <<= 8;
3829 val |= pcnetAPROMReadU8(pThis, addr+2);
3830 val <<= 8;
3831 val |= pcnetAPROMReadU8(pThis, addr+1);
3832 val <<= 8;
3833 val |= pcnetAPROMReadU8(pThis, addr );
3834 }
3835#ifdef PCNET_DEBUG_IO
3836 Log2(("#%d pcnetMMIOReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
3837#endif
3838 return val;
3839}
3840
3841
3842/**
3843 * @callback_method_impl{FNIOMMMIOREAD}
3844 */
3845PDMBOTHCBDECL(int) pcnetMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3846{
3847 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3848 int rc = VINF_SUCCESS;
3849 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3850 RT_NOREF_PV(pDevIns);
3851
3852 /*
3853 * We have to check the range, because we're page aligning the MMIO.
3854 */
3855 if (GCPhysAddr - pThis->MMIOBase < PCNET_PNPMMIO_SIZE)
3856 {
3857 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIORead), a);
3858 switch (cb)
3859 {
3860 case 1: *(uint8_t *)pv = pcnetMMIOReadU8 (pThis, GCPhysAddr); break;
3861 case 2: *(uint16_t *)pv = pcnetMMIOReadU16(pThis, GCPhysAddr); break;
3862 case 4: *(uint32_t *)pv = pcnetMMIOReadU32(pThis, GCPhysAddr); break;
3863 default:
3864 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3865 "pcnetMMIORead: unsupported op size: address=%RGp cb=%u\n",
3866 GCPhysAddr, cb);
3867 }
3868 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIORead), a);
3869 }
3870 else
3871 memset(pv, 0, cb);
3872
3873 LogFlow(("#%d pcnetMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n",
3874 PCNET_INST_NR, pv, cb, pv, cb, GCPhysAddr, rc));
3875 return rc;
3876}
3877
3878
3879/**
3880 * @callback_method_impl{FNIOMMMIOWRITE}
3881 */
3882PDMBOTHCBDECL(int) pcnetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
3883{
3884 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3885 int rc = VINF_SUCCESS;
3886 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3887 RT_NOREF_PV(pDevIns);
3888
3889 /*
3890 * We have to check the range, because we're page aligning the MMIO stuff presently.
3891 */
3892 if (GCPhysAddr - pThis->MMIOBase < PCNET_PNPMMIO_SIZE)
3893 {
3894 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3895 switch (cb)
3896 {
3897 case 1: pcnetMMIOWriteU8 (pThis, GCPhysAddr, *(uint8_t *)pv); break;
3898 case 2: pcnetMMIOWriteU16(pThis, GCPhysAddr, *(uint16_t *)pv); break;
3899 case 4: pcnetMMIOWriteU32(pThis, GCPhysAddr, *(uint32_t *)pv); break;
3900 default:
3901 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3902 "pcnetMMIOWrite: unsupported op size: address=%RGp cb=%u\n",
3903 GCPhysAddr, cb);
3904 }
3905
3906 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3907 }
3908 LogFlow(("#%d pcnetMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n",
3909 PCNET_INST_NR, pv, cb, pv, cb, GCPhysAddr, rc));
3910 return rc;
3911}
3912
3913
3914#ifdef IN_RING3
3915
3916/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
3917
3918/**
3919 * @callback_method_impl{FNTMTIMERDEV, Poll timer}
3920 */
3921static DECLCALLBACK(void) pcnetTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3922{
3923 RT_NOREF(pDevIns, pTimer);
3924 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3925 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3926
3927 STAM_PROFILE_ADV_START(&pThis->StatTimer, a);
3928 pcnetPollTimer(pThis);
3929 STAM_PROFILE_ADV_STOP(&pThis->StatTimer, a);
3930}
3931
3932
3933/**
3934 * @callback_method_impl{FNTMTIMERDEV,
3935 * Software interrupt timer callback function.}
3936 */
3937static DECLCALLBACK(void) pcnetTimerSoftInt(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3938{
3939 RT_NOREF(pDevIns, pTimer);
3940 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3941 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3942
3943 pThis->aCSR[7] |= 0x0800; /* STINT */
3944 pcnetUpdateIrq(pThis);
3945 TMTimerSetNano(pThis->CTX_SUFF(pTimerSoftInt), 12800U * (pThis->aBCR[BCR_STVAL] & 0xffff));
3946}
3947
3948
3949/**
3950 * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
3951 *
3952 * This is only called when we restore a saved state and temporarily
3953 * disconnected the network link to inform the guest that network connections
3954 * should be considered lost.
3955 */
3956static DECLCALLBACK(void) pcnetTimerRestore(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3957{
3958 RT_NOREF(pTimer, pvUser);
3959 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3960 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
3961 AssertReleaseRC(rc);
3962
3963 rc = VERR_GENERAL_FAILURE;
3964 if (pThis->cLinkDownReported <= PCNET_MAX_LINKDOWN_REPORTED)
3965 rc = TMTimerSetMillies(pThis->pTimerRestore, 1500);
3966 if (RT_FAILURE(rc))
3967 {
3968 pThis->fLinkTempDown = false;
3969 if (pThis->fLinkUp)
3970 {
3971 LogRel(("PCnet#%d: The link is back up again after the restore.\n",
3972 pDevIns->iInstance));
3973 Log(("#%d pcnetTimerRestore: Clearing ERR and CERR after load. cLinkDownReported=%d\n",
3974 pDevIns->iInstance, pThis->cLinkDownReported));
3975 pThis->aCSR[0] &= ~(RT_BIT(15) | RT_BIT(13)); /* ERR | CERR - probably not 100% correct either... */
3976 pThis->Led.Actual.s.fError = 0;
3977 }
3978 }
3979 else
3980 Log(("#%d pcnetTimerRestore: cLinkDownReported=%d, wait another 1500ms...\n",
3981 pDevIns->iInstance, pThis->cLinkDownReported));
3982
3983 PDMCritSectLeave(&pThis->CritSect);
3984}
3985
3986
3987/* -=-=-=-=-=- PCI Device Callbacks -=-=-=-=-=- */
3988
3989/**
3990 * @callback_method_impl{FNPCIIOREGIONMAP, For the PCnet I/O Ports.}
3991 */
3992static DECLCALLBACK(int) pcnetIOPortMap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
3993 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
3994{
3995 RT_NOREF(iRegion, cb, enmType);
3996 int rc;
3997 RTIOPORT Port = (RTIOPORT)GCPhysAddress;
3998 PPCNETSTATE pThis = PCIDEV_2_PCNETSTATE(pPciDev);
3999
4000 Assert(enmType == PCI_ADDRESS_SPACE_IO);
4001 Assert(cb >= 0x20);
4002
4003 rc = PDMDevHlpIOPortRegister(pDevIns, Port, 0x10, 0, pcnetIOPortAPromWrite,
4004 pcnetIOPortAPromRead, NULL, NULL, "PCnet APROM");
4005 if (RT_FAILURE(rc))
4006 return rc;
4007 rc = PDMDevHlpIOPortRegister(pDevIns, Port + 0x10, 0x10, 0, pcnetIOPortWrite,
4008 pcnetIOPortRead, NULL, NULL, "PCnet");
4009 if (RT_FAILURE(rc))
4010 return rc;
4011
4012 if (pThis->fGCEnabled)
4013 {
4014 rc = PDMDevHlpIOPortRegisterRC(pDevIns, Port, 0x10, 0, "pcnetIOPortAPromWrite",
4015 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
4016 if (RT_FAILURE(rc))
4017 return rc;
4018 rc = PDMDevHlpIOPortRegisterRC(pDevIns, Port + 0x10, 0x10, 0, "pcnetIOPortWrite",
4019 "pcnetIOPortRead", NULL, NULL, "PCnet");
4020 if (RT_FAILURE(rc))
4021 return rc;
4022 }
4023 if (pThis->fR0Enabled)
4024 {
4025 rc = PDMDevHlpIOPortRegisterR0(pDevIns, Port, 0x10, 0, "pcnetIOPortAPromWrite",
4026 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
4027 if (RT_FAILURE(rc))
4028 return rc;
4029 rc = PDMDevHlpIOPortRegisterR0(pDevIns, Port + 0x10, 0x10, 0, "pcnetIOPortWrite",
4030 "pcnetIOPortRead", NULL, NULL, "PCnet");
4031 if (RT_FAILURE(rc))
4032 return rc;
4033 }
4034
4035 pThis->IOPortBase = Port;
4036 return VINF_SUCCESS;
4037}
4038
4039
4040/**
4041 * @callback_method_impl{FNPCIIOREGIONMAP, For the PCnet MMIO region.}
4042 */
4043static DECLCALLBACK(int) pcnetMMIOMap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
4044 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
4045{
4046 RT_NOREF(iRegion, cb, enmType);
4047 PPCNETSTATE pThis = PCIDEV_2_PCNETSTATE(pPciDev);
4048 int rc;
4049
4050 Assert(enmType == PCI_ADDRESS_SPACE_MEM);
4051 Assert(cb >= PCNET_PNPMMIO_SIZE);
4052
4053 /* We use the assigned size here, because we only support page aligned MMIO ranges. */
4054 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, pThis,
4055 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
4056 pcnetMMIOWrite, pcnetMMIORead, "PCnet");
4057 if (RT_FAILURE(rc))
4058 return rc;
4059 pThis->MMIOBase = GCPhysAddress;
4060 return rc;
4061}
4062
4063
4064/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
4065
4066/**
4067 * @callback_method_impl{FNDBGFHANDLERDEV}
4068 */
4069static DECLCALLBACK(void) pcnetInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4070{
4071 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4072 bool fRcvRing = false;
4073 bool fXmtRing = false;
4074 bool fAPROM = false;
4075
4076 /*
4077 * Parse args.
4078 */
4079 if (pszArgs)
4080 {
4081 fRcvRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "rcv");
4082 fXmtRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "xmt");
4083 fAPROM = strstr(pszArgs, "verbose") || strstr(pszArgs, "aprom");
4084 }
4085
4086 /*
4087 * Show info.
4088 */
4089 const char *pcszModel;
4090 switch (pThis->uDevType)
4091 {
4092 case DEV_AM79C970A: pcszModel = "AM79C970A"; break;
4093 case DEV_AM79C973: pcszModel = "AM79C973"; break;
4094 case DEV_AM79C960: pcszModel = "AM79C960/NE2100"; break;
4095 case DEV_AM79C960_EB: pcszModel = "AM79C960/EtherBlaster"; break;
4096 default: pcszModel = "Unknown"; break;
4097 }
4098 pHlp->pfnPrintf(pHlp,
4099 "pcnet #%d: port=%RTiop mmio=%RX32 mac-cfg=%RTmac %s%s%s\n",
4100 pDevIns->iInstance,
4101 pThis->IOPortBase, pThis->MMIOBase, &pThis->MacConfigured,
4102 pcszModel, pThis->fGCEnabled ? " RC" : "", pThis->fR0Enabled ? " R0" : "");
4103
4104 PDMCritSectEnter(&pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
4105
4106 pHlp->pfnPrintf(pHlp,
4107 "CSR0=%#06x:\n",
4108 pThis->aCSR[0]);
4109
4110 pHlp->pfnPrintf(pHlp,
4111 "CSR1=%#06x:\n",
4112 pThis->aCSR[1]);
4113
4114 pHlp->pfnPrintf(pHlp,
4115 "CSR2=%#06x:\n",
4116 pThis->aCSR[2]);
4117
4118 pHlp->pfnPrintf(pHlp,
4119 "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",
4120 pThis->aCSR[3],
4121 !!(pThis->aCSR[3] & RT_BIT(2)), !!(pThis->aCSR[3] & RT_BIT(3)), !!(pThis->aCSR[3] & RT_BIT(4)), CSR_LAPPEN(pThis),
4122 CSR_DXSUFLO(pThis), !!(pThis->aCSR[3] & RT_BIT(8)), !!(pThis->aCSR[3] & RT_BIT(9)), !!(pThis->aCSR[3] & RT_BIT(10)),
4123 !!(pThis->aCSR[3] & RT_BIT(11)), !!(pThis->aCSR[3] & RT_BIT(12)), !!(pThis->aCSR[3] & RT_BIT(14)));
4124
4125 pHlp->pfnPrintf(pHlp,
4126 "CSR4=%#06x: JABM=%d JAB=%d TXSTRM=%d TXSTRT=%d RCVCOOM=%d RCVCCO=%d UINT=%d UINTCMD=%d\n"
4127 " MFCOM=%d MFCO=%d ASTRP_RCV=%d APAD_XMT=%d DPOLL=%d TIMER=%d EMAPLUS=%d EN124=%d\n",
4128 pThis->aCSR[4],
4129 !!(pThis->aCSR[4] & RT_BIT( 0)), !!(pThis->aCSR[4] & RT_BIT( 1)), !!(pThis->aCSR[4] & RT_BIT( 2)), !!(pThis->aCSR[4] & RT_BIT( 3)),
4130 !!(pThis->aCSR[4] & RT_BIT( 4)), !!(pThis->aCSR[4] & RT_BIT( 5)), !!(pThis->aCSR[4] & RT_BIT( 6)), !!(pThis->aCSR[4] & RT_BIT( 7)),
4131 !!(pThis->aCSR[4] & RT_BIT( 8)), !!(pThis->aCSR[4] & RT_BIT( 9)), !!(pThis->aCSR[4] & RT_BIT(10)), !!(pThis->aCSR[4] & RT_BIT(11)),
4132 !!(pThis->aCSR[4] & RT_BIT(12)), !!(pThis->aCSR[4] & RT_BIT(13)), !!(pThis->aCSR[4] & RT_BIT(14)), !!(pThis->aCSR[4] & RT_BIT(15)));
4133
4134 pHlp->pfnPrintf(pHlp,
4135 "CSR5=%#06x:\n",
4136 pThis->aCSR[5]);
4137
4138 pHlp->pfnPrintf(pHlp,
4139 "CSR6=%#06x: RLEN=%#x* TLEN=%#x* [* encoded]\n",
4140 pThis->aCSR[6],
4141 (pThis->aCSR[6] >> 8) & 0xf, (pThis->aCSR[6] >> 12) & 0xf);
4142
4143 pHlp->pfnPrintf(pHlp,
4144 "CSR8..11=%#06x,%#06x,%#06x,%#06x: LADRF=%#018llx\n",
4145 pThis->aCSR[8], pThis->aCSR[9], pThis->aCSR[10], pThis->aCSR[11],
4146 (uint64_t)(pThis->aCSR[ 8] & 0xffff)
4147 | (uint64_t)(pThis->aCSR[ 9] & 0xffff) << 16
4148 | (uint64_t)(pThis->aCSR[10] & 0xffff) << 32
4149 | (uint64_t)(pThis->aCSR[11] & 0xffff) << 48);
4150
4151 pHlp->pfnPrintf(pHlp,
4152 "CSR12..14=%#06x,%#06x,%#06x: PADR=%02x:%02x:%02x:%02x:%02x:%02x (Current MAC Address)\n",
4153 pThis->aCSR[12], pThis->aCSR[13], pThis->aCSR[14],
4154 pThis->aCSR[12] & 0xff,
4155 (pThis->aCSR[12] >> 8) & 0xff,
4156 pThis->aCSR[13] & 0xff,
4157 (pThis->aCSR[13] >> 8) & 0xff,
4158 pThis->aCSR[14] & 0xff,
4159 (pThis->aCSR[14] >> 8) & 0xff);
4160
4161 pHlp->pfnPrintf(pHlp,
4162 "CSR15=%#06x: DXR=%d DTX=%d LOOP=%d DXMTFCS=%d FCOLL=%d DRTY=%d INTL=%d PORTSEL=%d LTR=%d\n"
4163 " MENDECL=%d DAPC=%d DLNKTST=%d DRCVPV=%d DRCVBC=%d PROM=%d\n",
4164 pThis->aCSR[15],
4165 !!(pThis->aCSR[15] & RT_BIT( 0)), !!(pThis->aCSR[15] & RT_BIT( 1)), !!(pThis->aCSR[15] & RT_BIT( 2)), !!(pThis->aCSR[15] & RT_BIT( 3)),
4166 !!(pThis->aCSR[15] & RT_BIT( 4)), !!(pThis->aCSR[15] & RT_BIT( 5)), !!(pThis->aCSR[15] & RT_BIT( 6)), (pThis->aCSR[15] >> 7) & 3,
4167 !!(pThis->aCSR[15] & RT_BIT( 9)), !!(pThis->aCSR[15] & RT_BIT(10)), !!(pThis->aCSR[15] & RT_BIT(11)),
4168 !!(pThis->aCSR[15] & RT_BIT(12)), !!(pThis->aCSR[15] & RT_BIT(13)), !!(pThis->aCSR[15] & RT_BIT(14)), !!(pThis->aCSR[15] & RT_BIT(15)));
4169
4170 pHlp->pfnPrintf(pHlp,
4171 "CSR46=%#06x: POLL=%#06x (Poll Time Counter)\n",
4172 pThis->aCSR[46], pThis->aCSR[46] & 0xffff);
4173
4174 pHlp->pfnPrintf(pHlp,
4175 "CSR47=%#06x: POLLINT=%#06x (Poll Time Interval)\n",
4176 pThis->aCSR[47], pThis->aCSR[47] & 0xffff);
4177
4178 pHlp->pfnPrintf(pHlp,
4179 "CSR58=%#06x: SWSTYLE=%d %s SSIZE32=%d CSRPCNET=%d APERRENT=%d\n",
4180 pThis->aCSR[58],
4181 pThis->aCSR[58] & 0x7f,
4182 (pThis->aCSR[58] & 0x7f) == 0 ? "C-LANCE / PCnet-ISA"
4183 : (pThis->aCSR[58] & 0x7f) == 1 ? "ILACC"
4184 : (pThis->aCSR[58] & 0x7f) == 2 ? "PCnet-PCI II"
4185 : (pThis->aCSR[58] & 0x7f) == 3 ? "PCnet-PCI II controller"
4186 : "!!reserved!!",
4187 !!(pThis->aCSR[58] & RT_BIT(8)), !!(pThis->aCSR[58] & RT_BIT(9)), !!(pThis->aCSR[58] & RT_BIT(10)));
4188
4189 pHlp->pfnPrintf(pHlp,
4190 "CSR112=%04RX32: MFC=%04x (Missed receive Frame Count)\n",
4191 pThis->aCSR[112], pThis->aCSR[112] & 0xffff);
4192
4193 pHlp->pfnPrintf(pHlp,
4194 "CSR122=%04RX32: RCVALGN=%04x (Receive Frame Align)\n",
4195 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(0)));
4196
4197 pHlp->pfnPrintf(pHlp,
4198 "CSR124=%04RX32: RPA=%04x (Runt Packet Accept)\n",
4199 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(3)));
4200
4201
4202 /*
4203 * Dump the receive ring.
4204 */
4205 pHlp->pfnPrintf(pHlp,
4206 "RCVRL=%04x RCVRC=%04x GCRDRA=%RX32 \n"
4207 "CRDA=%08RX32 CRBA=%08RX32 CRBC=%03x CRST=%04x\n"
4208 "NRDA=%08RX32 NRBA=%08RX32 NRBC=%03x NRST=%04x\n"
4209 "NNRDA=%08RX32\n"
4210 ,
4211 CSR_RCVRL(pThis), CSR_RCVRC(pThis), pThis->GCRDRA,
4212 CSR_CRDA(pThis), CSR_CRBA(pThis), CSR_CRBC(pThis), CSR_CRST(pThis),
4213 CSR_NRDA(pThis), CSR_NRBA(pThis), CSR_NRBC(pThis), CSR_NRST(pThis),
4214 CSR_NNRD(pThis));
4215 if (fRcvRing)
4216 {
4217 const unsigned cb = 1 << pThis->iLog2DescSize;
4218 RTGCPHYS32 GCPhys = pThis->GCRDRA;
4219 unsigned i = CSR_RCVRL(pThis);
4220 while (i-- > 0)
4221 {
4222 RMD rmd;
4223 pcnetRmdLoad(pThis, &rmd, PHYSADDR(pThis, GCPhys), false);
4224 pHlp->pfnPrintf(pHlp,
4225 "%04x %RX32:%c%c RBADR=%08RX32 BCNT=%03x MCNT=%03x "
4226 "OWN=%d ERR=%d FRAM=%d OFLO=%d CRC=%d BUFF=%d STP=%d ENP=%d BPE=%d "
4227 "PAM=%d LAFM=%d BAM=%d RCC=%02x RPC=%02x ONES=%#x ZEROS=%d\n",
4228 i, GCPhys, i + 1 == CSR_RCVRC(pThis) ? '*' : ' ', GCPhys == CSR_CRDA(pThis) ? '*' : ' ',
4229 rmd.rmd0.rbadr, 4096 - rmd.rmd1.bcnt, rmd.rmd2.mcnt,
4230 rmd.rmd1.own, rmd.rmd1.err, rmd.rmd1.fram, rmd.rmd1.oflo, rmd.rmd1.crc, rmd.rmd1.buff,
4231 rmd.rmd1.stp, rmd.rmd1.enp, rmd.rmd1.bpe,
4232 rmd.rmd1.pam, rmd.rmd1.lafm, rmd.rmd1.bam, rmd.rmd2.rcc, rmd.rmd2.rpc,
4233 rmd.rmd1.ones, rmd.rmd2.zeros);
4234
4235 GCPhys += cb;
4236 }
4237 }
4238
4239 /*
4240 * Dump the transmit ring.
4241 */
4242 pHlp->pfnPrintf(pHlp,
4243 "XMTRL=%04x XMTRC=%04x GCTDRA=%08RX32 BADX=%08RX32\n"
4244 "PXDA=%08RX32 PXBC=%03x PXST=%04x\n"
4245 "CXDA=%08RX32 CXBA=%08RX32 CXBC=%03x CXST=%04x\n"
4246 "NXDA=%08RX32 NXBA=%08RX32 NXBC=%03x NXST=%04x\n"
4247 "NNXDA=%08RX32\n"
4248 ,
4249 CSR_XMTRL(pThis), CSR_XMTRC(pThis),
4250 pThis->GCTDRA, CSR_BADX(pThis),
4251 CSR_PXDA(pThis), CSR_PXBC(pThis), CSR_PXST(pThis),
4252 CSR_CXDA(pThis), CSR_CXBA(pThis), CSR_CXBC(pThis), CSR_CXST(pThis),
4253 CSR_NXDA(pThis), CSR_NXBA(pThis), CSR_NXBC(pThis), CSR_NXST(pThis),
4254 CSR_NNXD(pThis));
4255 if (fXmtRing)
4256 {
4257 const unsigned cb = 1 << pThis->iLog2DescSize;
4258 RTGCPHYS32 GCPhys = pThis->GCTDRA;
4259 unsigned i = CSR_XMTRL(pThis);
4260 while (i-- > 0)
4261 {
4262 TMD tmd;
4263 pcnetTmdLoad(pThis, &tmd, PHYSADDR(pThis, GCPhys), false);
4264 pHlp->pfnPrintf(pHlp,
4265 "%04x %RX32:%c%c TBADR=%08RX32 BCNT=%03x OWN=%d "
4266 "ERR=%d NOFCS=%d LTINT=%d ONE=%d DEF=%d STP=%d ENP=%d BPE=%d "
4267 "BUFF=%d UFLO=%d EXDEF=%d LCOL=%d LCAR=%d RTRY=%d TDR=%03x TRC=%#x ONES=%#x\n"
4268 ,
4269 i,
4270 GCPhys,
4271 i + 1 == CSR_XMTRC(pThis) ? '*' : ' ',
4272 GCPhys == CSR_CXDA(pThis) ? '*' : ' ',
4273 tmd.tmd0.tbadr,
4274 4096 - tmd.tmd1.bcnt,
4275 tmd.tmd1.own,
4276 tmd.tmd1.err,
4277 tmd.tmd1.nofcs,
4278 tmd.tmd1.ltint,
4279 tmd.tmd1.one,
4280 tmd.tmd1.def,
4281 tmd.tmd1.stp,
4282 tmd.tmd1.enp,
4283 tmd.tmd1.bpe,
4284 tmd.tmd2.buff,
4285 tmd.tmd2.uflo,
4286 tmd.tmd2.exdef,
4287 tmd.tmd2.lcol,
4288 tmd.tmd2.lcar,
4289 tmd.tmd2.rtry,
4290 tmd.tmd2.tdr,
4291 tmd.tmd2.trc,
4292 tmd.tmd1.ones);
4293
4294 GCPhys += cb;
4295 }
4296 }
4297
4298 /* Dump the Addres PROM (APROM). */
4299 if (fAPROM)
4300 {
4301 pHlp->pfnPrintf(pHlp,
4302 "Address PROM:\n %Rhxs\n", pThis->aPROM);
4303
4304
4305 }
4306
4307 PDMCritSectLeave(&pThis->CritSect);
4308}
4309
4310
4311/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
4312
4313/**
4314 * Takes down the link temporarily if it's current status is up.
4315 *
4316 * This is used during restore and when replumbing the network link.
4317 *
4318 * The temporary link outage is supposed to indicate to the OS that all network
4319 * connections have been lost and that it for instance is appropriate to
4320 * renegotiate any DHCP lease.
4321 *
4322 * @param pThis The PCnet instance data.
4323 */
4324static void pcnetTempLinkDown(PPCNETSTATE pThis)
4325{
4326 if (pThis->fLinkUp)
4327 {
4328 pThis->fLinkTempDown = true;
4329 pThis->cLinkDownReported = 0;
4330 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4331 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4332 int rc = TMTimerSetMillies(pThis->pTimerRestore, pThis->cMsLinkUpDelay);
4333 AssertRC(rc);
4334 }
4335}
4336
4337
4338/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
4339
4340/**
4341 * Saves the configuration.
4342 *
4343 * @param pThis The PCnet instance data.
4344 * @param pSSM The saved state handle.
4345 */
4346static void pcnetSaveConfig(PPCNETSTATE pThis, PSSMHANDLE pSSM)
4347{
4348 SSMR3PutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4349 SSMR3PutU8(pSSM, pThis->uDevType);
4350 SSMR3PutU32(pSSM, pThis->u32LinkSpeed);
4351}
4352
4353
4354/**
4355 * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
4356 */
4357static DECLCALLBACK(int) pcnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
4358{
4359 RT_NOREF(uPass);
4360 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4361 pcnetSaveConfig(pThis, pSSM);
4362 return VINF_SSM_DONT_CALL_AGAIN;
4363}
4364
4365
4366/**
4367 * @callback_method_impl{FNSSMDEVSAVEPREP,
4368 * Serializes the receive thread, it may be working inside the critsect.}
4369 */
4370static DECLCALLBACK(int) pcnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4371{
4372 RT_NOREF(pSSM);
4373 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4374
4375 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4376 AssertRC(rc);
4377 PDMCritSectLeave(&pThis->CritSect);
4378
4379 return VINF_SUCCESS;
4380}
4381
4382
4383/**
4384 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4385 */
4386static DECLCALLBACK(int) pcnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4387{
4388 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4389
4390 SSMR3PutBool(pSSM, pThis->fLinkUp);
4391 SSMR3PutU32(pSSM, pThis->u32RAP);
4392 SSMR3PutS32(pSSM, pThis->iISR);
4393 SSMR3PutU32(pSSM, pThis->u32Lnkst);
4394 SSMR3PutBool(pSSM, false/* was ffPrivIfEnabled */); /* >= If version 0.9 */
4395 SSMR3PutBool(pSSM, pThis->fSignalRxMiss); /* >= If version 0.10 */
4396 SSMR3PutGCPhys32(pSSM, pThis->GCRDRA);
4397 SSMR3PutGCPhys32(pSSM, pThis->GCTDRA);
4398 SSMR3PutMem(pSSM, pThis->aPROM, sizeof(pThis->aPROM));
4399 SSMR3PutMem(pSSM, pThis->aCSR, sizeof(pThis->aCSR));
4400 SSMR3PutMem(pSSM, pThis->aBCR, sizeof(pThis->aBCR));
4401 SSMR3PutMem(pSSM, pThis->aMII, sizeof(pThis->aMII));
4402 SSMR3PutU16(pSSM, pThis->u16CSR0LastSeenByGuest);
4403 SSMR3PutU64(pSSM, pThis->u64LastPoll);
4404 pcnetSaveConfig(pThis, pSSM);
4405
4406 int rc = VINF_SUCCESS;
4407#ifndef PCNET_NO_POLLING
4408 rc = TMR3TimerSave(pThis->CTX_SUFF(pTimerPoll), pSSM);
4409 if (RT_FAILURE(rc))
4410 return rc;
4411#endif
4412 if (pThis->uDevType == DEV_AM79C973)
4413 rc = TMR3TimerSave(pThis->CTX_SUFF(pTimerSoftInt), pSSM);
4414 return rc;
4415}
4416
4417
4418/**
4419 * @callback_method_impl{FNSSMDEVLOADPREP},
4420 * Serializes the receive thread, it may be working inside the critsect.}
4421 */
4422static DECLCALLBACK(int) pcnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4423{
4424 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4425
4426 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4427 AssertRC(rc);
4428
4429 uint32_t uVer = SSMR3HandleVersion(pSSM);
4430 if ( uVer < VBOX_FULL_VERSION_MAKE(4, 3, 6)
4431 || ( uVer >= VBOX_FULL_VERSION_MAKE(4, 3, 51)
4432 && uVer < VBOX_FULL_VERSION_MAKE(4, 3, 53)))
4433 {
4434 /* older saved states contain the shared memory region which was never used for ages. */
4435 void *pvSharedMMIOR3;
4436 rc = PDMDevHlpMMIO2Register(pDevIns, &pThis->PciDev, 2, _512K, 0, (void **)&pvSharedMMIOR3, "PCnetSh");
4437 if (RT_FAILURE(rc))
4438 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
4439 N_("Failed to allocate the dummy shmem region for the PCnet device"));
4440 pThis->fSharedRegion = true;
4441 }
4442 PDMCritSectLeave(&pThis->CritSect);
4443
4444 return rc;
4445}
4446
4447
4448/**
4449 * @callback_method_impl{FNSSMDEVLOADEXEC}
4450 */
4451static DECLCALLBACK(int) pcnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4452{
4453 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4454
4455 if ( SSM_VERSION_MAJOR_CHANGED(uVersion, PCNET_SAVEDSTATE_VERSION)
4456 || SSM_VERSION_MINOR(uVersion) < 7)
4457 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4458
4459 if (uPass == SSM_PASS_FINAL)
4460 {
4461 /* restore data */
4462 SSMR3GetBool(pSSM, &pThis->fLinkUp);
4463 int rc = SSMR3GetU32(pSSM, &pThis->u32RAP);
4464 AssertRCReturn(rc, rc);
4465 AssertLogRelMsgReturn(pThis->u32RAP < RT_ELEMENTS(pThis->aCSR), ("%#x\n", pThis->u32RAP), VERR_SSM_LOAD_CONFIG_MISMATCH);
4466 SSMR3GetS32(pSSM, &pThis->iISR);
4467 SSMR3GetU32(pSSM, &pThis->u32Lnkst);
4468 if ( SSM_VERSION_MAJOR(uVersion) > 0
4469 || SSM_VERSION_MINOR(uVersion) >= 9)
4470 {
4471 bool fPrivIfEnabled = false;
4472 SSMR3GetBool(pSSM, &fPrivIfEnabled);
4473 if (fPrivIfEnabled)
4474 {
4475 /* no longer implemented */
4476 LogRel(("PCnet#%d: Cannot enable private interface!\n", PCNET_INST_NR));
4477 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4478 }
4479 }
4480 if ( SSM_VERSION_MAJOR(uVersion) > 0
4481 || SSM_VERSION_MINOR(uVersion) >= 10)
4482 {
4483 SSMR3GetBool(pSSM, &pThis->fSignalRxMiss);
4484 }
4485 SSMR3GetGCPhys32(pSSM, &pThis->GCRDRA);
4486 SSMR3GetGCPhys32(pSSM, &pThis->GCTDRA);
4487 SSMR3GetMem(pSSM, &pThis->aPROM, sizeof(pThis->aPROM));
4488 SSMR3GetMem(pSSM, &pThis->aCSR, sizeof(pThis->aCSR));
4489 SSMR3GetMem(pSSM, &pThis->aBCR, sizeof(pThis->aBCR));
4490 SSMR3GetMem(pSSM, &pThis->aMII, sizeof(pThis->aMII));
4491 SSMR3GetU16(pSSM, &pThis->u16CSR0LastSeenByGuest);
4492 SSMR3GetU64(pSSM, &pThis->u64LastPoll);
4493 }
4494
4495 /* check config */
4496 RTMAC Mac;
4497 int rc = SSMR3GetMem(pSSM, &Mac, sizeof(Mac));
4498 AssertRCReturn(rc, rc);
4499 if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
4500 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4501 LogRel(("PCnet#%u: The mac address differs: config=%RTmac saved=%RTmac\n", PCNET_INST_NR, &pThis->MacConfigured, &Mac));
4502
4503 uint8_t uDevType;
4504 rc = SSMR3GetU8(pSSM, &uDevType);
4505 AssertRCReturn(rc, rc);
4506 if (pThis->uDevType != uDevType)
4507 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("The uDevType setting differs: config=%u saved=%u"), pThis->uDevType, uDevType);
4508
4509 uint32_t u32LinkSpeed;
4510 rc = SSMR3GetU32(pSSM, &u32LinkSpeed);
4511 AssertRCReturn(rc, rc);
4512 if ( pThis->u32LinkSpeed != u32LinkSpeed
4513 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4514 LogRel(("PCnet#%u: The mac link speed differs: config=%u saved=%u\n", PCNET_INST_NR, pThis->u32LinkSpeed, u32LinkSpeed));
4515
4516 if (uPass == SSM_PASS_FINAL)
4517 {
4518 /* restore timers and stuff */
4519#ifndef PCNET_NO_POLLING
4520 TMR3TimerLoad(pThis->CTX_SUFF(pTimerPoll), pSSM);
4521#endif
4522 if (pThis->uDevType == DEV_AM79C973)
4523 {
4524 if ( SSM_VERSION_MAJOR(uVersion) > 0
4525 || SSM_VERSION_MINOR(uVersion) >= 8)
4526 TMR3TimerLoad(pThis->CTX_SUFF(pTimerSoftInt), pSSM);
4527 }
4528
4529 pThis->iLog2DescSize = BCR_SWSTYLE(pThis)
4530 ? 4
4531 : 3;
4532 pThis->GCUpperPhys = BCR_SSIZE32(pThis)
4533 ? 0
4534 : (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
4535
4536 /* update promiscuous mode. */
4537 if (pThis->pDrvR3)
4538 pThis->pDrvR3->pfnSetPromiscuousMode(pThis->pDrvR3, CSR_PROM(pThis));
4539
4540#ifdef PCNET_NO_POLLING
4541 /* Enable physical monitoring again (!) */
4542 pcnetUpdateRingHandlers(pThis);
4543#endif
4544 /* Indicate link down to the guest OS that all network connections have
4545 been lost, unless we've been teleported here. */
4546 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
4547 pcnetTempLinkDown(pThis);
4548 }
4549
4550 return VINF_SUCCESS;
4551}
4552
4553/**
4554 * @callback_method_impl{FNSSMDEVLOADDONE}
4555 */
4556static DECLCALLBACK(int) pcnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4557{
4558 RT_NOREF(pSSM);
4559 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4560 int rc = VINF_SUCCESS;
4561 if (pThis->fSharedRegion)
4562 {
4563 /* drop this dummy region */
4564 rc = PDMDevHlpMMIOExDeregister(pDevIns, NULL, 2);
4565 pThis->fSharedRegion = false;
4566 }
4567 return rc;
4568}
4569
4570/* -=-=-=-=-=- PCNETSTATE::INetworkDown -=-=-=-=-=- */
4571
4572/**
4573 * Check if the device/driver can receive data now.
4574 *
4575 * Worker for pcnetNetworkDown_WaitReceiveAvail(). This must be called before
4576 * the pfnRecieve() method is called.
4577 *
4578 * @returns VBox status code.
4579 * @param pThis The PCnet instance data.
4580 */
4581static int pcnetCanReceive(PPCNETSTATE pThis)
4582{
4583 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4584 AssertReleaseRC(rc);
4585
4586 rc = VERR_NET_NO_BUFFER_SPACE;
4587
4588 if (RT_LIKELY(!CSR_DRX(pThis) && !CSR_STOP(pThis) && !CSR_SPND(pThis)))
4589 {
4590 if (HOST_IS_OWNER(CSR_CRST(pThis)) && pThis->GCRDRA)
4591 pcnetRdtePoll(pThis);
4592
4593 if (RT_UNLIKELY(HOST_IS_OWNER(CSR_CRST(pThis))))
4594 {
4595 /** @todo Notify the guest _now_. Will potentially increase the interrupt load */
4596 if (pThis->fSignalRxMiss)
4597 pThis->aCSR[0] |= 0x1000; /* Set MISS flag */
4598 }
4599 else
4600 rc = VINF_SUCCESS;
4601 }
4602
4603 PDMCritSectLeave(&pThis->CritSect);
4604 return rc;
4605}
4606
4607
4608/**
4609 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
4610 */
4611static DECLCALLBACK(int) pcnetNetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
4612{
4613 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkDown);
4614
4615 int rc = pcnetCanReceive(pThis);
4616 if (RT_SUCCESS(rc))
4617 return VINF_SUCCESS;
4618 if (RT_UNLIKELY(cMillies == 0))
4619 return VERR_NET_NO_BUFFER_SPACE;
4620
4621 rc = VERR_INTERRUPTED;
4622 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
4623 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
4624 VMSTATE enmVMState;
4625 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pThis->CTX_SUFF(pDevIns))) == VMSTATE_RUNNING
4626 || enmVMState == VMSTATE_RUNNING_LS))
4627 {
4628 int rc2 = pcnetCanReceive(pThis);
4629 if (RT_SUCCESS(rc2))
4630 {
4631 rc = VINF_SUCCESS;
4632 break;
4633 }
4634 LogFlow(("pcnetNetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", cMillies));
4635 /* Start the poll timer once which will remain active as long fMaybeOutOfSpace
4636 * is true -- even if (transmit) polling is disabled (CSR_DPOLL). */
4637 rc2 = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4638 AssertReleaseRC(rc2);
4639#ifndef PCNET_NO_POLLING
4640 pcnetPollTimerStart(pThis);
4641#endif
4642 PDMCritSectLeave(&pThis->CritSect);
4643 RTSemEventWait(pThis->hEventOutOfRxSpace, cMillies);
4644 }
4645 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
4646 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
4647
4648 return rc;
4649}
4650
4651
4652/**
4653 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
4654 */
4655static DECLCALLBACK(int) pcnetNetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
4656{
4657 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkDown);
4658 int rc;
4659
4660 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
4661 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4662 AssertReleaseRC(rc);
4663
4664 /*
4665 * Check for the max ethernet frame size, taking the IEEE 802.1Q (VLAN) tag into
4666 * account. Note that the CRC Checksum is optional.
4667 * Ethernet frames consist of a 14-byte header [+ 4-byte vlan tag] + a 1500-byte body [+ 4-byte CRC].
4668 */
4669 if (RT_LIKELY( cb <= 1518
4670 || ( cb <= 1522
4671 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN))))
4672 {
4673 bool fAddFCS = cb <= 1514
4674 || ( cb <= 1518
4675 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN));
4676 if (cb > 70) /* unqualified guess */
4677 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
4678 pcnetReceiveNoSync(pThis, (const uint8_t *)pvBuf, cb, fAddFCS);
4679 pThis->Led.Actual.s.fReading = 0;
4680 }
4681#ifdef LOG_ENABLED
4682 else
4683 {
4684 static bool s_fFirstBigFrameLoss = true;
4685 unsigned cbMaxFrame = ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN)
4686 ? 1522 : 1518;
4687 if (s_fFirstBigFrameLoss)
4688 {
4689 s_fFirstBigFrameLoss = false;
4690 Log(("PCnet#%d: Received giant frame %zu, max %u. (Further giants will be reported at level5.)\n",
4691 PCNET_INST_NR, cb, cbMaxFrame));
4692 }
4693 else
4694 Log5(("PCnet#%d: Received giant frame %zu bytes, max %u.\n",
4695 PCNET_INST_NR, cb, cbMaxFrame));
4696 }
4697#endif /* LOG_ENABLED */
4698
4699 PDMCritSectLeave(&pThis->CritSect);
4700 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
4701
4702 return VINF_SUCCESS;
4703}
4704
4705
4706/**
4707 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
4708 */
4709static DECLCALLBACK(void) pcnetNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
4710{
4711 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkDown);
4712 pcnetXmitPending(pThis, true /*fOnWorkerThread*/);
4713}
4714
4715
4716/* -=-=-=-=-=- PCNETSTATE::INetworkConfig -=-=-=-=-=- */
4717
4718/**
4719 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
4720 */
4721static DECLCALLBACK(int) pcnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
4722{
4723 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkConfig);
4724 memcpy(pMac, pThis->aPROM, sizeof(*pMac));
4725 return VINF_SUCCESS;
4726}
4727
4728
4729/**
4730 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
4731 */
4732static DECLCALLBACK(PDMNETWORKLINKSTATE) pcnetGetLinkState(PPDMINETWORKCONFIG pInterface)
4733{
4734 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkConfig);
4735 if (pThis->fLinkUp && !pThis->fLinkTempDown)
4736 return PDMNETWORKLINKSTATE_UP;
4737 if (!pThis->fLinkUp)
4738 return PDMNETWORKLINKSTATE_DOWN;
4739 if (pThis->fLinkTempDown)
4740 return PDMNETWORKLINKSTATE_DOWN_RESUME;
4741 AssertMsgFailed(("Invalid link state!\n"));
4742 return PDMNETWORKLINKSTATE_INVALID;
4743}
4744
4745
4746/**
4747 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
4748 */
4749static DECLCALLBACK(int) pcnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
4750{
4751 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkConfig);
4752 bool fLinkUp;
4753
4754 AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
4755 ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
4756
4757 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
4758 {
4759 pcnetTempLinkDown(pThis);
4760 /*
4761 * Note that we do not notify the driver about the link state change because
4762 * the change is only temporary and can be disregarded from the driver's
4763 * point of view (see @bugref{7057}).
4764 */
4765 return VINF_SUCCESS;
4766 }
4767 /* has the state changed? */
4768 fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
4769 if (pThis->fLinkUp != fLinkUp)
4770 {
4771 pThis->fLinkUp = fLinkUp;
4772 if (fLinkUp)
4773 {
4774 /* Connect with a configured delay. */
4775 pThis->fLinkTempDown = true;
4776 pThis->cLinkDownReported = 0;
4777 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4778 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4779 int rc = TMTimerSetMillies(pThis->pTimerRestore, pThis->cMsLinkUpDelay);
4780 AssertRC(rc);
4781 }
4782 else
4783 {
4784 /* disconnect */
4785 pThis->cLinkDownReported = 0;
4786 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4787 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4788 }
4789 Assert(!PDMCritSectIsOwner(&pThis->CritSect));
4790 if (pThis->pDrvR3)
4791 pThis->pDrvR3->pfnNotifyLinkChanged(pThis->pDrvR3, enmState);
4792 }
4793 return VINF_SUCCESS;
4794}
4795
4796
4797/* -=-=-=-=-=- PCNETSTATE::ILeds (LUN#0) -=-=-=-=-=- */
4798
4799/**
4800 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4801 */
4802static DECLCALLBACK(int) pcnetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4803{
4804 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, ILeds);
4805 if (iLUN == 0)
4806 {
4807 *ppLed = &pThis->Led;
4808 return VINF_SUCCESS;
4809 }
4810 return VERR_PDM_LUN_NOT_FOUND;
4811}
4812
4813
4814/* -=-=-=-=-=- PCNETSTATE::IBase (LUN#0) -=-=-=-=-=- */
4815
4816/**
4817 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4818 */
4819static DECLCALLBACK(void *) pcnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
4820{
4821 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, IBase);
4822 Assert(&pThis->IBase == pInterface);
4823 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4824 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
4825 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
4826 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
4827 return NULL;
4828}
4829
4830
4831/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
4832
4833/**
4834 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
4835 */
4836static DECLCALLBACK(void) pcnetPowerOff(PPDMDEVINS pDevIns)
4837{
4838 /* Poke thread waiting for buffer space. */
4839 pcnetWakeupReceive(pDevIns);
4840}
4841
4842
4843/**
4844 * @interface_method_impl{PDMDEVREG,pfnDetach}
4845 *
4846 * One port on the network card has been disconnected from the network.
4847 */
4848static DECLCALLBACK(void) pcnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4849{
4850 RT_NOREF(fFlags);
4851 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4852 Log(("#%d pcnetDetach:\n", PCNET_INST_NR));
4853
4854 AssertLogRelReturnVoid(iLUN == 0);
4855
4856 PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4857
4858 /** @todo r=pritesh still need to check if i missed
4859 * to clean something in this function
4860 */
4861
4862 /*
4863 * Zero some important members.
4864 */
4865 pThis->pDrvBase = NULL;
4866 pThis->pDrvR3 = NULL;
4867 pThis->pDrvR0 = NIL_RTR0PTR;
4868 pThis->pDrvRC = NIL_RTRCPTR;
4869
4870 PDMCritSectLeave(&pThis->CritSect);
4871}
4872
4873
4874/**
4875 * @interface_method_impl{PDMDEVREG,pfnAttach}
4876 * One port on the network card has been connected to a network.
4877 */
4878static DECLCALLBACK(int) pcnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4879{
4880 RT_NOREF(fFlags);
4881 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4882 LogFlow(("#%d pcnetAttach:\n", PCNET_INST_NR));
4883
4884 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
4885
4886 PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4887
4888 /*
4889 * Attach the driver.
4890 */
4891 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Network Port");
4892 if (RT_SUCCESS(rc))
4893 {
4894 if (rc == VINF_NAT_DNS)
4895 {
4896#ifdef RT_OS_LINUX
4897 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
4898 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"));
4899#else
4900 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
4901 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"));
4902#endif
4903 }
4904 pThis->pDrvR3 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
4905 AssertMsgStmt(pThis->pDrvR3, ("Failed to obtain the PDMINETWORKUP interface!\n"),
4906 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
4907 pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
4908 pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
4909 }
4910 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
4911 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
4912 {
4913 /* This should never happen because this function is not called
4914 * if there is no driver to attach! */
4915 Log(("#%d No attached driver!\n", PCNET_INST_NR));
4916 }
4917
4918 /*
4919 * Temporary set the link down if it was up so that the guest
4920 * will know that we have change the configuration of the
4921 * network card
4922 */
4923 if (RT_SUCCESS(rc))
4924 pcnetTempLinkDown(pThis);
4925
4926 PDMCritSectLeave(&pThis->CritSect);
4927 return rc;
4928
4929}
4930
4931
4932/**
4933 * @interface_method_impl{PDMDEVREG,pfnSuspend}
4934 */
4935static DECLCALLBACK(void) pcnetSuspend(PPDMDEVINS pDevIns)
4936{
4937 /* Poke thread waiting for buffer space. */
4938 pcnetWakeupReceive(pDevIns);
4939}
4940
4941
4942/**
4943 * @interface_method_impl{PDMDEVREG,pfnReset}
4944 */
4945static DECLCALLBACK(void) pcnetReset(PPDMDEVINS pDevIns)
4946{
4947 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4948 if (pThis->fLinkTempDown)
4949 {
4950 pThis->cLinkDownReported = 0x10000;
4951 TMTimerStop(pThis->pTimerRestore);
4952 pcnetTimerRestore(pDevIns, pThis->pTimerRestore, pThis);
4953 }
4954
4955 /** @todo How to flush the queues? */
4956 pcnetR3HardReset(pThis);
4957}
4958
4959
4960/**
4961 * @interface_method_impl{PDMDEVREG,pfnRelocate}
4962 */
4963static DECLCALLBACK(void) pcnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4964{
4965 RT_NOREF(offDelta);
4966 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4967 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
4968 pThis->pXmitQueueRC = PDMQueueRCPtr(pThis->pXmitQueueR3);
4969 pThis->pCanRxQueueRC = PDMQueueRCPtr(pThis->pCanRxQueueR3);
4970#ifdef PCNET_NO_POLLING
4971 pThis->pfnEMInterpretInstructionRC += offDelta;
4972#else
4973 pThis->pTimerPollRC = TMTimerRCPtr(pThis->pTimerPollR3);
4974#endif
4975 if (pThis->uDevType == DEV_AM79C973)
4976 pThis->pTimerSoftIntRC = TMTimerRCPtr(pThis->pTimerSoftIntR3);
4977}
4978
4979
4980/**
4981 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4982 */
4983static DECLCALLBACK(int) pcnetDestruct(PPDMDEVINS pDevIns)
4984{
4985 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
4986 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4987
4988 if (PDMCritSectIsInitialized(&pThis->CritSect))
4989 {
4990 RTSemEventSignal(pThis->hEventOutOfRxSpace);
4991 RTSemEventDestroy(pThis->hEventOutOfRxSpace);
4992 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
4993 PDMR3CritSectDelete(&pThis->CritSect);
4994 }
4995 return VINF_SUCCESS;
4996}
4997
4998
4999/**
5000 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5001 */
5002static DECLCALLBACK(int) pcnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5003{
5004 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5005 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
5006 PPDMIBASE pBase;
5007 char szTmp[128];
5008 int rc;
5009
5010 Assert(RT_ELEMENTS(pThis->aBCR) == BCR_MAX_RAP);
5011 Assert(RT_ELEMENTS(pThis->aMII) == MII_MAX_REG);
5012 Assert(sizeof(pThis->abLoopBuf) == RT_ALIGN_Z(sizeof(pThis->abLoopBuf), 16));
5013
5014 /*
5015 * Init what's required to make the destructor safe.
5016 */
5017 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
5018
5019 /*
5020 * Validate configuration.
5021 */
5022 if (!CFGMR3AreValuesValid(pCfg, "MAC\0" "CableConnected\0" "Am79C973\0" "ChipType\0" "LineSpeed\0" "GCEnabled\0" "R0Enabled\0" "PrivIfEnabled\0" "LinkUpDelay\0"))
5023 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5024 N_("Invalid configuration for pcnet device"));
5025
5026 /*
5027 * Read the configuration.
5028 */
5029 rc = CFGMR3QueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
5030 if (RT_FAILURE(rc))
5031 return PDMDEV_SET_ERROR(pDevIns, rc,
5032 N_("Configuration error: Failed to get the \"MAC\" value"));
5033 rc = CFGMR3QueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
5034 if (RT_FAILURE(rc))
5035 return PDMDEV_SET_ERROR(pDevIns, rc,
5036 N_("Configuration error: Failed to get the \"CableConnected\" value"));
5037
5038 /*
5039 * Determine the model.
5040 */
5041 char szChipType[16];
5042 rc = CFGMR3QueryStringDef(pCfg, "ChipType", &szChipType[0], sizeof(szChipType), "Am79C970A");
5043 if (RT_FAILURE(rc))
5044 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5045 N_("Configuration error: Querying \"ChipType\" as string failed"));
5046
5047 if (!strcmp(szChipType, "Am79C970A"))
5048 pThis->uDevType = DEV_AM79C970A; /* 10 Mbps PCnet-PCI II. */
5049 else if (!strcmp(szChipType, "Am79C973"))
5050 pThis->uDevType = DEV_AM79C973; /* 10/100 Mbps PCnet-FAST III. */
5051 else if (!strcmp(szChipType, "Am79C960"))
5052 pThis->uDevType = DEV_AM79C960; /* 10 Mbps PCnet-ISA, NE2100/Am2100 compatible. */
5053 else if (!strcmp(szChipType, "Am79C960_EB"))
5054 {
5055 pThis->uDevType = DEV_AM79C960_EB; /* 10 Mbps PCnet-ISA, Racal InterLink NI6510 EtherBlaster compatible. */
5056 /* NI6510 drivers (at least Racal's and Linux) require the OUI to be InterLan's (Racal-Datacom).
5057 * Refuse loading if OUI doesn't match, because otherwise drivers won't load in the guest.
5058 */
5059 if (memcmp(&pThis->MacConfigured, "\x02\x07\x01", 3))
5060 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5061 N_("Configuration error: MAC address OUI for EtherBlaster must be 02 07 01"),
5062 szChipType);
5063 }
5064 else
5065 {
5066 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5067 N_("Configuration error: The \"ChipType\" value \"%s\" is unsupported"),
5068 szChipType);
5069 }
5070
5071
5072 /*
5073 * Process the old model configuration. If present, it must take precedence for saved state compatibility.
5074 */
5075 bool fAm79C973;
5076 rc = CFGMR3QueryBoolDef(pCfg, "Am79C973", &fAm79C973, false);
5077 if (RT_FAILURE(rc))
5078 return PDMDEV_SET_ERROR(pDevIns, rc,
5079 N_("Configuration error: Failed to get the \"Am79C973\" value"));
5080 if (fAm79C973)
5081 pThis->uDevType = DEV_AM79C973;
5082
5083 /*
5084 * Process ISA configuration options. The defaults are chosen to be NE2100/Am2100 compatible.
5085 */
5086 rc = CFGMR3QueryPortDef(pCfg, "Port", &pThis->IOPortBase, 0x300);
5087 if (RT_FAILURE(rc))
5088 return PDMDEV_SET_ERROR(pDevIns, rc,
5089 N_("Configuration error: Failed to get the \"Port\" value"));
5090
5091 rc = CFGMR3QueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, 3);
5092 if (RT_FAILURE(rc))
5093 return PDMDEV_SET_ERROR(pDevIns, rc,
5094 N_("Configuration error: Failed to get the \"IRQ\" value"));
5095
5096 rc = CFGMR3QueryU32Def(pCfg, "LineSpeed", &pThis->u32LinkSpeed, 1000000); /* 1GBit/s (in kbps units)*/
5097 if (RT_FAILURE(rc))
5098 return PDMDEV_SET_ERROR(pDevIns, rc,
5099 N_("Configuration error: Failed to get the \"LineSpeed\" value"));
5100
5101#ifdef PCNET_GC_ENABLED
5102 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
5103 if (RT_FAILURE(rc))
5104 return PDMDEV_SET_ERROR(pDevIns, rc,
5105 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
5106
5107 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
5108 if (RT_FAILURE(rc))
5109 return PDMDEV_SET_ERROR(pDevIns, rc,
5110 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
5111
5112#else /* !PCNET_GC_ENABLED */
5113 pThis->fGCEnabled = false;
5114 pThis->fR0Enabled = false;
5115#endif /* !PCNET_GC_ENABLED */
5116
5117 rc = CFGMR3QueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
5118 if (RT_FAILURE(rc))
5119 return PDMDEV_SET_ERROR(pDevIns, rc,
5120 N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
5121 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
5122 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
5123 {
5124 LogRel(("PCnet#%d WARNING! Link up delay is set to %u seconds!\n",
5125 iInstance, pThis->cMsLinkUpDelay / 1000));
5126 }
5127 Log(("#%d Link up delay is set to %u seconds\n",
5128 iInstance, pThis->cMsLinkUpDelay / 1000));
5129
5130
5131 /*
5132 * Initialize data (most of it anyway).
5133 */
5134 pThis->pDevInsR3 = pDevIns;
5135 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5136 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5137 pThis->Led.u32Magic = PDMLED_MAGIC;
5138 /* IBase */
5139 pThis->IBase.pfnQueryInterface = pcnetQueryInterface;
5140 /* INeworkPort */
5141 pThis->INetworkDown.pfnWaitReceiveAvail = pcnetNetworkDown_WaitReceiveAvail;
5142 pThis->INetworkDown.pfnReceive = pcnetNetworkDown_Receive;
5143 pThis->INetworkDown.pfnXmitPending = pcnetNetworkDown_XmitPending;
5144 /* INetworkConfig */
5145 pThis->INetworkConfig.pfnGetMac = pcnetGetMac;
5146 pThis->INetworkConfig.pfnGetLinkState = pcnetGetLinkState;
5147 pThis->INetworkConfig.pfnSetLinkState = pcnetSetLinkState;
5148 /* ILeds */
5149 pThis->ILeds.pfnQueryStatusLed = pcnetQueryStatusLed;
5150
5151 /* PCI Device */
5152 PCIDevSetVendorId(&pThis->PciDev, 0x1022);
5153 PCIDevSetDeviceId(&pThis->PciDev, 0x2000);
5154 pThis->PciDev.abConfig[0x04] = 0x07; /* command */
5155 pThis->PciDev.abConfig[0x05] = 0x00;
5156 pThis->PciDev.abConfig[0x06] = 0x80; /* status */
5157 pThis->PciDev.abConfig[0x07] = 0x02;
5158 pThis->PciDev.abConfig[0x08] = pThis->uDevType == DEV_AM79C973 ? 0x40 : 0x10; /* revision */
5159 pThis->PciDev.abConfig[0x09] = 0x00;
5160 pThis->PciDev.abConfig[0x0a] = 0x00; /* ethernet network controller */
5161 pThis->PciDev.abConfig[0x0b] = 0x02;
5162 pThis->PciDev.abConfig[0x0e] = 0x00; /* header_type */
5163
5164 pThis->PciDev.abConfig[0x10] = 0x01; /* IO Base */
5165 pThis->PciDev.abConfig[0x11] = 0x00;
5166 pThis->PciDev.abConfig[0x12] = 0x00;
5167 pThis->PciDev.abConfig[0x13] = 0x00;
5168 pThis->PciDev.abConfig[0x14] = 0x00; /* MMIO Base */
5169 pThis->PciDev.abConfig[0x15] = 0x00;
5170 pThis->PciDev.abConfig[0x16] = 0x00;
5171 pThis->PciDev.abConfig[0x17] = 0x00;
5172
5173 /* subsystem and subvendor IDs */
5174 pThis->PciDev.abConfig[0x2c] = 0x22; /* subsystem vendor id */
5175 pThis->PciDev.abConfig[0x2d] = 0x10;
5176 pThis->PciDev.abConfig[0x2e] = 0x00; /* subsystem id */
5177 pThis->PciDev.abConfig[0x2f] = 0x20;
5178 pThis->PciDev.abConfig[0x3d] = 1; /* interrupt pin 0 */
5179 pThis->PciDev.abConfig[0x3e] = 0x06;
5180 pThis->PciDev.abConfig[0x3f] = 0xff;
5181
5182 /*
5183 * We use our own critical section (historical reasons).
5184 */
5185 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PCnet#%u", iInstance);
5186 AssertRCReturn(rc, rc);
5187 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5188 AssertRCReturn(rc, rc);
5189
5190 rc = RTSemEventCreate(&pThis->hEventOutOfRxSpace);
5191 AssertRCReturn(rc, rc);
5192
5193 /*
5194 * Register the PCI device, its I/O regions, the timer and the saved state item.
5195 */
5196 if (PCNET_IS_PCI(pThis))
5197 {
5198 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
5199 if (RT_FAILURE(rc))
5200 return rc;
5201 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, PCNET_IOPORT_SIZE, PCI_ADDRESS_SPACE_IO, pcnetIOPortMap);
5202 if (RT_FAILURE(rc))
5203 return rc;
5204 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, PCNET_PNPMMIO_SIZE, PCI_ADDRESS_SPACE_MEM, pcnetMMIOMap);
5205 if (RT_FAILURE(rc))
5206 return rc;
5207 }
5208
5209 /*
5210 * Register ISA I/O ranges for PCnet-ISA.
5211 */
5212 if (PCNET_IS_ISA(pThis))
5213 {
5214 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 0x10, 0, pcnetIOPortAPromWrite,
5215 pcnetIOPortAPromRead, NULL, NULL, "PCnet APROM");
5216 if (RT_FAILURE(rc))
5217 return rc;
5218 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase + 0x10, 0x10, 0, pcnetIOPortWrite,
5219 pcnetIOPortRead, NULL, NULL, "PCnet");
5220 if (RT_FAILURE(rc))
5221 return rc;
5222
5223 if (pThis->fGCEnabled)
5224 {
5225 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 0x10, 0, "pcnetIOPortAPromWrite",
5226 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
5227 if (RT_FAILURE(rc))
5228 return rc;
5229 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase + 0x10, 0x10, 0, "pcnetIOPortWrite",
5230 "pcnetIOPortRead", NULL, NULL, "PCnet");
5231 if (RT_FAILURE(rc))
5232 return rc;
5233 }
5234 if (pThis->fR0Enabled)
5235 {
5236 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 0x10, 0, "pcnetIOPortAPromWrite",
5237 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
5238 if (RT_FAILURE(rc))
5239 return rc;
5240 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase + 0x10, 0x10, 0, "pcnetIOPortWrite",
5241 "pcnetIOPortRead", NULL, NULL, "PCnet");
5242 if (RT_FAILURE(rc))
5243 return rc;
5244 }
5245
5246 }
5247
5248
5249#ifdef PCNET_NO_POLLING
5250 /*
5251 * Resolve the R0 and RC handlers.
5252 */
5253 rc = PDMR3LdrGetSymbolR0Lazy(PDMDevHlpGetVM(pDevIns), NULL, NULL, "EMInterpretInstruction", &pThis->pfnEMInterpretInstructionR0);
5254 if (RT_SUCCESS(rc))
5255 rc = PDMR3LdrGetSymbolRCLazy(PDMDevHlpGetVM(pDevIns), NULL, NULL, "EMInterpretInstruction", &pThis->pfnEMInterpretInstructionRC);
5256 AssertLogRelMsgRCReturn(rc, ("PDMR3LdrGetSymbolRCLazy(EMInterpretInstruction) -> %Rrc\n", rc), rc);
5257
5258 rc = PGMR3HandlerPhysicalTypeRegister(PDMDevHlpGetVM(pDevIns), PGMPHYSHANDLERKIND_WRITE,
5259 pcnetHandleRingWrite,
5260 g_DevicePCNet.szR0Mod, NULL, "pcnetHandleRingWritePf",
5261 g_DevicePCNet.szRCMod, NULL, "pcnetHandleRingWritePf",
5262 "PCnet ring write access handler",
5263 &pThis->hNoPollingHandlerType);
5264 AssertRCReturn(rc, rc);
5265
5266#else
5267 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimer, pThis,
5268 TMTIMER_FLAGS_NO_CRIT_SECT, "PCnet Poll Timer", &pThis->pTimerPollR3);
5269 if (RT_FAILURE(rc))
5270 return rc;
5271 pThis->pTimerPollR0 = TMTimerR0Ptr(pThis->pTimerPollR3);
5272 pThis->pTimerPollRC = TMTimerRCPtr(pThis->pTimerPollR3);
5273 TMR3TimerSetCritSect(pThis->pTimerPollR3, &pThis->CritSect);
5274#endif
5275 if (pThis->uDevType == DEV_AM79C973)
5276 {
5277 /* Software Interrupt timer */
5278 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimerSoftInt, pThis, /** @todo r=bird: the locking here looks bogus now with SMP... */
5279 TMTIMER_FLAGS_NO_CRIT_SECT, "PCnet SoftInt Timer", &pThis->pTimerSoftIntR3);
5280 if (RT_FAILURE(rc))
5281 return rc;
5282 pThis->pTimerSoftIntR0 = TMTimerR0Ptr(pThis->pTimerSoftIntR3);
5283 pThis->pTimerSoftIntRC = TMTimerRCPtr(pThis->pTimerSoftIntR3);
5284 TMR3TimerSetCritSect(pThis->pTimerSoftIntR3, &pThis->CritSect);
5285 }
5286 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimerRestore, pThis,
5287 TMTIMER_FLAGS_NO_CRIT_SECT, "PCnet Restore Timer", &pThis->pTimerRestore);
5288 if (RT_FAILURE(rc))
5289 return rc;
5290
5291 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCNET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
5292 NULL, pcnetLiveExec, NULL,
5293 pcnetSavePrep, pcnetSaveExec, NULL,
5294 pcnetLoadPrep, pcnetLoadExec, pcnetLoadDone);
5295 if (RT_FAILURE(rc))
5296 return rc;
5297
5298 /*
5299 * Create the transmit queue.
5300 */
5301 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
5302 pcnetXmitQueueConsumer, true, "PCnet-Xmit", &pThis->pXmitQueueR3);
5303 if (RT_FAILURE(rc))
5304 return rc;
5305 pThis->pXmitQueueR0 = PDMQueueR0Ptr(pThis->pXmitQueueR3);
5306 pThis->pXmitQueueRC = PDMQueueRCPtr(pThis->pXmitQueueR3);
5307
5308 /*
5309 * Create the RX notifier signaller.
5310 */
5311 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
5312 pcnetCanRxQueueConsumer, true, "PCnet-Rcv", &pThis->pCanRxQueueR3);
5313 if (RT_FAILURE(rc))
5314 return rc;
5315 pThis->pCanRxQueueR0 = PDMQueueR0Ptr(pThis->pCanRxQueueR3);
5316 pThis->pCanRxQueueRC = PDMQueueRCPtr(pThis->pCanRxQueueR3);
5317
5318 /*
5319 * Register the info item.
5320 */
5321 RTStrPrintf(szTmp, sizeof(szTmp), "pcnet%d", pDevIns->iInstance);
5322 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "PCNET info.", pcnetInfo);
5323
5324 /*
5325 * Attach status driver (optional).
5326 */
5327 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
5328 if (RT_SUCCESS(rc))
5329 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5330 else if ( rc != VERR_PDM_NO_ATTACHED_DRIVER
5331 && rc != VERR_PDM_CFG_MISSING_DRIVER_NAME)
5332 {
5333 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5334 return rc;
5335 }
5336
5337 /*
5338 * Attach driver.
5339 */
5340 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Network Port");
5341 if (RT_SUCCESS(rc))
5342 {
5343 if (rc == VINF_NAT_DNS)
5344 {
5345#ifdef RT_OS_LINUX
5346 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
5347 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"));
5348#else
5349 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
5350 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"));
5351#endif
5352 }
5353 pThis->pDrvR3 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
5354 AssertMsgReturn(pThis->pDrvR3, ("Failed to obtain the PDMINETWORKUP interface!\n"),
5355 VERR_PDM_MISSING_INTERFACE_BELOW);
5356 pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
5357 pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
5358 }
5359 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
5360 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
5361 {
5362 /* No error! */
5363 Log(("No attached driver!\n"));
5364 }
5365 else
5366 return rc;
5367
5368 /*
5369 * Reset the device state. (Do after attaching.)
5370 */
5371 pcnetR3HardReset(pThis);
5372
5373 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Public/Net/PCnet%u/BytesReceived", iInstance);
5374 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Public/Net/PCnet%u/BytesTransmitted", iInstance);
5375
5376 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/PCnet%d/ReceiveBytes", iInstance);
5377 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/PCnet%d/TransmitBytes", iInstance);
5378
5379#ifdef VBOX_WITH_STATISTICS
5380 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in RZ", "/Devices/PCnet%d/MMIO/ReadRZ", iInstance);
5381 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in R3", "/Devices/PCnet%d/MMIO/ReadR3", iInstance);
5382 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in RZ", "/Devices/PCnet%d/MMIO/WriteRZ", iInstance);
5383 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in R3", "/Devices/PCnet%d/MMIO/WriteR3", iInstance);
5384 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatAPROMRead, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM reads", "/Devices/PCnet%d/IO/APROMRead", iInstance);
5385 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatAPROMWrite, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM writes", "/Devices/PCnet%d/IO/APROMWrite", iInstance);
5386 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ", "/Devices/PCnet%d/IO/ReadRZ", iInstance);
5387 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3", "/Devices/PCnet%d/IO/ReadR3", iInstance);
5388 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ", "/Devices/PCnet%d/IO/WriteRZ", iInstance);
5389 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3", "/Devices/PCnet%d/IO/WriteR3", iInstance);
5390 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling Timer", "/Devices/PCnet%d/Timer", iInstance);
5391 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/PCnet%d/Receive", iInstance);
5392 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/PCnet%d/RxOverflow", iInstance);
5393 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Nr of RX overflow wakeups", "/Devices/PCnet%d/RxOverflowWakeup", iInstance);
5394 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitCase1, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Single descriptor transmit", "/Devices/PCnet%d/Transmit/Case1", iInstance);
5395 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitCase2, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Multi descriptor transmit", "/Devices/PCnet%d/Transmit/Case2", iInstance);
5396 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ", "/Devices/PCnet%d/Transmit/TotalRZ", iInstance);
5397 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3", "/Devices/PCnet%d/Transmit/TotalR3", iInstance);
5398 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in RZ","/Devices/PCnet%d/Transmit/SendRZ", iInstance);
5399 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in R3","/Devices/PCnet%d/Transmit/SendR3", iInstance);
5400 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);
5401 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);
5402 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTdtePollRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in RZ", "/Devices/PCnet%d/TdtePollRZ", iInstance);
5403 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTdtePollR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in R3", "/Devices/PCnet%d/TdtePollR3", iInstance);
5404 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRdtePollRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in RZ", "/Devices/PCnet%d/RdtePollRZ", iInstance);
5405 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRdtePollR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in R3", "/Devices/PCnet%d/RdtePollR3", iInstance);
5406
5407 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTmdStoreRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in RZ", "/Devices/PCnet%d/TmdStoreRZ", iInstance);
5408 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTmdStoreR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in R3", "/Devices/PCnet%d/TmdStoreR3", iInstance);
5409
5410 unsigned i;
5411 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitFlush) - 1; i++)
5412 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitFlushIrq/%d", iInstance, i + 1);
5413 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitFlushIrq/%d+", iInstance, i + 1);
5414
5415 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitChainCounts) - 1; i++)
5416 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitChainCounts/%d", iInstance, i + 1);
5417 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitChainCounts/%d+", iInstance, i + 1);
5418
5419 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatXmitSkipCurrent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/Xmit/Skipped", iInstance);
5420
5421 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks", "/Devices/PCnet%d/UpdateIRQ", iInstance);
5422 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatPollTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling poll timer", "/Devices/PCnet%d/PollTimer", iInstance);
5423 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMIIReads, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of MII reads", "/Devices/PCnet%d/MIIReads", iInstance);
5424# ifdef PCNET_NO_POLLING
5425 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRCVRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of receive ring writes", "/Devices/PCnet%d/Ring/RCVWrites", iInstance);
5426 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTXRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of transmit ring writes", "/Devices/PCnet%d/Ring/TXWrites", iInstance);
5427 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCnet%d/Ring/R3/Writes", iInstance);
5428 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCnet%d/Ring/R0/Writes", iInstance);
5429 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteRC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCnet%d/Ring/RC/Writes", iInstance);
5430 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteFailedR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCnet%d/Ring/R3/Failed", iInstance);
5431 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteFailedR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCnet%d/Ring/R0/Failed", iInstance);
5432 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteFailedRC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCnet%d/Ring/RC/Failed", iInstance);
5433 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteOutsideR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring","/Devices/PCnet%d/Ring/R3/Outside", iInstance);
5434 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteOutsideR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring","/Devices/PCnet%d/Ring/R0/Outside", iInstance);
5435 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteOutsideRC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring","/Devices/PCnet%d/Ring/RC/Outside", iInstance);
5436# endif /* PCNET_NO_POLLING */
5437#endif /* VBOX_WITH_STATISTICS */
5438
5439 return VINF_SUCCESS;
5440}
5441
5442
5443/**
5444 * The device registration structure.
5445 */
5446const PDMDEVREG g_DevicePCNet =
5447{
5448 /* u32Version */
5449 PDM_DEVREG_VERSION,
5450 /* szName */
5451 "pcnet",
5452 /* szRCMod */
5453#ifdef PCNET_GC_ENABLED
5454 "VBoxDDRC.rc",
5455 "VBoxDDR0.r0",
5456#else
5457 "",
5458 "",
5459#endif
5460 /* pszDescription */
5461 "AMD PCnet Ethernet controller.\n",
5462 /* fFlags */
5463#ifdef PCNET_GC_ENABLED
5464 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
5465#else
5466 PDM_DEVREG_FLAGS_DEFAULT_BITS,
5467#endif
5468 /* fClass */
5469 PDM_DEVREG_CLASS_NETWORK,
5470 /* cMaxInstances */
5471 ~0U,
5472 /* cbInstance */
5473 sizeof(PCNETSTATE),
5474 /* pfnConstruct */
5475 pcnetConstruct,
5476 /* pfnDestruct */
5477 pcnetDestruct,
5478 /* pfnRelocate */
5479 pcnetRelocate,
5480 /* pfnMemSetup */
5481 NULL,
5482 /* pfnPowerOn */
5483 NULL,
5484 /* pfnReset */
5485 pcnetReset,
5486 /* pfnSuspend */
5487 pcnetSuspend,
5488 /* pfnResume */
5489 NULL,
5490 /* pfnAttach */
5491 pcnetAttach,
5492 /* pfnDetach */
5493 pcnetDetach,
5494 /* pfnQueryInterface. */
5495 NULL,
5496 /* pfnInitComplete. */
5497 NULL,
5498 /* pfnPowerOff. */
5499 pcnetPowerOff,
5500 /* pfnSoftReset */
5501 NULL,
5502 /* u32VersionEnd */
5503 PDM_DEVREG_VERSION
5504};
5505
5506#endif /* IN_RING3 */
5507#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5508
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