diff options
Diffstat (limited to 'qemu/hw/net')
51 files changed, 2184 insertions, 591 deletions
diff --git a/qemu/hw/net/Makefile.objs b/qemu/hw/net/Makefile.objs index 98801739e..64d044923 100644 --- a/qemu/hw/net/Makefile.objs +++ b/qemu/hw/net/Makefile.objs @@ -19,6 +19,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o common-obj-$(CONFIG_MIPSNET) += mipsnet.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o +common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o diff --git a/qemu/hw/net/allwinner_emac.c b/qemu/hw/net/allwinner_emac.c index 0407dee6d..16d4b63ba 100644 --- a/qemu/hw/net/allwinner_emac.c +++ b/qemu/hw/net/allwinner_emac.c @@ -16,6 +16,7 @@ * GNU General Public License for more details. * */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "qemu/fifo8.h" diff --git a/qemu/hw/net/cadence_gem.c b/qemu/hw/net/cadence_gem.c index 494a346cf..0346f3e33 100644 --- a/qemu/hw/net/cadence_gem.c +++ b/qemu/hw/net/cadence_gem.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include <zlib.h> /* For crc32 */ #include "hw/net/cadence_gem.h" @@ -677,6 +678,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } else { unsigned crc_val; + if (size > sizeof(rxbuf) - sizeof(crc_val)) { + size = sizeof(rxbuf) - sizeof(crc_val); + } + bytes_to_copy = size; /* The application wants the FCS field, which QEMU does not provide. * We must try and calculate one. */ @@ -862,6 +867,14 @@ static void gem_transmit(CadenceGEMState *s) break; } + if (tx_desc_get_length(desc) > sizeof(tx_packet) - (p - tx_packet)) { + DB_PRINT("TX descriptor @ 0x%x too large: size 0x%x space 0x%x\n", + (unsigned)packet_desc_addr, + (unsigned)tx_desc_get_length(desc), + sizeof(tx_packet) - (p - tx_packet)); + break; + } + /* Gather this fragment of the packet from "dma memory" to our contig. * buffer. */ @@ -951,7 +964,7 @@ static void gem_phy_reset(CadenceGEMState *s) s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00; s->phy_regs[PHY_REG_EXTSTAT] = 0x3000; s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078; - s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00; + s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0x7C00; s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60; s->phy_regs[PHY_REG_LED] = 0x4100; s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A; @@ -964,6 +977,7 @@ static void gem_reset(DeviceState *d) { int i; CadenceGEMState *s = CADENCE_GEM(d); + const uint8_t *a; DB_PRINT("\n"); @@ -982,6 +996,11 @@ static void gem_reset(DeviceState *d) s->regs[GEM_DESCONF5] = 0x002f2145; s->regs[GEM_DESCONF6] = 0x00000200; + /* Set MAC address */ + a = &s->conf.macaddr.a[0]; + s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); + s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8); + for (i = 0; i < 4; i++) { s->sar_active[i] = false; } diff --git a/qemu/hw/net/dp8393x.c b/qemu/hw/net/dp8393x.c index ab607e484..0fa652c39 100644 --- a/qemu/hw/net/dp8393x.c +++ b/qemu/hw/net/dp8393x.c @@ -17,9 +17,11 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/devices.h" #include "net/net.h" +#include "qapi/error.h" #include "qemu/timer.h" #include <zlib.h> @@ -292,7 +294,7 @@ static void dp8393x_set_next_tick(dp8393xState *s) ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - delay = get_ticks_per_sec() * ticks / 5000000; + delay = NANOSECONDS_PER_SECOND * ticks / 5000000; timer_mod(s->watchdog, s->wt_last_update + delay); } diff --git a/qemu/hw/net/e1000.c b/qemu/hw/net/e1000.c index 09c9e9d53..8e79b550e 100644 --- a/qemu/hw/net/e1000.c +++ b/qemu/hw/net/e1000.c @@ -25,6 +25,7 @@ */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -37,24 +38,26 @@ #include "e1000_regs.h" +static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + #define E1000_DEBUG #ifdef E1000_DEBUG enum { - DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT, - DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM, - DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR, + DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT, + DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM, + DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR, DEBUG_RXFILTER, DEBUG_PHY, DEBUG_NOTYET, }; -#define DBGBIT(x) (1<<DEBUG_##x) +#define DBGBIT(x) (1<<DEBUG_##x) static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL); -#define DBGOUT(what, fmt, ...) do { \ +#define DBGOUT(what, fmt, ...) do { \ if (debugflags & DBGBIT(what)) \ fprintf(stderr, "e1000: " fmt, ## __VA_ARGS__); \ } while (0) #else -#define DBGOUT(what, fmt, ...) do {} while (0) +#define DBGOUT(what, fmt, ...) do {} while (0) #endif #define IOPORT_SIZE 0x40 @@ -118,7 +121,7 @@ typedef struct E1000State_st { } tx; struct { - uint32_t val_in; // shifted in from guest driver + uint32_t val_in; /* shifted in from guest driver */ uint16_t bitnum_in; uint16_t bitnum_out; uint16_t reading; @@ -135,11 +138,15 @@ typedef struct E1000State_st { /* Compatibility flags for migration to/from qemu 1.3.0 and older */ #define E1000_FLAG_AUTONEG_BIT 0 #define E1000_FLAG_MIT_BIT 1 +#define E1000_FLAG_MAC_BIT 2 #define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT) #define E1000_FLAG_MIT (1 << E1000_FLAG_MIT_BIT) +#define E1000_FLAG_MAC (1 << E1000_FLAG_MAC_BIT) uint32_t compat_flags; } E1000State; +#define chkflag(x) (s->compat_flags & E1000_FLAG_##x) + typedef struct E1000BaseClass { PCIDeviceClass parent_class; uint16_t phy_id2; @@ -155,20 +162,36 @@ typedef struct E1000BaseClass { #define E1000_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE) -#define defreg(x) x = (E1000_##x>>2) +#define defreg(x) x = (E1000_##x>>2) enum { - defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), - defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), - defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), - defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH), - defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT), - defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), - defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), - defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), - defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), - defreg(RA), defreg(MTA), defreg(CRCERRS),defreg(VFTA), - defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV), - defreg(ITR), + defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), + defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), + defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), + defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH), + defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT), + defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), + defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), + defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), + defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), + defreg(RA), defreg(MTA), defreg(CRCERRS), defreg(VFTA), + defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV), + defreg(ITR), defreg(FCRUC), defreg(TDFH), defreg(TDFT), + defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(RDFH), + defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), + defreg(IPAV), defreg(WUC), defreg(WUS), defreg(AIT), + defreg(IP6AT), defreg(IP4AT), defreg(FFLT), defreg(FFMT), + defreg(FFVT), defreg(WUPM), defreg(PBM), defreg(SCC), + defreg(ECOL), defreg(MCC), defreg(LATECOL), defreg(COLC), + defreg(DC), defreg(TNCRS), defreg(SEC), defreg(CEXTERR), + defreg(RLEC), defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), + defreg(XOFFTXC), defreg(RFC), defreg(RJC), defreg(RNBC), + defreg(TSCTFC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), + defreg(RUC), defreg(ROC), defreg(GORCL), defreg(GORCH), + defreg(GOTCL), defreg(GOTCH), defreg(BPRC), defreg(MPRC), + defreg(TSCTC), defreg(PRC64), defreg(PRC127), defreg(PRC255), + defreg(PRC511), defreg(PRC1023), defreg(PRC1522), defreg(PTC64), + defreg(PTC127), defreg(PTC255), defreg(PTC511), defreg(PTC1023), + defreg(PTC1522), defreg(MPTC), defreg(BPTC) }; static void @@ -193,8 +216,7 @@ e1000_link_up(E1000State *s) static bool have_autoneg(E1000State *s) { - return (s->compat_flags & E1000_FLAG_AUTONEG) && - (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN); + return chkflag(AUTONEG) && (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN); } static void @@ -226,18 +248,18 @@ enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) }; enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W }; static const char phy_regcap[0x20] = { - [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, - [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, - [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, - [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, - [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, - [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, + [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, + [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, + [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, + [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, + [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, + [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, [PHY_AUTONEG_EXP] = PHY_R, }; /* PHY_ID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */ static const uint16_t phy_reg_init[] = { - [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | + [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | MII_CR_FULL_DUPLEX | MII_CR_AUTO_NEG_EN, @@ -264,15 +286,15 @@ static const uint16_t phy_reg_init[] = { }; static const uint32_t mac_reg_init[] = { - [PBA] = 0x00100030, - [LEDCTL] = 0x602, - [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | + [PBA] = 0x00100030, + [LEDCTL] = 0x602, + [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | E1000_CTRL_SPD_1000 | E1000_CTRL_SLU, - [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE | + [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE | E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK | E1000_STATUS_SPEED_1000 | E1000_STATUS_FD | E1000_STATUS_LU, - [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN | + [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN | E1000_MANC_ARP_EN | E1000_MANC_0298_EN | E1000_MANC_RMCP_EN, }; @@ -319,7 +341,7 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) if (s->mit_timer_on) { return; } - if (s->compat_flags & E1000_FLAG_MIT) { + if (chkflag(MIT)) { /* Compute the next mitigation delay according to pending * interrupts and the current values of RADV (provided * RDTR!=0), TADV and ITR. @@ -335,6 +357,14 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) } mit_update_delay(&mit_delay, s->mac_reg[ITR]); + /* + * According to e1000 SPEC, the Ethernet controller guarantees + * a maximum observable interrupt rate of 7813 interrupts/sec. + * Thus if mit_delay < 500 then the delay should be set to the + * minimum delay possible which is 500. + */ + mit_delay = (mit_delay < 500) ? 500 : mit_delay; + if (mit_delay) { s->mit_timer_on = 1; timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + @@ -510,17 +540,19 @@ set_eecd(E1000State *s, int index, uint32_t val) s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS | E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ); - if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do - return; - if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state - s->eecd_state.val_in = 0; - s->eecd_state.bitnum_in = 0; - s->eecd_state.bitnum_out = 0; - s->eecd_state.reading = 0; + if (!(E1000_EECD_CS & val)) { /* CS inactive; nothing to do */ + return; } - if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge + if (E1000_EECD_CS & (val ^ oldval)) { /* CS rise edge; reset state */ + s->eecd_state.val_in = 0; + s->eecd_state.bitnum_in = 0; + s->eecd_state.bitnum_out = 0; + s->eecd_state.reading = 0; + } + if (!(E1000_EECD_SK & (val ^ oldval))) { /* no clock edge */ return; - if (!(E1000_EECD_SK & val)) { // falling edge + } + if (!(E1000_EECD_SK & val)) { /* falling edge */ s->eecd_state.bitnum_out++; return; } @@ -565,6 +597,56 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse) } } +static inline void +inc_reg_if_not_full(E1000State *s, int index) +{ + if (s->mac_reg[index] != 0xffffffff) { + s->mac_reg[index]++; + } +} + +static inline void +inc_tx_bcast_or_mcast_count(E1000State *s, const unsigned char *arr) +{ + if (!memcmp(arr, bcast, sizeof bcast)) { + inc_reg_if_not_full(s, BPTC); + } else if (arr[0] & 1) { + inc_reg_if_not_full(s, MPTC); + } +} + +static void +grow_8reg_if_not_full(E1000State *s, int index, int size) +{ + uint64_t sum = s->mac_reg[index] | (uint64_t)s->mac_reg[index+1] << 32; + + if (sum + size < sum) { + sum = ~0ULL; + } else { + sum += size; + } + s->mac_reg[index] = sum; + s->mac_reg[index+1] = sum >> 32; +} + +static void +increase_size_stats(E1000State *s, const int *size_regs, int size) +{ + if (size > 1023) { + inc_reg_if_not_full(s, size_regs[5]); + } else if (size > 511) { + inc_reg_if_not_full(s, size_regs[4]); + } else if (size > 255) { + inc_reg_if_not_full(s, size_regs[3]); + } else if (size > 127) { + inc_reg_if_not_full(s, size_regs[2]); + } else if (size > 64) { + inc_reg_if_not_full(s, size_regs[1]); + } else if (size == 64) { + inc_reg_if_not_full(s, size_regs[0]); + } +} + static inline int vlan_enabled(E1000State *s) { @@ -602,40 +684,49 @@ fcs_len(E1000State *s) static void e1000_send_packet(E1000State *s, const uint8_t *buf, int size) { + static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511, + PTC1023, PTC1522 }; + NetClientState *nc = qemu_get_queue(s->nic); if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) { nc->info->receive(nc, buf, size); } else { qemu_send_packet(nc, buf, size); } + inc_tx_bcast_or_mcast_count(s, buf); + increase_size_stats(s, PTCregs, size); } static void xmit_seg(E1000State *s) { uint16_t len, *sp; - unsigned int frames = s->tx.tso_frames, css, sofar, n; + unsigned int frames = s->tx.tso_frames, css, sofar; struct e1000_tx *tp = &s->tx; if (tp->tse && tp->cptse) { css = tp->ipcss; DBGOUT(TXSUM, "frames %d size %d ipcss %d\n", frames, tp->size, css); - if (tp->ip) { // IPv4 + if (tp->ip) { /* IPv4 */ stw_be_p(tp->data+css+2, tp->size - css); stw_be_p(tp->data+css+4, - be16_to_cpup((uint16_t *)(tp->data+css+4))+frames); - } else // IPv6 + be16_to_cpup((uint16_t *)(tp->data+css+4))+frames); + } else { /* IPv6 */ stw_be_p(tp->data+css+4, tp->size - css); + } css = tp->tucss; len = tp->size - css; DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len); if (tp->tcp) { sofar = frames * tp->mss; stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */ - if (tp->paylen - sofar > tp->mss) - tp->data[css + 13] &= ~9; // PSH, FIN - } else // UDP + if (tp->paylen - sofar > tp->mss) { + tp->data[css + 13] &= ~9; /* PSH, FIN */ + } else if (frames) { + inc_reg_if_not_full(s, TSCTC); + } + } else /* UDP */ stw_be_p(tp->data+css+4, len); if (tp->sum_needed & E1000_TXD_POPTS_TXSM) { unsigned int phsum; @@ -657,13 +748,15 @@ xmit_seg(E1000State *s) memmove(tp->data, tp->data + 4, 8); memcpy(tp->data + 8, tp->vlan_header, 4); e1000_send_packet(s, tp->vlan, tp->size + 4); - } else + } else { e1000_send_packet(s, tp->data, tp->size); - s->mac_reg[TPT]++; - s->mac_reg[GPTC]++; - n = s->mac_reg[TOTL]; - if ((s->mac_reg[TOTL] += s->tx.size) < n) - s->mac_reg[TOTH]++; + } + + inc_reg_if_not_full(s, TPT); + grow_8reg_if_not_full(s, TOTL, s->tx.size); + s->mac_reg[GPTC] = s->mac_reg[TPT]; + s->mac_reg[GOTCL] = s->mac_reg[TOTL]; + s->mac_reg[GOTCH] = s->mac_reg[TOTH]; } static void @@ -679,7 +772,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) struct e1000_tx *tp = &s->tx; s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE); - if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor + if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */ op = le32_to_cpu(xp->cmd_and_length); tp->ipcss = xp->lower_setup.ip_fields.ipcss; tp->ipcso = xp->lower_setup.ip_fields.ipcso; @@ -694,7 +787,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0; tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0; tp->tso_frames = 0; - if (tp->tucso == 0) { // this is probably wrong + if (tp->tucso == 0) { /* this is probably wrong */ DBGOUT(TXSUM, "TCP/UDP: cso 0!\n"); tp->tucso = tp->tucss + (tp->tcp ? 16 : 6); } @@ -718,7 +811,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) stw_be_p(tp->vlan_header + 2, le16_to_cpu(dp->upper.fields.special)); } - + addr = le64_to_cpu(dp->buffer_addr); if (tp->tse && tp->cptse) { msh = tp->hdr_len + tp->mss; @@ -819,7 +912,8 @@ start_xmit(E1000State *s) * bogus values to TDT/TDLEN. * there's nothing too intelligent we could do about this. */ - if (s->mac_reg[TDH] == tdh_start) { + if (s->mac_reg[TDH] == tdh_start || + tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) { DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n", tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]); break; @@ -831,9 +925,9 @@ start_xmit(E1000State *s) static int receive_filter(E1000State *s, const uint8_t *buf, int size) { - static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static const int mta_shift[] = {4, 3, 2, 0}; uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp; + int isbcast = !memcmp(buf, bcast, sizeof bcast), ismcast = (buf[0] & 1); if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) { uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14)); @@ -843,14 +937,19 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) return 0; } - if (rctl & E1000_RCTL_UPE) // promiscuous + if (!isbcast && !ismcast && (rctl & E1000_RCTL_UPE)) { /* promiscuous ucast */ return 1; + } - if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE)) // promiscuous mcast + if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */ + inc_reg_if_not_full(s, MPRC); return 1; + } - if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast)) + if (isbcast && (rctl & E1000_RCTL_BAM)) { /* broadcast enabled */ + inc_reg_if_not_full(s, BPRC); return 1; + } for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) { if (!(rp[1] & E1000_RAH_AV)) @@ -870,8 +969,10 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3]; f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff; - if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) + if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) { + inc_reg_if_not_full(s, MPRC); return 1; + } DBGOUT(RXFILTER, "dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], @@ -960,6 +1061,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) size_t desc_offset; size_t desc_size; size_t total_size; + static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511, + PRC1023, PRC1522 }; if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) { return -1; @@ -973,6 +1076,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) if (size < sizeof(min_buf)) { iov_to_buf(iov, iovcnt, 0, min_buf, size); memset(&min_buf[size], 0, sizeof(min_buf) - size); + inc_reg_if_not_full(s, RUC); min_iov.iov_base = filter_buf = min_buf; min_iov.iov_len = size = sizeof(min_buf); iovcnt = 1; @@ -988,6 +1092,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) (size > MAXIMUM_ETHERNET_VLAN_SIZE && !(s->mac_reg[RCTL] & E1000_RCTL_LPE))) && !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) { + inc_reg_if_not_full(s, ROC); return size; } @@ -1065,7 +1170,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) s->mac_reg[RDH] = 0; /* see comment in start_xmit; same here */ - if (s->mac_reg[RDH] == rdh_start) { + if (s->mac_reg[RDH] == rdh_start || + rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) { DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n", rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]); set_ics(s, 0, E1000_ICS_RXO); @@ -1073,16 +1179,17 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) } } while (desc_offset < total_size); - s->mac_reg[GPRC]++; - s->mac_reg[TPR]++; + increase_size_stats(s, PRCregs, total_size); + inc_reg_if_not_full(s, TPR); + s->mac_reg[GPRC] = s->mac_reg[TPR]; /* TOR - Total Octets Received: * This register includes bytes received in a packet from the <Destination * Address> field through the <CRC> field, inclusively. + * Always include FCS length (4) in size. */ - n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4; - if (n < s->mac_reg[TORL]) - s->mac_reg[TORH]++; - s->mac_reg[TORL] = n; + grow_8reg_if_not_full(s, TORL, size+4); + s->mac_reg[GORCL] = s->mac_reg[TORL]; + s->mac_reg[GORCH] = s->mac_reg[TORH]; n = E1000_ICS_RXT0; if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH]) @@ -1114,6 +1221,30 @@ mac_readreg(E1000State *s, int index) } static uint32_t +mac_low4_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0xf; +} + +static uint32_t +mac_low11_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0x7ff; +} + +static uint32_t +mac_low13_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0x1fff; +} + +static uint32_t +mac_low16_read(E1000State *s, int index) +{ + return s->mac_reg[index] & 0xffff; +} + +static uint32_t mac_icr_read(E1000State *s, int index) { uint32_t ret = s->mac_reg[ICR]; @@ -1206,46 +1337,144 @@ set_ims(E1000State *s, int index, uint32_t val) set_ics(s, 0, 0); } -#define getreg(x) [x] = mac_readreg +#define getreg(x) [x] = mac_readreg static uint32_t (*macreg_readops[])(E1000State *, int) = { - getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL), - getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL), - getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS), - getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL), - getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS), - getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL), - getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV), - getreg(TADV), getreg(ITR), - - [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4, - [GPTC] = mac_read_clr4, [TPR] = mac_read_clr4, [TPT] = mac_read_clr4, - [ICR] = mac_icr_read, [EECD] = get_eecd, [EERD] = flash_eerd_read, - [CRCERRS ... MPC] = &mac_readreg, - [RA ... RA+31] = &mac_readreg, - [MTA ... MTA+127] = &mac_readreg, + getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL), + getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL), + getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS), + getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL), + getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS), + getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL), + getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV), + getreg(TADV), getreg(ITR), getreg(FCRUC), getreg(IPAV), + getreg(WUC), getreg(WUS), getreg(SCC), getreg(ECOL), + getreg(MCC), getreg(LATECOL), getreg(COLC), getreg(DC), + getreg(TNCRS), getreg(SEC), getreg(CEXTERR), getreg(RLEC), + getreg(XONRXC), getreg(XONTXC), getreg(XOFFRXC), getreg(XOFFTXC), + getreg(RFC), getreg(RJC), getreg(RNBC), getreg(TSCTFC), + getreg(MGTPRC), getreg(MGTPDC), getreg(MGTPTC), getreg(GORCL), + getreg(GOTCL), + + [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, + [GOTCH] = mac_read_clr8, [GORCH] = mac_read_clr8, + [PRC64] = mac_read_clr4, [PRC127] = mac_read_clr4, + [PRC255] = mac_read_clr4, [PRC511] = mac_read_clr4, + [PRC1023] = mac_read_clr4, [PRC1522] = mac_read_clr4, + [PTC64] = mac_read_clr4, [PTC127] = mac_read_clr4, + [PTC255] = mac_read_clr4, [PTC511] = mac_read_clr4, + [PTC1023] = mac_read_clr4, [PTC1522] = mac_read_clr4, + [GPRC] = mac_read_clr4, [GPTC] = mac_read_clr4, + [TPT] = mac_read_clr4, [TPR] = mac_read_clr4, + [RUC] = mac_read_clr4, [ROC] = mac_read_clr4, + [BPRC] = mac_read_clr4, [MPRC] = mac_read_clr4, + [TSCTC] = mac_read_clr4, [BPTC] = mac_read_clr4, + [MPTC] = mac_read_clr4, + [ICR] = mac_icr_read, [EECD] = get_eecd, + [EERD] = flash_eerd_read, + [RDFH] = mac_low13_read, [RDFT] = mac_low13_read, + [RDFHS] = mac_low13_read, [RDFTS] = mac_low13_read, + [RDFPC] = mac_low13_read, + [TDFH] = mac_low11_read, [TDFT] = mac_low11_read, + [TDFHS] = mac_low13_read, [TDFTS] = mac_low13_read, + [TDFPC] = mac_low13_read, + [AIT] = mac_low16_read, + + [CRCERRS ... MPC] = &mac_readreg, + [IP6AT ... IP6AT+3] = &mac_readreg, [IP4AT ... IP4AT+6] = &mac_readreg, + [FFLT ... FFLT+6] = &mac_low11_read, + [RA ... RA+31] = &mac_readreg, + [WUPM ... WUPM+31] = &mac_readreg, + [MTA ... MTA+127] = &mac_readreg, [VFTA ... VFTA+127] = &mac_readreg, + [FFMT ... FFMT+254] = &mac_low4_read, + [FFVT ... FFVT+254] = &mac_readreg, + [PBM ... PBM+16383] = &mac_readreg, }; enum { NREADOPS = ARRAY_SIZE(macreg_readops) }; -#define putreg(x) [x] = mac_writereg +#define putreg(x) [x] = mac_writereg static void (*macreg_writeops[])(E1000State *, int, uint32_t) = { - putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC), - putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH), - putreg(RDBAL), putreg(LEDCTL), putreg(VET), - [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl, - [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics, - [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, - [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, - [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, - [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit, - [ITR] = set_16bit, - [RA ... RA+31] = &mac_writereg, - [MTA ... MTA+127] = &mac_writereg, + putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC), + putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH), + putreg(RDBAL), putreg(LEDCTL), putreg(VET), putreg(FCRUC), + putreg(TDFH), putreg(TDFT), putreg(TDFHS), putreg(TDFTS), + putreg(TDFPC), putreg(RDFH), putreg(RDFT), putreg(RDFHS), + putreg(RDFTS), putreg(RDFPC), putreg(IPAV), putreg(WUC), + putreg(WUS), putreg(AIT), + + [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl, + [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics, + [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, + [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, + [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, + [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit, + [ITR] = set_16bit, + + [IP6AT ... IP6AT+3] = &mac_writereg, [IP4AT ... IP4AT+6] = &mac_writereg, + [FFLT ... FFLT+6] = &mac_writereg, + [RA ... RA+31] = &mac_writereg, + [WUPM ... WUPM+31] = &mac_writereg, + [MTA ... MTA+127] = &mac_writereg, [VFTA ... VFTA+127] = &mac_writereg, + [FFMT ... FFMT+254] = &mac_writereg, [FFVT ... FFVT+254] = &mac_writereg, + [PBM ... PBM+16383] = &mac_writereg, }; enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) }; +enum { MAC_ACCESS_PARTIAL = 1, MAC_ACCESS_FLAG_NEEDED = 2 }; + +#define markflag(x) ((E1000_FLAG_##x << 2) | MAC_ACCESS_FLAG_NEEDED) +/* In the array below the meaning of the bits is: [f|f|f|f|f|f|n|p] + * f - flag bits (up to 6 possible flags) + * n - flag needed + * p - partially implenented */ +static const uint8_t mac_reg_access[0x8000] = { + [RDTR] = markflag(MIT), [TADV] = markflag(MIT), + [RADV] = markflag(MIT), [ITR] = markflag(MIT), + + [IPAV] = markflag(MAC), [WUC] = markflag(MAC), + [IP6AT] = markflag(MAC), [IP4AT] = markflag(MAC), + [FFVT] = markflag(MAC), [WUPM] = markflag(MAC), + [ECOL] = markflag(MAC), [MCC] = markflag(MAC), + [DC] = markflag(MAC), [TNCRS] = markflag(MAC), + [RLEC] = markflag(MAC), [XONRXC] = markflag(MAC), + [XOFFTXC] = markflag(MAC), [RFC] = markflag(MAC), + [TSCTFC] = markflag(MAC), [MGTPRC] = markflag(MAC), + [WUS] = markflag(MAC), [AIT] = markflag(MAC), + [FFLT] = markflag(MAC), [FFMT] = markflag(MAC), + [SCC] = markflag(MAC), [FCRUC] = markflag(MAC), + [LATECOL] = markflag(MAC), [COLC] = markflag(MAC), + [SEC] = markflag(MAC), [CEXTERR] = markflag(MAC), + [XONTXC] = markflag(MAC), [XOFFRXC] = markflag(MAC), + [RJC] = markflag(MAC), [RNBC] = markflag(MAC), + [MGTPDC] = markflag(MAC), [MGTPTC] = markflag(MAC), + [RUC] = markflag(MAC), [ROC] = markflag(MAC), + [GORCL] = markflag(MAC), [GORCH] = markflag(MAC), + [GOTCL] = markflag(MAC), [GOTCH] = markflag(MAC), + [BPRC] = markflag(MAC), [MPRC] = markflag(MAC), + [TSCTC] = markflag(MAC), [PRC64] = markflag(MAC), + [PRC127] = markflag(MAC), [PRC255] = markflag(MAC), + [PRC511] = markflag(MAC), [PRC1023] = markflag(MAC), + [PRC1522] = markflag(MAC), [PTC64] = markflag(MAC), + [PTC127] = markflag(MAC), [PTC255] = markflag(MAC), + [PTC511] = markflag(MAC), [PTC1023] = markflag(MAC), + [PTC1522] = markflag(MAC), [MPTC] = markflag(MAC), + [BPTC] = markflag(MAC), + + [TDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [TDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [RDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [PBM] = markflag(MAC) | MAC_ACCESS_PARTIAL, +}; + static void e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) @@ -1254,9 +1483,20 @@ e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned int index = (addr & 0x1ffff) >> 2; if (index < NWRITEOPS && macreg_writeops[index]) { - macreg_writeops[index](s, index, val); + if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED) + || (s->compat_flags & (mac_reg_access[index] >> 2))) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + DBGOUT(GENERAL, "Writing to register at offset: 0x%08x. " + "It is not fully implemented.\n", index<<2); + } + macreg_writeops[index](s, index, val); + } else { /* "flag needed" bit is set, but the flag is not active */ + DBGOUT(MMIO, "MMIO write attempt to disabled reg. addr=0x%08x\n", + index<<2); + } } else if (index < NREADOPS && macreg_readops[index]) { - DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", index<<2, val); + DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", + index<<2, val); } else { DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08"PRIx64"\n", index<<2, val); @@ -1269,11 +1509,21 @@ e1000_mmio_read(void *opaque, hwaddr addr, unsigned size) E1000State *s = opaque; unsigned int index = (addr & 0x1ffff) >> 2; - if (index < NREADOPS && macreg_readops[index]) - { - return macreg_readops[index](s, index); + if (index < NREADOPS && macreg_readops[index]) { + if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED) + || (s->compat_flags & (mac_reg_access[index] >> 2))) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + DBGOUT(GENERAL, "Reading register at offset: 0x%08x. " + "It is not fully implemented.\n", index<<2); + } + return macreg_readops[index](s, index); + } else { /* "flag needed" bit is set, but the flag is not active */ + DBGOUT(MMIO, "MMIO read attempt of disabled reg. addr=0x%08x\n", + index<<2); + } + } else { + DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2); } - DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2); return 0; } @@ -1340,7 +1590,7 @@ static int e1000_post_load(void *opaque, int version_id) E1000State *s = opaque; NetClientState *nc = qemu_get_queue(s->nic); - if (!(s->compat_flags & E1000_FLAG_MIT)) { + if (!chkflag(MIT)) { s->mac_reg[ITR] = s->mac_reg[RDTR] = s->mac_reg[RADV] = s->mac_reg[TADV] = 0; s->mit_irq_level = false; @@ -1367,7 +1617,14 @@ static bool e1000_mit_state_needed(void *opaque) { E1000State *s = opaque; - return s->compat_flags & E1000_FLAG_MIT; + return chkflag(MIT); +} + +static bool e1000_full_mac_needed(void *opaque) +{ + E1000State *s = opaque; + + return chkflag(MAC); } static const VMStateDescription vmstate_e1000_mit_state = { @@ -1385,6 +1642,17 @@ static const VMStateDescription vmstate_e1000_mit_state = { } }; +static const VMStateDescription vmstate_e1000_full_mac_state = { + .name = "e1000/full_mac_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = e1000_full_mac_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(mac_reg, E1000State, 0x8000), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_e1000 = { .name = "e1000", .version_id = 2, @@ -1464,6 +1732,7 @@ static const VMStateDescription vmstate_e1000 = { }, .subsections = (const VMStateDescription*[]) { &vmstate_e1000_mit_state, + &vmstate_e1000_full_mac_state, NULL } }; @@ -1596,6 +1865,8 @@ static Property e1000_properties[] = { compat_flags, E1000_FLAG_AUTONEG_BIT, true), DEFINE_PROP_BIT("mitigation", E1000State, compat_flags, E1000_FLAG_MIT_BIT, true), + DEFINE_PROP_BIT("extra_mac_registers", E1000State, + compat_flags, E1000_FLAG_MAC_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1647,7 +1918,7 @@ static const TypeInfo e1000_base_info = { static const E1000Info e1000_devices[] = { { - .name = "e1000-82540em", + .name = "e1000", .device_id = E1000_DEV_ID_82540EM, .revision = 0x03, .phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT, @@ -1666,11 +1937,6 @@ static const E1000Info e1000_devices[] = { }, }; -static const TypeInfo e1000_default_info = { - .name = "e1000", - .parent = "e1000-82540em", -}; - static void e1000_register_types(void) { int i; @@ -1688,7 +1954,6 @@ static void e1000_register_types(void) type_register(&type_info); } - type_register_static(&e1000_default_info); } type_init(e1000_register_types) diff --git a/qemu/hw/net/e1000_regs.h b/qemu/hw/net/e1000_regs.h index 60b96aaf1..1c40244ab 100644 --- a/qemu/hw/net/e1000_regs.h +++ b/qemu/hw/net/e1000_regs.h @@ -158,7 +158,8 @@ #define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ #define FEXTNVM_SW_CONFIG 0x0001 #define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ -#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_PBM 0x10000 /* Packet Buffer Memory - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size - RW */ #define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ #define E1000_FLASH_UPDATES 1000 #define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ @@ -191,6 +192,11 @@ #define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ #define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ #define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_RDFH 0x02410 /* Receive Data FIFO Head Register - RW */ +#define E1000_RDFT 0x02418 /* Receive Data FIFO Tail Register - RW */ +#define E1000_RDFHS 0x02420 /* Receive Data FIFO Head Saved Register - RW */ +#define E1000_RDFTS 0x02428 /* Receive Data FIFO Tail Saved Register - RW */ +#define E1000_RDFPC 0x02430 /* Receive Data FIFO Packet Count - RW */ #define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ #define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ #define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ diff --git a/qemu/hw/net/eepro100.c b/qemu/hw/net/eepro100.c index 60333b7fc..9b4b9b59d 100644 --- a/qemu/hw/net/eepro100.c +++ b/qemu/hw/net/eepro100.c @@ -40,7 +40,7 @@ * * Wake-on-LAN is not implemented. */ -#include <stddef.h> /* offsetof */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -774,6 +774,11 @@ static void tx_command(EEPRO100State *s) #if 0 uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); #endif + if (tx_buffer_size == 0) { + /* Prevent an endless loop. */ + logout("loop in %s:%u\n", __FILE__, __LINE__); + break; + } tbd_address += 8; TRACE(RXTX, logout ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", @@ -855,6 +860,10 @@ static void set_multicast_list(EEPRO100State *s) static void action_command(EEPRO100State *s) { + /* The loop below won't stop if it gets special handcrafted data. + Therefore we limit the number of iterations. */ + unsigned max_loop_count = 16; + for (;;) { bool bit_el; bool bit_s; @@ -870,6 +879,13 @@ static void action_command(EEPRO100State *s) #if 0 bool bit_sf = ((s->tx.command & COMMAND_SF) != 0); #endif + + if (max_loop_count-- == 0) { + /* Prevent an endless loop. */ + logout("loop in %s:%u\n", __FILE__, __LINE__); + break; + } + s->cu_offset = s->tx.link; TRACE(OTHER, logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", diff --git a/qemu/hw/net/etraxfs_eth.c b/qemu/hw/net/etraxfs_eth.c index d6002750f..05495ec40 100644 --- a/qemu/hw/net/etraxfs_eth.c +++ b/qemu/hw/net/etraxfs_eth.c @@ -22,10 +22,11 @@ * THE SOFTWARE. */ -#include <stdio.h> +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/cris/etraxfs.h" +#include "qemu/error-report.h" #define D(x) @@ -589,7 +590,8 @@ static int fs_eth_init(SysBusDevice *sbd) ETRAXFSEthState *s = ETRAX_FS_ETH(dev); if (!s->dma_out || !s->dma_in) { - hw_error("Unconnected ETRAX-FS Ethernet MAC.\n"); + error_report("Unconnected ETRAX-FS Ethernet MAC"); + return -1; } s->dma_out->client.push = eth_tx_push; diff --git a/qemu/hw/net/fsl_etsec/etsec.c b/qemu/hw/net/fsl_etsec/etsec.c index 0f5cf4477..1e35f7f8c 100644 --- a/qemu/hw/net/fsl_etsec/etsec.c +++ b/qemu/hw/net/fsl_etsec/etsec.c @@ -26,6 +26,7 @@ * This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. */ +#include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "trace.h" @@ -353,7 +354,7 @@ static ssize_t etsec_receive(NetClientState *nc, etsec->need_flush = false; ret = etsec_rx_ring_write(etsec, buf, size); if (ret == 0) { - /* The packet will be queued, let's flush it when buffer is avilable + /* The packet will be queued, let's flush it when buffer is available * again. */ etsec->need_flush = true; } diff --git a/qemu/hw/net/fsl_etsec/miim.c b/qemu/hw/net/fsl_etsec/miim.c index 1931b74e6..6bba01c82 100644 --- a/qemu/hw/net/fsl_etsec/miim.c +++ b/qemu/hw/net/fsl_etsec/miim.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "etsec.h" #include "registers.h" diff --git a/qemu/hw/net/fsl_etsec/registers.c b/qemu/hw/net/fsl_etsec/registers.c index a7bbfa113..46ce7a84b 100644 --- a/qemu/hw/net/fsl_etsec/registers.c +++ b/qemu/hw/net/fsl_etsec/registers.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "registers.h" const eTSEC_Register_Definition eTSEC_registers_def[] = { diff --git a/qemu/hw/net/fsl_etsec/registers.h b/qemu/hw/net/fsl_etsec/registers.h index 7ad768647..6fb96842b 100644 --- a/qemu/hw/net/fsl_etsec/registers.h +++ b/qemu/hw/net/fsl_etsec/registers.h @@ -24,7 +24,6 @@ #ifndef _ETSEC_REGISTERS_H_ #define _ETSEC_REGISTERS_H_ -#include <stdint.h> enum eTSEC_Register_Access_Type { ACC_RW = 1, /* Read/Write */ diff --git a/qemu/hw/net/fsl_etsec/rings.c b/qemu/hw/net/fsl_etsec/rings.c index 68e7b6d16..ed1de7da9 100644 --- a/qemu/hw/net/fsl_etsec/rings.c +++ b/qemu/hw/net/fsl_etsec/rings.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "net/checksum.h" #include "etsec.h" @@ -464,9 +465,7 @@ static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) etsec->rx_fcb_size = 0; } - if (etsec->rx_buffer != NULL) { - g_free(etsec->rx_buffer); - } + g_free(etsec->rx_buffer); /* Do not copy the frame for now */ etsec->rx_buffer = (uint8_t *)buf; diff --git a/qemu/hw/net/imx_fec.c b/qemu/hw/net/imx_fec.c new file mode 100644 index 000000000..e60e3380e --- /dev/null +++ b/qemu/hw/net/imx_fec.c @@ -0,0 +1,711 @@ +/* + * i.MX Fast Ethernet Controller emulation. + * + * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net> + * + * Based on Coldfire Fast Ethernet Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/net/imx_fec.h" +#include "sysemu/dma.h" + +/* For crc32 */ +#include <zlib.h> + +#ifndef DEBUG_IMX_FEC +#define DEBUG_IMX_FEC 0 +#endif + +#define FEC_PRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_FEC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_FEC, \ + __func__, ##args); \ + } \ + } while (0) + +#ifndef DEBUG_IMX_PHY +#define DEBUG_IMX_PHY 0 +#endif + +#define PHY_PRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_PHY) { \ + fprintf(stderr, "[%s.phy]%s: " fmt , TYPE_IMX_FEC, \ + __func__, ##args); \ + } \ + } while (0) + +static const VMStateDescription vmstate_imx_fec = { + .name = TYPE_IMX_FEC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_state, IMXFECState), + VMSTATE_UINT32(eir, IMXFECState), + VMSTATE_UINT32(eimr, IMXFECState), + VMSTATE_UINT32(rx_enabled, IMXFECState), + VMSTATE_UINT32(rx_descriptor, IMXFECState), + VMSTATE_UINT32(tx_descriptor, IMXFECState), + VMSTATE_UINT32(ecr, IMXFECState), + VMSTATE_UINT32(mmfr, IMXFECState), + VMSTATE_UINT32(mscr, IMXFECState), + VMSTATE_UINT32(mibc, IMXFECState), + VMSTATE_UINT32(rcr, IMXFECState), + VMSTATE_UINT32(tcr, IMXFECState), + VMSTATE_UINT32(tfwr, IMXFECState), + VMSTATE_UINT32(frsr, IMXFECState), + VMSTATE_UINT32(erdsr, IMXFECState), + VMSTATE_UINT32(etdsr, IMXFECState), + VMSTATE_UINT32(emrbr, IMXFECState), + VMSTATE_UINT32(miigsk_cfgr, IMXFECState), + VMSTATE_UINT32(miigsk_enr, IMXFECState), + + VMSTATE_UINT32(phy_status, IMXFECState), + VMSTATE_UINT32(phy_control, IMXFECState), + VMSTATE_UINT32(phy_advertise, IMXFECState), + VMSTATE_UINT32(phy_int, IMXFECState), + VMSTATE_UINT32(phy_int_mask, IMXFECState), + VMSTATE_END_OF_LIST() + } +}; + +#define PHY_INT_ENERGYON (1 << 7) +#define PHY_INT_AUTONEG_COMPLETE (1 << 6) +#define PHY_INT_FAULT (1 << 5) +#define PHY_INT_DOWN (1 << 4) +#define PHY_INT_AUTONEG_LP (1 << 3) +#define PHY_INT_PARFAULT (1 << 2) +#define PHY_INT_AUTONEG_PAGE (1 << 1) + +static void imx_fec_update(IMXFECState *s); + +/* + * The MII phy could raise a GPIO to the processor which in turn + * could be handled as an interrpt by the OS. + * For now we don't handle any GPIO/interrupt line, so the OS will + * have to poll for the PHY status. + */ +static void phy_update_irq(IMXFECState *s) +{ + imx_fec_update(s); +} + +static void phy_update_link(IMXFECState *s) +{ + /* Autonegotiation status mirrors link status. */ + if (qemu_get_queue(s->nic)->link_down) { + PHY_PRINTF("link is down\n"); + s->phy_status &= ~0x0024; + s->phy_int |= PHY_INT_DOWN; + } else { + PHY_PRINTF("link is up\n"); + s->phy_status |= 0x0024; + s->phy_int |= PHY_INT_ENERGYON; + s->phy_int |= PHY_INT_AUTONEG_COMPLETE; + } + phy_update_irq(s); +} + +static void imx_fec_set_link(NetClientState *nc) +{ + phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); +} + +static void phy_reset(IMXFECState *s) +{ + s->phy_status = 0x7809; + s->phy_control = 0x3000; + s->phy_advertise = 0x01e1; + s->phy_int_mask = 0; + s->phy_int = 0; + phy_update_link(s); +} + +static uint32_t do_phy_read(IMXFECState *s, int reg) +{ + uint32_t val; + + if (reg > 31) { + /* we only advertise one phy */ + return 0; + } + + switch (reg) { + case 0: /* Basic Control */ + val = s->phy_control; + break; + case 1: /* Basic Status */ + val = s->phy_status; + break; + case 2: /* ID1 */ + val = 0x0007; + break; + case 3: /* ID2 */ + val = 0xc0d1; + break; + case 4: /* Auto-neg advertisement */ + val = s->phy_advertise; + break; + case 5: /* Auto-neg Link Partner Ability */ + val = 0x0f71; + break; + case 6: /* Auto-neg Expansion */ + val = 1; + break; + case 29: /* Interrupt source. */ + val = s->phy_int; + s->phy_int = 0; + phy_update_irq(s); + break; + case 30: /* Interrupt mask */ + val = s->phy_int_mask; + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", + TYPE_IMX_FEC, __func__, reg); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, reg); + val = 0; + break; + } + + PHY_PRINTF("read 0x%04x @ %d\n", val, reg); + + return val; +} + +static void do_phy_write(IMXFECState *s, int reg, uint32_t val) +{ + PHY_PRINTF("write 0x%04x @ %d\n", val, reg); + + if (reg > 31) { + /* we only advertise one phy */ + return; + } + + switch (reg) { + case 0: /* Basic Control */ + if (val & 0x8000) { + phy_reset(s); + } else { + s->phy_control = val & 0x7980; + /* Complete autonegotiation immediately. */ + if (val & 0x1000) { + s->phy_status |= 0x0020; + } + } + break; + case 4: /* Auto-neg advertisement */ + s->phy_advertise = (val & 0x2d7f) | 0x80; + break; + case 30: /* Interrupt mask */ + s->phy_int_mask = val & 0xff; + phy_update_irq(s); + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", + TYPE_IMX_FEC, __func__, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, reg); + break; + } +} + +static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) +{ + dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); +} + +static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) +{ + dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); +} + +static void imx_fec_update(IMXFECState *s) +{ + uint32_t active; + uint32_t changed; + + active = s->eir & s->eimr; + changed = active ^ s->irq_state; + if (changed) { + qemu_set_irq(s->irq, active); + } + s->irq_state = active; +} + +static void imx_fec_do_tx(IMXFECState *s) +{ + int frame_size = 0; + uint8_t frame[FEC_MAX_FRAME_SIZE]; + uint8_t *ptr = frame; + uint32_t addr = s->tx_descriptor; + + while (1) { + IMXFECBufDesc bd; + int len; + + imx_fec_read_bd(&bd, addr); + FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n", + addr, bd.flags, bd.length, bd.data); + if ((bd.flags & FEC_BD_R) == 0) { + /* Run out of descriptors to transmit. */ + break; + } + len = bd.length; + if (frame_size + len > FEC_MAX_FRAME_SIZE) { + len = FEC_MAX_FRAME_SIZE - frame_size; + s->eir |= FEC_INT_BABT; + } + dma_memory_read(&address_space_memory, bd.data, ptr, len); + ptr += len; + frame_size += len; + if (bd.flags & FEC_BD_L) { + /* Last buffer in frame. */ + qemu_send_packet(qemu_get_queue(s->nic), frame, len); + ptr = frame; + frame_size = 0; + s->eir |= FEC_INT_TXF; + } + s->eir |= FEC_INT_TXB; + bd.flags &= ~FEC_BD_R; + /* Write back the modified descriptor. */ + imx_fec_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & FEC_BD_W) != 0) { + addr = s->etdsr; + } else { + addr += 8; + } + } + + s->tx_descriptor = addr; + + imx_fec_update(s); +} + +static void imx_fec_enable_rx(IMXFECState *s) +{ + IMXFECBufDesc bd; + uint32_t tmp; + + imx_fec_read_bd(&bd, s->rx_descriptor); + + tmp = ((bd.flags & FEC_BD_E) != 0); + + if (!tmp) { + FEC_PRINTF("RX buffer full\n"); + } else if (!s->rx_enabled) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + + s->rx_enabled = tmp; +} + +static void imx_fec_reset(DeviceState *d) +{ + IMXFECState *s = IMX_FEC(d); + + /* Reset the FEC */ + s->eir = 0; + s->eimr = 0; + s->rx_enabled = 0; + s->ecr = 0; + s->mscr = 0; + s->mibc = 0xc0000000; + s->rcr = 0x05ee0001; + s->tcr = 0; + s->tfwr = 0; + s->frsr = 0x500; + s->miigsk_cfgr = 0; + s->miigsk_enr = 0x6; + + /* We also reset the PHY */ + phy_reset(s); +} + +static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) +{ + IMXFECState *s = IMX_FEC(opaque); + + FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", addr); + + switch (addr & 0x3ff) { + case 0x004: + return s->eir; + case 0x008: + return s->eimr; + case 0x010: + return s->rx_enabled ? (1 << 24) : 0; /* RDAR */ + case 0x014: + return 0; /* TDAR */ + case 0x024: + return s->ecr; + case 0x040: + return s->mmfr; + case 0x044: + return s->mscr; + case 0x064: + return s->mibc; /* MIBC */ + case 0x084: + return s->rcr; + case 0x0c4: + return s->tcr; + case 0x0e4: /* PALR */ + return (s->conf.macaddr.a[0] << 24) + | (s->conf.macaddr.a[1] << 16) + | (s->conf.macaddr.a[2] << 8) + | s->conf.macaddr.a[3]; + break; + case 0x0e8: /* PAUR */ + return (s->conf.macaddr.a[4] << 24) + | (s->conf.macaddr.a[5] << 16) + | 0x8808; + case 0x0ec: + return 0x10000; /* OPD */ + case 0x118: + return 0; + case 0x11c: + return 0; + case 0x120: + return 0; + case 0x124: + return 0; + case 0x144: + return s->tfwr; + case 0x14c: + return 0x600; + case 0x150: + return s->frsr; + case 0x180: + return s->erdsr; + case 0x184: + return s->etdsr; + case 0x188: + return s->emrbr; + case 0x300: + return s->miigsk_cfgr; + case 0x308: + return s->miigsk_enr; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); + return 0; + } +} + +static void imx_fec_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + IMXFECState *s = IMX_FEC(opaque); + + FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr); + + switch (addr & 0x3ff) { + case 0x004: /* EIR */ + s->eir &= ~value; + break; + case 0x008: /* EIMR */ + s->eimr = value; + break; + case 0x010: /* RDAR */ + if ((s->ecr & FEC_EN) && !s->rx_enabled) { + imx_fec_enable_rx(s); + } + break; + case 0x014: /* TDAR */ + if (s->ecr & FEC_EN) { + imx_fec_do_tx(s); + } + break; + case 0x024: /* ECR */ + s->ecr = value; + if (value & FEC_RESET) { + imx_fec_reset(DEVICE(s)); + } + if ((s->ecr & FEC_EN) == 0) { + s->rx_enabled = 0; + } + break; + case 0x040: /* MMFR */ + /* store the value */ + s->mmfr = value; + if (extract32(value, 28, 1)) { + do_phy_write(s, extract32(value, 18, 9), extract32(value, 0, 16)); + } else { + s->mmfr = do_phy_read(s, extract32(value, 18, 9)); + } + /* raise the interrupt as the PHY operation is done */ + s->eir |= FEC_INT_MII; + break; + case 0x044: /* MSCR */ + s->mscr = value & 0xfe; + break; + case 0x064: /* MIBC */ + /* TODO: Implement MIB. */ + s->mibc = (value & 0x80000000) ? 0xc0000000 : 0; + break; + case 0x084: /* RCR */ + s->rcr = value & 0x07ff003f; + /* TODO: Implement LOOP mode. */ + break; + case 0x0c4: /* TCR */ + /* We transmit immediately, so raise GRA immediately. */ + s->tcr = value; + if (value & 1) { + s->eir |= FEC_INT_GRA; + } + break; + case 0x0e4: /* PALR */ + s->conf.macaddr.a[0] = value >> 24; + s->conf.macaddr.a[1] = value >> 16; + s->conf.macaddr.a[2] = value >> 8; + s->conf.macaddr.a[3] = value; + break; + case 0x0e8: /* PAUR */ + s->conf.macaddr.a[4] = value >> 24; + s->conf.macaddr.a[5] = value >> 16; + break; + case 0x0ec: /* OPDR */ + break; + case 0x118: /* IAUR */ + case 0x11c: /* IALR */ + case 0x120: /* GAUR */ + case 0x124: /* GALR */ + /* TODO: implement MAC hash filtering. */ + break; + case 0x144: /* TFWR */ + s->tfwr = value & 3; + break; + case 0x14c: /* FRBR */ + /* FRBR writes ignored. */ + break; + case 0x150: /* FRSR */ + s->frsr = (value & 0x3fc) | 0x400; + break; + case 0x180: /* ERDSR */ + s->erdsr = value & ~3; + s->rx_descriptor = s->erdsr; + break; + case 0x184: /* ETDSR */ + s->etdsr = value & ~3; + s->tx_descriptor = s->etdsr; + break; + case 0x188: /* EMRBR */ + s->emrbr = value & 0x7f0; + break; + case 0x300: /* MIIGSK_CFGR */ + s->miigsk_cfgr = value & 0x53; + break; + case 0x308: /* MIIGSK_ENR */ + s->miigsk_enr = (value & 0x2) ? 0x6 : 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); + break; + } + + imx_fec_update(s); +} + +static int imx_fec_can_receive(NetClientState *nc) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + + return s->rx_enabled; +} + +static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, + size_t len) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + IMXFECBufDesc bd; + uint32_t flags = 0; + uint32_t addr; + uint32_t crc; + uint32_t buf_addr; + uint8_t *crc_ptr; + unsigned int buf_len; + size_t size = len; + + FEC_PRINTF("len %d\n", (int)size); + + if (!s->rx_enabled) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", + TYPE_IMX_FEC, __func__); + return 0; + } + + /* 4 bytes for the CRC. */ + size += 4; + crc = cpu_to_be32(crc32(~0, buf, size)); + crc_ptr = (uint8_t *) &crc; + + /* Huge frames are truncted. */ + if (size > FEC_MAX_FRAME_SIZE) { + size = FEC_MAX_FRAME_SIZE; + flags |= FEC_BD_TR | FEC_BD_LG; + } + + /* Frames larger than the user limit just set error flags. */ + if (size > (s->rcr >> 16)) { + flags |= FEC_BD_LG; + } + + addr = s->rx_descriptor; + while (size > 0) { + imx_fec_read_bd(&bd, addr); + if ((bd.flags & FEC_BD_E) == 0) { + /* No descriptors available. Bail out. */ + /* + * FIXME: This is wrong. We should probably either + * save the remainder for when more RX buffers are + * available, or flag an error. + */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", + TYPE_IMX_FEC, __func__); + break; + } + buf_len = (size <= s->emrbr) ? size : s->emrbr; + bd.length = buf_len; + size -= buf_len; + + FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length); + + /* The last 4 bytes are the CRC. */ + if (size < 4) { + buf_len += size - 4; + } + buf_addr = bd.data; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + buf += buf_len; + if (size < 4) { + dma_memory_write(&address_space_memory, buf_addr + buf_len, + crc_ptr, 4 - size); + crc_ptr += 4 - size; + } + bd.flags &= ~FEC_BD_E; + if (size == 0) { + /* Last buffer in frame. */ + bd.flags |= flags | FEC_BD_L; + FEC_PRINTF("rx frame flags %04x\n", bd.flags); + s->eir |= FEC_INT_RXF; + } else { + s->eir |= FEC_INT_RXB; + } + imx_fec_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & FEC_BD_W) != 0) { + addr = s->erdsr; + } else { + addr += 8; + } + } + s->rx_descriptor = addr; + imx_fec_enable_rx(s); + imx_fec_update(s); + return len; +} + +static const MemoryRegionOps imx_fec_ops = { + .read = imx_fec_read, + .write = imx_fec_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void imx_fec_cleanup(NetClientState *nc) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + + s->nic = NULL; +} + +static NetClientInfo net_imx_fec_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = imx_fec_can_receive, + .receive = imx_fec_receive, + .cleanup = imx_fec_cleanup, + .link_status_changed = imx_fec_set_link, +}; + + +static void imx_fec_realize(DeviceState *dev, Error **errp) +{ + IMXFECState *s = IMX_FEC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s, + TYPE_IMX_FEC, 0x400); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->conf.peers.ncs[0] = nd_table[0].netdev; + + s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf, + object_get_typename(OBJECT(dev)), DEVICE(dev)->id, + s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static Property imx_fec_properties[] = { + DEFINE_NIC_PROPERTIES(IMXFECState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void imx_fec_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_imx_fec; + dc->reset = imx_fec_reset; + dc->props = imx_fec_properties; + dc->realize = imx_fec_realize; + dc->desc = "i.MX FEC Ethernet Controller"; +} + +static const TypeInfo imx_fec_info = { + .name = TYPE_IMX_FEC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXFECState), + .class_init = imx_fec_class_init, +}; + +static void imx_fec_register_types(void) +{ + type_register_static(&imx_fec_info); +} + +type_init(imx_fec_register_types) diff --git a/qemu/hw/net/lan9118.c b/qemu/hw/net/lan9118.c index 4f0e840f0..08dc474d6 100644 --- a/qemu/hw/net/lan9118.c +++ b/qemu/hw/net/lan9118.c @@ -10,6 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/devices.h" @@ -56,6 +57,8 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0) #define CSR_E2P_CMD 0xb0 #define CSR_E2P_DATA 0xb4 +#define E2P_CMD_MAC_ADDR_LOADED 0x100 + /* IRQ_CFG */ #define IRQ_INT 0x00001000 #define IRQ_EN 0x00000100 @@ -352,14 +355,14 @@ static void lan9118_reload_eeprom(lan9118_state *s) { int i; if (s->eeprom[0] != 0xa5) { - s->e2p_cmd &= ~0x10; + s->e2p_cmd &= ~E2P_CMD_MAC_ADDR_LOADED; DPRINTF("MACADDR load failed\n"); return; } for (i = 0; i < 6; i++) { s->conf.macaddr.a[i] = s->eeprom[i + 1]; } - s->e2p_cmd |= 0x10; + s->e2p_cmd |= E2P_CMD_MAC_ADDR_LOADED; DPRINTF("MACADDR loaded from eeprom\n"); lan9118_mac_changed(s); } @@ -902,7 +905,8 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val) */ break; default: - hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n", + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118: Unimplemented MAC register write: %d = 0x%x\n", s->mac_cmd & 0xf, val); } } @@ -930,14 +934,16 @@ static uint32_t do_mac_read(lan9118_state *s, int reg) case MAC_FLOW: return s->mac_flow; default: - hw_error("lan9118: Unimplemented MAC register read: %d\n", + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118: Unimplemented MAC register read: %d\n", s->mac_cmd & 0xf); + return 0; } } static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr) { - s->e2p_cmd = (s->e2p_cmd & 0x10) | (cmd << 28) | addr; + s->e2p_cmd = (s->e2p_cmd & E2P_CMD_MAC_ADDR_LOADED) | (cmd << 28) | addr; switch (cmd) { case 0: s->e2p_data = s->eeprom[addr]; @@ -1128,7 +1134,8 @@ static void lan9118_writel(void *opaque, hwaddr offset, break; default: - hw_error("lan9118_write: Bad reg 0x%x = %x\n", (int)offset, (int)val); + qemu_log_mask(LOG_GUEST_ERROR, "lan9118_write: Bad reg 0x%x = %x\n", + (int)offset, (int)val); break; } lan9118_update(s); @@ -1246,7 +1253,7 @@ static uint64_t lan9118_readl(void *opaque, hwaddr offset, case CSR_E2P_DATA: return s->e2p_data; } - hw_error("lan9118_read: Bad reg 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "lan9118_read: Bad reg 0x%x\n", (int)offset); return 0; } diff --git a/qemu/hw/net/lance.c b/qemu/hw/net/lance.c index 780b39d65..6253d2103 100644 --- a/qemu/hw/net/lance.c +++ b/qemu/hw/net/lance.c @@ -35,6 +35,7 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "qemu/timer.h" diff --git a/qemu/hw/net/mcf_fec.c b/qemu/hw/net/mcf_fec.c index 21928f9f3..7c0398ed9 100644 --- a/qemu/hw/net/mcf_fec.c +++ b/qemu/hw/net/mcf_fec.c @@ -5,6 +5,7 @@ * * This code is licensed under the GPL */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "net/net.h" #include "hw/m68k/mcf.h" diff --git a/qemu/hw/net/milkymist-minimac2.c b/qemu/hw/net/milkymist-minimac2.c index 5d1cf0851..1e147c33c 100644 --- a/qemu/hw/net/milkymist-minimac2.c +++ b/qemu/hw/net/milkymist-minimac2.c @@ -22,6 +22,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" /* FIXME: why does this use TARGET_PAGE_ALIGN? */ #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" @@ -463,7 +467,7 @@ static int milkymist_minimac2_init(SysBusDevice *sbd) /* register buffers memory */ memory_region_init_ram(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers", - buffers_size, &error_abort); + buffers_size, &error_fatal); vmstate_register_ram_global(&s->buffers); s->rx0_buf = memory_region_get_ram_ptr(&s->buffers); s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE; diff --git a/qemu/hw/net/mipsnet.c b/qemu/hw/net/mipsnet.c index f261011a2..740cd98ff 100644 --- a/qemu/hw/net/mipsnet.c +++ b/qemu/hw/net/mipsnet.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "net/net.h" #include "trace.h" diff --git a/qemu/hw/net/ne2000-isa.c b/qemu/hw/net/ne2000-isa.c index 17e7199f7..a7f5a9464 100644 --- a/qemu/hw/net/ne2000-isa.c +++ b/qemu/hw/net/ne2000-isa.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" @@ -28,6 +29,7 @@ #include "net/net.h" #include "ne2000.h" #include "exec/address-spaces.h" +#include "qapi/error.h" #include "qapi/visitor.h" #define TYPE_ISA_NE2000 "ne2k_isa" @@ -44,7 +46,6 @@ typedef struct ISANE2000State { static NetClientInfo net_ne2000_isa_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = ne2000_can_receive, .receive = ne2000_receive, }; @@ -94,24 +95,26 @@ static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } -static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { ISANE2000State *isa = ISA_NE2000(obj); NE2000State *s = &isa->ne2000; - visit_type_int32(v, &s->c.bootindex, name, errp); + visit_type_int32(v, name, &s->c.bootindex, errp); } -static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { ISANE2000State *isa = ISA_NE2000(obj); NE2000State *s = &isa->ne2000; int32_t boot_index; Error *local_err = NULL; - visit_type_int32(v, &boot_index, name, &local_err); + visit_type_int32(v, name, &boot_index, &local_err); if (local_err) { goto out; } diff --git a/qemu/hw/net/ne2000.c b/qemu/hw/net/ne2000.c index 2bdb4c927..f0feaf96b 100644 --- a/qemu/hw/net/ne2000.c +++ b/qemu/hw/net/ne2000.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -154,6 +155,10 @@ static int ne2000_buffer_full(NE2000State *s) { int avail, index, boundary; + if (s->stop <= s->start) { + return 1; + } + index = s->curpag << 8; boundary = s->boundary << 8; if (index < boundary) @@ -165,15 +170,6 @@ static int ne2000_buffer_full(NE2000State *s) return 0; } -int ne2000_can_receive(NetClientState *nc) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - - if (s->cmd & E8390_STOP) - return 1; - return !ne2000_buffer_full(s); -} - #define MIN_BUF_SIZE 60 ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) @@ -476,8 +472,9 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, uint32_t val) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + if (addr < 32 + || (addr >= NE2000_PMEM_START + && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { stl_le_p(s->mem + addr, val); } } @@ -506,8 +503,9 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + if (addr < 32 + || (addr >= NE2000_PMEM_START + && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { return ldl_le_p(s->mem + addr); } else { return 0xffffffff; @@ -716,7 +714,6 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) static NetClientInfo net_ne2000_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = ne2000_can_receive, .receive = ne2000_receive, }; diff --git a/qemu/hw/net/ne2000.h b/qemu/hw/net/ne2000.h index e500306aa..d022b28fc 100644 --- a/qemu/hw/net/ne2000.h +++ b/qemu/hw/net/ne2000.h @@ -34,7 +34,6 @@ typedef struct NE2000State { void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size); extern const VMStateDescription vmstate_ne2000; void ne2000_reset(NE2000State *s); -int ne2000_can_receive(NetClientState *nc); ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_); #endif diff --git a/qemu/hw/net/opencores_eth.c b/qemu/hw/net/opencores_eth.c index 3642046ef..c6094fbb5 100644 --- a/qemu/hw/net/opencores_eth.c +++ b/qemu/hw/net/opencores_eth.c @@ -31,6 +31,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "net/net.h" @@ -84,7 +85,7 @@ static void mii_reset(Mii *s) { memset(s->regs, 0, sizeof(s->regs)); s->regs[MII_BMCR] = 0x1000; - s->regs[MII_BMSR] = 0x7848; /* no ext regs */ + s->regs[MII_BMSR] = 0x7868; /* no ext regs */ s->regs[MII_PHYIDR1] = 0x2000; s->regs[MII_PHYIDR2] = 0x5c90; s->regs[MII_ANAR] = 0x01e1; diff --git a/qemu/hw/net/pcnet-pci.c b/qemu/hw/net/pcnet-pci.c index b4d60b812..595439a65 100644 --- a/qemu/hw/net/pcnet-pci.c +++ b/qemu/hw/net/pcnet-pci.c @@ -27,6 +27,7 @@ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000 */ +#include "qemu/osdep.h" #include "hw/pci/pci.h" #include "net/net.h" #include "hw/loader.h" diff --git a/qemu/hw/net/pcnet.c b/qemu/hw/net/pcnet.c index 34373767d..198a01f92 100644 --- a/qemu/hw/net/pcnet.c +++ b/qemu/hw/net/pcnet.c @@ -35,6 +35,7 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt */ +#include "qemu/osdep.h" #include "hw/qdev.h" #include "net/net.h" #include "qemu/timer.h" @@ -670,8 +671,7 @@ static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx) static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time) { int64_t next_time = current_time + - muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)), - get_ticks_per_sec(), 33000000L); + (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30; if (next_time <= current_time) next_time = current_time + 1; return next_time; @@ -1065,6 +1065,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) int pktcount = 0; if (!s->looptest) { + if (size > 4092) { +#ifdef PCNET_DEBUG_RMD + fprintf(stderr, "pcnet: truncates rx packet.\n"); +#endif + size = 4092; + } memcpy(src, buf, size); /* no need to compute the CRC */ src[size] = 0; @@ -1085,7 +1091,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) uint32_t fcs = ~0; uint8_t *p = src; - while (p != &src[size-4]) + while (p != &src[size]) CRC(fcs, *p++); crc_err = (*(uint32_t *)p != htonl(fcs)); } @@ -1234,8 +1240,10 @@ static void pcnet_transmit(PCNetState *s) bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); /* if multi-tmd packet outsizes s->buffer then skip it silently. - Note: this is not what real hw does */ - if (s->xmit_pos + bcnt > sizeof(s->buffer)) { + * Note: this is not what real hw does. + * Last four bytes of s->buffer are used to store CRC FCS code. + */ + if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) { s->xmit_pos = -1; goto txdone; } diff --git a/qemu/hw/net/rocker/qmp-norocker.c b/qemu/hw/net/rocker/qmp-norocker.c index 49b498b64..6acbcdb02 100644 --- a/qemu/hw/net/rocker/qmp-norocker.c +++ b/qemu/hw/net/rocker/qmp-norocker.c @@ -15,6 +15,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qmp-commands.h" #include "qapi/qmp/qerror.h" diff --git a/qemu/hw/net/rocker/rocker.c b/qemu/hw/net/rocker/rocker.c index 47d080fd3..30f2ce417 100644 --- a/qemu/hw/net/rocker/rocker.c +++ b/qemu/hw/net/rocker/rocker.c @@ -15,6 +15,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci/msix.h" @@ -42,6 +43,7 @@ struct rocker { /* switch configuration */ char *name; /* switch name */ + char *world_name; /* world name */ uint32_t fp_ports; /* front-panel port count */ NICPeers *fp_ports_peers; MACAddr fp_start_macaddr; /* front-panel port 0 mac addr */ @@ -101,8 +103,7 @@ RockerSwitch *qmp_query_rocker(const char *name, Error **errp) r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } @@ -122,8 +123,7 @@ RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } @@ -234,6 +234,9 @@ static int tx_consume(Rocker *r, DescInfo *info) frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]); frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]); + if (iovcnt >= ROCKER_TX_FRAGS_MAX) { + goto err_too_many_frags; + } iov[iovcnt].iov_len = frag_len; iov[iovcnt].iov_base = g_malloc(frag_len); if (!iov[iovcnt].iov_base) { @@ -246,10 +249,7 @@ static int tx_consume(Rocker *r, DescInfo *info) err = -ROCKER_ENXIO; goto err_bad_io; } - - if (++iovcnt > ROCKER_TX_FRAGS_MAX) { - goto err_too_many_frags; - } + iovcnt++; } if (iovcnt) { @@ -265,9 +265,7 @@ err_bad_io: err_no_mem: err_bad_attr: for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) { - if (iov[i].iov_base) { - g_free(iov[i].iov_base); - } + g_free(iov[i].iov_base); } return err; @@ -403,7 +401,13 @@ static int cmd_set_port_settings(Rocker *r, if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) { mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]); - fp_port_set_world(fp_port, r->worlds[mode]); + if (mode >= ROCKER_WORLD_TYPE_MAX) { + return -ROCKER_EINVAL; + } + /* We don't support world change. */ + if (!fp_port_check_world(fp_port, r->worlds[mode])) { + return -ROCKER_EINVAL; + } } if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]) { @@ -1283,6 +1287,18 @@ static void rocker_msix_uninit(Rocker *r) rocker_msix_vectors_unuse(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports)); } +static World *rocker_world_type_by_name(Rocker *r, const char *name) +{ + int i; + + for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { + if (strcmp(name, world_name(r->worlds[i])) == 0) { + return r->worlds[i]; + } + } + return NULL; +} + static int pci_rocker_init(PCIDevice *dev) { Rocker *r = to_rocker(dev); @@ -1294,14 +1310,27 @@ static int pci_rocker_init(PCIDevice *dev) /* allocate worlds */ r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r); - r->world_dflt = r->worlds[ROCKER_WORLD_TYPE_OF_DPA]; for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { if (!r->worlds[i]) { + err = -ENOMEM; goto err_world_alloc; } } + if (!r->world_name) { + r->world_name = g_strdup(world_name(r->worlds[ROCKER_WORLD_TYPE_OF_DPA])); + } + + r->world_dflt = rocker_world_type_by_name(r, r->world_name); + if (!r->world_dflt) { + fprintf(stderr, + "rocker: requested world \"%s\" does not exist\n", + r->world_name); + err = -EINVAL; + goto err_world_type_by_name; + } + /* set up memory-mapped region at BAR0 */ memory_region_init_io(&r->mmio, OBJECT(r), &rocker_mmio_ops, r, @@ -1364,7 +1393,7 @@ static int pci_rocker_init(PCIDevice *dev) r->fp_ports = ROCKER_FP_PORTS_MAX; } - r->rings = g_malloc(sizeof(DescRing *) * rocker_pci_ring_count(r)); + r->rings = g_new(DescRing *, rocker_pci_ring_count(r)); if (!r->rings) { goto err_rings_alloc; } @@ -1435,6 +1464,7 @@ err_duplicate: err_msix_init: object_unparent(OBJECT(&r->msix_bar)); object_unparent(OBJECT(&r->mmio)); +err_world_type_by_name: err_world_alloc: for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { if (r->worlds[i]) { @@ -1506,6 +1536,7 @@ static void rocker_reset(DeviceState *dev) static Property rocker_properties[] = { DEFINE_PROP_STRING("name", Rocker, name), + DEFINE_PROP_STRING("world", Rocker, world_name), DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker, fp_start_macaddr), DEFINE_PROP_UINT64("switch_id", Rocker, diff --git a/qemu/hw/net/rocker/rocker_desc.c b/qemu/hw/net/rocker/rocker_desc.c index 9d896fe47..ac02797b7 100644 --- a/qemu/hw/net/rocker/rocker_desc.c +++ b/qemu/hw/net/rocker/rocker_desc.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "net/net.h" #include "hw/hw.h" #include "hw/pci/pci.h" @@ -136,15 +137,13 @@ bool desc_ring_set_size(DescRing *ring, uint32_t size) } for (i = 0; i < ring->size; i++) { - if (ring->info[i].buf) { - g_free(ring->info[i].buf); - } + g_free(ring->info[i].buf); } ring->size = size; ring->head = ring->tail = 0; - ring->info = g_realloc(ring->info, size * sizeof(DescInfo)); + ring->info = g_renew(DescInfo, ring->info, size); if (!ring->info) { return false; } @@ -347,7 +346,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index) { DescRing *ring; - ring = g_malloc0(sizeof(DescRing)); + ring = g_new0(DescRing, 1); if (!ring) { return NULL; } @@ -360,9 +359,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index) void desc_ring_free(DescRing *ring) { - if (ring->info) { - g_free(ring->info); - } + g_free(ring->info); g_free(ring); } diff --git a/qemu/hw/net/rocker/rocker_fp.c b/qemu/hw/net/rocker/rocker_fp.c index c693ae508..0149899c6 100644 --- a/qemu/hw/net/rocker/rocker_fp.c +++ b/qemu/hw/net/rocker/rocker_fp.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "net/clients.h" #include "rocker.h" @@ -185,6 +186,11 @@ void fp_port_set_world(FpPort *port, World *world) port->world = world; } +bool fp_port_check_world(FpPort *port, World *world) +{ + return port->world == world; +} + bool fp_port_enabled(FpPort *port) { return port->enabled; @@ -218,7 +224,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name, MACAddr *start_mac, unsigned int index, NICPeers *peers) { - FpPort *port = g_malloc0(sizeof(FpPort)); + FpPort *port = g_new0(FpPort, 1); if (!port) { return NULL; diff --git a/qemu/hw/net/rocker/rocker_fp.h b/qemu/hw/net/rocker/rocker_fp.h index ab80fd833..04592bbfd 100644 --- a/qemu/hw/net/rocker/rocker_fp.h +++ b/qemu/hw/net/rocker/rocker_fp.h @@ -40,6 +40,7 @@ int fp_port_set_settings(FpPort *port, uint32_t speed, bool fp_port_from_pport(uint32_t pport, uint32_t *port); World *fp_port_get_world(FpPort *port); void fp_port_set_world(FpPort *port, World *world); +bool fp_port_check_world(FpPort *port, World *world); bool fp_port_enabled(FpPort *port); void fp_port_enable(FpPort *port); void fp_port_disable(FpPort *port); diff --git a/qemu/hw/net/rocker/rocker_of_dpa.c b/qemu/hw/net/rocker/rocker_of_dpa.c index 874fb01d6..0a134ebca 100644 --- a/qemu/hw/net/rocker/rocker_of_dpa.c +++ b/qemu/hw/net/rocker/rocker_of_dpa.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "net/eth.h" #include "qemu/iov.h" #include "qemu/timer.h" @@ -367,7 +368,7 @@ static OfDpaFlow *of_dpa_flow_alloc(uint64_t cookie) OfDpaFlow *flow; int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000; - flow = g_malloc0(sizeof(OfDpaFlow)); + flow = g_new0(OfDpaFlow, 1); if (!flow) { return NULL; } @@ -811,7 +812,7 @@ static int of_dpa_group_get_stats(OfDpa *of_dpa, uint32_t id) static OfDpaGroup *of_dpa_group_alloc(uint32_t id) { - OfDpaGroup *group = g_malloc0(sizeof(OfDpaGroup)); + OfDpaGroup *group = g_new0(OfDpaGroup, 1); if (!group) { return NULL; @@ -2039,15 +2040,14 @@ static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group, group->l2_flood.group_count = rocker_tlv_get_le16(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT]); - tlvs = g_malloc0((group->l2_flood.group_count + 1) * - sizeof(RockerTlv *)); + tlvs = g_new0(RockerTlv *, group->l2_flood.group_count + 1); if (!tlvs) { return -ROCKER_ENOMEM; } g_free(group->l2_flood.group_ids); group->l2_flood.group_ids = - g_malloc0(group->l2_flood.group_count * sizeof(uint32_t)); + g_new0(uint32_t, group->l2_flood.group_count); if (!group->l2_flood.group_ids) { err = -ROCKER_ENOMEM; goto err_out; @@ -2463,15 +2463,13 @@ RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); if (!w) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s doesn't have OF-DPA world", name); + error_setg(errp, "rocker %s doesn't have OF-DPA world", name); return NULL; } @@ -2598,15 +2596,13 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, r = rocker_find(name); if (!r) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s not found", name); + error_setg(errp, "rocker %s not found", name); return NULL; } w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); if (!w) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "rocker %s doesn't have OF-DPA world", name); + error_setg(errp, "rocker %s doesn't have OF-DPA world", name); return NULL; } @@ -2618,6 +2614,7 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, } static WorldOps of_dpa_ops = { + .name = "ofdpa", .init = of_dpa_init, .uninit = of_dpa_uninit, .ig = of_dpa_ig, diff --git a/qemu/hw/net/rocker/rocker_world.c b/qemu/hw/net/rocker/rocker_world.c index a6b18f175..89777e968 100644 --- a/qemu/hw/net/rocker/rocker_world.c +++ b/qemu/hw/net/rocker/rocker_world.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include "qemu/osdep.h" #include "qemu/iov.h" #include "rocker.h" @@ -97,10 +98,5 @@ enum rocker_world_type world_type(World *world) const char *world_name(World *world) { - switch (world->type) { - case ROCKER_WORLD_TYPE_OF_DPA: - return "OF_DPA"; - default: - return "unknown"; - } + return world->ops->name; } diff --git a/qemu/hw/net/rocker/rocker_world.h b/qemu/hw/net/rocker/rocker_world.h index 18d277b92..58ade4733 100644 --- a/qemu/hw/net/rocker/rocker_world.h +++ b/qemu/hw/net/rocker/rocker_world.h @@ -33,6 +33,7 @@ typedef int (world_cmd)(World *world, DescInfo *info, RockerTlv *cmd_info_tlv); typedef struct world_ops { + const char *name; world_init *init; world_uninit *uninit; world_ig *ig; diff --git a/qemu/hw/net/rtl8139.c b/qemu/hw/net/rtl8139.c index edbb61ccf..1e5ec149f 100644 --- a/qemu/hw/net/rtl8139.c +++ b/qemu/hw/net/rtl8139.c @@ -43,12 +43,13 @@ * Added rx/tx buffer reset when enabling rx/tx operation * * 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only - * when strictly needed (required for for + * when strictly needed (required for * Darwin) * 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading */ /* For crc32 */ +#include "qemu/osdep.h" #include <zlib.h> #include "hw/hw.h" @@ -56,6 +57,7 @@ #include "sysemu/dma.h" #include "qemu/timer.h" #include "net/net.h" +#include "net/eth.h" #include "hw/loader.h" #include "sysemu/sysemu.h" #include "qemu/iov.h" @@ -63,7 +65,7 @@ /* debug RTL8139 card */ //#define DEBUG_RTL8139 1 -#define PCI_FREQUENCY 33000000L +#define PCI_PERIOD 30 /* 30 ns period = 33.333333 Mhz frequency */ #define SET_MASKED(input, mask, curr) \ ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) ) @@ -72,11 +74,7 @@ #define MOD2(input, size) \ ( ( input ) & ( size - 1 ) ) -#define ETHER_ADDR_LEN 6 #define ETHER_TYPE_LEN 2 -#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) -#define ETH_P_IP 0x0800 /* Internet Protocol packet */ -#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ #define ETH_MTU 1500 #define VLAN_TCI_LEN 2 @@ -1016,8 +1014,8 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* write VLAN info to descriptor variables. */ if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *) - &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) { - dot1q_buf = &buf[ETHER_ADDR_LEN * 2]; + &buf[ETH_ALEN * 2]) == ETH_P_VLAN) { + dot1q_buf = &buf[ETH_ALEN * 2]; size -= VLAN_HLEN; /* if too small buffer, use the tailroom added duing expansion */ if (size < MIN_BUF_SIZE) { @@ -1058,10 +1056,10 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* receive/copy to target memory */ if (dot1q_buf) { - pci_dma_write(d, rx_addr, buf, 2 * ETHER_ADDR_LEN); - pci_dma_write(d, rx_addr + 2 * ETHER_ADDR_LEN, - buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN, - size - 2 * ETHER_ADDR_LEN); + pci_dma_write(d, rx_addr, buf, 2 * ETH_ALEN); + pci_dma_write(d, rx_addr + 2 * ETH_ALEN, + buf + 2 * ETH_ALEN + VLAN_HLEN, + size - 2 * ETH_ALEN); } else { pci_dma_write(d, rx_addr, buf, size); } @@ -1148,7 +1146,9 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* if receiver buffer is empty then avail == 0 */ - if (avail != 0 && size + 8 >= avail) +#define RX_ALIGN(x) (((x) + 3) & ~0x3) + + if (avail != 0 && RX_ALIGN(size + 8) >= avail) { DPRINTF("rx overflow: rx buffer length %d head 0x%04x " "read 0x%04x === available 0x%04x need 0x%04x\n", @@ -1157,7 +1157,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t s->IntrStatus |= RxOverflow; ++s->RxMissed; rtl8139_update_irq(s); - return size_; + return 0; } packet_header |= RxStatusOK; @@ -1176,7 +1176,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t rtl8139_write_buffer(s, (uint8_t *)&val, 4); /* correct buffer write pointer */ - s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize); + s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize); /* now we can signal we have received something */ @@ -1783,12 +1783,12 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, return; } - if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) { + if (dot1q_buf && size >= ETH_ALEN * 2) { iov = (struct iovec[3]) { - { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 }, + { .iov_base = buf, .iov_len = ETH_ALEN * 2 }, { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN }, - { .iov_base = buf + ETHER_ADDR_LEN * 2, - .iov_len = size - ETHER_ADDR_LEN * 2 }, + { .iov_base = buf + ETH_ALEN * 2, + .iov_len = size - ETH_ALEN * 2 }, }; memcpy(vlan_iov, iov, sizeof(vlan_iov)); @@ -1868,64 +1868,12 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) } /* structures and macros for task offloading */ -typedef struct ip_header -{ - uint8_t ip_ver_len; /* version and header length */ - uint8_t ip_tos; /* type of service */ - uint16_t ip_len; /* total length */ - uint16_t ip_id; /* identification */ - uint16_t ip_off; /* fragment offset field */ - uint8_t ip_ttl; /* time to live */ - uint8_t ip_p; /* protocol */ - uint16_t ip_sum; /* checksum */ - uint32_t ip_src,ip_dst; /* source and dest address */ -} ip_header; - -#define IP_HEADER_VERSION_4 4 -#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf) -#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2) - -typedef struct tcp_header -{ - uint16_t th_sport; /* source port */ - uint16_t th_dport; /* destination port */ - uint32_t th_seq; /* sequence number */ - uint32_t th_ack; /* acknowledgement number */ - uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */ - uint16_t th_win; /* window */ - uint16_t th_sum; /* checksum */ - uint16_t th_urp; /* urgent pointer */ -} tcp_header; - -typedef struct udp_header -{ - uint16_t uh_sport; /* source port */ - uint16_t uh_dport; /* destination port */ - uint16_t uh_ulen; /* udp length */ - uint16_t uh_sum; /* udp checksum */ -} udp_header; - -typedef struct ip_pseudo_header -{ - uint32_t ip_src; - uint32_t ip_dst; - uint8_t zeros; - uint8_t ip_proto; - uint16_t ip_payload; -} ip_pseudo_header; - -#define IP_PROTO_TCP 6 -#define IP_PROTO_UDP 17 - #define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2) #define TCP_FLAGS_ONLY(flags) ((flags)&0x3f) #define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags)) #define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off))) -#define TCP_FLAG_FIN 0x01 -#define TCP_FLAG_PUSH 0x08 - /* produces ones' complement sum of data */ static uint16_t ones_complement_sum(uint8_t *data, size_t len) { @@ -2098,7 +2046,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) } /* transfer ownership to target */ - txdw0 &= ~CP_RX_OWN; + txdw0 &= ~CP_TX_OWN; /* reset error indicator bits */ txdw0 &= ~CP_TX_STATUS_UNF; @@ -2134,7 +2082,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) bswap16(txdw1 & CP_TX_VLAN_TAG_MASK)); dot1q_buffer = (uint16_t *) dot1q_buffer_space; - dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q); + dot1q_buffer[0] = cpu_to_be16(ETH_P_VLAN); /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */ dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK); } else { @@ -2151,12 +2099,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DPRINTF("+++ C+ mode offloaded task checksum\n"); /* Large enough for Ethernet and IP headers? */ - if (saved_size < ETH_HLEN + sizeof(ip_header)) { + if (saved_size < ETH_HLEN + sizeof(struct ip_header)) { goto skip_offload; } /* ip packet header */ - ip_header *ip = NULL; + struct ip_header *ip = NULL; int hlen = 0; uint8_t ip_protocol = 0; uint16_t ip_data_len = 0; @@ -2172,11 +2120,15 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DPRINTF("+++ C+ mode has IP packet\n"); - /* not aligned */ + /* Note on memory alignment: eth_payload_data is 16-bit aligned + * since saved_buffer is allocated with g_malloc() and ETH_HLEN is + * even. 32-bit accesses must use ldl/stl wrappers to avoid + * unaligned accesses. + */ eth_payload_data = saved_buffer + ETH_HLEN; eth_payload_len = saved_size - ETH_HLEN; - ip = (ip_header*)eth_payload_data; + ip = (struct ip_header*)eth_payload_data; if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { DPRINTF("+++ C+ mode packet has bad IP version %d " @@ -2185,8 +2137,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) goto skip_offload; } - hlen = IP_HEADER_LENGTH(ip); - if (hlen < sizeof(ip_header) || hlen > eth_payload_len) { + hlen = IP_HDR_GET_LEN(ip); + if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) { goto skip_offload; } @@ -2269,7 +2221,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) } DPRINTF("+++ C+ mode TSO TCP seqno %08x\n", - be32_to_cpu(p_tcp_hdr->th_seq)); + ldl_be_p(&p_tcp_hdr->th_seq)); /* add 4 TCP pseudoheader fields */ /* copy IP source and destination fields */ @@ -2287,7 +2239,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* keep PUSH and FIN flags only for the last frame */ if (!is_last_frame) { - TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN); + TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN); } /* recalculate TCP checksum */ @@ -2325,7 +2277,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) 0, (uint8_t *) dot1q_buffer); /* add transferred count to TCP sequence number */ - p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); + stl_be_p(&p_tcp_hdr->th_seq, + chunk_size + ldl_be_p(&p_tcp_hdr->th_seq)); ++send_count; } @@ -2881,8 +2834,7 @@ static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val) static void rtl8139_set_next_tctr_time(RTL8139State *s) { - const uint64_t ns_per_period = - muldiv64(0x100000000LL, get_ticks_per_sec(), PCI_FREQUENCY); + const uint64_t ns_per_period = (uint64_t)PCI_PERIOD << 32; DPRINTF("entered rtl8139_set_next_tctr_time\n"); @@ -2900,7 +2852,7 @@ static void rtl8139_set_next_tctr_time(RTL8139State *s) if (!s->TimerInt) { timer_del(s->timer); } else { - uint64_t delta = muldiv64(s->TimerInt, get_ticks_per_sec(), PCI_FREQUENCY); + uint64_t delta = (uint64_t)s->TimerInt * PCI_PERIOD; if (s->TCTR_base + delta <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { delta += ns_per_period; } @@ -3174,8 +3126,8 @@ static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr) break; case Timer: - ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base, - PCI_FREQUENCY, get_ticks_per_sec()); + ret = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base) / + PCI_PERIOD; DPRINTF("TCTR Timer read val=0x%08x\n", ret); break; @@ -3269,8 +3221,7 @@ static void rtl8139_pre_save(void *opaque) int64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); /* for migration to older versions */ - s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, - get_ticks_per_sec()); + s->TCTR = (current_time - s->TCTR_base) / PCI_PERIOD; s->rtl8139_mmio_io_addr_dummy = 0; } @@ -3438,10 +3389,8 @@ static void pci_rtl8139_uninit(PCIDevice *dev) { RTL8139State *s = RTL8139(dev); - if (s->cplus_txbuffer) { - g_free(s->cplus_txbuffer); - s->cplus_txbuffer = NULL; - } + g_free(s->cplus_txbuffer); + s->cplus_txbuffer = NULL; timer_del(s->timer); timer_free(s->timer); qemu_del_nic(s->nic); diff --git a/qemu/hw/net/smc91c111.c b/qemu/hw/net/smc91c111.c index 74e06e6c7..21c1b8f54 100644 --- a/qemu/hw/net/smc91c111.c +++ b/qemu/hw/net/smc91c111.c @@ -7,6 +7,7 @@ * This code is licensed under the GPL */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/devices.h" @@ -124,6 +125,25 @@ static void smc91c111_update(smc91c111_state *s) qemu_set_irq(s->irq, level); } +static int smc91c111_can_receive(smc91c111_state *s) +{ + if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) { + return 1; + } + if (s->allocated == (1 << NUM_PACKETS) - 1 || + s->rx_fifo_len == NUM_PACKETS) { + return 0; + } + return 1; +} + +static inline void smc91c111_flush_queued_packets(smc91c111_state *s) +{ + if (smc91c111_can_receive(s)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } +} + /* Try to allocate a packet. Returns 0x80 on failure. */ static int smc91c111_allocate_packet(smc91c111_state *s) { @@ -164,6 +184,7 @@ static void smc91c111_pop_rx_fifo(smc91c111_state *s) } else { s->int_level &= ~INT_RCV; } + smc91c111_flush_queued_packets(s); smc91c111_update(s); } @@ -185,7 +206,7 @@ static void smc91c111_release_packet(smc91c111_state *s, int packet) s->allocated &= ~(1 << packet); if (s->tx_alloc == 0x80) smc91c111_tx_alloc(s); - qemu_flush_queued_packets(qemu_get_queue(s->nic)); + smc91c111_flush_queued_packets(s); } /* Flush the TX FIFO. */ @@ -311,6 +332,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, if (s->rcr & RCR_SOFT_RST) { smc91c111_reset(DEVICE(s)); } + smc91c111_flush_queued_packets(s); return; case 10: case 11: /* RPCR */ /* Ignored */ @@ -636,15 +658,11 @@ static uint32_t smc91c111_readl(void *opaque, hwaddr offset) return val; } -static int smc91c111_can_receive(NetClientState *nc) +static int smc91c111_can_receive_nc(NetClientState *nc) { smc91c111_state *s = qemu_get_nic_opaque(nc); - if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) - return 1; - if (s->allocated == (1 << NUM_PACKETS) - 1) - return 0; - return 1; + return smc91c111_can_receive(s); } static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size) @@ -739,7 +757,7 @@ static const MemoryRegionOps smc91c111_mem_ops = { static NetClientInfo net_smc91c111_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = smc91c111_can_receive, + .can_receive = smc91c111_can_receive_nc, .receive = smc91c111_receive, }; diff --git a/qemu/hw/net/spapr_llan.c b/qemu/hw/net/spapr_llan.c index 1ca5e9ce6..a647f25d9 100644 --- a/qemu/hw/net/spapr_llan.c +++ b/qemu/hw/net/spapr_llan.c @@ -24,6 +24,9 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "net/net.h" #include "hw/qdev.h" @@ -44,6 +47,10 @@ #define DPRINTF(fmt...) #endif +/* Compatibility flags for migration */ +#define SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT 0 +#define SPAPRVLAN_FLAG_RX_BUF_POOLS (1 << SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT) + /* * Virtual LAN device */ @@ -85,6 +92,15 @@ typedef uint64_t vlan_bd_t; #define VIO_SPAPR_VLAN_DEVICE(obj) \ OBJECT_CHECK(VIOsPAPRVLANDevice, (obj), TYPE_VIO_SPAPR_VLAN_DEVICE) +#define RX_POOL_MAX_BDS 4096 +#define RX_MAX_POOLS 5 + +typedef struct { + int32_t bufsize; + int32_t count; + vlan_bd_t bds[RX_POOL_MAX_BDS]; +} RxBufPool; + typedef struct VIOsPAPRVLANDevice { VIOsPAPRDevice sdev; NICConf nicconf; @@ -93,6 +109,8 @@ typedef struct VIOsPAPRVLANDevice { target_ulong buf_list; uint32_t add_buf_ptr, use_buf_ptr, rx_bufs; target_ulong rxq_ptr; + uint32_t compat_flags; /* Compatability flags for migration */ + RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */ } VIOsPAPRVLANDevice; static int spapr_vlan_can_receive(NetClientState *nc) @@ -102,6 +120,73 @@ static int spapr_vlan_can_receive(NetClientState *nc) return (dev->isopen && dev->rx_bufs > 0); } +/** + * Get buffer descriptor from one of our receive buffer pools + */ +static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev, + size_t size) +{ + vlan_bd_t bd; + int pool; + + for (pool = 0; pool < RX_MAX_POOLS; pool++) { + if (dev->rx_pool[pool]->count > 0 && + dev->rx_pool[pool]->bufsize >= size + 8) { + break; + } + } + if (pool == RX_MAX_POOLS) { + /* Failed to find a suitable buffer */ + return 0; + } + + DPRINTF("Found buffer: pool=%d count=%d rxbufs=%d\n", pool, + dev->rx_pool[pool]->count, dev->rx_bufs); + + /* Remove the buffer from the pool */ + dev->rx_pool[pool]->count--; + bd = dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count]; + dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count] = 0; + + return bd; +} + +/** + * Get buffer descriptor from the receive buffer list page that has been + * supplied by the guest with the H_REGISTER_LOGICAL_LAN call + */ +static vlan_bd_t spapr_vlan_get_rx_bd_from_page(VIOsPAPRVLANDevice *dev, + size_t size) +{ + int buf_ptr = dev->use_buf_ptr; + vlan_bd_t bd; + + do { + buf_ptr += 8; + if (buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { + buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = vio_ldq(&dev->sdev, dev->buf_list + buf_ptr); + DPRINTF("use_buf_ptr=%d bd=0x%016llx\n", + buf_ptr, (unsigned long long)bd); + } while ((!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) + && buf_ptr != dev->use_buf_ptr); + + if (!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) { + /* Failed to find a suitable buffer */ + return 0; + } + + /* Remove the buffer from the pool */ + dev->use_buf_ptr = buf_ptr; + vio_stq(&dev->sdev, dev->buf_list + dev->use_buf_ptr, 0); + + DPRINTF("Found buffer: ptr=%d rxbufs=%d\n", dev->use_buf_ptr, dev->rx_bufs); + + return bd; +} + static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, size_t size) { @@ -109,7 +194,6 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, VIOsPAPRDevice *sdev = VIO_SPAPR_DEVICE(dev); vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; - int buf_ptr = dev->use_buf_ptr; uint64_t handle; uint8_t control; @@ -124,29 +208,16 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, return -1; } - do { - buf_ptr += 8; - if (buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { - buf_ptr = VLAN_RX_BDS_OFF; - } - - bd = vio_ldq(sdev, dev->buf_list + buf_ptr); - DPRINTF("use_buf_ptr=%d bd=0x%016llx\n", - buf_ptr, (unsigned long long)bd); - } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) - && (buf_ptr != dev->use_buf_ptr)); - - if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) { - /* Failed to find a suitable buffer */ + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + bd = spapr_vlan_get_rx_bd_from_pool(dev, size); + } else { + bd = spapr_vlan_get_rx_bd_from_page(dev, size); + } + if (!bd) { return -1; } - /* Remove the buffer from the pool */ dev->rx_bufs--; - dev->use_buf_ptr = buf_ptr; - vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0); - - DPRINTF("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs); /* Transfer the packet data */ if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { @@ -194,13 +265,31 @@ static NetClientInfo net_spapr_vlan_info = { .receive = spapr_vlan_receive, }; +static void spapr_vlan_reset_rx_pool(RxBufPool *rxp) +{ + /* + * Use INT_MAX as bufsize so that unused buffers are moved to the end + * of the list during the qsort in spapr_vlan_add_rxbuf_to_pool() later. + */ + rxp->bufsize = INT_MAX; + rxp->count = 0; + memset(rxp->bds, 0, sizeof(rxp->bds)); +} + static void spapr_vlan_reset(VIOsPAPRDevice *sdev) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + int i; dev->buf_list = 0; dev->rx_bufs = 0; dev->isopen = 0; + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + spapr_vlan_reset_rx_pool(dev->rx_pool[i]); + } + } } static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) @@ -217,10 +306,31 @@ static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) static void spapr_vlan_instance_init(Object *obj) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + int i; device_add_bootindex_property(obj, &dev->nicconf.bootindex, "bootindex", "", DEVICE(dev), NULL); + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + dev->rx_pool[i] = g_new(RxBufPool, 1); + spapr_vlan_reset_rx_pool(dev->rx_pool[i]); + } + } +} + +static void spapr_vlan_instance_finalize(Object *obj) +{ + VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + int i; + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + g_free(dev->rx_pool[i]); + dev->rx_pool[i] = NULL; + } + } } void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) @@ -371,6 +481,113 @@ static target_ulong h_free_logical_lan(PowerPCCPU *cpu, return H_SUCCESS; } +/** + * Used for qsort, this function compares two RxBufPools by size. + */ +static int rx_pool_size_compare(const void *p1, const void *p2) +{ + const RxBufPool *pool1 = *(RxBufPool **)p1; + const RxBufPool *pool2 = *(RxBufPool **)p2; + + if (pool1->bufsize < pool2->bufsize) { + return -1; + } + return pool1->bufsize > pool2->bufsize; +} + +/** + * Search for a matching buffer pool with exact matching size, + * or return -1 if no matching pool has been found. + */ +static int spapr_vlan_get_rx_pool_id(VIOsPAPRVLANDevice *dev, int size) +{ + int pool; + + for (pool = 0; pool < RX_MAX_POOLS; pool++) { + if (dev->rx_pool[pool]->bufsize == size) { + return pool; + } + } + + return -1; +} + +/** + * Enqueuing receive buffer by adding it to one of our receive buffer pools + */ +static target_long spapr_vlan_add_rxbuf_to_pool(VIOsPAPRVLANDevice *dev, + target_ulong buf) +{ + int size = VLAN_BD_LEN(buf); + int pool; + + pool = spapr_vlan_get_rx_pool_id(dev, size); + if (pool < 0) { + /* + * No matching pool found? Try to use a new one. If the guest used all + * pools before, but changed the size of one pool inbetween, we might + * need to recycle that pool here (if it's empty already). Thus scan + * all buffer pools now, starting with the last (likely empty) one. + */ + for (pool = RX_MAX_POOLS - 1; pool >= 0 ; pool--) { + if (dev->rx_pool[pool]->count == 0) { + dev->rx_pool[pool]->bufsize = size; + /* + * Sort pools by size so that spapr_vlan_receive() + * can later find the smallest buffer pool easily. + */ + qsort(dev->rx_pool, RX_MAX_POOLS, sizeof(dev->rx_pool[0]), + rx_pool_size_compare); + pool = spapr_vlan_get_rx_pool_id(dev, size); + DPRINTF("created RX pool %d for size %lld\n", pool, + VLAN_BD_LEN(buf)); + break; + } + } + } + /* Still no usable pool? Give up */ + if (pool < 0 || dev->rx_pool[pool]->count >= RX_POOL_MAX_BDS) { + return H_RESOURCE; + } + + DPRINTF("h_add_llan_buf(): Add buf using pool %i (size %lli, count=%i)\n", + pool, VLAN_BD_LEN(buf), dev->rx_pool[pool]->count); + + dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count++] = buf; + + return 0; +} + +/** + * This is the old way of enqueuing receive buffers: Add it to the rx queue + * page that has been supplied by the guest (which is quite limited in size). + */ +static target_long spapr_vlan_add_rxbuf_to_page(VIOsPAPRVLANDevice *dev, + target_ulong buf) +{ + vlan_bd_t bd; + + if (dev->rx_bufs >= VLAN_MAX_BUFS) { + return H_RESOURCE; + } + + do { + dev->add_buf_ptr += 8; + if (dev->add_buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { + dev->add_buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = vio_ldq(&dev->sdev, dev->buf_list + dev->add_buf_ptr); + } while (bd & VLAN_BD_VALID); + + vio_stq(&dev->sdev, dev->buf_list + dev->add_buf_ptr, buf); + + DPRINTF("h_add_llan_buf(): Added buf ptr=%d rx_bufs=%d bd=0x%016llx\n", + dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf); + + return 0; +} + static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, @@ -380,7 +597,7 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, target_ulong buf = args[1]; VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - vlan_bd_t bd; + target_long ret; DPRINTF("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx ", 0x" TARGET_FMT_lx ")\n", reg, buf); @@ -396,29 +613,23 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, return H_PARAMETER; } - if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) { + if (!dev->isopen) { return H_RESOURCE; } - do { - dev->add_buf_ptr += 8; - if (dev->add_buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { - dev->add_buf_ptr = VLAN_RX_BDS_OFF; - } - - bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr); - } while (bd & VLAN_BD_VALID); - - vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf); + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + ret = spapr_vlan_add_rxbuf_to_pool(dev, buf); + } else { + ret = spapr_vlan_add_rxbuf_to_page(dev, buf); + } + if (ret) { + return ret; + } dev->rx_bufs++; qemu_flush_queued_packets(qemu_get_queue(dev->nic)); - DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d" - " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs, - (unsigned long long)buf); - return H_SUCCESS; } @@ -508,9 +719,44 @@ static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr, static Property spapr_vlan_properties[] = { DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev), DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), + DEFINE_PROP_BIT("use-rx-buffer-pools", VIOsPAPRVLANDevice, + compat_flags, SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT, true), DEFINE_PROP_END_OF_LIST(), }; +static bool spapr_vlan_rx_buffer_pools_needed(void *opaque) +{ + VIOsPAPRVLANDevice *dev = opaque; + + return (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) != 0; +} + +static const VMStateDescription vmstate_rx_buffer_pool = { + .name = "spapr_llan/rx_buffer_pool", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_vlan_rx_buffer_pools_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32(bufsize, RxBufPool), + VMSTATE_INT32(count, RxBufPool), + VMSTATE_UINT64_ARRAY(bds, RxBufPool, RX_POOL_MAX_BDS), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_rx_pools = { + .name = "spapr_llan/rx_pools", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_vlan_rx_buffer_pools_needed, + .fields = (VMStateField[]) { + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, VIOsPAPRVLANDevice, + RX_MAX_POOLS, 1, + vmstate_rx_buffer_pool, RxBufPool), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_spapr_llan = { .name = "spapr_llan", .version_id = 1, @@ -527,6 +773,10 @@ static const VMStateDescription vmstate_spapr_llan = { VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * []) { + &vmstate_rx_pools, + NULL + } }; static void spapr_vlan_class_init(ObjectClass *klass, void *data) @@ -553,6 +803,7 @@ static const TypeInfo spapr_vlan_info = { .instance_size = sizeof(VIOsPAPRVLANDevice), .class_init = spapr_vlan_class_init, .instance_init = spapr_vlan_instance_init, + .instance_finalize = spapr_vlan_instance_finalize, }; static void spapr_vlan_register_types(void) diff --git a/qemu/hw/net/stellaris_enet.c b/qemu/hw/net/stellaris_enet.c index 21a47735d..688089494 100644 --- a/qemu/hw/net/stellaris_enet.c +++ b/qemu/hw/net/stellaris_enet.c @@ -6,6 +6,7 @@ * * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" #include <zlib.h> @@ -235,8 +236,18 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si n = s->next_packet + s->np; if (n >= 31) n -= 31; - s->np++; + if (size >= sizeof(s->rx[n].data) - 6) { + /* If the packet won't fit into the + * emulated 2K RAM, this is reported + * as a FIFO overrun error. + */ + s->ris |= SE_INT_FOV; + stellaris_enet_update(s); + return -1; + } + + s->np++; s->rx[n].len = size + 6; p = s->rx[n].data; *(p++) = (size + 6); diff --git a/qemu/hw/net/vhost_net.c b/qemu/hw/net/vhost_net.c index 5c1d11f51..6e1032fc1 100644 --- a/qemu/hw/net/vhost_net.c +++ b/qemu/hw/net/vhost_net.c @@ -13,6 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "net/net.h" #include "net/tap.h" #include "net/vhost-user.h" @@ -21,24 +22,20 @@ #include "net/vhost_net.h" #include "qemu/error-report.h" -#include "config.h" #ifdef CONFIG_VHOST_NET #include <linux/vhost.h> #include <sys/socket.h> #include <linux/kvm.h> -#include <fcntl.h> #include <netpacket/packet.h> #include <net/ethernet.h> #include <net/if.h> #include <netinet/in.h> -#include <stdio.h> #include "standard-headers/linux/virtio_ring.h" #include "hw/virtio/vhost.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" struct vhost_net { struct vhost_dev dev; @@ -77,13 +74,9 @@ static const int user_feature_bits[] = { VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_NET_F_STATUS, - VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_CTRL_RX, - VIRTIO_NET_F_CTRL_VLAN, - VIRTIO_NET_F_CTRL_RX_EXTRA, - VIRTIO_NET_F_CTRL_MAC_ADDR, - VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, + + /* This bit implies RARP isn't sent by QEMU out of band */ + VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, @@ -122,6 +115,11 @@ void vhost_net_ack_features(struct vhost_net *net, uint64_t features) vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features); } +uint64_t vhost_net_get_max_queues(VHostNetState *net) +{ + return net->dev.max_queues; +} + static int vhost_net_get_fd(NetClientState *backend) { switch (backend->info->type) { @@ -143,6 +141,11 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) fprintf(stderr, "vhost-net requires net backend to be setup\n"); goto fail; } + net->nc = options->net_backend; + + net->dev.max_queues = 1; + net->dev.nvqs = 2; + net->dev.vqs = net->vqs; if (backend_kernel) { r = vhost_net_get_fd(options->net_backend); @@ -152,14 +155,15 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->dev.backend_features = qemu_has_vnet_hdr(options->net_backend) ? 0 : (1ULL << VHOST_NET_F_VIRTIO_NET_HDR); net->backend = r; + net->dev.protocol_features = 0; } else { net->dev.backend_features = 0; + net->dev.protocol_features = 0; net->backend = -1; - } - net->nc = options->net_backend; - net->dev.nvqs = 2; - net->dev.vqs = net->vqs; + /* vhost-user needs vq_index to initiate a specific queue pair */ + net->dev.vq_index = net->nc->queue_index * net->dev.nvqs; + } r = vhost_dev_init(&net->dev, options->opaque, options->backend_type); @@ -192,27 +196,6 @@ static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index) net->dev.vq_index = vq_index; } -static int vhost_net_set_vnet_endian(VirtIODevice *dev, NetClientState *peer, - bool set) -{ - int r = 0; - - if (virtio_has_feature(dev, VIRTIO_F_VERSION_1) || - (virtio_legacy_is_cross_endian(dev) && !virtio_is_big_endian(dev))) { - r = qemu_set_vnet_le(peer, set); - if (r) { - error_report("backend does not support LE vnet headers"); - } - } else if (virtio_legacy_is_cross_endian(dev)) { - r = qemu_set_vnet_be(peer, set); - if (r) { - error_report("backend does not support BE vnet headers"); - } - } - - return r; -} - static int vhost_net_start_one(struct vhost_net *net, VirtIODevice *dev) { @@ -241,8 +224,7 @@ static int vhost_net_start_one(struct vhost_net *net, file.fd = net->backend; for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { const VhostOps *vhost_ops = net->dev.vhost_ops; - r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, - &file); + r = vhost_ops->vhost_net_set_backend(&net->dev, &file); if (r < 0) { r = -errno; goto fail; @@ -255,8 +237,7 @@ fail: if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { while (file.index-- > 0) { const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, - &file); + int r = vhost_ops->vhost_net_set_backend(&net->dev, &file); assert(r >= 0); } } @@ -278,15 +259,7 @@ static void vhost_net_stop_one(struct vhost_net *net, if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, - &file); - assert(r >= 0); - } - } else if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { - for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { - const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_call(&net->dev, VHOST_RESET_OWNER, - NULL); + int r = vhost_ops->vhost_net_set_backend(&net->dev, &file); assert(r >= 0); } } @@ -307,23 +280,28 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); - r = -ENOSYS; - goto err; - } - - r = vhost_net_set_vnet_endian(dev, ncs[0].peer, true); - if (r < 0) { - goto err; + return -ENOSYS; } for (i = 0; i < total_queues; i++) { - vhost_net_set_vq_index(get_vhost_net(ncs[i].peer), i * 2); - } + struct vhost_net *net; + + net = get_vhost_net(ncs[i].peer); + vhost_net_set_vq_index(net, i * 2); + + /* Suppress the masking guest notifiers on vhost user + * because vhost user doesn't interrupt masking/unmasking + * properly. + */ + if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + dev->use_guest_notifier_mask = false; + } + } r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); - goto err_endian; + goto err; } for (i = 0; i < total_queues; i++) { @@ -345,8 +323,6 @@ err_start: fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); } -err_endian: - vhost_net_set_vnet_endian(dev, ncs[0].peer, false); err: return r; } @@ -369,8 +345,6 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, fflush(stderr); } assert(r >= 0); - - assert(vhost_net_set_vnet_endian(dev, ncs[0].peer, false) >= 0); } void vhost_net_cleanup(struct vhost_net *net) @@ -379,6 +353,18 @@ void vhost_net_cleanup(struct vhost_net *net) g_free(net); } +int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr) +{ + const VhostOps *vhost_ops = net->dev.vhost_ops; + int r = -1; + + if (vhost_ops->vhost_migration_done) { + r = vhost_ops->vhost_migration_done(&net->dev, mac_addr); + } + + return r; +} + bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) { return vhost_virtqueue_pending(&net->dev, idx); @@ -411,7 +397,25 @@ VHostNetState *get_vhost_net(NetClientState *nc) return vhost_net; } + +int vhost_set_vring_enable(NetClientState *nc, int enable) +{ + VHostNetState *net = get_vhost_net(nc); + const VhostOps *vhost_ops = net->dev.vhost_ops; + + if (vhost_ops->vhost_set_vring_enable) { + return vhost_ops->vhost_set_vring_enable(&net->dev, enable); + } + + return 0; +} + #else +uint64_t vhost_net_get_max_queues(VHostNetState *net) +{ + return 1; +} + struct vhost_net *vhost_net_init(VhostNetOptions *options) { error_report("vhost-net support is not compiled in"); @@ -452,8 +456,18 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, { } +int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr) +{ + return -1; +} + VHostNetState *get_vhost_net(NetClientState *nc) { return 0; } + +int vhost_set_vring_enable(NetClientState *nc, int enable) +{ + return 0; +} #endif diff --git a/qemu/hw/net/virtio-net.c b/qemu/hw/net/virtio-net.c index 151083954..5798f87d8 100644 --- a/qemu/hw/net/virtio-net.c +++ b/qemu/hw/net/virtio-net.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include "qemu/iov.h" #include "hw/virtio/virtio.h" #include "net/net.h" @@ -86,8 +87,8 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(&netcfg, config, n->config_size); - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && - !virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && + !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) && memcmp(netcfg.mac, n->mac, ETH_ALEN)) { memcpy(n->mac, netcfg.mac, ETH_ALEN); qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); @@ -128,6 +129,13 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) if (!n->vhost_started) { int r, i; + if (n->needs_vnet_hdr_swap) { + error_report("backend does not support %s vnet headers; " + "falling back on userspace virtio", + virtio_is_big_endian(vdev) ? "BE" : "LE"); + return; + } + /* Any packets outstanding? Purge them to avoid touching rings * when vhost is running. */ @@ -152,6 +160,59 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } } +static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev, + NetClientState *peer, + bool enable) +{ + if (virtio_is_big_endian(vdev)) { + return qemu_set_vnet_be(peer, enable); + } else { + return qemu_set_vnet_le(peer, enable); + } +} + +static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, + int queues, bool enable) +{ + int i; + + for (i = 0; i < queues; i++) { + if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 && + enable) { + while (--i >= 0) { + virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false); + } + + return true; + } + } + + return false; +} + +static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(n); + int queues = n->multiqueue ? n->max_queues : 1; + + if (virtio_net_started(n, status)) { + /* Before using the device, we tell the network backend about the + * endianness to use when parsing vnet headers. If the backend + * can't do it, we fallback onto fixing the headers in the core + * virtio-net code. + */ + n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, + queues, true); + } else if (virtio_net_started(n, vdev->status)) { + /* After using the device, we need to reset the network backend to + * the default (guest native endianness), otherwise the guest may + * lose network connectivity if it is rebooted into a different + * endianness. + */ + virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false); + } +} + static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) { VirtIONet *n = VIRTIO_NET(vdev); @@ -159,6 +220,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) int i; uint8_t queue_status; + virtio_net_vnet_endian_status(n, status); virtio_net_vhost_status(n, status); for (i = 0; i < n->max_queues; i++) { @@ -304,7 +366,7 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) info->multicast_table = str_list; info->vlan_table = get_vlan_table(n); - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { info->vlan = RX_STATE_ALL; } else if (!info->vlan_table) { info->vlan = RX_STATE_NONE; @@ -406,6 +468,10 @@ static int peer_attach(VirtIONet *n, int index) return 0; } + if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + vhost_set_vring_enable(nc->peer, 1); + } + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return 0; } @@ -421,6 +487,10 @@ static int peer_detach(VirtIONet *n, int index) return 0; } + if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + vhost_set_vring_enable(nc->peer, 0); + } + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return 0; } @@ -529,13 +599,13 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) int i; virtio_net_set_multiqueue(n, - __virtio_has_feature(features, VIRTIO_NET_F_MQ)); + virtio_has_feature(features, VIRTIO_NET_F_MQ)); virtio_net_set_mrg_rx_bufs(n, - __virtio_has_feature(features, - VIRTIO_NET_F_MRG_RXBUF), - __virtio_has_feature(features, - VIRTIO_F_VERSION_1)); + virtio_has_feature(features, + VIRTIO_NET_F_MRG_RXBUF), + virtio_has_feature(features, + VIRTIO_F_VERSION_1)); if (n->has_vnet_hdr) { n->curr_guest_offloads = @@ -552,7 +622,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) vhost_net_ack_features(get_vhost_net(nc->peer), features); } - if (__virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { + if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { memset(n->vlans, 0, MAX_VLAN >> 3); } else { memset(n->vlans, 0xff, MAX_VLAN >> 3); @@ -599,7 +669,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, uint64_t offloads; size_t s; - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { return VIRTIO_NET_ERR; } @@ -810,20 +880,24 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_ctrl_hdr ctrl; virtio_net_ctrl_ack status = VIRTIO_NET_ERR; - VirtQueueElement elem; + VirtQueueElement *elem; size_t s; struct iovec *iov, *iov2; unsigned int iov_cnt; - while (virtqueue_pop(vq, &elem)) { - if (iov_size(elem.in_sg, elem.in_num) < sizeof(status) || - iov_size(elem.out_sg, elem.out_num) < sizeof(ctrl)) { + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || + iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { error_report("virtio-net ctrl missing headers"); exit(1); } - iov_cnt = elem.out_num; - iov2 = iov = g_memdup(elem.out_sg, sizeof(struct iovec) * elem.out_num); + iov_cnt = elem->out_num; + iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num); s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); if (s != sizeof(ctrl)) { @@ -842,12 +916,13 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt); } - s = iov_from_buf(elem.in_sg, elem.in_num, 0, &status, sizeof(status)); + s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status)); assert(s == sizeof(status)); - virtqueue_push(vq, &elem, sizeof(status)); + virtqueue_push(vq, elem, sizeof(status)); virtio_notify(vdev, vq); g_free(iov2); + g_free(elem); } } @@ -949,7 +1024,10 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, void *wbuf = (void *)buf; work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, size - n->host_hdr_len); - virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); + + if (n->needs_vnet_hdr_swap) { + virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); + } iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { @@ -1036,13 +1114,14 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t offset = i = 0; while (offset < size) { - VirtQueueElement elem; + VirtQueueElement *elem; int len, total; - const struct iovec *sg = elem.in_sg; + const struct iovec *sg; total = 0; - if (virtqueue_pop(q->rx_vq, &elem) == 0) { + elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement)); + if (!elem) { if (i == 0) return -1; error_report("virtio-net unexpected empty queue: " @@ -1055,21 +1134,22 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t exit(1); } - if (elem.in_num < 1) { + if (elem->in_num < 1) { error_report("virtio-net receive queue contains no in buffers"); exit(1); } + sg = elem->in_sg; if (i == 0) { assert(offset == 0); if (n->mergeable_rx_bufs) { mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), - sg, elem.in_num, + sg, elem->in_num, offsetof(typeof(mhdr), num_buffers), sizeof(mhdr.num_buffers)); } - receive_header(n, sg, elem.in_num, buf, size); + receive_header(n, sg, elem->in_num, buf, size); offset = n->host_hdr_len; total += n->guest_hdr_len; guest_offset = n->guest_hdr_len; @@ -1078,7 +1158,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } /* copy in packet. ugh */ - len = iov_from_buf(sg, elem.in_num, guest_offset, + len = iov_from_buf(sg, elem->in_num, guest_offset, buf + offset, size - offset); total += len; offset += len; @@ -1086,18 +1166,14 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t * must have consumed the complete packet. * Otherwise, drop it. */ if (!n->mergeable_rx_bufs && offset < size) { -#if 0 - error_report("virtio-net truncated non-mergeable packet: " - "i %zd mergeable %d offset %zd, size %zd, " - "guest hdr len %zd, host hdr len %zd", - i, n->mergeable_rx_bufs, - offset, size, n->guest_hdr_len, n->host_hdr_len); -#endif + virtqueue_discard(q->rx_vq, elem, total); + g_free(elem); return size; } /* signal other side */ - virtqueue_fill(q->rx_vq, &elem, total, i++); + virtqueue_fill(q->rx_vq, elem, total, i++); + g_free(elem); } if (mhdr_cnt) { @@ -1121,10 +1197,11 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) VirtIONetQueue *q = virtio_net_get_subqueue(nc); VirtIODevice *vdev = VIRTIO_DEVICE(n); - virtqueue_push(q->tx_vq, &q->async_tx.elem, 0); + virtqueue_push(q->tx_vq, q->async_tx.elem, 0); virtio_notify(vdev, q->tx_vq); - q->async_tx.elem.out_num = q->async_tx.len = 0; + g_free(q->async_tx.elem); + q->async_tx.elem = NULL; virtio_queue_set_notification(q->tx_vq, 1); virtio_net_flush_tx(q); @@ -1135,25 +1212,31 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) { VirtIONet *n = q->n; VirtIODevice *vdev = VIRTIO_DEVICE(n); - VirtQueueElement elem; + VirtQueueElement *elem; int32_t num_packets = 0; int queue_index = vq2q(virtio_get_queue_index(q->tx_vq)); if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { return num_packets; } - if (q->async_tx.elem.out_num) { + if (q->async_tx.elem) { virtio_queue_set_notification(q->tx_vq, 0); return num_packets; } - while (virtqueue_pop(q->tx_vq, &elem)) { - ssize_t ret, len; - unsigned int out_num = elem.out_num; - struct iovec *out_sg = &elem.out_sg[0]; - struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1]; + for (;;) { + ssize_t ret; + unsigned int out_num; + struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; struct virtio_net_hdr_mrg_rxbuf mhdr; + elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + + out_num = elem->out_num; + out_sg = elem->out_sg; if (out_num < 1) { error_report("virtio-net header not in first element"); exit(1); @@ -1165,7 +1248,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) error_report("virtio-net header incorrect"); exit(1); } - if (virtio_needs_swap(vdev)) { + if (n->needs_vnet_hdr_swap) { virtio_net_hdr_swap(vdev, (void *) &mhdr); sg2[0].iov_base = &mhdr; sg2[0].iov_len = n->guest_hdr_len; @@ -1196,21 +1279,18 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) out_sg = sg; } - len = n->guest_hdr_len; - ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), out_sg, out_num, virtio_net_tx_complete); if (ret == 0) { virtio_queue_set_notification(q->tx_vq, 0); q->async_tx.elem = elem; - q->async_tx.len = len; return -EBUSY; } - len += ret; drop: - virtqueue_push(q->tx_vq, &elem, 0); + virtqueue_push(q->tx_vq, elem, 0); virtio_notify(vdev, q->tx_vq); + g_free(elem); if (++num_packets >= n->tx_burst) { break; @@ -1453,7 +1533,7 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) } } - if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { qemu_put_be64(f, n->curr_guest_offloads); } } @@ -1462,11 +1542,33 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); + int ret; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; - return virtio_load(vdev, f, version_id); + ret = virtio_load(vdev, f, version_id); + if (ret) { + return ret; + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + n->curr_guest_offloads = qemu_get_be64(f); + } else { + n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); + } + + if (peer_has_vnet_hdr(n)) { + virtio_net_apply_guest_offloads(n); + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && + virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { + n->announce_counter = SELF_ANNOUNCE_ROUNDS; + timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); + } + + return 0; } static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, @@ -1479,7 +1581,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, n->vqs[0].tx_waiting = qemu_get_be32(f); virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f), - virtio_has_feature(vdev, VIRTIO_F_VERSION_1)); + virtio_vdev_has_feature(vdev, + VIRTIO_F_VERSION_1)); if (version_id >= 3) n->status = qemu_get_be16(f); @@ -1562,16 +1665,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, } } - if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { - n->curr_guest_offloads = qemu_get_be64(f); - } else { - n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); - } - - if (peer_has_vnet_hdr(n)) { - virtio_net_apply_guest_offloads(n); - } - virtio_net_set_queues(n); /* Find the first multicast entry in the saved MAC filter */ @@ -1589,12 +1682,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_subqueue(n->nic, i)->link_down = link_down; } - if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && - virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { - n->announce_counter = SELF_ANNOUNCE_ROUNDS; - timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); - } - return 0; } diff --git a/qemu/hw/net/vmware_utils.h b/qemu/hw/net/vmware_utils.h index 1099df669..c0dbb2ff4 100644 --- a/qemu/hw/net/vmware_utils.h +++ b/qemu/hw/net/vmware_utils.h @@ -18,10 +18,7 @@ #define VMWARE_UTILS_H #include "qemu/range.h" - -#ifndef VMW_SHPRN -#define VMW_SHPRN(fmt, ...) do {} while (0) -#endif +#include "vmxnet_debug.h" /* * Shared memory access functions with byte swap support @@ -52,7 +49,7 @@ vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write) } static inline void -vmw_shmem_set(hwaddr addr, uint8 val, int len) +vmw_shmem_set(hwaddr addr, uint8_t val, int len) { int i; VMW_SHPRN("SHMEM set: %" PRIx64 ", len: %d (value 0x%X)", addr, len, val); diff --git a/qemu/hw/net/vmxnet3.c b/qemu/hw/net/vmxnet3.c index 59b06b841..093a71e12 100644 --- a/qemu/hw/net/vmxnet3.c +++ b/qemu/hw/net/vmxnet3.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -36,12 +37,28 @@ #define VMXNET3_MSIX_BAR_SIZE 0x2000 #define MIN_BUF_SIZE 60 +/* Compatability flags for migration */ +#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT 0 +#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS \ + (1 << VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT) +#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT 1 +#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE \ + (1 << VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT) + +#define VMXNET3_EXP_EP_OFFSET (0x48) +#define VMXNET3_MSI_OFFSET(s) \ + ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x50 : 0x84) +#define VMXNET3_MSIX_OFFSET(s) \ + ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0 : 0x9c) +#define VMXNET3_DSN_OFFSET (0x100) + #define VMXNET3_BAR0_IDX (0) #define VMXNET3_BAR1_IDX (1) #define VMXNET3_MSIX_BAR_IDX (2) #define VMXNET3_OFF_MSIX_TABLE (0x000) -#define VMXNET3_OFF_MSIX_PBA (0x800) +#define VMXNET3_OFF_MSIX_PBA(s) \ + ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x800 : 0x1000) /* Link speed in Mbps should be shifted by 16 */ #define VMXNET3_LINK_SPEED (1000 << 16) @@ -50,7 +67,7 @@ #define VMXNET3_LINK_STATUS_UP 0x1 /* Least significant bit should be set for revision and version */ -#define VMXNET3_DEVICE_VERSION 0x1 +#define VMXNET3_UPT_REVISION 0x1 #define VMXNET3_DEVICE_REVISION 0x1 /* Number of interrupt vectors for non-MSIx modes */ @@ -108,9 +125,19 @@ #define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag)) +typedef struct VMXNET3Class { + PCIDeviceClass parent_class; + DeviceRealize parent_dc_realize; +} VMXNET3Class; + #define TYPE_VMXNET3 "vmxnet3" #define VMXNET3(obj) OBJECT_CHECK(VMXNET3State, (obj), TYPE_VMXNET3) +#define VMXNET3_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VMXNET3Class, (klass), TYPE_VMXNET3) +#define VMXNET3_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VMXNET3Class, (obj), TYPE_VMXNET3) + /* Cyclic ring abstraction */ typedef struct { hwaddr pa; @@ -138,7 +165,7 @@ static inline void vmxnet3_ring_init(Vmxnet3Ring *ring, } #define VMXNET3_RING_DUMP(macro, ring_name, ridx, r) \ - macro("%s#%d: base %" PRIx64 " size %lu cell_size %lu gen %d next %lu", \ + macro("%s#%d: base %" PRIx64 " size %zu cell_size %zu gen %d next %zu", \ (ring_name), (ridx), \ (r)->pa, (r)->size, (r)->cell_size, (r)->gen, (r)->next) @@ -313,6 +340,9 @@ typedef struct { MACAddr *mcast_list; uint32_t mcast_list_len; uint32_t mcast_list_buff_size; /* needed for live migration. */ + + /* Compatability flags for migration */ + uint32_t compat_flags; } VMXNET3State; /* Interrupt management */ @@ -493,7 +523,7 @@ vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx) vmxnet3_ring_dec(&s->rxq_descr[qidx].comp_ring); } -static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32 tx_ridx) +static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx) { struct Vmxnet3_TxCompDesc txcq_descr; @@ -729,9 +759,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) } if (txd.eop) { - if (!s->skip_current_tx_pkt) { - vmxnet_tx_pkt_parse(s->tx_pkt); - + if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) { if (s->needs_vlan) { vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); } @@ -927,9 +955,9 @@ static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt, /* Validate packet len: csum_start + scum_offset + length of csum field */ if (pkt_len < (vhdr->csum_start + vhdr->csum_offset + 2)) { - VMW_PKPRN("packet len:%d < csum_start(%d) + csum_offset(%d) + 2, " + VMW_PKPRN("packet len:%zu < csum_start(%d) + csum_offset(%d) + 2, " "cannot calculate checksum", - len, vhdr->csum_start, vhdr->csum_offset); + pkt_len, vhdr->csum_start, vhdr->csum_offset); return; } @@ -1165,9 +1193,13 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr, static uint64_t vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size) { + VMXNET3State *s = opaque; + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { - g_assert_not_reached(); + int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR, + VMXNET3_REG_ALIGN); + return s->interrupt_states[l].is_masked; } VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size); @@ -1192,8 +1224,13 @@ static void vmxnet3_reset_mac(VMXNET3State *s) static void vmxnet3_deactivate_device(VMXNET3State *s) { - VMW_CBPRN("Deactivating vmxnet3..."); - s->device_active = false; + if (s->device_active) { + VMW_CBPRN("Deactivating vmxnet3..."); + vmxnet_tx_pkt_reset(s->tx_pkt); + vmxnet_tx_pkt_uninit(s->tx_pkt); + vmxnet_rx_pkt_uninit(s->rx_pkt); + s->device_active = false; + } } static void vmxnet3_reset(VMXNET3State *s) @@ -1202,7 +1239,6 @@ static void vmxnet3_reset(VMXNET3State *s) vmxnet3_deactivate_device(s); vmxnet3_reset_interrupt_states(s); - vmxnet_tx_pkt_reset(s->tx_pkt); s->drv_shmem = 0; s->tx_sop = true; s->skip_current_tx_pkt = false; @@ -1287,6 +1323,10 @@ static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s) static void vmxnet3_fill_stats(VMXNET3State *s) { int i; + + if (!s->device_active) + return; + for (i = 0; i < s->txq_num; i++) { cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa, &s->txq_descr[i].txq_stats, @@ -1425,6 +1465,12 @@ static void vmxnet3_activate_device(VMXNET3State *s) return; } + /* Verify if device is active */ + if (s->device_active) { + VMW_CFPRN("Vmxnet3 device is active"); + return; + } + vmxnet3_adjust_by_guest_type(s); vmxnet3_update_features(s); vmxnet3_update_pm_state(s); @@ -1621,7 +1667,7 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) break; case VMXNET3_CMD_QUIESCE_DEV: - VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device"); + VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - deactivate the device"); vmxnet3_deactivate_device(s); break; @@ -1629,6 +1675,23 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration"); break; + case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO: + VMW_CBPRN("Set: VMXNET3_CMD_GET_ADAPTIVE_RING_INFO - " + "adaptive ring info flags"); + break; + + case VMXNET3_CMD_GET_DID_LO: + VMW_CBPRN("Set: Get lower part of device ID"); + break; + + case VMXNET3_CMD_GET_DID_HI: + VMW_CBPRN("Set: Get upper part of device ID"); + break; + + case VMXNET3_CMD_GET_DEV_EXTRA_INFO: + VMW_CBPRN("Set: Get device extra info"); + break; + default: VMW_CBPRN("Received unknown command: %" PRIx64, cmd); break; @@ -1641,13 +1704,14 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s) switch (s->last_command) { case VMXNET3_CMD_ACTIVATE_DEV: - ret = (s->device_active) ? 0 : -1; + ret = (s->device_active) ? 0 : 1; VMW_CFPRN("Device active: %" PRIx64, ret); break; case VMXNET3_CMD_RESET_DEV: case VMXNET3_CMD_QUIESCE_DEV: case VMXNET3_CMD_GET_QUEUE_STATUS: + case VMXNET3_CMD_GET_DEV_EXTRA_INFO: ret = 0; break; @@ -1668,9 +1732,21 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s) ret = vmxnet3_get_interrupt_config(s); break; + case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO: + ret = VMXNET3_DISABLE_ADAPTIVE_RING; + break; + + case VMXNET3_CMD_GET_DID_LO: + ret = PCI_DEVICE_ID_VMWARE_VMXNET3; + break; + + case VMXNET3_CMD_GET_DID_HI: + ret = VMXNET3_DEVICE_REVISION; + break; + default: VMW_WRPRN("Received request for unknown command: %x", s->last_command); - ret = -1; + ret = 0; break; } @@ -1726,7 +1802,7 @@ vmxnet3_io_bar1_write(void *opaque, * shared address only after we get the high part */ if (val == 0) { - s->device_active = false; + vmxnet3_deactivate_device(s); } s->temp_shared_guest_driver_memory = val; s->drv_shmem = 0; @@ -1801,7 +1877,7 @@ vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size) /* UPT Version Report Selection */ case VMXNET3_REG_UVRS: VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size); - ret = VMXNET3_DEVICE_VERSION; + ret = VMXNET3_UPT_REVISION; break; /* Command */ @@ -1959,7 +2035,7 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1; if (bytes_indicated < size) { - VMW_PKPRN("RX: %lu of %lu bytes indicated", bytes_indicated, size); + VMW_PKPRN("RX: %zu of %zu bytes indicated", bytes_indicated, size); } } else { VMW_PKPRN("Packet dropped by RX filter"); @@ -1988,7 +2064,6 @@ static void vmxnet3_set_link_status(NetClientState *nc) static NetClientInfo net_vmxnet3_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = vmxnet3_can_receive, .receive = vmxnet3_receive, .link_status_changed = vmxnet3_set_link_status, }; @@ -2001,16 +2076,13 @@ static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) return true; } - VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated."); return false; } static void vmxnet3_net_uninit(VMXNET3State *s) { g_free(s->mcast_list); - vmxnet_tx_pkt_reset(s->tx_pkt); - vmxnet_tx_pkt_uninit(s->tx_pkt); - vmxnet_rx_pkt_uninit(s->rx_pkt); + vmxnet3_deactivate_device(s); qemu_del_nic(s->nic); } @@ -2030,7 +2102,7 @@ static void vmxnet3_net_init(VMXNET3State *s) s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP; - VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a)); + VMW_CFPRN("Permanent MAC: " VMXNET_MF, VMXNET_MA(s->perm_mac.a)); s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf, object_get_typename(OBJECT(s)), @@ -2088,8 +2160,8 @@ vmxnet3_init_msix(VMXNET3State *s) &s->msix_bar, VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE, &s->msix_bar, - VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA, - 0); + VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA(s), + VMXNET3_MSIX_OFFSET(s)); if (0 > res) { VMW_WRPRN("Failed to initialize MSI-X, error %d", res); @@ -2117,7 +2189,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s) } } -#define VMXNET3_MSI_OFFSET (0x50) #define VMXNET3_USE_64BIT (true) #define VMXNET3_PER_VECTOR_MASK (false) @@ -2127,7 +2198,7 @@ vmxnet3_init_msi(VMXNET3State *s) PCIDevice *d = PCI_DEVICE(s); int res; - res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS, + res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK); if (0 > res) { VMW_WRPRN("Failed to initialize MSI, error %d", res); @@ -2184,6 +2255,22 @@ static const MemoryRegionOps b1_ops = { }, }; +static uint8_t *vmxnet3_device_serial_num(VMXNET3State *s) +{ + static uint64_t dsn_payload; + uint8_t *dsnp = (uint8_t *)&dsn_payload; + + dsnp[0] = 0xfe; + dsnp[1] = s->conf.macaddr.a[3]; + dsnp[2] = s->conf.macaddr.a[4]; + dsnp[3] = s->conf.macaddr.a[5]; + dsnp[4] = s->conf.macaddr.a[0]; + dsnp[5] = s->conf.macaddr.a[1]; + dsnp[6] = s->conf.macaddr.a[2]; + dsnp[7] = 0xff; + return dsnp; +} + static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); @@ -2221,6 +2308,17 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) vmxnet3_net_init(s); + if (pci_is_express(pci_dev)) { + if (pci_bus_is_express(pci_dev->bus)) { + pcie_endpoint_cap_init(pci_dev, VMXNET3_EXP_EP_OFFSET); + } + + pcie_add_capability(pci_dev, PCI_EXT_CAP_ID_DSN, 0x1, + VMXNET3_DSN_OFFSET, PCI_EXT_CAP_DSN_SIZEOF); + memcpy(pci_dev->config + VMXNET3_DSN_OFFSET + 4, + vmxnet3_device_serial_num(s), sizeof(uint64_t)); + } + register_savevm(dev, "vmxnet3-msix", -1, 1, vmxnet3_msix_save, vmxnet3_msix_load, s); } @@ -2490,6 +2588,29 @@ static const VMStateInfo int_state_info = { .put = vmxnet3_put_int_state }; +static bool vmxnet3_vmstate_need_pcie_device(void *opaque) +{ + VMXNET3State *s = VMXNET3(opaque); + + return !(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE); +} + +static bool vmxnet3_vmstate_test_pci_device(void *opaque, int version_id) +{ + return !vmxnet3_vmstate_need_pcie_device(opaque); +} + +static const VMStateDescription vmstate_vmxnet3_pcie_device = { + .name = "vmxnet3/pcie", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmxnet3_vmstate_need_pcie_device, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(parent_obj, VMXNET3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_vmxnet3 = { .name = "vmxnet3", .version_id = 1, @@ -2497,7 +2618,9 @@ static const VMStateDescription vmstate_vmxnet3 = { .pre_save = vmxnet3_pre_save, .post_load = vmxnet3_post_load, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State), + VMSTATE_STRUCT_TEST(parent_obj, VMXNET3State, + vmxnet3_vmstate_test_pci_device, 0, + vmstate_pci_device, PCIDevice), VMSTATE_BOOL(rx_packets_compound, VMXNET3State), VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State), VMSTATE_BOOL(lro_supported, VMXNET3State), @@ -2532,19 +2655,38 @@ static const VMStateDescription vmstate_vmxnet3 = { }, .subsections = (const VMStateDescription*[]) { &vmxstate_vmxnet3_mcast_list, + &vmstate_vmxnet3_pcie_device, NULL } }; static Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), + DEFINE_PROP_BIT("x-old-msi-offsets", VMXNET3State, compat_flags, + VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT, false), + DEFINE_PROP_BIT("x-disable-pcie", VMXNET3State, compat_flags, + VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT, false), DEFINE_PROP_END_OF_LIST(), }; +static void vmxnet3_realize(DeviceState *qdev, Error **errp) +{ + VMXNET3Class *vc = VMXNET3_DEVICE_GET_CLASS(qdev); + PCIDevice *pci_dev = PCI_DEVICE(qdev); + VMXNET3State *s = VMXNET3(qdev); + + if (!(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE)) { + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + } + + vc->parent_dc_realize(qdev, errp); +} + static void vmxnet3_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); + VMXNET3Class *vc = VMXNET3_DEVICE_CLASS(class); c->realize = vmxnet3_pci_realize; c->exit = vmxnet3_pci_uninit; @@ -2554,6 +2696,8 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; + vc->parent_dc_realize = dc->realize; + dc->realize = vmxnet3_realize; dc->desc = "VMWare Paravirtualized Ethernet v3"; dc->reset = vmxnet3_qdev_reset; dc->vmsd = &vmstate_vmxnet3; @@ -2564,6 +2708,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) static const TypeInfo vmxnet3_info = { .name = TYPE_VMXNET3, .parent = TYPE_PCI_DEVICE, + .class_size = sizeof(VMXNET3Class), .instance_size = sizeof(VMXNET3State), .class_init = vmxnet3_class_init, .instance_init = vmxnet3_instance_init, diff --git a/qemu/hw/net/vmxnet3.h b/qemu/hw/net/vmxnet3.h index f987d7126..f7006afe9 100644 --- a/qemu/hw/net/vmxnet3.h +++ b/qemu/hw/net/vmxnet3.h @@ -198,9 +198,13 @@ enum { VMXNET3_CMD_GET_DID_LO, /* 0xF00D0005 */ VMXNET3_CMD_GET_DID_HI, /* 0xF00D0006 */ VMXNET3_CMD_GET_DEV_EXTRA_INFO, /* 0xF00D0007 */ - VMXNET3_CMD_GET_CONF_INTR /* 0xF00D0008 */ + VMXNET3_CMD_GET_CONF_INTR, /* 0xF00D0008 */ + VMXNET3_CMD_GET_ADAPTIVE_RING_INFO /* 0xF00D0009 */ }; +/* Adaptive Ring Info Flags */ +#define VMXNET3_DISABLE_ADAPTIVE_RING 1 + /* * Little Endian layout of bitfields - * Byte 0 : 7.....len.....0 diff --git a/qemu/hw/net/vmxnet_debug.h b/qemu/hw/net/vmxnet_debug.h index 96dae0f91..96495dbb1 100644 --- a/qemu/hw/net/vmxnet_debug.h +++ b/qemu/hw/net/vmxnet_debug.h @@ -20,94 +20,127 @@ #define VMXNET_DEVICE_NAME "vmxnet3" -/* #define VMXNET_DEBUG_CB */ #define VMXNET_DEBUG_WARNINGS #define VMXNET_DEBUG_ERRORS -/* #define VMXNET_DEBUG_INTERRUPTS */ -/* #define VMXNET_DEBUG_CONFIG */ -/* #define VMXNET_DEBUG_RINGS */ -/* #define VMXNET_DEBUG_PACKETS */ -/* #define VMXNET_DEBUG_SHMEM_ACCESS */ + +#undef VMXNET_DEBUG_CB +#undef VMXNET_DEBUG_INTERRUPTS +#undef VMXNET_DEBUG_CONFIG +#undef VMXNET_DEBUG_RINGS +#undef VMXNET_DEBUG_PACKETS +#undef VMXNET_DEBUG_SHMEM_ACCESS + +#ifdef VMXNET_DEBUG_CB +# define VMXNET_DEBUG_CB_ENABLED 1 +#else +# define VMXNET_DEBUG_CB_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_WARNINGS +# define VMXNET_DEBUG_WARNINGS_ENABLED 1 +#else +# define VMXNET_DEBUG_WARNINGS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_ERRORS +# define VMXNET_DEBUG_ERRORS_ENABLED 1 +#else +# define VMXNET_DEBUG_ERRORS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_CONFIG +# define VMXNET_DEBUG_CONFIG_ENABLED 1 +#else +# define VMXNET_DEBUG_CONFIG_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_RINGS +# define VMXNET_DEBUG_RINGS_ENABLED 1 +#else +# define VMXNET_DEBUG_RINGS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_PACKETS +# define VMXNET_DEBUG_PACKETS_ENABLED 1 +#else +# define VMXNET_DEBUG_PACKETS_ENABLED 0 +#endif + +#ifdef VMXNET_DEBUG_INTERRUPTS +# define VMXNET_DEBUG_INTERRUPTS_ENABLED 1 +#else +# define VMXNET_DEBUG_INTERRUPTS_ENABLED 0 +#endif #ifdef VMXNET_DEBUG_SHMEM_ACCESS +# define VMXNET_DEBUG_SHMEM_ACCESS_ENABLED 1 +#else +# define VMXNET_DEBUG_SHMEM_ACCESS_ENABLED 0 +#endif + #define VMW_SHPRN(fmt, ...) \ do { \ - printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_SHMEM_ACCESS_ENABLED) { \ + printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_SHPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_CB #define VMW_CBPRN(fmt, ...) \ do { \ - printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_CB_ENABLED) { \ + printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_CBPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_PACKETS #define VMW_PKPRN(fmt, ...) \ do { \ - printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_PACKETS_ENABLED) { \ + printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_PKPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_WARNINGS #define VMW_WRPRN(fmt, ...) \ do { \ - printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_WARNINGS_ENABLED) { \ + printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_WRPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_ERRORS #define VMW_ERPRN(fmt, ...) \ do { \ - printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_ERRORS_ENABLED) { \ + printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_ERPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_INTERRUPTS #define VMW_IRPRN(fmt, ...) \ do { \ - printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_INTERRUPTS_ENABLED) { \ + printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_IRPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_CONFIG #define VMW_CFPRN(fmt, ...) \ do { \ - printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_CONFIG_ENABLED) { \ + printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_CFPRN(fmt, ...) do {} while (0) -#endif -#ifdef VMXNET_DEBUG_RINGS #define VMW_RIPRN(fmt, ...) \ do { \ - printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ + if (VMXNET_DEBUG_RINGS_ENABLED) { \ + printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } \ } while (0) -#else -#define VMW_RIPRN(fmt, ...) do {} while (0) -#endif #define VMXNET_MF "%02X:%02X:%02X:%02X:%02X:%02X" #define VMXNET_MA(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] diff --git a/qemu/hw/net/vmxnet_rx_pkt.c b/qemu/hw/net/vmxnet_rx_pkt.c index aa5462931..21bb46e68 100644 --- a/qemu/hw/net/vmxnet_rx_pkt.c +++ b/qemu/hw/net/vmxnet_rx_pkt.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "vmxnet_rx_pkt.h" #include "net/eth.h" #include "qemu-common.h" diff --git a/qemu/hw/net/vmxnet_rx_pkt.h b/qemu/hw/net/vmxnet_rx_pkt.h index a425846b5..0a45c1ba0 100644 --- a/qemu/hw/net/vmxnet_rx_pkt.h +++ b/qemu/hw/net/vmxnet_rx_pkt.h @@ -18,8 +18,6 @@ #ifndef VMXNET_RX_PKT_H #define VMXNET_RX_PKT_H -#include "stdint.h" -#include "stdbool.h" #include "net/eth.h" /* defines to enable packet dump functions */ diff --git a/qemu/hw/net/vmxnet_tx_pkt.c b/qemu/hw/net/vmxnet_tx_pkt.c index f7344c4cb..91e1e08fd 100644 --- a/qemu/hw/net/vmxnet_tx_pkt.c +++ b/qemu/hw/net/vmxnet_tx_pkt.c @@ -15,6 +15,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "vmxnet_tx_pkt.h" #include "net/eth.h" @@ -142,11 +143,24 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, ETH_MAX_L2_HDR_LEN); - if (bytes_read < ETH_MAX_L2_HDR_LEN) { + if (bytes_read < sizeof(struct eth_header)) { + l2_hdr->iov_len = 0; + return false; + } + + l2_hdr->iov_len = sizeof(struct eth_header); + switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { + case ETH_P_VLAN: + l2_hdr->iov_len += sizeof(struct vlan_header); + break; + case ETH_P_DVLAN: + l2_hdr->iov_len += 2 * sizeof(struct vlan_header); + break; + } + + if (bytes_read < l2_hdr->iov_len) { l2_hdr->iov_len = 0; return false; - } else { - l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); } l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); diff --git a/qemu/hw/net/vmxnet_tx_pkt.h b/qemu/hw/net/vmxnet_tx_pkt.h index 57121a6fe..f51e98ad9 100644 --- a/qemu/hw/net/vmxnet_tx_pkt.h +++ b/qemu/hw/net/vmxnet_tx_pkt.h @@ -18,8 +18,6 @@ #ifndef VMXNET_TX_PKT_H #define VMXNET_TX_PKT_H -#include "stdint.h" -#include "stdbool.h" #include "net/eth.h" #include "exec/hwaddr.h" diff --git a/qemu/hw/net/xen_nic.c b/qemu/hw/net/xen_nic.c index d7cbfc103..7281730d9 100644 --- a/qemu/hw/net/xen_nic.c +++ b/qemu/hw/net/xen_nic.c @@ -19,19 +19,9 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <inttypes.h> -#include <fcntl.h> -#include <errno.h> +#include "qemu/osdep.h" #include <sys/socket.h> #include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> #include <sys/mman.h> #include <sys/wait.h> @@ -169,7 +159,7 @@ static void net_tx_packets(struct XenNetDev *netdev) (txreq.flags & NETTXF_more_data) ? " more_data" : "", (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, txreq.gref, PROT_READ); if (page == NULL) { @@ -191,7 +181,7 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -261,7 +251,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, rxreq.gref, PROT_WRITE); if (page == NULL) { @@ -271,7 +261,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -343,19 +333,19 @@ static int net_connect(struct XenDevice *xendev) return -1; } - netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->txs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, netdev->tx_ring_ref, PROT_READ | PROT_WRITE); if (!netdev->txs) { return -1; } - netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->rxs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, netdev->rx_ring_ref, PROT_READ | PROT_WRITE); if (!netdev->rxs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); netdev->txs = NULL; return -1; } @@ -380,11 +370,11 @@ static void net_disconnect(struct XenDevice *xendev) xen_be_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); netdev->txs = NULL; } if (netdev->rxs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1); netdev->rxs = NULL; } } diff --git a/qemu/hw/net/xgmac.c b/qemu/hw/net/xgmac.c index 15fb68194..0c5f793bd 100644 --- a/qemu/hw/net/xgmac.c +++ b/qemu/hw/net/xgmac.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/char.h" #include "qemu/log.h" diff --git a/qemu/hw/net/xilinx_axienet.c b/qemu/hw/net/xilinx_axienet.c index d63c42324..de23ab5dc 100644 --- a/qemu/hw/net/xilinx_axienet.c +++ b/qemu/hw/net/xilinx_axienet.c @@ -22,7 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qapi/error.h" #include "qemu/log.h" #include "net/net.h" #include "net/checksum.h" diff --git a/qemu/hw/net/xilinx_ethlite.c b/qemu/hw/net/xilinx_ethlite.c index ad6b55306..bc846e709 100644 --- a/qemu/hw/net/xilinx_ethlite.c +++ b/qemu/hw/net/xilinx_ethlite.c @@ -22,6 +22,9 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" /* FIXME should not use tswap* */ #include "hw/sysbus.h" #include "hw/hw.h" #include "net/net.h" |