cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

etraxfs_eth.c (18017B)


      1/*
      2 * QEMU ETRAX Ethernet Controller.
      3 *
      4 * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a copy
      7 * of this software and associated documentation files (the "Software"), to deal
      8 * in the Software without restriction, including without limitation the rights
      9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 * copies of the Software, and to permit persons to whom the Software is
     11 * furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice shall be included in
     14 * all copies or substantial portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22 * THE SOFTWARE.
     23 */
     24
     25#include "qemu/osdep.h"
     26#include "qapi/error.h"
     27#include "hw/sysbus.h"
     28#include "net/net.h"
     29#include "hw/cris/etraxfs.h"
     30#include "qemu/error-report.h"
     31#include "qemu/module.h"
     32#include "trace.h"
     33#include "qom/object.h"
     34
     35#define D(x)
     36
     37/* Advertisement control register. */
     38#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
     39#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
     40#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
     41#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
     42
     43/*
     44 * The MDIO extensions in the TDK PHY model were reversed engineered from the
     45 * linux driver (PHYID and Diagnostics reg).
     46 * TODO: Add friendly names for the register nums.
     47 */
     48struct qemu_phy
     49{
     50    uint32_t regs[32];
     51
     52    int link;
     53
     54    unsigned int (*read)(struct qemu_phy *phy, unsigned int req);
     55    void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data);
     56};
     57
     58static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req)
     59{
     60    int regnum;
     61    unsigned r = 0;
     62
     63    regnum = req & 0x1f;
     64
     65    switch (regnum) {
     66    case 1:
     67        if (!phy->link) {
     68            break;
     69        }
     70        /* MR1.     */
     71        /* Speeds and modes.  */
     72        r |= (1 << 13) | (1 << 14);
     73        r |= (1 << 11) | (1 << 12);
     74        r |= (1 << 5); /* Autoneg complete.  */
     75        r |= (1 << 3); /* Autoneg able.     */
     76        r |= (1 << 2); /* link.     */
     77        break;
     78    case 5:
     79        /* Link partner ability.
     80           We are kind; always agree with whatever best mode
     81           the guest advertises.  */
     82        r = 1 << 14; /* Success.  */
     83        /* Copy advertised modes.  */
     84        r |= phy->regs[4] & (15 << 5);
     85        /* Autoneg support.  */
     86        r |= 1;
     87        break;
     88    case 18:
     89    {
     90        /* Diagnostics reg.  */
     91        int duplex = 0;
     92        int speed_100 = 0;
     93
     94        if (!phy->link) {
     95            break;
     96        }
     97
     98        /* Are we advertising 100 half or 100 duplex ? */
     99        speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
    100        speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
    101
    102        /* Are we advertising 10 duplex or 100 duplex ? */
    103        duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
    104        duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
    105        r = (speed_100 << 10) | (duplex << 11);
    106    }
    107    break;
    108
    109    default:
    110        r = phy->regs[regnum];
    111        break;
    112    }
    113    trace_mdio_phy_read(regnum, r);
    114    return r;
    115}
    116
    117static void
    118tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data)
    119{
    120    int regnum;
    121
    122    regnum = req & 0x1f;
    123    trace_mdio_phy_write(regnum, data);
    124    switch (regnum) {
    125    default:
    126        phy->regs[regnum] = data;
    127        break;
    128    }
    129}
    130
    131static void
    132tdk_reset(struct qemu_phy *phy)
    133{
    134    phy->regs[0] = 0x3100;
    135    /* PHY Id.  */
    136    phy->regs[2] = 0x0300;
    137    phy->regs[3] = 0xe400;
    138    /* Autonegotiation advertisement reg.  */
    139    phy->regs[4] = 0x01E1;
    140    phy->link = 1;
    141}
    142
    143struct qemu_mdio
    144{
    145    /* bus.     */
    146    int mdc;
    147    int mdio;
    148
    149    /* decoder.  */
    150    enum {
    151        PREAMBLE,
    152        SOF,
    153        OPC,
    154        ADDR,
    155        REQ,
    156        TURNAROUND,
    157        DATA
    158    } state;
    159    unsigned int drive;
    160
    161    unsigned int cnt;
    162    unsigned int addr;
    163    unsigned int opc;
    164    unsigned int req;
    165    unsigned int data;
    166
    167    struct qemu_phy *devs[32];
    168};
    169
    170static void
    171mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
    172{
    173    bus->devs[addr & 0x1f] = phy;
    174}
    175
    176#ifdef USE_THIS_DEAD_CODE
    177static void
    178mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
    179{
    180    bus->devs[addr & 0x1f] = NULL;
    181}
    182#endif
    183
    184static void mdio_read_req(struct qemu_mdio *bus)
    185{
    186    struct qemu_phy *phy;
    187
    188    phy = bus->devs[bus->addr];
    189    if (phy && phy->read) {
    190        bus->data = phy->read(phy, bus->req);
    191    } else {
    192        bus->data = 0xffff;
    193    }
    194}
    195
    196static void mdio_write_req(struct qemu_mdio *bus)
    197{
    198    struct qemu_phy *phy;
    199
    200    phy = bus->devs[bus->addr];
    201    if (phy && phy->write) {
    202        phy->write(phy, bus->req, bus->data);
    203    }
    204}
    205
    206static void mdio_cycle(struct qemu_mdio *bus)
    207{
    208    bus->cnt++;
    209
    210    trace_mdio_bitbang(bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive);
    211#if 0
    212    if (bus->mdc) {
    213        printf("%d", bus->mdio);
    214    }
    215#endif
    216    switch (bus->state) {
    217    case PREAMBLE:
    218        if (bus->mdc) {
    219            if (bus->cnt >= (32 * 2) && !bus->mdio) {
    220                bus->cnt = 0;
    221                bus->state = SOF;
    222                bus->data = 0;
    223            }
    224        }
    225        break;
    226    case SOF:
    227        if (bus->mdc) {
    228            if (bus->mdio != 1) {
    229                printf("WARNING: no SOF\n");
    230            }
    231            if (bus->cnt == 1*2) {
    232                bus->cnt = 0;
    233                bus->opc = 0;
    234                bus->state = OPC;
    235            }
    236        }
    237        break;
    238    case OPC:
    239        if (bus->mdc) {
    240            bus->opc <<= 1;
    241            bus->opc |= bus->mdio & 1;
    242            if (bus->cnt == 2*2) {
    243                bus->cnt = 0;
    244                bus->addr = 0;
    245                bus->state = ADDR;
    246            }
    247        }
    248        break;
    249    case ADDR:
    250        if (bus->mdc) {
    251            bus->addr <<= 1;
    252            bus->addr |= bus->mdio & 1;
    253
    254            if (bus->cnt == 5*2) {
    255                bus->cnt = 0;
    256                bus->req = 0;
    257                bus->state = REQ;
    258            }
    259        }
    260        break;
    261    case REQ:
    262        if (bus->mdc) {
    263            bus->req <<= 1;
    264            bus->req |= bus->mdio & 1;
    265            if (bus->cnt == 5*2) {
    266                bus->cnt = 0;
    267                bus->state = TURNAROUND;
    268            }
    269        }
    270        break;
    271    case TURNAROUND:
    272        if (bus->mdc && bus->cnt == 2*2) {
    273            bus->mdio = 0;
    274            bus->cnt = 0;
    275
    276            if (bus->opc == 2) {
    277                bus->drive = 1;
    278                mdio_read_req(bus);
    279                bus->mdio = bus->data & 1;
    280            }
    281            bus->state = DATA;
    282        }
    283        break;
    284    case DATA:
    285        if (!bus->mdc) {
    286            if (bus->drive) {
    287                bus->mdio = !!(bus->data & (1 << 15));
    288                bus->data <<= 1;
    289            }
    290        } else {
    291            if (!bus->drive) {
    292                bus->data <<= 1;
    293                bus->data |= bus->mdio;
    294            }
    295            if (bus->cnt == 16 * 2) {
    296                bus->cnt = 0;
    297                bus->state = PREAMBLE;
    298                if (!bus->drive) {
    299                    mdio_write_req(bus);
    300                }
    301                bus->drive = 0;
    302            }
    303        }
    304        break;
    305    default:
    306        break;
    307    }
    308}
    309
    310/* ETRAX-FS Ethernet MAC block starts here.  */
    311
    312#define RW_MA0_LO      0x00
    313#define RW_MA0_HI      0x01
    314#define RW_MA1_LO      0x02
    315#define RW_MA1_HI      0x03
    316#define RW_GA_LO      0x04
    317#define RW_GA_HI      0x05
    318#define RW_GEN_CTRL      0x06
    319#define RW_REC_CTRL      0x07
    320#define RW_TR_CTRL      0x08
    321#define RW_CLR_ERR      0x09
    322#define RW_MGM_CTRL      0x0a
    323#define R_STAT          0x0b
    324#define FS_ETH_MAX_REGS      0x17
    325
    326#define TYPE_ETRAX_FS_ETH "etraxfs-eth"
    327OBJECT_DECLARE_SIMPLE_TYPE(ETRAXFSEthState, ETRAX_FS_ETH)
    328
    329struct ETRAXFSEthState {
    330    SysBusDevice parent_obj;
    331
    332    MemoryRegion mmio;
    333    NICState *nic;
    334    NICConf conf;
    335
    336    /* Two addrs in the filter.  */
    337    uint8_t macaddr[2][6];
    338    uint32_t regs[FS_ETH_MAX_REGS];
    339
    340    struct etraxfs_dma_client *dma_out;
    341    struct etraxfs_dma_client *dma_in;
    342
    343    /* MDIO bus.  */
    344    struct qemu_mdio mdio_bus;
    345    unsigned int phyaddr;
    346    int duplex_mismatch;
    347
    348    /* PHY.     */
    349    struct qemu_phy phy;
    350};
    351
    352static void eth_validate_duplex(ETRAXFSEthState *eth)
    353{
    354    struct qemu_phy *phy;
    355    unsigned int phy_duplex;
    356    unsigned int mac_duplex;
    357    int new_mm = 0;
    358
    359    phy = eth->mdio_bus.devs[eth->phyaddr];
    360    phy_duplex = !!(phy->read(phy, 18) & (1 << 11));
    361    mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128);
    362
    363    if (mac_duplex != phy_duplex) {
    364        new_mm = 1;
    365    }
    366
    367    if (eth->regs[RW_GEN_CTRL] & 1) {
    368        if (new_mm != eth->duplex_mismatch) {
    369            if (new_mm) {
    370                printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n",
    371                       mac_duplex, phy_duplex);
    372            } else {
    373                printf("HW: ETH duplex ok.\n");
    374            }
    375        }
    376        eth->duplex_mismatch = new_mm;
    377    }
    378}
    379
    380static uint64_t
    381eth_read(void *opaque, hwaddr addr, unsigned int size)
    382{
    383    ETRAXFSEthState *eth = opaque;
    384    uint32_t r = 0;
    385
    386    addr >>= 2;
    387
    388    switch (addr) {
    389    case R_STAT:
    390        r = eth->mdio_bus.mdio & 1;
    391        break;
    392    default:
    393        r = eth->regs[addr];
    394        D(printf("%s %x\n", __func__, addr * 4));
    395        break;
    396    }
    397    return r;
    398}
    399
    400static void eth_update_ma(ETRAXFSEthState *eth, int ma)
    401{
    402    int reg;
    403    int i = 0;
    404
    405    ma &= 1;
    406
    407    reg = RW_MA0_LO;
    408    if (ma) {
    409        reg = RW_MA1_LO;
    410    }
    411
    412    eth->macaddr[ma][i++] = eth->regs[reg];
    413    eth->macaddr[ma][i++] = eth->regs[reg] >> 8;
    414    eth->macaddr[ma][i++] = eth->regs[reg] >> 16;
    415    eth->macaddr[ma][i++] = eth->regs[reg] >> 24;
    416    eth->macaddr[ma][i++] = eth->regs[reg + 1];
    417    eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8;
    418
    419    D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma,
    420             eth->macaddr[ma][0], eth->macaddr[ma][1],
    421             eth->macaddr[ma][2], eth->macaddr[ma][3],
    422             eth->macaddr[ma][4], eth->macaddr[ma][5]));
    423}
    424
    425static void
    426eth_write(void *opaque, hwaddr addr,
    427          uint64_t val64, unsigned int size)
    428{
    429    ETRAXFSEthState *eth = opaque;
    430    uint32_t value = val64;
    431
    432    addr >>= 2;
    433    switch (addr) {
    434    case RW_MA0_LO:
    435    case RW_MA0_HI:
    436        eth->regs[addr] = value;
    437        eth_update_ma(eth, 0);
    438        break;
    439    case RW_MA1_LO:
    440    case RW_MA1_HI:
    441        eth->regs[addr] = value;
    442        eth_update_ma(eth, 1);
    443        break;
    444
    445    case RW_MGM_CTRL:
    446        /* Attach an MDIO/PHY abstraction.  */
    447        if (value & 2) {
    448            eth->mdio_bus.mdio = value & 1;
    449        }
    450        if (eth->mdio_bus.mdc != (value & 4)) {
    451            mdio_cycle(&eth->mdio_bus);
    452            eth_validate_duplex(eth);
    453        }
    454        eth->mdio_bus.mdc = !!(value & 4);
    455        eth->regs[addr] = value;
    456        break;
    457
    458    case RW_REC_CTRL:
    459        eth->regs[addr] = value;
    460        eth_validate_duplex(eth);
    461        break;
    462
    463    default:
    464        eth->regs[addr] = value;
    465        D(printf("%s %x %x\n", __func__, addr, value));
    466        break;
    467    }
    468}
    469
    470/* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom
    471   filter dropping group addresses we have not joined.    The filter has 64
    472   bits (m). The has function is a simple nible xor of the group addr.    */
    473static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa)
    474{
    475    unsigned int hsh;
    476    int m_individual = eth->regs[RW_REC_CTRL] & 4;
    477    int match;
    478
    479    /* First bit on the wire of a MAC address signals multicast or
    480       physical address.  */
    481    if (!m_individual && !(sa[0] & 1)) {
    482        return 0;
    483    }
    484
    485    /* Calculate the hash index for the GA registers. */
    486    hsh = 0;
    487    hsh ^= (*sa) & 0x3f;
    488    hsh ^= ((*sa) >> 6) & 0x03;
    489    ++sa;
    490    hsh ^= ((*sa) << 2) & 0x03c;
    491    hsh ^= ((*sa) >> 4) & 0xf;
    492    ++sa;
    493    hsh ^= ((*sa) << 4) & 0x30;
    494    hsh ^= ((*sa) >> 2) & 0x3f;
    495    ++sa;
    496    hsh ^= (*sa) & 0x3f;
    497    hsh ^= ((*sa) >> 6) & 0x03;
    498    ++sa;
    499    hsh ^= ((*sa) << 2) & 0x03c;
    500    hsh ^= ((*sa) >> 4) & 0xf;
    501    ++sa;
    502    hsh ^= ((*sa) << 4) & 0x30;
    503    hsh ^= ((*sa) >> 2) & 0x3f;
    504
    505    hsh &= 63;
    506    if (hsh > 31) {
    507        match = eth->regs[RW_GA_HI] & (1 << (hsh - 32));
    508    } else {
    509        match = eth->regs[RW_GA_LO] & (1 << hsh);
    510    }
    511    D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh,
    512             eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match));
    513    return match;
    514}
    515
    516static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
    517{
    518    unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
    519    ETRAXFSEthState *eth = qemu_get_nic_opaque(nc);
    520    int use_ma0 = eth->regs[RW_REC_CTRL] & 1;
    521    int use_ma1 = eth->regs[RW_REC_CTRL] & 2;
    522    int r_bcast = eth->regs[RW_REC_CTRL] & 8;
    523
    524    if (size < 12) {
    525        return -1;
    526    }
    527
    528    D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n",
    529         buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
    530         use_ma0, use_ma1, r_bcast));
    531
    532    /* Does the frame get through the address filters?  */
    533    if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6))
    534        && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6))
    535        && (!r_bcast || memcmp(buf, sa_bcast, 6))
    536        && !eth_match_groupaddr(eth, buf)) {
    537        return size;
    538    }
    539
    540    /* FIXME: Find another way to pass on the fake csum.  */
    541    etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1);
    542
    543    return size;
    544}
    545
    546static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop)
    547{
    548    ETRAXFSEthState *eth = opaque;
    549
    550    D(printf("%s buf=%p len=%d\n", __func__, buf, len));
    551    qemu_send_packet(qemu_get_queue(eth->nic), buf, len);
    552    return len;
    553}
    554
    555static void eth_set_link(NetClientState *nc)
    556{
    557    ETRAXFSEthState *eth = qemu_get_nic_opaque(nc);
    558    D(printf("%s %d\n", __func__, nc->link_down));
    559    eth->phy.link = !nc->link_down;
    560}
    561
    562static const MemoryRegionOps eth_ops = {
    563    .read = eth_read,
    564    .write = eth_write,
    565    .endianness = DEVICE_LITTLE_ENDIAN,
    566    .valid = {
    567        .min_access_size = 4,
    568        .max_access_size = 4
    569    }
    570};
    571
    572static NetClientInfo net_etraxfs_info = {
    573    .type = NET_CLIENT_DRIVER_NIC,
    574    .size = sizeof(NICState),
    575    .receive = eth_receive,
    576    .link_status_changed = eth_set_link,
    577};
    578
    579static void etraxfs_eth_reset(DeviceState *dev)
    580{
    581    ETRAXFSEthState *s = ETRAX_FS_ETH(dev);
    582
    583    memset(s->regs, 0, sizeof(s->regs));
    584    memset(s->macaddr, 0, sizeof(s->macaddr));
    585    s->duplex_mismatch = 0;
    586
    587    s->mdio_bus.mdc = 0;
    588    s->mdio_bus.mdio = 0;
    589    s->mdio_bus.state = 0;
    590    s->mdio_bus.drive = 0;
    591    s->mdio_bus.cnt = 0;
    592    s->mdio_bus.addr = 0;
    593    s->mdio_bus.opc = 0;
    594    s->mdio_bus.req = 0;
    595    s->mdio_bus.data = 0;
    596
    597    tdk_reset(&s->phy);
    598}
    599
    600static void etraxfs_eth_realize(DeviceState *dev, Error **errp)
    601{
    602    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    603    ETRAXFSEthState *s = ETRAX_FS_ETH(dev);
    604
    605    if (!s->dma_out || !s->dma_in) {
    606        error_setg(errp, "Unconnected ETRAX-FS Ethernet MAC");
    607        return;
    608    }
    609
    610    s->dma_out->client.push = eth_tx_push;
    611    s->dma_out->client.opaque = s;
    612    s->dma_in->client.opaque = s;
    613    s->dma_in->client.pull = NULL;
    614
    615    memory_region_init_io(&s->mmio, OBJECT(dev), &eth_ops, s,
    616                          "etraxfs-eth", 0x5c);
    617    sysbus_init_mmio(sbd, &s->mmio);
    618
    619    qemu_macaddr_default_if_unset(&s->conf.macaddr);
    620    s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf,
    621                          object_get_typename(OBJECT(s)), dev->id, s);
    622    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
    623
    624    s->phy.read = tdk_read;
    625    s->phy.write = tdk_write;
    626    mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr);
    627}
    628
    629static Property etraxfs_eth_properties[] = {
    630    DEFINE_PROP_UINT32("phyaddr", ETRAXFSEthState, phyaddr, 1),
    631    DEFINE_NIC_PROPERTIES(ETRAXFSEthState, conf),
    632    DEFINE_PROP_END_OF_LIST(),
    633};
    634
    635static void etraxfs_eth_class_init(ObjectClass *klass, void *data)
    636{
    637    DeviceClass *dc = DEVICE_CLASS(klass);
    638
    639    dc->realize = etraxfs_eth_realize;
    640    dc->reset = etraxfs_eth_reset;
    641    device_class_set_props(dc, etraxfs_eth_properties);
    642    /* Reason: dma_out, dma_in are not user settable */
    643    dc->user_creatable = false;
    644}
    645
    646
    647/* Instantiate an ETRAXFS Ethernet MAC.  */
    648DeviceState *
    649etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr,
    650                 struct etraxfs_dma_client *dma_out,
    651                 struct etraxfs_dma_client *dma_in)
    652{
    653    DeviceState *dev;
    654    qemu_check_nic_model(nd, "fseth");
    655
    656    dev = qdev_new("etraxfs-eth");
    657    qdev_set_nic_properties(dev, nd);
    658    qdev_prop_set_uint32(dev, "phyaddr", phyaddr);
    659
    660    /*
    661     * TODO: QOM design, define a QOM interface for "I am an etraxfs
    662     * DMA client" (which replaces the current 'struct
    663     * etraxfs_dma_client' ad-hoc interface), implement it on the
    664     * ethernet device, and then have QOM link properties on the DMA
    665     * controller device so that you can pass the interface
    666     * implementations to it.
    667     */
    668    ETRAX_FS_ETH(dev)->dma_out = dma_out;
    669    ETRAX_FS_ETH(dev)->dma_in = dma_in;
    670    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
    671    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
    672
    673    return dev;
    674}
    675
    676static const TypeInfo etraxfs_eth_info = {
    677    .name          = TYPE_ETRAX_FS_ETH,
    678    .parent        = TYPE_SYS_BUS_DEVICE,
    679    .instance_size = sizeof(ETRAXFSEthState),
    680    .class_init    = etraxfs_eth_class_init,
    681};
    682
    683static void etraxfs_eth_register_types(void)
    684{
    685    type_register_static(&etraxfs_eth_info);
    686}
    687
    688type_init(etraxfs_eth_register_types)