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