VirtualBox

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

Last change on this file since 94993 was 93591, checked in by vboxsync, 3 years ago

Comment.

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