@samitouri / QOSamiQemu / commits / ae5f259a5c

virtio-rtc: Add basic virtio-rtc support

Introduce the core device and PCI bindings for the virtio-rtc device (VIRTIO_ID_CLOCK). This implementation provides a read-only clock that returns the host's time (QEMU_CLOCK_HOST) to the guest. It handles fundamental control requests, reporting a single supported clock of type VIRTIO_RTC_CLOCK_UTC, and responds to standard read requests. - Virtio RTC Spec: https://github.com/oasis-tcs/virtio-spec/tree/master/device-types/rtc - Linux Virtio RTC driver: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/virtio/virtio_rtc_driver.c?h=v7.0-rc1 Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-Id: <20260228182246.700714-3-visitorckw@gmail.com>

Kuan-Wei Chiu committed Feb 28, 2026 at 18:22 UTC ae5f259a5c77b501b951d487030e617e546facc3
6 files changed +291
MAINTAINERS
+7
@@ -2527,6 +2527,13 @@ F: include/system/rng*.h
2527 F: backends/rng*.c
2528 F: tests/qtest/virtio-rng-test.c
2529
2530 +virtio-rtc
2531 +M: Kuan-Wei Chiu <visitorckw@gmail.com>
2532 +S: Maintained
2533 +F: hw/virtio/virtio-rtc.c
2534 +F: hw/virtio/virtio-rtc-pci.c
2535 +F: include/hw/virtio/virtio-rtc.h
2536 +
2537 virtio-nsm
2538 M: Alexander Graf <graf@amazon.com>
2539 M: Dorjoy Chowdhury <dorjoychy111@gmail.com>
hw/virtio/Kconfig
+5
@@ -76,6 +76,11 @@ config VIRTIO_MEM
76 depends on VIRTIO_MEM_SUPPORTED
77 select VIRTIO_MD
78
79 +config VIRTIO_RTC
80 + bool
81 + default y
82 + depends on VIRTIO
83 +
84 config VHOST_VSOCK_COMMON
85 bool
86 depends on VIRTIO
hw/virtio/meson.build
+2
@@ -54,6 +54,7 @@ if have_vhost
54 endif
55 system_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
56 system_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
57 +system_virtio_ss.add(when: 'CONFIG_VIRTIO_RTC', if_true: files('virtio-rtc.c'))
58
59 system_virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c'))
60 system_virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs.c'))
@@ -77,6 +78,7 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto-pc
78 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host-pci.c'))
79 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-pci.c'))
80 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng-pci.c'))
81 +virtio_pci_ss.add(when: 'CONFIG_VIRTIO_RTC', if_true: files('virtio-rtc-pci.c'))
82 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('virtio-nsm-pci.c', 'cbor-helpers.c'), libcbor])
83 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon-pci.c'))
84 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-pci.c'))
hw/virtio/virtio-rtc-pci.c new
+65
@@ -0,0 +1,65 @@
1 +/*
2 + * Virtio RTC PCI Bindings
3 + *
4 + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
5 + * SPDX-License-Identifier: GPL-2.0-or-later
6 + */
7 +
8 +#include "qemu/osdep.h"
9 +#include "hw/virtio/virtio-pci.h"
10 +#include "hw/virtio/virtio-rtc.h"
11 +#include "standard-headers/linux/virtio_ids.h"
12 +
13 +typedef struct VirtIORtcPCI {
14 + VirtIOPCIProxy parent_obj;
15 + VirtIORtc vdev;
16 +} VirtIORtcPCI;
17 +
18 +#define TYPE_VIRTIO_RTC_PCI "virtio-rtc-pci-base"
19 +OBJECT_DECLARE_SIMPLE_TYPE(VirtIORtcPCI, VIRTIO_RTC_PCI)
20 +
21 +static void virtio_rtc_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
22 +{
23 + VirtIORtcPCI *dev = VIRTIO_RTC_PCI(vpci_dev);
24 + DeviceState *vdev = DEVICE(&dev->vdev);
25 +
26 + qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
27 +}
28 +
29 +static void virtio_rtc_pci_class_init(ObjectClass *klass, const void *data)
30 +{
31 + DeviceClass *dc = DEVICE_CLASS(klass);
32 + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
33 + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
34 +
35 + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
36 + k->realize = virtio_rtc_pci_realize;
37 +
38 + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
39 + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_10_BASE + VIRTIO_ID_CLOCK;
40 + pcidev_k->revision = 0x00;
41 + pcidev_k->class_id = PCI_CLASS_SYSTEM_RTC;
42 +}
43 +
44 +static void virtio_rtc_pci_instance_init(Object *obj)
45 +{
46 + VirtIORtcPCI *dev = VIRTIO_RTC_PCI(obj);
47 +
48 + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
49 + TYPE_VIRTIO_RTC);
50 +}
51 +
52 +static const VirtioPCIDeviceTypeInfo virtio_rtc_pci_info = {
53 + .base_name = TYPE_VIRTIO_RTC_PCI,
54 + .non_transitional_name = "virtio-rtc-pci",
55 + .instance_size = sizeof(VirtIORtcPCI),
56 + .instance_init = virtio_rtc_pci_instance_init,
57 + .class_init = virtio_rtc_pci_class_init,
58 +};
59 +
60 +static void virtio_rtc_pci_register(void)
61 +{
62 + virtio_pci_types_register(&virtio_rtc_pci_info);
63 +}
64 +
65 +type_init(virtio_rtc_pci_register);
hw/virtio/virtio-rtc.c new
+190
@@ -0,0 +1,190 @@
1 +/*
2 + * Virtio RTC device core
3 + *
4 + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
5 + * SPDX-License-Identifier: GPL-2.0-or-later
6 + */
7 +
8 +#include "qemu/osdep.h"
9 +#include "qemu/iov.h"
10 +#include "qemu/timer.h"
11 +#include "hw/virtio/virtio.h"
12 +#include "hw/virtio/virtio-rtc.h"
13 +#include "standard-headers/linux/virtio_ids.h"
14 +#include "standard-headers/linux/virtio_rtc.h"
15 +
16 +static void virtio_rtc_handle_request(VirtIODevice *vdev, VirtQueue *vq)
17 +{
18 + VirtQueueElement *elem;
19 + struct virtio_rtc_req_head req_head;
20 + size_t written;
21 +
22 + while ((elem = virtqueue_pop(vq, sizeof(VirtQueueElement)))) {
23 + if (elem->out_num < 1 || elem->in_num < 1) {
24 + virtio_error(vdev, "virtio-rtc: request missing in/out buffers");
25 + virtqueue_detach_element(vq, elem, 0);
26 + g_free(elem);
27 + break;
28 + }
29 +
30 + if (iov_to_buf(elem->out_sg, elem->out_num, 0, &req_head,
31 + sizeof(req_head)) != sizeof(req_head)) {
32 + virtio_error(vdev, "virtio-rtc: request header too short");
33 + virtqueue_detach_element(vq, elem, 0);
34 + g_free(elem);
35 + break;
36 + }
37 +
38 + written = 0;
39 +
40 + switch (le16_to_cpu(req_head.msg_type)) {
41 + case VIRTIO_RTC_REQ_CFG: {
42 + struct virtio_rtc_resp_cfg resp = {0};
43 + resp.head.status = VIRTIO_RTC_S_OK;
44 + resp.num_clocks = cpu_to_le16(1);
45 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
46 + sizeof(resp));
47 + break;
48 + }
49 + case VIRTIO_RTC_REQ_CLOCK_CAP: {
50 + struct virtio_rtc_req_clock_cap req;
51 + struct virtio_rtc_resp_clock_cap resp = {0};
52 +
53 + if (iov_to_buf(elem->out_sg, elem->out_num, 0, &req,
54 + sizeof(req)) != sizeof(req)) {
55 + resp.head.status = VIRTIO_RTC_S_EINVAL;
56 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
57 + sizeof(resp.head));
58 + break;
59 + }
60 +
61 + if (le16_to_cpu(req.clock_id) != 0) {
62 + resp.head.status = VIRTIO_RTC_S_ENODEV;
63 + } else {
64 + resp.head.status = VIRTIO_RTC_S_OK;
65 + resp.type = VIRTIO_RTC_CLOCK_UTC;
66 + }
67 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
68 + sizeof(resp));
69 + break;
70 + }
71 + case VIRTIO_RTC_REQ_CROSS_CAP: {
72 + struct virtio_rtc_req_cross_cap req;
73 + struct virtio_rtc_resp_cross_cap resp = {0};
74 +
75 + if (iov_to_buf(elem->out_sg, elem->out_num, 0, &req,
76 + sizeof(req)) != sizeof(req)) {
77 + resp.head.status = VIRTIO_RTC_S_EINVAL;
78 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
79 + sizeof(resp.head));
80 + break;
81 + }
82 +
83 + if (le16_to_cpu(req.clock_id) != 0) {
84 + resp.head.status = VIRTIO_RTC_S_ENODEV;
85 + } else {
86 + resp.head.status = VIRTIO_RTC_S_OK;
87 + }
88 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
89 + sizeof(resp));
90 + break;
91 + }
92 + case VIRTIO_RTC_REQ_READ: {
93 + struct virtio_rtc_req_read req;
94 + struct virtio_rtc_resp_read resp = {0};
95 +
96 + if (iov_to_buf(elem->out_sg, elem->out_num, 0, &req,
97 + sizeof(req)) != sizeof(req)) {
98 + resp.head.status = VIRTIO_RTC_S_EINVAL;
99 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
100 + sizeof(resp.head));
101 + break;
102 + }
103 +
104 + if (le16_to_cpu(req.clock_id) != 0) {
105 + resp.head.status = VIRTIO_RTC_S_ENODEV;
106 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
107 + sizeof(resp.head));
108 + } else {
109 + resp.head.status = VIRTIO_RTC_S_OK;
110 + resp.clock_reading =
111 + cpu_to_le64(qemu_clock_get_ns(QEMU_CLOCK_HOST));
112 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
113 + sizeof(resp));
114 + }
115 + break;
116 + }
117 + default: {
118 + struct virtio_rtc_resp_head resp = {0};
119 + resp.status = VIRTIO_RTC_S_EOPNOTSUPP;
120 + written = iov_from_buf(elem->in_sg, elem->in_num, 0, &resp,
121 + sizeof(resp));
122 + break;
123 + }
124 + }
125 +
126 + virtqueue_push(vq, elem, written);
127 + virtio_notify(vdev, vq);
128 + g_free(elem);
129 + }
130 +}
131 +
132 +static void virtio_rtc_device_realize(DeviceState *dev, Error **errp)
133 +{
134 + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
135 + VirtIORtc *vrtc = VIRTIO_RTC(dev);
136 +
137 + virtio_init(vdev, VIRTIO_ID_CLOCK, 0);
138 + vrtc->vq = virtio_add_queue(vdev, 64, virtio_rtc_handle_request);
139 +}
140 +
141 +static void virtio_rtc_device_unrealize(DeviceState *dev)
142 +{
143 + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
144 + VirtIORtc *vrtc = VIRTIO_RTC(dev);
145 +
146 + virtio_delete_queue(vrtc->vq);
147 + virtio_cleanup(vdev);
148 +}
149 +
150 +static uint64_t virtio_rtc_get_features(VirtIODevice *vdev, uint64_t f,
151 + Error **errp)
152 +{
153 + return f;
154 +}
155 +
156 +static const VMStateDescription vmstate_virtio_rtc = {
157 + .name = "virtio-rtc",
158 + .minimum_version_id = 1,
159 + .version_id = 1,
160 + .fields = (const VMStateField[]) {
161 + VMSTATE_VIRTIO_DEVICE,
162 + VMSTATE_END_OF_LIST()
163 + },
164 +};
165 +
166 +static void virtio_rtc_class_init(ObjectClass *klass, const void *data)
167 +{
168 + DeviceClass *dc = DEVICE_CLASS(klass);
169 + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
170 +
171 + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
172 + vdc->realize = virtio_rtc_device_realize;
173 + vdc->unrealize = virtio_rtc_device_unrealize;
174 + vdc->get_features = virtio_rtc_get_features;
175 + dc->vmsd = &vmstate_virtio_rtc;
176 +}
177 +
178 +static const TypeInfo virtio_rtc_info = {
179 + .name = TYPE_VIRTIO_RTC,
180 + .parent = TYPE_VIRTIO_DEVICE,
181 + .instance_size = sizeof(VirtIORtc),
182 + .class_init = virtio_rtc_class_init,
183 +};
184 +
185 +static void virtio_rtc_register_types(void)
186 +{
187 + type_register_static(&virtio_rtc_info);
188 +}
189 +
190 +type_init(virtio_rtc_register_types)
include/hw/virtio/virtio-rtc.h new
+22
@@ -0,0 +1,22 @@
1 +/*
2 + * Virtio RTC device
3 + *
4 + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
5 + * SPDX-License-Identifier: GPL-2.0-or-later
6 + */
7 +
8 +#ifndef QEMU_VIRTIO_RTC_H
9 +#define QEMU_VIRTIO_RTC_H
10 +
11 +#include "hw/virtio/virtio.h"
12 +#include "qom/object.h"
13 +
14 +#define TYPE_VIRTIO_RTC "virtio-rtc-device"
15 +OBJECT_DECLARE_SIMPLE_TYPE(VirtIORtc, VIRTIO_RTC)
16 +
17 +struct VirtIORtc {
18 + VirtIODevice parent_obj;
19 + VirtQueue *vq;
20 +};
21 +
22 +#endif