qemu/hw/net/rocker/rocker.c
<<
>>
Prefs
   1/*
   2 * QEMU rocker switch emulation - PCI device
   3 *
   4 * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
   5 * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 * GNU General Public License for more details.
  16 */
  17
  18#include "qemu/osdep.h"
  19#include "hw/pci/pci.h"
  20#include "hw/qdev-properties.h"
  21#include "hw/qdev-properties-system.h"
  22#include "migration/vmstate.h"
  23#include "hw/pci/msix.h"
  24#include "net/net.h"
  25#include "net/eth.h"
  26#include "qapi/error.h"
  27#include "qapi/qapi-commands-rocker.h"
  28#include "qemu/iov.h"
  29#include "qemu/module.h"
  30#include "qemu/bitops.h"
  31#include "qemu/log.h"
  32
  33#include "rocker.h"
  34#include "rocker_hw.h"
  35#include "rocker_fp.h"
  36#include "rocker_desc.h"
  37#include "rocker_tlv.h"
  38#include "rocker_world.h"
  39#include "rocker_of_dpa.h"
  40
  41struct rocker {
  42    /* private */
  43    PCIDevice parent_obj;
  44    /* public */
  45
  46    MemoryRegion mmio;
  47    MemoryRegion msix_bar;
  48
  49    /* switch configuration */
  50    char *name;                  /* switch name */
  51    char *world_name;            /* world name */
  52    uint32_t fp_ports;           /* front-panel port count */
  53    NICPeers *fp_ports_peers;
  54    MACAddr fp_start_macaddr;    /* front-panel port 0 mac addr */
  55    uint64_t switch_id;          /* switch id */
  56
  57    /* front-panel ports */
  58    FpPort *fp_port[ROCKER_FP_PORTS_MAX];
  59
  60    /* register backings */
  61    uint32_t test_reg;
  62    uint64_t test_reg64;
  63    dma_addr_t test_dma_addr;
  64    uint32_t test_dma_size;
  65    uint64_t lower32;            /* lower 32-bit val in 2-part 64-bit access */
  66
  67    /* desc rings */
  68    DescRing **rings;
  69
  70    /* switch worlds */
  71    World *worlds[ROCKER_WORLD_TYPE_MAX];
  72    World *world_dflt;
  73
  74    QLIST_ENTRY(rocker) next;
  75};
  76
  77static QLIST_HEAD(, rocker) rockers;
  78
  79Rocker *rocker_find(const char *name)
  80{
  81    Rocker *r;
  82
  83    QLIST_FOREACH(r, &rockers, next)
  84        if (strcmp(r->name, name) == 0) {
  85            return r;
  86        }
  87
  88    return NULL;
  89}
  90
  91World *rocker_get_world(Rocker *r, enum rocker_world_type type)
  92{
  93    if (type < ROCKER_WORLD_TYPE_MAX) {
  94        return r->worlds[type];
  95    }
  96    return NULL;
  97}
  98
  99RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
 100{
 101    RockerSwitch *rocker;
 102    Rocker *r;
 103
 104    r = rocker_find(name);
 105    if (!r) {
 106        error_setg(errp, "rocker %s not found", name);
 107        return NULL;
 108    }
 109
 110    rocker = g_new0(RockerSwitch, 1);
 111    rocker->name = g_strdup(r->name);
 112    rocker->id = r->switch_id;
 113    rocker->ports = r->fp_ports;
 114
 115    return rocker;
 116}
 117
 118RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
 119{
 120    RockerPortList *list = NULL;
 121    Rocker *r;
 122    int i;
 123
 124    r = rocker_find(name);
 125    if (!r) {
 126        error_setg(errp, "rocker %s not found", name);
 127        return NULL;
 128    }
 129
 130    for (i = r->fp_ports - 1; i >= 0; i--) {
 131        QAPI_LIST_PREPEND(list, fp_port_get_info(r->fp_port[i]));
 132    }
 133
 134    return list;
 135}
 136
 137uint32_t rocker_fp_ports(Rocker *r)
 138{
 139    return r->fp_ports;
 140}
 141
 142static uint32_t rocker_get_pport_by_tx_ring(Rocker *r,
 143                                            DescRing *ring)
 144{
 145    return (desc_ring_index(ring) - 2) / 2 + 1;
 146}
 147
 148static int tx_consume(Rocker *r, DescInfo *info)
 149{
 150    PCIDevice *dev = PCI_DEVICE(r);
 151    char *buf = desc_get_buf(info, true);
 152    RockerTlv *tlv_frag;
 153    RockerTlv *tlvs[ROCKER_TLV_TX_MAX + 1];
 154    struct iovec iov[ROCKER_TX_FRAGS_MAX] = { { 0, }, };
 155    uint32_t pport;
 156    uint32_t port;
 157    uint16_t tx_offload = ROCKER_TX_OFFLOAD_NONE;
 158    uint16_t tx_l3_csum_off = 0;
 159    uint16_t tx_tso_mss = 0;
 160    uint16_t tx_tso_hdr_len = 0;
 161    int iovcnt = 0;
 162    int err = ROCKER_OK;
 163    int rem;
 164    int i;
 165
 166    if (!buf) {
 167        return -ROCKER_ENXIO;
 168    }
 169
 170    rocker_tlv_parse(tlvs, ROCKER_TLV_TX_MAX, buf, desc_tlv_size(info));
 171
 172    if (!tlvs[ROCKER_TLV_TX_FRAGS]) {
 173        return -ROCKER_EINVAL;
 174    }
 175
 176    pport = rocker_get_pport_by_tx_ring(r, desc_get_ring(info));
 177    if (!fp_port_from_pport(pport, &port)) {
 178        return -ROCKER_EINVAL;
 179    }
 180
 181    if (tlvs[ROCKER_TLV_TX_OFFLOAD]) {
 182        tx_offload = rocker_tlv_get_u8(tlvs[ROCKER_TLV_TX_OFFLOAD]);
 183    }
 184
 185    switch (tx_offload) {
 186    case ROCKER_TX_OFFLOAD_L3_CSUM:
 187        if (!tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) {
 188            return -ROCKER_EINVAL;
 189        }
 190        break;
 191    case ROCKER_TX_OFFLOAD_TSO:
 192        if (!tlvs[ROCKER_TLV_TX_TSO_MSS] ||
 193            !tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) {
 194            return -ROCKER_EINVAL;
 195        }
 196        break;
 197    }
 198
 199    if (tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) {
 200        tx_l3_csum_off = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]);
 201        qemu_log_mask(LOG_UNIMP, "rocker %s: L3 not implemented"
 202                                 " (cksum off: %u)\n",
 203                      __func__, tx_l3_csum_off);
 204    }
 205
 206    if (tlvs[ROCKER_TLV_TX_TSO_MSS]) {
 207        tx_tso_mss = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_MSS]);
 208        qemu_log_mask(LOG_UNIMP, "rocker %s: TSO not implemented (MSS: %u)\n",
 209                      __func__, tx_tso_mss);
 210    }
 211
 212    if (tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) {
 213        tx_tso_hdr_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]);
 214        qemu_log_mask(LOG_UNIMP, "rocker %s: TSO not implemented"
 215                                 " (hdr length: %u)\n",
 216                      __func__, tx_tso_hdr_len);
 217    }
 218
 219    rocker_tlv_for_each_nested(tlv_frag, tlvs[ROCKER_TLV_TX_FRAGS], rem) {
 220        hwaddr frag_addr;
 221        uint16_t frag_len;
 222
 223        if (rocker_tlv_type(tlv_frag) != ROCKER_TLV_TX_FRAG) {
 224            err = -ROCKER_EINVAL;
 225            goto err_bad_attr;
 226        }
 227
 228        rocker_tlv_parse_nested(tlvs, ROCKER_TLV_TX_FRAG_ATTR_MAX, tlv_frag);
 229
 230        if (!tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] ||
 231            !tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]) {
 232            err = -ROCKER_EINVAL;
 233            goto err_bad_attr;
 234        }
 235
 236        frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]);
 237        frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]);
 238
 239        if (iovcnt >= ROCKER_TX_FRAGS_MAX) {
 240            goto err_too_many_frags;
 241        }
 242        iov[iovcnt].iov_len = frag_len;
 243        iov[iovcnt].iov_base = g_malloc(frag_len);
 244
 245        pci_dma_read(dev, frag_addr, iov[iovcnt].iov_base,
 246                     iov[iovcnt].iov_len);
 247
 248        iovcnt++;
 249    }
 250
 251    err = fp_port_eg(r->fp_port[port], iov, iovcnt);
 252
 253err_too_many_frags:
 254err_bad_attr:
 255    for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) {
 256        g_free(iov[i].iov_base);
 257    }
 258
 259    return err;
 260}
 261
 262static int cmd_get_port_settings(Rocker *r,
 263                                 DescInfo *info, char *buf,
 264                                 RockerTlv *cmd_info_tlv)
 265{
 266    RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
 267    RockerTlv *nest;
 268    FpPort *fp_port;
 269    uint32_t pport;
 270    uint32_t port;
 271    uint32_t speed;
 272    uint8_t duplex;
 273    uint8_t autoneg;
 274    uint8_t learning;
 275    char *phys_name;
 276    MACAddr macaddr;
 277    enum rocker_world_type mode;
 278    size_t tlv_size;
 279    int pos;
 280    int err;
 281
 282    rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
 283                            cmd_info_tlv);
 284
 285    if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) {
 286        return -ROCKER_EINVAL;
 287    }
 288
 289    pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]);
 290    if (!fp_port_from_pport(pport, &port)) {
 291        return -ROCKER_EINVAL;
 292    }
 293    fp_port = r->fp_port[port];
 294
 295    err = fp_port_get_settings(fp_port, &speed, &duplex, &autoneg);
 296    if (err) {
 297        return err;
 298    }
 299
 300    fp_port_get_macaddr(fp_port, &macaddr);
 301    mode = world_type(fp_port_get_world(fp_port));
 302    learning = fp_port_get_learning(fp_port);
 303    phys_name = fp_port_get_name(fp_port);
 304
 305    tlv_size = rocker_tlv_total_size(0) +                 /* nest */
 306               rocker_tlv_total_size(sizeof(uint32_t)) +  /*   pport */
 307               rocker_tlv_total_size(sizeof(uint32_t)) +  /*   speed */
 308               rocker_tlv_total_size(sizeof(uint8_t)) +   /*   duplex */
 309               rocker_tlv_total_size(sizeof(uint8_t)) +   /*   autoneg */
 310               rocker_tlv_total_size(sizeof(macaddr.a)) + /*   macaddr */
 311               rocker_tlv_total_size(sizeof(uint8_t)) +   /*   mode */
 312               rocker_tlv_total_size(sizeof(uint8_t)) +   /*   learning */
 313               rocker_tlv_total_size(strlen(phys_name));
 314
 315    if (tlv_size > desc_buf_size(info)) {
 316        return -ROCKER_EMSGSIZE;
 317    }
 318
 319    pos = 0;
 320    nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_CMD_INFO);
 321    rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, pport);
 322    rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, speed);
 323    rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, duplex);
 324    rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, autoneg);
 325    rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR,
 326                   sizeof(macaddr.a), macaddr.a);
 327    rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode);
 328    rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
 329                      learning);
 330    rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME,
 331                   strlen(phys_name), phys_name);
 332    rocker_tlv_nest_end(buf, &pos, nest);
 333
 334    return desc_set_buf(info, tlv_size);
 335}
 336
 337static int cmd_set_port_settings(Rocker *r,
 338                                 RockerTlv *cmd_info_tlv)
 339{
 340    RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
 341    FpPort *fp_port;
 342    uint32_t pport;
 343    uint32_t port;
 344    uint32_t speed;
 345    uint8_t duplex;
 346    uint8_t autoneg;
 347    uint8_t learning;
 348    MACAddr macaddr;
 349    enum rocker_world_type mode;
 350    int err;
 351
 352    rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
 353                            cmd_info_tlv);
 354
 355    if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) {
 356        return -ROCKER_EINVAL;
 357    }
 358
 359    pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]);
 360    if (!fp_port_from_pport(pport, &port)) {
 361        return -ROCKER_EINVAL;
 362    }
 363    fp_port = r->fp_port[port];
 364
 365    if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] &&
 366        tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] &&
 367        tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]) {
 368
 369        speed = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]);
 370        duplex = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]);
 371        autoneg = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]);
 372
 373        err = fp_port_set_settings(fp_port, speed, duplex, autoneg);
 374        if (err) {
 375            return err;
 376        }
 377    }
 378
 379    if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) {
 380        if (rocker_tlv_len(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) !=
 381            sizeof(macaddr.a)) {
 382            return -ROCKER_EINVAL;
 383        }
 384        memcpy(macaddr.a,
 385               rocker_tlv_data(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]),
 386               sizeof(macaddr.a));
 387        fp_port_set_macaddr(fp_port, &macaddr);
 388    }
 389
 390    if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) {
 391        mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]);
 392        if (mode >= ROCKER_WORLD_TYPE_MAX) {
 393            return -ROCKER_EINVAL;
 394        }
 395        /* We don't support world change. */
 396        if (!fp_port_check_world(fp_port, r->worlds[mode])) {
 397            return -ROCKER_EINVAL;
 398        }
 399    }
 400
 401    if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]) {
 402        learning =
 403            rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]);
 404        fp_port_set_learning(fp_port, learning);
 405    }
 406
 407    return ROCKER_OK;
 408}
 409
 410static int cmd_consume(Rocker *r, DescInfo *info)
 411{
 412    char *buf = desc_get_buf(info, false);
 413    RockerTlv *tlvs[ROCKER_TLV_CMD_MAX + 1];
 414    RockerTlv *info_tlv;
 415    World *world;
 416    uint16_t cmd;
 417    int err;
 418
 419    if (!buf) {
 420        return -ROCKER_ENXIO;
 421    }
 422
 423    rocker_tlv_parse(tlvs, ROCKER_TLV_CMD_MAX, buf, desc_tlv_size(info));
 424
 425    if (!tlvs[ROCKER_TLV_CMD_TYPE] || !tlvs[ROCKER_TLV_CMD_INFO]) {
 426        return -ROCKER_EINVAL;
 427    }
 428
 429    cmd = rocker_tlv_get_le16(tlvs[ROCKER_TLV_CMD_TYPE]);
 430    info_tlv = tlvs[ROCKER_TLV_CMD_INFO];
 431
 432    /* This might be reworked to something like this:
 433     * Every world will have an array of command handlers from
 434     * ROCKER_TLV_CMD_TYPE_UNSPEC to ROCKER_TLV_CMD_TYPE_MAX. There is
 435     * up to each world to implement whatever command it want.
 436     * It can reference "generic" commands as cmd_set_port_settings or
 437     * cmd_get_port_settings
 438     */
 439
 440    switch (cmd) {
 441    case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD:
 442    case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD:
 443    case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL:
 444    case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS:
 445    case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD:
 446    case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD:
 447    case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL:
 448    case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS:
 449        world = r->worlds[ROCKER_WORLD_TYPE_OF_DPA];
 450        err = world_do_cmd(world, info, buf, cmd, info_tlv);
 451        break;
 452    case ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS:
 453        err = cmd_get_port_settings(r, info, buf, info_tlv);
 454        break;
 455    case ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS:
 456        err = cmd_set_port_settings(r, info_tlv);
 457        break;
 458    default:
 459        err = -ROCKER_EINVAL;
 460        break;
 461    }
 462
 463    return err;
 464}
 465
 466static void rocker_msix_irq(Rocker *r, unsigned vector)
 467{
 468    PCIDevice *dev = PCI_DEVICE(r);
 469
 470    DPRINTF("MSI-X notify request for vector %d\n", vector);
 471    if (vector >= ROCKER_MSIX_VEC_COUNT(r->fp_ports)) {
 472        DPRINTF("incorrect vector %d\n", vector);
 473        return;
 474    }
 475    msix_notify(dev, vector);
 476}
 477
 478int rocker_event_link_changed(Rocker *r, uint32_t pport, bool link_up)
 479{
 480    DescRing *ring = r->rings[ROCKER_RING_EVENT];
 481    DescInfo *info = desc_ring_fetch_desc(ring);
 482    RockerTlv *nest;
 483    char *buf;
 484    size_t tlv_size;
 485    int pos;
 486    int err;
 487
 488    if (!info) {
 489        return -ROCKER_ENOBUFS;
 490    }
 491
 492    tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) +  /* event type */
 493               rocker_tlv_total_size(0) +                 /* nest */
 494               rocker_tlv_total_size(sizeof(uint32_t)) +  /*   pport */
 495               rocker_tlv_total_size(sizeof(uint8_t));    /*   link up */
 496
 497    if (tlv_size > desc_buf_size(info)) {
 498        err = -ROCKER_EMSGSIZE;
 499        goto err_too_big;
 500    }
 501
 502    buf = desc_get_buf(info, false);
 503    if (!buf) {
 504        err = -ROCKER_ENOMEM;
 505        goto err_no_mem;
 506    }
 507
 508    pos = 0;
 509    rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE,
 510                        ROCKER_TLV_EVENT_TYPE_LINK_CHANGED);
 511    nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO);
 512    rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_PPORT, pport);
 513    rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP,
 514                      link_up ? 1 : 0);
 515    rocker_tlv_nest_end(buf, &pos, nest);
 516
 517    err = desc_set_buf(info, tlv_size);
 518
 519err_too_big:
 520err_no_mem:
 521    if (desc_ring_post_desc(ring, err)) {
 522        rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT);
 523    }
 524
 525    return err;
 526}
 527
 528int rocker_event_mac_vlan_seen(Rocker *r, uint32_t pport, uint8_t *addr,
 529                               uint16_t vlan_id)
 530{
 531    DescRing *ring = r->rings[ROCKER_RING_EVENT];
 532    DescInfo *info;
 533    FpPort *fp_port;
 534    uint32_t port;
 535    RockerTlv *nest;
 536    char *buf;
 537    size_t tlv_size;
 538    int pos;
 539    int err;
 540
 541    if (!fp_port_from_pport(pport, &port)) {
 542        return -ROCKER_EINVAL;
 543    }
 544    fp_port = r->fp_port[port];
 545    if (!fp_port_get_learning(fp_port)) {
 546        return ROCKER_OK;
 547    }
 548
 549    info = desc_ring_fetch_desc(ring);
 550    if (!info) {
 551        return -ROCKER_ENOBUFS;
 552    }
 553
 554    tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) +  /* event type */
 555               rocker_tlv_total_size(0) +                 /* nest */
 556               rocker_tlv_total_size(sizeof(uint32_t)) +  /*   pport */
 557               rocker_tlv_total_size(ETH_ALEN) +          /*   mac addr */
 558               rocker_tlv_total_size(sizeof(uint16_t));   /*   vlan_id */
 559
 560    if (tlv_size > desc_buf_size(info)) {
 561        err = -ROCKER_EMSGSIZE;
 562        goto err_too_big;
 563    }
 564
 565    buf = desc_get_buf(info, false);
 566    if (!buf) {
 567        err = -ROCKER_ENOMEM;
 568        goto err_no_mem;
 569    }
 570
 571    pos = 0;
 572    rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE,
 573                        ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN);
 574    nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO);
 575    rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_PPORT, pport);
 576    rocker_tlv_put(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_MAC, ETH_ALEN, addr);
 577    rocker_tlv_put_u16(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID, vlan_id);
 578    rocker_tlv_nest_end(buf, &pos, nest);
 579
 580    err = desc_set_buf(info, tlv_size);
 581
 582err_too_big:
 583err_no_mem:
 584    if (desc_ring_post_desc(ring, err)) {
 585        rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT);
 586    }
 587
 588    return err;
 589}
 590
 591static DescRing *rocker_get_rx_ring_by_pport(Rocker *r,
 592                                                     uint32_t pport)
 593{
 594    return r->rings[(pport - 1) * 2 + 3];
 595}
 596
 597int rx_produce(World *world, uint32_t pport,
 598               const struct iovec *iov, int iovcnt, uint8_t copy_to_cpu)
 599{
 600    Rocker *r = world_rocker(world);
 601    PCIDevice *dev = (PCIDevice *)r;
 602    DescRing *ring = rocker_get_rx_ring_by_pport(r, pport);
 603    DescInfo *info = desc_ring_fetch_desc(ring);
 604    char *data;
 605    size_t data_size = iov_size(iov, iovcnt);
 606    char *buf;
 607    uint16_t rx_flags = 0;
 608    uint16_t rx_csum = 0;
 609    size_t tlv_size;
 610    RockerTlv *tlvs[ROCKER_TLV_RX_MAX + 1];
 611    hwaddr frag_addr;
 612    uint16_t frag_max_len;
 613    int pos;
 614    int err;
 615
 616    if (!info) {
 617        return -ROCKER_ENOBUFS;
 618    }
 619
 620    buf = desc_get_buf(info, false);
 621    if (!buf) {
 622        err = -ROCKER_ENXIO;
 623        goto out;
 624    }
 625    rocker_tlv_parse(tlvs, ROCKER_TLV_RX_MAX, buf, desc_tlv_size(info));
 626
 627    if (!tlvs[ROCKER_TLV_RX_FRAG_ADDR] ||
 628        !tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]) {
 629        err = -ROCKER_EINVAL;
 630        goto out;
 631    }
 632
 633    frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_RX_FRAG_ADDR]);
 634    frag_max_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]);
 635
 636    if (data_size > frag_max_len) {
 637        err = -ROCKER_EMSGSIZE;
 638        goto out;
 639    }
 640
 641    if (copy_to_cpu) {
 642        rx_flags |= ROCKER_RX_FLAGS_FWD_OFFLOAD;
 643    }
 644
 645    /* XXX calc rx flags/csum */
 646
 647    tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* flags */
 648               rocker_tlv_total_size(sizeof(uint16_t)) + /* scum */
 649               rocker_tlv_total_size(sizeof(uint64_t)) + /* frag addr */
 650               rocker_tlv_total_size(sizeof(uint16_t)) + /* frag max len */
 651               rocker_tlv_total_size(sizeof(uint16_t));  /* frag len */
 652
 653    if (tlv_size > desc_buf_size(info)) {
 654        err = -ROCKER_EMSGSIZE;
 655        goto out;
 656    }
 657
 658    /* TODO:
 659     * iov dma write can be optimized in similar way e1000 does it in
 660     * e1000_receive_iov. But maybe if would make sense to introduce
 661     * generic helper iov_dma_write.
 662     */
 663
 664    data = g_malloc(data_size);
 665
 666    iov_to_buf(iov, iovcnt, 0, data, data_size);
 667    pci_dma_write(dev, frag_addr, data, data_size);
 668    g_free(data);
 669
 670    pos = 0;
 671    rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FLAGS, rx_flags);
 672    rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_CSUM, rx_csum);
 673    rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_RX_FRAG_ADDR, frag_addr);
 674    rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_MAX_LEN, frag_max_len);
 675    rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_LEN, data_size);
 676
 677    err = desc_set_buf(info, tlv_size);
 678
 679out:
 680    if (desc_ring_post_desc(ring, err)) {
 681        rocker_msix_irq(r, ROCKER_MSIX_VEC_RX(pport - 1));
 682    }
 683
 684    return err;
 685}
 686
 687int rocker_port_eg(Rocker *r, uint32_t pport,
 688                   const struct iovec *iov, int iovcnt)
 689{
 690    FpPort *fp_port;
 691    uint32_t port;
 692
 693    if (!fp_port_from_pport(pport, &port)) {
 694        return -ROCKER_EINVAL;
 695    }
 696
 697    fp_port = r->fp_port[port];
 698
 699    return fp_port_eg(fp_port, iov, iovcnt);
 700}
 701
 702static void rocker_test_dma_ctrl(Rocker *r, uint32_t val)
 703{
 704    PCIDevice *dev = PCI_DEVICE(r);
 705    char *buf;
 706    int i;
 707
 708    buf = g_malloc(r->test_dma_size);
 709
 710    switch (val) {
 711    case ROCKER_TEST_DMA_CTRL_CLEAR:
 712        memset(buf, 0, r->test_dma_size);
 713        break;
 714    case ROCKER_TEST_DMA_CTRL_FILL:
 715        memset(buf, 0x96, r->test_dma_size);
 716        break;
 717    case ROCKER_TEST_DMA_CTRL_INVERT:
 718        pci_dma_read(dev, r->test_dma_addr, buf, r->test_dma_size);
 719        for (i = 0; i < r->test_dma_size; i++) {
 720            buf[i] = ~buf[i];
 721        }
 722        break;
 723    default:
 724        DPRINTF("not test dma control val=0x%08x\n", val);
 725        goto err_out;
 726    }
 727    pci_dma_write(dev, r->test_dma_addr, buf, r->test_dma_size);
 728
 729    rocker_msix_irq(r, ROCKER_MSIX_VEC_TEST);
 730
 731err_out:
 732    g_free(buf);
 733}
 734
 735static void rocker_reset(DeviceState *dev);
 736
 737static void rocker_control(Rocker *r, uint32_t val)
 738{
 739    if (val & ROCKER_CONTROL_RESET) {
 740        rocker_reset(DEVICE(r));
 741    }
 742}
 743
 744static int rocker_pci_ring_count(Rocker *r)
 745{
 746    /* There are:
 747     * - command ring
 748     * - event ring
 749     * - tx and rx ring per each port
 750     */
 751    return 2 + (2 * r->fp_ports);
 752}
 753
 754static bool rocker_addr_is_desc_reg(Rocker *r, hwaddr addr)
 755{
 756    hwaddr start = ROCKER_DMA_DESC_BASE;
 757    hwaddr end = start + (ROCKER_DMA_DESC_SIZE * rocker_pci_ring_count(r));
 758
 759    return addr >= start && addr < end;
 760}
 761
 762static void rocker_port_phys_enable_write(Rocker *r, uint64_t new)
 763{
 764    int i;
 765    bool old_enabled;
 766    bool new_enabled;
 767    FpPort *fp_port;
 768
 769    for (i = 0; i < r->fp_ports; i++) {
 770        fp_port = r->fp_port[i];
 771        old_enabled = fp_port_enabled(fp_port);
 772        new_enabled = (new >> (i + 1)) & 0x1;
 773        if (new_enabled == old_enabled) {
 774            continue;
 775        }
 776        if (new_enabled) {
 777            fp_port_enable(r->fp_port[i]);
 778        } else {
 779            fp_port_disable(r->fp_port[i]);
 780        }
 781    }
 782}
 783
 784static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val)
 785{
 786    Rocker *r = opaque;
 787
 788    if (rocker_addr_is_desc_reg(r, addr)) {
 789        unsigned index = ROCKER_RING_INDEX(addr);
 790        unsigned offset = addr & ROCKER_DMA_DESC_MASK;
 791
 792        switch (offset) {
 793        case ROCKER_DMA_DESC_ADDR_OFFSET:
 794            r->lower32 = (uint64_t)val;
 795            break;
 796        case ROCKER_DMA_DESC_ADDR_OFFSET + 4:
 797            desc_ring_set_base_addr(r->rings[index],
 798                                    ((uint64_t)val) << 32 | r->lower32);
 799            r->lower32 = 0;
 800            break;
 801        case ROCKER_DMA_DESC_SIZE_OFFSET:
 802            desc_ring_set_size(r->rings[index], val);
 803            break;
 804        case ROCKER_DMA_DESC_HEAD_OFFSET:
 805            if (desc_ring_set_head(r->rings[index], val)) {
 806                rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index]));
 807            }
 808            break;
 809        case ROCKER_DMA_DESC_CTRL_OFFSET:
 810            desc_ring_set_ctrl(r->rings[index], val);
 811            break;
 812        case ROCKER_DMA_DESC_CREDITS_OFFSET:
 813            if (desc_ring_ret_credits(r->rings[index], val)) {
 814                rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index]));
 815            }
 816            break;
 817        default:
 818            DPRINTF("not implemented dma reg write(l) addr=0x" TARGET_FMT_plx
 819                    " val=0x%08x (ring %d, addr=0x%02x)\n",
 820                    addr, val, index, offset);
 821            break;
 822        }
 823        return;
 824    }
 825
 826    switch (addr) {
 827    case ROCKER_TEST_REG:
 828        r->test_reg = val;
 829        break;
 830    case ROCKER_TEST_REG64:
 831    case ROCKER_TEST_DMA_ADDR:
 832    case ROCKER_PORT_PHYS_ENABLE:
 833        r->lower32 = (uint64_t)val;
 834        break;
 835    case ROCKER_TEST_REG64 + 4:
 836        r->test_reg64 = ((uint64_t)val) << 32 | r->lower32;
 837        r->lower32 = 0;
 838        break;
 839    case ROCKER_TEST_IRQ:
 840        rocker_msix_irq(r, val);
 841        break;
 842    case ROCKER_TEST_DMA_SIZE:
 843        r->test_dma_size = val & 0xFFFF;
 844        break;
 845    case ROCKER_TEST_DMA_ADDR + 4:
 846        r->test_dma_addr = ((uint64_t)val) << 32 | r->lower32;
 847        r->lower32 = 0;
 848        break;
 849    case ROCKER_TEST_DMA_CTRL:
 850        rocker_test_dma_ctrl(r, val);
 851        break;
 852    case ROCKER_CONTROL:
 853        rocker_control(r, val);
 854        break;
 855    case ROCKER_PORT_PHYS_ENABLE + 4:
 856        rocker_port_phys_enable_write(r, ((uint64_t)val) << 32 | r->lower32);
 857        r->lower32 = 0;
 858        break;
 859    default:
 860        DPRINTF("not implemented write(l) addr=0x" TARGET_FMT_plx
 861                " val=0x%08x\n", addr, val);
 862        break;
 863    }
 864}
 865
 866static void rocker_io_writeq(void *opaque, hwaddr addr, uint64_t val)
 867{
 868    Rocker *r = opaque;
 869
 870    if (rocker_addr_is_desc_reg(r, addr)) {
 871        unsigned index = ROCKER_RING_INDEX(addr);
 872        unsigned offset = addr & ROCKER_DMA_DESC_MASK;
 873
 874        switch (offset) {
 875        case ROCKER_DMA_DESC_ADDR_OFFSET:
 876            desc_ring_set_base_addr(r->rings[index], val);
 877            break;
 878        default:
 879            DPRINTF("not implemented dma reg write(q) addr=0x" TARGET_FMT_plx
 880                    " val=0x" TARGET_FMT_plx " (ring %d, offset=0x%02x)\n",
 881                    addr, val, index, offset);
 882            break;
 883        }
 884        return;
 885    }
 886
 887    switch (addr) {
 888    case ROCKER_TEST_REG64:
 889        r->test_reg64 = val;
 890        break;
 891    case ROCKER_TEST_DMA_ADDR:
 892        r->test_dma_addr = val;
 893        break;
 894    case ROCKER_PORT_PHYS_ENABLE:
 895        rocker_port_phys_enable_write(r, val);
 896        break;
 897    default:
 898        DPRINTF("not implemented write(q) addr=0x" TARGET_FMT_plx
 899                " val=0x" TARGET_FMT_plx "\n", addr, val);
 900        break;
 901    }
 902}
 903
 904#ifdef DEBUG_ROCKER
 905#define regname(reg) case (reg): return #reg
 906static const char *rocker_reg_name(void *opaque, hwaddr addr)
 907{
 908    Rocker *r = opaque;
 909
 910    if (rocker_addr_is_desc_reg(r, addr)) {
 911        unsigned index = ROCKER_RING_INDEX(addr);
 912        unsigned offset = addr & ROCKER_DMA_DESC_MASK;
 913        static char buf[100];
 914        char ring_name[10];
 915
 916        switch (index) {
 917        case 0:
 918            sprintf(ring_name, "cmd");
 919            break;
 920        case 1:
 921            sprintf(ring_name, "event");
 922            break;
 923        default:
 924            sprintf(ring_name, "%s-%d", index % 2 ? "rx" : "tx",
 925                    (index - 2) / 2);
 926        }
 927
 928        switch (offset) {
 929        case ROCKER_DMA_DESC_ADDR_OFFSET:
 930            sprintf(buf, "Ring[%s] ADDR", ring_name);
 931            return buf;
 932        case ROCKER_DMA_DESC_ADDR_OFFSET+4:
 933            sprintf(buf, "Ring[%s] ADDR+4", ring_name);
 934            return buf;
 935        case ROCKER_DMA_DESC_SIZE_OFFSET:
 936            sprintf(buf, "Ring[%s] SIZE", ring_name);
 937            return buf;
 938        case ROCKER_DMA_DESC_HEAD_OFFSET:
 939            sprintf(buf, "Ring[%s] HEAD", ring_name);
 940            return buf;
 941        case ROCKER_DMA_DESC_TAIL_OFFSET:
 942            sprintf(buf, "Ring[%s] TAIL", ring_name);
 943            return buf;
 944        case ROCKER_DMA_DESC_CTRL_OFFSET:
 945            sprintf(buf, "Ring[%s] CTRL", ring_name);
 946            return buf;
 947        case ROCKER_DMA_DESC_CREDITS_OFFSET:
 948            sprintf(buf, "Ring[%s] CREDITS", ring_name);
 949            return buf;
 950        default:
 951            sprintf(buf, "Ring[%s] ???", ring_name);
 952            return buf;
 953        }
 954    } else {
 955        switch (addr) {
 956            regname(ROCKER_BOGUS_REG0);
 957            regname(ROCKER_BOGUS_REG1);
 958            regname(ROCKER_BOGUS_REG2);
 959            regname(ROCKER_BOGUS_REG3);
 960            regname(ROCKER_TEST_REG);
 961            regname(ROCKER_TEST_REG64);
 962            regname(ROCKER_TEST_REG64+4);
 963            regname(ROCKER_TEST_IRQ);
 964            regname(ROCKER_TEST_DMA_ADDR);
 965            regname(ROCKER_TEST_DMA_ADDR+4);
 966            regname(ROCKER_TEST_DMA_SIZE);
 967            regname(ROCKER_TEST_DMA_CTRL);
 968            regname(ROCKER_CONTROL);
 969            regname(ROCKER_PORT_PHYS_COUNT);
 970            regname(ROCKER_PORT_PHYS_LINK_STATUS);
 971            regname(ROCKER_PORT_PHYS_LINK_STATUS+4);
 972            regname(ROCKER_PORT_PHYS_ENABLE);
 973            regname(ROCKER_PORT_PHYS_ENABLE+4);
 974            regname(ROCKER_SWITCH_ID);
 975            regname(ROCKER_SWITCH_ID+4);
 976        }
 977    }
 978    return "???";
 979}
 980#else
 981static const char *rocker_reg_name(void *opaque, hwaddr addr)
 982{
 983    return NULL;
 984}
 985#endif
 986
 987static void rocker_mmio_write(void *opaque, hwaddr addr, uint64_t val,
 988                              unsigned size)
 989{
 990    DPRINTF("Write %s addr " TARGET_FMT_plx
 991            ", size %u, val " TARGET_FMT_plx "\n",
 992            rocker_reg_name(opaque, addr), addr, size, val);
 993
 994    switch (size) {
 995    case 4:
 996        rocker_io_writel(opaque, addr, val);
 997        break;
 998    case 8:
 999        rocker_io_writeq(opaque, addr, val);
1000        break;
1001    }
1002}
1003
1004static uint64_t rocker_port_phys_link_status(Rocker *r)
1005{
1006    int i;
1007    uint64_t status = 0;
1008
1009    for (i = 0; i < r->fp_ports; i++) {
1010        FpPort *port = r->fp_port[i];
1011
1012        if (fp_port_get_link_up(port)) {
1013            status |= 1 << (i + 1);
1014        }
1015    }
1016    return status;
1017}
1018
1019static uint64_t rocker_port_phys_enable_read(Rocker *r)
1020{
1021    int i;
1022    uint64_t ret = 0;
1023
1024    for (i = 0; i < r->fp_ports; i++) {
1025        FpPort *port = r->fp_port[i];
1026
1027        if (fp_port_enabled(port)) {
1028            ret |= 1 << (i + 1);
1029        }
1030    }
1031    return ret;
1032}
1033
1034static uint32_t rocker_io_readl(void *opaque, hwaddr addr)
1035{
1036    Rocker *r = opaque;
1037    uint32_t ret;
1038
1039    if (rocker_addr_is_desc_reg(r, addr)) {
1040        unsigned index = ROCKER_RING_INDEX(addr);
1041        unsigned offset = addr & ROCKER_DMA_DESC_MASK;
1042
1043        switch (offset) {
1044        case ROCKER_DMA_DESC_ADDR_OFFSET:
1045            ret = (uint32_t)desc_ring_get_base_addr(r->rings[index]);
1046            break;
1047        case ROCKER_DMA_DESC_ADDR_OFFSET + 4:
1048            ret = (uint32_t)(desc_ring_get_base_addr(r->rings[index]) >> 32);
1049            break;
1050        case ROCKER_DMA_DESC_SIZE_OFFSET:
1051            ret = desc_ring_get_size(r->rings[index]);
1052            break;
1053        case ROCKER_DMA_DESC_HEAD_OFFSET:
1054            ret = desc_ring_get_head(r->rings[index]);
1055            break;
1056        case ROCKER_DMA_DESC_TAIL_OFFSET:
1057            ret = desc_ring_get_tail(r->rings[index]);
1058            break;
1059        case ROCKER_DMA_DESC_CREDITS_OFFSET:
1060            ret = desc_ring_get_credits(r->rings[index]);
1061            break;
1062        default:
1063            DPRINTF("not implemented dma reg read(l) addr=0x" TARGET_FMT_plx
1064                    " (ring %d, addr=0x%02x)\n", addr, index, offset);
1065            ret = 0;
1066            break;
1067        }
1068        return ret;
1069    }
1070
1071    switch (addr) {
1072    case ROCKER_BOGUS_REG0:
1073    case ROCKER_BOGUS_REG1:
1074    case ROCKER_BOGUS_REG2:
1075    case ROCKER_BOGUS_REG3:
1076        ret = 0xDEADBABE;
1077        break;
1078    case ROCKER_TEST_REG:
1079        ret = r->test_reg * 2;
1080        break;
1081    case ROCKER_TEST_REG64:
1082        ret = (uint32_t)(r->test_reg64 * 2);
1083        break;
1084    case ROCKER_TEST_REG64 + 4:
1085        ret = (uint32_t)((r->test_reg64 * 2) >> 32);
1086        break;
1087    case ROCKER_TEST_DMA_SIZE:
1088        ret = r->test_dma_size;
1089        break;
1090    case ROCKER_TEST_DMA_ADDR:
1091        ret = (uint32_t)r->test_dma_addr;
1092        break;
1093    case ROCKER_TEST_DMA_ADDR + 4:
1094        ret = (uint32_t)(r->test_dma_addr >> 32);
1095        break;
1096    case ROCKER_PORT_PHYS_COUNT:
1097        ret = r->fp_ports;
1098        break;
1099    case ROCKER_PORT_PHYS_LINK_STATUS:
1100        ret = (uint32_t)rocker_port_phys_link_status(r);
1101        break;
1102    case ROCKER_PORT_PHYS_LINK_STATUS + 4:
1103        ret = (uint32_t)(rocker_port_phys_link_status(r) >> 32);
1104        break;
1105    case ROCKER_PORT_PHYS_ENABLE:
1106        ret = (uint32_t)rocker_port_phys_enable_read(r);
1107        break;
1108    case ROCKER_PORT_PHYS_ENABLE + 4:
1109        ret = (uint32_t)(rocker_port_phys_enable_read(r) >> 32);
1110        break;
1111    case ROCKER_SWITCH_ID:
1112        ret = (uint32_t)r->switch_id;
1113        break;
1114    case ROCKER_SWITCH_ID + 4:
1115        ret = (uint32_t)(r->switch_id >> 32);
1116        break;
1117    default:
1118        DPRINTF("not implemented read(l) addr=0x" TARGET_FMT_plx "\n", addr);
1119        ret = 0;
1120        break;
1121    }
1122    return ret;
1123}
1124
1125static uint64_t rocker_io_readq(void *opaque, hwaddr addr)
1126{
1127    Rocker *r = opaque;
1128    uint64_t ret;
1129
1130    if (rocker_addr_is_desc_reg(r, addr)) {
1131        unsigned index = ROCKER_RING_INDEX(addr);
1132        unsigned offset = addr & ROCKER_DMA_DESC_MASK;
1133
1134        switch (addr & ROCKER_DMA_DESC_MASK) {
1135        case ROCKER_DMA_DESC_ADDR_OFFSET:
1136            ret = desc_ring_get_base_addr(r->rings[index]);
1137            break;
1138        default:
1139            DPRINTF("not implemented dma reg read(q) addr=0x" TARGET_FMT_plx
1140                    " (ring %d, addr=0x%02x)\n", addr, index, offset);
1141            ret = 0;
1142            break;
1143        }
1144        return ret;
1145    }
1146
1147    switch (addr) {
1148    case ROCKER_BOGUS_REG0:
1149    case ROCKER_BOGUS_REG2:
1150        ret = 0xDEADBABEDEADBABEULL;
1151        break;
1152    case ROCKER_TEST_REG64:
1153        ret = r->test_reg64 * 2;
1154        break;
1155    case ROCKER_TEST_DMA_ADDR:
1156        ret = r->test_dma_addr;
1157        break;
1158    case ROCKER_PORT_PHYS_LINK_STATUS:
1159        ret = rocker_port_phys_link_status(r);
1160        break;
1161    case ROCKER_PORT_PHYS_ENABLE:
1162        ret = rocker_port_phys_enable_read(r);
1163        break;
1164    case ROCKER_SWITCH_ID:
1165        ret = r->switch_id;
1166        break;
1167    default:
1168        DPRINTF("not implemented read(q) addr=0x" TARGET_FMT_plx "\n", addr);
1169        ret = 0;
1170        break;
1171    }
1172    return ret;
1173}
1174
1175static uint64_t rocker_mmio_read(void *opaque, hwaddr addr, unsigned size)
1176{
1177    DPRINTF("Read %s addr " TARGET_FMT_plx ", size %u\n",
1178            rocker_reg_name(opaque, addr), addr, size);
1179
1180    switch (size) {
1181    case 4:
1182        return rocker_io_readl(opaque, addr);
1183    case 8:
1184        return rocker_io_readq(opaque, addr);
1185    }
1186
1187    return -1;
1188}
1189
1190static const MemoryRegionOps rocker_mmio_ops = {
1191    .read = rocker_mmio_read,
1192    .write = rocker_mmio_write,
1193    .endianness = DEVICE_LITTLE_ENDIAN,
1194    .valid = {
1195        .min_access_size = 4,
1196        .max_access_size = 8,
1197    },
1198    .impl = {
1199        .min_access_size = 4,
1200        .max_access_size = 8,
1201    },
1202};
1203
1204static void rocker_msix_vectors_unuse(Rocker *r,
1205                                      unsigned int num_vectors)
1206{
1207    PCIDevice *dev = PCI_DEVICE(r);
1208    int i;
1209
1210    for (i = 0; i < num_vectors; i++) {
1211        msix_vector_unuse(dev, i);
1212    }
1213}
1214
1215static int rocker_msix_vectors_use(Rocker *r,
1216                                   unsigned int num_vectors)
1217{
1218    PCIDevice *dev = PCI_DEVICE(r);
1219    int err;
1220    int i;
1221
1222    for (i = 0; i < num_vectors; i++) {
1223        err = msix_vector_use(dev, i);
1224        if (err) {
1225            goto rollback;
1226        }
1227    }
1228    return 0;
1229
1230rollback:
1231    rocker_msix_vectors_unuse(r, i);
1232    return err;
1233}
1234
1235static int rocker_msix_init(Rocker *r, Error **errp)
1236{
1237    PCIDevice *dev = PCI_DEVICE(r);
1238    int err;
1239
1240    err = msix_init(dev, ROCKER_MSIX_VEC_COUNT(r->fp_ports),
1241                    &r->msix_bar,
1242                    ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_TABLE_OFFSET,
1243                    &r->msix_bar,
1244                    ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_PBA_OFFSET,
1245                    0, errp);
1246    if (err) {
1247        return err;
1248    }
1249
1250    err = rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
1251    if (err) {
1252        goto err_msix_vectors_use;
1253    }
1254
1255    return 0;
1256
1257err_msix_vectors_use:
1258    msix_uninit(dev, &r->msix_bar, &r->msix_bar);
1259    return err;
1260}
1261
1262static void rocker_msix_uninit(Rocker *r)
1263{
1264    PCIDevice *dev = PCI_DEVICE(r);
1265
1266    msix_uninit(dev, &r->msix_bar, &r->msix_bar);
1267    rocker_msix_vectors_unuse(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
1268}
1269
1270static World *rocker_world_type_by_name(Rocker *r, const char *name)
1271{
1272    int i;
1273
1274    for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
1275        if (strcmp(name, world_name(r->worlds[i])) == 0) {
1276            return r->worlds[i];
1277        }
1278    }
1279    return NULL;
1280}
1281
1282static void pci_rocker_realize(PCIDevice *dev, Error **errp)
1283{
1284    Rocker *r = ROCKER(dev);
1285    const MACAddr zero = { .a = { 0, 0, 0, 0, 0, 0 } };
1286    const MACAddr dflt = { .a = { 0x52, 0x54, 0x00, 0x12, 0x35, 0x01 } };
1287    static int sw_index;
1288    int i, err = 0;
1289
1290    /* allocate worlds */
1291
1292    r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r);
1293
1294    if (!r->world_name) {
1295        r->world_name = g_strdup(world_name(r->worlds[ROCKER_WORLD_TYPE_OF_DPA]));
1296    }
1297
1298    r->world_dflt = rocker_world_type_by_name(r, r->world_name);
1299    if (!r->world_dflt) {
1300        error_setg(errp,
1301                "invalid argument requested world %s does not exist",
1302                r->world_name);
1303        goto err_world_type_by_name;
1304    }
1305
1306    /* set up memory-mapped region at BAR0 */
1307
1308    memory_region_init_io(&r->mmio, OBJECT(r), &rocker_mmio_ops, r,
1309                          "rocker-mmio", ROCKER_PCI_BAR0_SIZE);
1310    pci_register_bar(dev, ROCKER_PCI_BAR0_IDX,
1311                     PCI_BASE_ADDRESS_SPACE_MEMORY, &r->mmio);
1312
1313    /* set up memory-mapped region for MSI-X */
1314
1315    memory_region_init(&r->msix_bar, OBJECT(r), "rocker-msix-bar",
1316                       ROCKER_PCI_MSIX_BAR_SIZE);
1317    pci_register_bar(dev, ROCKER_PCI_MSIX_BAR_IDX,
1318                     PCI_BASE_ADDRESS_SPACE_MEMORY, &r->msix_bar);
1319
1320    /* MSI-X init */
1321
1322    err = rocker_msix_init(r, errp);
1323    if (err) {
1324        goto err_msix_init;
1325    }
1326
1327    /* validate switch properties */
1328
1329    if (!r->name) {
1330        r->name = g_strdup(TYPE_ROCKER);
1331    }
1332
1333    if (rocker_find(r->name)) {
1334        error_setg(errp, "%s already exists", r->name);
1335        goto err_duplicate;
1336    }
1337
1338    /* Rocker name is passed in port name requests to OS with the intention
1339     * that the name is used in interface names. Limit the length of the
1340     * rocker name to avoid naming problems in the OS. Also, adding the
1341     * port number as p# and unganged breakout b#, where # is at most 2
1342     * digits, so leave room for it too (-1 for string terminator, -3 for
1343     * p# and -3 for b#)
1344     */
1345#define ROCKER_IFNAMSIZ 16
1346#define MAX_ROCKER_NAME_LEN  (ROCKER_IFNAMSIZ - 1 - 3 - 3)
1347    if (strlen(r->name) > MAX_ROCKER_NAME_LEN) {
1348        error_setg(errp,
1349                "name too long; please shorten to at most %d chars",
1350                MAX_ROCKER_NAME_LEN);
1351        goto err_name_too_long;
1352    }
1353
1354    if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) {
1355        memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt));
1356        r->fp_start_macaddr.a[4] += (sw_index++);
1357    }
1358
1359    if (!r->switch_id) {
1360        memcpy(&r->switch_id, &r->fp_start_macaddr,
1361               sizeof(r->fp_start_macaddr));
1362    }
1363
1364    if (r->fp_ports > ROCKER_FP_PORTS_MAX) {
1365        r->fp_ports = ROCKER_FP_PORTS_MAX;
1366    }
1367
1368    r->rings = g_new(DescRing *, rocker_pci_ring_count(r));
1369
1370    /* Rings are ordered like this:
1371     * - command ring
1372     * - event ring
1373     * - port0 tx ring
1374     * - port0 rx ring
1375     * - port1 tx ring
1376     * - port1 rx ring
1377     * .....
1378     */
1379
1380    for (i = 0; i < rocker_pci_ring_count(r); i++) {
1381        DescRing *ring = desc_ring_alloc(r, i);
1382
1383        if (i == ROCKER_RING_CMD) {
1384            desc_ring_set_consume(ring, cmd_consume, ROCKER_MSIX_VEC_CMD);
1385        } else if (i == ROCKER_RING_EVENT) {
1386            desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_EVENT);
1387        } else if (i % 2 == 0) {
1388            desc_ring_set_consume(ring, tx_consume,
1389                                  ROCKER_MSIX_VEC_TX((i - 2) / 2));
1390        } else if (i % 2 == 1) {
1391            desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_RX((i - 3) / 2));
1392        }
1393
1394        r->rings[i] = ring;
1395    }
1396
1397    for (i = 0; i < r->fp_ports; i++) {
1398        FpPort *port =
1399            fp_port_alloc(r, r->name, &r->fp_start_macaddr,
1400                          i, &r->fp_ports_peers[i]);
1401
1402        r->fp_port[i] = port;
1403        fp_port_set_world(port, r->world_dflt);
1404    }
1405
1406    QLIST_INSERT_HEAD(&rockers, r, next);
1407
1408    return;
1409
1410err_name_too_long:
1411err_duplicate:
1412    rocker_msix_uninit(r);
1413err_msix_init:
1414    object_unparent(OBJECT(&r->msix_bar));
1415    object_unparent(OBJECT(&r->mmio));
1416err_world_type_by_name:
1417    for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
1418        if (r->worlds[i]) {
1419            world_free(r->worlds[i]);
1420        }
1421    }
1422}
1423
1424static void pci_rocker_uninit(PCIDevice *dev)
1425{
1426    Rocker *r = ROCKER(dev);
1427    int i;
1428
1429    QLIST_REMOVE(r, next);
1430
1431    for (i = 0; i < r->fp_ports; i++) {
1432        FpPort *port = r->fp_port[i];
1433
1434        fp_port_free(port);
1435        r->fp_port[i] = NULL;
1436    }
1437
1438    for (i = 0; i < rocker_pci_ring_count(r); i++) {
1439        if (r->rings[i]) {
1440            desc_ring_free(r->rings[i]);
1441        }
1442    }
1443    g_free(r->rings);
1444
1445    rocker_msix_uninit(r);
1446    object_unparent(OBJECT(&r->msix_bar));
1447    object_unparent(OBJECT(&r->mmio));
1448
1449    for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
1450        if (r->worlds[i]) {
1451            world_free(r->worlds[i]);
1452        }
1453    }
1454    g_free(r->fp_ports_peers);
1455}
1456
1457static void rocker_reset(DeviceState *dev)
1458{
1459    Rocker *r = ROCKER(dev);
1460    int i;
1461
1462    for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
1463        if (r->worlds[i]) {
1464            world_reset(r->worlds[i]);
1465        }
1466    }
1467    for (i = 0; i < r->fp_ports; i++) {
1468        fp_port_reset(r->fp_port[i]);
1469        fp_port_set_world(r->fp_port[i], r->world_dflt);
1470    }
1471
1472    r->test_reg = 0;
1473    r->test_reg64 = 0;
1474    r->test_dma_addr = 0;
1475    r->test_dma_size = 0;
1476
1477    for (i = 0; i < rocker_pci_ring_count(r); i++) {
1478        desc_ring_reset(r->rings[i]);
1479    }
1480
1481    DPRINTF("Reset done\n");
1482}
1483
1484static Property rocker_properties[] = {
1485    DEFINE_PROP_STRING("name", Rocker, name),
1486    DEFINE_PROP_STRING("world", Rocker, world_name),
1487    DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker,
1488                        fp_start_macaddr),
1489    DEFINE_PROP_UINT64("switch_id", Rocker,
1490                       switch_id, 0),
1491    DEFINE_PROP_ARRAY("ports", Rocker, fp_ports,
1492                      fp_ports_peers, qdev_prop_netdev, NICPeers),
1493    DEFINE_PROP_END_OF_LIST(),
1494};
1495
1496static const VMStateDescription rocker_vmsd = {
1497    .name = TYPE_ROCKER,
1498    .unmigratable = 1,
1499};
1500
1501static void rocker_class_init(ObjectClass *klass, void *data)
1502{
1503    DeviceClass *dc = DEVICE_CLASS(klass);
1504    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
1505
1506    k->realize = pci_rocker_realize;
1507    k->exit = pci_rocker_uninit;
1508    k->vendor_id = PCI_VENDOR_ID_REDHAT;
1509    k->device_id = PCI_DEVICE_ID_REDHAT_ROCKER;
1510    k->revision = ROCKER_PCI_REVISION;
1511    k->class_id = PCI_CLASS_NETWORK_OTHER;
1512    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
1513    dc->desc = "Rocker Switch";
1514    dc->reset = rocker_reset;
1515    device_class_set_props(dc, rocker_properties);
1516    dc->vmsd = &rocker_vmsd;
1517}
1518
1519static const TypeInfo rocker_info = {
1520    .name          = TYPE_ROCKER,
1521    .parent        = TYPE_PCI_DEVICE,
1522    .instance_size = sizeof(Rocker),
1523    .class_init    = rocker_class_init,
1524    .interfaces = (InterfaceInfo[]) {
1525        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
1526        { },
1527    },
1528};
1529
1530static void rocker_register_types(void)
1531{
1532    type_register_static(&rocker_info);
1533}
1534
1535type_init(rocker_register_types)
1536