From patchwork Tue Oct 12 13:58:16 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony PERARD X-Patchwork-Id: 67594 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 38415B70E0 for ; Wed, 13 Oct 2010 01:19:44 +1100 (EST) Received: from localhost ([127.0.0.1]:33328 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1P5fhc-0000Yc-Fe for incoming@patchwork.ozlabs.org; Tue, 12 Oct 2010 10:19:40 -0400 Received: from [140.186.70.92] (port=57280 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1P5fO9-0001B2-RA for qemu-devel@nongnu.org; Tue, 12 Oct 2010 09:59:36 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1P5fO7-0006Rc-FP for qemu-devel@nongnu.org; Tue, 12 Oct 2010 09:59:33 -0400 Received: from smtp02.citrix.com ([66.165.176.63]:10278) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1P5fO7-0006RS-6l for qemu-devel@nongnu.org; Tue, 12 Oct 2010 09:59:31 -0400 X-IronPort-AV: E=Sophos;i="4.57,320,1283745600"; d="scan'208";a="117712711" Received: from ftlpexchmx01.citrite.net ([10.9.154.126]) by FTLPIPO02.CITRIX.COM with ESMTP; 12 Oct 2010 09:59:16 -0400 Received: from smtp01.ad.xensource.com ([10.219.128.104]) by FTLPEXCHMX01.citrite.net with Microsoft SMTPSVC(6.0.3790.4675); Tue, 12 Oct 2010 09:59:16 -0400 Received: from perard.cam.xci-test.com (perard.cam.xci-test.com [10.80.248.106]) by smtp01.ad.xensource.com (8.13.1/8.13.1) with ESMTP id o9CDxENa004207; Tue, 12 Oct 2010 06:59:15 -0700 From: anthony.perard@citrix.com To: qemu-devel@nongnu.org Date: Tue, 12 Oct 2010 14:58:16 +0100 Message-Id: <1286891896-4105-1-git-send-email-anthony.perard@citrix.com> X-Mailer: git-send-email 1.7.1 X-OriginalArrivalTime: 12 Oct 2010 13:59:16.0256 (UTC) FILETIME=[A869E200:01CB6A15] X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. Cc: Anthony PERARD Subject: [Qemu-devel] [PATCH] e1000: Handle IO Port. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Anthony PERARD This patch introduces the two IOPorts on e1000, IOADDR and IODATA. The IOADDR is used to specify which register we want to access when we read or write on IODATA. It also check the RDLEN register when a packet is received, if the value is 0, the receive descriptor buffer is not set, so we don't accept any network packets. This patch fixes some weird behavior that I see when I use e1000 with QEMU/Xen, the guest memory can be corrupted by this NIC because it will write on memory that it doesn't own anymore after a reset. It's because the kernel Linux use the IOPort to reset the network card instead of the MMIO. Signed-off-by: Anthony PERARD --- hw/e1000.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 97 insertions(+), 9 deletions(-) diff --git a/hw/e1000.c b/hw/e1000.c index 532efdc..d02c8aa 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -57,6 +57,9 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL); #define PNPMMIO_SIZE 0x20000 #define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */ +#define REG_IOADDR 0x0 +#define REG_IODATA 0x4 + /* * HW models: * E1000_DEV_ID_82540EM works with Windows and Linux @@ -82,6 +85,8 @@ typedef struct E1000State_st { NICState *nic; NICConf conf; int mmio_index; + int ioport_base; + uint32_t ioport_reg[2]; uint32_t mac_reg[0x8000]; uint16_t phy_reg[0x20]; @@ -149,13 +154,7 @@ static const char phy_regcap[0x20] = { [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R }; -static void -ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, - pcibus_t size, int type) -{ - DBGOUT(IO, "e1000_ioport_map addr=0x%04"FMT_PCIBUS - " size=0x%08"FMT_PCIBUS"\n", addr, size); -} +static void e1000_reset(void *opaque); static void set_interrupt_cause(E1000State *s, int index, uint32_t val) @@ -201,6 +200,11 @@ rxbufsize(uint32_t v) static void set_ctrl(E1000State *s, int index, uint32_t val) { + DBGOUT(IO, "set ctrl = %08x\n", val); + if (val & E1000_CTRL_RST) { + s->mac_reg[CTRL] = val; + e1000_reset(s); + } /* RST is self clearing */ s->mac_reg[CTRL] = val & ~E1000_CTRL_RST; } @@ -623,7 +627,7 @@ e1000_can_receive(VLANClientState *nc) { E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - return (s->mac_reg[RCTL] & E1000_RCTL_EN); + return (s->mac_reg[RCTL] & E1000_RCTL_EN && s->mac_reg[RDLEN] != 0); } static ssize_t @@ -666,6 +670,11 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) size -= 4; } + if (s->mac_reg[RDLEN] == 0) { + DBGOUT(RX, "receive descriptor buffer not set\n"); + return -1; + } + rdh_start = s->mac_reg[RDH]; do { if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) { @@ -847,6 +856,60 @@ static void (*macreg_writeops[])(E1000State *, int, uint32_t) = { enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) }; static void +e1000_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + E1000State *s = opaque; + + if (addr == s->ioport_base + REG_IOADDR) { + DBGOUT(IO, "e1000_ioport_writel write base: 0x%04x\n", val); + s->ioport_reg[REG_IOADDR] = val & 0xfffff; + } else if (addr == (s->ioport_base + REG_IODATA)) { + unsigned int index = (s->ioport_reg[REG_IOADDR] & 0x1ffff) >> 2; + +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + DBGOUT(IO, "e1000_ioport_writel %x: 0x%04x\n", index, val); + + if (index < NWRITEOPS && macreg_writeops[index]) { + macreg_writeops[index](s, index, val); + } else if (index < NREADOPS && macreg_readops[index]) { + DBGOUT(IO, "e1000_ioport_writel RO %x: 0x%04x\n", index << 2, val); + } else { + DBGOUT(UNKNOWN, "IO unknown write index=0x%08x,val=0x%08x\n", + index, val); + } + } else { + DBGOUT(UNKNOWN, "IO unknown write addr=0x%08x,val=0x%08x\n", + addr, val); + } +} + +static uint32_t +e1000_ioport_readl(void *opaque, uint32_t addr) +{ + E1000State *s = opaque; + + if (addr == s->ioport_base + REG_IOADDR) { + return s->ioport_reg[REG_IOADDR] & 0xfffff; + } else if (addr == (s->ioport_base + REG_IODATA)) { + unsigned int index = (s->ioport_reg[REG_IOADDR] & 0x1ffff) >> 2; + + if (index < NREADOPS && macreg_readops[index]) { + uint32_t val = macreg_readops[index](s, index); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + return val; + } + DBGOUT(UNKNOWN, "IO unknown read addr=0x%08x\n", index<<2); + } else { + DBGOUT(UNKNOWN, "IO unknown read addr=0x%08x\n", addr); + } + return 0; +} + +static void e1000_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { E1000State *s = opaque; @@ -1031,6 +1094,30 @@ static const uint32_t mac_reg_init[] = { /* PCI interface */ +static void +e1000_ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, + pcibus_t size, int type) +{ + E1000State *d = DO_UPCAST(E1000State, dev, pci_dev); + + DBGOUT(IO, "e1000_ioport_map addr=0x%04" FMT_PCIBUS + " size=0x%08" FMT_PCIBUS "\n", addr, size); + + d->ioport_base = addr; + + /* Writes that are less than 32 bits are ignored on IOADDR. + * For the Flash access, a write can be less than 32 bits for + * IODATA register, but is not handled. + */ + + register_ioport_read(addr, size, 1, e1000_ioport_readl, d); + + register_ioport_read(addr, size, 2, e1000_ioport_readl, d); + + register_ioport_write(addr, size, 4, e1000_ioport_writel, d); + register_ioport_read(addr, size, 4, e1000_ioport_readl, d); +} + static CPUWriteMemoryFunc * const e1000_mmio_write[] = { e1000_mmio_writeb, e1000_mmio_writew, e1000_mmio_writel }; @@ -1085,6 +1172,7 @@ static void e1000_reset(void *opaque) { E1000State *d = opaque; + memset(d->ioport_reg, 0, sizeof d->ioport_reg); memset(d->phy_reg, 0, sizeof d->phy_reg); memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init); memset(d->mac_reg, 0, sizeof d->mac_reg); @@ -1131,7 +1219,7 @@ static int pci_e1000_init(PCIDevice *pci_dev) PCI_BASE_ADDRESS_SPACE_MEMORY, e1000_mmio_map); pci_register_bar((PCIDevice *)d, 1, IOPORT_SIZE, - PCI_BASE_ADDRESS_SPACE_IO, ioport_map); + PCI_BASE_ADDRESS_SPACE_IO, e1000_ioport_map); memmove(d->eeprom_data, e1000_eeprom_template, sizeof e1000_eeprom_template);