#include "net.h"
#include "net/checksum.h"
#include "loader.h"
+#include "sysemu.h"
#include "e1000_hw.h"
#define IOPORT_SIZE 0x40
#define PNPMMIO_SIZE 0x20000
+#define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */
/*
* HW models:
/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
* fill it in, just pad descriptor length by 4 bytes unless guest
- * told us to trip it off the packet. */
+ * told us to strip it off the packet. */
static inline int
fcs_len(E1000State *s)
{
} else // UDP
cpu_to_be16wu((uint16_t *)(tp->data+css+4), len);
if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
+ unsigned int phsum;
// add pseudo-header length before checksum calculation
sp = (uint16_t *)(tp->data + tp->tucso);
- cpu_to_be16wu(sp, be16_to_cpup(sp) + len);
+ phsum = be16_to_cpup(sp) + len;
+ phsum = (phsum >> 16) + (phsum & 0xffff);
+ cpu_to_be16wu(sp, phsum);
}
tp->tso_frames++;
}
return;
} else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
// data descriptor
- tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
+ if (tp->size == 0) {
+ tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
+ }
tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0;
- } else
+ } else {
// legacy descriptor
tp->cptse = 0;
+ }
if (vlan_enabled(s) && is_vlan_txd(txd_lower) &&
(tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) {
return E1000_ICR_TXDW;
}
+static uint64_t tx_desc_base(E1000State *s)
+{
+ uint64_t bah = s->mac_reg[TDBAH];
+ uint64_t bal = s->mac_reg[TDBAL] & ~0xf;
+
+ return (bah << 32) + bal;
+}
+
static void
start_xmit(E1000State *s)
{
}
while (s->mac_reg[TDH] != s->mac_reg[TDT]) {
- base = ((uint64_t)s->mac_reg[TDBAH] << 32) + s->mac_reg[TDBAL] +
+ base = tx_desc_base(s) +
sizeof(struct e1000_tx_desc) * s->mac_reg[TDH];
cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
set_ics(s, 0, E1000_ICR_LSC);
}
+static bool e1000_has_rxbufs(E1000State *s, size_t total_size)
+{
+ int bufs;
+ /* Fast-path short packets */
+ if (total_size <= s->rxbuf_size) {
+ return s->mac_reg[RDH] != s->mac_reg[RDT] || !s->check_rxov;
+ }
+ if (s->mac_reg[RDH] < s->mac_reg[RDT]) {
+ bufs = s->mac_reg[RDT] - s->mac_reg[RDH];
+ } else if (s->mac_reg[RDH] > s->mac_reg[RDT] || !s->check_rxov) {
+ bufs = s->mac_reg[RDLEN] / sizeof(struct e1000_rx_desc) +
+ s->mac_reg[RDT] - s->mac_reg[RDH];
+ } else {
+ return false;
+ }
+ return total_size <= bufs * s->rxbuf_size;
+}
+
static int
e1000_can_receive(VLANClientState *nc)
{
E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
- return (s->mac_reg[RCTL] & E1000_RCTL_EN);
+ return (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1);
+}
+
+static uint64_t rx_desc_base(E1000State *s)
+{
+ uint64_t bah = s->mac_reg[RDBAH];
+ uint64_t bal = s->mac_reg[RDBAL] & ~0xf;
+
+ return (bah << 32) + bal;
}
static ssize_t
uint32_t rdh_start;
uint16_t vlan_special = 0;
uint8_t vlan_status = 0, vlan_offset = 0;
+ uint8_t min_buf[MIN_BUF_SIZE];
+ size_t desc_offset;
+ size_t desc_size;
+ size_t total_size;
if (!(s->mac_reg[RCTL] & E1000_RCTL_EN))
return -1;
- if (size > s->rxbuf_size) {
- DBGOUT(RX, "packet too large for buffers (%lu > %d)\n",
- (unsigned long)size, s->rxbuf_size);
- return -1;
+ /* Pad to minimum Ethernet frame length */
+ if (size < sizeof(min_buf)) {
+ memcpy(min_buf, buf, size);
+ memset(&min_buf[size], 0, sizeof(min_buf) - size);
+ buf = min_buf;
+ size = sizeof(min_buf);
}
if (!receive_filter(s, buf, size))
}
rdh_start = s->mac_reg[RDH];
- do {
- if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) {
+ desc_offset = 0;
+ total_size = size + fcs_len(s);
+ if (!e1000_has_rxbufs(s, total_size)) {
set_ics(s, 0, E1000_ICS_RXO);
return -1;
+ }
+ do {
+ desc_size = total_size - desc_offset;
+ if (desc_size > s->rxbuf_size) {
+ desc_size = s->rxbuf_size;
}
- base = ((uint64_t)s->mac_reg[RDBAH] << 32) + s->mac_reg[RDBAL] +
- sizeof(desc) * s->mac_reg[RDH];
+ base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH];
cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
desc.special = vlan_special;
desc.status |= (vlan_status | E1000_RXD_STAT_DD);
if (desc.buffer_addr) {
- cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr),
- (void *)(buf + vlan_offset), size);
- desc.length = cpu_to_le16(size + fcs_len(s));
- desc.status |= E1000_RXD_STAT_EOP|E1000_RXD_STAT_IXSM;
- } else // as per intel docs; skip descriptors with null buf addr
+ if (desc_offset < size) {
+ size_t copy_size = size - desc_offset;
+ if (copy_size > s->rxbuf_size) {
+ copy_size = s->rxbuf_size;
+ }
+ cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr),
+ (void *)(buf + desc_offset + vlan_offset),
+ copy_size);
+ }
+ desc_offset += desc_size;
+ desc.length = cpu_to_le16(desc_size);
+ if (desc_offset >= total_size) {
+ desc.status |= E1000_RXD_STAT_EOP | E1000_RXD_STAT_IXSM;
+ } else {
+ /* Guest zeroing out status is not a hardware requirement.
+ Clear EOP in case guest didn't do it. */
+ desc.status &= ~E1000_RXD_STAT_EOP;
+ }
+ } else { // as per intel docs; skip descriptors with null buf addr
DBGOUT(RX, "Null RX descriptor!!\n");
+ }
cpu_physical_memory_write(base, (void *)&desc, sizeof(desc));
if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
set_ics(s, 0, E1000_ICS_RXO);
return -1;
}
- } while (desc.buffer_addr == 0);
+ } while (desc_offset < total_size);
s->mac_reg[GPRC]++;
s->mac_reg[TPR]++;
- n = s->mac_reg[TORL];
- if ((s->mac_reg[TORL] += size) < n)
+ /* TOR - Total Octets Received:
+ * This register includes bytes received in a packet from the <Destination
+ * Address> field through the <CRC> field, inclusively.
+ */
+ 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;
n = E1000_ICS_RXT0;
if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
E1000State *s = opaque;
unsigned int index = (addr & 0x1ffff) >> 2;
-#ifdef TARGET_WORDS_BIGENDIAN
- val = bswap32(val);
-#endif
- if (index < NWRITEOPS && macreg_writeops[index])
+ if (index < NWRITEOPS && macreg_writeops[index]) {
macreg_writeops[index](s, index, val);
- else if (index < NREADOPS && macreg_readops[index])
+ } else if (index < NREADOPS && macreg_readops[index]) {
DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04x\n", index<<2, val);
- else
+ } else {
DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08x\n",
index<<2, val);
+ }
}
static void
if (index < NREADOPS && macreg_readops[index])
{
- uint32_t val = macreg_readops[index](s, index);
-#ifdef TARGET_WORDS_BIGENDIAN
- val = bswap32(val);
-#endif
- return val;
+ return macreg_readops[index](s, index);
}
DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
return 0;
pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0
d->mmio_index = cpu_register_io_memory(e1000_mmio_read,
- e1000_mmio_write, d);
+ e1000_mmio_write, d, DEVICE_LITTLE_ENDIAN);
- pci_register_bar((PCIDevice *)d, 0, PNPMMIO_SIZE,
+ pci_register_bar(&d->dev, 0, PNPMMIO_SIZE,
PCI_BASE_ADDRESS_SPACE_MEMORY, e1000_mmio_map);
- pci_register_bar((PCIDevice *)d, 1, IOPORT_SIZE,
+ pci_register_bar(&d->dev, 1, IOPORT_SIZE,
PCI_BASE_ADDRESS_SPACE_IO, ioport_map);
memmove(d->eeprom_data, e1000_eeprom_template,
d->dev.qdev.info->name, d->dev.qdev.id, d);
qemu_format_nic_info_str(&d->nic->nc, macaddr);
+
+ add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
+
return 0;
}