212f7b1dac
The current implementation of igb uses only part of a advanced Tx context descriptor and first data descriptor because it misses some features and sniffs the trait of the packet instead of respecting the packet type specified in the descriptor. However, we will certainly need the entire Tx context descriptor when we update igb to respect these ignored fields. Save the entire context descriptor and first data descriptor except the buffer address to prepare for such a change. This also introduces the distinction of contexts with different indexes, which was not present in e1000e but in igb. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Sriram Yagnaraman <sriram.yagnaraman@est.tech> Signed-off-by: Jason Wang <jasowang@redhat.com>
4077 lines
115 KiB
C
4077 lines
115 KiB
C
/*
|
|
* Core code for QEMU igb emulation
|
|
*
|
|
* Datasheet:
|
|
* https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82576eg-gbe-datasheet.pdf
|
|
*
|
|
* Copyright (c) 2020-2023 Red Hat, Inc.
|
|
* Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com)
|
|
* Developed by Daynix Computing LTD (http://www.daynix.com)
|
|
*
|
|
* Authors:
|
|
* Akihiko Odaki <akihiko.odaki@daynix.com>
|
|
* Gal Hammmer <gal.hammer@sap.com>
|
|
* Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
|
* Dmitry Fleytman <dmitry@daynix.com>
|
|
* Leonid Bloch <leonid@daynix.com>
|
|
* Yan Vugenfirer <yan@daynix.com>
|
|
*
|
|
* Based on work done by:
|
|
* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc.
|
|
* Copyright (c) 2008 Qumranet
|
|
* Based on work done by:
|
|
* Copyright (c) 2007 Dan Aloni
|
|
* Copyright (c) 2004 Antony T Curtis
|
|
*
|
|
* 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.1 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/log.h"
|
|
#include "net/net.h"
|
|
#include "net/tap.h"
|
|
#include "hw/net/mii.h"
|
|
#include "hw/pci/msi.h"
|
|
#include "hw/pci/msix.h"
|
|
#include "sysemu/runstate.h"
|
|
|
|
#include "net_tx_pkt.h"
|
|
#include "net_rx_pkt.h"
|
|
|
|
#include "igb_common.h"
|
|
#include "e1000x_common.h"
|
|
#include "igb_core.h"
|
|
|
|
#include "trace.h"
|
|
|
|
#define E1000E_MAX_TX_FRAGS (64)
|
|
|
|
union e1000_rx_desc_union {
|
|
struct e1000_rx_desc legacy;
|
|
union e1000_adv_rx_desc adv;
|
|
};
|
|
|
|
typedef struct IGBTxPktVmdqCallbackContext {
|
|
IGBCore *core;
|
|
NetClientState *nc;
|
|
} IGBTxPktVmdqCallbackContext;
|
|
|
|
static ssize_t
|
|
igb_receive_internal(IGBCore *core, const struct iovec *iov, int iovcnt,
|
|
bool has_vnet, bool *external_tx);
|
|
|
|
static inline void
|
|
igb_set_interrupt_cause(IGBCore *core, uint32_t val);
|
|
|
|
static void igb_update_interrupt_state(IGBCore *core);
|
|
static void igb_reset(IGBCore *core, bool sw);
|
|
|
|
static inline void
|
|
igb_raise_legacy_irq(IGBCore *core)
|
|
{
|
|
trace_e1000e_irq_legacy_notify(true);
|
|
e1000x_inc_reg_if_not_full(core->mac, IAC);
|
|
pci_set_irq(core->owner, 1);
|
|
}
|
|
|
|
static inline void
|
|
igb_lower_legacy_irq(IGBCore *core)
|
|
{
|
|
trace_e1000e_irq_legacy_notify(false);
|
|
pci_set_irq(core->owner, 0);
|
|
}
|
|
|
|
static void igb_msix_notify(IGBCore *core, unsigned int vector)
|
|
{
|
|
PCIDevice *dev = core->owner;
|
|
uint16_t vfn;
|
|
|
|
vfn = 8 - (vector + 2) / IGBVF_MSIX_VEC_NUM;
|
|
if (vfn < pcie_sriov_num_vfs(core->owner)) {
|
|
dev = pcie_sriov_get_vf_at_index(core->owner, vfn);
|
|
assert(dev);
|
|
vector = (vector + 2) % IGBVF_MSIX_VEC_NUM;
|
|
} else if (vector >= IGB_MSIX_VEC_NUM) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"igb: Tried to use vector unavailable for PF");
|
|
return;
|
|
}
|
|
|
|
msix_notify(dev, vector);
|
|
}
|
|
|
|
static inline void
|
|
igb_intrmgr_rearm_timer(IGBIntrDelayTimer *timer)
|
|
{
|
|
int64_t delay_ns = (int64_t) timer->core->mac[timer->delay_reg] *
|
|
timer->delay_resolution_ns;
|
|
|
|
trace_e1000e_irq_rearm_timer(timer->delay_reg << 2, delay_ns);
|
|
|
|
timer_mod(timer->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + delay_ns);
|
|
|
|
timer->running = true;
|
|
}
|
|
|
|
static void
|
|
igb_intmgr_timer_resume(IGBIntrDelayTimer *timer)
|
|
{
|
|
if (timer->running) {
|
|
igb_intrmgr_rearm_timer(timer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_intmgr_timer_pause(IGBIntrDelayTimer *timer)
|
|
{
|
|
if (timer->running) {
|
|
timer_del(timer->timer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_intrmgr_on_msix_throttling_timer(void *opaque)
|
|
{
|
|
IGBIntrDelayTimer *timer = opaque;
|
|
int idx = timer - &timer->core->eitr[0];
|
|
|
|
timer->running = false;
|
|
|
|
trace_e1000e_irq_msix_notify_postponed_vec(idx);
|
|
igb_msix_notify(timer->core, idx);
|
|
}
|
|
|
|
static void
|
|
igb_intrmgr_initialize_all_timers(IGBCore *core, bool create)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IGB_INTR_NUM; i++) {
|
|
core->eitr[i].core = core;
|
|
core->eitr[i].delay_reg = EITR0 + i;
|
|
core->eitr[i].delay_resolution_ns = E1000_INTR_DELAY_NS_RES;
|
|
}
|
|
|
|
if (!create) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < IGB_INTR_NUM; i++) {
|
|
core->eitr[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
|
igb_intrmgr_on_msix_throttling_timer,
|
|
&core->eitr[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_intrmgr_resume(IGBCore *core)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IGB_INTR_NUM; i++) {
|
|
igb_intmgr_timer_resume(&core->eitr[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_intrmgr_pause(IGBCore *core)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IGB_INTR_NUM; i++) {
|
|
igb_intmgr_timer_pause(&core->eitr[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_intrmgr_reset(IGBCore *core)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IGB_INTR_NUM; i++) {
|
|
if (core->eitr[i].running) {
|
|
timer_del(core->eitr[i].timer);
|
|
igb_intrmgr_on_msix_throttling_timer(&core->eitr[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_intrmgr_pci_unint(IGBCore *core)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IGB_INTR_NUM; i++) {
|
|
timer_free(core->eitr[i].timer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_intrmgr_pci_realize(IGBCore *core)
|
|
{
|
|
igb_intrmgr_initialize_all_timers(core, true);
|
|
}
|
|
|
|
static inline bool
|
|
igb_rx_csum_enabled(IGBCore *core)
|
|
{
|
|
return (core->mac[RXCSUM] & E1000_RXCSUM_PCSD) ? false : true;
|
|
}
|
|
|
|
static inline bool
|
|
igb_rx_use_legacy_descriptor(IGBCore *core)
|
|
{
|
|
/*
|
|
* TODO: If SRRCTL[n],DESCTYPE = 000b, the 82576 uses the legacy Rx
|
|
* descriptor.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
static inline bool
|
|
igb_rss_enabled(IGBCore *core)
|
|
{
|
|
return (core->mac[MRQC] & 3) == E1000_MRQC_ENABLE_RSS_MQ &&
|
|
!igb_rx_csum_enabled(core) &&
|
|
!igb_rx_use_legacy_descriptor(core);
|
|
}
|
|
|
|
typedef struct E1000E_RSSInfo_st {
|
|
bool enabled;
|
|
uint32_t hash;
|
|
uint32_t queue;
|
|
uint32_t type;
|
|
} E1000E_RSSInfo;
|
|
|
|
static uint32_t
|
|
igb_rss_get_hash_type(IGBCore *core, struct NetRxPkt *pkt)
|
|
{
|
|
bool hasip4, hasip6;
|
|
EthL4HdrProto l4hdr_proto;
|
|
|
|
assert(igb_rss_enabled(core));
|
|
|
|
net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto);
|
|
|
|
if (hasip4) {
|
|
trace_e1000e_rx_rss_ip4(l4hdr_proto, core->mac[MRQC],
|
|
E1000_MRQC_EN_TCPIPV4(core->mac[MRQC]),
|
|
E1000_MRQC_EN_IPV4(core->mac[MRQC]));
|
|
|
|
if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP &&
|
|
E1000_MRQC_EN_TCPIPV4(core->mac[MRQC])) {
|
|
return E1000_MRQ_RSS_TYPE_IPV4TCP;
|
|
}
|
|
|
|
if (E1000_MRQC_EN_IPV4(core->mac[MRQC])) {
|
|
return E1000_MRQ_RSS_TYPE_IPV4;
|
|
}
|
|
} else if (hasip6) {
|
|
eth_ip6_hdr_info *ip6info = net_rx_pkt_get_ip6_info(pkt);
|
|
|
|
bool ex_dis = core->mac[RFCTL] & E1000_RFCTL_IPV6_EX_DIS;
|
|
bool new_ex_dis = core->mac[RFCTL] & E1000_RFCTL_NEW_IPV6_EXT_DIS;
|
|
|
|
/*
|
|
* Following two traces must not be combined because resulting
|
|
* event will have 11 arguments totally and some trace backends
|
|
* (at least "ust") have limitation of maximum 10 arguments per
|
|
* event. Events with more arguments fail to compile for
|
|
* backends like these.
|
|
*/
|
|
trace_e1000e_rx_rss_ip6_rfctl(core->mac[RFCTL]);
|
|
trace_e1000e_rx_rss_ip6(ex_dis, new_ex_dis, l4hdr_proto,
|
|
ip6info->has_ext_hdrs,
|
|
ip6info->rss_ex_dst_valid,
|
|
ip6info->rss_ex_src_valid,
|
|
core->mac[MRQC],
|
|
E1000_MRQC_EN_TCPIPV6(core->mac[MRQC]),
|
|
E1000_MRQC_EN_IPV6EX(core->mac[MRQC]),
|
|
E1000_MRQC_EN_IPV6(core->mac[MRQC]));
|
|
|
|
if ((!ex_dis || !ip6info->has_ext_hdrs) &&
|
|
(!new_ex_dis || !(ip6info->rss_ex_dst_valid ||
|
|
ip6info->rss_ex_src_valid))) {
|
|
|
|
if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP &&
|
|
E1000_MRQC_EN_TCPIPV6(core->mac[MRQC])) {
|
|
return E1000_MRQ_RSS_TYPE_IPV6TCP;
|
|
}
|
|
|
|
if (E1000_MRQC_EN_IPV6EX(core->mac[MRQC])) {
|
|
return E1000_MRQ_RSS_TYPE_IPV6EX;
|
|
}
|
|
|
|
}
|
|
|
|
if (E1000_MRQC_EN_IPV6(core->mac[MRQC])) {
|
|
return E1000_MRQ_RSS_TYPE_IPV6;
|
|
}
|
|
|
|
}
|
|
|
|
return E1000_MRQ_RSS_TYPE_NONE;
|
|
}
|
|
|
|
static uint32_t
|
|
igb_rss_calc_hash(IGBCore *core, struct NetRxPkt *pkt, E1000E_RSSInfo *info)
|
|
{
|
|
NetRxPktRssType type;
|
|
|
|
assert(igb_rss_enabled(core));
|
|
|
|
switch (info->type) {
|
|
case E1000_MRQ_RSS_TYPE_IPV4:
|
|
type = NetPktRssIpV4;
|
|
break;
|
|
case E1000_MRQ_RSS_TYPE_IPV4TCP:
|
|
type = NetPktRssIpV4Tcp;
|
|
break;
|
|
case E1000_MRQ_RSS_TYPE_IPV6TCP:
|
|
type = NetPktRssIpV6TcpEx;
|
|
break;
|
|
case E1000_MRQ_RSS_TYPE_IPV6:
|
|
type = NetPktRssIpV6;
|
|
break;
|
|
case E1000_MRQ_RSS_TYPE_IPV6EX:
|
|
type = NetPktRssIpV6Ex;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
|
|
return net_rx_pkt_calc_rss_hash(pkt, type, (uint8_t *) &core->mac[RSSRK]);
|
|
}
|
|
|
|
static void
|
|
igb_rss_parse_packet(IGBCore *core, struct NetRxPkt *pkt, bool tx,
|
|
E1000E_RSSInfo *info)
|
|
{
|
|
trace_e1000e_rx_rss_started();
|
|
|
|
if (tx || !igb_rss_enabled(core)) {
|
|
info->enabled = false;
|
|
info->hash = 0;
|
|
info->queue = 0;
|
|
info->type = 0;
|
|
trace_e1000e_rx_rss_disabled();
|
|
return;
|
|
}
|
|
|
|
info->enabled = true;
|
|
|
|
info->type = igb_rss_get_hash_type(core, pkt);
|
|
|
|
trace_e1000e_rx_rss_type(info->type);
|
|
|
|
if (info->type == E1000_MRQ_RSS_TYPE_NONE) {
|
|
info->hash = 0;
|
|
info->queue = 0;
|
|
return;
|
|
}
|
|
|
|
info->hash = igb_rss_calc_hash(core, pkt, info);
|
|
info->queue = E1000_RSS_QUEUE(&core->mac[RETA], info->hash);
|
|
}
|
|
|
|
static bool
|
|
igb_setup_tx_offloads(IGBCore *core, struct igb_tx *tx)
|
|
{
|
|
if (tx->first_cmd_type_len & E1000_ADVTXD_DCMD_TSE) {
|
|
uint32_t idx = (tx->first_olinfo_status >> 4) & 1;
|
|
uint32_t mss = tx->ctx[idx].mss_l4len_idx >> 16;
|
|
if (!net_tx_pkt_build_vheader(tx->tx_pkt, true, true, mss)) {
|
|
return false;
|
|
}
|
|
|
|
net_tx_pkt_update_ip_checksums(tx->tx_pkt);
|
|
e1000x_inc_reg_if_not_full(core->mac, TSCTC);
|
|
return true;
|
|
}
|
|
|
|
if (tx->first_olinfo_status & E1000_ADVTXD_POTS_TXSM) {
|
|
if (!net_tx_pkt_build_vheader(tx->tx_pkt, false, true, 0)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (tx->first_olinfo_status & E1000_ADVTXD_POTS_IXSM) {
|
|
net_tx_pkt_update_ip_hdr_checksum(tx->tx_pkt);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void igb_tx_pkt_mac_callback(void *core,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
const struct iovec *virt_iov,
|
|
int virt_iovcnt)
|
|
{
|
|
igb_receive_internal(core, virt_iov, virt_iovcnt, true, NULL);
|
|
}
|
|
|
|
static void igb_tx_pkt_vmdq_callback(void *opaque,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
const struct iovec *virt_iov,
|
|
int virt_iovcnt)
|
|
{
|
|
IGBTxPktVmdqCallbackContext *context = opaque;
|
|
bool external_tx;
|
|
|
|
igb_receive_internal(context->core, virt_iov, virt_iovcnt, true,
|
|
&external_tx);
|
|
|
|
if (external_tx) {
|
|
if (context->core->has_vnet) {
|
|
qemu_sendv_packet(context->nc, virt_iov, virt_iovcnt);
|
|
} else {
|
|
qemu_sendv_packet(context->nc, iov, iovcnt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TX Packets Switching (7.10.3.6) */
|
|
static bool igb_tx_pkt_switch(IGBCore *core, struct igb_tx *tx,
|
|
NetClientState *nc)
|
|
{
|
|
IGBTxPktVmdqCallbackContext context;
|
|
|
|
/* TX switching is only used to serve VM to VM traffic. */
|
|
if (!(core->mac[MRQC] & 1)) {
|
|
goto send_out;
|
|
}
|
|
|
|
/* TX switching requires DTXSWC.Loopback_en bit enabled. */
|
|
if (!(core->mac[DTXSWC] & E1000_DTXSWC_VMDQ_LOOPBACK_EN)) {
|
|
goto send_out;
|
|
}
|
|
|
|
context.core = core;
|
|
context.nc = nc;
|
|
|
|
return net_tx_pkt_send_custom(tx->tx_pkt, false,
|
|
igb_tx_pkt_vmdq_callback, &context);
|
|
|
|
send_out:
|
|
return net_tx_pkt_send(tx->tx_pkt, nc);
|
|
}
|
|
|
|
static bool
|
|
igb_tx_pkt_send(IGBCore *core, struct igb_tx *tx, int queue_index)
|
|
{
|
|
int target_queue = MIN(core->max_queue_num, queue_index);
|
|
NetClientState *queue = qemu_get_subqueue(core->owner_nic, target_queue);
|
|
|
|
if (!igb_setup_tx_offloads(core, tx)) {
|
|
return false;
|
|
}
|
|
|
|
net_tx_pkt_dump(tx->tx_pkt);
|
|
|
|
if ((core->phy[MII_BMCR] & MII_BMCR_LOOPBACK) ||
|
|
((core->mac[RCTL] & E1000_RCTL_LBM_MAC) == E1000_RCTL_LBM_MAC)) {
|
|
return net_tx_pkt_send_custom(tx->tx_pkt, false,
|
|
igb_tx_pkt_mac_callback, core);
|
|
} else {
|
|
return igb_tx_pkt_switch(core, tx, queue);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_on_tx_done_update_stats(IGBCore *core, struct NetTxPkt *tx_pkt)
|
|
{
|
|
static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511,
|
|
PTC1023, PTC1522 };
|
|
|
|
size_t tot_len = net_tx_pkt_get_total_len(tx_pkt) + 4;
|
|
|
|
e1000x_increase_size_stats(core->mac, PTCregs, tot_len);
|
|
e1000x_inc_reg_if_not_full(core->mac, TPT);
|
|
e1000x_grow_8reg_if_not_full(core->mac, TOTL, tot_len);
|
|
|
|
switch (net_tx_pkt_get_packet_type(tx_pkt)) {
|
|
case ETH_PKT_BCAST:
|
|
e1000x_inc_reg_if_not_full(core->mac, BPTC);
|
|
break;
|
|
case ETH_PKT_MCAST:
|
|
e1000x_inc_reg_if_not_full(core->mac, MPTC);
|
|
break;
|
|
case ETH_PKT_UCAST:
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
core->mac[GPTC] = core->mac[TPT];
|
|
core->mac[GOTCL] = core->mac[TOTL];
|
|
core->mac[GOTCH] = core->mac[TOTH];
|
|
}
|
|
|
|
static void
|
|
igb_process_tx_desc(IGBCore *core,
|
|
struct igb_tx *tx,
|
|
union e1000_adv_tx_desc *tx_desc,
|
|
int queue_index)
|
|
{
|
|
struct e1000_adv_tx_context_desc *tx_ctx_desc;
|
|
uint32_t cmd_type_len;
|
|
uint32_t idx;
|
|
uint64_t buffer_addr;
|
|
uint16_t length;
|
|
|
|
cmd_type_len = le32_to_cpu(tx_desc->read.cmd_type_len);
|
|
|
|
if (cmd_type_len & E1000_ADVTXD_DCMD_DEXT) {
|
|
if ((cmd_type_len & E1000_ADVTXD_DTYP_DATA) ==
|
|
E1000_ADVTXD_DTYP_DATA) {
|
|
/* advanced transmit data descriptor */
|
|
if (tx->first) {
|
|
tx->first_cmd_type_len = cmd_type_len;
|
|
tx->first_olinfo_status = le32_to_cpu(tx_desc->read.olinfo_status);
|
|
tx->first = false;
|
|
}
|
|
} else if ((cmd_type_len & E1000_ADVTXD_DTYP_CTXT) ==
|
|
E1000_ADVTXD_DTYP_CTXT) {
|
|
/* advanced transmit context descriptor */
|
|
tx_ctx_desc = (struct e1000_adv_tx_context_desc *)tx_desc;
|
|
idx = (le32_to_cpu(tx_ctx_desc->mss_l4len_idx) >> 4) & 1;
|
|
tx->ctx[idx].vlan_macip_lens = le32_to_cpu(tx_ctx_desc->vlan_macip_lens);
|
|
tx->ctx[idx].seqnum_seed = le32_to_cpu(tx_ctx_desc->seqnum_seed);
|
|
tx->ctx[idx].type_tucmd_mlhl = le32_to_cpu(tx_ctx_desc->type_tucmd_mlhl);
|
|
tx->ctx[idx].mss_l4len_idx = le32_to_cpu(tx_ctx_desc->mss_l4len_idx);
|
|
return;
|
|
} else {
|
|
/* unknown descriptor type */
|
|
return;
|
|
}
|
|
} else {
|
|
/* legacy descriptor */
|
|
|
|
/* TODO: Implement a support for legacy descriptors (7.2.2.1). */
|
|
}
|
|
|
|
buffer_addr = le64_to_cpu(tx_desc->read.buffer_addr);
|
|
length = cmd_type_len & 0xFFFF;
|
|
|
|
if (!tx->skip_cp) {
|
|
if (!net_tx_pkt_add_raw_fragment(tx->tx_pkt, buffer_addr, length)) {
|
|
tx->skip_cp = true;
|
|
}
|
|
}
|
|
|
|
if (cmd_type_len & E1000_TXD_CMD_EOP) {
|
|
if (!tx->skip_cp && net_tx_pkt_parse(tx->tx_pkt)) {
|
|
if (cmd_type_len & E1000_TXD_CMD_VLE) {
|
|
idx = (tx->first_olinfo_status >> 4) & 1;
|
|
uint16_t vlan = tx->ctx[idx].vlan_macip_lens >> 16;
|
|
uint16_t vet = core->mac[VET] & 0xffff;
|
|
net_tx_pkt_setup_vlan_header_ex(tx->tx_pkt, vlan, vet);
|
|
}
|
|
if (igb_tx_pkt_send(core, tx, queue_index)) {
|
|
igb_on_tx_done_update_stats(core, tx->tx_pkt);
|
|
}
|
|
}
|
|
|
|
tx->first = true;
|
|
tx->skip_cp = false;
|
|
net_tx_pkt_reset(tx->tx_pkt);
|
|
}
|
|
}
|
|
|
|
static uint32_t igb_tx_wb_eic(IGBCore *core, int queue_idx)
|
|
{
|
|
uint32_t n, ent = 0;
|
|
|
|
n = igb_ivar_entry_tx(queue_idx);
|
|
ent = (core->mac[IVAR0 + n / 4] >> (8 * (n % 4))) & 0xff;
|
|
|
|
return (ent & E1000_IVAR_VALID) ? BIT(ent & 0x1f) : 0;
|
|
}
|
|
|
|
static uint32_t igb_rx_wb_eic(IGBCore *core, int queue_idx)
|
|
{
|
|
uint32_t n, ent = 0;
|
|
|
|
n = igb_ivar_entry_rx(queue_idx);
|
|
ent = (core->mac[IVAR0 + n / 4] >> (8 * (n % 4))) & 0xff;
|
|
|
|
return (ent & E1000_IVAR_VALID) ? BIT(ent & 0x1f) : 0;
|
|
}
|
|
|
|
typedef struct E1000E_RingInfo_st {
|
|
int dbah;
|
|
int dbal;
|
|
int dlen;
|
|
int dh;
|
|
int dt;
|
|
int idx;
|
|
} E1000E_RingInfo;
|
|
|
|
static inline bool
|
|
igb_ring_empty(IGBCore *core, const E1000E_RingInfo *r)
|
|
{
|
|
return core->mac[r->dh] == core->mac[r->dt] ||
|
|
core->mac[r->dt] >= core->mac[r->dlen] / E1000_RING_DESC_LEN;
|
|
}
|
|
|
|
static inline uint64_t
|
|
igb_ring_base(IGBCore *core, const E1000E_RingInfo *r)
|
|
{
|
|
uint64_t bah = core->mac[r->dbah];
|
|
uint64_t bal = core->mac[r->dbal];
|
|
|
|
return (bah << 32) + bal;
|
|
}
|
|
|
|
static inline uint64_t
|
|
igb_ring_head_descr(IGBCore *core, const E1000E_RingInfo *r)
|
|
{
|
|
return igb_ring_base(core, r) + E1000_RING_DESC_LEN * core->mac[r->dh];
|
|
}
|
|
|
|
static inline void
|
|
igb_ring_advance(IGBCore *core, const E1000E_RingInfo *r, uint32_t count)
|
|
{
|
|
core->mac[r->dh] += count;
|
|
|
|
if (core->mac[r->dh] * E1000_RING_DESC_LEN >= core->mac[r->dlen]) {
|
|
core->mac[r->dh] = 0;
|
|
}
|
|
}
|
|
|
|
static inline uint32_t
|
|
igb_ring_free_descr_num(IGBCore *core, const E1000E_RingInfo *r)
|
|
{
|
|
trace_e1000e_ring_free_space(r->idx, core->mac[r->dlen],
|
|
core->mac[r->dh], core->mac[r->dt]);
|
|
|
|
if (core->mac[r->dh] <= core->mac[r->dt]) {
|
|
return core->mac[r->dt] - core->mac[r->dh];
|
|
}
|
|
|
|
if (core->mac[r->dh] > core->mac[r->dt]) {
|
|
return core->mac[r->dlen] / E1000_RING_DESC_LEN +
|
|
core->mac[r->dt] - core->mac[r->dh];
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
return 0;
|
|
}
|
|
|
|
static inline bool
|
|
igb_ring_enabled(IGBCore *core, const E1000E_RingInfo *r)
|
|
{
|
|
return core->mac[r->dlen] > 0;
|
|
}
|
|
|
|
typedef struct IGB_TxRing_st {
|
|
const E1000E_RingInfo *i;
|
|
struct igb_tx *tx;
|
|
} IGB_TxRing;
|
|
|
|
static inline int
|
|
igb_mq_queue_idx(int base_reg_idx, int reg_idx)
|
|
{
|
|
return (reg_idx - base_reg_idx) / 16;
|
|
}
|
|
|
|
static inline void
|
|
igb_tx_ring_init(IGBCore *core, IGB_TxRing *txr, int idx)
|
|
{
|
|
static const E1000E_RingInfo i[IGB_NUM_QUEUES] = {
|
|
{ TDBAH0, TDBAL0, TDLEN0, TDH0, TDT0, 0 },
|
|
{ TDBAH1, TDBAL1, TDLEN1, TDH1, TDT1, 1 },
|
|
{ TDBAH2, TDBAL2, TDLEN2, TDH2, TDT2, 2 },
|
|
{ TDBAH3, TDBAL3, TDLEN3, TDH3, TDT3, 3 },
|
|
{ TDBAH4, TDBAL4, TDLEN4, TDH4, TDT4, 4 },
|
|
{ TDBAH5, TDBAL5, TDLEN5, TDH5, TDT5, 5 },
|
|
{ TDBAH6, TDBAL6, TDLEN6, TDH6, TDT6, 6 },
|
|
{ TDBAH7, TDBAL7, TDLEN7, TDH7, TDT7, 7 },
|
|
{ TDBAH8, TDBAL8, TDLEN8, TDH8, TDT8, 8 },
|
|
{ TDBAH9, TDBAL9, TDLEN9, TDH9, TDT9, 9 },
|
|
{ TDBAH10, TDBAL10, TDLEN10, TDH10, TDT10, 10 },
|
|
{ TDBAH11, TDBAL11, TDLEN11, TDH11, TDT11, 11 },
|
|
{ TDBAH12, TDBAL12, TDLEN12, TDH12, TDT12, 12 },
|
|
{ TDBAH13, TDBAL13, TDLEN13, TDH13, TDT13, 13 },
|
|
{ TDBAH14, TDBAL14, TDLEN14, TDH14, TDT14, 14 },
|
|
{ TDBAH15, TDBAL15, TDLEN15, TDH15, TDT15, 15 }
|
|
};
|
|
|
|
assert(idx < ARRAY_SIZE(i));
|
|
|
|
txr->i = &i[idx];
|
|
txr->tx = &core->tx[idx];
|
|
}
|
|
|
|
typedef struct E1000E_RxRing_st {
|
|
const E1000E_RingInfo *i;
|
|
} E1000E_RxRing;
|
|
|
|
static inline void
|
|
igb_rx_ring_init(IGBCore *core, E1000E_RxRing *rxr, int idx)
|
|
{
|
|
static const E1000E_RingInfo i[IGB_NUM_QUEUES] = {
|
|
{ RDBAH0, RDBAL0, RDLEN0, RDH0, RDT0, 0 },
|
|
{ RDBAH1, RDBAL1, RDLEN1, RDH1, RDT1, 1 },
|
|
{ RDBAH2, RDBAL2, RDLEN2, RDH2, RDT2, 2 },
|
|
{ RDBAH3, RDBAL3, RDLEN3, RDH3, RDT3, 3 },
|
|
{ RDBAH4, RDBAL4, RDLEN4, RDH4, RDT4, 4 },
|
|
{ RDBAH5, RDBAL5, RDLEN5, RDH5, RDT5, 5 },
|
|
{ RDBAH6, RDBAL6, RDLEN6, RDH6, RDT6, 6 },
|
|
{ RDBAH7, RDBAL7, RDLEN7, RDH7, RDT7, 7 },
|
|
{ RDBAH8, RDBAL8, RDLEN8, RDH8, RDT8, 8 },
|
|
{ RDBAH9, RDBAL9, RDLEN9, RDH9, RDT9, 9 },
|
|
{ RDBAH10, RDBAL10, RDLEN10, RDH10, RDT10, 10 },
|
|
{ RDBAH11, RDBAL11, RDLEN11, RDH11, RDT11, 11 },
|
|
{ RDBAH12, RDBAL12, RDLEN12, RDH12, RDT12, 12 },
|
|
{ RDBAH13, RDBAL13, RDLEN13, RDH13, RDT13, 13 },
|
|
{ RDBAH14, RDBAL14, RDLEN14, RDH14, RDT14, 14 },
|
|
{ RDBAH15, RDBAL15, RDLEN15, RDH15, RDT15, 15 }
|
|
};
|
|
|
|
assert(idx < ARRAY_SIZE(i));
|
|
|
|
rxr->i = &i[idx];
|
|
}
|
|
|
|
static uint32_t
|
|
igb_txdesc_writeback(IGBCore *core, dma_addr_t base,
|
|
union e1000_adv_tx_desc *tx_desc,
|
|
const E1000E_RingInfo *txi)
|
|
{
|
|
PCIDevice *d;
|
|
uint32_t cmd_type_len = le32_to_cpu(tx_desc->read.cmd_type_len);
|
|
uint64_t tdwba;
|
|
|
|
tdwba = core->mac[E1000_TDWBAL(txi->idx) >> 2];
|
|
tdwba |= (uint64_t)core->mac[E1000_TDWBAH(txi->idx) >> 2] << 32;
|
|
|
|
if (!(cmd_type_len & E1000_TXD_CMD_RS)) {
|
|
return 0;
|
|
}
|
|
|
|
d = pcie_sriov_get_vf_at_index(core->owner, txi->idx % 8);
|
|
if (!d) {
|
|
d = core->owner;
|
|
}
|
|
|
|
if (tdwba & 1) {
|
|
uint32_t buffer = cpu_to_le32(core->mac[txi->dh]);
|
|
pci_dma_write(d, tdwba & ~3, &buffer, sizeof(buffer));
|
|
} else {
|
|
uint32_t status = le32_to_cpu(tx_desc->wb.status) | E1000_TXD_STAT_DD;
|
|
|
|
tx_desc->wb.status = cpu_to_le32(status);
|
|
pci_dma_write(d, base + offsetof(union e1000_adv_tx_desc, wb),
|
|
&tx_desc->wb, sizeof(tx_desc->wb));
|
|
}
|
|
|
|
return igb_tx_wb_eic(core, txi->idx);
|
|
}
|
|
|
|
static void
|
|
igb_start_xmit(IGBCore *core, const IGB_TxRing *txr)
|
|
{
|
|
PCIDevice *d;
|
|
dma_addr_t base;
|
|
union e1000_adv_tx_desc desc;
|
|
const E1000E_RingInfo *txi = txr->i;
|
|
uint32_t eic = 0;
|
|
|
|
/* TODO: check if the queue itself is enabled too. */
|
|
if (!(core->mac[TCTL] & E1000_TCTL_EN)) {
|
|
trace_e1000e_tx_disabled();
|
|
return;
|
|
}
|
|
|
|
d = pcie_sriov_get_vf_at_index(core->owner, txi->idx % 8);
|
|
if (!d) {
|
|
d = core->owner;
|
|
}
|
|
|
|
while (!igb_ring_empty(core, txi)) {
|
|
base = igb_ring_head_descr(core, txi);
|
|
|
|
pci_dma_read(d, base, &desc, sizeof(desc));
|
|
|
|
trace_e1000e_tx_descr((void *)(intptr_t)desc.read.buffer_addr,
|
|
desc.read.cmd_type_len, desc.wb.status);
|
|
|
|
igb_process_tx_desc(core, txr->tx, &desc, txi->idx);
|
|
igb_ring_advance(core, txi, 1);
|
|
eic |= igb_txdesc_writeback(core, base, &desc, txi);
|
|
}
|
|
|
|
if (eic) {
|
|
core->mac[EICR] |= eic;
|
|
igb_set_interrupt_cause(core, E1000_ICR_TXDW);
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
igb_rxbufsize(IGBCore *core, const E1000E_RingInfo *r)
|
|
{
|
|
uint32_t srrctl = core->mac[E1000_SRRCTL(r->idx) >> 2];
|
|
uint32_t bsizepkt = srrctl & E1000_SRRCTL_BSIZEPKT_MASK;
|
|
if (bsizepkt) {
|
|
return bsizepkt << E1000_SRRCTL_BSIZEPKT_SHIFT;
|
|
}
|
|
|
|
return e1000x_rxbufsize(core->mac[RCTL]);
|
|
}
|
|
|
|
static bool
|
|
igb_has_rxbufs(IGBCore *core, const E1000E_RingInfo *r, size_t total_size)
|
|
{
|
|
uint32_t bufs = igb_ring_free_descr_num(core, r);
|
|
uint32_t bufsize = igb_rxbufsize(core, r);
|
|
|
|
trace_e1000e_rx_has_buffers(r->idx, bufs, total_size, bufsize);
|
|
|
|
return total_size <= bufs / (core->rx_desc_len / E1000_MIN_RX_DESC_LEN) *
|
|
bufsize;
|
|
}
|
|
|
|
void
|
|
igb_start_recv(IGBCore *core)
|
|
{
|
|
int i;
|
|
|
|
trace_e1000e_rx_start_recv();
|
|
|
|
for (i = 0; i <= core->max_queue_num; i++) {
|
|
qemu_flush_queued_packets(qemu_get_subqueue(core->owner_nic, i));
|
|
}
|
|
}
|
|
|
|
bool
|
|
igb_can_receive(IGBCore *core)
|
|
{
|
|
int i;
|
|
|
|
if (!e1000x_rx_ready(core->owner, core->mac)) {
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < IGB_NUM_QUEUES; i++) {
|
|
E1000E_RxRing rxr;
|
|
|
|
igb_rx_ring_init(core, &rxr, i);
|
|
if (igb_ring_enabled(core, rxr.i) && igb_has_rxbufs(core, rxr.i, 1)) {
|
|
trace_e1000e_rx_can_recv();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
trace_e1000e_rx_can_recv_rings_full();
|
|
return false;
|
|
}
|
|
|
|
ssize_t
|
|
igb_receive(IGBCore *core, const uint8_t *buf, size_t size)
|
|
{
|
|
const struct iovec iov = {
|
|
.iov_base = (uint8_t *)buf,
|
|
.iov_len = size
|
|
};
|
|
|
|
return igb_receive_iov(core, &iov, 1);
|
|
}
|
|
|
|
static inline bool
|
|
igb_rx_l3_cso_enabled(IGBCore *core)
|
|
{
|
|
return !!(core->mac[RXCSUM] & E1000_RXCSUM_IPOFLD);
|
|
}
|
|
|
|
static inline bool
|
|
igb_rx_l4_cso_enabled(IGBCore *core)
|
|
{
|
|
return !!(core->mac[RXCSUM] & E1000_RXCSUM_TUOFLD);
|
|
}
|
|
|
|
static uint16_t igb_receive_assign(IGBCore *core, const struct eth_header *ehdr,
|
|
E1000E_RSSInfo *rss_info, bool *external_tx)
|
|
{
|
|
static const int ta_shift[] = { 4, 3, 2, 0 };
|
|
uint32_t f, ra[2], *macp, rctl = core->mac[RCTL];
|
|
uint16_t queues = 0;
|
|
uint16_t vid = lduw_be_p(&PKT_GET_VLAN_HDR(ehdr)->h_tci) & VLAN_VID_MASK;
|
|
bool accepted = false;
|
|
int i;
|
|
|
|
memset(rss_info, 0, sizeof(E1000E_RSSInfo));
|
|
|
|
if (external_tx) {
|
|
*external_tx = true;
|
|
}
|
|
|
|
if (e1000x_is_vlan_packet(ehdr, core->mac[VET] & 0xffff) &&
|
|
e1000x_vlan_rx_filter_enabled(core->mac)) {
|
|
uint32_t vfta =
|
|
ldl_le_p((uint32_t *)(core->mac + VFTA) +
|
|
((vid >> E1000_VFTA_ENTRY_SHIFT) & E1000_VFTA_ENTRY_MASK));
|
|
if ((vfta & (1 << (vid & E1000_VFTA_ENTRY_BIT_SHIFT_MASK))) == 0) {
|
|
trace_e1000e_rx_flt_vlan_mismatch(vid);
|
|
return queues;
|
|
} else {
|
|
trace_e1000e_rx_flt_vlan_match(vid);
|
|
}
|
|
}
|
|
|
|
if (core->mac[MRQC] & 1) {
|
|
if (is_broadcast_ether_addr(ehdr->h_dest)) {
|
|
for (i = 0; i < 8; i++) {
|
|
if (core->mac[VMOLR0 + i] & E1000_VMOLR_BAM) {
|
|
queues |= BIT(i);
|
|
}
|
|
}
|
|
} else {
|
|
for (macp = core->mac + RA; macp < core->mac + RA + 32; macp += 2) {
|
|
if (!(macp[1] & E1000_RAH_AV)) {
|
|
continue;
|
|
}
|
|
ra[0] = cpu_to_le32(macp[0]);
|
|
ra[1] = cpu_to_le32(macp[1]);
|
|
if (!memcmp(ehdr->h_dest, (uint8_t *)ra, ETH_ALEN)) {
|
|
queues |= (macp[1] & E1000_RAH_POOL_MASK) / E1000_RAH_POOL_1;
|
|
}
|
|
}
|
|
|
|
for (macp = core->mac + RA2; macp < core->mac + RA2 + 16; macp += 2) {
|
|
if (!(macp[1] & E1000_RAH_AV)) {
|
|
continue;
|
|
}
|
|
ra[0] = cpu_to_le32(macp[0]);
|
|
ra[1] = cpu_to_le32(macp[1]);
|
|
if (!memcmp(ehdr->h_dest, (uint8_t *)ra, ETH_ALEN)) {
|
|
queues |= (macp[1] & E1000_RAH_POOL_MASK) / E1000_RAH_POOL_1;
|
|
}
|
|
}
|
|
|
|
if (!queues) {
|
|
macp = core->mac + (is_multicast_ether_addr(ehdr->h_dest) ? MTA : UTA);
|
|
|
|
f = ta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3];
|
|
f = (((ehdr->h_dest[5] << 8) | ehdr->h_dest[4]) >> f) & 0xfff;
|
|
if (macp[f >> 5] & (1 << (f & 0x1f))) {
|
|
for (i = 0; i < 8; i++) {
|
|
if (core->mac[VMOLR0 + i] & E1000_VMOLR_ROMPE) {
|
|
queues |= BIT(i);
|
|
}
|
|
}
|
|
}
|
|
} else if (is_unicast_ether_addr(ehdr->h_dest) && external_tx) {
|
|
*external_tx = false;
|
|
}
|
|
}
|
|
|
|
if (e1000x_vlan_rx_filter_enabled(core->mac)) {
|
|
uint16_t mask = 0;
|
|
|
|
if (e1000x_is_vlan_packet(ehdr, core->mac[VET] & 0xffff)) {
|
|
for (i = 0; i < E1000_VLVF_ARRAY_SIZE; i++) {
|
|
if ((core->mac[VLVF0 + i] & E1000_VLVF_VLANID_MASK) == vid &&
|
|
(core->mac[VLVF0 + i] & E1000_VLVF_VLANID_ENABLE)) {
|
|
uint32_t poolsel = core->mac[VLVF0 + i] & E1000_VLVF_POOLSEL_MASK;
|
|
mask |= poolsel >> E1000_VLVF_POOLSEL_SHIFT;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < 8; i++) {
|
|
if (core->mac[VMOLR0 + i] & E1000_VMOLR_AUPE) {
|
|
mask |= BIT(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
queues &= mask;
|
|
}
|
|
|
|
if (is_unicast_ether_addr(ehdr->h_dest) && !queues && !external_tx &&
|
|
!(core->mac[VT_CTL] & E1000_VT_CTL_DISABLE_DEF_POOL)) {
|
|
uint32_t def_pl = core->mac[VT_CTL] & E1000_VT_CTL_DEFAULT_POOL_MASK;
|
|
queues = BIT(def_pl >> E1000_VT_CTL_DEFAULT_POOL_SHIFT);
|
|
}
|
|
|
|
igb_rss_parse_packet(core, core->rx_pkt, external_tx != NULL, rss_info);
|
|
if (rss_info->queue & 1) {
|
|
queues <<= 8;
|
|
}
|
|
} else {
|
|
switch (net_rx_pkt_get_packet_type(core->rx_pkt)) {
|
|
case ETH_PKT_UCAST:
|
|
if (rctl & E1000_RCTL_UPE) {
|
|
accepted = true; /* promiscuous ucast */
|
|
}
|
|
break;
|
|
|
|
case ETH_PKT_BCAST:
|
|
if (rctl & E1000_RCTL_BAM) {
|
|
accepted = true; /* broadcast enabled */
|
|
}
|
|
break;
|
|
|
|
case ETH_PKT_MCAST:
|
|
if (rctl & E1000_RCTL_MPE) {
|
|
accepted = true; /* promiscuous mcast */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
if (!accepted) {
|
|
accepted = e1000x_rx_group_filter(core->mac, ehdr->h_dest);
|
|
}
|
|
|
|
if (!accepted) {
|
|
for (macp = core->mac + RA2; macp < core->mac + RA2 + 16; macp += 2) {
|
|
if (!(macp[1] & E1000_RAH_AV)) {
|
|
continue;
|
|
}
|
|
ra[0] = cpu_to_le32(macp[0]);
|
|
ra[1] = cpu_to_le32(macp[1]);
|
|
if (!memcmp(ehdr->h_dest, (uint8_t *)ra, ETH_ALEN)) {
|
|
trace_e1000x_rx_flt_ucast_match((int)(macp - core->mac - RA2) / 2,
|
|
MAC_ARG(ehdr->h_dest));
|
|
|
|
accepted = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (accepted) {
|
|
igb_rss_parse_packet(core, core->rx_pkt, false, rss_info);
|
|
queues = BIT(rss_info->queue);
|
|
}
|
|
}
|
|
|
|
return queues;
|
|
}
|
|
|
|
static inline void
|
|
igb_read_lgcy_rx_descr(IGBCore *core, struct e1000_rx_desc *desc,
|
|
hwaddr *buff_addr)
|
|
{
|
|
*buff_addr = le64_to_cpu(desc->buffer_addr);
|
|
}
|
|
|
|
static inline void
|
|
igb_read_adv_rx_descr(IGBCore *core, union e1000_adv_rx_desc *desc,
|
|
hwaddr *buff_addr)
|
|
{
|
|
*buff_addr = le64_to_cpu(desc->read.pkt_addr);
|
|
}
|
|
|
|
static inline void
|
|
igb_read_rx_descr(IGBCore *core, union e1000_rx_desc_union *desc,
|
|
hwaddr *buff_addr)
|
|
{
|
|
if (igb_rx_use_legacy_descriptor(core)) {
|
|
igb_read_lgcy_rx_descr(core, &desc->legacy, buff_addr);
|
|
} else {
|
|
igb_read_adv_rx_descr(core, &desc->adv, buff_addr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_verify_csum_in_sw(IGBCore *core,
|
|
struct NetRxPkt *pkt,
|
|
uint32_t *status_flags,
|
|
EthL4HdrProto l4hdr_proto)
|
|
{
|
|
bool csum_valid;
|
|
uint32_t csum_error;
|
|
|
|
if (igb_rx_l3_cso_enabled(core)) {
|
|
if (!net_rx_pkt_validate_l3_csum(pkt, &csum_valid)) {
|
|
trace_e1000e_rx_metadata_l3_csum_validation_failed();
|
|
} else {
|
|
csum_error = csum_valid ? 0 : E1000_RXDEXT_STATERR_IPE;
|
|
*status_flags |= E1000_RXD_STAT_IPCS | csum_error;
|
|
}
|
|
} else {
|
|
trace_e1000e_rx_metadata_l3_cso_disabled();
|
|
}
|
|
|
|
if (!igb_rx_l4_cso_enabled(core)) {
|
|
trace_e1000e_rx_metadata_l4_cso_disabled();
|
|
return;
|
|
}
|
|
|
|
if (!net_rx_pkt_validate_l4_csum(pkt, &csum_valid)) {
|
|
trace_e1000e_rx_metadata_l4_csum_validation_failed();
|
|
return;
|
|
}
|
|
|
|
csum_error = csum_valid ? 0 : E1000_RXDEXT_STATERR_TCPE;
|
|
*status_flags |= E1000_RXD_STAT_TCPCS | csum_error;
|
|
|
|
if (l4hdr_proto == ETH_L4_HDR_PROTO_UDP) {
|
|
*status_flags |= E1000_RXD_STAT_UDPCS;
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_build_rx_metadata(IGBCore *core,
|
|
struct NetRxPkt *pkt,
|
|
bool is_eop,
|
|
const E1000E_RSSInfo *rss_info,
|
|
uint16_t *pkt_info, uint16_t *hdr_info,
|
|
uint32_t *rss,
|
|
uint32_t *status_flags,
|
|
uint16_t *ip_id,
|
|
uint16_t *vlan_tag)
|
|
{
|
|
struct virtio_net_hdr *vhdr;
|
|
bool hasip4, hasip6;
|
|
EthL4HdrProto l4hdr_proto;
|
|
uint32_t pkt_type;
|
|
|
|
*status_flags = E1000_RXD_STAT_DD;
|
|
|
|
/* No additional metadata needed for non-EOP descriptors */
|
|
/* TODO: EOP apply only to status so don't skip whole function. */
|
|
if (!is_eop) {
|
|
goto func_exit;
|
|
}
|
|
|
|
*status_flags |= E1000_RXD_STAT_EOP;
|
|
|
|
net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto);
|
|
trace_e1000e_rx_metadata_protocols(hasip4, hasip6, l4hdr_proto);
|
|
|
|
/* VLAN state */
|
|
if (net_rx_pkt_is_vlan_stripped(pkt)) {
|
|
*status_flags |= E1000_RXD_STAT_VP;
|
|
*vlan_tag = cpu_to_le16(net_rx_pkt_get_vlan_tag(pkt));
|
|
trace_e1000e_rx_metadata_vlan(*vlan_tag);
|
|
}
|
|
|
|
/* Packet parsing results */
|
|
if ((core->mac[RXCSUM] & E1000_RXCSUM_PCSD) != 0) {
|
|
if (rss_info->enabled) {
|
|
*rss = cpu_to_le32(rss_info->hash);
|
|
trace_igb_rx_metadata_rss(*rss);
|
|
}
|
|
} else if (hasip4) {
|
|
*status_flags |= E1000_RXD_STAT_IPIDV;
|
|
*ip_id = cpu_to_le16(net_rx_pkt_get_ip_id(pkt));
|
|
trace_e1000e_rx_metadata_ip_id(*ip_id);
|
|
}
|
|
|
|
if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP && net_rx_pkt_is_tcp_ack(pkt)) {
|
|
*status_flags |= E1000_RXD_STAT_ACK;
|
|
trace_e1000e_rx_metadata_ack();
|
|
}
|
|
|
|
if (hasip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_DIS)) {
|
|
trace_e1000e_rx_metadata_ipv6_filtering_disabled();
|
|
pkt_type = E1000_RXD_PKT_MAC;
|
|
} else if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP ||
|
|
l4hdr_proto == ETH_L4_HDR_PROTO_UDP) {
|
|
pkt_type = hasip4 ? E1000_RXD_PKT_IP4_XDP : E1000_RXD_PKT_IP6_XDP;
|
|
} else if (hasip4 || hasip6) {
|
|
pkt_type = hasip4 ? E1000_RXD_PKT_IP4 : E1000_RXD_PKT_IP6;
|
|
} else {
|
|
pkt_type = E1000_RXD_PKT_MAC;
|
|
}
|
|
|
|
trace_e1000e_rx_metadata_pkt_type(pkt_type);
|
|
|
|
if (pkt_info) {
|
|
if (rss_info->enabled) {
|
|
*pkt_info = rss_info->type;
|
|
}
|
|
|
|
*pkt_info |= (pkt_type << 4);
|
|
} else {
|
|
*status_flags |= E1000_RXD_PKT_TYPE(pkt_type);
|
|
}
|
|
|
|
if (hdr_info) {
|
|
*hdr_info = 0;
|
|
}
|
|
|
|
/* RX CSO information */
|
|
if (hasip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_XSUM_DIS)) {
|
|
trace_e1000e_rx_metadata_ipv6_sum_disabled();
|
|
goto func_exit;
|
|
}
|
|
|
|
vhdr = net_rx_pkt_get_vhdr(pkt);
|
|
|
|
if (!(vhdr->flags & VIRTIO_NET_HDR_F_DATA_VALID) &&
|
|
!(vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
|
|
trace_e1000e_rx_metadata_virthdr_no_csum_info();
|
|
igb_verify_csum_in_sw(core, pkt, status_flags, l4hdr_proto);
|
|
goto func_exit;
|
|
}
|
|
|
|
if (igb_rx_l3_cso_enabled(core)) {
|
|
*status_flags |= hasip4 ? E1000_RXD_STAT_IPCS : 0;
|
|
} else {
|
|
trace_e1000e_rx_metadata_l3_cso_disabled();
|
|
}
|
|
|
|
if (igb_rx_l4_cso_enabled(core)) {
|
|
switch (l4hdr_proto) {
|
|
case ETH_L4_HDR_PROTO_TCP:
|
|
*status_flags |= E1000_RXD_STAT_TCPCS;
|
|
break;
|
|
|
|
case ETH_L4_HDR_PROTO_UDP:
|
|
*status_flags |= E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS;
|
|
break;
|
|
|
|
default:
|
|
goto func_exit;
|
|
}
|
|
} else {
|
|
trace_e1000e_rx_metadata_l4_cso_disabled();
|
|
}
|
|
|
|
trace_e1000e_rx_metadata_status_flags(*status_flags);
|
|
|
|
func_exit:
|
|
*status_flags = cpu_to_le32(*status_flags);
|
|
}
|
|
|
|
static inline void
|
|
igb_write_lgcy_rx_descr(IGBCore *core, struct e1000_rx_desc *desc,
|
|
struct NetRxPkt *pkt,
|
|
const E1000E_RSSInfo *rss_info,
|
|
uint16_t length)
|
|
{
|
|
uint32_t status_flags, rss;
|
|
uint16_t ip_id;
|
|
|
|
assert(!rss_info->enabled);
|
|
desc->length = cpu_to_le16(length);
|
|
desc->csum = 0;
|
|
|
|
igb_build_rx_metadata(core, pkt, pkt != NULL,
|
|
rss_info,
|
|
NULL, NULL, &rss,
|
|
&status_flags, &ip_id,
|
|
&desc->special);
|
|
desc->errors = (uint8_t) (le32_to_cpu(status_flags) >> 24);
|
|
desc->status = (uint8_t) le32_to_cpu(status_flags);
|
|
}
|
|
|
|
static inline void
|
|
igb_write_adv_rx_descr(IGBCore *core, union e1000_adv_rx_desc *desc,
|
|
struct NetRxPkt *pkt,
|
|
const E1000E_RSSInfo *rss_info,
|
|
uint16_t length)
|
|
{
|
|
memset(&desc->wb, 0, sizeof(desc->wb));
|
|
|
|
desc->wb.upper.length = cpu_to_le16(length);
|
|
|
|
igb_build_rx_metadata(core, pkt, pkt != NULL,
|
|
rss_info,
|
|
&desc->wb.lower.lo_dword.pkt_info,
|
|
&desc->wb.lower.lo_dword.hdr_info,
|
|
&desc->wb.lower.hi_dword.rss,
|
|
&desc->wb.upper.status_error,
|
|
&desc->wb.lower.hi_dword.csum_ip.ip_id,
|
|
&desc->wb.upper.vlan);
|
|
}
|
|
|
|
static inline void
|
|
igb_write_rx_descr(IGBCore *core, union e1000_rx_desc_union *desc,
|
|
struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info, uint16_t length)
|
|
{
|
|
if (igb_rx_use_legacy_descriptor(core)) {
|
|
igb_write_lgcy_rx_descr(core, &desc->legacy, pkt, rss_info, length);
|
|
} else {
|
|
igb_write_adv_rx_descr(core, &desc->adv, pkt, rss_info, length);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
igb_pci_dma_write_rx_desc(IGBCore *core, PCIDevice *dev, dma_addr_t addr,
|
|
union e1000_rx_desc_union *desc, dma_addr_t len)
|
|
{
|
|
if (igb_rx_use_legacy_descriptor(core)) {
|
|
struct e1000_rx_desc *d = &desc->legacy;
|
|
size_t offset = offsetof(struct e1000_rx_desc, status);
|
|
uint8_t status = d->status;
|
|
|
|
d->status &= ~E1000_RXD_STAT_DD;
|
|
pci_dma_write(dev, addr, desc, len);
|
|
|
|
if (status & E1000_RXD_STAT_DD) {
|
|
d->status = status;
|
|
pci_dma_write(dev, addr + offset, &status, sizeof(status));
|
|
}
|
|
} else {
|
|
union e1000_adv_rx_desc *d = &desc->adv;
|
|
size_t offset =
|
|
offsetof(union e1000_adv_rx_desc, wb.upper.status_error);
|
|
uint32_t status = d->wb.upper.status_error;
|
|
|
|
d->wb.upper.status_error &= ~E1000_RXD_STAT_DD;
|
|
pci_dma_write(dev, addr, desc, len);
|
|
|
|
if (status & E1000_RXD_STAT_DD) {
|
|
d->wb.upper.status_error = status;
|
|
pci_dma_write(dev, addr + offset, &status, sizeof(status));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_write_to_rx_buffers(IGBCore *core,
|
|
PCIDevice *d,
|
|
hwaddr ba,
|
|
uint16_t *written,
|
|
const char *data,
|
|
dma_addr_t data_len)
|
|
{
|
|
trace_igb_rx_desc_buff_write(ba, *written, data, data_len);
|
|
pci_dma_write(d, ba + *written, data, data_len);
|
|
*written += data_len;
|
|
}
|
|
|
|
static void
|
|
igb_update_rx_stats(IGBCore *core, size_t data_size, size_t data_fcs_size)
|
|
{
|
|
e1000x_update_rx_total_stats(core->mac, data_size, data_fcs_size);
|
|
|
|
switch (net_rx_pkt_get_packet_type(core->rx_pkt)) {
|
|
case ETH_PKT_BCAST:
|
|
e1000x_inc_reg_if_not_full(core->mac, BPRC);
|
|
break;
|
|
|
|
case ETH_PKT_MCAST:
|
|
e1000x_inc_reg_if_not_full(core->mac, MPRC);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline bool
|
|
igb_rx_descr_threshold_hit(IGBCore *core, const E1000E_RingInfo *rxi)
|
|
{
|
|
return igb_ring_free_descr_num(core, rxi) ==
|
|
((core->mac[E1000_SRRCTL(rxi->idx) >> 2] >> 20) & 31) * 16;
|
|
}
|
|
|
|
static void
|
|
igb_write_packet_to_guest(IGBCore *core, struct NetRxPkt *pkt,
|
|
const E1000E_RxRing *rxr,
|
|
const E1000E_RSSInfo *rss_info)
|
|
{
|
|
PCIDevice *d;
|
|
dma_addr_t base;
|
|
union e1000_rx_desc_union desc;
|
|
size_t desc_size;
|
|
size_t desc_offset = 0;
|
|
size_t iov_ofs = 0;
|
|
|
|
struct iovec *iov = net_rx_pkt_get_iovec(pkt);
|
|
size_t size = net_rx_pkt_get_total_len(pkt);
|
|
size_t total_size = size + e1000x_fcs_len(core->mac);
|
|
const E1000E_RingInfo *rxi = rxr->i;
|
|
size_t bufsize = igb_rxbufsize(core, rxi);
|
|
|
|
d = pcie_sriov_get_vf_at_index(core->owner, rxi->idx % 8);
|
|
if (!d) {
|
|
d = core->owner;
|
|
}
|
|
|
|
do {
|
|
hwaddr ba;
|
|
uint16_t written = 0;
|
|
bool is_last = false;
|
|
|
|
desc_size = total_size - desc_offset;
|
|
|
|
if (desc_size > bufsize) {
|
|
desc_size = bufsize;
|
|
}
|
|
|
|
if (igb_ring_empty(core, rxi)) {
|
|
return;
|
|
}
|
|
|
|
base = igb_ring_head_descr(core, rxi);
|
|
|
|
pci_dma_read(d, base, &desc, core->rx_desc_len);
|
|
|
|
trace_e1000e_rx_descr(rxi->idx, base, core->rx_desc_len);
|
|
|
|
igb_read_rx_descr(core, &desc, &ba);
|
|
|
|
if (ba) {
|
|
if (desc_offset < size) {
|
|
static const uint32_t fcs_pad;
|
|
size_t iov_copy;
|
|
size_t copy_size = size - desc_offset;
|
|
if (copy_size > bufsize) {
|
|
copy_size = bufsize;
|
|
}
|
|
|
|
/* Copy packet payload */
|
|
while (copy_size) {
|
|
iov_copy = MIN(copy_size, iov->iov_len - iov_ofs);
|
|
|
|
igb_write_to_rx_buffers(core, d, ba, &written,
|
|
iov->iov_base + iov_ofs, iov_copy);
|
|
|
|
copy_size -= iov_copy;
|
|
iov_ofs += iov_copy;
|
|
if (iov_ofs == iov->iov_len) {
|
|
iov++;
|
|
iov_ofs = 0;
|
|
}
|
|
}
|
|
|
|
if (desc_offset + desc_size >= total_size) {
|
|
/* Simulate FCS checksum presence in the last descriptor */
|
|
igb_write_to_rx_buffers(core, d, ba, &written,
|
|
(const char *) &fcs_pad, e1000x_fcs_len(core->mac));
|
|
}
|
|
}
|
|
} else { /* as per intel docs; skip descriptors with null buf addr */
|
|
trace_e1000e_rx_null_descriptor();
|
|
}
|
|
desc_offset += desc_size;
|
|
if (desc_offset >= total_size) {
|
|
is_last = true;
|
|
}
|
|
|
|
igb_write_rx_descr(core, &desc, is_last ? core->rx_pkt : NULL,
|
|
rss_info, written);
|
|
igb_pci_dma_write_rx_desc(core, d, base, &desc, core->rx_desc_len);
|
|
|
|
igb_ring_advance(core, rxi, core->rx_desc_len / E1000_MIN_RX_DESC_LEN);
|
|
|
|
} while (desc_offset < total_size);
|
|
|
|
igb_update_rx_stats(core, size, total_size);
|
|
}
|
|
|
|
static inline void
|
|
igb_rx_fix_l4_csum(IGBCore *core, struct NetRxPkt *pkt)
|
|
{
|
|
struct virtio_net_hdr *vhdr = net_rx_pkt_get_vhdr(pkt);
|
|
|
|
if (vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
|
|
net_rx_pkt_fix_l4_csum(pkt);
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
igb_receive_iov(IGBCore *core, const struct iovec *iov, int iovcnt)
|
|
{
|
|
return igb_receive_internal(core, iov, iovcnt, core->has_vnet, NULL);
|
|
}
|
|
|
|
static ssize_t
|
|
igb_receive_internal(IGBCore *core, const struct iovec *iov, int iovcnt,
|
|
bool has_vnet, bool *external_tx)
|
|
{
|
|
static const int maximum_ethernet_hdr_len = (ETH_HLEN + 4);
|
|
|
|
uint16_t queues = 0;
|
|
uint32_t n = 0;
|
|
uint8_t min_buf[ETH_ZLEN];
|
|
struct iovec min_iov;
|
|
struct eth_header *ehdr;
|
|
uint8_t *filter_buf;
|
|
size_t size, orig_size;
|
|
size_t iov_ofs = 0;
|
|
E1000E_RxRing rxr;
|
|
E1000E_RSSInfo rss_info;
|
|
size_t total_size;
|
|
int i;
|
|
|
|
trace_e1000e_rx_receive_iov(iovcnt);
|
|
|
|
if (external_tx) {
|
|
*external_tx = true;
|
|
}
|
|
|
|
if (!e1000x_hw_rx_enabled(core->mac)) {
|
|
return -1;
|
|
}
|
|
|
|
/* Pull virtio header in */
|
|
if (has_vnet) {
|
|
net_rx_pkt_set_vhdr_iovec(core->rx_pkt, iov, iovcnt);
|
|
iov_ofs = sizeof(struct virtio_net_hdr);
|
|
} else {
|
|
net_rx_pkt_unset_vhdr(core->rx_pkt);
|
|
}
|
|
|
|
filter_buf = iov->iov_base + iov_ofs;
|
|
orig_size = iov_size(iov, iovcnt);
|
|
size = orig_size - iov_ofs;
|
|
|
|
/* Pad to minimum Ethernet frame length */
|
|
if (size < sizeof(min_buf)) {
|
|
iov_to_buf(iov, iovcnt, iov_ofs, min_buf, size);
|
|
memset(&min_buf[size], 0, sizeof(min_buf) - size);
|
|
e1000x_inc_reg_if_not_full(core->mac, RUC);
|
|
min_iov.iov_base = filter_buf = min_buf;
|
|
min_iov.iov_len = size = sizeof(min_buf);
|
|
iovcnt = 1;
|
|
iov = &min_iov;
|
|
iov_ofs = 0;
|
|
} else if (iov->iov_len < maximum_ethernet_hdr_len) {
|
|
/* This is very unlikely, but may happen. */
|
|
iov_to_buf(iov, iovcnt, iov_ofs, min_buf, maximum_ethernet_hdr_len);
|
|
filter_buf = min_buf;
|
|
}
|
|
|
|
/* Discard oversized packets if !LPE and !SBP. */
|
|
if (e1000x_is_oversized(core->mac, size)) {
|
|
return orig_size;
|
|
}
|
|
|
|
ehdr = PKT_GET_ETH_HDR(filter_buf);
|
|
net_rx_pkt_set_packet_type(core->rx_pkt, get_eth_packet_type(ehdr));
|
|
|
|
net_rx_pkt_attach_iovec_ex(core->rx_pkt, iov, iovcnt, iov_ofs,
|
|
e1000x_vlan_enabled(core->mac),
|
|
core->mac[VET] & 0xffff);
|
|
|
|
queues = igb_receive_assign(core, ehdr, &rss_info, external_tx);
|
|
if (!queues) {
|
|
trace_e1000e_rx_flt_dropped();
|
|
return orig_size;
|
|
}
|
|
|
|
total_size = net_rx_pkt_get_total_len(core->rx_pkt) +
|
|
e1000x_fcs_len(core->mac);
|
|
|
|
for (i = 0; i < IGB_NUM_QUEUES; i++) {
|
|
if (!(queues & BIT(i))) {
|
|
continue;
|
|
}
|
|
|
|
igb_rx_ring_init(core, &rxr, i);
|
|
|
|
if (!igb_has_rxbufs(core, rxr.i, total_size)) {
|
|
n |= E1000_ICS_RXO;
|
|
trace_e1000e_rx_not_written_to_guest(rxr.i->idx);
|
|
continue;
|
|
}
|
|
|
|
n |= E1000_ICR_RXT0;
|
|
|
|
igb_rx_fix_l4_csum(core, core->rx_pkt);
|
|
igb_write_packet_to_guest(core, core->rx_pkt, &rxr, &rss_info);
|
|
|
|
/* Check if receive descriptor minimum threshold hit */
|
|
if (igb_rx_descr_threshold_hit(core, rxr.i)) {
|
|
n |= E1000_ICS_RXDMT0;
|
|
}
|
|
|
|
core->mac[EICR] |= igb_rx_wb_eic(core, rxr.i->idx);
|
|
|
|
trace_e1000e_rx_written_to_guest(rxr.i->idx);
|
|
}
|
|
|
|
trace_e1000e_rx_interrupt_set(n);
|
|
igb_set_interrupt_cause(core, n);
|
|
|
|
return orig_size;
|
|
}
|
|
|
|
static inline bool
|
|
igb_have_autoneg(IGBCore *core)
|
|
{
|
|
return core->phy[MII_BMCR] & MII_BMCR_AUTOEN;
|
|
}
|
|
|
|
static void igb_update_flowctl_status(IGBCore *core)
|
|
{
|
|
if (igb_have_autoneg(core) && core->phy[MII_BMSR] & MII_BMSR_AN_COMP) {
|
|
trace_e1000e_link_autoneg_flowctl(true);
|
|
core->mac[CTRL] |= E1000_CTRL_TFCE | E1000_CTRL_RFCE;
|
|
} else {
|
|
trace_e1000e_link_autoneg_flowctl(false);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
igb_link_down(IGBCore *core)
|
|
{
|
|
e1000x_update_regs_on_link_down(core->mac, core->phy);
|
|
igb_update_flowctl_status(core);
|
|
}
|
|
|
|
static inline void
|
|
igb_set_phy_ctrl(IGBCore *core, uint16_t val)
|
|
{
|
|
/* bits 0-5 reserved; MII_BMCR_[ANRESTART,RESET] are self clearing */
|
|
core->phy[MII_BMCR] = val & ~(0x3f | MII_BMCR_RESET | MII_BMCR_ANRESTART);
|
|
|
|
if ((val & MII_BMCR_ANRESTART) && igb_have_autoneg(core)) {
|
|
e1000x_restart_autoneg(core->mac, core->phy, core->autoneg_timer);
|
|
}
|
|
}
|
|
|
|
void igb_core_set_link_status(IGBCore *core)
|
|
{
|
|
NetClientState *nc = qemu_get_queue(core->owner_nic);
|
|
uint32_t old_status = core->mac[STATUS];
|
|
|
|
trace_e1000e_link_status_changed(nc->link_down ? false : true);
|
|
|
|
if (nc->link_down) {
|
|
e1000x_update_regs_on_link_down(core->mac, core->phy);
|
|
} else {
|
|
if (igb_have_autoneg(core) &&
|
|
!(core->phy[MII_BMSR] & MII_BMSR_AN_COMP)) {
|
|
e1000x_restart_autoneg(core->mac, core->phy,
|
|
core->autoneg_timer);
|
|
} else {
|
|
e1000x_update_regs_on_link_up(core->mac, core->phy);
|
|
igb_start_recv(core);
|
|
}
|
|
}
|
|
|
|
if (core->mac[STATUS] != old_status) {
|
|
igb_set_interrupt_cause(core, E1000_ICR_LSC);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_ctrl(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
trace_e1000e_core_ctrl_write(index, val);
|
|
|
|
/* RST is self clearing */
|
|
core->mac[CTRL] = val & ~E1000_CTRL_RST;
|
|
core->mac[CTRL_DUP] = core->mac[CTRL];
|
|
|
|
trace_e1000e_link_set_params(
|
|
!!(val & E1000_CTRL_ASDE),
|
|
(val & E1000_CTRL_SPD_SEL) >> E1000_CTRL_SPD_SHIFT,
|
|
!!(val & E1000_CTRL_FRCSPD),
|
|
!!(val & E1000_CTRL_FRCDPX),
|
|
!!(val & E1000_CTRL_RFCE),
|
|
!!(val & E1000_CTRL_TFCE));
|
|
|
|
if (val & E1000_CTRL_RST) {
|
|
trace_e1000e_core_ctrl_sw_reset();
|
|
igb_reset(core, true);
|
|
}
|
|
|
|
if (val & E1000_CTRL_PHY_RST) {
|
|
trace_e1000e_core_ctrl_phy_reset();
|
|
core->mac[STATUS] |= E1000_STATUS_PHYRA;
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_rfctl(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
trace_e1000e_rx_set_rfctl(val);
|
|
|
|
if (!(val & E1000_RFCTL_ISCSI_DIS)) {
|
|
trace_e1000e_wrn_iscsi_filtering_not_supported();
|
|
}
|
|
|
|
if (!(val & E1000_RFCTL_NFSW_DIS)) {
|
|
trace_e1000e_wrn_nfsw_filtering_not_supported();
|
|
}
|
|
|
|
if (!(val & E1000_RFCTL_NFSR_DIS)) {
|
|
trace_e1000e_wrn_nfsr_filtering_not_supported();
|
|
}
|
|
|
|
core->mac[RFCTL] = val;
|
|
}
|
|
|
|
static void
|
|
igb_calc_rxdesclen(IGBCore *core)
|
|
{
|
|
if (igb_rx_use_legacy_descriptor(core)) {
|
|
core->rx_desc_len = sizeof(struct e1000_rx_desc);
|
|
} else {
|
|
core->rx_desc_len = sizeof(union e1000_adv_rx_desc);
|
|
}
|
|
trace_e1000e_rx_desc_len(core->rx_desc_len);
|
|
}
|
|
|
|
static void
|
|
igb_set_rx_control(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[RCTL] = val;
|
|
trace_e1000e_rx_set_rctl(core->mac[RCTL]);
|
|
|
|
if (val & E1000_RCTL_DTYP_MASK) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"igb: RCTL.DTYP must be zero for compatibility");
|
|
}
|
|
|
|
if (val & E1000_RCTL_EN) {
|
|
igb_calc_rxdesclen(core);
|
|
igb_start_recv(core);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
igb_clear_ims_bits(IGBCore *core, uint32_t bits)
|
|
{
|
|
trace_e1000e_irq_clear_ims(bits, core->mac[IMS], core->mac[IMS] & ~bits);
|
|
core->mac[IMS] &= ~bits;
|
|
}
|
|
|
|
static inline bool
|
|
igb_postpone_interrupt(IGBIntrDelayTimer *timer)
|
|
{
|
|
if (timer->running) {
|
|
trace_e1000e_irq_postponed_by_xitr(timer->delay_reg << 2);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (timer->core->mac[timer->delay_reg] != 0) {
|
|
igb_intrmgr_rearm_timer(timer);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static inline bool
|
|
igb_eitr_should_postpone(IGBCore *core, int idx)
|
|
{
|
|
return igb_postpone_interrupt(&core->eitr[idx]);
|
|
}
|
|
|
|
static void igb_send_msix(IGBCore *core)
|
|
{
|
|
uint32_t causes = core->mac[EICR] & core->mac[EIMS];
|
|
uint32_t effective_eiac;
|
|
int vector;
|
|
|
|
for (vector = 0; vector < IGB_INTR_NUM; ++vector) {
|
|
if ((causes & BIT(vector)) && !igb_eitr_should_postpone(core, vector)) {
|
|
|
|
trace_e1000e_irq_msix_notify_vec(vector);
|
|
igb_msix_notify(core, vector);
|
|
|
|
trace_e1000e_irq_icr_clear_eiac(core->mac[EICR], core->mac[EIAC]);
|
|
effective_eiac = core->mac[EIAC] & BIT(vector);
|
|
core->mac[EICR] &= ~effective_eiac;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
igb_fix_icr_asserted(IGBCore *core)
|
|
{
|
|
core->mac[ICR] &= ~E1000_ICR_ASSERTED;
|
|
if (core->mac[ICR]) {
|
|
core->mac[ICR] |= E1000_ICR_ASSERTED;
|
|
}
|
|
|
|
trace_e1000e_irq_fix_icr_asserted(core->mac[ICR]);
|
|
}
|
|
|
|
static void
|
|
igb_update_interrupt_state(IGBCore *core)
|
|
{
|
|
uint32_t icr;
|
|
uint32_t causes;
|
|
uint32_t int_alloc;
|
|
|
|
icr = core->mac[ICR] & core->mac[IMS];
|
|
|
|
if (msix_enabled(core->owner)) {
|
|
if (icr) {
|
|
causes = 0;
|
|
if (icr & E1000_ICR_DRSTA) {
|
|
int_alloc = core->mac[IVAR_MISC] & 0xff;
|
|
if (int_alloc & E1000_IVAR_VALID) {
|
|
causes |= BIT(int_alloc & 0x1f);
|
|
}
|
|
}
|
|
/* Check if other bits (excluding the TCP Timer) are enabled. */
|
|
if (icr & ~E1000_ICR_DRSTA) {
|
|
int_alloc = (core->mac[IVAR_MISC] >> 8) & 0xff;
|
|
if (int_alloc & E1000_IVAR_VALID) {
|
|
causes |= BIT(int_alloc & 0x1f);
|
|
}
|
|
trace_e1000e_irq_add_msi_other(core->mac[EICR]);
|
|
}
|
|
core->mac[EICR] |= causes;
|
|
}
|
|
|
|
if ((core->mac[EICR] & core->mac[EIMS])) {
|
|
igb_send_msix(core);
|
|
}
|
|
} else {
|
|
igb_fix_icr_asserted(core);
|
|
|
|
if (icr) {
|
|
core->mac[EICR] |= (icr & E1000_ICR_DRSTA) | E1000_EICR_OTHER;
|
|
} else {
|
|
core->mac[EICR] &= ~E1000_EICR_OTHER;
|
|
}
|
|
|
|
trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS],
|
|
core->mac[ICR], core->mac[IMS]);
|
|
|
|
if (msi_enabled(core->owner)) {
|
|
if (icr) {
|
|
msi_notify(core->owner, 0);
|
|
}
|
|
} else {
|
|
if (icr) {
|
|
igb_raise_legacy_irq(core);
|
|
} else {
|
|
igb_lower_legacy_irq(core);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_interrupt_cause(IGBCore *core, uint32_t val)
|
|
{
|
|
trace_e1000e_irq_set_cause_entry(val, core->mac[ICR]);
|
|
|
|
core->mac[ICR] |= val;
|
|
|
|
trace_e1000e_irq_set_cause_exit(val, core->mac[ICR]);
|
|
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
|
|
static void igb_set_eics(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE);
|
|
|
|
trace_igb_irq_write_eics(val, msix);
|
|
|
|
core->mac[EICS] |=
|
|
val & (msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK);
|
|
|
|
/*
|
|
* TODO: Move to igb_update_interrupt_state if EICS is modified in other
|
|
* places.
|
|
*/
|
|
core->mac[EICR] = core->mac[EICS];
|
|
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
|
|
static void igb_set_eims(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE);
|
|
|
|
trace_igb_irq_write_eims(val, msix);
|
|
|
|
core->mac[EIMS] |=
|
|
val & (msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK);
|
|
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
|
|
static void igb_vf_reset(IGBCore *core, uint16_t vfn)
|
|
{
|
|
/* TODO: Reset of the queue enable and the interrupt registers of the VF. */
|
|
|
|
core->mac[V2PMAILBOX0 + vfn] &= ~E1000_V2PMAILBOX_RSTI;
|
|
core->mac[V2PMAILBOX0 + vfn] = E1000_V2PMAILBOX_RSTD;
|
|
}
|
|
|
|
static void mailbox_interrupt_to_vf(IGBCore *core, uint16_t vfn)
|
|
{
|
|
uint32_t ent = core->mac[VTIVAR_MISC + vfn];
|
|
|
|
if ((ent & E1000_IVAR_VALID)) {
|
|
core->mac[EICR] |= (ent & 0x3) << (22 - vfn * IGBVF_MSIX_VEC_NUM);
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
}
|
|
|
|
static void mailbox_interrupt_to_pf(IGBCore *core)
|
|
{
|
|
igb_set_interrupt_cause(core, E1000_ICR_VMMB);
|
|
}
|
|
|
|
static void igb_set_pfmailbox(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = index - P2VMAILBOX0;
|
|
|
|
trace_igb_set_pfmailbox(vfn, val);
|
|
|
|
if (val & E1000_P2VMAILBOX_STS) {
|
|
core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_PFSTS;
|
|
mailbox_interrupt_to_vf(core, vfn);
|
|
}
|
|
|
|
if (val & E1000_P2VMAILBOX_ACK) {
|
|
core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_PFACK;
|
|
mailbox_interrupt_to_vf(core, vfn);
|
|
}
|
|
|
|
/* Buffer Taken by PF (can be set only if the VFU is cleared). */
|
|
if (val & E1000_P2VMAILBOX_PFU) {
|
|
if (!(core->mac[index] & E1000_P2VMAILBOX_VFU)) {
|
|
core->mac[index] |= E1000_P2VMAILBOX_PFU;
|
|
core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_PFU;
|
|
}
|
|
} else {
|
|
core->mac[index] &= ~E1000_P2VMAILBOX_PFU;
|
|
core->mac[V2PMAILBOX0 + vfn] &= ~E1000_V2PMAILBOX_PFU;
|
|
}
|
|
|
|
if (val & E1000_P2VMAILBOX_RVFU) {
|
|
core->mac[V2PMAILBOX0 + vfn] &= ~E1000_V2PMAILBOX_VFU;
|
|
core->mac[MBVFICR] &= ~((E1000_MBVFICR_VFACK_VF1 << vfn) |
|
|
(E1000_MBVFICR_VFREQ_VF1 << vfn));
|
|
}
|
|
}
|
|
|
|
static void igb_set_vfmailbox(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = index - V2PMAILBOX0;
|
|
|
|
trace_igb_set_vfmailbox(vfn, val);
|
|
|
|
if (val & E1000_V2PMAILBOX_REQ) {
|
|
core->mac[MBVFICR] |= E1000_MBVFICR_VFREQ_VF1 << vfn;
|
|
mailbox_interrupt_to_pf(core);
|
|
}
|
|
|
|
if (val & E1000_V2PMAILBOX_ACK) {
|
|
core->mac[MBVFICR] |= E1000_MBVFICR_VFACK_VF1 << vfn;
|
|
mailbox_interrupt_to_pf(core);
|
|
}
|
|
|
|
/* Buffer Taken by VF (can be set only if the PFU is cleared). */
|
|
if (val & E1000_V2PMAILBOX_VFU) {
|
|
if (!(core->mac[index] & E1000_V2PMAILBOX_PFU)) {
|
|
core->mac[index] |= E1000_V2PMAILBOX_VFU;
|
|
core->mac[P2VMAILBOX0 + vfn] |= E1000_P2VMAILBOX_VFU;
|
|
}
|
|
} else {
|
|
core->mac[index] &= ~E1000_V2PMAILBOX_VFU;
|
|
core->mac[P2VMAILBOX0 + vfn] &= ~E1000_P2VMAILBOX_VFU;
|
|
}
|
|
}
|
|
|
|
static void igb_w1c(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[index] &= ~val;
|
|
}
|
|
|
|
static void igb_set_eimc(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE);
|
|
|
|
/* Interrupts are disabled via a write to EIMC and reflected in EIMS. */
|
|
core->mac[EIMS] &=
|
|
~(val & (msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK));
|
|
|
|
trace_igb_irq_write_eimc(val, core->mac[EIMS], msix);
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
|
|
static void igb_set_eiac(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE);
|
|
|
|
if (msix) {
|
|
trace_igb_irq_write_eiac(val);
|
|
|
|
/*
|
|
* TODO: When using IOV, the bits that correspond to MSI-X vectors
|
|
* that are assigned to a VF are read-only.
|
|
*/
|
|
core->mac[EIAC] |= (val & E1000_EICR_MSIX_MASK);
|
|
}
|
|
}
|
|
|
|
static void igb_set_eiam(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE);
|
|
|
|
/*
|
|
* TODO: When using IOV, the bits that correspond to MSI-X vectors that
|
|
* are assigned to a VF are read-only.
|
|
*/
|
|
core->mac[EIAM] |=
|
|
~(val & (msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK));
|
|
|
|
trace_igb_irq_write_eiam(val, msix);
|
|
}
|
|
|
|
static void igb_set_eicr(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE);
|
|
|
|
/*
|
|
* TODO: In IOV mode, only bit zero of this vector is available for the PF
|
|
* function.
|
|
*/
|
|
core->mac[EICR] &=
|
|
~(val & (msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK));
|
|
|
|
trace_igb_irq_write_eicr(val, msix);
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
|
|
static void igb_set_vtctrl(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn;
|
|
|
|
if (val & E1000_CTRL_RST) {
|
|
vfn = (index - PVTCTRL0) / 0x40;
|
|
igb_vf_reset(core, vfn);
|
|
}
|
|
}
|
|
|
|
static void igb_set_vteics(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = (index - PVTEICS0) / 0x40;
|
|
|
|
core->mac[index] = val;
|
|
igb_set_eics(core, EICS, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM));
|
|
}
|
|
|
|
static void igb_set_vteims(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = (index - PVTEIMS0) / 0x40;
|
|
|
|
core->mac[index] = val;
|
|
igb_set_eims(core, EIMS, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM));
|
|
}
|
|
|
|
static void igb_set_vteimc(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = (index - PVTEIMC0) / 0x40;
|
|
|
|
core->mac[index] = val;
|
|
igb_set_eimc(core, EIMC, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM));
|
|
}
|
|
|
|
static void igb_set_vteiac(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = (index - PVTEIAC0) / 0x40;
|
|
|
|
core->mac[index] = val;
|
|
igb_set_eiac(core, EIAC, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM));
|
|
}
|
|
|
|
static void igb_set_vteiam(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = (index - PVTEIAM0) / 0x40;
|
|
|
|
core->mac[index] = val;
|
|
igb_set_eiam(core, EIAM, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM));
|
|
}
|
|
|
|
static void igb_set_vteicr(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = (index - PVTEICR0) / 0x40;
|
|
|
|
core->mac[index] = val;
|
|
igb_set_eicr(core, EICR, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM));
|
|
}
|
|
|
|
static void igb_set_vtivar(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint16_t vfn = (index - VTIVAR);
|
|
uint16_t qn = vfn;
|
|
uint8_t ent;
|
|
int n;
|
|
|
|
core->mac[index] = val;
|
|
|
|
/* Get assigned vector associated with queue Rx#0. */
|
|
if ((val & E1000_IVAR_VALID)) {
|
|
n = igb_ivar_entry_rx(qn);
|
|
ent = E1000_IVAR_VALID | (24 - vfn * IGBVF_MSIX_VEC_NUM - (2 - (val & 0x7)));
|
|
core->mac[IVAR0 + n / 4] |= ent << 8 * (n % 4);
|
|
}
|
|
|
|
/* Get assigned vector associated with queue Tx#0 */
|
|
ent = val >> 8;
|
|
if ((ent & E1000_IVAR_VALID)) {
|
|
n = igb_ivar_entry_tx(qn);
|
|
ent = E1000_IVAR_VALID | (24 - vfn * IGBVF_MSIX_VEC_NUM - (2 - (ent & 0x7)));
|
|
core->mac[IVAR0 + n / 4] |= ent << 8 * (n % 4);
|
|
}
|
|
|
|
/*
|
|
* Ignoring assigned vectors associated with queues Rx#1 and Tx#1 for now.
|
|
*/
|
|
}
|
|
|
|
static inline void
|
|
igb_autoneg_timer(void *opaque)
|
|
{
|
|
IGBCore *core = opaque;
|
|
if (!qemu_get_queue(core->owner_nic)->link_down) {
|
|
e1000x_update_regs_on_autoneg_done(core->mac, core->phy);
|
|
igb_start_recv(core);
|
|
|
|
igb_update_flowctl_status(core);
|
|
/* signal link status change to the guest */
|
|
igb_set_interrupt_cause(core, E1000_ICR_LSC);
|
|
}
|
|
}
|
|
|
|
static inline uint16_t
|
|
igb_get_reg_index_with_offset(const uint16_t *mac_reg_access, hwaddr addr)
|
|
{
|
|
uint16_t index = (addr & 0x1ffff) >> 2;
|
|
return index + (mac_reg_access[index] & 0xfffe);
|
|
}
|
|
|
|
static const char igb_phy_regcap[MAX_PHY_REG_ADDRESS + 1] = {
|
|
[MII_BMCR] = PHY_RW,
|
|
[MII_BMSR] = PHY_R,
|
|
[MII_PHYID1] = PHY_R,
|
|
[MII_PHYID2] = PHY_R,
|
|
[MII_ANAR] = PHY_RW,
|
|
[MII_ANLPAR] = PHY_R,
|
|
[MII_ANER] = PHY_R,
|
|
[MII_ANNP] = PHY_RW,
|
|
[MII_ANLPRNP] = PHY_R,
|
|
[MII_CTRL1000] = PHY_RW,
|
|
[MII_STAT1000] = PHY_R,
|
|
[MII_EXTSTAT] = PHY_R,
|
|
|
|
[IGP01E1000_PHY_PORT_CONFIG] = PHY_RW,
|
|
[IGP01E1000_PHY_PORT_STATUS] = PHY_R,
|
|
[IGP01E1000_PHY_PORT_CTRL] = PHY_RW,
|
|
[IGP01E1000_PHY_LINK_HEALTH] = PHY_R,
|
|
[IGP02E1000_PHY_POWER_MGMT] = PHY_RW,
|
|
[IGP01E1000_PHY_PAGE_SELECT] = PHY_W
|
|
};
|
|
|
|
static void
|
|
igb_phy_reg_write(IGBCore *core, uint32_t addr, uint16_t data)
|
|
{
|
|
assert(addr <= MAX_PHY_REG_ADDRESS);
|
|
|
|
if (addr == MII_BMCR) {
|
|
igb_set_phy_ctrl(core, data);
|
|
} else {
|
|
core->phy[addr] = data;
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_mdic(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint32_t data = val & E1000_MDIC_DATA_MASK;
|
|
uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
|
|
|
|
if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) { /* phy # */
|
|
val = core->mac[MDIC] | E1000_MDIC_ERROR;
|
|
} else if (val & E1000_MDIC_OP_READ) {
|
|
if (!(igb_phy_regcap[addr] & PHY_R)) {
|
|
trace_igb_core_mdic_read_unhandled(addr);
|
|
val |= E1000_MDIC_ERROR;
|
|
} else {
|
|
val = (val ^ data) | core->phy[addr];
|
|
trace_igb_core_mdic_read(addr, val);
|
|
}
|
|
} else if (val & E1000_MDIC_OP_WRITE) {
|
|
if (!(igb_phy_regcap[addr] & PHY_W)) {
|
|
trace_igb_core_mdic_write_unhandled(addr);
|
|
val |= E1000_MDIC_ERROR;
|
|
} else {
|
|
trace_igb_core_mdic_write(addr, data);
|
|
igb_phy_reg_write(core, addr, data);
|
|
}
|
|
}
|
|
core->mac[MDIC] = val | E1000_MDIC_READY;
|
|
|
|
if (val & E1000_MDIC_INT_EN) {
|
|
igb_set_interrupt_cause(core, E1000_ICR_MDAC);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_rdt(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[index] = val & 0xffff;
|
|
trace_e1000e_rx_set_rdt(igb_mq_queue_idx(RDT0, index), val);
|
|
igb_start_recv(core);
|
|
}
|
|
|
|
static void
|
|
igb_set_status(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
if ((val & E1000_STATUS_PHYRA) == 0) {
|
|
core->mac[index] &= ~E1000_STATUS_PHYRA;
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_ctrlext(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
trace_e1000e_link_set_ext_params(!!(val & E1000_CTRL_EXT_ASDCHK),
|
|
!!(val & E1000_CTRL_EXT_SPD_BYPS));
|
|
|
|
/* TODO: PFRSTD */
|
|
|
|
/* Zero self-clearing bits */
|
|
val &= ~(E1000_CTRL_EXT_ASDCHK | E1000_CTRL_EXT_EE_RST);
|
|
core->mac[CTRL_EXT] = val;
|
|
}
|
|
|
|
static void
|
|
igb_set_pbaclr(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
int i;
|
|
|
|
core->mac[PBACLR] = val & E1000_PBACLR_VALID_MASK;
|
|
|
|
if (!msix_enabled(core->owner)) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < IGB_INTR_NUM; i++) {
|
|
if (core->mac[PBACLR] & BIT(i)) {
|
|
msix_clr_pending(core->owner, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_fcrth(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[FCRTH] = val & 0xFFF8;
|
|
}
|
|
|
|
static void
|
|
igb_set_fcrtl(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[FCRTL] = val & 0x8000FFF8;
|
|
}
|
|
|
|
#define IGB_LOW_BITS_SET_FUNC(num) \
|
|
static void \
|
|
igb_set_##num##bit(IGBCore *core, int index, uint32_t val) \
|
|
{ \
|
|
core->mac[index] = val & (BIT(num) - 1); \
|
|
}
|
|
|
|
IGB_LOW_BITS_SET_FUNC(4)
|
|
IGB_LOW_BITS_SET_FUNC(13)
|
|
IGB_LOW_BITS_SET_FUNC(16)
|
|
|
|
static void
|
|
igb_set_dlen(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[index] = val & 0xffff0;
|
|
}
|
|
|
|
static void
|
|
igb_set_dbal(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[index] = val & E1000_XDBAL_MASK;
|
|
}
|
|
|
|
static void
|
|
igb_set_tdt(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
IGB_TxRing txr;
|
|
int qn = igb_mq_queue_idx(TDT0, index);
|
|
|
|
core->mac[index] = val & 0xffff;
|
|
|
|
igb_tx_ring_init(core, &txr, qn);
|
|
igb_start_xmit(core, &txr);
|
|
}
|
|
|
|
static void
|
|
igb_set_ics(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
trace_e1000e_irq_write_ics(val);
|
|
igb_set_interrupt_cause(core, val);
|
|
}
|
|
|
|
static void
|
|
igb_set_imc(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
trace_e1000e_irq_ims_clear_set_imc(val);
|
|
igb_clear_ims_bits(core, val);
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
|
|
static void
|
|
igb_set_ims(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint32_t valid_val = val & 0x77D4FBFD;
|
|
|
|
trace_e1000e_irq_set_ims(val, core->mac[IMS], core->mac[IMS] | valid_val);
|
|
core->mac[IMS] |= valid_val;
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
|
|
static void igb_commit_icr(IGBCore *core)
|
|
{
|
|
/*
|
|
* If GPIE.NSICR = 0, then the copy of IAM to IMS will occur only if at
|
|
* least one bit is set in the IMS and there is a true interrupt as
|
|
* reflected in ICR.INTA.
|
|
*/
|
|
if ((core->mac[GPIE] & E1000_GPIE_NSICR) ||
|
|
(core->mac[IMS] && (core->mac[ICR] & E1000_ICR_INT_ASSERTED))) {
|
|
igb_set_ims(core, IMS, core->mac[IAM]);
|
|
} else {
|
|
igb_update_interrupt_state(core);
|
|
}
|
|
}
|
|
|
|
static void igb_set_icr(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint32_t icr = core->mac[ICR] & ~val;
|
|
|
|
trace_igb_irq_icr_write(val, core->mac[ICR], icr);
|
|
core->mac[ICR] = icr;
|
|
igb_commit_icr(core);
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_readreg(IGBCore *core, int index)
|
|
{
|
|
return core->mac[index];
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_ics_read(IGBCore *core, int index)
|
|
{
|
|
trace_e1000e_irq_read_ics(core->mac[ICS]);
|
|
return core->mac[ICS];
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_ims_read(IGBCore *core, int index)
|
|
{
|
|
trace_e1000e_irq_read_ims(core->mac[IMS]);
|
|
return core->mac[IMS];
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_swsm_read(IGBCore *core, int index)
|
|
{
|
|
uint32_t val = core->mac[SWSM];
|
|
core->mac[SWSM] = val | E1000_SWSM_SMBI;
|
|
return val;
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_eitr_read(IGBCore *core, int index)
|
|
{
|
|
return core->eitr_guest_value[index - EITR0];
|
|
}
|
|
|
|
static uint32_t igb_mac_vfmailbox_read(IGBCore *core, int index)
|
|
{
|
|
uint32_t val = core->mac[index];
|
|
|
|
core->mac[index] &= ~(E1000_V2PMAILBOX_PFSTS | E1000_V2PMAILBOX_PFACK |
|
|
E1000_V2PMAILBOX_RSTD);
|
|
|
|
return val;
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_icr_read(IGBCore *core, int index)
|
|
{
|
|
uint32_t ret = core->mac[ICR];
|
|
trace_e1000e_irq_icr_read_entry(ret);
|
|
|
|
if (core->mac[GPIE] & E1000_GPIE_NSICR) {
|
|
trace_igb_irq_icr_clear_gpie_nsicr();
|
|
core->mac[ICR] = 0;
|
|
} else if (core->mac[IMS] == 0) {
|
|
trace_e1000e_irq_icr_clear_zero_ims();
|
|
core->mac[ICR] = 0;
|
|
} else if (!msix_enabled(core->owner)) {
|
|
trace_e1000e_irq_icr_clear_nonmsix_icr_read();
|
|
core->mac[ICR] = 0;
|
|
}
|
|
|
|
trace_e1000e_irq_icr_read_exit(core->mac[ICR]);
|
|
igb_commit_icr(core);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_read_clr4(IGBCore *core, int index)
|
|
{
|
|
uint32_t ret = core->mac[index];
|
|
|
|
core->mac[index] = 0;
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t
|
|
igb_mac_read_clr8(IGBCore *core, int index)
|
|
{
|
|
uint32_t ret = core->mac[index];
|
|
|
|
core->mac[index] = 0;
|
|
core->mac[index - 1] = 0;
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t
|
|
igb_get_ctrl(IGBCore *core, int index)
|
|
{
|
|
uint32_t val = core->mac[CTRL];
|
|
|
|
trace_e1000e_link_read_params(
|
|
!!(val & E1000_CTRL_ASDE),
|
|
(val & E1000_CTRL_SPD_SEL) >> E1000_CTRL_SPD_SHIFT,
|
|
!!(val & E1000_CTRL_FRCSPD),
|
|
!!(val & E1000_CTRL_FRCDPX),
|
|
!!(val & E1000_CTRL_RFCE),
|
|
!!(val & E1000_CTRL_TFCE));
|
|
|
|
return val;
|
|
}
|
|
|
|
static uint32_t igb_get_status(IGBCore *core, int index)
|
|
{
|
|
uint32_t res = core->mac[STATUS];
|
|
uint16_t num_vfs = pcie_sriov_num_vfs(core->owner);
|
|
|
|
if (core->mac[CTRL] & E1000_CTRL_FRCDPX) {
|
|
res |= (core->mac[CTRL] & E1000_CTRL_FD) ? E1000_STATUS_FD : 0;
|
|
} else {
|
|
res |= E1000_STATUS_FD;
|
|
}
|
|
|
|
if ((core->mac[CTRL] & E1000_CTRL_FRCSPD) ||
|
|
(core->mac[CTRL_EXT] & E1000_CTRL_EXT_SPD_BYPS)) {
|
|
switch (core->mac[CTRL] & E1000_CTRL_SPD_SEL) {
|
|
case E1000_CTRL_SPD_10:
|
|
res |= E1000_STATUS_SPEED_10;
|
|
break;
|
|
case E1000_CTRL_SPD_100:
|
|
res |= E1000_STATUS_SPEED_100;
|
|
break;
|
|
case E1000_CTRL_SPD_1000:
|
|
default:
|
|
res |= E1000_STATUS_SPEED_1000;
|
|
break;
|
|
}
|
|
} else {
|
|
res |= E1000_STATUS_SPEED_1000;
|
|
}
|
|
|
|
if (num_vfs) {
|
|
res |= num_vfs << E1000_STATUS_NUM_VFS_SHIFT;
|
|
res |= E1000_STATUS_IOV_MODE;
|
|
}
|
|
|
|
/*
|
|
* Windows driver 12.18.9.23 resets if E1000_STATUS_GIO_MASTER_ENABLE is
|
|
* left set after E1000_CTRL_LRST is set.
|
|
*/
|
|
if (!(core->mac[CTRL] & E1000_CTRL_GIO_MASTER_DISABLE) &&
|
|
!(core->mac[CTRL] & E1000_CTRL_LRST)) {
|
|
res |= E1000_STATUS_GIO_MASTER_ENABLE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
igb_mac_writereg(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[index] = val;
|
|
}
|
|
|
|
static void
|
|
igb_mac_setmacaddr(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint32_t macaddr[2];
|
|
|
|
core->mac[index] = val;
|
|
|
|
macaddr[0] = cpu_to_le32(core->mac[RA]);
|
|
macaddr[1] = cpu_to_le32(core->mac[RA + 1]);
|
|
qemu_format_nic_info_str(qemu_get_queue(core->owner_nic),
|
|
(uint8_t *) macaddr);
|
|
|
|
trace_e1000e_mac_set_sw(MAC_ARG(macaddr));
|
|
}
|
|
|
|
static void
|
|
igb_set_eecd(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
static const uint32_t ro_bits = E1000_EECD_PRES |
|
|
E1000_EECD_AUTO_RD |
|
|
E1000_EECD_SIZE_EX_MASK;
|
|
|
|
core->mac[EECD] = (core->mac[EECD] & ro_bits) | (val & ~ro_bits);
|
|
}
|
|
|
|
static void
|
|
igb_set_eerd(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint32_t addr = (val >> E1000_EERW_ADDR_SHIFT) & E1000_EERW_ADDR_MASK;
|
|
uint32_t flags = 0;
|
|
uint32_t data = 0;
|
|
|
|
if ((addr < IGB_EEPROM_SIZE) && (val & E1000_EERW_START)) {
|
|
data = core->eeprom[addr];
|
|
flags = E1000_EERW_DONE;
|
|
}
|
|
|
|
core->mac[EERD] = flags |
|
|
(addr << E1000_EERW_ADDR_SHIFT) |
|
|
(data << E1000_EERW_DATA_SHIFT);
|
|
}
|
|
|
|
static void
|
|
igb_set_eitr(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint32_t eitr_num = index - EITR0;
|
|
|
|
trace_igb_irq_eitr_set(eitr_num, val);
|
|
|
|
core->eitr_guest_value[eitr_num] = val & ~E1000_EITR_CNT_IGNR;
|
|
core->mac[index] = val & 0x7FFE;
|
|
}
|
|
|
|
static void
|
|
igb_update_rx_offloads(IGBCore *core)
|
|
{
|
|
int cso_state = igb_rx_l4_cso_enabled(core);
|
|
|
|
trace_e1000e_rx_set_cso(cso_state);
|
|
|
|
if (core->has_vnet) {
|
|
qemu_set_offload(qemu_get_queue(core->owner_nic)->peer,
|
|
cso_state, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_set_rxcsum(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[RXCSUM] = val;
|
|
igb_update_rx_offloads(core);
|
|
}
|
|
|
|
static void
|
|
igb_set_gcr(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
uint32_t ro_bits = core->mac[GCR] & E1000_GCR_RO_BITS;
|
|
core->mac[GCR] = (val & ~E1000_GCR_RO_BITS) | ro_bits;
|
|
}
|
|
|
|
static uint32_t igb_get_systiml(IGBCore *core, int index)
|
|
{
|
|
e1000x_timestamp(core->mac, core->timadj, SYSTIML, SYSTIMH);
|
|
return core->mac[SYSTIML];
|
|
}
|
|
|
|
static uint32_t igb_get_rxsatrh(IGBCore *core, int index)
|
|
{
|
|
core->mac[TSYNCRXCTL] &= ~E1000_TSYNCRXCTL_VALID;
|
|
return core->mac[RXSATRH];
|
|
}
|
|
|
|
static uint32_t igb_get_txstmph(IGBCore *core, int index)
|
|
{
|
|
core->mac[TSYNCTXCTL] &= ~E1000_TSYNCTXCTL_VALID;
|
|
return core->mac[TXSTMPH];
|
|
}
|
|
|
|
static void igb_set_timinca(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
e1000x_set_timinca(core->mac, &core->timadj, val);
|
|
}
|
|
|
|
static void igb_set_timadjh(IGBCore *core, int index, uint32_t val)
|
|
{
|
|
core->mac[TIMADJH] = val;
|
|
core->timadj += core->mac[TIMADJL] | ((int64_t)core->mac[TIMADJH] << 32);
|
|
}
|
|
|
|
#define igb_getreg(x) [x] = igb_mac_readreg
|
|
typedef uint32_t (*readops)(IGBCore *, int);
|
|
static const readops igb_macreg_readops[] = {
|
|
igb_getreg(WUFC),
|
|
igb_getreg(MANC),
|
|
igb_getreg(TOTL),
|
|
igb_getreg(RDT0),
|
|
igb_getreg(RDT1),
|
|
igb_getreg(RDT2),
|
|
igb_getreg(RDT3),
|
|
igb_getreg(RDT4),
|
|
igb_getreg(RDT5),
|
|
igb_getreg(RDT6),
|
|
igb_getreg(RDT7),
|
|
igb_getreg(RDT8),
|
|
igb_getreg(RDT9),
|
|
igb_getreg(RDT10),
|
|
igb_getreg(RDT11),
|
|
igb_getreg(RDT12),
|
|
igb_getreg(RDT13),
|
|
igb_getreg(RDT14),
|
|
igb_getreg(RDT15),
|
|
igb_getreg(RDBAH0),
|
|
igb_getreg(RDBAH1),
|
|
igb_getreg(RDBAH2),
|
|
igb_getreg(RDBAH3),
|
|
igb_getreg(RDBAH4),
|
|
igb_getreg(RDBAH5),
|
|
igb_getreg(RDBAH6),
|
|
igb_getreg(RDBAH7),
|
|
igb_getreg(RDBAH8),
|
|
igb_getreg(RDBAH9),
|
|
igb_getreg(RDBAH10),
|
|
igb_getreg(RDBAH11),
|
|
igb_getreg(RDBAH12),
|
|
igb_getreg(RDBAH13),
|
|
igb_getreg(RDBAH14),
|
|
igb_getreg(RDBAH15),
|
|
igb_getreg(TDBAL0),
|
|
igb_getreg(TDBAL1),
|
|
igb_getreg(TDBAL2),
|
|
igb_getreg(TDBAL3),
|
|
igb_getreg(TDBAL4),
|
|
igb_getreg(TDBAL5),
|
|
igb_getreg(TDBAL6),
|
|
igb_getreg(TDBAL7),
|
|
igb_getreg(TDBAL8),
|
|
igb_getreg(TDBAL9),
|
|
igb_getreg(TDBAL10),
|
|
igb_getreg(TDBAL11),
|
|
igb_getreg(TDBAL12),
|
|
igb_getreg(TDBAL13),
|
|
igb_getreg(TDBAL14),
|
|
igb_getreg(TDBAL15),
|
|
igb_getreg(RDLEN0),
|
|
igb_getreg(RDLEN1),
|
|
igb_getreg(RDLEN2),
|
|
igb_getreg(RDLEN3),
|
|
igb_getreg(RDLEN4),
|
|
igb_getreg(RDLEN5),
|
|
igb_getreg(RDLEN6),
|
|
igb_getreg(RDLEN7),
|
|
igb_getreg(RDLEN8),
|
|
igb_getreg(RDLEN9),
|
|
igb_getreg(RDLEN10),
|
|
igb_getreg(RDLEN11),
|
|
igb_getreg(RDLEN12),
|
|
igb_getreg(RDLEN13),
|
|
igb_getreg(RDLEN14),
|
|
igb_getreg(RDLEN15),
|
|
igb_getreg(SRRCTL0),
|
|
igb_getreg(SRRCTL1),
|
|
igb_getreg(SRRCTL2),
|
|
igb_getreg(SRRCTL3),
|
|
igb_getreg(SRRCTL4),
|
|
igb_getreg(SRRCTL5),
|
|
igb_getreg(SRRCTL6),
|
|
igb_getreg(SRRCTL7),
|
|
igb_getreg(SRRCTL8),
|
|
igb_getreg(SRRCTL9),
|
|
igb_getreg(SRRCTL10),
|
|
igb_getreg(SRRCTL11),
|
|
igb_getreg(SRRCTL12),
|
|
igb_getreg(SRRCTL13),
|
|
igb_getreg(SRRCTL14),
|
|
igb_getreg(SRRCTL15),
|
|
igb_getreg(LATECOL),
|
|
igb_getreg(XONTXC),
|
|
igb_getreg(TDFH),
|
|
igb_getreg(TDFT),
|
|
igb_getreg(TDFHS),
|
|
igb_getreg(TDFTS),
|
|
igb_getreg(TDFPC),
|
|
igb_getreg(WUS),
|
|
igb_getreg(RDFH),
|
|
igb_getreg(RDFT),
|
|
igb_getreg(RDFHS),
|
|
igb_getreg(RDFTS),
|
|
igb_getreg(RDFPC),
|
|
igb_getreg(GORCL),
|
|
igb_getreg(MGTPRC),
|
|
igb_getreg(EERD),
|
|
igb_getreg(EIAC),
|
|
igb_getreg(MANC2H),
|
|
igb_getreg(RXCSUM),
|
|
igb_getreg(GSCL_3),
|
|
igb_getreg(GSCN_2),
|
|
igb_getreg(FCAH),
|
|
igb_getreg(FCRTH),
|
|
igb_getreg(FLOP),
|
|
igb_getreg(RXSTMPH),
|
|
igb_getreg(TXSTMPL),
|
|
igb_getreg(TIMADJL),
|
|
igb_getreg(RDH0),
|
|
igb_getreg(RDH1),
|
|
igb_getreg(RDH2),
|
|
igb_getreg(RDH3),
|
|
igb_getreg(RDH4),
|
|
igb_getreg(RDH5),
|
|
igb_getreg(RDH6),
|
|
igb_getreg(RDH7),
|
|
igb_getreg(RDH8),
|
|
igb_getreg(RDH9),
|
|
igb_getreg(RDH10),
|
|
igb_getreg(RDH11),
|
|
igb_getreg(RDH12),
|
|
igb_getreg(RDH13),
|
|
igb_getreg(RDH14),
|
|
igb_getreg(RDH15),
|
|
igb_getreg(TDT0),
|
|
igb_getreg(TDT1),
|
|
igb_getreg(TDT2),
|
|
igb_getreg(TDT3),
|
|
igb_getreg(TDT4),
|
|
igb_getreg(TDT5),
|
|
igb_getreg(TDT6),
|
|
igb_getreg(TDT7),
|
|
igb_getreg(TDT8),
|
|
igb_getreg(TDT9),
|
|
igb_getreg(TDT10),
|
|
igb_getreg(TDT11),
|
|
igb_getreg(TDT12),
|
|
igb_getreg(TDT13),
|
|
igb_getreg(TDT14),
|
|
igb_getreg(TDT15),
|
|
igb_getreg(TNCRS),
|
|
igb_getreg(RJC),
|
|
igb_getreg(IAM),
|
|
igb_getreg(GSCL_2),
|
|
igb_getreg(TIPG),
|
|
igb_getreg(FLMNGCTL),
|
|
igb_getreg(FLMNGCNT),
|
|
igb_getreg(TSYNCTXCTL),
|
|
igb_getreg(EEMNGDATA),
|
|
igb_getreg(CTRL_EXT),
|
|
igb_getreg(SYSTIMH),
|
|
igb_getreg(EEMNGCTL),
|
|
igb_getreg(FLMNGDATA),
|
|
igb_getreg(TSYNCRXCTL),
|
|
igb_getreg(LEDCTL),
|
|
igb_getreg(TCTL),
|
|
igb_getreg(TCTL_EXT),
|
|
igb_getreg(DTXCTL),
|
|
igb_getreg(RXPBS),
|
|
igb_getreg(TDH0),
|
|
igb_getreg(TDH1),
|
|
igb_getreg(TDH2),
|
|
igb_getreg(TDH3),
|
|
igb_getreg(TDH4),
|
|
igb_getreg(TDH5),
|
|
igb_getreg(TDH6),
|
|
igb_getreg(TDH7),
|
|
igb_getreg(TDH8),
|
|
igb_getreg(TDH9),
|
|
igb_getreg(TDH10),
|
|
igb_getreg(TDH11),
|
|
igb_getreg(TDH12),
|
|
igb_getreg(TDH13),
|
|
igb_getreg(TDH14),
|
|
igb_getreg(TDH15),
|
|
igb_getreg(ECOL),
|
|
igb_getreg(DC),
|
|
igb_getreg(RLEC),
|
|
igb_getreg(XOFFTXC),
|
|
igb_getreg(RFC),
|
|
igb_getreg(RNBC),
|
|
igb_getreg(MGTPTC),
|
|
igb_getreg(TIMINCA),
|
|
igb_getreg(FACTPS),
|
|
igb_getreg(GSCL_1),
|
|
igb_getreg(GSCN_0),
|
|
igb_getreg(PBACLR),
|
|
igb_getreg(FCTTV),
|
|
igb_getreg(RXSATRL),
|
|
igb_getreg(TORL),
|
|
igb_getreg(TDLEN0),
|
|
igb_getreg(TDLEN1),
|
|
igb_getreg(TDLEN2),
|
|
igb_getreg(TDLEN3),
|
|
igb_getreg(TDLEN4),
|
|
igb_getreg(TDLEN5),
|
|
igb_getreg(TDLEN6),
|
|
igb_getreg(TDLEN7),
|
|
igb_getreg(TDLEN8),
|
|
igb_getreg(TDLEN9),
|
|
igb_getreg(TDLEN10),
|
|
igb_getreg(TDLEN11),
|
|
igb_getreg(TDLEN12),
|
|
igb_getreg(TDLEN13),
|
|
igb_getreg(TDLEN14),
|
|
igb_getreg(TDLEN15),
|
|
igb_getreg(MCC),
|
|
igb_getreg(WUC),
|
|
igb_getreg(EECD),
|
|
igb_getreg(FCRTV),
|
|
igb_getreg(TXDCTL0),
|
|
igb_getreg(TXDCTL1),
|
|
igb_getreg(TXDCTL2),
|
|
igb_getreg(TXDCTL3),
|
|
igb_getreg(TXDCTL4),
|
|
igb_getreg(TXDCTL5),
|
|
igb_getreg(TXDCTL6),
|
|
igb_getreg(TXDCTL7),
|
|
igb_getreg(TXDCTL8),
|
|
igb_getreg(TXDCTL9),
|
|
igb_getreg(TXDCTL10),
|
|
igb_getreg(TXDCTL11),
|
|
igb_getreg(TXDCTL12),
|
|
igb_getreg(TXDCTL13),
|
|
igb_getreg(TXDCTL14),
|
|
igb_getreg(TXDCTL15),
|
|
igb_getreg(TXCTL0),
|
|
igb_getreg(TXCTL1),
|
|
igb_getreg(TXCTL2),
|
|
igb_getreg(TXCTL3),
|
|
igb_getreg(TXCTL4),
|
|
igb_getreg(TXCTL5),
|
|
igb_getreg(TXCTL6),
|
|
igb_getreg(TXCTL7),
|
|
igb_getreg(TXCTL8),
|
|
igb_getreg(TXCTL9),
|
|
igb_getreg(TXCTL10),
|
|
igb_getreg(TXCTL11),
|
|
igb_getreg(TXCTL12),
|
|
igb_getreg(TXCTL13),
|
|
igb_getreg(TXCTL14),
|
|
igb_getreg(TXCTL15),
|
|
igb_getreg(TDWBAL0),
|
|
igb_getreg(TDWBAL1),
|
|
igb_getreg(TDWBAL2),
|
|
igb_getreg(TDWBAL3),
|
|
igb_getreg(TDWBAL4),
|
|
igb_getreg(TDWBAL5),
|
|
igb_getreg(TDWBAL6),
|
|
igb_getreg(TDWBAL7),
|
|
igb_getreg(TDWBAL8),
|
|
igb_getreg(TDWBAL9),
|
|
igb_getreg(TDWBAL10),
|
|
igb_getreg(TDWBAL11),
|
|
igb_getreg(TDWBAL12),
|
|
igb_getreg(TDWBAL13),
|
|
igb_getreg(TDWBAL14),
|
|
igb_getreg(TDWBAL15),
|
|
igb_getreg(TDWBAH0),
|
|
igb_getreg(TDWBAH1),
|
|
igb_getreg(TDWBAH2),
|
|
igb_getreg(TDWBAH3),
|
|
igb_getreg(TDWBAH4),
|
|
igb_getreg(TDWBAH5),
|
|
igb_getreg(TDWBAH6),
|
|
igb_getreg(TDWBAH7),
|
|
igb_getreg(TDWBAH8),
|
|
igb_getreg(TDWBAH9),
|
|
igb_getreg(TDWBAH10),
|
|
igb_getreg(TDWBAH11),
|
|
igb_getreg(TDWBAH12),
|
|
igb_getreg(TDWBAH13),
|
|
igb_getreg(TDWBAH14),
|
|
igb_getreg(TDWBAH15),
|
|
igb_getreg(PVTCTRL0),
|
|
igb_getreg(PVTCTRL1),
|
|
igb_getreg(PVTCTRL2),
|
|
igb_getreg(PVTCTRL3),
|
|
igb_getreg(PVTCTRL4),
|
|
igb_getreg(PVTCTRL5),
|
|
igb_getreg(PVTCTRL6),
|
|
igb_getreg(PVTCTRL7),
|
|
igb_getreg(PVTEIMS0),
|
|
igb_getreg(PVTEIMS1),
|
|
igb_getreg(PVTEIMS2),
|
|
igb_getreg(PVTEIMS3),
|
|
igb_getreg(PVTEIMS4),
|
|
igb_getreg(PVTEIMS5),
|
|
igb_getreg(PVTEIMS6),
|
|
igb_getreg(PVTEIMS7),
|
|
igb_getreg(PVTEIAC0),
|
|
igb_getreg(PVTEIAC1),
|
|
igb_getreg(PVTEIAC2),
|
|
igb_getreg(PVTEIAC3),
|
|
igb_getreg(PVTEIAC4),
|
|
igb_getreg(PVTEIAC5),
|
|
igb_getreg(PVTEIAC6),
|
|
igb_getreg(PVTEIAC7),
|
|
igb_getreg(PVTEIAM0),
|
|
igb_getreg(PVTEIAM1),
|
|
igb_getreg(PVTEIAM2),
|
|
igb_getreg(PVTEIAM3),
|
|
igb_getreg(PVTEIAM4),
|
|
igb_getreg(PVTEIAM5),
|
|
igb_getreg(PVTEIAM6),
|
|
igb_getreg(PVTEIAM7),
|
|
igb_getreg(PVFGPRC0),
|
|
igb_getreg(PVFGPRC1),
|
|
igb_getreg(PVFGPRC2),
|
|
igb_getreg(PVFGPRC3),
|
|
igb_getreg(PVFGPRC4),
|
|
igb_getreg(PVFGPRC5),
|
|
igb_getreg(PVFGPRC6),
|
|
igb_getreg(PVFGPRC7),
|
|
igb_getreg(PVFGPTC0),
|
|
igb_getreg(PVFGPTC1),
|
|
igb_getreg(PVFGPTC2),
|
|
igb_getreg(PVFGPTC3),
|
|
igb_getreg(PVFGPTC4),
|
|
igb_getreg(PVFGPTC5),
|
|
igb_getreg(PVFGPTC6),
|
|
igb_getreg(PVFGPTC7),
|
|
igb_getreg(PVFGORC0),
|
|
igb_getreg(PVFGORC1),
|
|
igb_getreg(PVFGORC2),
|
|
igb_getreg(PVFGORC3),
|
|
igb_getreg(PVFGORC4),
|
|
igb_getreg(PVFGORC5),
|
|
igb_getreg(PVFGORC6),
|
|
igb_getreg(PVFGORC7),
|
|
igb_getreg(PVFGOTC0),
|
|
igb_getreg(PVFGOTC1),
|
|
igb_getreg(PVFGOTC2),
|
|
igb_getreg(PVFGOTC3),
|
|
igb_getreg(PVFGOTC4),
|
|
igb_getreg(PVFGOTC5),
|
|
igb_getreg(PVFGOTC6),
|
|
igb_getreg(PVFGOTC7),
|
|
igb_getreg(PVFMPRC0),
|
|
igb_getreg(PVFMPRC1),
|
|
igb_getreg(PVFMPRC2),
|
|
igb_getreg(PVFMPRC3),
|
|
igb_getreg(PVFMPRC4),
|
|
igb_getreg(PVFMPRC5),
|
|
igb_getreg(PVFMPRC6),
|
|
igb_getreg(PVFMPRC7),
|
|
igb_getreg(PVFGPRLBC0),
|
|
igb_getreg(PVFGPRLBC1),
|
|
igb_getreg(PVFGPRLBC2),
|
|
igb_getreg(PVFGPRLBC3),
|
|
igb_getreg(PVFGPRLBC4),
|
|
igb_getreg(PVFGPRLBC5),
|
|
igb_getreg(PVFGPRLBC6),
|
|
igb_getreg(PVFGPRLBC7),
|
|
igb_getreg(PVFGPTLBC0),
|
|
igb_getreg(PVFGPTLBC1),
|
|
igb_getreg(PVFGPTLBC2),
|
|
igb_getreg(PVFGPTLBC3),
|
|
igb_getreg(PVFGPTLBC4),
|
|
igb_getreg(PVFGPTLBC5),
|
|
igb_getreg(PVFGPTLBC6),
|
|
igb_getreg(PVFGPTLBC7),
|
|
igb_getreg(PVFGORLBC0),
|
|
igb_getreg(PVFGORLBC1),
|
|
igb_getreg(PVFGORLBC2),
|
|
igb_getreg(PVFGORLBC3),
|
|
igb_getreg(PVFGORLBC4),
|
|
igb_getreg(PVFGORLBC5),
|
|
igb_getreg(PVFGORLBC6),
|
|
igb_getreg(PVFGORLBC7),
|
|
igb_getreg(PVFGOTLBC0),
|
|
igb_getreg(PVFGOTLBC1),
|
|
igb_getreg(PVFGOTLBC2),
|
|
igb_getreg(PVFGOTLBC3),
|
|
igb_getreg(PVFGOTLBC4),
|
|
igb_getreg(PVFGOTLBC5),
|
|
igb_getreg(PVFGOTLBC6),
|
|
igb_getreg(PVFGOTLBC7),
|
|
igb_getreg(RCTL),
|
|
igb_getreg(MDIC),
|
|
igb_getreg(FCRUC),
|
|
igb_getreg(VET),
|
|
igb_getreg(RDBAL0),
|
|
igb_getreg(RDBAL1),
|
|
igb_getreg(RDBAL2),
|
|
igb_getreg(RDBAL3),
|
|
igb_getreg(RDBAL4),
|
|
igb_getreg(RDBAL5),
|
|
igb_getreg(RDBAL6),
|
|
igb_getreg(RDBAL7),
|
|
igb_getreg(RDBAL8),
|
|
igb_getreg(RDBAL9),
|
|
igb_getreg(RDBAL10),
|
|
igb_getreg(RDBAL11),
|
|
igb_getreg(RDBAL12),
|
|
igb_getreg(RDBAL13),
|
|
igb_getreg(RDBAL14),
|
|
igb_getreg(RDBAL15),
|
|
igb_getreg(TDBAH0),
|
|
igb_getreg(TDBAH1),
|
|
igb_getreg(TDBAH2),
|
|
igb_getreg(TDBAH3),
|
|
igb_getreg(TDBAH4),
|
|
igb_getreg(TDBAH5),
|
|
igb_getreg(TDBAH6),
|
|
igb_getreg(TDBAH7),
|
|
igb_getreg(TDBAH8),
|
|
igb_getreg(TDBAH9),
|
|
igb_getreg(TDBAH10),
|
|
igb_getreg(TDBAH11),
|
|
igb_getreg(TDBAH12),
|
|
igb_getreg(TDBAH13),
|
|
igb_getreg(TDBAH14),
|
|
igb_getreg(TDBAH15),
|
|
igb_getreg(SCC),
|
|
igb_getreg(COLC),
|
|
igb_getreg(XOFFRXC),
|
|
igb_getreg(IPAV),
|
|
igb_getreg(GOTCL),
|
|
igb_getreg(MGTPDC),
|
|
igb_getreg(GCR),
|
|
igb_getreg(MFVAL),
|
|
igb_getreg(FUNCTAG),
|
|
igb_getreg(GSCL_4),
|
|
igb_getreg(GSCN_3),
|
|
igb_getreg(MRQC),
|
|
igb_getreg(FCT),
|
|
igb_getreg(FLA),
|
|
igb_getreg(RXDCTL0),
|
|
igb_getreg(RXDCTL1),
|
|
igb_getreg(RXDCTL2),
|
|
igb_getreg(RXDCTL3),
|
|
igb_getreg(RXDCTL4),
|
|
igb_getreg(RXDCTL5),
|
|
igb_getreg(RXDCTL6),
|
|
igb_getreg(RXDCTL7),
|
|
igb_getreg(RXDCTL8),
|
|
igb_getreg(RXDCTL9),
|
|
igb_getreg(RXDCTL10),
|
|
igb_getreg(RXDCTL11),
|
|
igb_getreg(RXDCTL12),
|
|
igb_getreg(RXDCTL13),
|
|
igb_getreg(RXDCTL14),
|
|
igb_getreg(RXDCTL15),
|
|
igb_getreg(RXSTMPL),
|
|
igb_getreg(TIMADJH),
|
|
igb_getreg(FCRTL),
|
|
igb_getreg(XONRXC),
|
|
igb_getreg(RFCTL),
|
|
igb_getreg(GSCN_1),
|
|
igb_getreg(FCAL),
|
|
igb_getreg(GPIE),
|
|
igb_getreg(TXPBS),
|
|
igb_getreg(RLPML),
|
|
|
|
[TOTH] = igb_mac_read_clr8,
|
|
[GOTCH] = igb_mac_read_clr8,
|
|
[PRC64] = igb_mac_read_clr4,
|
|
[PRC255] = igb_mac_read_clr4,
|
|
[PRC1023] = igb_mac_read_clr4,
|
|
[PTC64] = igb_mac_read_clr4,
|
|
[PTC255] = igb_mac_read_clr4,
|
|
[PTC1023] = igb_mac_read_clr4,
|
|
[GPRC] = igb_mac_read_clr4,
|
|
[TPT] = igb_mac_read_clr4,
|
|
[RUC] = igb_mac_read_clr4,
|
|
[BPRC] = igb_mac_read_clr4,
|
|
[MPTC] = igb_mac_read_clr4,
|
|
[IAC] = igb_mac_read_clr4,
|
|
[ICR] = igb_mac_icr_read,
|
|
[STATUS] = igb_get_status,
|
|
[ICS] = igb_mac_ics_read,
|
|
/*
|
|
* 8.8.10: Reading the IMC register returns the value of the IMS register.
|
|
*/
|
|
[IMC] = igb_mac_ims_read,
|
|
[TORH] = igb_mac_read_clr8,
|
|
[GORCH] = igb_mac_read_clr8,
|
|
[PRC127] = igb_mac_read_clr4,
|
|
[PRC511] = igb_mac_read_clr4,
|
|
[PRC1522] = igb_mac_read_clr4,
|
|
[PTC127] = igb_mac_read_clr4,
|
|
[PTC511] = igb_mac_read_clr4,
|
|
[PTC1522] = igb_mac_read_clr4,
|
|
[GPTC] = igb_mac_read_clr4,
|
|
[TPR] = igb_mac_read_clr4,
|
|
[ROC] = igb_mac_read_clr4,
|
|
[MPRC] = igb_mac_read_clr4,
|
|
[BPTC] = igb_mac_read_clr4,
|
|
[TSCTC] = igb_mac_read_clr4,
|
|
[CTRL] = igb_get_ctrl,
|
|
[SWSM] = igb_mac_swsm_read,
|
|
[IMS] = igb_mac_ims_read,
|
|
[SYSTIML] = igb_get_systiml,
|
|
[RXSATRH] = igb_get_rxsatrh,
|
|
[TXSTMPH] = igb_get_txstmph,
|
|
|
|
[CRCERRS ... MPC] = igb_mac_readreg,
|
|
[IP6AT ... IP6AT + 3] = igb_mac_readreg,
|
|
[IP4AT ... IP4AT + 6] = igb_mac_readreg,
|
|
[RA ... RA + 31] = igb_mac_readreg,
|
|
[RA2 ... RA2 + 31] = igb_mac_readreg,
|
|
[WUPM ... WUPM + 31] = igb_mac_readreg,
|
|
[MTA ... MTA + E1000_MC_TBL_SIZE - 1] = igb_mac_readreg,
|
|
[VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = igb_mac_readreg,
|
|
[FFMT ... FFMT + 254] = igb_mac_readreg,
|
|
[MDEF ... MDEF + 7] = igb_mac_readreg,
|
|
[FTFT ... FTFT + 254] = igb_mac_readreg,
|
|
[RETA ... RETA + 31] = igb_mac_readreg,
|
|
[RSSRK ... RSSRK + 9] = igb_mac_readreg,
|
|
[MAVTV0 ... MAVTV3] = igb_mac_readreg,
|
|
[EITR0 ... EITR0 + IGB_INTR_NUM - 1] = igb_mac_eitr_read,
|
|
[PVTEICR0] = igb_mac_read_clr4,
|
|
[PVTEICR1] = igb_mac_read_clr4,
|
|
[PVTEICR2] = igb_mac_read_clr4,
|
|
[PVTEICR3] = igb_mac_read_clr4,
|
|
[PVTEICR4] = igb_mac_read_clr4,
|
|
[PVTEICR5] = igb_mac_read_clr4,
|
|
[PVTEICR6] = igb_mac_read_clr4,
|
|
[PVTEICR7] = igb_mac_read_clr4,
|
|
|
|
/* IGB specific: */
|
|
[FWSM] = igb_mac_readreg,
|
|
[SW_FW_SYNC] = igb_mac_readreg,
|
|
[HTCBDPC] = igb_mac_read_clr4,
|
|
[EICR] = igb_mac_read_clr4,
|
|
[EIMS] = igb_mac_readreg,
|
|
[EIAM] = igb_mac_readreg,
|
|
[IVAR0 ... IVAR0 + 7] = igb_mac_readreg,
|
|
igb_getreg(IVAR_MISC),
|
|
igb_getreg(VT_CTL),
|
|
[P2VMAILBOX0 ... P2VMAILBOX7] = igb_mac_readreg,
|
|
[V2PMAILBOX0 ... V2PMAILBOX7] = igb_mac_vfmailbox_read,
|
|
igb_getreg(MBVFICR),
|
|
[VMBMEM0 ... VMBMEM0 + 127] = igb_mac_readreg,
|
|
igb_getreg(MBVFIMR),
|
|
igb_getreg(VFLRE),
|
|
igb_getreg(VFRE),
|
|
igb_getreg(VFTE),
|
|
igb_getreg(QDE),
|
|
igb_getreg(DTXSWC),
|
|
igb_getreg(RPLOLR),
|
|
[VLVF0 ... VLVF0 + E1000_VLVF_ARRAY_SIZE - 1] = igb_mac_readreg,
|
|
[VMVIR0 ... VMVIR7] = igb_mac_readreg,
|
|
[VMOLR0 ... VMOLR7] = igb_mac_readreg,
|
|
[WVBR] = igb_mac_read_clr4,
|
|
[RQDPC0] = igb_mac_read_clr4,
|
|
[RQDPC1] = igb_mac_read_clr4,
|
|
[RQDPC2] = igb_mac_read_clr4,
|
|
[RQDPC3] = igb_mac_read_clr4,
|
|
[RQDPC4] = igb_mac_read_clr4,
|
|
[RQDPC5] = igb_mac_read_clr4,
|
|
[RQDPC6] = igb_mac_read_clr4,
|
|
[RQDPC7] = igb_mac_read_clr4,
|
|
[RQDPC8] = igb_mac_read_clr4,
|
|
[RQDPC9] = igb_mac_read_clr4,
|
|
[RQDPC10] = igb_mac_read_clr4,
|
|
[RQDPC11] = igb_mac_read_clr4,
|
|
[RQDPC12] = igb_mac_read_clr4,
|
|
[RQDPC13] = igb_mac_read_clr4,
|
|
[RQDPC14] = igb_mac_read_clr4,
|
|
[RQDPC15] = igb_mac_read_clr4,
|
|
[VTIVAR ... VTIVAR + 7] = igb_mac_readreg,
|
|
[VTIVAR_MISC ... VTIVAR_MISC + 7] = igb_mac_readreg,
|
|
};
|
|
enum { IGB_NREADOPS = ARRAY_SIZE(igb_macreg_readops) };
|
|
|
|
#define igb_putreg(x) [x] = igb_mac_writereg
|
|
typedef void (*writeops)(IGBCore *, int, uint32_t);
|
|
static const writeops igb_macreg_writeops[] = {
|
|
igb_putreg(SWSM),
|
|
igb_putreg(WUFC),
|
|
igb_putreg(RDBAH0),
|
|
igb_putreg(RDBAH1),
|
|
igb_putreg(RDBAH2),
|
|
igb_putreg(RDBAH3),
|
|
igb_putreg(RDBAH4),
|
|
igb_putreg(RDBAH5),
|
|
igb_putreg(RDBAH6),
|
|
igb_putreg(RDBAH7),
|
|
igb_putreg(RDBAH8),
|
|
igb_putreg(RDBAH9),
|
|
igb_putreg(RDBAH10),
|
|
igb_putreg(RDBAH11),
|
|
igb_putreg(RDBAH12),
|
|
igb_putreg(RDBAH13),
|
|
igb_putreg(RDBAH14),
|
|
igb_putreg(RDBAH15),
|
|
igb_putreg(SRRCTL0),
|
|
igb_putreg(SRRCTL1),
|
|
igb_putreg(SRRCTL2),
|
|
igb_putreg(SRRCTL3),
|
|
igb_putreg(SRRCTL4),
|
|
igb_putreg(SRRCTL5),
|
|
igb_putreg(SRRCTL6),
|
|
igb_putreg(SRRCTL7),
|
|
igb_putreg(SRRCTL8),
|
|
igb_putreg(SRRCTL9),
|
|
igb_putreg(SRRCTL10),
|
|
igb_putreg(SRRCTL11),
|
|
igb_putreg(SRRCTL12),
|
|
igb_putreg(SRRCTL13),
|
|
igb_putreg(SRRCTL14),
|
|
igb_putreg(SRRCTL15),
|
|
igb_putreg(RXDCTL0),
|
|
igb_putreg(RXDCTL1),
|
|
igb_putreg(RXDCTL2),
|
|
igb_putreg(RXDCTL3),
|
|
igb_putreg(RXDCTL4),
|
|
igb_putreg(RXDCTL5),
|
|
igb_putreg(RXDCTL6),
|
|
igb_putreg(RXDCTL7),
|
|
igb_putreg(RXDCTL8),
|
|
igb_putreg(RXDCTL9),
|
|
igb_putreg(RXDCTL10),
|
|
igb_putreg(RXDCTL11),
|
|
igb_putreg(RXDCTL12),
|
|
igb_putreg(RXDCTL13),
|
|
igb_putreg(RXDCTL14),
|
|
igb_putreg(RXDCTL15),
|
|
igb_putreg(LEDCTL),
|
|
igb_putreg(TCTL),
|
|
igb_putreg(TCTL_EXT),
|
|
igb_putreg(DTXCTL),
|
|
igb_putreg(RXPBS),
|
|
igb_putreg(RQDPC0),
|
|
igb_putreg(FCAL),
|
|
igb_putreg(FCRUC),
|
|
igb_putreg(WUC),
|
|
igb_putreg(WUS),
|
|
igb_putreg(IPAV),
|
|
igb_putreg(TDBAH0),
|
|
igb_putreg(TDBAH1),
|
|
igb_putreg(TDBAH2),
|
|
igb_putreg(TDBAH3),
|
|
igb_putreg(TDBAH4),
|
|
igb_putreg(TDBAH5),
|
|
igb_putreg(TDBAH6),
|
|
igb_putreg(TDBAH7),
|
|
igb_putreg(TDBAH8),
|
|
igb_putreg(TDBAH9),
|
|
igb_putreg(TDBAH10),
|
|
igb_putreg(TDBAH11),
|
|
igb_putreg(TDBAH12),
|
|
igb_putreg(TDBAH13),
|
|
igb_putreg(TDBAH14),
|
|
igb_putreg(TDBAH15),
|
|
igb_putreg(IAM),
|
|
igb_putreg(MANC),
|
|
igb_putreg(MANC2H),
|
|
igb_putreg(MFVAL),
|
|
igb_putreg(FACTPS),
|
|
igb_putreg(FUNCTAG),
|
|
igb_putreg(GSCL_1),
|
|
igb_putreg(GSCL_2),
|
|
igb_putreg(GSCL_3),
|
|
igb_putreg(GSCL_4),
|
|
igb_putreg(GSCN_0),
|
|
igb_putreg(GSCN_1),
|
|
igb_putreg(GSCN_2),
|
|
igb_putreg(GSCN_3),
|
|
igb_putreg(MRQC),
|
|
igb_putreg(FLOP),
|
|
igb_putreg(FLA),
|
|
igb_putreg(TXDCTL0),
|
|
igb_putreg(TXDCTL1),
|
|
igb_putreg(TXDCTL2),
|
|
igb_putreg(TXDCTL3),
|
|
igb_putreg(TXDCTL4),
|
|
igb_putreg(TXDCTL5),
|
|
igb_putreg(TXDCTL6),
|
|
igb_putreg(TXDCTL7),
|
|
igb_putreg(TXDCTL8),
|
|
igb_putreg(TXDCTL9),
|
|
igb_putreg(TXDCTL10),
|
|
igb_putreg(TXDCTL11),
|
|
igb_putreg(TXDCTL12),
|
|
igb_putreg(TXDCTL13),
|
|
igb_putreg(TXDCTL14),
|
|
igb_putreg(TXDCTL15),
|
|
igb_putreg(TXCTL0),
|
|
igb_putreg(TXCTL1),
|
|
igb_putreg(TXCTL2),
|
|
igb_putreg(TXCTL3),
|
|
igb_putreg(TXCTL4),
|
|
igb_putreg(TXCTL5),
|
|
igb_putreg(TXCTL6),
|
|
igb_putreg(TXCTL7),
|
|
igb_putreg(TXCTL8),
|
|
igb_putreg(TXCTL9),
|
|
igb_putreg(TXCTL10),
|
|
igb_putreg(TXCTL11),
|
|
igb_putreg(TXCTL12),
|
|
igb_putreg(TXCTL13),
|
|
igb_putreg(TXCTL14),
|
|
igb_putreg(TXCTL15),
|
|
igb_putreg(TDWBAL0),
|
|
igb_putreg(TDWBAL1),
|
|
igb_putreg(TDWBAL2),
|
|
igb_putreg(TDWBAL3),
|
|
igb_putreg(TDWBAL4),
|
|
igb_putreg(TDWBAL5),
|
|
igb_putreg(TDWBAL6),
|
|
igb_putreg(TDWBAL7),
|
|
igb_putreg(TDWBAL8),
|
|
igb_putreg(TDWBAL9),
|
|
igb_putreg(TDWBAL10),
|
|
igb_putreg(TDWBAL11),
|
|
igb_putreg(TDWBAL12),
|
|
igb_putreg(TDWBAL13),
|
|
igb_putreg(TDWBAL14),
|
|
igb_putreg(TDWBAL15),
|
|
igb_putreg(TDWBAH0),
|
|
igb_putreg(TDWBAH1),
|
|
igb_putreg(TDWBAH2),
|
|
igb_putreg(TDWBAH3),
|
|
igb_putreg(TDWBAH4),
|
|
igb_putreg(TDWBAH5),
|
|
igb_putreg(TDWBAH6),
|
|
igb_putreg(TDWBAH7),
|
|
igb_putreg(TDWBAH8),
|
|
igb_putreg(TDWBAH9),
|
|
igb_putreg(TDWBAH10),
|
|
igb_putreg(TDWBAH11),
|
|
igb_putreg(TDWBAH12),
|
|
igb_putreg(TDWBAH13),
|
|
igb_putreg(TDWBAH14),
|
|
igb_putreg(TDWBAH15),
|
|
igb_putreg(TIPG),
|
|
igb_putreg(RXSTMPH),
|
|
igb_putreg(RXSTMPL),
|
|
igb_putreg(RXSATRL),
|
|
igb_putreg(RXSATRH),
|
|
igb_putreg(TXSTMPL),
|
|
igb_putreg(TXSTMPH),
|
|
igb_putreg(SYSTIML),
|
|
igb_putreg(SYSTIMH),
|
|
igb_putreg(TIMADJL),
|
|
igb_putreg(TSYNCRXCTL),
|
|
igb_putreg(TSYNCTXCTL),
|
|
igb_putreg(EEMNGCTL),
|
|
igb_putreg(GPIE),
|
|
igb_putreg(TXPBS),
|
|
igb_putreg(RLPML),
|
|
igb_putreg(VET),
|
|
|
|
[TDH0] = igb_set_16bit,
|
|
[TDH1] = igb_set_16bit,
|
|
[TDH2] = igb_set_16bit,
|
|
[TDH3] = igb_set_16bit,
|
|
[TDH4] = igb_set_16bit,
|
|
[TDH5] = igb_set_16bit,
|
|
[TDH6] = igb_set_16bit,
|
|
[TDH7] = igb_set_16bit,
|
|
[TDH8] = igb_set_16bit,
|
|
[TDH9] = igb_set_16bit,
|
|
[TDH10] = igb_set_16bit,
|
|
[TDH11] = igb_set_16bit,
|
|
[TDH12] = igb_set_16bit,
|
|
[TDH13] = igb_set_16bit,
|
|
[TDH14] = igb_set_16bit,
|
|
[TDH15] = igb_set_16bit,
|
|
[TDT0] = igb_set_tdt,
|
|
[TDT1] = igb_set_tdt,
|
|
[TDT2] = igb_set_tdt,
|
|
[TDT3] = igb_set_tdt,
|
|
[TDT4] = igb_set_tdt,
|
|
[TDT5] = igb_set_tdt,
|
|
[TDT6] = igb_set_tdt,
|
|
[TDT7] = igb_set_tdt,
|
|
[TDT8] = igb_set_tdt,
|
|
[TDT9] = igb_set_tdt,
|
|
[TDT10] = igb_set_tdt,
|
|
[TDT11] = igb_set_tdt,
|
|
[TDT12] = igb_set_tdt,
|
|
[TDT13] = igb_set_tdt,
|
|
[TDT14] = igb_set_tdt,
|
|
[TDT15] = igb_set_tdt,
|
|
[MDIC] = igb_set_mdic,
|
|
[ICS] = igb_set_ics,
|
|
[RDH0] = igb_set_16bit,
|
|
[RDH1] = igb_set_16bit,
|
|
[RDH2] = igb_set_16bit,
|
|
[RDH3] = igb_set_16bit,
|
|
[RDH4] = igb_set_16bit,
|
|
[RDH5] = igb_set_16bit,
|
|
[RDH6] = igb_set_16bit,
|
|
[RDH7] = igb_set_16bit,
|
|
[RDH8] = igb_set_16bit,
|
|
[RDH9] = igb_set_16bit,
|
|
[RDH10] = igb_set_16bit,
|
|
[RDH11] = igb_set_16bit,
|
|
[RDH12] = igb_set_16bit,
|
|
[RDH13] = igb_set_16bit,
|
|
[RDH14] = igb_set_16bit,
|
|
[RDH15] = igb_set_16bit,
|
|
[RDT0] = igb_set_rdt,
|
|
[RDT1] = igb_set_rdt,
|
|
[RDT2] = igb_set_rdt,
|
|
[RDT3] = igb_set_rdt,
|
|
[RDT4] = igb_set_rdt,
|
|
[RDT5] = igb_set_rdt,
|
|
[RDT6] = igb_set_rdt,
|
|
[RDT7] = igb_set_rdt,
|
|
[RDT8] = igb_set_rdt,
|
|
[RDT9] = igb_set_rdt,
|
|
[RDT10] = igb_set_rdt,
|
|
[RDT11] = igb_set_rdt,
|
|
[RDT12] = igb_set_rdt,
|
|
[RDT13] = igb_set_rdt,
|
|
[RDT14] = igb_set_rdt,
|
|
[RDT15] = igb_set_rdt,
|
|
[IMC] = igb_set_imc,
|
|
[IMS] = igb_set_ims,
|
|
[ICR] = igb_set_icr,
|
|
[EECD] = igb_set_eecd,
|
|
[RCTL] = igb_set_rx_control,
|
|
[CTRL] = igb_set_ctrl,
|
|
[EERD] = igb_set_eerd,
|
|
[TDFH] = igb_set_13bit,
|
|
[TDFT] = igb_set_13bit,
|
|
[TDFHS] = igb_set_13bit,
|
|
[TDFTS] = igb_set_13bit,
|
|
[TDFPC] = igb_set_13bit,
|
|
[RDFH] = igb_set_13bit,
|
|
[RDFT] = igb_set_13bit,
|
|
[RDFHS] = igb_set_13bit,
|
|
[RDFTS] = igb_set_13bit,
|
|
[RDFPC] = igb_set_13bit,
|
|
[GCR] = igb_set_gcr,
|
|
[RXCSUM] = igb_set_rxcsum,
|
|
[TDLEN0] = igb_set_dlen,
|
|
[TDLEN1] = igb_set_dlen,
|
|
[TDLEN2] = igb_set_dlen,
|
|
[TDLEN3] = igb_set_dlen,
|
|
[TDLEN4] = igb_set_dlen,
|
|
[TDLEN5] = igb_set_dlen,
|
|
[TDLEN6] = igb_set_dlen,
|
|
[TDLEN7] = igb_set_dlen,
|
|
[TDLEN8] = igb_set_dlen,
|
|
[TDLEN9] = igb_set_dlen,
|
|
[TDLEN10] = igb_set_dlen,
|
|
[TDLEN11] = igb_set_dlen,
|
|
[TDLEN12] = igb_set_dlen,
|
|
[TDLEN13] = igb_set_dlen,
|
|
[TDLEN14] = igb_set_dlen,
|
|
[TDLEN15] = igb_set_dlen,
|
|
[RDLEN0] = igb_set_dlen,
|
|
[RDLEN1] = igb_set_dlen,
|
|
[RDLEN2] = igb_set_dlen,
|
|
[RDLEN3] = igb_set_dlen,
|
|
[RDLEN4] = igb_set_dlen,
|
|
[RDLEN5] = igb_set_dlen,
|
|
[RDLEN6] = igb_set_dlen,
|
|
[RDLEN7] = igb_set_dlen,
|
|
[RDLEN8] = igb_set_dlen,
|
|
[RDLEN9] = igb_set_dlen,
|
|
[RDLEN10] = igb_set_dlen,
|
|
[RDLEN11] = igb_set_dlen,
|
|
[RDLEN12] = igb_set_dlen,
|
|
[RDLEN13] = igb_set_dlen,
|
|
[RDLEN14] = igb_set_dlen,
|
|
[RDLEN15] = igb_set_dlen,
|
|
[TDBAL0] = igb_set_dbal,
|
|
[TDBAL1] = igb_set_dbal,
|
|
[TDBAL2] = igb_set_dbal,
|
|
[TDBAL3] = igb_set_dbal,
|
|
[TDBAL4] = igb_set_dbal,
|
|
[TDBAL5] = igb_set_dbal,
|
|
[TDBAL6] = igb_set_dbal,
|
|
[TDBAL7] = igb_set_dbal,
|
|
[TDBAL8] = igb_set_dbal,
|
|
[TDBAL9] = igb_set_dbal,
|
|
[TDBAL10] = igb_set_dbal,
|
|
[TDBAL11] = igb_set_dbal,
|
|
[TDBAL12] = igb_set_dbal,
|
|
[TDBAL13] = igb_set_dbal,
|
|
[TDBAL14] = igb_set_dbal,
|
|
[TDBAL15] = igb_set_dbal,
|
|
[RDBAL0] = igb_set_dbal,
|
|
[RDBAL1] = igb_set_dbal,
|
|
[RDBAL2] = igb_set_dbal,
|
|
[RDBAL3] = igb_set_dbal,
|
|
[RDBAL4] = igb_set_dbal,
|
|
[RDBAL5] = igb_set_dbal,
|
|
[RDBAL6] = igb_set_dbal,
|
|
[RDBAL7] = igb_set_dbal,
|
|
[RDBAL8] = igb_set_dbal,
|
|
[RDBAL9] = igb_set_dbal,
|
|
[RDBAL10] = igb_set_dbal,
|
|
[RDBAL11] = igb_set_dbal,
|
|
[RDBAL12] = igb_set_dbal,
|
|
[RDBAL13] = igb_set_dbal,
|
|
[RDBAL14] = igb_set_dbal,
|
|
[RDBAL15] = igb_set_dbal,
|
|
[STATUS] = igb_set_status,
|
|
[PBACLR] = igb_set_pbaclr,
|
|
[CTRL_EXT] = igb_set_ctrlext,
|
|
[FCAH] = igb_set_16bit,
|
|
[FCT] = igb_set_16bit,
|
|
[FCTTV] = igb_set_16bit,
|
|
[FCRTV] = igb_set_16bit,
|
|
[FCRTH] = igb_set_fcrth,
|
|
[FCRTL] = igb_set_fcrtl,
|
|
[CTRL_DUP] = igb_set_ctrl,
|
|
[RFCTL] = igb_set_rfctl,
|
|
[TIMINCA] = igb_set_timinca,
|
|
[TIMADJH] = igb_set_timadjh,
|
|
|
|
[IP6AT ... IP6AT + 3] = igb_mac_writereg,
|
|
[IP4AT ... IP4AT + 6] = igb_mac_writereg,
|
|
[RA] = igb_mac_writereg,
|
|
[RA + 1] = igb_mac_setmacaddr,
|
|
[RA + 2 ... RA + 31] = igb_mac_writereg,
|
|
[RA2 ... RA2 + 31] = igb_mac_writereg,
|
|
[WUPM ... WUPM + 31] = igb_mac_writereg,
|
|
[MTA ... MTA + E1000_MC_TBL_SIZE - 1] = igb_mac_writereg,
|
|
[VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = igb_mac_writereg,
|
|
[FFMT ... FFMT + 254] = igb_set_4bit,
|
|
[MDEF ... MDEF + 7] = igb_mac_writereg,
|
|
[FTFT ... FTFT + 254] = igb_mac_writereg,
|
|
[RETA ... RETA + 31] = igb_mac_writereg,
|
|
[RSSRK ... RSSRK + 9] = igb_mac_writereg,
|
|
[MAVTV0 ... MAVTV3] = igb_mac_writereg,
|
|
[EITR0 ... EITR0 + IGB_INTR_NUM - 1] = igb_set_eitr,
|
|
|
|
/* IGB specific: */
|
|
[FWSM] = igb_mac_writereg,
|
|
[SW_FW_SYNC] = igb_mac_writereg,
|
|
[EICR] = igb_set_eicr,
|
|
[EICS] = igb_set_eics,
|
|
[EIAC] = igb_set_eiac,
|
|
[EIAM] = igb_set_eiam,
|
|
[EIMC] = igb_set_eimc,
|
|
[EIMS] = igb_set_eims,
|
|
[IVAR0 ... IVAR0 + 7] = igb_mac_writereg,
|
|
igb_putreg(IVAR_MISC),
|
|
igb_putreg(VT_CTL),
|
|
[P2VMAILBOX0 ... P2VMAILBOX7] = igb_set_pfmailbox,
|
|
[V2PMAILBOX0 ... V2PMAILBOX7] = igb_set_vfmailbox,
|
|
[MBVFICR] = igb_w1c,
|
|
[VMBMEM0 ... VMBMEM0 + 127] = igb_mac_writereg,
|
|
igb_putreg(MBVFIMR),
|
|
[VFLRE] = igb_w1c,
|
|
igb_putreg(VFRE),
|
|
igb_putreg(VFTE),
|
|
igb_putreg(QDE),
|
|
igb_putreg(DTXSWC),
|
|
igb_putreg(RPLOLR),
|
|
[VLVF0 ... VLVF0 + E1000_VLVF_ARRAY_SIZE - 1] = igb_mac_writereg,
|
|
[VMVIR0 ... VMVIR7] = igb_mac_writereg,
|
|
[VMOLR0 ... VMOLR7] = igb_mac_writereg,
|
|
[UTA ... UTA + E1000_MC_TBL_SIZE - 1] = igb_mac_writereg,
|
|
[PVTCTRL0] = igb_set_vtctrl,
|
|
[PVTCTRL1] = igb_set_vtctrl,
|
|
[PVTCTRL2] = igb_set_vtctrl,
|
|
[PVTCTRL3] = igb_set_vtctrl,
|
|
[PVTCTRL4] = igb_set_vtctrl,
|
|
[PVTCTRL5] = igb_set_vtctrl,
|
|
[PVTCTRL6] = igb_set_vtctrl,
|
|
[PVTCTRL7] = igb_set_vtctrl,
|
|
[PVTEICS0] = igb_set_vteics,
|
|
[PVTEICS1] = igb_set_vteics,
|
|
[PVTEICS2] = igb_set_vteics,
|
|
[PVTEICS3] = igb_set_vteics,
|
|
[PVTEICS4] = igb_set_vteics,
|
|
[PVTEICS5] = igb_set_vteics,
|
|
[PVTEICS6] = igb_set_vteics,
|
|
[PVTEICS7] = igb_set_vteics,
|
|
[PVTEIMS0] = igb_set_vteims,
|
|
[PVTEIMS1] = igb_set_vteims,
|
|
[PVTEIMS2] = igb_set_vteims,
|
|
[PVTEIMS3] = igb_set_vteims,
|
|
[PVTEIMS4] = igb_set_vteims,
|
|
[PVTEIMS5] = igb_set_vteims,
|
|
[PVTEIMS6] = igb_set_vteims,
|
|
[PVTEIMS7] = igb_set_vteims,
|
|
[PVTEIMC0] = igb_set_vteimc,
|
|
[PVTEIMC1] = igb_set_vteimc,
|
|
[PVTEIMC2] = igb_set_vteimc,
|
|
[PVTEIMC3] = igb_set_vteimc,
|
|
[PVTEIMC4] = igb_set_vteimc,
|
|
[PVTEIMC5] = igb_set_vteimc,
|
|
[PVTEIMC6] = igb_set_vteimc,
|
|
[PVTEIMC7] = igb_set_vteimc,
|
|
[PVTEIAC0] = igb_set_vteiac,
|
|
[PVTEIAC1] = igb_set_vteiac,
|
|
[PVTEIAC2] = igb_set_vteiac,
|
|
[PVTEIAC3] = igb_set_vteiac,
|
|
[PVTEIAC4] = igb_set_vteiac,
|
|
[PVTEIAC5] = igb_set_vteiac,
|
|
[PVTEIAC6] = igb_set_vteiac,
|
|
[PVTEIAC7] = igb_set_vteiac,
|
|
[PVTEIAM0] = igb_set_vteiam,
|
|
[PVTEIAM1] = igb_set_vteiam,
|
|
[PVTEIAM2] = igb_set_vteiam,
|
|
[PVTEIAM3] = igb_set_vteiam,
|
|
[PVTEIAM4] = igb_set_vteiam,
|
|
[PVTEIAM5] = igb_set_vteiam,
|
|
[PVTEIAM6] = igb_set_vteiam,
|
|
[PVTEIAM7] = igb_set_vteiam,
|
|
[PVTEICR0] = igb_set_vteicr,
|
|
[PVTEICR1] = igb_set_vteicr,
|
|
[PVTEICR2] = igb_set_vteicr,
|
|
[PVTEICR3] = igb_set_vteicr,
|
|
[PVTEICR4] = igb_set_vteicr,
|
|
[PVTEICR5] = igb_set_vteicr,
|
|
[PVTEICR6] = igb_set_vteicr,
|
|
[PVTEICR7] = igb_set_vteicr,
|
|
[VTIVAR ... VTIVAR + 7] = igb_set_vtivar,
|
|
[VTIVAR_MISC ... VTIVAR_MISC + 7] = igb_mac_writereg
|
|
};
|
|
enum { IGB_NWRITEOPS = ARRAY_SIZE(igb_macreg_writeops) };
|
|
|
|
enum { MAC_ACCESS_PARTIAL = 1 };
|
|
|
|
/*
|
|
* The array below combines alias offsets of the index values for the
|
|
* MAC registers that have aliases, with the indication of not fully
|
|
* implemented registers (lowest bit). This combination is possible
|
|
* because all of the offsets are even.
|
|
*/
|
|
static const uint16_t mac_reg_access[E1000E_MAC_SIZE] = {
|
|
/* Alias index offsets */
|
|
[FCRTL_A] = 0x07fe,
|
|
[RDFH_A] = 0xe904, [RDFT_A] = 0xe904,
|
|
[TDFH_A] = 0xed00, [TDFT_A] = 0xed00,
|
|
[RA_A ... RA_A + 31] = 0x14f0,
|
|
[VFTA_A ... VFTA_A + E1000_VLAN_FILTER_TBL_SIZE - 1] = 0x1400,
|
|
|
|
[RDBAL0_A] = 0x2600,
|
|
[RDBAH0_A] = 0x2600,
|
|
[RDLEN0_A] = 0x2600,
|
|
[SRRCTL0_A] = 0x2600,
|
|
[RDH0_A] = 0x2600,
|
|
[RDT0_A] = 0x2600,
|
|
[RXDCTL0_A] = 0x2600,
|
|
[RXCTL0_A] = 0x2600,
|
|
[RQDPC0_A] = 0x2600,
|
|
[RDBAL1_A] = 0x25D0,
|
|
[RDBAL2_A] = 0x25A0,
|
|
[RDBAL3_A] = 0x2570,
|
|
[RDBAH1_A] = 0x25D0,
|
|
[RDBAH2_A] = 0x25A0,
|
|
[RDBAH3_A] = 0x2570,
|
|
[RDLEN1_A] = 0x25D0,
|
|
[RDLEN2_A] = 0x25A0,
|
|
[RDLEN3_A] = 0x2570,
|
|
[SRRCTL1_A] = 0x25D0,
|
|
[SRRCTL2_A] = 0x25A0,
|
|
[SRRCTL3_A] = 0x2570,
|
|
[RDH1_A] = 0x25D0,
|
|
[RDH2_A] = 0x25A0,
|
|
[RDH3_A] = 0x2570,
|
|
[RDT1_A] = 0x25D0,
|
|
[RDT2_A] = 0x25A0,
|
|
[RDT3_A] = 0x2570,
|
|
[RXDCTL1_A] = 0x25D0,
|
|
[RXDCTL2_A] = 0x25A0,
|
|
[RXDCTL3_A] = 0x2570,
|
|
[RXCTL1_A] = 0x25D0,
|
|
[RXCTL2_A] = 0x25A0,
|
|
[RXCTL3_A] = 0x2570,
|
|
[RQDPC1_A] = 0x25D0,
|
|
[RQDPC2_A] = 0x25A0,
|
|
[RQDPC3_A] = 0x2570,
|
|
[TDBAL0_A] = 0x2A00,
|
|
[TDBAH0_A] = 0x2A00,
|
|
[TDLEN0_A] = 0x2A00,
|
|
[TDH0_A] = 0x2A00,
|
|
[TDT0_A] = 0x2A00,
|
|
[TXCTL0_A] = 0x2A00,
|
|
[TDWBAL0_A] = 0x2A00,
|
|
[TDWBAH0_A] = 0x2A00,
|
|
[TDBAL1_A] = 0x29D0,
|
|
[TDBAL2_A] = 0x29A0,
|
|
[TDBAL3_A] = 0x2970,
|
|
[TDBAH1_A] = 0x29D0,
|
|
[TDBAH2_A] = 0x29A0,
|
|
[TDBAH3_A] = 0x2970,
|
|
[TDLEN1_A] = 0x29D0,
|
|
[TDLEN2_A] = 0x29A0,
|
|
[TDLEN3_A] = 0x2970,
|
|
[TDH1_A] = 0x29D0,
|
|
[TDH2_A] = 0x29A0,
|
|
[TDH3_A] = 0x2970,
|
|
[TDT1_A] = 0x29D0,
|
|
[TDT2_A] = 0x29A0,
|
|
[TDT3_A] = 0x2970,
|
|
[TXDCTL0_A] = 0x2A00,
|
|
[TXDCTL1_A] = 0x29D0,
|
|
[TXDCTL2_A] = 0x29A0,
|
|
[TXDCTL3_A] = 0x2970,
|
|
[TXCTL1_A] = 0x29D0,
|
|
[TXCTL2_A] = 0x29A0,
|
|
[TXCTL3_A] = 0x29D0,
|
|
[TDWBAL1_A] = 0x29D0,
|
|
[TDWBAL2_A] = 0x29A0,
|
|
[TDWBAL3_A] = 0x2970,
|
|
[TDWBAH1_A] = 0x29D0,
|
|
[TDWBAH2_A] = 0x29A0,
|
|
[TDWBAH3_A] = 0x2970,
|
|
|
|
/* Access options */
|
|
[RDFH] = MAC_ACCESS_PARTIAL, [RDFT] = MAC_ACCESS_PARTIAL,
|
|
[RDFHS] = MAC_ACCESS_PARTIAL, [RDFTS] = MAC_ACCESS_PARTIAL,
|
|
[RDFPC] = MAC_ACCESS_PARTIAL,
|
|
[TDFH] = MAC_ACCESS_PARTIAL, [TDFT] = MAC_ACCESS_PARTIAL,
|
|
[TDFHS] = MAC_ACCESS_PARTIAL, [TDFTS] = MAC_ACCESS_PARTIAL,
|
|
[TDFPC] = MAC_ACCESS_PARTIAL, [EECD] = MAC_ACCESS_PARTIAL,
|
|
[FLA] = MAC_ACCESS_PARTIAL,
|
|
[FCAL] = MAC_ACCESS_PARTIAL, [FCAH] = MAC_ACCESS_PARTIAL,
|
|
[FCT] = MAC_ACCESS_PARTIAL, [FCTTV] = MAC_ACCESS_PARTIAL,
|
|
[FCRTV] = MAC_ACCESS_PARTIAL, [FCRTL] = MAC_ACCESS_PARTIAL,
|
|
[FCRTH] = MAC_ACCESS_PARTIAL,
|
|
[MAVTV0 ... MAVTV3] = MAC_ACCESS_PARTIAL
|
|
};
|
|
|
|
void
|
|
igb_core_write(IGBCore *core, hwaddr addr, uint64_t val, unsigned size)
|
|
{
|
|
uint16_t index = igb_get_reg_index_with_offset(mac_reg_access, addr);
|
|
|
|
if (index < IGB_NWRITEOPS && igb_macreg_writeops[index]) {
|
|
if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) {
|
|
trace_e1000e_wrn_regs_write_trivial(index << 2);
|
|
}
|
|
trace_e1000e_core_write(index << 2, size, val);
|
|
igb_macreg_writeops[index](core, index, val);
|
|
} else if (index < IGB_NREADOPS && igb_macreg_readops[index]) {
|
|
trace_e1000e_wrn_regs_write_ro(index << 2, size, val);
|
|
} else {
|
|
trace_e1000e_wrn_regs_write_unknown(index << 2, size, val);
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
igb_core_read(IGBCore *core, hwaddr addr, unsigned size)
|
|
{
|
|
uint64_t val;
|
|
uint16_t index = igb_get_reg_index_with_offset(mac_reg_access, addr);
|
|
|
|
if (index < IGB_NREADOPS && igb_macreg_readops[index]) {
|
|
if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) {
|
|
trace_e1000e_wrn_regs_read_trivial(index << 2);
|
|
}
|
|
val = igb_macreg_readops[index](core, index);
|
|
trace_e1000e_core_read(index << 2, size, val);
|
|
return val;
|
|
} else {
|
|
trace_e1000e_wrn_regs_read_unknown(index << 2, size);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
igb_autoneg_pause(IGBCore *core)
|
|
{
|
|
timer_del(core->autoneg_timer);
|
|
}
|
|
|
|
static void
|
|
igb_autoneg_resume(IGBCore *core)
|
|
{
|
|
if (igb_have_autoneg(core) &&
|
|
!(core->phy[MII_BMSR] & MII_BMSR_AN_COMP)) {
|
|
qemu_get_queue(core->owner_nic)->link_down = false;
|
|
timer_mod(core->autoneg_timer,
|
|
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500);
|
|
}
|
|
}
|
|
|
|
static void
|
|
igb_vm_state_change(void *opaque, bool running, RunState state)
|
|
{
|
|
IGBCore *core = opaque;
|
|
|
|
if (running) {
|
|
trace_e1000e_vm_state_running();
|
|
igb_intrmgr_resume(core);
|
|
igb_autoneg_resume(core);
|
|
} else {
|
|
trace_e1000e_vm_state_stopped();
|
|
igb_autoneg_pause(core);
|
|
igb_intrmgr_pause(core);
|
|
}
|
|
}
|
|
|
|
void
|
|
igb_core_pci_realize(IGBCore *core,
|
|
const uint16_t *eeprom_templ,
|
|
uint32_t eeprom_size,
|
|
const uint8_t *macaddr)
|
|
{
|
|
int i;
|
|
|
|
core->autoneg_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
|
|
igb_autoneg_timer, core);
|
|
igb_intrmgr_pci_realize(core);
|
|
|
|
core->vmstate = qemu_add_vm_change_state_handler(igb_vm_state_change, core);
|
|
|
|
for (i = 0; i < IGB_NUM_QUEUES; i++) {
|
|
net_tx_pkt_init(&core->tx[i].tx_pkt, core->owner, E1000E_MAX_TX_FRAGS);
|
|
}
|
|
|
|
net_rx_pkt_init(&core->rx_pkt);
|
|
|
|
e1000x_core_prepare_eeprom(core->eeprom,
|
|
eeprom_templ,
|
|
eeprom_size,
|
|
PCI_DEVICE_GET_CLASS(core->owner)->device_id,
|
|
macaddr);
|
|
igb_update_rx_offloads(core);
|
|
}
|
|
|
|
void
|
|
igb_core_pci_uninit(IGBCore *core)
|
|
{
|
|
int i;
|
|
|
|
timer_free(core->autoneg_timer);
|
|
|
|
igb_intrmgr_pci_unint(core);
|
|
|
|
qemu_del_vm_change_state_handler(core->vmstate);
|
|
|
|
for (i = 0; i < IGB_NUM_QUEUES; i++) {
|
|
net_tx_pkt_reset(core->tx[i].tx_pkt);
|
|
net_tx_pkt_uninit(core->tx[i].tx_pkt);
|
|
}
|
|
|
|
net_rx_pkt_uninit(core->rx_pkt);
|
|
}
|
|
|
|
static const uint16_t
|
|
igb_phy_reg_init[] = {
|
|
[MII_BMCR] = MII_BMCR_SPEED1000 |
|
|
MII_BMCR_FD |
|
|
MII_BMCR_AUTOEN,
|
|
|
|
[MII_BMSR] = MII_BMSR_EXTCAP |
|
|
MII_BMSR_LINK_ST |
|
|
MII_BMSR_AUTONEG |
|
|
MII_BMSR_MFPS |
|
|
MII_BMSR_EXTSTAT |
|
|
MII_BMSR_10T_HD |
|
|
MII_BMSR_10T_FD |
|
|
MII_BMSR_100TX_HD |
|
|
MII_BMSR_100TX_FD,
|
|
|
|
[MII_PHYID1] = IGP03E1000_E_PHY_ID >> 16,
|
|
[MII_PHYID2] = (IGP03E1000_E_PHY_ID & 0xfff0) | 1,
|
|
[MII_ANAR] = MII_ANAR_CSMACD | MII_ANAR_10 |
|
|
MII_ANAR_10FD | MII_ANAR_TX |
|
|
MII_ANAR_TXFD | MII_ANAR_PAUSE |
|
|
MII_ANAR_PAUSE_ASYM,
|
|
[MII_ANLPAR] = MII_ANLPAR_10 | MII_ANLPAR_10FD |
|
|
MII_ANLPAR_TX | MII_ANLPAR_TXFD |
|
|
MII_ANLPAR_T4 | MII_ANLPAR_PAUSE,
|
|
[MII_ANER] = MII_ANER_NP | MII_ANER_NWAY,
|
|
[MII_ANNP] = 0x1 | MII_ANNP_MP,
|
|
[MII_CTRL1000] = MII_CTRL1000_HALF | MII_CTRL1000_FULL |
|
|
MII_CTRL1000_PORT | MII_CTRL1000_MASTER,
|
|
[MII_STAT1000] = MII_STAT1000_HALF | MII_STAT1000_FULL |
|
|
MII_STAT1000_ROK | MII_STAT1000_LOK,
|
|
[MII_EXTSTAT] = MII_EXTSTAT_1000T_HD | MII_EXTSTAT_1000T_FD,
|
|
|
|
[IGP01E1000_PHY_PORT_CONFIG] = BIT(5) | BIT(8),
|
|
[IGP01E1000_PHY_PORT_STATUS] = IGP01E1000_PSSR_SPEED_1000MBPS,
|
|
[IGP02E1000_PHY_POWER_MGMT] = BIT(0) | BIT(3) | IGP02E1000_PM_D3_LPLU |
|
|
IGP01E1000_PSCFR_SMART_SPEED
|
|
};
|
|
|
|
static const uint32_t igb_mac_reg_init[] = {
|
|
[LEDCTL] = 2 | (3 << 8) | BIT(15) | (6 << 16) | (7 << 24),
|
|
[EEMNGCTL] = BIT(31),
|
|
[RXDCTL0] = E1000_RXDCTL_QUEUE_ENABLE | (1 << 16),
|
|
[RXDCTL1] = 1 << 16,
|
|
[RXDCTL2] = 1 << 16,
|
|
[RXDCTL3] = 1 << 16,
|
|
[RXDCTL4] = 1 << 16,
|
|
[RXDCTL5] = 1 << 16,
|
|
[RXDCTL6] = 1 << 16,
|
|
[RXDCTL7] = 1 << 16,
|
|
[RXDCTL8] = 1 << 16,
|
|
[RXDCTL9] = 1 << 16,
|
|
[RXDCTL10] = 1 << 16,
|
|
[RXDCTL11] = 1 << 16,
|
|
[RXDCTL12] = 1 << 16,
|
|
[RXDCTL13] = 1 << 16,
|
|
[RXDCTL14] = 1 << 16,
|
|
[RXDCTL15] = 1 << 16,
|
|
[TIPG] = 0x08 | (0x04 << 10) | (0x06 << 20),
|
|
[CTRL] = E1000_CTRL_FD | E1000_CTRL_LRST | E1000_CTRL_SPD_1000 |
|
|
E1000_CTRL_ADVD3WUC,
|
|
[STATUS] = E1000_STATUS_PHYRA | BIT(31),
|
|
[EECD] = E1000_EECD_FWE_DIS | E1000_EECD_PRES |
|
|
(2 << E1000_EECD_SIZE_EX_SHIFT),
|
|
[GCR] = E1000_L0S_ADJUST |
|
|
E1000_GCR_CMPL_TMOUT_RESEND |
|
|
E1000_GCR_CAP_VER2 |
|
|
E1000_L1_ENTRY_LATENCY_MSB |
|
|
E1000_L1_ENTRY_LATENCY_LSB,
|
|
[RXCSUM] = E1000_RXCSUM_IPOFLD | E1000_RXCSUM_TUOFLD,
|
|
[TXPBS] = 0x28,
|
|
[RXPBS] = 0x40,
|
|
[TCTL] = E1000_TCTL_PSP | (0xF << E1000_CT_SHIFT) |
|
|
(0x40 << E1000_COLD_SHIFT) | (0x1 << 26) | (0xA << 28),
|
|
[TCTL_EXT] = 0x40 | (0x42 << 10),
|
|
[DTXCTL] = E1000_DTXCTL_8023LL | E1000_DTXCTL_SPOOF_INT,
|
|
[VET] = ETH_P_VLAN | (ETH_P_VLAN << 16),
|
|
|
|
[V2PMAILBOX0 ... V2PMAILBOX0 + IGB_MAX_VF_FUNCTIONS - 1] = E1000_V2PMAILBOX_RSTI,
|
|
[MBVFIMR] = 0xFF,
|
|
[VFRE] = 0xFF,
|
|
[VFTE] = 0xFF,
|
|
[VMOLR0 ... VMOLR0 + 7] = 0x2600 | E1000_VMOLR_STRCRC,
|
|
[RPLOLR] = E1000_RPLOLR_STRCRC,
|
|
[RLPML] = 0x2600,
|
|
[TXCTL0] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL1] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL2] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL3] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL4] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL5] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL6] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL7] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL8] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL9] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL10] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL11] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL12] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL13] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL14] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
[TXCTL15] = E1000_DCA_TXCTRL_DATA_RRO_EN |
|
|
E1000_DCA_TXCTRL_TX_WB_RO_EN |
|
|
E1000_DCA_TXCTRL_DESC_RRO_EN,
|
|
};
|
|
|
|
static void igb_reset(IGBCore *core, bool sw)
|
|
{
|
|
struct igb_tx *tx;
|
|
int i;
|
|
|
|
timer_del(core->autoneg_timer);
|
|
|
|
igb_intrmgr_reset(core);
|
|
|
|
memset(core->phy, 0, sizeof core->phy);
|
|
memcpy(core->phy, igb_phy_reg_init, sizeof igb_phy_reg_init);
|
|
|
|
for (i = 0; i < E1000E_MAC_SIZE; i++) {
|
|
if (sw &&
|
|
(i == RXPBS || i == TXPBS ||
|
|
(i >= EITR0 && i < EITR0 + IGB_INTR_NUM))) {
|
|
continue;
|
|
}
|
|
|
|
core->mac[i] = i < ARRAY_SIZE(igb_mac_reg_init) ?
|
|
igb_mac_reg_init[i] : 0;
|
|
}
|
|
|
|
if (qemu_get_queue(core->owner_nic)->link_down) {
|
|
igb_link_down(core);
|
|
}
|
|
|
|
e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(core->tx); i++) {
|
|
tx = &core->tx[i];
|
|
net_tx_pkt_reset(tx->tx_pkt);
|
|
memset(tx->ctx, 0, sizeof(tx->ctx));
|
|
tx->first = true;
|
|
tx->skip_cp = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
igb_core_reset(IGBCore *core)
|
|
{
|
|
igb_reset(core, false);
|
|
}
|
|
|
|
void igb_core_pre_save(IGBCore *core)
|
|
{
|
|
int i;
|
|
NetClientState *nc = qemu_get_queue(core->owner_nic);
|
|
|
|
/*
|
|
* If link is down and auto-negotiation is supported and ongoing,
|
|
* complete auto-negotiation immediately. This allows us to look
|
|
* at MII_BMSR_AN_COMP to infer link status on load.
|
|
*/
|
|
if (nc->link_down && igb_have_autoneg(core)) {
|
|
core->phy[MII_BMSR] |= MII_BMSR_AN_COMP;
|
|
igb_update_flowctl_status(core);
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(core->tx); i++) {
|
|
if (net_tx_pkt_has_fragments(core->tx[i].tx_pkt)) {
|
|
core->tx[i].skip_cp = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
igb_core_post_load(IGBCore *core)
|
|
{
|
|
NetClientState *nc = qemu_get_queue(core->owner_nic);
|
|
|
|
/*
|
|
* nc.link_down can't be migrated, so infer link_down according
|
|
* to link status bit in core.mac[STATUS].
|
|
*/
|
|
nc->link_down = (core->mac[STATUS] & E1000_STATUS_LU) == 0;
|
|
|
|
return 0;
|
|
}
|