VirtualBox

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

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

PCnet: Do not access the 'user' dword of RX/TX descriptors.

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