VirtualBox

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

Last change on this file since 82141 was 82141, checked in by vboxsync, 5 years ago

DevPCNet: Converted MMIO handlers. [fixes] bugref:9218

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