c6dd095321
and cleaned up the PCI devices code.
1592 lines
55 KiB
C++
1592 lines
55 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intel(R) 82540EM Gigabit Ethernet support (ported from QEMU)
|
|
// Software developer's manual:
|
|
// http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf
|
|
//
|
|
// Nir Peleg, Tutis Systems Ltd. for Qumranet Inc.
|
|
// Copyright (c) 2008 Qumranet
|
|
// Based on work done by:
|
|
// Copyright (c) 2007 Dan Aloni
|
|
// Copyright (c) 2004 Antony T Curtis
|
|
//
|
|
// Copyright (C) 2011-2013 The Bochs Project
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
|
|
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
|
|
// is used to know when we are exporting symbols and when we are importing.
|
|
#define BX_PLUGGABLE
|
|
|
|
#include "iodev.h"
|
|
#if BX_SUPPORT_PCI && BX_SUPPORT_E1000
|
|
|
|
#include "pci.h"
|
|
#include "netmod.h"
|
|
#include "e1000.h"
|
|
|
|
#define LOG_THIS theE1000Device->
|
|
|
|
bx_e1000_c* theE1000Device = NULL;
|
|
|
|
const Bit8u e1000_iomask[64] = {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
|
|
|
|
#define E1000_CTRL 0x00000 // Device Control - RW
|
|
#define E1000_STATUS 0x00008 // Device Status - RO
|
|
#define E1000_EECD 0x00010 // EEPROM/Flash Control - RW
|
|
#define E1000_EERD 0x00014 // EEPROM Read - RW
|
|
#define E1000_MDIC 0x00020 // MDI Control - RW
|
|
#define E1000_VET 0x00038 // VLAN Ether Type - RW
|
|
#define E1000_ICR 0x000C0 // Interrupt Cause Read - R/clr
|
|
#define E1000_ICS 0x000C8 // Interrupt Cause Set - WO
|
|
#define E1000_IMS 0x000D0 // Interrupt Mask Set - RW
|
|
#define E1000_IMC 0x000D8 // Interrupt Mask Clear - WO
|
|
#define E1000_RCTL 0x00100 // RX Control - RW
|
|
#define E1000_TCTL 0x00400 // TX Control - RW
|
|
#define E1000_LEDCTL 0x00E00 // LED Control - RW
|
|
#define E1000_PBA 0x01000 // Packet Buffer Allocation - RW
|
|
#define E1000_RDBAL 0x02800 // RX Descriptor Base Address Low - RW
|
|
#define E1000_RDBAH 0x02804 // RX Descriptor Base Address High - RW
|
|
#define E1000_RDLEN 0x02808 // RX Descriptor Length - RW
|
|
#define E1000_RDH 0x02810 // RX Descriptor Head - RW
|
|
#define E1000_RDT 0x02818 // RX Descriptor Tail - RW
|
|
#define E1000_TDBAL 0x03800 // TX Descriptor Base Address Low - RW
|
|
#define E1000_TDBAH 0x03804 // TX Descriptor Base Address High - RW
|
|
#define E1000_TDLEN 0x03808 // TX Descriptor Length - RW
|
|
#define E1000_TDH 0x03810 // TX Descriptor Head - RW
|
|
#define E1000_TDT 0x03818 // TX Descripotr Tail - RW
|
|
#define E1000_TXDCTL 0x03828 // TX Descriptor Control - RW
|
|
#define E1000_CRCERRS 0x04000 // CRC Error Count - R/clr
|
|
#define E1000_MPC 0x04010 // Missed Packet Count - R/clr
|
|
#define E1000_GPRC 0x04074 // Good Packets RX Count - R/clr
|
|
#define E1000_GPTC 0x04080 // Good Packets TX Count - R/clr
|
|
#define E1000_TORL 0x040C0 // Total Octets RX Low - R/clr
|
|
#define E1000_TORH 0x040C4 // Total Octets RX High - R/clr
|
|
#define E1000_TOTL 0x040C8 // Total Octets TX Low - R/clr
|
|
#define E1000_TOTH 0x040CC // Total Octets TX High - R/clr
|
|
#define E1000_TPR 0x040D0 // Total Packets RX - R/clr
|
|
#define E1000_TPT 0x040D4 // Total Packets TX - R/clr
|
|
#define E1000_MTA 0x05200 // Multicast Table Array - RW Array
|
|
#define E1000_RA 0x05400 // Receive Address - RW Array
|
|
#define E1000_VFTA 0x05600 // VLAN Filter Table Array - RW Array
|
|
#define E1000_WUFC 0x05808 // Wakeup Filter Control - RW
|
|
#define E1000_MANC 0x05820 // Management Control - RW
|
|
#define E1000_SWSM 0x05B50 // SW Semaphore
|
|
|
|
#define PHY_CTRL 0x00 // Control Register
|
|
#define PHY_STATUS 0x01 // Status Regiser
|
|
#define PHY_ID1 0x02 // Phy Id Reg (word 1)
|
|
#define PHY_ID2 0x03 // Phy Id Reg (word 2)
|
|
#define PHY_AUTONEG_ADV 0x04 // Autoneg Advertisement
|
|
#define PHY_LP_ABILITY 0x05 // Link Partner Ability (Base Page)
|
|
#define PHY_1000T_CTRL 0x09 // 1000Base-T Control Reg
|
|
#define PHY_1000T_STATUS 0x0A // 1000Base-T Status Reg
|
|
|
|
#define M88E1000_PHY_SPEC_CTRL 0x10 // PHY Specific Control Register
|
|
#define M88E1000_PHY_SPEC_STATUS 0x11 // PHY Specific Status Register
|
|
#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 // Extended PHY Specific Control
|
|
|
|
#define E1000_ICR_TXDW 0x00000001 // Transmit desc written back
|
|
#define E1000_ICR_TXQE 0x00000002 // Transmit Queue empty
|
|
#define E1000_ICR_RXDMT0 0x00000010 // rx desc min. threshold (0)
|
|
#define E1000_ICR_RXO 0x00000040 // rx overrun
|
|
#define E1000_ICR_RXT0 0x00000080 // rx timer intr (ring 0)
|
|
#define E1000_ICR_MDAC 0x00000200 // MDIO access complete
|
|
#define E1000_ICR_INT_ASSERTED 0x80000000 // If this bit asserted, the driver should claim the interrupt
|
|
|
|
#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 // rx desc min. threshold
|
|
#define E1000_ICS_RXO E1000_ICR_RXO // rx overrun
|
|
#define E1000_ICS_RXT0 E1000_ICR_RXT0 // rx timer intr
|
|
#define E1000_ICS_TXQE E1000_ICR_TXQE // Transmit Queue empty
|
|
|
|
#define E1000_RCTL_EN 0x00000002 // enable
|
|
#define E1000_RCTL_UPE 0x00000008 // unicast promiscuous enable
|
|
#define E1000_RCTL_MPE 0x00000010 // multicast promiscuous enab
|
|
#define E1000_RCTL_RDMTS_QUAT 0x00000100 // rx desc min threshold size
|
|
#define E1000_RCTL_MO_SHIFT 12 // multicast offset shift
|
|
#define E1000_RCTL_BAM 0x00008000 // broadcast enable
|
|
// these buffer sizes are valid if E1000_RCTL_BSEX is 0
|
|
#define E1000_RCTL_SZ_2048 0x00000000 // rx buffer size 2048
|
|
#define E1000_RCTL_SZ_1024 0x00010000 // rx buffer size 1024
|
|
#define E1000_RCTL_SZ_512 0x00020000 // rx buffer size 512
|
|
#define E1000_RCTL_SZ_256 0x00030000 // rx buffer size 256
|
|
// these buffer sizes are valid if E1000_RCTL_BSEX is 1
|
|
#define E1000_RCTL_SZ_16384 0x00010000 // rx buffer size 16384
|
|
#define E1000_RCTL_SZ_8192 0x00020000 // rx buffer size 8192
|
|
#define E1000_RCTL_SZ_4096 0x00030000 // rx buffer size 4096
|
|
#define E1000_RCTL_VFE 0x00040000 // vlan filter enable
|
|
#define E1000_RCTL_BSEX 0x02000000 // Buffer size extension
|
|
#define E1000_RCTL_SECRC 0x04000000 // Strip Ethernet CRC
|
|
|
|
#define E1000_EEPROM_RW_REG_DATA 16 // Offset to data in EEPROM read/write registers
|
|
#define E1000_EEPROM_RW_REG_DONE 0x10 // Offset to READ/WRITE done bit
|
|
#define E1000_EEPROM_RW_REG_START 1 // First bit for telling part to start operation
|
|
#define E1000_EEPROM_RW_ADDR_SHIFT 8 // Shift to the address bits
|
|
|
|
#define E1000_CTRL_SLU 0x00000040 // Set link up (Force Link)
|
|
#define E1000_CTRL_SPD_1000 0x00000200 // Force 1Gb
|
|
#define E1000_CTRL_SWDPIN0 0x00040000 // SWDPIN 0 value
|
|
#define E1000_CTRL_SWDPIN2 0x00100000 // SWDPIN 2 value
|
|
#define E1000_CTRL_RST 0x04000000 // Global reset
|
|
#define E1000_CTRL_VME 0x40000000 // IEEE VLAN mode enable
|
|
|
|
#define E1000_STATUS_FD 0x00000001 // Full duplex.0=half,1=full
|
|
#define E1000_STATUS_LU 0x00000002 // Link up.0=no,1=link
|
|
#define E1000_STATUS_SPEED_1000 0x00000080 // Speed 1000Mb/s
|
|
#define E1000_STATUS_ASDV 0x00000300 // Auto speed detect value
|
|
#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 // Status of Master requests
|
|
#define E1000_STATUS_MTXCKOK 0x00000400 // MTX clock running OK
|
|
|
|
#define E1000_EECD_SK 0x00000001 // EEPROM Clock
|
|
#define E1000_EECD_CS 0x00000002 // EEPROM Chip Select
|
|
#define E1000_EECD_DI 0x00000004 // EEPROM Data In
|
|
#define E1000_EECD_DO 0x00000008 // EEPROM Data Out
|
|
#define E1000_EECD_FWE_MASK 0x00000030
|
|
#define E1000_EECD_REQ 0x00000040 // EEPROM Access Request
|
|
#define E1000_EECD_GNT 0x00000080 // EEPROM Access Grant
|
|
#define E1000_EECD_PRES 0x00000100 // EEPROM Present
|
|
|
|
#define E1000_MDIC_DATA_MASK 0x0000FFFF
|
|
#define E1000_MDIC_REG_MASK 0x001F0000
|
|
#define E1000_MDIC_REG_SHIFT 16
|
|
#define E1000_MDIC_PHY_MASK 0x03E00000
|
|
#define E1000_MDIC_PHY_SHIFT 21
|
|
#define E1000_MDIC_OP_WRITE 0x04000000
|
|
#define E1000_MDIC_OP_READ 0x08000000
|
|
#define E1000_MDIC_READY 0x10000000
|
|
#define E1000_MDIC_INT_EN 0x20000000
|
|
#define E1000_MDIC_ERROR 0x40000000
|
|
|
|
#define EEPROM_READ_OPCODE_MICROWIRE 0x6 // EEPROM read opcode
|
|
|
|
#define E1000_TXD_DTYP_D 0x00100000 // Data Descriptor
|
|
#define E1000_TXD_POPTS_IXSM 0x01 // Insert IP checksum
|
|
#define E1000_TXD_POPTS_TXSM 0x02 // Insert TCP/UDP checksum
|
|
#define E1000_TXD_CMD_RS 0x08000000 // Report Status
|
|
#define E1000_TXD_CMD_RPS 0x10000000 // Report Packet Sent
|
|
#define E1000_TXD_CMD_VLE 0x40000000 // Add VLAN tag
|
|
#define E1000_TXD_CMD_DEXT 0x20000000 // Descriptor extension (0 = legacy)
|
|
#define E1000_TXD_STAT_DD 0x00000001 // Descriptor Done
|
|
#define E1000_TXD_STAT_EC 0x00000002 // Excess Collisions
|
|
#define E1000_TXD_STAT_LC 0x00000004 // Late Collisions
|
|
#define E1000_TXD_STAT_TU 0x00000008 // Transmit underrun
|
|
#define E1000_TXD_CMD_EOP 0x01000000 // End of Packet
|
|
#define E1000_TXD_CMD_TCP 0x01000000 // TCP packet
|
|
#define E1000_TXD_CMD_IP 0x02000000 // IP packet
|
|
#define E1000_TXD_CMD_TSE 0x04000000 // TCP Seg enable
|
|
|
|
#define E1000_TCTL_EN 0x00000002 // enable tx
|
|
|
|
struct e1000_rx_desc {
|
|
Bit64u buffer_addr; // Address of the descriptor's data buffer
|
|
Bit16u length; // Length of data DMAed into data buffer
|
|
Bit16u csum; // Packet checksum
|
|
Bit8u status; // Descriptor status
|
|
Bit8u errors; // Descriptor Errors
|
|
Bit16u special;
|
|
};
|
|
|
|
#define E1000_RXD_STAT_DD 0x01 // Descriptor Done
|
|
#define E1000_RXD_STAT_EOP 0x02 // End of Packet
|
|
#define E1000_RXD_STAT_IXSM 0x04 // Ignore checksum
|
|
#define E1000_RXD_STAT_VP 0x08 // IEEE VLAN Packet
|
|
|
|
#define E1000_RAH_AV 0x80000000 // Receive descriptor valid
|
|
|
|
struct e1000_context_desc {
|
|
union {
|
|
Bit32u ip_config;
|
|
struct {
|
|
Bit8u ipcss; // IP checksum start */
|
|
Bit8u ipcso; // IP checksum offset */
|
|
Bit16u ipcse; // IP checksum end */
|
|
} ip_fields;
|
|
} lower_setup;
|
|
union {
|
|
Bit32u tcp_config;
|
|
struct {
|
|
Bit8u tucss; // TCP checksum start */
|
|
Bit8u tucso; // TCP checksum offset */
|
|
Bit16u tucse; // TCP checksum end */
|
|
} tcp_fields;
|
|
} upper_setup;
|
|
Bit32u cmd_and_length;
|
|
union {
|
|
Bit32u data;
|
|
struct {
|
|
Bit8u status; // Descriptor status */
|
|
Bit8u hdr_len; // Header length */
|
|
Bit16u mss; // Maximum segment size */
|
|
} fields;
|
|
} tcp_seg_setup;
|
|
};
|
|
|
|
#define E1000_MANC_RMCP_EN 0x00000100 // Enable RCMP 026Fh Filtering
|
|
#define E1000_MANC_0298_EN 0x00000200 // Enable RCMP 0298h Filtering
|
|
#define E1000_MANC_ARP_EN 0x00002000 // Enable ARP Request Filtering
|
|
#define E1000_MANC_RCV_TCO_EN 0x00020000 // Receive TCO Packets Enabled
|
|
#define E1000_MANC_EN_MNG2HOST 0x00200000 // Enable MNG packets to host memory
|
|
|
|
#define EEPROM_CHECKSUM_REG 0x3f
|
|
#define EEPROM_SUM 0xbaba
|
|
|
|
#define MIN_BUF_SIZE 60
|
|
|
|
#define defreg(x) x = (E1000_##x>>2)
|
|
enum {
|
|
defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC),
|
|
defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC),
|
|
defreg(IMS), defreg(LEDCTL),defreg(MANC), defreg(MDIC),
|
|
defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH),
|
|
defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT),
|
|
defreg(STATUS),defreg(SWSM), defreg(TCTL), defreg(TDBAH),
|
|
defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT),
|
|
defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL),
|
|
defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC),
|
|
defreg(RA), defreg(MTA), defreg(CRCERRS),defreg(VFTA),
|
|
defreg(VET),
|
|
};
|
|
|
|
enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };
|
|
static const char phy_regcap[0x20] = {
|
|
PHY_RW, PHY_R, PHY_R, PHY_R, PHY_RW, PHY_R, 0, 0,
|
|
0, PHY_RW, PHY_R, 0, 0, 0, 0, 0,
|
|
PHY_RW, PHY_R, 0, 0, PHY_RW, PHY_R, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
static const Bit16u e1000_eeprom_template[64] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
|
|
0x3000, 0x1000, 0x6403, 0x100e, 0x8086, 0x100e, 0x8086, 0x3040,
|
|
0x0008, 0x2000, 0x7e14, 0x0048, 0x1000, 0x00d8, 0x0000, 0x2700,
|
|
0x6cc9, 0x3150, 0x0722, 0x040b, 0x0984, 0x0000, 0xc000, 0x0706,
|
|
0x1008, 0x0000, 0x0f04, 0x7fff, 0x4d01, 0xffff, 0xffff, 0xffff,
|
|
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
|
0x0100, 0x4000, 0x121c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
|
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000,
|
|
};
|
|
|
|
// builtin configuration handling functions
|
|
|
|
void e1000_init_options(void)
|
|
{
|
|
bx_param_c *network = SIM->get_param("network");
|
|
bx_list_c *menu = new bx_list_c(network, "e1000", "Intel(R) Gigabit Ethernet");
|
|
menu->set_options(menu->SHOW_PARENT);
|
|
bx_param_bool_c *enabled = new bx_param_bool_c(menu,
|
|
"enabled",
|
|
"Enable Intel(R) Gigabit Ethernet emulation",
|
|
"Enables the Intel(R) Gigabit Ethernet emulation",
|
|
1);
|
|
SIM->init_std_nic_options("Intel(R) Gigabit Ethernet", menu);
|
|
enabled->set_dependent_list(menu->clone());
|
|
}
|
|
|
|
Bit32s e1000_options_parser(const char *context, int num_params, char *params[])
|
|
{
|
|
int ret, valid = 0;
|
|
|
|
if (!strcmp(params[0], "e1000")) {
|
|
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_E1000);
|
|
if (!SIM->get_param_bool("enabled", base)->get()) {
|
|
SIM->get_param_enum("ethmod", base)->set_by_name("null");
|
|
}
|
|
for (int i = 1; i < num_params; i++) {
|
|
ret = SIM->parse_nic_params(context, params[i], base);
|
|
if (ret > 0) {
|
|
valid |= ret;
|
|
}
|
|
}
|
|
if (!SIM->get_param_bool("enabled", base)->get()) {
|
|
if (valid == 0x04) {
|
|
SIM->get_param_bool("enabled", base)->set(1);
|
|
}
|
|
}
|
|
if (valid < 0x80) {
|
|
if ((valid & 0x04) == 0) {
|
|
BX_PANIC(("%s: 'e1000' directive incomplete (mac is required)", context));
|
|
}
|
|
}
|
|
} else {
|
|
BX_PANIC(("%s: unknown directive '%s'", context, params[0]));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Bit32s e1000_options_save(FILE *fp)
|
|
{
|
|
return SIM->write_param_list(fp, (bx_list_c*) SIM->get_param(BXPN_E1000), NULL, 0);
|
|
}
|
|
|
|
// device plugin entry points
|
|
|
|
int libe1000_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
|
|
{
|
|
theE1000Device = new bx_e1000_c();
|
|
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theE1000Device, BX_PLUGIN_E1000);
|
|
// add new configuration parameter for the config interface
|
|
e1000_init_options();
|
|
// register add-on option for bochsrc and command line
|
|
SIM->register_addon_option("e1000", e1000_options_parser, e1000_options_save);
|
|
return 0; // Success
|
|
}
|
|
|
|
void libe1000_LTX_plugin_fini(void)
|
|
{
|
|
SIM->unregister_addon_option("e1000");
|
|
bx_list_c *menu = (bx_list_c*)SIM->get_param("network");
|
|
menu->remove("e1000");
|
|
delete theE1000Device;
|
|
}
|
|
|
|
// macros and helper functions
|
|
|
|
#if defined (BX_LITTLE_ENDIAN)
|
|
#define cpu_to_le16(val) (val)
|
|
#define cpu_to_le32(val) (val)
|
|
#define cpu_to_le64(val) (val)
|
|
#else
|
|
#define cpu_to_le16(val) bx_bswap16(val)
|
|
#define cpu_to_le32(val) bx_bswap32(val)
|
|
#define cpu_to_le64(val) bx_bswap64(val)
|
|
#endif
|
|
|
|
#define le16_to_cpu cpu_to_le16
|
|
#define le32_to_cpu cpu_to_le32
|
|
#define le64_to_cpu cpu_to_le64
|
|
|
|
Bit32u net_checksum_add(Bit8u *buf, unsigned buf_len)
|
|
{
|
|
Bit32u sum = 0;
|
|
unsigned i;
|
|
|
|
for (i = 0; i < buf_len; i++) {
|
|
if (i & 1)
|
|
sum += (Bit32u)buf[i];
|
|
else
|
|
sum += (Bit32u)buf[i] << 8;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
Bit16u net_checksum_finish(Bit32u sum)
|
|
{
|
|
while (sum >> 16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
return ~sum;
|
|
}
|
|
|
|
|
|
// the device object
|
|
|
|
bx_e1000_c::bx_e1000_c()
|
|
{
|
|
put("E1000");
|
|
memset(&s, 0, sizeof(bx_e1000_t));
|
|
s.tx_timer_index = BX_NULL_TIMER_HANDLE;
|
|
ethdev = NULL;
|
|
}
|
|
|
|
bx_e1000_c::~bx_e1000_c()
|
|
{
|
|
if (s.mac_reg != NULL) {
|
|
delete [] s.mac_reg;
|
|
}
|
|
if (s.tx.vlan != NULL) {
|
|
delete [] s.tx.vlan;
|
|
}
|
|
if (ethdev != NULL) {
|
|
delete ethdev;
|
|
}
|
|
SIM->get_bochs_root()->remove("e1000");
|
|
BX_DEBUG(("Exit"));
|
|
}
|
|
|
|
void bx_e1000_c::init(void)
|
|
{
|
|
Bit8u macaddr[6];
|
|
int i;
|
|
Bit16u checksum = 0;
|
|
const char *bootrom;
|
|
|
|
// Read in values from config interface
|
|
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_E1000);
|
|
// Check if the device is disabled or not configured
|
|
if (!SIM->get_param_bool("enabled", base)->get()) {
|
|
BX_INFO(("E1000 disabled"));
|
|
// mark unused plugin for removal
|
|
((bx_param_bool_c*)((bx_list_c*)SIM->get_param(BXPN_PLUGIN_CTRL))->get_by_name("e1000"))->set(0);
|
|
return;
|
|
}
|
|
memcpy(macaddr, SIM->get_param_string("mac", base)->getptr(), 6);
|
|
|
|
memcpy(BX_E1000_THIS s.eeprom_data, e1000_eeprom_template,
|
|
sizeof(e1000_eeprom_template));
|
|
for (i = 0; i < 3; i++)
|
|
BX_E1000_THIS s.eeprom_data[i] = (macaddr[2*i+1]<<8) | macaddr[2*i];
|
|
for (i = 0; i < EEPROM_CHECKSUM_REG; i++)
|
|
checksum += BX_E1000_THIS s.eeprom_data[i];
|
|
checksum = (Bit16u) EEPROM_SUM - checksum;
|
|
BX_E1000_THIS s.eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
|
|
BX_E1000_THIS s.mac_reg = new Bit32u[0x8000];
|
|
BX_E1000_THIS s.tx.vlan = new Bit8u[0x10004];
|
|
BX_E1000_THIS s.tx.data = BX_E1000_THIS s.tx.vlan + 4;
|
|
|
|
BX_E1000_THIS s.devfunc = 0x00;
|
|
DEV_register_pci_handlers(this, &BX_E1000_THIS s.devfunc, BX_PLUGIN_E1000,
|
|
"Experimental Intel(R) Gigabit Ethernet");
|
|
|
|
// initialize readonly registers
|
|
init_pci_conf(0x8086, 0x100e, 0x03, 0x020000, 0x00);
|
|
BX_E1000_THIS pci_conf[0x3d] = BX_PCI_INTA;
|
|
|
|
BX_E1000_THIS pci_base_address[0] = 0;
|
|
BX_E1000_THIS pci_base_address[1] = 0;
|
|
BX_E1000_THIS pci_rom_address = 0;
|
|
bootrom = SIM->get_param_string("bootrom", base)->getptr();
|
|
if ((strlen(bootrom) > 0) && (strcmp(bootrom, "none"))) {
|
|
BX_E1000_THIS load_pci_rom(bootrom);
|
|
}
|
|
|
|
if (BX_E1000_THIS s.tx_timer_index == BX_NULL_TIMER_HANDLE) {
|
|
BX_E1000_THIS s.tx_timer_index =
|
|
bx_pc_system.register_timer(this, tx_timer_handler, 0,
|
|
0, 0, "e1000"); // one-shot, inactive
|
|
}
|
|
BX_E1000_THIS s.statusbar_id = bx_gui->register_statusitem("E1000", 1);
|
|
|
|
// Attach to the selected ethernet module
|
|
BX_E1000_THIS ethdev = DEV_net_init_module(base, rx_handler, rx_status_handler, this);
|
|
|
|
BX_INFO(("E1000 initialized"));
|
|
}
|
|
|
|
void bx_e1000_c::reset(unsigned type)
|
|
{
|
|
unsigned i;
|
|
Bit8u *saved_ptr;
|
|
|
|
static const struct reset_vals_t {
|
|
unsigned addr;
|
|
unsigned char val;
|
|
} reset_vals[] = {
|
|
{ 0x04, 0x03 }, { 0x05, 0x00 }, // command io / memory
|
|
{ 0x06, 0x00 }, { 0x07, 0x00 }, // status
|
|
// address space 0x10 - 0x13
|
|
{ 0x10, 0x00 }, { 0x11, 0x00 },
|
|
{ 0x12, 0x00 }, { 0x13, 0x00 },
|
|
// address space 0x14 - 0x17
|
|
{ 0x14, 0x01 }, { 0x15, 0x00 },
|
|
{ 0x16, 0x00 }, { 0x17, 0x00 },
|
|
{ 0x3c, 0x00 }, // IRQ
|
|
};
|
|
for (i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) {
|
|
BX_E1000_THIS pci_conf[reset_vals[i].addr] = reset_vals[i].val;
|
|
}
|
|
|
|
memset(BX_E1000_THIS s.phy_reg, 0, sizeof(BX_E1000_THIS s.phy_reg));
|
|
BX_E1000_THIS s.phy_reg[PHY_CTRL] = 0x1140;
|
|
BX_E1000_THIS s.phy_reg[PHY_STATUS] = 0x796d; // link initially up
|
|
BX_E1000_THIS s.phy_reg[PHY_ID1] = 0x141;
|
|
BX_E1000_THIS s.phy_reg[PHY_ID2] = 0xc20;
|
|
BX_E1000_THIS s.phy_reg[PHY_1000T_CTRL] = 0x0e00;
|
|
BX_E1000_THIS s.phy_reg[M88E1000_PHY_SPEC_CTRL] = 0x360;
|
|
BX_E1000_THIS s.phy_reg[M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60;
|
|
BX_E1000_THIS s.phy_reg[PHY_AUTONEG_ADV] = 0xde1;
|
|
BX_E1000_THIS s.phy_reg[PHY_LP_ABILITY] = 0x1e0;
|
|
BX_E1000_THIS s.phy_reg[PHY_1000T_STATUS] = 0x3c00;
|
|
BX_E1000_THIS s.phy_reg[M88E1000_PHY_SPEC_STATUS] = 0xac00;
|
|
memset(BX_E1000_THIS s.mac_reg, 0, 0x20000);
|
|
BX_E1000_THIS s.mac_reg[PBA] = 0x00100030;
|
|
BX_E1000_THIS s.mac_reg[LEDCTL] = 0x602;
|
|
BX_E1000_THIS s.mac_reg[CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
|
|
E1000_CTRL_SPD_1000 | E1000_CTRL_SLU;
|
|
BX_E1000_THIS s.mac_reg[STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
|
|
E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK |
|
|
E1000_STATUS_SPEED_1000 | E1000_STATUS_FD |
|
|
E1000_STATUS_LU;
|
|
BX_E1000_THIS s.mac_reg[MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
|
|
E1000_MANC_ARP_EN | E1000_MANC_0298_EN |
|
|
E1000_MANC_RMCP_EN;
|
|
|
|
BX_E1000_THIS s.rxbuf_min_shift = 1;
|
|
saved_ptr = BX_E1000_THIS s.tx.vlan;
|
|
memset(&BX_E1000_THIS s.tx, 0, sizeof(BX_E1000_THIS s.tx));
|
|
BX_E1000_THIS s.tx.vlan = saved_ptr;
|
|
BX_E1000_THIS s.tx.data = BX_E1000_THIS s.tx.vlan + 4;
|
|
|
|
// Deassert IRQ
|
|
set_irq_level(0);
|
|
}
|
|
|
|
void bx_e1000_c::register_state(void)
|
|
{
|
|
unsigned i;
|
|
char pname[5];
|
|
|
|
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "e1000", "E1000 State");
|
|
new bx_shadow_data_c(list, "mac_reg", (Bit8u*)BX_E1000_THIS s.mac_reg, 0x20000);
|
|
bx_list_c *phy = new bx_list_c(list, "phy_reg", "");
|
|
for (i = 0; i < 32; i++) {
|
|
sprintf(pname, "0x%02x", i);
|
|
new bx_shadow_num_c(phy, pname, &BX_E1000_THIS s.phy_reg[i], BASE_HEX);
|
|
}
|
|
bx_list_c *eeprom = new bx_list_c(list, "eeprom_data", "");
|
|
for (i = 0; i < 64; i++) {
|
|
sprintf(pname, "0x%02x", i);
|
|
new bx_shadow_num_c(eeprom, pname, &BX_E1000_THIS s.eeprom_data[i], BASE_HEX);
|
|
}
|
|
BXRS_DEC_PARAM_FIELD(list, rxbuf_size, BX_E1000_THIS s.rxbuf_size);
|
|
BXRS_DEC_PARAM_FIELD(list, rxbuf_min_shift, BX_E1000_THIS s.rxbuf_min_shift);
|
|
BXRS_PARAM_BOOL(list, check_rxov, BX_E1000_THIS s.check_rxov);
|
|
bx_list_c *tx = new bx_list_c(list, "tx", "");
|
|
bx_list_c *header = new bx_list_c(tx, "header", "");
|
|
for (i = 0; i < 256; i++) {
|
|
sprintf(pname, "0x%02x", i);
|
|
new bx_shadow_num_c(header, pname, &BX_E1000_THIS s.tx.header[i], BASE_HEX);
|
|
}
|
|
bx_list_c *vlh = new bx_list_c(tx, "vlan_header", "");
|
|
for (i = 0; i < 4; i++) {
|
|
sprintf(pname, "0x%02x", i);
|
|
new bx_shadow_num_c(vlh, pname, &BX_E1000_THIS s.tx.vlan_header[i], BASE_HEX);
|
|
}
|
|
new bx_shadow_data_c(list, "tx_vlan_data", BX_E1000_THIS s.tx.vlan, 0x10004);
|
|
BXRS_DEC_PARAM_FIELD(tx, size, BX_E1000_THIS s.tx.size);
|
|
BXRS_DEC_PARAM_FIELD(tx, sum_needed, BX_E1000_THIS s.tx.sum_needed);
|
|
BXRS_PARAM_BOOL(tx, vlan_needed, BX_E1000_THIS s.tx.vlan_needed);
|
|
BXRS_DEC_PARAM_FIELD(tx, ipcss, BX_E1000_THIS s.tx.ipcss);
|
|
BXRS_DEC_PARAM_FIELD(tx, ipcso, BX_E1000_THIS s.tx.ipcso);
|
|
BXRS_DEC_PARAM_FIELD(tx, ipcse, BX_E1000_THIS s.tx.ipcse);
|
|
BXRS_DEC_PARAM_FIELD(tx, tucss, BX_E1000_THIS s.tx.tucss);
|
|
BXRS_DEC_PARAM_FIELD(tx, tucso, BX_E1000_THIS s.tx.tucso);
|
|
BXRS_DEC_PARAM_FIELD(tx, tucse, BX_E1000_THIS s.tx.tucse);
|
|
BXRS_DEC_PARAM_FIELD(tx, hdr_len, BX_E1000_THIS s.tx.hdr_len);
|
|
BXRS_DEC_PARAM_FIELD(tx, mss, BX_E1000_THIS s.tx.mss);
|
|
BXRS_DEC_PARAM_FIELD(tx, paylen, BX_E1000_THIS s.tx.paylen);
|
|
BXRS_DEC_PARAM_FIELD(tx, tso_frames, BX_E1000_THIS s.tx.tso_frames);
|
|
BXRS_PARAM_BOOL(tx, tse, BX_E1000_THIS s.tx.tse);
|
|
BXRS_PARAM_BOOL(tx, ip, BX_E1000_THIS s.tx.ip);
|
|
BXRS_PARAM_BOOL(tx, tcp, BX_E1000_THIS s.tx.tcp);
|
|
BXRS_PARAM_BOOL(tx, cptse, BX_E1000_THIS s.tx.cptse);
|
|
BXRS_HEX_PARAM_FIELD(tx, int_cause, BX_E1000_THIS s.tx.int_cause);
|
|
bx_list_c *eecds = new bx_list_c(list, "eecd_state", "");
|
|
BXRS_DEC_PARAM_FIELD(eecds, val_in, BX_E1000_THIS s.eecd_state.val_in);
|
|
BXRS_DEC_PARAM_FIELD(eecds, bitnum_in, BX_E1000_THIS s.eecd_state.bitnum_in);
|
|
BXRS_DEC_PARAM_FIELD(eecds, bitnum_out, BX_E1000_THIS s.eecd_state.bitnum_out);
|
|
BXRS_PARAM_BOOL(eecds, reading, BX_E1000_THIS s.eecd_state.reading);
|
|
BXRS_HEX_PARAM_FIELD(eecds, old_eecd, BX_E1000_THIS s.eecd_state.old_eecd);
|
|
|
|
register_pci_state(list);
|
|
}
|
|
|
|
void bx_e1000_c::after_restore_state(void)
|
|
{
|
|
if (DEV_pci_set_base_mem(BX_E1000_THIS_PTR, mem_read_handler, mem_write_handler,
|
|
&BX_E1000_THIS pci_base_address[0],
|
|
&BX_E1000_THIS pci_conf[0x10],
|
|
0x20000)) {
|
|
BX_INFO(("new mem base address: 0x%08x", BX_E1000_THIS pci_base_address[0]));
|
|
}
|
|
if (DEV_pci_set_base_io(BX_E1000_THIS_PTR, read_handler, write_handler,
|
|
&BX_E1000_THIS pci_base_address[1],
|
|
&BX_E1000_THIS pci_conf[0x14],
|
|
64, &e1000_iomask[0], "e1000")) {
|
|
BX_INFO(("new i/o base address: 0x%04x", BX_E1000_THIS pci_base_address[1]));
|
|
}
|
|
if (BX_E1000_THIS pci_rom_size > 0) {
|
|
if (DEV_pci_set_base_mem(BX_E1000_THIS_PTR, mem_read_handler,
|
|
mem_write_handler,
|
|
&BX_E1000_THIS pci_rom_address,
|
|
&BX_E1000_THIS pci_conf[0x30],
|
|
BX_E1000_THIS pci_rom_size)) {
|
|
BX_INFO(("new ROM address: 0x%08x", BX_E1000_THIS pci_rom_address));
|
|
}
|
|
}
|
|
}
|
|
|
|
bx_bool bx_e1000_c::mem_read_handler(bx_phy_address addr, unsigned len,
|
|
void *data, void *param)
|
|
{
|
|
Bit32u *data_ptr = (Bit32u*) data;
|
|
Bit8u *data8_ptr;
|
|
Bit32u offset, value = 0;
|
|
Bit16u index;
|
|
|
|
if (BX_E1000_THIS pci_rom_size > 0) {
|
|
Bit32u mask = (BX_E1000_THIS pci_rom_size - 1);
|
|
if ((addr & ~mask) == BX_E1000_THIS pci_rom_address) {
|
|
#ifdef BX_LITTLE_ENDIAN
|
|
data8_ptr = (Bit8u *) data;
|
|
#else // BX_BIG_ENDIAN
|
|
data8_ptr = (Bit8u *) data + (len - 1);
|
|
#endif
|
|
for (unsigned i = 0; i < len; i++) {
|
|
if (BX_E1000_THIS pci_conf[0x30] & 0x01) {
|
|
*data8_ptr = BX_E1000_THIS pci_rom[addr & mask];
|
|
} else {
|
|
*data8_ptr = 0xff;
|
|
}
|
|
addr++;
|
|
#ifdef BX_LITTLE_ENDIAN
|
|
data8_ptr++;
|
|
#else // BX_BIG_ENDIAN
|
|
data8_ptr--;
|
|
#endif
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
offset = addr & 0x1ffff;
|
|
index = (offset >> 2);
|
|
if (len == 4) {
|
|
BX_DEBUG(("mem read from offset 0x%08x -", offset));
|
|
switch (offset) {
|
|
case E1000_PBA:
|
|
case E1000_RCTL:
|
|
case E1000_TDH:
|
|
case E1000_TXDCTL:
|
|
case E1000_WUFC:
|
|
case E1000_TDT:
|
|
case E1000_CTRL:
|
|
case E1000_LEDCTL:
|
|
case E1000_MANC:
|
|
case E1000_MDIC:
|
|
case E1000_SWSM:
|
|
case E1000_STATUS:
|
|
case E1000_TORL:
|
|
case E1000_TOTL:
|
|
case E1000_IMS:
|
|
case E1000_TCTL:
|
|
case E1000_RDH:
|
|
case E1000_RDT:
|
|
case E1000_VET:
|
|
case E1000_ICS:
|
|
case E1000_TDBAL:
|
|
case E1000_TDBAH:
|
|
case E1000_RDBAH:
|
|
case E1000_RDBAL:
|
|
case E1000_TDLEN:
|
|
case E1000_RDLEN:
|
|
value = BX_E1000_THIS s.mac_reg[index];
|
|
break;
|
|
case E1000_TOTH:
|
|
case E1000_TORH:
|
|
value = BX_E1000_THIS s.mac_reg[index];
|
|
BX_E1000_THIS s.mac_reg[index] = 0;
|
|
BX_E1000_THIS s.mac_reg[index - 1] = 0;
|
|
break;
|
|
case E1000_GPRC:
|
|
case E1000_GPTC:
|
|
case E1000_TPR:
|
|
case E1000_TPT:
|
|
value = BX_E1000_THIS s.mac_reg[index];
|
|
BX_E1000_THIS s.mac_reg[index] = 0;
|
|
break;
|
|
case E1000_ICR:
|
|
value = BX_E1000_THIS s.mac_reg[index];
|
|
BX_DEBUG(("ICR read: %x", value));
|
|
set_interrupt_cause(0);
|
|
break;
|
|
case E1000_EECD:
|
|
value = get_eecd();
|
|
break;
|
|
case E1000_EERD:
|
|
value = flash_eerd_read();
|
|
break;
|
|
default:
|
|
if (((offset >= E1000_CRCERRS) && (offset <= E1000_MPC)) ||
|
|
((offset >= E1000_RA) && (offset <= (E1000_RA + 31))) ||
|
|
((offset >= E1000_MTA) && (offset <= (E1000_MTA + 127))) ||
|
|
((offset >= E1000_VFTA) && (offset <= (E1000_VFTA + 127)))) {
|
|
value = BX_E1000_THIS s.mac_reg[index];
|
|
} else {
|
|
BX_DEBUG(("mem read from offset 0x%08x returns 0", offset));
|
|
}
|
|
}
|
|
} else if ((len == 1) && (offset == E1000_STATUS)) {
|
|
BX_DEBUG(("mem read from offset 0x%08x with len 1 -", offset));
|
|
value = BX_E1000_THIS s.mac_reg[index] & 0xff;
|
|
} else {
|
|
BX_DEBUG(("mem read from offset 0x%08x with len %d not implemented", offset, len));
|
|
}
|
|
BX_DEBUG(("val = 0x%08x", value));
|
|
*data_ptr = value;
|
|
return 1;
|
|
}
|
|
|
|
bx_bool bx_e1000_c::mem_write_handler(bx_phy_address addr, unsigned len,
|
|
void *data, void *param)
|
|
{
|
|
Bit32u value = *(Bit32u*) data;
|
|
Bit32u offset;
|
|
Bit16u index;
|
|
|
|
if (BX_E1000_THIS pci_rom_size > 0) {
|
|
Bit32u mask = (BX_E1000_THIS pci_rom_size - 1);
|
|
if ((addr & ~mask) == BX_E1000_THIS pci_rom_address) {
|
|
BX_INFO(("write to ROM ignored (addr=0x%08x len=%d)", (Bit32u)addr, len));
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
offset = addr & 0x1ffff;
|
|
index = (offset >> 2);
|
|
if (len == 4) {
|
|
BX_DEBUG(("mem write to offset 0x%08x - value = 0x%08x", offset, value));
|
|
switch (offset) {
|
|
case E1000_PBA:
|
|
case E1000_EERD:
|
|
case E1000_SWSM:
|
|
case E1000_WUFC:
|
|
case E1000_TDBAL:
|
|
case E1000_TDBAH:
|
|
case E1000_TXDCTL:
|
|
case E1000_RDBAH:
|
|
case E1000_RDBAL:
|
|
case E1000_LEDCTL:
|
|
case E1000_VET:
|
|
BX_E1000_THIS s.mac_reg[index] = value;
|
|
break;
|
|
case E1000_TDLEN:
|
|
case E1000_RDLEN:
|
|
BX_E1000_THIS s.mac_reg[index] = value & 0xfff80;
|
|
break;
|
|
case E1000_TCTL:
|
|
case E1000_TDT:
|
|
BX_E1000_THIS s.mac_reg[index] = value;
|
|
BX_E1000_THIS s.mac_reg[TDT] &= 0xffff;
|
|
start_xmit();
|
|
break;
|
|
case E1000_MDIC:
|
|
set_mdic(value);
|
|
break;
|
|
case E1000_ICS:
|
|
set_ics(value);
|
|
break;
|
|
case E1000_TDH:
|
|
case E1000_RDH:
|
|
BX_E1000_THIS s.mac_reg[index] = value & 0xffff;
|
|
break;
|
|
case E1000_RDT:
|
|
BX_E1000_THIS s.check_rxov = 0;
|
|
BX_E1000_THIS s.mac_reg[index] = value & 0xffff;
|
|
break;
|
|
case E1000_IMC:
|
|
BX_E1000_THIS s.mac_reg[IMS] &= ~value;
|
|
set_ics(0);
|
|
break;
|
|
case E1000_IMS:
|
|
BX_E1000_THIS s.mac_reg[IMS] |= value;
|
|
set_ics(0);
|
|
break;
|
|
case E1000_ICR:
|
|
BX_DEBUG(("set_icr %x", value));
|
|
set_interrupt_cause(BX_E1000_THIS s.mac_reg[ICR] & ~value);
|
|
break;
|
|
case E1000_EECD:
|
|
set_eecd(value);
|
|
break;
|
|
case E1000_RCTL:
|
|
set_rx_control(value);
|
|
break;
|
|
case E1000_CTRL:
|
|
// RST is self clearing
|
|
BX_E1000_THIS s.mac_reg[CTRL] = value & ~E1000_CTRL_RST;
|
|
break;
|
|
default:
|
|
if (((offset >= E1000_RA) && (offset <= (E1000_RA + 31))) ||
|
|
((offset >= E1000_MTA) && (offset <= (E1000_MTA + 127))) ||
|
|
((offset >= E1000_VFTA) && (offset <= (E1000_VFTA + 127)))) {
|
|
BX_E1000_THIS s.mac_reg[index] = value;
|
|
} else {
|
|
BX_DEBUG(("mem write to offset 0x%08x ignored - value = 0x%08x", offset, value));
|
|
}
|
|
}
|
|
} else {
|
|
BX_DEBUG(("mem write to offset 0x%08x with len %d not implemented", offset, len));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// static IO port read callback handler
|
|
// redirects to non-static class handler to avoid virtual functions
|
|
|
|
Bit32u bx_e1000_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
|
|
{
|
|
#if !BX_USE_E1000_SMF
|
|
bx_e1000_c *class_ptr = (bx_e1000_c *) this_ptr;
|
|
return class_ptr->read(address, io_len);
|
|
}
|
|
|
|
Bit32u bx_e1000_c::read(Bit32u address, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_E1000_SMF
|
|
Bit8u offset;
|
|
|
|
offset = address - BX_E1000_THIS pci_base_address[1];
|
|
|
|
BX_ERROR(("register read from offset 0x%02x returns 0", offset));
|
|
|
|
return 0;
|
|
}
|
|
|
|
// static IO port write callback handler
|
|
// redirects to non-static class handler to avoid virtual functions
|
|
|
|
void bx_e1000_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#if !BX_USE_E1000_SMF
|
|
bx_e1000_c *class_ptr = (bx_e1000_c *) this_ptr;
|
|
|
|
class_ptr->write(address, value, io_len);
|
|
}
|
|
|
|
void bx_e1000_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_E1000_SMF
|
|
Bit8u offset;
|
|
|
|
offset = address - BX_E1000_THIS pci_base_address[1];
|
|
|
|
BX_ERROR(("register write to offset 0x%02x ignored - value = 0x%08x", offset, value));
|
|
}
|
|
|
|
void bx_e1000_c::set_irq_level(bx_bool level)
|
|
{
|
|
DEV_pci_set_irq(BX_E1000_THIS s.devfunc, BX_E1000_THIS pci_conf[0x3d], level);
|
|
}
|
|
|
|
void bx_e1000_c::set_interrupt_cause(Bit32u value)
|
|
{
|
|
if (value != 0)
|
|
value |= E1000_ICR_INT_ASSERTED;
|
|
BX_E1000_THIS s.mac_reg[ICR] = value;
|
|
BX_E1000_THIS s.mac_reg[ICS] = value;
|
|
set_irq_level((BX_E1000_THIS s.mac_reg[IMS] & BX_E1000_THIS s.mac_reg[ICR]) != 0);
|
|
}
|
|
|
|
void bx_e1000_c::set_ics(Bit32u value)
|
|
{
|
|
BX_DEBUG(("set_ics %x, ICR %x, IMR %x", value, BX_E1000_THIS s.mac_reg[ICR],
|
|
BX_E1000_THIS s.mac_reg[IMS]));
|
|
set_interrupt_cause(value | BX_E1000_THIS s.mac_reg[ICR]);
|
|
}
|
|
|
|
int bx_e1000_c::rxbufsize(Bit32u v)
|
|
{
|
|
v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 |
|
|
E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 |
|
|
E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256;
|
|
switch (v) {
|
|
case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384:
|
|
return 16384;
|
|
case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192:
|
|
return 8192;
|
|
case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096:
|
|
return 4096;
|
|
case E1000_RCTL_SZ_1024:
|
|
return 1024;
|
|
case E1000_RCTL_SZ_512:
|
|
return 512;
|
|
case E1000_RCTL_SZ_256:
|
|
return 256;
|
|
}
|
|
return 2048;
|
|
}
|
|
|
|
void bx_e1000_c::set_rx_control(Bit32u value)
|
|
{
|
|
BX_E1000_THIS s.mac_reg[RCTL] = value;
|
|
BX_E1000_THIS s.rxbuf_size = rxbufsize(value);
|
|
BX_E1000_THIS s.rxbuf_min_shift = ((value / E1000_RCTL_RDMTS_QUAT) & 3) + 1;
|
|
BX_DEBUG(("RCTL: %d, mac_reg[RCTL] = 0x%x", BX_E1000_THIS s.mac_reg[RDT],
|
|
BX_E1000_THIS s.mac_reg[RCTL]));
|
|
}
|
|
|
|
void bx_e1000_c::set_mdic(Bit32u value)
|
|
{
|
|
Bit32u data = value & E1000_MDIC_DATA_MASK;
|
|
Bit32u addr = ((value & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
|
|
|
|
if ((value & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) { // phy #
|
|
value = BX_E1000_THIS s.mac_reg[MDIC] | E1000_MDIC_ERROR;
|
|
} else if (value & E1000_MDIC_OP_READ) {
|
|
BX_DEBUG(("MDIC read reg 0x%x", addr));
|
|
if (!(phy_regcap[addr] & PHY_R)) {
|
|
BX_DEBUG(("MDIC read reg %x unhandled", addr));
|
|
value |= E1000_MDIC_ERROR;
|
|
} else {
|
|
value = (value ^ data) | BX_E1000_THIS s.phy_reg[addr];
|
|
}
|
|
} else if (value & E1000_MDIC_OP_WRITE) {
|
|
BX_DEBUG(("MDIC write reg 0x%x, value 0x%x", addr, data));
|
|
if (!(phy_regcap[addr] & PHY_W)) {
|
|
BX_DEBUG(("MDIC write reg %x unhandled", addr));
|
|
value |= E1000_MDIC_ERROR;
|
|
} else {
|
|
BX_E1000_THIS s.phy_reg[addr] = data;
|
|
}
|
|
}
|
|
BX_E1000_THIS s.mac_reg[MDIC] = value | E1000_MDIC_READY;
|
|
set_ics(E1000_ICR_MDAC);
|
|
}
|
|
|
|
Bit32u bx_e1000_c::get_eecd()
|
|
{
|
|
BX_DEBUG(("reading eeprom bit %d (reading %d)",
|
|
BX_E1000_THIS s.eecd_state.bitnum_out, BX_E1000_THIS s.eecd_state.reading));
|
|
Bit32u ret = E1000_EECD_PRES|E1000_EECD_GNT | BX_E1000_THIS s.eecd_state.old_eecd;
|
|
if (!BX_E1000_THIS s.eecd_state.reading ||
|
|
((BX_E1000_THIS s.eeprom_data[(BX_E1000_THIS s.eecd_state.bitnum_out >> 4) & 0x3f] >>
|
|
((BX_E1000_THIS s.eecd_state.bitnum_out & 0xf) ^ 0xf))) & 1) {
|
|
ret |= E1000_EECD_DO;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void bx_e1000_c::set_eecd(Bit32u value)
|
|
{
|
|
Bit32u oldval = BX_E1000_THIS s.eecd_state.old_eecd;
|
|
|
|
BX_E1000_THIS s.eecd_state.old_eecd = value & (E1000_EECD_SK | E1000_EECD_CS |
|
|
E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
|
|
if (!(E1000_EECD_CS & value)) // CS inactive; nothing to do
|
|
return;
|
|
if (E1000_EECD_CS & (value ^ oldval)) { // CS rise edge; reset state
|
|
BX_E1000_THIS s.eecd_state.val_in = 0;
|
|
BX_E1000_THIS s.eecd_state.bitnum_in = 0;
|
|
BX_E1000_THIS s.eecd_state.bitnum_out = 0;
|
|
BX_E1000_THIS s.eecd_state.reading = 0;
|
|
}
|
|
if (!(E1000_EECD_SK & (value ^ oldval))) // no clock edge
|
|
return;
|
|
if (!(E1000_EECD_SK & value)) { // falling edge
|
|
BX_E1000_THIS s.eecd_state.bitnum_out++;
|
|
return;
|
|
}
|
|
BX_E1000_THIS s.eecd_state.val_in <<= 1;
|
|
if (value & E1000_EECD_DI)
|
|
BX_E1000_THIS s.eecd_state.val_in |= 1;
|
|
if (++BX_E1000_THIS s.eecd_state.bitnum_in == 9 && !BX_E1000_THIS s.eecd_state.reading) {
|
|
BX_E1000_THIS s.eecd_state.bitnum_out = ((BX_E1000_THIS s.eecd_state.val_in & 0x3f)<<4)-1;
|
|
BX_E1000_THIS s.eecd_state.reading = (((BX_E1000_THIS s.eecd_state.val_in >> 6) & 7) ==
|
|
EEPROM_READ_OPCODE_MICROWIRE);
|
|
}
|
|
BX_DEBUG(("eeprom bitnum in %d out %d, reading %d",
|
|
BX_E1000_THIS s.eecd_state.bitnum_in, BX_E1000_THIS s.eecd_state.bitnum_out,
|
|
BX_E1000_THIS s.eecd_state.reading));
|
|
}
|
|
|
|
Bit32u bx_e1000_c::flash_eerd_read()
|
|
{
|
|
unsigned int index, r = BX_E1000_THIS s.mac_reg[EERD] & ~E1000_EEPROM_RW_REG_START;
|
|
|
|
if ((BX_E1000_THIS s.mac_reg[EERD] & E1000_EEPROM_RW_REG_START) == 0)
|
|
return (BX_E1000_THIS s.mac_reg[EERD]);
|
|
|
|
if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG)
|
|
return (E1000_EEPROM_RW_REG_DONE | r);
|
|
|
|
return ((BX_E1000_THIS s.eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) |
|
|
E1000_EEPROM_RW_REG_DONE | r);
|
|
}
|
|
|
|
void bx_e1000_c::putsum(Bit8u *data, Bit32u n, Bit32u sloc, Bit32u css, Bit32u cse)
|
|
{
|
|
Bit32u sum;
|
|
|
|
if (cse && cse < n)
|
|
n = cse + 1;
|
|
if (sloc < n-1) {
|
|
sum = net_checksum_add(data+css, n-css);
|
|
put_net2(data + sloc, net_checksum_finish(sum));
|
|
}
|
|
}
|
|
|
|
bx_bool bx_e1000_c::vlan_enabled()
|
|
{
|
|
return ((BX_E1000_THIS s.mac_reg[CTRL] & E1000_CTRL_VME) != 0);
|
|
}
|
|
|
|
bx_bool bx_e1000_c::vlan_rx_filter_enabled()
|
|
{
|
|
return ((BX_E1000_THIS s.mac_reg[RCTL] & E1000_RCTL_VFE) != 0);
|
|
}
|
|
|
|
bx_bool bx_e1000_c::is_vlan_packet(const Bit8u *buf)
|
|
{
|
|
return (get_net2(buf + 12) == (Bit16u)BX_E1000_THIS s.mac_reg[VET]);
|
|
}
|
|
|
|
bx_bool bx_e1000_c::is_vlan_txd(Bit32u txd_lower)
|
|
{
|
|
return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
|
|
}
|
|
|
|
int bx_e1000_c::fcs_len()
|
|
{
|
|
return (BX_E1000_THIS s.mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
|
|
}
|
|
|
|
void bx_e1000_c::xmit_seg()
|
|
{
|
|
Bit16u len;
|
|
Bit8u *sp;
|
|
unsigned int frames = BX_E1000_THIS s.tx.tso_frames, css, sofar, n;
|
|
e1000_tx *tp = &BX_E1000_THIS s.tx;
|
|
|
|
if (tp->tse && tp->cptse) {
|
|
css = tp->ipcss;
|
|
BX_DEBUG(("frames %d size %d ipcss %d", frames, tp->size, css));
|
|
if (tp->ip) { // IPv4
|
|
put_net2(tp->data+css+2, tp->size - css);
|
|
put_net2(tp->data+css+4, get_net2(tp->data+css+4+frames));
|
|
} else // IPv6
|
|
put_net2(tp->data+css+4, tp->size - css);
|
|
css = tp->tucss;
|
|
len = tp->size - css;
|
|
BX_DEBUG(("tcp %d tucss %d len %d", tp->tcp, css, len));
|
|
if (tp->tcp) {
|
|
sofar = frames * tp->mss;
|
|
put_net4(tp->data+css+4, // seq
|
|
get_net4(tp->data+css+4+sofar));
|
|
if (tp->paylen - sofar > tp->mss)
|
|
tp->data[css + 13] &= ~9; // PSH, FIN
|
|
} else // UDP
|
|
put_net2(tp->data+css+4, len);
|
|
if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
|
|
unsigned int phsum;
|
|
// add pseudo-header length before checksum calculation
|
|
sp = tp->data + tp->tucso;
|
|
phsum = get_net2(sp) + len;
|
|
phsum = (phsum >> 16) + (phsum & 0xffff);
|
|
put_net2(sp, phsum);
|
|
}
|
|
tp->tso_frames++;
|
|
}
|
|
|
|
if (tp->sum_needed & E1000_TXD_POPTS_TXSM)
|
|
putsum(tp->data, tp->size, tp->tucso, tp->tucss, tp->tucse);
|
|
if (tp->sum_needed & E1000_TXD_POPTS_IXSM)
|
|
putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse);
|
|
if (tp->vlan_needed) {
|
|
memmove(tp->vlan, tp->data, 4);
|
|
memmove(tp->data, tp->data + 4, 8);
|
|
memcpy(tp->data + 8, tp->vlan_header, 4);
|
|
BX_E1000_THIS ethdev->sendpkt(tp->vlan, tp->size + 4);
|
|
} else
|
|
BX_E1000_THIS ethdev->sendpkt(tp->data, tp->size);
|
|
BX_E1000_THIS s.mac_reg[TPT]++;
|
|
BX_E1000_THIS s.mac_reg[GPTC]++;
|
|
n = BX_E1000_THIS s.mac_reg[TOTL];
|
|
if ((BX_E1000_THIS s.mac_reg[TOTL] += BX_E1000_THIS s.tx.size) < n)
|
|
BX_E1000_THIS s.mac_reg[TOTH]++;
|
|
}
|
|
|
|
void bx_e1000_c::process_tx_desc(struct e1000_tx_desc *dp)
|
|
{
|
|
Bit32u txd_lower = le32_to_cpu(dp->lower.data);
|
|
Bit32u dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D);
|
|
unsigned int split_size = txd_lower & 0xffff, bytes, sz, op;
|
|
unsigned int msh = 0xfffff, hdr = 0;
|
|
Bit64u addr;
|
|
struct e1000_context_desc *xp = (struct e1000_context_desc *)dp;
|
|
e1000_tx *tp = &BX_E1000_THIS s.tx;
|
|
|
|
if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor
|
|
op = le32_to_cpu(xp->cmd_and_length);
|
|
tp->ipcss = xp->lower_setup.ip_fields.ipcss;
|
|
tp->ipcso = xp->lower_setup.ip_fields.ipcso;
|
|
tp->ipcse = le16_to_cpu(xp->lower_setup.ip_fields.ipcse);
|
|
tp->tucss = xp->upper_setup.tcp_fields.tucss;
|
|
tp->tucso = xp->upper_setup.tcp_fields.tucso;
|
|
tp->tucse = le16_to_cpu(xp->upper_setup.tcp_fields.tucse);
|
|
tp->paylen = op & 0xfffff;
|
|
tp->hdr_len = xp->tcp_seg_setup.fields.hdr_len;
|
|
tp->mss = le16_to_cpu(xp->tcp_seg_setup.fields.mss);
|
|
tp->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0;
|
|
tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0;
|
|
tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0;
|
|
tp->tso_frames = 0;
|
|
if (tp->tucso == 0) { // this is probably wrong
|
|
BX_DEBUG(("TCP/UDP: cso 0!"));
|
|
tp->tucso = tp->tucss + (tp->tcp ? 16 : 6);
|
|
}
|
|
return;
|
|
} else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
|
|
// data descriptor
|
|
if (tp->size == 0) {
|
|
tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
|
|
}
|
|
tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0;
|
|
} else {
|
|
// legacy descriptor
|
|
tp->cptse = 0;
|
|
}
|
|
|
|
if (vlan_enabled() && is_vlan_txd(txd_lower) &&
|
|
(tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) {
|
|
tp->vlan_needed = 1;
|
|
put_net2(tp->vlan_header, (Bit16u)BX_E1000_THIS s.mac_reg[VET]);
|
|
put_net2(tp->vlan_header + 2, le16_to_cpu(dp->upper.fields.special));
|
|
}
|
|
|
|
addr = le64_to_cpu(dp->buffer_addr);
|
|
if (tp->tse && tp->cptse) {
|
|
hdr = tp->hdr_len;
|
|
msh = hdr + tp->mss;
|
|
do {
|
|
bytes = split_size;
|
|
if (tp->size + bytes > msh)
|
|
bytes = msh - tp->size;
|
|
DEV_MEM_READ_PHYSICAL_DMA(addr, bytes, tp->data + tp->size);
|
|
if ((sz = tp->size + bytes) >= hdr && tp->size < hdr)
|
|
memmove(tp->header, tp->data, hdr);
|
|
tp->size = sz;
|
|
addr += bytes;
|
|
if (sz == msh) {
|
|
xmit_seg();
|
|
memmove(tp->data, tp->header, hdr);
|
|
tp->size = hdr;
|
|
}
|
|
} while (split_size -= bytes);
|
|
} else if (!tp->tse && tp->cptse) {
|
|
// context descriptor TSE is not set, while data descriptor TSE is set
|
|
BX_DEBUG(("TCP segmentaion Error"));
|
|
} else {
|
|
DEV_MEM_READ_PHYSICAL_DMA(addr, split_size, tp->data + tp->size);
|
|
tp->size += split_size;
|
|
}
|
|
|
|
if (!(txd_lower & E1000_TXD_CMD_EOP))
|
|
return;
|
|
if (!(tp->tse && tp->cptse && tp->size < hdr))
|
|
xmit_seg();
|
|
tp->tso_frames = 0;
|
|
tp->sum_needed = 0;
|
|
tp->vlan_needed = 0;
|
|
tp->size = 0;
|
|
tp->cptse = 0;
|
|
}
|
|
|
|
Bit32u bx_e1000_c::txdesc_writeback(bx_phy_address base, struct e1000_tx_desc *dp)
|
|
{
|
|
Bit32u txd_upper, txd_lower = le32_to_cpu(dp->lower.data);
|
|
|
|
if (!(txd_lower & (E1000_TXD_CMD_RS|E1000_TXD_CMD_RPS)))
|
|
return 0;
|
|
txd_upper = (le32_to_cpu(dp->upper.data) | E1000_TXD_STAT_DD) &
|
|
~(E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU);
|
|
dp->upper.data = cpu_to_le32(txd_upper);
|
|
DEV_MEM_WRITE_PHYSICAL_DMA(base + ((char *)&dp->upper - (char *)dp),
|
|
sizeof(dp->upper), (Bit8u *)&dp->upper);
|
|
return E1000_ICR_TXDW;
|
|
}
|
|
|
|
Bit64u bx_e1000_c::tx_desc_base()
|
|
{
|
|
Bit64u bah = BX_E1000_THIS s.mac_reg[TDBAH];
|
|
Bit64u bal = BX_E1000_THIS s.mac_reg[TDBAL] & ~0xf;
|
|
|
|
return (bah << 32) + bal;
|
|
}
|
|
|
|
void bx_e1000_c::start_xmit()
|
|
{
|
|
bx_phy_address base;
|
|
struct e1000_tx_desc desc;
|
|
Bit32u tdh_start = BX_E1000_THIS s.mac_reg[TDH], cause = E1000_ICS_TXQE;
|
|
|
|
if (!(BX_E1000_THIS s.mac_reg[TCTL] & E1000_TCTL_EN)) {
|
|
BX_DEBUG(("tx disabled"));
|
|
return;
|
|
}
|
|
|
|
while (BX_E1000_THIS s.mac_reg[TDH] != BX_E1000_THIS s.mac_reg[TDT]) {
|
|
base = tx_desc_base() +
|
|
sizeof(struct e1000_tx_desc) * BX_E1000_THIS s.mac_reg[TDH];
|
|
DEV_MEM_READ_PHYSICAL_DMA(base, sizeof(struct e1000_tx_desc), (Bit8u *)&desc);
|
|
BX_DEBUG(("index %d: %p : %x %x", BX_E1000_THIS s.mac_reg[TDH],
|
|
(void *)desc.buffer_addr, desc.lower.data,
|
|
desc.upper.data));
|
|
|
|
process_tx_desc(&desc);
|
|
cause |= txdesc_writeback(base, &desc);
|
|
|
|
if (++BX_E1000_THIS s.mac_reg[TDH] * sizeof(desc) >= BX_E1000_THIS s.mac_reg[TDLEN])
|
|
BX_E1000_THIS s.mac_reg[TDH] = 0;
|
|
/*
|
|
* the following could happen only if guest sw assigns
|
|
* bogus values to TDT/TDLEN.
|
|
* there's nothing too intelligent we could do about this.
|
|
*/
|
|
if (BX_E1000_THIS s.mac_reg[TDH] == tdh_start) {
|
|
BX_ERROR(("TDH wraparound @%x, TDT %x, TDLEN %x", tdh_start,
|
|
BX_E1000_THIS s.mac_reg[TDT], BX_E1000_THIS s.mac_reg[TDLEN]));
|
|
break;
|
|
}
|
|
}
|
|
BX_E1000_THIS s.tx.int_cause = cause;
|
|
bx_pc_system.activate_timer(BX_E1000_THIS s.tx_timer_index, 10, 0); // not continuous
|
|
bx_gui->statusbar_setitem(BX_E1000_THIS s.statusbar_id, 1, 1);
|
|
}
|
|
|
|
void bx_e1000_c::tx_timer_handler(void *this_ptr)
|
|
{
|
|
bx_e1000_c *class_ptr = (bx_e1000_c *) this_ptr;
|
|
class_ptr->tx_timer();
|
|
}
|
|
|
|
void bx_e1000_c::tx_timer(void)
|
|
{
|
|
set_ics(BX_E1000_THIS s.tx.int_cause);
|
|
}
|
|
|
|
int bx_e1000_c::receive_filter(const Bit8u *buf, int size)
|
|
{
|
|
static const Bit8u bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
static const int mta_shift[] = {4, 3, 2, 0};
|
|
Bit32u f, rctl = BX_E1000_THIS s.mac_reg[RCTL], ra[2], *rp;
|
|
|
|
if (is_vlan_packet(buf) && vlan_rx_filter_enabled()) {
|
|
Bit16u vid = get_net2(buf + 14);
|
|
Bit32u vfta = BX_E1000_THIS s.mac_reg[VFTA + ((vid >> 5) & 0x7f)];
|
|
if ((vfta & (1 << (vid & 0x1f))) == 0)
|
|
return 0;
|
|
}
|
|
|
|
if (rctl & E1000_RCTL_UPE) // promiscuous
|
|
return 1;
|
|
|
|
if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE)) // promiscuous mcast
|
|
return 1;
|
|
|
|
if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast))
|
|
return 1;
|
|
|
|
for (rp = BX_E1000_THIS s.mac_reg + RA; rp < BX_E1000_THIS s.mac_reg + RA + 32; rp += 2) {
|
|
if (!(rp[1] & E1000_RAH_AV))
|
|
continue;
|
|
ra[0] = cpu_to_le32(rp[0]);
|
|
ra[1] = cpu_to_le32(rp[1]);
|
|
if (!memcmp(buf, (Bit8u *)ra, 6)) {
|
|
BX_DEBUG(("unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x",
|
|
(int)(rp - BX_E1000_THIS s.mac_reg - RA) / 2,
|
|
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]));
|
|
return 1;
|
|
}
|
|
}
|
|
BX_DEBUG(("unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x",
|
|
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]));
|
|
|
|
f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3];
|
|
f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff;
|
|
if (BX_E1000_THIS s.mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f)))
|
|
return 1;
|
|
BX_DEBUG(("dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x",
|
|
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
|
|
(rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5,
|
|
BX_E1000_THIS s.mac_reg[MTA + (f >> 5)]));
|
|
|
|
return 0;
|
|
}
|
|
|
|
bx_bool bx_e1000_c::e1000_has_rxbufs(size_t total_size)
|
|
{
|
|
int bufs;
|
|
// Fast-path short packets
|
|
if (total_size <= BX_E1000_THIS s.rxbuf_size) {
|
|
return (BX_E1000_THIS s.mac_reg[RDH] != BX_E1000_THIS s.mac_reg[RDT]) ||
|
|
!BX_E1000_THIS s.check_rxov;
|
|
}
|
|
if (BX_E1000_THIS s.mac_reg[RDH] < BX_E1000_THIS s.mac_reg[RDT]) {
|
|
bufs = BX_E1000_THIS s.mac_reg[RDT] - BX_E1000_THIS s.mac_reg[RDH];
|
|
} else if (BX_E1000_THIS s.mac_reg[RDH] > BX_E1000_THIS s.mac_reg[RDT] ||
|
|
!BX_E1000_THIS s.check_rxov) {
|
|
bufs = BX_E1000_THIS s.mac_reg[RDLEN] / sizeof(struct e1000_rx_desc) +
|
|
BX_E1000_THIS s.mac_reg[RDT] - BX_E1000_THIS s.mac_reg[RDH];
|
|
} else {
|
|
return 0;
|
|
}
|
|
return (total_size <= (bufs * BX_E1000_THIS s.rxbuf_size));
|
|
}
|
|
|
|
Bit64u bx_e1000_c::rx_desc_base()
|
|
{
|
|
Bit64u bah = BX_E1000_THIS s.mac_reg[RDBAH];
|
|
Bit64u bal = BX_E1000_THIS s.mac_reg[RDBAL] & ~0xf;
|
|
|
|
return (bah << 32) + bal;
|
|
}
|
|
|
|
/*
|
|
* Callback from the eth system driver to check if the device can receive
|
|
*/
|
|
Bit32u bx_e1000_c::rx_status_handler(void *arg)
|
|
{
|
|
bx_e1000_c *class_ptr = (bx_e1000_c *) arg;
|
|
return class_ptr->rx_status();
|
|
}
|
|
|
|
Bit32u bx_e1000_c::rx_status()
|
|
{
|
|
Bit32u status = BX_NETDEV_1GBIT;
|
|
if ((BX_E1000_THIS s.mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(1)) {
|
|
status |= BX_NETDEV_RXREADY;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Callback from the eth system driver when a frame has arrived
|
|
*/
|
|
void bx_e1000_c::rx_handler(void *arg, const void *buf, unsigned len)
|
|
{
|
|
bx_e1000_c *class_ptr = (bx_e1000_c *) arg;
|
|
class_ptr->rx_frame(buf, len);
|
|
}
|
|
|
|
void bx_e1000_c::rx_frame(const void *buf, unsigned buf_size)
|
|
{
|
|
struct e1000_rx_desc desc;
|
|
bx_phy_address base;
|
|
unsigned int n, rdt;
|
|
Bit32u rdh_start;
|
|
Bit16u vlan_special = 0;
|
|
Bit8u vlan_status = 0, vlan_offset = 0;
|
|
Bit8u min_buf[MIN_BUF_SIZE];
|
|
size_t desc_offset;
|
|
size_t desc_size;
|
|
size_t total_size;
|
|
|
|
if (!(BX_E1000_THIS s.mac_reg[RCTL] & E1000_RCTL_EN))
|
|
return;
|
|
|
|
// Pad to minimum Ethernet frame length
|
|
if (buf_size < sizeof(min_buf)) {
|
|
memcpy(min_buf, buf, buf_size);
|
|
memset(&min_buf[buf_size], 0, sizeof(min_buf) - buf_size);
|
|
buf = min_buf;
|
|
buf_size = sizeof(min_buf);
|
|
}
|
|
|
|
if (!receive_filter((Bit8u *)buf, buf_size))
|
|
return;
|
|
|
|
if (vlan_enabled() && is_vlan_packet((Bit8u *)buf)) {
|
|
vlan_special = cpu_to_le16(get_net2(((Bit8u *)(buf) + 14)));
|
|
memmove((Bit8u *)buf + 4, buf, 12);
|
|
vlan_status = E1000_RXD_STAT_VP;
|
|
vlan_offset = 4;
|
|
buf_size -= 4;
|
|
}
|
|
|
|
rdh_start = BX_E1000_THIS s.mac_reg[RDH];
|
|
desc_offset = 0;
|
|
total_size = buf_size + fcs_len();
|
|
if (!e1000_has_rxbufs(total_size)) {
|
|
set_ics(E1000_ICS_RXO);
|
|
return;
|
|
}
|
|
do {
|
|
desc_size = total_size - desc_offset;
|
|
if (desc_size > BX_E1000_THIS s.rxbuf_size) {
|
|
desc_size = BX_E1000_THIS s.rxbuf_size;
|
|
}
|
|
base = rx_desc_base() + sizeof(desc) * BX_E1000_THIS s.mac_reg[RDH];
|
|
DEV_MEM_READ_PHYSICAL_DMA(base, sizeof(desc), (Bit8u *)&desc);
|
|
desc.special = vlan_special;
|
|
desc.status |= (vlan_status | E1000_RXD_STAT_DD);
|
|
if (desc.buffer_addr) {
|
|
if (desc_offset < buf_size) {
|
|
size_t copy_size = buf_size - desc_offset;
|
|
if (copy_size > BX_E1000_THIS s.rxbuf_size) {
|
|
copy_size = BX_E1000_THIS s.rxbuf_size;
|
|
}
|
|
DEV_MEM_WRITE_PHYSICAL_DMA(le64_to_cpu(desc.buffer_addr), copy_size,
|
|
(Bit8u *)buf + desc_offset + vlan_offset);
|
|
}
|
|
desc_offset += desc_size;
|
|
desc.length = cpu_to_le16(desc_size);
|
|
if (desc_offset >= total_size) {
|
|
desc.status |= E1000_RXD_STAT_EOP | E1000_RXD_STAT_IXSM;
|
|
} else {
|
|
/* Guest zeroing out status is not a hardware requirement.
|
|
Clear EOP in case guest didn't do it. */
|
|
desc.status &= ~E1000_RXD_STAT_EOP;
|
|
}
|
|
} else { // as per intel docs; skip descriptors with null buf addr
|
|
BX_ERROR(("Null RX descriptor!!"));
|
|
}
|
|
DEV_MEM_WRITE_PHYSICAL_DMA(base, sizeof(desc), (Bit8u *)&desc);
|
|
if (++BX_E1000_THIS s.mac_reg[RDH] * sizeof(desc) >= BX_E1000_THIS s.mac_reg[RDLEN])
|
|
BX_E1000_THIS s.mac_reg[RDH] = 0;
|
|
BX_E1000_THIS s.check_rxov = 1;
|
|
/* see comment in start_xmit; same here */
|
|
if (BX_E1000_THIS s.mac_reg[RDH] == rdh_start) {
|
|
BX_DEBUG(("RDH wraparound @%x, RDT %x, RDLEN %x",
|
|
rdh_start, BX_E1000_THIS s.mac_reg[RDT], BX_E1000_THIS s.mac_reg[RDLEN]));
|
|
set_ics(E1000_ICS_RXO);
|
|
return;
|
|
}
|
|
} while (desc_offset < total_size);
|
|
|
|
BX_E1000_THIS s.mac_reg[GPRC]++;
|
|
BX_E1000_THIS s.mac_reg[TPR]++;
|
|
/* TOR - Total Octets Received:
|
|
* This register includes bytes received in a packet from the <Destination
|
|
* Address> field through the <CRC> field, inclusively.
|
|
*/
|
|
n = BX_E1000_THIS s.mac_reg[TORL] + buf_size + /* Always include FCS length. */ 4;
|
|
if (n < BX_E1000_THIS s.mac_reg[TORL])
|
|
BX_E1000_THIS s.mac_reg[TORH]++;
|
|
BX_E1000_THIS s.mac_reg[TORL] = n;
|
|
|
|
n = E1000_ICS_RXT0;
|
|
if ((rdt = BX_E1000_THIS s.mac_reg[RDT]) < BX_E1000_THIS s.mac_reg[RDH])
|
|
rdt += BX_E1000_THIS s.mac_reg[RDLEN] / sizeof(desc);
|
|
if (((rdt - BX_E1000_THIS s.mac_reg[RDH]) * sizeof(desc)) <= BX_E1000_THIS s.mac_reg[RDLEN] >>
|
|
BX_E1000_THIS s.rxbuf_min_shift)
|
|
n |= E1000_ICS_RXDMT0;
|
|
|
|
set_ics(n);
|
|
|
|
bx_gui->statusbar_setitem(BX_E1000_THIS s.statusbar_id, 1);
|
|
}
|
|
|
|
|
|
// pci configuration space read callback handler
|
|
Bit32u bx_e1000_c::pci_read_handler(Bit8u address, unsigned io_len)
|
|
{
|
|
Bit32u value = 0;
|
|
|
|
for (unsigned i=0; i<io_len; i++) {
|
|
value |= (BX_E1000_THIS pci_conf[address+i] << (i*8));
|
|
}
|
|
|
|
if (io_len == 1)
|
|
BX_DEBUG(("read PCI register 0x%02x value 0x%02x", address, value));
|
|
else if (io_len == 2)
|
|
BX_DEBUG(("read PCI register 0x%02x value 0x%04x", address, value));
|
|
else if (io_len == 4)
|
|
BX_DEBUG(("read PCI register 0x%02x value 0x%08x", address, value));
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
// pci configuration space write callback handler
|
|
void bx_e1000_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_len)
|
|
{
|
|
Bit8u value8, oldval;
|
|
bx_bool baseaddr0_change = 0;
|
|
bx_bool baseaddr1_change = 0;
|
|
bx_bool romaddr_change = 0;
|
|
|
|
if ((address >= 0x18) && (address < 0x30))
|
|
return;
|
|
|
|
for (unsigned i=0; i<io_len; i++) {
|
|
value8 = (value >> (i*8)) & 0xFF;
|
|
oldval = BX_E1000_THIS pci_conf[address+i];
|
|
switch (address+i) {
|
|
case 0x04:
|
|
value8 &= 0x07;
|
|
break;
|
|
case 0x3c:
|
|
if (value8 != oldval) {
|
|
BX_INFO(("new irq line = %d", value8));
|
|
}
|
|
break;
|
|
case 0x10:
|
|
value8 = (value8 & 0xf0) | (oldval & 0x0f);
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
baseaddr0_change |= (value8 != oldval);
|
|
break;
|
|
case 0x14:
|
|
value8 = (value8 & 0xf0) | (oldval & 0x0f);
|
|
case 0x15:
|
|
case 0x16:
|
|
case 0x17:
|
|
baseaddr1_change |= (value8 != oldval);
|
|
break;
|
|
case 0x30:
|
|
case 0x31:
|
|
case 0x32:
|
|
case 0x33:
|
|
if (BX_E1000_THIS pci_rom_size > 0) {
|
|
if ((address+i) == 0x30) {
|
|
value8 &= 0x01;
|
|
} else if ((address+i) == 0x31) {
|
|
value8 &= 0xfc;
|
|
}
|
|
romaddr_change = 1;
|
|
break;
|
|
}
|
|
default:
|
|
value8 = oldval;
|
|
}
|
|
BX_E1000_THIS pci_conf[address+i] = value8;
|
|
}
|
|
if (baseaddr0_change) {
|
|
if (DEV_pci_set_base_mem(BX_E1000_THIS_PTR, mem_read_handler, mem_write_handler,
|
|
&BX_E1000_THIS pci_base_address[0],
|
|
&BX_E1000_THIS pci_conf[0x10],
|
|
0x20000)) {
|
|
BX_INFO(("new mem base address: 0x%08x", BX_E1000_THIS pci_base_address[0]));
|
|
}
|
|
}
|
|
if (baseaddr1_change) {
|
|
if (DEV_pci_set_base_io(BX_E1000_THIS_PTR, read_handler, write_handler,
|
|
&BX_E1000_THIS pci_base_address[1],
|
|
&BX_E1000_THIS pci_conf[0x14],
|
|
64, &e1000_iomask[0], "e1000")) {
|
|
BX_INFO(("new i/o base address: 0x%04x", BX_E1000_THIS pci_base_address[1]));
|
|
}
|
|
}
|
|
if (romaddr_change) {
|
|
if (DEV_pci_set_base_mem(BX_E1000_THIS_PTR, mem_read_handler,
|
|
mem_write_handler,
|
|
&BX_E1000_THIS pci_rom_address,
|
|
&BX_E1000_THIS pci_conf[0x30],
|
|
BX_E1000_THIS pci_rom_size)) {
|
|
BX_INFO(("new ROM address: 0x%08x", BX_E1000_THIS pci_rom_address));
|
|
}
|
|
}
|
|
|
|
if (io_len == 1)
|
|
BX_DEBUG(("write PCI register 0x%02x value 0x%02x", address, value));
|
|
else if (io_len == 2)
|
|
BX_DEBUG(("write PCI register 0x%02x value 0x%04x", address, value));
|
|
else if (io_len == 4)
|
|
BX_DEBUG(("write PCI register 0x%02x value 0x%08x", address, value));
|
|
}
|
|
|
|
#endif // BX_SUPPORT_PCI && BX_SUPPORT_E1000
|