VirtualBox

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

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

PCnet: Forgot to allow some CFGM keys.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette