VirtualBox

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

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

Poke waiting thread on suspend and power off to make PDM happy.

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