xref: /illumos-gate/usr/src/uts/common/io/virtio/virtio_modern.c (revision 2576e7a56bb1b296053722f3ebc688cef754350f)
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