DevPCNet.cpp revision 4f5f864ce3e12b7e9bc27e775432e8472bdcd2bc
/** @file
*
* VBox network devices:
* AMD PC-Net II (Am79C970A + Am79C973) Ethernet Controller
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*
* --------------------------------------------------------------------
*
* This code is based on:
*
* AMD PC-Net II (Am79C970A) emulation
*
* Copyright (c) 2004 Antony T Curtis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* This software was written to be compatible with the specification:
* AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
* AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_PCNET
#include <iprt/critsect.h>
#ifdef IN_RING3
#include <iprt/semaphore.h>
#endif
#include "Builtins.h"
#include "vl_vbox.h"
/* Enable this to catch writes to the ring descriptors instead of using excessive polling */
/* #define PCNET_NO_POLLING */
/* Enable to handle frequent io reads in the guest context */
#define PCNET_GC_ENABLED
#ifdef __GNUC__
#else
#define PACKED
#endif
#if 0
#define LOG_REGISTER(a) LogRel(a)
#else
#define LOG_REGISTER(a)
#endif
#if 0
#define LOG_PACKETS
#else
#endif
#if defined(LOG_ENABLED)
#define PCNET_DEBUG_IO
#define PCNET_DEBUG_BCR
#define PCNET_DEBUG_CSR
#define PCNET_DEBUG_RMD
#define PCNET_DEBUG_TMD
#define PCNET_DEBUG_MATCH
#define PCNET_DEBUG_MII
#endif
#define PCNET_IOPORT_SIZE 0x20
#define PCNET_PNPMMIO_SIZE 0x20
#define PCNET_SAVEDSTATE_VERSION 6
#define BCR_MAX_RAP 50
#define MII_MAX_REG 32
#define CSR_MAX_REG 128
/* Maximum number of times we report a link down to the guest (failure to send frame) */
#define PCNET_MAX_LINKDOWN_REPORTED 3
/* Frame cache */
typedef struct PCNETFRAME
{
/** The current frame size. Starts at -1. Only the top frame can be expanded. */
#if HC_ARCH_BITS == 64
#endif
/** The virtual address of the frame (copied or direct pointer) */
} PCNETFRAME;
/* Pointer to PCNETFRAME */
typedef PCNETFRAME *PPCNETFRAME;
typedef struct PCNetState_st PCNetState;
struct PCNetState_st
{
#ifndef PCNET_NO_POLLING
/** Poll timer (address for host context) */
/** Poll timer (address for guest context) */
#endif
/** Register Address Pointer */
/** Internal interrupt service */
/** ??? */
/** Address of the RX descriptor table (ring). Loaded at init. */
/** Address of the TX descriptor table (ring). Loaded at init. */
/** Last time we polled the queues */
/** Array of frames. */
/** The xmit buffer. */
/** The recv buffer. */
/** Pending send packet counter. */
int iLog2DescSize;
/** Bits 16..23 in 16-bit mode */
/** Transmit signaller */
/** Receive signaller */
/** Pointer to the device instance. */
/** Pointer to the device instance. */
/** Restore timer.
* This is used to disconnect and reconnect the link after a restore. */
/** Pointer to the connector of the attached network driver. */
/** Pointer to the attached network driver. */
/** The base interface. */
/** The network port interface. */
/** The network config port interface. */
/** Base address of the MMIO region. */
/** Base port of the I/O space region. */
/** If set the link is currently up. */
bool fLinkUp;
/** If set the link is temporarily down because of a saved state load. */
bool fLinkTempDown;
/** This flag is set on SavePrep to prevent altering of memory after pgmR3Save() was called */
bool fSaving;
/** Number of times we've reported the link down. */
/** The configured MAC address. */
/** The LED. */
/** The LED ports. */
/** Partner of ILeds. */
/** Async send thread */
/** Access critical section. */
#ifdef PCNET_NO_POLLING
DECLGCCALLBACKMEMBER(int, pfnEMInterpretInstructionGC, (PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, uint32_t *pcbSize));
DECLR0CALLBACKMEMBER(int, pfnEMInterpretInstructionR0, (PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, uint32_t *pcbSize));
#endif
bool fGCEnabled;
bool fR0Enabled;
bool fAm79C973;
bool afAlignment[5];
#ifdef VBOX_WITH_STATISTICS
# ifdef PCNET_NO_POLLING
# endif
#endif /* VBOX_WITH_STATISTICS */
};
/* BUS CONFIGURATION REGISTERS */
#define BCR_MSRDA 0
#define BCR_MSWRA 1
#define BCR_MC 2
#define BCR_RESERVED3 3
#define BCR_LNKST 4
#define BCR_LED1 5
#define BCR_LED2 6
#define BCR_LED3 7
#define BCR_RESERVED8 8
#define BCR_FDC 9
/* 10 - 15 = reserved */
#define BCR_BSBC 18
#define BCR_EECAS 19
#define BCR_SWS 20
#define BCR_PLAT 22
#define BCR_PCISID 23
#define BCR_PCISVID 24
#define BCR_SRAMSIZ 25
#define BCR_SRAMB 26
#define BCR_SRAMIC 27
#define BCR_EBADDRL 28
#define BCR_EBADDRU 29
#define BCR_EBD 30
#define BCR_STVAL 31
#define BCR_MIICAS 32
#define BCR_MIIADDR 33
#define BCR_MIIMDR 34
#define BCR_PCIVID 35
#define BCR_PMC_A 36
#define BCR_DATA0 37
#define BCR_DATA1 38
#define BCR_DATA2 39
#define BCR_DATA3 40
#define BCR_DATA4 41
#define BCR_DATA5 42
#define BCR_DATA6 43
#define BCR_DATA7 44
#define BCR_PMR1 45
#define BCR_PMR2 46
#define BCR_PMR3 47
(readable, settable, not clearable) */
Underflow error */
#endif
#define PHYSADDR(S,A) ((A) | (S)->GCUpperPhys)
#define CSR_VERSION_HIGH 0x0262
/** @todo All structs: big endian? */
/** frank: From the gcc manual: ``This attribute, attached to `struct' or `union' type
* definition, specifies that each member (other than zero-width bitfields) of
* the structure or union is placed to minimize the memory required. ...
* Specifying this attribute for `struct' and `union' types is equivalent to
* specifying the `packed attribute' on each the structure or union members.''
* @todo r=bird: #pragma pack(1) / __attribute__((packed)) isn't necessary here
* because of the way the alignment rules work.
*/
{
};
/** bird: I've changed the type for the bitfields. They should only be 16-bit all together.
* frank: I've changed the bitfiled types to uint32_t to prevent compiler warnings. */
{
};
/** Transmit Message Descriptor */
typedef struct TMD
{
struct
{
} tmd0;
struct PACKED
{
transmitter FCS generation is activated. */
} tmd1;
struct PACKED
{
} tmd2;
struct
{
} tmd3;
} TMD;
/** Receive Message Descriptor */
typedef struct RMD
{
struct
{
} rmd0;
struct PACKED
{
} rmd1;
struct PACKED
{
} rmd2;
struct
{
} rmd3;
} RMD;
#pragma pack()
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
"TMD0 : TBADR=0x%08x\n" \
"TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, " \
"ONE=%d, DEF=%d, STP=%d, ENP=%d,\n" \
" BPE=%d, BCNT=%d\n" \
"TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, " \
"LCA=%d, RTR=%d,\n" \
" TDR=%d, TRC=%d\n", \
"RMD0 : RBADR=0x%08x\n" \
"RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, " \
"CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n " \
"BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
"RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n", \
/**
* Load transmit message descriptor
* Make sure we read the own flag first.
*/
{
if (!BCR_SWSTYLE(pData))
{
}
{
}
else
{
}
/* Double check the own bit; guest drivers might be buggy and lock prefixes in the recompiler are ignored by other threads. */
#ifdef DEBUG
Log(("pcnetTmdLoad: own bit flipped while reading!!\n"));
#endif
if (!(ownbyte & 0x80))
}
/**
* Store transmit message descriptor and hand it over to the host (the VM guest).
* Make sure that all data are transmitted before we clear the own flag.
*/
{
if (!BCR_SWSTYLE(pData))
{
}
{
}
else
{
}
}
/**
* Load receive message descriptor
* Make sure we read the own flag first.
*/
{
if (!BCR_SWSTYLE(pData))
{
}
{
}
else
{
}
/* Double check the own bit; guest drivers might be buggy and lock prefixes in the recompiler are ignored by other threads. */
#ifdef DEBUG
Log(("pcnetTmdLoad: own bit flipped while reading!!\n"));
#endif
if (!(ownbyte & 0x80))
}
/**
* Store receive message descriptor and hand it over to the host (the VM guest).
* Make sure that all data are transmitted before we clear the own flag.
*/
{
if (!BCR_SWSTYLE(pData))
{
}
{
}
else
{
}
}
/** Checks if it's a bad (as in invalid) RMD.*/
#ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
#endif
#define ETHER_ADDR_LEN ETH_ALEN
#define ETH_ALEN 6
#pragma pack(1)
struct ether_header
{
};
#pragma pack()
#ifdef LOG_PACKETS
{
int i, j;
i = 14; // length of MAC header
i += 4*(p[i] & 15); // length of IP header
i += 4*(p[i+12] >> 4); // length of TCP header
for (j=i; j<70 && j<count; j++)
LogRel((" %02x", p[j]));
LogRel((" ("));
for (j=i; j<70 && j<count; j++)
LogRel((")\n"));
}
#endif
#define PRINT_PKTHDR(BUF) do { \
Log(("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \
"shost=%02x:%02x:%02x:%02x:%02x:%02x, " \
"type=0x%04x (bcast=%d)\n", \
} while (0)
#ifdef IN_RING3
#define MULTICAST_FILTER_LEN 8
{
#define LNC_POLYNOMIAL 0xEDB88320UL
{
{
data >>= 1;
}
}
return crc;
}
/* generated using the AUTODIN II polynomial
* x^32 + x^26 + x^23 + x^22 + x^16 +
* x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
*/
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
{
int result;
#else
#endif
#ifdef PCNET_DEBUG_MATCH
Log(("#%d packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, "
"padr=%02x:%02x:%02x:%02x:%02x:%02x\n",
#endif
return result;
}
{
#ifdef PCNET_DEBUG_MATCH
#endif
return result;
}
{
{
int index;
#else
#endif
}
return 0;
}
#endif /* IN_RING3 */
/**
* Get the receive descriptor ring address with a given index.
*/
{
}
/**
* Get the transmit descriptor ring address with a given index.
*/
{
}
#ifndef IN_RING3
#endif
#define htonl(x) ASMByteSwapU32(x)
#ifdef PCNET_NO_POLLING
# ifndef IN_RING3
/**
* #PF Virtual Handler callback for Guest write access to the ring descriptor page(pData)
*
* @return VBox status code (appropriate for trap handling and GC return).
* @param pVM VM Handle.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param GCPhysFault The GC physical address corresponding to pvFault.
* @param pvUser User argument.
*/
{
Log(("#%d pcnetHandleRingWriteGC: write to %08x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, GCPhysFault));
{
#ifdef PCNET_MONITOR_RECEIVE_RING
#endif
)
{
if (VBOX_SUCCESS(rc))
{
/* Check if we can do something now */
return VINF_SUCCESS;
}
}
else
{
return VINF_SUCCESS; /* outside of the ring range */
}
}
return VINF_IOM_HC_MMIO_WRITE; /* handle in ring3 */
}
# else /* IN_RING3 */
/**
* #PF Handler callback for physical access handler ranges (MMIO among others) in HC.
*
* The handler can not raise any faults, it's mainly for monitoring write access
* to certain pages.
*
* @returns VINF_SUCCESS if the handler have carried out the operation.
* @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
* @param pVM VM Handle.
* @param GCPhys The physical address the guest is writing to.
* @param pvPhys The HC mapping of that address.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
{
#ifdef VBOX_WITH_STATISTICS
#endif
/* Perform the actual write */
/* Writes done by our code don't require polling of course */
{
#ifdef PCNET_MONITOR_RECEIVE_RING
#endif
)
{
/* Check if we can do something now */
}
}
return VINF_SUCCESS;
}
# endif /* !IN_RING3 */
#endif /* PCNET_NO_POLLING */
{
}
/**
* Check if we have to send an interrupt to the guest. An interrupt can occur on
* - csr0 (written quite often)
* - csr4 (only written by pcnetSoftReset(), pcnetStop() or by the guest driver)
* - csr5 (only written by pcnetSoftReset(), pcnetStop or by the driver guest)
*/
{
register int iISR = 0;
/* Linux guests set csr4=0x0915
* W2k guests set csr3=0x4940 (disable BABL, MERR, IDON, DXSUFLO */
#if 1
#else
#endif
{
}
#ifdef VBOX
{
}
{
iISR = 1;
}
#else /* !VBOX */
{
iISR = 1;
}
#endif /* !VBOX */
#if 1
#else
#endif
{
iISR = 1;
}
/* normal path is to _not_ change the IRQ status */
{
}
}
#ifdef IN_RING3
#ifdef PCNET_NO_POLLING
{
int rc;
Log(("pcnetUpdateRingHandlers TD %VGp size %x -> %VGp size %x\n", pData->TDRAPhysOld, pData->cbTDRAOld, pData->GCTDRA, pcnetTdraAddr(pData, 0)));
Log(("pcnetUpdateRingHandlers RX %VGp size %x -> %VGp size %x\n", pData->RDRAPhysOld, pData->cbRDRAOld, pData->GCRDRA, pcnetRdraAddr(pData, 0)));
/** @todo unregister order not correct! */
#ifdef PCNET_MONITOR_RECEIVE_RING
{
if (pData->RDRAPhysOld != 0)
"PCNet receive ring write access handler");
}
#endif /* PCNET_MONITOR_RECEIVE_RING */
#ifdef PCNET_MONITOR_RECEIVE_RING
/* 3 possibilities:
* 1) TDRA on different physical page as RDRA
* 2) TDRA completely on same physical page as RDRA
* 3) TDRA & RDRA overlap partly with different physical pages
*/
if ( RDRAPageStart > TDRAPageEnd
|| TDRAPageStart > RDRAPageEnd)
{
#endif /* PCNET_MONITOR_RECEIVE_RING */
/* 1) */
{
if (pData->TDRAPhysOld != 0)
"PCNet transmit ring write access handler");
}
#ifdef PCNET_MONITOR_RECEIVE_RING
}
else
if ( RDRAPageStart != TDRAPageStart
&& ( TDRAPageStart == RDRAPageEnd
|| TDRAPageEnd == RDRAPageStart
)
)
{
/* 3) */
AssertFailed();
}
/* else 2) */
#endif
}
#endif /* PCNET_NO_POLLING */
{
/** @todo Documentation says that RCVRL and XMTRL are stored as two's complement!
* Software is allowed to write these registers directly. */
#define PCNET_INIT() do { \
} while (0)
if (BCR_SSIZE32(pData))
{
pData->GCUpperPhys = 0;
PCNET_INIT();
}
else
{
PCNET_INIT();
}
#ifdef PCNET_NO_POLLING
#endif
/* Reset cached RX and TX states */
LogRel(("PCNet#%d: Init: ss32=%d GCRDRA=0x%08x[%d] GCTDRA=0x%08x[%d]\n",
}
#endif /* IN_RING3 */
/**
*/
{
}
/**
*/
{
}
#ifdef IN_RING3
{
return true;
}
#endif
/**
* Poll Receive Descriptor Table Entry and cache the results in the appropriate registers.
* Note: Once a descriptor belongs to the network card (this driver), it cannot be changed
* by the host (the guest driver) anymore. Well, it could but the results are undefined by
* definition.
* @param fSkipCurrent if true, don't scan the current RDTE.
*/
{
/* assume lack of a next receive descriptor */
{
/*
* The current receive message descriptor.
*/
if (i < 1)
if (!fSkipCurrent)
{
{
return;
}
{
#ifdef IN_RING3
#else
if (pItem)
#endif
}
else
{
/* This is not problematic since we don't own the descriptor */
LogRel(("PCNet#%d: BAD RMD ENTRIES AT 0x%08x (i=%d)\n",
return;
}
}
/*
* The next descriptor.
*/
if (--i < 1)
{
return;
}
{
}
else
{
/* This is not problematic since we don't own the descriptor */
LogRel(("PCNet#%d: BAD RMD ENTRIES + AT 0x%08x (i=%d)\n",
return;
}
/**
* @todo NNRD
*/
}
else
{
}
}
/**
* Poll Transmit Descriptor Table Entry
* @return true if transmit descriptors available
*/
{
{
{
return 0;
}
{
LogRel(("PCNet#%d: BAD TMD XDA=0x%08x\n",
return 0;
}
/* previous xmit descriptor */
/* set current trasmit decriptor. */
}
else
{
/** @todo consistency with previous receive descriptor */
return 0;
}
}
#ifdef IN_RING3
/**
* Check if there is at least one free receive buffer available.
*/
{
return 0;
{
/** @todo Notify the guest _now_. Will potentially increase the interrupt load */
return 0;
}
/* byte count stored in two's complement 12 bits wide */
}
/**
* Write data into guest receive buffers.
*/
{
return;
/*
* Perform address matching.
*/
{
{
/* Not owned by controller. This should not be possible as
* we already called pcnetCanReceive(). */
LogRel(("PCNet#%d: no buffer: RCVRC=%d\n",
/* Dump the status of all RX descriptors */
while (i-- > 0)
{
}
}
else
{
int pktcount = 0;
if (!CSR_ASTRP_RCV(pData))
{
while (size < 60)
/* FCS at end of packet */
}
size += 4;
#ifdef PCNET_DEBUG_MATCH
#endif
/*if (!CSR_LAPPEN(pData))*/
pktcount++;
if (size > 0)
{
{
/* From the manual: ``Regardless of ownership of the second receive
* descriptor, the Am79C972 controller will continue to perform receive
* data DMA transfers to the first buffer. If the frame length exceeds
* the length of the first buffer, and the Am79C972 controller does not
* own the second buffer, ownership of the current descriptor will be
* passed back to the system by writing a 0 to the OWN bit of RMD1.
* Status will be written indicating buffer (BUFF = 1) and possibly
* overflow (OFLO = 1) errors.
* If the frame length exceeds the length of the first (current) buffer,
* and the Am79C972 controller does own the second (next) buffer,
* ownership will be passed back to the system by writing a 0 to the OWN
* bit of RMD1 when the first buffer is full. The OWN bit is the only bit
* modified in the descriptor. Receive data transfers to the second buffer
* may occur before the Am79C972 controller proceeds to look ahead to the
* ownership of the third buffer. Such action will depend upon the state
* of the FIFO when the OWN bit has been updated in the first descriptor.
* In any case, lookahead will be performed to the third buffer and the
* information gathered will be stored in the chip, regardless of the state
* of the ownership bit.'' */
pcnetRdtePoll(pData, true);
}
{
/* write back, clear the own bit */
pktcount++;
}
}
{
}
else
{
LogRel(("PCNet#%d: Overflow by %ubytes\n",
}
/* write back, clear the own bit */
#ifdef PCNET_DEBUG_RMD
#endif
while (pktcount--)
{
else
}
/* guest driver is owner: force repoll of current and next RDTEs */
}
}
/* see description of TXDPOLL:
* ``transmit polling will take place following receive activities'' */
}
/**
* Checks if the link is up.
* @returns true if the link is up.
* @returns false if the link is down.
*/
{
}
/**
* Transmit queue consumer
* This is just a very simple way of delaying sending to R3.
*
* @returns Success indicator.
* If false the item will not be removed and the flushing will stop.
* @param pDevIns The device instance.
* @param pItem The item to consume. Upon return this item will be freed.
*/
{
/* Clear counter .*/
return true;
}
/**
* Scraps the top frame.
* This is done as a precaution against mess left over by on
*/
{
}
/**
* If we are in RING3 don't copy the frame from GC here but only store the address. We
* don't need to buffer the frames because a direct address translation was possible.
*/
{
}
/**
* Reads the first part of a frame
*/
{
cbFrame);
}
/**
* Reads more into the current frame.
*/
{
cbFrame);
}
/**
* Completes the current frame.
* If we've reached the maxium number of frames, they will be flushed.
*/
{
/* Don't hold the critical section while transmitting data. */
/** @note also avoids deadlocks with NAT as it can call us right back. */
}
/**
* Fails a TMD with a link down error.
*/
{
/* make carrier error - hope this is correct. */
Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
}
/**
* Fails a TMD with a generic error.
*/
{
/* make carrier error - hope this is correct. */
Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
}
/**
* Transmit a loopback frame.
*/
{
}
/**
* Flushes queued frames.
*/
{
}
#endif /* IN_RING3 */
/**
* Try to transmit frames
*/
{
{
return;
}
/*
* Check the current transmit descriptors.
*/
return;
/* Update TDMD, TXSTRT and TINT. */
/*
*/
#ifdef IN_RING3
#else
# if 1
if (RT_UNLIKELY(pItem))
# else
{
if (RT_UNLIKELY(pItem))
}
else
# endif
#endif
}
#ifdef IN_RING3
/**
* Try to transmit frames
*/
{
unsigned cFlushIrq = 0;
{
return VINF_SUCCESS;
}
/*
* Iterate the transmit descriptors.
*/
do
{
#ifdef VBOX_WITH_STATISTICS
unsigned cBuffers = 1;
#endif
break;
/* Don't continue sending packets when the link is down. */
)
break;
#ifdef PCNET_DEBUG_TMD
Log2(("#%d TMDLOAD 0x%08x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, PHYSADDR(pData, CSR_CXDA(pData))));
#endif
/*
* The typical case - a complete packet.
* This can be performed with zero copy in Ring-3.
*/
{
Log(("#%d pcnetTransmit: stp&enp: cb=%d xmtrc=%#x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, cb, CSR_XMTRC(pData)));
{
/* From the manual: ``A zero length buffer is acceptable as
* long as it is not the last buffer in a chain (STP = 0 and
* ENP = 1).'' That means that the first buffer might have a
* zero length if it is not the last one in the chain. */
{
if (RT_SUCCESS(rc))
else
{
}
else
{
if (VBOX_FAILURE(rc))
return rc; /* can happen during termination */
}
}
else if (cb == 4096)
{
/* The Windows NT4 pcnet driver sometimes marks the first
* unused descriptor as owned by us. Ignore that (by
* passing it back). Do not update the ring counter in this
* case (otherwise that driver becomes even more confused,
* which causes transmit to stall for about 10 seconds).
* This is just a workaround, not a final solution. */
/* r=frank: IMHO this is the correct implementation. The
* manual says: ``If the OWN bit is set and the buffer
* length is 0, the OWN bit will be cleared. In the C-LANCE
* the buffer length of 0 is interpreted as a 4096-byte
* buffer.'' */
LogRel(("PCNET: pcnetAsyncTransmit: illegal 4kb frame -> ignoring\n"));
break;
}
else
{
/* Signal error, as this violates the Ethernet specs. */
/** @todo check if the correct error is generated. */
LogRel(("PCNET: pcnetAsyncTransmit: illegal 4kb frame -> signalling error\n"));
}
}
else
/* Write back the TMD and pass it to the host (clear own bit). */
/* advance the ring counter register */
else
}
{
/*
* Read TMDs until end-of-packet or tdte poll fails (underflow).
*/
const unsigned cbMaxFrame = 1536;
bool fDropFrame = false;
for (;;)
{
/*
* Advance the ring counter register and check the next tmd.
*/
#ifdef LOG_ENABLED
#endif
else
{
/*
* Underflow!
*/
AssertMsgFailed(("pcnetTransmit: Underflow!!!\n"));
break;
}
/* release & save the previous tmd, pass it to the host */
/*
* The next tdm.
*/
#ifdef VBOX_WITH_STATISTICS
cBuffers++;
#endif
&& !fDropFrame)
else
{
fDropFrame = true;
}
{
{
if (VBOX_FAILURE(rc))
return rc; /* can happen during termination */
}
else
{
if (!fDropFrame)
}
/* Write back the TMD, pass it to the host */
/* advance the ring counter register */
else
break;
}
}
}
else
{
/*
* We underflowed in a previous transfer, or the driver is giving us shit.
* Simply stop the transmitting for now.
*/
/** @todo according to the specs we're supposed to clear the own bit and move on to the next one. */
break;
}
/* Update TDMD, TXSTRT and TINT. */
{
cFlushIrq++;
}
/** @todo should we continue after an error (tmd.tmd1.err) or not? */
if (cFlushIrq)
{
}
return VINF_SUCCESS;
}
/**
* Async I/O thread for delayed sending of packets.
*/
{
int rc = VINF_SUCCESS;
while(rc == VINF_SUCCESS)
{
if (VBOX_FAILURE(rc))
break;
}
return VINF_SUCCESS;
}
#endif /* IN_RING3 */
/**
* Poll for changes in RX and TX descriptor rings.
*/
{
}
/**
* Update the poller timer
* @thread EMT,
*/
{
#ifdef LOG_ENABLED
Log2(("#%d pcnetPollTimer time=%08x TDMD=%d TXON=%d POLL=%d TDTE=%d TDRA=%x\n",
Log2(("#%d pcnetPollTimer: CSR_CXDA=%x CSR_XMTRL=%d CSR_XMTRC=%d\n",
#endif
#ifdef PCNET_DEBUG_TMD
{
Log2(("#%d pcnetPollTimer: TMDLOAD 0x%08x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, PHYSADDR(pData, CSR_CXDA(pData))));
}
#endif
{
/* We ensure that we poll at least every 2ms (500Hz) but not more often than
* 5000 times per second. This way we completely prevent the overhead from
* heavy reprogramming the timer which turned out to be very CPU-intensive.
* The drawback is that csr46 and csr47 are not updated properly anymore
* but so far I have not seen any guest depending on these values. The 2ms
* interval is the default polling interval of the PCNet card (65536/33MHz). */
#ifdef PCNET_NO_POLLING
#else
{
}
/* Poll timer interval is fixed to 500Hz. Don't stop it. */
#endif
}
}
{
int rc = VINF_SUCCESS;
#ifdef PCNET_DEBUG_CSR
Log(("#%d pcnetCSRWriteU16: rap=%d val=0x%04x\n", PCNETSTATE_2_DEVINS(pData)->iInstance, u32RAP, val));
#endif
switch (u32RAP)
{
case 0:
{
/* Clear any interrupt flags.
* Don't clear an interrupt flag which was not seen by the guest yet. */
/* Iff STOP, STRT and INIT are set, clear STRT and INIT */
val &= ~3;
#ifndef IN_RING3
{
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %04x => %04x (%04x)\n",
#ifdef IN_RING3
#endif
return rc;
}
case 1: /* IADRL */
case 2: /* IADRH */
case 8: /* LADRF 0..15 */
case 9: /* LADRF 16..31 */
case 10: /* LADRF 32..47 */
case 11: /* LADRF 48..63 */
case 12: /* PADR 0..15 */
case 13: /* PADR 16..31 */
case 14: /* PADR 32..47 */
case 18: /* CRBAL */
case 19: /* CRBAU */
case 20: /* CXBAL */
case 21: /* CXBAU */
case 22: /* NRBAL */
case 23: /* NRBAU */
case 24: /* BADRL */
case 25: /* BADRU */
case 26: /* NRDAL */
case 27: /* NRDAU */
case 28: /* CRDAL */
case 29: /* CRDAU */
case 30: /* BADXL */
case 31: /* BADXU */
case 32: /* NXDAL */
case 33: /* NXDAU */
case 34: /* CXDAL */
case 35: /* CXDAU */
case 36: /* NNRDL */
case 37: /* NNRDU */
case 38: /* NNXDL */
case 39: /* NNXDU */
case 40: /* CRBCL */
case 41: /* CRBCU */
case 42: /* CXBCL */
case 43: /* CXBCU */
case 44: /* NRBCL */
case 45: /* NRBCU */
case 46: /* POLL */
case 47: /* POLLINT */
case 72: /* RCVRC */
case 74: /* XMTRC */
case 76: /* RCVRL */ /** @todo call pcnetUpdateRingHandlers */
/** @todo receive ring length is stored in two's complement! */
case 78: /* XMTRL */ /** @todo call pcnetUpdateRingHandlers */
/** @todo transmit ring length is stored in two's complement! */
case 112: /* MISSC */
break;
LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %04x\n",
return rc;
case 3: /* Interrupt Mask and Deferral Control */
LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %04x\n",
break;
case 4: /* Test and Features Control */
LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %04x\n",
val &= ~0x026a;
break;
case 5: /* Extended Control and Interrupt 1 */
LOG_REGISTER(("PCNet#%d: WRITE CSR%d, %04x\n",
val &= ~0x0a90;
break;
case 15: /* Mode */
{
Log(("PCNet#%d: promiscuous mode changed to %d\n",
#ifndef IN_RING3
return VINF_IOM_HC_IOPORT_WRITE;
#else
/* check for promiscuous mode change */
#endif
}
break;
case 16: /* IADRL */
case 17: /* IADRH */
case 58: /* Software Style */
LOG_REGISTER(("PCNet#%d: WRITE SW_STYLE, %04x\n",
break;
default:
return rc;
}
return rc;
}
{
switch (u32RAP)
{
case 0:
break;
case 16:
case 17:
case 58:
case 88:
val <<= 16;
break;
default:
LOG_REGISTER(("PCNet#%d: read CSR%d => %04x\n",
}
#ifdef PCNET_DEBUG_CSR
#endif
return val;
}
{
int rc = VINF_SUCCESS;
u32RAP &= 0x7f;
#ifdef PCNET_DEBUG_BCR
#endif
switch (u32RAP)
{
case BCR_SWS:
return rc;
val &= ~0x0300;
switch (val & 0x00ff)
{
default:
// fall through
case 0:
break;
case 1:
pData->GCUpperPhys = 0;
break;
case 2:
case 3:
pData->GCUpperPhys = 0;
break;
}
LOG_REGISTER(("PCNet#%d: WRITE SW_STYLE, %04x\n",
/* fall through */
case BCR_LNKST:
case BCR_LED1:
case BCR_LED2:
case BCR_LED3:
case BCR_MC:
case BCR_FDC:
case BCR_BSBC:
case BCR_EECAS:
case BCR_PLAT:
case BCR_MIIADDR:
LOG_REGISTER(("PCNet#%d: WRITE BCR%d, %04x\n",
break;
case BCR_MIIMDR:
LOG_REGISTER(("PCNet#%d: WRITE MII%d, %04x\n",
break;
default:
break;
}
return rc;
}
{
switch (miiaddr)
{
case 0:
/* MII basic mode control register. */
break;
case 1:
/* MII basic mode status register. */
| 0x0020 /* Auto-negotiation complete. */
| 0x0008 /* Able to do auto-negotiation. */
| 0x0004 /* Link status. */
| 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
else
{
| 0x0008 /* Able to do auto-negotiation. */
| 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
}
break;
case 2:
/* PHY identifier 1. */
val = 0; /* No name PHY. */
break;
case 3:
/* PHY identifier 2. */
val = 0; /* No name PHY. */
break;
case 4:
/* Advertisement control register. */
| 0x0001; /* CSMA selector. */
break;
case 5:
/* Link partner ability register. */
| 0x4000 /* Link partner acked us. */
| 0x0001; /* Use CSMA selector. */
else
{
val = 0;
}
break;
case 6:
/* Auto negotiation expansion register. */
| 0x0004 /* Enable npage words. */
| 0x0001; /* Can do N-way auto-negotiation. */
else
{
val = 0;
}
break;
default:
val = 0;
break;
}
#ifdef PCNET_DEBUG_MII
#endif
return val;
}
{
u32RAP &= 0x7f;
switch (u32RAP)
{
case BCR_LNKST:
case BCR_LED1:
case BCR_LED2:
case BCR_LED3:
/* Clear LNKSTE if we're not connected or if we've just loaded a VM state. */
{
if (u32RAP == 4)
val &= ~0x40;
}
break;
case BCR_MIIMDR:
{
}
else
val = 0xffff;
break;
default:
break;
}
#ifdef PCNET_DEBUG_BCR
#endif
return val;
}
#ifdef IN_RING3 /* move down */
{
int i;
/* Initialize the PROM */
for (i = 0, checksum = 0; i < 16; i++)
}
#endif /* IN_RING3 */
{
addr &= 0x0f;
val &= 0xff;
/* Check APROMWE bit to enable write access */
}
{
return val;
}
{
int rc = VINF_SUCCESS;
#ifdef PCNET_DEBUG_IO
#endif
{
switch (addr & 0x0f)
{
case 0x00: /* RDP */
break;
case 0x02: /* RAP */
break;
case 0x06: /* BDP */
break;
}
}
return rc;
}
{
*pRC = VINF_SUCCESS;
{
switch (addr & 0x0f)
{
case 0x00: /* RDP */
/** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
/** Polling is then useless here and possibly expensive. */
goto skip_update_irq;
break;
case 0x02: /* RAP */
goto skip_update_irq;
case 0x04: /* RESET */
val = 0;
break;
case 0x06: /* BDP */
break;
}
}
#ifdef PCNET_DEBUG_IO
#endif
return val;
}
{
int rc = VINF_SUCCESS;
#ifdef PCNET_DEBUG_IO
#endif
{
switch (addr & 0x0f)
{
case 0x00: /* RDP */
break;
case 0x04: /* RAP */
break;
case 0x0c: /* BDP */
break;
}
}
else if (addr == 0)
{
/* switch device to dword I/O mode */
#ifdef PCNET_DEBUG_IO
Log2(("device switched into dword i/o mode\n"));
#endif
}
return rc;
}
{
*pRC = VINF_SUCCESS;
{
switch (addr & 0x0f)
{
case 0x00: /* RDP */
/** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
/** Polling is then useless here and possibly expensive. */
goto skip_update_irq;
break;
case 0x04: /* RAP */
goto skip_update_irq;
case 0x08: /* RESET */
val = 0;
break;
case 0x0c: /* BDP */
break;
}
}
#ifdef PCNET_DEBUG_IO
#endif
return val;
}
{
#ifdef PCNET_DEBUG_IO
#endif
if (!(addr & 0x10))
}
{
if (!(addr & 0x10))
#ifdef PCNET_DEBUG_IO
#endif
return val;
}
{
#ifdef PCNET_DEBUG_IO
#endif
if (addr & 0x10)
else
{
}
}
{
int rc;
if (addr & 0x10)
else
{
val <<= 8;
}
#ifdef PCNET_DEBUG_IO
#endif
return val;
}
{
#ifdef PCNET_DEBUG_IO
#endif
if (addr & 0x10)
else
{
}
}
{
int rc;
if (addr & 0x10)
else
{
val <<= 8;
val <<= 8;
val <<= 8;
}
#ifdef PCNET_DEBUG_IO
#endif
return val;
}
/**
* Port I/O Handler for IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
{
int rc;
if (cb == 1)
{
if (rc == VINF_SUCCESS)
{
}
}
else
LogFlow(("#%d pcnetIOPortAPromRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Vrc\n",
return rc;
}
/**
* Port I/O Handler for OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
int rc;
if (cb == 1)
{
if (rc == VINF_SUCCESS)
{
}
}
else
{
rc = VINF_SUCCESS;
}
LogFlow(("#%d pcnetIOPortAPromWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Vrc\n",
return rc;
}
/**
* Port I/O Handler for IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
{
int rc = VINF_SUCCESS;
if (rc == VINF_SUCCESS)
{
switch (cb)
{
default:
break;
}
}
LogFlow(("#%d pcnetIOPortRead: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Vrc\n",
return rc;
}
/**
* Port I/O Handler for OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
int rc = VINF_SUCCESS;
if (rc == VINF_SUCCESS)
{
switch (cb)
{
default:
break;
}
}
LogFlow(("#%d pcnetIOPortWrite: Port=%RTiop u32=%#RX32 cb=%d rc=%Vrc\n",
return rc;
}
/**
* Memory mapped I/O Handler for read operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param GCPhysAddr Physical address (in GC) where the read starts.
* @param pv Where to store the result.
* @param cb Number of bytes read.
*/
{
int rc = VINF_SUCCESS;
/*
* We have to check the range, because we're page aligning the MMIO stuff presently.
*/
{
if (rc == VINF_SUCCESS)
{
switch (cb)
{
default:
break;
}
}
}
else
LogFlow(("#%d pcnetMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Vrc\n",
return rc;
}
/**
* Port I/O Handler for write operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param GCPhysAddr Physical address (in GC) where the read starts.
* @param pv Where to fetch the result.
* @param cb Number of bytes to write.
*/
{
int rc = VINF_SUCCESS;
/*
* We have to check the range, because we're page aligning the MMIO stuff presently.
*/
{
if (rc == VINF_SUCCESS)
{
switch (cb)
{
default:
break;
}
}
// else rc == VINF_IOM_HC_MMIO_WRITE => handle in ring3
}
LogFlow(("#%d pcnetMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Vrc\n",
return rc;
}
#ifdef IN_RING3
/**
* Device timer callback function.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
* @thread EMT
*/
{
int rc;
}
/**
* Restore timer callback.
*
* This is only called when've restored a saved state and temporarily
* disconnected the network link to inform the guest that network connections
* should be considered lost.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
*/
{
if (VBOX_FAILURE(rc))
{
pData->fLinkTempDown = false;
{
LogRel(("PCNet#%d: The link is back up again after the restore.\n",
Log(("#%d pcnetTimerRestore: Clearing ERR and CERR after load. cLinkDownReported=%d\n",
}
}
else
Log(("#%d pcnetTimerRestore: cLinkDownReported=%d, wait another 1500ms...\n",
}
/**
* Callback function for mapping an PCI I/O region.
*
* @return VBox status code.
* @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
* @param iRegion The region number.
* @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
* I/O port, else it's a physical address.
* This address is *NOT* relative to pci_mem_base like earlier!
* @param cb Region size.
* @param enmType One of the PCI_ADDRESS_SPACE_* values.
*/
{
int rc;
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
if (pData->fGCEnabled)
{
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
}
if (pData->fR0Enabled)
{
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* Callback function for mapping the MMIO region.
*
* @return VBox status code.
* @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
* @param iRegion The region number.
* @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
* I/O port, else it's a physical address.
* This address is *NOT* relative to pci_mem_base like earlier!
* @param cb Region size.
* @param enmType One of the PCI_ADDRESS_SPACE_* values.
*/
{
int rc;
/* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
if (VBOX_FAILURE(rc))
return rc;
return rc;
}
/**
* PCNET status info callback.
*
* @param pDevIns The device instance.
* @param pHlp The output helpers.
* @param pszArgs The arguments.
*/
{
bool fRcvRing = false;
bool fXmtRing = false;
/*
* Parse args.
*/
if (pszArgs)
{
}
/*
* Show info.
*/
"pcnet #%d: port=%RTiop mmio=%RGp mac-cfg=%.*Rhxs %s\n",
pData->fAm79C973 ? "Am79C973" : "Am79C970A", pData->fGCEnabled ? " GC" : "", pData->fR0Enabled ? " R0" : "");
PDMCritSectEnter(&pData->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
"CSR0=%04RX32:\n",
"CSR1=%04RX32:\n",
"CSR2=%04RX32:\n",
"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",
!!(pData->aCSR[3] & BIT(2)), !!(pData->aCSR[3] & BIT(3)), !!(pData->aCSR[3] & BIT(4)), CSR_LAPPEN(pData),
CSR_DXSUFLO(pData), !!(pData->aCSR[3] & BIT(8)), !!(pData->aCSR[3] & BIT(9)), !!(pData->aCSR[3] & BIT(10)),
"CSR4=%04RX32: JABM=%d JAB=%d TXSTRM=%d TXSTRT=%d RCVCOOM=%d RCVCCO=%d UINT=%d UINTCMD=%d\n"
" MFCOM=%d MFCO=%d ASTRP_RCV=%d APAD_XMT=%d DPOLL=%d TIMER=%d EMAPLUS=%d EN124=%d\n",
!!(pData->aCSR[4] & BIT( 0)), !!(pData->aCSR[4] & BIT( 1)), !!(pData->aCSR[4] & BIT( 2)), !!(pData->aCSR[4] & BIT( 3)),
!!(pData->aCSR[4] & BIT( 4)), !!(pData->aCSR[4] & BIT( 5)), !!(pData->aCSR[4] & BIT( 6)), !!(pData->aCSR[4] & BIT( 7)),
!!(pData->aCSR[4] & BIT( 8)), !!(pData->aCSR[4] & BIT( 9)), !!(pData->aCSR[4] & BIT(10)), !!(pData->aCSR[4] & BIT(11)),
!!(pData->aCSR[4] & BIT(12)), !!(pData->aCSR[4] & BIT(13)), !!(pData->aCSR[4] & BIT(14)), !!(pData->aCSR[4] & BIT(15)));
"CSR5=%04RX32:\n",
"CSR6=%04RX32: RLEN=%03x* TLEN=%03x* [* encoded]\n",
"CSR8..11=%04RX32,%04RX32,%04RX32,%04RX32: LADRF=%016RX64\n",
"CSR12..14=%04RX32,%04RX32,%04RX32: PADR=%02x %02x %02x %02x %02x %02x (Current MAC Address)\n",
"CSR15=%04RX32: DXR=%d DTX=%d LOOP=%d DXMTFCS=%d FCOLL=%d DRTY=%d INTL=%d PORTSEL=%d LTR=%d\n"
" MENDECL=%d DAPC=%d DLNKTST=%d DRCVPV=%d DRCVBC=%d PROM=%d\n",
!!(pData->aCSR[15] & BIT( 0)), !!(pData->aCSR[15] & BIT( 1)), !!(pData->aCSR[15] & BIT( 2)), !!(pData->aCSR[15] & BIT( 3)),
!!(pData->aCSR[15] & BIT( 4)), !!(pData->aCSR[15] & BIT( 5)), !!(pData->aCSR[15] & BIT( 6)), (pData->aCSR[15] >> 7) & 3,
!!(pData->aCSR[15] & BIT(12)), !!(pData->aCSR[15] & BIT(13)), !!(pData->aCSR[15] & BIT(14)), !!(pData->aCSR[15] & BIT(15)));
"CSR46=%04RX32: POLL=%04x (Poll Time Counter)\n",
"CSR47=%04RX32: POLLINT=%04x (Poll Time Interval)\n",
"CSR58=%04RX32: SWSTYLE=%02x %s SSIZE32=%d CSRPCNET=%d APERRENT=%d\n",
: "!!reserved!!",
"CSR112=%04RX32: MFC=%04x (Missed receive Frame Count)\n",
"CSR122=%04RX32: RCVALGN=%04x (Receive Frame Align)\n",
"CSR124=%04RX32: RPA=%04x (Runt Packet Accept)\n",
/*
* Dump the receive ring.
*/
"RCVRL=%04x RCVRC=%04x GCRDRA=%RX32 \n"
"CRDA=%08RX32 CRBA=%08RX32 CRBC=%03x CRST=%04x\n"
"NRDA=%08RX32 NRBA=%08RX32 NRBC=%03x NRST=%04x\n"
"NNRDA=%08RX32\n"
,
if (fRcvRing)
{
while (i-- > 0)
{
"%04x %RGp:%c%c RBADR=%08RX32 BCNT=%03x MCNT=%03x "
"OWN=%d ERR=%d FRAM=%d OFLO=%d CRC=%d BUFF=%d STP=%d ENP=%d BPE=%d "
"PAM=%d LAFM=%d BAM=%d RCC=%02x RPC=%02x ONES=%x ZEROS=%d\n",
}
}
/*
* Dump the transmit ring.
*/
"XMTRL=%04x XMTRC=%04x GCTDRA=%08RX32 BADX=%08RX32\n"
"PXDA=%08RX32 PXBC=%03x PXST=%04x\n"
"CXDA=%08RX32 CXBA=%08RX32 CXBC=%03x CXST=%04x\n"
"NXDA=%08RX32 NXBA=%08RX32 NXBC=%03x NXST=%04x\n"
"NNXDA=%08RX32\n"
,
if (fXmtRing)
{
while (i-- > 0)
{
"%04x %RGp:%c%c TBADR=%08RX32 BCNT=%03x OWN=%d "
"ERR=%d NOFCS=%d LTINT=%d ONE=%d DEF=%d STP=%d ENP=%d BPE=%d "
"BUFF=%d UFLO=%d EXDEF=%d LCOL=%d LCAR=%d RTRY=%d TDR=%03x TRC=%x ONES=%x\n"
,
}
}
}
/**
* Prepares for state saving.
* We must stop the RX process to prevent altering of the main memory after saving.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to save the state to.
*/
{
/* From now on drop all received packets to prevent altering of main memory after
* pgmR3Save() was called but before the RX thread is terminated */
return VINF_SUCCESS;
}
/**
* Saves a state of the PC-Net II device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to save the state to.
*/
{
#ifdef PCNET_NO_POLLING
return VINF_SUCCESS;
#else
#endif
}
/**
* Loads a saved PC-Net II device state.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to the saved state.
* @param u32Version The data unit version number.
*/
static DECLCALLBACK(int) pcnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
{
if (u32Version != PCNET_SAVEDSTATE_VERSION)
/* restore data */
#ifndef PCNET_NO_POLLING
#endif
? 4
: 3;
? 0
/* update promiscuous mode. */
#ifdef PCNET_NO_POLLING
/* Enable physical monitoring again (!) */
#endif
/* Indicate link down to the guest OS that all network connections have been lost. */
{
pData->fLinkTempDown = true;
pData->cLinkDownReported = 0;
}
return VINF_SUCCESS;
}
/**
* Queries an interface to the driver.
*
* @returns Pointer to interface.
* @returns NULL if the interface was not supported by the driver.
* @param pInterface Pointer to this interface structure.
* @param enmInterface The requested interface identification.
* @thread Any thread.
*/
static DECLCALLBACK(void *) pcnetQueryInterface(struct PDMIBASE *pInterface, PDMINTERFACE enmInterface)
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
return &pData->INetworkPort;
return &pData->INetworkConfig;
case PDMINTERFACE_LED_PORTS:
default:
return NULL;
}
}
/** Converts a pointer to PCNetState::INetworkPort to a PCNetState pointer. */
#define INETWORKPORT_2_DATA(pInterface) ( (PCNetState *)((uintptr_t)pInterface - RT_OFFSETOF(PCNetState, INetworkPort)) )
/**
* This must be called before the pfnRecieve() method is called.
*
* @returns Number of bytes the driver can receive.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @thread EMT
*/
{
int rc;
return cb;
}
/**
* Receive data from the network.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param pvBuf The available data.
* @param cb Number of bytes available in the buffer.
* @thread EMT
*/
{
int rc;
{
}
/* otherwise junk the data to Nirwana. */
return VINF_SUCCESS;
}
/** Converts a pointer to PCNetState::INetworkConfig to a PCNetState pointer. */
#define INETWORKCONFIG_2_DATA(pInterface) ( (PCNetState *)((uintptr_t)pInterface - RT_OFFSETOF(PCNetState, INetworkConfig)) )
/**
* Gets the current Media Access Control (MAC) address.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param pMac Where to store the MAC address.
* @thread EMT
*/
{
return VINF_SUCCESS;
}
/**
* Gets the new link state.
*
* @returns The current link state.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @thread EMT
*/
{
return PDMNETWORKLINKSTATE_UP;
return PDMNETWORKLINKSTATE_DOWN;
if (pData->fLinkTempDown)
return PDMNETWORKLINKSTATE_DOWN_RESUME;
AssertMsgFailed(("Invalid link state!\n"));
return PDMNETWORKLINKSTATE_INVALID;
}
/**
* Sets the new link state.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param enmState The new link state
* @thread EMT
*/
static DECLCALLBACK(int) pcnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
{
bool fLinkUp;
if ( enmState != PDMNETWORKLINKSTATE_DOWN
&& enmState != PDMNETWORKLINKSTATE_UP)
{
return VERR_INVALID_PARAMETER;
}
/* has the state changed? */
{
if (fLinkUp)
{
/* connect */
}
else
{
/* disconnect */
pData->cLinkDownReported = 0;
}
}
return VINF_SUCCESS;
}
/**
* Gets the pointer to the status LED of a unit.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param iLUN The unit which status LED we desire.
* @param ppLed Where to store the LED pointer.
*/
static DECLCALLBACK(int) pcnetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
if (iLUN == 0)
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/**
* @copydoc FNPDMDEVRESET
*/
{
if (pData->fLinkTempDown)
{
}
/** @todo How to flush the queues? */
}
/**
* @copydoc FNPDMDEVRELOCATE
*/
{
#ifdef PCNET_NO_POLLING
#else
#endif
}
/**
* Destruct a device instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
{
pData->hSendEventSem = 0;
return VINF_SUCCESS;
}
/**
* Construct a device instance for a VM.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* If the registration structure is needed, pDevIns->pDevReg points to it.
* @param iInstance Instance number. Use this to figure out which registers and such to use.
* The device number is also found in pDevIns->iInstance, but since it's
* likely to be freqently used PDM passes it as parameter.
* @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
* of the device instance. It's also found in pDevIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
*/
{
char szTmp[128];
int rc;
/* up to four instances are supported */
/*
* Validate configuration.
*/
N_("Invalid configuraton for pcnet device"));
/*
* Read the configuration.
*/
if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"MAC\" value"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"CableConnected\" value"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"Am79C973\" value"));
#ifdef PCNET_GC_ENABLED
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pData->fGCEnabled = true;
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"GCEnabled\" value"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pData->fR0Enabled = true;
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"R0Enabled\" value"));
#else /* !PCNET_GC_ENABLED */
pData->fGCEnabled = false;
pData->fR0Enabled = false;
#endif /* !PCNET_GC_ENABLED */
/*
* Initialize data (most of it anyway).
*/
/* IBase */
/* INeworkPort */
/* INetworkConfig */
/* ILeds */
/* PCI Device */
/* subsystem and subvendor IDs */
/*
* Register the PCI device, its I/O regions, the timer and the saved state item.
*/
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
if (VBOX_FAILURE(rc))
return rc;
#ifdef PCNET_NO_POLLING
rc = PDMR3GetSymbolR0Lazy(PDMDevHlpGetVM(pDevIns), NULL, "EMInterpretInstruction", &pData->pfnEMInterpretInstructionR0);
if (VBOX_SUCCESS(rc))
{
/*
* Resolve the GC handler.
*/
rc = PDMR3GetSymbolGCLazy(PDMDevHlpGetVM(pDevIns), NULL, "EMInterpretInstruction", (RTGCPTR *)&pData->pfnEMInterpretInstructionGC);
}
if (VBOX_FAILURE(rc))
{
return rc;
}
#else
if (VBOX_FAILURE(rc))
{
return rc;
}
#endif
if (VBOX_FAILURE(rc))
{
return rc;
}
/** @todo r=bird: we're not locking down pcnet properly during saving and loading! */
PCNET_SAVEDSTATE_VERSION, sizeof(*pData),
if (VBOX_FAILURE(rc))
return rc;
/*
* Initialize critical section.
* This must of course be done before attaching drivers or anything else which can call us back..
*/
char szName[24];
if (VBOX_FAILURE(rc))
return rc;
/*
* Create the transmit queue.
*/
if (VBOX_FAILURE(rc))
return rc;
/*
* Create the RX notifer signaller.
*/
if (VBOX_FAILURE(rc))
return rc;
/*
* Register the info item.
*/
/*
* Attach status driver (optional).
*/
if (VBOX_SUCCESS(rc))
else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
{
return rc;
}
/*
* Attach driver.
*/
if (VBOX_SUCCESS(rc))
{
if (rc == VINF_NAT_DNS)
{
#ifdef __LINUX__
N_("A Domain Name Server (DNS) for NAT networking could not be determined. Please check your /etc/resolv.conf for <tt>nameserver</tt> entries. Either add one manually (<i>man resolv.conf</i>) or ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
#else
N_("A Domain Name Server (DNS) for NAT networking could not be determined. Ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
#endif
}
{
AssertMsgFailed(("Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
return VERR_PDM_MISSING_INTERFACE_BELOW;
}
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
Log(("No attached driver!\n"));
else
return rc;
/*
* Reset the device state. (Do after attaching.)
*/
/* Create send queue for the async send thread. */
/* Create asynchronous thread */
rc = RTThreadCreate(&pData->hSendThread, pcnetAsyncSend, (void *)pData, 128*1024, RTTHREADTYPE_IO, 0, "PCNET_SEND");
#ifdef VBOX_WITH_STATISTICS
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOReadGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in GC", "/Devices/PCNet%d/MMIO/ReadGC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOReadHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in HC", "/Devices/PCNet%d/MMIO/ReadHC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOWriteGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in GC", "/Devices/PCNet%d/MMIO/WriteGC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMMIOWriteHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in HC", "/Devices/PCNet%d/MMIO/WriteHC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatAPROMRead, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM reads", "/Devices/PCNet%d/IO/APROMRead", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatAPROMWrite, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling APROM writes", "/Devices/PCNet%d/IO/APROMWrite", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOReadGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNetIO reads in GC", "/Devices/PCNet%d/IO/ReadGC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOReadHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNetIO reads in HC", "/Devices/PCNet%d/IO/ReadHC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOWriteGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet IO writes in GC", "/Devices/PCNet%d/IO/WriteGC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatIOWriteHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet IO writes in HC", "/Devices/PCNet%d/IO/WriteHC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet Timer", "/Devices/PCNet%d/Timer", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet receive", "/Devices/PCNet%d/Receive", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet transmit in HC", "/Devices/PCNet%d/Transmit/Total", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet send transmit in HC", "/Devices/PCNet%d/Transmit/Send", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTdtePollGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TdtePoll in GC", "/Devices/PCNet%d/TdtePollGC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTdtePollHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TdtePoll in HC", "/Devices/PCNet%d/TdtePollHC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRdtePollGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet RdtePoll in GC", "/Devices/PCNet%d/RdtePollGC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRdtePollHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet RdtePoll in HC", "/Devices/PCNet%d/RdtePollHC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTmdStoreGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TmdStore in GC", "/Devices/PCNet%d/TmdStoreGC", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTmdStoreHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet TmdStore in HC", "/Devices/PCNet%d/TmdStoreHC", iInstance);
unsigned i;
PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitFlushIrq/%d", iInstance, i + 1);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitFlushIrq/%d+", iInstance, i + 1);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitChainCounts/%d", iInstance, i + 1);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/XmitChainCounts/%d+", iInstance, i + 1);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatXmitSkipCurrent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "", "/Devices/PCNet%d/Xmit/Skipped", iInstance, i + 1);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet interrupt checks", "/Devices/PCNet%d/Interrupt", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatPollTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling PCNet poll timer", "/Devices/PCNet%d/PollTimer", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatMIIReads, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of MII reads", "/Devices/PCNet%d/MIIReads", iInstance);
# ifdef PCNET_NO_POLLING
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRCVRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of receive ring writes", "/Devices/PCNet%d/Ring/RCVWrites", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatTXRingWrite, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of transmit ring writes", "/Devices/PCNet%d/Ring/TXWrites", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteHC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCNet%d/Ring/HC/Writes", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCNet%d/Ring/R0/Writes", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteGC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored ring page writes", "/Devices/PCNet%d/Ring/GC/Writes", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteFailedHC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCNet%d/Ring/HC/Failed", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteFailedR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCNet%d/Ring/R0/Failed", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteFailedGC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of failed ring page writes", "/Devices/PCNet%d/Ring/GC/Failed", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteOutsideRangeHC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring range", "/Devices/PCNet%d/Ring/HC/Outside", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteOutsideRangeR0, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring range", "/Devices/PCNet%d/Ring/R0/Outside", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pData->StatRingWriteOutsideRangeGC, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of monitored writes outside ring range", "/Devices/PCNet%d/Ring/GC/Outside", iInstance);
# endif /* PCNET_NO_POLLING */
#endif
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DevicePCNet =
{
/* u32Version */
/* szDeviceName */
"pcnet",
/* szGCMod */
#ifdef PCNET_GC_ENABLED
"VBoxDDGC.gc",
"VBoxDDR0.r0",
#else
"",
"",
#endif
/* pszDescription */
"AMD PC-Net II Ethernet controller.\n",
/* fFlags */
#ifdef PCNET_GC_ENABLED
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
#else
#endif
/* fClass */
/* cMaxInstances */
4,
/* cbInstance */
sizeof(PCNetState),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */