VirtualBox

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

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

PCNet: Create private PCI memory for shared RX/TX descriptors and buffers.

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

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