]> Git Repo - qemu.git/blobdiff - hw/e1000.c
PPC: Bump MPIC up to 32 supported CPUs
[qemu.git] / hw / e1000.c
index 616d685deb94795b68d8f24b1a37437b1ded2658..ce8fc8b510d411bf572a1ca8300df334a766285f 100644 (file)
 #include "net.h"
 #include "net/checksum.h"
 #include "loader.h"
+#include "sysemu.h"
 
 #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,
@@ -55,6 +56,7 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
 
 #define IOPORT_SIZE       0x40
 #define PNPMMIO_SIZE      0x20000
+#define MIN_BUF_SIZE      60 /* Min. octets in an ethernet frame sans FCS */
 
 /*
  * HW models:
@@ -80,7 +82,8 @@ typedef struct E1000State_st {
     PCIDevice dev;
     NICState *nic;
     NICConf conf;
-    int mmio_index;
+    MemoryRegion mmio;
+    MemoryRegion io;
 
     uint32_t mac_reg[0x8000];
     uint16_t phy_reg[0x20];
@@ -148,14 +151,6 @@ static const char phy_regcap[0x20] = {
     [PHY_ID2] = PHY_R,         [M88E1000_PHY_SPEC_STATUS] = PHY_R
 };
 
-static void
-ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr,
-           pcibus_t size, int type)
-{
-    DBGOUT(IO, "e1000_ioport_map addr=0x%04"FMT_PCIBUS
-           " size=0x%08"FMT_PCIBUS"\n", addr, size);
-}
-
 static void
 set_interrupt_cause(E1000State *s, int index, uint32_t val)
 {
@@ -262,21 +257,20 @@ 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_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;
@@ -344,6 +338,15 @@ is_vlan_txd(uint32_t txd_lower)
     return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
 }
 
+/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
+ * fill it in, just pad descriptor length by 4 bytes unless guest
+ * told us to strip it off the packet. */
+static inline int
+fcs_len(E1000State *s)
+{
+    return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
+}
+
 static void
 xmit_seg(E1000State *s)
 {
@@ -375,9 +378,12 @@ 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++;
     }
@@ -433,11 +439,14 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
         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)) {
@@ -501,6 +510,14 @@ txdesc_writeback(target_phys_addr_t base, struct e1000_tx_desc *dp)
     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)
 {
@@ -514,7 +531,7 @@ 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));
 
@@ -544,8 +561,8 @@ start_xmit(E1000State *s)
 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)) {
@@ -600,21 +617,50 @@ e1000_set_link_status(VLANClientState *nc)
     E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
     uint32_t old_status = s->mac_reg[STATUS];
 
-    if (nc->link_down)
+    if (nc->link_down) {
         s->mac_reg[STATUS] &= ~E1000_STATUS_LU;
-    else
+        s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS;
+    } else {
         s->mac_reg[STATUS] |= E1000_STATUS_LU;
+        s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS;
+    }
 
     if (s->mac_reg[STATUS] != old_status)
         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
@@ -627,14 +673,20 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
     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))
@@ -642,31 +694,50 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t 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) {
+    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);
-            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])
@@ -679,13 +750,18 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
             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])
@@ -825,71 +901,69 @@ static void (*macreg_writeops[])(E1000State *, int, uint32_t) = {
 enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) };
 
 static void
-e1000_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+e1000_mmio_write(void *opaque, target_phys_addr_t addr, uint64_t val,
+                 unsigned size)
 {
     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])
-        DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04x\n", index<<2, val);
-    else
-        DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08x\n",
+    } else if (index < NREADOPS && macreg_readops[index]) {
+        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);
+    }
 }
 
-static void
-e1000_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-    // emulate hw without byte enables: no RMW
-    e1000_mmio_writel(opaque, addr & ~3,
-                      (val & 0xffff) << (8*(addr & 3)));
-}
-
-static void
-e1000_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-    // emulate hw without byte enables: no RMW
-    e1000_mmio_writel(opaque, addr & ~3,
-                      (val & 0xff) << (8*(addr & 3)));
-}
-
-static uint32_t
-e1000_mmio_readl(void *opaque, target_phys_addr_t addr)
+static uint64_t
+e1000_mmio_read(void *opaque, target_phys_addr_t addr, unsigned size)
 {
     E1000State *s = opaque;
     unsigned int index = (addr & 0x1ffff) >> 2;
 
     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;
 }
 
-static uint32_t
-e1000_mmio_readb(void *opaque, target_phys_addr_t addr)
+static const MemoryRegionOps e1000_mmio_ops = {
+    .read = e1000_mmio_read,
+    .write = e1000_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static uint64_t e1000_io_read(void *opaque, target_phys_addr_t addr,
+                              unsigned size)
 {
-    return ((e1000_mmio_readl(opaque, addr & ~3)) >>
-            (8 * (addr & 3))) & 0xff;
+    E1000State *s = opaque;
+
+    (void)s;
+    return 0;
 }
 
-static uint32_t
-e1000_mmio_readw(void *opaque, target_phys_addr_t addr)
+static void e1000_io_write(void *opaque, target_phys_addr_t addr,
+                           uint64_t val, unsigned size)
 {
-    return ((e1000_mmio_readl(opaque, addr & ~3)) >>
-            (8 * (addr & 3))) & 0xffff;
+    E1000State *s = opaque;
+
+    (void)s;
 }
 
+static const MemoryRegionOps e1000_io_ops = {
+    .read = e1000_io_read,
+    .write = e1000_io_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
 static bool is_version_1(void *opaque, int version_id)
 {
     return version_id == 1;
@@ -1009,36 +1083,22 @@ static const uint32_t mac_reg_init[] = {
 
 /* PCI interface */
 
-static CPUWriteMemoryFunc * const e1000_mmio_write[] = {
-    e1000_mmio_writeb, e1000_mmio_writew,      e1000_mmio_writel
-};
-
-static CPUReadMemoryFunc * const e1000_mmio_read[] = {
-    e1000_mmio_readb,  e1000_mmio_readw,       e1000_mmio_readl
-};
-
 static void
-e1000_mmio_map(PCIDevice *pci_dev, int region_num,
-                pcibus_t addr, pcibus_t size, int type)
+e1000_mmio_setup(E1000State *d)
 {
-    E1000State *d = DO_UPCAST(E1000State, dev, pci_dev);
     int i;
     const uint32_t excluded_regs[] = {
         E1000_MDIC, E1000_ICR, E1000_ICS, E1000_IMS,
         E1000_IMC, E1000_TCTL, E1000_TDT, PNPMMIO_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]);
-
+    memory_region_init_io(&d->mmio, &e1000_mmio_ops, d, "e1000-mmio",
+                          PNPMMIO_SIZE);
+    memory_region_add_coalescing(&d->mmio, 0, excluded_regs[0]);
     for (i = 0; excluded_regs[i] != PNPMMIO_SIZE; i++)
-        qemu_register_coalesced_mmio(addr + excluded_regs[i] + 4,
-                                     excluded_regs[i + 1] -
-                                     excluded_regs[i] - 4);
+        memory_region_add_coalescing(&d->mmio, excluded_regs[i] + 4,
+                                     excluded_regs[i+1] - excluded_regs[i] - 4);
+    memory_region_init_io(&d->io, &e1000_io_ops, d, "e1000-io", IOPORT_SIZE);
 }
 
 static void
@@ -1054,7 +1114,8 @@ pci_e1000_uninit(PCIDevice *dev)
 {
     E1000State *d = DO_UPCAST(E1000State, dev, dev);
 
-    cpu_unregister_io_memory(d->mmio_index);
+    memory_region_destroy(&d->mmio);
+    memory_region_destroy(&d->io);
     qemu_del_vlan_client(&d->nic->nc);
     return 0;
 }
@@ -1090,23 +1151,16 @@ static int pci_e1000_init(PCIDevice *pci_dev)
 
     pci_conf = d->dev.config;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, E1000_DEVID);
-    *(uint16_t *)(pci_conf+0x06) = cpu_to_le16(0x0010);
-    pci_conf[0x08] = 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
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
 
-    d->mmio_index = cpu_register_io_memory(e1000_mmio_read,
-            e1000_mmio_write, d);
+    e1000_mmio_setup(d);
 
-    pci_register_bar((PCIDevice *)d, 0, PNPMMIO_SIZE,
-                           PCI_BASE_ADDRESS_SPACE_MEMORY, e1000_mmio_map);
+    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
 
-    pci_register_bar((PCIDevice *)d, 1, IOPORT_SIZE,
-                           PCI_BASE_ADDRESS_SPACE_IO, ioport_map);
+    pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io);
 
     memmove(d->eeprom_data, e1000_eeprom_template,
         sizeof e1000_eeprom_template);
@@ -1123,6 +1177,9 @@ static int pci_e1000_init(PCIDevice *pci_dev)
                           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;
 }
 
@@ -1140,7 +1197,11 @@ static PCIDeviceInfo e1000_info = {
     .qdev.vmsd  = &vmstate_e1000,
     .init       = pci_e1000_init,
     .exit       = pci_e1000_uninit,
-    .romfile    = "pxe-e1000.bin",
+    .romfile    = "pxe-e1000.rom",
+    .vendor_id  = PCI_VENDOR_ID_INTEL,
+    .device_id  = E1000_DEVID,
+    .revision   = 0x03,
+    .class_id   = PCI_CLASS_NETWORK_ETHERNET,
     .qdev.props = (Property[]) {
         DEFINE_NIC_PROPERTIES(E1000State, conf),
         DEFINE_PROP_END_OF_LIST(),
This page took 0.038179 seconds and 4 git commands to generate.