1 /* 2 * virtio-gpio nodes for testing 3 * 4 * Copyright (c) 2022 Linaro Ltd 5 * 6 * SPDX-License-Identifier: GPL-2.0-or-later 7 */ 8 9 #include "qemu/osdep.h" 10 #include "standard-headers/linux/virtio_config.h" 11 #include "../libqtest.h" 12 #include "qemu/module.h" 13 #include "qgraph.h" 14 #include "virtio-gpio.h" 15 16 static QGuestAllocator *alloc; 17 18 static void virtio_gpio_cleanup(QVhostUserGPIO *gpio) 19 { 20 QVirtioDevice *vdev = gpio->vdev; 21 int i; 22 23 for (i = 0; i < 2; i++) { 24 qvirtqueue_cleanup(vdev->bus, gpio->queues[i], alloc); 25 } 26 g_free(gpio->queues); 27 } 28 29 /* 30 * This handles the VirtIO setup from the point of view of the driver 31 * frontend and therefore doesn't present any vhost specific features 32 * and in fact masks of the re-used bit. 33 */ 34 static void virtio_gpio_setup(QVhostUserGPIO *gpio) 35 { 36 QVirtioDevice *vdev = gpio->vdev; 37 uint64_t features; 38 int i; 39 40 features = qvirtio_get_features(vdev); 41 features &= ~QVIRTIO_F_BAD_FEATURE; 42 qvirtio_set_features(vdev, features); 43 44 gpio->queues = g_new(QVirtQueue *, 2); 45 for (i = 0; i < 2; i++) { 46 gpio->queues[i] = qvirtqueue_setup(vdev, alloc, i); 47 } 48 qvirtio_set_driver_ok(vdev); 49 } 50 51 static void *qvirtio_gpio_get_driver(QVhostUserGPIO *v_gpio, 52 const char *interface) 53 { 54 if (!g_strcmp0(interface, "vhost-user-gpio")) { 55 return v_gpio; 56 } 57 if (!g_strcmp0(interface, "virtio")) { 58 return v_gpio->vdev; 59 } 60 61 g_assert_not_reached(); 62 } 63 64 static void *qvirtio_gpio_device_get_driver(void *object, 65 const char *interface) 66 { 67 QVhostUserGPIODevice *v_gpio = object; 68 return qvirtio_gpio_get_driver(&v_gpio->gpio, interface); 69 } 70 71 /* virtio-gpio (mmio) */ 72 static void qvirtio_gpio_device_destructor(QOSGraphObject *obj) 73 { 74 QVhostUserGPIODevice *gpio_dev = (QVhostUserGPIODevice *) obj; 75 virtio_gpio_cleanup(&gpio_dev->gpio); 76 } 77 78 static void qvirtio_gpio_device_start_hw(QOSGraphObject *obj) 79 { 80 QVhostUserGPIODevice *gpio_dev = (QVhostUserGPIODevice *) obj; 81 virtio_gpio_setup(&gpio_dev->gpio); 82 } 83 84 static void *virtio_gpio_device_create(void *virtio_dev, 85 QGuestAllocator *t_alloc, 86 void *addr) 87 { 88 QVhostUserGPIODevice *virtio_device = g_new0(QVhostUserGPIODevice, 1); 89 QVhostUserGPIO *interface = &virtio_device->gpio; 90 91 interface->vdev = virtio_dev; 92 alloc = t_alloc; 93 94 virtio_device->obj.get_driver = qvirtio_gpio_device_get_driver; 95 virtio_device->obj.start_hw = qvirtio_gpio_device_start_hw; 96 virtio_device->obj.destructor = qvirtio_gpio_device_destructor; 97 98 return &virtio_device->obj; 99 } 100 101 /* virtio-gpio-pci */ 102 static void qvirtio_gpio_pci_destructor(QOSGraphObject *obj) 103 { 104 QVhostUserGPIOPCI *gpio_pci = (QVhostUserGPIOPCI *) obj; 105 QOSGraphObject *pci_vobj = &gpio_pci->pci_vdev.obj; 106 107 virtio_gpio_cleanup(&gpio_pci->gpio); 108 qvirtio_pci_destructor(pci_vobj); 109 } 110 111 static void qvirtio_gpio_pci_start_hw(QOSGraphObject *obj) 112 { 113 QVhostUserGPIOPCI *gpio_pci = (QVhostUserGPIOPCI *) obj; 114 QOSGraphObject *pci_vobj = &gpio_pci->pci_vdev.obj; 115 116 qvirtio_pci_start_hw(pci_vobj); 117 virtio_gpio_setup(&gpio_pci->gpio); 118 } 119 120 static void *qvirtio_gpio_pci_get_driver(void *object, const char *interface) 121 { 122 QVhostUserGPIOPCI *v_gpio = object; 123 124 if (!g_strcmp0(interface, "pci-device")) { 125 return v_gpio->pci_vdev.pdev; 126 } 127 return qvirtio_gpio_get_driver(&v_gpio->gpio, interface); 128 } 129 130 static void *virtio_gpio_pci_create(void *pci_bus, QGuestAllocator *t_alloc, 131 void *addr) 132 { 133 QVhostUserGPIOPCI *virtio_spci = g_new0(QVhostUserGPIOPCI, 1); 134 QVhostUserGPIO *interface = &virtio_spci->gpio; 135 QOSGraphObject *obj = &virtio_spci->pci_vdev.obj; 136 137 virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); 138 interface->vdev = &virtio_spci->pci_vdev.vdev; 139 alloc = t_alloc; 140 141 obj->get_driver = qvirtio_gpio_pci_get_driver; 142 obj->start_hw = qvirtio_gpio_pci_start_hw; 143 obj->destructor = qvirtio_gpio_pci_destructor; 144 145 return obj; 146 } 147 148 static void virtio_gpio_register_nodes(void) 149 { 150 QPCIAddress addr = { 151 .devfn = QPCI_DEVFN(4, 0), 152 }; 153 154 QOSGraphEdgeOptions edge_opts = { }; 155 156 /* vhost-user-gpio-device */ 157 edge_opts.extra_device_opts = "id=gpio0,chardev=chr-vhost-user-test " 158 "-global virtio-mmio.force-legacy=false"; 159 qos_node_create_driver("vhost-user-gpio-device", 160 virtio_gpio_device_create); 161 qos_node_consumes("vhost-user-gpio-device", "virtio-bus", &edge_opts); 162 qos_node_produces("vhost-user-gpio-device", "vhost-user-gpio"); 163 164 /* virtio-gpio-pci */ 165 edge_opts.extra_device_opts = "id=gpio0,addr=04.0,chardev=chr-vhost-user-test"; 166 add_qpci_address(&edge_opts, &addr); 167 qos_node_create_driver("vhost-user-gpio-pci", virtio_gpio_pci_create); 168 qos_node_consumes("vhost-user-gpio-pci", "pci-bus", &edge_opts); 169 qos_node_produces("vhost-user-gpio-pci", "vhost-user-gpio"); 170 } 171 172 libqos_init(virtio_gpio_register_nodes); 173