All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/2] MIPS Boston / pch_gbe ethernet support
@ 2018-02-17 19:22 Paul Burton
  2018-02-17 19:22 ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Paul Burton
  2018-02-17 19:22 ` [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller Paul Burton
  0 siblings, 2 replies; 8+ messages in thread
From: Paul Burton @ 2018-02-17 19:22 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Yongbok Kim, Paul Burton

This short series introduces support for emulating the ethernet
controller found in the Intel EG20T Platform Controller Hub, and then
enables that device for the MIPS Boston board. This gives the Boston
board a network device matching that found on real Boston boards,
providing unmodified Boston Linux kernels with network access.

Applies atop master as of 5e8d6a12d643 ("Merge remote-tracking branch
'remotes/kraxel/tags/ui-20180216-pull-request' into staging").


Paul Burton (2):
  hw/net: Add support for Intel pch_gbe ethernet
  hw/mips/boston: Enable pch_gbe ethernet controller

 default-configs/mips64el-softmmu.mak |   1 +
 hw/mips/boston.c                     |   8 +-
 hw/net/Makefile.objs                 |   1 +
 hw/net/pch_gbe.c                     | 766 +++++++++++++++++++++++++++++++++++
 4 files changed, 775 insertions(+), 1 deletion(-)
 create mode 100644 hw/net/pch_gbe.c

-- 
2.16.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet
  2018-02-17 19:22 [Qemu-devel] [PATCH 0/2] MIPS Boston / pch_gbe ethernet support Paul Burton
@ 2018-02-17 19:22 ` Paul Burton
  2018-07-02 23:36   ` Philippe Mathieu-Daudé
  2018-07-04  7:24   ` Jason Wang
  2018-02-17 19:22 ` [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller Paul Burton
  1 sibling, 2 replies; 8+ messages in thread
From: Paul Burton @ 2018-02-17 19:22 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Yongbok Kim, Paul Burton

This patch introduces support for emulating the ethernet controller
found in the Intel EG20T Platform Controller Hub, referred to as pch_gbe
for consistency with both Linux & U-Boot.

Documentation for the hardware can be found here:

  https://www.intel.com/content/www/us/en/intelligent-systems/queens-bay/platform-controller-hub-eg20t-datasheet.html

The device is used on MIPS Boston development boards as well as the
Intel Crown Bay platform including devices such as the Minnowboard V1.

Enough functionality is implemented for Linux to make use of the device,
and has been tested using Linux v4.16-rc1.

Signed-off-by: Paul Burton <paul.burton@mips.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: Yongbok Kim <yongbok.kim@mips.com>
---

 hw/net/Makefile.objs |   1 +
 hw/net/pch_gbe.c     | 766 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 767 insertions(+)
 create mode 100644 hw/net/pch_gbe.c

diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index ab22968641..08706d9a96 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -12,6 +12,7 @@ common-obj-$(CONFIG_E1000E_PCI) += e1000e.o e1000e_core.o e1000x_common.o
 common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
 common-obj-$(CONFIG_VMXNET3_PCI) += net_tx_pkt.o net_rx_pkt.o
 common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o
+common-obj-$(CONFIG_PCH_GBE_PCI) += pch_gbe.o
 
 common-obj-$(CONFIG_SMC91C111) += smc91c111.o
 common-obj-$(CONFIG_LAN9118) += lan9118.o
diff --git a/hw/net/pch_gbe.c b/hw/net/pch_gbe.c
new file mode 100644
index 0000000000..be9a9f5916
--- /dev/null
+++ b/hw/net/pch_gbe.c
@@ -0,0 +1,766 @@
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/net/mii.h"
+#include "hw/pci/pci.h"
+#include "net/checksum.h"
+#include "net/eth.h"
+#include "net/net.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+
+#define TYPE_PCH_GBE    "pch_gbe"
+#define PCH_GBE(obj)    OBJECT_CHECK(PCHGBEState, (obj), TYPE_PCH_GBE)
+
+#define PCH_GBE_INTR_RX_DMA_CMPLT       BIT(0)
+#define PCH_GBE_INTR_RX_VALID           BIT(1)
+#define PCH_GBE_INTR_RX_FRAME_ERR       BIT(2)
+#define PCH_GBE_INTR_RX_FIFO_ERR        BIT(3)
+#define PCH_GBE_INTR_RX_DMA_ERR         BIT(4)
+#define PCH_GBE_INTR_RX_DSC_EMP         BIT(5)
+#define PCH_GBE_INTR_TX_CMPLT           BIT(8)
+#define PCH_GBE_INTR_TX_DMA_CMPLT       BIT(9)
+#define PCH_GBE_INTR_TX_FIFO_ERR        BIT(10)
+#define PCH_GBE_INTR_TX_DMA_ERR         BIT(11)
+#define PCH_GBE_INTR_PAUSE_CMPLT        BIT(12)
+#define PCH_GBE_INTR_MIIM_CMPLT         BIT(16)
+#define PCH_GBE_INTR_PHY_INT            BIT(20)
+#define PCH_GBE_INTR_WOL_DET            BIT(24)
+#define PCH_GBE_INTR_TCPIP_ERR          BIT(28)
+#define PCH_GBE_INTR_ALL (              \
+        PCH_GBE_INTR_RX_DMA_CMPLT |     \
+        PCH_GBE_INTR_RX_VALID |         \
+        PCH_GBE_INTR_RX_FRAME_ERR |     \
+        PCH_GBE_INTR_RX_FIFO_ERR |      \
+        PCH_GBE_INTR_RX_DMA_ERR |       \
+        PCH_GBE_INTR_RX_DSC_EMP |       \
+        PCH_GBE_INTR_TX_CMPLT |         \
+        PCH_GBE_INTR_TX_DMA_CMPLT |     \
+        PCH_GBE_INTR_TX_FIFO_ERR |      \
+        PCH_GBE_INTR_TX_DMA_ERR |       \
+        PCH_GBE_INTR_PAUSE_CMPLT |      \
+        PCH_GBE_INTR_MIIM_CMPLT |       \
+        PCH_GBE_INTR_PHY_INT |          \
+        PCH_GBE_INTR_WOL_DET |          \
+        PCH_GBE_INTR_TCPIP_ERR)
+
+struct pch_gbe_tx_desc {
+    uint32_t addr;
+
+    uint32_t len;
+#define PCH_GBE_TX_LENGTH               0xffff
+
+    uint32_t control;
+#define PCH_GBE_TX_CONTROL_EOB          0x3
+#define PCH_GBE_TX_CONTROL_WORDS        0xfffc
+#define PCH_GBE_TX_CONTROL_APAD         BIT(16)
+#define PCH_GBE_TX_CONTROL_ICRC         BIT(17)
+#define PCH_GBE_TX_CONTROL_ITAG         BIT(18)
+#define PCH_GBE_TX_CONTROL_ACCOFF       BIT(19)
+
+    uint32_t status;
+#define PCH_GBE_TX_STATUS_TSHRT         BIT(22)
+#define PCH_GBE_TX_STATUS_TLNG          BIT(23)
+#define PCH_GBE_TX_STATUS_ABT           BIT(28)
+#define PCH_GBE_TX_STATUS_CMPLT         BIT(29)
+};
+
+struct pch_gbe_rx_desc {
+    uint32_t addr;
+
+    uint32_t acc_status;
+
+    uint32_t mac_status;
+#define PCH_GBE_RX_MAC_STATUS_EOB       0x3
+#define PCH_GBE_RX_MAC_STATUS_WORDS     0xfffc
+#define PCH_GBE_RX_MAC_STATUS_LENGTH    0xffff
+#define PCH_GBE_RX_MAC_STATUS_TSHRT     BIT(19)
+#define PCH_GBE_RX_MAC_STATUS_TLNG      BIT(20)
+
+    uint32_t dma_status;
+};
+
+typedef struct {
+    /*< private >*/
+    PCIDevice parent_obj;
+    /*< public >*/
+
+    NICState *nic;
+    NICConf conf;
+
+    bool reset;
+    bool phy_reset;
+
+    bool link;
+
+    uint32_t intr_status;
+    uint32_t intr_status_hold;
+    uint32_t intr_enable;
+
+    uint16_t addr_mask;
+
+    bool rx_enable;
+    bool rx_dma_enable;
+    bool rx_acc_enable;
+    bool rx_acc_csum_off;
+    uint32_t rx_desc_base;
+    uint32_t rx_desc_size;
+    uint32_t rx_desc_hard_ptr;
+    uint32_t rx_desc_hard_ptr_hold;
+    uint32_t rx_desc_soft_ptr;
+
+    bool tx_dma_enable;
+    bool tx_acc_enable;
+    uint32_t tx_desc_base;
+    uint32_t tx_desc_size;
+    uint32_t tx_desc_hard_ptr;
+    uint32_t tx_desc_hard_ptr_hold;
+    uint32_t tx_desc_soft_ptr;
+
+    uint8_t miim_phy_addr;
+    uint8_t miim_reg_addr;
+    uint16_t miim_data;
+
+    MemoryRegion bar_mem;
+    MemoryRegion bar_io;
+    uint16_t io_index;
+
+    uint8_t *pkt_buf;
+} PCHGBEState;
+
+static void pch_gbe_update_irq(PCHGBEState *s)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+
+    pci_set_irq(d, !!(s->intr_status & s->intr_enable));
+}
+
+static void pch_gbe_set_intr(PCHGBEState *s, uint32_t intr)
+{
+    s->intr_status |= intr;
+    pch_gbe_update_irq(s);
+}
+
+static void pch_gbe_tx(PCHGBEState *s)
+{
+    struct pch_gbe_tx_desc desc;
+    dma_addr_t addr, len, pad;
+    uint32_t ctl, sts;
+
+    if (!s->tx_dma_enable) {
+        return;
+    }
+
+    while (s->tx_desc_hard_ptr != s->tx_desc_soft_ptr) {
+        if ((s->tx_desc_hard_ptr & 0xf) ||
+            (s->tx_desc_hard_ptr < s->tx_desc_base) ||
+            (s->tx_desc_hard_ptr >= (s->tx_desc_base + s->tx_desc_size))) {
+            pch_gbe_set_intr(s, PCH_GBE_INTR_TX_DMA_ERR);
+            break;
+        }
+
+        pci_dma_read(PCI_DEVICE(s), s->tx_desc_hard_ptr, &desc, sizeof(desc));
+
+        ctl = le32_to_cpu(desc.control);
+        addr = le32_to_cpu(desc.addr);
+        len = le32_to_cpu(desc.len) & PCH_GBE_TX_LENGTH;
+        pad = s->tx_acc_enable ? 2 : 0;
+
+        pci_dma_read(PCI_DEVICE(s), addr, s->pkt_buf, len + pad);
+
+        if (pad && (len >= 14)) {
+            memcpy(s->pkt_buf + 14, s->pkt_buf + 16, len - 14);
+        }
+
+        if ((ctl & PCH_GBE_TX_CONTROL_APAD) && (len < 64)) {
+            memset(s->pkt_buf + len, 0, 64 - len);
+            len = 64;
+        }
+
+        if (s->tx_acc_enable &&
+            !(ctl & (PCH_GBE_TX_CONTROL_ICRC | PCH_GBE_TX_CONTROL_ACCOFF))) {
+                net_checksum_calculate(s->pkt_buf, len);
+        }
+
+        qemu_send_packet(qemu_get_queue(s->nic), s->pkt_buf, len);
+        pch_gbe_set_intr(s, PCH_GBE_INTR_TX_DMA_CMPLT);
+
+        sts = PCH_GBE_TX_STATUS_CMPLT;
+        desc.status = cpu_to_le32(sts);
+        pci_dma_write(PCI_DEVICE(s), s->tx_desc_hard_ptr, &desc, sizeof(desc));
+        pch_gbe_set_intr(s, PCH_GBE_INTR_TX_CMPLT);
+
+        s->tx_desc_hard_ptr += sizeof(desc);
+        if (s->tx_desc_hard_ptr >= (s->tx_desc_base + s->tx_desc_size)) {
+            s->tx_desc_hard_ptr = s->tx_desc_base;
+        }
+    }
+}
+
+static ssize_t pch_gbe_receive(NetClientState *nc,
+                               const uint8_t *buf, size_t len)
+{
+    PCHGBEState *s = qemu_get_nic_opaque(nc);
+    struct pch_gbe_rx_desc desc;
+    uint32_t mac_status;
+    dma_addr_t addr;
+
+    if (s->reset || !s->link || !s->rx_enable || !s->rx_dma_enable) {
+        return -1;
+    }
+
+    if (s->rx_desc_hard_ptr == s->rx_desc_soft_ptr) {
+        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_DSC_EMP);
+        return -1;
+    }
+
+    pci_dma_read(PCI_DEVICE(s), s->rx_desc_hard_ptr, &desc, sizeof(desc));
+    addr = le32_to_cpu(desc.addr);
+
+    if (len < 1519) {
+        memcpy(s->pkt_buf, buf, len);
+
+        /* Add an empty FCS */
+        memset(&s->pkt_buf[len], 0, 4);
+        len += 4;
+
+        pci_dma_write(PCI_DEVICE(s), addr, s->pkt_buf, len);
+
+        mac_status = (len + 3) & PCH_GBE_RX_MAC_STATUS_EOB;
+        mac_status |= (len + 3) & PCH_GBE_RX_MAC_STATUS_WORDS;
+
+        /*
+         * Unsure why this is required, but the Linux driver subtracts 4 from
+         * the length if bit 1 of rx_eob is set. We add 4 here to compensate.
+         */
+        if (mac_status & BIT(1)) {
+            mac_status = (mac_status + 4) & PCH_GBE_RX_MAC_STATUS_LENGTH;
+        }
+
+        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_DMA_CMPLT);
+        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_VALID);
+    } else {
+        mac_status = PCH_GBE_RX_MAC_STATUS_TLNG;
+        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_FRAME_ERR);
+    }
+
+    desc.acc_status = 0;
+    desc.mac_status = cpu_to_le32(mac_status);
+    desc.dma_status = 0;
+    pci_dma_write(PCI_DEVICE(s), s->rx_desc_hard_ptr, &desc, sizeof(desc));
+
+    s->rx_desc_hard_ptr += sizeof(desc);
+    if (s->rx_desc_hard_ptr >= (s->rx_desc_base + s->rx_desc_size)) {
+        s->rx_desc_hard_ptr = s->rx_desc_base;
+    }
+
+    return len;
+}
+
+static int pch_gbe_can_receive(NetClientState *nc)
+{
+    PCHGBEState *s = qemu_get_nic_opaque(nc);
+
+    return s->rx_desc_hard_ptr != s->rx_desc_soft_ptr;
+}
+
+static void pch_gbe_set_link_status(NetClientState *nc)
+{
+    PCHGBEState *s = qemu_get_nic_opaque(nc);
+
+    s->link = !nc->link_down;
+}
+
+static NetClientInfo pch_gbe_net_client_info = {
+    .type = NET_CLIENT_DRIVER_NIC,
+    .size = sizeof(NICState),
+    .can_receive = pch_gbe_can_receive,
+    .receive = pch_gbe_receive,
+    .link_status_changed = pch_gbe_set_link_status,
+};
+
+static void pch_gbe_reset(DeviceState *d)
+{
+    PCHGBEState *s = PCH_GBE(d);
+
+    s->io_index = 0;
+
+    s->intr_status = 0;
+    s->intr_status_hold = 0;
+    s->intr_enable = 0;
+    pch_gbe_update_irq(s);
+
+    pch_gbe_set_link_status(qemu_get_queue(s->nic));
+}
+
+/*
+ * PHY registers
+ */
+
+static void pch_gbe_phy_write(PCHGBEState *s, uint8_t addr, uint16_t val)
+{
+    switch (addr) {
+    default:
+        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PHY write 0x%x = 0x%x\n",
+                      addr, val);
+    }
+}
+
+static uint16_t pch_gbe_phy_read(PCHGBEState *s, uint8_t addr)
+{
+    switch (addr) {
+    case MII_BMCR:
+        return MII_BMCR_SPEED1000 | MII_BMCR_FD;
+
+    case MII_BMSR:
+        return MII_BMSR_100TX_FD | MII_BMSR_AN_COMP |
+               (s->link ? MII_BMSR_LINK_ST : 0);
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PHY read 0x%x\n", addr);
+    }
+    return 0;
+}
+
+/*
+ * PCI Memory Mapped I/O Space
+ */
+
+enum pch_gbe_mem_regs {
+    PCH_GBE_MEM_INTR                    = 0x000,
+    PCH_GBE_MEM_INTR_EN                 = 0x004,
+    PCH_GBE_MEM_INTR_HOLD               = 0x018,
+
+    PCH_GBE_MEM_RESET                   = 0x00c,
+#define PCH_GBE_MEM_RESET_ALL           BIT(31)
+#define PCH_GBE_MEM_RESET_TX            BIT(15)
+#define PCH_GBE_MEM_RESET_RX            BIT(14)
+
+    PCH_GBE_MEM_TCPIPACC                = 0x010,
+#define PCH_GBE_MEM_TCPIPACC_RXEN       BIT(0)
+#define PCH_GBE_MEM_TCPIPACC_TXEN       BIT(1)
+#define PCH_GBE_MEM_TCPIPACC_RXSUMOFF   BIT(2)
+
+    PCH_GBE_MEM_MAX_RXEN                = 0x020,
+#define PCH_GBE_MEM_MAX_RXEN_EN         BIT(0)
+
+    PCH_GBE_MEM_MAC_ADDR_1A             = 0x060,
+    PCH_GBE_MEM_MAC_ADDR_1B             = 0x064,
+
+    PCH_GBE_MEM_ADDR_MASK               = 0x0e0,
+#define PCH_GBE_MEM_ADDR_MASK_MAC0      BIT(0)
+#define PCH_GBE_MEM_ADDR_MASK_BUSY      BIT(31)
+
+    PCH_GBE_MEM_MIIM                    = 0x0e4,
+#define PCH_GBE_MEM_MIIM_READY          BIT(26)
+#define PCH_GBE_MEM_MIIM_WRITE          BIT(26)
+#define PCH_GBE_MEM_MIIM_PHY_ADDR_SHF   21
+#define PCH_GBE_MEM_MIIM_PHY_ADDR_MSK   (0x1f << 21)
+#define PCH_GBE_MEM_MIIM_REG_ADDR_SHF   16
+#define PCH_GBE_MEM_MIIM_REG_ADDR_MSK   (0x1f << 16)
+#define PCH_GBE_MEM_MIIM_DATA           0xffff
+
+    PCH_GBE_MEM_RGMII_STATUS            = 0x0ec,
+#define PCH_GBE_MEM_RGMII_STATUS_FDPLX  BIT(0)
+#define PCH_GBE_MEM_RGMII_STATUS_UP     BIT(3)
+
+    PCH_GBE_MEM_DMA_CONTROL             = 0x100,
+#define PCH_GBE_MEM_DMA_CONTROL_TX_EN   BIT(0)
+#define PCH_GBE_MEM_DMA_CONTROL_RX_EN   BIT(1)
+
+    PCH_GBE_MEM_RX_DESC_BASE            = 0x110,
+
+    PCH_GBE_MEM_RX_DESC_SIZE            = 0x114,
+#define PCH_GBE_MEM_RX_DESC_SIZE_SIZE   0xfff0
+
+    PCH_GBE_MEM_RX_DESC_HARD_PTR        = 0x118,
+    PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD   = 0x11c,
+    PCH_GBE_MEM_RX_DESC_SOFT_PTR        = 0x120,
+
+    PCH_GBE_MEM_TX_DESC_BASE            = 0x130,
+
+    PCH_GBE_MEM_TX_DESC_SIZE            = 0x134,
+#define PCH_GBE_MEM_TX_DESC_SIZE_SIZE   0xfff0
+
+    PCH_GBE_MEM_TX_DESC_HARD_PTR        = 0x138,
+    PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD   = 0x13c,
+    PCH_GBE_MEM_TX_DESC_SOFT_PTR        = 0x140,
+
+    PCH_GBE_MEM_SRST                    = 0x1fc,
+#define PCH_GBE_MEM_SRST_SRST           BIT(0)
+};
+
+static void pch_gbe_mem_write(void *opaque, hwaddr addr,
+                              uint64_t val, unsigned size)
+{
+    PCHGBEState *s = PCH_GBE(opaque);
+
+    switch (addr) {
+    case PCH_GBE_MEM_INTR:
+    case PCH_GBE_MEM_INTR_HOLD:
+    case PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD:
+    case PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD:
+        /* read-only */
+        break;
+
+    case PCH_GBE_MEM_INTR_EN:
+        s->intr_enable = val & PCH_GBE_INTR_ALL;
+        pch_gbe_update_irq(s);
+        break;
+
+    case PCH_GBE_MEM_RESET:
+        s->reset = !!(val & PCH_GBE_MEM_RESET_ALL);
+        if (s->reset) {
+            pch_gbe_reset(DEVICE(s));
+            s->reset = false;
+            break;
+        }
+        if (val & PCH_GBE_MEM_RESET_TX) {
+            qemu_log_mask(LOG_UNIMP,
+                          "pch_gbe: Partial (TX) reset unimplemented\n");
+        }
+        if (val & PCH_GBE_MEM_RESET_RX) {
+            qemu_log_mask(LOG_UNIMP,
+                          "pch_gbe: Partial (RX) reset unimplemented\n");
+        }
+        break;
+
+    case PCH_GBE_MEM_TCPIPACC:
+        s->rx_acc_enable = !!(val & PCH_GBE_MEM_TCPIPACC_RXEN);
+        s->tx_acc_enable = !!(val & PCH_GBE_MEM_TCPIPACC_TXEN);
+        s->rx_acc_csum_off = !!(val & PCH_GBE_MEM_TCPIPACC_RXSUMOFF);
+        if (s->rx_acc_enable) {
+            qemu_log_mask(LOG_UNIMP,
+                          "pch_gbe: RX acceleration unimplemented\n");
+        }
+        break;
+
+    case PCH_GBE_MEM_MAX_RXEN:
+        s->rx_enable = !!(val & PCH_GBE_MEM_MAX_RXEN_EN);
+        break;
+
+    case PCH_GBE_MEM_MAC_ADDR_1A:
+        s->conf.macaddr.a[0] = (val >> 0);
+        s->conf.macaddr.a[1] = (val >> 8);
+        s->conf.macaddr.a[2] = (val >> 16);
+        s->conf.macaddr.a[3] = (val >> 24);
+        break;
+
+    case PCH_GBE_MEM_MAC_ADDR_1B:
+        s->conf.macaddr.a[4] = (val >> 0);
+        s->conf.macaddr.a[5] = (val >> 8);
+        break;
+
+    case PCH_GBE_MEM_ADDR_MASK:
+        s->addr_mask = val & PCH_GBE_MEM_ADDR_MASK_MAC0;
+        break;
+
+    case PCH_GBE_MEM_MIIM:
+        s->miim_phy_addr = (val & PCH_GBE_MEM_MIIM_PHY_ADDR_MSK)
+                         >> PCH_GBE_MEM_MIIM_PHY_ADDR_SHF;
+        s->miim_reg_addr = (val & PCH_GBE_MEM_MIIM_REG_ADDR_MSK)
+                         >> PCH_GBE_MEM_MIIM_REG_ADDR_SHF;
+        s->miim_data = val & PCH_GBE_MEM_MIIM_DATA;
+        if (s->miim_phy_addr == 1) {
+            if (val & PCH_GBE_MEM_MIIM_WRITE) {
+                pch_gbe_phy_write(s, s->miim_reg_addr, s->miim_data);
+            } else {
+                s->miim_data = pch_gbe_phy_read(s, s->miim_reg_addr);
+            }
+        } else if (!(val & PCH_GBE_MEM_MIIM_WRITE)) {
+            s->miim_data = PCH_GBE_MEM_MIIM_DATA;
+        }
+        pch_gbe_set_intr(s, PCH_GBE_INTR_MIIM_CMPLT);
+        break;
+
+    case PCH_GBE_MEM_DMA_CONTROL:
+        s->rx_dma_enable = !!(val & PCH_GBE_MEM_DMA_CONTROL_RX_EN);
+        s->tx_dma_enable = !!(val & PCH_GBE_MEM_DMA_CONTROL_TX_EN);
+        break;
+
+    case PCH_GBE_MEM_RX_DESC_BASE:
+        s->rx_desc_base = val;
+        s->rx_desc_hard_ptr = s->rx_desc_base;
+        break;
+
+    case PCH_GBE_MEM_RX_DESC_SIZE:
+        s->rx_desc_size = (val & PCH_GBE_MEM_RX_DESC_SIZE_SIZE) + 0x10;
+        break;
+
+    case PCH_GBE_MEM_RX_DESC_HARD_PTR:
+        s->rx_desc_hard_ptr = val;
+        break;
+
+    case PCH_GBE_MEM_RX_DESC_SOFT_PTR:
+        s->rx_desc_soft_ptr = val;
+        break;
+
+    case PCH_GBE_MEM_TX_DESC_BASE:
+        s->tx_desc_base = val;
+        s->tx_desc_hard_ptr = s->tx_desc_base;
+        pch_gbe_tx(s);
+        break;
+
+    case PCH_GBE_MEM_TX_DESC_SIZE:
+        s->tx_desc_size = (val & PCH_GBE_MEM_TX_DESC_SIZE_SIZE) + 0x10;
+        pch_gbe_tx(s);
+        break;
+
+    case PCH_GBE_MEM_TX_DESC_HARD_PTR:
+        s->tx_desc_hard_ptr = val;
+        pch_gbe_tx(s);
+        break;
+
+    case PCH_GBE_MEM_TX_DESC_SOFT_PTR:
+        s->tx_desc_soft_ptr = val;
+        pch_gbe_tx(s);
+        break;
+
+    case PCH_GBE_MEM_SRST:
+        s->reset = val & PCH_GBE_MEM_SRST_SRST;
+        if (s->reset) {
+            pch_gbe_reset(DEVICE(s));
+        }
+        break;
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI mem write 0x%"
+                      HWADDR_PRIx " = 0x%" PRIx64 "\n", addr, val);
+    }
+}
+
+static uint64_t pch_gbe_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PCHGBEState *s = PCH_GBE(opaque);
+
+    switch (addr) {
+    case PCH_GBE_MEM_INTR:
+        s->rx_desc_hard_ptr_hold = s->rx_desc_hard_ptr;
+        s->tx_desc_hard_ptr_hold = s->tx_desc_hard_ptr;
+        s->intr_status_hold = s->intr_status;
+        s->intr_status = 0;
+        pch_gbe_update_irq(s);
+    case PCH_GBE_MEM_INTR_HOLD:
+        return s->intr_status_hold;
+
+    case PCH_GBE_MEM_INTR_EN:
+        return s->intr_enable;
+
+    case PCH_GBE_MEM_RESET:
+        return 0;
+
+    case PCH_GBE_MEM_TCPIPACC:
+        return (s->rx_acc_enable ? PCH_GBE_MEM_TCPIPACC_RXEN : 0) |
+               (s->tx_acc_enable ? PCH_GBE_MEM_TCPIPACC_TXEN : 0) |
+               (s->rx_acc_csum_off ? PCH_GBE_MEM_TCPIPACC_RXSUMOFF : 0);
+
+    case PCH_GBE_MEM_MAX_RXEN:
+        return s->rx_enable ? PCH_GBE_MEM_MAX_RXEN_EN : 0;
+
+    case PCH_GBE_MEM_MAC_ADDR_1A:
+        return s->conf.macaddr.a[0] << 0 |
+               s->conf.macaddr.a[1] << 8 |
+               s->conf.macaddr.a[2] << 16 |
+               s->conf.macaddr.a[3] << 24;
+
+    case PCH_GBE_MEM_MAC_ADDR_1B:
+        return s->conf.macaddr.a[4] << 0 |
+               s->conf.macaddr.a[5] << 8;
+
+    case PCH_GBE_MEM_ADDR_MASK:
+        return s->addr_mask;
+
+    case PCH_GBE_MEM_MIIM:
+        return PCH_GBE_MEM_MIIM_READY |
+            (s->miim_phy_addr << PCH_GBE_MEM_MIIM_PHY_ADDR_SHF) |
+            (s->miim_reg_addr << PCH_GBE_MEM_MIIM_REG_ADDR_SHF) |
+            s->miim_data;
+
+    case PCH_GBE_MEM_SRST:
+        return s->reset ? PCH_GBE_MEM_SRST_SRST : 0;
+
+    case PCH_GBE_MEM_RGMII_STATUS:
+        return (s->link ? PCH_GBE_MEM_RGMII_STATUS_UP : 0) |
+               PCH_GBE_MEM_RGMII_STATUS_FDPLX;
+
+    case PCH_GBE_MEM_DMA_CONTROL:
+        return (s->rx_dma_enable ? PCH_GBE_MEM_DMA_CONTROL_RX_EN : 0) |
+               (s->tx_dma_enable ? PCH_GBE_MEM_DMA_CONTROL_TX_EN : 0);
+
+    case PCH_GBE_MEM_RX_DESC_BASE:
+        return s->rx_desc_base;
+
+    case PCH_GBE_MEM_RX_DESC_SIZE:
+        return (s->rx_desc_size - 0x10) & PCH_GBE_MEM_RX_DESC_SIZE_SIZE;
+
+    case PCH_GBE_MEM_RX_DESC_HARD_PTR:
+        return s->rx_desc_hard_ptr;
+
+    case PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD:
+        return s->rx_desc_hard_ptr_hold;
+
+    case PCH_GBE_MEM_RX_DESC_SOFT_PTR:
+        return s->rx_desc_soft_ptr;
+
+    case PCH_GBE_MEM_TX_DESC_BASE:
+        return s->tx_desc_base;
+
+    case PCH_GBE_MEM_TX_DESC_SIZE:
+        return (s->tx_desc_size - 0x10) & PCH_GBE_MEM_TX_DESC_SIZE_SIZE;
+
+    case PCH_GBE_MEM_TX_DESC_HARD_PTR:
+        return s->tx_desc_hard_ptr;
+
+    case PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD:
+        return s->tx_desc_hard_ptr_hold;
+
+    case PCH_GBE_MEM_TX_DESC_SOFT_PTR:
+        return s->tx_desc_soft_ptr;
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI mem read 0x%"
+                      HWADDR_PRIx "\n", addr);
+        return -1;
+    }
+}
+
+static const MemoryRegionOps pch_gbe_mem_ops = {
+    .read = pch_gbe_mem_read,
+    .write = pch_gbe_mem_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/*
+ * PCI I/O Space
+ */
+
+enum pch_gbe_io_regs {
+    PCH_GBE_IO_INDEX                    = 0x0,
+#define PCH_GBE_IO_INDEX_INDEX          0x1ff
+
+    PCH_GBE_IO_DATA                     = 0x4,
+};
+
+static void pch_gbe_io_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned size)
+{
+    PCHGBEState *s = PCH_GBE(opaque);
+
+    switch (addr) {
+    case PCH_GBE_IO_INDEX:
+        s->io_index = val & PCH_GBE_IO_INDEX_INDEX;
+        break;
+
+    case PCH_GBE_IO_DATA:
+        pch_gbe_mem_write(opaque, s->io_index, val, size);
+        break;
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI I/O write 0x%"
+                      HWADDR_PRIx " = 0x%" PRIx64 "\n", addr, val);
+    }
+}
+
+static uint64_t pch_gbe_io_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PCHGBEState *s = PCH_GBE(opaque);
+
+    switch (addr) {
+    case PCH_GBE_IO_INDEX:
+        return s->io_index;
+
+    case PCH_GBE_IO_DATA:
+        return pch_gbe_mem_read(opaque, s->io_index, size);
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI I/O read 0x%"
+                      HWADDR_PRIx "\n", addr);
+        return -1;
+    }
+}
+
+static const MemoryRegionOps pch_gbe_io_ops = {
+    .read = pch_gbe_io_read,
+    .write = pch_gbe_io_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void pch_gbe_realize(PCIDevice *dev, Error **errp)
+{
+    PCHGBEState *s = PCH_GBE(dev);
+
+    pci_config_set_interrupt_pin(dev->config, 1);
+
+    memory_region_init_io(&s->bar_io, OBJECT(s), &pch_gbe_io_ops, s,
+                          "pch_gbe-io", 0x20);
+    memory_region_init_io(&s->bar_mem, OBJECT(s), &pch_gbe_mem_ops, s,
+                          "pch_gbe-mem", 0x200);
+
+    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io);
+    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    s->pkt_buf = g_malloc(64 * 1024);
+
+    s->nic = qemu_new_nic(&pch_gbe_net_client_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), DEVICE(dev)->id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static void pch_gbe_uninit(PCIDevice *dev)
+{
+    PCHGBEState *s = PCH_GBE(dev);
+
+    g_free(s->pkt_buf);
+}
+
+static void pch_gbe_instance_init(Object *obj)
+{
+}
+
+static Property pch_gbe_properties[] = {
+    DEFINE_NIC_PROPERTIES(PCHGBEState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pch_gbe_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->realize = pch_gbe_realize;
+    k->exit = pch_gbe_uninit;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = 0x8802;
+    k->revision = 0x2;
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    dc->reset = pch_gbe_reset;
+    dc->props = pch_gbe_properties;
+    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+}
+
+static const TypeInfo pch_gbe_info = {
+    .name          = TYPE_PCH_GBE,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCHGBEState),
+    .class_init    = pch_gbe_class_init,
+    .instance_init = pch_gbe_instance_init,
+    .interfaces = (InterfaceInfo[]) {
+        { INTERFACE_PCIE_DEVICE },
+        { },
+    },
+};
+
+static void pch_gbe_register_types(void)
+{
+    type_register_static(&pch_gbe_info);
+}
+type_init(pch_gbe_register_types)
-- 
2.16.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller
  2018-02-17 19:22 [Qemu-devel] [PATCH 0/2] MIPS Boston / pch_gbe ethernet support Paul Burton
  2018-02-17 19:22 ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Paul Burton
@ 2018-02-17 19:22 ` Paul Burton
  1 sibling, 0 replies; 8+ messages in thread
From: Paul Burton @ 2018-02-17 19:22 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Yongbok Kim, Paul Burton

Enable CONFIG_PCH_GBE_PCI in mips64el-softmmu.mak (currently the only
default config to enable Boston board support) and create the pch_gbe
device when using the Boston board.

This provides the board with an ethernet controller matching that found
on real Boston boards as part of the Intel EG20T Platform Controller
Hub, and allows standard Boston Linux kernels to have network access.

This is most easily tested using the downstream linux-mti kernels at the
moment, until MIPS support for the Linux pch_gbe driver is upstream. For
example, presuming U-Boot's mkimage tool is present in your $PATH, this
should be sufficient to boot Linux & see it obtain an IP address using
the emulated pch_gbe device:

  $ git clone git://git.linux-mips.org/pub/scm/linux-mti.git -b eng
  $ cd linux-mti
  $ make ARCH=mips 64r6el_defconfig
  $ make ARCH=mips CROSS_COMPILE=/path/to/compiler/bin/mips-linux-gnu-
  $ qemu-system-mips64el \
      -M boston -cpu I6400 \
      -kernel arch/mips/boot/vmlinux.gz.itb \
      -serial stdio -append "ip=dhcp"

Signed-off-by: Paul Burton <paul.burton@mips.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: Yongbok Kim <yongbok.kim@mips.com>

---

 default-configs/mips64el-softmmu.mak | 1 +
 hw/mips/boston.c                     | 8 +++++++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak
index c2ae313f47..85175ea223 100644
--- a/default-configs/mips64el-softmmu.mak
+++ b/default-configs/mips64el-softmmu.mak
@@ -13,3 +13,4 @@ CONFIG_VT82C686=y
 CONFIG_MIPS_BOSTON=y
 CONFIG_FITLOADER=y
 CONFIG_PCI_XILINX=y
+CONFIG_PCH_GBE_PCI=y
diff --git a/hw/mips/boston.c b/hw/mips/boston.c
index fb23161b33..408977bca1 100644
--- a/hw/mips/boston.c
+++ b/hw/mips/boston.c
@@ -31,6 +31,7 @@
 #include "hw/mips/cps.h"
 #include "hw/mips/cpudevs.h"
 #include "hw/pci-host/xilinx-pcie.h"
+#include "net/net.h"
 #include "qapi/error.h"
 #include "qemu/cutils.h"
 #include "qemu/error-report.h"
@@ -430,7 +431,7 @@ static void boston_mach_init(MachineState *machine)
     MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg;
     MemoryRegion *sys_mem = get_system_memory();
     XilinxPCIEHost *pcie2;
-    PCIDevice *ahci;
+    PCIDevice *ahci, *eth;
     DriveInfo *hd[6];
     Chardev *chr;
     int fw_size, fit_err;
@@ -529,6 +530,11 @@ static void boston_mach_init(MachineState *machine)
     ide_drive_get(hd, ahci_get_num_ports(ahci));
     ahci_ide_create_devs(ahci, hd);
 
+    eth = pci_create(&PCI_BRIDGE(&pcie2->root)->sec_bus,
+                     PCI_DEVFN(0, 1), "pch_gbe");
+    qdev_set_nic_properties(&eth->qdev, &nd_table[0]);
+    qdev_init_nofail(&eth->qdev);
+
     if (machine->firmware) {
         fw_size = load_image_targphys(machine->firmware,
                                       0x1fc00000, 4 * M_BYTE);
-- 
2.16.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet
  2018-02-17 19:22 ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Paul Burton
@ 2018-07-02 23:36   ` Philippe Mathieu-Daudé
  2018-07-03  9:46     ` Aleksandar Markovic
  2018-07-04  7:24   ` Jason Wang
  1 sibling, 1 reply; 8+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-07-02 23:36 UTC (permalink / raw)
  To: Paul Burton, Aleksandar Markovic, Jason Wang; +Cc: qemu-devel, Aurelien Jarno

Cc'ing Jason, the "Network devices" maintainer.

On 02/17/2018 04:22 PM, Paul Burton wrote:
> This patch introduces support for emulating the ethernet controller
> found in the Intel EG20T Platform Controller Hub, referred to as pch_gbe
> for consistency with both Linux & U-Boot.
> 
> Documentation for the hardware can be found here:
> 
>   https://www.intel.com/content/www/us/en/intelligent-systems/queens-bay/platform-controller-hub-eg20t-datasheet.html
> 
> The device is used on MIPS Boston development boards as well as the
> Intel Crown Bay platform including devices such as the Minnowboard V1.
> 
> Enough functionality is implemented for Linux to make use of the device,
> and has been tested using Linux v4.16-rc1.
> 
> Signed-off-by: Paul Burton <paul.burton@mips.com>
> Cc: Aurelien Jarno <aurelien@aurel32.net>
> Cc: Yongbok Kim <yongbok.kim@mips.com>
> ---
> 
>  hw/net/Makefile.objs |   1 +
>  hw/net/pch_gbe.c     | 766 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 767 insertions(+)
>  create mode 100644 hw/net/pch_gbe.c
> 
> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
> index ab22968641..08706d9a96 100644
> --- a/hw/net/Makefile.objs
> +++ b/hw/net/Makefile.objs
> @@ -12,6 +12,7 @@ common-obj-$(CONFIG_E1000E_PCI) += e1000e.o e1000e_core.o e1000x_common.o
>  common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
>  common-obj-$(CONFIG_VMXNET3_PCI) += net_tx_pkt.o net_rx_pkt.o
>  common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o
> +common-obj-$(CONFIG_PCH_GBE_PCI) += pch_gbe.o
>  
>  common-obj-$(CONFIG_SMC91C111) += smc91c111.o
>  common-obj-$(CONFIG_LAN9118) += lan9118.o
> diff --git a/hw/net/pch_gbe.c b/hw/net/pch_gbe.c
> new file mode 100644
> index 0000000000..be9a9f5916
> --- /dev/null
> +++ b/hw/net/pch_gbe.c
> @@ -0,0 +1,766 @@
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "hw/net/mii.h"
> +#include "hw/pci/pci.h"
> +#include "net/checksum.h"
> +#include "net/eth.h"
> +#include "net/net.h"
> +#include "qemu/bitops.h"
> +#include "qemu/log.h"
> +
> +#define TYPE_PCH_GBE    "pch_gbe"
> +#define PCH_GBE(obj)    OBJECT_CHECK(PCHGBEState, (obj), TYPE_PCH_GBE)
> +
> +#define PCH_GBE_INTR_RX_DMA_CMPLT       BIT(0)
> +#define PCH_GBE_INTR_RX_VALID           BIT(1)
> +#define PCH_GBE_INTR_RX_FRAME_ERR       BIT(2)
> +#define PCH_GBE_INTR_RX_FIFO_ERR        BIT(3)
> +#define PCH_GBE_INTR_RX_DMA_ERR         BIT(4)
> +#define PCH_GBE_INTR_RX_DSC_EMP         BIT(5)
> +#define PCH_GBE_INTR_TX_CMPLT           BIT(8)
> +#define PCH_GBE_INTR_TX_DMA_CMPLT       BIT(9)
> +#define PCH_GBE_INTR_TX_FIFO_ERR        BIT(10)
> +#define PCH_GBE_INTR_TX_DMA_ERR         BIT(11)
> +#define PCH_GBE_INTR_PAUSE_CMPLT        BIT(12)
> +#define PCH_GBE_INTR_MIIM_CMPLT         BIT(16)
> +#define PCH_GBE_INTR_PHY_INT            BIT(20)
> +#define PCH_GBE_INTR_WOL_DET            BIT(24)
> +#define PCH_GBE_INTR_TCPIP_ERR          BIT(28)
> +#define PCH_GBE_INTR_ALL (              \
> +        PCH_GBE_INTR_RX_DMA_CMPLT |     \
> +        PCH_GBE_INTR_RX_VALID |         \
> +        PCH_GBE_INTR_RX_FRAME_ERR |     \
> +        PCH_GBE_INTR_RX_FIFO_ERR |      \
> +        PCH_GBE_INTR_RX_DMA_ERR |       \
> +        PCH_GBE_INTR_RX_DSC_EMP |       \
> +        PCH_GBE_INTR_TX_CMPLT |         \
> +        PCH_GBE_INTR_TX_DMA_CMPLT |     \
> +        PCH_GBE_INTR_TX_FIFO_ERR |      \
> +        PCH_GBE_INTR_TX_DMA_ERR |       \
> +        PCH_GBE_INTR_PAUSE_CMPLT |      \
> +        PCH_GBE_INTR_MIIM_CMPLT |       \
> +        PCH_GBE_INTR_PHY_INT |          \
> +        PCH_GBE_INTR_WOL_DET |          \
> +        PCH_GBE_INTR_TCPIP_ERR)
> +
> +struct pch_gbe_tx_desc {
> +    uint32_t addr;
> +
> +    uint32_t len;
> +#define PCH_GBE_TX_LENGTH               0xffff
> +
> +    uint32_t control;
> +#define PCH_GBE_TX_CONTROL_EOB          0x3
> +#define PCH_GBE_TX_CONTROL_WORDS        0xfffc
> +#define PCH_GBE_TX_CONTROL_APAD         BIT(16)
> +#define PCH_GBE_TX_CONTROL_ICRC         BIT(17)
> +#define PCH_GBE_TX_CONTROL_ITAG         BIT(18)
> +#define PCH_GBE_TX_CONTROL_ACCOFF       BIT(19)
> +
> +    uint32_t status;
> +#define PCH_GBE_TX_STATUS_TSHRT         BIT(22)
> +#define PCH_GBE_TX_STATUS_TLNG          BIT(23)
> +#define PCH_GBE_TX_STATUS_ABT           BIT(28)
> +#define PCH_GBE_TX_STATUS_CMPLT         BIT(29)
> +};
> +
> +struct pch_gbe_rx_desc {
> +    uint32_t addr;
> +
> +    uint32_t acc_status;
> +
> +    uint32_t mac_status;
> +#define PCH_GBE_RX_MAC_STATUS_EOB       0x3
> +#define PCH_GBE_RX_MAC_STATUS_WORDS     0xfffc
> +#define PCH_GBE_RX_MAC_STATUS_LENGTH    0xffff
> +#define PCH_GBE_RX_MAC_STATUS_TSHRT     BIT(19)
> +#define PCH_GBE_RX_MAC_STATUS_TLNG      BIT(20)
> +
> +    uint32_t dma_status;
> +};
> +
> +typedef struct {
> +    /*< private >*/
> +    PCIDevice parent_obj;
> +    /*< public >*/
> +
> +    NICState *nic;
> +    NICConf conf;
> +
> +    bool reset;
> +    bool phy_reset;
> +
> +    bool link;
> +
> +    uint32_t intr_status;
> +    uint32_t intr_status_hold;
> +    uint32_t intr_enable;
> +
> +    uint16_t addr_mask;
> +
> +    bool rx_enable;
> +    bool rx_dma_enable;
> +    bool rx_acc_enable;
> +    bool rx_acc_csum_off;
> +    uint32_t rx_desc_base;
> +    uint32_t rx_desc_size;
> +    uint32_t rx_desc_hard_ptr;
> +    uint32_t rx_desc_hard_ptr_hold;
> +    uint32_t rx_desc_soft_ptr;
> +
> +    bool tx_dma_enable;
> +    bool tx_acc_enable;
> +    uint32_t tx_desc_base;
> +    uint32_t tx_desc_size;
> +    uint32_t tx_desc_hard_ptr;
> +    uint32_t tx_desc_hard_ptr_hold;
> +    uint32_t tx_desc_soft_ptr;
> +
> +    uint8_t miim_phy_addr;
> +    uint8_t miim_reg_addr;
> +    uint16_t miim_data;
> +
> +    MemoryRegion bar_mem;
> +    MemoryRegion bar_io;
> +    uint16_t io_index;
> +
> +    uint8_t *pkt_buf;
> +} PCHGBEState;
> +
> +static void pch_gbe_update_irq(PCHGBEState *s)
> +{
> +    PCIDevice *d = PCI_DEVICE(s);
> +
> +    pci_set_irq(d, !!(s->intr_status & s->intr_enable));
> +}
> +
> +static void pch_gbe_set_intr(PCHGBEState *s, uint32_t intr)
> +{
> +    s->intr_status |= intr;
> +    pch_gbe_update_irq(s);
> +}
> +
> +static void pch_gbe_tx(PCHGBEState *s)
> +{
> +    struct pch_gbe_tx_desc desc;
> +    dma_addr_t addr, len, pad;
> +    uint32_t ctl, sts;
> +
> +    if (!s->tx_dma_enable) {
> +        return;
> +    }
> +
> +    while (s->tx_desc_hard_ptr != s->tx_desc_soft_ptr) {
> +        if ((s->tx_desc_hard_ptr & 0xf) ||
> +            (s->tx_desc_hard_ptr < s->tx_desc_base) ||
> +            (s->tx_desc_hard_ptr >= (s->tx_desc_base + s->tx_desc_size))) {
> +            pch_gbe_set_intr(s, PCH_GBE_INTR_TX_DMA_ERR);
> +            break;
> +        }
> +
> +        pci_dma_read(PCI_DEVICE(s), s->tx_desc_hard_ptr, &desc, sizeof(desc));
> +
> +        ctl = le32_to_cpu(desc.control);
> +        addr = le32_to_cpu(desc.addr);
> +        len = le32_to_cpu(desc.len) & PCH_GBE_TX_LENGTH;
> +        pad = s->tx_acc_enable ? 2 : 0;
> +
> +        pci_dma_read(PCI_DEVICE(s), addr, s->pkt_buf, len + pad);
> +
> +        if (pad && (len >= 14)) {
> +            memcpy(s->pkt_buf + 14, s->pkt_buf + 16, len - 14);
> +        }
> +
> +        if ((ctl & PCH_GBE_TX_CONTROL_APAD) && (len < 64)) {
> +            memset(s->pkt_buf + len, 0, 64 - len);
> +            len = 64;
> +        }
> +
> +        if (s->tx_acc_enable &&
> +            !(ctl & (PCH_GBE_TX_CONTROL_ICRC | PCH_GBE_TX_CONTROL_ACCOFF))) {
> +                net_checksum_calculate(s->pkt_buf, len);
> +        }
> +
> +        qemu_send_packet(qemu_get_queue(s->nic), s->pkt_buf, len);
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_TX_DMA_CMPLT);
> +
> +        sts = PCH_GBE_TX_STATUS_CMPLT;
> +        desc.status = cpu_to_le32(sts);
> +        pci_dma_write(PCI_DEVICE(s), s->tx_desc_hard_ptr, &desc, sizeof(desc));
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_TX_CMPLT);
> +
> +        s->tx_desc_hard_ptr += sizeof(desc);
> +        if (s->tx_desc_hard_ptr >= (s->tx_desc_base + s->tx_desc_size)) {
> +            s->tx_desc_hard_ptr = s->tx_desc_base;
> +        }
> +    }
> +}
> +
> +static ssize_t pch_gbe_receive(NetClientState *nc,
> +                               const uint8_t *buf, size_t len)
> +{
> +    PCHGBEState *s = qemu_get_nic_opaque(nc);
> +    struct pch_gbe_rx_desc desc;
> +    uint32_t mac_status;
> +    dma_addr_t addr;
> +
> +    if (s->reset || !s->link || !s->rx_enable || !s->rx_dma_enable) {
> +        return -1;
> +    }
> +
> +    if (s->rx_desc_hard_ptr == s->rx_desc_soft_ptr) {
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_DSC_EMP);
> +        return -1;
> +    }
> +
> +    pci_dma_read(PCI_DEVICE(s), s->rx_desc_hard_ptr, &desc, sizeof(desc));
> +    addr = le32_to_cpu(desc.addr);
> +
> +    if (len < 1519) {
> +        memcpy(s->pkt_buf, buf, len);
> +
> +        /* Add an empty FCS */
> +        memset(&s->pkt_buf[len], 0, 4);
> +        len += 4;
> +
> +        pci_dma_write(PCI_DEVICE(s), addr, s->pkt_buf, len);
> +
> +        mac_status = (len + 3) & PCH_GBE_RX_MAC_STATUS_EOB;
> +        mac_status |= (len + 3) & PCH_GBE_RX_MAC_STATUS_WORDS;
> +
> +        /*
> +         * Unsure why this is required, but the Linux driver subtracts 4 from
> +         * the length if bit 1 of rx_eob is set. We add 4 here to compensate.
> +         */
> +        if (mac_status & BIT(1)) {
> +            mac_status = (mac_status + 4) & PCH_GBE_RX_MAC_STATUS_LENGTH;
> +        }
> +
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_DMA_CMPLT);
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_VALID);
> +    } else {
> +        mac_status = PCH_GBE_RX_MAC_STATUS_TLNG;
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_FRAME_ERR);
> +    }
> +
> +    desc.acc_status = 0;
> +    desc.mac_status = cpu_to_le32(mac_status);
> +    desc.dma_status = 0;
> +    pci_dma_write(PCI_DEVICE(s), s->rx_desc_hard_ptr, &desc, sizeof(desc));
> +
> +    s->rx_desc_hard_ptr += sizeof(desc);
> +    if (s->rx_desc_hard_ptr >= (s->rx_desc_base + s->rx_desc_size)) {
> +        s->rx_desc_hard_ptr = s->rx_desc_base;
> +    }
> +
> +    return len;
> +}
> +
> +static int pch_gbe_can_receive(NetClientState *nc)
> +{
> +    PCHGBEState *s = qemu_get_nic_opaque(nc);
> +
> +    return s->rx_desc_hard_ptr != s->rx_desc_soft_ptr;
> +}
> +
> +static void pch_gbe_set_link_status(NetClientState *nc)
> +{
> +    PCHGBEState *s = qemu_get_nic_opaque(nc);
> +
> +    s->link = !nc->link_down;
> +}
> +
> +static NetClientInfo pch_gbe_net_client_info = {
> +    .type = NET_CLIENT_DRIVER_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = pch_gbe_can_receive,
> +    .receive = pch_gbe_receive,
> +    .link_status_changed = pch_gbe_set_link_status,
> +};
> +
> +static void pch_gbe_reset(DeviceState *d)
> +{
> +    PCHGBEState *s = PCH_GBE(d);
> +
> +    s->io_index = 0;
> +
> +    s->intr_status = 0;
> +    s->intr_status_hold = 0;
> +    s->intr_enable = 0;
> +    pch_gbe_update_irq(s);
> +
> +    pch_gbe_set_link_status(qemu_get_queue(s->nic));
> +}
> +
> +/*
> + * PHY registers
> + */
> +
> +static void pch_gbe_phy_write(PCHGBEState *s, uint8_t addr, uint16_t val)
> +{
> +    switch (addr) {
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PHY write 0x%x = 0x%x\n",
> +                      addr, val);
> +    }
> +}
> +
> +static uint16_t pch_gbe_phy_read(PCHGBEState *s, uint8_t addr)
> +{
> +    switch (addr) {
> +    case MII_BMCR:
> +        return MII_BMCR_SPEED1000 | MII_BMCR_FD;
> +
> +    case MII_BMSR:
> +        return MII_BMSR_100TX_FD | MII_BMSR_AN_COMP |
> +               (s->link ? MII_BMSR_LINK_ST : 0);
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PHY read 0x%x\n", addr);
> +    }
> +    return 0;
> +}
> +
> +/*
> + * PCI Memory Mapped I/O Space
> + */
> +
> +enum pch_gbe_mem_regs {
> +    PCH_GBE_MEM_INTR                    = 0x000,
> +    PCH_GBE_MEM_INTR_EN                 = 0x004,
> +    PCH_GBE_MEM_INTR_HOLD               = 0x018,
> +
> +    PCH_GBE_MEM_RESET                   = 0x00c,
> +#define PCH_GBE_MEM_RESET_ALL           BIT(31)
> +#define PCH_GBE_MEM_RESET_TX            BIT(15)
> +#define PCH_GBE_MEM_RESET_RX            BIT(14)
> +
> +    PCH_GBE_MEM_TCPIPACC                = 0x010,
> +#define PCH_GBE_MEM_TCPIPACC_RXEN       BIT(0)
> +#define PCH_GBE_MEM_TCPIPACC_TXEN       BIT(1)
> +#define PCH_GBE_MEM_TCPIPACC_RXSUMOFF   BIT(2)
> +
> +    PCH_GBE_MEM_MAX_RXEN                = 0x020,
> +#define PCH_GBE_MEM_MAX_RXEN_EN         BIT(0)
> +
> +    PCH_GBE_MEM_MAC_ADDR_1A             = 0x060,
> +    PCH_GBE_MEM_MAC_ADDR_1B             = 0x064,
> +
> +    PCH_GBE_MEM_ADDR_MASK               = 0x0e0,
> +#define PCH_GBE_MEM_ADDR_MASK_MAC0      BIT(0)
> +#define PCH_GBE_MEM_ADDR_MASK_BUSY      BIT(31)
> +
> +    PCH_GBE_MEM_MIIM                    = 0x0e4,
> +#define PCH_GBE_MEM_MIIM_READY          BIT(26)
> +#define PCH_GBE_MEM_MIIM_WRITE          BIT(26)
> +#define PCH_GBE_MEM_MIIM_PHY_ADDR_SHF   21
> +#define PCH_GBE_MEM_MIIM_PHY_ADDR_MSK   (0x1f << 21)
> +#define PCH_GBE_MEM_MIIM_REG_ADDR_SHF   16
> +#define PCH_GBE_MEM_MIIM_REG_ADDR_MSK   (0x1f << 16)
> +#define PCH_GBE_MEM_MIIM_DATA           0xffff
> +
> +    PCH_GBE_MEM_RGMII_STATUS            = 0x0ec,
> +#define PCH_GBE_MEM_RGMII_STATUS_FDPLX  BIT(0)
> +#define PCH_GBE_MEM_RGMII_STATUS_UP     BIT(3)
> +
> +    PCH_GBE_MEM_DMA_CONTROL             = 0x100,
> +#define PCH_GBE_MEM_DMA_CONTROL_TX_EN   BIT(0)
> +#define PCH_GBE_MEM_DMA_CONTROL_RX_EN   BIT(1)
> +
> +    PCH_GBE_MEM_RX_DESC_BASE            = 0x110,
> +
> +    PCH_GBE_MEM_RX_DESC_SIZE            = 0x114,
> +#define PCH_GBE_MEM_RX_DESC_SIZE_SIZE   0xfff0
> +
> +    PCH_GBE_MEM_RX_DESC_HARD_PTR        = 0x118,
> +    PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD   = 0x11c,
> +    PCH_GBE_MEM_RX_DESC_SOFT_PTR        = 0x120,
> +
> +    PCH_GBE_MEM_TX_DESC_BASE            = 0x130,
> +
> +    PCH_GBE_MEM_TX_DESC_SIZE            = 0x134,
> +#define PCH_GBE_MEM_TX_DESC_SIZE_SIZE   0xfff0
> +
> +    PCH_GBE_MEM_TX_DESC_HARD_PTR        = 0x138,
> +    PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD   = 0x13c,
> +    PCH_GBE_MEM_TX_DESC_SOFT_PTR        = 0x140,
> +
> +    PCH_GBE_MEM_SRST                    = 0x1fc,
> +#define PCH_GBE_MEM_SRST_SRST           BIT(0)
> +};
> +
> +static void pch_gbe_mem_write(void *opaque, hwaddr addr,
> +                              uint64_t val, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_MEM_INTR:
> +    case PCH_GBE_MEM_INTR_HOLD:
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD:
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD:
> +        /* read-only */
> +        break;
> +
> +    case PCH_GBE_MEM_INTR_EN:
> +        s->intr_enable = val & PCH_GBE_INTR_ALL;
> +        pch_gbe_update_irq(s);
> +        break;
> +
> +    case PCH_GBE_MEM_RESET:
> +        s->reset = !!(val & PCH_GBE_MEM_RESET_ALL);
> +        if (s->reset) {
> +            pch_gbe_reset(DEVICE(s));
> +            s->reset = false;
> +            break;
> +        }
> +        if (val & PCH_GBE_MEM_RESET_TX) {
> +            qemu_log_mask(LOG_UNIMP,
> +                          "pch_gbe: Partial (TX) reset unimplemented\n");
> +        }
> +        if (val & PCH_GBE_MEM_RESET_RX) {
> +            qemu_log_mask(LOG_UNIMP,
> +                          "pch_gbe: Partial (RX) reset unimplemented\n");
> +        }
> +        break;
> +
> +    case PCH_GBE_MEM_TCPIPACC:
> +        s->rx_acc_enable = !!(val & PCH_GBE_MEM_TCPIPACC_RXEN);
> +        s->tx_acc_enable = !!(val & PCH_GBE_MEM_TCPIPACC_TXEN);
> +        s->rx_acc_csum_off = !!(val & PCH_GBE_MEM_TCPIPACC_RXSUMOFF);
> +        if (s->rx_acc_enable) {
> +            qemu_log_mask(LOG_UNIMP,
> +                          "pch_gbe: RX acceleration unimplemented\n");
> +        }
> +        break;
> +
> +    case PCH_GBE_MEM_MAX_RXEN:
> +        s->rx_enable = !!(val & PCH_GBE_MEM_MAX_RXEN_EN);
> +        break;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1A:
> +        s->conf.macaddr.a[0] = (val >> 0);
> +        s->conf.macaddr.a[1] = (val >> 8);
> +        s->conf.macaddr.a[2] = (val >> 16);
> +        s->conf.macaddr.a[3] = (val >> 24);
> +        break;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1B:
> +        s->conf.macaddr.a[4] = (val >> 0);
> +        s->conf.macaddr.a[5] = (val >> 8);
> +        break;
> +
> +    case PCH_GBE_MEM_ADDR_MASK:
> +        s->addr_mask = val & PCH_GBE_MEM_ADDR_MASK_MAC0;
> +        break;
> +
> +    case PCH_GBE_MEM_MIIM:
> +        s->miim_phy_addr = (val & PCH_GBE_MEM_MIIM_PHY_ADDR_MSK)
> +                         >> PCH_GBE_MEM_MIIM_PHY_ADDR_SHF;
> +        s->miim_reg_addr = (val & PCH_GBE_MEM_MIIM_REG_ADDR_MSK)
> +                         >> PCH_GBE_MEM_MIIM_REG_ADDR_SHF;
> +        s->miim_data = val & PCH_GBE_MEM_MIIM_DATA;
> +        if (s->miim_phy_addr == 1) {
> +            if (val & PCH_GBE_MEM_MIIM_WRITE) {
> +                pch_gbe_phy_write(s, s->miim_reg_addr, s->miim_data);
> +            } else {
> +                s->miim_data = pch_gbe_phy_read(s, s->miim_reg_addr);
> +            }
> +        } else if (!(val & PCH_GBE_MEM_MIIM_WRITE)) {
> +            s->miim_data = PCH_GBE_MEM_MIIM_DATA;
> +        }
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_MIIM_CMPLT);
> +        break;
> +
> +    case PCH_GBE_MEM_DMA_CONTROL:
> +        s->rx_dma_enable = !!(val & PCH_GBE_MEM_DMA_CONTROL_RX_EN);
> +        s->tx_dma_enable = !!(val & PCH_GBE_MEM_DMA_CONTROL_TX_EN);
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_BASE:
> +        s->rx_desc_base = val;
> +        s->rx_desc_hard_ptr = s->rx_desc_base;
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_SIZE:
> +        s->rx_desc_size = (val & PCH_GBE_MEM_RX_DESC_SIZE_SIZE) + 0x10;
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR:
> +        s->rx_desc_hard_ptr = val;
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_SOFT_PTR:
> +        s->rx_desc_soft_ptr = val;
> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_BASE:
> +        s->tx_desc_base = val;
> +        s->tx_desc_hard_ptr = s->tx_desc_base;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_SIZE:
> +        s->tx_desc_size = (val & PCH_GBE_MEM_TX_DESC_SIZE_SIZE) + 0x10;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR:
> +        s->tx_desc_hard_ptr = val;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_SOFT_PTR:
> +        s->tx_desc_soft_ptr = val;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_SRST:
> +        s->reset = val & PCH_GBE_MEM_SRST_SRST;
> +        if (s->reset) {
> +            pch_gbe_reset(DEVICE(s));
> +        }
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI mem write 0x%"
> +                      HWADDR_PRIx " = 0x%" PRIx64 "\n", addr, val);
> +    }
> +}
> +
> +static uint64_t pch_gbe_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_MEM_INTR:
> +        s->rx_desc_hard_ptr_hold = s->rx_desc_hard_ptr;
> +        s->tx_desc_hard_ptr_hold = s->tx_desc_hard_ptr;
> +        s->intr_status_hold = s->intr_status;
> +        s->intr_status = 0;
> +        pch_gbe_update_irq(s);
> +    case PCH_GBE_MEM_INTR_HOLD:
> +        return s->intr_status_hold;
> +
> +    case PCH_GBE_MEM_INTR_EN:
> +        return s->intr_enable;
> +
> +    case PCH_GBE_MEM_RESET:
> +        return 0;
> +
> +    case PCH_GBE_MEM_TCPIPACC:
> +        return (s->rx_acc_enable ? PCH_GBE_MEM_TCPIPACC_RXEN : 0) |
> +               (s->tx_acc_enable ? PCH_GBE_MEM_TCPIPACC_TXEN : 0) |
> +               (s->rx_acc_csum_off ? PCH_GBE_MEM_TCPIPACC_RXSUMOFF : 0);
> +
> +    case PCH_GBE_MEM_MAX_RXEN:
> +        return s->rx_enable ? PCH_GBE_MEM_MAX_RXEN_EN : 0;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1A:
> +        return s->conf.macaddr.a[0] << 0 |
> +               s->conf.macaddr.a[1] << 8 |
> +               s->conf.macaddr.a[2] << 16 |
> +               s->conf.macaddr.a[3] << 24;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1B:
> +        return s->conf.macaddr.a[4] << 0 |
> +               s->conf.macaddr.a[5] << 8;
> +
> +    case PCH_GBE_MEM_ADDR_MASK:
> +        return s->addr_mask;
> +
> +    case PCH_GBE_MEM_MIIM:
> +        return PCH_GBE_MEM_MIIM_READY |
> +            (s->miim_phy_addr << PCH_GBE_MEM_MIIM_PHY_ADDR_SHF) |
> +            (s->miim_reg_addr << PCH_GBE_MEM_MIIM_REG_ADDR_SHF) |
> +            s->miim_data;
> +
> +    case PCH_GBE_MEM_SRST:
> +        return s->reset ? PCH_GBE_MEM_SRST_SRST : 0;
> +
> +    case PCH_GBE_MEM_RGMII_STATUS:
> +        return (s->link ? PCH_GBE_MEM_RGMII_STATUS_UP : 0) |
> +               PCH_GBE_MEM_RGMII_STATUS_FDPLX;
> +
> +    case PCH_GBE_MEM_DMA_CONTROL:
> +        return (s->rx_dma_enable ? PCH_GBE_MEM_DMA_CONTROL_RX_EN : 0) |
> +               (s->tx_dma_enable ? PCH_GBE_MEM_DMA_CONTROL_TX_EN : 0);
> +
> +    case PCH_GBE_MEM_RX_DESC_BASE:
> +        return s->rx_desc_base;
> +
> +    case PCH_GBE_MEM_RX_DESC_SIZE:
> +        return (s->rx_desc_size - 0x10) & PCH_GBE_MEM_RX_DESC_SIZE_SIZE;
> +
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR:
> +        return s->rx_desc_hard_ptr;
> +
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD:
> +        return s->rx_desc_hard_ptr_hold;
> +
> +    case PCH_GBE_MEM_RX_DESC_SOFT_PTR:
> +        return s->rx_desc_soft_ptr;
> +
> +    case PCH_GBE_MEM_TX_DESC_BASE:
> +        return s->tx_desc_base;
> +
> +    case PCH_GBE_MEM_TX_DESC_SIZE:
> +        return (s->tx_desc_size - 0x10) & PCH_GBE_MEM_TX_DESC_SIZE_SIZE;
> +
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR:
> +        return s->tx_desc_hard_ptr;
> +
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD:
> +        return s->tx_desc_hard_ptr_hold;
> +
> +    case PCH_GBE_MEM_TX_DESC_SOFT_PTR:
> +        return s->tx_desc_soft_ptr;
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI mem read 0x%"
> +                      HWADDR_PRIx "\n", addr);
> +        return -1;
> +    }
> +}
> +
> +static const MemoryRegionOps pch_gbe_mem_ops = {
> +    .read = pch_gbe_mem_read,
> +    .write = pch_gbe_mem_write,
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +/*
> + * PCI I/O Space
> + */
> +
> +enum pch_gbe_io_regs {
> +    PCH_GBE_IO_INDEX                    = 0x0,
> +#define PCH_GBE_IO_INDEX_INDEX          0x1ff
> +
> +    PCH_GBE_IO_DATA                     = 0x4,
> +};
> +
> +static void pch_gbe_io_write(void *opaque, hwaddr addr,
> +                             uint64_t val, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_IO_INDEX:
> +        s->io_index = val & PCH_GBE_IO_INDEX_INDEX;
> +        break;
> +
> +    case PCH_GBE_IO_DATA:
> +        pch_gbe_mem_write(opaque, s->io_index, val, size);
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI I/O write 0x%"
> +                      HWADDR_PRIx " = 0x%" PRIx64 "\n", addr, val);
> +    }
> +}
> +
> +static uint64_t pch_gbe_io_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_IO_INDEX:
> +        return s->io_index;
> +
> +    case PCH_GBE_IO_DATA:
> +        return pch_gbe_mem_read(opaque, s->io_index, size);
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI I/O read 0x%"
> +                      HWADDR_PRIx "\n", addr);
> +        return -1;
> +    }
> +}
> +
> +static const MemoryRegionOps pch_gbe_io_ops = {
> +    .read = pch_gbe_io_read,
> +    .write = pch_gbe_io_write,
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static void pch_gbe_realize(PCIDevice *dev, Error **errp)
> +{
> +    PCHGBEState *s = PCH_GBE(dev);
> +
> +    pci_config_set_interrupt_pin(dev->config, 1);
> +
> +    memory_region_init_io(&s->bar_io, OBJECT(s), &pch_gbe_io_ops, s,
> +                          "pch_gbe-io", 0x20);
> +    memory_region_init_io(&s->bar_mem, OBJECT(s), &pch_gbe_mem_ops, s,
> +                          "pch_gbe-mem", 0x200);
> +
> +    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io);
> +    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem);
> +
> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +
> +    s->pkt_buf = g_malloc(64 * 1024);
> +
> +    s->nic = qemu_new_nic(&pch_gbe_net_client_info, &s->conf,
> +                          object_get_typename(OBJECT(dev)), DEVICE(dev)->id, s);
> +    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
> +}
> +
> +static void pch_gbe_uninit(PCIDevice *dev)
> +{
> +    PCHGBEState *s = PCH_GBE(dev);
> +
> +    g_free(s->pkt_buf);
> +}
> +
> +static void pch_gbe_instance_init(Object *obj)
> +{
> +}
> +
> +static Property pch_gbe_properties[] = {
> +    DEFINE_NIC_PROPERTIES(PCHGBEState, conf),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void pch_gbe_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> +    k->realize = pch_gbe_realize;
> +    k->exit = pch_gbe_uninit;
> +    k->vendor_id = PCI_VENDOR_ID_INTEL;
> +    k->device_id = 0x8802;
> +    k->revision = 0x2;
> +    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
> +    dc->reset = pch_gbe_reset;
> +    dc->props = pch_gbe_properties;
> +    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
> +}
> +
> +static const TypeInfo pch_gbe_info = {
> +    .name          = TYPE_PCH_GBE,
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(PCHGBEState),
> +    .class_init    = pch_gbe_class_init,
> +    .instance_init = pch_gbe_instance_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { INTERFACE_PCIE_DEVICE },
> +        { },
> +    },
> +};
> +
> +static void pch_gbe_register_types(void)
> +{
> +    type_register_static(&pch_gbe_info);
> +}
> +type_init(pch_gbe_register_types)
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet
  2018-07-02 23:36   ` Philippe Mathieu-Daudé
@ 2018-07-03  9:46     ` Aleksandar Markovic
  2018-07-03 10:02       ` [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller Aleksandar Markovic
  2018-07-03 14:25       ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Philippe Mathieu-Daudé
  0 siblings, 2 replies; 8+ messages in thread
From: Aleksandar Markovic @ 2018-07-03  9:46 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, Paul Burton, Jason Wang
  Cc: qemu-devel, Aurelien Jarno

On 03.07.2018. 01:37, Philippe Mathieu-Daudé wrote:
> Cc'ing Jason, the "Network devices" maintainer.
>
> On 02/17/2018 04:22 PM, Paul Burton wrote:
>> This patch introduces support for emulating the ethernet controller
>> found in the Intel EG20T Platform Controller Hub, referred to as pch_gbe
>> for consistency with both Linux & U-Boot.
>>
>> Documentation for the hardware can be found here:
>>
>>   https://www.intel.com/content/www/us/en/intelligent-systems/queens-bay/platform-controller-hub-eg20t-datasheet.html
>>
>> The device is used on MIPS Boston development boards as well as the
>> Intel Crown Bay platform including devices such as the Minnowboard V1.
>>
>> Enough functionality is implemented for Linux to make use of the device,
>> and has been tested using Linux v4.16-rc1.

Acked-by: Aleksandar Markovic <AMarkovic@wavecomp.com>

We need this device to complete QEMU's MIPS Boston board support. May I
ask Jason to take a closer look at the proposed code for this network
device?

Regards,
Aleksandar

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller
  2018-07-03  9:46     ` Aleksandar Markovic
@ 2018-07-03 10:02       ` Aleksandar Markovic
  2018-07-03 14:25       ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Philippe Mathieu-Daudé
  1 sibling, 0 replies; 8+ messages in thread
From: Aleksandar Markovic @ 2018-07-03 10:02 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, Paul Burton, Jason Wang
  Cc: qemu-devel, Aurelien Jarno

> Enable CONFIG_PCH_GBE_PCI in mips64el-softmmu.mak (currently the only
> default config to enable Boston board support) and create the pch_gbe
> device when using the Boston board.
>
> This provides the board with an ethernet controller matching that found
> on real Boston boards as part of the Intel EG20T Platform Controller
> Hub, and allows standard Boston Linux kernels to have network access.
>
> This is most easily tested using the downstream linux-mti kernels at the
> moment, until MIPS support for the Linux pch_gbe driver is upstream. For
> example, presuming U-Boot's mkimage tool is present in your $PATH, this
> should be sufficient to boot Linux & see it obtain an IP address using
> the emulated pch_gbe device:
>
>   $ git clone git://git.linux-mips.org/pub/scm/linux-mti.git -b eng
>   $ cd linux-mti
>   $ make ARCH=mips 64r6el_defconfig
>   $ make ARCH=mips CROSS_COMPILE=/path/to/compiler/bin/mips-linux-gnu-
>   $ qemu-system-mips64el \
>       -M boston -cpu I6400 \
>       -kernel arch/mips/boot/vmlinux.gz.itb \
>       -serial stdio -append "ip=dhcp"
>
> Signed-off-by: Paul Burton <address@hidden>
> Cc: Aurelien Jarno <address@hidden>
> Cc: Yongbok Kim <address@hidden>
>

Reviewed-by: Aleksandar Markovic <AMarkovic@wavecomp.com>

> ---
>
>  default-configs/mips64el-softmmu.mak | 1 +
>  hw/mips/boston.c                     | 8 +++++++-
>  2 files changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/default-configs/mips64el-softmmu.mak
> b/default-configs/mips64el-softmmu.mak
> index c2ae313f47..85175ea223 100644
> --- a/default-configs/mips64el-softmmu.mak
> +++ b/default-configs/mips64el-softmmu.mak
> @@ -13,3 +13,4 @@ CONFIG_VT82C686=y
>  CONFIG_MIPS_BOSTON=y
>  CONFIG_FITLOADER=y
>  CONFIG_PCI_XILINX=y
> +CONFIG_PCH_GBE_PCI=y
> diff --git a/hw/mips/boston.c b/hw/mips/boston.c
> index fb23161b33..408977bca1 100644
> --- a/hw/mips/boston.c
> +++ b/hw/mips/boston.c
> @@ -31,6 +31,7 @@
>  #include "hw/mips/cps.h"
>  #include "hw/mips/cpudevs.h"
>  #include "hw/pci-host/xilinx-pcie.h"
> +#include "net/net.h"
>  #include "qapi/error.h"
>  #include "qemu/cutils.h"
>  #include "qemu/error-report.h"
> @@ -430,7 +431,7 @@ static void boston_mach_init(MachineState *machine)
>      MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg;
>      MemoryRegion *sys_mem = get_system_memory();
>      XilinxPCIEHost *pcie2;
> -    PCIDevice *ahci;
> +    PCIDevice *ahci, *eth;
>      DriveInfo *hd[6];
>      Chardev *chr;
>      int fw_size, fit_err;
> @@ -529,6 +530,11 @@ static void boston_mach_init(MachineState *machine)
>      ide_drive_get(hd, ahci_get_num_ports(ahci));
>      ahci_ide_create_devs(ahci, hd);
>  
> +    eth = pci_create(&PCI_BRIDGE(&pcie2->root)->sec_bus,
> +                     PCI_DEVFN(0, 1), "pch_gbe");
> +    qdev_set_nic_properties(&eth->qdev, &nd_table[0]);
> +    qdev_init_nofail(&eth->qdev);
> +
>      if (machine->firmware) {
>          fw_size = load_image_targphys(machine->firmware,
>                                        0x1fc00000, 4 * M_BYTE);
> --
> 2.16.1>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet
  2018-07-03  9:46     ` Aleksandar Markovic
  2018-07-03 10:02       ` [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller Aleksandar Markovic
@ 2018-07-03 14:25       ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 8+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-07-03 14:25 UTC (permalink / raw)
  To: Aleksandar Markovic, Paul Burton, Jason Wang, Cédric Le Goater
  Cc: qemu-devel, Aurelien Jarno, David Gibson

Hi Aleksandar,

On 07/03/2018 06:46 AM, Aleksandar Markovic wrote:
> On 03.07.2018. 01:37, Philippe Mathieu-Daudé wrote:
>> Cc'ing Jason, the "Network devices" maintainer.
>>
>> On 02/17/2018 04:22 PM, Paul Burton wrote:
>>> This patch introduces support for emulating the ethernet controller
>>> found in the Intel EG20T Platform Controller Hub, referred to as pch_gbe
>>> for consistency with both Linux & U-Boot.
>>>
>>> Documentation for the hardware can be found here:
>>>
>>>   https://www.intel.com/content/www/us/en/intelligent-systems/queens-bay/platform-controller-hub-eg20t-datasheet.html
>>>
>>> The device is used on MIPS Boston development boards as well as the
>>> Intel Crown Bay platform including devices such as the Minnowboard V1.
>>>
>>> Enough functionality is implemented for Linux to make use of the device,
>>> and has been tested using Linux v4.16-rc1.
> 
> Acked-by: Aleksandar Markovic <AMarkovic@wavecomp.com>
> 
> We need this device to complete QEMU's MIPS Boston board support. May I

I tried to test this device using the 'eng' branch of
git://git.linux-mips.org/pub/scm/linux-mti.git and using the
'64r2el_defconfig' config but I get:

qemu-system-mips64el: Unable to copy device tree in memory
unable to load FIT image

I noticed the PPC machines use the _FDT() macro (see "hw/ppc/fdt.h") to
eventually give more useful error message.

> ask Jason to take a closer look at the proposed code for this network
> device?
> 
> Regards,
> Aleksandar
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet
  2018-02-17 19:22 ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Paul Burton
  2018-07-02 23:36   ` Philippe Mathieu-Daudé
@ 2018-07-04  7:24   ` Jason Wang
  1 sibling, 0 replies; 8+ messages in thread
From: Jason Wang @ 2018-07-04  7:24 UTC (permalink / raw)
  To: Paul Burton, qemu-devel; +Cc: Yongbok Kim, Aurelien Jarno



On 2018年02月18日 03:22, Paul Burton wrote:
> This patch introduces support for emulating the ethernet controller
> found in the Intel EG20T Platform Controller Hub, referred to as pch_gbe
> for consistency with both Linux & U-Boot.
>
> Documentation for the hardware can be found here:
>
>    https://www.intel.com/content/www/us/en/intelligent-systems/queens-bay/platform-controller-hub-eg20t-datasheet.html
>
> The device is used on MIPS Boston development boards as well as the
> Intel Crown Bay platform including devices such as the Minnowboard V1.
>
> Enough functionality is implemented for Linux to make use of the device,
> and has been tested using Linux v4.16-rc1.
>
> Signed-off-by: Paul Burton <paul.burton@mips.com>
> Cc: Aurelien Jarno <aurelien@aurel32.net>
> Cc: Yongbok Kim <yongbok.kim@mips.com>
> ---

Looks good overall. Two questions:

1) Sending new NIC emulation codes implies you need to maintain it in 
the future. We don't want to have a unmaintained one. Please send a 
patch to MAINTAINER.
2) I don't see migration codes, (there's even one for mipsnet). Please 
either add migration support or block migration for pch_gbe.

And some comments inline.

>   hw/net/Makefile.objs |   1 +
>   hw/net/pch_gbe.c     | 766 +++++++++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 767 insertions(+)
>   create mode 100644 hw/net/pch_gbe.c
>
> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
> index ab22968641..08706d9a96 100644
> --- a/hw/net/Makefile.objs
> +++ b/hw/net/Makefile.objs
> @@ -12,6 +12,7 @@ common-obj-$(CONFIG_E1000E_PCI) += e1000e.o e1000e_core.o e1000x_common.o
>   common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
>   common-obj-$(CONFIG_VMXNET3_PCI) += net_tx_pkt.o net_rx_pkt.o
>   common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o
> +common-obj-$(CONFIG_PCH_GBE_PCI) += pch_gbe.o
>   
>   common-obj-$(CONFIG_SMC91C111) += smc91c111.o
>   common-obj-$(CONFIG_LAN9118) += lan9118.o
> diff --git a/hw/net/pch_gbe.c b/hw/net/pch_gbe.c
> new file mode 100644
> index 0000000000..be9a9f5916
> --- /dev/null
> +++ b/hw/net/pch_gbe.c
> @@ -0,0 +1,766 @@
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "hw/net/mii.h"
> +#include "hw/pci/pci.h"
> +#include "net/checksum.h"
> +#include "net/eth.h"
> +#include "net/net.h"
> +#include "qemu/bitops.h"
> +#include "qemu/log.h"
> +
> +#define TYPE_PCH_GBE    "pch_gbe"
> +#define PCH_GBE(obj)    OBJECT_CHECK(PCHGBEState, (obj), TYPE_PCH_GBE)
> +
> +#define PCH_GBE_INTR_RX_DMA_CMPLT       BIT(0)
> +#define PCH_GBE_INTR_RX_VALID           BIT(1)
> +#define PCH_GBE_INTR_RX_FRAME_ERR       BIT(2)
> +#define PCH_GBE_INTR_RX_FIFO_ERR        BIT(3)
> +#define PCH_GBE_INTR_RX_DMA_ERR         BIT(4)
> +#define PCH_GBE_INTR_RX_DSC_EMP         BIT(5)
> +#define PCH_GBE_INTR_TX_CMPLT           BIT(8)
> +#define PCH_GBE_INTR_TX_DMA_CMPLT       BIT(9)
> +#define PCH_GBE_INTR_TX_FIFO_ERR        BIT(10)
> +#define PCH_GBE_INTR_TX_DMA_ERR         BIT(11)
> +#define PCH_GBE_INTR_PAUSE_CMPLT        BIT(12)
> +#define PCH_GBE_INTR_MIIM_CMPLT         BIT(16)
> +#define PCH_GBE_INTR_PHY_INT            BIT(20)
> +#define PCH_GBE_INTR_WOL_DET            BIT(24)
> +#define PCH_GBE_INTR_TCPIP_ERR          BIT(28)
> +#define PCH_GBE_INTR_ALL (              \
> +        PCH_GBE_INTR_RX_DMA_CMPLT |     \
> +        PCH_GBE_INTR_RX_VALID |         \
> +        PCH_GBE_INTR_RX_FRAME_ERR |     \
> +        PCH_GBE_INTR_RX_FIFO_ERR |      \
> +        PCH_GBE_INTR_RX_DMA_ERR |       \
> +        PCH_GBE_INTR_RX_DSC_EMP |       \
> +        PCH_GBE_INTR_TX_CMPLT |         \
> +        PCH_GBE_INTR_TX_DMA_CMPLT |     \
> +        PCH_GBE_INTR_TX_FIFO_ERR |      \
> +        PCH_GBE_INTR_TX_DMA_ERR |       \
> +        PCH_GBE_INTR_PAUSE_CMPLT |      \
> +        PCH_GBE_INTR_MIIM_CMPLT |       \
> +        PCH_GBE_INTR_PHY_INT |          \
> +        PCH_GBE_INTR_WOL_DET |          \
> +        PCH_GBE_INTR_TCPIP_ERR)
> +
> +struct pch_gbe_tx_desc {
> +    uint32_t addr;
> +
> +    uint32_t len;
> +#define PCH_GBE_TX_LENGTH               0xffff
> +
> +    uint32_t control;
> +#define PCH_GBE_TX_CONTROL_EOB          0x3
> +#define PCH_GBE_TX_CONTROL_WORDS        0xfffc
> +#define PCH_GBE_TX_CONTROL_APAD         BIT(16)
> +#define PCH_GBE_TX_CONTROL_ICRC         BIT(17)
> +#define PCH_GBE_TX_CONTROL_ITAG         BIT(18)
> +#define PCH_GBE_TX_CONTROL_ACCOFF       BIT(19)
> +
> +    uint32_t status;
> +#define PCH_GBE_TX_STATUS_TSHRT         BIT(22)
> +#define PCH_GBE_TX_STATUS_TLNG          BIT(23)
> +#define PCH_GBE_TX_STATUS_ABT           BIT(28)
> +#define PCH_GBE_TX_STATUS_CMPLT         BIT(29)
> +};
> +
> +struct pch_gbe_rx_desc {
> +    uint32_t addr;
> +
> +    uint32_t acc_status;
> +
> +    uint32_t mac_status;
> +#define PCH_GBE_RX_MAC_STATUS_EOB       0x3
> +#define PCH_GBE_RX_MAC_STATUS_WORDS     0xfffc
> +#define PCH_GBE_RX_MAC_STATUS_LENGTH    0xffff
> +#define PCH_GBE_RX_MAC_STATUS_TSHRT     BIT(19)
> +#define PCH_GBE_RX_MAC_STATUS_TLNG      BIT(20)
> +
> +    uint32_t dma_status;
> +};
> +
> +typedef struct {
> +    /*< private >*/
> +    PCIDevice parent_obj;
> +    /*< public >*/
> +
> +    NICState *nic;
> +    NICConf conf;
> +
> +    bool reset;
> +    bool phy_reset;
> +
> +    bool link;
> +
> +    uint32_t intr_status;
> +    uint32_t intr_status_hold;
> +    uint32_t intr_enable;
> +
> +    uint16_t addr_mask;
> +
> +    bool rx_enable;
> +    bool rx_dma_enable;
> +    bool rx_acc_enable;
> +    bool rx_acc_csum_off;
> +    uint32_t rx_desc_base;
> +    uint32_t rx_desc_size;
> +    uint32_t rx_desc_hard_ptr;
> +    uint32_t rx_desc_hard_ptr_hold;
> +    uint32_t rx_desc_soft_ptr;
> +
> +    bool tx_dma_enable;
> +    bool tx_acc_enable;
> +    uint32_t tx_desc_base;
> +    uint32_t tx_desc_size;
> +    uint32_t tx_desc_hard_ptr;
> +    uint32_t tx_desc_hard_ptr_hold;
> +    uint32_t tx_desc_soft_ptr;
> +
> +    uint8_t miim_phy_addr;
> +    uint8_t miim_reg_addr;
> +    uint16_t miim_data;
> +
> +    MemoryRegion bar_mem;
> +    MemoryRegion bar_io;
> +    uint16_t io_index;
> +
> +    uint8_t *pkt_buf;
> +} PCHGBEState;
> +
> +static void pch_gbe_update_irq(PCHGBEState *s)
> +{
> +    PCIDevice *d = PCI_DEVICE(s);
> +
> +    pci_set_irq(d, !!(s->intr_status & s->intr_enable));
> +}
> +
> +static void pch_gbe_set_intr(PCHGBEState *s, uint32_t intr)
> +{
> +    s->intr_status |= intr;
> +    pch_gbe_update_irq(s);
> +}
> +
> +static void pch_gbe_tx(PCHGBEState *s)
> +{
> +    struct pch_gbe_tx_desc desc;
> +    dma_addr_t addr, len, pad;
> +    uint32_t ctl, sts;
> +
> +    if (!s->tx_dma_enable) {
> +        return;
> +    }
> +
> +    while (s->tx_desc_hard_ptr != s->tx_desc_soft_ptr) {
> +        if ((s->tx_desc_hard_ptr & 0xf) ||
> +            (s->tx_desc_hard_ptr < s->tx_desc_base) ||
> +            (s->tx_desc_hard_ptr >= (s->tx_desc_base + s->tx_desc_size))) {
> +            pch_gbe_set_intr(s, PCH_GBE_INTR_TX_DMA_ERR);
> +            break;
> +        }

It looks to me we can move those checks out of the loop.

> +
> +        pci_dma_read(PCI_DEVICE(s), s->tx_desc_hard_ptr, &desc, sizeof(desc));
> +
> +        ctl = le32_to_cpu(desc.control);
> +        addr = le32_to_cpu(desc.addr);
> +        len = le32_to_cpu(desc.len) & PCH_GBE_TX_LENGTH;
> +        pad = s->tx_acc_enable ? 2 : 0;
> +
> +        pci_dma_read(PCI_DEVICE(s), addr, s->pkt_buf, len + pad);

So pkt_buf is 65536, when tx_acc_enable is true we may end up with 65535 
+ 2 > 65536?

> +
> +        if (pad && (len >= 14)) {
> +            memcpy(s->pkt_buf + 14, s->pkt_buf + 16, len - 14);

Another chance of OOB access? (e.g len is PCH_GBE_TX_LENGTH). Can we 
avoid such memcpy()?

> +        }
> +
> +        if ((ctl & PCH_GBE_TX_CONTROL_APAD) && (len < 64)) {
> +            memset(s->pkt_buf + len, 0, 64 - len);
> +            len = 64;
> +        }
> +
> +        if (s->tx_acc_enable &&
> +            !(ctl & (PCH_GBE_TX_CONTROL_ICRC | PCH_GBE_TX_CONTROL_ACCOFF))) {
> +                net_checksum_calculate(s->pkt_buf, len);
> +        }
> +
> +        qemu_send_packet(qemu_get_queue(s->nic), s->pkt_buf, len);
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_TX_DMA_CMPLT);
> +
> +        sts = PCH_GBE_TX_STATUS_CMPLT;
> +        desc.status = cpu_to_le32(sts);
> +        pci_dma_write(PCI_DEVICE(s), s->tx_desc_hard_ptr, &desc, sizeof(desc));
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_TX_CMPLT);
> +
> +        s->tx_desc_hard_ptr += sizeof(desc);
> +        if (s->tx_desc_hard_ptr >= (s->tx_desc_base + s->tx_desc_size)) {
> +            s->tx_desc_hard_ptr = s->tx_desc_base;
> +        }
> +    }
> +}
> +
> +static ssize_t pch_gbe_receive(NetClientState *nc,
> +                               const uint8_t *buf, size_t len)
> +{
> +    PCHGBEState *s = qemu_get_nic_opaque(nc);
> +    struct pch_gbe_rx_desc desc;
> +    uint32_t mac_status;
> +    dma_addr_t addr;
> +
> +    if (s->reset || !s->link || !s->rx_enable || !s->rx_dma_enable) {
> +        return -1;
> +    }
> +
> +    if (s->rx_desc_hard_ptr == s->rx_desc_soft_ptr) {
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_DSC_EMP);
> +        return -1;
> +    }
> +
> +    pci_dma_read(PCI_DEVICE(s), s->rx_desc_hard_ptr, &desc, sizeof(desc));
> +    addr = le32_to_cpu(desc.addr);
> +
> +    if (len < 1519) {
> +        memcpy(s->pkt_buf, buf, len);

Why not DMA to guest directly here?

> +
> +        /* Add an empty FCS */
> +        memset(&s->pkt_buf[len], 0, 4);
> +        len += 4;
> +
> +        pci_dma_write(PCI_DEVICE(s), addr, s->pkt_buf, len);
> +
> +        mac_status = (len + 3) & PCH_GBE_RX_MAC_STATUS_EOB;
> +        mac_status |= (len + 3) & PCH_GBE_RX_MAC_STATUS_WORDS;
> +
> +        /*
> +         * Unsure why this is required, but the Linux driver subtracts 4 from
> +         * the length if bit 1 of rx_eob is set. We add 4 here to compensate.
> +         */
> +        if (mac_status & BIT(1)) {
> +            mac_status = (mac_status + 4) & PCH_GBE_RX_MAC_STATUS_LENGTH;
> +        }
> +
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_DMA_CMPLT);
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_VALID);
> +    } else {
> +        mac_status = PCH_GBE_RX_MAC_STATUS_TLNG;
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_RX_FRAME_ERR);
> +    }
> +
> +    desc.acc_status = 0;
> +    desc.mac_status = cpu_to_le32(mac_status);
> +    desc.dma_status = 0;
> +    pci_dma_write(PCI_DEVICE(s), s->rx_desc_hard_ptr, &desc, sizeof(desc));
> +
> +    s->rx_desc_hard_ptr += sizeof(desc);
> +    if (s->rx_desc_hard_ptr >= (s->rx_desc_base + s->rx_desc_size)) {
> +        s->rx_desc_hard_ptr = s->rx_desc_base;
> +    }
> +
> +    return len;
> +}
> +
> +static int pch_gbe_can_receive(NetClientState *nc)
> +{
> +    PCHGBEState *s = qemu_get_nic_opaque(nc);
> +
> +    return s->rx_desc_hard_ptr != s->rx_desc_soft_ptr;
> +}
> +
> +static void pch_gbe_set_link_status(NetClientState *nc)
> +{
> +    PCHGBEState *s = qemu_get_nic_opaque(nc);
> +
> +    s->link = !nc->link_down;

So the link status were reported by MII?

> +}
> +
> +static NetClientInfo pch_gbe_net_client_info = {
> +    .type = NET_CLIENT_DRIVER_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = pch_gbe_can_receive,
> +    .receive = pch_gbe_receive,
> +    .link_status_changed = pch_gbe_set_link_status,
> +};
> +
> +static void pch_gbe_reset(DeviceState *d)
> +{
> +    PCHGBEState *s = PCH_GBE(d);
> +
> +    s->io_index = 0;
> +
> +    s->intr_status = 0;
> +    s->intr_status_hold = 0;
> +    s->intr_enable = 0;
> +    pch_gbe_update_irq(s);
> +
> +    pch_gbe_set_link_status(qemu_get_queue(s->nic));
> +}
> +
> +/*
> + * PHY registers
> + */
> +
> +static void pch_gbe_phy_write(PCHGBEState *s, uint8_t addr, uint16_t val)
> +{
> +    switch (addr) {
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PHY write 0x%x = 0x%x\n",
> +                      addr, val);
> +    }
> +}
> +
> +static uint16_t pch_gbe_phy_read(PCHGBEState *s, uint8_t addr)
> +{
> +    switch (addr) {
> +    case MII_BMCR:
> +        return MII_BMCR_SPEED1000 | MII_BMCR_FD;
> +
> +    case MII_BMSR:
> +        return MII_BMSR_100TX_FD | MII_BMSR_AN_COMP |
> +               (s->link ? MII_BMSR_LINK_ST : 0);
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PHY read 0x%x\n", addr);
> +    }
> +    return 0;
> +}
> +
> +/*
> + * PCI Memory Mapped I/O Space
> + */
> +
> +enum pch_gbe_mem_regs {
> +    PCH_GBE_MEM_INTR                    = 0x000,
> +    PCH_GBE_MEM_INTR_EN                 = 0x004,
> +    PCH_GBE_MEM_INTR_HOLD               = 0x018,
> +
> +    PCH_GBE_MEM_RESET                   = 0x00c,
> +#define PCH_GBE_MEM_RESET_ALL           BIT(31)
> +#define PCH_GBE_MEM_RESET_TX            BIT(15)
> +#define PCH_GBE_MEM_RESET_RX            BIT(14)
> +
> +    PCH_GBE_MEM_TCPIPACC                = 0x010,
> +#define PCH_GBE_MEM_TCPIPACC_RXEN       BIT(0)
> +#define PCH_GBE_MEM_TCPIPACC_TXEN       BIT(1)
> +#define PCH_GBE_MEM_TCPIPACC_RXSUMOFF   BIT(2)
> +
> +    PCH_GBE_MEM_MAX_RXEN                = 0x020,
> +#define PCH_GBE_MEM_MAX_RXEN_EN         BIT(0)
> +
> +    PCH_GBE_MEM_MAC_ADDR_1A             = 0x060,
> +    PCH_GBE_MEM_MAC_ADDR_1B             = 0x064,
> +
> +    PCH_GBE_MEM_ADDR_MASK               = 0x0e0,
> +#define PCH_GBE_MEM_ADDR_MASK_MAC0      BIT(0)
> +#define PCH_GBE_MEM_ADDR_MASK_BUSY      BIT(31)
> +
> +    PCH_GBE_MEM_MIIM                    = 0x0e4,
> +#define PCH_GBE_MEM_MIIM_READY          BIT(26)
> +#define PCH_GBE_MEM_MIIM_WRITE          BIT(26)
> +#define PCH_GBE_MEM_MIIM_PHY_ADDR_SHF   21
> +#define PCH_GBE_MEM_MIIM_PHY_ADDR_MSK   (0x1f << 21)
> +#define PCH_GBE_MEM_MIIM_REG_ADDR_SHF   16
> +#define PCH_GBE_MEM_MIIM_REG_ADDR_MSK   (0x1f << 16)
> +#define PCH_GBE_MEM_MIIM_DATA           0xffff
> +
> +    PCH_GBE_MEM_RGMII_STATUS            = 0x0ec,
> +#define PCH_GBE_MEM_RGMII_STATUS_FDPLX  BIT(0)
> +#define PCH_GBE_MEM_RGMII_STATUS_UP     BIT(3)
> +
> +    PCH_GBE_MEM_DMA_CONTROL             = 0x100,
> +#define PCH_GBE_MEM_DMA_CONTROL_TX_EN   BIT(0)
> +#define PCH_GBE_MEM_DMA_CONTROL_RX_EN   BIT(1)
> +
> +    PCH_GBE_MEM_RX_DESC_BASE            = 0x110,
> +
> +    PCH_GBE_MEM_RX_DESC_SIZE            = 0x114,
> +#define PCH_GBE_MEM_RX_DESC_SIZE_SIZE   0xfff0
> +
> +    PCH_GBE_MEM_RX_DESC_HARD_PTR        = 0x118,
> +    PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD   = 0x11c,
> +    PCH_GBE_MEM_RX_DESC_SOFT_PTR        = 0x120,
> +
> +    PCH_GBE_MEM_TX_DESC_BASE            = 0x130,
> +
> +    PCH_GBE_MEM_TX_DESC_SIZE            = 0x134,
> +#define PCH_GBE_MEM_TX_DESC_SIZE_SIZE   0xfff0
> +
> +    PCH_GBE_MEM_TX_DESC_HARD_PTR        = 0x138,
> +    PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD   = 0x13c,
> +    PCH_GBE_MEM_TX_DESC_SOFT_PTR        = 0x140,
> +
> +    PCH_GBE_MEM_SRST                    = 0x1fc,
> +#define PCH_GBE_MEM_SRST_SRST           BIT(0)
> +};
> +
> +static void pch_gbe_mem_write(void *opaque, hwaddr addr,
> +                              uint64_t val, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_MEM_INTR:
> +    case PCH_GBE_MEM_INTR_HOLD:
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD:
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD:
> +        /* read-only */
> +        break;
> +
> +    case PCH_GBE_MEM_INTR_EN:
> +        s->intr_enable = val & PCH_GBE_INTR_ALL;
> +        pch_gbe_update_irq(s);
> +        break;
> +
> +    case PCH_GBE_MEM_RESET:
> +        s->reset = !!(val & PCH_GBE_MEM_RESET_ALL);
> +        if (s->reset) {
> +            pch_gbe_reset(DEVICE(s));
> +            s->reset = false;
> +            break;
> +        }
> +        if (val & PCH_GBE_MEM_RESET_TX) {
> +            qemu_log_mask(LOG_UNIMP,
> +                          "pch_gbe: Partial (TX) reset unimplemented\n");
> +        }
> +        if (val & PCH_GBE_MEM_RESET_RX) {
> +            qemu_log_mask(LOG_UNIMP,
> +                          "pch_gbe: Partial (RX) reset unimplemented\n");
> +        }
> +        break;
> +
> +    case PCH_GBE_MEM_TCPIPACC:
> +        s->rx_acc_enable = !!(val & PCH_GBE_MEM_TCPIPACC_RXEN);
> +        s->tx_acc_enable = !!(val & PCH_GBE_MEM_TCPIPACC_TXEN);
> +        s->rx_acc_csum_off = !!(val & PCH_GBE_MEM_TCPIPACC_RXSUMOFF);
> +        if (s->rx_acc_enable) {
> +            qemu_log_mask(LOG_UNIMP,
> +                          "pch_gbe: RX acceleration unimplemented\n");
> +        }
> +        break;
> +
> +    case PCH_GBE_MEM_MAX_RXEN:
> +        s->rx_enable = !!(val & PCH_GBE_MEM_MAX_RXEN_EN);
> +        break;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1A:
> +        s->conf.macaddr.a[0] = (val >> 0);
> +        s->conf.macaddr.a[1] = (val >> 8);
> +        s->conf.macaddr.a[2] = (val >> 16);
> +        s->conf.macaddr.a[3] = (val >> 24);
> +        break;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1B:
> +        s->conf.macaddr.a[4] = (val >> 0);
> +        s->conf.macaddr.a[5] = (val >> 8);
> +        break;
> +
> +    case PCH_GBE_MEM_ADDR_MASK:
> +        s->addr_mask = val & PCH_GBE_MEM_ADDR_MASK_MAC0;
> +        break;
> +
> +    case PCH_GBE_MEM_MIIM:
> +        s->miim_phy_addr = (val & PCH_GBE_MEM_MIIM_PHY_ADDR_MSK)
> +                         >> PCH_GBE_MEM_MIIM_PHY_ADDR_SHF;
> +        s->miim_reg_addr = (val & PCH_GBE_MEM_MIIM_REG_ADDR_MSK)
> +                         >> PCH_GBE_MEM_MIIM_REG_ADDR_SHF;
> +        s->miim_data = val & PCH_GBE_MEM_MIIM_DATA;
> +        if (s->miim_phy_addr == 1) {
> +            if (val & PCH_GBE_MEM_MIIM_WRITE) {
> +                pch_gbe_phy_write(s, s->miim_reg_addr, s->miim_data);
> +            } else {
> +                s->miim_data = pch_gbe_phy_read(s, s->miim_reg_addr);
> +            }
> +        } else if (!(val & PCH_GBE_MEM_MIIM_WRITE)) {
> +            s->miim_data = PCH_GBE_MEM_MIIM_DATA;
> +        }
> +        pch_gbe_set_intr(s, PCH_GBE_INTR_MIIM_CMPLT);
> +        break;
> +
> +    case PCH_GBE_MEM_DMA_CONTROL:
> +        s->rx_dma_enable = !!(val & PCH_GBE_MEM_DMA_CONTROL_RX_EN);
> +        s->tx_dma_enable = !!(val & PCH_GBE_MEM_DMA_CONTROL_TX_EN);
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_BASE:
> +        s->rx_desc_base = val;
> +        s->rx_desc_hard_ptr = s->rx_desc_base;
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_SIZE:
> +        s->rx_desc_size = (val & PCH_GBE_MEM_RX_DESC_SIZE_SIZE) + 0x10;
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR:
> +        s->rx_desc_hard_ptr = val;
> +        break;
> +
> +    case PCH_GBE_MEM_RX_DESC_SOFT_PTR:
> +        s->rx_desc_soft_ptr = val;

You need call qemu_flush_queued_packets() to flush pending packets in 
the queue here.

> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_BASE:
> +        s->tx_desc_base = val;
> +        s->tx_desc_hard_ptr = s->tx_desc_base;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_SIZE:
> +        s->tx_desc_size = (val & PCH_GBE_MEM_TX_DESC_SIZE_SIZE) + 0x10;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR:
> +        s->tx_desc_hard_ptr = val;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_TX_DESC_SOFT_PTR:
> +        s->tx_desc_soft_ptr = val;
> +        pch_gbe_tx(s);
> +        break;
> +
> +    case PCH_GBE_MEM_SRST:
> +        s->reset = val & PCH_GBE_MEM_SRST_SRST;
> +        if (s->reset) {
> +            pch_gbe_reset(DEVICE(s));
> +        }
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI mem write 0x%"
> +                      HWADDR_PRIx " = 0x%" PRIx64 "\n", addr, val);
> +    }
> +}
> +
> +static uint64_t pch_gbe_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_MEM_INTR:
> +        s->rx_desc_hard_ptr_hold = s->rx_desc_hard_ptr;
> +        s->tx_desc_hard_ptr_hold = s->tx_desc_hard_ptr;
> +        s->intr_status_hold = s->intr_status;
> +        s->intr_status = 0;
> +        pch_gbe_update_irq(s);
> +    case PCH_GBE_MEM_INTR_HOLD:
> +        return s->intr_status_hold;
> +
> +    case PCH_GBE_MEM_INTR_EN:
> +        return s->intr_enable;
> +
> +    case PCH_GBE_MEM_RESET:
> +        return 0;
> +
> +    case PCH_GBE_MEM_TCPIPACC:
> +        return (s->rx_acc_enable ? PCH_GBE_MEM_TCPIPACC_RXEN : 0) |
> +               (s->tx_acc_enable ? PCH_GBE_MEM_TCPIPACC_TXEN : 0) |
> +               (s->rx_acc_csum_off ? PCH_GBE_MEM_TCPIPACC_RXSUMOFF : 0);
> +
> +    case PCH_GBE_MEM_MAX_RXEN:
> +        return s->rx_enable ? PCH_GBE_MEM_MAX_RXEN_EN : 0;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1A:
> +        return s->conf.macaddr.a[0] << 0 |
> +               s->conf.macaddr.a[1] << 8 |
> +               s->conf.macaddr.a[2] << 16 |
> +               s->conf.macaddr.a[3] << 24;
> +
> +    case PCH_GBE_MEM_MAC_ADDR_1B:
> +        return s->conf.macaddr.a[4] << 0 |
> +               s->conf.macaddr.a[5] << 8;
> +
> +    case PCH_GBE_MEM_ADDR_MASK:
> +        return s->addr_mask;
> +
> +    case PCH_GBE_MEM_MIIM:
> +        return PCH_GBE_MEM_MIIM_READY |
> +            (s->miim_phy_addr << PCH_GBE_MEM_MIIM_PHY_ADDR_SHF) |
> +            (s->miim_reg_addr << PCH_GBE_MEM_MIIM_REG_ADDR_SHF) |
> +            s->miim_data;
> +
> +    case PCH_GBE_MEM_SRST:
> +        return s->reset ? PCH_GBE_MEM_SRST_SRST : 0;
> +
> +    case PCH_GBE_MEM_RGMII_STATUS:
> +        return (s->link ? PCH_GBE_MEM_RGMII_STATUS_UP : 0) |
> +               PCH_GBE_MEM_RGMII_STATUS_FDPLX;
> +
> +    case PCH_GBE_MEM_DMA_CONTROL:
> +        return (s->rx_dma_enable ? PCH_GBE_MEM_DMA_CONTROL_RX_EN : 0) |
> +               (s->tx_dma_enable ? PCH_GBE_MEM_DMA_CONTROL_TX_EN : 0);
> +
> +    case PCH_GBE_MEM_RX_DESC_BASE:
> +        return s->rx_desc_base;
> +
> +    case PCH_GBE_MEM_RX_DESC_SIZE:
> +        return (s->rx_desc_size - 0x10) & PCH_GBE_MEM_RX_DESC_SIZE_SIZE;
> +
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR:
> +        return s->rx_desc_hard_ptr;
> +
> +    case PCH_GBE_MEM_RX_DESC_HARD_PTR_HOLD:
> +        return s->rx_desc_hard_ptr_hold;
> +
> +    case PCH_GBE_MEM_RX_DESC_SOFT_PTR:
> +        return s->rx_desc_soft_ptr;
> +
> +    case PCH_GBE_MEM_TX_DESC_BASE:
> +        return s->tx_desc_base;
> +
> +    case PCH_GBE_MEM_TX_DESC_SIZE:
> +        return (s->tx_desc_size - 0x10) & PCH_GBE_MEM_TX_DESC_SIZE_SIZE;
> +
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR:
> +        return s->tx_desc_hard_ptr;
> +
> +    case PCH_GBE_MEM_TX_DESC_HARD_PTR_HOLD:
> +        return s->tx_desc_hard_ptr_hold;
> +
> +    case PCH_GBE_MEM_TX_DESC_SOFT_PTR:
> +        return s->tx_desc_soft_ptr;
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI mem read 0x%"
> +                      HWADDR_PRIx "\n", addr);
> +        return -1;
> +    }
> +}
> +
> +static const MemoryRegionOps pch_gbe_mem_ops = {
> +    .read = pch_gbe_mem_read,
> +    .write = pch_gbe_mem_write,
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +/*
> + * PCI I/O Space
> + */
> +
> +enum pch_gbe_io_regs {
> +    PCH_GBE_IO_INDEX                    = 0x0,
> +#define PCH_GBE_IO_INDEX_INDEX          0x1ff
> +
> +    PCH_GBE_IO_DATA                     = 0x4,
> +};
> +
> +static void pch_gbe_io_write(void *opaque, hwaddr addr,
> +                             uint64_t val, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_IO_INDEX:
> +        s->io_index = val & PCH_GBE_IO_INDEX_INDEX;
> +        break;
> +
> +    case PCH_GBE_IO_DATA:
> +        pch_gbe_mem_write(opaque, s->io_index, val, size);
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI I/O write 0x%"
> +                      HWADDR_PRIx " = 0x%" PRIx64 "\n", addr, val);
> +    }
> +}
> +
> +static uint64_t pch_gbe_io_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PCHGBEState *s = PCH_GBE(opaque);
> +
> +    switch (addr) {
> +    case PCH_GBE_IO_INDEX:
> +        return s->io_index;
> +
> +    case PCH_GBE_IO_DATA:
> +        return pch_gbe_mem_read(opaque, s->io_index, size);
> +
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "pch_gbe: Unhandled PCI I/O read 0x%"
> +                      HWADDR_PRIx "\n", addr);
> +        return -1;
> +    }
> +}
> +
> +static const MemoryRegionOps pch_gbe_io_ops = {
> +    .read = pch_gbe_io_read,
> +    .write = pch_gbe_io_write,
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static void pch_gbe_realize(PCIDevice *dev, Error **errp)
> +{
> +    PCHGBEState *s = PCH_GBE(dev);
> +
> +    pci_config_set_interrupt_pin(dev->config, 1);
> +
> +    memory_region_init_io(&s->bar_io, OBJECT(s), &pch_gbe_io_ops, s,
> +                          "pch_gbe-io", 0x20);
> +    memory_region_init_io(&s->bar_mem, OBJECT(s), &pch_gbe_mem_ops, s,
> +                          "pch_gbe-mem", 0x200);
> +
> +    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io);
> +    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem);
> +
> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +
> +    s->pkt_buf = g_malloc(64 * 1024);
> +
> +    s->nic = qemu_new_nic(&pch_gbe_net_client_info, &s->conf,
> +                          object_get_typename(OBJECT(dev)), DEVICE(dev)->id, s);
> +    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
> +}
> +
> +static void pch_gbe_uninit(PCIDevice *dev)
> +{
> +    PCHGBEState *s = PCH_GBE(dev);
> +
> +    g_free(s->pkt_buf);
> +}
> +
> +static void pch_gbe_instance_init(Object *obj)
> +{

Is PXE supported? If yes, may want to add boot index here.

> +}
> +
> +static Property pch_gbe_properties[] = {
> +    DEFINE_NIC_PROPERTIES(PCHGBEState, conf),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void pch_gbe_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> +    k->realize = pch_gbe_realize;
> +    k->exit = pch_gbe_uninit;
> +    k->vendor_id = PCI_VENDOR_ID_INTEL;
> +    k->device_id = 0x8802;
> +    k->revision = 0x2;
> +    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
> +    dc->reset = pch_gbe_reset;
> +    dc->props = pch_gbe_properties;
> +    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
> +}
> +
> +static const TypeInfo pch_gbe_info = {
> +    .name          = TYPE_PCH_GBE,
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(PCHGBEState),
> +    .class_init    = pch_gbe_class_init,
> +    .instance_init = pch_gbe_instance_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { INTERFACE_PCIE_DEVICE },
> +        { },
> +    },
> +};
> +
> +static void pch_gbe_register_types(void)
> +{
> +    type_register_static(&pch_gbe_info);
> +}
> +type_init(pch_gbe_register_types)

Thanks

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2018-07-04  7:24 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-17 19:22 [Qemu-devel] [PATCH 0/2] MIPS Boston / pch_gbe ethernet support Paul Burton
2018-02-17 19:22 ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Paul Burton
2018-07-02 23:36   ` Philippe Mathieu-Daudé
2018-07-03  9:46     ` Aleksandar Markovic
2018-07-03 10:02       ` [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller Aleksandar Markovic
2018-07-03 14:25       ` [Qemu-devel] [PATCH 1/2] hw/net: Add support for Intel pch_gbe ethernet Philippe Mathieu-Daudé
2018-07-04  7:24   ` Jason Wang
2018-02-17 19:22 ` [Qemu-devel] [PATCH 2/2] hw/mips/boston: Enable pch_gbe ethernet controller Paul Burton

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.