VirtualBox

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

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

pcnet: logging

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