From: Dan Aloni <da-x@monatomic.org>
To: qemu-devel@nongnu.org
Cc: Triggered <frederik@ietsmet.nl>
Subject: Re: [Qemu-devel] Re: e1000 emulation
Date: Sat, 1 Sep 2007 22:51:09 +0300 [thread overview]
Message-ID: <20070901195108.GA9438@localdomain> (raw)
In-Reply-To: <46D17155.7090306@ietsmet.nl>
On Sun, Aug 26, 2007 at 02:25:57PM +0200, Triggered wrote:
> Dan Aloni schreef:
> > Hello,
> >
> > Is anyone interested in a patch for an unstable, though working e1000
> > emulation for
> > QEMU? It is aimed for this chip-set version:
> >
> > 0e:00.0 Ethernet controller: Intel Corporation 82573L Gigabit Ethernet
> > Controller
> >
> > I originally created it very recently to provide a virtual NIC that
> > supports Jumbo
> > packets. Unfortunately it appears that the tun driver doesn't implement
> > change_mtu() so it didn't integrate very well with qemu-kvm.
> >
> I am very interested in a patch for Gbit-capable network card.
> Please post a patch!
Okay, just a few notes:
* Sorry for sending this rather badly written patch, I have loads of
other stuff at the moment. I hope that it serves as a ground for
further development, though.
* It depends on the e1000_hw.h header from a the Linux kernel driver (look
in drivers/net/e1000), I haven't included this header in the patch.
* As far as I've seen, ICMP ping works, but TCP doesn't (I think it
is because this emulator implementation doesn't do the off-load of
packet checksuming, as the guest expects)
* Several up/downs of the interface while it is working seem to cause
guest memory corruption. Haven't looked into this yet.
Have fun hacking!
diff --git a/qemu/Makefile.target b/qemu/Makefile.target
index aeee2af..9363e22 100644
--- a/qemu/Makefile.target
+++ b/qemu/Makefile.target
@@ -372,7 +372,7 @@ VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o
VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o
# PCI network cards
-VL_OBJS+= ne2000.o rtl8139.o pcnet.o
+VL_OBJS+= ne2000.o rtl8139.o pcnet.o e1000.o
# PCI Hypercall
VL_OBJS+= hypercall.o
diff --git a/qemu/hw/e1000.c b/qemu/hw/e1000.c
new file mode 100644
index 0000000..7e3c014
--- /dev/null
+++ b/qemu/hw/e1000.c
@@ -0,0 +1,695 @@
+/*
+ * QEMU e1000 emulation
+ *
+ * Copyright (c) 2007 Dan Aloni
+ * Based on work done by:
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "vl.h"
+
+#define E1000_DEBUG
+#define E1000_DEBUG_UNK
+//#define E1000_DEBUG_CSR
+//#define E1000_DEBUG_RMD
+//#define E1000_DEBUG_TMD
+//#define E1000_DEBUG_MATCH
+
+#ifdef E1000_DEBUG
+#define EDBGOUT(fmt, params...) fprintf(stderr, "e1000: " fmt, ##params)
+#else
+#define EDBGOUT(fmt, params...) do {} while (0)
+#endif
+
+#ifdef E1000_DEBUG_UNK
+#define EDBGUNKOUT(fmt, params...) EDBGOUT(fmt, ##params)
+#else
+#define EDBGUNKOUT(fmt, params...) do {} while (0)
+#endif
+
+
+#define E1000_IOPORT_SIZE 0x40
+#define E1000_PNPMMIO_SIZE 0x60000
+
+#include "e1000_hw.h"
+
+typedef struct e1000_vmhw_st e1000_vmhw;
+
+struct e1000_eeprom {
+ unsigned short raw[64];
+};
+
+struct e1000_vmhw_st {
+ PCIDevice dev;
+ PCIDevice *pci_dev;
+ VLANClientState *vc;
+ NICInfo *nd;
+ uint32_t mmio_base;
+ uint32_t interrupt_mask;
+ uint32_t eeprom_semaphore;
+ uint32_t mdic;
+
+ uint32_t icr;
+ uint32_t tdlen;
+ uint64_t tdb;
+ uint32_t tdt;
+ uint32_t tdh;
+
+ uint32_t rdlen;
+ uint64_t rdb;
+ uint32_t rdt;
+ uint32_t rdh;
+ uint32_t rctl;
+ uint32_t receive_buf_size;
+
+ uint16_t phy_regs[0x20];
+
+ struct e1000_eeprom eeprom_data;
+ int eeprom_offset_to_read;
+ int mmio_index, rap, isr, lnkst;
+ void *dma_opaque;
+};
+
+static void e1000_ioport_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ EDBGOUT("e1000_ioport_map addr=0x%04x size=0x%08x\n", addr, size);
+}
+
+static void e1000_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ EDBGOUT("e1000_mmio_writeb addr=%p val=0x%08x\n", (void *)addr, val);
+}
+
+static uint32_t e1000_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ EDBGOUT("e1000_mmio_readb addr=%p\n", (void *)addr);
+ return 0;
+}
+
+static void e1000_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ EDBGOUT("e1000_mmio_writew addr=0x%08x val=0x%04x\n", addr, val);
+}
+
+static uint32_t e1000_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ EDBGOUT("e1000_mmio_readw addr=0x%08x\n", addr);
+ return 0;
+}
+
+void e1000_update_interrupt(struct e1000_vmhw_st *s)
+{
+ if ((~s->interrupt_mask) & s->icr) {
+ EDBGOUT("update interrupt 0x%08x\n", (~s->interrupt_mask) & s->icr);
+ }
+ pci_set_irq(&s->dev, 0, ((~s->interrupt_mask) & s->icr) != 0);
+}
+
+void e1000_clear_interrupt_mask(struct e1000_vmhw_st *s, uint32_t val)
+{
+ s->interrupt_mask = val;
+ e1000_update_interrupt(s);
+}
+
+void e1000_set_interrupt_mask(struct e1000_vmhw_st *s, uint32_t val)
+{
+ s->interrupt_mask = ~val;
+ e1000_update_interrupt(s);
+}
+
+void e1000_set_interrupt_cause(struct e1000_vmhw_st *s, uint32_t val)
+{
+ if (val != 0) {
+ if (val & E1000_ICR_LSC) {
+ EDBGOUT("mocking up a link\n");
+ /* Trick the driver to think there's a link */
+ s->phy_regs[PHY_STATUS] = 0x796d;
+ }
+ val |= E1000_ICR_INT_ASSERTED;
+ }
+ s->icr = val;
+ e1000_update_interrupt(s);
+}
+
+uint32_t e1000_get_receive_control(struct e1000_vmhw_st *s)
+{
+ return s->rctl;
+}
+
+void e1000_set_receive_control(struct e1000_vmhw_st *s, uint32_t val)
+{
+ uint32_t reg_buf_size;
+
+ s->rctl = val;
+ s->receive_buf_size = 2048;
+
+ reg_buf_size = val & 0x00030000;
+
+ if (val & E1000_RCTL_BSEX) {
+ switch (reg_buf_size) {
+ case E1000_RCTL_SZ_16384:
+ s->receive_buf_size = 16384;
+ break;
+ case E1000_RCTL_SZ_8192:
+ s->receive_buf_size = 8192;
+ break;
+ case E1000_RCTL_SZ_4096:
+ s->receive_buf_size = 4096;
+ break;
+ }
+ } else {
+ switch (reg_buf_size) {
+ case E1000_RCTL_SZ_2048:
+ s->receive_buf_size = 2048;
+ break;
+ case E1000_RCTL_SZ_1024:
+ s->receive_buf_size = 1024;
+ break;
+ case E1000_RCTL_SZ_512:
+ s->receive_buf_size = 512;
+ break;
+ case E1000_RCTL_SZ_256:
+ s->receive_buf_size = 256;
+ break;
+ }
+ }
+
+ EDBGOUT("RDT: %d, rctl = 0x%x\n", s->rdt, s->rctl);
+}
+
+uint32_t e1000_get_interrupt_cause(struct e1000_vmhw_st *s)
+{
+ uint32_t icr = s->icr;
+ e1000_set_interrupt_cause(s, 0);
+ return icr;
+}
+
+uint32_t e1000_get_control(struct e1000_vmhw_st *s)
+{
+ uint32_t reg;
+
+ reg = (E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | E1000_CTRL_SPD_1000 |
+ E1000_CTRL_SLU | E1000_CTRL_LRST);
+
+ /* 00140248 */
+
+ EDBGOUT("e1000_mmio_readl CTRL val=0x%08x\n", reg);
+
+ return reg;
+}
+
+void e1000_set_control(struct e1000_vmhw_st *s, uint32_t val)
+{
+ EDBGOUT("e1000_mmio_write CTRL val=0x%08x\n", val);
+}
+
+void e1000_set_eeprom_semaphore(struct e1000_vmhw_st *s, uint32_t val)
+{
+ s->eeprom_semaphore = val;
+}
+
+uint32_t e1000_get_eeprom_semaphore(struct e1000_vmhw_st *s)
+{
+ return s->eeprom_semaphore;
+}
+
+void e1000_set_mdic(struct e1000_vmhw_st *s, uint32_t val)
+{
+ uint32_t phy_addr;
+ uint32_t reg_addr;
+ uint32_t reg_value;
+
+ reg_value = val & E1000_MDIC_DATA_MASK;
+ reg_addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
+ phy_addr = ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT);
+
+ if (phy_addr != 1)
+ goto exit;
+
+ s->mdic = val;
+
+ if (val & E1000_MDIC_OP_READ) {
+ EDBGOUT("MDIC read reg 0x%x\n", reg_addr);
+
+ switch (reg_addr) {
+ case PHY_STATUS:
+ case PHY_ID1:
+ case PHY_ID2:
+ case PHY_CTRL:
+ case M88E1000_PHY_SPEC_CTRL:
+ case M88E1000_EXT_PHY_SPEC_CTRL:
+ case PHY_AUTONEG_ADV:
+ case PHY_1000T_CTRL:
+ s->mdic |= s->phy_regs[reg_addr] | E1000_MDIC_READY;
+ return;
+ default:
+ EDBGOUT("MDIC read reg unhandled\n");
+ break;
+ }
+ } else if (val & E1000_MDIC_OP_WRITE) {
+ EDBGOUT("MDIC write reg 0x%x, value 0x%x\n", reg_addr, reg_value);
+ switch (reg_addr) {
+ case M88E1000_PHY_SPEC_CTRL:
+ case M88E1000_EXT_PHY_SPEC_CTRL:
+ case PHY_CTRL:
+ case PHY_AUTONEG_ADV:
+ case PHY_1000T_CTRL:
+ s->phy_regs[reg_addr] = reg_value;
+ s->mdic |= E1000_MDIC_READY;
+ return;
+ default:
+ EDBGOUT("MDIC write reg unhandled\n");
+ break;
+ }
+ }
+
+ exit:
+ s->mdic |= E1000_MDIC_ERROR;
+}
+
+uint32_t e1000_get_mdic(struct e1000_vmhw_st *s)
+{
+ return s->mdic;
+}
+
+uint32_t e1000_get_status(struct e1000_vmhw_st *s)
+{
+ uint32_t reg;
+ reg = 0x80000000;
+ reg |= E1000_STATUS_GIO_MASTER_ENABLE |
+ E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK |
+ E1000_STATUS_SPEED_1000;
+
+ reg |= E1000_STATUS_FD | E1000_STATUS_LU;
+ return reg;
+}
+
+uint32_t e1000_get_flash_control(struct e1000_vmhw_st *s)
+{
+ uint32_t reg = 0;
+
+ reg = 0x08 << E1000_EECD_SECVAL_SHIFT;
+ reg |= 0x10000;
+ reg |= 3 << E1000_EECD_SIZE_EX_SHIFT;
+ reg |= E1000_EECD_PRES;
+ reg |= E1000_EECD_SIZE;
+ reg |= E1000_EECD_FWE_DIS;
+ reg |= E1000_EECD_DO;
+
+ //EDBGOUT("ECD: %08x\n", reg);
+
+ return reg;
+}
+
+uint32_t e1000_get_management_control(struct e1000_vmhw_st *s)
+{
+ return (E1000_MANC_EN_MNG2HOST |
+ E1000_MANC_RCV_TCO_EN |
+ E1000_MANC_ARP_EN |
+ E1000_MANC_0298_EN |
+ E1000_MANC_RMCP_EN);
+}
+
+void e1000_flash_eerd_write(struct e1000_vmhw_st *s, uint32_t val)
+{
+ s->eeprom_offset_to_read = val;
+}
+
+uint32_t e1000_flash_eerd_read(struct e1000_vmhw_st *s)
+{
+ unsigned int index;
+
+ index = (s->eeprom_offset_to_read - E1000_EEPROM_RW_REG_START) >> E1000_EEPROM_RW_ADDR_SHIFT;
+
+ if (index >= 0 && index <= EEPROM_CHECKSUM_REG) {
+ uint32_t reg;
+ reg = s->eeprom_data.raw[index];
+ reg <<= E1000_EEPROM_RW_REG_DATA;
+ reg |= E1000_EEPROM_RW_REG_DONE | (s->eeprom_offset_to_read - E1000_EEPROM_RW_REG_START);
+ return reg;
+ }
+
+ return 0;
+}
+
+void e1000_set_transmit_descriptor(struct e1000_vmhw_st *s, uint32_t val)
+{
+ uint32_t index;
+ target_phys_addr_t base;
+ struct e1000_tx_desc desc;
+ char packet_data[0x8000];
+ unsigned int packet_size;
+ unsigned int split_size;
+
+ s->tdt = val;
+ packet_size = 0;
+
+ while (s->tdh != s->tdt) {
+ index = s->tdh;
+
+ base = s->tdb + sizeof(struct e1000_tx_desc) * index;
+ memset(&desc, 0, sizeof(desc));
+ cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
+
+ EDBGOUT("index %d: %p : %x %x\n", index,
+ (void *)desc.buffer_addr, desc.lower.data, desc.upper.data);
+
+ split_size = desc.lower.flags.length;
+
+ if (split_size) {
+ cpu_physical_memory_read(desc.buffer_addr, (void *)&packet_data[packet_size],
+ split_size);
+ packet_size += split_size;
+ }
+
+ desc.upper.data |= E1000_TXD_STAT_DD;
+ cpu_physical_memory_write(base, &desc, sizeof(desc));
+
+ if (desc.lower.data & E1000_TXD_CMD_EOP) {
+ EDBGOUT("packet whose size is %d\n", packet_size);
+ qemu_send_packet(s->vc, packet_data, packet_size);
+ packet_size = 0;
+ }
+
+ s->tdh++;
+
+ if (base == s->tdb + s->tdlen - sizeof(desc))
+ s->tdh = 0;
+ }
+}
+
+static void e1000_receive(void *opaque, const uint8_t *buf, int size)
+{
+ struct e1000_vmhw_st *s = opaque;
+ struct e1000_rx_desc desc;
+ target_phys_addr_t base;
+ unsigned int index;
+
+
+ EDBGOUT("e1000_receive: %d\n", size);
+
+ if (!(s->rctl & E1000_RCTL_EN))
+ return;
+
+ if (s->rdh == s->rdt)
+ return;
+
+ if (size > s->receive_buf_size) {
+ EDBGOUT("packet too large for buffers (%d > %d)\n",
+ size, s->receive_buf_size);
+ return;
+ }
+
+ size += 4; /* for the header */
+ index = s->rdh;
+ base = s->rdb + sizeof(desc) * index;
+ cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
+ cpu_physical_memory_write(desc.buffer_addr, (void *)buf, size);
+ desc.length = size;
+ desc.status |= E1000_RXD_STAT_DD;
+ desc.status |= E1000_RXD_STAT_EOP;
+ cpu_physical_memory_write(base, (void *)&desc, sizeof(desc));
+
+ s->rdh++;
+ if (base == s->rdb + s->rdlen - sizeof(desc))
+ s->rdh = 0;
+
+ e1000_set_interrupt_cause(s, E1000_ICS_RXDMT0);
+}
+
+void e1000_set_receive_descriptor(struct e1000_vmhw_st *s, uint32_t val)
+{
+ s->rdt = val;
+}
+
+static int e1000_can_receive(void *opaque)
+{
+ struct e1000_vmhw_st *s = opaque;
+
+ if (!(s->rctl & E1000_RCTL_EN))
+ return 1;
+
+ if (s->rdh == s->rdt)
+ return 1;
+
+ return 0;
+}
+
+static void e1000_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ struct e1000_vmhw_st *s = opaque;
+
+ switch (addr - s->mmio_base) {
+ case E1000_TDLEN:
+ s->tdlen = val;
+ break;
+ case E1000_TDBAH:
+ s->tdb &= 0x00000000ffffffff;
+ s->tdb |= ((uint64_t)val) << 32;
+ break;
+ case E1000_TDBAL:
+ s->tdb &= 0xffffffff00000000;
+ s->tdb |= val;
+ break;
+ case E1000_TDH:
+ s->tdh = val;
+ break;
+ case E1000_TDT:
+ e1000_set_transmit_descriptor(s, val);
+ break;
+
+ case E1000_RDLEN:
+ s->rdlen = val;
+ break;
+ case E1000_RDBAH:
+ s->rdb &= 0x00000000ffffffff;
+ s->rdb |= ((uint64_t)val) << 32;
+ break;
+ case E1000_RDBAL:
+ s->rdb &= 0xffffffff00000000;
+ s->rdb |= val;
+ break;
+ case E1000_RDH:
+ s->rdh = val;
+ break;
+ case E1000_RDT:
+ e1000_set_receive_descriptor(s, val);
+ break;
+
+ case E1000_STATUS:
+ EDBGOUT("e1000_mmio_writel STATUS 0x%04x\n", val);
+ break;
+ case E1000_CTRL:
+ e1000_set_control(s, val);
+ break;
+ case E1000_SWSM:
+ e1000_set_eeprom_semaphore(s, val);
+ break;
+ case E1000_IMC:
+ e1000_clear_interrupt_mask(s, val);
+ break;
+ case E1000_IMS:
+ e1000_set_interrupt_mask(s, val);
+ break;
+ case E1000_MDIC:
+ e1000_set_mdic(s, val);
+ break;
+ case E1000_ICR:
+ EDBGOUT("e1000_mmio_writel ICR val=0x%08x\n", val);
+ break;
+ case E1000_ICS:
+ e1000_set_interrupt_cause(s, val);
+ break;
+ case E1000_RCTL:
+ e1000_set_receive_control(s, val);
+ break;
+ case E1000_TCTL:
+ EDBGOUT("e1000_mmio_writel TCTL val=0x%08x\n", val);
+ break;
+ case E1000_EERD:
+ e1000_flash_eerd_write(s, val);
+ break;
+ default:
+ EDBGUNKOUT("MMIO unknown write addr=0x%08x,val=0x%08x\n", addr - s->mmio_base, val);
+ break;
+ }
+
+}
+
+static uint32_t e1000_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ struct e1000_vmhw_st *s = opaque;
+
+ switch (addr - s->mmio_base) {
+ case E1000_CRCERRS ... E1000_ICRXOC:
+ /* statistics - return zero for now */
+ return 0;
+ case E1000_TDH:
+ return s->tdh;
+ case E1000_CTRL:
+ return e1000_get_control(s);
+ case E1000_STATUS:
+ return e1000_get_status(s);
+ case E1000_SWSM:
+ return e1000_get_eeprom_semaphore(s);
+ case E1000_ICR:
+ return e1000_get_interrupt_cause(s);
+ case E1000_MDIC:
+ return e1000_get_mdic(s);
+ case E1000_EECD:
+ return e1000_get_flash_control(s);
+ case E1000_EERD:
+ return e1000_flash_eerd_read(s);
+ case E1000_MANC:
+ return e1000_get_management_control(s);
+ case E1000_RCTL:
+ return e1000_get_receive_control(s);
+ default:
+ EDBGUNKOUT("MMIO unknown read addr=0x%08x\n", addr - s->mmio_base);
+ break;
+ }
+
+ return 0;
+}
+
+struct e1000_eeprom e1000_eeprom_template = { .raw = {
+ 0x0000, 0x0000, 0x0000, 0x0d30, 0xf746, 0x00f4, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0x026b, 0x108c, 0x15d9, 0x108c, 0x8086, 0x83df,
+ 0x0008, 0x2000, 0x7e14, 0x0048, 0x1000, 0x00d8, 0x0000, 0x2700,
+ 0x6cc9, 0x3150, 0x0722, 0x040b, 0x0984, 0x0000, 0xc000, 0x0706,
+ 0x1008, 0x0000, 0x0f04, 0x7fff, 0x4d01, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0x0100, 0x4000, 0x121c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000,
+}};
+
+static void e1000_common_init(e1000_vmhw *d, NICInfo *nd, const char *info_str)
+{
+ uint16_t checksum = 0;
+ int i;
+
+ d->nd = nd;
+ d->eeprom_data = e1000_eeprom_template;
+ d->eeprom_data.raw[0] = (nd->macaddr[1] << 8) | nd->macaddr[0];
+ d->eeprom_data.raw[1] = (nd->macaddr[3] << 8) | nd->macaddr[2];
+ d->eeprom_data.raw[2] = (nd->macaddr[5] << 8) | nd->macaddr[4];
+ for (i=0; i < EEPROM_CHECKSUM_REG; i++)
+ checksum += d->eeprom_data.raw[i];
+ checksum = (uint16_t) EEPROM_SUM - checksum;
+ d->eeprom_data.raw[EEPROM_CHECKSUM_REG] = checksum;
+
+ d->phy_regs[PHY_CTRL] = 0x1140;
+ d->phy_regs[PHY_STATUS] = 0x7949;
+ d->phy_regs[PHY_ID1] = 0x141;
+ d->phy_regs[PHY_ID2] = 0xcc2;
+ d->phy_regs[PHY_1000T_CTRL] = 0x0e00;
+ d->phy_regs[M88E1000_PHY_SPEC_CTRL] = 0x360;
+ d->phy_regs[M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60;
+ d->phy_regs[PHY_AUTONEG_ADV] = 0xde1;
+ d->rctl = 0;
+
+ d->interrupt_mask = ~0;
+
+ EDBGOUT("eeprom checksum: 0x%04x\n", checksum);
+
+ d->vc = qemu_new_vlan_client(nd->vlan, e1000_receive,
+ e1000_can_receive, d);
+
+ snprintf(d->vc->info_str, sizeof(d->vc->info_str),
+ "e1000 macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ d->nd->macaddr[0],
+ d->nd->macaddr[1],
+ d->nd->macaddr[2],
+ d->nd->macaddr[3],
+ d->nd->macaddr[4],
+ d->nd->macaddr[5]);
+}
+
+/* PCI interface */
+
+static CPUWriteMemoryFunc *e1000_mmio_write[] = {
+ (CPUWriteMemoryFunc *)&e1000_mmio_writeb,
+ (CPUWriteMemoryFunc *)&e1000_mmio_writew,
+ (CPUWriteMemoryFunc *)&e1000_mmio_writel
+};
+
+static CPUReadMemoryFunc *e1000_mmio_read[] = {
+ (CPUReadMemoryFunc *)&e1000_mmio_readb,
+ (CPUReadMemoryFunc *)&e1000_mmio_readw,
+ (CPUReadMemoryFunc *)&e1000_mmio_readl
+};
+
+static void e1000_mmio_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ e1000_vmhw *d = (e1000_vmhw *)pci_dev;
+
+ EDBGOUT("e1000_mmio_map addr=0x%08x 0x%08x\n", addr, size);
+
+ d->mmio_base = addr;
+ cpu_register_physical_memory(addr, E1000_PNPMMIO_SIZE, d->mmio_index);
+}
+
+void pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
+{
+ e1000_vmhw *d;
+ uint8_t *pci_conf;
+
+ d = (e1000_vmhw *)pci_register_device(bus, "e1000", sizeof(e1000_vmhw),
+ devfn, NULL, NULL);
+
+ pci_conf = d->dev.config;
+
+ *(uint16_t *)&pci_conf[0x00] = cpu_to_le16(0x8086);
+ *(uint16_t *)&pci_conf[0x02] = cpu_to_le16(0x109a);
+ *(uint16_t *)&pci_conf[0x04] = cpu_to_le16(0x0407);
+ *(uint16_t *)&pci_conf[0x06] = cpu_to_le16(0x0010);
+ pci_conf[0x08] = 0x03;
+ pci_conf[0x09] = 0x00;
+ pci_conf[0x0a] = 0x00; // ethernet network controller
+ pci_conf[0x0b] = 0x02;
+
+ pci_conf[0x0c] = 0x10;
+ pci_conf[0x0d] = 0x00;
+ pci_conf[0x0e] = 0x00;
+ pci_conf[0x0f] = 0x00;
+
+ *(uint32_t *)&pci_conf[0x10] = cpu_to_le32(0xe0300000);
+ *(uint32_t *)&pci_conf[0x14] = cpu_to_le32(0x00000000);
+
+ pci_conf[0x3d] = 1; // interrupt pin 0
+ pci_conf[0x3e] = 0x00;
+ pci_conf[0x3f] = 0x00;
+
+ /* Handler for memory-mapped I/O */
+ d->mmio_index =
+ cpu_register_io_memory(0, e1000_mmio_read, e1000_mmio_write, d);
+
+ pci_register_io_region((PCIDevice *)d, 0, E1000_PNPMMIO_SIZE,
+ PCI_ADDRESS_SPACE_MEM, e1000_mmio_map);
+
+ pci_register_io_region((PCIDevice *)d, 1, E1000_IOPORT_SIZE,
+ PCI_ADDRESS_SPACE_IO, e1000_ioport_map);
+
+ e1000_common_init(d, nd, "e1000");
+}
diff --git a/qemu/hw/pci.c b/qemu/hw/pci.c
index b895f98..6d3be5c 100644
--- a/qemu/hw/pci.c
+++ b/qemu/hw/pci.c
@@ -552,6 +552,8 @@ void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
pci_rtl8139_init(bus, nd, devfn);
} else if (strcmp(nd->model, "pcnet") == 0) {
pci_pcnet_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "e1000") == 0) {
+ pci_e1000_init(bus, nd, devfn);
} else {
fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
exit (1);
--
Dan Aloni
XIV LTD, http://www.xivstorage.com
da-x (at) monatomic.org, dan (at) xiv.co.il
next prev parent reply other threads:[~2007-09-01 19:51 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-08-25 21:18 [Qemu-devel] e1000 emulation Dan Aloni
2007-08-26 12:25 ` [Qemu-devel] " Triggered
2007-09-01 19:51 ` Dan Aloni [this message]
2007-09-02 6:55 ` Mulyadi Santosa
2007-08-26 15:57 ` [Qemu-devel] " Anthony Liguori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070901195108.GA9438@localdomain \
--to=da-x@monatomic.org \
--cc=frederik@ietsmet.nl \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.