VirtualBox

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

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

Added a flag to the MMIO2 registration and did some other MMIO2 related cleanups.

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