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

bcm2835_dma.c (12444B)


      1/*
      2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
      3 *
      4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
      5 * See the COPYING file in the top-level directory.
      6 */
      7
      8#include "qemu/osdep.h"
      9#include "qapi/error.h"
     10#include "hw/dma/bcm2835_dma.h"
     11#include "hw/irq.h"
     12#include "migration/vmstate.h"
     13#include "qemu/log.h"
     14#include "qemu/module.h"
     15
     16/* DMA CS Control and Status bits */
     17#define BCM2708_DMA_ACTIVE      (1 << 0)
     18#define BCM2708_DMA_END         (1 << 1) /* GE */
     19#define BCM2708_DMA_INT         (1 << 2)
     20#define BCM2708_DMA_ISPAUSED    (1 << 4)  /* Pause requested or not active */
     21#define BCM2708_DMA_ISHELD      (1 << 5)  /* Is held by DREQ flow control */
     22#define BCM2708_DMA_ERR         (1 << 8)
     23#define BCM2708_DMA_ABORT       (1 << 30) /* stop current CB, go to next, WO */
     24#define BCM2708_DMA_RESET       (1 << 31) /* WO, self clearing */
     25
     26/* DMA control block "info" field bits */
     27#define BCM2708_DMA_INT_EN      (1 << 0)
     28#define BCM2708_DMA_TDMODE      (1 << 1)
     29#define BCM2708_DMA_WAIT_RESP   (1 << 3)
     30#define BCM2708_DMA_D_INC       (1 << 4)
     31#define BCM2708_DMA_D_WIDTH     (1 << 5)
     32#define BCM2708_DMA_D_DREQ      (1 << 6)
     33#define BCM2708_DMA_D_IGNORE    (1 << 7)
     34#define BCM2708_DMA_S_INC       (1 << 8)
     35#define BCM2708_DMA_S_WIDTH     (1 << 9)
     36#define BCM2708_DMA_S_DREQ      (1 << 10)
     37#define BCM2708_DMA_S_IGNORE    (1 << 11)
     38
     39/* Register offsets */
     40#define BCM2708_DMA_CS          0x00 /* Control and Status */
     41#define BCM2708_DMA_ADDR        0x04 /* Control block address */
     42/* the current control block appears in the following registers - read only */
     43#define BCM2708_DMA_INFO        0x08
     44#define BCM2708_DMA_SOURCE_AD   0x0c
     45#define BCM2708_DMA_DEST_AD     0x10
     46#define BCM2708_DMA_TXFR_LEN    0x14
     47#define BCM2708_DMA_STRIDE      0x18
     48#define BCM2708_DMA_NEXTCB      0x1C
     49#define BCM2708_DMA_DEBUG       0x20
     50
     51#define BCM2708_DMA_INT_STATUS  0xfe0 /* Interrupt status of each channel */
     52#define BCM2708_DMA_ENABLE      0xff0 /* Global enable bits for each channel */
     53
     54#define BCM2708_DMA_CS_RW_MASK  0x30ff0001 /* All RW bits in DMA_CS */
     55
     56static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
     57{
     58    BCM2835DMAChan *ch = &s->chan[c];
     59    uint32_t data, xlen, xlen_td, ylen;
     60    int16_t dst_stride, src_stride;
     61
     62    if (!(s->enable & (1 << c))) {
     63        return;
     64    }
     65
     66    while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
     67        /* CB fetch */
     68        ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
     69        ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
     70        ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
     71        ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
     72        ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
     73        ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
     74
     75        ylen = 1;
     76        if (ch->ti & BCM2708_DMA_TDMODE) {
     77            /* 2D transfer mode */
     78            ylen += (ch->txfr_len >> 16) & 0x3fff;
     79            xlen = ch->txfr_len & 0xffff;
     80            dst_stride = ch->stride >> 16;
     81            src_stride = ch->stride & 0xffff;
     82        } else {
     83            xlen = ch->txfr_len;
     84            dst_stride = 0;
     85            src_stride = 0;
     86        }
     87        xlen_td = xlen;
     88
     89        while (ylen != 0) {
     90            /* Normal transfer mode */
     91            while (xlen != 0) {
     92                if (ch->ti & BCM2708_DMA_S_IGNORE) {
     93                    /* Ignore reads */
     94                    data = 0;
     95                } else {
     96                    data = ldl_le_phys(&s->dma_as, ch->source_ad);
     97                }
     98                if (ch->ti & BCM2708_DMA_S_INC) {
     99                    ch->source_ad += 4;
    100                }
    101
    102                if (ch->ti & BCM2708_DMA_D_IGNORE) {
    103                    /* Ignore writes */
    104                } else {
    105                    stl_le_phys(&s->dma_as, ch->dest_ad, data);
    106                }
    107                if (ch->ti & BCM2708_DMA_D_INC) {
    108                    ch->dest_ad += 4;
    109                }
    110
    111                /* update remaining transfer length */
    112                xlen -= 4;
    113                if (ch->ti & BCM2708_DMA_TDMODE) {
    114                    ch->txfr_len = (ylen << 16) | xlen;
    115                } else {
    116                    ch->txfr_len = xlen;
    117                }
    118            }
    119
    120            if (--ylen != 0) {
    121                ch->source_ad += src_stride;
    122                ch->dest_ad += dst_stride;
    123                xlen = xlen_td;
    124            }
    125        }
    126        ch->cs |= BCM2708_DMA_END;
    127        if (ch->ti & BCM2708_DMA_INT_EN) {
    128            ch->cs |= BCM2708_DMA_INT;
    129            s->int_status |= (1 << c);
    130            qemu_set_irq(ch->irq, 1);
    131        }
    132
    133        /* Process next CB */
    134        ch->conblk_ad = ch->nextconbk;
    135    }
    136
    137    ch->cs &= ~BCM2708_DMA_ACTIVE;
    138    ch->cs |= BCM2708_DMA_ISPAUSED;
    139}
    140
    141static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
    142{
    143    ch->cs = 0;
    144    ch->conblk_ad = 0;
    145}
    146
    147static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
    148                                 unsigned size, unsigned c)
    149{
    150    BCM2835DMAChan *ch;
    151    uint32_t res = 0;
    152
    153    assert(size == 4);
    154    assert(c < BCM2835_DMA_NCHANS);
    155
    156    ch = &s->chan[c];
    157
    158    switch (offset) {
    159    case BCM2708_DMA_CS:
    160        res = ch->cs;
    161        break;
    162    case BCM2708_DMA_ADDR:
    163        res = ch->conblk_ad;
    164        break;
    165    case BCM2708_DMA_INFO:
    166        res = ch->ti;
    167        break;
    168    case BCM2708_DMA_SOURCE_AD:
    169        res = ch->source_ad;
    170        break;
    171    case BCM2708_DMA_DEST_AD:
    172        res = ch->dest_ad;
    173        break;
    174    case BCM2708_DMA_TXFR_LEN:
    175        res = ch->txfr_len;
    176        break;
    177    case BCM2708_DMA_STRIDE:
    178        res = ch->stride;
    179        break;
    180    case BCM2708_DMA_NEXTCB:
    181        res = ch->nextconbk;
    182        break;
    183    case BCM2708_DMA_DEBUG:
    184        res = ch->debug;
    185        break;
    186    default:
    187        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
    188                      __func__, offset);
    189        break;
    190    }
    191    return res;
    192}
    193
    194static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
    195                              uint64_t value, unsigned size, unsigned c)
    196{
    197    BCM2835DMAChan *ch;
    198    uint32_t oldcs;
    199
    200    assert(size == 4);
    201    assert(c < BCM2835_DMA_NCHANS);
    202
    203    ch = &s->chan[c];
    204
    205    switch (offset) {
    206    case BCM2708_DMA_CS:
    207        oldcs = ch->cs;
    208        if (value & BCM2708_DMA_RESET) {
    209            bcm2835_dma_chan_reset(ch);
    210        }
    211        if (value & BCM2708_DMA_ABORT) {
    212            /* abort is a no-op, since we always run to completion */
    213        }
    214        if (value & BCM2708_DMA_END) {
    215            ch->cs &= ~BCM2708_DMA_END;
    216        }
    217        if (value & BCM2708_DMA_INT) {
    218            ch->cs &= ~BCM2708_DMA_INT;
    219            s->int_status &= ~(1 << c);
    220            qemu_set_irq(ch->irq, 0);
    221        }
    222        ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
    223        ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
    224        if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
    225            bcm2835_dma_update(s, c);
    226        }
    227        break;
    228    case BCM2708_DMA_ADDR:
    229        ch->conblk_ad = value;
    230        break;
    231    case BCM2708_DMA_DEBUG:
    232        ch->debug = value;
    233        break;
    234    default:
    235        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
    236                      __func__, offset);
    237        break;
    238    }
    239}
    240
    241static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
    242{
    243    BCM2835DMAState *s = opaque;
    244
    245    if (offset < 0xf00) {
    246        return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
    247    } else {
    248        switch (offset) {
    249        case BCM2708_DMA_INT_STATUS:
    250            return s->int_status;
    251        case BCM2708_DMA_ENABLE:
    252            return s->enable;
    253        default:
    254            qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
    255                          __func__, offset);
    256            return 0;
    257        }
    258    }
    259}
    260
    261static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
    262{
    263    return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
    264}
    265
    266static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
    267                               unsigned size)
    268{
    269    BCM2835DMAState *s = opaque;
    270
    271    if (offset < 0xf00) {
    272        bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
    273    } else {
    274        switch (offset) {
    275        case BCM2708_DMA_INT_STATUS:
    276            break;
    277        case BCM2708_DMA_ENABLE:
    278            s->enable = (value & 0xffff);
    279            break;
    280        default:
    281            qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
    282                          __func__, offset);
    283        }
    284    }
    285
    286}
    287
    288static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
    289                                unsigned size)
    290{
    291    bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
    292}
    293
    294static const MemoryRegionOps bcm2835_dma0_ops = {
    295    .read = bcm2835_dma0_read,
    296    .write = bcm2835_dma0_write,
    297    .endianness = DEVICE_NATIVE_ENDIAN,
    298    .valid.min_access_size = 4,
    299    .valid.max_access_size = 4,
    300};
    301
    302static const MemoryRegionOps bcm2835_dma15_ops = {
    303    .read = bcm2835_dma15_read,
    304    .write = bcm2835_dma15_write,
    305    .endianness = DEVICE_NATIVE_ENDIAN,
    306    .valid.min_access_size = 4,
    307    .valid.max_access_size = 4,
    308};
    309
    310static const VMStateDescription vmstate_bcm2835_dma_chan = {
    311    .name = TYPE_BCM2835_DMA "-chan",
    312    .version_id = 1,
    313    .minimum_version_id = 1,
    314    .fields = (VMStateField[]) {
    315        VMSTATE_UINT32(cs, BCM2835DMAChan),
    316        VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
    317        VMSTATE_UINT32(ti, BCM2835DMAChan),
    318        VMSTATE_UINT32(source_ad, BCM2835DMAChan),
    319        VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
    320        VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
    321        VMSTATE_UINT32(stride, BCM2835DMAChan),
    322        VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
    323        VMSTATE_UINT32(debug, BCM2835DMAChan),
    324        VMSTATE_END_OF_LIST()
    325    }
    326};
    327
    328static const VMStateDescription vmstate_bcm2835_dma = {
    329    .name = TYPE_BCM2835_DMA,
    330    .version_id = 1,
    331    .minimum_version_id = 1,
    332    .fields = (VMStateField[]) {
    333        VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
    334                             vmstate_bcm2835_dma_chan, BCM2835DMAChan),
    335        VMSTATE_UINT32(int_status, BCM2835DMAState),
    336        VMSTATE_UINT32(enable, BCM2835DMAState),
    337        VMSTATE_END_OF_LIST()
    338    }
    339};
    340
    341static void bcm2835_dma_init(Object *obj)
    342{
    343    BCM2835DMAState *s = BCM2835_DMA(obj);
    344    int n;
    345
    346    /* DMA channels 0-14 occupy a contiguous block of IO memory, along
    347     * with the global enable and interrupt status bits. Channel 15
    348     * has the same register map, but is mapped at a discontiguous
    349     * address in a separate IO block.
    350     */
    351    memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
    352                          TYPE_BCM2835_DMA, 0x1000);
    353    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
    354
    355    memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
    356                          TYPE_BCM2835_DMA "-chan15", 0x100);
    357    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
    358
    359    for (n = 0; n < 16; n++) {
    360        sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
    361    }
    362}
    363
    364static void bcm2835_dma_reset(DeviceState *dev)
    365{
    366    BCM2835DMAState *s = BCM2835_DMA(dev);
    367    int n;
    368
    369    s->enable = 0xffff;
    370    s->int_status = 0;
    371    for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
    372        bcm2835_dma_chan_reset(&s->chan[n]);
    373    }
    374}
    375
    376static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
    377{
    378    BCM2835DMAState *s = BCM2835_DMA(dev);
    379    Object *obj;
    380
    381    obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort);
    382    s->dma_mr = MEMORY_REGION(obj);
    383    address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_DMA "-memory");
    384
    385    bcm2835_dma_reset(dev);
    386}
    387
    388static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
    389{
    390    DeviceClass *dc = DEVICE_CLASS(klass);
    391
    392    dc->realize = bcm2835_dma_realize;
    393    dc->reset = bcm2835_dma_reset;
    394    dc->vmsd = &vmstate_bcm2835_dma;
    395}
    396
    397static TypeInfo bcm2835_dma_info = {
    398    .name          = TYPE_BCM2835_DMA,
    399    .parent        = TYPE_SYS_BUS_DEVICE,
    400    .instance_size = sizeof(BCM2835DMAState),
    401    .class_init    = bcm2835_dma_class_init,
    402    .instance_init = bcm2835_dma_init,
    403};
    404
    405static void bcm2835_dma_register_types(void)
    406{
    407    type_register_static(&bcm2835_dma_info);
    408}
    409
    410type_init(bcm2835_dma_register_types)