diff --git a/repos/libports/include/qemu/usb.h b/repos/libports/include/qemu/usb.h index 5a001595ae..86bc781d7e 100644 --- a/repos/libports/include/qemu/usb.h +++ b/repos/libports/include/qemu/usb.h @@ -57,6 +57,8 @@ namespace Qemu { */ struct Pci_device { + enum class Dma_direction { IN = 0, OUT = 1, }; + /** * Raise interrupt * @@ -65,8 +67,8 @@ namespace Qemu { virtual void raise_interrupt(int assert) = 0; virtual int read_dma(addr_t addr, void *buf, size_t size) = 0; virtual int write_dma(addr_t addr, void const *buf, size_t size) = 0; - virtual void *map_dma(addr_t base, size_t size) = 0; - virtual void unmap_dma(void *addr, size_t size) = 0; + virtual void *map_dma(addr_t base, size_t size, Dma_direction dir) = 0; + virtual void unmap_dma(void *addr, size_t size, Dma_direction dir) = 0; }; diff --git a/repos/libports/src/lib/qemu-usb/qemu_emul.cc b/repos/libports/src/lib/qemu-usb/qemu_emul.cc index cd0e13d28a..926627bc8e 100644 --- a/repos/libports/src/lib/qemu-usb/qemu_emul.cc +++ b/repos/libports/src/lib/qemu-usb/qemu_emul.cc @@ -849,6 +849,10 @@ void qemu_sglist_destroy(QEMUSGList *sgl) { int usb_packet_map(USBPacket *p, QEMUSGList *sgl) { + Qemu::Pci_device::Dma_direction dir = + (p->pid == USB_TOKEN_IN) ? Qemu::Pci_device::Dma_direction::IN + : Qemu::Pci_device::Dma_direction::OUT; + void *mem; for (int i = 0; i < sgl->niov; i++) { @@ -857,7 +861,7 @@ int usb_packet_map(USBPacket *p, QEMUSGList *sgl) while (len) { dma_addr_t xlen = len; - mem = _pci_device->map_dma(base, xlen); + mem = _pci_device->map_dma(base, xlen, dir); if (verbose_iov) Genode::log("mem: ", mem, " base: ", (void *)base, " len: ", Genode::Hex(len)); @@ -884,9 +888,14 @@ err: void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl) { + Qemu::Pci_device::Dma_direction dir = + (p->pid == USB_TOKEN_IN) ? Qemu::Pci_device::Dma_direction::IN + : Qemu::Pci_device::Dma_direction::OUT; + for (int i = 0; i < p->iov.niov; i++) { _pci_device->unmap_dma(p->iov.iov[i].iov_base, - p->iov.iov[i].iov_len); + p->iov.iov[i].iov_len, + dir); } } diff --git a/repos/ports/src/virtualbox5/devxhci.cc b/repos/ports/src/virtualbox5/devxhci.cc index ca685a56a2..cd68cab1d2 100644 --- a/repos/ports/src/virtualbox5/devxhci.cc +++ b/repos/ports/src/virtualbox5/devxhci.cc @@ -15,6 +15,8 @@ /* Genode includes */ #include #include +#include +#include #include /* qemu-usb includes */ @@ -285,8 +287,33 @@ struct Timer_queue : public Qemu::Timer_queue struct Pci_device : public Qemu::Pci_device { + Libc::Allocator _alloc { }; + PPDMDEVINS pci_dev; + struct Dma_bounce_buffer + { + Genode::Allocator &_alloc; + + Qemu::addr_t const base; + Qemu::size_t const size; + void * const addr { _alloc.alloc(size) }; + + Dma_bounce_buffer(Genode::Allocator &alloc, + Qemu::addr_t base, + Qemu::size_t size) + : _alloc { alloc }, base { base }, size { size } + { } + + virtual ~Dma_bounce_buffer() + { + _alloc.free(addr, size); + } + }; + + using Reg_dma_buffer = Genode::Registered; + Genode::Registry _dma_buffers { }; + Pci_device(PPDMDEVINS pDevIns) : pci_dev(pDevIns) { } void raise_interrupt(int level) override { @@ -298,20 +325,43 @@ struct Pci_device : public Qemu::Pci_device int write_dma(Qemu::addr_t addr, void const *buf, Qemu::size_t size) override { return PDMDevHlpPhysWrite(pci_dev, addr, buf, size); } - void *map_dma(Qemu::addr_t base, Qemu::size_t size) override + void *map_dma(Qemu::addr_t base, Qemu::size_t size, + Qemu::Pci_device::Dma_direction dir) override { - PGMPAGEMAPLOCK lock; - void * vmm_addr = nullptr; + Reg_dma_buffer *dma = nullptr; - int rc = PDMDevHlpPhysGCPhys2CCPtr(pci_dev, base, 0, &vmm_addr, &lock); - Assert(rc == VINF_SUCCESS); + try { + dma = new (_alloc) Reg_dma_buffer(_dma_buffers, + _alloc, base, size); + } catch (...) { + return nullptr; + } - /* the mapping doesn't go away, so release internal lock immediately */ - PDMDevHlpPhysReleasePageMappingLock(pci_dev, &lock); - return vmm_addr; + /* copy data for write request to bounce buffer */ + if (dir == Qemu::Pci_device::Dma_direction::OUT) { + (void)PDMDevHlpPhysRead(pci_dev, base, dma->addr, size); + } + + return dma->addr; } - void unmap_dma(void *addr, Qemu::size_t size) override { } + void unmap_dma(void *addr, Qemu::size_t size, + Qemu::Pci_device::Dma_direction dir) override + { + _dma_buffers.for_each([&] (Reg_dma_buffer &dma) { + if (dma.addr != addr) { + return; + } + + /* copy data for read request from bounce buffer */ + if (dir == Qemu::Pci_device::Dma_direction::IN) { + (void)PDMDevHlpPhysWrite(pci_dev, + dma.base, dma.addr, dma.size); + } + + Genode::destroy(_alloc, &dma); + }); + } };