qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 8/9] Add xgmac ethernet model


From: Peter Maydell
Subject: Re: [Qemu-devel] [PATCH 8/9] Add xgmac ethernet model
Date: Tue, 20 Dec 2011 20:24:42 +0000

On 20 December 2011 19:15, Mark Langsdorf <address@hidden> wrote:
> This adds very basic support for xgmac block. Missing things include:
>
> - statistics counters
> - WoL support
> - rx checksum offload
> - chained descriptors (only linear descriptor ring)
> - broadcast and multicast handling

So, er, what's an xgmac? Any public documentation? Are you planning
to submit a board model that uses this?

Missing save/load support.

> Signed-off-by: Rob Herring <address@hidden>
> Signed-off-by: Mark Langsdorf <address@hidden>
> ---
>  Makefile.target |    1 +
>  hw/xgmac.c      |  409
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++

Your mailer's linewrapping has damaged this patch too.

>  2 files changed, 410 insertions(+), 0 deletions(-)
>  create mode 100644 hw/xgmac.c
>
> diff --git a/Makefile.target b/Makefile.target
> index e4132d6..85f00a4 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -339,6 +339,7 @@ obj-arm-y += realview_gic.o realview.o arm_sysctl.o
> arm11mpcore.o a9mpcore.o arm
>  obj-arm-y += arm_mptimer.o
>  obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
>  obj-arm-y += pl061.o
> +obj-arm-y += xgmac.o
>  obj-arm-y += arm-semi.o
>  obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o
> pxa2xx_dma.o
>  obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
> diff --git a/hw/xgmac.c b/hw/xgmac.c
> new file mode 100644
> index 0000000..621be5d
> --- /dev/null
> +++ b/hw/xgmac.c
> @@ -0,0 +1,409 @@
> +/*
> + * QEMU model of XGMAC Ethernet.
> + *
> + * derived from the Xilinx AXI-Ethernet by Edgar E. Iglesias.
> + *
> + * Copyright (c) 2011 Calxeda, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> copy
> + * of this software and associated documentation files (the "Software"), to
> deal
> + * in the Software without restriction, including without limitation the
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
> sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included
> in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "qemu-char.h"
> +#include "qemu-log.h"
> +#include "net.h"
> +#include "net/checksum.h"
> +
> +#define XGMAC_CONTROL                 0x00000000        /* MAC
> Configuration */
> +#define XGMAC_FRAME_FILTER            0x00000001        /* MAC Frame Filter
> */
> +#define XGMAC_FLOW_CTRL               0x00000006        /* MAC Flow Control
> */
> +#define XGMAC_VLAN_TAG                0x00000007        /* VLAN Tags */
> +#define XGMAC_VERSION                 0x00000008        /* Version */
> +#define XGMAC_VLAN_INCL               0x00000009        /* VLAN tag for
> insertion or replacement into tx frames */
> +#define XGMAC_LPI_CTRL                0x0000000a        /* LPI Control and
> Status */
> +#define XGMAC_LPI_TIMER               0x0000000b        /* LPI Timers
> Control */
> +#define XGMAC_TX_PACE                 0x0000000c        /* Transmit Pace
> and Stretch */
> +#define XGMAC_VLAN_HASH               0x0000000d        /* VLAN Hash Table
> */
> +#define XGMAC_DEBUG                   0x0000000e        /* Debug */
> +#define XGMAC_INT_STATUS              0x0000000f        /* Interrupt and
> Control */
> +#define XGMAC_HASH(n)                 ((0x00000300/4) + (n))        /* HASH
> table registers */
> +#define XGMAC_NUM_HASH                16
> +#define XGMAC_OPMODE                  (0x00000400/4)    /* Operation Mode
> */
> +#define XGMAC_REMOTE_WAKE             (0x00000700/4)    /* Remote Wake-Up
> Frame Filter */
> +#define XGMAC_PMT                     (0x00000704/4)    /* PMT Control and
> Status */
> +
> +#define XGMAC_ADDR_HIGH(reg)          (0x00000010+((reg) * 2))
> +#define XGMAC_ADDR_LOW(reg)           (0x00000011+((reg) * 2))
> +
> +#define DMA_BUS_MODE                  0x000003c0        /* Bus Mode */
> +#define DMA_XMT_POLL_DEMAND           0x000003c1        /* Transmit Poll
> Demand */
> +#define DMA_RCV_POLL_DEMAND           0x000003c2        /* Received Poll
> Demand */
> +#define DMA_RCV_BASE_ADDR             0x000003c3        /* Receive List
> Base */
> +#define DMA_TX_BASE_ADDR              0x000003c4        /* Transmit List
> Base */
> +#define DMA_STATUS                    0x000003c5        /* Status Register
> */
> +#define DMA_CONTROL                   0x000003c6        /* Ctrl
> (Operational Mode) */
> +#define DMA_INTR_ENA                  0x000003c7        /* Interrupt Enable
> */
> +#define DMA_MISSED_FRAME_CTR          0x000003c8        /* Missed Frame
> Counter */
> +#define DMA_RI_WATCHDOG_TIMER         0x000003c9        /* Receive
> Interrupt Watchdog Timer */
> +#define DMA_AXI_BUS                   0x000003ca        /* AXI Bus Mode */
> +#define DMA_AXI_STATUS                0x000003cb        /* AXI Status */
> +#define DMA_CUR_TX_DESC_ADDR          0x000003d2        /* Current Host Tx
> Descriptor */
> +#define DMA_CUR_RX_DESC_ADDR          0x000003d3        /* Current Host Rx
> Descriptor */
> +#define DMA_CUR_TX_BUF_ADDR           0x000003d4        /* Current Host Tx
> Buffer */
> +#define DMA_CUR_RX_BUF_ADDR           0x000003d5        /* Current Host Rx
> Buffer */
> +#define DMA_HW_FEATURE                0x000003d6        /* Enabled Hardware
> Features */
> +
> +/* DMA Status register defines */
> +#define DMA_STATUS_GMI                0x08000000        /* MMC interrupt */
> +#define DMA_STATUS_GLI                0x04000000        /* GMAC Line
> interface int */
> +#define DMA_STATUS_EB_MASK            0x00380000        /* Error Bits Mask
> */
> +#define DMA_STATUS_EB_TX_ABORT        0x00080000        /* Error Bits - TX
> Abort */
> +#define DMA_STATUS_EB_RX_ABORT        0x00100000        /* Error Bits - RX
> Abort */
> +#define DMA_STATUS_TS_MASK            0x00700000        /* Transmit Process
> State */
> +#define DMA_STATUS_TS_SHIFT           20
> +#define DMA_STATUS_RS_MASK            0x000e0000        /* Receive Process
> State */
> +#define DMA_STATUS_RS_SHIFT           17
> +#define DMA_STATUS_NIS                0x00010000        /* Normal Interrupt
> Summary */
> +#define DMA_STATUS_AIS                0x00008000        /* Abnormal
> Interrupt Summary */
> +#define DMA_STATUS_ERI                0x00004000        /* Early Receive
> Interrupt */
> +#define DMA_STATUS_FBI                0x00002000        /* Fatal Bus Error
> Interrupt */
> +#define DMA_STATUS_ETI                0x00000400        /* Early Transmit
> Interrupt */
> +#define DMA_STATUS_RWT                0x00000200        /* Receive Watchdog
> Timeout */
> +#define DMA_STATUS_RPS                0x00000100        /* Receive Process
> Stopped */
> +#define DMA_STATUS_RU                 0x00000080        /* Receive Buffer
> Unavailable */
> +#define DMA_STATUS_RI                 0x00000040        /* Receive
> Interrupt */
> +#define DMA_STATUS_UNF                0x00000020        /* Transmit
> Underflow */
> +#define DMA_STATUS_OVF                0x00000010        /* Receive Overflow
> */
> +#define DMA_STATUS_TJT                0x00000008        /* Transmit Jabber
> Timeout */
> +#define DMA_STATUS_TU                 0x00000004        /* Transmit Buffer
> Unavailable */
> +#define DMA_STATUS_TPS                0x00000002        /* Transmit Process
> Stopped */
> +#define DMA_STATUS_TI                 0x00000001        /* Transmit
> Interrupt */
> +
> +/* DMA Control register defines */
> +#define DMA_CONTROL_ST                0x00002000        /* Start/Stop
> Transmission */
> +#define DMA_CONTROL_SR                0x00000002        /* Start/Stop
> Receive */
> +#define DMA_CONTROL_DFF               0x01000000        /* Disable flush of
> rx frames */
> +
> +struct desc {
> +    uint32_t ctl_stat;
> +    uint16_t buffer1_size;
> +    uint16_t buffer2_size;
> +    uint32_t buffer1_addr;
> +    uint32_t buffer2_addr;
> +    uint32_t ext_stat;
> +    uint32_t res[3];
> +};
> +
> +#define R_MAX 0x400
> +
> +struct XgmacEnet {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    qemu_irq sbd_irq;
> +    qemu_irq pmt_irq;
> +    qemu_irq mci_irq;
> +    void *dmach;
> +    NICState *nic;
> +    NICConf conf;
> +
> +    uint32_t c_rxmem;
> +    uint32_t c_txmem;
> +    uint32_t c_phyaddr;
> +
> +    struct {
> +        uint64_t rx_bytes;
> +        uint64_t tx_bytes;
> +
> +        uint64_t rx;
> +        uint64_t rx_bcast;
> +        uint64_t rx_mcast;
> +    } stats;
> +
> +    /* Receive configuration words.  */
> +    uint32_t rcw[2];
> +    /* Transmit config.  */
> +    uint32_t tc;
> +    uint32_t emmc;
> +    uint32_t phyc;
> +
> +    /* Unicast Address Word.  */
> +    uint32_t uaw[2];
> +    /* Unicast address filter used with extended mcast.  */
> +    uint32_t ext_uaw[2];
> +    uint32_t fmi;
> +
> +    uint32_t regs[R_MAX];
> +
> +    /* Multicast filter addrs.  */
> +    uint32_t maddr[4][2];
> +    /* 32K x 1 lookup filter.  */
> +    uint32_t ext_mtable[1024];
> +
> +
> +    uint8_t *rxmem;
> +};
> +
> +static void xgmac_read_desc(struct XgmacEnet *s, struct desc *d, int rx)
> +{
> +    uint32_t addr = rx ? s->regs[DMA_CUR_RX_DESC_ADDR] :
> +        s->regs[DMA_CUR_TX_DESC_ADDR];
> +    cpu_physical_memory_read(addr, d, sizeof(*d));
> +}
> +
> +static void xgmac_write_desc(struct XgmacEnet *s, struct desc *d, int rx)
> +{
> +    int reg = rx ? DMA_CUR_RX_DESC_ADDR : DMA_CUR_TX_DESC_ADDR;
> +    uint32_t addr = s->regs[reg];
> +
> +    if (!rx && (d->ctl_stat & 0x00200000)) {
> +        s->regs[reg] = s->regs[DMA_TX_BASE_ADDR];
> +    } else if (rx && (d->buffer1_size & 0x8000)) {
> +        s->regs[reg] = s->regs[DMA_RCV_BASE_ADDR];
> +    } else {
> +        s->regs[reg] += sizeof(*d);
> +    }
> +    cpu_physical_memory_write(addr, d, sizeof(*d));
> +}
> +
> +static void xgmac_enet_send(struct XgmacEnet *s)
> +{
> +    struct desc bd;
> +    int frame_size;
> +    int len;
> +    uint8_t frame[8192];
> +    uint8_t *ptr;
> +
> +    ptr = frame;
> +    frame_size = 0;
> +    while (1) {
> +        xgmac_read_desc(s, &bd, 0);
> +        if ((bd.ctl_stat & 0x80000000) == 0) {
> +            /* Run out of descriptors to transmit.  */
> +            break;
> +        }
> +        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);
> +
> +        if ((bd.buffer1_size & 0xfff) > 2048) {
> +            fprintf(stdout, "qemu:%s:ERROR...ERROR...ERROR... -- "
> +                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
> +                         __func__, bd.buffer1_size & 0xfff);
> +            fflush(stdout);
> +        }
> +        if ((bd.buffer2_size & 0xfff) != 0) {
> +            fprintf(stdout, "qemu:%s:ERROR...ERROR...ERROR... -- "
> +                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
> +                        __func__, bd.buffer2_size & 0xfff);
> +            fflush(stdout);
> +        }
> +        if (len >= sizeof(frame)) {
> +            fprintf(stdout, "qemu:%s: buffer overflow %d read into %zu "
> +                        "buffer\n" , __func__, len, sizeof(frame));
> +            fprintf(stdout, "qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
> +                        __func__, bd.buffer1_size, bd.buffer2_size);
> +            fflush(stdout);
> +        }
> +
> +        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
> +        ptr += len;
> +        frame_size += len;
> +        if (bd.ctl_stat & 0x20000000) {
> +            /* Last buffer in frame.  */
> +            qemu_send_packet(&s->nic->nc, frame, len);
> +            ptr = frame;
> +            frame_size = 0;
> +            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
> +        }
> +        bd.ctl_stat &= ~0x80000000;
> +        /* Write back the modified descriptor.  */
> +        xgmac_write_desc(s, &bd, 0);
> +    }
> +}
> +
> +static void enet_update_irq(struct XgmacEnet *s)
> +{
> +    int stat = s->regs[DMA_STATUS] & s->regs[DMA_INTR_ENA];
> +    qemu_set_irq(s->sbd_irq, !!stat);
> +}
> +
> +static uint64_t enet_read(void *opaque, target_phys_addr_t addr, unsigned
> size)
> +{
> +    struct XgmacEnet *s = opaque;
> +    uint64_t r = 0;
> +    addr >>= 2;
> +
> +    switch (addr) {
> +    case XGMAC_VERSION:
> +        r = 0x1012;
> +        break;
> +    default:
> +        if (addr < ARRAY_SIZE(s->regs)) {
> +            r = s->regs[addr];
> +        }
> +        break;
> +    }
> +    return r;
> +}
> +
> +static void enet_write(void *opaque, target_phys_addr_t addr, uint64_t
> value, unsigned size)
> +{
> +    struct XgmacEnet *s = opaque;
> +
> +    addr >>= 2;
> +    switch (addr) {
> +    case DMA_BUS_MODE:
> +        s->regs[DMA_BUS_MODE] = value & ~0x1;
> +        break;
> +    case DMA_XMT_POLL_DEMAND:
> +        xgmac_enet_send(s);
> +        break;
> +    case DMA_STATUS:
> +        s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value;
> +        break;
> +    case DMA_RCV_BASE_ADDR:
> +        s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value;
> +        break;
> +    case DMA_TX_BASE_ADDR:
> +        s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value;
> +        break;
> +    default:
> +        if (addr < ARRAY_SIZE(s->regs)) {
> +            s->regs[addr] = value;
> +        }
> +        break;
> +    }
> +    enet_update_irq(s);
> +}
> +
> +static const MemoryRegionOps enet_mem_ops = {
> +    .read = enet_read,
> +    .write = enet_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static int eth_can_rx(VLANClientState *nc)
> +{
> +    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +
> +    /* RX enabled?  */
> +    return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
> +}
> +
> +static ssize_t eth_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
> +{
> +    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
> +                                              0xff, 0xff, 0xff};
> +    int unicast, broadcast, multicast;
> +    struct desc bd;
> +    ssize_t ret;
> +
> +    unicast = ~buf[0] & 0x1;
> +    broadcast = memcmp(buf, sa_bcast, 6) == 0;
> +    multicast = !unicast && !broadcast;
> +    if (size < 12) {
> +        s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
> +        ret = -1;
> +        goto out;
> +    }
> +
> +    xgmac_read_desc(s, &bd, 1);
> +    if ((bd.ctl_stat & 0x80000000) == 0) {
> +        s->regs[DMA_STATUS] |= DMA_STATUS_RU | DMA_STATUS_AIS;
> +        ret = size;
> +        goto out;
> +    }
> +
> +    cpu_physical_memory_write(bd.buffer1_addr, buf, size);
> +
> +    /* Add in the 4 bytes for crc (the real hw returns length incl crc) */
> +    size += 4;
> +    bd.ctl_stat = (size << 16) | 0x300;
> +    xgmac_write_desc(s, &bd, 1);
> +
> +    s->stats.rx_bytes += size;
> +    s->stats.rx++;
> +    if (multicast) {
> +        s->stats.rx_mcast++;
> +    } else if (broadcast) {
> +        s->stats.rx_bcast++;
> +    }
> +
> +    s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
> +    ret = size;
> +
> +out:
> +    enet_update_irq(s);
> +    return ret;
> +}
> +
> +static void eth_cleanup(VLANClientState *nc)
> +{
> +    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +    g_free(s);
> +}

This looks suspicious: I don't think you should be freeing
your own device struct.

> +static NetClientInfo net_xgmac_enet_info = {
> +    .type = NET_CLIENT_TYPE_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = eth_can_rx,
> +    .receive = eth_rx,
> +    .cleanup = eth_cleanup,
> +};
> +
> +static int xgmac_enet_init(SysBusDevice *dev)
> +{
> +    struct XgmacEnet *s = FROM_SYSBUS(typeof(*s), dev);
> +
> +    memory_region_init_io(&s->iomem, &enet_mem_ops, s, "xgmac", 0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    sysbus_init_irq(dev, &s->sbd_irq);
> +    sysbus_init_irq(dev, &s->pmt_irq);
> +    sysbus_init_irq(dev, &s->mci_irq);
> +
> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +    s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
> +                          dev->qdev.info->name, dev->qdev.id, s);
> +    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
> +
> +    s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |
> +                                   s->conf.macaddr.a[4];
> +    s->regs[XGMAC_ADDR_LOW(0)] = (s->conf.macaddr.a[3] << 24) |
> +                                 (s->conf.macaddr.a[2] << 16) |
> +                                 (s->conf.macaddr.a[1] << 8) |
> +                                  s->conf.macaddr.a[0];
> +
> +    return 0;
> +}
> +
> +static SysBusDeviceInfo xgmac_enet_info = {
> +    .init = xgmac_enet_init,
> +    .qdev.name  = "xgmac",
> +    .qdev.size  = sizeof(struct XgmacEnet),
> +    .qdev.props = (Property[]) {
> +        DEFINE_PROP_UINT32("phyaddr", struct XgmacEnet, c_phyaddr, 7),
> +        DEFINE_NIC_PROPERTIES(struct XgmacEnet, conf),
> +        DEFINE_PROP_END_OF_LIST(),
> +    }
> +};
> +static void xgmac_enet_register(void)
> +{
> +    sysbus_register_withprop(&xgmac_enet_info);
> +}
> +
> +device_init(xgmac_enet_register)
> --
> 1.7.5.4
>



-- 
12345678901234567890123456789012345678901234567890123456789012345678901234567890
         1         2         3         4         5         6         7         8



reply via email to

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