1a5dab090SMilan Zamazal /* 2a5dab090SMilan Zamazal * Vhost-user SCMI virtio device 3a5dab090SMilan Zamazal * 4a5dab090SMilan Zamazal * SPDX-FileCopyrightText: Red Hat, Inc. 5a5dab090SMilan Zamazal * SPDX-License-Identifier: GPL-2.0-or-later 6a5dab090SMilan Zamazal * 7a5dab090SMilan Zamazal * Implementation based on other vhost-user devices in QEMU. 8a5dab090SMilan Zamazal */ 9a5dab090SMilan Zamazal 10a5dab090SMilan Zamazal #include "qemu/osdep.h" 11a5dab090SMilan Zamazal #include "qapi/error.h" 12a5dab090SMilan Zamazal #include "qemu/error-report.h" 13a5dab090SMilan Zamazal #include "hw/virtio/virtio-bus.h" 14a5dab090SMilan Zamazal #include "hw/virtio/vhost-user-scmi.h" 15a5dab090SMilan Zamazal #include "standard-headers/linux/virtio_ids.h" 16a5dab090SMilan Zamazal #include "standard-headers/linux/virtio_scmi.h" 17a5dab090SMilan Zamazal #include "trace.h" 18a5dab090SMilan Zamazal 19a5dab090SMilan Zamazal /* 20a5dab090SMilan Zamazal * In this version, we don't support VIRTIO_SCMI_F_SHARED_MEMORY. 21a5dab090SMilan Zamazal * Note that VIRTIO_SCMI_F_SHARED_MEMORY is currently not supported in 22a5dab090SMilan Zamazal * Linux VirtIO SCMI guest driver. 23a5dab090SMilan Zamazal */ 24a5dab090SMilan Zamazal static const int feature_bits[] = { 25a5dab090SMilan Zamazal VIRTIO_F_VERSION_1, 26a5dab090SMilan Zamazal VIRTIO_F_NOTIFY_ON_EMPTY, 27a5dab090SMilan Zamazal VIRTIO_RING_F_INDIRECT_DESC, 28a5dab090SMilan Zamazal VIRTIO_RING_F_EVENT_IDX, 29a5dab090SMilan Zamazal VIRTIO_F_RING_RESET, 30a5dab090SMilan Zamazal VIRTIO_SCMI_F_P2A_CHANNELS, 31a5dab090SMilan Zamazal VHOST_INVALID_FEATURE_BIT 32a5dab090SMilan Zamazal }; 33a5dab090SMilan Zamazal 34a5dab090SMilan Zamazal static int vu_scmi_start(VirtIODevice *vdev) 35a5dab090SMilan Zamazal { 36a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 37a5dab090SMilan Zamazal BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 38a5dab090SMilan Zamazal VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 39a5dab090SMilan Zamazal struct vhost_dev *vhost_dev = &scmi->vhost_dev; 40a5dab090SMilan Zamazal int ret, i; 41a5dab090SMilan Zamazal 42a5dab090SMilan Zamazal if (!k->set_guest_notifiers) { 43a5dab090SMilan Zamazal error_report("binding does not support guest notifiers"); 44a5dab090SMilan Zamazal return -ENOSYS; 45a5dab090SMilan Zamazal } 46a5dab090SMilan Zamazal 47a5dab090SMilan Zamazal ret = vhost_dev_enable_notifiers(vhost_dev, vdev); 48a5dab090SMilan Zamazal if (ret < 0) { 49a5dab090SMilan Zamazal error_report("Error enabling host notifiers: %d", ret); 50a5dab090SMilan Zamazal return ret; 51a5dab090SMilan Zamazal } 52a5dab090SMilan Zamazal 53a5dab090SMilan Zamazal ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, true); 54a5dab090SMilan Zamazal if (ret < 0) { 55a5dab090SMilan Zamazal error_report("Error binding guest notifier: %d", ret); 56a5dab090SMilan Zamazal goto err_host_notifiers; 57a5dab090SMilan Zamazal } 58a5dab090SMilan Zamazal 59ee1004bbSPhilippe Mathieu-Daudé vhost_ack_features(vhost_dev, feature_bits, vdev->guest_features); 60a5dab090SMilan Zamazal 61ee1004bbSPhilippe Mathieu-Daudé ret = vhost_dev_start(vhost_dev, vdev, true); 62a5dab090SMilan Zamazal if (ret < 0) { 63a5dab090SMilan Zamazal error_report("Error starting vhost-user-scmi: %d", ret); 64a5dab090SMilan Zamazal goto err_guest_notifiers; 65a5dab090SMilan Zamazal } 6663a3520eSMilan Zamazal scmi->started_vu = true; 67a5dab090SMilan Zamazal 68a5dab090SMilan Zamazal /* 69a5dab090SMilan Zamazal * guest_notifier_mask/pending not used yet, so just unmask 70a5dab090SMilan Zamazal * everything here. virtio-pci will do the right thing by 71a5dab090SMilan Zamazal * enabling/disabling irqfd. 72a5dab090SMilan Zamazal */ 73a5dab090SMilan Zamazal for (i = 0; i < scmi->vhost_dev.nvqs; i++) { 74ee1004bbSPhilippe Mathieu-Daudé vhost_virtqueue_mask(vhost_dev, vdev, i, false); 75a5dab090SMilan Zamazal } 76a5dab090SMilan Zamazal return 0; 77a5dab090SMilan Zamazal 78a5dab090SMilan Zamazal err_guest_notifiers: 79a5dab090SMilan Zamazal k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); 80a5dab090SMilan Zamazal err_host_notifiers: 81a5dab090SMilan Zamazal vhost_dev_disable_notifiers(vhost_dev, vdev); 82a5dab090SMilan Zamazal 83a5dab090SMilan Zamazal return ret; 84a5dab090SMilan Zamazal } 85a5dab090SMilan Zamazal 86a5dab090SMilan Zamazal static void vu_scmi_stop(VirtIODevice *vdev) 87a5dab090SMilan Zamazal { 88a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 89a5dab090SMilan Zamazal BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 90a5dab090SMilan Zamazal VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 91a5dab090SMilan Zamazal struct vhost_dev *vhost_dev = &scmi->vhost_dev; 92a5dab090SMilan Zamazal int ret; 93a5dab090SMilan Zamazal 9463a3520eSMilan Zamazal /* vhost_dev_is_started() check in the callers is not fully reliable. */ 9563a3520eSMilan Zamazal if (!scmi->started_vu) { 9663a3520eSMilan Zamazal return; 9763a3520eSMilan Zamazal } 9863a3520eSMilan Zamazal scmi->started_vu = false; 9963a3520eSMilan Zamazal 100a5dab090SMilan Zamazal if (!k->set_guest_notifiers) { 101a5dab090SMilan Zamazal return; 102a5dab090SMilan Zamazal } 103a5dab090SMilan Zamazal 104a5dab090SMilan Zamazal vhost_dev_stop(vhost_dev, vdev, true); 105a5dab090SMilan Zamazal 106a5dab090SMilan Zamazal ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); 107a5dab090SMilan Zamazal if (ret < 0) { 108a5dab090SMilan Zamazal error_report("vhost guest notifier cleanup failed: %d", ret); 109a5dab090SMilan Zamazal return; 110a5dab090SMilan Zamazal } 111a5dab090SMilan Zamazal vhost_dev_disable_notifiers(vhost_dev, vdev); 112a5dab090SMilan Zamazal } 113a5dab090SMilan Zamazal 114a5dab090SMilan Zamazal static void vu_scmi_set_status(VirtIODevice *vdev, uint8_t status) 115a5dab090SMilan Zamazal { 116a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 117a5dab090SMilan Zamazal bool should_start = virtio_device_should_start(vdev, status); 118a5dab090SMilan Zamazal 119a5dab090SMilan Zamazal if (!scmi->connected) { 120a5dab090SMilan Zamazal return; 121a5dab090SMilan Zamazal } 122a5dab090SMilan Zamazal if (vhost_dev_is_started(&scmi->vhost_dev) == should_start) { 123a5dab090SMilan Zamazal return; 124a5dab090SMilan Zamazal } 125a5dab090SMilan Zamazal 126a5dab090SMilan Zamazal if (should_start) { 127a5dab090SMilan Zamazal vu_scmi_start(vdev); 128a5dab090SMilan Zamazal } else { 129a5dab090SMilan Zamazal vu_scmi_stop(vdev); 130a5dab090SMilan Zamazal } 131a5dab090SMilan Zamazal } 132a5dab090SMilan Zamazal 133a5dab090SMilan Zamazal static uint64_t vu_scmi_get_features(VirtIODevice *vdev, uint64_t features, 134a5dab090SMilan Zamazal Error **errp) 135a5dab090SMilan Zamazal { 136a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 137a5dab090SMilan Zamazal 138a5dab090SMilan Zamazal return vhost_get_features(&scmi->vhost_dev, feature_bits, features); 139a5dab090SMilan Zamazal } 140a5dab090SMilan Zamazal 141a5dab090SMilan Zamazal static void vu_scmi_handle_output(VirtIODevice *vdev, VirtQueue *vq) 142a5dab090SMilan Zamazal { 143a5dab090SMilan Zamazal /* 144a5dab090SMilan Zamazal * Not normally called; it's the daemon that handles the queue; 145a5dab090SMilan Zamazal * however virtio's cleanup path can call this. 146a5dab090SMilan Zamazal */ 147a5dab090SMilan Zamazal } 148a5dab090SMilan Zamazal 149a5dab090SMilan Zamazal static void vu_scmi_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) 150a5dab090SMilan Zamazal { 151a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 152a5dab090SMilan Zamazal 153a5dab090SMilan Zamazal if (idx == VIRTIO_CONFIG_IRQ_IDX) { 154a5dab090SMilan Zamazal return; 155a5dab090SMilan Zamazal } 156a5dab090SMilan Zamazal 157a5dab090SMilan Zamazal vhost_virtqueue_mask(&scmi->vhost_dev, vdev, idx, mask); 158a5dab090SMilan Zamazal } 159a5dab090SMilan Zamazal 160a5dab090SMilan Zamazal static bool vu_scmi_guest_notifier_pending(VirtIODevice *vdev, int idx) 161a5dab090SMilan Zamazal { 162a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 163a5dab090SMilan Zamazal 164a5dab090SMilan Zamazal return vhost_virtqueue_pending(&scmi->vhost_dev, idx); 165a5dab090SMilan Zamazal } 166a5dab090SMilan Zamazal 167a5dab090SMilan Zamazal static void vu_scmi_connect(DeviceState *dev) 168a5dab090SMilan Zamazal { 169a5dab090SMilan Zamazal VirtIODevice *vdev = VIRTIO_DEVICE(dev); 170a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 171a5dab090SMilan Zamazal 172a5dab090SMilan Zamazal if (scmi->connected) { 173a5dab090SMilan Zamazal return; 174a5dab090SMilan Zamazal } 175a5dab090SMilan Zamazal scmi->connected = true; 176a5dab090SMilan Zamazal 177a5dab090SMilan Zamazal /* restore vhost state */ 178a5dab090SMilan Zamazal if (virtio_device_started(vdev, vdev->status)) { 179a5dab090SMilan Zamazal vu_scmi_start(vdev); 180a5dab090SMilan Zamazal } 181a5dab090SMilan Zamazal } 182a5dab090SMilan Zamazal 183a5dab090SMilan Zamazal static void vu_scmi_disconnect(DeviceState *dev) 184a5dab090SMilan Zamazal { 185a5dab090SMilan Zamazal VirtIODevice *vdev = VIRTIO_DEVICE(dev); 186a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); 187a5dab090SMilan Zamazal 188a5dab090SMilan Zamazal if (!scmi->connected) { 189a5dab090SMilan Zamazal return; 190a5dab090SMilan Zamazal } 191a5dab090SMilan Zamazal scmi->connected = false; 192a5dab090SMilan Zamazal 193a5dab090SMilan Zamazal if (vhost_dev_is_started(&scmi->vhost_dev)) { 194a5dab090SMilan Zamazal vu_scmi_stop(vdev); 195a5dab090SMilan Zamazal } 196a5dab090SMilan Zamazal } 197a5dab090SMilan Zamazal 198a5dab090SMilan Zamazal static void vu_scmi_event(void *opaque, QEMUChrEvent event) 199a5dab090SMilan Zamazal { 200a5dab090SMilan Zamazal DeviceState *dev = opaque; 201a5dab090SMilan Zamazal 202a5dab090SMilan Zamazal switch (event) { 203a5dab090SMilan Zamazal case CHR_EVENT_OPENED: 204a5dab090SMilan Zamazal vu_scmi_connect(dev); 205a5dab090SMilan Zamazal break; 206a5dab090SMilan Zamazal case CHR_EVENT_CLOSED: 207a5dab090SMilan Zamazal vu_scmi_disconnect(dev); 208a5dab090SMilan Zamazal break; 209a5dab090SMilan Zamazal case CHR_EVENT_BREAK: 210a5dab090SMilan Zamazal case CHR_EVENT_MUX_IN: 211a5dab090SMilan Zamazal case CHR_EVENT_MUX_OUT: 212a5dab090SMilan Zamazal /* Ignore */ 213a5dab090SMilan Zamazal break; 214a5dab090SMilan Zamazal } 215a5dab090SMilan Zamazal } 216a5dab090SMilan Zamazal 217a5dab090SMilan Zamazal static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserSCMI *scmi) 218a5dab090SMilan Zamazal { 219a5dab090SMilan Zamazal virtio_delete_queue(scmi->cmd_vq); 220a5dab090SMilan Zamazal virtio_delete_queue(scmi->event_vq); 221a5dab090SMilan Zamazal g_free(scmi->vhost_dev.vqs); 222a5dab090SMilan Zamazal virtio_cleanup(vdev); 223a5dab090SMilan Zamazal vhost_user_cleanup(&scmi->vhost_user); 224a5dab090SMilan Zamazal } 225a5dab090SMilan Zamazal 226a5dab090SMilan Zamazal static void vu_scmi_device_realize(DeviceState *dev, Error **errp) 227a5dab090SMilan Zamazal { 228a5dab090SMilan Zamazal VirtIODevice *vdev = VIRTIO_DEVICE(dev); 229a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(dev); 230a5dab090SMilan Zamazal int ret; 231a5dab090SMilan Zamazal 232a5dab090SMilan Zamazal if (!scmi->chardev.chr) { 233a5dab090SMilan Zamazal error_setg(errp, "vhost-user-scmi: chardev is mandatory"); 234a5dab090SMilan Zamazal return; 235a5dab090SMilan Zamazal } 236a5dab090SMilan Zamazal 237a5dab090SMilan Zamazal vdev->host_features |= (1ULL << VIRTIO_SCMI_F_P2A_CHANNELS); 238a5dab090SMilan Zamazal 239a5dab090SMilan Zamazal if (!vhost_user_init(&scmi->vhost_user, &scmi->chardev, errp)) { 240a5dab090SMilan Zamazal return; 241a5dab090SMilan Zamazal } 242a5dab090SMilan Zamazal 243a5dab090SMilan Zamazal virtio_init(vdev, VIRTIO_ID_SCMI, 0); 244a5dab090SMilan Zamazal 245a5dab090SMilan Zamazal scmi->cmd_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output); 246a5dab090SMilan Zamazal scmi->event_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output); 247a5dab090SMilan Zamazal scmi->vhost_dev.nvqs = 2; 248a5dab090SMilan Zamazal scmi->vhost_dev.vqs = g_new0(struct vhost_virtqueue, scmi->vhost_dev.nvqs); 249a5dab090SMilan Zamazal 250a5dab090SMilan Zamazal ret = vhost_dev_init(&scmi->vhost_dev, &scmi->vhost_user, 251a5dab090SMilan Zamazal VHOST_BACKEND_TYPE_USER, 0, errp); 252a5dab090SMilan Zamazal if (ret < 0) { 253a5dab090SMilan Zamazal error_setg_errno(errp, -ret, 254a5dab090SMilan Zamazal "vhost-user-scmi: vhost_dev_init() failed"); 255a5dab090SMilan Zamazal do_vhost_user_cleanup(vdev, scmi); 256a5dab090SMilan Zamazal return; 257a5dab090SMilan Zamazal } 258a5dab090SMilan Zamazal 259a5dab090SMilan Zamazal qemu_chr_fe_set_handlers(&scmi->chardev, NULL, NULL, vu_scmi_event, NULL, 260a5dab090SMilan Zamazal dev, NULL, true); 261a5dab090SMilan Zamazal 262a5dab090SMilan Zamazal return; 263a5dab090SMilan Zamazal } 264a5dab090SMilan Zamazal 265a5dab090SMilan Zamazal static void vu_scmi_device_unrealize(DeviceState *dev) 266a5dab090SMilan Zamazal { 267a5dab090SMilan Zamazal VirtIODevice *vdev = VIRTIO_DEVICE(dev); 268a5dab090SMilan Zamazal VHostUserSCMI *scmi = VHOST_USER_SCMI(dev); 269a5dab090SMilan Zamazal 270a5dab090SMilan Zamazal vu_scmi_set_status(vdev, 0); 271a5dab090SMilan Zamazal vhost_dev_cleanup(&scmi->vhost_dev); 272a5dab090SMilan Zamazal do_vhost_user_cleanup(vdev, scmi); 273a5dab090SMilan Zamazal } 274a5dab090SMilan Zamazal 275a5dab090SMilan Zamazal static const VMStateDescription vu_scmi_vmstate = { 276a5dab090SMilan Zamazal .name = "vhost-user-scmi", 277a5dab090SMilan Zamazal .unmigratable = 1, 278a5dab090SMilan Zamazal }; 279a5dab090SMilan Zamazal 280*1577a918SRichard Henderson static const Property vu_scmi_properties[] = { 281a5dab090SMilan Zamazal DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev), 282a5dab090SMilan Zamazal DEFINE_PROP_END_OF_LIST(), 283a5dab090SMilan Zamazal }; 284a5dab090SMilan Zamazal 285a5dab090SMilan Zamazal static void vu_scmi_class_init(ObjectClass *klass, void *data) 286a5dab090SMilan Zamazal { 287a5dab090SMilan Zamazal DeviceClass *dc = DEVICE_CLASS(klass); 288a5dab090SMilan Zamazal VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 289a5dab090SMilan Zamazal 290a5dab090SMilan Zamazal device_class_set_props(dc, vu_scmi_properties); 291a5dab090SMilan Zamazal dc->vmsd = &vu_scmi_vmstate; 292a5dab090SMilan Zamazal set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 293a5dab090SMilan Zamazal vdc->realize = vu_scmi_device_realize; 294a5dab090SMilan Zamazal vdc->unrealize = vu_scmi_device_unrealize; 295a5dab090SMilan Zamazal vdc->get_features = vu_scmi_get_features; 296a5dab090SMilan Zamazal vdc->set_status = vu_scmi_set_status; 297a5dab090SMilan Zamazal vdc->guest_notifier_mask = vu_scmi_guest_notifier_mask; 298a5dab090SMilan Zamazal vdc->guest_notifier_pending = vu_scmi_guest_notifier_pending; 299a5dab090SMilan Zamazal } 300a5dab090SMilan Zamazal 301a5dab090SMilan Zamazal static const TypeInfo vu_scmi_info = { 302a5dab090SMilan Zamazal .name = TYPE_VHOST_USER_SCMI, 303a5dab090SMilan Zamazal .parent = TYPE_VIRTIO_DEVICE, 304a5dab090SMilan Zamazal .instance_size = sizeof(VHostUserSCMI), 305a5dab090SMilan Zamazal .class_init = vu_scmi_class_init, 306a5dab090SMilan Zamazal }; 307a5dab090SMilan Zamazal 308a5dab090SMilan Zamazal static void vu_scmi_register_types(void) 309a5dab090SMilan Zamazal { 310a5dab090SMilan Zamazal type_register_static(&vu_scmi_info); 311a5dab090SMilan Zamazal } 312a5dab090SMilan Zamazal 313a5dab090SMilan Zamazal type_init(vu_scmi_register_types) 314