X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=dma-helpers.c;h=272632f3671988e479d0c3473efbb8c12b1bb075;hb=b2a856d99281f2fee60a4313d204205bcd2c4269;hp=c29ea6d3abdf8d4344f53fa5fcb9fdfacf224fb8;hpb=14655e482bc9ef44b841bdca55d79ab0891058e9;p=qemu.git diff --git a/dma-helpers.c b/dma-helpers.c index c29ea6d3a..272632f36 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -7,16 +7,48 @@ * (GNU GPL), version 2 or later. */ -#include "dma.h" -#include "block_int.h" +#include "sysemu/dma.h" #include "trace.h" +#include "qemu/range.h" +#include "qemu/thread.h" -void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint) +/* #define DEBUG_IOMMU */ + +static void do_dma_memory_set(AddressSpace *as, + dma_addr_t addr, uint8_t c, dma_addr_t len) +{ +#define FILLBUF_SIZE 512 + uint8_t fillbuf[FILLBUF_SIZE]; + int l; + + memset(fillbuf, c, FILLBUF_SIZE); + while (len > 0) { + l = len < FILLBUF_SIZE ? len : FILLBUF_SIZE; + address_space_rw(as, addr, fillbuf, l, true); + len -= l; + addr += l; + } +} + +int dma_memory_set(DMAContext *dma, dma_addr_t addr, uint8_t c, dma_addr_t len) +{ + dma_barrier(dma, DMA_DIRECTION_FROM_DEVICE); + + if (dma_has_iommu(dma)) { + return iommu_dma_memory_set(dma, addr, c, len); + } + do_dma_memory_set(dma->as, addr, c, len); + + return 0; +} + +void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint, DMAContext *dma) { qsg->sg = g_malloc(alloc_hint * sizeof(ScatterGatherEntry)); qsg->nsg = 0; qsg->nalloc = alloc_hint; qsg->size = 0; + qsg->dma = dma; } void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len) @@ -34,6 +66,7 @@ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len) void qemu_sglist_destroy(QEMUSGList *qsg) { g_free(qsg->sg); + memset(qsg, 0, sizeof(*qsg)); } typedef struct { @@ -42,7 +75,7 @@ typedef struct { BlockDriverAIOCB *acb; QEMUSGList *sg; uint64_t sector_num; - bool to_dev; + DMADirection dir; bool in_cancel; int sg_cur_index; dma_addr_t sg_cur_byte; @@ -75,9 +108,9 @@ static void dma_bdrv_unmap(DMAAIOCB *dbs) int i; for (i = 0; i < dbs->iov.niov; ++i) { - cpu_physical_memory_unmap(dbs->iov.iov[i].iov_base, - dbs->iov.iov[i].iov_len, !dbs->to_dev, - dbs->iov.iov[i].iov_len); + dma_memory_unmap(dbs->sg->dma, dbs->iov.iov[i].iov_base, + dbs->iov.iov[i].iov_len, dbs->dir, + dbs->iov.iov[i].iov_len); } qemu_iovec_reset(&dbs->iov); } @@ -106,7 +139,7 @@ static void dma_complete(DMAAIOCB *dbs, int ret) static void dma_bdrv_cb(void *opaque, int ret) { DMAAIOCB *dbs = (DMAAIOCB *)opaque; - target_phys_addr_t cur_addr, cur_len; + dma_addr_t cur_addr, cur_len; void *mem; trace_dma_bdrv_cb(dbs, ret); @@ -123,7 +156,7 @@ static void dma_bdrv_cb(void *opaque, int ret) while (dbs->sg_cur_index < dbs->sg->nsg) { cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte; cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte; - mem = cpu_physical_memory_map(cur_addr, &cur_len, !dbs->to_dev); + mem = dma_memory_map(dbs->sg->dma, cur_addr, &cur_len, dbs->dir); if (!mem) break; qemu_iovec_add(&dbs->iov, mem, cur_len); @@ -162,7 +195,7 @@ static void dma_aio_cancel(BlockDriverAIOCB *acb) dma_complete(dbs, 0); } -static AIOPool dma_aio_pool = { +static const AIOCBInfo dma_aiocb_info = { .aiocb_size = sizeof(DMAAIOCB), .cancel = dma_aio_cancel, }; @@ -170,11 +203,11 @@ static AIOPool dma_aio_pool = { BlockDriverAIOCB *dma_bdrv_io( BlockDriverState *bs, QEMUSGList *sg, uint64_t sector_num, DMAIOFunc *io_func, BlockDriverCompletionFunc *cb, - void *opaque, bool to_dev) + void *opaque, DMADirection dir) { - DMAAIOCB *dbs = qemu_aio_get(&dma_aio_pool, bs, cb, opaque); + DMAAIOCB *dbs = qemu_aio_get(&dma_aiocb_info, bs, cb, opaque); - trace_dma_bdrv_io(dbs, bs, sector_num, to_dev); + trace_dma_bdrv_io(dbs, bs, sector_num, (dir == DMA_DIRECTION_TO_DEVICE)); dbs->acb = NULL; dbs->bs = bs; @@ -182,7 +215,7 @@ BlockDriverAIOCB *dma_bdrv_io( dbs->sector_num = sector_num; dbs->sg_cur_index = 0; dbs->sg_cur_byte = 0; - dbs->to_dev = to_dev; + dbs->dir = dir; dbs->io_func = io_func; dbs->bh = NULL; qemu_iovec_init(&dbs->iov, sg->nsg); @@ -195,18 +228,21 @@ BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs, QEMUSGList *sg, uint64_t sector, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_bdrv_io(bs, sg, sector, bdrv_aio_readv, cb, opaque, false); + return dma_bdrv_io(bs, sg, sector, bdrv_aio_readv, cb, opaque, + DMA_DIRECTION_FROM_DEVICE); } BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs, QEMUSGList *sg, uint64_t sector, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_bdrv_io(bs, sg, sector, bdrv_aio_writev, cb, opaque, true); + return dma_bdrv_io(bs, sg, sector, bdrv_aio_writev, cb, opaque, + DMA_DIRECTION_TO_DEVICE); } -static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg, bool to_dev) +static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg, + DMADirection dir) { uint64_t resid; int sg_cur_index; @@ -217,7 +253,7 @@ static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg, bool to_de while (len > 0) { ScatterGatherEntry entry = sg->sg[sg_cur_index++]; int32_t xfer = MIN(len, entry.len); - cpu_physical_memory_rw(entry.base, ptr, xfer, !to_dev); + dma_memory_rw(sg->dma, entry.base, ptr, xfer, dir); ptr += xfer; len -= xfer; resid -= xfer; @@ -228,12 +264,12 @@ static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg, bool to_de uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg) { - return dma_buf_rw(ptr, len, sg, 0); + return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_FROM_DEVICE); } uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg) { - return dma_buf_rw(ptr, len, sg, 1); + return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_TO_DEVICE); } void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie, @@ -241,3 +277,158 @@ void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie, { bdrv_acct_start(bs, cookie, sg->size, type); } + +bool iommu_dma_memory_valid(DMAContext *dma, dma_addr_t addr, dma_addr_t len, + DMADirection dir) +{ + hwaddr paddr, plen; + +#ifdef DEBUG_IOMMU + fprintf(stderr, "dma_memory_check context=%p addr=0x" DMA_ADDR_FMT + " len=0x" DMA_ADDR_FMT " dir=%d\n", dma, addr, len, dir); +#endif + + while (len) { + if (dma->translate(dma, addr, &paddr, &plen, dir) != 0) { + return false; + } + + /* The translation might be valid for larger regions. */ + if (plen > len) { + plen = len; + } + + len -= plen; + addr += plen; + } + + return true; +} + +int iommu_dma_memory_rw(DMAContext *dma, dma_addr_t addr, + void *buf, dma_addr_t len, DMADirection dir) +{ + hwaddr paddr, plen; + int err; + +#ifdef DEBUG_IOMMU + fprintf(stderr, "dma_memory_rw context=%p addr=0x" DMA_ADDR_FMT " len=0x" + DMA_ADDR_FMT " dir=%d\n", dma, addr, len, dir); +#endif + + while (len) { + err = dma->translate(dma, addr, &paddr, &plen, dir); + if (err) { + /* + * In case of failure on reads from the guest, we clean the + * destination buffer so that a device that doesn't test + * for errors will not expose qemu internal memory. + */ + memset(buf, 0, len); + return -1; + } + + /* The translation might be valid for larger regions. */ + if (plen > len) { + plen = len; + } + + address_space_rw(dma->as, paddr, buf, plen, dir == DMA_DIRECTION_FROM_DEVICE); + + len -= plen; + addr += plen; + buf += plen; + } + + return 0; +} + +int iommu_dma_memory_set(DMAContext *dma, dma_addr_t addr, uint8_t c, + dma_addr_t len) +{ + hwaddr paddr, plen; + int err; + +#ifdef DEBUG_IOMMU + fprintf(stderr, "dma_memory_set context=%p addr=0x" DMA_ADDR_FMT + " len=0x" DMA_ADDR_FMT "\n", dma, addr, len); +#endif + + while (len) { + err = dma->translate(dma, addr, &paddr, &plen, + DMA_DIRECTION_FROM_DEVICE); + if (err) { + return err; + } + + /* The translation might be valid for larger regions. */ + if (plen > len) { + plen = len; + } + + do_dma_memory_set(dma->as, paddr, c, plen); + + len -= plen; + addr += plen; + } + + return 0; +} + +void dma_context_init(DMAContext *dma, AddressSpace *as, DMATranslateFunc translate, + DMAMapFunc map, DMAUnmapFunc unmap) +{ +#ifdef DEBUG_IOMMU + fprintf(stderr, "dma_context_init(%p, %p, %p, %p)\n", + dma, translate, map, unmap); +#endif + dma->as = as; + dma->translate = translate; + dma->map = map; + dma->unmap = unmap; +} + +void *iommu_dma_memory_map(DMAContext *dma, dma_addr_t addr, dma_addr_t *len, + DMADirection dir) +{ + int err; + hwaddr paddr, plen; + void *buf; + + if (dma->map) { + return dma->map(dma, addr, len, dir); + } + + plen = *len; + err = dma->translate(dma, addr, &paddr, &plen, dir); + if (err) { + return NULL; + } + + /* + * If this is true, the virtual region is contiguous, + * but the translated physical region isn't. We just + * clamp *len, much like address_space_map() does. + */ + if (plen < *len) { + *len = plen; + } + + buf = address_space_map(dma->as, paddr, &plen, dir == DMA_DIRECTION_FROM_DEVICE); + *len = plen; + + return buf; +} + +void iommu_dma_memory_unmap(DMAContext *dma, void *buffer, dma_addr_t len, + DMADirection dir, dma_addr_t access_len) +{ + if (dma->unmap) { + dma->unmap(dma, buffer, len, dir, access_len); + return; + } + + address_space_unmap(dma->as, buffer, len, dir == DMA_DIRECTION_FROM_DEVICE, + access_len); + +}