///////////////////////////////////////////////////////////////////////// // $Id$ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2011 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 ///////////////////////////////////////////////////////////////////////// // Intel(R) 82540EM Gigabit Ethernet support ported from Qemu // 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", 0); bx_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 = bx_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); } else if (valid < 0x80) { BX_PANIC(("%s: 'e1000' directive incomplete (mac is required)", context)); } } else { if (valid & 0x80) { SIM->get_param_bool("enabled", base)->set(0); } } } else { BX_PANIC(("%s: unknown directive '%s'", context, params[0])); } return 0; } Bit32s e1000_options_save(FILE *fp) { bx_write_pci_nic_options(fp, (bx_list_c*) SIM->get_param(BXPN_E1000)); return 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; } // temporary helper functions // we should use host from/to little endian conversion directly Bit16u cpu_to_le16(Bit16u value) { Bit16u hvalue; WriteHostWordToLittleEndian(&hvalue, value); return hvalue; } Bit32u cpu_to_le32(Bit32u value) { Bit32u hvalue; WriteHostDWordToLittleEndian(&hvalue, value); return hvalue; } Bit64u cpu_to_le64(Bit64u value) { Bit64u hvalue; WriteHostQWordToLittleEndian(&hvalue, value); return hvalue; } #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; } 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")); BX_UNREGISTER_DEVICE_DEVMODEL("e1000"); return; } memcpy(macaddr, SIM->get_param_string("macaddr", 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"); for (unsigned i=0; i<256; i++) { BX_E1000_THIS pci_conf[i] = 0x0; } 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) { 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[] = { { 0x00, 0x86 }, { 0x01, 0x80 }, { 0x02, 0x0e }, { 0x03, 0x10 }, { 0x04, 0x03 }, { 0x05, 0x00 }, // command io / memory { 0x06, 0x00 }, { 0x07, 0x00 }, // status { 0x08, 0x03 }, // revision number { 0x09, 0x00 }, // interface { 0x0a, 0x00 }, // class_sub { 0x0b, 0x02 }, // class_base Network Controller { 0x0e, 0x00 }, // header type generic // 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 { 0x3d, BX_PCI_INTA }, // INT }; 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[4]; bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "e1000", "E1000 State", 10); 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", "", 32); 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", "", 64); 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", "", 21); bx_list_c *header = new bx_list_c(tx, "header", "", 256); 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", "", 4); 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", "", 5); 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 field through the 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= 0x18) && (address < 0x30)) return; for (unsigned i=0; i> (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