hw/virtio/Kconfig | 5 ++ hw/virtio/meson.build | 2 + hw/virtio/vhost-user-console-pci.c | 72 ++++++++++++++++++++++++++ hw/virtio/vhost-user-console.c | 65 +++++++++++++++++++++++ include/hw/virtio/vhost-user-console.h | 24 +++++++++ 5 files changed, 168 insertions(+) create mode 100644 hw/virtio/vhost-user-console-pci.c create mode 100644 hw/virtio/vhost-user-console.c create mode 100644 include/hw/virtio/vhost-user-console.h
From: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
Add vhost-user-console support enabling console device emulation via
the vhost-user protocol. This allows console handling to be performed
by separate userspace processes such as the rust-vmm
vhost-device-console daemon.
The device is based on vhost-user-base: set the console virtio id,
four queues, the console config layout, and add a PCI wrapper.
Migration is disabled for now.
Tested with rust-vmm vhost-device-console:
- https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-console
Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
---
Invocation of rust-vmm device:
RUST_LOG=trace cargo run --bin vhost-device-console \
--socket-path /tmp/console.sock \
--console-path <PATH_TO_CONSOLE_FILE>
Invocation of QEMU:
qemu-system-x86_64 \
-m 4096 \
-numa node,memdev=mem \
-object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \
-chardev socket,id=char1,path=/tmp/console.sock \
-device vhost-user-console-pci,chardev=char1,id=console \
...
Acknowledgements:
- This device was developed in the context of AGL (Automotive Grade Linux)
SDV-EG (Software Defined Vehicles - Expert Group).
- The development was based on the vhost-user-snd QEMU device introduced by the
following repository:
https://github.com/epilys/qemu-virtio-snd/tree/vhost-sound
---
hw/virtio/Kconfig | 5 ++
hw/virtio/meson.build | 2 +
hw/virtio/vhost-user-console-pci.c | 72 ++++++++++++++++++++++++++
hw/virtio/vhost-user-console.c | 65 +++++++++++++++++++++++
include/hw/virtio/vhost-user-console.h | 24 +++++++++
5 files changed, 168 insertions(+)
create mode 100644 hw/virtio/vhost-user-console-pci.c
create mode 100644 hw/virtio/vhost-user-console.c
create mode 100644 include/hw/virtio/vhost-user-console.h
diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
index 10f5c53ac0..9c9dd8d7e3 100644
--- a/hw/virtio/Kconfig
+++ b/hw/virtio/Kconfig
@@ -112,6 +112,11 @@ config VHOST_USER_GPIO
default y
depends on VIRTIO && VHOST_USER
+config VHOST_USER_CONSOLE
+ bool
+ default y
+ depends on VIRTIO && VHOST_USER
+
config VHOST_VDPA_DEV
bool
default y
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
index affd66887d..232178e217 100644
--- a/hw/virtio/meson.build
+++ b/hw/virtio/meson.build
@@ -61,6 +61,7 @@ system_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: files('virtio-nsm.c'))
system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('cbor-helpers.c'), libcbor])
system_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c'))
+system_virtio_ss.add(when: 'CONFIG_VHOST_USER_CONSOLE', if_true: files('vhost-user-console.c'))
virtio_pci_ss = ss.source_set()
virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c'))
@@ -82,6 +83,7 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: files('virtio-scsi-pci.c'
virtio_pci_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk-pci.c'))
virtio_pci_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('virtio-net-pci.c'))
virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-serial-pci.c'))
+virtio_pci_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_CONSOLE'], if_true: files('vhost-user-console-pci.c'))
virtio_pci_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem-pci.c'))
virtio_pci_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu-pci.c'))
virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem-pci.c'))
diff --git a/hw/virtio/vhost-user-console-pci.c b/hw/virtio/vhost-user-console-pci.c
new file mode 100644
index 0000000000..6c856a3424
--- /dev/null
+++ b/hw/virtio/vhost-user-console-pci.c
@@ -0,0 +1,72 @@
+/*
+ * Vhost-user console virtio device PCI glue
+ *
+ * Copyright (c) 2024-2025 Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/vhost-user-console.h"
+#include "hw/virtio/virtio-pci.h"
+
+struct VHostUserConsolePCI {
+ VirtIOPCIProxy parent_obj;
+ VHostUserConsole vdev;
+};
+
+typedef struct VHostUserConsolePCI VHostUserConsolePCI;
+
+#define TYPE_VHOST_USER_CONSOLE_PCI "vhost-user-console-pci-base"
+
+DECLARE_INSTANCE_CHECKER(VHostUserConsolePCI, VHOST_USER_CONSOLE_PCI,
+ TYPE_VHOST_USER_CONSOLE_PCI)
+
+static void vhost_user_console_pci_realize(VirtIOPCIProxy *vpci_dev,
+ Error **errp)
+{
+ VHostUserConsolePCI *dev = VHOST_USER_CONSOLE_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ vpci_dev->nvectors = 1;
+
+ qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
+}
+
+static void vhost_user_console_pci_class_init(ObjectClass *klass,
+ const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = vhost_user_console_pci_realize;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
+}
+
+static void vhost_user_console_pci_instance_init(Object *obj)
+{
+ VHostUserConsolePCI *dev = VHOST_USER_CONSOLE_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_USER_CONSOLE);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_user_console_pci_info = {
+ .base_name = TYPE_VHOST_USER_CONSOLE_PCI,
+ .non_transitional_name = "vhost-user-console-pci",
+ .instance_size = sizeof(VHostUserConsolePCI),
+ .instance_init = vhost_user_console_pci_instance_init,
+ .class_init = vhost_user_console_pci_class_init,
+};
+
+static void vhost_user_console_pci_register(void)
+{
+ virtio_pci_types_register(&vhost_user_console_pci_info);
+}
+
+type_init(vhost_user_console_pci_register);
diff --git a/hw/virtio/vhost-user-console.c b/hw/virtio/vhost-user-console.c
new file mode 100644
index 0000000000..d1eba5bc34
--- /dev/null
+++ b/hw/virtio/vhost-user-console.c
@@ -0,0 +1,65 @@
+/*
+ * Vhost-user console virtio device
+ *
+ * Copyright (c) 2024-2025 Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+ *
+ * Simple wrapper of the generic vhost-user-device.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/vhost-user-console.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "standard-headers/linux/virtio_console.h"
+
+static const VMStateDescription vu_console_vmstate = {
+ .name = "vhost-user-console",
+ .unmigratable = 1,
+};
+
+static const Property vconsole_properties[] = {
+ DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
+};
+
+static void vu_console_base_realize(DeviceState *dev, Error **errp)
+{
+ VHostUserBase *vub = VHOST_USER_BASE(dev);
+ VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev);
+
+ vub->virtio_id = VIRTIO_ID_CONSOLE;
+ vub->num_vqs = 4;
+ vub->config_size = sizeof(struct virtio_console_config);
+
+ vubs->parent_realize(dev, errp);
+}
+
+static void vu_console_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass);
+
+ dc->vmsd = &vu_console_vmstate;
+ device_class_set_props(dc, vconsole_properties);
+ device_class_set_parent_realize(dc, vu_console_base_realize,
+ &vubc->parent_realize);
+
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+}
+
+static const TypeInfo vu_console_info = {
+ .name = TYPE_VHOST_USER_CONSOLE,
+ .parent = TYPE_VHOST_USER_BASE,
+ .instance_size = sizeof(VHostUserConsole),
+ .class_init = vu_console_class_init,
+};
+
+static void vu_console_register_types(void)
+{
+ type_register_static(&vu_console_info);
+}
+
+type_init(vu_console_register_types)
diff --git a/include/hw/virtio/vhost-user-console.h b/include/hw/virtio/vhost-user-console.h
new file mode 100644
index 0000000000..261d62b756
--- /dev/null
+++ b/include/hw/virtio/vhost-user-console.h
@@ -0,0 +1,24 @@
+/*
+ * Vhost-user console virtio device
+ *
+ * Copyright (c) 2024-2025 Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QEMU_VHOST_USER_CONSOLE_H
+#define QEMU_VHOST_USER_CONSOLE_H
+
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/vhost.h"
+#include "hw/virtio/vhost-user.h"
+#include "hw/virtio/vhost-user-base.h"
+
+#define TYPE_VHOST_USER_CONSOLE "vhost-user-console"
+OBJECT_DECLARE_SIMPLE_TYPE(VHostUserConsole, VHOST_USER_CONSOLE)
+
+struct VHostUserConsole {
+ VHostUserBase parent;
+};
+
+#endif /* QEMU_VHOST_USER_CONSOLE_H */
--
2.43.0
© 2016 - 2026 Red Hat, Inc.