/*
* QEMU e1000 emulation
*
+ * Software developer's manual:
+ * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf
+ *
* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc.
* Copyright (c) 2008 Qumranet
* Based on work done by:
#include "e1000_hw.h"
-#define DEBUG
+#define E1000_DEBUG
-#ifdef DEBUG
+#ifdef E1000_DEBUG
enum {
DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT,
DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM,
#define IOPORT_SIZE 0x40
#define PNPMMIO_SIZE 0x20000
+#define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */
/*
* HW models:
typedef struct E1000State_st {
PCIDevice dev;
- VLANClientState *vc;
+ NICState *nic;
NICConf conf;
int mmio_index;
struct e1000_tx {
unsigned char header[256];
unsigned char vlan_header[4];
+ /* Fields vlan and data must not be reordered or separated. */
unsigned char vlan[4];
unsigned char data[0x10000];
uint16_t size;
};
static void
-ioport_map(PCIDevice *pci_dev, int region_num, uint32_t addr,
- uint32_t size, int type)
+ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr,
+ pcibus_t size, int type)
{
- DBGOUT(IO, "e1000_ioport_map addr=0x%04x size=0x%08x\n", addr, size);
+ DBGOUT(IO, "e1000_ioport_map addr=0x%04"FMT_PCIBUS
+ " size=0x%08"FMT_PCIBUS"\n", addr, size);
}
static void
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_SK & (val ^ oldval))) // no clock edge
return;
if (!(E1000_EECD_SK & val)) { // falling edge
s->eecd_state.bitnum_out++;
return;
}
- if (!(val & E1000_EECD_CS)) { // rising, no CS (EEPROM reset)
- memset(&s->eecd_state, 0, sizeof s->eecd_state);
- /*
- * restore old_eecd's E1000_EECD_SK (known to be on)
- * to avoid false detection of a clock edge
- */
- s->eecd_state.old_eecd = E1000_EECD_SK;
- return;
- }
s->eecd_state.val_in <<= 1;
if (val & E1000_EECD_DI)
s->eecd_state.val_in |= 1;
return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
}
+/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
+ * fill it in, just pad descriptor length by 4 bytes unless guest
+ * told us to strip it off the packet. */
+static inline int
+fcs_len(E1000State *s)
+{
+ return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
+}
+
static void
xmit_seg(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++;
}
if (tp->sum_needed & E1000_TXD_POPTS_IXSM)
putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse);
if (tp->vlan_needed) {
- memmove(tp->vlan, tp->data, 12);
+ memmove(tp->vlan, tp->data, 4);
+ memmove(tp->data, tp->data + 4, 8);
memcpy(tp->data + 8, tp->vlan_header, 4);
- qemu_send_packet(s->vc, tp->vlan, tp->size + 4);
+ qemu_send_packet(&s->nic->nc, tp->vlan, tp->size + 4);
} else
- qemu_send_packet(s->vc, tp->data, tp->size);
+ qemu_send_packet(&s->nic->nc, tp->data, tp->size);
s->mac_reg[TPT]++;
s->mac_reg[GPTC]++;
n = s->mac_reg[TOTL];
static int
receive_filter(E1000State *s, const uint8_t *buf, int size)
{
- static uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- static int mta_shift[] = {4, 3, 2, 0};
+ 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;
if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) {
}
static void
-e1000_set_link_status(VLANClientState *vc)
+e1000_set_link_status(VLANClientState *nc)
{
- E1000State *s = vc->opaque;
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
uint32_t old_status = s->mac_reg[STATUS];
- if (vc->link_down)
+ if (nc->link_down)
s->mac_reg[STATUS] &= ~E1000_STATUS_LU;
else
s->mac_reg[STATUS] |= E1000_STATUS_LU;
}
static int
-e1000_can_receive(VLANClientState *vc)
+e1000_can_receive(VLANClientState *nc)
{
- E1000State *s = vc->opaque;
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
return (s->mac_reg[RCTL] & E1000_RCTL_EN);
}
static ssize_t
-e1000_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
+e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
{
- E1000State *s = vc->opaque;
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
struct e1000_rx_desc desc;
target_phys_addr_t base;
unsigned int n, rdt;
uint32_t rdh_start;
uint16_t vlan_special = 0;
uint8_t vlan_status = 0, vlan_offset = 0;
+ uint8_t min_buf[MIN_BUF_SIZE];
if (!(s->mac_reg[RCTL] & E1000_RCTL_EN))
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 (size > s->rxbuf_size) {
DBGOUT(RX, "packet too large for buffers (%lu > %d)\n",
(unsigned long)size, s->rxbuf_size);
if (vlan_enabled(s) && is_vlan_packet(s, buf)) {
vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(buf + 14)));
- memmove((void *)(buf + 4), buf, 12);
+ memmove((uint8_t *)buf + 4, buf, 12);
vlan_status = E1000_RXD_STAT_VP;
vlan_offset = 4;
size -= 4;
}
rdh_start = s->mac_reg[RDH];
- size += 4; // for the header
do {
if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) {
set_ics(s, 0, E1000_ICS_RXO);
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);
+ 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
DBGOUT(RX, "Null RX descriptor!!\n");
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])
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),
[TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4,
[GPTC] = mac_read_clr4, [TPR] = mac_read_clr4, [TPT] = mac_read_clr4,
static void
e1000_mmio_map(PCIDevice *pci_dev, int region_num,
- uint32_t addr, uint32_t size, int type)
+ pcibus_t addr, pcibus_t size, int type)
{
E1000State *d = DO_UPCAST(E1000State, dev, pci_dev);
int i;
};
- DBGOUT(MMIO, "e1000_mmio_map addr=0x%08x 0x%08x\n", addr, size);
+ DBGOUT(MMIO, "e1000_mmio_map addr=0x%08"FMT_PCIBUS" 0x%08"FMT_PCIBUS"\n",
+ addr, size);
cpu_register_physical_memory(addr, PNPMMIO_SIZE, d->mmio_index);
qemu_register_coalesced_mmio(addr, excluded_regs[0]);
}
static void
-e1000_cleanup(VLANClientState *vc)
+e1000_cleanup(VLANClientState *nc)
{
- E1000State *d = vc->opaque;
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
- d->vc = NULL;
+ s->nic = NULL;
}
static int
E1000State *d = DO_UPCAST(E1000State, dev, dev);
cpu_unregister_io_memory(d->mmio_index);
- qemu_del_vlan_client(d->vc);
- vmstate_unregister(&vmstate_e1000, d);
+ qemu_del_vlan_client(&d->nic->nc);
return 0;
}
memset(&d->tx, 0, sizeof d->tx);
}
+static NetClientInfo net_e1000_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = e1000_can_receive,
+ .receive = e1000_receive,
+ .cleanup = e1000_cleanup,
+ .link_status_changed = e1000_set_link_status,
+};
+
static int pci_e1000_init(PCIDevice *pci_dev)
{
E1000State *d = DO_UPCAST(E1000State, dev, pci_dev);
pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
pci_config_set_device_id(pci_conf, E1000_DEVID);
- *(uint16_t *)(pci_conf+0x04) = cpu_to_le16(0x0407);
- *(uint16_t *)(pci_conf+0x06) = cpu_to_le16(0x0010);
- pci_conf[0x08] = 0x03;
+ /* TODO: we have no capabilities, so why is this bit set? */
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST);
+ pci_conf[PCI_REVISION_ID] = 0x03;
pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
- pci_conf[0x0c] = 0x10;
+ /* TODO: RST# value should be 0, PCI spec 6.2.4 */
+ pci_conf[PCI_CACHE_LINE_SIZE] = 0x10;
- pci_conf[0x3d] = 1; // interrupt pin 0
+ /* TODO: RST# value should be 0 if programmable, PCI spec 6.2.4 */
+ pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0
d->mmio_index = cpu_register_io_memory(e1000_mmio_read,
e1000_mmio_write, d);
pci_register_bar((PCIDevice *)d, 0, PNPMMIO_SIZE,
- PCI_ADDRESS_SPACE_MEM, e1000_mmio_map);
+ PCI_BASE_ADDRESS_SPACE_MEMORY, e1000_mmio_map);
pci_register_bar((PCIDevice *)d, 1, IOPORT_SIZE,
- PCI_ADDRESS_SPACE_IO, ioport_map);
+ PCI_BASE_ADDRESS_SPACE_IO, ioport_map);
memmove(d->eeprom_data, e1000_eeprom_template,
sizeof e1000_eeprom_template);
checksum = (uint16_t) EEPROM_SUM - checksum;
d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
- d->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC,
- d->conf.vlan, d->conf.peer,
- d->dev.qdev.info->name, d->dev.qdev.id,
- e1000_can_receive, e1000_receive, NULL,
- NULL, e1000_cleanup, d);
- d->vc->link_status_changed = e1000_set_link_status;
-
- qemu_format_nic_info_str(d->vc, macaddr);
+ d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
+ d->dev.qdev.info->name, d->dev.qdev.id, d);
- vmstate_register(-1, &vmstate_e1000, d);
- e1000_reset(d);
-
- if (!pci_dev->qdev.hotplugged) {
- static int loaded = 0;
- if (!loaded) {
- rom_add_option("pxe-e1000.bin");
- loaded = 1;
- }
- }
+ qemu_format_nic_info_str(&d->nic->nc, macaddr);
return 0;
}
.qdev.desc = "Intel Gigabit Ethernet",
.qdev.size = sizeof(E1000State),
.qdev.reset = qdev_e1000_reset,
+ .qdev.vmsd = &vmstate_e1000,
.init = pci_e1000_init,
.exit = pci_e1000_uninit,
+ .romfile = "pxe-e1000.bin",
.qdev.props = (Property[]) {
DEFINE_NIC_PROPERTIES(E1000State, conf),
DEFINE_PROP_END_OF_LIST(),