VirtualBox

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

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

Use TMCLOCK_VIRTUAL_SYNC for PCnet polling/softint timers.

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