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