@@ -40,4 +40,6 @@ typedef struct IORegionFDObject IORegionFDObject;
GSList *ioregionfd_get_obj_list(void);
IORegionFD *ioregionfd_get_by_bar(GSList *list, uint32_t bar);
void ioregionfd_set_bar_type(GSList *list, uint32_t bar, bool memory);
+int qio_channel_ioregionfd_read(QIOChannel *ioc, gpointer opaque,
+ Error **errp);
#endif /* IOREGIONFD_H */
@@ -23,6 +23,8 @@ struct RemoteObject {
DeviceState *dev;
DeviceListener listener;
+ QIOChannel *ioregfd_ioc;
+ AioContext *ioregfd_ctx;
GHashTable *ioregionfd_hash;
};
new file mode 100644
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */
+#ifndef _UAPI_LINUX_IOREGION_H
+#define _UAPI_LINUX_IOREGION_H
+
+/* Wire protocol */
+
+struct ioregionfd_cmd {
+ __u8 cmd;
+ __u8 size_exponent : 4;
+ __u8 resp : 1;
+ __u8 padding[6];
+ __u64 user_data;
+ __u64 offset;
+ __u64 data;
+};
+
+struct ioregionfd_resp {
+ __u64 data;
+ __u8 pad[24];
+};
+
+#define IOREGIONFD_CMD_READ 0
+#define IOREGIONFD_CMD_WRITE 1
+
+#define IOREGIONFD_SIZE_8BIT 0
+#define IOREGIONFD_SIZE_16BIT 1
+#define IOREGIONFD_SIZE_32BIT 2
+#define IOREGIONFD_SIZE_64BIT 3
+
+#endif
@@ -26,6 +26,7 @@
#include "hw/pci/pci.h"
#include "qapi/qapi-visit-qom.h"
#include "hw/remote/remote.h"
+#include "ioregionfd.h"
#define TYPE_IOREGIONFD_OBJECT "ioregionfd-object"
OBJECT_DECLARE_TYPE(IORegionFDObject, IORegionFDObjectClass, IOREGIONFD_OBJECT)
@@ -91,6 +92,116 @@ void ioregionfd_set_bar_type(GSList *list, uint32_t bar, bool memory)
}
}
+int qio_channel_ioregionfd_read(QIOChannel *ioc, gpointer opaque,
+ Error **errp)
+{
+ struct RemoteObject *o = (struct RemoteObject *)opaque;
+ struct ioregionfd_cmd cmd = {};
+ struct iovec iov = {
+ .iov_base = &cmd,
+ .iov_len = sizeof(struct ioregionfd_cmd),
+ };
+ IORegionFDObject *ioregfd_obj;
+ PCIDevice *pci_dev;
+ hwaddr addr;
+ struct ioregionfd_resp resp = {};
+ int bar = 0;
+ Error *local_err = NULL;
+ uint64_t val = UINT64_MAX;
+ AddressSpace *as;
+ int ret = -EINVAL;
+
+ ERRP_GUARD();
+
+ if (!ioc) {
+ return -EINVAL;
+ }
+ ret = qio_channel_readv_full(ioc, &iov, 1, NULL, 0, &local_err);
+
+ if (ret == QIO_CHANNEL_ERR_BLOCK) {
+ return -EINVAL;
+ }
+
+ if (ret <= 0) {
+ /* read error or other side closed connection */
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ error_setg(errp, "ioregionfd receive error");
+ return -EINVAL;
+ }
+
+ bar = cmd.user_data;
+ pci_dev = PCI_DEVICE(o->dev);
+ addr = (hwaddr)(pci_get_bar_addr(pci_dev, bar) + cmd.offset);
+ IORegionFDObject key = {.ioregfd = {.bar = bar} };
+ ioregfd_obj = g_hash_table_lookup(o->ioregionfd_hash, &key);
+
+ if (!ioregfd_obj) {
+ error_setg(errp, "Could not find IORegionFDObject");
+ return -EINVAL;
+ }
+ if (ioregfd_obj->ioregfd.memory) {
+ as = &address_space_memory;
+ } else {
+ as = &address_space_io;
+ }
+
+ if (ret > 0 && pci_dev) {
+ switch (cmd.cmd) {
+ case IOREGIONFD_CMD_READ:
+ ret = address_space_rw(as, addr, MEMTXATTRS_UNSPECIFIED,
+ (void *)&val, 1 << cmd.size_exponent,
+ false);
+ if (ret != MEMTX_OK) {
+ ret = -EINVAL;
+ error_setg(errp, "Bad address %"PRIx64" in mem read", addr);
+ val = UINT64_MAX;
+ }
+
+ memset(&resp, 0, sizeof(resp));
+ resp.data = val;
+ if (qio_channel_write_all(ioc, (char *)&resp, sizeof(resp),
+ &local_err)) {
+ error_propagate(errp, local_err);
+ goto fatal;
+ }
+ break;
+ case IOREGIONFD_CMD_WRITE:
+ ret = address_space_rw(as, addr, MEMTXATTRS_UNSPECIFIED,
+ (void *)&cmd.data, 1 << cmd.size_exponent,
+ true);
+ if (ret != MEMTX_OK) {
+ error_setg(errp, "Bad address %"PRIx64" for mem write", addr);
+ val = UINT64_MAX;
+ }
+
+ if (cmd.resp) {
+ memset(&resp, 0, sizeof(resp));
+ if (ret != MEMTX_OK) {
+ resp.data = UINT64_MAX;
+ ret = -EINVAL;
+ } else {
+ resp.data = cmd.data;
+ }
+ if (qio_channel_write_all(ioc, (char *)&resp, sizeof(resp),
+ &local_err)) {
+ error_propagate(errp, local_err);
+ goto fatal;
+ }
+ }
+ break;
+ default:
+ error_setg(errp, "Unknown ioregionfd command from kvm");
+ break;
+ }
+ }
+ return ret;
+
+ fatal:
+ return -EINVAL;
+}
+
static void ioregionfd_object_init(Object *obj)
{
IORegionFDObjectClass *k = IOREGIONFD_OBJECT_GET_CLASS(obj);
@@ -11,6 +11,7 @@
#include "qemu-common.h"
#include "qemu/error-report.h"
+#include "sysemu/iothread.h"
#include "qemu/notify.h"
#include "qom/object_interfaces.h"
#include "hw/qdev-core.h"
@@ -78,6 +79,16 @@ static void remote_object_unrealize_listener(DeviceListener *listener,
}
}
+static IOThread *ioregionfd_iot;
+
+static void ioregion_read(void *opaque)
+{
+ struct RemoteObject *o = opaque;
+ Error *local_error = NULL;
+
+ qio_channel_ioregionfd_read(o->ioregfd_ioc, opaque, &local_error);
+}
+
static GSList *ioregions_list;
static unsigned int ioregionfd_bar_hash(const void *key)
@@ -104,6 +115,8 @@ static void ioregionfd_prepare_for_dev(RemoteObject *o, PCIDevice *dev)
{
IORegionFDObject *ioregfd_obj = NULL;
GSList *obj_list, *list;
+ QIOChannel *ioc = NULL;
+ Error *local_err = NULL;
list = ioregionfd_get_obj_list();
@@ -143,6 +156,30 @@ static void ioregionfd_prepare_for_dev(RemoteObject *o, PCIDevice *dev)
/* This is default and will be changed when proxy requests region info. */
ioregfd_obj->ioregfd.memory = true;
+ ioc = qio_channel_new_fd(ioregfd_obj->ioregfd.fd, &local_err);
+ if (!ioc) {
+ error_prepend(&local_err, "Could not create IOC channel for" \
+ "ioregionfd fd %d", ioregfd_obj->ioregfd.fd);
+ error_report_err(local_err);
+ goto fatal;
+ }
+ o->ioregfd_ioc = ioc;
+
+ if (ioregionfd_iot == NULL) {
+ ioregionfd_iot = iothread_create("ioregionfd iothread",
+ &local_err);
+ if (local_err) {
+ qio_channel_shutdown(o->ioregfd_ioc, QIO_CHANNEL_SHUTDOWN_BOTH,
+ NULL);
+ qio_channel_close(o->ioregfd_ioc, NULL);
+ error_report_err(local_err);
+ goto fatal;
+ }
+ }
+ o->ioregfd_ctx = iothread_get_aio_context(ioregionfd_iot);
+ qio_channel_set_aio_fd_handler(o->ioregfd_ioc, o->ioregfd_ctx,
+ ioregion_read, NULL, o);
+
ioregions_list = list;
return;
@@ -238,8 +275,15 @@ static void remote_object_finalize(Object *obj)
k->nr_devs--;
g_free(o->devid);
+
+ iothread_destroy(ioregionfd_iot);
/* Free the list of the ioregions. */
g_slist_foreach(ioregions_list, ioregionfd_release, NULL);
+ if (o->ioregfd_ioc) {
+ qio_channel_shutdown(o->ioregfd_ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+ qio_channel_close(o->ioregfd_ioc, NULL);
+ }
+
g_slist_free(ioregions_list);
g_hash_table_destroy(o->ioregionfd_hash);
}
Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com> --- include/hw/remote/ioregionfd.h | 2 + include/hw/remote/remote.h | 2 + linux-headers/ioregionfd.h | 30 +++++++++ hw/remote/ioregionfd.c | 111 +++++++++++++++++++++++++++++++++ hw/remote/remote-obj.c | 44 +++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 linux-headers/ioregionfd.h