1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2019 Joyent, Inc.
14 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
15 * Copyright 2025 Oxide Computer Company
16 */
17
18 /*
19 * VIRTIO FRAMEWORK: Operations via the modern interface.
20 *
21 * For design and usage documentation, see the comments in "virtio.h".
22 */
23
24 #include <sys/conf.h>
25 #include <sys/kmem.h>
26 #include <sys/debug.h>
27 #include <sys/types.h>
28 #include <sys/inttypes.h>
29 #include <sys/ddi_impldefs.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/sysmacros.h>
33
34 #include "virtio.h"
35 #include "virtio_impl.h"
36 #include "virtio_endian.h"
37
38 static void virtio_modern_set_status_locked(virtio_t *, uint8_t);
39 static uint8_t virtio_modern_get_status(virtio_t *);
40
41 /*
42 * Reads and writes to the modern common configuration area.
43 */
44
45 static inline uint8_t
virtio_get_cmn8(virtio_t * vio,uintptr_t offset)46 virtio_get_cmn8(virtio_t *vio, uintptr_t offset)
47 {
48 return (ddi_get8(vio->vio_cap_common.vpc_barh,
49 (uint8_t *)(vio->vio_cap_common.vpc_bar + offset)));
50 }
51
52 static inline uint16_t
virtio_get_cmn16(virtio_t * vio,uintptr_t offset)53 virtio_get_cmn16(virtio_t *vio, uintptr_t offset)
54 {
55 return virtio_le16toh((ddi_get16(vio->vio_cap_common.vpc_barh,
56 (uint16_t *)(vio->vio_cap_common.vpc_bar + offset))));
57 }
58
59 static inline uint32_t
virtio_get_cmn32(virtio_t * vio,uintptr_t offset)60 virtio_get_cmn32(virtio_t *vio, uintptr_t offset)
61 {
62 return virtio_le32toh((ddi_get32(vio->vio_cap_common.vpc_barh,
63 (uint32_t *)(vio->vio_cap_common.vpc_bar + offset))));
64 }
65
66 static inline void
virtio_put_cmn8(virtio_t * vio,uintptr_t offset,uint8_t value)67 virtio_put_cmn8(virtio_t *vio, uintptr_t offset, uint8_t value)
68 {
69 ddi_put8(vio->vio_cap_common.vpc_barh,
70 (uint8_t *)(vio->vio_cap_common.vpc_bar + offset), value);
71 }
72
73 static inline void
virtio_put_cmn16(virtio_t * vio,uintptr_t offset,uint16_t value)74 virtio_put_cmn16(virtio_t *vio, uintptr_t offset, uint16_t value)
75 {
76 ddi_put16(vio->vio_cap_common.vpc_barh,
77 (uint16_t *)(vio->vio_cap_common.vpc_bar + offset),
78 virtio_htole16(value));
79 }
80
81 static inline void
virtio_put_cmn32(virtio_t * vio,uintptr_t offset,uint32_t value)82 virtio_put_cmn32(virtio_t *vio, uintptr_t offset, uint32_t value)
83 {
84 ddi_put32(vio->vio_cap_common.vpc_barh,
85 (uint32_t *)(vio->vio_cap_common.vpc_bar + offset),
86 virtio_htole32(value));
87 }
88
89 /*
90 * Reads and writes to the modern device configuration area.
91 */
92
93 static uint8_t
virtio_modern_devcfg_getgen(virtio_t * vio)94 virtio_modern_devcfg_getgen(virtio_t *vio)
95 {
96 return (virtio_get_cmn8(vio, VIRTIO_MODERN_COMMON_CFGGENERATION));
97 }
98
99 static uint8_t
virtio_modern_devcfg_get8(virtio_t * vio,uintptr_t offset)100 virtio_modern_devcfg_get8(virtio_t *vio, uintptr_t offset)
101 {
102 return (ddi_get8(vio->vio_cap_device.vpc_barh,
103 (uint8_t *)(vio->vio_cap_device.vpc_bar + offset)));
104 }
105
106 static uint16_t
virtio_modern_devcfg_get16(virtio_t * vio,uintptr_t offset)107 virtio_modern_devcfg_get16(virtio_t *vio, uintptr_t offset)
108 {
109 return (virtio_le16toh(ddi_get16(vio->vio_cap_device.vpc_barh,
110 (uint16_t *)(vio->vio_cap_device.vpc_bar + offset))));
111 }
112
113 static uint32_t
virtio_modern_devcfg_get32(virtio_t * vio,uintptr_t offset)114 virtio_modern_devcfg_get32(virtio_t *vio, uintptr_t offset)
115 {
116 return (virtio_le32toh(ddi_get32(vio->vio_cap_device.vpc_barh,
117 (uint32_t *)(vio->vio_cap_device.vpc_bar + offset))));
118 }
119
120 static uint64_t
virtio_modern_devcfg_get64(virtio_t * vio,uintptr_t offset)121 virtio_modern_devcfg_get64(virtio_t *vio, uintptr_t offset)
122 {
123 uint64_t val;
124 uint8_t gen;
125
126 /*
127 * On at least some systems, a 64-bit read or write to this BAR is not
128 * possible. Modern devices have a generation number that can be
129 * inspected to determine if configuration may have changed half-way
130 * through a read. We need to continue to read both halves of the
131 * value until we read the same generation value either side.
132 */
133 do {
134 gen = virtio_modern_devcfg_getgen(vio);
135 val = (uint64_t)virtio_le32toh(virtio_modern_devcfg_get32(vio,
136 offset + sizeof (uint32_t))) << 32;
137 val |= virtio_le32toh(virtio_modern_devcfg_get32(vio, offset));
138 } while (virtio_modern_devcfg_getgen(vio) != gen);
139
140 return (val);
141 }
142
143 static void
virtio_modern_devcfg_put8(virtio_t * vio,uintptr_t offset,uint8_t value)144 virtio_modern_devcfg_put8(virtio_t *vio, uintptr_t offset, uint8_t value)
145 {
146 ddi_put8(vio->vio_cap_device.vpc_barh,
147 (uint8_t *)(vio->vio_cap_device.vpc_bar + offset), value);
148 }
149
150 static void
virtio_modern_devcfg_put16(virtio_t * vio,uintptr_t offset,uint16_t value)151 virtio_modern_devcfg_put16(virtio_t *vio, uintptr_t offset, uint16_t value)
152 {
153 ddi_put16(vio->vio_cap_device.vpc_barh,
154 (uint16_t *)(vio->vio_cap_device.vpc_bar + offset),
155 virtio_htole16(value));
156 }
157
158 static void
virtio_modern_devcfg_put32(virtio_t * vio,uintptr_t offset,uint32_t value)159 virtio_modern_devcfg_put32(virtio_t *vio, uintptr_t offset, uint32_t value)
160 {
161 ddi_put32(vio->vio_cap_device.vpc_barh,
162 (uint32_t *)(vio->vio_cap_device.vpc_bar + offset),
163 virtio_htole32(value));
164 }
165
166 /*
167 * Reads and writes to the modern notification area.
168 */
169
170 static inline void
virtio_put_nfy16(virtio_t * vio,uintptr_t offset,uint16_t value)171 virtio_put_nfy16(virtio_t *vio, uintptr_t offset, uint16_t value)
172 {
173 ddi_put16(vio->vio_cap_notify.vpc_barh,
174 (uint16_t *)(vio->vio_cap_notify.vpc_bar + offset),
175 virtio_htole16(value));
176 }
177
178 /*
179 * Reads from the modern ISR area.
180 */
181
182 static inline uint8_t
virtio_get_isr8(virtio_t * vio,uintptr_t offset)183 virtio_get_isr8(virtio_t *vio, uintptr_t offset)
184 {
185 return (ddi_get8(vio->vio_cap_isr.vpc_barh,
186 (uint8_t *)(vio->vio_cap_isr.vpc_bar + offset)));
187 }
188
189 static uint64_t
virtio_modern_device_get_features(virtio_t * vio)190 virtio_modern_device_get_features(virtio_t *vio)
191 {
192 uint64_t features = 0;
193
194 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_DFSELECT, 1);
195 features = virtio_get_cmn32(vio, VIRTIO_MODERN_COMMON_DF);
196 features <<= 32;
197 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_DFSELECT, 0);
198 features |= virtio_get_cmn32(vio, VIRTIO_MODERN_COMMON_DF);
199
200 return (features);
201 }
202
203 static bool
virtio_modern_device_set_features(virtio_t * vio,uint64_t features)204 virtio_modern_device_set_features(virtio_t *vio, uint64_t features)
205 {
206 uint8_t status;
207
208 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_GFSELECT, 1);
209 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_GF, features >> 32);
210 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_GFSELECT, 0);
211 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_GF, features & 0xffffffff);
212 /* Signal that we are finished setting guest features */
213 mutex_enter(&vio->vio_mutex);
214 virtio_modern_set_status_locked(vio, VIRTIO_STATUS_FEAT_OK);
215 /*
216 * We now need to check that this bit is still set in the status. If
217 * it is not, then feature negotiation has failed and the device is
218 * unusable.
219 */
220 status = virtio_modern_get_status(vio);
221 mutex_exit(&vio->vio_mutex);
222 return ((status & VIRTIO_STATUS_FEAT_OK) != 0);
223 }
224
225 static void
virtio_modern_set_status_locked(virtio_t * vio,uint8_t status)226 virtio_modern_set_status_locked(virtio_t *vio, uint8_t status)
227 {
228 VERIFY3U(status, !=, 0);
229 VERIFY(MUTEX_HELD(&vio->vio_mutex));
230
231 uint8_t old = virtio_get_cmn8(vio, VIRTIO_MODERN_COMMON_STATUS);
232 virtio_put_cmn8(vio, VIRTIO_MODERN_COMMON_STATUS, status | old);
233 }
234
235 static uint8_t
virtio_modern_get_status(virtio_t * vio)236 virtio_modern_get_status(virtio_t *vio)
237 {
238 return (virtio_get_cmn8(vio, VIRTIO_MODERN_COMMON_STATUS));
239 }
240
241 static void
virtio_modern_device_reset_locked(virtio_t * vio)242 virtio_modern_device_reset_locked(virtio_t *vio)
243 {
244 VERIFY(MUTEX_HELD(&vio->vio_mutex));
245 virtio_put_cmn8(vio, VIRTIO_MODERN_COMMON_STATUS, VIRTIO_STATUS_RESET);
246 }
247
248 static uint8_t
virtio_modern_isr_status(virtio_t * vio)249 virtio_modern_isr_status(virtio_t *vio)
250 {
251 return (virtio_get_isr8(vio, 0));
252 }
253
254 static uint16_t
virtio_modern_msix_config_get(virtio_t * vio)255 virtio_modern_msix_config_get(virtio_t *vio)
256 {
257 return (virtio_get_cmn16(vio, VIRTIO_MODERN_COMMON_MSIX));
258 }
259
260 static void
virtio_modern_msix_config_set(virtio_t * vio,uint16_t msi)261 virtio_modern_msix_config_set(virtio_t *vio, uint16_t msi)
262 {
263 virtio_put_cmn16(vio, VIRTIO_MODERN_COMMON_MSIX, msi);
264 }
265
266 static void
virtio_modern_queue_notify(virtio_queue_t * viq)267 virtio_modern_queue_notify(virtio_queue_t *viq)
268 {
269 virtio_put_nfy16(viq->viq_virtio, viq->viq_noff, viq->viq_index);
270 }
271
272 static void
virtio_modern_queue_select(virtio_t * vio,uint16_t qidx)273 virtio_modern_queue_select(virtio_t *vio, uint16_t qidx)
274 {
275 virtio_put_cmn16(vio, VIRTIO_MODERN_COMMON_Q_SELECT, qidx);
276 }
277
278 static uint16_t
virtio_modern_queue_size_get(virtio_t * vio,uint16_t qidx)279 virtio_modern_queue_size_get(virtio_t *vio, uint16_t qidx)
280 {
281 uint16_t val;
282
283 virtio_acquireq(vio, qidx);
284 val = virtio_get_cmn16(vio, VIRTIO_MODERN_COMMON_Q_SIZE);
285 virtio_releaseq(vio);
286
287 return (val);
288 }
289
290 static void
virtio_modern_queue_size_set(virtio_t * vio,uint16_t qidx,uint16_t qsz)291 virtio_modern_queue_size_set(virtio_t *vio, uint16_t qidx, uint16_t qsz)
292 {
293 virtio_acquireq(vio, qidx);
294 virtio_put_cmn16(vio, VIRTIO_MODERN_COMMON_Q_SIZE, qsz);
295 virtio_releaseq(vio);
296 }
297
298 static uint64_t
virtio_modern_queue_noff_get(virtio_t * vio,uint16_t qidx)299 virtio_modern_queue_noff_get(virtio_t *vio, uint16_t qidx)
300 {
301 uint64_t noff;
302
303 virtio_acquireq(vio, qidx);
304 noff = (uint64_t)virtio_get_cmn16(vio, VIRTIO_MODERN_COMMON_Q_NOFF);
305 virtio_releaseq(vio);
306
307 noff *= vio->vio_multiplier;
308
309 return (noff);
310 }
311
312 static bool
virtio_modern_queue_enable_get(virtio_t * vio,uint16_t qidx)313 virtio_modern_queue_enable_get(virtio_t *vio, uint16_t qidx)
314 {
315 bool val;
316
317 virtio_acquireq(vio, qidx);
318 val = (virtio_get_cmn16(vio, VIRTIO_MODERN_COMMON_Q_ENABLE) != 0);
319 virtio_releaseq(vio);
320
321 return (val);
322 }
323
324 static void
virtio_modern_queue_enable_set(virtio_t * vio,uint16_t qidx,bool enable)325 virtio_modern_queue_enable_set(virtio_t *vio, uint16_t qidx, bool enable)
326 {
327 virtio_acquireq(vio, qidx);
328 virtio_put_cmn16(vio, VIRTIO_MODERN_COMMON_Q_ENABLE, enable);
329 virtio_releaseq(vio);
330 }
331
332 static void
virtio_modern_queue_addr_set(virtio_t * vio,uint16_t qidx,uint64_t descaddr,uint64_t availaddr,uint64_t usedaddr)333 virtio_modern_queue_addr_set(virtio_t *vio, uint16_t qidx, uint64_t descaddr,
334 uint64_t availaddr, uint64_t usedaddr)
335 {
336 virtio_acquireq(vio, qidx);
337 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_Q_DESCLO,
338 descaddr & 0xffffffff);
339 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_Q_DESCHI,
340 descaddr >> 32);
341 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_Q_AVAILLO,
342 availaddr & 0xffffffff);
343 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_Q_AVAILHI,
344 availaddr >> 32);
345 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_Q_USEDLO,
346 usedaddr & 0xffffffff);
347 virtio_put_cmn32(vio, VIRTIO_MODERN_COMMON_Q_USEDHI,
348 usedaddr >> 32);
349 virtio_releaseq(vio);
350 }
351
352 static uint16_t
virtio_modern_msix_queue_get(virtio_t * vio,uint16_t qidx)353 virtio_modern_msix_queue_get(virtio_t *vio, uint16_t qidx)
354 {
355 uint16_t val;
356
357 virtio_acquireq(vio, qidx);
358 val = virtio_get_cmn16(vio, VIRTIO_MODERN_COMMON_Q_MSIX);
359 virtio_releaseq(vio);
360
361 return (val);
362 }
363
364 static void
virtio_modern_msix_queue_set(virtio_t * vio,uint16_t qidx,uint16_t msi)365 virtio_modern_msix_queue_set(virtio_t *vio, uint16_t qidx, uint16_t msi)
366 {
367 virtio_acquireq(vio, qidx);
368 virtio_put_cmn16(vio, VIRTIO_MODERN_COMMON_Q_MSIX, msi);
369 virtio_releaseq(vio);
370 }
371
372 virtio_ops_t virtio_modern_ops = {
373 .vop_device_get_features = virtio_modern_device_get_features,
374 .vop_device_set_features = virtio_modern_device_set_features,
375 .vop_set_status_locked = virtio_modern_set_status_locked,
376 .vop_get_status = virtio_modern_get_status,
377 .vop_device_reset_locked = virtio_modern_device_reset_locked,
378 .vop_isr_status = virtio_modern_isr_status,
379 .vop_msix_config_get = virtio_modern_msix_config_get,
380 .vop_msix_config_set = virtio_modern_msix_config_set,
381 .vop_queue_notify = virtio_modern_queue_notify,
382
383 .vop_queue_select = virtio_modern_queue_select,
384 .vop_queue_size_get = virtio_modern_queue_size_get,
385 .vop_queue_size_set = virtio_modern_queue_size_set,
386 .vop_queue_noff_get = virtio_modern_queue_noff_get,
387 .vop_queue_enable_get = virtio_modern_queue_enable_get,
388 .vop_queue_enable_set = virtio_modern_queue_enable_set,
389 .vop_queue_addr_set = virtio_modern_queue_addr_set,
390 .vop_msix_queue_get = virtio_modern_msix_queue_get,
391 .vop_msix_queue_set = virtio_modern_msix_queue_set,
392
393 .vop_device_cfg_gen = virtio_modern_devcfg_getgen,
394 .vop_device_cfg_get8 = virtio_modern_devcfg_get8,
395 .vop_device_cfg_get16 = virtio_modern_devcfg_get16,
396 .vop_device_cfg_get32 = virtio_modern_devcfg_get32,
397 .vop_device_cfg_get64 = virtio_modern_devcfg_get64,
398 .vop_device_cfg_put8 = virtio_modern_devcfg_put8,
399 .vop_device_cfg_put16 = virtio_modern_devcfg_put16,
400 .vop_device_cfg_put32 = virtio_modern_devcfg_put32,
401 };
402