xref: /qemu/hw/vfio-user/pci.c (revision 0b3d881a061b284a3db00d7fe9d33581fb424287)
19fca2b7dSJohn Levon /*
29fca2b7dSJohn Levon  * vfio PCI device over a UNIX socket.
39fca2b7dSJohn Levon  *
49fca2b7dSJohn Levon  * Copyright © 2018, 2021 Oracle and/or its affiliates.
59fca2b7dSJohn Levon  *
69fca2b7dSJohn Levon  * SPDX-License-Identifier: GPL-2.0-or-later
79fca2b7dSJohn Levon  */
89fca2b7dSJohn Levon 
99fca2b7dSJohn Levon #include <sys/ioctl.h>
109fca2b7dSJohn Levon #include "qemu/osdep.h"
119fca2b7dSJohn Levon #include "qapi-visit-sockets.h"
129fca2b7dSJohn Levon 
139fca2b7dSJohn Levon #include "hw/qdev-properties.h"
149fca2b7dSJohn Levon #include "hw/vfio/pci.h"
15438d863fSJohn Levon #include "hw/vfio-user/proxy.h"
169fca2b7dSJohn Levon 
179fca2b7dSJohn Levon #define TYPE_VFIO_USER_PCI "vfio-user-pci"
189fca2b7dSJohn Levon OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI)
199fca2b7dSJohn Levon 
209fca2b7dSJohn Levon struct VFIOUserPCIDevice {
219fca2b7dSJohn Levon     VFIOPCIDevice device;
229fca2b7dSJohn Levon     SocketAddress *socket;
239fca2b7dSJohn Levon };
249fca2b7dSJohn Levon 
259fca2b7dSJohn Levon /*
26*0b3d881aSJohn Levon  * Incoming request message callback.
27*0b3d881aSJohn Levon  *
28*0b3d881aSJohn Levon  * Runs off main loop, so BQL held.
29*0b3d881aSJohn Levon  */
30*0b3d881aSJohn Levon static void vfio_user_pci_process_req(void *opaque, VFIOUserMsg *msg)
31*0b3d881aSJohn Levon {
32*0b3d881aSJohn Levon 
33*0b3d881aSJohn Levon }
34*0b3d881aSJohn Levon 
35*0b3d881aSJohn Levon /*
369fca2b7dSJohn Levon  * Emulated devices don't use host hot reset
379fca2b7dSJohn Levon  */
389fca2b7dSJohn Levon static void vfio_user_compute_needs_reset(VFIODevice *vbasedev)
399fca2b7dSJohn Levon {
409fca2b7dSJohn Levon     vbasedev->needs_reset = false;
419fca2b7dSJohn Levon }
429fca2b7dSJohn Levon 
439fca2b7dSJohn Levon static Object *vfio_user_pci_get_object(VFIODevice *vbasedev)
449fca2b7dSJohn Levon {
459fca2b7dSJohn Levon     VFIOUserPCIDevice *vdev = container_of(vbasedev, VFIOUserPCIDevice,
469fca2b7dSJohn Levon                                            device.vbasedev);
479fca2b7dSJohn Levon 
489fca2b7dSJohn Levon     return OBJECT(vdev);
499fca2b7dSJohn Levon }
509fca2b7dSJohn Levon 
519fca2b7dSJohn Levon static VFIODeviceOps vfio_user_pci_ops = {
529fca2b7dSJohn Levon     .vfio_compute_needs_reset = vfio_user_compute_needs_reset,
539fca2b7dSJohn Levon     .vfio_eoi = vfio_pci_intx_eoi,
549fca2b7dSJohn Levon     .vfio_get_object = vfio_user_pci_get_object,
559fca2b7dSJohn Levon     /* No live migration support yet. */
569fca2b7dSJohn Levon     .vfio_save_config = NULL,
579fca2b7dSJohn Levon     .vfio_load_config = NULL,
589fca2b7dSJohn Levon };
599fca2b7dSJohn Levon 
609fca2b7dSJohn Levon static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp)
619fca2b7dSJohn Levon {
629fca2b7dSJohn Levon     ERRP_GUARD();
639fca2b7dSJohn Levon     VFIOUserPCIDevice *udev = VFIO_USER_PCI(pdev);
649fca2b7dSJohn Levon     VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev);
659fca2b7dSJohn Levon     VFIODevice *vbasedev = &vdev->vbasedev;
669fca2b7dSJohn Levon     const char *sock_name;
679fca2b7dSJohn Levon     AddressSpace *as;
68438d863fSJohn Levon     SocketAddress addr;
69438d863fSJohn Levon     VFIOUserProxy *proxy;
709fca2b7dSJohn Levon 
719fca2b7dSJohn Levon     if (!udev->socket) {
729fca2b7dSJohn Levon         error_setg(errp, "No socket specified");
739fca2b7dSJohn Levon         error_append_hint(errp, "e.g. -device '{"
749fca2b7dSJohn Levon             "\"driver\":\"vfio-user-pci\", "
759fca2b7dSJohn Levon             "\"socket\": {\"path\": \"/tmp/vfio-user.sock\", "
769fca2b7dSJohn Levon             "\"type\": \"unix\"}'"
779fca2b7dSJohn Levon             "}'\n");
789fca2b7dSJohn Levon         return;
799fca2b7dSJohn Levon     }
809fca2b7dSJohn Levon 
819fca2b7dSJohn Levon     sock_name = udev->socket->u.q_unix.path;
829fca2b7dSJohn Levon 
839fca2b7dSJohn Levon     vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name);
849fca2b7dSJohn Levon 
85438d863fSJohn Levon     memset(&addr, 0, sizeof(addr));
86438d863fSJohn Levon     addr.type = SOCKET_ADDRESS_TYPE_UNIX;
87438d863fSJohn Levon     addr.u.q_unix.path = (char *)sock_name;
88438d863fSJohn Levon     proxy = vfio_user_connect_dev(&addr, errp);
89438d863fSJohn Levon     if (!proxy) {
90438d863fSJohn Levon         return;
91438d863fSJohn Levon     }
92438d863fSJohn Levon     vbasedev->proxy = proxy;
93*0b3d881aSJohn Levon     vfio_user_set_handler(vbasedev, vfio_user_pci_process_req, vdev);
94438d863fSJohn Levon 
959fca2b7dSJohn Levon     /*
969fca2b7dSJohn Levon      * vfio-user devices are effectively mdevs (don't use a host iommu).
979fca2b7dSJohn Levon      */
989fca2b7dSJohn Levon     vbasedev->mdev = true;
999fca2b7dSJohn Levon 
1009fca2b7dSJohn Levon     as = pci_device_iommu_address_space(pdev);
1019fca2b7dSJohn Levon     if (!vfio_device_attach_by_iommu_type(TYPE_VFIO_IOMMU_USER,
1029fca2b7dSJohn Levon                                           vbasedev->name, vbasedev,
1039fca2b7dSJohn Levon                                           as, errp)) {
1049fca2b7dSJohn Levon         error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name);
1059fca2b7dSJohn Levon         return;
1069fca2b7dSJohn Levon     }
1079fca2b7dSJohn Levon }
1089fca2b7dSJohn Levon 
1099fca2b7dSJohn Levon static void vfio_user_instance_init(Object *obj)
1109fca2b7dSJohn Levon {
1119fca2b7dSJohn Levon     PCIDevice *pci_dev = PCI_DEVICE(obj);
1129fca2b7dSJohn Levon     VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj);
1139fca2b7dSJohn Levon     VFIODevice *vbasedev = &vdev->vbasedev;
1149fca2b7dSJohn Levon 
1159fca2b7dSJohn Levon     device_add_bootindex_property(obj, &vdev->bootindex,
1169fca2b7dSJohn Levon                                   "bootindex", NULL,
1179fca2b7dSJohn Levon                                   &pci_dev->qdev);
1189fca2b7dSJohn Levon     vdev->host.domain = ~0U;
1199fca2b7dSJohn Levon     vdev->host.bus = ~0U;
1209fca2b7dSJohn Levon     vdev->host.slot = ~0U;
1219fca2b7dSJohn Levon     vdev->host.function = ~0U;
1229fca2b7dSJohn Levon 
1239fca2b7dSJohn Levon     vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PCI, &vfio_user_pci_ops,
1249fca2b7dSJohn Levon                      DEVICE(vdev), false);
1259fca2b7dSJohn Levon 
1269fca2b7dSJohn Levon     vdev->nv_gpudirect_clique = 0xFF;
1279fca2b7dSJohn Levon 
1289fca2b7dSJohn Levon     /*
1299fca2b7dSJohn Levon      * QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
1309fca2b7dSJohn Levon      * line, therefore, no need to wait to realize like other devices.
1319fca2b7dSJohn Levon      */
1329fca2b7dSJohn Levon     pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
1339fca2b7dSJohn Levon }
1349fca2b7dSJohn Levon 
1359fca2b7dSJohn Levon static void vfio_user_instance_finalize(Object *obj)
1369fca2b7dSJohn Levon {
1379fca2b7dSJohn Levon     VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj);
138438d863fSJohn Levon     VFIODevice *vbasedev = &vdev->vbasedev;
1399fca2b7dSJohn Levon 
1409fca2b7dSJohn Levon     vfio_pci_put_device(vdev);
141438d863fSJohn Levon 
142438d863fSJohn Levon     if (vbasedev->proxy != NULL) {
143438d863fSJohn Levon         vfio_user_disconnect(vbasedev->proxy);
144438d863fSJohn Levon     }
1459fca2b7dSJohn Levon }
1469fca2b7dSJohn Levon 
1479fca2b7dSJohn Levon static const Property vfio_user_pci_dev_properties[] = {
1489fca2b7dSJohn Levon     DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice,
1499fca2b7dSJohn Levon                        vendor_id, PCI_ANY_ID),
1509fca2b7dSJohn Levon     DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice,
1519fca2b7dSJohn Levon                        device_id, PCI_ANY_ID),
1529fca2b7dSJohn Levon     DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice,
1539fca2b7dSJohn Levon                        sub_vendor_id, PCI_ANY_ID),
1549fca2b7dSJohn Levon     DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice,
1559fca2b7dSJohn Levon                        sub_device_id, PCI_ANY_ID),
1569fca2b7dSJohn Levon };
1579fca2b7dSJohn Levon 
1589fca2b7dSJohn Levon static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name,
1599fca2b7dSJohn Levon                                      void *opaque, Error **errp)
1609fca2b7dSJohn Levon {
1619fca2b7dSJohn Levon     VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj);
1629fca2b7dSJohn Levon     bool success;
1639fca2b7dSJohn Levon 
164438d863fSJohn Levon     if (udev->device.vbasedev.proxy) {
165438d863fSJohn Levon         error_setg(errp, "Proxy is connected");
166438d863fSJohn Levon         return;
167438d863fSJohn Levon     }
168438d863fSJohn Levon 
1699fca2b7dSJohn Levon     qapi_free_SocketAddress(udev->socket);
1709fca2b7dSJohn Levon 
1719fca2b7dSJohn Levon     udev->socket = NULL;
1729fca2b7dSJohn Levon 
1739fca2b7dSJohn Levon     success = visit_type_SocketAddress(v, name, &udev->socket, errp);
1749fca2b7dSJohn Levon 
1759fca2b7dSJohn Levon     if (!success) {
1769fca2b7dSJohn Levon         return;
1779fca2b7dSJohn Levon     }
1789fca2b7dSJohn Levon 
1799fca2b7dSJohn Levon     if (udev->socket->type != SOCKET_ADDRESS_TYPE_UNIX) {
1809fca2b7dSJohn Levon         error_setg(errp, "Unsupported socket type %s",
1819fca2b7dSJohn Levon                    SocketAddressType_str(udev->socket->type));
1829fca2b7dSJohn Levon         qapi_free_SocketAddress(udev->socket);
1839fca2b7dSJohn Levon         udev->socket = NULL;
1849fca2b7dSJohn Levon         return;
1859fca2b7dSJohn Levon     }
1869fca2b7dSJohn Levon }
1879fca2b7dSJohn Levon 
1889fca2b7dSJohn Levon static void vfio_user_pci_dev_class_init(ObjectClass *klass, const void *data)
1899fca2b7dSJohn Levon {
1909fca2b7dSJohn Levon     DeviceClass *dc = DEVICE_CLASS(klass);
1919fca2b7dSJohn Levon     PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass);
1929fca2b7dSJohn Levon 
1939fca2b7dSJohn Levon     device_class_set_props(dc, vfio_user_pci_dev_properties);
1949fca2b7dSJohn Levon 
1959fca2b7dSJohn Levon     object_class_property_add(klass, "socket", "SocketAddress", NULL,
1969fca2b7dSJohn Levon                               vfio_user_pci_set_socket, NULL, NULL);
1979fca2b7dSJohn Levon     object_class_property_set_description(klass, "socket",
1989fca2b7dSJohn Levon                                           "SocketAddress (UNIX sockets only)");
1999fca2b7dSJohn Levon 
2009fca2b7dSJohn Levon     dc->desc = "VFIO over socket PCI device assignment";
2019fca2b7dSJohn Levon     pdc->realize = vfio_user_pci_realize;
2029fca2b7dSJohn Levon }
2039fca2b7dSJohn Levon 
2049fca2b7dSJohn Levon static const TypeInfo vfio_user_pci_dev_info = {
2059fca2b7dSJohn Levon     .name = TYPE_VFIO_USER_PCI,
2069fca2b7dSJohn Levon     .parent = TYPE_VFIO_PCI_BASE,
2079fca2b7dSJohn Levon     .instance_size = sizeof(VFIOUserPCIDevice),
2089fca2b7dSJohn Levon     .class_init = vfio_user_pci_dev_class_init,
2099fca2b7dSJohn Levon     .instance_init = vfio_user_instance_init,
2109fca2b7dSJohn Levon     .instance_finalize = vfio_user_instance_finalize,
2119fca2b7dSJohn Levon };
2129fca2b7dSJohn Levon 
2139fca2b7dSJohn Levon static void register_vfio_user_dev_type(void)
2149fca2b7dSJohn Levon {
2159fca2b7dSJohn Levon     type_register_static(&vfio_user_pci_dev_info);
2169fca2b7dSJohn Levon }
2179fca2b7dSJohn Levon 
2189fca2b7dSJohn Levon  type_init(register_vfio_user_dev_type)
219