VirtualBox

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

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

Attempt to fix breakage on 64 bits linux host

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