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

xlnx_csu_dma.c (25301B)


      1/*
      2 * Xilinx Platform CSU Stream DMA emulation
      3 *
      4 * This implementation is based on
      5 * https://github.com/Xilinx/qemu/blob/master/hw/dma/csu_stream_dma.c
      6 *
      7 * This program is free software; you can redistribute it and/or
      8 * modify it under the terms of the GNU General Public License as
      9 * published by the Free Software Foundation; either version 2 or
     10 * (at your option) version 3 of the License.
     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 * You should have received a copy of the GNU General Public License along
     18 * with this program; if not, see <http://www.gnu.org/licenses/>.
     19 */
     20
     21#include "qemu/osdep.h"
     22#include "qemu/log.h"
     23#include "qapi/error.h"
     24#include "hw/irq.h"
     25#include "hw/qdev-properties.h"
     26#include "hw/sysbus.h"
     27#include "migration/vmstate.h"
     28#include "sysemu/dma.h"
     29#include "hw/ptimer.h"
     30#include "hw/stream.h"
     31#include "hw/register.h"
     32#include "hw/dma/xlnx_csu_dma.h"
     33
     34/*
     35 * Ref: UG1087 (v1.7) February 8, 2019
     36 * https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html
     37 * CSUDMA Module section
     38 */
     39REG32(ADDR, 0x0)
     40    FIELD(ADDR, ADDR, 2, 30) /* wo */
     41REG32(SIZE, 0x4)
     42    FIELD(SIZE, SIZE, 2, 27) /* wo */
     43    FIELD(SIZE, LAST_WORD, 0, 1) /* rw, only exists in SRC */
     44REG32(STATUS, 0x8)
     45    FIELD(STATUS, DONE_CNT, 13, 3) /* wtc */
     46    FIELD(STATUS, FIFO_LEVEL, 5, 8) /* ro */
     47    FIELD(STATUS, OUTSTANDING, 1, 4) /* ro */
     48    FIELD(STATUS, BUSY, 0, 1) /* ro */
     49REG32(CTRL, 0xc)
     50    FIELD(CTRL, FIFOTHRESH, 25, 7) /* rw, only exists in DST, reset 0x40 */
     51    FIELD(CTRL, APB_ERR_RESP, 24, 1) /* rw */
     52    FIELD(CTRL, ENDIANNESS, 23, 1) /* rw */
     53    FIELD(CTRL, AXI_BRST_TYPE, 22, 1) /* rw */
     54    FIELD(CTRL, TIMEOUT_VAL, 10, 12) /* rw, reset: 0xFFE */
     55    FIELD(CTRL, FIFO_THRESH, 2, 8) /* rw, reset: 0x80 */
     56    FIELD(CTRL, PAUSE_STRM, 1, 1) /* rw */
     57    FIELD(CTRL, PAUSE_MEM, 0, 1) /* rw */
     58REG32(CRC, 0x10)
     59REG32(INT_STATUS, 0x14)
     60    FIELD(INT_STATUS, FIFO_OVERFLOW, 7, 1) /* wtc */
     61    FIELD(INT_STATUS, INVALID_APB, 6, 1) /* wtc */
     62    FIELD(INT_STATUS, THRESH_HIT, 5, 1) /* wtc */
     63    FIELD(INT_STATUS, TIMEOUT_MEM, 4, 1) /* wtc */
     64    FIELD(INT_STATUS, TIMEOUT_STRM, 3, 1) /* wtc */
     65    FIELD(INT_STATUS, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
     66    FIELD(INT_STATUS, DONE, 1, 1) /* wtc */
     67    FIELD(INT_STATUS, MEM_DONE, 0, 1) /* wtc */
     68REG32(INT_ENABLE, 0x18)
     69    FIELD(INT_ENABLE, FIFO_OVERFLOW, 7, 1) /* wtc */
     70    FIELD(INT_ENABLE, INVALID_APB, 6, 1) /* wtc */
     71    FIELD(INT_ENABLE, THRESH_HIT, 5, 1) /* wtc */
     72    FIELD(INT_ENABLE, TIMEOUT_MEM, 4, 1) /* wtc */
     73    FIELD(INT_ENABLE, TIMEOUT_STRM, 3, 1) /* wtc */
     74    FIELD(INT_ENABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
     75    FIELD(INT_ENABLE, DONE, 1, 1) /* wtc */
     76    FIELD(INT_ENABLE, MEM_DONE, 0, 1) /* wtc */
     77REG32(INT_DISABLE, 0x1c)
     78    FIELD(INT_DISABLE, FIFO_OVERFLOW, 7, 1) /* wtc */
     79    FIELD(INT_DISABLE, INVALID_APB, 6, 1) /* wtc */
     80    FIELD(INT_DISABLE, THRESH_HIT, 5, 1) /* wtc */
     81    FIELD(INT_DISABLE, TIMEOUT_MEM, 4, 1) /* wtc */
     82    FIELD(INT_DISABLE, TIMEOUT_STRM, 3, 1) /* wtc */
     83    FIELD(INT_DISABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
     84    FIELD(INT_DISABLE, DONE, 1, 1) /* wtc */
     85    FIELD(INT_DISABLE, MEM_DONE, 0, 1) /* wtc */
     86REG32(INT_MASK, 0x20)
     87    FIELD(INT_MASK, FIFO_OVERFLOW, 7, 1) /* ro, reset: 0x1 */
     88    FIELD(INT_MASK, INVALID_APB, 6, 1) /* ro, reset: 0x1 */
     89    FIELD(INT_MASK, THRESH_HIT, 5, 1) /* ro, reset: 0x1 */
     90    FIELD(INT_MASK, TIMEOUT_MEM, 4, 1) /* ro, reset: 0x1 */
     91    FIELD(INT_MASK, TIMEOUT_STRM, 3, 1) /* ro, reset: 0x1 */
     92    FIELD(INT_MASK, AXI_BRESP_ERR, 2, 1) /* ro, reset: 0x1, SRC: AXI_RDERR */
     93    FIELD(INT_MASK, DONE, 1, 1) /* ro, reset: 0x1 */
     94    FIELD(INT_MASK, MEM_DONE, 0, 1) /* ro, reset: 0x1 */
     95REG32(CTRL2, 0x24)
     96    FIELD(CTRL2, ARCACHE, 24, 3) /* rw */
     97    FIELD(CTRL2, ROUTE_BIT, 23, 1) /* rw */
     98    FIELD(CTRL2, TIMEOUT_EN, 22, 1) /* rw */
     99    FIELD(CTRL2, TIMEOUT_PRE, 4, 12) /* rw, reset: 0xFFF */
    100    FIELD(CTRL2, MAX_OUTS_CMDS, 0, 4) /* rw, reset: 0x8 */
    101REG32(ADDR_MSB, 0x28)
    102    FIELD(ADDR_MSB, ADDR_MSB, 0, 17) /* wo */
    103
    104#define R_CTRL_TIMEOUT_VAL_RESET    (0xFFE)
    105#define R_CTRL_FIFO_THRESH_RESET    (0x80)
    106#define R_CTRL_FIFOTHRESH_RESET     (0x40)
    107
    108#define R_CTRL2_TIMEOUT_PRE_RESET   (0xFFF)
    109#define R_CTRL2_MAX_OUTS_CMDS_RESET (0x8)
    110
    111#define XLNX_CSU_DMA_ERR_DEBUG      (0)
    112#define XLNX_CSU_DMA_INT_R_MASK     (0xff)
    113
    114/* UG1807: Set the prescaler value for the timeout in clk (~2.5ns) cycles */
    115#define XLNX_CSU_DMA_TIMER_FREQ     (400 * 1000 * 1000)
    116
    117static bool xlnx_csu_dma_is_paused(XlnxCSUDMA *s)
    118{
    119    bool paused;
    120
    121    paused = !!(s->regs[R_CTRL] & R_CTRL_PAUSE_STRM_MASK);
    122    paused |= !!(s->regs[R_CTRL] & R_CTRL_PAUSE_MEM_MASK);
    123
    124    return paused;
    125}
    126
    127static bool xlnx_csu_dma_get_eop(XlnxCSUDMA *s)
    128{
    129    return s->r_size_last_word;
    130}
    131
    132static bool xlnx_csu_dma_burst_is_fixed(XlnxCSUDMA *s)
    133{
    134    return !!(s->regs[R_CTRL] & R_CTRL_AXI_BRST_TYPE_MASK);
    135}
    136
    137static bool xlnx_csu_dma_timeout_enabled(XlnxCSUDMA *s)
    138{
    139    return !!(s->regs[R_CTRL2] & R_CTRL2_TIMEOUT_EN_MASK);
    140}
    141
    142static void xlnx_csu_dma_update_done_cnt(XlnxCSUDMA *s, int a)
    143{
    144    int cnt;
    145
    146    /* Increase DONE_CNT */
    147    cnt = ARRAY_FIELD_EX32(s->regs, STATUS, DONE_CNT) + a;
    148    ARRAY_FIELD_DP32(s->regs, STATUS, DONE_CNT, cnt);
    149}
    150
    151static void xlnx_csu_dma_data_process(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
    152{
    153    uint32_t bswap;
    154    uint32_t i;
    155
    156    bswap = s->regs[R_CTRL] & R_CTRL_ENDIANNESS_MASK;
    157    if (s->is_dst && !bswap) {
    158        /* Fast when ENDIANNESS cleared */
    159        return;
    160    }
    161
    162    for (i = 0; i < len; i += 4) {
    163        uint8_t *b = &buf[i];
    164        union {
    165            uint8_t u8[4];
    166            uint32_t u32;
    167        } v = {
    168            .u8 = { b[0], b[1], b[2], b[3] }
    169        };
    170
    171        if (!s->is_dst) {
    172            s->regs[R_CRC] += v.u32;
    173        }
    174        if (bswap) {
    175            /*
    176             * No point using bswap, we need to writeback
    177             * into a potentially unaligned pointer.
    178             */
    179            b[0] = v.u8[3];
    180            b[1] = v.u8[2];
    181            b[2] = v.u8[1];
    182            b[3] = v.u8[0];
    183        }
    184    }
    185}
    186
    187static void xlnx_csu_dma_update_irq(XlnxCSUDMA *s)
    188{
    189    qemu_set_irq(s->irq, !!(s->regs[R_INT_STATUS] & ~s->regs[R_INT_MASK]));
    190}
    191
    192/* len is in bytes */
    193static uint32_t xlnx_csu_dma_read(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
    194{
    195    hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
    196    MemTxResult result = MEMTX_OK;
    197
    198    if (xlnx_csu_dma_burst_is_fixed(s)) {
    199        uint32_t i;
    200
    201        for (i = 0; i < len && (result == MEMTX_OK); i += s->width) {
    202            uint32_t mlen = MIN(len - i, s->width);
    203
    204            result = address_space_rw(&s->dma_as, addr, s->attr,
    205                                      buf + i, mlen, false);
    206        }
    207    } else {
    208        result = address_space_rw(&s->dma_as, addr, s->attr, buf, len, false);
    209    }
    210
    211    if (result == MEMTX_OK) {
    212        xlnx_csu_dma_data_process(s, buf, len);
    213    } else {
    214        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx
    215                      " for mem read", __func__, addr);
    216        s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK;
    217        xlnx_csu_dma_update_irq(s);
    218    }
    219    return len;
    220}
    221
    222/* len is in bytes */
    223static uint32_t xlnx_csu_dma_write(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
    224{
    225    hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
    226    MemTxResult result = MEMTX_OK;
    227
    228    xlnx_csu_dma_data_process(s, buf, len);
    229    if (xlnx_csu_dma_burst_is_fixed(s)) {
    230        uint32_t i;
    231
    232        for (i = 0; i < len && (result == MEMTX_OK); i += s->width) {
    233            uint32_t mlen = MIN(len - i, s->width);
    234
    235            result = address_space_rw(&s->dma_as, addr, s->attr,
    236                                      buf, mlen, true);
    237            buf += mlen;
    238        }
    239    } else {
    240        result = address_space_rw(&s->dma_as, addr, s->attr, buf, len, true);
    241    }
    242
    243    if (result != MEMTX_OK) {
    244        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx
    245                      " for mem write", __func__, addr);
    246        s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK;
    247        xlnx_csu_dma_update_irq(s);
    248    }
    249    return len;
    250}
    251
    252static void xlnx_csu_dma_done(XlnxCSUDMA *s)
    253{
    254    s->regs[R_STATUS] &= ~R_STATUS_BUSY_MASK;
    255    s->regs[R_INT_STATUS] |= R_INT_STATUS_DONE_MASK;
    256
    257    if (!s->is_dst) {
    258        s->regs[R_INT_STATUS] |= R_INT_STATUS_MEM_DONE_MASK;
    259    }
    260
    261    xlnx_csu_dma_update_done_cnt(s, 1);
    262}
    263
    264static uint32_t xlnx_csu_dma_advance(XlnxCSUDMA *s, uint32_t len)
    265{
    266    uint32_t size = s->regs[R_SIZE];
    267    hwaddr dst = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
    268
    269    assert(len <= size);
    270
    271    size -= len;
    272    s->regs[R_SIZE] = size;
    273
    274    if (!xlnx_csu_dma_burst_is_fixed(s)) {
    275        dst += len;
    276        s->regs[R_ADDR] = (uint32_t) dst;
    277        s->regs[R_ADDR_MSB] = dst >> 32;
    278    }
    279
    280    if (size == 0) {
    281        xlnx_csu_dma_done(s);
    282    }
    283
    284    return size;
    285}
    286
    287static void xlnx_csu_dma_src_notify(void *opaque)
    288{
    289    XlnxCSUDMA *s = XLNX_CSU_DMA(opaque);
    290    unsigned char buf[4 * 1024];
    291    size_t rlen = 0;
    292
    293    ptimer_transaction_begin(s->src_timer);
    294    /* Stop the backpreassure timer */
    295    ptimer_stop(s->src_timer);
    296
    297    while (s->regs[R_SIZE] && !xlnx_csu_dma_is_paused(s) &&
    298           stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) {
    299        uint32_t plen = MIN(s->regs[R_SIZE], sizeof buf);
    300        bool eop = false;
    301
    302        /* Did we fit it all? */
    303        if (s->regs[R_SIZE] == plen && xlnx_csu_dma_get_eop(s)) {
    304            eop = true;
    305        }
    306
    307        /* DMA transfer */
    308        xlnx_csu_dma_read(s, buf, plen);
    309        rlen = stream_push(s->tx_dev, buf, plen, eop);
    310        xlnx_csu_dma_advance(s, rlen);
    311    }
    312
    313    if (xlnx_csu_dma_timeout_enabled(s) && s->regs[R_SIZE] &&
    314        !stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) {
    315        uint32_t timeout = ARRAY_FIELD_EX32(s->regs, CTRL, TIMEOUT_VAL);
    316        uint32_t div = ARRAY_FIELD_EX32(s->regs, CTRL2, TIMEOUT_PRE) + 1;
    317        uint32_t freq = XLNX_CSU_DMA_TIMER_FREQ;
    318
    319        freq /= div;
    320        ptimer_set_freq(s->src_timer, freq);
    321        ptimer_set_count(s->src_timer, timeout);
    322        ptimer_run(s->src_timer, 1);
    323    }
    324
    325    ptimer_transaction_commit(s->src_timer);
    326    xlnx_csu_dma_update_irq(s);
    327}
    328
    329static uint64_t addr_pre_write(RegisterInfo *reg, uint64_t val)
    330{
    331    /* Address is word aligned */
    332    return val & R_ADDR_ADDR_MASK;
    333}
    334
    335static uint64_t size_pre_write(RegisterInfo *reg, uint64_t val)
    336{
    337    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    338
    339    if (s->regs[R_SIZE] != 0) {
    340        qemu_log_mask(LOG_GUEST_ERROR,
    341                      "%s: Starting DMA while already running.\n", __func__);
    342    }
    343
    344    if (!s->is_dst) {
    345        s->r_size_last_word = !!(val & R_SIZE_LAST_WORD_MASK);
    346    }
    347
    348    /* Size is word aligned */
    349    return val & R_SIZE_SIZE_MASK;
    350}
    351
    352static uint64_t size_post_read(RegisterInfo *reg, uint64_t val)
    353{
    354    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    355
    356    return val | s->r_size_last_word;
    357}
    358
    359static void size_post_write(RegisterInfo *reg, uint64_t val)
    360{
    361    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    362
    363    s->regs[R_STATUS] |= R_STATUS_BUSY_MASK;
    364
    365    /*
    366     * Note that if SIZE is programmed to 0, and the DMA is started,
    367     * the interrupts DONE and MEM_DONE will be asserted.
    368     */
    369    if (s->regs[R_SIZE] == 0) {
    370        xlnx_csu_dma_done(s);
    371        xlnx_csu_dma_update_irq(s);
    372        return;
    373    }
    374
    375    /* Set SIZE is considered the last step in transfer configuration */
    376    if (!s->is_dst) {
    377        xlnx_csu_dma_src_notify(s);
    378    } else {
    379        if (s->notify) {
    380            s->notify(s->notify_opaque);
    381        }
    382    }
    383}
    384
    385static uint64_t status_pre_write(RegisterInfo *reg, uint64_t val)
    386{
    387    return val & (R_STATUS_DONE_CNT_MASK | R_STATUS_BUSY_MASK);
    388}
    389
    390static void ctrl_post_write(RegisterInfo *reg, uint64_t val)
    391{
    392    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    393
    394    if (!s->is_dst) {
    395        if (!xlnx_csu_dma_is_paused(s)) {
    396            xlnx_csu_dma_src_notify(s);
    397        }
    398    } else {
    399        if (!xlnx_csu_dma_is_paused(s) && s->notify) {
    400            s->notify(s->notify_opaque);
    401        }
    402    }
    403}
    404
    405static uint64_t int_status_pre_write(RegisterInfo *reg, uint64_t val)
    406{
    407    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    408
    409    /* DMA counter decrements when flag 'DONE' is cleared */
    410    if ((val & s->regs[R_INT_STATUS] & R_INT_STATUS_DONE_MASK)) {
    411        xlnx_csu_dma_update_done_cnt(s, -1);
    412    }
    413
    414    return s->regs[R_INT_STATUS] & ~val;
    415}
    416
    417static void int_status_post_write(RegisterInfo *reg, uint64_t val)
    418{
    419    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    420
    421    xlnx_csu_dma_update_irq(s);
    422}
    423
    424static uint64_t int_enable_pre_write(RegisterInfo *reg, uint64_t val)
    425{
    426    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    427    uint32_t v32 = val;
    428
    429    /*
    430     * R_INT_ENABLE doesn't have its own state.
    431     * It is used to indirectly modify R_INT_MASK.
    432     *
    433     * 1: Enable this interrupt field (the mask bit will be cleared to 0)
    434     * 0: No effect
    435     */
    436    s->regs[R_INT_MASK] &= ~v32;
    437    return 0;
    438}
    439
    440static void int_enable_post_write(RegisterInfo *reg, uint64_t val)
    441{
    442    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    443
    444    xlnx_csu_dma_update_irq(s);
    445}
    446
    447static uint64_t int_disable_pre_write(RegisterInfo *reg, uint64_t val)
    448{
    449    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    450    uint32_t v32 = val;
    451
    452    /*
    453     * R_INT_DISABLE doesn't have its own state.
    454     * It is used to indirectly modify R_INT_MASK.
    455     *
    456     * 1: Disable this interrupt field (the mask bit will be set to 1)
    457     * 0: No effect
    458     */
    459    s->regs[R_INT_MASK] |= v32;
    460    return 0;
    461}
    462
    463static void int_disable_post_write(RegisterInfo *reg, uint64_t val)
    464{
    465    XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
    466
    467    xlnx_csu_dma_update_irq(s);
    468}
    469
    470static uint64_t addr_msb_pre_write(RegisterInfo *reg, uint64_t val)
    471{
    472    return val & R_ADDR_MSB_ADDR_MSB_MASK;
    473}
    474
    475static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = {
    476#define DMACH_REGINFO(NAME, snd)                                              \
    477    (const RegisterAccessInfo []) {                                           \
    478        {                                                                     \
    479            .name = #NAME "_ADDR",                                            \
    480            .addr = A_ADDR,                                                   \
    481            .pre_write = addr_pre_write                                       \
    482        }, {                                                                  \
    483            .name = #NAME "_SIZE",                                            \
    484            .addr = A_SIZE,                                                   \
    485            .pre_write = size_pre_write,                                      \
    486            .post_write = size_post_write,                                    \
    487            .post_read = size_post_read                                       \
    488        }, {                                                                  \
    489            .name = #NAME "_STATUS",                                          \
    490            .addr = A_STATUS,                                                 \
    491            .pre_write = status_pre_write,                                    \
    492            .w1c = R_STATUS_DONE_CNT_MASK,                                    \
    493            .ro = (R_STATUS_BUSY_MASK                                         \
    494                   | R_STATUS_FIFO_LEVEL_MASK                                 \
    495                   | R_STATUS_OUTSTANDING_MASK)                               \
    496        }, {                                                                  \
    497            .name = #NAME "_CTRL",                                            \
    498            .addr = A_CTRL,                                                   \
    499            .post_write = ctrl_post_write,                                    \
    500            .reset = ((R_CTRL_TIMEOUT_VAL_RESET << R_CTRL_TIMEOUT_VAL_SHIFT)  \
    501                      | (R_CTRL_FIFO_THRESH_RESET << R_CTRL_FIFO_THRESH_SHIFT)\
    502                      | (snd ? 0 : R_CTRL_FIFOTHRESH_RESET                    \
    503                         << R_CTRL_FIFOTHRESH_SHIFT))                         \
    504        }, {                                                                  \
    505            .name = #NAME "_CRC",                                             \
    506            .addr = A_CRC,                                                    \
    507        }, {                                                                  \
    508            .name =  #NAME "_INT_STATUS",                                     \
    509            .addr = A_INT_STATUS,                                             \
    510            .pre_write = int_status_pre_write,                                \
    511            .post_write = int_status_post_write                               \
    512        }, {                                                                  \
    513            .name = #NAME "_INT_ENABLE",                                      \
    514            .addr = A_INT_ENABLE,                                             \
    515            .pre_write = int_enable_pre_write,                                \
    516            .post_write = int_enable_post_write                               \
    517        }, {                                                                  \
    518            .name = #NAME "_INT_DISABLE",                                     \
    519            .addr = A_INT_DISABLE,                                            \
    520            .pre_write = int_disable_pre_write,                               \
    521            .post_write = int_disable_post_write                              \
    522        }, {                                                                  \
    523            .name = #NAME "_INT_MASK",                                        \
    524            .addr = A_INT_MASK,                                               \
    525            .ro = ~0,                                                         \
    526            .reset = XLNX_CSU_DMA_INT_R_MASK                                  \
    527        }, {                                                                  \
    528            .name = #NAME "_CTRL2",                                           \
    529            .addr = A_CTRL2,                                                  \
    530            .reset = ((R_CTRL2_TIMEOUT_PRE_RESET                              \
    531                       << R_CTRL2_TIMEOUT_PRE_SHIFT)                          \
    532                      | (R_CTRL2_MAX_OUTS_CMDS_RESET                          \
    533                         << R_CTRL2_MAX_OUTS_CMDS_SHIFT))                     \
    534        }, {                                                                  \
    535            .name = #NAME "_ADDR_MSB",                                        \
    536            .addr = A_ADDR_MSB,                                               \
    537            .pre_write = addr_msb_pre_write                                   \
    538        }                                                                     \
    539    }
    540
    541    DMACH_REGINFO(DMA_SRC, true),
    542    DMACH_REGINFO(DMA_DST, false)
    543};
    544
    545static const MemoryRegionOps xlnx_csu_dma_ops = {
    546    .read = register_read_memory,
    547    .write = register_write_memory,
    548    .endianness = DEVICE_LITTLE_ENDIAN,
    549    .valid = {
    550        .min_access_size = 4,
    551        .max_access_size = 4,
    552    }
    553};
    554
    555static void xlnx_csu_dma_src_timeout_hit(void *opaque)
    556{
    557    XlnxCSUDMA *s = XLNX_CSU_DMA(opaque);
    558
    559    /* Ignore if the timeout is masked */
    560    if (!xlnx_csu_dma_timeout_enabled(s)) {
    561        return;
    562    }
    563
    564    s->regs[R_INT_STATUS] |= R_INT_STATUS_TIMEOUT_STRM_MASK;
    565    xlnx_csu_dma_update_irq(s);
    566}
    567
    568static size_t xlnx_csu_dma_stream_push(StreamSink *obj, uint8_t *buf,
    569                                       size_t len, bool eop)
    570{
    571    XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
    572    uint32_t size = s->regs[R_SIZE];
    573    uint32_t mlen = MIN(size, len) & (~3); /* Size is word aligned */
    574
    575    /* Be called when it's DST */
    576    assert(s->is_dst);
    577
    578    if (size == 0 || len <= 0) {
    579        return 0;
    580    }
    581
    582    if (len && (xlnx_csu_dma_is_paused(s) || mlen == 0)) {
    583        qemu_log_mask(LOG_GUEST_ERROR,
    584                      "csu-dma: DST channel dropping %zd b of data.\n", len);
    585        s->regs[R_INT_STATUS] |= R_INT_STATUS_FIFO_OVERFLOW_MASK;
    586        return len;
    587    }
    588
    589    if (xlnx_csu_dma_write(s, buf, mlen) != mlen) {
    590        return 0;
    591    }
    592
    593    xlnx_csu_dma_advance(s, mlen);
    594    xlnx_csu_dma_update_irq(s);
    595
    596    return mlen;
    597}
    598
    599static bool xlnx_csu_dma_stream_can_push(StreamSink *obj,
    600                                         StreamCanPushNotifyFn notify,
    601                                         void *notify_opaque)
    602{
    603    XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
    604
    605    if (s->regs[R_SIZE] != 0) {
    606        return true;
    607    } else {
    608        s->notify = notify;
    609        s->notify_opaque = notify_opaque;
    610        return false;
    611    }
    612}
    613
    614static void xlnx_csu_dma_reset(DeviceState *dev)
    615{
    616    XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
    617    unsigned int i;
    618
    619    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
    620        register_reset(&s->regs_info[i]);
    621    }
    622}
    623
    624static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp)
    625{
    626    XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
    627    RegisterInfoArray *reg_array;
    628
    629    if (!s->is_dst && !s->tx_dev) {
    630        error_setg(errp, "zynqmp.csu-dma: Stream not connected");
    631        return;
    632    }
    633
    634    if (!s->dma_mr) {
    635        error_setg(errp, TYPE_XLNX_CSU_DMA " 'dma' link not set");
    636        return;
    637    }
    638    address_space_init(&s->dma_as, s->dma_mr, "csu-dma");
    639
    640    reg_array =
    641        register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst],
    642                              XLNX_CSU_DMA_R_MAX,
    643                              s->regs_info, s->regs,
    644                              &xlnx_csu_dma_ops,
    645                              XLNX_CSU_DMA_ERR_DEBUG,
    646                              XLNX_CSU_DMA_R_MAX * 4);
    647    memory_region_add_subregion(&s->iomem,
    648                                0x0,
    649                                &reg_array->mem);
    650
    651    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
    652    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
    653
    654    s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit,
    655                               s, PTIMER_POLICY_DEFAULT);
    656
    657    s->attr = MEMTXATTRS_UNSPECIFIED;
    658
    659    s->r_size_last_word = 0;
    660}
    661
    662static const VMStateDescription vmstate_xlnx_csu_dma = {
    663    .name = TYPE_XLNX_CSU_DMA,
    664    .version_id = 0,
    665    .minimum_version_id = 0,
    666    .minimum_version_id_old = 0,
    667    .fields = (VMStateField[]) {
    668        VMSTATE_PTIMER(src_timer, XlnxCSUDMA),
    669        VMSTATE_UINT16(width, XlnxCSUDMA),
    670        VMSTATE_BOOL(is_dst, XlnxCSUDMA),
    671        VMSTATE_BOOL(r_size_last_word, XlnxCSUDMA),
    672        VMSTATE_UINT32_ARRAY(regs, XlnxCSUDMA, XLNX_CSU_DMA_R_MAX),
    673        VMSTATE_END_OF_LIST(),
    674    }
    675};
    676
    677static Property xlnx_csu_dma_properties[] = {
    678    /*
    679     * Ref PG021, Stream Data Width:
    680     * Data width in bits of the AXI S2MM AXI4-Stream Data bus.
    681     * This value must be equal or less than the Memory Map Data Width.
    682     * Valid values are 8, 16, 32, 64, 128, 512 and 1024.
    683     * "dma-width" is the byte value of the "Stream Data Width".
    684     */
    685    DEFINE_PROP_UINT16("dma-width", XlnxCSUDMA, width, 4),
    686    /*
    687     * The CSU DMA is a two-channel, simple DMA, allowing separate control of
    688     * the SRC (read) channel and DST (write) channel. "is-dst" is used to mark
    689     * which channel the device is connected to.
    690     */
    691    DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true),
    692    DEFINE_PROP_END_OF_LIST(),
    693};
    694
    695static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data)
    696{
    697    DeviceClass *dc = DEVICE_CLASS(klass);
    698    StreamSinkClass *ssc = STREAM_SINK_CLASS(klass);
    699
    700    dc->reset = xlnx_csu_dma_reset;
    701    dc->realize = xlnx_csu_dma_realize;
    702    dc->vmsd = &vmstate_xlnx_csu_dma;
    703    device_class_set_props(dc, xlnx_csu_dma_properties);
    704
    705    ssc->push = xlnx_csu_dma_stream_push;
    706    ssc->can_push = xlnx_csu_dma_stream_can_push;
    707}
    708
    709static void xlnx_csu_dma_init(Object *obj)
    710{
    711    XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
    712
    713    memory_region_init(&s->iomem, obj, TYPE_XLNX_CSU_DMA,
    714                       XLNX_CSU_DMA_R_MAX * 4);
    715
    716    object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SINK,
    717                             (Object **)&s->tx_dev,
    718                             qdev_prop_allow_set_link_before_realize,
    719                             OBJ_PROP_LINK_STRONG);
    720    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
    721                             (Object **)&s->dma_mr,
    722                             qdev_prop_allow_set_link_before_realize,
    723                             OBJ_PROP_LINK_STRONG);
    724}
    725
    726static const TypeInfo xlnx_csu_dma_info = {
    727    .name          = TYPE_XLNX_CSU_DMA,
    728    .parent        = TYPE_SYS_BUS_DEVICE,
    729    .instance_size = sizeof(XlnxCSUDMA),
    730    .class_init    = xlnx_csu_dma_class_init,
    731    .instance_init = xlnx_csu_dma_init,
    732    .interfaces = (InterfaceInfo[]) {
    733        { TYPE_STREAM_SINK },
    734        { }
    735    }
    736};
    737
    738static void xlnx_csu_dma_register_types(void)
    739{
    740    type_register_static(&xlnx_csu_dma_info);
    741}
    742
    743type_init(xlnx_csu_dma_register_types)