VirtualBox

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

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

Experiment with send buffering. (disabled)

  • 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 7266 2008-03-04 10:00:24Z 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 (!BCR_SWSTYLE(pData))
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 (!BCR_SWSTYLE(pData))
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 (!BCR_SWSTYLE(pData))
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 (!BCR_SWSTYLE(pData))
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,
1887 &pData->abSendBuf[0],
1888 cbFrame);
1889 pData->SendFrame.pvBuf = pData->abSendBuf;
1890 pData->SendFrame.cb = cbFrame;
1891}
1892
1893
1894/**
1895 * Reads more into the current frame.
1896 */
1897DECLINLINE(void) pcnetXmitReadMore(PCNetState *pData, RTGCPHYS32 GCPhysFrame, const unsigned cbFrame)
1898{
1899 Assert(pData->SendFrame.cb + cbFrame < sizeof(pData->abSendBuf));
1900 PDMDevHlpPhysRead(pData->CTXSUFF(pDevIns), GCPhysFrame,
1901 &pData->abSendBuf[pData->SendFrame.cb],
1902 cbFrame);
1903 pData->SendFrame.cb += cbFrame;
1904}
1905
1906
1907/**
1908 * Completes the current frame.
1909 * If we've reached the maxium number of frames, they will be flushed.
1910 */
1911DECLINLINE(int) pcnetXmitCompleteFrame(PCNetState *pData)
1912{
1913#ifdef PCNET_QUEUE_SEND_PACKETS
1914 Assert(!pData->cbXmitRingBuffer[pData->ulXmitRingBufProd]);
1915 memcpy(pData->pXmitRingBuffer[pData->ulXmitRingBufProd], pData->SendFrame.pvBuf, pData->SendFrame.cb);
1916 pData->cbXmitRingBuffer[pData->ulXmitRingBufProd] = (uint16_t)pData->SendFrame.cb;
1917 pData->ulXmitRingBufProd = (pData->ulXmitRingBufProd+1) & PCNET_MAX_XMIT_SLOTS_MASK;
1918
1919 int rc = RTSemEventSignal(pData->hSendEventSem);
1920 AssertRC(rc);
1921
1922 return VINF_SUCCESS;
1923#else
1924 /* Don't hold the critical section while transmitting data. */
1925 /** @note also avoids deadlocks with NAT as it can call us right back. */
1926 PDMCritSectLeave(&pData->CritSect);
1927
1928 STAM_PROFILE_ADV_START(&pData->StatTransmitSend, a);
1929 if (pData->SendFrame.cb > 70) /* unqualified guess */
1930 pData->Led.Asserted.s.fWriting = pData->Led.Actual.s.fWriting = 1;
1931
1932 pData->pDrv->pfnSend(pData->pDrv, pData->SendFrame.pvBuf, pData->SendFrame.cb);
1933 STAM_REL_COUNTER_ADD(&pData->StatTransmitBytes, pData->SendFrame.cb);
1934 pData->Led.Actual.s.fWriting = 0;
1935 STAM_PROFILE_ADV_STOP(&pData->StatTransmitSend, a);
1936
1937 return PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
1938#endif
1939}
1940
1941
1942/**
1943 * Fails a TMD with a link down error.
1944 */
1945static void pcnetXmitFailTMDLinkDown(PCNetState *pData, TMD *pTmd)
1946{
1947 /* make carrier error - hope this is correct. */
1948 pData->cLinkDownReported++;
1949 pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
1950 pData->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
1951 pData->Led.Asserted.s.fError = pData->Led.Actual.s.fError = 1;
1952 Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
1953 PCNETSTATE_2_DEVINS(pData)->iInstance, pData->aBCR[BCR_SWS]));
1954}
1955
1956/**
1957 * Fails a TMD with a generic error.
1958 */
1959static void pcnetXmitFailTMDGeneric(PCNetState *pData, TMD *pTmd)
1960{
1961 /* make carrier error - hope this is correct. */
1962 pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
1963 pData->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
1964 pData->Led.Asserted.s.fError = pData->Led.Actual.s.fError = 1;
1965 Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
1966 PCNETSTATE_2_DEVINS(pData)->iInstance, pData->aBCR[BCR_SWS]));
1967}
1968
1969
1970/**
1971 * Transmit a loopback frame.
1972 */
1973DECLINLINE(void) pcnetXmitLoopbackFrame(PCNetState *pData)
1974{
1975 pData->Led.Asserted.s.fReading = pData->Led.Actual.s.fReading = 1;
1976 if (HOST_IS_OWNER(CSR_CRST(pData)))
1977 pcnetRdtePoll(pData);
1978
1979 Assert(pData->SendFrame.pvBuf);
1980 pcnetReceiveNoSync(pData, (const uint8_t *)pData->SendFrame.pvBuf, pData->SendFrame.cb);
1981 pcnetXmitScrapFrame(pData);
1982 pData->Led.Actual.s.fReading = 0;
1983}
1984
1985/**
1986 * Flushes queued frames.
1987 */
1988DECLINLINE(void) pcnetXmitFlushFrames(PCNetState *pData)
1989{
1990 pcnetXmitQueueConsumer(CTXSUFF(pData->pDevIns), NULL);
1991}
1992
1993#endif /* IN_RING3 */
1994
1995
1996
1997/**
1998 * Try to transmit frames
1999 */
2000static void pcnetTransmit(PCNetState *pData)
2001{
2002 if (RT_UNLIKELY(!CSR_TXON(pData)))
2003 {
2004 pData->aCSR[0] &= ~0x0008; /* Clear TDMD */
2005 return;
2006 }
2007
2008 /*
2009 * Check the current transmit descriptors.
2010 */
2011 TMD tmd;
2012 if (!pcnetTdtePoll(pData, &tmd))
2013 return;
2014
2015 /* Update TDMD, TXSTRT and TINT. */
2016 pData->aCSR[0] &= ~0x0008; /* clear TDMD */
2017
2018 /*
2019 * If we're in Ring-3 we should flush the queue now, in GC/R0 we'll queue a flush job.
2020 */
2021#ifdef IN_RING3
2022 pcnetXmitFlushFrames(pData);
2023#else
2024# if 1
2025 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(CTXSUFF(pData->pXmitQueue));
2026 if (RT_UNLIKELY(pItem))
2027 PDMQueueInsert(CTXSUFF(pData->pXmitQueue), pItem);
2028# else
2029 if (ASMAtomicIncU32(&pData->cPendingSends) < 16)
2030 {
2031 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(CTXSUFF(pData->pXmitQueue));
2032 if (RT_UNLIKELY(pItem))
2033 PDMQueueInsert(CTXSUFF(pData->pXmitQueue), pItem);
2034 }
2035 else
2036 PDMQueueFlush(CTXSUFF(pData->pXmitQueue));
2037# endif
2038#endif
2039}
2040
2041#ifdef IN_RING3
2042
2043/**
2044 * Try to transmit frames
2045 */
2046#ifdef PCNET_QUEUE_SEND_PACKETS
2047static int pcnetAsyncTransmit(PCNetState *pData)
2048{
2049 Assert(PDMCritSectIsOwner(&pData->CritSect));
2050
2051 while (pData->cbXmitRingBuffer[pData->ulXmitRingBufCons])
2052 {
2053 /* Don't hold the critical section while transmitting data. */
2054 /** @note also avoids deadlocks with NAT as it can call us right back. */
2055 PDMCritSectLeave(&pData->CritSect);
2056
2057 STAM_PROFILE_ADV_START(&pData->StatTransmitSend, a);
2058 if (pData->SendFrame.cb > 70) /* unqualified guess */
2059 pData->Led.Asserted.s.fWriting = pData->Led.Actual.s.fWriting = 1;
2060
2061 pData->pDrv->pfnSend(pData->pDrv, pData->pXmitRingBuffer[pData->ulXmitRingBufCons], pData->cbXmitRingBuffer[pData->ulXmitRingBufCons]);
2062 STAM_REL_COUNTER_ADD(&pData->StatTransmitBytes, pData->cbXmitRingBuffer[pData->ulXmitRingBufCons]);
2063 pData->Led.Actual.s.fWriting = 0;
2064 STAM_PROFILE_ADV_STOP(&pData->StatTransmitSend, a);
2065
2066 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
2067
2068 pData->cbXmitRingBuffer[pData->ulXmitRingBufCons] = 0;
2069 pData->ulXmitRingBufCons = (pData->ulXmitRingBufCons+1) & PCNET_MAX_XMIT_SLOTS_MASK;
2070 }
2071 return VINF_SUCCESS;
2072}
2073
2074static int pcnetSyncTransmit(PCNetState *pData)
2075#else
2076static int pcnetAsyncTransmit(PCNetState *pData)
2077#endif
2078{
2079 unsigned cFlushIrq = 0;
2080
2081 Assert(PDMCritSectIsOwner(&pData->CritSect));
2082
2083 if (RT_UNLIKELY(!CSR_TXON(pData)))
2084 {
2085 pData->aCSR[0] &= ~0x0008; /* Clear TDMD */
2086 return VINF_SUCCESS;
2087 }
2088
2089 /*
2090 * Iterate the transmit descriptors.
2091 */
2092 STAM_PROFILE_ADV_START(&pData->StatTransmit, a);
2093 do
2094 {
2095#ifdef VBOX_WITH_STATISTICS
2096 unsigned cBuffers = 1;
2097#endif
2098 TMD tmd;
2099 if (!pcnetTdtePoll(pData, &tmd))
2100 break;
2101
2102 /* Don't continue sending packets when the link is down. */
2103 if (RT_UNLIKELY( !pcnetIsLinkUp(pData)
2104 && pData->cLinkDownReported > PCNET_MAX_LINKDOWN_REPORTED)
2105 )
2106 break;
2107
2108#ifdef PCNET_DEBUG_TMD
2109 Log2(("#%d TMDLOAD %#010x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, PHYSADDR(pData, CSR_CXDA(pData))));
2110 PRINT_TMD(&tmd);
2111#endif
2112 pcnetXmitScrapFrame(pData);
2113
2114 /*
2115 * The typical case - a complete packet.
2116 * This can be performed with zero copy in Ring-3.
2117 */
2118 if (tmd.tmd1.stp && tmd.tmd1.enp)
2119 {
2120 const unsigned cb = 4096 - tmd.tmd1.bcnt;
2121 Log(("#%d pcnetTransmit: stp&enp: cb=%d xmtrc=%#x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, cb, CSR_XMTRC(pData)));
2122
2123 if (RT_LIKELY(pcnetIsLinkUp(pData) || CSR_LOOP(pData)))
2124 {
2125 /* From the manual: ``A zero length buffer is acceptable as
2126 * long as it is not the last buffer in a chain (STP = 0 and
2127 * ENP = 1).'' That means that the first buffer might have a
2128 * zero length if it is not the last one in the chain. */
2129 if (RT_LIKELY(cb <= 1536))
2130 {
2131 pcnetXmitRead1st(pData, PHYSADDR(pData, tmd.tmd0.tbadr), cb);
2132 if (CSR_LOOP(pData))
2133 pcnetXmitLoopbackFrame(pData);
2134 else
2135 {
2136 int rc = pcnetXmitCompleteFrame(pData);
2137 AssertRCReturn(rc, rc);
2138 }
2139 }
2140 else if (cb == 4096)
2141 {
2142 /* The Windows NT4 pcnet driver sometimes marks the first
2143 * unused descriptor as owned by us. Ignore that (by
2144 * passing it back). Do not update the ring counter in this
2145 * case (otherwise that driver becomes even more confused,
2146 * which causes transmit to stall for about 10 seconds).
2147 * This is just a workaround, not a final solution. */
2148 /* r=frank: IMHO this is the correct implementation. The
2149 * manual says: ``If the OWN bit is set and the buffer
2150 * length is 0, the OWN bit will be cleared. In the C-LANCE
2151 * the buffer length of 0 is interpreted as a 4096-byte
2152 * buffer.'' */
2153 LogRel(("PCNET: pcnetAsyncTransmit: illegal 4kb frame -> ignoring\n"));
2154 pcnetTmdStorePassHost(pData, &tmd, PHYSADDR(pData, CSR_CXDA(pData)));
2155 break;
2156 }
2157 else
2158 {
2159 /* Signal error, as this violates the Ethernet specs. */
2160 /** @todo check if the correct error is generated. */
2161 LogRel(("PCNET: pcnetAsyncTransmit: illegal 4kb frame -> signalling error\n"));
2162
2163 pcnetXmitFailTMDGeneric(pData, &tmd);
2164 }
2165 }
2166 else
2167 pcnetXmitFailTMDLinkDown(pData, &tmd);
2168
2169 /* Write back the TMD and pass it to the host (clear own bit). */
2170 pcnetTmdStorePassHost(pData, &tmd, PHYSADDR(pData, CSR_CXDA(pData)));
2171
2172 /* advance the ring counter register */
2173 if (CSR_XMTRC(pData) < 2)
2174 CSR_XMTRC(pData) = CSR_XMTRL(pData);
2175 else
2176 CSR_XMTRC(pData)--;
2177 }
2178 else if (tmd.tmd1.stp)
2179 {
2180 /*
2181 * Read TMDs until end-of-packet or tdte poll fails (underflow).
2182 */
2183 const unsigned cbMaxFrame = 1536;
2184 bool fDropFrame = false;
2185 unsigned cb = 4096 - tmd.tmd1.bcnt;
2186 pcnetXmitRead1st(pData, PHYSADDR(pData, tmd.tmd0.tbadr), cb);
2187 for (;;)
2188 {
2189 /*
2190 * Advance the ring counter register and check the next tmd.
2191 */
2192#ifdef LOG_ENABLED
2193 const uint32_t iStart = CSR_XMTRC(pData);
2194#endif
2195 const uint32_t GCPhysPrevTmd = PHYSADDR(pData, CSR_CXDA(pData));
2196 if (CSR_XMTRC(pData) < 2)
2197 CSR_XMTRC(pData) = CSR_XMTRL(pData);
2198 else
2199 CSR_XMTRC(pData)--;
2200
2201 TMD dummy;
2202 if (!pcnetTdtePoll(pData, &dummy))
2203 {
2204 /*
2205 * Underflow!
2206 */
2207 tmd.tmd2.buff = tmd.tmd2.uflo = tmd.tmd1.err = 1;
2208 pData->aCSR[0] |= 0x0200; /* set TINT */
2209 if (!CSR_DXSUFLO(pData)) /* stop on xmit underflow */
2210 pData->aCSR[0] &= ~0x0010; /* clear TXON */
2211 pcnetTmdStorePassHost(pData, &tmd, GCPhysPrevTmd);
2212 AssertMsgFailed(("pcnetTransmit: Underflow!!!\n"));
2213 break;
2214 }
2215
2216 /* release & save the previous tmd, pass it to the host */
2217 pcnetTmdStorePassHost(pData, &tmd, GCPhysPrevTmd);
2218
2219 /*
2220 * The next tdm.
2221 */
2222#ifdef VBOX_WITH_STATISTICS
2223 cBuffers++;
2224#endif
2225 pcnetTmdLoad(pData, &tmd, PHYSADDR(pData, CSR_CXDA(pData)));
2226 cb = 4096 - tmd.tmd1.bcnt;
2227 if ( pData->SendFrame.cb + cb < cbMaxFrame
2228 && !fDropFrame)
2229 pcnetXmitReadMore(pData, PHYSADDR(pData, tmd.tmd0.tbadr), cb);
2230 else
2231 {
2232 AssertMsg(fDropFrame, ("pcnetTransmit: Frame is too big!!! %d bytes\n",
2233 pData->SendFrame.cb + cb));
2234 fDropFrame = true;
2235 }
2236 if (tmd.tmd1.enp)
2237 {
2238 Log(("#%d pcnetTransmit: stp: cb=%d xmtrc=%#x-%#x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2239 pData->SendFrame.cb, iStart, CSR_XMTRC(pData)));
2240 if (pcnetIsLinkUp(pData) && !fDropFrame)
2241 {
2242 int rc = pcnetXmitCompleteFrame(pData);
2243 AssertRCReturn(rc, rc);
2244 }
2245 else if (CSR_LOOP(pData) && !fDropFrame)
2246 pcnetXmitLoopbackFrame(pData);
2247 else
2248 {
2249 if (!fDropFrame)
2250 pcnetXmitFailTMDLinkDown(pData, &tmd);
2251 pcnetXmitScrapFrame(pData);
2252 }
2253
2254 /* Write back the TMD, pass it to the host */
2255 pcnetTmdStorePassHost(pData, &tmd, PHYSADDR(pData, CSR_CXDA(pData)));
2256
2257 /* advance the ring counter register */
2258 if (CSR_XMTRC(pData) < 2)
2259 CSR_XMTRC(pData) = CSR_XMTRL(pData);
2260 else
2261 CSR_XMTRC(pData)--;
2262 break;
2263 }
2264 }
2265 }
2266 else
2267 {
2268 /*
2269 * We underflowed in a previous transfer, or the driver is giving us shit.
2270 * Simply stop the transmitting for now.
2271 */
2272 /** @todo according to the specs we're supposed to clear the own bit and move on to the next one. */
2273 Log(("#%d pcnetTransmit: guest is giving us shit!\n", PCNETSTATE_2_DEVINS(pData)->iInstance));
2274 break;
2275 }
2276 /* Update TDMD, TXSTRT and TINT. */
2277 pData->aCSR[0] &= ~0x0008; /* clear TDMD */
2278
2279 pData->aCSR[4] |= 0x0004; /* set TXSTRT */
2280 if ( !CSR_TOKINTD(pData) /* Transmit OK Interrupt Disable, no infl. on errors. */
2281 || (CSR_LTINTEN(pData) && tmd.tmd1.ltint)
2282 || tmd.tmd1.err)
2283 {
2284 cFlushIrq++;
2285 pData->aCSR[0] |= 0x0200; /* set TINT */
2286 }
2287
2288 /** @todo should we continue after an error (tmd.tmd1.err) or not? */
2289
2290 STAM_COUNTER_INC(&pData->aStatXmitChainCounts[RT_MIN(cBuffers,
2291 ELEMENTS(pData->aStatXmitChainCounts)) - 1]);
2292 } while (CSR_TXON(pData)); /* transfer on */
2293
2294 if (cFlushIrq)
2295 {
2296 STAM_COUNTER_INC(&pData->aStatXmitFlush[RT_MIN(cFlushIrq, ELEMENTS(pData->aStatXmitFlush)) - 1]);
2297 pcnetUpdateIrq(pData);
2298 }
2299
2300 STAM_PROFILE_ADV_STOP(&pData->StatTransmit, a);
2301
2302 return VINF_SUCCESS;
2303}
2304
2305
2306/**
2307 * Async I/O thread for delayed sending of packets.
2308 *
2309 * @returns VBox status code. Returning failure will naturally terminate the thread.
2310 * @param pDevIns The pcnet device instance.
2311 * @param pThread The thread.
2312 */
2313static DECLCALLBACK(int) pcnetAsyncSendThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
2314{
2315 PCNetState *pThis = PDMINS2DATA(pDevIns, PCNetState *);
2316
2317 /*
2318 * We can enter this function in two states, initializing or resuming.
2319 *
2320 * The idea about the initializing bit is that we can do per-thread
2321 * initialization while the creator thread can still pick up errors.
2322 * At present, there is nothing to init, or at least nothing that
2323 * need initing in the thread.
2324 */
2325 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
2326 return VINF_SUCCESS;
2327
2328 /*
2329 * Stay in the run-loop until we're supposed to leave the
2330 * running state. If something really bad happens, we'll
2331 * quit the loop while in the running state and return
2332 * an error status to PDM and let it terminate the thread.
2333 */
2334 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
2335 {
2336 /*
2337 * Block until we've got something to send or is supposed
2338 * to leave the running state.
2339 */
2340 int rc = RTSemEventWait(pThis->hSendEventSem, RT_INDEFINITE_WAIT);
2341 AssertRCReturn(rc, rc);
2342 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
2343 break;
2344
2345 /*
2346 * Perform async send. Mind that we might be requested to
2347 * suspended while waiting for the critical section.
2348 */
2349 rc = PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
2350 AssertReleaseRCReturn(rc, rc);
2351
2352 if (pThread->enmState == PDMTHREADSTATE_RUNNING)
2353 {
2354 rc = pcnetAsyncTransmit(pThis);
2355 AssertReleaseRC(rc);
2356 }
2357
2358 PDMCritSectLeave(&pThis->CritSect);
2359 }
2360
2361 /* The thread is being suspended or terminated. */
2362 return VINF_SUCCESS;
2363}
2364
2365
2366/**
2367 * Unblock the send thread so it can respond to a state change.
2368 *
2369 * @returns VBox status code.
2370 * @param pDevIns The pcnet device instance.
2371 * @param pThread The send thread.
2372 */
2373static DECLCALLBACK(int) pcnetAsyncSendThreadWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
2374{
2375 PCNetState *pThis = PDMINS2DATA(pDevIns, PCNetState *);
2376 return RTSemEventSignal(pThis->hSendEventSem);
2377}
2378
2379#endif /* IN_RING3 */
2380
2381/**
2382 * Poll for changes in RX and TX descriptor rings.
2383 */
2384static void pcnetPollRxTx(PCNetState *pData)
2385{
2386 if (CSR_RXON(pData))
2387 if (HOST_IS_OWNER(CSR_CRST(pData))) /* Only poll RDTEs if none available */
2388 pcnetRdtePoll(pData);
2389
2390 if (CSR_TDMD(pData) || (CSR_TXON(pData) && !CSR_DPOLL(pData)))
2391 pcnetTransmit(pData);
2392}
2393
2394
2395/**
2396 * Update the poller timer
2397 * @thread EMT,
2398 */
2399static void pcnetPollTimer(PCNetState *pData)
2400{
2401 STAM_PROFILE_ADV_START(&pData->StatPollTimer, a);
2402
2403#ifdef LOG_ENABLED
2404 TMD dummy;
2405 if (CSR_STOP(pData) || CSR_SPND(pData))
2406 Log2(("#%d pcnetPollTimer time=%#010llx CSR_STOP=%d CSR_SPND=%d\n",
2407 PCNETSTATE_2_DEVINS(pData)->iInstance, RTTimeMilliTS(), CSR_STOP(pData), CSR_SPND(pData)));
2408 else
2409 Log2(("#%d pcnetPollTimer time=%#010llx TDMD=%d TXON=%d POLL=%d TDTE=%d TDRA=%#x\n",
2410 PCNETSTATE_2_DEVINS(pData)->iInstance, RTTimeMilliTS(), CSR_TDMD(pData), CSR_TXON(pData),
2411 !CSR_DPOLL(pData), pcnetTdtePoll(pData, &dummy), pData->GCTDRA));
2412 Log2(("#%d pcnetPollTimer: CSR_CXDA=%#x CSR_XMTRL=%d CSR_XMTRC=%d\n",
2413 PCNETSTATE_2_DEVINS(pData)->iInstance, CSR_CXDA(pData), CSR_XMTRL(pData), CSR_XMTRC(pData)));
2414#endif
2415#ifdef PCNET_DEBUG_TMD
2416 if (CSR_CXDA(pData))
2417 {
2418 TMD tmd;
2419 pcnetTmdLoad(pData, &tmd, PHYSADDR(pData, CSR_CXDA(pData)));
2420 Log2(("#%d pcnetPollTimer: TMDLOAD %#010x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, PHYSADDR(pData, CSR_CXDA(pData))));
2421 PRINT_TMD(&tmd);
2422 }
2423#endif
2424 if (CSR_TDMD(pData))
2425 pcnetTransmit(pData);
2426
2427 pcnetUpdateIrq(pData);
2428
2429 if (RT_LIKELY(!CSR_STOP(pData) && !CSR_SPND(pData) && !CSR_DPOLL(pData)))
2430 {
2431 /* We ensure that we poll at least every 2ms (500Hz) but not more often than
2432 * 5000 times per second. This way we completely prevent the overhead from
2433 * heavy reprogramming the timer which turned out to be very CPU-intensive.
2434 * The drawback is that csr46 and csr47 are not updated properly anymore
2435 * but so far I have not seen any guest depending on these values. The 2ms
2436 * interval is the default polling interval of the PCNet card (65536/33MHz). */
2437#ifdef PCNET_NO_POLLING
2438 pcnetPollRxTx(pData);
2439#else
2440 uint64_t u64Now = TMTimerGet(pData->CTXSUFF(pTimerPoll));
2441 if (RT_UNLIKELY(u64Now - pData->u64LastPoll > 200000))
2442 {
2443 pData->u64LastPoll = u64Now;
2444 pcnetPollRxTx(pData);
2445 }
2446 if (!TMTimerIsActive(pData->CTXSUFF(pTimerPoll)))
2447 /* Poll timer interval is fixed to 500Hz. Don't stop it. */
2448 TMTimerSet(pData->CTXSUFF(pTimerPoll),
2449 TMTimerGet(pData->CTXSUFF(pTimerPoll))
2450 + TMTimerFromMilli(pData->CTXSUFF(pTimerPoll), 2));
2451#endif
2452 }
2453 STAM_PROFILE_ADV_STOP(&pData->StatPollTimer, a);
2454}
2455
2456
2457static int pcnetCSRWriteU16(PCNetState *pData, uint32_t u32RAP, uint32_t new_value)
2458{
2459 uint16_t val = new_value;
2460 int rc = VINF_SUCCESS;
2461#ifdef PCNET_DEBUG_CSR
2462 Log(("#%d pcnetCSRWriteU16: rap=%d val=%#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2463#endif
2464 switch (u32RAP)
2465 {
2466 case 0:
2467 {
2468 uint16_t csr0 = pData->aCSR[0];
2469 /* Clear any interrupt flags.
2470 * Don't clear an interrupt flag which was not seen by the guest yet. */
2471 csr0 &= ~(val & 0x7f00 & pData->u16CSR0LastSeenByGuest);
2472 csr0 = (csr0 & ~0x0040) | (val & 0x0048);
2473 val = (val & 0x007f) | (csr0 & 0x7f00);
2474
2475 /* Iff STOP, STRT and INIT are set, clear STRT and INIT */
2476 if ((val & 7) == 7)
2477 val &= ~3;
2478
2479 Log(("#%d pcnetWriteCSR0: %#06x => %#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, pData->aCSR[0], csr0));
2480
2481#ifndef IN_RING3
2482 if (!(csr0 & 0x0001/*init*/) && (val & 1))
2483 {
2484 Log(("#%d pcnetCSRWriteU16: pcnetInit requested => HC\n", PCNETSTATE_2_DEVINS(pData)->iInstance));
2485 return VINF_IOM_HC_IOPORT_WRITE;
2486 }
2487#endif
2488 LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %#06x => %#06x (%#06x)\n",
2489 PCNETSTATE_2_DEVINS(pData)->iInstance,
2490 u32RAP, new_value, csr0, pData->aCSR[0]));
2491 pData->aCSR[0] = csr0;
2492
2493 if (!CSR_STOP(pData) && (val & 4))
2494 pcnetStop(pData);
2495
2496#ifdef IN_RING3
2497 if (!CSR_INIT(pData) && (val & 1))
2498 pcnetInit(pData);
2499#endif
2500
2501 if (!CSR_STRT(pData) && (val & 2))
2502 pcnetStart(pData);
2503
2504 if (CSR_TDMD(pData))
2505 pcnetTransmit(pData);
2506
2507 return rc;
2508 }
2509 case 1: /* IADRL */
2510 case 2: /* IADRH */
2511 case 8: /* LADRF 0..15 */
2512 case 9: /* LADRF 16..31 */
2513 case 10: /* LADRF 32..47 */
2514 case 11: /* LADRF 48..63 */
2515 case 12: /* PADR 0..15 */
2516 case 13: /* PADR 16..31 */
2517 case 14: /* PADR 32..47 */
2518 case 18: /* CRBAL */
2519 case 19: /* CRBAU */
2520 case 20: /* CXBAL */
2521 case 21: /* CXBAU */
2522 case 22: /* NRBAL */
2523 case 23: /* NRBAU */
2524 case 26: /* NRDAL */
2525 case 27: /* NRDAU */
2526 case 28: /* CRDAL */
2527 case 29: /* CRDAU */
2528 case 32: /* NXDAL */
2529 case 33: /* NXDAU */
2530 case 34: /* CXDAL */
2531 case 35: /* CXDAU */
2532 case 36: /* NNRDL */
2533 case 37: /* NNRDU */
2534 case 38: /* NNXDL */
2535 case 39: /* NNXDU */
2536 case 40: /* CRBCL */
2537 case 41: /* CRBCU */
2538 case 42: /* CXBCL */
2539 case 43: /* CXBCU */
2540 case 44: /* NRBCL */
2541 case 45: /* NRBCU */
2542 case 46: /* POLL */
2543 case 47: /* POLLINT */
2544 case 72: /* RCVRC */
2545 case 74: /* XMTRC */
2546 case 112: /* MISSC */
2547 if (CSR_STOP(pData) || CSR_SPND(pData))
2548 break;
2549 LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %#06x\n",
2550 PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2551 return rc;
2552 case 3: /* Interrupt Mask and Deferral Control */
2553 LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %#06x\n",
2554 PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2555 break;
2556 case 4: /* Test and Features Control */
2557 LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %#06x\n",
2558 PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2559 pData->aCSR[4] &= ~(val & 0x026a);
2560 val &= ~0x026a;
2561 val |= pData->aCSR[4] & 0x026a;
2562 break;
2563 case 5: /* Extended Control and Interrupt 1 */
2564 LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %#06x\n",
2565 PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2566 pData->aCSR[5] &= ~(val & 0x0a90);
2567 val &= ~0x0a90;
2568 val |= pData->aCSR[5] & 0x0a90;
2569 break;
2570 case 7: /* Extended Control and Interrupt 2 */
2571 {
2572 uint16_t csr7 = pData->aCSR[7];
2573 csr7 &= ~0x0400 ;
2574 csr7 &= ~(val & 0x0800);
2575 csr7 |= (val & 0x0400);
2576 pData->aCSR[7] = csr7;
2577 return rc;
2578 }
2579 case 15: /* Mode */
2580 if ((pData->aCSR[15] & 0x8000) != (val & 0x8000) && pData->pDrv)
2581 {
2582 Log(("PCNet#%d: promiscuous mode changed to %d\n",
2583 PCNETSTATE_2_DEVINS(pData)->iInstance, !!(val & 0x8000)));
2584#ifndef IN_RING3
2585 return VINF_IOM_HC_IOPORT_WRITE;
2586#else
2587 /* check for promiscuous mode change */
2588 if (pData->pDrv)
2589 pData->pDrv->pfnSetPromiscuousMode(pData->pDrv, !!(val & 0x8000));
2590#endif
2591 }
2592 break;
2593 case 16: /* IADRL */
2594 return pcnetCSRWriteU16(pData, 1, val);
2595 case 17: /* IADRH */
2596 return pcnetCSRWriteU16(pData, 2, val);
2597
2598 /*
2599 * 24 and 25 are the Base Address of Receive Descriptor.
2600 * We combine and mirror these in GCRDRA.
2601 */
2602 case 24: /* BADRL */
2603 case 25: /* BADRU */
2604 if (!CSR_STOP(pData) && !CSR_SPND(pData))
2605 {
2606 Log(("PCNet#%d: WRITE CSR%d, %#06x !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2607 return rc;
2608 }
2609 if (u32RAP == 24)
2610 pData->GCRDRA = (pData->GCRDRA & 0xffff0000) | (val & 0x0000ffff);
2611 else
2612 pData->GCRDRA = (pData->GCRDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
2613 Log(("PCNet#%d: WRITE CSR%d, %#06x => GCRDRA=%08x (alt init)\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2614 u32RAP, val, pData->GCRDRA));
2615 break;
2616
2617 /*
2618 * 30 & 31 are the Base Address of Transmit Descriptor.
2619 * We combine and mirrorthese in GCTDRA.
2620 */
2621 case 30: /* BADXL */
2622 case 31: /* BADXU */
2623 if (!CSR_STOP(pData) && !CSR_SPND(pData))
2624 {
2625 Log(("PCNet#%d: WRITE CSR%d, %#06x !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2626 return rc;
2627 }
2628 if (u32RAP == 30)
2629 pData->GCTDRA = (pData->GCTDRA & 0xffff0000) | (val & 0x0000ffff);
2630 else
2631 pData->GCTDRA = (pData->GCTDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
2632 Log(("PCNet#%d: WRITE CSR%d, %#06x => GCTDRA=%08x (alt init)\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2633 u32RAP, val, pData->GCTDRA));
2634 break;
2635
2636 case 58: /* Software Style */
2637 LOG_REGISTER(("PCNet#%d: WRITE SW_STYLE, %#06x\n",
2638 PCNETSTATE_2_DEVINS(pData)->iInstance, val));
2639 rc = pcnetBCRWriteU16(pData, BCR_SWS, val);
2640 break;
2641
2642 /*
2643 * Registers 76 and 78 aren't stored correctly (see todos), but I'm don't dare
2644 * try fix that right now. So, as a quick hack for 'alt init' I'll just correct them here.
2645 */
2646 case 76: /* RCVRL */ /** @todo call pcnetUpdateRingHandlers */
2647 /** @todo receive ring length is stored in two's complement! */
2648 case 78: /* XMTRL */ /** @todo call pcnetUpdateRingHandlers */
2649 /** @todo transmit ring length is stored in two's complement! */
2650 if (!CSR_STOP(pData) && !CSR_SPND(pData))
2651 {
2652 Log(("PCNet#%d: WRITE CSR%d, %#06x !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2653 return rc;
2654 }
2655 Log(("PCNet#%d: WRITE CSR%d, %#06x (hacked %#06x) (alt init)\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2656 u32RAP, val, 1 + ~(uint16_t)val));
2657 val = 1 + ~(uint16_t)val;
2658
2659 /*
2660 * HACK ALERT! Set the counter registers too.
2661 */
2662 pData->aCSR[u32RAP - 4] = val;
2663 break;
2664
2665 default:
2666 return rc;
2667 }
2668 pData->aCSR[u32RAP] = val;
2669 return rc;
2670}
2671
2672/**
2673 * Encode a 32-bit link speed into a custom 16-bit floating-point value
2674 */
2675static uint32_t pcnetLinkSpd(uint32_t speed)
2676{
2677 unsigned exp = 0;
2678
2679 while (speed & 0xFFFFE000)
2680 {
2681 speed /= 10;
2682 ++exp;
2683 }
2684 return (exp << 13) | speed;
2685}
2686
2687static uint32_t pcnetCSRReadU16(PCNetState *pData, uint32_t u32RAP)
2688{
2689 uint32_t val;
2690 switch (u32RAP)
2691 {
2692 case 0:
2693 pcnetUpdateIrq(pData);
2694 val = pData->aCSR[0];
2695 val |= (val & 0x7800) ? 0x8000 : 0;
2696 pData->u16CSR0LastSeenByGuest = val;
2697 break;
2698 case 16:
2699 return pcnetCSRReadU16(pData, 1);
2700 case 17:
2701 return pcnetCSRReadU16(pData, 2);
2702 case 58:
2703 return pcnetBCRReadU16(pData, BCR_SWS);
2704 case 68: /* Custom register to pass link speed to driver */
2705 return pcnetLinkSpd(pData->u32LinkSpeed);
2706 case 88:
2707 val = pData->aCSR[89];
2708 val <<= 16;
2709 val |= pData->aCSR[88];
2710 break;
2711 default:
2712 val = pData->aCSR[u32RAP];
2713 LOG_REGISTER(("PCNet#%d: read CSR%d => %#06x\n",
2714 PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2715 }
2716#ifdef PCNET_DEBUG_CSR
2717 Log(("#%d pcnetCSRReadU16: rap=%d val=%#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2718 u32RAP, val));
2719#endif
2720 return val;
2721}
2722
2723static int pcnetBCRWriteU16(PCNetState *pData, uint32_t u32RAP, uint32_t val)
2724{
2725 int rc = VINF_SUCCESS;
2726 u32RAP &= 0x7f;
2727#ifdef PCNET_DEBUG_BCR
2728 Log2(("#%d pcnetBCRWriteU16: rap=%d val=%#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2729 u32RAP, val));
2730#endif
2731 switch (u32RAP)
2732 {
2733 case BCR_SWS:
2734 if (!(CSR_STOP(pData) || CSR_SPND(pData)))
2735 return rc;
2736 val &= ~0x0300;
2737 switch (val & 0x00ff)
2738 {
2739 default:
2740 Log(("Bad SWSTYLE=%#04x\n", val & 0xff));
2741 // fall through
2742 case 0:
2743 val |= 0x0200; /* 16 bit */
2744 pData->iLog2DescSize = 3;
2745 pData->GCUpperPhys = (0xff00 & (uint32_t)pData->aCSR[2]) << 16;
2746 break;
2747 case 1:
2748 val |= 0x0100; /* 32 bit */
2749 pData->iLog2DescSize = 4;
2750 pData->GCUpperPhys = 0;
2751 break;
2752 case 2:
2753 case 3:
2754 val |= 0x0300; /* 32 bit */
2755 pData->iLog2DescSize = 4;
2756 pData->GCUpperPhys = 0;
2757 break;
2758 }
2759 LOG_REGISTER(("PCNet#%d: WRITE SW_STYLE, %#06x\n",
2760 PCNETSTATE_2_DEVINS(pData)->iInstance, val));
2761 Log(("BCR_SWS=%#06x\n", val));
2762 pData->aCSR[58] = val;
2763 /* fall through */
2764 case BCR_LNKST:
2765 case BCR_LED1:
2766 case BCR_LED2:
2767 case BCR_LED3:
2768 case BCR_MC:
2769 case BCR_FDC:
2770 case BCR_BSBC:
2771 case BCR_EECAS:
2772 case BCR_PLAT:
2773 case BCR_MIICAS:
2774 case BCR_MIIADDR:
2775 LOG_REGISTER(("PCNet#%d: WRITE BCR%d, %#06x\n",
2776 PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2777 pData->aBCR[u32RAP] = val;
2778 break;
2779
2780 case BCR_STVAL:
2781 val &= 0xffff;
2782 pData->aBCR[BCR_STVAL] = val;
2783 if (pData->fAm79C973)
2784 TMTimerSetNano(pData->CTXSUFF(pTimerSoftInt), 12800U * val);
2785 break;
2786
2787 case BCR_MIIMDR:
2788 LOG_REGISTER(("PCNet#%d: WRITE MII%d, %#06x\n",
2789 PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
2790 pData->aMII[pData->aBCR[BCR_MIIADDR] & 0x1f] = val;
2791#ifdef PCNET_DEBUG_MII
2792 Log(("#%d pcnet: mii write %d <- %#x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2793 pData->aBCR[BCR_MIIADDR] & 0x1f, val));
2794#endif
2795 break;
2796
2797 default:
2798 break;
2799 }
2800 return rc;
2801}
2802
2803static uint32_t pcnetMIIReadU16(PCNetState *pData, uint32_t miiaddr)
2804{
2805 uint32_t val;
2806 bool autoneg, duplex, fast;
2807 STAM_COUNTER_INC(&pData->StatMIIReads);
2808
2809 autoneg = (pData->aBCR[BCR_MIICAS] & 0x20) != 0;
2810 duplex = (pData->aBCR[BCR_MIICAS] & 0x10) != 0;
2811 fast = (pData->aBCR[BCR_MIICAS] & 0x08) != 0;
2812
2813 switch (miiaddr)
2814 {
2815 case 0:
2816 /* MII basic mode control register. */
2817 val = 0;
2818 if (autoneg)
2819 val |= 0x1000; /* Enable auto negotiation. */
2820 if (fast)
2821 val |= 0x2000; /* 100 Mbps */
2822 if (duplex) /* Full duplex forced */
2823 val |= 0x0100; /* Full duplex */
2824 break;
2825
2826 case 1:
2827 /* MII basic mode status register. */
2828 val = 0x7800 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
2829 | 0x0040 /* Mgmt frame preamble not required. */
2830 | 0x0020 /* Auto-negotiation complete. */
2831 | 0x0008 /* Able to do auto-negotiation. */
2832 | 0x0004 /* Link up. */
2833 | 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
2834 if (!pData->fLinkUp || pData->fLinkTempDown) {
2835 val &= ~(0x0020 | 0x0004);
2836 pData->cLinkDownReported++;
2837 }
2838 if (!autoneg) {
2839 /* Auto-negotiation disabled. */
2840 val &= ~(0x0020 | 0x0008);
2841 if (duplex)
2842 /* Full duplex forced. */
2843 val &= ~0x2800;
2844 else
2845 /* Half duplex forced. */
2846 val &= ~0x5000;
2847
2848 if (fast)
2849 /* 100 Mbps forced */
2850 val &= ~0x1800;
2851 else
2852 /* 10 Mbps forced */
2853 val &= ~0x6000;
2854 }
2855 break;
2856
2857 case 2:
2858 /* PHY identifier 1. */
2859 val = 0x22; /* Am79C874 PHY */
2860 break;
2861
2862 case 3:
2863 /* PHY identifier 2. */
2864 val = 0x561b; /* Am79C874 PHY */
2865 break;
2866
2867 case 4:
2868 /* Advertisement control register. */
2869 val = 0x01e0 /* Try 100mbps FD/HD and 10mbps FD/HD. */
2870#if 0
2871 // Advertising flow control is a) not the default, and b) confuses
2872 // the link speed detection routine in Windows PCnet driver
2873 | 0x0400 /* Try flow control. */
2874#endif
2875 | 0x0001; /* CSMA selector. */
2876 break;
2877
2878 case 5:
2879 /* Link partner ability register. */
2880 if (pData->fLinkUp && !pData->fLinkTempDown)
2881 val = 0x8000 /* Next page bit. */
2882 | 0x4000 /* Link partner acked us. */
2883 | 0x0400 /* Can do flow control. */
2884 | 0x01e0 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
2885 | 0x0001; /* Use CSMA selector. */
2886 else
2887 {
2888 val = 0;
2889 pData->cLinkDownReported++;
2890 }
2891 break;
2892
2893 case 6:
2894 /* Auto negotiation expansion register. */
2895 if (pData->fLinkUp && !pData->fLinkTempDown)
2896 val = 0x0008 /* Link partner supports npage. */
2897 | 0x0004 /* Enable npage words. */
2898 | 0x0001; /* Can do N-way auto-negotiation. */
2899 else
2900 {
2901 val = 0;
2902 pData->cLinkDownReported++;
2903 }
2904 break;
2905
2906 default:
2907 val = 0;
2908 break;
2909 }
2910
2911#ifdef PCNET_DEBUG_MII
2912 Log(("#%d pcnet: mii read %d -> %#x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2913 miiaddr, val));
2914#endif
2915 return val;
2916}
2917
2918static uint32_t pcnetBCRReadU16(PCNetState *pData, uint32_t u32RAP)
2919{
2920 uint32_t val;
2921 u32RAP &= 0x7f;
2922 switch (u32RAP)
2923 {
2924 case BCR_LNKST:
2925 case BCR_LED1:
2926 case BCR_LED2:
2927 case BCR_LED3:
2928 val = pData->aBCR[u32RAP] & ~0x8000;
2929 /* Clear LNKSTE if we're not connected or if we've just loaded a VM state. */
2930 if (!pData->pDrv || pData->fLinkTempDown || !pData->fLinkUp)
2931 {
2932 if (u32RAP == 4)
2933 pData->cLinkDownReported++;
2934 val &= ~0x40;
2935 }
2936 val |= (val & 0x017f & pData->u32Lnkst) ? 0x8000 : 0;
2937 break;
2938
2939 case BCR_MIIMDR:
2940 if (pData->fAm79C973 && (pData->aBCR[BCR_MIIADDR] >> 5 & 0x1f) == 0)
2941 {
2942 uint32_t miiaddr = pData->aBCR[BCR_MIIADDR] & 0x1f;
2943 val = pcnetMIIReadU16(pData, miiaddr);
2944 }
2945 else
2946 val = 0xffff;
2947 break;
2948
2949 default:
2950 val = u32RAP < BCR_MAX_RAP ? pData->aBCR[u32RAP] : 0;
2951 break;
2952 }
2953#ifdef PCNET_DEBUG_BCR
2954 Log2(("#%d pcnetBCRReadU16: rap=%d val=%#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
2955 u32RAP, val));
2956#endif
2957 return val;
2958}
2959
2960#ifdef IN_RING3 /* move down */
2961static void pcnetHardReset(PCNetState *pData)
2962{
2963 int i;
2964 uint16_t checksum;
2965
2966 /* Initialize the PROM */
2967 Assert(sizeof(pData->MacConfigured) == 6);
2968 memcpy(pData->aPROM, &pData->MacConfigured, sizeof(pData->MacConfigured));
2969 pData->aPROM[ 8] = 0x00;
2970 pData->aPROM[ 9] = 0x11;
2971 pData->aPROM[12] = pData->aPROM[13] = 0x00;
2972 pData->aPROM[14] = pData->aPROM[15] = 0x57;
2973
2974 for (i = 0, checksum = 0; i < 16; i++)
2975 checksum += pData->aPROM[i];
2976 *(uint16_t *)&pData->aPROM[12] = RT_H2LE_U16(checksum);
2977
2978 pData->aBCR[BCR_MSRDA] = 0x0005;
2979 pData->aBCR[BCR_MSWRA] = 0x0005;
2980 pData->aBCR[BCR_MC ] = 0x0002;
2981 pData->aBCR[BCR_LNKST] = 0x00c0;
2982 pData->aBCR[BCR_LED1 ] = 0x0084;
2983 pData->aBCR[BCR_LED2 ] = 0x0088;
2984 pData->aBCR[BCR_LED3 ] = 0x0090;
2985 pData->aBCR[BCR_FDC ] = 0x0000;
2986 pData->aBCR[BCR_BSBC ] = 0x9001;
2987 pData->aBCR[BCR_EECAS] = 0x0002;
2988 pData->aBCR[BCR_STVAL] = 0xffff;
2989 pData->aCSR[58 ] = /* CSR58 is an alias for BCR20 */
2990 pData->aBCR[BCR_SWS ] = 0x0200;
2991 pData->iLog2DescSize = 3;
2992 pData->aBCR[BCR_PLAT ] = 0xff06;
2993 pData->aBCR[BCR_MIIADDR ] = 0; /* Internal PHY on Am79C973 would be (0x1e << 5) */
2994 pData->aBCR[BCR_PCIVID] = PCIDevGetVendorId(&pData->PciDev);
2995 pData->aBCR[BCR_PCISID] = PCIDevGetSubSystemId(&pData->PciDev);
2996 pData->aBCR[BCR_PCISVID] = PCIDevGetSubSystemVendorId(&pData->PciDev);
2997
2998 pcnetSoftReset(pData);
2999}
3000#endif /* IN_RING3 */
3001
3002static void pcnetAPROMWriteU8(PCNetState *pData, uint32_t addr, uint32_t val)
3003{
3004 addr &= 0x0f;
3005 val &= 0xff;
3006 Log(("#%d pcnetAPROMWriteU8: addr=%#010x val=%#04x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3007 addr, val));
3008 /* Check APROMWE bit to enable write access */
3009 if (pcnetBCRReadU16(pData, 2) & 0x80)
3010 pData->aPROM[addr] = val;
3011}
3012
3013static uint32_t pcnetAPROMReadU8(PCNetState *pData, uint32_t addr)
3014{
3015 uint32_t val = pData->aPROM[addr &= 0x0f];
3016 Log(("#%d pcnetAPROMReadU8: addr=%#010x val=%#04x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3017 addr, val));
3018 return val;
3019}
3020
3021static int pcnetIoportWriteU16(PCNetState *pData, uint32_t addr, uint32_t val)
3022{
3023 int rc = VINF_SUCCESS;
3024
3025#ifdef PCNET_DEBUG_IO
3026 Log2(("#%d pcnetIoportWriteU16: addr=%#010x val=%#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3027 addr, val));
3028#endif
3029 if (RT_LIKELY(!BCR_DWIO(pData)))
3030 {
3031 switch (addr & 0x0f)
3032 {
3033 case 0x00: /* RDP */
3034 pcnetPollTimer(pData);
3035 rc = pcnetCSRWriteU16(pData, pData->u32RAP, val);
3036 pcnetUpdateIrq(pData);
3037 break;
3038 case 0x02: /* RAP */
3039 pData->u32RAP = val & 0x7f;
3040 break;
3041 case 0x06: /* BDP */
3042 rc = pcnetBCRWriteU16(pData, pData->u32RAP, val);
3043 break;
3044 }
3045 }
3046 else
3047 Log(("#%d pcnetIoportWriteU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, addr, val));
3048
3049 return rc;
3050}
3051
3052static uint32_t pcnetIoportReadU16(PCNetState *pData, uint32_t addr, int *pRC)
3053{
3054 uint32_t val = ~0U;
3055
3056 *pRC = VINF_SUCCESS;
3057
3058 if (RT_LIKELY(!BCR_DWIO(pData)))
3059 {
3060 switch (addr & 0x0f)
3061 {
3062 case 0x00: /* RDP */
3063 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3064 /** Polling is then useless here and possibly expensive. */
3065 if (!CSR_DPOLL(pData))
3066 pcnetPollTimer(pData);
3067
3068 val = pcnetCSRReadU16(pData, pData->u32RAP);
3069 if (pData->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3070 goto skip_update_irq;
3071 break;
3072 case 0x02: /* RAP */
3073 val = pData->u32RAP;
3074 goto skip_update_irq;
3075 case 0x04: /* RESET */
3076 pcnetSoftReset(pData);
3077 val = 0;
3078 break;
3079 case 0x06: /* BDP */
3080 val = pcnetBCRReadU16(pData, pData->u32RAP);
3081 break;
3082 }
3083 }
3084 else
3085 Log(("#%d pcnetIoportReadU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, addr, val & 0xffff));
3086
3087 pcnetUpdateIrq(pData);
3088
3089skip_update_irq:
3090#ifdef PCNET_DEBUG_IO
3091 Log2(("#%d pcnetIoportReadU16: addr=%#010x val=%#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3092 addr, val & 0xffff));
3093#endif
3094 return val;
3095}
3096
3097static int pcnetIoportWriteU32(PCNetState *pData, uint32_t addr, uint32_t val)
3098{
3099 int rc = VINF_SUCCESS;
3100
3101#ifdef PCNET_DEBUG_IO
3102 Log2(("#%d pcnetIoportWriteU32: addr=%#010x val=%#010x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3103 addr, val));
3104#endif
3105 if (RT_LIKELY(BCR_DWIO(pData)))
3106 {
3107 switch (addr & 0x0f)
3108 {
3109 case 0x00: /* RDP */
3110 pcnetPollTimer(pData);
3111 rc = pcnetCSRWriteU16(pData, pData->u32RAP, val & 0xffff);
3112 pcnetUpdateIrq(pData);
3113 break;
3114 case 0x04: /* RAP */
3115 pData->u32RAP = val & 0x7f;
3116 break;
3117 case 0x0c: /* BDP */
3118 rc = pcnetBCRWriteU16(pData, pData->u32RAP, val & 0xffff);
3119 break;
3120 }
3121 }
3122 else if ((addr & 0x0f) == 0)
3123 {
3124 /* switch device to dword I/O mode */
3125 pcnetBCRWriteU16(pData, BCR_BSBC, pcnetBCRReadU16(pData, BCR_BSBC) | 0x0080);
3126#ifdef PCNET_DEBUG_IO
3127 Log2(("device switched into dword i/o mode\n"));
3128#endif
3129 }
3130 else
3131 Log(("#%d pcnetIoportWriteU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, addr, val));
3132
3133 return rc;
3134}
3135
3136static uint32_t pcnetIoportReadU32(PCNetState *pData, uint32_t addr, int *pRC)
3137{
3138 uint32_t val = ~0U;
3139
3140 *pRC = VINF_SUCCESS;
3141
3142 if (RT_LIKELY(BCR_DWIO(pData)))
3143 {
3144 switch (addr & 0x0f)
3145 {
3146 case 0x00: /* RDP */
3147 /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
3148 /** Polling is then useless here and possibly expensive. */
3149 if (!CSR_DPOLL(pData))
3150 pcnetPollTimer(pData);
3151
3152 val = pcnetCSRReadU16(pData, pData->u32RAP);
3153 if (pData->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
3154 goto skip_update_irq;
3155 break;
3156 case 0x04: /* RAP */
3157 val = pData->u32RAP;
3158 goto skip_update_irq;
3159 case 0x08: /* RESET */
3160 pcnetSoftReset(pData);
3161 val = 0;
3162 break;
3163 case 0x0c: /* BDP */
3164 val = pcnetBCRReadU16(pData, pData->u32RAP);
3165 break;
3166 }
3167 }
3168 else
3169 Log(("#%d pcnetIoportReadU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, addr, val));
3170 pcnetUpdateIrq(pData);
3171
3172skip_update_irq:
3173#ifdef PCNET_DEBUG_IO
3174 Log2(("#%d pcnetIoportReadU32: addr=%#010x val=%#010x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3175 addr, val));
3176#endif
3177 return val;
3178}
3179
3180static void pcnetMMIOWriteU8(PCNetState *pData, RTGCPHYS addr, uint32_t val)
3181{
3182#ifdef PCNET_DEBUG_IO
3183 Log2(("#%d pcnetMMIOWriteU8: addr=%#010x val=%#04x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3184 addr, val));
3185#endif
3186 if (!(addr & 0x10))
3187 pcnetAPROMWriteU8(pData, addr, val);
3188}
3189
3190static uint32_t pcnetMMIOReadU8(PCNetState *pData, RTGCPHYS addr)
3191{
3192 uint32_t val = ~0U;
3193 if (!(addr & 0x10))
3194 val = pcnetAPROMReadU8(pData, addr);
3195#ifdef PCNET_DEBUG_IO
3196 Log2(("#%d pcnetMMIOReadU8: addr=%#010x val=%#04x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3197 addr, val & 0xff));
3198#endif
3199 return val;
3200}
3201
3202static void pcnetMMIOWriteU16(PCNetState *pData, RTGCPHYS addr, uint32_t val)
3203{
3204#ifdef PCNET_DEBUG_IO
3205 Log2(("#%d pcnetMMIOWriteU16: addr=%#010x val=%#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3206 addr, val));
3207#endif
3208 if (addr & 0x10)
3209 pcnetIoportWriteU16(pData, addr & 0x0f, val);
3210 else
3211 {
3212 pcnetAPROMWriteU8(pData, addr, val );
3213 pcnetAPROMWriteU8(pData, addr+1, val >> 8);
3214 }
3215}
3216
3217static uint32_t pcnetMMIOReadU16(PCNetState *pData, RTGCPHYS addr)
3218{
3219 uint32_t val = ~0U;
3220 int rc;
3221
3222 if (addr & 0x10)
3223 val = pcnetIoportReadU16(pData, addr & 0x0f, &rc);
3224 else
3225 {
3226 val = pcnetAPROMReadU8(pData, addr+1);
3227 val <<= 8;
3228 val |= pcnetAPROMReadU8(pData, addr);
3229 }
3230#ifdef PCNET_DEBUG_IO
3231 Log2(("#%d pcnetMMIOReadU16: addr=%#010x val = %#06x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3232 addr, val & 0xffff));
3233#endif
3234 return val;
3235}
3236
3237static void pcnetMMIOWriteU32(PCNetState *pData, RTGCPHYS addr, uint32_t val)
3238{
3239#ifdef PCNET_DEBUG_IO
3240 Log2(("#%d pcnetMMIOWriteU32: addr=%#010x val=%#010x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3241 addr, val));
3242#endif
3243 if (addr & 0x10)
3244 pcnetIoportWriteU32(pData, addr & 0x0f, val);
3245 else
3246 {
3247 pcnetAPROMWriteU8(pData, addr, val );
3248 pcnetAPROMWriteU8(pData, addr+1, val >> 8);
3249 pcnetAPROMWriteU8(pData, addr+2, val >> 16);
3250 pcnetAPROMWriteU8(pData, addr+3, val >> 24);
3251 }
3252}
3253
3254static uint32_t pcnetMMIOReadU32(PCNetState *pData, RTGCPHYS addr)
3255{
3256 uint32_t val;
3257 int rc;
3258
3259 if (addr & 0x10)
3260 val = pcnetIoportReadU32(pData, addr & 0x0f, &rc);
3261 else
3262 {
3263 val = pcnetAPROMReadU8(pData, addr+3);
3264 val <<= 8;
3265 val |= pcnetAPROMReadU8(pData, addr+2);
3266 val <<= 8;
3267 val |= pcnetAPROMReadU8(pData, addr+1);
3268 val <<= 8;
3269 val |= pcnetAPROMReadU8(pData, addr );
3270 }
3271#ifdef PCNET_DEBUG_IO
3272 Log2(("#%d pcnetMMIOReadU32: addr=%#010x val=%#010x\n", PCNETSTATE_2_DEVINS(pData)->iInstance,
3273 addr, val));
3274#endif
3275 return val;
3276}
3277
3278
3279/**
3280 * Port I/O Handler for IN operations.
3281 *
3282 * @returns VBox status code.
3283 *
3284 * @param pDevIns The device instance.
3285 * @param pvUser User argument.
3286 * @param Port Port number used for the IN operation.
3287 * @param pu32 Where to store the result.
3288 * @param cb Number of bytes read.
3289 */
3290PDMBOTHCBDECL(int) pcnetIOPortAPromRead(PPDMDEVINS pDevIns, void *pvUser,
3291 RTIOPORT Port, uint32_t *pu32, unsigned cb)
3292{
3293 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3294 int rc;
3295
3296 STAM_PROFILE_ADV_START(&pData->StatAPROMRead, a);
3297 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
3298 if (rc == VINF_SUCCESS)
3299 {
3300
3301 /* FreeBSD is accessing in dwords. */
3302 if (cb == 1)
3303 *pu32 = pcnetAPROMReadU8(pData, Port);
3304 else if (cb == 2 && !BCR_DWIO(pData))
3305 *pu32 = pcnetAPROMReadU8(pData, Port)
3306 | (pcnetAPROMReadU8(pData, Port + 1) << 8);
3307 else if (cb == 4 && BCR_DWIO(pData))
3308 *pu32 = pcnetAPROMReadU8(pData, Port)
3309 | (pcnetAPROMReadU8(pData, Port + 1) << 8)
3310 | (pcnetAPROMReadU8(pData, Port + 2) << 16)
3311 | (pcnetAPROMReadU8(pData, Port + 3) << 24);
3312 else
3313 {
3314 Log(("#%d pcnetIOPortAPromRead: Port=%RTiop cb=%d BCR_DWIO !!\n", PCNETSTATE_2_DEVINS(pData)->iInstance, Port, cb));
3315 rc = VERR_IOM_IOPORT_UNUSED;
3316 }
3317 PDMCritSectLeave(&pData->CritSect);
3318 }
3319 STAM_PROFILE_ADV_STOP(&pData->StatAPROMRead, a);
3320 LogFlow(("#%d pcnetIOPortAPromRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Vrc\n",
3321 PCNETSTATE_2_DEVINS(pData)->iInstance, Port, *pu32, cb, rc));
3322 return rc;
3323}
3324
3325
3326/**
3327 * Port I/O Handler for OUT operations.
3328 *
3329 * @returns VBox status code.
3330 *
3331 * @param pDevIns The device instance.
3332 * @param pvUser User argument.
3333 * @param Port Port number used for the IN operation.
3334 * @param u32 The value to output.
3335 * @param cb The value size in bytes.
3336 */
3337PDMBOTHCBDECL(int) pcnetIOPortAPromWrite(PPDMDEVINS pDevIns, void *pvUser,
3338 RTIOPORT Port, uint32_t u32, unsigned cb)
3339{
3340 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3341 int rc;
3342
3343 if (cb == 1)
3344 {
3345 STAM_PROFILE_ADV_START(&pData->StatAPROMWrite, a);
3346 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
3347 if (rc == VINF_SUCCESS)
3348 {
3349 pcnetAPROMWriteU8(pData, Port, u32);
3350 PDMCritSectLeave(&pData->CritSect);
3351 }
3352 STAM_PROFILE_ADV_STOP(&pData->StatAPROMWrite, a);
3353 }
3354 else
3355 {
3356 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3357 rc = VINF_SUCCESS;
3358 }
3359 LogFlow(("#%d pcnetIOPortAPromWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Vrc\n",
3360 PCNETSTATE_2_DEVINS(pData)->iInstance, Port, u32, cb, rc));
3361 return rc;
3362}
3363
3364
3365/**
3366 * Port I/O Handler for IN operations.
3367 *
3368 * @returns VBox status code.
3369 *
3370 * @param pDevIns The device instance.
3371 * @param pvUser User argument.
3372 * @param Port Port number used for the IN operation.
3373 * @param pu32 Where to store the result.
3374 * @param cb Number of bytes read.
3375 */
3376PDMBOTHCBDECL(int) pcnetIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
3377 RTIOPORT Port, uint32_t *pu32, unsigned cb)
3378{
3379 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3380 int rc = VINF_SUCCESS;
3381
3382 STAM_PROFILE_ADV_START(&pData->CTXSUFF(StatIORead), a);
3383 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_READ);
3384 if (rc == VINF_SUCCESS)
3385 {
3386 switch (cb)
3387 {
3388 case 2: *pu32 = pcnetIoportReadU16(pData, Port, &rc); break;
3389 case 4: *pu32 = pcnetIoportReadU32(pData, Port, &rc); break;
3390 default:
3391 rc = VERR_IOM_IOPORT_UNUSED;
3392 break;
3393 }
3394 PDMCritSectLeave(&pData->CritSect);
3395 }
3396 STAM_PROFILE_ADV_STOP(&pData->CTXSUFF(StatIORead), a);
3397 LogFlow(("#%d pcnetIOPortRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Vrc\n",
3398 PCNETSTATE_2_DEVINS(pData)->iInstance, Port, *pu32, cb, rc));
3399 return rc;
3400}
3401
3402
3403/**
3404 * Port I/O Handler for OUT operations.
3405 *
3406 * @returns VBox status code.
3407 *
3408 * @param pDevIns The device instance.
3409 * @param pvUser User argument.
3410 * @param Port Port number used for the IN operation.
3411 * @param u32 The value to output.
3412 * @param cb The value size in bytes.
3413 */
3414PDMBOTHCBDECL(int) pcnetIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
3415 RTIOPORT Port, uint32_t u32, unsigned cb)
3416{
3417 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3418 int rc = VINF_SUCCESS;
3419
3420 STAM_PROFILE_ADV_START(&pData->CTXSUFF(StatIOWrite), a);
3421 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
3422 if (rc == VINF_SUCCESS)
3423 {
3424 switch (cb)
3425 {
3426 case 2: rc = pcnetIoportWriteU16(pData, Port, u32); break;
3427 case 4: rc = pcnetIoportWriteU32(pData, Port, u32); break;
3428 default:
3429 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3430 rc = VERR_INTERNAL_ERROR;
3431 break;
3432 }
3433 PDMCritSectLeave(&pData->CritSect);
3434 }
3435 STAM_PROFILE_ADV_STOP(&pData->CTXSUFF(StatIOWrite), a);
3436 LogFlow(("#%d pcnetIOPortWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Vrc\n",
3437 PCNETSTATE_2_DEVINS(pData)->iInstance, Port, u32, cb, rc));
3438 return rc;
3439}
3440
3441
3442/**
3443 * Memory mapped I/O Handler for read operations.
3444 *
3445 * @returns VBox status code.
3446 *
3447 * @param pDevIns The device instance.
3448 * @param pvUser User argument.
3449 * @param GCPhysAddr Physical address (in GC) where the read starts.
3450 * @param pv Where to store the result.
3451 * @param cb Number of bytes read.
3452 */
3453PDMBOTHCBDECL(int) pcnetMMIORead(PPDMDEVINS pDevIns, void *pvUser,
3454 RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3455{
3456 PCNetState *pData = (PCNetState *)pvUser;
3457 int rc = VINF_SUCCESS;
3458
3459 /*
3460 * We have to check the range, because we're page aligning the MMIO stuff presently.
3461 */
3462 if (GCPhysAddr - pData->MMIOBase < PCNET_PNPMMIO_SIZE)
3463 {
3464 STAM_PROFILE_ADV_START(&pData->CTXSUFF(StatMMIORead), a);
3465 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_MMIO_READ);
3466 if (rc == VINF_SUCCESS)
3467 {
3468 switch (cb)
3469 {
3470 case 1: *(uint8_t *)pv = pcnetMMIOReadU8 (pData, GCPhysAddr); break;
3471 case 2: *(uint16_t *)pv = pcnetMMIOReadU16(pData, GCPhysAddr); break;
3472 case 4: *(uint32_t *)pv = pcnetMMIOReadU32(pData, GCPhysAddr); break;
3473 default:
3474 AssertMsgFailed(("cb=%d\n", cb));
3475 rc = VERR_INTERNAL_ERROR;
3476 break;
3477 }
3478 PDMCritSectLeave(&pData->CritSect);
3479 }
3480 STAM_PROFILE_ADV_STOP(&pData->CTXSUFF(StatMMIORead), a);
3481 }
3482 else
3483 memset(pv, 0, cb);
3484
3485 LogFlow(("#%d pcnetMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Vrc\n",
3486 PCNETSTATE_2_DEVINS(pData)->iInstance, pv, cb, pv, cb, GCPhysAddr, rc));
3487 return rc;
3488}
3489
3490
3491/**
3492 * Port I/O Handler for write operations.
3493 *
3494 * @returns VBox status code.
3495 *
3496 * @param pDevIns The device instance.
3497 * @param pvUser User argument.
3498 * @param GCPhysAddr Physical address (in GC) where the read starts.
3499 * @param pv Where to fetch the result.
3500 * @param cb Number of bytes to write.
3501 */
3502PDMBOTHCBDECL(int) pcnetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser,
3503 RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3504{
3505 PCNetState *pData = (PCNetState *)pvUser;
3506 int rc = VINF_SUCCESS;
3507
3508 /*
3509 * We have to check the range, because we're page aligning the MMIO stuff presently.
3510 */
3511 if (GCPhysAddr - pData->MMIOBase < PCNET_PNPMMIO_SIZE)
3512 {
3513 STAM_PROFILE_ADV_START(&pData->CTXSUFF(StatMMIOWrite), a);
3514 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_MMIO_WRITE);
3515 if (rc == VINF_SUCCESS)
3516 {
3517 switch (cb)
3518 {
3519 case 1: pcnetMMIOWriteU8 (pData, GCPhysAddr, *(uint8_t *)pv); break;
3520 case 2: pcnetMMIOWriteU16(pData, GCPhysAddr, *(uint16_t *)pv); break;
3521 case 4: pcnetMMIOWriteU32(pData, GCPhysAddr, *(uint32_t *)pv); break;
3522 default:
3523 AssertMsgFailed(("cb=%d\n", cb));
3524 rc = VERR_INTERNAL_ERROR;
3525 break;
3526 }
3527 PDMCritSectLeave(&pData->CritSect);
3528 }
3529 // else rc == VINF_IOM_HC_MMIO_WRITE => handle in ring3
3530
3531 STAM_PROFILE_ADV_STOP(&pData->CTXSUFF(StatMMIOWrite), a);
3532 }
3533 LogFlow(("#%d pcnetMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Vrc\n",
3534 PCNETSTATE_2_DEVINS(pData)->iInstance, pv, cb, pv, cb, GCPhysAddr, rc));
3535 return rc;
3536}
3537
3538
3539#ifdef IN_RING3
3540/**
3541 * Device timer callback function.
3542 *
3543 * @param pDevIns Device instance of the device which registered the timer.
3544 * @param pTimer The timer handle.
3545 * @thread EMT
3546 */
3547static DECLCALLBACK(void) pcnetTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer)
3548{
3549 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3550 int rc;
3551
3552 STAM_PROFILE_ADV_START(&pData->StatTimer, a);
3553 rc = PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
3554 AssertReleaseRC(rc);
3555
3556 pcnetPollTimer(pData);
3557
3558 PDMCritSectLeave(&pData->CritSect);
3559 STAM_PROFILE_ADV_STOP(&pData->StatTimer, a);
3560}
3561
3562
3563/**
3564 * Software interrupt timer callback function.
3565 *
3566 * @param pDevIns Device instance of the device which registered the timer.
3567 * @param pTimer The timer handle.
3568 * @thread EMT
3569 */
3570static DECLCALLBACK(void) pcnetTimerSoftInt(PPDMDEVINS pDevIns, PTMTIMER pTimer)
3571{
3572 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3573
3574 pData->aCSR[7] |= 0x0800; /* STINT */
3575 pcnetUpdateIrq(pData);
3576 TMTimerSetNano(pData->CTXSUFF(pTimerSoftInt), 12800U * (pData->aBCR[BCR_STVAL] & 0xffff));
3577}
3578
3579
3580/**
3581 * Restore timer callback.
3582 *
3583 * This is only called when've restored a saved state and temporarily
3584 * disconnected the network link to inform the guest that network connections
3585 * should be considered lost.
3586 *
3587 * @param pDevIns Device instance of the device which registered the timer.
3588 * @param pTimer The timer handle.
3589 */
3590static DECLCALLBACK(void) pcnetTimerRestore(PPDMDEVINS pDevIns, PTMTIMER pTimer)
3591{
3592 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3593 int rc = PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
3594 AssertReleaseRC(rc);
3595
3596 rc = VERR_GENERAL_FAILURE;
3597 if (pData->cLinkDownReported <= PCNET_MAX_LINKDOWN_REPORTED)
3598 rc = TMTimerSetMillies(pData->pTimerRestore, 1500);
3599 if (VBOX_FAILURE(rc))
3600 {
3601 pData->fLinkTempDown = false;
3602 if (pData->fLinkUp)
3603 {
3604 LogRel(("PCNet#%d: The link is back up again after the restore.\n",
3605 pDevIns->iInstance));
3606 Log(("#%d pcnetTimerRestore: Clearing ERR and CERR after load. cLinkDownReported=%d\n",
3607 pDevIns->iInstance, pData->cLinkDownReported));
3608 pData->aCSR[0] &= ~(RT_BIT(15) | RT_BIT(13)); /* ERR | CERR - probably not 100% correct either... */
3609 pData->Led.Actual.s.fError = 0;
3610 }
3611 }
3612 else
3613 Log(("#%d pcnetTimerRestore: cLinkDownReported=%d, wait another 1500ms...\n",
3614 pDevIns->iInstance, pData->cLinkDownReported));
3615
3616 PDMCritSectLeave(&pData->CritSect);
3617}
3618
3619
3620/**
3621 * Callback function for mapping an PCI I/O region.
3622 *
3623 * @return VBox status code.
3624 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
3625 * @param iRegion The region number.
3626 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
3627 * I/O port, else it's a physical address.
3628 * This address is *NOT* relative to pci_mem_base like earlier!
3629 * @param cb Region size.
3630 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
3631 */
3632static DECLCALLBACK(int) pcnetIOPortMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
3633 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
3634{
3635 int rc;
3636 PPDMDEVINS pDevIns = pPciDev->pDevIns;
3637 RTIOPORT Port = (RTIOPORT)GCPhysAddress;
3638 PCNetState *pData = PCIDEV_2_PCNETSTATE(pPciDev);
3639
3640 Assert(enmType == PCI_ADDRESS_SPACE_IO);
3641 Assert(cb >= 0x20);
3642
3643 rc = PDMDevHlpIOPortRegister(pDevIns, Port, 0x10, 0, pcnetIOPortAPromWrite,
3644 pcnetIOPortAPromRead, NULL, NULL, "PCNet ARPOM");
3645 if (VBOX_FAILURE(rc))
3646 return rc;
3647 rc = PDMDevHlpIOPortRegister(pDevIns, Port + 0x10, 0x10, 0, pcnetIOPortWrite,
3648 pcnetIOPortRead, NULL, NULL, "PCNet");
3649 if (VBOX_FAILURE(rc))
3650 return rc;
3651
3652 if (pData->fGCEnabled)
3653 {
3654 rc = PDMDevHlpIOPortRegisterGC(pDevIns, Port, 0x10, 0, "pcnetIOPortAPromWrite",
3655 "pcnetIOPortAPromRead", NULL, NULL, "PCNet aprom");
3656 if (VBOX_FAILURE(rc))
3657 return rc;
3658 rc = PDMDevHlpIOPortRegisterGC(pDevIns, Port + 0x10, 0x10, 0, "pcnetIOPortWrite",
3659 "pcnetIOPortRead", NULL, NULL, "PCNet");
3660 if (VBOX_FAILURE(rc))
3661 return rc;
3662 }
3663 if (pData->fR0Enabled)
3664 {
3665 rc = PDMDevHlpIOPortRegisterR0(pDevIns, Port, 0x10, 0, "pcnetIOPortAPromWrite",
3666 "pcnetIOPortAPromRead", NULL, NULL, "PCNet aprom");
3667 if (VBOX_FAILURE(rc))
3668 return rc;
3669 rc = PDMDevHlpIOPortRegisterR0(pDevIns, Port + 0x10, 0x10, 0, "pcnetIOPortWrite",
3670 "pcnetIOPortRead", NULL, NULL, "PCNet");
3671 if (VBOX_FAILURE(rc))
3672 return rc;
3673 }
3674
3675 pData->IOPortBase = Port;
3676 return VINF_SUCCESS;
3677}
3678
3679
3680/**
3681 * Callback function for mapping the MMIO region.
3682 *
3683 * @return VBox status code.
3684 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
3685 * @param iRegion The region number.
3686 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
3687 * I/O port, else it's a physical address.
3688 * This address is *NOT* relative to pci_mem_base like earlier!
3689 * @param cb Region size.
3690 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
3691 */
3692static DECLCALLBACK(int) pcnetMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
3693 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
3694{
3695 PCNetState *pData = PCIDEV_2_PCNETSTATE(pPciDev);
3696 int rc;
3697
3698 Assert(enmType == PCI_ADDRESS_SPACE_MEM);
3699 Assert(cb >= PCNET_PNPMMIO_SIZE);
3700
3701 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
3702 rc = PDMDevHlpMMIORegister(pPciDev->pDevIns, GCPhysAddress, cb, pData,
3703 pcnetMMIOWrite, pcnetMMIORead, NULL, "PCNet");
3704 if (VBOX_FAILURE(rc))
3705 return rc;
3706 pData->MMIOBase = GCPhysAddress;
3707 return rc;
3708}
3709
3710
3711/**
3712 * PCNET status info callback.
3713 *
3714 * @param pDevIns The device instance.
3715 * @param pHlp The output helpers.
3716 * @param pszArgs The arguments.
3717 */
3718static DECLCALLBACK(void) pcnetInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3719{
3720 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3721 bool fRcvRing = false;
3722 bool fXmtRing = false;
3723
3724 /*
3725 * Parse args.
3726 */
3727 if (pszArgs)
3728 {
3729 fRcvRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "rcv");
3730 fXmtRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "xmt");
3731 }
3732
3733 /*
3734 * Show info.
3735 */
3736 pHlp->pfnPrintf(pHlp,
3737 "pcnet #%d: port=%RTiop mmio=%RGp mac-cfg=%.*Rhxs %s\n",
3738 pDevIns->iInstance,
3739 pData->IOPortBase, pData->MMIOBase, sizeof(pData->MacConfigured), &pData->MacConfigured,
3740 pData->fAm79C973 ? "Am79C973" : "Am79C970A", pData->fGCEnabled ? " GC" : "", pData->fR0Enabled ? " R0" : "");
3741
3742 PDMCritSectEnter(&pData->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
3743
3744 pHlp->pfnPrintf(pHlp,
3745 "CSR0=%#06x:\n",
3746 pData->aCSR[0]);
3747
3748 pHlp->pfnPrintf(pHlp,
3749 "CSR1=%#06x:\n",
3750 pData->aCSR[1]);
3751
3752 pHlp->pfnPrintf(pHlp,
3753 "CSR2=%#06x:\n",
3754 pData->aCSR[2]);
3755
3756 pHlp->pfnPrintf(pHlp,
3757 "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",
3758 pData->aCSR[3],
3759 !!(pData->aCSR[3] & RT_BIT(2)), !!(pData->aCSR[3] & RT_BIT(3)), !!(pData->aCSR[3] & RT_BIT(4)), CSR_LAPPEN(pData),
3760 CSR_DXSUFLO(pData), !!(pData->aCSR[3] & RT_BIT(8)), !!(pData->aCSR[3] & RT_BIT(9)), !!(pData->aCSR[3] & RT_BIT(10)),
3761 !!(pData->aCSR[3] & RT_BIT(11)), !!(pData->aCSR[3] & RT_BIT(12)), !!(pData->aCSR[3] & RT_BIT(14)));
3762
3763 pHlp->pfnPrintf(pHlp,
3764 "CSR4=%#06x: JABM=%d JAB=%d TXSTRM=%d TXSTRT=%d RCVCOOM=%d RCVCCO=%d UINT=%d UINTCMD=%d\n"
3765 " MFCOM=%d MFCO=%d ASTRP_RCV=%d APAD_XMT=%d DPOLL=%d TIMER=%d EMAPLUS=%d EN124=%d\n",
3766 pData->aCSR[4],
3767 !!(pData->aCSR[4] & RT_BIT( 0)), !!(pData->aCSR[4] & RT_BIT( 1)), !!(pData->aCSR[4] & RT_BIT( 2)), !!(pData->aCSR[4] & RT_BIT( 3)),
3768 !!(pData->aCSR[4] & RT_BIT( 4)), !!(pData->aCSR[4] & RT_BIT( 5)), !!(pData->aCSR[4] & RT_BIT( 6)), !!(pData->aCSR[4] & RT_BIT( 7)),
3769 !!(pData->aCSR[4] & RT_BIT( 8)), !!(pData->aCSR[4] & RT_BIT( 9)), !!(pData->aCSR[4] & RT_BIT(10)), !!(pData->aCSR[4] & RT_BIT(11)),
3770 !!(pData->aCSR[4] & RT_BIT(12)), !!(pData->aCSR[4] & RT_BIT(13)), !!(pData->aCSR[4] & RT_BIT(14)), !!(pData->aCSR[4] & RT_BIT(15)));
3771
3772 pHlp->pfnPrintf(pHlp,
3773 "CSR5=%#06x:\n",
3774 pData->aCSR[5]);
3775
3776 pHlp->pfnPrintf(pHlp,
3777 "CSR6=%#06x: RLEN=%#x* TLEN=%#x* [* encoded]\n",
3778 pData->aCSR[6],
3779 (pData->aCSR[6] >> 8) & 0xf, (pData->aCSR[6] >> 12) & 0xf);
3780
3781 pHlp->pfnPrintf(pHlp,
3782 "CSR8..11=%#06x,%#06x,%#06x,%#06x: LADRF=%#018llx\n",
3783 pData->aCSR[8], pData->aCSR[9], pData->aCSR[10], pData->aCSR[11],
3784 (uint64_t)(pData->aCSR[ 8] & 0xffff)
3785 | (uint64_t)(pData->aCSR[ 9] & 0xffff) << 16
3786 | (uint64_t)(pData->aCSR[10] & 0xffff) << 32
3787 | (uint64_t)(pData->aCSR[11] & 0xffff) << 48);
3788
3789 pHlp->pfnPrintf(pHlp,
3790 "CSR12..14=%#06x,%#06x,%#06x: PADR=%02x:%02x:%02x:%02x:%02x:%02x (Current MAC Address)\n",
3791 pData->aCSR[12], pData->aCSR[13], pData->aCSR[14],
3792 pData->aCSR[12] & 0xff,
3793 (pData->aCSR[12] >> 8) & 0xff,
3794 pData->aCSR[13] & 0xff,
3795 (pData->aCSR[13] >> 8) & 0xff,
3796 pData->aCSR[14] & 0xff,
3797 (pData->aCSR[14] >> 8) & 0xff);
3798
3799 pHlp->pfnPrintf(pHlp,
3800 "CSR15=%#06x: DXR=%d DTX=%d LOOP=%d DXMTFCS=%d FCOLL=%d DRTY=%d INTL=%d PORTSEL=%d LTR=%d\n"
3801 " MENDECL=%d DAPC=%d DLNKTST=%d DRCVPV=%d DRCVBC=%d PROM=%d\n",
3802 pData->aCSR[15],
3803 !!(pData->aCSR[15] & RT_BIT( 0)), !!(pData->aCSR[15] & RT_BIT( 1)), !!(pData->aCSR[15] & RT_BIT( 2)), !!(pData->aCSR[15] & RT_BIT( 3)),
3804 !!(pData->aCSR[15] & RT_BIT( 4)), !!(pData->aCSR[15] & RT_BIT( 5)), !!(pData->aCSR[15] & RT_BIT( 6)), (pData->aCSR[15] >> 7) & 3,
3805 !!(pData->aCSR[15] & RT_BIT( 9)), !!(pData->aCSR[15] & RT_BIT(10)), !!(pData->aCSR[15] & RT_BIT(11)),
3806 !!(pData->aCSR[15] & RT_BIT(12)), !!(pData->aCSR[15] & RT_BIT(13)), !!(pData->aCSR[15] & RT_BIT(14)), !!(pData->aCSR[15] & RT_BIT(15)));
3807
3808 pHlp->pfnPrintf(pHlp,
3809 "CSR46=%#06x: POLL=%#06x (Poll Time Counter)\n",
3810 pData->aCSR[46], pData->aCSR[46] & 0xffff);
3811
3812 pHlp->pfnPrintf(pHlp,
3813 "CSR47=%#06x: POLLINT=%#06x (Poll Time Interval)\n",
3814 pData->aCSR[47], pData->aCSR[47] & 0xffff);
3815
3816 pHlp->pfnPrintf(pHlp,
3817 "CSR58=%#06x: SWSTYLE=%d %s SSIZE32=%d CSRPCNET=%d APERRENT=%d\n",
3818 pData->aCSR[58],
3819 pData->aCSR[58] & 0x7f,
3820 (pData->aCSR[58] & 0x7f) == 0 ? "C-LANCE / PCnet-ISA"
3821 : (pData->aCSR[58] & 0x7f) == 1 ? "ILACC"
3822 : (pData->aCSR[58] & 0x7f) == 2 ? "PCNet-PCI II"
3823 : (pData->aCSR[58] & 0x7f) == 3 ? "PCNet-PCI II controller"
3824 : "!!reserved!!",
3825 !!(pData->aCSR[58] & RT_BIT(8)), !!(pData->aCSR[58] & RT_BIT(9)), !!(pData->aCSR[58] & RT_BIT(10)));
3826
3827 pHlp->pfnPrintf(pHlp,
3828 "CSR112=%04RX32: MFC=%04x (Missed receive Frame Count)\n",
3829 pData->aCSR[112], pData->aCSR[112] & 0xffff);
3830
3831 pHlp->pfnPrintf(pHlp,
3832 "CSR122=%04RX32: RCVALGN=%04x (Receive Frame Align)\n",
3833 pData->aCSR[122], !!(pData->aCSR[122] & RT_BIT(0)));
3834
3835 pHlp->pfnPrintf(pHlp,
3836 "CSR124=%04RX32: RPA=%04x (Runt Packet Accept)\n",
3837 pData->aCSR[122], !!(pData->aCSR[122] & RT_BIT(3)));
3838
3839
3840 /*
3841 * Dump the receive ring.
3842 */
3843 pHlp->pfnPrintf(pHlp,
3844 "RCVRL=%04x RCVRC=%04x GCRDRA=%RX32 \n"
3845 "CRDA=%08RX32 CRBA=%08RX32 CRBC=%03x CRST=%04x\n"
3846 "NRDA=%08RX32 NRBA=%08RX32 NRBC=%03x NRST=%04x\n"
3847 "NNRDA=%08RX32\n"
3848 ,
3849 CSR_RCVRL(pData), CSR_RCVRC(pData), pData->GCRDRA,
3850 CSR_CRDA(pData), CSR_CRBA(pData), CSR_CRBC(pData), CSR_CRST(pData),
3851 CSR_NRDA(pData), CSR_NRBA(pData), CSR_NRBC(pData), CSR_NRST(pData),
3852 CSR_NNRD(pData));
3853 if (fRcvRing)
3854 {
3855 const unsigned cb = 1 << pData->iLog2DescSize;
3856 RTGCPHYS32 GCPhys = pData->GCRDRA;
3857 unsigned i = CSR_RCVRL(pData);
3858 while (i-- > 0)
3859 {
3860 RMD rmd;
3861 pcnetRmdLoad(pData, &rmd, PHYSADDR(pData, GCPhys));
3862 pHlp->pfnPrintf(pHlp,
3863 "%04x %RGp:%c%c RBADR=%08RX32 BCNT=%03x MCNT=%03x "
3864 "OWN=%d ERR=%d FRAM=%d OFLO=%d CRC=%d BUFF=%d STP=%d ENP=%d BPE=%d "
3865 "PAM=%d LAFM=%d BAM=%d RCC=%02x RPC=%02x ONES=%#x ZEROS=%d\n",
3866 i, GCPhys, i + 1 == CSR_RCVRC(pData) ? '*' : ' ', GCPhys == CSR_CRDA(pData) ? '*' : ' ',
3867 rmd.rmd0.rbadr, 4096 - rmd.rmd1.bcnt, rmd.rmd2.mcnt,
3868 rmd.rmd1.own, rmd.rmd1.err, rmd.rmd1.fram, rmd.rmd1.oflo, rmd.rmd1.crc, rmd.rmd1.buff,
3869 rmd.rmd1.stp, rmd.rmd1.enp, rmd.rmd1.bpe,
3870 rmd.rmd1.pam, rmd.rmd1.lafm, rmd.rmd1.bam, rmd.rmd2.rcc, rmd.rmd2.rpc,
3871 rmd.rmd1.ones, rmd.rmd2.zeros);
3872
3873 GCPhys += cb;
3874 }
3875 }
3876
3877 /*
3878 * Dump the transmit ring.
3879 */
3880 pHlp->pfnPrintf(pHlp,
3881 "XMTRL=%04x XMTRC=%04x GCTDRA=%08RX32 BADX=%08RX32\n"
3882 "PXDA=%08RX32 PXBC=%03x PXST=%04x\n"
3883 "CXDA=%08RX32 CXBA=%08RX32 CXBC=%03x CXST=%04x\n"
3884 "NXDA=%08RX32 NXBA=%08RX32 NXBC=%03x NXST=%04x\n"
3885 "NNXDA=%08RX32\n"
3886 ,
3887 CSR_XMTRL(pData), CSR_XMTRC(pData),
3888 pData->GCTDRA, CSR_BADX(pData),
3889 CSR_PXDA(pData), CSR_PXBC(pData), CSR_PXST(pData),
3890 CSR_CXDA(pData), CSR_CXBA(pData), CSR_CXBC(pData), CSR_CXST(pData),
3891 CSR_NXDA(pData), CSR_NXBA(pData), CSR_NXBC(pData), CSR_NXST(pData),
3892 CSR_NNXD(pData));
3893 if (fXmtRing)
3894 {
3895 const unsigned cb = 1 << pData->iLog2DescSize;
3896 RTGCPHYS32 GCPhys = pData->GCTDRA;
3897 unsigned i = CSR_XMTRL(pData);
3898 while (i-- > 0)
3899 {
3900 TMD tmd;
3901 pcnetTmdLoad(pData, &tmd, PHYSADDR(pData, GCPhys));
3902 pHlp->pfnPrintf(pHlp,
3903 "%04x %RGp:%c%c TBADR=%08RX32 BCNT=%03x OWN=%d "
3904 "ERR=%d NOFCS=%d LTINT=%d ONE=%d DEF=%d STP=%d ENP=%d BPE=%d "
3905 "BUFF=%d UFLO=%d EXDEF=%d LCOL=%d LCAR=%d RTRY=%d TDR=%03x TRC=%#x ONES=%#x\n"
3906 ,
3907 i, GCPhys, i + 1 == CSR_XMTRC(pData) ? '*' : ' ', GCPhys == CSR_CXDA(pData) ? '*' : ' ',
3908 tmd.tmd0.tbadr, 4096 - tmd.tmd1.bcnt,
3909 tmd.tmd2.tdr,
3910 tmd.tmd2.trc,
3911 tmd.tmd1.own,
3912 tmd.tmd1.err,
3913 tmd.tmd1.nofcs,
3914 tmd.tmd1.ltint,
3915 tmd.tmd1.one,
3916 tmd.tmd1.def,
3917 tmd.tmd1.stp,
3918 tmd.tmd1.enp,
3919 tmd.tmd1.bpe,
3920 tmd.tmd2.buff,
3921 tmd.tmd2.uflo,
3922 tmd.tmd2.exdef,
3923 tmd.tmd2.lcol,
3924 tmd.tmd2.lcar,
3925 tmd.tmd2.rtry,
3926 tmd.tmd2.tdr,
3927 tmd.tmd2.trc,
3928 tmd.tmd1.ones);
3929
3930 GCPhys += cb;
3931 }
3932 }
3933
3934 PDMCritSectLeave(&pData->CritSect);
3935}
3936
3937
3938/**
3939 * Prepares for state saving.
3940 * We must stop the RX process to prevent altering of the main memory after saving.
3941 *
3942 * @returns VBox status code.
3943 * @param pDevIns The device instance.
3944 * @param pSSMHandle The handle to save the state to.
3945 */
3946static DECLCALLBACK(int) pcnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
3947{
3948 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3949
3950 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
3951
3952 pData->fSaving = true;
3953 /* From now on drop all received packets to prevent altering of main memory after
3954 * pgmR3Save() was called but before the RX thread is terminated */
3955
3956 PDMCritSectLeave(&pData->CritSect);
3957 return VINF_SUCCESS;
3958}
3959
3960
3961/**
3962 * Saves a state of the PC-Net II device.
3963 *
3964 * @returns VBox status code.
3965 * @param pDevIns The device instance.
3966 * @param pSSMHandle The handle to save the state to.
3967 */
3968static DECLCALLBACK(int) pcnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
3969{
3970 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
3971 int rc = VINF_SUCCESS;
3972
3973 SSMR3PutBool(pSSMHandle, pData->fLinkUp);
3974 SSMR3PutU32(pSSMHandle, pData->u32RAP);
3975 SSMR3PutS32(pSSMHandle, pData->iISR);
3976 SSMR3PutU32(pSSMHandle, pData->u32Lnkst);
3977 SSMR3PutGCPhys32(pSSMHandle, pData->GCRDRA);
3978 SSMR3PutGCPhys32(pSSMHandle, pData->GCTDRA);
3979 SSMR3PutMem(pSSMHandle, pData->aPROM, sizeof(pData->aPROM));
3980 SSMR3PutMem(pSSMHandle, pData->aCSR, sizeof(pData->aCSR));
3981 SSMR3PutMem(pSSMHandle, pData->aBCR, sizeof(pData->aBCR));
3982 SSMR3PutMem(pSSMHandle, pData->aMII, sizeof(pData->aMII));
3983 SSMR3PutU16(pSSMHandle, pData->u16CSR0LastSeenByGuest);
3984 SSMR3PutU64(pSSMHandle, pData->u64LastPoll);
3985 SSMR3PutMem(pSSMHandle, &pData->MacConfigured, sizeof(pData->MacConfigured));
3986 SSMR3PutBool(pSSMHandle, pData->fAm79C973);
3987 SSMR3PutU32(pSSMHandle, pData->u32LinkSpeed);
3988#ifdef PCNET_NO_POLLING
3989 return VINF_SUCCESS;
3990#else
3991 rc = TMR3TimerSave(pData->CTXSUFF(pTimerPoll), pSSMHandle);
3992 if (VBOX_FAILURE(rc))
3993 return rc;
3994#endif
3995 if (pData->fAm79C973)
3996 rc = TMR3TimerSave(pData->CTXSUFF(pTimerSoftInt), pSSMHandle);
3997 return rc;
3998}
3999
4000
4001/**
4002 * Cleanup after saving.
4003 *
4004 * @returns VBox status code.
4005 * @param pDevIns The device instance.
4006 * @param pSSMHandle The handle to save the state to.
4007 */
4008static DECLCALLBACK(int) pcnetSaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
4009{
4010 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
4011
4012 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
4013 pData->fSaving = false;
4014 PDMCritSectLeave(&pData->CritSect);
4015 return VINF_SUCCESS;
4016}
4017
4018
4019/**
4020 * Loads a saved PC-Net II device state.
4021 *
4022 * @returns VBox status code.
4023 * @param pDevIns The device instance.
4024 * @param pSSMHandle The handle to the saved state.
4025 * @param u32Version The data unit version number.
4026 */
4027static DECLCALLBACK(int) pcnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
4028{
4029 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
4030 PDMMAC Mac;
4031 if (SSM_VERSION_MAJOR_CHANGED(u32Version, PCNET_SAVEDSTATE_VERSION))
4032 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4033
4034 /* restore data */
4035 SSMR3GetBool(pSSMHandle, &pData->fLinkUp);
4036 SSMR3GetU32(pSSMHandle, &pData->u32RAP);
4037 SSMR3GetS32(pSSMHandle, &pData->iISR);
4038 SSMR3GetU32(pSSMHandle, &pData->u32Lnkst);
4039 SSMR3GetGCPhys32(pSSMHandle, &pData->GCRDRA);
4040 SSMR3GetGCPhys32(pSSMHandle, &pData->GCTDRA);
4041 SSMR3GetMem(pSSMHandle, &pData->aPROM, sizeof(pData->aPROM));
4042 SSMR3GetMem(pSSMHandle, &pData->aCSR, sizeof(pData->aCSR));
4043 SSMR3GetMem(pSSMHandle, &pData->aBCR, sizeof(pData->aBCR));
4044 SSMR3GetMem(pSSMHandle, &pData->aMII, sizeof(pData->aMII));
4045 SSMR3GetU16(pSSMHandle, &pData->u16CSR0LastSeenByGuest);
4046 SSMR3GetU64(pSSMHandle, &pData->u64LastPoll);
4047 SSMR3GetMem(pSSMHandle, &Mac, sizeof(Mac));
4048 Assert( !memcmp(&Mac, &pData->MacConfigured, sizeof(Mac))
4049 || SSMR3HandleGetAfter(pSSMHandle) == SSMAFTER_DEBUG_IT);
4050 SSMR3GetBool(pSSMHandle, &pData->fAm79C973);
4051 SSMR3GetU32(pSSMHandle, &pData->u32LinkSpeed);
4052#ifndef PCNET_NO_POLLING
4053 TMR3TimerLoad(pData->CTXSUFF(pTimerPoll), pSSMHandle);
4054#endif
4055 if (pData->fAm79C973)
4056 {
4057 if ( SSM_VERSION_MAJOR(u32Version) > 0
4058 || SSM_VERSION_MINOR(u32Version) >= 8)
4059 TMR3TimerLoad(pData->CTXSUFF(pTimerSoftInt), pSSMHandle);
4060 }
4061
4062 pData->iLog2DescSize = BCR_SWSTYLE(pData)
4063 ? 4
4064 : 3;
4065 pData->GCUpperPhys = BCR_SSIZE32(pData)
4066 ? 0
4067 : (0xff00 & (uint32_t)pData->aCSR[2]) << 16;
4068
4069 /* update promiscuous mode. */
4070 if (pData->pDrv)
4071 pData->pDrv->pfnSetPromiscuousMode(pData->pDrv, CSR_PROM(pData));
4072
4073#ifdef PCNET_NO_POLLING
4074 /* Enable physical monitoring again (!) */
4075 pcnetUpdateRingHandlers(pData);
4076#endif
4077 /* Indicate link down to the guest OS that all network connections have been lost. */
4078 if (pData->fLinkUp)
4079 {
4080 pData->fLinkTempDown = true;
4081 pData->cLinkDownReported = 0;
4082 pData->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4083 pData->Led.Asserted.s.fError = pData->Led.Actual.s.fError = 1;
4084 return TMTimerSetMillies(pData->pTimerRestore, 5000);
4085 }
4086 return VINF_SUCCESS;
4087}
4088
4089
4090/**
4091 * Queries an interface to the driver.
4092 *
4093 * @returns Pointer to interface.
4094 * @returns NULL if the interface was not supported by the driver.
4095 * @param pInterface Pointer to this interface structure.
4096 * @param enmInterface The requested interface identification.
4097 * @thread Any thread.
4098 */
4099static DECLCALLBACK(void *) pcnetQueryInterface(struct PDMIBASE *pInterface, PDMINTERFACE enmInterface)
4100{
4101 PCNetState *pData = (PCNetState *)((uintptr_t)pInterface - RT_OFFSETOF(PCNetState, IBase));
4102 Assert(&pData->IBase == pInterface);
4103 switch (enmInterface)
4104 {
4105 case PDMINTERFACE_BASE:
4106 return &pData->IBase;
4107 case PDMINTERFACE_NETWORK_PORT:
4108 return &pData->INetworkPort;
4109 case PDMINTERFACE_NETWORK_CONFIG:
4110 return &pData->INetworkConfig;
4111 case PDMINTERFACE_LED_PORTS:
4112 return &pData->ILeds;
4113 default:
4114 return NULL;
4115 }
4116}
4117
4118/** Converts a pointer to PCNetState::INetworkPort to a PCNetState pointer. */
4119#define INETWORKPORT_2_DATA(pInterface) ( (PCNetState *)((uintptr_t)pInterface - RT_OFFSETOF(PCNetState, INetworkPort)) )
4120
4121
4122/**
4123 * Check if the device/driver can receive data now.
4124 * This must be called before the pfnRecieve() method is called.
4125 *
4126 * @returns Number of bytes the driver can receive.
4127 * @param pInterface Pointer to the interface structure containing the called function pointer.
4128 * @thread EMT
4129 */
4130static DECLCALLBACK(size_t) pcnetCanReceive(PPDMINETWORKPORT pInterface)
4131{
4132 size_t cb;
4133 int rc;
4134 PCNetState *pData = INETWORKPORT_2_DATA(pInterface);
4135
4136 rc = PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
4137 AssertReleaseRC(rc);
4138
4139 cb = pcnetCanReceiveNoSync(pData);
4140
4141 PDMCritSectLeave(&pData->CritSect);
4142 return cb;
4143}
4144
4145
4146/**
4147 * Receive data from the network.
4148 *
4149 * @returns VBox status code.
4150 * @param pInterface Pointer to the interface structure containing the called function pointer.
4151 * @param pvBuf The available data.
4152 * @param cb Number of bytes available in the buffer.
4153 * @thread EMT
4154 */
4155static DECLCALLBACK(int) pcnetReceive(PPDMINETWORKPORT pInterface, const void *pvBuf, size_t cb)
4156{
4157 PCNetState *pData = INETWORKPORT_2_DATA(pInterface);
4158 int rc;
4159
4160 STAM_PROFILE_ADV_START(&pData->StatReceive, a);
4161 rc = PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
4162 AssertReleaseRC(rc);
4163
4164 if (!pData->fSaving)
4165 {
4166 if (cb > 70) /* unqualified guess */
4167 pData->Led.Asserted.s.fReading = pData->Led.Actual.s.fReading = 1;
4168 pcnetReceiveNoSync(pData, (const uint8_t *)pvBuf, cb);
4169 pData->Led.Actual.s.fReading = 0;
4170 }
4171 /* otherwise junk the data to Nirwana. */
4172
4173 PDMCritSectLeave(&pData->CritSect);
4174 STAM_PROFILE_ADV_STOP(&pData->StatReceive, a);
4175
4176 return VINF_SUCCESS;
4177}
4178
4179/** Converts a pointer to PCNetState::INetworkConfig to a PCNetState pointer. */
4180#define INETWORKCONFIG_2_DATA(pInterface) ( (PCNetState *)((uintptr_t)pInterface - RT_OFFSETOF(PCNetState, INetworkConfig)) )
4181
4182
4183/**
4184 * Gets the current Media Access Control (MAC) address.
4185 *
4186 * @returns VBox status code.
4187 * @param pInterface Pointer to the interface structure containing the called function pointer.
4188 * @param pMac Where to store the MAC address.
4189 * @thread EMT
4190 */
4191static DECLCALLBACK(int) pcnetGetMac(PPDMINETWORKCONFIG pInterface, PPDMMAC *pMac)
4192{
4193 PCNetState *pData = INETWORKCONFIG_2_DATA(pInterface);
4194 memcpy(pMac, pData->aPROM, sizeof(*pMac));
4195 return VINF_SUCCESS;
4196}
4197
4198
4199/**
4200 * Gets the new link state.
4201 *
4202 * @returns The current link state.
4203 * @param pInterface Pointer to the interface structure containing the called function pointer.
4204 * @thread EMT
4205 */
4206static DECLCALLBACK(PDMNETWORKLINKSTATE) pcnetGetLinkState(PPDMINETWORKCONFIG pInterface)
4207{
4208 PCNetState *pData = INETWORKCONFIG_2_DATA(pInterface);
4209 if (pData->fLinkUp && !pData->fLinkTempDown)
4210 return PDMNETWORKLINKSTATE_UP;
4211 if (!pData->fLinkUp)
4212 return PDMNETWORKLINKSTATE_DOWN;
4213 if (pData->fLinkTempDown)
4214 return PDMNETWORKLINKSTATE_DOWN_RESUME;
4215 AssertMsgFailed(("Invalid link state!\n"));
4216 return PDMNETWORKLINKSTATE_INVALID;
4217}
4218
4219
4220/**
4221 * Sets the new link state.
4222 *
4223 * @returns VBox status code.
4224 * @param pInterface Pointer to the interface structure containing the called function pointer.
4225 * @param enmState The new link state
4226 * @thread EMT
4227 */
4228static DECLCALLBACK(int) pcnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
4229{
4230 PCNetState *pData = INETWORKCONFIG_2_DATA(pInterface);
4231 bool fLinkUp;
4232 if ( enmState != PDMNETWORKLINKSTATE_DOWN
4233 && enmState != PDMNETWORKLINKSTATE_UP)
4234 {
4235 AssertMsgFailed(("Invalid parameter enmState=%d\n", enmState));
4236 return VERR_INVALID_PARAMETER;
4237 }
4238
4239 /* has the state changed? */
4240 fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
4241 if (pData->fLinkUp != fLinkUp)
4242 {
4243 pData->fLinkUp = fLinkUp;
4244 if (fLinkUp)
4245 {
4246 /* connect */
4247 pData->aCSR[0] &= ~(RT_BIT(15) | RT_BIT(13)); /* ERR | CERR - probably not 100% correct either... */
4248 pData->Led.Actual.s.fError = 0;
4249 }
4250 else
4251 {
4252 /* disconnect */
4253 pData->cLinkDownReported = 0;
4254 pData->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
4255 pData->Led.Asserted.s.fError = pData->Led.Actual.s.fError = 1;
4256 }
4257 Assert(!PDMCritSectIsOwner(&pData->CritSect));
4258 pData->pDrv->pfnNotifyLinkChanged(pData->pDrv, enmState);
4259 }
4260 return VINF_SUCCESS;
4261}
4262
4263
4264/**
4265 * Gets the pointer to the status LED of a unit.
4266 *
4267 * @returns VBox status code.
4268 * @param pInterface Pointer to the interface structure containing the called function pointer.
4269 * @param iLUN The unit which status LED we desire.
4270 * @param ppLed Where to store the LED pointer.
4271 */
4272static DECLCALLBACK(int) pcnetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4273{
4274 PCNetState *pData = (PCNetState *)( (uintptr_t)pInterface - RT_OFFSETOF(PCNetState, ILeds) );
4275 if (iLUN == 0)
4276 {
4277 *ppLed = &pData->Led;
4278 return VINF_SUCCESS;
4279 }
4280 return VERR_PDM_LUN_NOT_FOUND;
4281}
4282
4283
4284/**
4285 * @copydoc FNPDMDEVRESET
4286 */
4287static DECLCALLBACK(void) pcnetReset(PPDMDEVINS pDevIns)
4288{
4289 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
4290 if (pData->fLinkTempDown)
4291 {
4292 pData->cLinkDownReported = 0x10000;
4293 TMTimerStop(pData->pTimerRestore);
4294 pcnetTimerRestore(pDevIns, pData->pTimerRestore);
4295 }
4296
4297 /** @todo How to flush the queues? */
4298 pcnetHardReset(pData);
4299}
4300
4301
4302/**
4303 * @copydoc FNPDMDEVRELOCATE
4304 */
4305static DECLCALLBACK(void) pcnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4306{
4307 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
4308 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
4309 pData->pXmitQueueGC = PDMQueueGCPtr(pData->pXmitQueueHC);
4310 pData->pCanRxQueueGC = PDMQueueGCPtr(pData->pCanRxQueueHC);
4311#ifdef PCNET_NO_POLLING
4312 *(RTHCUINTPTR *)&pData->pfnEMInterpretInstructionGC += offDelta;
4313#else
4314 pData->pTimerPollGC = TMTimerGCPtr(pData->pTimerPollHC);
4315#endif
4316 if (pData->fAm79C973)
4317 pData->pTimerSoftIntGC = TMTimerGCPtr(pData->pTimerSoftIntHC);
4318}
4319
4320
4321/**
4322 * Destruct a device instance.
4323 *
4324 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
4325 * resources can be freed correctly.
4326 *
4327 * @returns VBox status.
4328 * @param pDevIns The device instance data.
4329 */
4330static DECLCALLBACK(int) pcnetDestruct(PPDMDEVINS pDevIns)
4331{
4332 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
4333
4334 if (PDMCritSectIsInitialized(&pData->CritSect))
4335 {
4336 /*
4337 * At this point the send thread is suspended and will not enter
4338 * this module again. So, no coordination is needed here and PDM
4339 * will take care of terminating and cleaning up the thread.
4340 */
4341 RTSemEventDestroy(pData->hSendEventSem);
4342 pData->hSendEventSem = NIL_RTSEMEVENT;
4343 PDMR3CritSectDelete(&pData->CritSect);
4344 }
4345#ifdef PCNET_QUEUE_SEND_PACKETS
4346 if (pData->pXmitRingBuffer)
4347 RTMemFree(pData->pXmitRingBuffer[0]);
4348#endif
4349 return VINF_SUCCESS;
4350}
4351
4352
4353/**
4354 * Construct a device instance for a VM.
4355 *
4356 * @returns VBox status.
4357 * @param pDevIns The device instance data.
4358 * If the registration structure is needed, pDevIns->pDevReg points to it.
4359 * @param iInstance Instance number. Use this to figure out which registers and such to use.
4360 * The device number is also found in pDevIns->iInstance, but since it's
4361 * likely to be freqently used PDM passes it as parameter.
4362 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
4363 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
4364 * iInstance it's expected to be used a bit in this function.
4365 */
4366static DECLCALLBACK(int) pcnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
4367{
4368 PCNetState *pData = PDMINS2DATA(pDevIns, PCNetState *);
4369 PPDMIBASE pBase;
4370 char szTmp[128];
4371 int rc;
4372
4373 /* up to four instances are supported */
4374 Assert((iInstance >= 0) && (iInstance < 4));
4375
4376 Assert(RT_ELEMENTS(pData->aBCR) == BCR_MAX_RAP);
4377 Assert(RT_ELEMENTS(pData->aMII) == MII_MAX_REG);
4378 Assert(sizeof(pData->abSendBuf) == RT_ALIGN_Z(sizeof(pData->abSendBuf), 16));
4379
4380 /*
4381 * Validate configuration.
4382 */
4383 if (!CFGMR3AreValuesValid(pCfgHandle, "MAC\0CableConnected\0Am79C973\0LineSpeed\0GCEnabled\0R0Enabled\0"))
4384 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4385 N_("Invalid configuraton for pcnet device"));
4386
4387 /*
4388 * Read the configuration.
4389 */
4390 rc = CFGMR3QueryBytes(pCfgHandle, "MAC", &pData->MacConfigured, sizeof(pData->MacConfigured));
4391 if (VBOX_FAILURE(rc))
4392 return PDMDEV_SET_ERROR(pDevIns, rc,
4393 N_("Configuration error: Failed to get the \"MAC\" value"));
4394 rc = CFGMR3QueryBool(pCfgHandle, "CableConnected", &pData->fLinkUp);
4395 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4396 pData->fLinkUp = true;
4397 else if (VBOX_FAILURE(rc))
4398 return PDMDEV_SET_ERROR(pDevIns, rc,
4399 N_("Configuration error: Failed to get the \"CableConnected\" value"));
4400
4401 rc = CFGMR3QueryBool(pCfgHandle, "Am79C973", &pData->fAm79C973);
4402 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4403 pData->fAm79C973 = false;
4404 else if (VBOX_FAILURE(rc))
4405 return PDMDEV_SET_ERROR(pDevIns, rc,
4406 N_("Configuration error: Failed to get the \"Am79C973\" value"));
4407
4408 rc = CFGMR3QueryU32(pCfgHandle, "LineSpeed", &pData->u32LinkSpeed);
4409 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4410 pData->u32LinkSpeed = 100000; /* 100 Mbps (in kbps units)*/
4411 else if (VBOX_FAILURE(rc))
4412 return PDMDEV_SET_ERROR(pDevIns, rc,
4413 N_("Configuration error: Failed to get the \"LineSpeed\" value"));
4414
4415#ifdef PCNET_GC_ENABLED
4416 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &pData->fGCEnabled);
4417 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4418 pData->fGCEnabled = true;
4419 else if (VBOX_FAILURE(rc))
4420 return PDMDEV_SET_ERROR(pDevIns, rc,
4421 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
4422
4423 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &pData->fR0Enabled);
4424 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4425 pData->fR0Enabled = true;
4426 else if (VBOX_FAILURE(rc))
4427 return PDMDEV_SET_ERROR(pDevIns, rc,
4428 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
4429
4430#else /* !PCNET_GC_ENABLED */
4431 pData->fGCEnabled = false;
4432 pData->fR0Enabled = false;
4433#endif /* !PCNET_GC_ENABLED */
4434
4435
4436 /*
4437 * Initialize data (most of it anyway).
4438 */
4439 pData->pDevInsHC = pDevIns;
4440 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
4441 pData->Led.u32Magic = PDMLED_MAGIC;
4442 /* IBase */
4443 pData->IBase.pfnQueryInterface = pcnetQueryInterface;
4444 /* INeworkPort */
4445 pData->INetworkPort.pfnCanReceive = pcnetCanReceive;
4446 pData->INetworkPort.pfnReceive = pcnetReceive;
4447 /* INetworkConfig */
4448 pData->INetworkConfig.pfnGetMac = pcnetGetMac;
4449 pData->INetworkConfig.pfnGetLinkState = pcnetGetLinkState;
4450 pData->INetworkConfig.pfnSetLinkState = pcnetSetLinkState;
4451 /* ILeds */
4452 pData->ILeds.pfnQueryStatusLed = pcnetQueryStatusLed;
4453
4454 /* PCI Device */
4455 PCIDevSetVendorId(&pData->PciDev, 0x1022);
4456 PCIDevSetDeviceId(&pData->PciDev, 0x2000);
4457 pData->PciDev.config[0x04] = 0x07; /* command */
4458 pData->PciDev.config[0x05] = 0x00;
4459 pData->PciDev.config[0x06] = 0x80; /* status */
4460 pData->PciDev.config[0x07] = 0x02;
4461 pData->PciDev.config[0x08] = pData->fAm79C973 ? 0x40 : 0x10; /* revision */
4462 pData->PciDev.config[0x09] = 0x00;
4463 pData->PciDev.config[0x0a] = 0x00; /* ethernet network controller */
4464 pData->PciDev.config[0x0b] = 0x02;
4465 pData->PciDev.config[0x0e] = 0x00; /* header_type */
4466
4467 pData->PciDev.config[0x10] = 0x01; /* IO Base */
4468 pData->PciDev.config[0x11] = 0x00;
4469 pData->PciDev.config[0x12] = 0x00;
4470 pData->PciDev.config[0x13] = 0x00;
4471 pData->PciDev.config[0x14] = 0x00; /* MMIO Base */
4472 pData->PciDev.config[0x15] = 0x00;
4473 pData->PciDev.config[0x16] = 0x00;
4474 pData->PciDev.config[0x17] = 0x00;
4475
4476 /* subsystem and subvendor IDs */
4477 pData->PciDev.config[0x2c] = 0x22; /* subsystem vendor id */
4478 pData->PciDev.config[0x2d] = 0x10;
4479 pData->PciDev.config[0x2e] = 0x00; /* subsystem id */
4480 pData->PciDev.config[0x2f] = 0x20;
4481 pData->PciDev.config[0x3d] = 1; /* interrupt pin 0 */
4482 pData->PciDev.config[0x3e] = 0x06;
4483 pData->PciDev.config[0x3f] = 0xff;
4484
4485 /*
4486 * Register the PCI device, its I/O regions, the timer and the saved state item.
4487 */
4488 rc = PDMDevHlpPCIRegister(pDevIns, &pData->PciDev);
4489 if (VBOX_FAILURE(rc))
4490 return rc;
4491 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, PCNET_IOPORT_SIZE,
4492 PCI_ADDRESS_SPACE_IO, pcnetIOPortMap);
4493 if (VBOX_FAILURE(rc))
4494 return rc;
4495 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, PCNET_PNPMMIO_SIZE,
4496 PCI_ADDRESS_SPACE_MEM, pcnetMMIOMap);
4497 if (VBOX_FAILURE(rc))
4498 return rc;
4499
4500#ifdef PCNET_NO_POLLING
4501 rc = PDMR3GetSymbolR0Lazy(PDMDevHlpGetVM(pDevIns), NULL, "EMInterpretInstruction", &pData->pfnEMInterpretInstructionR0);
4502 if (VBOX_SUCCESS(rc))
4503 {
4504 /*
4505 * Resolve the GC handler.
4506 */
4507 RTGCPTR pfnHandlerGC;
4508 rc = PDMR3GetSymbolGCLazy(PDMDevHlpGetVM(pDevIns), NULL, "EMInterpretInstruction", (RTGCPTR *)&pData->pfnEMInterpretInstructionGC);
4509 }
4510 if (VBOX_FAILURE(rc))
4511 {
4512 AssertMsgFailed(("PDMR3GetSymbolGCLazy -> %Vrc\n", rc));
4513 return rc;
4514 }
4515#else
4516 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimer,
4517 "PCNet Poll Timer", &pData->pTimerPollHC);
4518 if (VBOX_FAILURE(rc))
4519 {
4520 AssertMsgFailed(("pfnTMTimerCreate pcnetTimer -> %Vrc\n", rc));
4521 return rc;
4522 }
4523#endif
4524 if (pData->fAm79C973)
4525 {
4526 /* Software Interrupt timer */
4527 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimerSoftInt,
4528 "PCNet SoftInt Timer", &pData->pTimerSoftIntHC);
4529 if (VBOX_FAILURE(rc))
4530 {
4531 AssertMsgFailed(("pfnTMTimerCreate pcnetTimerSoftInt -> %Vrc\n", rc));
4532 return rc;
4533 }
4534 }
4535 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetTimerRestore,
4536 "PCNet Restore Timer", &pData->pTimerRestore);
4537 if (VBOX_FAILURE(rc))
4538 {
4539 AssertMsgFailed(("pfnTMTimerCreate pcnetTimerRestore -> %Vrc\n", rc));
4540 return rc;
4541 }
4542/** @todo r=bird: we're not locking down pcnet properly during saving and loading! */
4543 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance,
4544 PCNET_SAVEDSTATE_VERSION, sizeof(*pData),
4545 pcnetSavePrep, pcnetSaveExec, pcnetSaveDone,
4546 NULL, pcnetLoadExec, NULL);
4547 if (VBOX_FAILURE(rc))
4548 return rc;
4549
4550 /*
4551 * Initialize critical section.
4552 * This must of course be done before attaching drivers or anything else which can call us back..
4553 */
4554 char szName[24];
4555 RTStrPrintf(szName, sizeof(szName), "PCNet#%d", iInstance);
4556 rc = PDMDevHlpCritSectInit(pDevIns, &pData->CritSect, szName);
4557 if (VBOX_FAILURE(rc))
4558 return rc;
4559
4560 /*
4561 * Create the transmit queue.
4562 */
4563 rc = PDMDevHlpPDMQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
4564 pcnetXmitQueueConsumer, true, &pData->pXmitQueueHC);
4565 if (VBOX_FAILURE(rc))
4566 return rc;
4567 pData->pXmitQueueGC = PDMQueueGCPtr(pData->pXmitQueueHC);
4568
4569 /*
4570 * Create the RX notifer signaller.
4571 */
4572 rc = PDMDevHlpPDMQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
4573 pcnetCanRxQueueConsumer, true, &pData->pCanRxQueueHC);
4574 if (VBOX_FAILURE(rc))
4575 return rc;
4576 pData->pCanRxQueueGC = PDMQueueGCPtr(pData->pCanRxQueueHC);
4577
4578 /*
4579 * Register the info item.
4580 */
4581 RTStrPrintf(szTmp, sizeof(szTmp), "pcnet%d", pDevIns->iInstance);
4582 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "PCNET info.", pcnetInfo);
4583
4584 /*
4585 * Attach status driver (optional).
4586 */
4587 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pData->IBase, &pBase, "Status Port");
4588 if (VBOX_SUCCESS(rc))
4589 pData->pLedsConnector = (PPDMILEDCONNECTORS)
4590 pBase->pfnQueryInterface(pBase, PDMINTERFACE_LED_CONNECTORS);
4591 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
4592 {
4593 AssertMsgFailed(("Failed to attach to status driver. rc=%Vrc\n", rc));
4594 return rc;
4595 }
4596
4597 /*
4598 * Attach driver.
4599 */
4600 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pData->IBase, &pData->pDrvBase, "Network Port");
4601 if (VBOX_SUCCESS(rc))
4602 {
4603 if (rc == VINF_NAT_DNS)
4604 {
4605#ifdef RT_OS_LINUX
4606 VMSetRuntimeError(PDMDevHlpGetVM(pDevIns), false, "NoDNSforNAT",
4607 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"));
4608#else
4609 VMSetRuntimeError(PDMDevHlpGetVM(pDevIns), false, "NoDNSforNAT",
4610 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"));
4611#endif
4612 }
4613 pData->pDrv = (PPDMINETWORKCONNECTOR)
4614 pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_NETWORK_CONNECTOR);
4615 if (!pData->pDrv)
4616 {
4617 AssertMsgFailed(("Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
4618 return VERR_PDM_MISSING_INTERFACE_BELOW;
4619 }
4620 }
4621 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4622 Log(("No attached driver!\n"));
4623 else
4624 return rc;
4625
4626 /*
4627 * Reset the device state. (Do after attaching.)
4628 */
4629 pcnetHardReset(pData);
4630
4631 /* Create send queue for the async send thread. */
4632 rc = RTSemEventCreate(&pData->hSendEventSem);
4633 AssertRC(rc);
4634
4635 /* Create asynchronous thread */
4636 rc = PDMDevHlpPDMThreadCreate(pDevIns, &pData->pSendThread, pData, pcnetAsyncSendThread, pcnetAsyncSendThreadWakeUp, 0, RTTHREADTYPE_IO, "PCNET_SEND");
4637 AssertRCReturn(rc, rc);
4638
4639 unsigned i;
4640#ifdef PCNET_QUEUE_SEND_PACKETS
4641 pData->ulXmitRingBufProd = 0;
4642 pData->ulXmitRingBufCons = 0;
4643 pData->pXmitRingBuffer[0] = (char *)RTMemAlloc(PCNET_MAX_XMIT_SLOTS * 1536);
4644 pData->cbXmitRingBuffer[0] = 0;
4645 for (i=1;i<PCNET_MAX_XMIT_SLOTS;i++)
4646 {
4647 pData->pXmitRingBuffer[i] = pData->pXmitRingBuffer[i-1] + 1536;
4648 pData->cbXmitRingBuffer[i] = 0;
4649 }
4650#endif
4651
4652#ifdef VBOX_WITH_STATISTICS
4653 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOReadGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in GC", "/Devices/PCNet%d/MMIO/ReadGC", iInstance);
4654 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOReadHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in HC", "/Devices/PCNet%d/MMIO/ReadHC", iInstance);
4655 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOWriteGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in GC", "/Devices/PCNet%d/MMIO/WriteGC", iInstance);
4656 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOWriteHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in HC", "/Devices/PCNet%d/MMIO/WriteHC", iInstance);
4657 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatAPROMRead, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM reads", "/Devices/PCNet%d/IO/APROMRead", iInstance);
4658 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatAPROMWrite, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM writes", "/Devices/PCNet%d/IO/APROMWrite", iInstance);
4659 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOReadGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNetIO reads in GC", "/Devices/PCNet%d/IO/ReadGC", iInstance);
4660 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOReadHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNetIO reads in HC", "/Devices/PCNet%d/IO/ReadHC", iInstance);
4661 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOWriteGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet IO writes in GC", "/Devices/PCNet%d/IO/WriteGC", iInstance);
4662 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOWriteHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet IO writes in HC", "/Devices/PCNet%d/IO/WriteHC", iInstance);
4663 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet Timer", "/Devices/PCNet%d/Timer", iInstance);
4664 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet receive", "/Devices/PCNet%d/Receive", iInstance);
4665#endif
4666 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/PCNet%d/ReceiveBytes", iInstance);
4667#ifdef VBOX_WITH_STATISTICS
4668 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet transmit in HC", "/Devices/PCNet%d/Transmit/Total", iInstance);
4669#endif
4670 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/PCNet%d/TransmitBytes", iInstance);
4671#ifdef VBOX_WITH_STATISTICS
4672 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet send transmit in HC","/Devices/PCNet%d/Transmit/Send", iInstance);
4673 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTdtePollGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TdtePoll in GC", "/Devices/PCNet%d/TdtePollGC", iInstance);
4674 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTdtePollHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TdtePoll in HC", "/Devices/PCNet%d/TdtePollHC", iInstance);
4675 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRdtePollGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet RdtePoll in GC", "/Devices/PCNet%d/RdtePollGC", iInstance);
4676 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRdtePollHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet RdtePoll in HC", "/Devices/PCNet%d/RdtePollHC", iInstance);
4677
4678 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTmdStoreGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TmdStore in GC", "/Devices/PCNet%d/TmdStoreGC", iInstance);
4679 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTmdStoreHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TmdStore in HC", "/Devices/PCNet%d/TmdStoreHC", iInstance);
4680
4681 for (i = 0; i < ELEMENTS(pData->aStatXmitFlush) - 1; i++)
4682 PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitFlushIrq/%d", iInstance, i + 1);
4683 PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitFlushIrq/%d+", iInstance, i + 1);
4684
4685 for (i = 0; i < ELEMENTS(pData->aStatXmitChainCounts) - 1; i++)
4686 PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitChainCounts/%d", iInstance, i + 1);
4687 PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitChainCounts/%d+", iInstance, i + 1);
4688
4689 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatXmitSkipCurrent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/Xmit/Skipped", iInstance, i + 1);
4690
4691 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet interrupt checks", "/Devices/PCNet%d/Interrupt", iInstance);
4692 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatPollTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet poll timer", "/Devices/PCNet%d/PollTimer", iInstance);
4693 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMIIReads, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of MII reads", "/Devices/PCNet%d/MIIReads", iInstance);
4694# ifdef PCNET_NO_POLLING
4695 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRCVRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of receive ring writes", "/Devices/PCNet%d/Ring/RCVWrites", iInstance);
4696 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTXRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of transmit ring writes", "/Devices/PCNet%d/Ring/TXWrites", iInstance);
4697 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteHC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCNet%d/Ring/HC/Writes", iInstance);
4698 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCNet%d/Ring/R0/Writes", iInstance);
4699 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteGC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCNet%d/Ring/GC/Writes", iInstance);
4700 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteFailedHC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCNet%d/Ring/HC/Failed", iInstance);
4701 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteFailedR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCNet%d/Ring/R0/Failed", iInstance);
4702 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteFailedGC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCNet%d/Ring/GC/Failed", iInstance);
4703 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteOutsideRangeHC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring range", "/Devices/PCNet%d/Ring/HC/Outside", iInstance);
4704 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteOutsideRangeR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring range", "/Devices/PCNet%d/Ring/R0/Outside", iInstance);
4705 PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteOutsideRangeGC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring range", "/Devices/PCNet%d/Ring/GC/Outside", iInstance);
4706# endif /* PCNET_NO_POLLING */
4707#endif
4708
4709 return VINF_SUCCESS;
4710}
4711
4712
4713/**
4714 * The device registration structure.
4715 */
4716const PDMDEVREG g_DevicePCNet =
4717{
4718 /* u32Version */
4719 PDM_DEVREG_VERSION,
4720 /* szDeviceName */
4721 "pcnet",
4722 /* szGCMod */
4723#ifdef PCNET_GC_ENABLED
4724 "VBoxDDGC.gc",
4725 "VBoxDDR0.r0",
4726#else
4727 "",
4728 "",
4729#endif
4730 /* pszDescription */
4731 "AMD PC-Net II Ethernet controller.\n",
4732 /* fFlags */
4733#ifdef PCNET_GC_ENABLED
4734 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
4735#else
4736 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT,
4737#endif
4738 /* fClass */
4739 PDM_DEVREG_CLASS_NETWORK,
4740 /* cMaxInstances */
4741 4,
4742 /* cbInstance */
4743 sizeof(PCNetState),
4744 /* pfnConstruct */
4745 pcnetConstruct,
4746 /* pfnDestruct */
4747 pcnetDestruct,
4748 /* pfnRelocate */
4749 pcnetRelocate,
4750 /* pfnIOCtl */
4751 NULL,
4752 /* pfnPowerOn */
4753 NULL,
4754 /* pfnReset */
4755 pcnetReset,
4756 /* pfnSuspend */
4757 NULL,
4758 /* pfnResume */
4759 NULL,
4760 /* pfnAttach */
4761 NULL,
4762 /* pfnDetach */
4763 NULL,
4764 /* pfnQueryInterface. */
4765 NULL
4766};
4767
4768#endif /* IN_RING3 */
4769#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
4770
Note: See TracBrowser for help on using the repository browser.

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