VirtualBox

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

Last change on this file since 808 was 808, checked in by vboxsync, 18 years ago

Irq check update

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

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