@@ -147,6 +147,18 @@ struct vfio_user_irq_info {
uint32_t count;
};
+/*
+ * VFIO_USER_REGION_READ
+ * VFIO_USER_REGION_WRITE
+ */
+struct vfio_user_region_rw {
+ vfio_user_hdr_t hdr;
+ uint64_t offset;
+ uint32_t region;
+ uint32_t count;
+ char data[];
+};
+
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);
@@ -154,4 +166,8 @@ void vfio_user_disconnect(VFIOProxy *proxy);
int vfio_user_validate_version(VFIODevice *vbasedev, Error **errp);
int vfio_user_get_info(VFIODevice *vbasedev);
int vfio_user_get_irq_info(VFIODevice *vbasedev, struct vfio_irq_info *info);
+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);
#endif /* VFIO_USER_H */
@@ -40,6 +40,7 @@
#include "trace.h"
#include "qapi/error.h"
#include "migration/migration.h"
+#include "hw/vfio/user.h"
VFIOGroupList vfio_group_list =
QLIST_HEAD_INITIALIZER(vfio_group_list);
@@ -214,6 +215,7 @@ void vfio_region_write(void *opaque, hwaddr addr,
uint32_t dword;
uint64_t qword;
} buf;
+ int ret;
switch (size) {
case 1:
@@ -233,7 +235,12 @@ void vfio_region_write(void *opaque, hwaddr addr,
break;
}
- if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
+ if (vbasedev->proxy != NULL) {
+ ret = vfio_user_region_write(vbasedev, region->nr, addr, size, &data);
+ } else {
+ ret = pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr);
+ }
+ if (ret != size) {
error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64
",%d) failed: %m",
__func__, vbasedev->name, region->nr,
@@ -265,8 +272,14 @@ uint64_t vfio_region_read(void *opaque,
uint64_t qword;
} buf;
uint64_t data = 0;
+ int ret;
- if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
+ if (vbasedev->proxy != NULL) {
+ ret = vfio_user_region_read(vbasedev, region->nr, addr, size, &buf);
+ } else {
+ ret = pread(vbasedev->fd, &buf, size, region->fd_offset + addr);
+ }
+ if (ret != size) {
error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m",
__func__, vbasedev->name, region->nr,
addr, size);
@@ -3424,6 +3424,19 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp)
error_propagate(errp, err);
goto error;
}
+ /* Get a copy of config space */
+ ret = vfio_user_region_read(vbasedev, VFIO_PCI_CONFIG_REGION_INDEX, 0,
+ MIN(pci_config_size(pdev), vdev->config_size),
+ pdev->config);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* vfio emulates a lot for us, but some bits need extra love */
+ vdev->emulated_config_bits = g_malloc0(vdev->config_size);
+
+ /* QEMU can also add or extend BARs */
+ memset(vdev->emulated_config_bits + PCI_BASE_ADDRESS_0, 0xff, 6 * 4);
return;
@@ -634,3 +634,48 @@ int vfio_user_get_irq_info(VFIODevice *vbasedev, struct vfio_irq_info *info)
memcpy(info, &msg.argsz, sizeof(*info));
return 0;
}
+
+int vfio_user_region_read(VFIODevice *vbasedev, uint32_t index, uint64_t offset,
+ uint32_t count, void *data)
+{
+ g_autofree struct vfio_user_region_rw *msgp = NULL;
+ int size = sizeof(*msgp) + count;
+
+ /* most reads are just registers, only allocate for larger ones */
+ msgp = g_malloc0(size);
+ vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0);
+ msgp->offset = offset;
+ msgp->region = index;
+ msgp->count = count;
+
+ vfio_user_send_recv(vbasedev->proxy, &msgp->hdr, NULL, size);
+ if (msgp->hdr.flags & VFIO_USER_ERROR) {
+ return -msgp->hdr.error_reply;
+ } else if (msgp->count > count) {
+ return -E2BIG;
+ } else {
+ memcpy(data, &msgp->data, msgp->count);
+ }
+
+ return msgp->count;
+}
+
+int vfio_user_region_write(VFIODevice *vbasedev, uint32_t index,
+ uint64_t offset, uint32_t count, void *data)
+{
+ g_autofree struct vfio_user_region_rw *msgp = NULL;
+ int size = sizeof(*msgp) + count;
+
+ /* most writes are just registers, only allocate for larger ones */
+ msgp = g_malloc0(size);
+ vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size,
+ VFIO_USER_NO_REPLY);
+ msgp->offset = offset;
+ msgp->region = index;
+ msgp->count = count;
+ memcpy(&msgp->data, data, count);
+
+ vfio_user_send(vbasedev->proxy, &msgp->hdr, NULL);
+
+ return count;
+}