xref: /qemu/hw/remote/vfio-user-obj.c (revision 15ccf9bee7804bfd969c171fc15ddc25f18431e3)
18f9a9259SJagannathan Raman /**
28f9a9259SJagannathan Raman  * QEMU vfio-user-server server object
38f9a9259SJagannathan Raman  *
48f9a9259SJagannathan Raman  * Copyright © 2022 Oracle and/or its affiliates.
58f9a9259SJagannathan Raman  *
68f9a9259SJagannathan Raman  * This work is licensed under the terms of the GNU GPL-v2, version 2 or later.
78f9a9259SJagannathan Raman  *
88f9a9259SJagannathan Raman  * See the COPYING file in the top-level directory.
98f9a9259SJagannathan Raman  *
108f9a9259SJagannathan Raman  */
118f9a9259SJagannathan Raman 
128f9a9259SJagannathan Raman /**
138f9a9259SJagannathan Raman  * Usage: add options:
148f9a9259SJagannathan Raman  *     -machine x-remote,vfio-user=on,auto-shutdown=on
158f9a9259SJagannathan Raman  *     -device <PCI-device>,id=<pci-dev-id>
168f9a9259SJagannathan Raman  *     -object x-vfio-user-server,id=<id>,type=unix,path=<socket-path>,
178f9a9259SJagannathan Raman  *             device=<pci-dev-id>
188f9a9259SJagannathan Raman  *
198f9a9259SJagannathan Raman  * Note that x-vfio-user-server object must be used with x-remote machine only.
208f9a9259SJagannathan Raman  * This server could only support PCI devices for now.
218f9a9259SJagannathan Raman  *
228f9a9259SJagannathan Raman  * type - SocketAddress type - presently "unix" alone is supported. Required
238f9a9259SJagannathan Raman  *        option
248f9a9259SJagannathan Raman  *
258f9a9259SJagannathan Raman  * path - named unix socket, it will be created by the server. It is
268f9a9259SJagannathan Raman  *        a required option
278f9a9259SJagannathan Raman  *
288f9a9259SJagannathan Raman  * device - id of a device on the server, a required option. PCI devices
298f9a9259SJagannathan Raman  *          alone are supported presently.
309fb3fba1SJagannathan Raman  *
319fb3fba1SJagannathan Raman  * notes - x-vfio-user-server could block IO and monitor during the
329fb3fba1SJagannathan Raman  *         initialization phase.
338f9a9259SJagannathan Raman  */
348f9a9259SJagannathan Raman 
358f9a9259SJagannathan Raman #include "qemu/osdep.h"
368f9a9259SJagannathan Raman 
378f9a9259SJagannathan Raman #include "qom/object.h"
388f9a9259SJagannathan Raman #include "qom/object_interfaces.h"
398f9a9259SJagannathan Raman #include "qemu/error-report.h"
408f9a9259SJagannathan Raman #include "trace.h"
418f9a9259SJagannathan Raman #include "sysemu/runstate.h"
428f9a9259SJagannathan Raman #include "hw/boards.h"
438f9a9259SJagannathan Raman #include "hw/remote/machine.h"
448f9a9259SJagannathan Raman #include "qapi/error.h"
458f9a9259SJagannathan Raman #include "qapi/qapi-visit-sockets.h"
469fb3fba1SJagannathan Raman #include "qapi/qapi-events-misc.h"
4787f7249fSJagannathan Raman #include "qemu/notify.h"
489fb3fba1SJagannathan Raman #include "qemu/thread.h"
4990072f29SJagannathan Raman #include "qemu/main-loop.h"
5087f7249fSJagannathan Raman #include "sysemu/sysemu.h"
5187f7249fSJagannathan Raman #include "libvfio-user.h"
52a6e8d6d9SJagannathan Raman #include "hw/qdev-core.h"
53a6e8d6d9SJagannathan Raman #include "hw/pci/pci.h"
549fb3fba1SJagannathan Raman #include "qemu/timer.h"
558f9a9259SJagannathan Raman 
568f9a9259SJagannathan Raman #define TYPE_VFU_OBJECT "x-vfio-user-server"
578f9a9259SJagannathan Raman OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT)
588f9a9259SJagannathan Raman 
598f9a9259SJagannathan Raman /**
608f9a9259SJagannathan Raman  * VFU_OBJECT_ERROR - reports an error message. If auto_shutdown
618f9a9259SJagannathan Raman  * is set, it aborts the machine on error. Otherwise, it logs an
628f9a9259SJagannathan Raman  * error message without aborting.
638f9a9259SJagannathan Raman  */
648f9a9259SJagannathan Raman #define VFU_OBJECT_ERROR(o, fmt, ...)                                     \
658f9a9259SJagannathan Raman     {                                                                     \
668f9a9259SJagannathan Raman         if (vfu_object_auto_shutdown()) {                                 \
678f9a9259SJagannathan Raman             error_setg(&error_abort, (fmt), ## __VA_ARGS__);              \
688f9a9259SJagannathan Raman         } else {                                                          \
698f9a9259SJagannathan Raman             error_report((fmt), ## __VA_ARGS__);                          \
708f9a9259SJagannathan Raman         }                                                                 \
718f9a9259SJagannathan Raman     }                                                                     \
728f9a9259SJagannathan Raman 
738f9a9259SJagannathan Raman struct VfuObjectClass {
748f9a9259SJagannathan Raman     ObjectClass parent_class;
758f9a9259SJagannathan Raman 
768f9a9259SJagannathan Raman     unsigned int nr_devs;
778f9a9259SJagannathan Raman };
788f9a9259SJagannathan Raman 
798f9a9259SJagannathan Raman struct VfuObject {
808f9a9259SJagannathan Raman     /* private */
818f9a9259SJagannathan Raman     Object parent;
828f9a9259SJagannathan Raman 
838f9a9259SJagannathan Raman     SocketAddress *socket;
848f9a9259SJagannathan Raman 
858f9a9259SJagannathan Raman     char *device;
868f9a9259SJagannathan Raman 
878f9a9259SJagannathan Raman     Error *err;
8887f7249fSJagannathan Raman 
8987f7249fSJagannathan Raman     Notifier machine_done;
9087f7249fSJagannathan Raman 
9187f7249fSJagannathan Raman     vfu_ctx_t *vfu_ctx;
92a6e8d6d9SJagannathan Raman 
93a6e8d6d9SJagannathan Raman     PCIDevice *pci_dev;
94a6e8d6d9SJagannathan Raman 
95a6e8d6d9SJagannathan Raman     Error *unplug_blocker;
969fb3fba1SJagannathan Raman 
979fb3fba1SJagannathan Raman     int vfu_poll_fd;
988f9a9259SJagannathan Raman };
998f9a9259SJagannathan Raman 
10087f7249fSJagannathan Raman static void vfu_object_init_ctx(VfuObject *o, Error **errp);
10187f7249fSJagannathan Raman 
1028f9a9259SJagannathan Raman static bool vfu_object_auto_shutdown(void)
1038f9a9259SJagannathan Raman {
1048f9a9259SJagannathan Raman     bool auto_shutdown = true;
1058f9a9259SJagannathan Raman     Error *local_err = NULL;
1068f9a9259SJagannathan Raman 
1078f9a9259SJagannathan Raman     if (!current_machine) {
1088f9a9259SJagannathan Raman         return auto_shutdown;
1098f9a9259SJagannathan Raman     }
1108f9a9259SJagannathan Raman 
1118f9a9259SJagannathan Raman     auto_shutdown = object_property_get_bool(OBJECT(current_machine),
1128f9a9259SJagannathan Raman                                              "auto-shutdown",
1138f9a9259SJagannathan Raman                                              &local_err);
1148f9a9259SJagannathan Raman 
1158f9a9259SJagannathan Raman     /*
1168f9a9259SJagannathan Raman      * local_err would be set if no such property exists - safe to ignore.
1178f9a9259SJagannathan Raman      * Unlikely scenario as auto-shutdown is always defined for
1188f9a9259SJagannathan Raman      * TYPE_REMOTE_MACHINE, and  TYPE_VFU_OBJECT only works with
1198f9a9259SJagannathan Raman      * TYPE_REMOTE_MACHINE
1208f9a9259SJagannathan Raman      */
1218f9a9259SJagannathan Raman     if (local_err) {
1228f9a9259SJagannathan Raman         auto_shutdown = true;
1238f9a9259SJagannathan Raman         error_free(local_err);
1248f9a9259SJagannathan Raman     }
1258f9a9259SJagannathan Raman 
1268f9a9259SJagannathan Raman     return auto_shutdown;
1278f9a9259SJagannathan Raman }
1288f9a9259SJagannathan Raman 
1298f9a9259SJagannathan Raman static void vfu_object_set_socket(Object *obj, Visitor *v, const char *name,
1308f9a9259SJagannathan Raman                                   void *opaque, Error **errp)
1318f9a9259SJagannathan Raman {
1328f9a9259SJagannathan Raman     VfuObject *o = VFU_OBJECT(obj);
1338f9a9259SJagannathan Raman 
13487f7249fSJagannathan Raman     if (o->vfu_ctx) {
13587f7249fSJagannathan Raman         error_setg(errp, "vfu: Unable to set socket property - server busy");
13687f7249fSJagannathan Raman         return;
13787f7249fSJagannathan Raman     }
13887f7249fSJagannathan Raman 
1398f9a9259SJagannathan Raman     qapi_free_SocketAddress(o->socket);
1408f9a9259SJagannathan Raman 
1418f9a9259SJagannathan Raman     o->socket = NULL;
1428f9a9259SJagannathan Raman 
1438f9a9259SJagannathan Raman     visit_type_SocketAddress(v, name, &o->socket, errp);
1448f9a9259SJagannathan Raman 
1458f9a9259SJagannathan Raman     if (o->socket->type != SOCKET_ADDRESS_TYPE_UNIX) {
1468f9a9259SJagannathan Raman         error_setg(errp, "vfu: Unsupported socket type - %s",
1478f9a9259SJagannathan Raman                    SocketAddressType_str(o->socket->type));
1488f9a9259SJagannathan Raman         qapi_free_SocketAddress(o->socket);
1498f9a9259SJagannathan Raman         o->socket = NULL;
1508f9a9259SJagannathan Raman         return;
1518f9a9259SJagannathan Raman     }
1528f9a9259SJagannathan Raman 
1538f9a9259SJagannathan Raman     trace_vfu_prop("socket", o->socket->u.q_unix.path);
15487f7249fSJagannathan Raman 
15587f7249fSJagannathan Raman     vfu_object_init_ctx(o, errp);
1568f9a9259SJagannathan Raman }
1578f9a9259SJagannathan Raman 
1588f9a9259SJagannathan Raman static void vfu_object_set_device(Object *obj, const char *str, Error **errp)
1598f9a9259SJagannathan Raman {
1608f9a9259SJagannathan Raman     VfuObject *o = VFU_OBJECT(obj);
1618f9a9259SJagannathan Raman 
16287f7249fSJagannathan Raman     if (o->vfu_ctx) {
16387f7249fSJagannathan Raman         error_setg(errp, "vfu: Unable to set device property - server busy");
16487f7249fSJagannathan Raman         return;
16587f7249fSJagannathan Raman     }
16687f7249fSJagannathan Raman 
1678f9a9259SJagannathan Raman     g_free(o->device);
1688f9a9259SJagannathan Raman 
1698f9a9259SJagannathan Raman     o->device = g_strdup(str);
1708f9a9259SJagannathan Raman 
1718f9a9259SJagannathan Raman     trace_vfu_prop("device", str);
17287f7249fSJagannathan Raman 
17387f7249fSJagannathan Raman     vfu_object_init_ctx(o, errp);
17487f7249fSJagannathan Raman }
17587f7249fSJagannathan Raman 
1769fb3fba1SJagannathan Raman static void vfu_object_ctx_run(void *opaque)
1779fb3fba1SJagannathan Raman {
1789fb3fba1SJagannathan Raman     VfuObject *o = opaque;
1799fb3fba1SJagannathan Raman     const char *vfu_id;
1809fb3fba1SJagannathan Raman     char *vfu_path, *pci_dev_path;
1819fb3fba1SJagannathan Raman     int ret = -1;
1829fb3fba1SJagannathan Raman 
1839fb3fba1SJagannathan Raman     while (ret != 0) {
1849fb3fba1SJagannathan Raman         ret = vfu_run_ctx(o->vfu_ctx);
1859fb3fba1SJagannathan Raman         if (ret < 0) {
1869fb3fba1SJagannathan Raman             if (errno == EINTR) {
1879fb3fba1SJagannathan Raman                 continue;
1889fb3fba1SJagannathan Raman             } else if (errno == ENOTCONN) {
1899fb3fba1SJagannathan Raman                 vfu_id = object_get_canonical_path_component(OBJECT(o));
1909fb3fba1SJagannathan Raman                 vfu_path = object_get_canonical_path(OBJECT(o));
1919fb3fba1SJagannathan Raman                 g_assert(o->pci_dev);
1929fb3fba1SJagannathan Raman                 pci_dev_path = object_get_canonical_path(OBJECT(o->pci_dev));
1939fb3fba1SJagannathan Raman                  /* o->device is a required property and is non-NULL here */
1949fb3fba1SJagannathan Raman                 g_assert(o->device);
1959fb3fba1SJagannathan Raman                 qapi_event_send_vfu_client_hangup(vfu_id, vfu_path,
1969fb3fba1SJagannathan Raman                                                   o->device, pci_dev_path);
1979fb3fba1SJagannathan Raman                 qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
1989fb3fba1SJagannathan Raman                 o->vfu_poll_fd = -1;
1999fb3fba1SJagannathan Raman                 object_unparent(OBJECT(o));
2009fb3fba1SJagannathan Raman                 g_free(vfu_path);
2019fb3fba1SJagannathan Raman                 g_free(pci_dev_path);
2029fb3fba1SJagannathan Raman                 break;
2039fb3fba1SJagannathan Raman             } else {
2049fb3fba1SJagannathan Raman                 VFU_OBJECT_ERROR(o, "vfu: Failed to run device %s - %s",
2059fb3fba1SJagannathan Raman                                  o->device, strerror(errno));
2069fb3fba1SJagannathan Raman                 break;
2079fb3fba1SJagannathan Raman             }
2089fb3fba1SJagannathan Raman         }
2099fb3fba1SJagannathan Raman     }
2109fb3fba1SJagannathan Raman }
2119fb3fba1SJagannathan Raman 
2129fb3fba1SJagannathan Raman static void vfu_object_attach_ctx(void *opaque)
2139fb3fba1SJagannathan Raman {
2149fb3fba1SJagannathan Raman     VfuObject *o = opaque;
2159fb3fba1SJagannathan Raman     GPollFD pfds[1];
2169fb3fba1SJagannathan Raman     int ret;
2179fb3fba1SJagannathan Raman 
2189fb3fba1SJagannathan Raman     qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
2199fb3fba1SJagannathan Raman 
2209fb3fba1SJagannathan Raman     pfds[0].fd = o->vfu_poll_fd;
2219fb3fba1SJagannathan Raman     pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
2229fb3fba1SJagannathan Raman 
2239fb3fba1SJagannathan Raman retry_attach:
2249fb3fba1SJagannathan Raman     ret = vfu_attach_ctx(o->vfu_ctx);
2259fb3fba1SJagannathan Raman     if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
2269fb3fba1SJagannathan Raman         /**
2279fb3fba1SJagannathan Raman          * vfu_object_attach_ctx can block QEMU's main loop
2289fb3fba1SJagannathan Raman          * during attach - the monitor and other IO
2299fb3fba1SJagannathan Raman          * could be unresponsive during this time.
2309fb3fba1SJagannathan Raman          */
2319fb3fba1SJagannathan Raman         (void)qemu_poll_ns(pfds, 1, 500 * (int64_t)SCALE_MS);
2329fb3fba1SJagannathan Raman         goto retry_attach;
2339fb3fba1SJagannathan Raman     } else if (ret < 0) {
2349fb3fba1SJagannathan Raman         VFU_OBJECT_ERROR(o, "vfu: Failed to attach device %s to context - %s",
2359fb3fba1SJagannathan Raman                          o->device, strerror(errno));
2369fb3fba1SJagannathan Raman         return;
2379fb3fba1SJagannathan Raman     }
2389fb3fba1SJagannathan Raman 
2399fb3fba1SJagannathan Raman     o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx);
2409fb3fba1SJagannathan Raman     if (o->vfu_poll_fd < 0) {
2419fb3fba1SJagannathan Raman         VFU_OBJECT_ERROR(o, "vfu: Failed to get poll fd %s", o->device);
2429fb3fba1SJagannathan Raman         return;
2439fb3fba1SJagannathan Raman     }
2449fb3fba1SJagannathan Raman 
2459fb3fba1SJagannathan Raman     qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_ctx_run, NULL, o);
2469fb3fba1SJagannathan Raman }
2479fb3fba1SJagannathan Raman 
24890072f29SJagannathan Raman static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf,
24990072f29SJagannathan Raman                                      size_t count, loff_t offset,
25090072f29SJagannathan Raman                                      const bool is_write)
25190072f29SJagannathan Raman {
25290072f29SJagannathan Raman     VfuObject *o = vfu_get_private(vfu_ctx);
25390072f29SJagannathan Raman     uint32_t pci_access_width = sizeof(uint32_t);
25490072f29SJagannathan Raman     size_t bytes = count;
25590072f29SJagannathan Raman     uint32_t val = 0;
25690072f29SJagannathan Raman     char *ptr = buf;
25790072f29SJagannathan Raman     int len;
25890072f29SJagannathan Raman 
25990072f29SJagannathan Raman     /*
26090072f29SJagannathan Raman      * Writes to the BAR registers would trigger an update to the
26190072f29SJagannathan Raman      * global Memory and IO AddressSpaces. But the remote device
26290072f29SJagannathan Raman      * never uses the global AddressSpaces, therefore overlapping
26390072f29SJagannathan Raman      * memory regions are not a problem
26490072f29SJagannathan Raman      */
26590072f29SJagannathan Raman     while (bytes > 0) {
26690072f29SJagannathan Raman         len = (bytes > pci_access_width) ? pci_access_width : bytes;
26790072f29SJagannathan Raman         if (is_write) {
26890072f29SJagannathan Raman             memcpy(&val, ptr, len);
26990072f29SJagannathan Raman             pci_host_config_write_common(o->pci_dev, offset,
27090072f29SJagannathan Raman                                          pci_config_size(o->pci_dev),
27190072f29SJagannathan Raman                                          val, len);
27290072f29SJagannathan Raman             trace_vfu_cfg_write(offset, val);
27390072f29SJagannathan Raman         } else {
27490072f29SJagannathan Raman             val = pci_host_config_read_common(o->pci_dev, offset,
27590072f29SJagannathan Raman                                               pci_config_size(o->pci_dev), len);
27690072f29SJagannathan Raman             memcpy(ptr, &val, len);
27790072f29SJagannathan Raman             trace_vfu_cfg_read(offset, val);
27890072f29SJagannathan Raman         }
27990072f29SJagannathan Raman         offset += len;
28090072f29SJagannathan Raman         ptr += len;
28190072f29SJagannathan Raman         bytes -= len;
28290072f29SJagannathan Raman     }
28390072f29SJagannathan Raman 
28490072f29SJagannathan Raman     return count;
28590072f29SJagannathan Raman }
28690072f29SJagannathan Raman 
287*15ccf9beSJagannathan Raman static void dma_register(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
288*15ccf9beSJagannathan Raman {
289*15ccf9beSJagannathan Raman     VfuObject *o = vfu_get_private(vfu_ctx);
290*15ccf9beSJagannathan Raman     AddressSpace *dma_as = NULL;
291*15ccf9beSJagannathan Raman     MemoryRegion *subregion = NULL;
292*15ccf9beSJagannathan Raman     g_autofree char *name = NULL;
293*15ccf9beSJagannathan Raman     struct iovec *iov = &info->iova;
294*15ccf9beSJagannathan Raman 
295*15ccf9beSJagannathan Raman     if (!info->vaddr) {
296*15ccf9beSJagannathan Raman         return;
297*15ccf9beSJagannathan Raman     }
298*15ccf9beSJagannathan Raman 
299*15ccf9beSJagannathan Raman     name = g_strdup_printf("mem-%s-%"PRIx64"", o->device,
300*15ccf9beSJagannathan Raman                            (uint64_t)info->vaddr);
301*15ccf9beSJagannathan Raman 
302*15ccf9beSJagannathan Raman     subregion = g_new0(MemoryRegion, 1);
303*15ccf9beSJagannathan Raman 
304*15ccf9beSJagannathan Raman     memory_region_init_ram_ptr(subregion, NULL, name,
305*15ccf9beSJagannathan Raman                                iov->iov_len, info->vaddr);
306*15ccf9beSJagannathan Raman 
307*15ccf9beSJagannathan Raman     dma_as = pci_device_iommu_address_space(o->pci_dev);
308*15ccf9beSJagannathan Raman 
309*15ccf9beSJagannathan Raman     memory_region_add_subregion(dma_as->root, (hwaddr)iov->iov_base, subregion);
310*15ccf9beSJagannathan Raman 
311*15ccf9beSJagannathan Raman     trace_vfu_dma_register((uint64_t)iov->iov_base, iov->iov_len);
312*15ccf9beSJagannathan Raman }
313*15ccf9beSJagannathan Raman 
314*15ccf9beSJagannathan Raman static void dma_unregister(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
315*15ccf9beSJagannathan Raman {
316*15ccf9beSJagannathan Raman     VfuObject *o = vfu_get_private(vfu_ctx);
317*15ccf9beSJagannathan Raman     AddressSpace *dma_as = NULL;
318*15ccf9beSJagannathan Raman     MemoryRegion *mr = NULL;
319*15ccf9beSJagannathan Raman     ram_addr_t offset;
320*15ccf9beSJagannathan Raman 
321*15ccf9beSJagannathan Raman     mr = memory_region_from_host(info->vaddr, &offset);
322*15ccf9beSJagannathan Raman     if (!mr) {
323*15ccf9beSJagannathan Raman         return;
324*15ccf9beSJagannathan Raman     }
325*15ccf9beSJagannathan Raman 
326*15ccf9beSJagannathan Raman     dma_as = pci_device_iommu_address_space(o->pci_dev);
327*15ccf9beSJagannathan Raman 
328*15ccf9beSJagannathan Raman     memory_region_del_subregion(dma_as->root, mr);
329*15ccf9beSJagannathan Raman 
330*15ccf9beSJagannathan Raman     object_unparent((OBJECT(mr)));
331*15ccf9beSJagannathan Raman 
332*15ccf9beSJagannathan Raman     trace_vfu_dma_unregister((uint64_t)info->iova.iov_base);
333*15ccf9beSJagannathan Raman }
334*15ccf9beSJagannathan Raman 
33587f7249fSJagannathan Raman /*
33687f7249fSJagannathan Raman  * TYPE_VFU_OBJECT depends on the availability of the 'socket' and 'device'
33787f7249fSJagannathan Raman  * properties. It also depends on devices instantiated in QEMU. These
33887f7249fSJagannathan Raman  * dependencies are not available during the instance_init phase of this
33987f7249fSJagannathan Raman  * object's life-cycle. As such, the server is initialized after the
34087f7249fSJagannathan Raman  * machine is setup. machine_init_done_notifier notifies TYPE_VFU_OBJECT
34187f7249fSJagannathan Raman  * when the machine is setup, and the dependencies are available.
34287f7249fSJagannathan Raman  */
34387f7249fSJagannathan Raman static void vfu_object_machine_done(Notifier *notifier, void *data)
34487f7249fSJagannathan Raman {
34587f7249fSJagannathan Raman     VfuObject *o = container_of(notifier, VfuObject, machine_done);
34687f7249fSJagannathan Raman     Error *err = NULL;
34787f7249fSJagannathan Raman 
34887f7249fSJagannathan Raman     vfu_object_init_ctx(o, &err);
34987f7249fSJagannathan Raman 
35087f7249fSJagannathan Raman     if (err) {
35187f7249fSJagannathan Raman         error_propagate(&error_abort, err);
35287f7249fSJagannathan Raman     }
35387f7249fSJagannathan Raman }
35487f7249fSJagannathan Raman 
3559fb3fba1SJagannathan Raman /**
3569fb3fba1SJagannathan Raman  * vfu_object_init_ctx: Create and initialize libvfio-user context. Add
3579fb3fba1SJagannathan Raman  *     an unplug blocker for the associated PCI device. Setup a FD handler
3589fb3fba1SJagannathan Raman  *     to process incoming messages in the context's socket.
3599fb3fba1SJagannathan Raman  *
3609fb3fba1SJagannathan Raman  *     The socket and device properties are mandatory, and this function
3619fb3fba1SJagannathan Raman  *     will not create the context without them - the setters for these
3629fb3fba1SJagannathan Raman  *     properties should call this function when the property is set. The
3639fb3fba1SJagannathan Raman  *     machine should also be ready when this function is invoked - it is
3649fb3fba1SJagannathan Raman  *     because QEMU objects are initialized before devices, and the
3659fb3fba1SJagannathan Raman  *     associated PCI device wouldn't be available at the object
3669fb3fba1SJagannathan Raman  *     initialization time. Until these conditions are satisfied, this
3679fb3fba1SJagannathan Raman  *     function would return early without performing any task.
3689fb3fba1SJagannathan Raman  */
36987f7249fSJagannathan Raman static void vfu_object_init_ctx(VfuObject *o, Error **errp)
37087f7249fSJagannathan Raman {
37187f7249fSJagannathan Raman     ERRP_GUARD();
372a6e8d6d9SJagannathan Raman     DeviceState *dev = NULL;
373a6e8d6d9SJagannathan Raman     vfu_pci_type_t pci_type = VFU_PCI_TYPE_CONVENTIONAL;
374a6e8d6d9SJagannathan Raman     int ret;
37587f7249fSJagannathan Raman 
37687f7249fSJagannathan Raman     if (o->vfu_ctx || !o->socket || !o->device ||
37787f7249fSJagannathan Raman             !phase_check(PHASE_MACHINE_READY)) {
37887f7249fSJagannathan Raman         return;
37987f7249fSJagannathan Raman     }
38087f7249fSJagannathan Raman 
38187f7249fSJagannathan Raman     if (o->err) {
38287f7249fSJagannathan Raman         error_propagate(errp, o->err);
38387f7249fSJagannathan Raman         o->err = NULL;
38487f7249fSJagannathan Raman         return;
38587f7249fSJagannathan Raman     }
38687f7249fSJagannathan Raman 
3879fb3fba1SJagannathan Raman     o->vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, o->socket->u.q_unix.path,
3889fb3fba1SJagannathan Raman                                 LIBVFIO_USER_FLAG_ATTACH_NB,
38987f7249fSJagannathan Raman                                 o, VFU_DEV_TYPE_PCI);
39087f7249fSJagannathan Raman     if (o->vfu_ctx == NULL) {
39187f7249fSJagannathan Raman         error_setg(errp, "vfu: Failed to create context - %s", strerror(errno));
39287f7249fSJagannathan Raman         return;
39387f7249fSJagannathan Raman     }
394a6e8d6d9SJagannathan Raman 
395a6e8d6d9SJagannathan Raman     dev = qdev_find_recursive(sysbus_get_default(), o->device);
396a6e8d6d9SJagannathan Raman     if (dev == NULL) {
397a6e8d6d9SJagannathan Raman         error_setg(errp, "vfu: Device %s not found", o->device);
398a6e8d6d9SJagannathan Raman         goto fail;
399a6e8d6d9SJagannathan Raman     }
400a6e8d6d9SJagannathan Raman 
401a6e8d6d9SJagannathan Raman     if (!object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
402a6e8d6d9SJagannathan Raman         error_setg(errp, "vfu: %s not a PCI device", o->device);
403a6e8d6d9SJagannathan Raman         goto fail;
404a6e8d6d9SJagannathan Raman     }
405a6e8d6d9SJagannathan Raman 
406a6e8d6d9SJagannathan Raman     o->pci_dev = PCI_DEVICE(dev);
407a6e8d6d9SJagannathan Raman 
408a6e8d6d9SJagannathan Raman     object_ref(OBJECT(o->pci_dev));
409a6e8d6d9SJagannathan Raman 
410a6e8d6d9SJagannathan Raman     if (pci_is_express(o->pci_dev)) {
411a6e8d6d9SJagannathan Raman         pci_type = VFU_PCI_TYPE_EXPRESS;
412a6e8d6d9SJagannathan Raman     }
413a6e8d6d9SJagannathan Raman 
414a6e8d6d9SJagannathan Raman     ret = vfu_pci_init(o->vfu_ctx, pci_type, PCI_HEADER_TYPE_NORMAL, 0);
415a6e8d6d9SJagannathan Raman     if (ret < 0) {
416a6e8d6d9SJagannathan Raman         error_setg(errp,
417a6e8d6d9SJagannathan Raman                    "vfu: Failed to attach PCI device %s to context - %s",
418a6e8d6d9SJagannathan Raman                    o->device, strerror(errno));
419a6e8d6d9SJagannathan Raman         goto fail;
420a6e8d6d9SJagannathan Raman     }
421a6e8d6d9SJagannathan Raman 
422a6e8d6d9SJagannathan Raman     error_setg(&o->unplug_blocker,
423a6e8d6d9SJagannathan Raman                "vfu: %s for %s must be deleted before unplugging",
424a6e8d6d9SJagannathan Raman                TYPE_VFU_OBJECT, o->device);
425a6e8d6d9SJagannathan Raman     qdev_add_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
426a6e8d6d9SJagannathan Raman 
42790072f29SJagannathan Raman     ret = vfu_setup_region(o->vfu_ctx, VFU_PCI_DEV_CFG_REGION_IDX,
42890072f29SJagannathan Raman                            pci_config_size(o->pci_dev), &vfu_object_cfg_access,
42990072f29SJagannathan Raman                            VFU_REGION_FLAG_RW | VFU_REGION_FLAG_ALWAYS_CB,
43090072f29SJagannathan Raman                            NULL, 0, -1, 0);
43190072f29SJagannathan Raman     if (ret < 0) {
43290072f29SJagannathan Raman         error_setg(errp,
43390072f29SJagannathan Raman                    "vfu: Failed to setup config space handlers for %s- %s",
43490072f29SJagannathan Raman                    o->device, strerror(errno));
43590072f29SJagannathan Raman         goto fail;
43690072f29SJagannathan Raman     }
43790072f29SJagannathan Raman 
438*15ccf9beSJagannathan Raman     ret = vfu_setup_device_dma(o->vfu_ctx, &dma_register, &dma_unregister);
439*15ccf9beSJagannathan Raman     if (ret < 0) {
440*15ccf9beSJagannathan Raman         error_setg(errp, "vfu: Failed to setup DMA handlers for %s",
441*15ccf9beSJagannathan Raman                    o->device);
442*15ccf9beSJagannathan Raman         goto fail;
443*15ccf9beSJagannathan Raman     }
444*15ccf9beSJagannathan Raman 
4459fb3fba1SJagannathan Raman     ret = vfu_realize_ctx(o->vfu_ctx);
4469fb3fba1SJagannathan Raman     if (ret < 0) {
4479fb3fba1SJagannathan Raman         error_setg(errp, "vfu: Failed to realize device %s- %s",
4489fb3fba1SJagannathan Raman                    o->device, strerror(errno));
4499fb3fba1SJagannathan Raman         goto fail;
4509fb3fba1SJagannathan Raman     }
4519fb3fba1SJagannathan Raman 
4529fb3fba1SJagannathan Raman     o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx);
4539fb3fba1SJagannathan Raman     if (o->vfu_poll_fd < 0) {
4549fb3fba1SJagannathan Raman         error_setg(errp, "vfu: Failed to get poll fd %s", o->device);
4559fb3fba1SJagannathan Raman         goto fail;
4569fb3fba1SJagannathan Raman     }
4579fb3fba1SJagannathan Raman 
4589fb3fba1SJagannathan Raman     qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_attach_ctx, NULL, o);
4599fb3fba1SJagannathan Raman 
460a6e8d6d9SJagannathan Raman     return;
461a6e8d6d9SJagannathan Raman 
462a6e8d6d9SJagannathan Raman fail:
463a6e8d6d9SJagannathan Raman     vfu_destroy_ctx(o->vfu_ctx);
464a6e8d6d9SJagannathan Raman     if (o->unplug_blocker && o->pci_dev) {
465a6e8d6d9SJagannathan Raman         qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
466a6e8d6d9SJagannathan Raman         error_free(o->unplug_blocker);
467a6e8d6d9SJagannathan Raman         o->unplug_blocker = NULL;
468a6e8d6d9SJagannathan Raman     }
469a6e8d6d9SJagannathan Raman     if (o->pci_dev) {
470a6e8d6d9SJagannathan Raman         object_unref(OBJECT(o->pci_dev));
471a6e8d6d9SJagannathan Raman         o->pci_dev = NULL;
472a6e8d6d9SJagannathan Raman     }
473a6e8d6d9SJagannathan Raman     o->vfu_ctx = NULL;
4748f9a9259SJagannathan Raman }
4758f9a9259SJagannathan Raman 
4768f9a9259SJagannathan Raman static void vfu_object_init(Object *obj)
4778f9a9259SJagannathan Raman {
4788f9a9259SJagannathan Raman     VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj);
4798f9a9259SJagannathan Raman     VfuObject *o = VFU_OBJECT(obj);
4808f9a9259SJagannathan Raman 
4818f9a9259SJagannathan Raman     k->nr_devs++;
4828f9a9259SJagannathan Raman 
4838f9a9259SJagannathan Raman     if (!object_dynamic_cast(OBJECT(current_machine), TYPE_REMOTE_MACHINE)) {
4848f9a9259SJagannathan Raman         error_setg(&o->err, "vfu: %s only compatible with %s machine",
4858f9a9259SJagannathan Raman                    TYPE_VFU_OBJECT, TYPE_REMOTE_MACHINE);
4868f9a9259SJagannathan Raman         return;
4878f9a9259SJagannathan Raman     }
48887f7249fSJagannathan Raman 
48987f7249fSJagannathan Raman     if (!phase_check(PHASE_MACHINE_READY)) {
49087f7249fSJagannathan Raman         o->machine_done.notify = vfu_object_machine_done;
49187f7249fSJagannathan Raman         qemu_add_machine_init_done_notifier(&o->machine_done);
49287f7249fSJagannathan Raman     }
49387f7249fSJagannathan Raman 
4949fb3fba1SJagannathan Raman     o->vfu_poll_fd = -1;
4958f9a9259SJagannathan Raman }
4968f9a9259SJagannathan Raman 
4978f9a9259SJagannathan Raman static void vfu_object_finalize(Object *obj)
4988f9a9259SJagannathan Raman {
4998f9a9259SJagannathan Raman     VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj);
5008f9a9259SJagannathan Raman     VfuObject *o = VFU_OBJECT(obj);
5018f9a9259SJagannathan Raman 
5028f9a9259SJagannathan Raman     k->nr_devs--;
5038f9a9259SJagannathan Raman 
5048f9a9259SJagannathan Raman     qapi_free_SocketAddress(o->socket);
5058f9a9259SJagannathan Raman 
5068f9a9259SJagannathan Raman     o->socket = NULL;
5078f9a9259SJagannathan Raman 
5089fb3fba1SJagannathan Raman     if (o->vfu_poll_fd != -1) {
5099fb3fba1SJagannathan Raman         qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
5109fb3fba1SJagannathan Raman         o->vfu_poll_fd = -1;
5119fb3fba1SJagannathan Raman     }
5129fb3fba1SJagannathan Raman 
51387f7249fSJagannathan Raman     if (o->vfu_ctx) {
51487f7249fSJagannathan Raman         vfu_destroy_ctx(o->vfu_ctx);
51587f7249fSJagannathan Raman         o->vfu_ctx = NULL;
51687f7249fSJagannathan Raman     }
51787f7249fSJagannathan Raman 
5188f9a9259SJagannathan Raman     g_free(o->device);
5198f9a9259SJagannathan Raman 
5208f9a9259SJagannathan Raman     o->device = NULL;
5218f9a9259SJagannathan Raman 
522a6e8d6d9SJagannathan Raman     if (o->unplug_blocker && o->pci_dev) {
523a6e8d6d9SJagannathan Raman         qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
524a6e8d6d9SJagannathan Raman         error_free(o->unplug_blocker);
525a6e8d6d9SJagannathan Raman         o->unplug_blocker = NULL;
526a6e8d6d9SJagannathan Raman     }
527a6e8d6d9SJagannathan Raman 
528a6e8d6d9SJagannathan Raman     if (o->pci_dev) {
529a6e8d6d9SJagannathan Raman         object_unref(OBJECT(o->pci_dev));
530a6e8d6d9SJagannathan Raman         o->pci_dev = NULL;
531a6e8d6d9SJagannathan Raman     }
532a6e8d6d9SJagannathan Raman 
5338f9a9259SJagannathan Raman     if (!k->nr_devs && vfu_object_auto_shutdown()) {
5348f9a9259SJagannathan Raman         qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
5358f9a9259SJagannathan Raman     }
53687f7249fSJagannathan Raman 
53787f7249fSJagannathan Raman     if (o->machine_done.notify) {
53887f7249fSJagannathan Raman         qemu_remove_machine_init_done_notifier(&o->machine_done);
53987f7249fSJagannathan Raman         o->machine_done.notify = NULL;
54087f7249fSJagannathan Raman     }
5418f9a9259SJagannathan Raman }
5428f9a9259SJagannathan Raman 
5438f9a9259SJagannathan Raman static void vfu_object_class_init(ObjectClass *klass, void *data)
5448f9a9259SJagannathan Raman {
5458f9a9259SJagannathan Raman     VfuObjectClass *k = VFU_OBJECT_CLASS(klass);
5468f9a9259SJagannathan Raman 
5478f9a9259SJagannathan Raman     k->nr_devs = 0;
5488f9a9259SJagannathan Raman 
5498f9a9259SJagannathan Raman     object_class_property_add(klass, "socket", "SocketAddress", NULL,
5508f9a9259SJagannathan Raman                               vfu_object_set_socket, NULL, NULL);
5518f9a9259SJagannathan Raman     object_class_property_set_description(klass, "socket",
5528f9a9259SJagannathan Raman                                           "SocketAddress "
5538f9a9259SJagannathan Raman                                           "(ex: type=unix,path=/tmp/sock). "
5548f9a9259SJagannathan Raman                                           "Only UNIX is presently supported");
5558f9a9259SJagannathan Raman     object_class_property_add_str(klass, "device", NULL,
5568f9a9259SJagannathan Raman                                   vfu_object_set_device);
5578f9a9259SJagannathan Raman     object_class_property_set_description(klass, "device",
5588f9a9259SJagannathan Raman                                           "device ID - only PCI devices "
5598f9a9259SJagannathan Raman                                           "are presently supported");
5608f9a9259SJagannathan Raman }
5618f9a9259SJagannathan Raman 
5628f9a9259SJagannathan Raman static const TypeInfo vfu_object_info = {
5638f9a9259SJagannathan Raman     .name = TYPE_VFU_OBJECT,
5648f9a9259SJagannathan Raman     .parent = TYPE_OBJECT,
5658f9a9259SJagannathan Raman     .instance_size = sizeof(VfuObject),
5668f9a9259SJagannathan Raman     .instance_init = vfu_object_init,
5678f9a9259SJagannathan Raman     .instance_finalize = vfu_object_finalize,
5688f9a9259SJagannathan Raman     .class_size = sizeof(VfuObjectClass),
5698f9a9259SJagannathan Raman     .class_init = vfu_object_class_init,
5708f9a9259SJagannathan Raman     .interfaces = (InterfaceInfo[]) {
5718f9a9259SJagannathan Raman         { TYPE_USER_CREATABLE },
5728f9a9259SJagannathan Raman         { }
5738f9a9259SJagannathan Raman     }
5748f9a9259SJagannathan Raman };
5758f9a9259SJagannathan Raman 
5768f9a9259SJagannathan Raman static void vfu_register_types(void)
5778f9a9259SJagannathan Raman {
5788f9a9259SJagannathan Raman     type_register_static(&vfu_object_info);
5798f9a9259SJagannathan Raman }
5808f9a9259SJagannathan Raman 
5818f9a9259SJagannathan Raman type_init(vfu_register_types);
582