18d7f2e76SPhilippe Mathieu-Daudé /* 28d7f2e76SPhilippe Mathieu-Daudé * DMA helper functions 38d7f2e76SPhilippe Mathieu-Daudé * 48d7f2e76SPhilippe Mathieu-Daudé * Copyright (c) 2009,2020 Red Hat 58d7f2e76SPhilippe Mathieu-Daudé * 68d7f2e76SPhilippe Mathieu-Daudé * This work is licensed under the terms of the GNU General Public License 78d7f2e76SPhilippe Mathieu-Daudé * (GNU GPL), version 2 or later. 88d7f2e76SPhilippe Mathieu-Daudé */ 98d7f2e76SPhilippe Mathieu-Daudé 108d7f2e76SPhilippe Mathieu-Daudé #include "qemu/osdep.h" 118d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/block-backend.h" 128d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/dma.h" 138d7f2e76SPhilippe Mathieu-Daudé #include "trace/trace-root.h" 148d7f2e76SPhilippe Mathieu-Daudé #include "qemu/thread.h" 158d7f2e76SPhilippe Mathieu-Daudé #include "qemu/main-loop.h" 168d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/cpu-timers.h" 178d7f2e76SPhilippe Mathieu-Daudé #include "qemu/range.h" 188d7f2e76SPhilippe Mathieu-Daudé 198d7f2e76SPhilippe Mathieu-Daudé /* #define DEBUG_IOMMU */ 208d7f2e76SPhilippe Mathieu-Daudé 218d7f2e76SPhilippe Mathieu-Daudé MemTxResult dma_memory_set(AddressSpace *as, dma_addr_t addr, 228d7f2e76SPhilippe Mathieu-Daudé uint8_t c, dma_addr_t len, MemTxAttrs attrs) 238d7f2e76SPhilippe Mathieu-Daudé { 248d7f2e76SPhilippe Mathieu-Daudé dma_barrier(as, DMA_DIRECTION_FROM_DEVICE); 258d7f2e76SPhilippe Mathieu-Daudé 268d7f2e76SPhilippe Mathieu-Daudé return address_space_set(as, addr, c, len, attrs); 278d7f2e76SPhilippe Mathieu-Daudé } 288d7f2e76SPhilippe Mathieu-Daudé 298d7f2e76SPhilippe Mathieu-Daudé void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint, 308d7f2e76SPhilippe Mathieu-Daudé AddressSpace *as) 318d7f2e76SPhilippe Mathieu-Daudé { 328d7f2e76SPhilippe Mathieu-Daudé qsg->sg = g_new(ScatterGatherEntry, alloc_hint); 338d7f2e76SPhilippe Mathieu-Daudé qsg->nsg = 0; 348d7f2e76SPhilippe Mathieu-Daudé qsg->nalloc = alloc_hint; 358d7f2e76SPhilippe Mathieu-Daudé qsg->size = 0; 368d7f2e76SPhilippe Mathieu-Daudé qsg->as = as; 378d7f2e76SPhilippe Mathieu-Daudé qsg->dev = dev; 388d7f2e76SPhilippe Mathieu-Daudé object_ref(OBJECT(dev)); 398d7f2e76SPhilippe Mathieu-Daudé } 408d7f2e76SPhilippe Mathieu-Daudé 418d7f2e76SPhilippe Mathieu-Daudé void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len) 428d7f2e76SPhilippe Mathieu-Daudé { 438d7f2e76SPhilippe Mathieu-Daudé if (qsg->nsg == qsg->nalloc) { 448d7f2e76SPhilippe Mathieu-Daudé qsg->nalloc = 2 * qsg->nalloc + 1; 458d7f2e76SPhilippe Mathieu-Daudé qsg->sg = g_renew(ScatterGatherEntry, qsg->sg, qsg->nalloc); 468d7f2e76SPhilippe Mathieu-Daudé } 478d7f2e76SPhilippe Mathieu-Daudé qsg->sg[qsg->nsg].base = base; 488d7f2e76SPhilippe Mathieu-Daudé qsg->sg[qsg->nsg].len = len; 498d7f2e76SPhilippe Mathieu-Daudé qsg->size += len; 508d7f2e76SPhilippe Mathieu-Daudé ++qsg->nsg; 518d7f2e76SPhilippe Mathieu-Daudé } 528d7f2e76SPhilippe Mathieu-Daudé 538d7f2e76SPhilippe Mathieu-Daudé void qemu_sglist_destroy(QEMUSGList *qsg) 548d7f2e76SPhilippe Mathieu-Daudé { 558d7f2e76SPhilippe Mathieu-Daudé object_unref(OBJECT(qsg->dev)); 568d7f2e76SPhilippe Mathieu-Daudé g_free(qsg->sg); 578d7f2e76SPhilippe Mathieu-Daudé memset(qsg, 0, sizeof(*qsg)); 588d7f2e76SPhilippe Mathieu-Daudé } 598d7f2e76SPhilippe Mathieu-Daudé 608d7f2e76SPhilippe Mathieu-Daudé typedef struct { 618d7f2e76SPhilippe Mathieu-Daudé BlockAIOCB common; 628d7f2e76SPhilippe Mathieu-Daudé AioContext *ctx; 638d7f2e76SPhilippe Mathieu-Daudé BlockAIOCB *acb; 648d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg; 658d7f2e76SPhilippe Mathieu-Daudé uint32_t align; 668d7f2e76SPhilippe Mathieu-Daudé uint64_t offset; 678d7f2e76SPhilippe Mathieu-Daudé DMADirection dir; 688d7f2e76SPhilippe Mathieu-Daudé int sg_cur_index; 698d7f2e76SPhilippe Mathieu-Daudé dma_addr_t sg_cur_byte; 708d7f2e76SPhilippe Mathieu-Daudé QEMUIOVector iov; 718d7f2e76SPhilippe Mathieu-Daudé QEMUBH *bh; 728d7f2e76SPhilippe Mathieu-Daudé DMAIOFunc *io_func; 738d7f2e76SPhilippe Mathieu-Daudé void *io_func_opaque; 748d7f2e76SPhilippe Mathieu-Daudé } DMAAIOCB; 758d7f2e76SPhilippe Mathieu-Daudé 768d7f2e76SPhilippe Mathieu-Daudé static void dma_blk_cb(void *opaque, int ret); 778d7f2e76SPhilippe Mathieu-Daudé 788d7f2e76SPhilippe Mathieu-Daudé static void reschedule_dma(void *opaque) 798d7f2e76SPhilippe Mathieu-Daudé { 808d7f2e76SPhilippe Mathieu-Daudé DMAAIOCB *dbs = (DMAAIOCB *)opaque; 818d7f2e76SPhilippe Mathieu-Daudé 828d7f2e76SPhilippe Mathieu-Daudé assert(!dbs->acb && dbs->bh); 838d7f2e76SPhilippe Mathieu-Daudé qemu_bh_delete(dbs->bh); 848d7f2e76SPhilippe Mathieu-Daudé dbs->bh = NULL; 858d7f2e76SPhilippe Mathieu-Daudé dma_blk_cb(dbs, 0); 868d7f2e76SPhilippe Mathieu-Daudé } 878d7f2e76SPhilippe Mathieu-Daudé 888d7f2e76SPhilippe Mathieu-Daudé static void dma_blk_unmap(DMAAIOCB *dbs) 898d7f2e76SPhilippe Mathieu-Daudé { 908d7f2e76SPhilippe Mathieu-Daudé int i; 918d7f2e76SPhilippe Mathieu-Daudé 928d7f2e76SPhilippe Mathieu-Daudé for (i = 0; i < dbs->iov.niov; ++i) { 938d7f2e76SPhilippe Mathieu-Daudé dma_memory_unmap(dbs->sg->as, dbs->iov.iov[i].iov_base, 948d7f2e76SPhilippe Mathieu-Daudé dbs->iov.iov[i].iov_len, dbs->dir, 958d7f2e76SPhilippe Mathieu-Daudé dbs->iov.iov[i].iov_len); 968d7f2e76SPhilippe Mathieu-Daudé } 978d7f2e76SPhilippe Mathieu-Daudé qemu_iovec_reset(&dbs->iov); 988d7f2e76SPhilippe Mathieu-Daudé } 998d7f2e76SPhilippe Mathieu-Daudé 1008d7f2e76SPhilippe Mathieu-Daudé static void dma_complete(DMAAIOCB *dbs, int ret) 1018d7f2e76SPhilippe Mathieu-Daudé { 1028d7f2e76SPhilippe Mathieu-Daudé trace_dma_complete(dbs, ret, dbs->common.cb); 1038d7f2e76SPhilippe Mathieu-Daudé 1048d7f2e76SPhilippe Mathieu-Daudé assert(!dbs->acb && !dbs->bh); 1058d7f2e76SPhilippe Mathieu-Daudé dma_blk_unmap(dbs); 1068d7f2e76SPhilippe Mathieu-Daudé if (dbs->common.cb) { 1078d7f2e76SPhilippe Mathieu-Daudé dbs->common.cb(dbs->common.opaque, ret); 1088d7f2e76SPhilippe Mathieu-Daudé } 1098d7f2e76SPhilippe Mathieu-Daudé qemu_iovec_destroy(&dbs->iov); 1108d7f2e76SPhilippe Mathieu-Daudé qemu_aio_unref(dbs); 1118d7f2e76SPhilippe Mathieu-Daudé } 1128d7f2e76SPhilippe Mathieu-Daudé 1138d7f2e76SPhilippe Mathieu-Daudé static void dma_blk_cb(void *opaque, int ret) 1148d7f2e76SPhilippe Mathieu-Daudé { 1158d7f2e76SPhilippe Mathieu-Daudé DMAAIOCB *dbs = (DMAAIOCB *)opaque; 1168d7f2e76SPhilippe Mathieu-Daudé AioContext *ctx = dbs->ctx; 1178d7f2e76SPhilippe Mathieu-Daudé dma_addr_t cur_addr, cur_len; 1188d7f2e76SPhilippe Mathieu-Daudé void *mem; 1198d7f2e76SPhilippe Mathieu-Daudé 1208d7f2e76SPhilippe Mathieu-Daudé trace_dma_blk_cb(dbs, ret); 1218d7f2e76SPhilippe Mathieu-Daudé 12210bcb0d9SStefan Hajnoczi /* DMAAIOCB is not thread-safe and must be accessed only from dbs->ctx */ 12310bcb0d9SStefan Hajnoczi assert(ctx == qemu_get_current_aio_context()); 12410bcb0d9SStefan Hajnoczi 1258d7f2e76SPhilippe Mathieu-Daudé dbs->acb = NULL; 1268d7f2e76SPhilippe Mathieu-Daudé dbs->offset += dbs->iov.size; 1278d7f2e76SPhilippe Mathieu-Daudé 1288d7f2e76SPhilippe Mathieu-Daudé if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) { 1298d7f2e76SPhilippe Mathieu-Daudé dma_complete(dbs, ret); 130e661a247SStefan Hajnoczi return; 1318d7f2e76SPhilippe Mathieu-Daudé } 1328d7f2e76SPhilippe Mathieu-Daudé dma_blk_unmap(dbs); 1338d7f2e76SPhilippe Mathieu-Daudé 1348d7f2e76SPhilippe Mathieu-Daudé while (dbs->sg_cur_index < dbs->sg->nsg) { 1358d7f2e76SPhilippe Mathieu-Daudé cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte; 1368d7f2e76SPhilippe Mathieu-Daudé cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte; 1378d7f2e76SPhilippe Mathieu-Daudé mem = dma_memory_map(dbs->sg->as, cur_addr, &cur_len, dbs->dir, 1388d7f2e76SPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED); 1398d7f2e76SPhilippe Mathieu-Daudé /* 1408d7f2e76SPhilippe Mathieu-Daudé * Make reads deterministic in icount mode. Windows sometimes issues 1418d7f2e76SPhilippe Mathieu-Daudé * disk read requests with overlapping SGs. It leads 1428d7f2e76SPhilippe Mathieu-Daudé * to non-determinism, because resulting buffer contents may be mixed 1438d7f2e76SPhilippe Mathieu-Daudé * from several sectors. This code splits all SGs into several 1448d7f2e76SPhilippe Mathieu-Daudé * groups. SGs in every group do not overlap. 1458d7f2e76SPhilippe Mathieu-Daudé */ 1468d7f2e76SPhilippe Mathieu-Daudé if (mem && icount_enabled() && dbs->dir == DMA_DIRECTION_FROM_DEVICE) { 1478d7f2e76SPhilippe Mathieu-Daudé int i; 1488d7f2e76SPhilippe Mathieu-Daudé for (i = 0 ; i < dbs->iov.niov ; ++i) { 1498d7f2e76SPhilippe Mathieu-Daudé if (ranges_overlap((intptr_t)dbs->iov.iov[i].iov_base, 1508d7f2e76SPhilippe Mathieu-Daudé dbs->iov.iov[i].iov_len, (intptr_t)mem, 1518d7f2e76SPhilippe Mathieu-Daudé cur_len)) { 1528d7f2e76SPhilippe Mathieu-Daudé dma_memory_unmap(dbs->sg->as, mem, cur_len, 1538d7f2e76SPhilippe Mathieu-Daudé dbs->dir, cur_len); 1548d7f2e76SPhilippe Mathieu-Daudé mem = NULL; 1558d7f2e76SPhilippe Mathieu-Daudé break; 1568d7f2e76SPhilippe Mathieu-Daudé } 1578d7f2e76SPhilippe Mathieu-Daudé } 1588d7f2e76SPhilippe Mathieu-Daudé } 1598d7f2e76SPhilippe Mathieu-Daudé if (!mem) 1608d7f2e76SPhilippe Mathieu-Daudé break; 1618d7f2e76SPhilippe Mathieu-Daudé qemu_iovec_add(&dbs->iov, mem, cur_len); 1628d7f2e76SPhilippe Mathieu-Daudé dbs->sg_cur_byte += cur_len; 1638d7f2e76SPhilippe Mathieu-Daudé if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) { 1648d7f2e76SPhilippe Mathieu-Daudé dbs->sg_cur_byte = 0; 1658d7f2e76SPhilippe Mathieu-Daudé ++dbs->sg_cur_index; 1668d7f2e76SPhilippe Mathieu-Daudé } 1678d7f2e76SPhilippe Mathieu-Daudé } 1688d7f2e76SPhilippe Mathieu-Daudé 1698d7f2e76SPhilippe Mathieu-Daudé if (dbs->iov.size == 0) { 1708d7f2e76SPhilippe Mathieu-Daudé trace_dma_map_wait(dbs); 1718d7f2e76SPhilippe Mathieu-Daudé dbs->bh = aio_bh_new(ctx, reschedule_dma, dbs); 172*5c627197SMattias Nissler address_space_register_map_client(dbs->sg->as, dbs->bh); 173e661a247SStefan Hajnoczi return; 1748d7f2e76SPhilippe Mathieu-Daudé } 1758d7f2e76SPhilippe Mathieu-Daudé 1768d7f2e76SPhilippe Mathieu-Daudé if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) { 1778d7f2e76SPhilippe Mathieu-Daudé qemu_iovec_discard_back(&dbs->iov, 1788d7f2e76SPhilippe Mathieu-Daudé QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align)); 1798d7f2e76SPhilippe Mathieu-Daudé } 1808d7f2e76SPhilippe Mathieu-Daudé 1818d7f2e76SPhilippe Mathieu-Daudé dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, 1828d7f2e76SPhilippe Mathieu-Daudé dma_blk_cb, dbs, dbs->io_func_opaque); 1838d7f2e76SPhilippe Mathieu-Daudé assert(dbs->acb); 1848d7f2e76SPhilippe Mathieu-Daudé } 1858d7f2e76SPhilippe Mathieu-Daudé 1868d7f2e76SPhilippe Mathieu-Daudé static void dma_aio_cancel(BlockAIOCB *acb) 1878d7f2e76SPhilippe Mathieu-Daudé { 1888d7f2e76SPhilippe Mathieu-Daudé DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common); 1898d7f2e76SPhilippe Mathieu-Daudé 1908d7f2e76SPhilippe Mathieu-Daudé trace_dma_aio_cancel(dbs); 1918d7f2e76SPhilippe Mathieu-Daudé 1928d7f2e76SPhilippe Mathieu-Daudé assert(!(dbs->acb && dbs->bh)); 1938d7f2e76SPhilippe Mathieu-Daudé if (dbs->acb) { 1948d7f2e76SPhilippe Mathieu-Daudé /* This will invoke dma_blk_cb. */ 1958d7f2e76SPhilippe Mathieu-Daudé blk_aio_cancel_async(dbs->acb); 1968d7f2e76SPhilippe Mathieu-Daudé return; 1978d7f2e76SPhilippe Mathieu-Daudé } 1988d7f2e76SPhilippe Mathieu-Daudé 1998d7f2e76SPhilippe Mathieu-Daudé if (dbs->bh) { 200*5c627197SMattias Nissler address_space_unregister_map_client(dbs->sg->as, dbs->bh); 2018d7f2e76SPhilippe Mathieu-Daudé qemu_bh_delete(dbs->bh); 2028d7f2e76SPhilippe Mathieu-Daudé dbs->bh = NULL; 2038d7f2e76SPhilippe Mathieu-Daudé } 2048d7f2e76SPhilippe Mathieu-Daudé if (dbs->common.cb) { 2058d7f2e76SPhilippe Mathieu-Daudé dbs->common.cb(dbs->common.opaque, -ECANCELED); 2068d7f2e76SPhilippe Mathieu-Daudé } 2078d7f2e76SPhilippe Mathieu-Daudé } 2088d7f2e76SPhilippe Mathieu-Daudé 2098d7f2e76SPhilippe Mathieu-Daudé static const AIOCBInfo dma_aiocb_info = { 2108d7f2e76SPhilippe Mathieu-Daudé .aiocb_size = sizeof(DMAAIOCB), 2118d7f2e76SPhilippe Mathieu-Daudé .cancel_async = dma_aio_cancel, 2128d7f2e76SPhilippe Mathieu-Daudé }; 2138d7f2e76SPhilippe Mathieu-Daudé 2148d7f2e76SPhilippe Mathieu-Daudé BlockAIOCB *dma_blk_io(AioContext *ctx, 2158d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg, uint64_t offset, uint32_t align, 2168d7f2e76SPhilippe Mathieu-Daudé DMAIOFunc *io_func, void *io_func_opaque, 2178d7f2e76SPhilippe Mathieu-Daudé BlockCompletionFunc *cb, 2188d7f2e76SPhilippe Mathieu-Daudé void *opaque, DMADirection dir) 2198d7f2e76SPhilippe Mathieu-Daudé { 2208d7f2e76SPhilippe Mathieu-Daudé DMAAIOCB *dbs = qemu_aio_get(&dma_aiocb_info, NULL, cb, opaque); 2218d7f2e76SPhilippe Mathieu-Daudé 2228d7f2e76SPhilippe Mathieu-Daudé trace_dma_blk_io(dbs, io_func_opaque, offset, (dir == DMA_DIRECTION_TO_DEVICE)); 2238d7f2e76SPhilippe Mathieu-Daudé 2248d7f2e76SPhilippe Mathieu-Daudé dbs->acb = NULL; 2258d7f2e76SPhilippe Mathieu-Daudé dbs->sg = sg; 2268d7f2e76SPhilippe Mathieu-Daudé dbs->ctx = ctx; 2278d7f2e76SPhilippe Mathieu-Daudé dbs->offset = offset; 2288d7f2e76SPhilippe Mathieu-Daudé dbs->align = align; 2298d7f2e76SPhilippe Mathieu-Daudé dbs->sg_cur_index = 0; 2308d7f2e76SPhilippe Mathieu-Daudé dbs->sg_cur_byte = 0; 2318d7f2e76SPhilippe Mathieu-Daudé dbs->dir = dir; 2328d7f2e76SPhilippe Mathieu-Daudé dbs->io_func = io_func; 2338d7f2e76SPhilippe Mathieu-Daudé dbs->io_func_opaque = io_func_opaque; 2348d7f2e76SPhilippe Mathieu-Daudé dbs->bh = NULL; 2358d7f2e76SPhilippe Mathieu-Daudé qemu_iovec_init(&dbs->iov, sg->nsg); 2368d7f2e76SPhilippe Mathieu-Daudé dma_blk_cb(dbs, 0); 2378d7f2e76SPhilippe Mathieu-Daudé return &dbs->common; 2388d7f2e76SPhilippe Mathieu-Daudé } 2398d7f2e76SPhilippe Mathieu-Daudé 2408d7f2e76SPhilippe Mathieu-Daudé 2418d7f2e76SPhilippe Mathieu-Daudé static 2428d7f2e76SPhilippe Mathieu-Daudé BlockAIOCB *dma_blk_read_io_func(int64_t offset, QEMUIOVector *iov, 2438d7f2e76SPhilippe Mathieu-Daudé BlockCompletionFunc *cb, void *cb_opaque, 2448d7f2e76SPhilippe Mathieu-Daudé void *opaque) 2458d7f2e76SPhilippe Mathieu-Daudé { 2468d7f2e76SPhilippe Mathieu-Daudé BlockBackend *blk = opaque; 2478d7f2e76SPhilippe Mathieu-Daudé return blk_aio_preadv(blk, offset, iov, 0, cb, cb_opaque); 2488d7f2e76SPhilippe Mathieu-Daudé } 2498d7f2e76SPhilippe Mathieu-Daudé 2508d7f2e76SPhilippe Mathieu-Daudé BlockAIOCB *dma_blk_read(BlockBackend *blk, 2518d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg, uint64_t offset, uint32_t align, 2528d7f2e76SPhilippe Mathieu-Daudé void (*cb)(void *opaque, int ret), void *opaque) 2538d7f2e76SPhilippe Mathieu-Daudé { 2548d7f2e76SPhilippe Mathieu-Daudé return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, 2558d7f2e76SPhilippe Mathieu-Daudé dma_blk_read_io_func, blk, cb, opaque, 2568d7f2e76SPhilippe Mathieu-Daudé DMA_DIRECTION_FROM_DEVICE); 2578d7f2e76SPhilippe Mathieu-Daudé } 2588d7f2e76SPhilippe Mathieu-Daudé 2598d7f2e76SPhilippe Mathieu-Daudé static 2608d7f2e76SPhilippe Mathieu-Daudé BlockAIOCB *dma_blk_write_io_func(int64_t offset, QEMUIOVector *iov, 2618d7f2e76SPhilippe Mathieu-Daudé BlockCompletionFunc *cb, void *cb_opaque, 2628d7f2e76SPhilippe Mathieu-Daudé void *opaque) 2638d7f2e76SPhilippe Mathieu-Daudé { 2648d7f2e76SPhilippe Mathieu-Daudé BlockBackend *blk = opaque; 2658d7f2e76SPhilippe Mathieu-Daudé return blk_aio_pwritev(blk, offset, iov, 0, cb, cb_opaque); 2668d7f2e76SPhilippe Mathieu-Daudé } 2678d7f2e76SPhilippe Mathieu-Daudé 2688d7f2e76SPhilippe Mathieu-Daudé BlockAIOCB *dma_blk_write(BlockBackend *blk, 2698d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg, uint64_t offset, uint32_t align, 2708d7f2e76SPhilippe Mathieu-Daudé void (*cb)(void *opaque, int ret), void *opaque) 2718d7f2e76SPhilippe Mathieu-Daudé { 2728d7f2e76SPhilippe Mathieu-Daudé return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, 2738d7f2e76SPhilippe Mathieu-Daudé dma_blk_write_io_func, blk, cb, opaque, 2748d7f2e76SPhilippe Mathieu-Daudé DMA_DIRECTION_TO_DEVICE); 2758d7f2e76SPhilippe Mathieu-Daudé } 2768d7f2e76SPhilippe Mathieu-Daudé 2778d7f2e76SPhilippe Mathieu-Daudé 2788d7f2e76SPhilippe Mathieu-Daudé static MemTxResult dma_buf_rw(void *buf, dma_addr_t len, dma_addr_t *residual, 2798d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg, DMADirection dir, 2808d7f2e76SPhilippe Mathieu-Daudé MemTxAttrs attrs) 2818d7f2e76SPhilippe Mathieu-Daudé { 2828d7f2e76SPhilippe Mathieu-Daudé uint8_t *ptr = buf; 2838d7f2e76SPhilippe Mathieu-Daudé dma_addr_t xresidual; 2848d7f2e76SPhilippe Mathieu-Daudé int sg_cur_index; 2858d7f2e76SPhilippe Mathieu-Daudé MemTxResult res = MEMTX_OK; 2868d7f2e76SPhilippe Mathieu-Daudé 2878d7f2e76SPhilippe Mathieu-Daudé xresidual = sg->size; 2888d7f2e76SPhilippe Mathieu-Daudé sg_cur_index = 0; 2898d7f2e76SPhilippe Mathieu-Daudé len = MIN(len, xresidual); 2908d7f2e76SPhilippe Mathieu-Daudé while (len > 0) { 2918d7f2e76SPhilippe Mathieu-Daudé ScatterGatherEntry entry = sg->sg[sg_cur_index++]; 2928d7f2e76SPhilippe Mathieu-Daudé dma_addr_t xfer = MIN(len, entry.len); 2938d7f2e76SPhilippe Mathieu-Daudé res |= dma_memory_rw(sg->as, entry.base, ptr, xfer, dir, attrs); 2948d7f2e76SPhilippe Mathieu-Daudé ptr += xfer; 2958d7f2e76SPhilippe Mathieu-Daudé len -= xfer; 2968d7f2e76SPhilippe Mathieu-Daudé xresidual -= xfer; 2978d7f2e76SPhilippe Mathieu-Daudé } 2988d7f2e76SPhilippe Mathieu-Daudé 2998d7f2e76SPhilippe Mathieu-Daudé if (residual) { 3008d7f2e76SPhilippe Mathieu-Daudé *residual = xresidual; 3018d7f2e76SPhilippe Mathieu-Daudé } 3028d7f2e76SPhilippe Mathieu-Daudé return res; 3038d7f2e76SPhilippe Mathieu-Daudé } 3048d7f2e76SPhilippe Mathieu-Daudé 3058d7f2e76SPhilippe Mathieu-Daudé MemTxResult dma_buf_read(void *ptr, dma_addr_t len, dma_addr_t *residual, 3068d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg, MemTxAttrs attrs) 3078d7f2e76SPhilippe Mathieu-Daudé { 3088d7f2e76SPhilippe Mathieu-Daudé return dma_buf_rw(ptr, len, residual, sg, DMA_DIRECTION_FROM_DEVICE, attrs); 3098d7f2e76SPhilippe Mathieu-Daudé } 3108d7f2e76SPhilippe Mathieu-Daudé 3118d7f2e76SPhilippe Mathieu-Daudé MemTxResult dma_buf_write(void *ptr, dma_addr_t len, dma_addr_t *residual, 3128d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg, MemTxAttrs attrs) 3138d7f2e76SPhilippe Mathieu-Daudé { 3148d7f2e76SPhilippe Mathieu-Daudé return dma_buf_rw(ptr, len, residual, sg, DMA_DIRECTION_TO_DEVICE, attrs); 3158d7f2e76SPhilippe Mathieu-Daudé } 3168d7f2e76SPhilippe Mathieu-Daudé 3178d7f2e76SPhilippe Mathieu-Daudé void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie, 3188d7f2e76SPhilippe Mathieu-Daudé QEMUSGList *sg, enum BlockAcctType type) 3198d7f2e76SPhilippe Mathieu-Daudé { 3208d7f2e76SPhilippe Mathieu-Daudé block_acct_start(blk_get_stats(blk), cookie, sg->size, type); 3218d7f2e76SPhilippe Mathieu-Daudé } 3228d7f2e76SPhilippe Mathieu-Daudé 3238d7f2e76SPhilippe Mathieu-Daudé uint64_t dma_aligned_pow2_mask(uint64_t start, uint64_t end, int max_addr_bits) 3248d7f2e76SPhilippe Mathieu-Daudé { 3258d7f2e76SPhilippe Mathieu-Daudé uint64_t max_mask = UINT64_MAX, addr_mask = end - start; 3268d7f2e76SPhilippe Mathieu-Daudé uint64_t alignment_mask, size_mask; 3278d7f2e76SPhilippe Mathieu-Daudé 3288d7f2e76SPhilippe Mathieu-Daudé if (max_addr_bits != 64) { 3298d7f2e76SPhilippe Mathieu-Daudé max_mask = (1ULL << max_addr_bits) - 1; 3308d7f2e76SPhilippe Mathieu-Daudé } 3318d7f2e76SPhilippe Mathieu-Daudé 3328d7f2e76SPhilippe Mathieu-Daudé alignment_mask = start ? (start & -start) - 1 : max_mask; 3338d7f2e76SPhilippe Mathieu-Daudé alignment_mask = MIN(alignment_mask, max_mask); 3348d7f2e76SPhilippe Mathieu-Daudé size_mask = MIN(addr_mask, max_mask); 3358d7f2e76SPhilippe Mathieu-Daudé 3368d7f2e76SPhilippe Mathieu-Daudé if (alignment_mask <= size_mask) { 3378d7f2e76SPhilippe Mathieu-Daudé /* Increase the alignment of start */ 3388d7f2e76SPhilippe Mathieu-Daudé return alignment_mask; 3398d7f2e76SPhilippe Mathieu-Daudé } else { 3408d7f2e76SPhilippe Mathieu-Daudé /* Find the largest page mask from size */ 3418d7f2e76SPhilippe Mathieu-Daudé if (addr_mask == UINT64_MAX) { 3428d7f2e76SPhilippe Mathieu-Daudé return UINT64_MAX; 3438d7f2e76SPhilippe Mathieu-Daudé } 3448d7f2e76SPhilippe Mathieu-Daudé return (1ULL << (63 - clz64(addr_mask + 1))) - 1; 3458d7f2e76SPhilippe Mathieu-Daudé } 3468d7f2e76SPhilippe Mathieu-Daudé } 3478d7f2e76SPhilippe Mathieu-Daudé 348