]> git.proxmox.com Git - qemu.git/blobdiff - hw/e1000.c
e1000.c doesn't properly emulate EERD and ICS registers
[qemu.git] / hw / e1000.c
index d573f7033b4648166d629b15de8fb4a583cfb24c..c55007a94a259b675f79f73cfc7d187f871c9075 100644 (file)
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
  */
 
 
@@ -27,8 +27,6 @@
 #include "pci.h"
 #include "net.h"
 
-#define __iomem
-typedef int boolean_t;
 #include "e1000_hw.h"
 
 #define DEBUG
@@ -52,7 +50,7 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
 #endif
 
 #define IOPORT_SIZE       0x40
-#define PNPMMIO_SIZE      0x60000
+#define PNPMMIO_SIZE      0x20000
 
 /*
  * HW models:
@@ -77,9 +75,6 @@ enum {
 typedef struct E1000State_st {
     PCIDevice dev;
     VLANClientState *vc;
-    NICInfo *nd;
-    uint32_t instance;
-    uint32_t mmio_base;
     int mmio_index;
 
     uint32_t mac_reg[0x8000];
@@ -91,9 +86,12 @@ typedef struct E1000State_st {
     int check_rxov;
     struct e1000_tx {
         unsigned char header[256];
+        unsigned char vlan_header[4];
+        unsigned char vlan[4];
         unsigned char data[0x10000];
         uint16_t size;
         unsigned char sum_needed;
+        unsigned char vlan_needed;
         uint8_t ipcss;
         uint8_t ipcso;
         uint16_t ipcse;
@@ -105,8 +103,9 @@ typedef struct E1000State_st {
         uint32_t paylen;
         uint16_t tso_frames;
         char tse;
-        char ip;
-        char tcp;
+        int8_t ip;
+        int8_t tcp;
+        char cptse;     // current packet tse bit
     } tx;
 
     struct {
@@ -129,17 +128,18 @@ enum {
     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(RA),                defreg(MTA),    defreg(CRCERRS),defreg(VFTA),
+    defreg(VET),
 };
 
 enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };
-static char phy_regcap[0x20] = {
+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,
+    [PHY_ID2] = PHY_R,         [M88E1000_PHY_SPEC_STATUS] = PHY_R
 };
 
 static void
@@ -155,6 +155,7 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val)
     if (val)
         val |= E1000_ICR_INT_ASSERTED;
     s->mac_reg[ICR] = val;
+    s->mac_reg[ICS] = val;
     qemu_set_irq(s->dev.irq[0], (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0);
 }
 
@@ -189,6 +190,13 @@ rxbufsize(uint32_t v)
     return 2048;
 }
 
+static void
+set_ctrl(E1000State *s, int index, uint32_t val)
+{
+    /* RST is self clearing */
+    s->mac_reg[CTRL] = val & ~E1000_CTRL_RST;
+}
+
 static void
 set_rx_control(E1000State *s, int index, uint32_t val)
 {
@@ -255,6 +263,11 @@ set_eecd(E1000State *s, int index, uint32_t val)
     }
     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;
@@ -275,32 +288,53 @@ flash_eerd_read(E1000State *s, int x)
 {
     unsigned int index, r = s->mac_reg[EERD] & ~E1000_EEPROM_RW_REG_START;
 
-    if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG)
-        return 0;
-    return (s->eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) |
-           E1000_EEPROM_RW_REG_DONE | r;
-}
+    if ((s->mac_reg[EERD] & E1000_EEPROM_RW_REG_START) == 0)
+        return (s->mac_reg[EERD]);
 
-static unsigned int
-do_cksum(uint8_t *dp, uint8_t *de)
-{
-    unsigned int bsum[2] = {0, 0}, i, sum;
+    if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG)
+        return (E1000_EEPROM_RW_REG_DONE | r);
 
-    for (i = 1; dp < de; bsum[i^=1] += *dp++)
-        ;
-    sum = (bsum[0] << 8) + bsum[1];
-    sum = (sum >> 16) + (sum & 0xffff);
-    return ~(sum + (sum >> 16));
+    return ((s->eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) |
+           E1000_EEPROM_RW_REG_DONE | r);
 }
 
 static void
 putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse)
 {
+    uint32_t sum;
+
     if (cse && cse < n)
         n = cse + 1;
-    if (sloc < n-1)
+    if (sloc < n-1) {
+        sum = net_checksum_add(n-css, data+css);
         cpu_to_be16wu((uint16_t *)(data + sloc),
-                      do_cksum(data + css, data + n));
+                      net_checksum_finish(sum));
+    }
+}
+
+static inline int
+vlan_enabled(E1000State *s)
+{
+    return ((s->mac_reg[CTRL] & E1000_CTRL_VME) != 0);
+}
+
+static inline int
+vlan_rx_filter_enabled(E1000State *s)
+{
+    return ((s->mac_reg[RCTL] & E1000_RCTL_VFE) != 0);
+}
+
+static inline int
+is_vlan_packet(E1000State *s, const uint8_t *buf)
+{
+    return (be16_to_cpup((uint16_t *)(buf + 12)) ==
+                le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
+}
+
+static inline int
+is_vlan_txd(uint32_t txd_lower)
+{
+    return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
 }
 
 static void
@@ -310,7 +344,7 @@ xmit_seg(E1000State *s)
     unsigned int frames = s->tx.tso_frames, css, sofar, n;
     struct e1000_tx *tp = &s->tx;
 
-    if (tp->tse) {
+    if (tp->tse && tp->cptse) {
         css = tp->ipcss;
         DBGOUT(TXSUM, "frames %d size %d ipcss %d\n",
                frames, tp->size, css);
@@ -328,7 +362,7 @@ xmit_seg(E1000State *s)
         if (tp->tcp) {
             sofar = frames * tp->mss;
             cpu_to_be32wu((uint32_t *)(tp->data+css+4),        // seq
-                be32_to_cpup((uint32_t *)(tp->data+css+4))+sofar);
+                be32_to_cpupu((uint32_t *)(tp->data+css+4))+sofar);
             if (tp->paylen - sofar > tp->mss)
                 tp->data[css + 13] &= ~9;              // PSH, FIN
         } else // UDP
@@ -345,7 +379,12 @@ xmit_seg(E1000State *s)
         putsum(tp->data, tp->size, tp->tucso, tp->tucss, tp->tucse);
     if (tp->sum_needed & E1000_TXD_POPTS_IXSM)
         putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse);
-    qemu_send_packet(s->vc, tp->data, tp->size);
+    if (tp->vlan_needed) {
+        memmove(tp->vlan, tp->data, 12);
+        memcpy(tp->data + 8, tp->vlan_header, 4);
+        qemu_send_packet(s->vc, tp->vlan, tp->size + 4);
+    } else
+        qemu_send_packet(s->vc, tp->data, tp->size);
     s->mac_reg[TPT]++;
     s->mac_reg[GPTC]++;
     n = s->mac_reg[TOTL];
@@ -384,37 +423,59 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
             tp->tucso = tp->tucss + (tp->tcp ? 16 : 6);
         }
         return;
-    } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D))
+    } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
+        // data descriptor
         tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
-
+        tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0;
+    } else
+        // legacy descriptor
+        tp->cptse = 0;
+
+    if (vlan_enabled(s) && is_vlan_txd(txd_lower) &&
+        (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) {
+        tp->vlan_needed = 1;
+        cpu_to_be16wu((uint16_t *)(tp->vlan_header),
+                      le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
+        cpu_to_be16wu((uint16_t *)(tp->vlan_header + 2),
+                      le16_to_cpu(dp->upper.fields.special));
+    }
+        
     addr = le64_to_cpu(dp->buffer_addr);
-    if (tp->tse) {
+    if (tp->tse && tp->cptse) {
         hdr = tp->hdr_len;
         msh = hdr + tp->mss;
+        do {
+            bytes = split_size;
+            if (tp->size + bytes > msh)
+                bytes = msh - tp->size;
+            cpu_physical_memory_read(addr, tp->data + tp->size, bytes);
+            if ((sz = tp->size + bytes) >= hdr && tp->size < hdr)
+                memmove(tp->header, tp->data, hdr);
+            tp->size = sz;
+            addr += bytes;
+            if (sz == msh) {
+                xmit_seg(s);
+                memmove(tp->data, tp->header, hdr);
+                tp->size = hdr;
+            }
+        } while (split_size -= bytes);
+    } else if (!tp->tse && tp->cptse) {
+        // context descriptor TSE is not set, while data descriptor TSE is set
+        DBGOUT(TXERR, "TCP segmentaion Error\n");
+    } else {
+        cpu_physical_memory_read(addr, tp->data + tp->size, split_size);
+        tp->size += split_size;
     }
-    do {
-        bytes = split_size;
-        if (tp->size + bytes > msh)
-            bytes = msh - tp->size;
-        cpu_physical_memory_read(addr, tp->data + tp->size, bytes);
-        if ((sz = tp->size + bytes) >= hdr && tp->size < hdr)
-            memmove(tp->header, tp->data, hdr);
-        tp->size = sz;
-        addr += bytes;
-        if (sz == msh) {
-            xmit_seg(s);
-            memmove(tp->data, tp->header, hdr);
-            tp->size = hdr;
-        }
-    } while (split_size -= bytes);
 
     if (!(txd_lower & E1000_TXD_CMD_EOP))
         return;
-    if (tp->size > hdr)
+    if (!(tp->tse && tp->cptse && tp->size < hdr))
         xmit_seg(s);
     tp->tso_frames = 0;
     tp->sum_needed = 0;
+    tp->vlan_needed = 0;
     tp->size = 0;
+    tp->cptse = 0;
 }
 
 static uint32_t
@@ -450,7 +511,7 @@ start_xmit(E1000State *s)
         cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
 
         DBGOUT(TX, "index %d: %p : %x %x\n", s->mac_reg[TDH],
-               (void *)desc.buffer_addr, desc.lower.data,
+               (void *)(intptr_t)desc.buffer_addr, desc.lower.data,
                desc.upper.data);
 
         process_tx_desc(s, &desc);
@@ -479,6 +540,14 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
     static 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)) {
+        uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
+        uint32_t vfta = le32_to_cpup((uint32_t *)(s->mac_reg + VFTA) +
+                                     ((vid >> 5) & 0x7f));
+        if ((vfta & (1 << (vid & 0x1f))) == 0)
+            return 0;
+    }
+
     if (rctl & E1000_RCTL_UPE)                 // promiscuous
         return 1;
 
@@ -517,13 +586,27 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
     return 0;
 }
 
+static void
+e1000_set_link_status(VLANClientState *vc)
+{
+    E1000State *s = vc->opaque;
+    uint32_t old_status = s->mac_reg[STATUS];
+
+    if (vc->link_down)
+        s->mac_reg[STATUS] &= ~E1000_STATUS_LU;
+    else
+        s->mac_reg[STATUS] |= E1000_STATUS_LU;
+
+    if (s->mac_reg[STATUS] != old_status)
+        set_ics(s, 0, E1000_ICR_LSC);
+}
+
 static int
 e1000_can_receive(void *opaque)
 {
     E1000State *s = opaque;
 
-    return (!(s->mac_reg[RCTL] & E1000_RCTL_EN) ||
-            s->mac_reg[RDH] != s->mac_reg[RDT]);
+    return (s->mac_reg[RCTL] & E1000_RCTL_EN);
 }
 
 static void
@@ -534,6 +617,8 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
     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;
 
     if (!(s->mac_reg[RCTL] & E1000_RCTL_EN))
         return;
@@ -547,6 +632,14 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
     if (!receive_filter(s, buf, size))
         return;
 
+    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);
+        vlan_status = E1000_RXD_STAT_VP;
+        vlan_offset = 4;
+        size -= 4;
+    }
+
     rdh_start = s->mac_reg[RDH];
     size += 4; // for the header
     do {
@@ -557,10 +650,11 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
         base = ((uint64_t)s->mac_reg[RDBAH] << 32) + s->mac_reg[RDBAL] +
                sizeof(desc) * s->mac_reg[RDH];
         cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
-        desc.status |= E1000_RXD_STAT_DD;
+        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, size);
+                                      (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
@@ -588,8 +682,8 @@ e1000_receive(void *opaque, const uint8_t *buf, int size)
     n = E1000_ICS_RXT0;
     if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
         rdt += s->mac_reg[RDLEN] / sizeof(desc);
-    if (((rdt - s->mac_reg[RDH]) * sizeof(desc)) << s->rxbuf_min_shift >=
-        s->mac_reg[RDLEN])
+    if (((rdt - s->mac_reg[RDH]) * sizeof(desc)) <= s->mac_reg[RDLEN] >>
+        s->rxbuf_min_shift)
         n |= E1000_ICS_RXDMT0;
 
     set_ics(s, 0, n);
@@ -690,7 +784,7 @@ static uint32_t (*macreg_readops[])(E1000State *, int) = {
     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(RDH),       getreg(RDT),    getreg(VET),    getreg(ICS),
 
     [TOTH] = mac_read_clr8,    [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4,
     [GPTC] = mac_read_clr4,    [TPR] = mac_read_clr4,  [TPT] = mac_read_clr4,
@@ -698,32 +792,37 @@ static uint32_t (*macreg_readops[])(E1000State *, int) = {
     [CRCERRS ... MPC] = &mac_readreg,
     [RA ... RA+31] = &mac_readreg,
     [MTA ... MTA+127] = &mac_readreg,
+    [VFTA ... VFTA+127] = &mac_readreg,
 };
-enum { NREADOPS = sizeof(macreg_readops) / sizeof(*macreg_readops) };
+enum { NREADOPS = ARRAY_SIZE(macreg_readops) };
 
 #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(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,
+    [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
     [RA ... RA+31] = &mac_writereg,
     [MTA ... MTA+127] = &mac_writereg,
+    [VFTA ... VFTA+127] = &mac_writereg,
 };
-enum { NWRITEOPS = sizeof(macreg_writeops) / sizeof(*macreg_writeops) };
+enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) };
 
 static void
 e1000_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
 {
     E1000State *s = opaque;
-    unsigned int index = ((addr - s->mmio_base) & 0x1ffff) >> 2;
+    unsigned int index = (addr & 0x1ffff) >> 2;
 
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap32(val);
+#endif
     if (index < NWRITEOPS && macreg_writeops[index])
-        macreg_writeops[index](s, index, le32_to_cpu(val));
+        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
@@ -736,7 +835,7 @@ 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,
-                      cpu_to_le32(le16_to_cpu(val & 0xffff) << (8*(addr & 3))));
+                      (val & 0xffff) << (8*(addr & 3)));
 }
 
 static void
@@ -744,17 +843,23 @@ 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,
-                      cpu_to_le32((val & 0xff)  << (8*(addr & 3))));
+                      (val & 0xff) << (8*(addr & 3)));
 }
 
 static uint32_t
 e1000_mmio_readl(void *opaque, target_phys_addr_t addr)
 {
     E1000State *s = opaque;
-    unsigned int index = ((addr - s->mmio_base) & 0x1ffff) >> 2;
+    unsigned int index = (addr & 0x1ffff) >> 2;
 
     if (index < NREADOPS && macreg_readops[index])
-        return cpu_to_le32(macreg_readops[index](s, index));
+    {
+        uint32_t val = macreg_readops[index](s, index);
+#ifdef TARGET_WORDS_BIGENDIAN
+        val = bswap32(val);
+#endif
+        return val;
+    }
     DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
     return 0;
 }
@@ -762,30 +867,31 @@ e1000_mmio_readl(void *opaque, target_phys_addr_t addr)
 static uint32_t
 e1000_mmio_readb(void *opaque, target_phys_addr_t addr)
 {
-    return (le32_to_cpu(e1000_mmio_readl(opaque, addr & ~3)) >>
+    return ((e1000_mmio_readl(opaque, addr & ~3)) >>
             (8 * (addr & 3))) & 0xff;
 }
 
 static uint32_t
 e1000_mmio_readw(void *opaque, target_phys_addr_t addr)
 {
-    return cpu_to_le16((le32_to_cpu(e1000_mmio_readl(opaque, addr & ~3)) >>
-                        (8 * (addr & 3))) & 0xffff);
+    return ((e1000_mmio_readl(opaque, addr & ~3)) >>
+            (8 * (addr & 3))) & 0xffff;
 }
 
-int mac_regtosave[] = {
+static const int mac_regtosave[] = {
     CTRL,      EECD,   EERD,   GPRC,   GPTC,   ICR,    ICS,    IMC,    IMS,
     LEDCTL,    MANC,   MDIC,   MPC,    PBA,    RCTL,   RDBAH,  RDBAL,  RDH,
     RDLEN,     RDT,    STATUS, SWSM,   TCTL,   TDBAH,  TDBAL,  TDH,    TDLEN,
     TDT,       TORH,   TORL,   TOTH,   TOTL,   TPR,    TPT,    TXDCTL, WUFC,
+    VET,
 };
-enum { MAC_NSAVE = sizeof mac_regtosave/sizeof *mac_regtosave };
+enum { MAC_NSAVE = ARRAY_SIZE(mac_regtosave) };
 
-struct {
+static const struct {
     int size;
     int array0;
-} mac_regarraystosave[] = { {32, RA}, {128, MTA} };
-enum { MAC_NARRAYS = sizeof mac_regarraystosave/sizeof *mac_regarraystosave };
+} mac_regarraystosave[] = { {32, RA}, {128, MTA}, {128, VFTA} };
+enum { MAC_NARRAYS = ARRAY_SIZE(mac_regarraystosave) };
 
 static void
 nic_save(QEMUFile *f, void *opaque)
@@ -794,8 +900,7 @@ nic_save(QEMUFile *f, void *opaque)
     int i, j;
 
     pci_device_save(&s->dev, f);
-    qemu_put_be32s(f, &s->instance);
-    qemu_put_be32s(f, &s->mmio_base);
+    qemu_put_be32(f, 0);
     qemu_put_be32s(f, &s->rxbuf_size);
     qemu_put_be32s(f, &s->rxbuf_min_shift);
     qemu_put_be32s(f, &s->eecd_state.val_in);
@@ -815,8 +920,8 @@ nic_save(QEMUFile *f, void *opaque)
     qemu_put_be16s(f, &s->tx.size);
     qemu_put_be16s(f, &s->tx.tso_frames);
     qemu_put_8s(f, &s->tx.sum_needed);
-    qemu_put_8s(f, &s->tx.ip);
-    qemu_put_8s(f, &s->tx.tcp);
+    qemu_put_s8s(f, &s->tx.ip);
+    qemu_put_s8s(f, &s->tx.tcp);
     qemu_put_buffer(f, s->tx.header, sizeof s->tx.header);
     qemu_put_buffer(f, s->tx.data, sizeof s->tx.data);
     for (i = 0; i < 64; i++)
@@ -839,8 +944,9 @@ nic_load(QEMUFile *f, void *opaque, int version_id)
 
     if ((ret = pci_device_load(&s->dev, f)) < 0)
         return ret;
-    qemu_get_be32s(f, &s->instance);
-    qemu_get_be32s(f, &s->mmio_base);
+    if (version_id == 1)
+        qemu_get_sbe32s(f, &i); /* once some unused instance id */
+    qemu_get_be32(f); /* Ignored.  Was mmio_base.  */
     qemu_get_be32s(f, &s->rxbuf_size);
     qemu_get_be32s(f, &s->rxbuf_min_shift);
     qemu_get_be32s(f, &s->eecd_state.val_in);
@@ -860,8 +966,8 @@ nic_load(QEMUFile *f, void *opaque, int version_id)
     qemu_get_be16s(f, &s->tx.size);
     qemu_get_be16s(f, &s->tx.tso_frames);
     qemu_get_8s(f, &s->tx.sum_needed);
-    qemu_get_8s(f, &s->tx.ip);
-    qemu_get_8s(f, &s->tx.tcp);
+    qemu_get_s8s(f, &s->tx.ip);
+    qemu_get_s8s(f, &s->tx.tcp);
     qemu_get_buffer(f, s->tx.header, sizeof s->tx.header);
     qemu_get_buffer(f, s->tx.data, sizeof s->tx.data);
     for (i = 0; i < 64; i++)
@@ -877,7 +983,7 @@ nic_load(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
-static uint16_t e1000_eeprom_template[64] = {
+static const uint16_t e1000_eeprom_template[64] = {
     0x0000, 0x0000, 0x0000, 0x0000,      0xffff, 0x0000,      0x0000, 0x0000,
     0x3000, 0x1000, 0x6403, E1000_DEVID, 0x8086, E1000_DEVID, 0x8086, 0x3040,
     0x0008, 0x2000, 0x7e14, 0x0048,      0x1000, 0x00d8,      0x0000, 0x2700,
@@ -888,15 +994,16 @@ static uint16_t e1000_eeprom_template[64] = {
     0xffff, 0xffff, 0xffff, 0xffff,      0xffff, 0xffff,      0xffff, 0x0000,
 };
 
-static uint16_t phy_reg_init[] = {
+static const uint16_t phy_reg_init[] = {
     [PHY_CTRL] = 0x1140,                       [PHY_STATUS] = 0x796d, // link initially up
     [PHY_ID1] = 0x141,                         [PHY_ID2] = PHY_ID2_INIT,
     [PHY_1000T_CTRL] = 0x0e00,                 [M88E1000_PHY_SPEC_CTRL] = 0x360,
     [M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60,     [PHY_AUTONEG_ADV] = 0xde1,
     [PHY_LP_ABILITY] = 0x1e0,                  [PHY_1000T_STATUS] = 0x3c00,
+    [M88E1000_PHY_SPEC_STATUS] = 0xac00,
 };
 
-static uint32_t mac_reg_init[] = {
+static const uint32_t mac_reg_init[] = {
     [PBA] =     0x00100030,
     [LEDCTL] =  0x602,
     [CTRL] =    E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
@@ -925,36 +1032,65 @@ e1000_mmio_map(PCIDevice *pci_dev, int region_num,
                 uint32_t addr, uint32_t size, int type)
 {
     E1000State *d = (E1000State *)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%08x 0x%08x\n", addr, size);
 
-    d->mmio_base = addr;
     cpu_register_physical_memory(addr, PNPMMIO_SIZE, d->mmio_index);
+    qemu_register_coalesced_mmio(addr, 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);
 }
 
-void
+static void
+e1000_cleanup(VLANClientState *vc)
+{
+    E1000State *d = vc->opaque;
+
+    unregister_savevm("e1000", d);
+}
+
+static int
+pci_e1000_uninit(PCIDevice *dev)
+{
+    E1000State *d = (E1000State *) dev;
+
+    cpu_unregister_io_memory(d->mmio_index);
+
+    return 0;
+}
+
+PCIDevice *
 pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
 {
     E1000State *d;
     uint8_t *pci_conf;
-    static int instance;
     uint16_t checksum = 0;
-    char *info_str = "e1000";
+    static const char info_str[] = "e1000";
     int i;
 
     d = (E1000State *)pci_register_device(bus, "e1000",
                 sizeof(E1000State), devfn, NULL, NULL);
 
+    if (!d)
+       return NULL;
+
     pci_conf = d->dev.config;
-    memset(pci_conf, 0, 256);
 
-    *(uint16_t *)(pci_conf+0x00) = cpu_to_le16(0x8086);
-    *(uint16_t *)(pci_conf+0x02) = cpu_to_le16(E1000_DEVID);
+    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;
-    pci_conf[0x0a] = 0x00; // ethernet network controller
-    pci_conf[0x0b] = 0x02;
+    pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
     pci_conf[0x0c] = 0x10;
 
     pci_conf[0x3d] = 1; // interrupt pin 0
@@ -968,9 +1104,6 @@ pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
     pci_register_io_region((PCIDevice *)d, 1, IOPORT_SIZE,
                            PCI_ADDRESS_SPACE_IO, ioport_map);
 
-    d->instance = instance++;
-
-    d->nd = nd;
     memmove(d->eeprom_data, e1000_eeprom_template,
         sizeof e1000_eeprom_template);
     for (i = 0; i < 3; i++)
@@ -987,13 +1120,15 @@ pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
     d->rxbuf_min_shift = 1;
     memset(&d->tx, 0, sizeof d->tx);
 
-    d->vc = qemu_new_vlan_client(nd->vlan, e1000_receive,
-                                 e1000_can_receive, d);
+    d->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
+                                 e1000_receive, e1000_can_receive,
+                                 e1000_cleanup, d);
+    d->vc->link_status_changed = e1000_set_link_status;
+
+    qemu_format_nic_info_str(d->vc, nd->macaddr);
 
-    snprintf(d->vc->info_str, sizeof(d->vc->info_str),
-             "%s macaddr=%02x:%02x:%02x:%02x:%02x:%02x", info_str,
-             d->nd->macaddr[0], d->nd->macaddr[1], d->nd->macaddr[2],
-             d->nd->macaddr[3], d->nd->macaddr[4], d->nd->macaddr[5]);
+    register_savevm(info_str, -1, 2, nic_save, nic_load, d);
+    d->dev.unregister = pci_e1000_uninit;
 
-    register_savevm(info_str, d->instance, 1, nic_save, nic_load, d);
+    return (PCIDevice *)d;
 }