| 1 | .. |
| 2 | Copyright (c) 2022, Linaro Limited |
| 3 | Written by Alex Bennée |
| 4 | |
| 5 | Writing VirtIO backends for QEMU |
| 6 | ================================ |
| 7 | |
| 8 | This document attempts to outline the information a developer needs to |
| 9 | know to write device emulations in QEMU. It is specifically focused on |
| 10 | implementing VirtIO devices. For VirtIO the frontend is the driver |
| 11 | running on the guest. The backend is the everything that QEMU needs to |
| 12 | do to handle the emulation of the VirtIO device. This can be done |
| 13 | entirely in QEMU, divided between QEMU and the kernel (vhost) or |
| 14 | handled by a separate process which is configured by QEMU |
| 15 | (vhost-user). |
| 16 | |
| 17 | VirtIO Transports |
| 18 | ----------------- |
| 19 | |
| 20 | VirtIO supports a number of different transports. While the details of |
| 21 | the configuration and operation of the device will generally be the |
| 22 | same QEMU represents them as different devices depending on the |
| 23 | transport they use. For example -device virtio-foo represents the foo |
| 24 | device using mmio and -device virtio-foo-pci is the same class of |
| 25 | device using the PCI transport. |
| 26 | |
| 27 | Using the QEMU Object Model (QOM) |
| 28 | --------------------------------- |
| 29 | |
| 30 | Generally all devices in QEMU are super classes of ``TYPE_DEVICE`` |
| 31 | however VirtIO devices should be based on ``TYPE_VIRTIO_DEVICE`` which |
| 32 | itself is derived from the base class. For example: |
| 33 | |
| 34 | .. code:: c |
| 35 | |
| 36 | static const TypeInfo virtio_blk_info = { |
| 37 | .name = TYPE_VIRTIO_BLK, |
| 38 | .parent = TYPE_VIRTIO_DEVICE, |
| 39 | .instance_size = sizeof(VirtIOBlock), |
| 40 | .instance_init = virtio_blk_instance_init, |
| 41 | .class_init = virtio_blk_class_init, |
| 42 | }; |
| 43 | |
| 44 | The author may decide to have a more expansive class hierarchy to |
| 45 | support multiple device types. For example the Virtio GPU device: |
| 46 | |
| 47 | .. code:: c |
| 48 | |
| 49 | static const TypeInfo virtio_gpu_base_info = { |
| 50 | .name = TYPE_VIRTIO_GPU_BASE, |
| 51 | .parent = TYPE_VIRTIO_DEVICE, |
| 52 | .instance_size = sizeof(VirtIOGPUBase), |
| 53 | .class_size = sizeof(VirtIOGPUBaseClass), |
| 54 | .class_init = virtio_gpu_base_class_init, |
| 55 | .abstract = true |
| 56 | }; |
| 57 | |
| 58 | static const TypeInfo vhost_user_gpu_info = { |
| 59 | .name = TYPE_VHOST_USER_GPU, |
| 60 | .parent = TYPE_VIRTIO_GPU_BASE, |
| 61 | .instance_size = sizeof(VhostUserGPU), |
| 62 | .instance_init = vhost_user_gpu_instance_init, |
| 63 | .instance_finalize = vhost_user_gpu_instance_finalize, |
| 64 | .class_init = vhost_user_gpu_class_init, |
| 65 | }; |
| 66 | |
| 67 | static const TypeInfo virtio_gpu_info = { |
| 68 | .name = TYPE_VIRTIO_GPU, |
| 69 | .parent = TYPE_VIRTIO_GPU_BASE, |
| 70 | .instance_size = sizeof(VirtIOGPU), |
| 71 | .class_size = sizeof(VirtIOGPUClass), |
| 72 | .class_init = virtio_gpu_class_init, |
| 73 | }; |
| 74 | |
| 75 | defines a base class for the VirtIO GPU and then specialises two |
| 76 | versions, one for the internal implementation and the other for the |
| 77 | vhost-user version. |
| 78 | |
| 79 | VirtIOPCIProxy |
| 80 | ^^^^^^^^^^^^^^ |
| 81 | |
| 82 | [AJB: the following is supposition and welcomes more informed |
| 83 | opinions] |
| 84 | |
| 85 | Probably due to legacy from the pre-QOM days PCI VirtIO devices don't |
| 86 | follow the normal hierarchy. Instead the a standalone object is based |
| 87 | on the VirtIOPCIProxy class and the specific VirtIO instance is |
| 88 | manually instantiated: |
| 89 | |
| 90 | .. code:: c |
| 91 | |
| 92 | /* |
| 93 | * virtio-blk-pci: This extends VirtioPCIProxy. |
| 94 | */ |
| 95 | #define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci-base" |
| 96 | DECLARE_INSTANCE_CHECKER(VirtIOBlkPCI, VIRTIO_BLK_PCI, |
| 97 | TYPE_VIRTIO_BLK_PCI) |
| 98 | |
| 99 | struct VirtIOBlkPCI { |
| 100 | VirtIOPCIProxy parent_obj; |
| 101 | VirtIOBlock vdev; |
| 102 | }; |
| 103 | |
| 104 | static const Property virtio_blk_pci_properties[] = { |
| 105 | DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), |
| 106 | DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, |
| 107 | VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), |
| 108 | DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, |
| 109 | DEV_NVECTORS_UNSPECIFIED), |
| 110 | }; |
| 111 | |
| 112 | static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) |
| 113 | { |
| 114 | VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev); |
| 115 | DeviceState *vdev = DEVICE(&dev->vdev); |
| 116 | |
| 117 | ... |
| 118 | |
| 119 | qdev_realize(vdev, BUS(&vpci_dev->bus), errp); |
| 120 | } |
| 121 | |
| 122 | static void virtio_blk_pci_class_init(ObjectClass *klass, const void *data) |
| 123 | { |
| 124 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 125 | VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); |
| 126 | PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); |
| 127 | |
| 128 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |
| 129 | device_class_set_props(dc, virtio_blk_pci_properties); |
| 130 | k->realize = virtio_blk_pci_realize; |
| 131 | pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; |
| 132 | pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; |
| 133 | pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; |
| 134 | pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; |
| 135 | } |
| 136 | |
| 137 | static void virtio_blk_pci_instance_init(Object *obj) |
| 138 | { |
| 139 | VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj); |
| 140 | |
| 141 | virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), |
| 142 | TYPE_VIRTIO_BLK); |
| 143 | object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), |
| 144 | "bootindex"); |
| 145 | } |
| 146 | |
| 147 | static const VirtioPCIDeviceTypeInfo virtio_blk_pci_info = { |
| 148 | .base_name = TYPE_VIRTIO_BLK_PCI, |
| 149 | .generic_name = "virtio-blk-pci", |
| 150 | .transitional_name = "virtio-blk-pci-transitional", |
| 151 | .non_transitional_name = "virtio-blk-pci-non-transitional", |
| 152 | .instance_size = sizeof(VirtIOBlkPCI), |
| 153 | .instance_init = virtio_blk_pci_instance_init, |
| 154 | .class_init = virtio_blk_pci_class_init, |
| 155 | }; |
| 156 | |
| 157 | Here you can see the instance_init has to manually instantiate the |
| 158 | underlying ``TYPE_VIRTIO_BLOCK`` object and link an alias for one of |
| 159 | it's properties to the PCI device. |
| 160 | |
| 161 | |
| 162 | Back End Implementations |
| 163 | ------------------------ |
| 164 | |
| 165 | There are a number of places where the implementation of the backend |
| 166 | can be done: |
| 167 | |
| 168 | * in QEMU itself |
| 169 | * in the host kernel (a.k.a vhost) |
| 170 | * in a separate process (a.k.a. vhost-user) |
| 171 | |
| 172 | vhost_ops vs TYPE_VHOST_USER_BACKEND |
| 173 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 174 | |
| 175 | There are two choices to how to implement vhost code. Most of the code |
| 176 | which has to work with either vhost or vhost-user uses |
| 177 | ``vhost_dev_init()`` to instantiate the appropriate backend. This |
| 178 | means including a ``struct vhost_dev`` in the main object structure. |
| 179 | |
| 180 | For vhost-user devices you also need to add code to track the |
| 181 | initialisation of the ``chardev`` device used for the control socket |
| 182 | between QEMU and the external vhost-user process. |
| 183 | |
| 184 | If you only need to implement a vhost-user backed the other option is |
| 185 | a use a QOM-ified version of vhost-user. |
| 186 | |
| 187 | .. code:: c |
| 188 | |
| 189 | static void |
| 190 | vhost_user_gpu_instance_init(Object *obj) |
| 191 | { |
| 192 | VhostUserGPU *g = VHOST_USER_GPU(obj); |
| 193 | |
| 194 | g->vhost = VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND)); |
| 195 | object_property_add_alias(obj, "chardev", |
| 196 | OBJECT(g->vhost), "chardev"); |
| 197 | } |
| 198 | |
| 199 | static const TypeInfo vhost_user_gpu_info = { |
| 200 | .name = TYPE_VHOST_USER_GPU, |
| 201 | .parent = TYPE_VIRTIO_GPU_BASE, |
| 202 | .instance_size = sizeof(VhostUserGPU), |
| 203 | .instance_init = vhost_user_gpu_instance_init, |
| 204 | .instance_finalize = vhost_user_gpu_instance_finalize, |
| 205 | .class_init = vhost_user_gpu_class_init, |
| 206 | }; |
| 207 | |
| 208 | Using it this way entails adding a ``struct VhostUserBackend`` to your |
| 209 | core object structure and manually instantiating the backend. This |
| 210 | sub-structure tracks both the ``vhost_dev`` and ``CharDev`` types |
| 211 | needed for the connection. Instead of calling ``vhost_dev_init`` you |
| 212 | would call ``vhost_user_backend_dev_init`` which does what is needed |
| 213 | on your behalf. |