qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]