VirtualBox

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

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

downgrade log message

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

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