@@ -121,6 +121,7 @@ typedef struct VFIOProxy {
} VFIOProxy;
#define VFIO_PROXY_CLIENT 0x1
+#define VFIO_PROXY_SECURE 0x2
/*
* VFIO_USER_DEVICE_GET_INFO
@@ -159,6 +160,52 @@ struct vfio_user_region_rw {
char data[];
};
+/*
+ * VFIO_USER_DMA_MAP
+ * imported from struct vfio_iommu_type1_dma_map
+ */
+struct vfio_user_dma_map {
+ vfio_user_hdr_t hdr;
+ uint32_t argsz;
+ uint32_t flags;
+ uint64_t offset; /* FD offset */
+ uint64_t iova;
+ uint64_t size;
+};
+
+/*imported from struct vfio_bitmap */
+struct vfio_user_bitmap {
+ uint64_t pgsize;
+ uint64_t size;
+ char data[];
+};
+
+/*
+ * VFIO_USER_DMA_UNMAP
+ * imported from struct vfio_iommu_type1_dma_unmap
+ */
+struct vfio_user_dma_unmap {
+ vfio_user_hdr_t hdr;
+ uint32_t argsz;
+ uint32_t flags;
+ uint64_t iova;
+ uint64_t size;
+};
+
+/*
+ * VFIO_USER_DEVICE_GET_REGION_INFO
+ * imported from struct_vfio_region_info
+ */
+struct vfio_user_region_info {
+ vfio_user_hdr_t hdr;
+ uint32_t argsz;
+ uint32_t flags;
+ uint32_t index;
+ uint32_t cap_offset;
+ uint64_t size;
+ uint64_t offset;
+};
+
void vfio_user_recv(void *opaque);
void vfio_user_send_reply(VFIOProxy *proxy, char *buf, int ret);
VFIOProxy *vfio_user_connect_dev(char *sockname, Error **errp);
@@ -170,4 +217,11 @@ int vfio_user_region_read(VFIODevice *vbasedev, uint32_t index, uint64_t offset,
uint32_t count, void *data);
int vfio_user_region_write(VFIODevice *vbasedev, uint32_t index,
uint64_t offset, uint32_t count, void *data);
+int vfio_user_dma_map(VFIOProxy *proxy, struct vfio_iommu_type1_dma_map *map,
+ VFIOUserFDs *fds);
+int vfio_user_dma_unmap(VFIOProxy *proxy,
+ struct vfio_iommu_type1_dma_unmap *unmap,
+ struct vfio_bitmap *bitmap);
+int vfio_user_get_region_info(VFIODevice *vbasedev, int index,
+ struct vfio_region_info *info, VFIOUserFDs *fds);
#endif /* VFIO_USER_H */
@@ -146,6 +146,8 @@ typedef struct VFIODevice {
Error *migration_blocker;
OnOffAuto pre_copy_dirty_page_tracking;
VFIOProxy *proxy;
+ struct vfio_region_info **regions;
+ int *regfds;
} VFIODevice;
struct VFIODeviceOps {
@@ -477,6 +477,10 @@ static int vfio_dma_unmap(VFIOContainer *container,
return vfio_dma_unmap_bitmap(container, iova, size, iotlb);
}
+ if (container->proxy != NULL) {
+ return vfio_user_dma_unmap(container->proxy, &unmap, NULL);
+ }
+
while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) {
/*
* The type1 backend has an off-by-one bug in the kernel (71a7d3d78e3c
@@ -503,7 +507,7 @@ static int vfio_dma_unmap(VFIOContainer *container,
return 0;
}
-static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
+static int vfio_dma_map(VFIOContainer *container, MemoryRegion *mr, hwaddr iova,
ram_addr_t size, void *vaddr, bool readonly)
{
struct vfio_iommu_type1_dma_map map = {
@@ -518,6 +522,24 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
map.flags |= VFIO_DMA_MAP_FLAG_WRITE;
}
+ if (container->proxy != NULL) {
+ VFIOUserFDs fds;
+ int fd;
+
+ fd = memory_region_get_fd(mr);
+ if (fd != -1 && !(container->proxy->flags & VFIO_PROXY_SECURE)) {
+ fds.send_fds = 1;
+ fds.recv_fds = 0;
+ fds.fds = &fd;
+ map.vaddr = qemu_ram_block_host_offset(mr->ram_block, vaddr);
+
+ return vfio_user_dma_map(container->proxy, &map, &fds);
+ } else {
+ map.vaddr = 0;
+ return vfio_user_dma_map(container->proxy, &map, NULL);
+ }
+ }
+
/*
* Try the mapping, if it fails with EBUSY, unmap the region and try
* again. This shouldn't be necessary, but we sometimes see it in
@@ -586,7 +608,8 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
/* Called with rcu_read_lock held. */
static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
- ram_addr_t *ram_addr, bool *read_only)
+ ram_addr_t *ram_addr, bool *read_only,
+ MemoryRegion **mrp)
{
MemoryRegion *mr;
hwaddr xlat;
@@ -667,6 +690,10 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
*read_only = !writable || mr->readonly;
}
+ if (mrp != NULL) {
+ *mrp = mr;
+ }
+
return true;
}
@@ -674,6 +701,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
{
VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n);
VFIOContainer *container = giommu->container;
+ MemoryRegion *mr;
hwaddr iova = iotlb->iova + giommu->iommu_offset;
void *vaddr;
int ret;
@@ -692,7 +720,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) {
bool read_only;
- if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only)) {
+ if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, &mr)) {
goto out;
}
/*
@@ -702,7 +730,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
* of vaddr will always be there, even if the memory object is
* destroyed and its backing memory munmap-ed.
*/
- ret = vfio_dma_map(container, iova,
+ ret = vfio_dma_map(container, mr, iova,
iotlb->addr_mask + 1, vaddr,
read_only);
if (ret) {
@@ -764,7 +792,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
section->offset_within_address_space;
vaddr = memory_region_get_ram_ptr(section->mr) + start;
- ret = vfio_dma_map(vrdl->container, iova, next - start,
+ ret = vfio_dma_map(vrdl->container, section->mr, iova, next - start,
vaddr, section->readonly);
if (ret) {
/* Rollback */
@@ -1064,7 +1092,7 @@ static void vfio_listener_region_add(MemoryListener *listener,
}
}
- ret = vfio_dma_map(container, iova, int128_get64(llsize),
+ ret = vfio_dma_map(container, section->mr, iova, int128_get64(llsize),
vaddr, section->readonly);
if (ret) {
error_setg(&err, "vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
@@ -1330,7 +1358,7 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
}
rcu_read_lock();
- if (vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) {
+ if (vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL, NULL)) {
int ret;
ret = vfio_get_dirty_bitmap(container, iova, iotlb->addr_mask + 1,
@@ -2493,6 +2521,24 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index,
struct vfio_region_info **info)
{
size_t argsz = sizeof(struct vfio_region_info);
+ int fd = -1;
+ int ret;
+
+ /* create region cache */
+ if (vbasedev->regions == NULL) {
+ vbasedev->regions = g_new0(struct vfio_region_info *,
+ vbasedev->num_regions);
+ if (vbasedev->proxy != NULL) {
+ vbasedev->regfds = g_new0(int, vbasedev->num_regions);
+ }
+ }
+ /* check cache */
+ if (vbasedev->regions[index] != NULL) {
+ *info = g_malloc0(vbasedev->regions[index]->argsz);
+ memcpy(*info, vbasedev->regions[index],
+ vbasedev->regions[index]->argsz);
+ return 0;
+ }
*info = g_malloc0(argsz);
@@ -2500,7 +2546,17 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index,
retry:
(*info)->argsz = argsz;
- if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) {
+ if (vbasedev->proxy != NULL) {
+ VFIOUserFDs fds = { 0, 1, &fd};
+
+ ret = vfio_user_get_region_info(vbasedev, index, *info, &fds);
+ } else {
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info);
+ if (ret < 0) {
+ ret = -errno;
+ }
+ }
+ if (ret != 0) {
g_free(*info);
*info = NULL;
return -errno;
@@ -2509,10 +2565,22 @@ retry:
if ((*info)->argsz > argsz) {
argsz = (*info)->argsz;
*info = g_realloc(*info, argsz);
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
goto retry;
}
+ /* fill cache */
+ vbasedev->regions[index] = g_malloc0(argsz);
+ memcpy(vbasedev->regions[index], *info, argsz);
+ *vbasedev->regions[index] = **info;
+ if (vbasedev->regfds != NULL) {
+ vbasedev->regfds[index] = fd;
+ }
+
return 0;
}
@@ -3387,6 +3387,10 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp)
}
vbasedev->proxy = proxy;
+ if (udev->secure) {
+ proxy->flags |= VFIO_PROXY_SECURE;
+ }
+
vfio_user_validate_version(vbasedev, &err);
if (err != NULL) {
error_propagate(errp, err);
@@ -679,3 +679,103 @@ int vfio_user_region_write(VFIODevice *vbasedev, uint32_t index,
return count;
}
+
+int vfio_user_dma_map(VFIOProxy *proxy, struct vfio_iommu_type1_dma_map *map,
+ VFIOUserFDs *fds)
+{
+ struct vfio_user_dma_map msg;
+ int ret;
+
+ vfio_user_request_msg(&msg.hdr, VFIO_USER_DMA_MAP, sizeof(msg), 0);
+ msg.argsz = map->argsz;
+ msg.flags = map->flags;
+ msg.offset = map->vaddr;
+ msg.iova = map->iova;
+ msg.size = map->size;
+
+ vfio_user_send_recv(proxy, &msg.hdr, fds, 0);
+ ret = (msg.hdr.flags & VFIO_USER_ERROR) ? -msg.hdr.error_reply : 0;
+ return ret;
+}
+
+int vfio_user_dma_unmap(VFIOProxy *proxy,
+ struct vfio_iommu_type1_dma_unmap *unmap,
+ struct vfio_bitmap *bitmap)
+{
+ g_autofree struct {
+ struct vfio_user_dma_unmap msg;
+ struct vfio_user_bitmap bitmap;
+ } *msgp = NULL;
+ int msize, rsize;
+
+ if (bitmap == NULL && (unmap->flags &
+ VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP)) {
+ error_printf("vfio_user_dma_unmap mismatched flags and bitmap\n");
+ return -EINVAL;
+ }
+
+ /*
+ * If a dirty bitmap is returned, allocate extra space for it
+ * otherwise, just send the unmap request
+ */
+ if (bitmap != NULL) {
+ msize = sizeof(*msgp);
+ rsize = msize + bitmap->size;
+ msgp = g_malloc0(rsize);
+ msgp->bitmap.pgsize = bitmap->pgsize;
+ msgp->bitmap.size = bitmap->size;
+ } else {
+ msize = rsize = sizeof(struct vfio_user_dma_unmap);
+ msgp = g_malloc0(rsize);
+ }
+
+ vfio_user_request_msg(&msgp->msg.hdr, VFIO_USER_DMA_UNMAP, msize, 0);
+ msgp->msg.argsz = unmap->argsz;
+ msgp->msg.flags = unmap->flags;
+ msgp->msg.iova = unmap->iova;
+ msgp->msg.size = unmap->size;
+
+ vfio_user_send_recv(proxy, &msgp->msg.hdr, NULL, rsize);
+ if (msgp->msg.hdr.flags & VFIO_USER_ERROR) {
+ return -msgp->msg.hdr.error_reply;
+ }
+
+ if (bitmap != NULL) {
+ memcpy(bitmap->data, &msgp->bitmap.data, bitmap->size);
+ }
+
+ return 0;
+}
+
+int vfio_user_get_region_info(VFIODevice *vbasedev, int index,
+ struct vfio_region_info *info, VFIOUserFDs *fds)
+{
+ g_autofree struct vfio_user_region_info *msgp = NULL;
+ int size;
+
+ /* data returned can be larger than vfio_region_info */
+ if (info->argsz < sizeof(*info)) {
+ error_printf("vfio_user_get_region_info argsz too small\n");
+ return -EINVAL;
+ }
+ if (fds != NULL && fds->send_fds != 0) {
+ error_printf("vfio_user_get_region_info can't send FDs\n");
+ return -EINVAL;
+ }
+
+ size = info->argsz + sizeof(vfio_user_hdr_t);
+ msgp = g_malloc0(size);
+
+ vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO,
+ sizeof(*msgp), 0);
+ msgp->argsz = info->argsz;
+ msgp->index = info->index;
+
+ vfio_user_send_recv(vbasedev->proxy, &msgp->hdr, fds, size);
+ if (msgp->hdr.flags & VFIO_USER_ERROR) {
+ return -msgp->hdr.error_reply;
+ }
+
+ memcpy(info, &msgp->argsz, info->argsz);
+ return 0;
+}