VirtualBox

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

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

DevPcNet: Clear the 'zeros' RMD field when writing (see bugref:1613).

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