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)