VirtualBox

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

最後變更 在這個檔案從63015是 62902,由 vboxsync 提交於 9 年 前

Devices: warnings

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

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