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
59*ee1004bbSPhilippe Mathieu-Daudé vhost_ack_features(vhost_dev, feature_bits, vdev->guest_features);
60a5dab090SMilan Zamazal
61*ee1004bbSPhilippe 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++) {
74*ee1004bbSPhilippe 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)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
vu_scmi_set_status(VirtIODevice * vdev,uint8_t status)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
vu_scmi_get_features(VirtIODevice * vdev,uint64_t features,Error ** errp)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
vu_scmi_handle_output(VirtIODevice * vdev,VirtQueue * vq)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
vu_scmi_guest_notifier_mask(VirtIODevice * vdev,int idx,bool mask)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
vu_scmi_guest_notifier_pending(VirtIODevice * vdev,int idx)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
vu_scmi_connect(DeviceState * dev)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
vu_scmi_disconnect(DeviceState * dev)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
vu_scmi_event(void * opaque,QEMUChrEvent event)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
do_vhost_user_cleanup(VirtIODevice * vdev,VHostUserSCMI * scmi)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
vu_scmi_device_realize(DeviceState * dev,Error ** errp)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
vu_scmi_device_unrealize(DeviceState * dev)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
280a5dab090SMilan Zamazal static Property vu_scmi_properties[] = {
281a5dab090SMilan Zamazal DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev),
282a5dab090SMilan Zamazal DEFINE_PROP_END_OF_LIST(),
283a5dab090SMilan Zamazal };
284a5dab090SMilan Zamazal
vu_scmi_class_init(ObjectClass * klass,void * data)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
vu_scmi_register_types(void)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