xref: /illumos-gate/usr/src/cmd/bhyve/pci_virtio_viona.c (revision c94be9439c4f0773ef60e2cec21d548359cfea20)
1 /*
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 /*
27  * This file and its contents are supplied under the terms of the
28  * Common Development and Distribution License ("CDDL"), version 1.0.
29  * You may only use this file in accordance with the terms of version
30  * 1.0 of the CDDL.
31  *
32  * A full copy of the text of the CDDL should have accompanied this
33  * source.  A copy of the CDDL is also available via the Internet at
34  * http://www.illumos.org/license/CDDL.
35  *
36  * Copyright 2015 Pluribus Networks Inc.
37  * Copyright 2019 Joyent, Inc.
38  */
39 
40 #include <sys/cdefs.h>
41 
42 #include <sys/param.h>
43 #include <sys/linker_set.h>
44 #include <sys/ioctl.h>
45 #include <sys/viona_io.h>
46 
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stdint.h>
52 #include <string.h>
53 #include <strings.h>
54 #include <unistd.h>
55 #include <assert.h>
56 #include <pthread.h>
57 #include <signal.h>
58 #include <poll.h>
59 #include <libdladm.h>
60 #include <libdllink.h>
61 #include <libdlvnic.h>
62 
63 #include <machine/vmm.h>
64 #include <vmmapi.h>
65 
66 #include "bhyverun.h"
67 #include "pci_emul.h"
68 #include "virtio.h"
69 
70 #define	VIONA_RINGSZ	1024
71 
72 /*
73  * PCI config-space register offsets
74  */
75 #define	VIONA_R_CFG0	24
76 #define	VIONA_R_CFG1	25
77 #define	VIONA_R_CFG2	26
78 #define	VIONA_R_CFG3	27
79 #define	VIONA_R_CFG4	28
80 #define	VIONA_R_CFG5	29
81 #define	VIONA_R_CFG6	30
82 #define	VIONA_R_CFG7	31
83 #define	VIONA_R_MAX	31
84 
85 #define	VIONA_REGSZ	VIONA_R_MAX+1
86 
87 /*
88  * Queue definitions.
89  */
90 #define	VIONA_RXQ	0
91 #define	VIONA_TXQ	1
92 #define	VIONA_CTLQ	2
93 
94 #define	VIONA_MAXQ	3
95 
96 /*
97  * Debug printf
98  */
99 static volatile int pci_viona_debug;
100 #define	DPRINTF(params) if (pci_viona_debug) printf params
101 #define	WPRINTF(params) printf params
102 
103 /*
104  * Per-device softc
105  */
106 struct pci_viona_softc {
107 	struct pci_devinst *vsc_pi;
108 	pthread_mutex_t vsc_mtx;
109 
110 	int		vsc_curq;
111 	int		vsc_status;
112 	int		vsc_isr;
113 
114 	datalink_id_t	vsc_linkid;
115 	int		vsc_vnafd;
116 
117 	/* Configurable parameters */
118 	char		vsc_linkname[MAXLINKNAMELEN];
119 	uint32_t	vsc_feature_mask;
120 	uint16_t	vsc_vq_size;
121 
122 	uint32_t	vsc_features;
123 	uint8_t		vsc_macaddr[6];
124 
125 	uint64_t	vsc_pfn[VIONA_MAXQ];
126 	uint16_t	vsc_msix_table_idx[VIONA_MAXQ];
127 	boolean_t	vsc_msix_active;
128 };
129 
130 /*
131  * Return the size of IO BAR that maps virtio header and device specific
132  * region. The size would vary depending on whether MSI-X is enabled or
133  * not.
134  */
135 static uint64_t
136 pci_viona_iosize(struct pci_devinst *pi)
137 {
138 	if (pci_msix_enabled(pi))
139 		return (VIONA_REGSZ);
140 	else
141 		return (VIONA_REGSZ - (VTCFG_R_CFG1 - VTCFG_R_MSIX));
142 }
143 
144 static uint16_t
145 pci_viona_qsize(struct pci_viona_softc *sc, int qnum)
146 {
147 	/* XXX no ctl queue currently */
148 	if (qnum == VIONA_CTLQ) {
149 		return (0);
150 	}
151 
152 	return (sc->vsc_vq_size);
153 }
154 
155 static void
156 pci_viona_ring_reset(struct pci_viona_softc *sc, int ring)
157 {
158 	assert(ring < VIONA_MAXQ);
159 
160 	switch (ring) {
161 	case VIONA_RXQ:
162 	case VIONA_TXQ:
163 		break;
164 	case VIONA_CTLQ:
165 	default:
166 		return;
167 	}
168 
169 	for (;;) {
170 		int res;
171 
172 		res = ioctl(sc->vsc_vnafd, VNA_IOC_RING_RESET, ring);
173 		if (res == 0) {
174 			break;
175 		} else if (errno != EINTR) {
176 			WPRINTF(("ioctl viona ring %d reset failed %d\n",
177 			    ring, errno));
178 			return;
179 		}
180 	}
181 
182 	sc->vsc_pfn[ring] = 0;
183 }
184 
185 static void
186 pci_viona_update_status(struct pci_viona_softc *sc, uint32_t value)
187 {
188 
189 	if (value == 0) {
190 		DPRINTF(("viona: device reset requested !\n"));
191 		pci_viona_ring_reset(sc, VIONA_RXQ);
192 		pci_viona_ring_reset(sc, VIONA_TXQ);
193 	}
194 
195 	sc->vsc_status = value;
196 }
197 
198 static void *
199 pci_viona_poll_thread(void *param)
200 {
201 	struct pci_viona_softc *sc = param;
202 	pollfd_t pollset;
203 	const int fd = sc->vsc_vnafd;
204 
205 	pollset.fd = fd;
206 	pollset.events = POLLRDBAND;
207 
208 	for (;;) {
209 		if (poll(&pollset, 1, -1) < 0) {
210 			if (errno == EINTR || errno == EAGAIN) {
211 				continue;
212 			} else {
213 				WPRINTF(("pci_viona_poll_thread poll()"
214 				    "error %d\n", errno));
215 				break;
216 			}
217 		}
218 		if (pollset.revents & POLLRDBAND) {
219 			vioc_intr_poll_t vip;
220 			uint_t i;
221 			int res;
222 			boolean_t assert_lintr = B_FALSE;
223 			const boolean_t do_msix = pci_msix_enabled(sc->vsc_pi);
224 
225 			res = ioctl(fd, VNA_IOC_INTR_POLL, &vip);
226 			for (i = 0; res > 0 && i < VIONA_VQ_MAX; i++) {
227 				if (vip.vip_status[i] == 0) {
228 					continue;
229 				}
230 				if (do_msix) {
231 					pci_generate_msix(sc->vsc_pi,
232 					    sc->vsc_msix_table_idx[i]);
233 				} else {
234 					assert_lintr = B_TRUE;
235 				}
236 				res = ioctl(fd, VNA_IOC_RING_INTR_CLR, i);
237 				if (res != 0) {
238 					WPRINTF(("ioctl viona vq %d intr "
239 					    "clear failed %d\n", i, errno));
240 				}
241 			}
242 			if (assert_lintr) {
243 				pthread_mutex_lock(&sc->vsc_mtx);
244 				sc->vsc_isr |= VTCFG_ISR_QUEUES;
245 				pci_lintr_assert(sc->vsc_pi);
246 				pthread_mutex_unlock(&sc->vsc_mtx);
247 			}
248 		}
249 	}
250 
251 	pthread_exit(NULL);
252 }
253 
254 static void
255 pci_viona_ring_init(struct pci_viona_softc *sc, uint64_t pfn)
256 {
257 	int			qnum = sc->vsc_curq;
258 	vioc_ring_init_t	vna_ri;
259 	int			error;
260 
261 	assert(qnum < VIONA_MAXQ);
262 
263 	if (qnum == VIONA_CTLQ) {
264 		return;
265 	}
266 
267 	sc->vsc_pfn[qnum] = (pfn << VRING_PFN);
268 
269 	vna_ri.ri_index = qnum;
270 	vna_ri.ri_qsize = pci_viona_qsize(sc, qnum);
271 	vna_ri.ri_qaddr = (pfn << VRING_PFN);
272 	error = ioctl(sc->vsc_vnafd, VNA_IOC_RING_INIT, &vna_ri);
273 
274 	if (error != 0) {
275 		WPRINTF(("ioctl viona ring %u init failed %d\n", qnum, errno));
276 	}
277 }
278 
279 static int
280 pci_viona_viona_init(struct vmctx *ctx, struct pci_viona_softc *sc)
281 {
282 	vioc_create_t		vna_create;
283 	int			error;
284 
285 	sc->vsc_vnafd = open("/dev/viona", O_RDWR | O_EXCL);
286 	if (sc->vsc_vnafd == -1) {
287 		WPRINTF(("open viona ctl failed: %d\n", errno));
288 		return (-1);
289 	}
290 
291 	vna_create.c_linkid = sc->vsc_linkid;
292 	vna_create.c_vmfd = vm_get_device_fd(ctx);
293 	error = ioctl(sc->vsc_vnafd, VNA_IOC_CREATE, &vna_create);
294 	if (error != 0) {
295 		(void) close(sc->vsc_vnafd);
296 		WPRINTF(("ioctl viona create failed %d\n", errno));
297 		return (-1);
298 	}
299 
300 	return (0);
301 }
302 
303 static int
304 pci_viona_parse_opts(struct pci_viona_softc *sc, char *opts)
305 {
306 	char *next, *cp, *vnic = NULL;
307 	int err = 0;
308 
309 	sc->vsc_vq_size = VIONA_RINGSZ;
310 	sc->vsc_feature_mask = 0;
311 
312 	for (; opts != NULL && *opts != '\0'; opts = next) {
313 		char *val;
314 
315 		if ((cp = strchr(opts, ',')) != NULL) {
316 			*cp = '\0';
317 			next = cp + 1;
318 		} else {
319 			next = NULL;
320 		}
321 
322 		if ((cp = strchr(opts, '=')) == NULL) {
323 			/* vnic chosen with bare name */
324 			if (vnic != NULL) {
325 				fprintf(stderr,
326 				    "viona: unexpected vnic name '%s'", opts);
327 				err = -1;
328 			} else {
329 				vnic = opts;
330 			}
331 			continue;
332 		}
333 
334 		/* <param>=<value> handling */
335 		val = cp + 1;
336 		*cp = '\0';
337 		if (strcmp(opts, "feature_mask") == 0) {
338 			long num;
339 
340 			errno = 0;
341 			num = strtol(val, NULL, 0);
342 			if (errno != 0 || num < 0) {
343 				fprintf(stderr,
344 				    "viona: invalid mask '%s'", val);
345 			} else {
346 				sc->vsc_feature_mask = num;
347 			}
348 		} else if (strcmp(opts, "vqsize") == 0) {
349 			long num;
350 
351 			errno = 0;
352 			num = strtol(val, NULL, 0);
353 			if (errno != 0) {
354 				fprintf(stderr,
355 				    "viona: invalid vsqize '%s'", val);
356 				err = -1;
357 			} else if (num <= 2 || num > 32768) {
358 				fprintf(stderr,
359 				    "viona: vqsize out of range", num);
360 				err = -1;
361 			} else if ((1 << (ffs(num) - 1)) != num) {
362 				fprintf(stderr,
363 				    "viona: vqsize must be power of 2", num);
364 				err = -1;
365 			} else {
366 				sc->vsc_vq_size = num;
367 			}
368 		} else {
369 			fprintf(stderr,
370 			    "viona: unrecognized option '%s'", opts);
371 			err = -1;
372 		}
373 	}
374 	if (vnic == NULL) {
375 		fprintf(stderr, "viona: vnic name required");
376 		sc->vsc_linkname[0] = '\0';
377 		err = -1;
378 	} else {
379 		(void) strlcpy(sc->vsc_linkname, vnic, MAXLINKNAMELEN);
380 	}
381 
382 	DPRINTF(("viona=%p dev=%s vqsize=%x feature_mask=%x\n", sc,
383 	    sc->vsc_linkname, sc->vsc_vq_size, sc->vsc_feature_mask));
384 	return (err);
385 }
386 
387 static int
388 pci_viona_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
389 {
390 	dladm_handle_t		handle;
391 	dladm_status_t		status;
392 	dladm_vnic_attr_t	attr;
393 	char			errmsg[DLADM_STRSIZE];
394 	int error, i;
395 	struct pci_viona_softc *sc;
396 	uint64_t ioport;
397 
398 	if (opts == NULL) {
399 		printf("virtio-viona: vnic required\n");
400 		return (1);
401 	}
402 
403 	sc = malloc(sizeof (struct pci_viona_softc));
404 	memset(sc, 0, sizeof (struct pci_viona_softc));
405 
406 	pi->pi_arg = sc;
407 	sc->vsc_pi = pi;
408 
409 	pthread_mutex_init(&sc->vsc_mtx, NULL);
410 
411 	if (pci_viona_parse_opts(sc, opts) != 0) {
412 		free(sc);
413 		return (1);
414 	}
415 
416 	if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
417 		WPRINTF(("could not open /dev/dld"));
418 		free(sc);
419 		return (1);
420 	}
421 
422 	if (dladm_name2info(handle, sc->vsc_linkname, &sc->vsc_linkid,
423 	    NULL, NULL, NULL) != DLADM_STATUS_OK) {
424 		WPRINTF(("dladm_name2info() for %s failed: %s\n", opts,
425 		    dladm_status2str(status, errmsg)));
426 		dladm_close(handle);
427 		free(sc);
428 		return (1);
429 	}
430 
431 	if (dladm_vnic_info(handle, sc->vsc_linkid, &attr,
432 	    DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
433 		WPRINTF(("dladm_vnic_info() for %s failed: %s\n", opts,
434 		    dladm_status2str(status, errmsg)));
435 		dladm_close(handle);
436 		free(sc);
437 		return (1);
438 	}
439 
440 	memcpy(sc->vsc_macaddr, attr.va_mac_addr, ETHERADDRL);
441 
442 	dladm_close(handle);
443 
444 	error = pci_viona_viona_init(ctx, sc);
445 	if (error != 0) {
446 		free(sc);
447 		return (1);
448 	}
449 
450 	error = pthread_create(NULL, NULL, pci_viona_poll_thread, sc);
451 	assert(error == 0);
452 
453 	/* initialize config space */
454 	pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET);
455 	pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
456 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK);
457 	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET);
458 	pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
459 
460 	/* MSI-X support */
461 	for (i = 0; i < VIONA_MAXQ; i++)
462 		sc->vsc_msix_table_idx[i] = VIRTIO_MSI_NO_VECTOR;
463 
464 	/* BAR 1 used to map MSI-X table and PBA */
465 	if (pci_emul_add_msixcap(pi, VIONA_MAXQ, 1)) {
466 		free(sc);
467 		return (1);
468 	}
469 
470 	/* BAR 0 for legacy-style virtio register access. */
471 	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, VIONA_REGSZ);
472 	if (error != 0) {
473 		WPRINTF(("could not allocate virtio BAR\n"));
474 		free(sc);
475 		return (1);
476 	}
477 
478 	/* Install ioport hook for virtqueue notification */
479 	ioport = pi->pi_bar[0].addr + VTCFG_R_QNOTIFY;
480 	error = ioctl(sc->vsc_vnafd, VNA_IOC_SET_NOTIFY_IOP, ioport);
481 	if (error != 0) {
482 		WPRINTF(("could not install ioport hook at %x\n", ioport));
483 		free(sc);
484 		return (1);
485 	}
486 
487 	/*
488 	 * Need a legacy interrupt for virtio compliance, even though MSI-X
489 	 * operation is _strongly_ suggested for adequate performance.
490 	 */
491 	pci_lintr_request(pi);
492 
493 	return (0);
494 }
495 
496 static uint64_t
497 viona_adjust_offset(struct pci_devinst *pi, uint64_t offset)
498 {
499 	/*
500 	 * Device specific offsets used by guest would change based on
501 	 * whether MSI-X capability is enabled or not
502 	 */
503 	if (!pci_msix_enabled(pi)) {
504 		if (offset >= VTCFG_R_MSIX)
505 			return (offset + (VTCFG_R_CFG1 - VTCFG_R_MSIX));
506 	}
507 
508 	return (offset);
509 }
510 
511 static void
512 pci_viona_ring_set_msix(struct pci_devinst *pi, uint_t ring)
513 {
514 	struct pci_viona_softc *sc = pi->pi_arg;
515 	struct msix_table_entry mte;
516 	uint16_t tab_index;
517 	vioc_ring_msi_t vrm;
518 	int res;
519 
520 	assert(ring <= VIONA_VQ_TX);
521 
522 	vrm.rm_index = ring;
523 	vrm.rm_addr = 0;
524 	vrm.rm_msg = 0;
525 	tab_index = sc->vsc_msix_table_idx[ring];
526 
527 	if (tab_index != VIRTIO_MSI_NO_VECTOR && sc->vsc_msix_active) {
528 		mte = pi->pi_msix.table[tab_index];
529 		if ((mte.vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
530 			vrm.rm_addr = mte.addr;
531 			vrm.rm_msg = mte.msg_data;
532 		}
533 	}
534 
535 	res = ioctl(sc->vsc_vnafd, VNA_IOC_RING_SET_MSI, &vrm);
536 	if (res != 0) {
537 		WPRINTF(("ioctl viona set_msi %d failed %d\n", ring, errno));
538 	}
539 }
540 
541 static void
542 pci_viona_lintrupdate(struct pci_devinst *pi)
543 {
544 	struct pci_viona_softc *sc = pi->pi_arg;
545 	boolean_t msix_on = B_FALSE;
546 
547 	pthread_mutex_lock(&sc->vsc_mtx);
548 	msix_on = pci_msix_enabled(pi) && (pi->pi_msix.function_mask == 0);
549 	if ((sc->vsc_msix_active && !msix_on) ||
550 	    (msix_on && !sc->vsc_msix_active)) {
551 		uint_t i;
552 
553 		sc->vsc_msix_active = msix_on;
554 		/* Update in-kernel ring configs */
555 		for (i = 0; i <= VIONA_VQ_TX; i++) {
556 			pci_viona_ring_set_msix(pi, i);
557 		}
558 	}
559 	pthread_mutex_unlock(&sc->vsc_mtx);
560 }
561 
562 static void
563 pci_viona_msix_update(struct pci_devinst *pi, uint64_t offset)
564 {
565 	struct pci_viona_softc *sc = pi->pi_arg;
566 	uint_t tab_index, i;
567 
568 	pthread_mutex_lock(&sc->vsc_mtx);
569 	if (!sc->vsc_msix_active) {
570 		pthread_mutex_unlock(&sc->vsc_mtx);
571 		return;
572 	}
573 
574 	/*
575 	 * Rather than update every possible MSI-X vector, cheat and use the
576 	 * offset to calculate the entry within the table.  Since this should
577 	 * only be called when a write to the table succeeds, the index should
578 	 * be valid.
579 	 */
580 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
581 
582 	for (i = 0; i <= VIONA_VQ_TX; i++) {
583 		if (sc->vsc_msix_table_idx[i] != tab_index) {
584 			continue;
585 		}
586 		pci_viona_ring_set_msix(pi, i);
587 	}
588 
589 	pthread_mutex_unlock(&sc->vsc_mtx);
590 }
591 
592 static void
593 pci_viona_qnotify(struct pci_viona_softc *sc, int ring)
594 {
595 	int error;
596 
597 	switch (ring) {
598 	case VIONA_TXQ:
599 	case VIONA_RXQ:
600 		error = ioctl(sc->vsc_vnafd, VNA_IOC_RING_KICK, ring);
601 		if (error != 0) {
602 			WPRINTF(("ioctl viona ring %d kick failed %d\n",
603 			    ring, errno));
604 		}
605 		break;
606 	case VIONA_CTLQ:
607 		DPRINTF(("viona: control qnotify!\n"));
608 		break;
609 	default:
610 		break;
611 	}
612 }
613 
614 static void
615 pci_viona_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
616     int baridx, uint64_t offset, int size, uint64_t value)
617 {
618 	struct pci_viona_softc *sc = pi->pi_arg;
619 	void *ptr;
620 	int err = 0;
621 
622 	if (baridx == pci_msix_table_bar(pi) ||
623 	    baridx == pci_msix_pba_bar(pi)) {
624 		if (pci_emul_msix_twrite(pi, offset, size, value) == 0) {
625 			pci_viona_msix_update(pi, offset);
626 		}
627 		return;
628 	}
629 
630 	assert(baridx == 0);
631 
632 	if (offset + size > pci_viona_iosize(pi)) {
633 		DPRINTF(("viona_write: 2big, offset %ld size %d\n",
634 		    offset, size));
635 		return;
636 	}
637 
638 	pthread_mutex_lock(&sc->vsc_mtx);
639 
640 	offset = viona_adjust_offset(pi, offset);
641 
642 	switch (offset) {
643 	case VTCFG_R_GUESTCAP:
644 		assert(size == 4);
645 		value &= ~(sc->vsc_feature_mask);
646 		err = ioctl(sc->vsc_vnafd, VNA_IOC_SET_FEATURES, &value);
647 		if (err != 0) {
648 			WPRINTF(("ioctl feature negotiation returned"
649 			    " err = %d\n", errno));
650 		} else {
651 			sc->vsc_features = value;
652 		}
653 		break;
654 	case VTCFG_R_PFN:
655 		assert(size == 4);
656 		pci_viona_ring_init(sc, value);
657 		break;
658 	case VTCFG_R_QSEL:
659 		assert(size == 2);
660 		assert(value < VIONA_MAXQ);
661 		sc->vsc_curq = value;
662 		break;
663 	case VTCFG_R_QNOTIFY:
664 		assert(size == 2);
665 		assert(value < VIONA_MAXQ);
666 		pci_viona_qnotify(sc, value);
667 		break;
668 	case VTCFG_R_STATUS:
669 		assert(size == 1);
670 		pci_viona_update_status(sc, value);
671 		break;
672 	case VTCFG_R_CFGVEC:
673 		assert(size == 2);
674 		sc->vsc_msix_table_idx[VIONA_CTLQ] = value;
675 		break;
676 	case VTCFG_R_QVEC:
677 		assert(size == 2);
678 		assert(sc->vsc_curq != VIONA_CTLQ);
679 		sc->vsc_msix_table_idx[sc->vsc_curq] = value;
680 		pci_viona_ring_set_msix(pi, sc->vsc_curq);
681 		break;
682 	case VIONA_R_CFG0:
683 	case VIONA_R_CFG1:
684 	case VIONA_R_CFG2:
685 	case VIONA_R_CFG3:
686 	case VIONA_R_CFG4:
687 	case VIONA_R_CFG5:
688 		assert((size + offset) <= (VIONA_R_CFG5 + 1));
689 		ptr = &sc->vsc_macaddr[offset - VIONA_R_CFG0];
690 		/*
691 		 * The driver is allowed to change the MAC address
692 		 */
693 		sc->vsc_macaddr[offset - VIONA_R_CFG0] = value;
694 		if (size == 1) {
695 			*(uint8_t *)ptr = value;
696 		} else if (size == 2) {
697 			*(uint16_t *)ptr = value;
698 		} else {
699 			*(uint32_t *)ptr = value;
700 		}
701 		break;
702 	case VTCFG_R_HOSTCAP:
703 	case VTCFG_R_QNUM:
704 	case VTCFG_R_ISR:
705 	case VIONA_R_CFG6:
706 	case VIONA_R_CFG7:
707 		DPRINTF(("viona: write to readonly reg %ld\n\r", offset));
708 		break;
709 	default:
710 		DPRINTF(("viona: unknown i/o write offset %ld\n\r", offset));
711 		value = 0;
712 		break;
713 	}
714 
715 	pthread_mutex_unlock(&sc->vsc_mtx);
716 }
717 
718 static uint64_t
719 pci_viona_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
720     int baridx, uint64_t offset, int size)
721 {
722 	struct pci_viona_softc *sc = pi->pi_arg;
723 	void *ptr;
724 	uint64_t value;
725 	int err = 0;
726 
727 	if (baridx == pci_msix_table_bar(pi) ||
728 	    baridx == pci_msix_pba_bar(pi)) {
729 		return (pci_emul_msix_tread(pi, offset, size));
730 	}
731 
732 	assert(baridx == 0);
733 
734 	if (offset + size > pci_viona_iosize(pi)) {
735 		DPRINTF(("viona_read: 2big, offset %ld size %d\n",
736 		    offset, size));
737 		return (0);
738 	}
739 
740 	pthread_mutex_lock(&sc->vsc_mtx);
741 
742 	offset = viona_adjust_offset(pi, offset);
743 
744 	switch (offset) {
745 	case VTCFG_R_HOSTCAP:
746 		assert(size == 4);
747 		err = ioctl(sc->vsc_vnafd, VNA_IOC_GET_FEATURES, &value);
748 		if (err != 0) {
749 			WPRINTF(("ioctl get host features returned"
750 			    " err = %d\n", errno));
751 		}
752 		value &= ~sc->vsc_feature_mask;
753 		break;
754 	case VTCFG_R_GUESTCAP:
755 		assert(size == 4);
756 		value = sc->vsc_features; /* XXX never read ? */
757 		break;
758 	case VTCFG_R_PFN:
759 		assert(size == 4);
760 		value = sc->vsc_pfn[sc->vsc_curq] >> VRING_PFN;
761 		break;
762 	case VTCFG_R_QNUM:
763 		assert(size == 2);
764 		value = pci_viona_qsize(sc, sc->vsc_curq);
765 		break;
766 	case VTCFG_R_QSEL:
767 		assert(size == 2);
768 		value = sc->vsc_curq;  /* XXX never read ? */
769 		break;
770 	case VTCFG_R_QNOTIFY:
771 		assert(size == 2);
772 		value = sc->vsc_curq;  /* XXX never read ? */
773 		break;
774 	case VTCFG_R_STATUS:
775 		assert(size == 1);
776 		value = sc->vsc_status;
777 		break;
778 	case VTCFG_R_ISR:
779 		assert(size == 1);
780 		value = sc->vsc_isr;
781 		sc->vsc_isr = 0;	/* a read clears this flag */
782 		if (value != 0) {
783 			pci_lintr_deassert(pi);
784 		}
785 		break;
786 	case VTCFG_R_CFGVEC:
787 		assert(size == 2);
788 		value = sc->vsc_msix_table_idx[VIONA_CTLQ];
789 		break;
790 	case VTCFG_R_QVEC:
791 		assert(size == 2);
792 		assert(sc->vsc_curq != VIONA_CTLQ);
793 		value = sc->vsc_msix_table_idx[sc->vsc_curq];
794 		break;
795 	case VIONA_R_CFG0:
796 	case VIONA_R_CFG1:
797 	case VIONA_R_CFG2:
798 	case VIONA_R_CFG3:
799 	case VIONA_R_CFG4:
800 	case VIONA_R_CFG5:
801 		assert((size + offset) <= (VIONA_R_CFG5 + 1));
802 		ptr = &sc->vsc_macaddr[offset - VIONA_R_CFG0];
803 		if (size == 1) {
804 			value = *(uint8_t *)ptr;
805 		} else if (size == 2) {
806 			value = *(uint16_t *)ptr;
807 		} else {
808 			value = *(uint32_t *)ptr;
809 		}
810 		break;
811 	case VIONA_R_CFG6:
812 		assert(size != 4);
813 		value = 0x01;	/* XXX link always up */
814 		break;
815 	case VIONA_R_CFG7:
816 		assert(size == 1);
817 		value = 0;	/* XXX link status in LSB */
818 		break;
819 	default:
820 		DPRINTF(("viona: unknown i/o read offset %ld\n\r", offset));
821 		value = 0;
822 		break;
823 	}
824 
825 	pthread_mutex_unlock(&sc->vsc_mtx);
826 
827 	return (value);
828 }
829 
830 struct pci_devemu pci_de_viona = {
831 	.pe_emu =	"virtio-net-viona",
832 	.pe_init =	pci_viona_init,
833 	.pe_barwrite =	pci_viona_write,
834 	.pe_barread =	pci_viona_read,
835 	.pe_lintrupdate = pci_viona_lintrupdate
836 };
837 PCI_EMUL_SET(pci_de_viona);
838