[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] Re: e1000 emulation
From: |
Dan Aloni |
Subject: |
Re: [Qemu-devel] Re: e1000 emulation |
Date: |
Sat, 1 Sep 2007 22:51:09 +0300 |
User-agent: |
Mutt/1.5.16 (2007-06-11) |
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
- Re: [Qemu-devel] Re: e1000 emulation,
Dan Aloni <=