VirtualBox

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

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

PCnet: Slightly tweaked to add the option of emulating PCnet-ISA, not requiring PCI.

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