All of lore.kernel.org
 help / color / mirror / Atom feed
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

  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.