xref: /qemu/hw/virtio/vhost-user-scmi.c (revision 21596064081e8d0c0153f68714981c7f0e040973)
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 
vu_scmi_start(VirtIODevice * vdev)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 
vu_scmi_stop(VirtIODevice * vdev)86*bc85aae4SHaoqian He static int 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) {
96*bc85aae4SHaoqian He         return 0;
9763a3520eSMilan Zamazal     }
9863a3520eSMilan Zamazal     scmi->started_vu = false;
9963a3520eSMilan Zamazal 
100a5dab090SMilan Zamazal     if (!k->set_guest_notifiers) {
101*bc85aae4SHaoqian He         return 0;
102a5dab090SMilan Zamazal     }
103a5dab090SMilan Zamazal 
104*bc85aae4SHaoqian He     ret = vhost_dev_stop(vhost_dev, vdev, true);
105a5dab090SMilan Zamazal 
106*bc85aae4SHaoqian He     if (k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false) < 0) {
107a5dab090SMilan Zamazal         error_report("vhost guest notifier cleanup failed: %d", ret);
108*bc85aae4SHaoqian He         return -1;
109a5dab090SMilan Zamazal     }
110a5dab090SMilan Zamazal     vhost_dev_disable_notifiers(vhost_dev, vdev);
111*bc85aae4SHaoqian He     return ret;
112a5dab090SMilan Zamazal }
113a5dab090SMilan Zamazal 
vu_scmi_set_status(VirtIODevice * vdev,uint8_t status)114*bc85aae4SHaoqian He static int 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) {
120*bc85aae4SHaoqian He         return -1;
121a5dab090SMilan Zamazal     }
122a5dab090SMilan Zamazal     if (vhost_dev_is_started(&scmi->vhost_dev) == should_start) {
123*bc85aae4SHaoqian He         return 0;
124a5dab090SMilan Zamazal     }
125a5dab090SMilan Zamazal 
126a5dab090SMilan Zamazal     if (should_start) {
127a5dab090SMilan Zamazal         vu_scmi_start(vdev);
128a5dab090SMilan Zamazal     } else {
129*bc85aae4SHaoqian He         int ret;
130*bc85aae4SHaoqian He         ret = vu_scmi_stop(vdev);
131*bc85aae4SHaoqian He         if (ret < 0) {
132*bc85aae4SHaoqian He             return ret;
133a5dab090SMilan Zamazal         }
134a5dab090SMilan Zamazal     }
135*bc85aae4SHaoqian He     return 0;
136*bc85aae4SHaoqian He }
137a5dab090SMilan Zamazal 
vu_scmi_get_features(VirtIODevice * vdev,uint64_t features,Error ** errp)138a5dab090SMilan Zamazal static uint64_t vu_scmi_get_features(VirtIODevice *vdev, uint64_t features,
139a5dab090SMilan Zamazal                                      Error **errp)
140a5dab090SMilan Zamazal {
141a5dab090SMilan Zamazal     VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev);
142a5dab090SMilan Zamazal 
143a5dab090SMilan Zamazal     return vhost_get_features(&scmi->vhost_dev, feature_bits, features);
144a5dab090SMilan Zamazal }
145a5dab090SMilan Zamazal 
vu_scmi_handle_output(VirtIODevice * vdev,VirtQueue * vq)146a5dab090SMilan Zamazal static void vu_scmi_handle_output(VirtIODevice *vdev, VirtQueue *vq)
147a5dab090SMilan Zamazal {
148a5dab090SMilan Zamazal     /*
149a5dab090SMilan Zamazal      * Not normally called; it's the daemon that handles the queue;
150a5dab090SMilan Zamazal      * however virtio's cleanup path can call this.
151a5dab090SMilan Zamazal      */
152a5dab090SMilan Zamazal }
153a5dab090SMilan Zamazal 
vu_scmi_guest_notifier_mask(VirtIODevice * vdev,int idx,bool mask)154a5dab090SMilan Zamazal static void vu_scmi_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask)
155a5dab090SMilan Zamazal {
156a5dab090SMilan Zamazal     VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev);
157a5dab090SMilan Zamazal 
158a5dab090SMilan Zamazal     if (idx == VIRTIO_CONFIG_IRQ_IDX) {
159a5dab090SMilan Zamazal         return;
160a5dab090SMilan Zamazal     }
161a5dab090SMilan Zamazal 
162a5dab090SMilan Zamazal     vhost_virtqueue_mask(&scmi->vhost_dev, vdev, idx, mask);
163a5dab090SMilan Zamazal }
164a5dab090SMilan Zamazal 
vu_scmi_guest_notifier_pending(VirtIODevice * vdev,int idx)165a5dab090SMilan Zamazal static bool vu_scmi_guest_notifier_pending(VirtIODevice *vdev, int idx)
166a5dab090SMilan Zamazal {
167a5dab090SMilan Zamazal     VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev);
168a5dab090SMilan Zamazal 
169a5dab090SMilan Zamazal     return vhost_virtqueue_pending(&scmi->vhost_dev, idx);
170a5dab090SMilan Zamazal }
171a5dab090SMilan Zamazal 
vu_scmi_connect(DeviceState * dev)172a5dab090SMilan Zamazal static void vu_scmi_connect(DeviceState *dev)
173a5dab090SMilan Zamazal {
174a5dab090SMilan Zamazal     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
175a5dab090SMilan Zamazal     VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev);
176a5dab090SMilan Zamazal 
177a5dab090SMilan Zamazal     if (scmi->connected) {
178a5dab090SMilan Zamazal         return;
179a5dab090SMilan Zamazal     }
180a5dab090SMilan Zamazal     scmi->connected = true;
181a5dab090SMilan Zamazal 
182a5dab090SMilan Zamazal     /* restore vhost state */
183a5dab090SMilan Zamazal     if (virtio_device_started(vdev, vdev->status)) {
184a5dab090SMilan Zamazal         vu_scmi_start(vdev);
185a5dab090SMilan Zamazal     }
186a5dab090SMilan Zamazal }
187a5dab090SMilan Zamazal 
vu_scmi_disconnect(DeviceState * dev)188a5dab090SMilan Zamazal static void vu_scmi_disconnect(DeviceState *dev)
189a5dab090SMilan Zamazal {
190a5dab090SMilan Zamazal     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
191a5dab090SMilan Zamazal     VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev);
192a5dab090SMilan Zamazal 
193a5dab090SMilan Zamazal     if (!scmi->connected) {
194a5dab090SMilan Zamazal         return;
195a5dab090SMilan Zamazal     }
196a5dab090SMilan Zamazal     scmi->connected = false;
197a5dab090SMilan Zamazal 
198a5dab090SMilan Zamazal     if (vhost_dev_is_started(&scmi->vhost_dev)) {
199a5dab090SMilan Zamazal         vu_scmi_stop(vdev);
200a5dab090SMilan Zamazal     }
201a5dab090SMilan Zamazal }
202a5dab090SMilan Zamazal 
vu_scmi_event(void * opaque,QEMUChrEvent event)203a5dab090SMilan Zamazal static void vu_scmi_event(void *opaque, QEMUChrEvent event)
204a5dab090SMilan Zamazal {
205a5dab090SMilan Zamazal     DeviceState *dev = opaque;
206a5dab090SMilan Zamazal 
207a5dab090SMilan Zamazal     switch (event) {
208a5dab090SMilan Zamazal     case CHR_EVENT_OPENED:
209a5dab090SMilan Zamazal         vu_scmi_connect(dev);
210a5dab090SMilan Zamazal         break;
211a5dab090SMilan Zamazal     case CHR_EVENT_CLOSED:
212a5dab090SMilan Zamazal         vu_scmi_disconnect(dev);
213a5dab090SMilan Zamazal         break;
214a5dab090SMilan Zamazal     case CHR_EVENT_BREAK:
215a5dab090SMilan Zamazal     case CHR_EVENT_MUX_IN:
216a5dab090SMilan Zamazal     case CHR_EVENT_MUX_OUT:
217a5dab090SMilan Zamazal         /* Ignore */
218a5dab090SMilan Zamazal         break;
219a5dab090SMilan Zamazal     }
220a5dab090SMilan Zamazal }
221a5dab090SMilan Zamazal 
do_vhost_user_cleanup(VirtIODevice * vdev,VHostUserSCMI * scmi)222a5dab090SMilan Zamazal static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserSCMI *scmi)
223a5dab090SMilan Zamazal {
224a5dab090SMilan Zamazal     virtio_delete_queue(scmi->cmd_vq);
225a5dab090SMilan Zamazal     virtio_delete_queue(scmi->event_vq);
226a5dab090SMilan Zamazal     g_free(scmi->vhost_dev.vqs);
227a5dab090SMilan Zamazal     virtio_cleanup(vdev);
228a5dab090SMilan Zamazal     vhost_user_cleanup(&scmi->vhost_user);
229a5dab090SMilan Zamazal }
230a5dab090SMilan Zamazal 
vu_scmi_device_realize(DeviceState * dev,Error ** errp)231a5dab090SMilan Zamazal static void vu_scmi_device_realize(DeviceState *dev, Error **errp)
232a5dab090SMilan Zamazal {
233a5dab090SMilan Zamazal     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
234a5dab090SMilan Zamazal     VHostUserSCMI *scmi = VHOST_USER_SCMI(dev);
235a5dab090SMilan Zamazal     int ret;
236a5dab090SMilan Zamazal 
237a5dab090SMilan Zamazal     if (!scmi->chardev.chr) {
238a5dab090SMilan Zamazal         error_setg(errp, "vhost-user-scmi: chardev is mandatory");
239a5dab090SMilan Zamazal         return;
240a5dab090SMilan Zamazal     }
241a5dab090SMilan Zamazal 
242a5dab090SMilan Zamazal     vdev->host_features |= (1ULL << VIRTIO_SCMI_F_P2A_CHANNELS);
243a5dab090SMilan Zamazal 
244a5dab090SMilan Zamazal     if (!vhost_user_init(&scmi->vhost_user, &scmi->chardev, errp)) {
245a5dab090SMilan Zamazal         return;
246a5dab090SMilan Zamazal     }
247a5dab090SMilan Zamazal 
248a5dab090SMilan Zamazal     virtio_init(vdev, VIRTIO_ID_SCMI, 0);
249a5dab090SMilan Zamazal 
250a5dab090SMilan Zamazal     scmi->cmd_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output);
251a5dab090SMilan Zamazal     scmi->event_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output);
252a5dab090SMilan Zamazal     scmi->vhost_dev.nvqs = 2;
253a5dab090SMilan Zamazal     scmi->vhost_dev.vqs = g_new0(struct vhost_virtqueue, scmi->vhost_dev.nvqs);
254a5dab090SMilan Zamazal 
255a5dab090SMilan Zamazal     ret = vhost_dev_init(&scmi->vhost_dev, &scmi->vhost_user,
256a5dab090SMilan Zamazal                          VHOST_BACKEND_TYPE_USER, 0, errp);
257a5dab090SMilan Zamazal     if (ret < 0) {
258a5dab090SMilan Zamazal         error_setg_errno(errp, -ret,
259a5dab090SMilan Zamazal                          "vhost-user-scmi: vhost_dev_init() failed");
260a5dab090SMilan Zamazal         do_vhost_user_cleanup(vdev, scmi);
261a5dab090SMilan Zamazal         return;
262a5dab090SMilan Zamazal     }
263a5dab090SMilan Zamazal 
264a5dab090SMilan Zamazal     qemu_chr_fe_set_handlers(&scmi->chardev, NULL, NULL, vu_scmi_event, NULL,
265a5dab090SMilan Zamazal                              dev, NULL, true);
266a5dab090SMilan Zamazal }
267a5dab090SMilan Zamazal 
vu_scmi_device_unrealize(DeviceState * dev)268a5dab090SMilan Zamazal static void vu_scmi_device_unrealize(DeviceState *dev)
269a5dab090SMilan Zamazal {
270a5dab090SMilan Zamazal     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
271a5dab090SMilan Zamazal     VHostUserSCMI *scmi = VHOST_USER_SCMI(dev);
272a5dab090SMilan Zamazal 
273a5dab090SMilan Zamazal     vu_scmi_set_status(vdev, 0);
274a5dab090SMilan Zamazal     vhost_dev_cleanup(&scmi->vhost_dev);
275a5dab090SMilan Zamazal     do_vhost_user_cleanup(vdev, scmi);
276a5dab090SMilan Zamazal }
277a5dab090SMilan Zamazal 
278a5dab090SMilan Zamazal static const VMStateDescription vu_scmi_vmstate = {
279a5dab090SMilan Zamazal     .name = "vhost-user-scmi",
280a5dab090SMilan Zamazal     .unmigratable = 1,
281a5dab090SMilan Zamazal };
282a5dab090SMilan Zamazal 
2831577a918SRichard Henderson static const Property vu_scmi_properties[] = {
284a5dab090SMilan Zamazal     DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev),
285a5dab090SMilan Zamazal };
286a5dab090SMilan Zamazal 
vu_scmi_class_init(ObjectClass * klass,const void * data)28712d1a768SPhilippe Mathieu-Daudé static void vu_scmi_class_init(ObjectClass *klass, const void *data)
288a5dab090SMilan Zamazal {
289a5dab090SMilan Zamazal     DeviceClass *dc = DEVICE_CLASS(klass);
290a5dab090SMilan Zamazal     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
291a5dab090SMilan Zamazal 
292a5dab090SMilan Zamazal     device_class_set_props(dc, vu_scmi_properties);
293a5dab090SMilan Zamazal     dc->vmsd = &vu_scmi_vmstate;
294a5dab090SMilan Zamazal     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
295a5dab090SMilan Zamazal     vdc->realize = vu_scmi_device_realize;
296a5dab090SMilan Zamazal     vdc->unrealize = vu_scmi_device_unrealize;
297a5dab090SMilan Zamazal     vdc->get_features = vu_scmi_get_features;
298a5dab090SMilan Zamazal     vdc->set_status = vu_scmi_set_status;
299a5dab090SMilan Zamazal     vdc->guest_notifier_mask = vu_scmi_guest_notifier_mask;
300a5dab090SMilan Zamazal     vdc->guest_notifier_pending = vu_scmi_guest_notifier_pending;
301a5dab090SMilan Zamazal }
302a5dab090SMilan Zamazal 
303a5dab090SMilan Zamazal static const TypeInfo vu_scmi_info = {
304a5dab090SMilan Zamazal     .name = TYPE_VHOST_USER_SCMI,
305a5dab090SMilan Zamazal     .parent = TYPE_VIRTIO_DEVICE,
306a5dab090SMilan Zamazal     .instance_size = sizeof(VHostUserSCMI),
307a5dab090SMilan Zamazal     .class_init = vu_scmi_class_init,
308a5dab090SMilan Zamazal };
309a5dab090SMilan Zamazal 
vu_scmi_register_types(void)310a5dab090SMilan Zamazal static void vu_scmi_register_types(void)
311a5dab090SMilan Zamazal {
312a5dab090SMilan Zamazal     type_register_static(&vu_scmi_info);
313a5dab090SMilan Zamazal }
314a5dab090SMilan Zamazal 
315a5dab090SMilan Zamazal type_init(vu_scmi_register_types)
316