123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958 |
- /**
- * QEMU vfio-user-server server object
- *
- * Copyright © 2022 Oracle and/or its affiliates.
- *
- * This work is licensed under the terms of the GNU GPL-v2, version 2 or later.
- *
- * See the COPYING file in the top-level directory.
- *
- */
- /**
- * Usage: add options:
- * -machine x-remote,vfio-user=on,auto-shutdown=on
- * -device <PCI-device>,id=<pci-dev-id>
- * -object x-vfio-user-server,id=<id>,type=unix,path=<socket-path>,
- * device=<pci-dev-id>
- *
- * Note that x-vfio-user-server object must be used with x-remote machine only.
- * This server could only support PCI devices for now.
- *
- * type - SocketAddress type - presently "unix" alone is supported. Required
- * option
- *
- * path - named unix socket, it will be created by the server. It is
- * a required option
- *
- * device - id of a device on the server, a required option. PCI devices
- * alone are supported presently.
- *
- * notes - x-vfio-user-server could block IO and monitor during the
- * initialization phase.
- *
- * When x-remote machine has the auto-shutdown property
- * enabled (default), x-vfio-user-server terminates after the last
- * client disconnects. Otherwise, it will continue running until
- * explicitly killed.
- */
- #include "qemu/osdep.h"
- #include "qom/object.h"
- #include "qom/object_interfaces.h"
- #include "qemu/error-report.h"
- #include "trace.h"
- #include "system/runstate.h"
- #include "hw/boards.h"
- #include "hw/remote/machine.h"
- #include "qapi/error.h"
- #include "qapi/qapi-visit-sockets.h"
- #include "qapi/qapi-events-misc.h"
- #include "qemu/notify.h"
- #include "qemu/thread.h"
- #include "qemu/main-loop.h"
- #include "system/system.h"
- #include "libvfio-user.h"
- #include "hw/qdev-core.h"
- #include "hw/pci/pci.h"
- #include "qemu/timer.h"
- #include "exec/memory.h"
- #include "hw/pci/msi.h"
- #include "hw/pci/msix.h"
- #include "hw/remote/vfio-user-obj.h"
- #define TYPE_VFU_OBJECT "x-vfio-user-server"
- OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT)
- /**
- * VFU_OBJECT_ERROR - reports an error message.
- *
- * If auto_shutdown is set, it aborts the machine on error. Otherwise,
- * it logs an error message without aborting. auto_shutdown is disabled
- * when the server serves clients from multiple VMs; as such, an error
- * from one VM shouldn't be able to disrupt other VM's services.
- */
- #define VFU_OBJECT_ERROR(o, fmt, ...) \
- { \
- if (vfu_object_auto_shutdown()) { \
- error_setg(&error_abort, (fmt), ## __VA_ARGS__); \
- } else { \
- error_report((fmt), ## __VA_ARGS__); \
- } \
- } \
- struct VfuObjectClass {
- ObjectClass parent_class;
- unsigned int nr_devs;
- };
- struct VfuObject {
- /* private */
- Object parent;
- SocketAddress *socket;
- char *device;
- Error *err;
- Notifier machine_done;
- vfu_ctx_t *vfu_ctx;
- PCIDevice *pci_dev;
- Error *unplug_blocker;
- int vfu_poll_fd;
- MSITriggerFunc *default_msi_trigger;
- MSIPrepareMessageFunc *default_msi_prepare_message;
- MSIxPrepareMessageFunc *default_msix_prepare_message;
- };
- static void vfu_object_init_ctx(VfuObject *o, Error **errp);
- static bool vfu_object_auto_shutdown(void)
- {
- bool auto_shutdown = true;
- Error *local_err = NULL;
- if (!current_machine) {
- return auto_shutdown;
- }
- auto_shutdown = object_property_get_bool(OBJECT(current_machine),
- "auto-shutdown",
- &local_err);
- /*
- * local_err would be set if no such property exists - safe to ignore.
- * Unlikely scenario as auto-shutdown is always defined for
- * TYPE_REMOTE_MACHINE, and TYPE_VFU_OBJECT only works with
- * TYPE_REMOTE_MACHINE
- */
- if (local_err) {
- auto_shutdown = true;
- error_free(local_err);
- }
- return auto_shutdown;
- }
- static void vfu_object_set_socket(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
- {
- VfuObject *o = VFU_OBJECT(obj);
- if (o->vfu_ctx) {
- error_setg(errp, "vfu: Unable to set socket property - server busy");
- return;
- }
- qapi_free_SocketAddress(o->socket);
- o->socket = NULL;
- visit_type_SocketAddress(v, name, &o->socket, errp);
- if (o->socket->type != SOCKET_ADDRESS_TYPE_UNIX) {
- error_setg(errp, "vfu: Unsupported socket type - %s",
- SocketAddressType_str(o->socket->type));
- qapi_free_SocketAddress(o->socket);
- o->socket = NULL;
- return;
- }
- trace_vfu_prop("socket", o->socket->u.q_unix.path);
- vfu_object_init_ctx(o, errp);
- }
- static void vfu_object_set_device(Object *obj, const char *str, Error **errp)
- {
- VfuObject *o = VFU_OBJECT(obj);
- if (o->vfu_ctx) {
- error_setg(errp, "vfu: Unable to set device property - server busy");
- return;
- }
- g_free(o->device);
- o->device = g_strdup(str);
- trace_vfu_prop("device", str);
- vfu_object_init_ctx(o, errp);
- }
- static void vfu_object_ctx_run(void *opaque)
- {
- VfuObject *o = opaque;
- const char *vfu_id;
- char *vfu_path, *pci_dev_path;
- int ret = -1;
- while (ret != 0) {
- ret = vfu_run_ctx(o->vfu_ctx);
- if (ret < 0) {
- if (errno == EINTR) {
- continue;
- } else if (errno == ENOTCONN) {
- vfu_id = object_get_canonical_path_component(OBJECT(o));
- vfu_path = object_get_canonical_path(OBJECT(o));
- g_assert(o->pci_dev);
- pci_dev_path = object_get_canonical_path(OBJECT(o->pci_dev));
- /* o->device is a required property and is non-NULL here */
- g_assert(o->device);
- qapi_event_send_vfu_client_hangup(vfu_id, vfu_path,
- o->device, pci_dev_path);
- qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
- o->vfu_poll_fd = -1;
- object_unparent(OBJECT(o));
- g_free(vfu_path);
- g_free(pci_dev_path);
- break;
- } else {
- VFU_OBJECT_ERROR(o, "vfu: Failed to run device %s - %s",
- o->device, strerror(errno));
- break;
- }
- }
- }
- }
- static void vfu_object_attach_ctx(void *opaque)
- {
- VfuObject *o = opaque;
- GPollFD pfds[1];
- int ret;
- qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
- pfds[0].fd = o->vfu_poll_fd;
- pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
- retry_attach:
- ret = vfu_attach_ctx(o->vfu_ctx);
- if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
- /**
- * vfu_object_attach_ctx can block QEMU's main loop
- * during attach - the monitor and other IO
- * could be unresponsive during this time.
- */
- (void)qemu_poll_ns(pfds, 1, 500 * (int64_t)SCALE_MS);
- goto retry_attach;
- } else if (ret < 0) {
- VFU_OBJECT_ERROR(o, "vfu: Failed to attach device %s to context - %s",
- o->device, strerror(errno));
- return;
- }
- o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx);
- if (o->vfu_poll_fd < 0) {
- VFU_OBJECT_ERROR(o, "vfu: Failed to get poll fd %s", o->device);
- return;
- }
- qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_ctx_run, NULL, o);
- }
- static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf,
- size_t count, loff_t offset,
- const bool is_write)
- {
- VfuObject *o = vfu_get_private(vfu_ctx);
- uint32_t pci_access_width = sizeof(uint32_t);
- size_t bytes = count;
- uint32_t val = 0;
- char *ptr = buf;
- int len;
- /*
- * Writes to the BAR registers would trigger an update to the
- * global Memory and IO AddressSpaces. But the remote device
- * never uses the global AddressSpaces, therefore overlapping
- * memory regions are not a problem
- */
- while (bytes > 0) {
- len = (bytes > pci_access_width) ? pci_access_width : bytes;
- if (is_write) {
- val = ldn_le_p(ptr, len);
- pci_host_config_write_common(o->pci_dev, offset,
- pci_config_size(o->pci_dev),
- val, len);
- trace_vfu_cfg_write(offset, val);
- } else {
- val = pci_host_config_read_common(o->pci_dev, offset,
- pci_config_size(o->pci_dev), len);
- stn_le_p(ptr, len, val);
- trace_vfu_cfg_read(offset, val);
- }
- offset += len;
- ptr += len;
- bytes -= len;
- }
- return count;
- }
- static void dma_register(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
- {
- VfuObject *o = vfu_get_private(vfu_ctx);
- AddressSpace *dma_as = NULL;
- MemoryRegion *subregion = NULL;
- g_autofree char *name = NULL;
- struct iovec *iov = &info->iova;
- if (!info->vaddr) {
- return;
- }
- name = g_strdup_printf("mem-%s-%"PRIx64"", o->device,
- (uint64_t)info->vaddr);
- subregion = g_new0(MemoryRegion, 1);
- memory_region_init_ram_ptr(subregion, NULL, name,
- iov->iov_len, info->vaddr);
- dma_as = pci_device_iommu_address_space(o->pci_dev);
- memory_region_add_subregion(dma_as->root, (hwaddr)iov->iov_base, subregion);
- trace_vfu_dma_register((uint64_t)iov->iov_base, iov->iov_len);
- }
- static void dma_unregister(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
- {
- VfuObject *o = vfu_get_private(vfu_ctx);
- AddressSpace *dma_as = NULL;
- MemoryRegion *mr = NULL;
- ram_addr_t offset;
- mr = memory_region_from_host(info->vaddr, &offset);
- if (!mr) {
- return;
- }
- dma_as = pci_device_iommu_address_space(o->pci_dev);
- memory_region_del_subregion(dma_as->root, mr);
- object_unparent((OBJECT(mr)));
- trace_vfu_dma_unregister((uint64_t)info->iova.iov_base);
- }
- static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset,
- hwaddr size, const bool is_write)
- {
- uint8_t *ptr = buf;
- bool release_lock = false;
- uint8_t *ram_ptr = NULL;
- MemTxResult result;
- int access_size;
- uint64_t val;
- if (memory_access_is_direct(mr, is_write)) {
- /**
- * Some devices expose a PCI expansion ROM, which could be buffer
- * based as compared to other regions which are primarily based on
- * MemoryRegionOps. memory_region_find() would already check
- * for buffer overflow, we don't need to repeat it here.
- */
- ram_ptr = memory_region_get_ram_ptr(mr);
- if (is_write) {
- memcpy((ram_ptr + offset), buf, size);
- } else {
- memcpy(buf, (ram_ptr + offset), size);
- }
- return 0;
- }
- while (size) {
- /**
- * The read/write logic used below is similar to the ones in
- * flatview_read/write_continue()
- */
- release_lock = prepare_mmio_access(mr);
- access_size = memory_access_size(mr, size, offset);
- if (is_write) {
- val = ldn_he_p(ptr, access_size);
- result = memory_region_dispatch_write(mr, offset, val,
- size_memop(access_size),
- MEMTXATTRS_UNSPECIFIED);
- } else {
- result = memory_region_dispatch_read(mr, offset, &val,
- size_memop(access_size),
- MEMTXATTRS_UNSPECIFIED);
- stn_he_p(ptr, access_size, val);
- }
- if (release_lock) {
- bql_unlock();
- release_lock = false;
- }
- if (result != MEMTX_OK) {
- return -1;
- }
- size -= access_size;
- ptr += access_size;
- offset += access_size;
- }
- return 0;
- }
- static size_t vfu_object_bar_rw(PCIDevice *pci_dev, int pci_bar,
- hwaddr bar_offset, char * const buf,
- hwaddr len, const bool is_write)
- {
- MemoryRegionSection section = { 0 };
- uint8_t *ptr = (uint8_t *)buf;
- MemoryRegion *section_mr = NULL;
- uint64_t section_size;
- hwaddr section_offset;
- hwaddr size = 0;
- while (len) {
- section = memory_region_find(pci_dev->io_regions[pci_bar].memory,
- bar_offset, len);
- if (!section.mr) {
- warn_report("vfu: invalid address 0x%"PRIx64"", bar_offset);
- return size;
- }
- section_mr = section.mr;
- section_offset = section.offset_within_region;
- section_size = int128_get64(section.size);
- if (is_write && section_mr->readonly) {
- warn_report("vfu: attempting to write to readonly region in "
- "bar %d - [0x%"PRIx64" - 0x%"PRIx64"]",
- pci_bar, bar_offset,
- (bar_offset + section_size));
- memory_region_unref(section_mr);
- return size;
- }
- if (vfu_object_mr_rw(section_mr, ptr, section_offset,
- section_size, is_write)) {
- warn_report("vfu: failed to %s "
- "[0x%"PRIx64" - 0x%"PRIx64"] in bar %d",
- is_write ? "write to" : "read from", bar_offset,
- (bar_offset + section_size), pci_bar);
- memory_region_unref(section_mr);
- return size;
- }
- size += section_size;
- bar_offset += section_size;
- ptr += section_size;
- len -= section_size;
- memory_region_unref(section_mr);
- }
- return size;
- }
- /**
- * VFU_OBJECT_BAR_HANDLER - macro for defining handlers for PCI BARs.
- *
- * To create handler for BAR number 2, VFU_OBJECT_BAR_HANDLER(2) would
- * define vfu_object_bar2_handler
- */
- #define VFU_OBJECT_BAR_HANDLER(BAR_NO) \
- static ssize_t vfu_object_bar##BAR_NO##_handler(vfu_ctx_t *vfu_ctx, \
- char * const buf, size_t count, \
- loff_t offset, const bool is_write) \
- { \
- VfuObject *o = vfu_get_private(vfu_ctx); \
- PCIDevice *pci_dev = o->pci_dev; \
- \
- return vfu_object_bar_rw(pci_dev, BAR_NO, offset, \
- buf, count, is_write); \
- } \
- VFU_OBJECT_BAR_HANDLER(0)
- VFU_OBJECT_BAR_HANDLER(1)
- VFU_OBJECT_BAR_HANDLER(2)
- VFU_OBJECT_BAR_HANDLER(3)
- VFU_OBJECT_BAR_HANDLER(4)
- VFU_OBJECT_BAR_HANDLER(5)
- VFU_OBJECT_BAR_HANDLER(6)
- static vfu_region_access_cb_t *vfu_object_bar_handlers[PCI_NUM_REGIONS] = {
- &vfu_object_bar0_handler,
- &vfu_object_bar1_handler,
- &vfu_object_bar2_handler,
- &vfu_object_bar3_handler,
- &vfu_object_bar4_handler,
- &vfu_object_bar5_handler,
- &vfu_object_bar6_handler,
- };
- /**
- * vfu_object_register_bars - Identify active BAR regions of pdev and setup
- * callbacks to handle read/write accesses
- */
- static void vfu_object_register_bars(vfu_ctx_t *vfu_ctx, PCIDevice *pdev)
- {
- int flags = VFU_REGION_FLAG_RW;
- int i;
- for (i = 0; i < PCI_NUM_REGIONS; i++) {
- if (!pdev->io_regions[i].size) {
- continue;
- }
- if ((i == VFU_PCI_DEV_ROM_REGION_IDX) ||
- pdev->io_regions[i].memory->readonly) {
- flags &= ~VFU_REGION_FLAG_WRITE;
- }
- vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX + i,
- (size_t)pdev->io_regions[i].size,
- vfu_object_bar_handlers[i],
- flags, NULL, 0, -1, 0);
- trace_vfu_bar_register(i, pdev->io_regions[i].addr,
- pdev->io_regions[i].size);
- }
- }
- static int vfu_object_map_irq(PCIDevice *pci_dev, int intx)
- {
- int pci_bdf = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(pci_dev)),
- pci_dev->devfn);
- return pci_bdf;
- }
- static void vfu_object_set_irq(void *opaque, int pirq, int level)
- {
- PCIBus *pci_bus = opaque;
- PCIDevice *pci_dev = NULL;
- vfu_ctx_t *vfu_ctx = NULL;
- int pci_bus_num, devfn;
- if (level) {
- pci_bus_num = PCI_BUS_NUM(pirq);
- devfn = PCI_BDF_TO_DEVFN(pirq);
- /*
- * pci_find_device() performs at O(1) if the device is attached
- * to the root PCI bus. Whereas, if the device is attached to a
- * secondary PCI bus (such as when a root port is involved),
- * finding the parent PCI bus could take O(n)
- */
- pci_dev = pci_find_device(pci_bus, pci_bus_num, devfn);
- vfu_ctx = pci_dev->irq_opaque;
- g_assert(vfu_ctx);
- vfu_irq_trigger(vfu_ctx, 0);
- }
- }
- static MSIMessage vfu_object_msi_prepare_msg(PCIDevice *pci_dev,
- unsigned int vector)
- {
- MSIMessage msg;
- msg.address = 0;
- msg.data = vector;
- return msg;
- }
- static void vfu_object_msi_trigger(PCIDevice *pci_dev, MSIMessage msg)
- {
- vfu_ctx_t *vfu_ctx = pci_dev->irq_opaque;
- vfu_irq_trigger(vfu_ctx, msg.data);
- }
- static void vfu_object_setup_msi_cbs(VfuObject *o)
- {
- o->default_msi_trigger = o->pci_dev->msi_trigger;
- o->default_msi_prepare_message = o->pci_dev->msi_prepare_message;
- o->default_msix_prepare_message = o->pci_dev->msix_prepare_message;
- o->pci_dev->msi_trigger = vfu_object_msi_trigger;
- o->pci_dev->msi_prepare_message = vfu_object_msi_prepare_msg;
- o->pci_dev->msix_prepare_message = vfu_object_msi_prepare_msg;
- }
- static void vfu_object_restore_msi_cbs(VfuObject *o)
- {
- o->pci_dev->msi_trigger = o->default_msi_trigger;
- o->pci_dev->msi_prepare_message = o->default_msi_prepare_message;
- o->pci_dev->msix_prepare_message = o->default_msix_prepare_message;
- }
- static void vfu_msix_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start,
- uint32_t count, bool mask)
- {
- VfuObject *o = vfu_get_private(vfu_ctx);
- uint32_t vector;
- for (vector = start; vector < count; vector++) {
- msix_set_mask(o->pci_dev, vector, mask);
- }
- }
- static void vfu_msi_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start,
- uint32_t count, bool mask)
- {
- VfuObject *o = vfu_get_private(vfu_ctx);
- Error *err = NULL;
- uint32_t vector;
- for (vector = start; vector < count; vector++) {
- msi_set_mask(o->pci_dev, vector, mask, &err);
- if (err) {
- VFU_OBJECT_ERROR(o, "vfu: %s: %s", o->device,
- error_get_pretty(err));
- error_free(err);
- err = NULL;
- }
- }
- }
- static int vfu_object_setup_irqs(VfuObject *o, PCIDevice *pci_dev)
- {
- vfu_ctx_t *vfu_ctx = o->vfu_ctx;
- int ret;
- ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1);
- if (ret < 0) {
- return ret;
- }
- if (msix_nr_vectors_allocated(pci_dev)) {
- ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSIX_IRQ,
- msix_nr_vectors_allocated(pci_dev));
- vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSIX_IRQ,
- &vfu_msix_irq_state);
- } else if (msi_nr_vectors_allocated(pci_dev)) {
- ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSI_IRQ,
- msi_nr_vectors_allocated(pci_dev));
- vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSI_IRQ,
- &vfu_msi_irq_state);
- }
- if (ret < 0) {
- return ret;
- }
- vfu_object_setup_msi_cbs(o);
- pci_dev->irq_opaque = vfu_ctx;
- return 0;
- }
- void vfu_object_set_bus_irq(PCIBus *pci_bus)
- {
- int bus_num = pci_bus_num(pci_bus);
- int max_bdf = PCI_BUILD_BDF(bus_num, PCI_DEVFN_MAX - 1);
- pci_bus_irqs(pci_bus, vfu_object_set_irq, pci_bus, max_bdf);
- pci_bus_map_irqs(pci_bus, vfu_object_map_irq);
- }
- static int vfu_object_device_reset(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type)
- {
- VfuObject *o = vfu_get_private(vfu_ctx);
- /* vfu_object_ctx_run() handles lost connection */
- if (type == VFU_RESET_LOST_CONN) {
- return 0;
- }
- device_cold_reset(DEVICE(o->pci_dev));
- return 0;
- }
- /*
- * TYPE_VFU_OBJECT depends on the availability of the 'socket' and 'device'
- * properties. It also depends on devices instantiated in QEMU. These
- * dependencies are not available during the instance_init phase of this
- * object's life-cycle. As such, the server is initialized after the
- * machine is setup. machine_init_done_notifier notifies TYPE_VFU_OBJECT
- * when the machine is setup, and the dependencies are available.
- */
- static void vfu_object_machine_done(Notifier *notifier, void *data)
- {
- VfuObject *o = container_of(notifier, VfuObject, machine_done);
- Error *err = NULL;
- vfu_object_init_ctx(o, &err);
- if (err) {
- error_propagate(&error_abort, err);
- }
- }
- /**
- * vfu_object_init_ctx: Create and initialize libvfio-user context. Add
- * an unplug blocker for the associated PCI device. Setup a FD handler
- * to process incoming messages in the context's socket.
- *
- * The socket and device properties are mandatory, and this function
- * will not create the context without them - the setters for these
- * properties should call this function when the property is set. The
- * machine should also be ready when this function is invoked - it is
- * because QEMU objects are initialized before devices, and the
- * associated PCI device wouldn't be available at the object
- * initialization time. Until these conditions are satisfied, this
- * function would return early without performing any task.
- */
- static void vfu_object_init_ctx(VfuObject *o, Error **errp)
- {
- DeviceState *dev = NULL;
- vfu_pci_type_t pci_type = VFU_PCI_TYPE_CONVENTIONAL;
- int ret;
- if (o->vfu_ctx || !o->socket || !o->device ||
- !phase_check(PHASE_MACHINE_READY)) {
- return;
- }
- if (o->err) {
- error_propagate(errp, o->err);
- o->err = NULL;
- return;
- }
- o->vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, o->socket->u.q_unix.path,
- LIBVFIO_USER_FLAG_ATTACH_NB,
- o, VFU_DEV_TYPE_PCI);
- if (o->vfu_ctx == NULL) {
- error_setg(errp, "vfu: Failed to create context - %s", strerror(errno));
- return;
- }
- dev = qdev_find_recursive(sysbus_get_default(), o->device);
- if (dev == NULL) {
- error_setg(errp, "vfu: Device %s not found", o->device);
- goto fail;
- }
- if (!object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
- error_setg(errp, "vfu: %s not a PCI device", o->device);
- goto fail;
- }
- o->pci_dev = PCI_DEVICE(dev);
- object_ref(OBJECT(o->pci_dev));
- if (pci_is_express(o->pci_dev)) {
- pci_type = VFU_PCI_TYPE_EXPRESS;
- }
- ret = vfu_pci_init(o->vfu_ctx, pci_type, PCI_HEADER_TYPE_NORMAL, 0);
- if (ret < 0) {
- error_setg(errp,
- "vfu: Failed to attach PCI device %s to context - %s",
- o->device, strerror(errno));
- goto fail;
- }
- error_setg(&o->unplug_blocker,
- "vfu: %s for %s must be deleted before unplugging",
- TYPE_VFU_OBJECT, o->device);
- qdev_add_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
- ret = vfu_setup_region(o->vfu_ctx, VFU_PCI_DEV_CFG_REGION_IDX,
- pci_config_size(o->pci_dev), &vfu_object_cfg_access,
- VFU_REGION_FLAG_RW | VFU_REGION_FLAG_ALWAYS_CB,
- NULL, 0, -1, 0);
- if (ret < 0) {
- error_setg(errp,
- "vfu: Failed to setup config space handlers for %s- %s",
- o->device, strerror(errno));
- goto fail;
- }
- ret = vfu_setup_device_dma(o->vfu_ctx, &dma_register, &dma_unregister);
- if (ret < 0) {
- error_setg(errp, "vfu: Failed to setup DMA handlers for %s",
- o->device);
- goto fail;
- }
- vfu_object_register_bars(o->vfu_ctx, o->pci_dev);
- ret = vfu_object_setup_irqs(o, o->pci_dev);
- if (ret < 0) {
- error_setg(errp, "vfu: Failed to setup interrupts for %s",
- o->device);
- goto fail;
- }
- ret = vfu_setup_device_reset_cb(o->vfu_ctx, &vfu_object_device_reset);
- if (ret < 0) {
- error_setg(errp, "vfu: Failed to setup reset callback");
- goto fail;
- }
- ret = vfu_realize_ctx(o->vfu_ctx);
- if (ret < 0) {
- error_setg(errp, "vfu: Failed to realize device %s- %s",
- o->device, strerror(errno));
- goto fail;
- }
- o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx);
- if (o->vfu_poll_fd < 0) {
- error_setg(errp, "vfu: Failed to get poll fd %s", o->device);
- goto fail;
- }
- qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_attach_ctx, NULL, o);
- return;
- fail:
- vfu_destroy_ctx(o->vfu_ctx);
- if (o->unplug_blocker && o->pci_dev) {
- qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
- error_free(o->unplug_blocker);
- o->unplug_blocker = NULL;
- }
- if (o->pci_dev) {
- vfu_object_restore_msi_cbs(o);
- o->pci_dev->irq_opaque = NULL;
- object_unref(OBJECT(o->pci_dev));
- o->pci_dev = NULL;
- }
- o->vfu_ctx = NULL;
- }
- static void vfu_object_init(Object *obj)
- {
- VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj);
- VfuObject *o = VFU_OBJECT(obj);
- k->nr_devs++;
- if (!object_dynamic_cast(OBJECT(current_machine), TYPE_REMOTE_MACHINE)) {
- error_setg(&o->err, "vfu: %s only compatible with %s machine",
- TYPE_VFU_OBJECT, TYPE_REMOTE_MACHINE);
- return;
- }
- if (!phase_check(PHASE_MACHINE_READY)) {
- o->machine_done.notify = vfu_object_machine_done;
- qemu_add_machine_init_done_notifier(&o->machine_done);
- }
- o->vfu_poll_fd = -1;
- }
- static void vfu_object_finalize(Object *obj)
- {
- VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj);
- VfuObject *o = VFU_OBJECT(obj);
- k->nr_devs--;
- qapi_free_SocketAddress(o->socket);
- o->socket = NULL;
- if (o->vfu_poll_fd != -1) {
- qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
- o->vfu_poll_fd = -1;
- }
- if (o->vfu_ctx) {
- vfu_destroy_ctx(o->vfu_ctx);
- o->vfu_ctx = NULL;
- }
- g_free(o->device);
- o->device = NULL;
- if (o->unplug_blocker && o->pci_dev) {
- qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
- error_free(o->unplug_blocker);
- o->unplug_blocker = NULL;
- }
- if (o->pci_dev) {
- vfu_object_restore_msi_cbs(o);
- o->pci_dev->irq_opaque = NULL;
- object_unref(OBJECT(o->pci_dev));
- o->pci_dev = NULL;
- }
- if (!k->nr_devs && vfu_object_auto_shutdown()) {
- qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
- }
- if (o->machine_done.notify) {
- qemu_remove_machine_init_done_notifier(&o->machine_done);
- o->machine_done.notify = NULL;
- }
- }
- static void vfu_object_class_init(ObjectClass *klass, void *data)
- {
- VfuObjectClass *k = VFU_OBJECT_CLASS(klass);
- k->nr_devs = 0;
- object_class_property_add(klass, "socket", "SocketAddress", NULL,
- vfu_object_set_socket, NULL, NULL);
- object_class_property_set_description(klass, "socket",
- "SocketAddress "
- "(ex: type=unix,path=/tmp/sock). "
- "Only UNIX is presently supported");
- object_class_property_add_str(klass, "device", NULL,
- vfu_object_set_device);
- object_class_property_set_description(klass, "device",
- "device ID - only PCI devices "
- "are presently supported");
- }
- static const TypeInfo vfu_object_info = {
- .name = TYPE_VFU_OBJECT,
- .parent = TYPE_OBJECT,
- .instance_size = sizeof(VfuObject),
- .instance_init = vfu_object_init,
- .instance_finalize = vfu_object_finalize,
- .class_size = sizeof(VfuObjectClass),
- .class_init = vfu_object_class_init,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_USER_CREATABLE },
- { }
- }
- };
- static void vfu_register_types(void)
- {
- type_register_static(&vfu_object_info);
- }
- type_init(vfu_register_types);
|