VirtualBox

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

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

pcnet: cosmetics

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

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