VirtualBox

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

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

pcnet: a little bit less logging noise, code cosmetics

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