From patchwork Tue May 31 07:20:57 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Fleytman X-Patchwork-Id: 9143685 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 0A1CE60757 for ; Tue, 31 May 2016 07:30:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EB0AE25D97 for ; Tue, 31 May 2016 07:30:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DFCA22821C; Tue, 31 May 2016 07:30:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, FUZZY_XPILL, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 5536325D97 for ; Tue, 31 May 2016 07:30:34 +0000 (UTC) Received: from localhost ([::1]:34805 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b7e8H-00006U-E0 for patchwork-qemu-devel@patchwork.kernel.org; Tue, 31 May 2016 03:30:33 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41688) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b7dzo-0000X8-Km for qemu-devel@nongnu.org; Tue, 31 May 2016 03:21:53 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1b7dzi-00004x-LQ for qemu-devel@nongnu.org; Tue, 31 May 2016 03:21:47 -0400 Received: from mail-wm0-x244.google.com ([2a00:1450:400c:c09::244]:36599) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b7dzh-0008WM-I8 for qemu-devel@nongnu.org; Tue, 31 May 2016 03:21:42 -0400 Received: by mail-wm0-x244.google.com with SMTP id q62so29686474wmg.3 for ; Tue, 31 May 2016 00:21:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daynix-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=gJLkq78S5onSMUEIaPWk56BrgjDvdUcUckLU3nI4rlQ=; b=WgtcneHU4BTcHnPZt6fpQGk6HY0pKc+VX/FYAQRwJtqALlOdMJR9ylnk8cxQrgljSO 6Xr5f4IOh9PY/AdRs+opojdVyH1wCjPvz159jYtXPrgm3qT4XVePDtg/wFQ2uhLrmis+ wz8TGc5MFD35Ql0b3QhmZU0iQqCBHVxFycI4zzQykG3i0QzlC3guWIi6uHJAneeg9qYR cQWITcmoHuoYAIHBKMJ9o7khmonfC5MLdtikSqfgGztieyclAeJZIYp/0N+NzcH+uvCf ruUYMaYF24HE7zH8ur9cyRMvZz+yzz6oQsxmJYa40Muhvi5W7lUTSTIG7qyd9kyKuC41 MV7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=gJLkq78S5onSMUEIaPWk56BrgjDvdUcUckLU3nI4rlQ=; b=F8czo7s8R1ZPGJcIc8cu33rLhYPKAQvLHn65gjsgBw/EdlJwSs4Oim1J4m4Gr/S+7c YYWtbtJbJF58ZluxvDNx8qnrVnWfLklBaSWVLK6XtGpQEHh+pCFgQLWaEbm2RJ+EwkI5 bWp303nLmIt9/EOrGL2XZF3PJLYEOVzR5RjSKE7oVzW7IAfls8YTE8MLgtiNwUU4SKY1 rCRRMULRXFEoqFkxFXqYFQaV7ZAj6e5MZSX95aW1atwqgLmO39c06jEMwbPVThSTs+0v QnPq9NCQ83gw0L3OWMjkAtt/zjWWwxtyecamYVKyucPEeGZUQMOgItfRz2b/JPRBh/0p 5SZg== X-Gm-Message-State: ALyK8tKh+g2hXPaT7cW7dJkzjnMkjW9QZLlhgNDVp2+pgpfktDpEURbG0l6T9eFjLRUGUg== X-Received: by 10.28.14.19 with SMTP id 19mr1761609wmo.11.1464679300446; Tue, 31 May 2016 00:21:40 -0700 (PDT) Received: from bark.daynix ([5.102.236.99]) by smtp.gmail.com with ESMTPSA id d5sm38256188wje.20.2016.05.31.00.21.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 31 May 2016 00:21:39 -0700 (PDT) From: Dmitry Fleytman To: qemu-devel@nongnu.org Date: Tue, 31 May 2016 10:20:57 +0300 Message-Id: <1464679259-1378-16-git-send-email-dmitry@daynix.com> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1464679259-1378-1-git-send-email-dmitry@daynix.com> References: <1464679259-1378-1-git-send-email-dmitry@daynix.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::244 Subject: [Qemu-devel] [PATCH v7 15/17] e1000: Move out code that will be reused in e1000e X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yan Vugenfirer , Jason Wang , Leonid Bloch , Shmulik Ladkani , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Dmitry Fleytman Code that will be shared moved to a separate files. Signed-off-by: Dmitry Fleytman Signed-off-by: Leonid Bloch --- MAINTAINERS | 5 + hw/net/Makefile.objs | 2 +- hw/net/e1000.c | 411 +++++++++++-------------------------------------- hw/net/e1000x_common.c | 267 ++++++++++++++++++++++++++++++++ hw/net/e1000x_common.h | 213 +++++++++++++++++++++++++ trace-events | 13 ++ 6 files changed, 591 insertions(+), 320 deletions(-) create mode 100644 hw/net/e1000x_common.c create mode 100644 hw/net/e1000x_common.h diff --git a/MAINTAINERS b/MAINTAINERS index e890849..ab4e884 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -981,6 +981,11 @@ F: hw/acpi/nvdimm.c F: hw/mem/nvdimm.c F: include/hw/mem/nvdimm.h +e1000x +M: Dmitry Fleytman +S: Maintained +F: hw/net/e1000x* + Subsystems ---------- Audio diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 527d264..bc69948 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -6,7 +6,7 @@ common-obj-$(CONFIG_NE2000_PCI) += ne2000.o common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o -common-obj-$(CONFIG_E1000_PCI) += e1000.o +common-obj-$(CONFIG_E1000_PCI) += e1000.o e1000x_common.o common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o common-obj-$(CONFIG_VMXNET3_PCI) += net_tx_pkt.o net_rx_pkt.o common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 8e79b55..36e3dbe 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -36,7 +36,7 @@ #include "qemu/iov.h" #include "qemu/range.h" -#include "e1000_regs.h" +#include "e1000x_common.h" static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -64,11 +64,6 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL); #define PNPMMIO_SIZE 0x20000 #define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */ -/* this is the size past which hardware will drop packets when setting LPE=0 */ -#define MAXIMUM_ETHERNET_VLAN_SIZE 1522 -/* this is the size past which hardware will drop packets when setting LPE=1 */ -#define MAXIMUM_ETHERNET_LPE_SIZE 16384 - #define MAXIMUM_ETHERNET_HDR_LEN (14+4) /* @@ -102,22 +97,9 @@ typedef struct E1000State_st { unsigned char vlan[4]; unsigned char data[0x10000]; uint16_t size; - unsigned char sum_needed; unsigned char vlan_needed; - uint8_t ipcss; - uint8_t ipcso; - uint16_t ipcse; - uint8_t tucss; - uint8_t tucso; - uint16_t tucse; - uint8_t hdr_len; - uint16_t mss; - uint32_t paylen; + e1000x_txd_props props; uint16_t tso_frames; - char tse; - int8_t ip; - int8_t tcp; - char cptse; // current packet tse bit } tx; struct { @@ -162,52 +144,19 @@ typedef struct E1000BaseClass { #define E1000_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE) -#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), defreg(RDTR), defreg(RADV), defreg(TADV), - defreg(ITR), defreg(FCRUC), defreg(TDFH), defreg(TDFT), - defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(RDFH), - defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), - defreg(IPAV), defreg(WUC), defreg(WUS), defreg(AIT), - defreg(IP6AT), defreg(IP4AT), defreg(FFLT), defreg(FFMT), - defreg(FFVT), defreg(WUPM), defreg(PBM), defreg(SCC), - defreg(ECOL), defreg(MCC), defreg(LATECOL), defreg(COLC), - defreg(DC), defreg(TNCRS), defreg(SEC), defreg(CEXTERR), - defreg(RLEC), defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), - defreg(XOFFTXC), defreg(RFC), defreg(RJC), defreg(RNBC), - defreg(TSCTFC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), - defreg(RUC), defreg(ROC), defreg(GORCL), defreg(GORCH), - defreg(GOTCL), defreg(GOTCH), defreg(BPRC), defreg(MPRC), - defreg(TSCTC), defreg(PRC64), defreg(PRC127), defreg(PRC255), - defreg(PRC511), defreg(PRC1023), defreg(PRC1522), defreg(PTC64), - defreg(PTC127), defreg(PTC255), defreg(PTC511), defreg(PTC1023), - defreg(PTC1522), defreg(MPTC), defreg(BPTC) -}; - static void -e1000_link_down(E1000State *s) +e1000_link_up(E1000State *s) { - s->mac_reg[STATUS] &= ~E1000_STATUS_LU; - s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS; - s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; - s->phy_reg[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; + e1000x_update_regs_on_link_up(s->mac_reg, s->phy_reg); + + /* E1000_STATUS_LU is tested by e1000_can_receive() */ + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } static void -e1000_link_up(E1000State *s) +e1000_autoneg_done(E1000State *s) { - s->mac_reg[STATUS] |= E1000_STATUS_LU; - s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS; + e1000x_update_regs_on_autoneg_done(s->mac_reg, s->phy_reg); /* E1000_STATUS_LU is tested by e1000_can_receive() */ qemu_flush_queued_packets(qemu_get_queue(s->nic)); @@ -233,10 +182,7 @@ set_phy_ctrl(E1000State *s, int index, uint16_t val) * down. */ if (have_autoneg(s) && (val & MII_CR_RESTART_AUTO_NEG)) { - e1000_link_down(s); - DBGOUT(PHY, "Start link auto negotiation\n"); - timer_mod(s->autoneg_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + e1000x_restart_autoneg(s->mac_reg, s->phy_reg, s->autoneg_timer); } } @@ -401,43 +347,16 @@ e1000_autoneg_timer(void *opaque) { E1000State *s = opaque; if (!qemu_get_queue(s->nic)->link_down) { - e1000_link_up(s); - s->phy_reg[PHY_LP_ABILITY] |= MII_LPAR_LPACK; - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; - DBGOUT(PHY, "Auto negotiation is completed\n"); + e1000_autoneg_done(s); set_ics(s, 0, E1000_ICS_LSC); /* signal link status change to guest */ } } -static int -rxbufsize(uint32_t 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; -} - static void e1000_reset(void *opaque) { E1000State *d = opaque; E1000BaseClass *edc = E1000_DEVICE_GET_CLASS(d); uint8_t *macaddr = d->conf.macaddr.a; - int i; timer_del(d->autoneg_timer); timer_del(d->mit_timer); @@ -453,17 +372,10 @@ static void e1000_reset(void *opaque) memset(&d->tx, 0, sizeof d->tx); if (qemu_get_queue(d->nic)->link_down) { - e1000_link_down(d); + e1000x_update_regs_on_link_down(d->mac_reg, d->phy_reg); } - /* Some guests expect pre-initialized RAH/RAL (AddrValid flag + MACaddr) */ - d->mac_reg[RA] = 0; - d->mac_reg[RA + 1] = E1000_RAH_AV; - for (i = 0; i < 4; i++) { - d->mac_reg[RA] |= macaddr[i] << (8 * i); - d->mac_reg[RA + 1] |= (i < 2) ? macaddr[i + 4] << (8 * i) : 0; - } - qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); + e1000x_reset_mac_addr(d->nic, d->mac_reg, macaddr); } static void @@ -477,7 +389,7 @@ static void set_rx_control(E1000State *s, int index, uint32_t val) { s->mac_reg[RCTL] = val; - s->rxbuf_size = rxbufsize(val); + s->rxbuf_size = e1000x_rxbufsize(val); s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1; DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT], s->mac_reg[RCTL]); @@ -598,89 +510,15 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse) } static inline void -inc_reg_if_not_full(E1000State *s, int index) -{ - if (s->mac_reg[index] != 0xffffffff) { - s->mac_reg[index]++; - } -} - -static inline void inc_tx_bcast_or_mcast_count(E1000State *s, const unsigned char *arr) { if (!memcmp(arr, bcast, sizeof bcast)) { - inc_reg_if_not_full(s, BPTC); + e1000x_inc_reg_if_not_full(s->mac_reg, BPTC); } else if (arr[0] & 1) { - inc_reg_if_not_full(s, MPTC); - } -} - -static void -grow_8reg_if_not_full(E1000State *s, int index, int size) -{ - uint64_t sum = s->mac_reg[index] | (uint64_t)s->mac_reg[index+1] << 32; - - if (sum + size < sum) { - sum = ~0ULL; - } else { - sum += size; - } - s->mac_reg[index] = sum; - s->mac_reg[index+1] = sum >> 32; -} - -static void -increase_size_stats(E1000State *s, const int *size_regs, int size) -{ - if (size > 1023) { - inc_reg_if_not_full(s, size_regs[5]); - } else if (size > 511) { - inc_reg_if_not_full(s, size_regs[4]); - } else if (size > 255) { - inc_reg_if_not_full(s, size_regs[3]); - } else if (size > 127) { - inc_reg_if_not_full(s, size_regs[2]); - } else if (size > 64) { - inc_reg_if_not_full(s, size_regs[1]); - } else if (size == 64) { - inc_reg_if_not_full(s, size_regs[0]); + e1000x_inc_reg_if_not_full(s->mac_reg, MPTC); } } -static inline int -vlan_enabled(E1000State *s) -{ - return ((s->mac_reg[CTRL] & E1000_CTRL_VME) != 0); -} - -static inline int -vlan_rx_filter_enabled(E1000State *s) -{ - return ((s->mac_reg[RCTL] & E1000_RCTL_VFE) != 0); -} - -static inline int -is_vlan_packet(E1000State *s, const uint8_t *buf) -{ - return (be16_to_cpup((uint16_t *)(buf + 12)) == - le16_to_cpu(s->mac_reg[VET])); -} - -static inline int -is_vlan_txd(uint32_t txd_lower) -{ - return ((txd_lower & E1000_TXD_CMD_VLE) != 0); -} - -/* FCS aka Ethernet CRC-32. We don't get it from backends and can't - * fill it in, just pad descriptor length by 4 bytes unless guest - * told us to strip it off the packet. */ -static inline int -fcs_len(E1000State *s) -{ - return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4; -} - static void e1000_send_packet(E1000State *s, const uint8_t *buf, int size) { @@ -694,7 +532,7 @@ e1000_send_packet(E1000State *s, const uint8_t *buf, int size) qemu_send_packet(nc, buf, size); } inc_tx_bcast_or_mcast_count(s, buf); - increase_size_stats(s, PTCregs, size); + e1000x_increase_size_stats(s->mac_reg, PTCregs, size); } static void @@ -704,34 +542,34 @@ xmit_seg(E1000State *s) unsigned int frames = s->tx.tso_frames, css, sofar; struct e1000_tx *tp = &s->tx; - if (tp->tse && tp->cptse) { - css = tp->ipcss; + if (tp->props.tse && tp->props.cptse) { + css = tp->props.ipcss; DBGOUT(TXSUM, "frames %d size %d ipcss %d\n", frames, tp->size, css); - if (tp->ip) { /* IPv4 */ + if (tp->props.ip) { /* IPv4 */ stw_be_p(tp->data+css+2, tp->size - css); stw_be_p(tp->data+css+4, be16_to_cpup((uint16_t *)(tp->data+css+4))+frames); } else { /* IPv6 */ stw_be_p(tp->data+css+4, tp->size - css); } - css = tp->tucss; + css = tp->props.tucss; len = tp->size - css; - DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len); - if (tp->tcp) { - sofar = frames * tp->mss; + DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->props.tcp, css, len); + if (tp->props.tcp) { + sofar = frames * tp->props.mss; stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */ - if (tp->paylen - sofar > tp->mss) { + if (tp->props.paylen - sofar > tp->props.mss) { tp->data[css + 13] &= ~9; /* PSH, FIN */ } else if (frames) { - inc_reg_if_not_full(s, TSCTC); + e1000x_inc_reg_if_not_full(s->mac_reg, TSCTC); } } else /* UDP */ stw_be_p(tp->data+css+4, len); - if (tp->sum_needed & E1000_TXD_POPTS_TXSM) { + if (tp->props.sum_needed & E1000_TXD_POPTS_TXSM) { unsigned int phsum; // add pseudo-header length before checksum calculation - sp = (uint16_t *)(tp->data + tp->tucso); + sp = (uint16_t *)(tp->data + tp->props.tucso); phsum = be16_to_cpup(sp) + len; phsum = (phsum >> 16) + (phsum & 0xffff); stw_be_p(sp, phsum); @@ -739,10 +577,14 @@ xmit_seg(E1000State *s) 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->props.sum_needed & E1000_TXD_POPTS_TXSM) { + putsum(tp->data, tp->size, tp->props.tucso, + tp->props.tucss, tp->props.tucse); + } + if (tp->props.sum_needed & E1000_TXD_POPTS_IXSM) { + putsum(tp->data, tp->size, tp->props.ipcso, + tp->props.ipcss, tp->props.ipcse); + } if (tp->vlan_needed) { memmove(tp->vlan, tp->data, 4); memmove(tp->data, tp->data + 4, 8); @@ -752,8 +594,8 @@ xmit_seg(E1000State *s) e1000_send_packet(s, tp->data, tp->size); } - inc_reg_if_not_full(s, TPT); - grow_8reg_if_not_full(s, TOTL, s->tx.size); + e1000x_inc_reg_if_not_full(s->mac_reg, TPT); + e1000x_grow_8reg_if_not_full(s->mac_reg, TOTL, s->tx.size); s->mac_reg[GPTC] = s->mac_reg[TPT]; s->mac_reg[GOTCL] = s->mac_reg[TOTL]; s->mac_reg[GOTCH] = s->mac_reg[TOTH]; @@ -765,7 +607,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) PCIDevice *d = PCI_DEVICE(s); uint32_t txd_lower = le32_to_cpu(dp->lower.data); uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D); - unsigned int split_size = txd_lower & 0xffff, bytes, sz, op; + unsigned int split_size = txd_lower & 0xffff, bytes, sz; unsigned int msh = 0xfffff; uint64_t addr; struct e1000_context_desc *xp = (struct e1000_context_desc *)dp; @@ -773,38 +615,27 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE); 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; + e1000x_read_tx_ctx_descr(xp, &tp->props); tp->tso_frames = 0; - if (tp->tucso == 0) { /* this is probably wrong */ + if (tp->props.tucso == 0) { /* this is probably wrong */ DBGOUT(TXSUM, "TCP/UDP: cso 0!\n"); - tp->tucso = tp->tucss + (tp->tcp ? 16 : 6); + tp->props.tucso = tp->props.tucss + (tp->props.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->props.sum_needed = le32_to_cpu(dp->upper.data) >> 8; } - tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0; + tp->props.cptse = (txd_lower & E1000_TXD_CMD_TSE) ? 1 : 0; } else { // legacy descriptor - tp->cptse = 0; + tp->props.cptse = 0; } - if (vlan_enabled(s) && is_vlan_txd(txd_lower) && - (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) { + if (e1000x_vlan_enabled(s->mac_reg) && + e1000x_is_vlan_txd(txd_lower) && + (tp->props.cptse || txd_lower & E1000_TXD_CMD_EOP)) { tp->vlan_needed = 1; stw_be_p(tp->vlan_header, le16_to_cpu(s->mac_reg[VET])); @@ -813,8 +644,8 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) } addr = le64_to_cpu(dp->buffer_addr); - if (tp->tse && tp->cptse) { - msh = tp->hdr_len + tp->mss; + if (tp->props.tse && tp->props.cptse) { + msh = tp->props.hdr_len + tp->props.mss; do { bytes = split_size; if (tp->size + bytes > msh) @@ -823,19 +654,19 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) bytes = MIN(sizeof(tp->data) - tp->size, bytes); pci_dma_read(d, addr, tp->data + tp->size, bytes); sz = tp->size + bytes; - if (sz >= tp->hdr_len && tp->size < tp->hdr_len) { - memmove(tp->header, tp->data, tp->hdr_len); + if (sz >= tp->props.hdr_len && tp->size < tp->props.hdr_len) { + memmove(tp->header, tp->data, tp->props.hdr_len); } tp->size = sz; addr += bytes; if (sz == msh) { xmit_seg(s); - memmove(tp->data, tp->header, tp->hdr_len); - tp->size = tp->hdr_len; + memmove(tp->data, tp->header, tp->props.hdr_len); + tp->size = tp->props.hdr_len; } split_size -= bytes; } while (bytes && split_size); - } else if (!tp->tse && tp->cptse) { + } else if (!tp->props.tse && tp->props.cptse) { // context descriptor TSE is not set, while data descriptor TSE is set DBGOUT(TXERR, "TCP segmentation error\n"); } else { @@ -846,14 +677,14 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) if (!(txd_lower & E1000_TXD_CMD_EOP)) return; - if (!(tp->tse && tp->cptse && tp->size < tp->hdr_len)) { + if (!(tp->props.tse && tp->props.cptse && tp->size < tp->props.hdr_len)) { xmit_seg(s); } tp->tso_frames = 0; - tp->sum_needed = 0; + tp->props.sum_needed = 0; tp->vlan_needed = 0; tp->size = 0; - tp->cptse = 0; + tp->props.cptse = 0; } static uint32_t @@ -925,11 +756,11 @@ start_xmit(E1000State *s) static int receive_filter(E1000State *s, const uint8_t *buf, int size) { - static const int mta_shift[] = {4, 3, 2, 0}; - uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp; + uint32_t rctl = s->mac_reg[RCTL]; int isbcast = !memcmp(buf, bcast, sizeof bcast), ismcast = (buf[0] & 1); - if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) { + if (e1000x_is_vlan_packet(buf, le16_to_cpu(s->mac_reg[VET])) && + e1000x_vlan_rx_filter_enabled(s->mac_reg)) { uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14)); uint32_t vfta = le32_to_cpup((uint32_t *)(s->mac_reg + VFTA) + ((vid >> 5) & 0x7f)); @@ -942,44 +773,16 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) } if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */ - inc_reg_if_not_full(s, MPRC); + e1000x_inc_reg_if_not_full(s->mac_reg, MPRC); return 1; } if (isbcast && (rctl & E1000_RCTL_BAM)) { /* broadcast enabled */ - inc_reg_if_not_full(s, BPRC); - return 1; - } - - for (rp = s->mac_reg + RA; rp < 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, (uint8_t *)ra, 6)) { - DBGOUT(RXFILTER, - "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x\n", - (int)(rp - s->mac_reg - RA)/2, - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - return 1; - } - } - DBGOUT(RXFILTER, "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x\n", - 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 (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) { - inc_reg_if_not_full(s, MPRC); + e1000x_inc_reg_if_not_full(s->mac_reg, BPRC); return 1; } - DBGOUT(RXFILTER, - "dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], - (rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5, - s->mac_reg[MTA + (f >> 5)]); - return 0; + return e1000x_rx_group_filter(s->mac_reg, buf); } static void @@ -989,13 +792,11 @@ e1000_set_link_status(NetClientState *nc) uint32_t old_status = s->mac_reg[STATUS]; if (nc->link_down) { - e1000_link_down(s); + e1000x_update_regs_on_link_down(s->mac_reg, s->phy_reg); } else { if (have_autoneg(s) && !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { - /* emulate auto-negotiation if supported */ - timer_mod(s->autoneg_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + e1000x_restart_autoneg(s->mac_reg, s->phy_reg, s->autoneg_timer); } else { e1000_link_up(s); } @@ -1028,9 +829,7 @@ e1000_can_receive(NetClientState *nc) { E1000State *s = qemu_get_nic_opaque(nc); - return (s->mac_reg[STATUS] & E1000_STATUS_LU) && - (s->mac_reg[RCTL] & E1000_RCTL_EN) && - (s->parent_obj.config[PCI_COMMAND] & PCI_COMMAND_MASTER) && + return e1000x_rx_ready(&s->parent_obj, s->mac_reg) && e1000_has_rxbufs(s, 1); } @@ -1061,14 +860,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) size_t desc_offset; size_t desc_size; size_t total_size; - static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511, - PRC1023, PRC1522 }; - - if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) { - return -1; - } - if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) { + if (!e1000x_hw_rx_enabled(s->mac_reg)) { return -1; } @@ -1076,7 +869,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) if (size < sizeof(min_buf)) { iov_to_buf(iov, iovcnt, 0, min_buf, size); memset(&min_buf[size], 0, sizeof(min_buf) - size); - inc_reg_if_not_full(s, RUC); + e1000x_inc_reg_if_not_full(s->mac_reg, RUC); min_iov.iov_base = filter_buf = min_buf; min_iov.iov_len = size = sizeof(min_buf); iovcnt = 1; @@ -1088,11 +881,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) } /* Discard oversized packets if !LPE and !SBP. */ - if ((size > MAXIMUM_ETHERNET_LPE_SIZE || - (size > MAXIMUM_ETHERNET_VLAN_SIZE - && !(s->mac_reg[RCTL] & E1000_RCTL_LPE))) - && !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) { - inc_reg_if_not_full(s, ROC); + if (e1000x_is_oversized(s->mac_reg, size)) { return size; } @@ -1100,7 +889,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) return size; } - if (vlan_enabled(s) && is_vlan_packet(s, filter_buf)) { + if (e1000x_vlan_enabled(s->mac_reg) && + e1000x_is_vlan_packet(filter_buf, le16_to_cpu(s->mac_reg[VET]))) { vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(filter_buf + 14))); iov_ofs = 4; @@ -1119,7 +909,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) rdh_start = s->mac_reg[RDH]; desc_offset = 0; - total_size = size + fcs_len(s); + total_size = size + e1000x_fcs_len(s->mac_reg); if (!e1000_has_rxbufs(s, total_size)) { set_ics(s, 0, E1000_ICS_RXO); return -1; @@ -1179,17 +969,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) } } while (desc_offset < total_size); - increase_size_stats(s, PRCregs, total_size); - inc_reg_if_not_full(s, TPR); - s->mac_reg[GPRC] = s->mac_reg[TPR]; - /* TOR - Total Octets Received: - * This register includes bytes received in a packet from the field through the field, inclusively. - * Always include FCS length (4) in size. - */ - grow_8reg_if_not_full(s, TORL, size+4); - s->mac_reg[GORCL] = s->mac_reg[TORL]; - s->mac_reg[GORCH] = s->mac_reg[TORH]; + e1000x_update_rx_total_stats(s->mac_reg, size, total_size); n = E1000_ICS_RXT0; if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH]) @@ -1670,20 +1450,20 @@ static const VMStateDescription vmstate_e1000 = { VMSTATE_UINT16(eecd_state.bitnum_out, E1000State), VMSTATE_UINT16(eecd_state.reading, E1000State), VMSTATE_UINT32(eecd_state.old_eecd, E1000State), - VMSTATE_UINT8(tx.ipcss, E1000State), - VMSTATE_UINT8(tx.ipcso, E1000State), - VMSTATE_UINT16(tx.ipcse, E1000State), - VMSTATE_UINT8(tx.tucss, E1000State), - VMSTATE_UINT8(tx.tucso, E1000State), - VMSTATE_UINT16(tx.tucse, E1000State), - VMSTATE_UINT32(tx.paylen, E1000State), - VMSTATE_UINT8(tx.hdr_len, E1000State), - VMSTATE_UINT16(tx.mss, E1000State), + VMSTATE_UINT8(tx.props.ipcss, E1000State), + VMSTATE_UINT8(tx.props.ipcso, E1000State), + VMSTATE_UINT16(tx.props.ipcse, E1000State), + VMSTATE_UINT8(tx.props.tucss, E1000State), + VMSTATE_UINT8(tx.props.tucso, E1000State), + VMSTATE_UINT16(tx.props.tucse, E1000State), + VMSTATE_UINT32(tx.props.paylen, E1000State), + VMSTATE_UINT8(tx.props.hdr_len, E1000State), + VMSTATE_UINT16(tx.props.mss, E1000State), VMSTATE_UINT16(tx.size, E1000State), VMSTATE_UINT16(tx.tso_frames, E1000State), - VMSTATE_UINT8(tx.sum_needed, E1000State), - VMSTATE_INT8(tx.ip, E1000State), - VMSTATE_INT8(tx.tcp, E1000State), + VMSTATE_UINT8(tx.props.sum_needed, E1000State), + VMSTATE_INT8(tx.props.ip, E1000State), + VMSTATE_INT8(tx.props.tcp, E1000State), VMSTATE_BUFFER(tx.header, E1000State), VMSTATE_BUFFER(tx.data, E1000State), VMSTATE_UINT16_ARRAY(eeprom_data, E1000State, 64), @@ -1806,15 +1586,11 @@ static void e1000_write_config(PCIDevice *pci_dev, uint32_t address, } } - static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); E1000State *d = E1000(pci_dev); - PCIDeviceClass *pdc = PCI_DEVICE_GET_CLASS(pci_dev); uint8_t *pci_conf; - uint16_t checksum = 0; - int i; uint8_t *macaddr; pci_dev->config_write = e1000_write_config; @@ -1832,17 +1608,14 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io); - memmove(d->eeprom_data, e1000_eeprom_template, - sizeof e1000_eeprom_template); qemu_macaddr_default_if_unset(&d->conf.macaddr); macaddr = d->conf.macaddr.a; - for (i = 0; i < 3; i++) - d->eeprom_data[i] = (macaddr[2*i+1]<<8) | macaddr[2*i]; - d->eeprom_data[11] = d->eeprom_data[13] = pdc->device_id; - for (i = 0; i < EEPROM_CHECKSUM_REG; i++) - checksum += d->eeprom_data[i]; - checksum = (uint16_t) EEPROM_SUM - checksum; - d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum; + + e1000x_core_prepare_eeprom(d->eeprom_data, + e1000_eeprom_template, + sizeof(e1000_eeprom_template), + PCI_DEVICE_GET_CLASS(pci_dev)->device_id, + macaddr); d->nic = qemu_new_nic(&net_e1000_info, &d->conf, object_get_typename(OBJECT(d)), dev->id, d); diff --git a/hw/net/e1000x_common.c b/hw/net/e1000x_common.c new file mode 100644 index 0000000..94f85c9 --- /dev/null +++ b/hw/net/e1000x_common.c @@ -0,0 +1,267 @@ +/* +* QEMU e1000(e) emulation - shared code +* +* Copyright (c) 2008 Qumranet +* +* Based on work done by: +* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. +* Copyright (c) 2007 Dan Aloni +* Copyright (c) 2004 Antony T Curtis +* +* 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, see . +*/ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "net/net.h" + +#include "e1000x_common.h" + +#include "trace.h" + +bool e1000x_rx_ready(PCIDevice *d, uint32_t *mac) +{ + bool link_up = mac[STATUS] & E1000_STATUS_LU; + bool rx_enabled = mac[RCTL] & E1000_RCTL_EN; + bool pci_master = d->config[PCI_COMMAND] & PCI_COMMAND_MASTER; + + if (!link_up || !rx_enabled || !pci_master) { + trace_e1000x_rx_can_recv_disabled(link_up, rx_enabled, pci_master); + return false; + } + + return true; +} + +bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet) +{ + uint16_t eth_proto = be16_to_cpup((uint16_t *)(buf + 12)); + bool res = (eth_proto == vet); + + trace_e1000x_vlan_is_vlan_pkt(res, eth_proto, vet); + + return res; +} + +bool e1000x_rx_group_filter(uint32_t *mac, const uint8_t *buf) +{ + static const int mta_shift[] = { 4, 3, 2, 0 }; + uint32_t f, ra[2], *rp, rctl = mac[RCTL]; + + for (rp = mac + RA; rp < mac + 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, (uint8_t *)ra, 6)) { + trace_e1000x_rx_flt_ucast_match((int)(rp - mac - RA) / 2, + MAC_ARG(buf)); + return true; + } + } + trace_e1000x_rx_flt_ucast_mismatch(MAC_ARG(buf)); + + f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3]; + f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff; + if (mac[MTA + (f >> 5)] & (1 << (f & 0x1f))) { + e1000x_inc_reg_if_not_full(mac, MPRC); + return true; + } + + trace_e1000x_rx_flt_inexact_mismatch(MAC_ARG(buf), + (rctl >> E1000_RCTL_MO_SHIFT) & 3, + f >> 5, + mac[MTA + (f >> 5)]); + + return false; +} + +bool e1000x_hw_rx_enabled(uint32_t *mac) +{ + if (!(mac[STATUS] & E1000_STATUS_LU)) { + trace_e1000x_rx_link_down(mac[STATUS]); + return false; + } + + if (!(mac[RCTL] & E1000_RCTL_EN)) { + trace_e1000x_rx_disabled(mac[RCTL]); + return false; + } + + return true; +} + +bool e1000x_is_oversized(uint32_t *mac, size_t size) +{ + /* this is the size past which hardware will + drop packets when setting LPE=0 */ + static const int maximum_ethernet_vlan_size = 1522; + /* this is the size past which hardware will + drop packets when setting LPE=1 */ + static const int maximum_ethernet_lpe_size = 16384; + + if ((size > maximum_ethernet_lpe_size || + (size > maximum_ethernet_vlan_size + && !(mac[RCTL] & E1000_RCTL_LPE))) + && !(mac[RCTL] & E1000_RCTL_SBP)) { + e1000x_inc_reg_if_not_full(mac, ROC); + trace_e1000x_rx_oversized(size); + return true; + } + + return false; +} + +void e1000x_restart_autoneg(uint32_t *mac, uint16_t *phy, QEMUTimer *timer) +{ + e1000x_update_regs_on_link_down(mac, phy); + trace_e1000x_link_negotiation_start(); + timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); +} + +void e1000x_reset_mac_addr(NICState *nic, uint32_t *mac_regs, + uint8_t *mac_addr) +{ + int i; + + mac_regs[RA] = 0; + mac_regs[RA + 1] = E1000_RAH_AV; + for (i = 0; i < 4; i++) { + mac_regs[RA] |= mac_addr[i] << (8 * i); + mac_regs[RA + 1] |= + (i < 2) ? mac_addr[i + 4] << (8 * i) : 0; + } + + qemu_format_nic_info_str(qemu_get_queue(nic), mac_addr); + trace_e1000x_mac_indicate(MAC_ARG(mac_addr)); +} + +void e1000x_update_regs_on_autoneg_done(uint32_t *mac, uint16_t *phy) +{ + e1000x_update_regs_on_link_up(mac, phy); + phy[PHY_LP_ABILITY] |= MII_LPAR_LPACK; + phy[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + trace_e1000x_link_negotiation_done(); +} + +void +e1000x_core_prepare_eeprom(uint16_t *eeprom, + const uint16_t *templ, + uint32_t templ_size, + uint16_t dev_id, + const uint8_t *macaddr) +{ + uint16_t checksum = 0; + int i; + + memmove(eeprom, templ, templ_size); + + for (i = 0; i < 3; i++) { + eeprom[i] = (macaddr[2 * i + 1] << 8) | macaddr[2 * i]; + } + + eeprom[11] = eeprom[13] = dev_id; + + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) { + checksum += eeprom[i]; + } + + checksum = (uint16_t) EEPROM_SUM - checksum; + + eeprom[EEPROM_CHECKSUM_REG] = checksum; +} + +uint32_t +e1000x_rxbufsize(uint32_t rctl) +{ + rctl &= 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 (rctl) { + 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 +e1000x_update_rx_total_stats(uint32_t *mac, + size_t data_size, + size_t data_fcs_size) +{ + static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511, + PRC1023, PRC1522 }; + + e1000x_increase_size_stats(mac, PRCregs, data_fcs_size); + e1000x_inc_reg_if_not_full(mac, TPR); + mac[GPRC] = mac[TPR]; + /* TOR - Total Octets Received: + * This register includes bytes received in a packet from the field through the field, inclusively. + * Always include FCS length (4) in size. + */ + e1000x_grow_8reg_if_not_full(mac, TORL, data_size + 4); + mac[GORCL] = mac[TORL]; + mac[GORCH] = mac[TORH]; +} + +void +e1000x_increase_size_stats(uint32_t *mac, const int *size_regs, int size) +{ + if (size > 1023) { + e1000x_inc_reg_if_not_full(mac, size_regs[5]); + } else if (size > 511) { + e1000x_inc_reg_if_not_full(mac, size_regs[4]); + } else if (size > 255) { + e1000x_inc_reg_if_not_full(mac, size_regs[3]); + } else if (size > 127) { + e1000x_inc_reg_if_not_full(mac, size_regs[2]); + } else if (size > 64) { + e1000x_inc_reg_if_not_full(mac, size_regs[1]); + } else if (size == 64) { + e1000x_inc_reg_if_not_full(mac, size_regs[0]); + } +} + +void +e1000x_read_tx_ctx_descr(struct e1000_context_desc *d, + e1000x_txd_props *props) +{ + uint32_t op = le32_to_cpu(d->cmd_and_length); + + props->ipcss = d->lower_setup.ip_fields.ipcss; + props->ipcso = d->lower_setup.ip_fields.ipcso; + props->ipcse = le16_to_cpu(d->lower_setup.ip_fields.ipcse); + props->tucss = d->upper_setup.tcp_fields.tucss; + props->tucso = d->upper_setup.tcp_fields.tucso; + props->tucse = le16_to_cpu(d->upper_setup.tcp_fields.tucse); + props->paylen = op & 0xfffff; + props->hdr_len = d->tcp_seg_setup.fields.hdr_len; + props->mss = le16_to_cpu(d->tcp_seg_setup.fields.mss); + props->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0; + props->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0; + props->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0; +} diff --git a/hw/net/e1000x_common.h b/hw/net/e1000x_common.h new file mode 100644 index 0000000..21bf28e --- /dev/null +++ b/hw/net/e1000x_common.h @@ -0,0 +1,213 @@ +/* +* QEMU e1000(e) emulation - shared code +* +* Copyright (c) 2008 Qumranet +* +* Based on work done by: +* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. +* Copyright (c) 2007 Dan Aloni +* Copyright (c) 2004 Antony T Curtis +* +* 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, see . +*/ + +#include "e1000_regs.h" + +#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(RDBAH0), + defreg(RDBAL0), defreg(RDH0), defreg(RDLEN0), defreg(RDT0), + defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), + defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), + defreg(TDLEN1), defreg(TDBAL1), defreg(TDBAH1), defreg(TDH1), + defreg(TDT1), 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), defreg(RDTR), defreg(RADV), + defreg(TADV), defreg(ITR), defreg(SCC), defreg(ECOL), + defreg(MCC), defreg(LATECOL), defreg(COLC), defreg(DC), + defreg(TNCRS), defreg(SEC), defreg(CEXTERR), defreg(RLEC), + defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), defreg(XOFFTXC), + defreg(FCRUC), defreg(AIT), defreg(TDFH), defreg(TDFT), + defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(WUC), + defreg(WUS), defreg(POEMB), defreg(PBS), defreg(RDFH), + defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), + defreg(PBM), defreg(IPAV), defreg(IP4AT), defreg(IP6AT), + defreg(WUPM), defreg(FFLT), defreg(FFMT), defreg(FFVT), + defreg(TARC0), defreg(TARC1), defreg(IAM), defreg(EXTCNF_CTRL), + defreg(GCR), defreg(TIMINCA), defreg(EIAC), defreg(CTRL_EXT), + defreg(IVAR), defreg(MFUTP01), defreg(MFUTP23), defreg(MANC2H), + defreg(MFVAL), defreg(MDEF), defreg(FACTPS), defreg(FTFT), + defreg(RUC), defreg(ROC), defreg(RFC), defreg(RJC), + defreg(PRC64), defreg(PRC127), defreg(PRC255), defreg(PRC511), + defreg(PRC1023), defreg(PRC1522), defreg(PTC64), defreg(PTC127), + defreg(PTC255), defreg(PTC511), defreg(PTC1023), defreg(PTC1522), + defreg(GORCL), defreg(GORCH), defreg(GOTCL), defreg(GOTCH), + defreg(RNBC), defreg(BPRC), defreg(MPRC), defreg(RFCTL), + defreg(PSRCTL), defreg(MPTC), defreg(BPTC), defreg(TSCTFC), + defreg(IAC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), + defreg(TSCTC), defreg(RXCSUM), defreg(FUNCTAG), defreg(GSCL_1), + defreg(GSCL_2), defreg(GSCL_3), defreg(GSCL_4), defreg(GSCN_0), + defreg(GSCN_1), defreg(GSCN_2), defreg(GSCN_3), defreg(GCR2), + defreg(RAID), defreg(RSRPD), defreg(TIDV), defreg(EITR), + defreg(MRQC), defreg(RETA), defreg(RSSRK), defreg(RDBAH1), + defreg(RDBAL1), defreg(RDLEN1), defreg(RDH1), defreg(RDT1), + defreg(PBACLR), defreg(FCAL), defreg(FCAH), defreg(FCT), + defreg(FCRTH), defreg(FCRTL), defreg(FCTTV), defreg(FCRTV), + defreg(FLA), defreg(EEWR), defreg(FLOP), defreg(FLOL), + defreg(FLSWCTL), defreg(FLSWCNT), defreg(RXDCTL), defreg(RXDCTL1), + defreg(MAVTV0), defreg(MAVTV1), defreg(MAVTV2), defreg(MAVTV3), + defreg(TXSTMPL), defreg(TXSTMPH), defreg(SYSTIML), defreg(SYSTIMH), + defreg(RXCFGL), defreg(RXUDP), defreg(TIMADJL), defreg(TIMADJH), + defreg(RXSTMPH), defreg(RXSTMPL), defreg(RXSATRL), defreg(RXSATRH), + defreg(FLASHT), defreg(TIPG), defreg(RDH), defreg(RDT), + defreg(RDLEN), defreg(RDBAH), defreg(RDBAL), + defreg(TXDCTL1), + defreg(FLSWDATA), + defreg(CTRL_DUP), + defreg(EXTCNF_SIZE), + defreg(EEMNGCTL), + defreg(EEMNGDATA), + defreg(FLMNGCTL), + defreg(FLMNGDATA), + defreg(FLMNGCNT), + defreg(TSYNCRXCTL), + defreg(TSYNCTXCTL), + + /* Aliases */ + defreg(RDH0_A), defreg(RDT0_A), defreg(RDTR_A), defreg(RDFH_A), + defreg(RDFT_A), defreg(TDH_A), defreg(TDT_A), defreg(TIDV_A), + defreg(TDFH_A), defreg(TDFT_A), defreg(RA_A), defreg(RDBAL0_A), + defreg(TDBAL_A), defreg(TDLEN_A), defreg(VFTA_A), defreg(RDLEN0_A), + defreg(FCRTL_A), defreg(FCRTH_A) +}; + +static inline void +e1000x_inc_reg_if_not_full(uint32_t *mac, int index) +{ + if (mac[index] != 0xffffffff) { + mac[index]++; + } +} + +static inline void +e1000x_grow_8reg_if_not_full(uint32_t *mac, int index, int size) +{ + uint64_t sum = mac[index] | (uint64_t)mac[index + 1] << 32; + + if (sum + size < sum) { + sum = ~0ULL; + } else { + sum += size; + } + mac[index] = sum; + mac[index + 1] = sum >> 32; +} + +static inline int +e1000x_vlan_enabled(uint32_t *mac) +{ + return ((mac[CTRL] & E1000_CTRL_VME) != 0); +} + +static inline int +e1000x_is_vlan_txd(uint32_t txd_lower) +{ + return ((txd_lower & E1000_TXD_CMD_VLE) != 0); +} + +static inline int +e1000x_vlan_rx_filter_enabled(uint32_t *mac) +{ + return ((mac[RCTL] & E1000_RCTL_VFE) != 0); +} + +static inline int +e1000x_fcs_len(uint32_t *mac) +{ + /* FCS aka Ethernet CRC-32. We don't get it from backends and can't + * fill it in, just pad descriptor length by 4 bytes unless guest + * told us to strip it off the packet. */ + return (mac[RCTL] & E1000_RCTL_SECRC) ? 0 : 4; +} + +static inline void +e1000x_update_regs_on_link_down(uint32_t *mac, uint16_t *phy) +{ + mac[STATUS] &= ~E1000_STATUS_LU; + phy[PHY_STATUS] &= ~MII_SR_LINK_STATUS; + phy[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; + phy[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; +} + +static inline void +e1000x_update_regs_on_link_up(uint32_t *mac, uint16_t *phy) +{ + mac[STATUS] |= E1000_STATUS_LU; + phy[PHY_STATUS] |= MII_SR_LINK_STATUS; +} + +void e1000x_update_rx_total_stats(uint32_t *mac, + size_t data_size, + size_t data_fcs_size); + +void e1000x_core_prepare_eeprom(uint16_t *eeprom, + const uint16_t *templ, + uint32_t templ_size, + uint16_t dev_id, + const uint8_t *macaddr); + +uint32_t e1000x_rxbufsize(uint32_t rctl); + +bool e1000x_rx_ready(PCIDevice *d, uint32_t *mac); + +bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet); + +bool e1000x_rx_group_filter(uint32_t *mac, const uint8_t *buf); + +bool e1000x_hw_rx_enabled(uint32_t *mac); + +bool e1000x_is_oversized(uint32_t *mac, size_t size); + +void e1000x_restart_autoneg(uint32_t *mac, uint16_t *phy, QEMUTimer *timer); + +void e1000x_reset_mac_addr(NICState *nic, uint32_t *mac_regs, + uint8_t *mac_addr); + +void e1000x_update_regs_on_autoneg_done(uint32_t *mac, uint16_t *phy); + +void e1000x_increase_size_stats(uint32_t *mac, const int *size_regs, int size); + +typedef struct e1000x_txd_props { + unsigned char sum_needed; + uint8_t ipcss; + uint8_t ipcso; + uint16_t ipcse; + uint8_t tucss; + uint8_t tucso; + uint16_t tucse; + uint32_t paylen; + uint8_t hdr_len; + uint16_t mss; + int8_t ip; + int8_t tcp; + bool tse; + bool cptse; +} e1000x_txd_props; + +void e1000x_read_tx_ctx_descr(struct e1000_context_desc *d, + e1000x_txd_props *props); diff --git a/trace-events b/trace-events index dbcfb8d..d7646a7 100644 --- a/trace-events +++ b/trace-events @@ -1986,3 +1986,16 @@ net_rx_pkt_rss_ip6(void) "Calculating IPv6 RSS hash" net_rx_pkt_rss_ip6_ex(void) "Calculating IPv6/EX RSS hash" net_rx_pkt_rss_hash(size_t rss_length, uint32_t rss_hash) "RSS hash for %lu bytes: 0x%X" net_rx_pkt_rss_add_chunk(void* ptr, size_t size, size_t input_offset) "Add RSS chunk %p, %lu bytes, RSS input offset %lu bytes" + +# hw/net/e1000x_common.c +e1000x_rx_can_recv_disabled(bool link_up, bool rx_enabled, bool pci_master) "link_up: %d, rx_enabled %d, pci_master %d" +e1000x_vlan_is_vlan_pkt(bool is_vlan_pkt, uint16_t eth_proto, uint16_t vet) "Is VLAN packet: %d, ETH proto: 0x%X, VET: 0x%X" +e1000x_rx_flt_ucast_match(uint32_t idx, uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x" +e1000x_rx_flt_ucast_mismatch(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x" +e1000x_rx_flt_inexact_mismatch(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint32_t mo, uint32_t mta, uint32_t mta_val) "inexact mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x" +e1000x_rx_link_down(uint32_t status_reg) "Received packet dropped because the link is down STATUS = %u" +e1000x_rx_disabled(uint32_t rctl_reg) "Received packet dropped because receive is disabled RCTL = %u" +e1000x_rx_oversized(size_t size) "Received packet dropped because it was oversized (%lu bytes)" +e1000x_mac_indicate(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Indicating MAC to guest: %02x:%02x:%02x:%02x:%02x:%02x" +e1000x_link_negotiation_start(void) "Start link auto negotiation" +e1000x_link_negotiation_done(void) "Auto negotiation is completed"