Message ID | 20180217192215.28581-2-paul.burton@mips.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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) >
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
> 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(ð->qdev, &nd_table[0]); > + qdev_init_nofail(ð->qdev); > + > if (machine->firmware) { > fw_size = load_image_targphys(machine->firmware, > 0x1fc00000, 4 * M_BYTE); > -- > 2.16.1>
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 >
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
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)
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