VirtualBox

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

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

PCnet: Really ignore writes to certain registers when STOP or SPND not set. Ignore high 8 bits of 32-bit initialization address base on ISA chips.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 210.3 KB
Line 
1/* $Id: DevPCNet.cpp 76843 2019-01-16 18:13:57Z 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 2: /* IADRH */
2932 if (PCNET_IS_ISA(pThis))
2933 val &= 0x00ff; /* Upper 8 bits ignored on ISA chips. */
2934 RT_FALL_THRU();
2935 case 1: /* IADRL */
2936 case 8: /* LADRF 0..15 */
2937 case 9: /* LADRF 16..31 */
2938 case 10: /* LADRF 32..47 */
2939 case 11: /* LADRF 48..63 */
2940 case 12: /* PADR 0..15 */
2941 case 13: /* PADR 16..31 */
2942 case 14: /* PADR 32..47 */
2943 case 18: /* CRBAL */
2944 case 19: /* CRBAU */
2945 case 20: /* CXBAL */
2946 case 21: /* CXBAU */
2947 case 22: /* NRBAL */
2948 case 23: /* NRBAU */
2949 case 26: /* NRDAL */
2950 case 27: /* NRDAU */
2951 case 28: /* CRDAL */
2952 case 29: /* CRDAU */
2953 case 32: /* NXDAL */
2954 case 33: /* NXDAU */
2955 case 34: /* CXDAL */
2956 case 35: /* CXDAU */
2957 case 36: /* NNRDL */
2958 case 37: /* NNRDU */
2959 case 38: /* NNXDL */
2960 case 39: /* NNXDU */
2961 case 40: /* CRBCL */
2962 case 41: /* CRBCU */
2963 case 42: /* CXBCL */
2964 case 43: /* CXBCU */
2965 case 44: /* NRBCL */
2966 case 45: /* NRBCU */
2967 case 46: /* POLL */
2968 case 47: /* POLLINT */
2969 case 72: /* RCVRC */
2970 case 74: /* XMTRC */
2971 case 112: /* MISSC */
2972 if (CSR_STOP(pThis) || CSR_SPND(pThis))
2973 break;
2974 else
2975 {
2976 Log(("#%d: WRITE CSR%d, %#06x, ignoring!!\n", PCNET_INST_NR, u32RAP, val));
2977 return rc;
2978 }
2979 case 3: /* Interrupt Mask and Deferral Control */
2980 break;
2981 case 4: /* Test and Features Control */
2982 pThis->aCSR[4] &= ~(val & 0x026a);
2983 val &= ~0x026a;
2984 val |= pThis->aCSR[4] & 0x026a;
2985 break;
2986 case 5: /* Extended Control and Interrupt 1 */
2987 pThis->aCSR[5] &= ~(val & 0x0a90);
2988 val &= ~0x0a90;
2989 val |= pThis->aCSR[5] & 0x0a90;
2990 break;
2991 case 7: /* Extended Control and Interrupt 2 */
2992 {
2993 uint16_t csr7 = pThis->aCSR[7];
2994 csr7 &= ~0x0400 ;
2995 csr7 &= ~(val & 0x0800);
2996 csr7 |= (val & 0x0400);
2997 pThis->aCSR[7] = csr7;
2998 return rc;
2999 }
3000 case 15: /* Mode */
3001 if ((pThis->aCSR[15] & 0x8000) != (uint16_t)(val & 0x8000) && pThis->pDrvR3)
3002 {
3003 Log(("#%d: promiscuous mode changed to %d\n", PCNET_INST_NR, !!(val & 0x8000)));
3004#ifndef IN_RING3
3005 return VINF_IOM_R3_IOPORT_WRITE;
3006#else
3007 /* check for promiscuous mode change */
3008 if (pThis->pDrvR3)
3009 pThis->pDrvR3->pfnSetPromiscuousMode(pThis->pDrvR3, !!(val & 0x8000));
3010#endif
3011 }
3012 break;
3013 case 16: /* IADRL */
3014 return pcnetCSRWriteU16(pThis, 1, val);
3015 case 17: /* IADRH */
3016 return pcnetCSRWriteU16(pThis, 2, val);
3017
3018 /*
3019 * 24 and 25 are the Base Address of Receive Descriptor.
3020 * We combine and mirror these in GCRDRA.
3021 */
3022 case 24: /* BADRL */
3023 case 25: /* BADRU */
3024 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
3025 {
3026 Log(("#%d: WRITE CSR%d, %#06x, ignoring!!\n", PCNET_INST_NR, u32RAP, val));
3027 return rc;
3028 }
3029 if (u32RAP == 24)
3030 pThis->GCRDRA = (pThis->GCRDRA & 0xffff0000) | (val & 0x0000ffff);
3031 else
3032 pThis->GCRDRA = (pThis->GCRDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
3033 Log(("#%d: WRITE CSR%d, %#06x => GCRDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCRDRA));
3034 break;
3035
3036 /*
3037 * 30 & 31 are the Base Address of Transmit Descriptor.
3038 * We combine and mirrorthese in GCTDRA.
3039 */
3040 case 30: /* BADXL */
3041 case 31: /* BADXU */
3042 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
3043 {
3044 Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
3045 return rc;
3046 }
3047 if (u32RAP == 30)
3048 pThis->GCTDRA = (pThis->GCTDRA & 0xffff0000) | (val & 0x0000ffff);
3049 else
3050 pThis->GCTDRA = (pThis->GCTDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
3051 Log(("#%d: WRITE CSR%d, %#06x => GCTDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCTDRA));
3052 break;
3053
3054 case 58: /* Software Style */
3055 rc = pcnetBCRWriteU16(pThis, BCR_SWS, val);
3056 break;
3057
3058 /*
3059 * Registers 76 and 78 aren't stored correctly (see todos), but I'm don't dare
3060 * try fix that right now. So, as a quick hack for 'alt init' I'll just correct them here.
3061 */
3062 case 76: /* RCVRL */ /** @todo call pcnetUpdateRingHandlers */
3063 /** @todo receive ring length is stored in two's complement! */
3064 case 78: /* XMTRL */ /** @todo call pcnetUpdateRingHandlers */
3065 /** @todo transmit ring length is stored in two's complement! */
3066 if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
3067 {
3068 Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
3069 return rc;
3070 }
3071 Log(("#%d: WRITE CSR%d, %#06x (hacked %#06x) (alt init)\n", PCNET_INST_NR,
3072 u32RAP, val, 1 + ~(uint16_t)val));
3073 val = 1 + ~(uint16_t)val;
3074
3075 /*
3076 * HACK ALERT! Set the counter registers too.
3077 */
3078 pThis->aCSR[u32RAP - 4] = val;
3079 break;
3080
3081 default:
3082 return rc;
3083 }
3084 pThis->aCSR[u32RAP] = val;
3085 return rc;
3086}
3087
3088/**
3089 * Encode a 32-bit link speed into a custom 16-bit floating-point value
3090 */
3091static uint32_t pcnetLinkSpd(uint32_t speed)
3092{
3093 unsigned exp = 0;
3094
3095 while (speed & 0xFFFFE000)
3096 {
3097 speed /= 10;
3098 ++exp;
3099 }
3100 return (exp << 13) | speed;
3101}
3102
3103static uint32_t pcnetCSRReadU16(PPCNETSTATE pThis, uint32_t u32RAP)
3104{
3105 uint32_t val;
3106 switch (u32RAP)
3107 {
3108 case 0:
3109 pcnetUpdateIrq(pThis);
3110 val = pThis->aCSR[0];
3111 val |= (val & 0x7800) ? 0x8000 : 0;
3112 pThis->u16CSR0LastSeenByGuest = val;
3113 break;
3114 case 16:
3115 return pcnetCSRReadU16(pThis, 1);
3116 case 17:
3117 return pcnetCSRReadU16(pThis, 2);
3118 case 58:
3119 return pcnetBCRReadU16(pThis, BCR_SWS);
3120 case 68: /* Custom register to pass link speed to driver */
3121 return pcnetLinkSpd(pThis->u32LinkSpeed);
3122 case 88:
3123 val = pThis->aCSR[89];
3124 val <<= 16;
3125 val |= pThis->aCSR[88];
3126 break;
3127 default:
3128 val = pThis->aCSR[u32RAP];
3129 }
3130#ifdef PCNET_DEBUG_CSR
3131 Log(("#%d pcnetCSRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3132#endif
3133 return val;
3134}
3135
3136static int pcnetBCRWriteU16(PPCNETSTATE pThis, uint32_t u32RAP, uint32_t val)
3137{
3138 int rc = VINF_SUCCESS;
3139 u32RAP &= 0x7f;
3140#ifdef PCNET_DEBUG_BCR
3141 Log2(("#%d pcnetBCRWriteU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3142#endif
3143 switch (u32RAP)
3144 {
3145 case BCR_SWS:
3146 if (!(CSR_STOP(pThis) || CSR_SPND(pThis)))
3147 return rc;
3148 val &= ~0x0300;
3149 switch (val & 0x00ff)
3150 {
3151 default:
3152 Log(("#%d Bad SWSTYLE=%#04x\n", PCNET_INST_NR, val & 0xff));
3153 RT_FALL_THRU();
3154 case 0:
3155 val |= 0x0200; /* 16 bit */
3156 pThis->iLog2DescSize = 3;
3157 pThis->GCUpperPhys = (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
3158 break;
3159 case 1:
3160 val |= 0x0100; /* 32 bit */
3161 pThis->iLog2DescSize = 4;
3162 pThis->GCUpperPhys = 0;
3163 break;
3164 case 2:
3165 case 3:
3166 val |= 0x0300; /* 32 bit */
3167 pThis->iLog2DescSize = 4;
3168 pThis->GCUpperPhys = 0;
3169 break;
3170 }
3171 Log(("#%d BCR_SWS=%#06x\n", PCNET_INST_NR, val));
3172 pThis->aCSR[58] = val;
3173 RT_FALL_THRU();
3174 case BCR_LNKST:
3175 case BCR_LED1:
3176 case BCR_LED2:
3177 case BCR_LED3:
3178 case BCR_MC:
3179 case BCR_FDC:
3180 case BCR_BSBC:
3181 case BCR_EECAS:
3182 case BCR_PLAT:
3183 case BCR_MIICAS:
3184 case BCR_MIIADDR:
3185 pThis->aBCR[u32RAP] = val;
3186 break;
3187
3188 case BCR_STVAL:
3189 val &= 0xffff;
3190 pThis->aBCR[BCR_STVAL] = val;
3191 if (pThis->uDevType == DEV_AM79C973)
3192 TMTimerSetNano(pThis->CTX_SUFF(pTimerSoftInt), 12800U * val);
3193 break;
3194
3195 case BCR_MIIMDR:
3196 pThis->aMII[pThis->aBCR[BCR_MIIADDR] & 0x1f] = val;
3197#ifdef PCNET_DEBUG_MII
3198 Log(("#%d pcnet: mii write %d <- %#x\n", PCNET_INST_NR, pThis->aBCR[BCR_MIIADDR] & 0x1f, val));
3199#endif
3200 break;
3201
3202 default:
3203 break;
3204 }
3205 return rc;
3206}
3207
3208static uint32_t pcnetMIIReadU16(PPCNETSTATE pThis, uint32_t miiaddr)
3209{
3210 uint32_t val;
3211 bool autoneg, duplex, fast;
3212 STAM_COUNTER_INC(&pThis->StatMIIReads);
3213
3214 autoneg = (pThis->aBCR[BCR_MIICAS] & 0x20) != 0;
3215 duplex = (pThis->aBCR[BCR_MIICAS] & 0x10) != 0;
3216 fast = (pThis->aBCR[BCR_MIICAS] & 0x08) != 0;
3217
3218 switch (miiaddr)
3219 {
3220 case 0:
3221 /* MII basic mode control register. */
3222 val = 0;
3223 if (autoneg)
3224 val |= 0x1000; /* Enable auto negotiation. */
3225 if (fast)
3226 val |= 0x2000; /* 100 Mbps */
3227 if (duplex) /* Full duplex forced */
3228 val |= 0x0100; /* Full duplex */
3229 break;
3230
3231 case 1:
3232 /* MII basic mode status register. */
3233 val = 0x7800 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
3234 | 0x0040 /* Mgmt frame preamble not required. */
3235 | 0x0020 /* Auto-negotiation complete. */
3236 | 0x0008 /* Able to do auto-negotiation. */
3237 | 0x0004 /* Link up. */
3238 | 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
3239 if (!pThis->fLinkUp || pThis->fLinkTempDown) {
3240 val &= ~(0x0020 | 0x0004);
3241 pThis->cLinkDownReported++;
3242 }
3243 if (!autoneg) {
3244 /* Auto-negotiation disabled. */
3245 val &= ~(0x0020 | 0x0008);
3246 if (duplex)
3247 /* Full duplex forced. */
3248 val &= ~0x2800;
3249 else
3250 /* Half duplex forced. */
3251 val &= ~0x5000;
3252
3253 if (fast)
3254 /* 100 Mbps forced */
3255 val &= ~0x1800;
3256 else
3257 /* 10 Mbps forced */
3258 val &= ~0x6000;
3259 }
3260 break;
3261
3262 case 2:
3263 /* PHY identifier 1. */
3264 val = 0x22; /* Am79C874 PHY */
3265 break;
3266
3267 case 3:
3268 /* PHY identifier 2. */
3269 val = 0x561b; /* Am79C874 PHY */
3270 break;
3271
3272 case 4:
3273 /* Advertisement control register. */
3274 val = 0x01e0 /* Try 100mbps FD/HD and 10mbps FD/HD. */
3275#if 0
3276 // Advertising flow control is a) not the default, and b) confuses
3277 // the link speed detection routine in Windows PCnet driver
3278 | 0x0400 /* Try flow control. */
3279#endif
3280 | 0x0001; /* CSMA selector. */
3281 break;
3282
3283 case 5:
3284 /* Link partner ability register. */
3285 if (pThis->fLinkUp && !pThis->fLinkTempDown)
3286 val = 0x8000 /* Next page bit. */
3287 | 0x4000 /* Link partner acked us. */
3288 | 0x0400 /* Can do flow control. */
3289 | 0x01e0 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
3290 | 0x0001; /* Use CSMA selector. */
3291 else
3292 {
3293 val = 0;
3294 pThis->cLinkDownReported++;
3295 }
3296 break;
3297
3298 case 6:
3299 /* Auto negotiation expansion register. */
3300 if (pThis->fLinkUp && !pThis->fLinkTempDown)
3301 val = 0x0008 /* Link partner supports npage. */
3302 | 0x0004 /* Enable npage words. */
3303 | 0x0001; /* Can do N-way auto-negotiation. */
3304 else
3305 {
3306 val = 0;
3307 pThis->cLinkDownReported++;
3308 }
3309 break;
3310
3311 default:
3312 val = 0;
3313 break;
3314 }
3315
3316#ifdef PCNET_DEBUG_MII
3317 Log(("#%d pcnet: mii read %d -> %#x\n", PCNET_INST_NR, miiaddr, val));
3318#endif
3319 return val;
3320}
3321
3322static uint32_t pcnetBCRReadU16(PPCNETSTATE pThis, uint32_t u32RAP)
3323{
3324 uint32_t val;
3325 u32RAP &= 0x7f;
3326 switch (u32RAP)
3327 {
3328 case BCR_LNKST:
3329 case BCR_LED1:
3330 case BCR_LED2:
3331 case BCR_LED3:
3332 val = pThis->aBCR[u32RAP] & ~0x8000;
3333 /* Clear LNKSTE if we're not connected or if we've just loaded a VM state. */
3334 if (!pThis->pDrvR3 || pThis->fLinkTempDown || !pThis->fLinkUp)
3335 {
3336 if (u32RAP == 4)
3337 pThis->cLinkDownReported++;
3338 val &= ~0x40;
3339 }
3340 val |= (val & 0x017f & pThis->u32Lnkst) ? 0x8000 : 0;
3341 break;
3342
3343 case BCR_MIIMDR:
3344 if ((pThis->uDevType == DEV_AM79C973) && (pThis->aBCR[BCR_MIIADDR] >> 5 & 0x1f) == 0)
3345 {
3346 uint32_t miiaddr = pThis->aBCR[BCR_MIIADDR] & 0x1f;
3347 val = pcnetMIIReadU16(pThis, miiaddr);
3348 }
3349 else
3350 val = 0xffff;
3351 break;
3352
3353 default:
3354 val = u32RAP < BCR_MAX_RAP ? pThis->aBCR[u32RAP] : 0;
3355 break;
3356 }
3357#ifdef PCNET_DEBUG_BCR
3358 Log2(("#%d pcnetBCRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
3359#endif
3360 return val;
3361}
3362
3363#ifdef IN_RING3 /* move down */
3364static void pcnetR3HardReset(PPCNETSTATE pThis)
3365{
3366 int i;
3367 uint16_t checksum;
3368
3369 /* Initialize the PROM */
3370 Assert(sizeof(pThis->MacConfigured) == 6);
3371 memcpy(pThis->aPROM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
3372 pThis->aPROM[ 8] = 0x00;
3373 pThis->aPROM[12] = pThis->aPROM[13] = 0x00;
3374 if (pThis->uDevType == DEV_AM79C960_EB)
3375 {
3376 pThis->aPROM[14] = 0x52;
3377 pThis->aPROM[15] = 0x44; /* NI6510 EtherBlaster 'RD' signature. */
3378 }
3379 else
3380 pThis->aPROM[14] = pThis->aPROM[15] = 0x57; /* NE2100 'WW' signature. */
3381
3382 /* 0x00/0xFF=ISA, 0x01=PnP, 0x10=VLB, 0x11=PCI */
3383 if (PCNET_IS_PCI(pThis))
3384 pThis->aPROM[ 9] = 0x11;
3385 else
3386 pThis->aPROM[ 9] = 0x00;
3387
3388 for (i = 0, checksum = 0; i < 16; i++)
3389 checksum += pThis->aPROM[i];
3390 *(uint16_t *)&pThis->aPROM[12] = RT_H2LE_U16(checksum);
3391
3392 pThis->aBCR[BCR_MSRDA] = 0x0005;
3393 pThis->aBCR[BCR_MSWRA] = 0x0005;
3394 pThis->aBCR[BCR_MC ] = 0x0002;
3395 pThis->aBCR[BCR_LNKST] = 0x00c0;
3396 pThis->aBCR[BCR_LED1 ] = 0x0084;
3397 pThis->aBCR[BCR_LED2 ] = 0x0088;
3398 pThis->aBCR[BCR_LED3 ] = 0x0090;
3399 /* For ISA PnP cards, BCR8 reports IRQ/DMA (e.g. 0x0035 means IRQ 3, DMA 5). */
3400 pThis->aBCR[BCR_FDC ] = 0x0000;
3401 pThis->aBCR[BCR_BSBC ] = 0x9001;
3402 pThis->aBCR[BCR_EECAS] = 0x0002;
3403 pThis->aBCR[BCR_STVAL] = 0xffff;
3404 pThis->aCSR[58 ] = /* CSR58 is an alias for BCR20 */
3405 pThis->aBCR[BCR_SWS ] = 0x0200;
3406 pThis->iLog2DescSize = 3;
3407 pThis->aBCR[BCR_PLAT ] = 0xff06;
3408 pThis->aBCR[BCR_MIIADDR ] = 0; /* Internal PHY on Am79C973 would be (0x1e << 5) */
3409 pThis->aBCR[BCR_PCIVID] = PCIDevGetVendorId(&pThis->PciDev);
3410 pThis->aBCR[BCR_PCISID] = PCIDevGetSubSystemId(&pThis->PciDev);
3411 pThis->aBCR[BCR_PCISVID] = PCIDevGetSubSystemVendorId(&pThis->PciDev);
3412
3413 /* Reset the error counter. */
3414 pThis->uCntBadRMD = 0;
3415
3416 pcnetSoftReset(pThis);
3417}
3418#endif /* IN_RING3 */
3419
3420
3421/* -=-=-=-=-=- APROM I/O Port access -=-=-=-=-=- */
3422
3423static void pcnetAPROMWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3424{
3425 addr &= 0x0f;
3426 val &= 0xff;
3427 Log(("#%d pcnetAPROMWriteU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3428 /* Check APROMWE bit to enable write access */
3429 if (pcnetBCRReadU16(pThis, 2) & 0x80)
3430 pThis->aPROM[addr] = val;
3431}
3432
3433static uint32_t pcnetAPROMReadU8(PPCNETSTATE pThis, uint32_t addr)
3434{
3435 uint32_t val = pThis->aPROM[addr &= 0x0f];
3436 Log(("#%d pcnetAPROMReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3437 return val;
3438}
3439
3440/**
3441 * @callback_method_impl{FNIOMIOPORTIN, APROM}
3442 */
3443PDMBOTHCBDECL(int) pcnetIOPortAPromRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3444{
3445 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3446 int rc = VINF_SUCCESS;
3447 STAM_PROFILE_ADV_START(&pThis->StatAPROMRead, a);
3448 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3449 RT_NOREF_PV(pvUser);
3450
3451 /* FreeBSD is accessing in dwords. */
3452 if (cb == 1)
3453 *pu32 = pcnetAPROMReadU8(pThis, Port);
3454 else if (cb == 2 && !BCR_DWIO(pThis))
3455 *pu32 = pcnetAPROMReadU8(pThis, Port)
3456 | (pcnetAPROMReadU8(pThis, Port + 1) << 8);
3457 else if (cb == 4 && BCR_DWIO(pThis))
3458 *pu32 = pcnetAPROMReadU8(pThis, Port)
3459 | (pcnetAPROMReadU8(pThis, Port + 1) << 8)
3460 | (pcnetAPROMReadU8(pThis, Port + 2) << 16)
3461 | (pcnetAPROMReadU8(pThis, Port + 3) << 24);
3462 else
3463 {
3464 Log(("#%d pcnetIOPortAPromRead: Port=%RTiop cb=%d BCR_DWIO !!\n", PCNET_INST_NR, Port, cb));
3465 rc = VERR_IOM_IOPORT_UNUSED;
3466 }
3467
3468 STAM_PROFILE_ADV_STOP(&pThis->StatAPROMRead, a);
3469 LogFlow(("#%d pcnetIOPortAPromRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, *pu32, cb, rc));
3470 return rc;
3471}
3472
3473
3474/**
3475 * @callback_method_impl{FNIOMIOPORTOUT, APROM}
3476 */
3477PDMBOTHCBDECL(int) pcnetIOPortAPromWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3478{
3479 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3480 int rc = VINF_SUCCESS;
3481 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3482 RT_NOREF_PV(pvUser);
3483
3484 if (cb == 1)
3485 {
3486 STAM_PROFILE_ADV_START(&pThis->StatAPROMWrite, a);
3487 pcnetAPROMWriteU8(pThis, Port, u32);
3488 STAM_PROFILE_ADV_STOP(&pThis->StatAPROMWrite, a);
3489 }
3490 else
3491 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Port=%#x cb=%d u32=%#x\n", Port, cb, u32);
3492
3493 LogFlow(("#%d pcnetIOPortAPromWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, u32, cb, rc));
3494 return rc;
3495}
3496
3497
3498/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
3499
3500
3501static int pcnetIoportWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3502{
3503 RT_NOREF1(val);
3504#ifdef PCNET_DEBUG_IO
3505 Log2(("#%d pcnetIoportWriteU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3506#endif
3507 if (RT_LIKELY(!BCR_DWIO(pThis)))
3508 {
3509 switch (addr & 0x0f)
3510 {
3511 case 0x04: /* RESET */
3512 break;
3513 }
3514 }
3515 else
3516 Log(("#%d pcnetIoportWriteU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3517
3518 return VINF_SUCCESS;
3519}
3520
3521static uint32_t pcnetIoportReadU8(PPCNETSTATE pThis, uint32_t addr, int *pRC)
3522{
3523 uint32_t val = UINT32_MAX;
3524
3525 *pRC = VINF_SUCCESS;
3526
3527 if (RT_LIKELY(!BCR_DWIO(pThis)))
3528 {
3529 switch (addr & 0x0f)
3530 {
3531 case 0x04: /* RESET */
3532 pcnetSoftReset(pThis);
3533 val = 0;
3534 break;
3535 }
3536 }
3537 else
3538 Log(("#%d pcnetIoportReadU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val & 0xff));
3539
3540 pcnetUpdateIrq(pThis);
3541
3542#ifdef PCNET_DEBUG_IO
3543 Log2(("#%d pcnetIoportReadU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val & 0xff));
3544#endif
3545 return val;
3546}
3547
3548static int pcnetIoportWriteU16(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3549{
3550 int rc = VINF_SUCCESS;
3551
3552#ifdef PCNET_DEBUG_IO
3553 Log2(("#%d pcnetIoportWriteU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3554#endif
3555 if (RT_LIKELY(!BCR_DWIO(pThis)))
3556 {
3557 switch (addr & 0x0f)
3558 {
3559 case 0x00: /* RDP */
3560 pcnetPollTimer(pThis);
3561 rc = pcnetCSRWriteU16(pThis, pThis->u32RAP, val);
3562 pcnetUpdateIrq(pThis);
3563 break;
3564 case 0x02: /* RAP */
3565 pThis->u32RAP = val & 0x7f;
3566 break;
3567 case 0x06: /* BDP */
3568 rc = pcnetBCRWriteU16(pThis, pThis->u32RAP, val);
3569 break;
3570 }
3571 }
3572 else
3573 Log(("#%d pcnetIoportWriteU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3574
3575 return rc;
3576}
3577
3578static uint32_t pcnetIoportReadU16(PPCNETSTATE pThis, uint32_t addr, int *pRC)
3579{
3580 uint32_t val = ~0U;
3581
3582 *pRC = VINF_SUCCESS;
3583
3584 if (RT_LIKELY(!BCR_DWIO(pThis)))
3585 {
3586 switch (addr & 0x0f)
3587 {
3588 case 0x00: /* RDP */
3589 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3590 /** Polling is then useless here and possibly expensive. */
3591 if (!CSR_DPOLL(pThis))
3592 pcnetPollTimer(pThis);
3593
3594 val = pcnetCSRReadU16(pThis, pThis->u32RAP);
3595 if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3596 goto skip_update_irq;
3597 break;
3598 case 0x02: /* RAP */
3599 val = pThis->u32RAP;
3600 goto skip_update_irq;
3601 case 0x04: /* RESET */
3602 pcnetSoftReset(pThis);
3603 val = 0;
3604 break;
3605 case 0x06: /* BDP */
3606 val = pcnetBCRReadU16(pThis, pThis->u32RAP);
3607 break;
3608 }
3609 }
3610 else
3611 Log(("#%d pcnetIoportReadU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val & 0xffff));
3612
3613 pcnetUpdateIrq(pThis);
3614
3615skip_update_irq:
3616#ifdef PCNET_DEBUG_IO
3617 Log2(("#%d pcnetIoportReadU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val & 0xffff));
3618#endif
3619 return val;
3620}
3621
3622static int pcnetIoportWriteU32(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
3623{
3624 int rc = VINF_SUCCESS;
3625
3626#ifdef PCNET_DEBUG_IO
3627 Log2(("#%d pcnetIoportWriteU32: addr=%#010x val=%#010x\n", PCNET_INST_NR,
3628 addr, val));
3629#endif
3630 if (RT_LIKELY(BCR_DWIO(pThis)))
3631 {
3632 switch (addr & 0x0f)
3633 {
3634 case 0x00: /* RDP */
3635 pcnetPollTimer(pThis);
3636 rc = pcnetCSRWriteU16(pThis, pThis->u32RAP, val & 0xffff);
3637 pcnetUpdateIrq(pThis);
3638 break;
3639 case 0x04: /* RAP */
3640 pThis->u32RAP = val & 0x7f;
3641 break;
3642 case 0x0c: /* BDP */
3643 rc = pcnetBCRWriteU16(pThis, pThis->u32RAP, val & 0xffff);
3644 break;
3645 }
3646 }
3647 else if ((addr & 0x0f) == 0)
3648 {
3649 /* switch device to dword I/O mode */
3650 pcnetBCRWriteU16(pThis, BCR_BSBC, pcnetBCRReadU16(pThis, BCR_BSBC) | 0x0080);
3651#ifdef PCNET_DEBUG_IO
3652 Log2(("device switched into dword i/o mode\n"));
3653#endif
3654 }
3655 else
3656 Log(("#%d pcnetIoportWriteU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3657
3658 return rc;
3659}
3660
3661static uint32_t pcnetIoportReadU32(PPCNETSTATE pThis, uint32_t addr, int *pRC)
3662{
3663 uint32_t val = ~0U;
3664
3665 *pRC = VINF_SUCCESS;
3666
3667 if (RT_LIKELY(BCR_DWIO(pThis)))
3668 {
3669 switch (addr & 0x0f)
3670 {
3671 case 0x00: /* RDP */
3672 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3673 /** Polling is then useless here and possibly expensive. */
3674 if (!CSR_DPOLL(pThis))
3675 pcnetPollTimer(pThis);
3676
3677 val = pcnetCSRReadU16(pThis, pThis->u32RAP);
3678 if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3679 goto skip_update_irq;
3680 break;
3681 case 0x04: /* RAP */
3682 val = pThis->u32RAP;
3683 goto skip_update_irq;
3684 case 0x08: /* RESET */
3685 pcnetSoftReset(pThis);
3686 val = 0;
3687 break;
3688 case 0x0c: /* BDP */
3689 val = pcnetBCRReadU16(pThis, pThis->u32RAP);
3690 break;
3691 }
3692 }
3693 else
3694 Log(("#%d pcnetIoportReadU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
3695 pcnetUpdateIrq(pThis);
3696
3697skip_update_irq:
3698#ifdef PCNET_DEBUG_IO
3699 Log2(("#%d pcnetIoportReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
3700#endif
3701 return val;
3702}
3703
3704
3705/**
3706 * @callback_method_impl{FNIOMIOPORTIN}
3707 */
3708PDMBOTHCBDECL(int) pcnetIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3709{
3710 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3711 int rc = VINF_SUCCESS;
3712 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
3713 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3714 RT_NOREF_PV(pvUser);
3715
3716 switch (cb)
3717 {
3718 case 1: *pu32 = pcnetIoportReadU8(pThis, Port, &rc); break;
3719 case 2: *pu32 = pcnetIoportReadU16(pThis, Port, &rc); break;
3720 case 4: *pu32 = pcnetIoportReadU32(pThis, Port, &rc); break;
3721 default:
3722 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3723 "pcnetIOPortRead: unsupported op size: offset=%#10x cb=%u\n",
3724 Port, cb);
3725 }
3726
3727 Log2(("#%d pcnetIOPortRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, *pu32, cb, rc));
3728 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
3729 return rc;
3730}
3731
3732
3733/**
3734 * @callback_method_impl{FNIOMIOPORTOUT}
3735 */
3736PDMBOTHCBDECL(int) pcnetIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3737{
3738 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3739 int rc = VINF_SUCCESS;
3740 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3741 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3742 RT_NOREF_PV(pvUser);
3743
3744 switch (cb)
3745 {
3746 case 1: rc = pcnetIoportWriteU8(pThis, Port, u32); break;
3747 case 2: rc = pcnetIoportWriteU16(pThis, Port, u32); break;
3748 case 4: rc = pcnetIoportWriteU32(pThis, Port, u32); break;
3749 default:
3750 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3751 "pcnetIOPortWrite: unsupported op size: offset=%#10x cb=%u\n",
3752 Port, cb);
3753 }
3754
3755 Log2(("#%d pcnetIOPortWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, Port, u32, cb, rc));
3756 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
3757 return rc;
3758}
3759
3760
3761/* -=-=-=-=-=- MMIO -=-=-=-=-=- */
3762
3763static void pcnetMMIOWriteU8(PPCNETSTATE pThis, RTGCPHYS addr, uint32_t val)
3764{
3765#ifdef PCNET_DEBUG_IO
3766 Log2(("#%d pcnetMMIOWriteU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
3767#endif
3768 if (!(addr & 0x10))
3769 pcnetAPROMWriteU8(pThis, addr, val);
3770}
3771
3772static uint32_t pcnetMMIOReadU8(PPCNETSTATE pThis, RTGCPHYS addr)
3773{
3774 uint32_t val = ~0U;
3775 if (!(addr & 0x10))
3776 val = pcnetAPROMReadU8(pThis, addr);
3777#ifdef PCNET_DEBUG_IO
3778 Log2(("#%d pcnetMMIOReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val & 0xff));
3779#endif
3780 return val;
3781}
3782
3783static void pcnetMMIOWriteU16(PPCNETSTATE pThis, RTGCPHYS addr, uint32_t val)
3784{
3785#ifdef PCNET_DEBUG_IO
3786 Log2(("#%d pcnetMMIOWriteU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
3787#endif
3788 if (addr & 0x10)
3789 pcnetIoportWriteU16(pThis, addr & 0x0f, val);
3790 else
3791 {
3792 pcnetAPROMWriteU8(pThis, addr, val );
3793 pcnetAPROMWriteU8(pThis, addr+1, val >> 8);
3794 }
3795}
3796
3797static uint32_t pcnetMMIOReadU16(PPCNETSTATE pThis, RTGCPHYS addr)
3798{
3799 uint32_t val = ~0U;
3800 int rc;
3801
3802 if (addr & 0x10)
3803 val = pcnetIoportReadU16(pThis, addr & 0x0f, &rc);
3804 else
3805 {
3806 val = pcnetAPROMReadU8(pThis, addr+1);
3807 val <<= 8;
3808 val |= pcnetAPROMReadU8(pThis, addr);
3809 }
3810#ifdef PCNET_DEBUG_IO
3811 Log2(("#%d pcnetMMIOReadU16: addr=%#010x val = %#06x\n", PCNET_INST_NR, addr, val & 0xffff));
3812#endif
3813 return val;
3814}
3815
3816static void pcnetMMIOWriteU32(PPCNETSTATE pThis, RTGCPHYS addr, uint32_t val)
3817{
3818#ifdef PCNET_DEBUG_IO
3819 Log2(("#%d pcnetMMIOWriteU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
3820#endif
3821 if (addr & 0x10)
3822 pcnetIoportWriteU32(pThis, addr & 0x0f, val);
3823 else
3824 {
3825 pcnetAPROMWriteU8(pThis, addr, val );
3826 pcnetAPROMWriteU8(pThis, addr+1, val >> 8);
3827 pcnetAPROMWriteU8(pThis, addr+2, val >> 16);
3828 pcnetAPROMWriteU8(pThis, addr+3, val >> 24);
3829 }
3830}
3831
3832static uint32_t pcnetMMIOReadU32(PPCNETSTATE pThis, RTGCPHYS addr)
3833{
3834 uint32_t val;
3835 int rc;
3836
3837 if (addr & 0x10)
3838 val = pcnetIoportReadU32(pThis, addr & 0x0f, &rc);
3839 else
3840 {
3841 val = pcnetAPROMReadU8(pThis, addr+3);
3842 val <<= 8;
3843 val |= pcnetAPROMReadU8(pThis, addr+2);
3844 val <<= 8;
3845 val |= pcnetAPROMReadU8(pThis, addr+1);
3846 val <<= 8;
3847 val |= pcnetAPROMReadU8(pThis, addr );
3848 }
3849#ifdef PCNET_DEBUG_IO
3850 Log2(("#%d pcnetMMIOReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
3851#endif
3852 return val;
3853}
3854
3855
3856/**
3857 * @callback_method_impl{FNIOMMMIOREAD}
3858 */
3859PDMBOTHCBDECL(int) pcnetMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3860{
3861 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3862 int rc = VINF_SUCCESS;
3863 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3864 RT_NOREF_PV(pDevIns);
3865
3866 /*
3867 * We have to check the range, because we're page aligning the MMIO.
3868 */
3869 if (GCPhysAddr - pThis->MMIOBase < PCNET_PNPMMIO_SIZE)
3870 {
3871 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIORead), a);
3872 switch (cb)
3873 {
3874 case 1: *(uint8_t *)pv = pcnetMMIOReadU8 (pThis, GCPhysAddr); break;
3875 case 2: *(uint16_t *)pv = pcnetMMIOReadU16(pThis, GCPhysAddr); break;
3876 case 4: *(uint32_t *)pv = pcnetMMIOReadU32(pThis, GCPhysAddr); break;
3877 default:
3878 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3879 "pcnetMMIORead: unsupported op size: address=%RGp cb=%u\n",
3880 GCPhysAddr, cb);
3881 }
3882 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIORead), a);
3883 }
3884 else
3885 memset(pv, 0, cb);
3886
3887 LogFlow(("#%d pcnetMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n",
3888 PCNET_INST_NR, pv, cb, pv, cb, GCPhysAddr, rc));
3889 return rc;
3890}
3891
3892
3893/**
3894 * @callback_method_impl{FNIOMMMIOWRITE}
3895 */
3896PDMBOTHCBDECL(int) pcnetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
3897{
3898 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3899 int rc = VINF_SUCCESS;
3900 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3901 RT_NOREF_PV(pDevIns);
3902
3903 /*
3904 * We have to check the range, because we're page aligning the MMIO stuff presently.
3905 */
3906 if (GCPhysAddr - pThis->MMIOBase < PCNET_PNPMMIO_SIZE)
3907 {
3908 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3909 switch (cb)
3910 {
3911 case 1: pcnetMMIOWriteU8 (pThis, GCPhysAddr, *(uint8_t *)pv); break;
3912 case 2: pcnetMMIOWriteU16(pThis, GCPhysAddr, *(uint16_t *)pv); break;
3913 case 4: pcnetMMIOWriteU32(pThis, GCPhysAddr, *(uint32_t *)pv); break;
3914 default:
3915 rc = PDMDevHlpDBGFStop(pThis->CTX_SUFF(pDevIns), RT_SRC_POS,
3916 "pcnetMMIOWrite: unsupported op size: address=%RGp cb=%u\n",
3917 GCPhysAddr, cb);
3918 }
3919
3920 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
3921 }
3922 LogFlow(("#%d pcnetMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n",
3923 PCNET_INST_NR, pv, cb, pv, cb, GCPhysAddr, rc));
3924 return rc;
3925}
3926
3927
3928#ifdef IN_RING3
3929
3930/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
3931
3932/**
3933 * @callback_method_impl{FNTMTIMERDEV, Poll timer}
3934 */
3935static DECLCALLBACK(void) pcnetTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3936{
3937 RT_NOREF(pDevIns, pTimer);
3938 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3939 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3940
3941 STAM_PROFILE_ADV_START(&pThis->StatTimer, a);
3942 pcnetPollTimer(pThis);
3943 STAM_PROFILE_ADV_STOP(&pThis->StatTimer, a);
3944}
3945
3946
3947/**
3948 * @callback_method_impl{FNTMTIMERDEV,
3949 * Software interrupt timer callback function.}
3950 */
3951static DECLCALLBACK(void) pcnetTimerSoftInt(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3952{
3953 RT_NOREF(pDevIns, pTimer);
3954 PPCNETSTATE pThis = (PPCNETSTATE)pvUser;
3955 Assert(PDMCritSectIsOwner(&pThis->CritSect));
3956
3957 pThis->aCSR[7] |= 0x0800; /* STINT */
3958 pcnetUpdateIrq(pThis);
3959 TMTimerSetNano(pThis->CTX_SUFF(pTimerSoftInt), 12800U * (pThis->aBCR[BCR_STVAL] & 0xffff));
3960}
3961
3962
3963/**
3964 * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
3965 *
3966 * This is only called when we restore a saved state and temporarily
3967 * disconnected the network link to inform the guest that network connections
3968 * should be considered lost.
3969 */
3970static DECLCALLBACK(void) pcnetTimerRestore(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3971{
3972 RT_NOREF(pTimer, pvUser);
3973 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
3974 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
3975 AssertReleaseRC(rc);
3976
3977 rc = VERR_GENERAL_FAILURE;
3978 if (pThis->cLinkDownReported <= PCNET_MAX_LINKDOWN_REPORTED)
3979 rc = TMTimerSetMillies(pThis->pTimerRestore, 1500);
3980 if (RT_FAILURE(rc))
3981 {
3982 pThis->fLinkTempDown = false;
3983 if (pThis->fLinkUp)
3984 {
3985 LogRel(("PCnet#%d: The link is back up again after the restore.\n",
3986 pDevIns->iInstance));
3987 Log(("#%d pcnetTimerRestore: Clearing ERR and CERR after load. cLinkDownReported=%d\n",
3988 pDevIns->iInstance, pThis->cLinkDownReported));
3989 pThis->aCSR[0] &= ~(RT_BIT(15) | RT_BIT(13)); /* ERR | CERR - probably not 100% correct either... */
3990 pThis->Led.Actual.s.fError = 0;
3991 }
3992 }
3993 else
3994 Log(("#%d pcnetTimerRestore: cLinkDownReported=%d, wait another 1500ms...\n",
3995 pDevIns->iInstance, pThis->cLinkDownReported));
3996
3997 PDMCritSectLeave(&pThis->CritSect);
3998}
3999
4000
4001/* -=-=-=-=-=- PCI Device Callbacks -=-=-=-=-=- */
4002
4003/**
4004 * @callback_method_impl{FNPCIIOREGIONMAP, For the PCnet I/O Ports.}
4005 */
4006static DECLCALLBACK(int) pcnetIOPortMap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
4007 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
4008{
4009 RT_NOREF(iRegion, cb, enmType);
4010 int rc;
4011 RTIOPORT Port = (RTIOPORT)GCPhysAddress;
4012 PPCNETSTATE pThis = PCIDEV_2_PCNETSTATE(pPciDev);
4013
4014 Assert(enmType == PCI_ADDRESS_SPACE_IO);
4015 Assert(cb >= 0x20);
4016
4017 rc = PDMDevHlpIOPortRegister(pDevIns, Port, 0x10, 0, pcnetIOPortAPromWrite,
4018 pcnetIOPortAPromRead, NULL, NULL, "PCnet APROM");
4019 if (RT_FAILURE(rc))
4020 return rc;
4021 rc = PDMDevHlpIOPortRegister(pDevIns, Port + 0x10, 0x10, 0, pcnetIOPortWrite,
4022 pcnetIOPortRead, NULL, NULL, "PCnet");
4023 if (RT_FAILURE(rc))
4024 return rc;
4025
4026 if (pThis->fGCEnabled)
4027 {
4028 rc = PDMDevHlpIOPortRegisterRC(pDevIns, Port, 0x10, 0, "pcnetIOPortAPromWrite",
4029 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
4030 if (RT_FAILURE(rc))
4031 return rc;
4032 rc = PDMDevHlpIOPortRegisterRC(pDevIns, Port + 0x10, 0x10, 0, "pcnetIOPortWrite",
4033 "pcnetIOPortRead", NULL, NULL, "PCnet");
4034 if (RT_FAILURE(rc))
4035 return rc;
4036 }
4037 if (pThis->fR0Enabled)
4038 {
4039 rc = PDMDevHlpIOPortRegisterR0(pDevIns, Port, 0x10, 0, "pcnetIOPortAPromWrite",
4040 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
4041 if (RT_FAILURE(rc))
4042 return rc;
4043 rc = PDMDevHlpIOPortRegisterR0(pDevIns, Port + 0x10, 0x10, 0, "pcnetIOPortWrite",
4044 "pcnetIOPortRead", NULL, NULL, "PCnet");
4045 if (RT_FAILURE(rc))
4046 return rc;
4047 }
4048
4049 pThis->IOPortBase = Port;
4050 return VINF_SUCCESS;
4051}
4052
4053
4054/**
4055 * @callback_method_impl{FNPCIIOREGIONMAP, For the PCnet MMIO region.}
4056 */
4057static DECLCALLBACK(int) pcnetMMIOMap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
4058 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
4059{
4060 RT_NOREF(iRegion, cb, enmType);
4061 PPCNETSTATE pThis = PCIDEV_2_PCNETSTATE(pPciDev);
4062 int rc;
4063
4064 Assert(enmType == PCI_ADDRESS_SPACE_MEM);
4065 Assert(cb >= PCNET_PNPMMIO_SIZE);
4066
4067 /* We use the assigned size here, because we only support page aligned MMIO ranges. */
4068 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, pThis,
4069 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
4070 pcnetMMIOWrite, pcnetMMIORead, "PCnet");
4071 if (RT_FAILURE(rc))
4072 return rc;
4073 pThis->MMIOBase = GCPhysAddress;
4074 return rc;
4075}
4076
4077
4078/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
4079
4080/**
4081 * @callback_method_impl{FNDBGFHANDLERDEV}
4082 */
4083static DECLCALLBACK(void) pcnetInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4084{
4085 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4086 bool fRcvRing = false;
4087 bool fXmtRing = false;
4088 bool fAPROM = false;
4089
4090 /*
4091 * Parse args.
4092 */
4093 if (pszArgs)
4094 {
4095 fRcvRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "rcv");
4096 fXmtRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "xmt");
4097 fAPROM = strstr(pszArgs, "verbose") || strstr(pszArgs, "aprom");
4098 }
4099
4100 /*
4101 * Show info.
4102 */
4103 const char *pcszModel;
4104 switch (pThis->uDevType)
4105 {
4106 case DEV_AM79C970A: pcszModel = "AM79C970A"; break;
4107 case DEV_AM79C973: pcszModel = "AM79C973"; break;
4108 case DEV_AM79C960: pcszModel = "AM79C960/NE2100"; break;
4109 case DEV_AM79C960_EB: pcszModel = "AM79C960/EtherBlaster"; break;
4110 default: pcszModel = "Unknown"; break;
4111 }
4112 pHlp->pfnPrintf(pHlp,
4113 "pcnet #%d: port=%RTiop mmio=%RX32 mac-cfg=%RTmac %s%s%s\n",
4114 pDevIns->iInstance,
4115 pThis->IOPortBase, pThis->MMIOBase, &pThis->MacConfigured,
4116 pcszModel, pThis->fGCEnabled ? " RC" : "", pThis->fR0Enabled ? " R0" : "");
4117
4118 PDMCritSectEnter(&pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
4119
4120 pHlp->pfnPrintf(pHlp,
4121 "CSR0=%#06x:\n",
4122 pThis->aCSR[0]);
4123
4124 pHlp->pfnPrintf(pHlp,
4125 "CSR1=%#06x:\n",
4126 pThis->aCSR[1]);
4127
4128 pHlp->pfnPrintf(pHlp,
4129 "CSR2=%#06x:\n",
4130 pThis->aCSR[2]);
4131
4132 pHlp->pfnPrintf(pHlp,
4133 "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",
4134 pThis->aCSR[3],
4135 !!(pThis->aCSR[3] & RT_BIT(2)), !!(pThis->aCSR[3] & RT_BIT(3)), !!(pThis->aCSR[3] & RT_BIT(4)), CSR_LAPPEN(pThis),
4136 CSR_DXSUFLO(pThis), !!(pThis->aCSR[3] & RT_BIT(8)), !!(pThis->aCSR[3] & RT_BIT(9)), !!(pThis->aCSR[3] & RT_BIT(10)),
4137 !!(pThis->aCSR[3] & RT_BIT(11)), !!(pThis->aCSR[3] & RT_BIT(12)), !!(pThis->aCSR[3] & RT_BIT(14)));
4138
4139 pHlp->pfnPrintf(pHlp,
4140 "CSR4=%#06x: JABM=%d JAB=%d TXSTRM=%d TXSTRT=%d RCVCOOM=%d RCVCCO=%d UINT=%d UINTCMD=%d\n"
4141 " MFCOM=%d MFCO=%d ASTRP_RCV=%d APAD_XMT=%d DPOLL=%d TIMER=%d EMAPLUS=%d EN124=%d\n",
4142 pThis->aCSR[4],
4143 !!(pThis->aCSR[4] & RT_BIT( 0)), !!(pThis->aCSR[4] & RT_BIT( 1)), !!(pThis->aCSR[4] & RT_BIT( 2)), !!(pThis->aCSR[4] & RT_BIT( 3)),
4144 !!(pThis->aCSR[4] & RT_BIT( 4)), !!(pThis->aCSR[4] & RT_BIT( 5)), !!(pThis->aCSR[4] & RT_BIT( 6)), !!(pThis->aCSR[4] & RT_BIT( 7)),
4145 !!(pThis->aCSR[4] & RT_BIT( 8)), !!(pThis->aCSR[4] & RT_BIT( 9)), !!(pThis->aCSR[4] & RT_BIT(10)), !!(pThis->aCSR[4] & RT_BIT(11)),
4146 !!(pThis->aCSR[4] & RT_BIT(12)), !!(pThis->aCSR[4] & RT_BIT(13)), !!(pThis->aCSR[4] & RT_BIT(14)), !!(pThis->aCSR[4] & RT_BIT(15)));
4147
4148 pHlp->pfnPrintf(pHlp,
4149 "CSR5=%#06x:\n",
4150 pThis->aCSR[5]);
4151
4152 pHlp->pfnPrintf(pHlp,
4153 "CSR6=%#06x: RLEN=%#x* TLEN=%#x* [* encoded]\n",
4154 pThis->aCSR[6],
4155 (pThis->aCSR[6] >> 8) & 0xf, (pThis->aCSR[6] >> 12) & 0xf);
4156
4157 pHlp->pfnPrintf(pHlp,
4158 "CSR8..11=%#06x,%#06x,%#06x,%#06x: LADRF=%#018llx\n",
4159 pThis->aCSR[8], pThis->aCSR[9], pThis->aCSR[10], pThis->aCSR[11],
4160 (uint64_t)(pThis->aCSR[ 8] & 0xffff)
4161 | (uint64_t)(pThis->aCSR[ 9] & 0xffff) << 16
4162 | (uint64_t)(pThis->aCSR[10] & 0xffff) << 32
4163 | (uint64_t)(pThis->aCSR[11] & 0xffff) << 48);
4164
4165 pHlp->pfnPrintf(pHlp,
4166 "CSR12..14=%#06x,%#06x,%#06x: PADR=%02x:%02x:%02x:%02x:%02x:%02x (Current MAC Address)\n",
4167 pThis->aCSR[12], pThis->aCSR[13], pThis->aCSR[14],
4168 pThis->aCSR[12] & 0xff,
4169 (pThis->aCSR[12] >> 8) & 0xff,
4170 pThis->aCSR[13] & 0xff,
4171 (pThis->aCSR[13] >> 8) & 0xff,
4172 pThis->aCSR[14] & 0xff,
4173 (pThis->aCSR[14] >> 8) & 0xff);
4174
4175 pHlp->pfnPrintf(pHlp,
4176 "CSR15=%#06x: DXR=%d DTX=%d LOOP=%d DXMTFCS=%d FCOLL=%d DRTY=%d INTL=%d PORTSEL=%d LTR=%d\n"
4177 " MENDECL=%d DAPC=%d DLNKTST=%d DRCVPV=%d DRCVBC=%d PROM=%d\n",
4178 pThis->aCSR[15],
4179 !!(pThis->aCSR[15] & RT_BIT( 0)), !!(pThis->aCSR[15] & RT_BIT( 1)), !!(pThis->aCSR[15] & RT_BIT( 2)), !!(pThis->aCSR[15] & RT_BIT( 3)),
4180 !!(pThis->aCSR[15] & RT_BIT( 4)), !!(pThis->aCSR[15] & RT_BIT( 5)), !!(pThis->aCSR[15] & RT_BIT( 6)), (pThis->aCSR[15] >> 7) & 3,
4181 !!(pThis->aCSR[15] & RT_BIT( 9)), !!(pThis->aCSR[15] & RT_BIT(10)), !!(pThis->aCSR[15] & RT_BIT(11)),
4182 !!(pThis->aCSR[15] & RT_BIT(12)), !!(pThis->aCSR[15] & RT_BIT(13)), !!(pThis->aCSR[15] & RT_BIT(14)), !!(pThis->aCSR[15] & RT_BIT(15)));
4183
4184 pHlp->pfnPrintf(pHlp,
4185 "CSR46=%#06x: POLL=%#06x (Poll Time Counter)\n",
4186 pThis->aCSR[46], pThis->aCSR[46] & 0xffff);
4187
4188 pHlp->pfnPrintf(pHlp,
4189 "CSR47=%#06x: POLLINT=%#06x (Poll Time Interval)\n",
4190 pThis->aCSR[47], pThis->aCSR[47] & 0xffff);
4191
4192 pHlp->pfnPrintf(pHlp,
4193 "CSR58=%#06x: SWSTYLE=%d %s SSIZE32=%d CSRPCNET=%d APERRENT=%d\n",
4194 pThis->aCSR[58],
4195 pThis->aCSR[58] & 0x7f,
4196 (pThis->aCSR[58] & 0x7f) == 0 ? "C-LANCE / PCnet-ISA"
4197 : (pThis->aCSR[58] & 0x7f) == 1 ? "ILACC"
4198 : (pThis->aCSR[58] & 0x7f) == 2 ? "PCnet-PCI II"
4199 : (pThis->aCSR[58] & 0x7f) == 3 ? "PCnet-PCI II controller"
4200 : "!!reserved!!",
4201 !!(pThis->aCSR[58] & RT_BIT(8)), !!(pThis->aCSR[58] & RT_BIT(9)), !!(pThis->aCSR[58] & RT_BIT(10)));
4202
4203 pHlp->pfnPrintf(pHlp,
4204 "CSR112=%04RX32: MFC=%04x (Missed receive Frame Count)\n",
4205 pThis->aCSR[112], pThis->aCSR[112] & 0xffff);
4206
4207 pHlp->pfnPrintf(pHlp,
4208 "CSR122=%04RX32: RCVALGN=%04x (Receive Frame Align)\n",
4209 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(0)));
4210
4211 pHlp->pfnPrintf(pHlp,
4212 "CSR124=%04RX32: RPA=%04x (Runt Packet Accept)\n",
4213 pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(3)));
4214
4215
4216 /*
4217 * Dump the receive ring.
4218 */
4219 pHlp->pfnPrintf(pHlp,
4220 "RCVRL=%04x RCVRC=%04x GCRDRA=%RX32 \n"
4221 "CRDA=%08RX32 CRBA=%08RX32 CRBC=%03x CRST=%04x\n"
4222 "NRDA=%08RX32 NRBA=%08RX32 NRBC=%03x NRST=%04x\n"
4223 "NNRDA=%08RX32\n"
4224 ,
4225 CSR_RCVRL(pThis), CSR_RCVRC(pThis), pThis->GCRDRA,
4226 CSR_CRDA(pThis), CSR_CRBA(pThis), CSR_CRBC(pThis), CSR_CRST(pThis),
4227 CSR_NRDA(pThis), CSR_NRBA(pThis), CSR_NRBC(pThis), CSR_NRST(pThis),
4228 CSR_NNRD(pThis));
4229 if (fRcvRing)
4230 {
4231 const unsigned cb = 1 << pThis->iLog2DescSize;
4232 RTGCPHYS32 GCPhys = pThis->GCRDRA;
4233 unsigned i = CSR_RCVRL(pThis);
4234 while (i-- > 0)
4235 {
4236 RMD rmd;
4237 pcnetRmdLoad(pThis, &rmd, PHYSADDR(pThis, GCPhys), false);
4238 pHlp->pfnPrintf(pHlp,
4239 "%04x %RX32:%c%c RBADR=%08RX32 BCNT=%03x MCNT=%03x "
4240 "OWN=%d ERR=%d FRAM=%d OFLO=%d CRC=%d BUFF=%d STP=%d ENP=%d BPE=%d "
4241 "PAM=%d LAFM=%d BAM=%d RCC=%02x RPC=%02x ONES=%#x ZEROS=%d\n",
4242 i, GCPhys, i + 1 == CSR_RCVRC(pThis) ? '*' : ' ', GCPhys == CSR_CRDA(pThis) ? '*' : ' ',
4243 rmd.rmd0.rbadr, 4096 - rmd.rmd1.bcnt, rmd.rmd2.mcnt,
4244 rmd.rmd1.own, rmd.rmd1.err, rmd.rmd1.fram, rmd.rmd1.oflo, rmd.rmd1.crc, rmd.rmd1.buff,
4245 rmd.rmd1.stp, rmd.rmd1.enp, rmd.rmd1.bpe,
4246 rmd.rmd1.pam, rmd.rmd1.lafm, rmd.rmd1.bam, rmd.rmd2.rcc, rmd.rmd2.rpc,
4247 rmd.rmd1.ones, rmd.rmd2.zeros);
4248
4249 GCPhys += cb;
4250 }
4251 }
4252
4253 /*
4254 * Dump the transmit ring.
4255 */
4256 pHlp->pfnPrintf(pHlp,
4257 "XMTRL=%04x XMTRC=%04x GCTDRA=%08RX32 BADX=%08RX32\n"
4258 "PXDA=%08RX32 PXBC=%03x PXST=%04x\n"
4259 "CXDA=%08RX32 CXBA=%08RX32 CXBC=%03x CXST=%04x\n"
4260 "NXDA=%08RX32 NXBA=%08RX32 NXBC=%03x NXST=%04x\n"
4261 "NNXDA=%08RX32\n"
4262 ,
4263 CSR_XMTRL(pThis), CSR_XMTRC(pThis),
4264 pThis->GCTDRA, CSR_BADX(pThis),
4265 CSR_PXDA(pThis), CSR_PXBC(pThis), CSR_PXST(pThis),
4266 CSR_CXDA(pThis), CSR_CXBA(pThis), CSR_CXBC(pThis), CSR_CXST(pThis),
4267 CSR_NXDA(pThis), CSR_NXBA(pThis), CSR_NXBC(pThis), CSR_NXST(pThis),
4268 CSR_NNXD(pThis));
4269 if (fXmtRing)
4270 {
4271 const unsigned cb = 1 << pThis->iLog2DescSize;
4272 RTGCPHYS32 GCPhys = pThis->GCTDRA;
4273 unsigned i = CSR_XMTRL(pThis);
4274 while (i-- > 0)
4275 {
4276 TMD tmd;
4277 pcnetTmdLoad(pThis, &tmd, PHYSADDR(pThis, GCPhys), false);
4278 pHlp->pfnPrintf(pHlp,
4279 "%04x %RX32:%c%c TBADR=%08RX32 BCNT=%03x OWN=%d "
4280 "ERR=%d NOFCS=%d LTINT=%d ONE=%d DEF=%d STP=%d ENP=%d BPE=%d "
4281 "BUFF=%d UFLO=%d EXDEF=%d LCOL=%d LCAR=%d RTRY=%d TDR=%03x TRC=%#x ONES=%#x\n"
4282 ,
4283 i,
4284 GCPhys,
4285 i + 1 == CSR_XMTRC(pThis) ? '*' : ' ',
4286 GCPhys == CSR_CXDA(pThis) ? '*' : ' ',
4287 tmd.tmd0.tbadr,
4288 4096 - tmd.tmd1.bcnt,
4289 tmd.tmd1.own,
4290 tmd.tmd1.err,
4291 tmd.tmd1.nofcs,
4292 tmd.tmd1.ltint,
4293 tmd.tmd1.one,
4294 tmd.tmd1.def,
4295 tmd.tmd1.stp,
4296 tmd.tmd1.enp,
4297 tmd.tmd1.bpe,
4298 tmd.tmd2.buff,
4299 tmd.tmd2.uflo,
4300 tmd.tmd2.exdef,
4301 tmd.tmd2.lcol,
4302 tmd.tmd2.lcar,
4303 tmd.tmd2.rtry,
4304 tmd.tmd2.tdr,
4305 tmd.tmd2.trc,
4306 tmd.tmd1.ones);
4307
4308 GCPhys += cb;
4309 }
4310 }
4311
4312 /* Dump the Addres PROM (APROM). */
4313 if (fAPROM)
4314 {
4315 pHlp->pfnPrintf(pHlp,
4316 "Address PROM:\n %Rhxs\n", pThis->aPROM);
4317
4318
4319 }
4320
4321 PDMCritSectLeave(&pThis->CritSect);
4322}
4323
4324
4325/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
4326
4327/**
4328 * Takes down the link temporarily if it's current status is up.
4329 *
4330 * This is used during restore and when replumbing the network link.
4331 *
4332 * The temporary link outage is supposed to indicate to the OS that all network
4333 * connections have been lost and that it for instance is appropriate to
4334 * renegotiate any DHCP lease.
4335 *
4336 * @param pThis The PCnet instance data.
4337 */
4338static void pcnetTempLinkDown(PPCNETSTATE pThis)
4339{
4340 if (pThis->fLinkUp)
4341 {
4342 pThis->fLinkTempDown = true;
4343 pThis->cLinkDownReported = 0;
4344 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4345 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4346 int rc = TMTimerSetMillies(pThis->pTimerRestore, pThis->cMsLinkUpDelay);
4347 AssertRC(rc);
4348 }
4349}
4350
4351
4352/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
4353
4354/**
4355 * Saves the configuration.
4356 *
4357 * @param pThis The PCnet instance data.
4358 * @param pSSM The saved state handle.
4359 */
4360static void pcnetSaveConfig(PPCNETSTATE pThis, PSSMHANDLE pSSM)
4361{
4362 SSMR3PutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
4363 SSMR3PutU8(pSSM, pThis->uDevType);
4364 SSMR3PutU32(pSSM, pThis->u32LinkSpeed);
4365}
4366
4367
4368/**
4369 * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
4370 */
4371static DECLCALLBACK(int) pcnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
4372{
4373 RT_NOREF(uPass);
4374 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4375 pcnetSaveConfig(pThis, pSSM);
4376 return VINF_SSM_DONT_CALL_AGAIN;
4377}
4378
4379
4380/**
4381 * @callback_method_impl{FNSSMDEVSAVEPREP,
4382 * Serializes the receive thread, it may be working inside the critsect.}
4383 */
4384static DECLCALLBACK(int) pcnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4385{
4386 RT_NOREF(pSSM);
4387 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4388
4389 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4390 AssertRC(rc);
4391 PDMCritSectLeave(&pThis->CritSect);
4392
4393 return VINF_SUCCESS;
4394}
4395
4396
4397/**
4398 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4399 */
4400static DECLCALLBACK(int) pcnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4401{
4402 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4403
4404 SSMR3PutBool(pSSM, pThis->fLinkUp);
4405 SSMR3PutU32(pSSM, pThis->u32RAP);
4406 SSMR3PutS32(pSSM, pThis->iISR);
4407 SSMR3PutU32(pSSM, pThis->u32Lnkst);
4408 SSMR3PutBool(pSSM, false/* was ffPrivIfEnabled */); /* >= If version 0.9 */
4409 SSMR3PutBool(pSSM, pThis->fSignalRxMiss); /* >= If version 0.10 */
4410 SSMR3PutGCPhys32(pSSM, pThis->GCRDRA);
4411 SSMR3PutGCPhys32(pSSM, pThis->GCTDRA);
4412 SSMR3PutMem(pSSM, pThis->aPROM, sizeof(pThis->aPROM));
4413 SSMR3PutMem(pSSM, pThis->aCSR, sizeof(pThis->aCSR));
4414 SSMR3PutMem(pSSM, pThis->aBCR, sizeof(pThis->aBCR));
4415 SSMR3PutMem(pSSM, pThis->aMII, sizeof(pThis->aMII));
4416 SSMR3PutU16(pSSM, pThis->u16CSR0LastSeenByGuest);
4417 SSMR3PutU64(pSSM, pThis->u64LastPoll);
4418 pcnetSaveConfig(pThis, pSSM);
4419
4420 int rc = VINF_SUCCESS;
4421#ifndef PCNET_NO_POLLING
4422 rc = TMR3TimerSave(pThis->CTX_SUFF(pTimerPoll), pSSM);
4423 if (RT_FAILURE(rc))
4424 return rc;
4425#endif
4426 if (pThis->uDevType == DEV_AM79C973)
4427 rc = TMR3TimerSave(pThis->CTX_SUFF(pTimerSoftInt), pSSM);
4428 return rc;
4429}
4430
4431
4432/**
4433 * @callback_method_impl{FNSSMDEVLOADPREP},
4434 * Serializes the receive thread, it may be working inside the critsect.}
4435 */
4436static DECLCALLBACK(int) pcnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4437{
4438 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4439
4440 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4441 AssertRC(rc);
4442
4443 uint32_t uVer = SSMR3HandleVersion(pSSM);
4444 if ( uVer < VBOX_FULL_VERSION_MAKE(4, 3, 6)
4445 || ( uVer >= VBOX_FULL_VERSION_MAKE(4, 3, 51)
4446 && uVer < VBOX_FULL_VERSION_MAKE(4, 3, 53)))
4447 {
4448 /* older saved states contain the shared memory region which was never used for ages. */
4449 void *pvSharedMMIOR3;
4450 rc = PDMDevHlpMMIO2Register(pDevIns, &pThis->PciDev, 2, _512K, 0, (void **)&pvSharedMMIOR3, "PCnetSh");
4451 if (RT_FAILURE(rc))
4452 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
4453 N_("Failed to allocate the dummy shmem region for the PCnet device"));
4454 pThis->fSharedRegion = true;
4455 }
4456 PDMCritSectLeave(&pThis->CritSect);
4457
4458 return rc;
4459}
4460
4461
4462/**
4463 * @callback_method_impl{FNSSMDEVLOADEXEC}
4464 */
4465static DECLCALLBACK(int) pcnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4466{
4467 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4468
4469 if ( SSM_VERSION_MAJOR_CHANGED(uVersion, PCNET_SAVEDSTATE_VERSION)
4470 || SSM_VERSION_MINOR(uVersion) < 7)
4471 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4472
4473 if (uPass == SSM_PASS_FINAL)
4474 {
4475 /* restore data */
4476 SSMR3GetBool(pSSM, &pThis->fLinkUp);
4477 int rc = SSMR3GetU32(pSSM, &pThis->u32RAP);
4478 AssertRCReturn(rc, rc);
4479 AssertLogRelMsgReturn(pThis->u32RAP < RT_ELEMENTS(pThis->aCSR), ("%#x\n", pThis->u32RAP), VERR_SSM_LOAD_CONFIG_MISMATCH);
4480 SSMR3GetS32(pSSM, &pThis->iISR);
4481 SSMR3GetU32(pSSM, &pThis->u32Lnkst);
4482 if ( SSM_VERSION_MAJOR(uVersion) > 0
4483 || SSM_VERSION_MINOR(uVersion) >= 9)
4484 {
4485 bool fPrivIfEnabled = false;
4486 SSMR3GetBool(pSSM, &fPrivIfEnabled);
4487 if (fPrivIfEnabled)
4488 {
4489 /* no longer implemented */
4490 LogRel(("PCnet#%d: Cannot enable private interface!\n", PCNET_INST_NR));
4491 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4492 }
4493 }
4494 if ( SSM_VERSION_MAJOR(uVersion) > 0
4495 || SSM_VERSION_MINOR(uVersion) >= 10)
4496 {
4497 SSMR3GetBool(pSSM, &pThis->fSignalRxMiss);
4498 }
4499 SSMR3GetGCPhys32(pSSM, &pThis->GCRDRA);
4500 SSMR3GetGCPhys32(pSSM, &pThis->GCTDRA);
4501 SSMR3GetMem(pSSM, &pThis->aPROM, sizeof(pThis->aPROM));
4502 SSMR3GetMem(pSSM, &pThis->aCSR, sizeof(pThis->aCSR));
4503 SSMR3GetMem(pSSM, &pThis->aBCR, sizeof(pThis->aBCR));
4504 SSMR3GetMem(pSSM, &pThis->aMII, sizeof(pThis->aMII));
4505 SSMR3GetU16(pSSM, &pThis->u16CSR0LastSeenByGuest);
4506 SSMR3GetU64(pSSM, &pThis->u64LastPoll);
4507 }
4508
4509 /* check config */
4510 RTMAC Mac;
4511 int rc = SSMR3GetMem(pSSM, &Mac, sizeof(Mac));
4512 AssertRCReturn(rc, rc);
4513 if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
4514 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4515 LogRel(("PCnet#%u: The mac address differs: config=%RTmac saved=%RTmac\n", PCNET_INST_NR, &pThis->MacConfigured, &Mac));
4516
4517 uint8_t uDevType;
4518 rc = SSMR3GetU8(pSSM, &uDevType);
4519 AssertRCReturn(rc, rc);
4520 if (pThis->uDevType != uDevType)
4521 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("The uDevType setting differs: config=%u saved=%u"), pThis->uDevType, uDevType);
4522
4523 uint32_t u32LinkSpeed;
4524 rc = SSMR3GetU32(pSSM, &u32LinkSpeed);
4525 AssertRCReturn(rc, rc);
4526 if ( pThis->u32LinkSpeed != u32LinkSpeed
4527 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
4528 LogRel(("PCnet#%u: The mac link speed differs: config=%u saved=%u\n", PCNET_INST_NR, pThis->u32LinkSpeed, u32LinkSpeed));
4529
4530 if (uPass == SSM_PASS_FINAL)
4531 {
4532 /* restore timers and stuff */
4533#ifndef PCNET_NO_POLLING
4534 TMR3TimerLoad(pThis->CTX_SUFF(pTimerPoll), pSSM);
4535#endif
4536 if (pThis->uDevType == DEV_AM79C973)
4537 {
4538 if ( SSM_VERSION_MAJOR(uVersion) > 0
4539 || SSM_VERSION_MINOR(uVersion) >= 8)
4540 TMR3TimerLoad(pThis->CTX_SUFF(pTimerSoftInt), pSSM);
4541 }
4542
4543 pThis->iLog2DescSize = BCR_SWSTYLE(pThis)
4544 ? 4
4545 : 3;
4546 pThis->GCUpperPhys = BCR_SSIZE32(pThis)
4547 ? 0
4548 : (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
4549
4550 /* update promiscuous mode. */
4551 if (pThis->pDrvR3)
4552 pThis->pDrvR3->pfnSetPromiscuousMode(pThis->pDrvR3, CSR_PROM(pThis));
4553
4554#ifdef PCNET_NO_POLLING
4555 /* Enable physical monitoring again (!) */
4556 pcnetUpdateRingHandlers(pThis);
4557#endif
4558 /* Indicate link down to the guest OS that all network connections have
4559 been lost, unless we've been teleported here. */
4560 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
4561 pcnetTempLinkDown(pThis);
4562 }
4563
4564 return VINF_SUCCESS;
4565}
4566
4567/**
4568 * @callback_method_impl{FNSSMDEVLOADDONE}
4569 */
4570static DECLCALLBACK(int) pcnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4571{
4572 RT_NOREF(pSSM);
4573 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4574 int rc = VINF_SUCCESS;
4575 if (pThis->fSharedRegion)
4576 {
4577 /* drop this dummy region */
4578 rc = PDMDevHlpMMIOExDeregister(pDevIns, NULL, 2);
4579 pThis->fSharedRegion = false;
4580 }
4581 return rc;
4582}
4583
4584/* -=-=-=-=-=- PCNETSTATE::INetworkDown -=-=-=-=-=- */
4585
4586/**
4587 * Check if the device/driver can receive data now.
4588 *
4589 * Worker for pcnetNetworkDown_WaitReceiveAvail(). This must be called before
4590 * the pfnRecieve() method is called.
4591 *
4592 * @returns VBox status code.
4593 * @param pThis The PCnet instance data.
4594 */
4595static int pcnetCanReceive(PPCNETSTATE pThis)
4596{
4597 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4598 AssertReleaseRC(rc);
4599
4600 rc = VERR_NET_NO_BUFFER_SPACE;
4601
4602 if (RT_LIKELY(!CSR_DRX(pThis) && !CSR_STOP(pThis) && !CSR_SPND(pThis)))
4603 {
4604 if (HOST_IS_OWNER(CSR_CRST(pThis)) && pThis->GCRDRA)
4605 pcnetRdtePoll(pThis);
4606
4607 if (RT_UNLIKELY(HOST_IS_OWNER(CSR_CRST(pThis))))
4608 {
4609 /** @todo Notify the guest _now_. Will potentially increase the interrupt load */
4610 if (pThis->fSignalRxMiss)
4611 pThis->aCSR[0] |= 0x1000; /* Set MISS flag */
4612 }
4613 else
4614 rc = VINF_SUCCESS;
4615 }
4616
4617 PDMCritSectLeave(&pThis->CritSect);
4618 return rc;
4619}
4620
4621
4622/**
4623 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
4624 */
4625static DECLCALLBACK(int) pcnetNetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
4626{
4627 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkDown);
4628
4629 int rc = pcnetCanReceive(pThis);
4630 if (RT_SUCCESS(rc))
4631 return VINF_SUCCESS;
4632 if (RT_UNLIKELY(cMillies == 0))
4633 return VERR_NET_NO_BUFFER_SPACE;
4634
4635 rc = VERR_INTERRUPTED;
4636 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
4637 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
4638 VMSTATE enmVMState;
4639 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pThis->CTX_SUFF(pDevIns))) == VMSTATE_RUNNING
4640 || enmVMState == VMSTATE_RUNNING_LS))
4641 {
4642 int rc2 = pcnetCanReceive(pThis);
4643 if (RT_SUCCESS(rc2))
4644 {
4645 rc = VINF_SUCCESS;
4646 break;
4647 }
4648 LogFlow(("pcnetNetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", cMillies));
4649 /* Start the poll timer once which will remain active as long fMaybeOutOfSpace
4650 * is true -- even if (transmit) polling is disabled (CSR_DPOLL). */
4651 rc2 = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4652 AssertReleaseRC(rc2);
4653#ifndef PCNET_NO_POLLING
4654 pcnetPollTimerStart(pThis);
4655#endif
4656 PDMCritSectLeave(&pThis->CritSect);
4657 RTSemEventWait(pThis->hEventOutOfRxSpace, cMillies);
4658 }
4659 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
4660 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
4661
4662 return rc;
4663}
4664
4665
4666/**
4667 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
4668 */
4669static DECLCALLBACK(int) pcnetNetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
4670{
4671 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkDown);
4672 int rc;
4673
4674 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
4675 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4676 AssertReleaseRC(rc);
4677
4678 /*
4679 * Check for the max ethernet frame size, taking the IEEE 802.1Q (VLAN) tag into
4680 * account. Note that the CRC Checksum is optional.
4681 * Ethernet frames consist of a 14-byte header [+ 4-byte vlan tag] + a 1500-byte body [+ 4-byte CRC].
4682 */
4683 if (RT_LIKELY( cb <= 1518
4684 || ( cb <= 1522
4685 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN))))
4686 {
4687 bool fAddFCS = cb <= 1514
4688 || ( cb <= 1518
4689 && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN));
4690 if (cb > 70) /* unqualified guess */
4691 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
4692 pcnetReceiveNoSync(pThis, (const uint8_t *)pvBuf, cb, fAddFCS, false);
4693 pThis->Led.Actual.s.fReading = 0;
4694 }
4695#ifdef LOG_ENABLED
4696 else
4697 {
4698 static bool s_fFirstBigFrameLoss = true;
4699 unsigned cbMaxFrame = ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN)
4700 ? 1522 : 1518;
4701 if (s_fFirstBigFrameLoss)
4702 {
4703 s_fFirstBigFrameLoss = false;
4704 Log(("PCnet#%d: Received giant frame %zu, max %u. (Further giants will be reported at level5.)\n",
4705 PCNET_INST_NR, cb, cbMaxFrame));
4706 }
4707 else
4708 Log5(("PCnet#%d: Received giant frame %zu bytes, max %u.\n",
4709 PCNET_INST_NR, cb, cbMaxFrame));
4710 }
4711#endif /* LOG_ENABLED */
4712
4713 PDMCritSectLeave(&pThis->CritSect);
4714 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
4715
4716 return VINF_SUCCESS;
4717}
4718
4719
4720/**
4721 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
4722 */
4723static DECLCALLBACK(void) pcnetNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
4724{
4725 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkDown);
4726 pcnetXmitPending(pThis, true /*fOnWorkerThread*/);
4727}
4728
4729
4730/* -=-=-=-=-=- PCNETSTATE::INetworkConfig -=-=-=-=-=- */
4731
4732/**
4733 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
4734 */
4735static DECLCALLBACK(int) pcnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
4736{
4737 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkConfig);
4738 memcpy(pMac, pThis->aPROM, sizeof(*pMac));
4739 return VINF_SUCCESS;
4740}
4741
4742
4743/**
4744 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
4745 */
4746static DECLCALLBACK(PDMNETWORKLINKSTATE) pcnetGetLinkState(PPDMINETWORKCONFIG pInterface)
4747{
4748 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkConfig);
4749 if (pThis->fLinkUp && !pThis->fLinkTempDown)
4750 return PDMNETWORKLINKSTATE_UP;
4751 if (!pThis->fLinkUp)
4752 return PDMNETWORKLINKSTATE_DOWN;
4753 if (pThis->fLinkTempDown)
4754 return PDMNETWORKLINKSTATE_DOWN_RESUME;
4755 AssertMsgFailed(("Invalid link state!\n"));
4756 return PDMNETWORKLINKSTATE_INVALID;
4757}
4758
4759
4760/**
4761 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
4762 */
4763static DECLCALLBACK(int) pcnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
4764{
4765 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, INetworkConfig);
4766 bool fLinkUp;
4767
4768 AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
4769 ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
4770
4771 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
4772 {
4773 pcnetTempLinkDown(pThis);
4774 /*
4775 * Note that we do not notify the driver about the link state change because
4776 * the change is only temporary and can be disregarded from the driver's
4777 * point of view (see @bugref{7057}).
4778 */
4779 return VINF_SUCCESS;
4780 }
4781 /* has the state changed? */
4782 fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
4783 if (pThis->fLinkUp != fLinkUp)
4784 {
4785 pThis->fLinkUp = fLinkUp;
4786 if (fLinkUp)
4787 {
4788 /* Connect with a configured delay. */
4789 pThis->fLinkTempDown = true;
4790 pThis->cLinkDownReported = 0;
4791 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4792 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4793 int rc = TMTimerSetMillies(pThis->pTimerRestore, pThis->cMsLinkUpDelay);
4794 AssertRC(rc);
4795 }
4796 else
4797 {
4798 /* disconnect */
4799 pThis->cLinkDownReported = 0;
4800 pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4801 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
4802 }
4803 Assert(!PDMCritSectIsOwner(&pThis->CritSect));
4804 if (pThis->pDrvR3)
4805 pThis->pDrvR3->pfnNotifyLinkChanged(pThis->pDrvR3, enmState);
4806 }
4807 return VINF_SUCCESS;
4808}
4809
4810
4811/* -=-=-=-=-=- PCNETSTATE::ILeds (LUN#0) -=-=-=-=-=- */
4812
4813/**
4814 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4815 */
4816static DECLCALLBACK(int) pcnetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4817{
4818 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, ILeds);
4819 if (iLUN == 0)
4820 {
4821 *ppLed = &pThis->Led;
4822 return VINF_SUCCESS;
4823 }
4824 return VERR_PDM_LUN_NOT_FOUND;
4825}
4826
4827
4828/* -=-=-=-=-=- PCNETSTATE::IBase (LUN#0) -=-=-=-=-=- */
4829
4830/**
4831 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4832 */
4833static DECLCALLBACK(void *) pcnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
4834{
4835 PPCNETSTATE pThis = RT_FROM_MEMBER(pInterface, PCNETSTATE, IBase);
4836 Assert(&pThis->IBase == pInterface);
4837 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4838 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
4839 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
4840 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
4841 return NULL;
4842}
4843
4844
4845/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
4846
4847/**
4848 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
4849 */
4850static DECLCALLBACK(void) pcnetPowerOff(PPDMDEVINS pDevIns)
4851{
4852 /* Poke thread waiting for buffer space. */
4853 pcnetWakeupReceive(pDevIns);
4854}
4855
4856
4857/**
4858 * @interface_method_impl{PDMDEVREG,pfnDetach}
4859 *
4860 * One port on the network card has been disconnected from the network.
4861 */
4862static DECLCALLBACK(void) pcnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4863{
4864 RT_NOREF(fFlags);
4865 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4866 Log(("#%d pcnetDetach:\n", PCNET_INST_NR));
4867
4868 AssertLogRelReturnVoid(iLUN == 0);
4869
4870 PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4871
4872 /** @todo r=pritesh still need to check if i missed
4873 * to clean something in this function
4874 */
4875
4876 /*
4877 * Zero some important members.
4878 */
4879 pThis->pDrvBase = NULL;
4880 pThis->pDrvR3 = NULL;
4881 pThis->pDrvR0 = NIL_RTR0PTR;
4882 pThis->pDrvRC = NIL_RTRCPTR;
4883
4884 PDMCritSectLeave(&pThis->CritSect);
4885}
4886
4887
4888/**
4889 * @interface_method_impl{PDMDEVREG,pfnAttach}
4890 * One port on the network card has been connected to a network.
4891 */
4892static DECLCALLBACK(int) pcnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4893{
4894 RT_NOREF(fFlags);
4895 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4896 LogFlow(("#%d pcnetAttach:\n", PCNET_INST_NR));
4897
4898 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
4899
4900 PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4901
4902 /*
4903 * Attach the driver.
4904 */
4905 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Network Port");
4906 if (RT_SUCCESS(rc))
4907 {
4908 if (rc == VINF_NAT_DNS)
4909 {
4910#ifdef RT_OS_LINUX
4911 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
4912 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"));
4913#else
4914 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
4915 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"));
4916#endif
4917 }
4918 pThis->pDrvR3 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
4919 AssertMsgStmt(pThis->pDrvR3, ("Failed to obtain the PDMINETWORKUP interface!\n"),
4920 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
4921 pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
4922 pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
4923 }
4924 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
4925 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
4926 {
4927 /* This should never happen because this function is not called
4928 * if there is no driver to attach! */
4929 Log(("#%d No attached driver!\n", PCNET_INST_NR));
4930 }
4931
4932 /*
4933 * Temporary set the link down if it was up so that the guest
4934 * will know that we have change the configuration of the
4935 * network card
4936 */
4937 if (RT_SUCCESS(rc))
4938 pcnetTempLinkDown(pThis);
4939
4940 PDMCritSectLeave(&pThis->CritSect);
4941 return rc;
4942
4943}
4944
4945
4946/**
4947 * @interface_method_impl{PDMDEVREG,pfnSuspend}
4948 */
4949static DECLCALLBACK(void) pcnetSuspend(PPDMDEVINS pDevIns)
4950{
4951 /* Poke thread waiting for buffer space. */
4952 pcnetWakeupReceive(pDevIns);
4953}
4954
4955
4956/**
4957 * @interface_method_impl{PDMDEVREG,pfnReset}
4958 */
4959static DECLCALLBACK(void) pcnetReset(PPDMDEVINS pDevIns)
4960{
4961 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4962 if (pThis->fLinkTempDown)
4963 {
4964 pThis->cLinkDownReported = 0x10000;
4965 TMTimerStop(pThis->pTimerRestore);
4966 pcnetTimerRestore(pDevIns, pThis->pTimerRestore, pThis);
4967 }
4968
4969 /** @todo How to flush the queues? */
4970 pcnetR3HardReset(pThis);
4971}
4972
4973
4974/**
4975 * @interface_method_impl{PDMDEVREG,pfnRelocate}
4976 */
4977static DECLCALLBACK(void) pcnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4978{
4979 RT_NOREF(offDelta);
4980 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
4981 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
4982 pThis->pXmitQueueRC = PDMQueueRCPtr(pThis->pXmitQueueR3);
4983 pThis->pCanRxQueueRC = PDMQueueRCPtr(pThis->pCanRxQueueR3);
4984#ifdef PCNET_NO_POLLING
4985 pThis->pfnEMInterpretInstructionRC += offDelta;
4986#else
4987 pThis->pTimerPollRC = TMTimerRCPtr(pThis->pTimerPollR3);
4988#endif
4989 if (pThis->uDevType == DEV_AM79C973)
4990 pThis->pTimerSoftIntRC = TMTimerRCPtr(pThis->pTimerSoftIntR3);
4991}
4992
4993
4994/**
4995 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4996 */
4997static DECLCALLBACK(int) pcnetDestruct(PPDMDEVINS pDevIns)
4998{
4999 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5000 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
5001
5002 if (PDMCritSectIsInitialized(&pThis->CritSect))
5003 {
5004 RTSemEventSignal(pThis->hEventOutOfRxSpace);
5005 RTSemEventDestroy(pThis->hEventOutOfRxSpace);
5006 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
5007 PDMR3CritSectDelete(&pThis->CritSect);
5008 }
5009 return VINF_SUCCESS;
5010}
5011
5012
5013/**
5014 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5015 */
5016static DECLCALLBACK(int) pcnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5017{
5018 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5019 PPCNETSTATE pThis = PDMINS_2_DATA(pDevIns, PPCNETSTATE);
5020 PPDMIBASE pBase;
5021 char szTmp[128];
5022 int rc;
5023
5024 Assert(RT_ELEMENTS(pThis->aBCR) == BCR_MAX_RAP);
5025 Assert(RT_ELEMENTS(pThis->aMII) == MII_MAX_REG);
5026 Assert(sizeof(pThis->abLoopBuf) == RT_ALIGN_Z(sizeof(pThis->abLoopBuf), 16));
5027
5028 /*
5029 * Init what's required to make the destructor safe.
5030 */
5031 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
5032
5033 /*
5034 * Validate configuration.
5035 */
5036 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"))
5037 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5038 N_("Invalid configuration for pcnet device"));
5039
5040 /*
5041 * Read the configuration.
5042 */
5043 rc = CFGMR3QueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
5044 if (RT_FAILURE(rc))
5045 return PDMDEV_SET_ERROR(pDevIns, rc,
5046 N_("Configuration error: Failed to get the \"MAC\" value"));
5047 rc = CFGMR3QueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
5048 if (RT_FAILURE(rc))
5049 return PDMDEV_SET_ERROR(pDevIns, rc,
5050 N_("Configuration error: Failed to get the \"CableConnected\" value"));
5051
5052 /*
5053 * Determine the model.
5054 */
5055 char szChipType[16];
5056 rc = CFGMR3QueryStringDef(pCfg, "ChipType", &szChipType[0], sizeof(szChipType), "Am79C970A");
5057 if (RT_FAILURE(rc))
5058 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5059 N_("Configuration error: Querying \"ChipType\" as string failed"));
5060
5061 if (!strcmp(szChipType, "Am79C970A"))
5062 pThis->uDevType = DEV_AM79C970A; /* 10 Mbps PCnet-PCI II. */
5063 else if (!strcmp(szChipType, "Am79C973"))
5064 pThis->uDevType = DEV_AM79C973; /* 10/100 Mbps PCnet-FAST III. */
5065 else if (!strcmp(szChipType, "Am79C960"))
5066 pThis->uDevType = DEV_AM79C960; /* 10 Mbps PCnet-ISA, NE2100/Am2100 compatible. */
5067 else if (!strcmp(szChipType, "Am79C960_EB"))
5068 {
5069 pThis->uDevType = DEV_AM79C960_EB; /* 10 Mbps PCnet-ISA, Racal InterLink NI6510 EtherBlaster compatible. */
5070 /* NI6510 drivers (at least Racal's and Linux) require the OUI to be InterLan's (Racal-Datacom).
5071 * Refuse loading if OUI doesn't match, because otherwise drivers won't load in the guest.
5072 */
5073 if (memcmp(&pThis->MacConfigured, "\x02\x07\x01", 3))
5074 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5075 N_("Configuration error: MAC address OUI for EtherBlaster must be 02 07 01"));
5076 }
5077 else
5078 {
5079 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
5080 N_("Configuration error: The \"ChipType\" value \"%s\" is unsupported"),
5081 szChipType);
5082 }
5083
5084
5085 /*
5086 * Process the old model configuration. If present, it must take precedence for saved state compatibility.
5087 */
5088 bool fAm79C973;
5089 rc = CFGMR3QueryBoolDef(pCfg, "Am79C973", &fAm79C973, false);
5090 if (RT_FAILURE(rc))
5091 return PDMDEV_SET_ERROR(pDevIns, rc,
5092 N_("Configuration error: Failed to get the \"Am79C973\" value"));
5093 if (fAm79C973)
5094 pThis->uDevType = DEV_AM79C973;
5095
5096 /*
5097 * Process ISA configuration options. The defaults are chosen to be NE2100/Am2100 compatible.
5098 */
5099 rc = CFGMR3QueryPortDef(pCfg, "Port", &pThis->IOPortBase, 0x300);
5100 if (RT_FAILURE(rc))
5101 return PDMDEV_SET_ERROR(pDevIns, rc,
5102 N_("Configuration error: Failed to get the \"Port\" value"));
5103
5104 rc = CFGMR3QueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, 3);
5105 if (RT_FAILURE(rc))
5106 return PDMDEV_SET_ERROR(pDevIns, rc,
5107 N_("Configuration error: Failed to get the \"IRQ\" value"));
5108
5109 rc = CFGMR3QueryU32Def(pCfg, "LineSpeed", &pThis->u32LinkSpeed, 1000000); /* 1GBit/s (in kbps units)*/
5110 if (RT_FAILURE(rc))
5111 return PDMDEV_SET_ERROR(pDevIns, rc,
5112 N_("Configuration error: Failed to get the \"LineSpeed\" value"));
5113
5114#ifdef PCNET_GC_ENABLED
5115 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
5116 if (RT_FAILURE(rc))
5117 return PDMDEV_SET_ERROR(pDevIns, rc,
5118 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
5119
5120 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
5121 if (RT_FAILURE(rc))
5122 return PDMDEV_SET_ERROR(pDevIns, rc,
5123 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
5124
5125#else /* !PCNET_GC_ENABLED */
5126 pThis->fGCEnabled = false;
5127 pThis->fR0Enabled = false;
5128#endif /* !PCNET_GC_ENABLED */
5129
5130 rc = CFGMR3QueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
5131 if (RT_FAILURE(rc))
5132 return PDMDEV_SET_ERROR(pDevIns, rc,
5133 N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
5134 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
5135 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
5136 {
5137 LogRel(("PCnet#%d WARNING! Link up delay is set to %u seconds!\n",
5138 iInstance, pThis->cMsLinkUpDelay / 1000));
5139 }
5140 Log(("#%d Link up delay is set to %u seconds\n",
5141 iInstance, pThis->cMsLinkUpDelay / 1000));
5142
5143
5144 /*
5145 * Initialize data (most of it anyway).
5146 */
5147 pThis->pDevInsR3 = pDevIns;
5148 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5149 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5150 pThis->Led.u32Magic = PDMLED_MAGIC;
5151 /* IBase */
5152 pThis->IBase.pfnQueryInterface = pcnetQueryInterface;
5153 /* INeworkPort */
5154 pThis->INetworkDown.pfnWaitReceiveAvail = pcnetNetworkDown_WaitReceiveAvail;
5155 pThis->INetworkDown.pfnReceive = pcnetNetworkDown_Receive;
5156 pThis->INetworkDown.pfnXmitPending = pcnetNetworkDown_XmitPending;
5157 /* INetworkConfig */
5158 pThis->INetworkConfig.pfnGetMac = pcnetGetMac;
5159 pThis->INetworkConfig.pfnGetLinkState = pcnetGetLinkState;
5160 pThis->INetworkConfig.pfnSetLinkState = pcnetSetLinkState;
5161 /* ILeds */
5162 pThis->ILeds.pfnQueryStatusLed = pcnetQueryStatusLed;
5163
5164 /* PCI Device */
5165 PCIDevSetVendorId(&pThis->PciDev, 0x1022);
5166 PCIDevSetDeviceId(&pThis->PciDev, 0x2000);
5167 pThis->PciDev.abConfig[0x04] = 0x07; /* command */
5168 pThis->PciDev.abConfig[0x05] = 0x00;
5169 pThis->PciDev.abConfig[0x06] = 0x80; /* status */
5170 pThis->PciDev.abConfig[0x07] = 0x02;
5171 pThis->PciDev.abConfig[0x08] = pThis->uDevType == DEV_AM79C973 ? 0x40 : 0x10; /* revision */
5172 pThis->PciDev.abConfig[0x09] = 0x00;
5173 pThis->PciDev.abConfig[0x0a] = 0x00; /* ethernet network controller */
5174 pThis->PciDev.abConfig[0x0b] = 0x02;
5175 pThis->PciDev.abConfig[0x0e] = 0x00; /* header_type */
5176
5177 pThis->PciDev.abConfig[0x10] = 0x01; /* IO Base */
5178 pThis->PciDev.abConfig[0x11] = 0x00;
5179 pThis->PciDev.abConfig[0x12] = 0x00;
5180 pThis->PciDev.abConfig[0x13] = 0x00;
5181 pThis->PciDev.abConfig[0x14] = 0x00; /* MMIO Base */
5182 pThis->PciDev.abConfig[0x15] = 0x00;
5183 pThis->PciDev.abConfig[0x16] = 0x00;
5184 pThis->PciDev.abConfig[0x17] = 0x00;
5185
5186 /* subsystem and subvendor IDs */
5187 pThis->PciDev.abConfig[0x2c] = 0x22; /* subsystem vendor id */
5188 pThis->PciDev.abConfig[0x2d] = 0x10;
5189 pThis->PciDev.abConfig[0x2e] = 0x00; /* subsystem id */
5190 pThis->PciDev.abConfig[0x2f] = 0x20;
5191 pThis->PciDev.abConfig[0x3d] = 1; /* interrupt pin 0 */
5192 pThis->PciDev.abConfig[0x3e] = 0x06;
5193 pThis->PciDev.abConfig[0x3f] = 0xff;
5194
5195 /*
5196 * We use our own critical section (historical reasons).
5197 */
5198 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PCnet#%u", iInstance);
5199 AssertRCReturn(rc, rc);
5200 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5201 AssertRCReturn(rc, rc);
5202
5203 rc = RTSemEventCreate(&pThis->hEventOutOfRxSpace);
5204 AssertRCReturn(rc, rc);
5205
5206 /*
5207 * Register the PCI device, its I/O regions, the timer and the saved state item.
5208 */
5209 if (PCNET_IS_PCI(pThis))
5210 {
5211 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
5212 if (RT_FAILURE(rc))
5213 return rc;
5214 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, PCNET_IOPORT_SIZE, PCI_ADDRESS_SPACE_IO, pcnetIOPortMap);
5215 if (RT_FAILURE(rc))
5216 return rc;
5217 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, PCNET_PNPMMIO_SIZE, PCI_ADDRESS_SPACE_MEM, pcnetMMIOMap);
5218 if (RT_FAILURE(rc))
5219 return rc;
5220 }
5221
5222 /*
5223 * Register ISA I/O ranges for PCnet-ISA.
5224 */
5225 if (PCNET_IS_ISA(pThis))
5226 {
5227 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 0x10, 0, pcnetIOPortAPromWrite,
5228 pcnetIOPortAPromRead, NULL, NULL, "PCnet APROM");
5229 if (RT_FAILURE(rc))
5230 return rc;
5231 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase + 0x10, 0x10, 0, pcnetIOPortWrite,
5232 pcnetIOPortRead, NULL, NULL, "PCnet");
5233 if (RT_FAILURE(rc))
5234 return rc;
5235
5236 if (pThis->fGCEnabled)
5237 {
5238 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 0x10, 0, "pcnetIOPortAPromWrite",
5239 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
5240 if (RT_FAILURE(rc))
5241 return rc;
5242 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase + 0x10, 0x10, 0, "pcnetIOPortWrite",
5243 "pcnetIOPortRead", NULL, NULL, "PCnet");
5244 if (RT_FAILURE(rc))
5245 return rc;
5246 }
5247 if (pThis->fR0Enabled)
5248 {
5249 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 0x10, 0, "pcnetIOPortAPromWrite",
5250 "pcnetIOPortAPromRead", NULL, NULL, "PCnet APROM");
5251 if (RT_FAILURE(rc))
5252 return rc;
5253 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase + 0x10, 0x10, 0, "pcnetIOPortWrite",
5254 "pcnetIOPortRead", NULL, NULL, "PCnet");
5255 if (RT_FAILURE(rc))
5256 return rc;
5257 }
5258
5259 }
5260
5261
5262#ifdef PCNET_NO_POLLING
5263 /*
5264 * Resolve the R0 and RC handlers.
5265 */
5266 rc = PDMR3LdrGetSymbolR0Lazy(PDMDevHlpGetVM(pDevIns), NULL, NULL, "EMInterpretInstruction", &pThis->pfnEMInterpretInstructionR0);
5267 if (RT_SUCCESS(rc))
5268 rc = PDMR3LdrGetSymbolRCLazy(PDMDevHlpGetVM(pDevIns), NULL, NULL, "EMInterpretInstruction", &pThis->pfnEMInterpretInstructionRC);
5269 AssertLogRelMsgRCReturn(rc, ("PDMR3LdrGetSymbolRCLazy(EMInterpretInstruction) -> %Rrc\n", rc), rc);
5270
5271 rc = PGMR3HandlerPhysicalTypeRegister(PDMDevHlpGetVM(pDevIns), PGMPHYSHANDLERKIND_WRITE,
5272 pcnetHandleRingWrite,
5273 g_DevicePCNet.szR0Mod, NULL, "pcnetHandleRingWritePf",
5274 g_DevicePCNet.szRCMod, NULL, "pcnetHandleRingWritePf",
5275 "PCnet ring write access handler",
5276 &pThis->hNoPollingHandlerType);
5277 AssertRCReturn(rc, rc);
5278
5279#else
5280 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimer, pThis,
5281 TMTIMER_FLAGS_NO_CRIT_SECT, "PCnet Poll Timer", &pThis->pTimerPollR3);
5282 if (RT_FAILURE(rc))
5283 return rc;
5284 pThis->pTimerPollR0 = TMTimerR0Ptr(pThis->pTimerPollR3);
5285 pThis->pTimerPollRC = TMTimerRCPtr(pThis->pTimerPollR3);
5286 TMR3TimerSetCritSect(pThis->pTimerPollR3, &pThis->CritSect);
5287#endif
5288 if (pThis->uDevType == DEV_AM79C973)
5289 {
5290 /* Software Interrupt timer */
5291 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimerSoftInt, pThis, /** @todo r=bird: the locking here looks bogus now with SMP... */
5292 TMTIMER_FLAGS_NO_CRIT_SECT, "PCnet SoftInt Timer", &pThis->pTimerSoftIntR3);
5293 if (RT_FAILURE(rc))
5294 return rc;
5295 pThis->pTimerSoftIntR0 = TMTimerR0Ptr(pThis->pTimerSoftIntR3);
5296 pThis->pTimerSoftIntRC = TMTimerRCPtr(pThis->pTimerSoftIntR3);
5297 TMR3TimerSetCritSect(pThis->pTimerSoftIntR3, &pThis->CritSect);
5298 }
5299 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimerRestore, pThis,
5300 TMTIMER_FLAGS_NO_CRIT_SECT, "PCnet Restore Timer", &pThis->pTimerRestore);
5301 if (RT_FAILURE(rc))
5302 return rc;
5303
5304 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCNET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
5305 NULL, pcnetLiveExec, NULL,
5306 pcnetSavePrep, pcnetSaveExec, NULL,
5307 pcnetLoadPrep, pcnetLoadExec, pcnetLoadDone);
5308 if (RT_FAILURE(rc))
5309 return rc;
5310
5311 /*
5312 * Create the transmit queue.
5313 */
5314 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
5315 pcnetXmitQueueConsumer, true, "PCnet-Xmit", &pThis->pXmitQueueR3);
5316 if (RT_FAILURE(rc))
5317 return rc;
5318 pThis->pXmitQueueR0 = PDMQueueR0Ptr(pThis->pXmitQueueR3);
5319 pThis->pXmitQueueRC = PDMQueueRCPtr(pThis->pXmitQueueR3);
5320
5321 /*
5322 * Create the RX notifier signaller.
5323 */
5324 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
5325 pcnetCanRxQueueConsumer, true, "PCnet-Rcv", &pThis->pCanRxQueueR3);
5326 if (RT_FAILURE(rc))
5327 return rc;
5328 pThis->pCanRxQueueR0 = PDMQueueR0Ptr(pThis->pCanRxQueueR3);
5329 pThis->pCanRxQueueRC = PDMQueueRCPtr(pThis->pCanRxQueueR3);
5330
5331 /*
5332 * Register the info item.
5333 */
5334 RTStrPrintf(szTmp, sizeof(szTmp), "pcnet%d", pDevIns->iInstance);
5335 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "PCNET info.", pcnetInfo);
5336
5337 /*
5338 * Attach status driver (optional).
5339 */
5340 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
5341 if (RT_SUCCESS(rc))
5342 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5343 else if ( rc != VERR_PDM_NO_ATTACHED_DRIVER
5344 && rc != VERR_PDM_CFG_MISSING_DRIVER_NAME)
5345 {
5346 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5347 return rc;
5348 }
5349
5350 /*
5351 * Attach driver.
5352 */
5353 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Network Port");
5354 if (RT_SUCCESS(rc))
5355 {
5356 if (rc == VINF_NAT_DNS)
5357 {
5358#ifdef RT_OS_LINUX
5359 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
5360 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"));
5361#else
5362 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
5363 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"));
5364#endif
5365 }
5366 pThis->pDrvR3 = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
5367 AssertMsgReturn(pThis->pDrvR3, ("Failed to obtain the PDMINETWORKUP interface!\n"),
5368 VERR_PDM_MISSING_INTERFACE_BELOW);
5369 pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
5370 pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
5371 }
5372 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
5373 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
5374 {
5375 /* No error! */
5376 Log(("No attached driver!\n"));
5377 }
5378 else
5379 return rc;
5380
5381 /*
5382 * Reset the device state. (Do after attaching.)
5383 */
5384 pcnetR3HardReset(pThis);
5385
5386 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Public/Net/PCnet%u/BytesReceived", iInstance);
5387 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Public/Net/PCnet%u/BytesTransmitted", iInstance);
5388
5389 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/PCnet%d/ReceiveBytes", iInstance);
5390 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/PCnet%d/TransmitBytes", iInstance);
5391
5392#ifdef VBOX_WITH_STATISTICS
5393 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in RZ", "/Devices/PCnet%d/MMIO/ReadRZ", iInstance);
5394 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in R3", "/Devices/PCnet%d/MMIO/ReadR3", iInstance);
5395 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in RZ", "/Devices/PCnet%d/MMIO/WriteRZ", iInstance);
5396 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMMIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in R3", "/Devices/PCnet%d/MMIO/WriteR3", iInstance);
5397 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatAPROMRead, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM reads", "/Devices/PCnet%d/IO/APROMRead", iInstance);
5398 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatAPROMWrite, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM writes", "/Devices/PCnet%d/IO/APROMWrite", iInstance);
5399 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ", "/Devices/PCnet%d/IO/ReadRZ", iInstance);
5400 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3", "/Devices/PCnet%d/IO/ReadR3", iInstance);
5401 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ", "/Devices/PCnet%d/IO/WriteRZ", iInstance);
5402 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3", "/Devices/PCnet%d/IO/WriteR3", iInstance);
5403 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling Timer", "/Devices/PCnet%d/Timer", iInstance);
5404 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/PCnet%d/Receive", iInstance);
5405 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/PCnet%d/RxOverflow", iInstance);
5406 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Nr of RX overflow wakeups", "/Devices/PCnet%d/RxOverflowWakeup", iInstance);
5407 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitCase1, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Single descriptor transmit", "/Devices/PCnet%d/Transmit/Case1", iInstance);
5408 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitCase2, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Multi descriptor transmit", "/Devices/PCnet%d/Transmit/Case2", iInstance);
5409 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ", "/Devices/PCnet%d/Transmit/TotalRZ", iInstance);
5410 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3", "/Devices/PCnet%d/Transmit/TotalR3", iInstance);
5411 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in RZ","/Devices/PCnet%d/Transmit/SendRZ", iInstance);
5412 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in R3","/Devices/PCnet%d/Transmit/SendR3", iInstance);
5413 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);
5414 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);
5415 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTdtePollRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in RZ", "/Devices/PCnet%d/TdtePollRZ", iInstance);
5416 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTdtePollR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in R3", "/Devices/PCnet%d/TdtePollR3", iInstance);
5417 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRdtePollRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in RZ", "/Devices/PCnet%d/RdtePollRZ", iInstance);
5418 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRdtePollR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in R3", "/Devices/PCnet%d/RdtePollR3", iInstance);
5419
5420 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTmdStoreRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in RZ", "/Devices/PCnet%d/TmdStoreRZ", iInstance);
5421 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTmdStoreR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in R3", "/Devices/PCnet%d/TmdStoreR3", iInstance);
5422
5423 unsigned i;
5424 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitFlush) - 1; i++)
5425 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitFlushIrq/%d", iInstance, i + 1);
5426 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitFlushIrq/%d+", iInstance, i + 1);
5427
5428 for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitChainCounts) - 1; i++)
5429 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitChainCounts/%d", iInstance, i + 1);
5430 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/XmitChainCounts/%d+", iInstance, i + 1);
5431
5432 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatXmitSkipCurrent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "", "/Devices/PCnet%d/Xmit/Skipped", iInstance);
5433
5434 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks", "/Devices/PCnet%d/UpdateIRQ", iInstance);
5435 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatPollTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling poll timer", "/Devices/PCnet%d/PollTimer", iInstance);
5436 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatMIIReads, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of MII reads", "/Devices/PCnet%d/MIIReads", iInstance);
5437# ifdef PCNET_NO_POLLING
5438 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRCVRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of receive ring writes", "/Devices/PCnet%d/Ring/RCVWrites", iInstance);
5439 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTXRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of transmit ring writes", "/Devices/PCnet%d/Ring/TXWrites", iInstance);
5440 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCnet%d/Ring/R3/Writes", iInstance);
5441 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCnet%d/Ring/R0/Writes", iInstance);
5442 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteRC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCnet%d/Ring/RC/Writes", iInstance);
5443 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteFailedR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCnet%d/Ring/R3/Failed", iInstance);
5444 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteFailedR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCnet%d/Ring/R0/Failed", iInstance);
5445 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteFailedRC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCnet%d/Ring/RC/Failed", iInstance);
5446 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteOutsideR3, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring","/Devices/PCnet%d/Ring/R3/Outside", iInstance);
5447 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteOutsideR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring","/Devices/PCnet%d/Ring/R0/Outside", iInstance);
5448 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRingWriteOutsideRC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring","/Devices/PCnet%d/Ring/RC/Outside", iInstance);
5449# endif /* PCNET_NO_POLLING */
5450#endif /* VBOX_WITH_STATISTICS */
5451
5452 return VINF_SUCCESS;
5453}
5454
5455
5456/**
5457 * The device registration structure.
5458 */
5459const PDMDEVREG g_DevicePCNet =
5460{
5461 /* u32Version */
5462 PDM_DEVREG_VERSION,
5463 /* szName */
5464 "pcnet",
5465 /* szRCMod */
5466#ifdef PCNET_GC_ENABLED
5467 "VBoxDDRC.rc",
5468 "VBoxDDR0.r0",
5469#else
5470 "",
5471 "",
5472#endif
5473 /* pszDescription */
5474 "AMD PCnet Ethernet controller.\n",
5475 /* fFlags */
5476#ifdef PCNET_GC_ENABLED
5477 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
5478#else
5479 PDM_DEVREG_FLAGS_DEFAULT_BITS,
5480#endif
5481 /* fClass */
5482 PDM_DEVREG_CLASS_NETWORK,
5483 /* cMaxInstances */
5484 ~0U,
5485 /* cbInstance */
5486 sizeof(PCNETSTATE),
5487 /* pfnConstruct */
5488 pcnetConstruct,
5489 /* pfnDestruct */
5490 pcnetDestruct,
5491 /* pfnRelocate */
5492 pcnetRelocate,
5493 /* pfnMemSetup */
5494 NULL,
5495 /* pfnPowerOn */
5496 NULL,
5497 /* pfnReset */
5498 pcnetReset,
5499 /* pfnSuspend */
5500 pcnetSuspend,
5501 /* pfnResume */
5502 NULL,
5503 /* pfnAttach */
5504 pcnetAttach,
5505 /* pfnDetach */
5506 pcnetDetach,
5507 /* pfnQueryInterface. */
5508 NULL,
5509 /* pfnInitComplete. */
5510 NULL,
5511 /* pfnPowerOff. */
5512 pcnetPowerOff,
5513 /* pfnSoftReset */
5514 NULL,
5515 /* u32VersionEnd */
5516 PDM_DEVREG_VERSION
5517};
5518
5519#endif /* IN_RING3 */
5520#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5521
Note: See TracBrowser for help on using the repository browser.

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