VirtualBox

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

Last change on this file since 8599 was 8599, checked in by vboxsync, 17 years ago

PCNet: fix for broken hostif for with certain guests

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

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