xref: /illumos-gate/usr/src/cmd/bhyve/common/pci_virtio_viona.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
1*5c4a5fe1SAndy Fiddaman /*
2*5c4a5fe1SAndy Fiddaman  * Copyright (c) 2011 NetApp, Inc.
3*5c4a5fe1SAndy Fiddaman  * All rights reserved.
4*5c4a5fe1SAndy Fiddaman  *
5*5c4a5fe1SAndy Fiddaman  * Redistribution and use in source and binary forms, with or without
6*5c4a5fe1SAndy Fiddaman  * modification, are permitted provided that the following conditions
7*5c4a5fe1SAndy Fiddaman  * are met:
8*5c4a5fe1SAndy Fiddaman  * 1. Redistributions of source code must retain the above copyright
9*5c4a5fe1SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer.
10*5c4a5fe1SAndy Fiddaman  * 2. Redistributions in binary form must reproduce the above copyright
11*5c4a5fe1SAndy Fiddaman  *    notice, this list of conditions and the following disclaimer in the
12*5c4a5fe1SAndy Fiddaman  *    documentation and/or other materials provided with the distribution.
13*5c4a5fe1SAndy Fiddaman  *
14*5c4a5fe1SAndy Fiddaman  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15*5c4a5fe1SAndy Fiddaman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*5c4a5fe1SAndy Fiddaman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*5c4a5fe1SAndy Fiddaman  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18*5c4a5fe1SAndy Fiddaman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*5c4a5fe1SAndy Fiddaman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*5c4a5fe1SAndy Fiddaman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*5c4a5fe1SAndy Fiddaman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*5c4a5fe1SAndy Fiddaman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*5c4a5fe1SAndy Fiddaman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*5c4a5fe1SAndy Fiddaman  * SUCH DAMAGE.
25*5c4a5fe1SAndy Fiddaman  */
26*5c4a5fe1SAndy Fiddaman /*
27*5c4a5fe1SAndy Fiddaman  * This file and its contents are supplied under the terms of the
28*5c4a5fe1SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
29*5c4a5fe1SAndy Fiddaman  * You may only use this file in accordance with the terms of version
30*5c4a5fe1SAndy Fiddaman  * 1.0 of the CDDL.
31*5c4a5fe1SAndy Fiddaman  *
32*5c4a5fe1SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
33*5c4a5fe1SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
34*5c4a5fe1SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
35*5c4a5fe1SAndy Fiddaman  *
36*5c4a5fe1SAndy Fiddaman  * Copyright 2015 Pluribus Networks Inc.
37*5c4a5fe1SAndy Fiddaman  * Copyright 2019 Joyent, Inc.
38*5c4a5fe1SAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
39*5c4a5fe1SAndy Fiddaman  */
40*5c4a5fe1SAndy Fiddaman 
41*5c4a5fe1SAndy Fiddaman 
42*5c4a5fe1SAndy Fiddaman #include <sys/param.h>
43*5c4a5fe1SAndy Fiddaman #include <sys/linker_set.h>
44*5c4a5fe1SAndy Fiddaman #include <sys/ioctl.h>
45*5c4a5fe1SAndy Fiddaman #include <sys/uio.h>
46*5c4a5fe1SAndy Fiddaman #include <sys/viona_io.h>
47*5c4a5fe1SAndy Fiddaman 
48*5c4a5fe1SAndy Fiddaman #include <errno.h>
49*5c4a5fe1SAndy Fiddaman #include <fcntl.h>
50*5c4a5fe1SAndy Fiddaman #include <stdio.h>
51*5c4a5fe1SAndy Fiddaman #include <stdlib.h>
52*5c4a5fe1SAndy Fiddaman #include <stdint.h>
53*5c4a5fe1SAndy Fiddaman #include <string.h>
54*5c4a5fe1SAndy Fiddaman #include <strings.h>
55*5c4a5fe1SAndy Fiddaman #include <unistd.h>
56*5c4a5fe1SAndy Fiddaman #include <assert.h>
57*5c4a5fe1SAndy Fiddaman #include <pthread.h>
58*5c4a5fe1SAndy Fiddaman #include <signal.h>
59*5c4a5fe1SAndy Fiddaman #include <stdbool.h>
60*5c4a5fe1SAndy Fiddaman #include <poll.h>
61*5c4a5fe1SAndy Fiddaman #include <libdladm.h>
62*5c4a5fe1SAndy Fiddaman #include <libdllink.h>
63*5c4a5fe1SAndy Fiddaman #include <libdlvnic.h>
64*5c4a5fe1SAndy Fiddaman 
65*5c4a5fe1SAndy Fiddaman #include <machine/vmm.h>
66*5c4a5fe1SAndy Fiddaman #include <vmmapi.h>
67*5c4a5fe1SAndy Fiddaman 
68*5c4a5fe1SAndy Fiddaman #include "bhyverun.h"
69*5c4a5fe1SAndy Fiddaman #include "config.h"
70*5c4a5fe1SAndy Fiddaman #include "debug.h"
71*5c4a5fe1SAndy Fiddaman #include "pci_emul.h"
72*5c4a5fe1SAndy Fiddaman #include "virtio.h"
73*5c4a5fe1SAndy Fiddaman #include "iov.h"
74*5c4a5fe1SAndy Fiddaman #include "virtio_net.h"
75*5c4a5fe1SAndy Fiddaman 
76*5c4a5fe1SAndy Fiddaman #define	VIONA_RINGSZ		1024
77*5c4a5fe1SAndy Fiddaman #define	VIONA_CTLQ_SIZE		64
78*5c4a5fe1SAndy Fiddaman #define	VIONA_CTLQ_MAXSEGS	32
79*5c4a5fe1SAndy Fiddaman 
80*5c4a5fe1SAndy Fiddaman /*
81*5c4a5fe1SAndy Fiddaman  * PCI config-space register offsets
82*5c4a5fe1SAndy Fiddaman  */
83*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG0	24
84*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG1	25
85*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG2	26
86*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG3	27
87*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG4	28
88*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG5	29
89*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG6	30
90*5c4a5fe1SAndy Fiddaman #define	VIONA_R_CFG7	31
91*5c4a5fe1SAndy Fiddaman #define	VIONA_R_MAX	31
92*5c4a5fe1SAndy Fiddaman 
93*5c4a5fe1SAndy Fiddaman #define	VIONA_REGSZ	(VIONA_R_MAX + 1)
94*5c4a5fe1SAndy Fiddaman 
95*5c4a5fe1SAndy Fiddaman /*
96*5c4a5fe1SAndy Fiddaman  * Queue definitions.
97*5c4a5fe1SAndy Fiddaman  */
98*5c4a5fe1SAndy Fiddaman #define	VIONA_RXQ	0
99*5c4a5fe1SAndy Fiddaman #define	VIONA_TXQ	1
100*5c4a5fe1SAndy Fiddaman #define	VIONA_CTLQ	2
101*5c4a5fe1SAndy Fiddaman 
102*5c4a5fe1SAndy Fiddaman #define	VIONA_MAXQ	3
103*5c4a5fe1SAndy Fiddaman 
104*5c4a5fe1SAndy Fiddaman /*
105*5c4a5fe1SAndy Fiddaman  * Supplementary host capabilities provided in the userspace component.
106*5c4a5fe1SAndy Fiddaman  */
107*5c4a5fe1SAndy Fiddaman #define	VIONA_S_HOSTCAPS_USERSPACE	(	\
108*5c4a5fe1SAndy Fiddaman 	VIRTIO_NET_F_CTRL_VQ |			\
109*5c4a5fe1SAndy Fiddaman 	VIRTIO_NET_F_CTRL_RX)
110*5c4a5fe1SAndy Fiddaman 
111*5c4a5fe1SAndy Fiddaman /*
112*5c4a5fe1SAndy Fiddaman  * Debug printf
113*5c4a5fe1SAndy Fiddaman  */
114*5c4a5fe1SAndy Fiddaman static volatile int pci_viona_debug;
115*5c4a5fe1SAndy Fiddaman #define	DPRINTF(fmt, arg...) \
116*5c4a5fe1SAndy Fiddaman 	do { \
117*5c4a5fe1SAndy Fiddaman 		if (pci_viona_debug) { \
118*5c4a5fe1SAndy Fiddaman 			FPRINTLN(stdout, fmt, ##arg); \
119*5c4a5fe1SAndy Fiddaman 			fflush(stdout); \
120*5c4a5fe1SAndy Fiddaman 		} \
121*5c4a5fe1SAndy Fiddaman 	} while (0)
122*5c4a5fe1SAndy Fiddaman #define	WPRINTF(fmt, arg...) FPRINTLN(stderr, fmt, ##arg)
123*5c4a5fe1SAndy Fiddaman 
124*5c4a5fe1SAndy Fiddaman /*
125*5c4a5fe1SAndy Fiddaman  * Per-device softc
126*5c4a5fe1SAndy Fiddaman  */
127*5c4a5fe1SAndy Fiddaman struct pci_viona_softc {
128*5c4a5fe1SAndy Fiddaman 	struct virtio_softc	vsc_vs;
129*5c4a5fe1SAndy Fiddaman 	struct virtio_consts	vsc_consts;
130*5c4a5fe1SAndy Fiddaman 	struct vqueue_info	vsc_queues[VIONA_MAXQ];
131*5c4a5fe1SAndy Fiddaman 	pthread_mutex_t		vsc_mtx;
132*5c4a5fe1SAndy Fiddaman 
133*5c4a5fe1SAndy Fiddaman 	datalink_id_t	vsc_linkid;
134*5c4a5fe1SAndy Fiddaman 	int		vsc_vnafd;
135*5c4a5fe1SAndy Fiddaman 
136*5c4a5fe1SAndy Fiddaman 	/* Configurable parameters */
137*5c4a5fe1SAndy Fiddaman 	char		vsc_linkname[MAXLINKNAMELEN];
138*5c4a5fe1SAndy Fiddaman 	uint32_t	vsc_feature_mask;
139*5c4a5fe1SAndy Fiddaman 	uint16_t	vsc_vq_size;
140*5c4a5fe1SAndy Fiddaman 
141*5c4a5fe1SAndy Fiddaman 	uint8_t		vsc_macaddr[6];
142*5c4a5fe1SAndy Fiddaman 
143*5c4a5fe1SAndy Fiddaman 	bool		vsc_resetting;
144*5c4a5fe1SAndy Fiddaman 	bool		vsc_msix_active;
145*5c4a5fe1SAndy Fiddaman 
146*5c4a5fe1SAndy Fiddaman 	viona_promisc_t	vsc_promisc;		/* Current promisc mode */
147*5c4a5fe1SAndy Fiddaman 	bool		vsc_promisc_promisc;	/* PROMISC enabled */
148*5c4a5fe1SAndy Fiddaman 	bool		vsc_promisc_allmulti;	/* ALLMULTI enabled */
149*5c4a5fe1SAndy Fiddaman 	bool		vsc_promisc_umac;	/* unicast MACs sent */
150*5c4a5fe1SAndy Fiddaman 	bool		vsc_promisc_mmac;	/* multicast MACs sent */
151*5c4a5fe1SAndy Fiddaman };
152*5c4a5fe1SAndy Fiddaman 
153*5c4a5fe1SAndy Fiddaman static struct virtio_consts viona_vi_consts = {
154*5c4a5fe1SAndy Fiddaman 	.vc_name		= "viona",
155*5c4a5fe1SAndy Fiddaman 	.vc_nvq			= VIONA_MAXQ,
156*5c4a5fe1SAndy Fiddaman 	/*
157*5c4a5fe1SAndy Fiddaman 	 * We use the common bhyve virtio framework so that we can call
158*5c4a5fe1SAndy Fiddaman 	 * the utility functions to work with the queues handled in userspace.
159*5c4a5fe1SAndy Fiddaman 	 * The framework PCI read/write functions are not used so these
160*5c4a5fe1SAndy Fiddaman 	 * callbacks will not be invoked.
161*5c4a5fe1SAndy Fiddaman 	 */
162*5c4a5fe1SAndy Fiddaman 	.vc_cfgsize		= 0,
163*5c4a5fe1SAndy Fiddaman 	.vc_reset		= NULL,
164*5c4a5fe1SAndy Fiddaman 	.vc_qnotify		= NULL,
165*5c4a5fe1SAndy Fiddaman 	.vc_cfgread		= NULL,
166*5c4a5fe1SAndy Fiddaman 	.vc_cfgwrite		= NULL,
167*5c4a5fe1SAndy Fiddaman 	.vc_apply_features	= NULL,
168*5c4a5fe1SAndy Fiddaman 	/*
169*5c4a5fe1SAndy Fiddaman 	 * The following field is populated using the response from the
170*5c4a5fe1SAndy Fiddaman 	 * viona driver during initialisation, augmented with the additional
171*5c4a5fe1SAndy Fiddaman 	 * capabilities emulated in userspace.
172*5c4a5fe1SAndy Fiddaman 	 */
173*5c4a5fe1SAndy Fiddaman 	.vc_hv_caps		= 0,
174*5c4a5fe1SAndy Fiddaman };
175*5c4a5fe1SAndy Fiddaman 
176*5c4a5fe1SAndy Fiddaman /*
177*5c4a5fe1SAndy Fiddaman  * Return the size of IO BAR that maps virtio header and device specific
178*5c4a5fe1SAndy Fiddaman  * region. The size would vary depending on whether MSI-X is enabled or
179*5c4a5fe1SAndy Fiddaman  * not.
180*5c4a5fe1SAndy Fiddaman  */
181*5c4a5fe1SAndy Fiddaman static uint64_t
pci_viona_iosize(struct pci_devinst * pi)182*5c4a5fe1SAndy Fiddaman pci_viona_iosize(struct pci_devinst *pi)
183*5c4a5fe1SAndy Fiddaman {
184*5c4a5fe1SAndy Fiddaman 	if (pci_msix_enabled(pi)) {
185*5c4a5fe1SAndy Fiddaman 		return (VIONA_REGSZ);
186*5c4a5fe1SAndy Fiddaman 	} else {
187*5c4a5fe1SAndy Fiddaman 		return (VIONA_REGSZ -
188*5c4a5fe1SAndy Fiddaman 		    (VIRTIO_PCI_CONFIG_OFF(1) - VIRTIO_PCI_CONFIG_OFF(0)));
189*5c4a5fe1SAndy Fiddaman 	}
190*5c4a5fe1SAndy Fiddaman }
191*5c4a5fe1SAndy Fiddaman 
192*5c4a5fe1SAndy Fiddaman static uint16_t
pci_viona_qsize(struct pci_viona_softc * sc,int qnum)193*5c4a5fe1SAndy Fiddaman pci_viona_qsize(struct pci_viona_softc *sc, int qnum)
194*5c4a5fe1SAndy Fiddaman {
195*5c4a5fe1SAndy Fiddaman 	if (qnum == VIONA_CTLQ)
196*5c4a5fe1SAndy Fiddaman 		return (VIONA_CTLQ_SIZE);
197*5c4a5fe1SAndy Fiddaman 
198*5c4a5fe1SAndy Fiddaman 	return (sc->vsc_vq_size);
199*5c4a5fe1SAndy Fiddaman }
200*5c4a5fe1SAndy Fiddaman 
201*5c4a5fe1SAndy Fiddaman static void
pci_viona_ring_reset(struct pci_viona_softc * sc,int ring)202*5c4a5fe1SAndy Fiddaman pci_viona_ring_reset(struct pci_viona_softc *sc, int ring)
203*5c4a5fe1SAndy Fiddaman {
204*5c4a5fe1SAndy Fiddaman 	assert(ring < VIONA_MAXQ);
205*5c4a5fe1SAndy Fiddaman 
206*5c4a5fe1SAndy Fiddaman 	switch (ring) {
207*5c4a5fe1SAndy Fiddaman 	case VIONA_RXQ:
208*5c4a5fe1SAndy Fiddaman 	case VIONA_TXQ:
209*5c4a5fe1SAndy Fiddaman 		break;
210*5c4a5fe1SAndy Fiddaman 	case VIONA_CTLQ:
211*5c4a5fe1SAndy Fiddaman 	default:
212*5c4a5fe1SAndy Fiddaman 		return;
213*5c4a5fe1SAndy Fiddaman 	}
214*5c4a5fe1SAndy Fiddaman 
215*5c4a5fe1SAndy Fiddaman 	for (;;) {
216*5c4a5fe1SAndy Fiddaman 		int res;
217*5c4a5fe1SAndy Fiddaman 
218*5c4a5fe1SAndy Fiddaman 		res = ioctl(sc->vsc_vnafd, VNA_IOC_RING_RESET, ring);
219*5c4a5fe1SAndy Fiddaman 		if (res == 0) {
220*5c4a5fe1SAndy Fiddaman 			break;
221*5c4a5fe1SAndy Fiddaman 		} else if (errno != EINTR) {
222*5c4a5fe1SAndy Fiddaman 			WPRINTF("ioctl viona ring %d reset failed %d",
223*5c4a5fe1SAndy Fiddaman 			    ring, errno);
224*5c4a5fe1SAndy Fiddaman 			return;
225*5c4a5fe1SAndy Fiddaman 		}
226*5c4a5fe1SAndy Fiddaman 	}
227*5c4a5fe1SAndy Fiddaman }
228*5c4a5fe1SAndy Fiddaman 
229*5c4a5fe1SAndy Fiddaman static void
pci_viona_update_status(struct pci_viona_softc * sc,uint32_t value)230*5c4a5fe1SAndy Fiddaman pci_viona_update_status(struct pci_viona_softc *sc, uint32_t value)
231*5c4a5fe1SAndy Fiddaman {
232*5c4a5fe1SAndy Fiddaman 
233*5c4a5fe1SAndy Fiddaman 	if (value == 0) {
234*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: device reset requested !");
235*5c4a5fe1SAndy Fiddaman 
236*5c4a5fe1SAndy Fiddaman 		vi_reset_dev(&sc->vsc_vs);
237*5c4a5fe1SAndy Fiddaman 		pci_viona_ring_reset(sc, VIONA_RXQ);
238*5c4a5fe1SAndy Fiddaman 		pci_viona_ring_reset(sc, VIONA_TXQ);
239*5c4a5fe1SAndy Fiddaman 	}
240*5c4a5fe1SAndy Fiddaman 
241*5c4a5fe1SAndy Fiddaman 	sc->vsc_vs.vs_status = value;
242*5c4a5fe1SAndy Fiddaman }
243*5c4a5fe1SAndy Fiddaman 
244*5c4a5fe1SAndy Fiddaman static const char *
pci_viona_promisc_descr(viona_promisc_t mode)245*5c4a5fe1SAndy Fiddaman pci_viona_promisc_descr(viona_promisc_t mode)
246*5c4a5fe1SAndy Fiddaman {
247*5c4a5fe1SAndy Fiddaman 	switch (mode) {
248*5c4a5fe1SAndy Fiddaman 	case VIONA_PROMISC_NONE:
249*5c4a5fe1SAndy Fiddaman 		return ("none");
250*5c4a5fe1SAndy Fiddaman 	case VIONA_PROMISC_MULTI:
251*5c4a5fe1SAndy Fiddaman 		return ("multicast");
252*5c4a5fe1SAndy Fiddaman 	case VIONA_PROMISC_ALL:
253*5c4a5fe1SAndy Fiddaman 		return ("all");
254*5c4a5fe1SAndy Fiddaman 	default:
255*5c4a5fe1SAndy Fiddaman 		abort();
256*5c4a5fe1SAndy Fiddaman 	}
257*5c4a5fe1SAndy Fiddaman }
258*5c4a5fe1SAndy Fiddaman 
259*5c4a5fe1SAndy Fiddaman static int
pci_viona_eval_promisc(struct pci_viona_softc * sc)260*5c4a5fe1SAndy Fiddaman pci_viona_eval_promisc(struct pci_viona_softc *sc)
261*5c4a5fe1SAndy Fiddaman {
262*5c4a5fe1SAndy Fiddaman 	viona_promisc_t mode = VIONA_PROMISC_NONE;
263*5c4a5fe1SAndy Fiddaman 	int err = 0;
264*5c4a5fe1SAndy Fiddaman 
265*5c4a5fe1SAndy Fiddaman 	/*
266*5c4a5fe1SAndy Fiddaman 	 * If the guest has explicitly requested promiscuous mode or has sent a
267*5c4a5fe1SAndy Fiddaman 	 * non-empty unicast MAC address table, then set viona to promiscuous
268*5c4a5fe1SAndy Fiddaman 	 * mode. Otherwise, if the guest has explicitly requested multicast
269*5c4a5fe1SAndy Fiddaman 	 * promiscuity or has sent a non-empty multicast MAC address table,
270*5c4a5fe1SAndy Fiddaman 	 * then set viona to multicast promiscuous mode.
271*5c4a5fe1SAndy Fiddaman 	 */
272*5c4a5fe1SAndy Fiddaman 	if (sc->vsc_promisc_promisc || sc->vsc_promisc_umac)
273*5c4a5fe1SAndy Fiddaman 		mode = VIONA_PROMISC_ALL;
274*5c4a5fe1SAndy Fiddaman 	else if (sc->vsc_promisc_allmulti || sc->vsc_promisc_mmac)
275*5c4a5fe1SAndy Fiddaman 		mode = VIONA_PROMISC_MULTI;
276*5c4a5fe1SAndy Fiddaman 
277*5c4a5fe1SAndy Fiddaman 	if (mode != sc->vsc_promisc) {
278*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: setting promiscuous mode to %d (%s)",
279*5c4a5fe1SAndy Fiddaman 		    mode, pci_viona_promisc_descr(mode));
280*5c4a5fe1SAndy Fiddaman 		DPRINTF("       promisc=%u, umac=%u, allmulti=%u, mmac=%u",
281*5c4a5fe1SAndy Fiddaman 		    sc->vsc_promisc_promisc, sc->vsc_promisc_umac,
282*5c4a5fe1SAndy Fiddaman 		    sc->vsc_promisc_allmulti, sc->vsc_promisc_mmac);
283*5c4a5fe1SAndy Fiddaman 
284*5c4a5fe1SAndy Fiddaman 		err = ioctl(sc->vsc_vnafd, VNA_IOC_SET_PROMISC, mode);
285*5c4a5fe1SAndy Fiddaman 		if (err == 0)
286*5c4a5fe1SAndy Fiddaman 			sc->vsc_promisc = mode;
287*5c4a5fe1SAndy Fiddaman 		else
288*5c4a5fe1SAndy Fiddaman 			WPRINTF("ioctl viona set promisc failed %d", errno);
289*5c4a5fe1SAndy Fiddaman 	}
290*5c4a5fe1SAndy Fiddaman 
291*5c4a5fe1SAndy Fiddaman 	return (err);
292*5c4a5fe1SAndy Fiddaman }
293*5c4a5fe1SAndy Fiddaman 
294*5c4a5fe1SAndy Fiddaman static uint8_t
pci_viona_control_rx(struct vqueue_info * vq,const virtio_net_ctrl_hdr_t * hdr,struct iovec * iov,size_t niov)295*5c4a5fe1SAndy Fiddaman pci_viona_control_rx(struct vqueue_info *vq, const virtio_net_ctrl_hdr_t *hdr,
296*5c4a5fe1SAndy Fiddaman     struct iovec *iov, size_t niov)
297*5c4a5fe1SAndy Fiddaman {
298*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = (struct pci_viona_softc *)vq->vq_vs;
299*5c4a5fe1SAndy Fiddaman 	uint8_t v;
300*5c4a5fe1SAndy Fiddaman 
301*5c4a5fe1SAndy Fiddaman 	if (iov[0].iov_len != sizeof (uint8_t) || niov != 1) {
302*5c4a5fe1SAndy Fiddaman 		EPRINTLN("viona: bad control RX data");
303*5c4a5fe1SAndy Fiddaman 		return (VIRTIO_NET_CQ_ERR);
304*5c4a5fe1SAndy Fiddaman 	}
305*5c4a5fe1SAndy Fiddaman 
306*5c4a5fe1SAndy Fiddaman 	v = *(uint8_t *)iov[0].iov_base;
307*5c4a5fe1SAndy Fiddaman 
308*5c4a5fe1SAndy Fiddaman 	switch (hdr->vnch_command) {
309*5c4a5fe1SAndy Fiddaman 	case VIRTIO_NET_CTRL_RX_PROMISC:
310*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: ctrl RX promisc %d", v);
311*5c4a5fe1SAndy Fiddaman 		sc->vsc_promisc_promisc = (v != 0);
312*5c4a5fe1SAndy Fiddaman 		break;
313*5c4a5fe1SAndy Fiddaman 	case VIRTIO_NET_CTRL_RX_ALLMULTI:
314*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: ctrl RX allmulti %d", v);
315*5c4a5fe1SAndy Fiddaman 		sc->vsc_promisc_allmulti = (v != 0);
316*5c4a5fe1SAndy Fiddaman 		break;
317*5c4a5fe1SAndy Fiddaman 	default:
318*5c4a5fe1SAndy Fiddaman 		/*
319*5c4a5fe1SAndy Fiddaman 		 * VIRTIO_NET_F_CTRL_RX_EXTRA was not offered so no other
320*5c4a5fe1SAndy Fiddaman 		 * commands are expected.
321*5c4a5fe1SAndy Fiddaman 		 */
322*5c4a5fe1SAndy Fiddaman 		EPRINTLN("viona: unrecognised RX control cmd %u",
323*5c4a5fe1SAndy Fiddaman 		    hdr->vnch_command);
324*5c4a5fe1SAndy Fiddaman 		return (VIRTIO_NET_CQ_ERR);
325*5c4a5fe1SAndy Fiddaman 	}
326*5c4a5fe1SAndy Fiddaman 
327*5c4a5fe1SAndy Fiddaman 	if (pci_viona_eval_promisc(sc) == 0)
328*5c4a5fe1SAndy Fiddaman 		return (VIRTIO_NET_CQ_OK);
329*5c4a5fe1SAndy Fiddaman 	return (VIRTIO_NET_CQ_ERR);
330*5c4a5fe1SAndy Fiddaman }
331*5c4a5fe1SAndy Fiddaman 
332*5c4a5fe1SAndy Fiddaman static void
pci_viona_control_mac_dump(const char * tag,const struct iovec * iov)333*5c4a5fe1SAndy Fiddaman pci_viona_control_mac_dump(const char *tag, const struct iovec *iov)
334*5c4a5fe1SAndy Fiddaman {
335*5c4a5fe1SAndy Fiddaman 	virtio_net_ctrl_mac_t *table = (virtio_net_ctrl_mac_t *)iov->iov_base;
336*5c4a5fe1SAndy Fiddaman 	ether_addr_t *mac = &table->vncm_mac;
337*5c4a5fe1SAndy Fiddaman 
338*5c4a5fe1SAndy Fiddaman 	DPRINTF("-- %s MAC TABLE (entries: %u)", tag, table->vncm_entries);
339*5c4a5fe1SAndy Fiddaman 
340*5c4a5fe1SAndy Fiddaman 	if (table->vncm_entries * ETHERADDRL !=
341*5c4a5fe1SAndy Fiddaman 	    iov->iov_len - sizeof (table->vncm_entries)) {
342*5c4a5fe1SAndy Fiddaman 		DPRINTF("   Bad table size %u", iov->iov_len);
343*5c4a5fe1SAndy Fiddaman 		return;
344*5c4a5fe1SAndy Fiddaman 	}
345*5c4a5fe1SAndy Fiddaman 
346*5c4a5fe1SAndy Fiddaman 	for (uint32_t i = 0; i < table->vncm_entries; i++) {
347*5c4a5fe1SAndy Fiddaman 		DPRINTF("   [%2d] %s", i, ether_ntoa((struct ether_addr *)mac));
348*5c4a5fe1SAndy Fiddaman 		mac++;
349*5c4a5fe1SAndy Fiddaman 	}
350*5c4a5fe1SAndy Fiddaman }
351*5c4a5fe1SAndy Fiddaman 
352*5c4a5fe1SAndy Fiddaman static uint8_t
pci_viona_control_mac(struct vqueue_info * vq,const virtio_net_ctrl_hdr_t * hdr,struct iovec * iov,size_t niov)353*5c4a5fe1SAndy Fiddaman pci_viona_control_mac(struct vqueue_info *vq, const virtio_net_ctrl_hdr_t *hdr,
354*5c4a5fe1SAndy Fiddaman     struct iovec *iov, size_t niov)
355*5c4a5fe1SAndy Fiddaman {
356*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = (struct pci_viona_softc *)vq->vq_vs;
357*5c4a5fe1SAndy Fiddaman 
358*5c4a5fe1SAndy Fiddaman 	switch (hdr->vnch_command) {
359*5c4a5fe1SAndy Fiddaman 	case VIRTIO_NET_CTRL_MAC_TABLE_SET: {
360*5c4a5fe1SAndy Fiddaman 		virtio_net_ctrl_mac_t *table;
361*5c4a5fe1SAndy Fiddaman 
362*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: ctrl MAC table set");
363*5c4a5fe1SAndy Fiddaman 
364*5c4a5fe1SAndy Fiddaman 		if (niov != 2) {
365*5c4a5fe1SAndy Fiddaman 			EPRINTLN("viona: bad control MAC data");
366*5c4a5fe1SAndy Fiddaman 			return (VIRTIO_NET_CQ_ERR);
367*5c4a5fe1SAndy Fiddaman 		}
368*5c4a5fe1SAndy Fiddaman 
369*5c4a5fe1SAndy Fiddaman 		/*
370*5c4a5fe1SAndy Fiddaman 		 * We advertise VIRTIO_NET_F_CTRL_RX and therefore need to
371*5c4a5fe1SAndy Fiddaman 		 * accept VIRTIO_NET_CTRL_MAC, but we don't support passing
372*5c4a5fe1SAndy Fiddaman 		 * changes in the MAC address lists down to viona.
373*5c4a5fe1SAndy Fiddaman 		 * Instead, we set flags to indicate if the guest has sent
374*5c4a5fe1SAndy Fiddaman 		 * any MAC addresses for each table, and use these to determine
375*5c4a5fe1SAndy Fiddaman 		 * the resulting promiscuous mode, see pci_viona_eval_promisc()
376*5c4a5fe1SAndy Fiddaman 		 * above.
377*5c4a5fe1SAndy Fiddaman 		 */
378*5c4a5fe1SAndy Fiddaman 
379*5c4a5fe1SAndy Fiddaman 		/* Unicast MAC table */
380*5c4a5fe1SAndy Fiddaman 		table = (virtio_net_ctrl_mac_t *)iov[0].iov_base;
381*5c4a5fe1SAndy Fiddaman 		sc->vsc_promisc_umac = (table->vncm_entries != 0);
382*5c4a5fe1SAndy Fiddaman 		if (pci_viona_debug)
383*5c4a5fe1SAndy Fiddaman 			pci_viona_control_mac_dump("UNICAST", &iov[0]);
384*5c4a5fe1SAndy Fiddaman 
385*5c4a5fe1SAndy Fiddaman 		/* Multicast MAC table */
386*5c4a5fe1SAndy Fiddaman 		table = (virtio_net_ctrl_mac_t *)iov[1].iov_base;
387*5c4a5fe1SAndy Fiddaman 		sc->vsc_promisc_mmac = (table->vncm_entries != 0);
388*5c4a5fe1SAndy Fiddaman 		if (pci_viona_debug)
389*5c4a5fe1SAndy Fiddaman 			pci_viona_control_mac_dump("MULTICAST", &iov[1]);
390*5c4a5fe1SAndy Fiddaman 
391*5c4a5fe1SAndy Fiddaman 		break;
392*5c4a5fe1SAndy Fiddaman 	}
393*5c4a5fe1SAndy Fiddaman 	case VIRTIO_NET_CTRL_MAC_ADDR_SET:
394*5c4a5fe1SAndy Fiddaman 		/* disallow setting the primary filter MAC address */
395*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: ctrl MAC addr set %d", niov);
396*5c4a5fe1SAndy Fiddaman 		return (VIRTIO_NET_CQ_ERR);
397*5c4a5fe1SAndy Fiddaman 	default:
398*5c4a5fe1SAndy Fiddaman 		EPRINTLN("viona: unrecognised MAC control cmd %u",
399*5c4a5fe1SAndy Fiddaman 		    hdr->vnch_command);
400*5c4a5fe1SAndy Fiddaman 		return (VIRTIO_NET_CQ_ERR);
401*5c4a5fe1SAndy Fiddaman 	}
402*5c4a5fe1SAndy Fiddaman 
403*5c4a5fe1SAndy Fiddaman 	if (pci_viona_eval_promisc(sc) == 0)
404*5c4a5fe1SAndy Fiddaman 		return (VIRTIO_NET_CQ_OK);
405*5c4a5fe1SAndy Fiddaman 	return (VIRTIO_NET_CQ_ERR);
406*5c4a5fe1SAndy Fiddaman }
407*5c4a5fe1SAndy Fiddaman 
408*5c4a5fe1SAndy Fiddaman static void
pci_viona_control(struct vqueue_info * vq)409*5c4a5fe1SAndy Fiddaman pci_viona_control(struct vqueue_info *vq)
410*5c4a5fe1SAndy Fiddaman {
411*5c4a5fe1SAndy Fiddaman 	struct iovec iov[VIONA_CTLQ_MAXSEGS + 1];
412*5c4a5fe1SAndy Fiddaman 	const virtio_net_ctrl_hdr_t *hdr;
413*5c4a5fe1SAndy Fiddaman 	struct iovec *siov = iov;
414*5c4a5fe1SAndy Fiddaman 	struct vi_req req = { 0 };
415*5c4a5fe1SAndy Fiddaman 	uint8_t *ackp;
416*5c4a5fe1SAndy Fiddaman 	size_t nsiov;
417*5c4a5fe1SAndy Fiddaman 	uint32_t len;
418*5c4a5fe1SAndy Fiddaman 	int n;
419*5c4a5fe1SAndy Fiddaman 
420*5c4a5fe1SAndy Fiddaman 	n = vq_getchain(vq, iov, VIONA_CTLQ_MAXSEGS, &req);
421*5c4a5fe1SAndy Fiddaman 
422*5c4a5fe1SAndy Fiddaman 	assert(n >= 1 && n <= VIONA_CTLQ_MAXSEGS);
423*5c4a5fe1SAndy Fiddaman 
424*5c4a5fe1SAndy Fiddaman 	/*
425*5c4a5fe1SAndy Fiddaman 	 * Since we have not negotiated VIRTIO_F_ANY_LAYOUT, we expect the
426*5c4a5fe1SAndy Fiddaman 	 * control message to be laid out in at least three descriptors as
427*5c4a5fe1SAndy Fiddaman 	 * follows:
428*5c4a5fe1SAndy Fiddaman 	 *	header		- sizeof (virtio_net_ctrl_hdr_t)
429*5c4a5fe1SAndy Fiddaman 	 *	data[]		- at least one descriptor, varying size
430*5c4a5fe1SAndy Fiddaman 	 *	ack		- uint8_t, flagged as writable
431*5c4a5fe1SAndy Fiddaman 	 * Check the incoming message to make sure it matches this layout and
432*5c4a5fe1SAndy Fiddaman 	 * drop the entire chain if not.
433*5c4a5fe1SAndy Fiddaman 	 */
434*5c4a5fe1SAndy Fiddaman 	if (n < 3 || req.writable != 1 || req.readable + 1 != n ||
435*5c4a5fe1SAndy Fiddaman 	    iov[req.readable].iov_len != sizeof (uint8_t)) {
436*5c4a5fe1SAndy Fiddaman 		EPRINTLN("viona: bad control chain, len=%d, w=%d, r=%d",
437*5c4a5fe1SAndy Fiddaman 		    n, req.writable, req.readable);
438*5c4a5fe1SAndy Fiddaman 		goto drop;
439*5c4a5fe1SAndy Fiddaman 	}
440*5c4a5fe1SAndy Fiddaman 
441*5c4a5fe1SAndy Fiddaman 	hdr = (const virtio_net_ctrl_hdr_t *)iov[0].iov_base;
442*5c4a5fe1SAndy Fiddaman 	if (iov[0].iov_len < sizeof (virtio_net_ctrl_hdr_t)) {
443*5c4a5fe1SAndy Fiddaman 		EPRINTLN("viona: control header too short: %u", iov[0].iov_len);
444*5c4a5fe1SAndy Fiddaman 		goto drop;
445*5c4a5fe1SAndy Fiddaman 	}
446*5c4a5fe1SAndy Fiddaman 
447*5c4a5fe1SAndy Fiddaman 	/*
448*5c4a5fe1SAndy Fiddaman 	 * Writable iovecs start at iov[req.readable], and we've already
449*5c4a5fe1SAndy Fiddaman 	 * checked that there is only one writable, it's at the end, and the
450*5c4a5fe1SAndy Fiddaman 	 * right size; it's the acknowledgement byte.
451*5c4a5fe1SAndy Fiddaman 	 */
452*5c4a5fe1SAndy Fiddaman 	ackp = (uint8_t *)iov[req.readable].iov_base;
453*5c4a5fe1SAndy Fiddaman 
454*5c4a5fe1SAndy Fiddaman 	siov = &iov[1];
455*5c4a5fe1SAndy Fiddaman 	nsiov = n - 2;
456*5c4a5fe1SAndy Fiddaman 
457*5c4a5fe1SAndy Fiddaman 	switch (hdr->vnch_class) {
458*5c4a5fe1SAndy Fiddaman 	case VIRTIO_NET_CTRL_RX:
459*5c4a5fe1SAndy Fiddaman 		*ackp = pci_viona_control_rx(vq, hdr, siov, nsiov);
460*5c4a5fe1SAndy Fiddaman 		break;
461*5c4a5fe1SAndy Fiddaman 	case VIRTIO_NET_CTRL_MAC:
462*5c4a5fe1SAndy Fiddaman 		*ackp = pci_viona_control_mac(vq, hdr, siov, nsiov);
463*5c4a5fe1SAndy Fiddaman 		break;
464*5c4a5fe1SAndy Fiddaman 	default:
465*5c4a5fe1SAndy Fiddaman 		EPRINTLN("viona: unrecognised control class %u, cmd %u",
466*5c4a5fe1SAndy Fiddaman 		    hdr->vnch_class, hdr->vnch_command);
467*5c4a5fe1SAndy Fiddaman 		*ackp = VIRTIO_NET_CQ_ERR;
468*5c4a5fe1SAndy Fiddaman 		break;
469*5c4a5fe1SAndy Fiddaman 	}
470*5c4a5fe1SAndy Fiddaman 
471*5c4a5fe1SAndy Fiddaman drop:
472*5c4a5fe1SAndy Fiddaman 	len = 0;
473*5c4a5fe1SAndy Fiddaman 	for (uint_t i = 0; i < n; i++)
474*5c4a5fe1SAndy Fiddaman 		len += iov[i].iov_len;
475*5c4a5fe1SAndy Fiddaman 
476*5c4a5fe1SAndy Fiddaman 	vq_relchain(vq, req.idx, len);
477*5c4a5fe1SAndy Fiddaman }
478*5c4a5fe1SAndy Fiddaman 
479*5c4a5fe1SAndy Fiddaman static void
pci_viona_process_ctrlq(struct vqueue_info * vq)480*5c4a5fe1SAndy Fiddaman pci_viona_process_ctrlq(struct vqueue_info *vq)
481*5c4a5fe1SAndy Fiddaman {
482*5c4a5fe1SAndy Fiddaman 	for (;;) {
483*5c4a5fe1SAndy Fiddaman 		vq_kick_disable(vq);
484*5c4a5fe1SAndy Fiddaman 
485*5c4a5fe1SAndy Fiddaman 		while (vq_has_descs(vq))
486*5c4a5fe1SAndy Fiddaman 			pci_viona_control(vq);
487*5c4a5fe1SAndy Fiddaman 
488*5c4a5fe1SAndy Fiddaman 		vq_kick_enable(vq);
489*5c4a5fe1SAndy Fiddaman 
490*5c4a5fe1SAndy Fiddaman 		/*
491*5c4a5fe1SAndy Fiddaman 		 * One more check in case a late addition raced with
492*5c4a5fe1SAndy Fiddaman 		 * re-enabling kicks. Note that vq_kick_enable() includes a
493*5c4a5fe1SAndy Fiddaman 		 * memory barrier.
494*5c4a5fe1SAndy Fiddaman 		 */
495*5c4a5fe1SAndy Fiddaman 
496*5c4a5fe1SAndy Fiddaman 		if (!vq_has_descs(vq))
497*5c4a5fe1SAndy Fiddaman 			break;
498*5c4a5fe1SAndy Fiddaman 	}
499*5c4a5fe1SAndy Fiddaman 
500*5c4a5fe1SAndy Fiddaman 	vq_endchains(vq, /* used_all_avail= */1);
501*5c4a5fe1SAndy Fiddaman }
502*5c4a5fe1SAndy Fiddaman 
503*5c4a5fe1SAndy Fiddaman static void *
pci_viona_poll_thread(void * param)504*5c4a5fe1SAndy Fiddaman pci_viona_poll_thread(void *param)
505*5c4a5fe1SAndy Fiddaman {
506*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = param;
507*5c4a5fe1SAndy Fiddaman 	pollfd_t pollset;
508*5c4a5fe1SAndy Fiddaman 	const int fd = sc->vsc_vnafd;
509*5c4a5fe1SAndy Fiddaman 
510*5c4a5fe1SAndy Fiddaman 	pollset.fd = fd;
511*5c4a5fe1SAndy Fiddaman 	pollset.events = POLLRDBAND;
512*5c4a5fe1SAndy Fiddaman 
513*5c4a5fe1SAndy Fiddaman 	for (;;) {
514*5c4a5fe1SAndy Fiddaman 		if (poll(&pollset, 1, -1) < 0) {
515*5c4a5fe1SAndy Fiddaman 			if (errno == EINTR || errno == EAGAIN) {
516*5c4a5fe1SAndy Fiddaman 				continue;
517*5c4a5fe1SAndy Fiddaman 			} else {
518*5c4a5fe1SAndy Fiddaman 				WPRINTF("pci_viona_poll_thread poll() error %d",
519*5c4a5fe1SAndy Fiddaman 				    errno);
520*5c4a5fe1SAndy Fiddaman 				break;
521*5c4a5fe1SAndy Fiddaman 			}
522*5c4a5fe1SAndy Fiddaman 		}
523*5c4a5fe1SAndy Fiddaman 		if (pollset.revents & POLLRDBAND) {
524*5c4a5fe1SAndy Fiddaman 			vioc_intr_poll_t vip;
525*5c4a5fe1SAndy Fiddaman 			uint_t i;
526*5c4a5fe1SAndy Fiddaman 			int res;
527*5c4a5fe1SAndy Fiddaman 			bool assert_lintr = false;
528*5c4a5fe1SAndy Fiddaman 			const bool do_msix = pci_msix_enabled(sc->vsc_vs.vs_pi);
529*5c4a5fe1SAndy Fiddaman 
530*5c4a5fe1SAndy Fiddaman 			res = ioctl(fd, VNA_IOC_INTR_POLL, &vip);
531*5c4a5fe1SAndy Fiddaman 			for (i = 0; res > 0 && i < VIONA_VQ_MAX; i++) {
532*5c4a5fe1SAndy Fiddaman 				if (vip.vip_status[i] == 0) {
533*5c4a5fe1SAndy Fiddaman 					continue;
534*5c4a5fe1SAndy Fiddaman 				}
535*5c4a5fe1SAndy Fiddaman 				if (do_msix) {
536*5c4a5fe1SAndy Fiddaman 					pci_generate_msix(sc->vsc_vs.vs_pi,
537*5c4a5fe1SAndy Fiddaman 					    sc->vsc_queues[i].vq_msix_idx);
538*5c4a5fe1SAndy Fiddaman 				} else {
539*5c4a5fe1SAndy Fiddaman 					assert_lintr = true;
540*5c4a5fe1SAndy Fiddaman 				}
541*5c4a5fe1SAndy Fiddaman 				res = ioctl(fd, VNA_IOC_RING_INTR_CLR, i);
542*5c4a5fe1SAndy Fiddaman 				if (res != 0) {
543*5c4a5fe1SAndy Fiddaman 					WPRINTF("ioctl viona vq %d intr "
544*5c4a5fe1SAndy Fiddaman 					    "clear failed %d", i, errno);
545*5c4a5fe1SAndy Fiddaman 				}
546*5c4a5fe1SAndy Fiddaman 			}
547*5c4a5fe1SAndy Fiddaman 			if (assert_lintr) {
548*5c4a5fe1SAndy Fiddaman 				pthread_mutex_lock(&sc->vsc_mtx);
549*5c4a5fe1SAndy Fiddaman 				sc->vsc_vs.vs_isr |= VIRTIO_PCI_ISR_INTR;
550*5c4a5fe1SAndy Fiddaman 				pci_lintr_assert(sc->vsc_vs.vs_pi);
551*5c4a5fe1SAndy Fiddaman 				pthread_mutex_unlock(&sc->vsc_mtx);
552*5c4a5fe1SAndy Fiddaman 			}
553*5c4a5fe1SAndy Fiddaman 		}
554*5c4a5fe1SAndy Fiddaman 	}
555*5c4a5fe1SAndy Fiddaman 
556*5c4a5fe1SAndy Fiddaman 	pthread_exit(NULL);
557*5c4a5fe1SAndy Fiddaman }
558*5c4a5fe1SAndy Fiddaman 
559*5c4a5fe1SAndy Fiddaman static void
pci_viona_ring_init(struct pci_viona_softc * sc,uint64_t pfn)560*5c4a5fe1SAndy Fiddaman pci_viona_ring_init(struct pci_viona_softc *sc, uint64_t pfn)
561*5c4a5fe1SAndy Fiddaman {
562*5c4a5fe1SAndy Fiddaman 	int			qnum = sc->vsc_vs.vs_curq;
563*5c4a5fe1SAndy Fiddaman 	vioc_ring_init_t	vna_ri;
564*5c4a5fe1SAndy Fiddaman 	int			error;
565*5c4a5fe1SAndy Fiddaman 
566*5c4a5fe1SAndy Fiddaman 	assert(qnum < VIONA_MAXQ);
567*5c4a5fe1SAndy Fiddaman 
568*5c4a5fe1SAndy Fiddaman 	if (qnum == VIONA_CTLQ) {
569*5c4a5fe1SAndy Fiddaman 		vi_vq_init(&sc->vsc_vs, pfn);
570*5c4a5fe1SAndy Fiddaman 		return;
571*5c4a5fe1SAndy Fiddaman 	}
572*5c4a5fe1SAndy Fiddaman 
573*5c4a5fe1SAndy Fiddaman 	sc->vsc_queues[qnum].vq_pfn = (pfn << VRING_PFN);
574*5c4a5fe1SAndy Fiddaman 	vna_ri.ri_index = qnum;
575*5c4a5fe1SAndy Fiddaman 	vna_ri.ri_qsize = pci_viona_qsize(sc, qnum);
576*5c4a5fe1SAndy Fiddaman 	vna_ri.ri_qaddr = (pfn << VRING_PFN);
577*5c4a5fe1SAndy Fiddaman 	error = ioctl(sc->vsc_vnafd, VNA_IOC_RING_INIT, &vna_ri);
578*5c4a5fe1SAndy Fiddaman 
579*5c4a5fe1SAndy Fiddaman 	if (error != 0) {
580*5c4a5fe1SAndy Fiddaman 		WPRINTF("ioctl viona ring %u init failed %d", qnum, errno);
581*5c4a5fe1SAndy Fiddaman 	}
582*5c4a5fe1SAndy Fiddaman }
583*5c4a5fe1SAndy Fiddaman 
584*5c4a5fe1SAndy Fiddaman static int
pci_viona_viona_init(struct vmctx * ctx,struct pci_viona_softc * sc)585*5c4a5fe1SAndy Fiddaman pci_viona_viona_init(struct vmctx *ctx, struct pci_viona_softc *sc)
586*5c4a5fe1SAndy Fiddaman {
587*5c4a5fe1SAndy Fiddaman 	vioc_create_t		vna_create;
588*5c4a5fe1SAndy Fiddaman 	int			error;
589*5c4a5fe1SAndy Fiddaman 
590*5c4a5fe1SAndy Fiddaman 	sc->vsc_vnafd = open("/dev/viona", O_RDWR | O_EXCL);
591*5c4a5fe1SAndy Fiddaman 	if (sc->vsc_vnafd == -1) {
592*5c4a5fe1SAndy Fiddaman 		WPRINTF("open viona ctl failed: %d", errno);
593*5c4a5fe1SAndy Fiddaman 		return (-1);
594*5c4a5fe1SAndy Fiddaman 	}
595*5c4a5fe1SAndy Fiddaman 
596*5c4a5fe1SAndy Fiddaman 	vna_create.c_linkid = sc->vsc_linkid;
597*5c4a5fe1SAndy Fiddaman 	vna_create.c_vmfd = vm_get_device_fd(ctx);
598*5c4a5fe1SAndy Fiddaman 	error = ioctl(sc->vsc_vnafd, VNA_IOC_CREATE, &vna_create);
599*5c4a5fe1SAndy Fiddaman 	if (error != 0) {
600*5c4a5fe1SAndy Fiddaman 		(void) close(sc->vsc_vnafd);
601*5c4a5fe1SAndy Fiddaman 		WPRINTF("ioctl viona create failed %d", errno);
602*5c4a5fe1SAndy Fiddaman 		return (-1);
603*5c4a5fe1SAndy Fiddaman 	}
604*5c4a5fe1SAndy Fiddaman 
605*5c4a5fe1SAndy Fiddaman 	return (0);
606*5c4a5fe1SAndy Fiddaman }
607*5c4a5fe1SAndy Fiddaman 
608*5c4a5fe1SAndy Fiddaman static int
pci_viona_legacy_config(nvlist_t * nvl,const char * opt)609*5c4a5fe1SAndy Fiddaman pci_viona_legacy_config(nvlist_t *nvl, const char *opt)
610*5c4a5fe1SAndy Fiddaman {
611*5c4a5fe1SAndy Fiddaman 	char *config, *name, *tofree, *value;
612*5c4a5fe1SAndy Fiddaman 
613*5c4a5fe1SAndy Fiddaman 	if (opt == NULL)
614*5c4a5fe1SAndy Fiddaman 		return (0);
615*5c4a5fe1SAndy Fiddaman 
616*5c4a5fe1SAndy Fiddaman 	config = tofree = strdup(opt);
617*5c4a5fe1SAndy Fiddaman 	while ((name = strsep(&config, ",")) != NULL) {
618*5c4a5fe1SAndy Fiddaman 		value = strchr(name, '=');
619*5c4a5fe1SAndy Fiddaman 		if (value != NULL) {
620*5c4a5fe1SAndy Fiddaman 			*value++ = '\0';
621*5c4a5fe1SAndy Fiddaman 			set_config_value_node(nvl, name, value);
622*5c4a5fe1SAndy Fiddaman 		} else {
623*5c4a5fe1SAndy Fiddaman 			set_config_value_node(nvl, "vnic", name);
624*5c4a5fe1SAndy Fiddaman 		}
625*5c4a5fe1SAndy Fiddaman 	}
626*5c4a5fe1SAndy Fiddaman 	free(tofree);
627*5c4a5fe1SAndy Fiddaman 	return (0);
628*5c4a5fe1SAndy Fiddaman }
629*5c4a5fe1SAndy Fiddaman 
630*5c4a5fe1SAndy Fiddaman static int
pci_viona_parse_opts(struct pci_viona_softc * sc,nvlist_t * nvl)631*5c4a5fe1SAndy Fiddaman pci_viona_parse_opts(struct pci_viona_softc *sc, nvlist_t *nvl)
632*5c4a5fe1SAndy Fiddaman {
633*5c4a5fe1SAndy Fiddaman 	const char *value;
634*5c4a5fe1SAndy Fiddaman 	int err = 0;
635*5c4a5fe1SAndy Fiddaman 
636*5c4a5fe1SAndy Fiddaman 	sc->vsc_vq_size = VIONA_RINGSZ;
637*5c4a5fe1SAndy Fiddaman 	sc->vsc_feature_mask = 0;
638*5c4a5fe1SAndy Fiddaman 	sc->vsc_linkname[0] = '\0';
639*5c4a5fe1SAndy Fiddaman 
640*5c4a5fe1SAndy Fiddaman 	value = get_config_value_node(nvl, "feature_mask");
641*5c4a5fe1SAndy Fiddaman 	if (value != NULL) {
642*5c4a5fe1SAndy Fiddaman 		long num;
643*5c4a5fe1SAndy Fiddaman 
644*5c4a5fe1SAndy Fiddaman 		errno = 0;
645*5c4a5fe1SAndy Fiddaman 		num = strtol(value, NULL, 0);
646*5c4a5fe1SAndy Fiddaman 		if (errno != 0 || num < 0) {
647*5c4a5fe1SAndy Fiddaman 			fprintf(stderr,
648*5c4a5fe1SAndy Fiddaman 			    "viona: invalid mask '%s'", value);
649*5c4a5fe1SAndy Fiddaman 		} else {
650*5c4a5fe1SAndy Fiddaman 			sc->vsc_feature_mask = num;
651*5c4a5fe1SAndy Fiddaman 		}
652*5c4a5fe1SAndy Fiddaman 	}
653*5c4a5fe1SAndy Fiddaman 
654*5c4a5fe1SAndy Fiddaman 	value = get_config_value_node(nvl, "vqsize");
655*5c4a5fe1SAndy Fiddaman 	if (value != NULL) {
656*5c4a5fe1SAndy Fiddaman 		long num;
657*5c4a5fe1SAndy Fiddaman 
658*5c4a5fe1SAndy Fiddaman 		errno = 0;
659*5c4a5fe1SAndy Fiddaman 		num = strtol(value, NULL, 0);
660*5c4a5fe1SAndy Fiddaman 		if (errno != 0) {
661*5c4a5fe1SAndy Fiddaman 			fprintf(stderr,
662*5c4a5fe1SAndy Fiddaman 			    "viona: invalid vsqize '%s'", value);
663*5c4a5fe1SAndy Fiddaman 			err = -1;
664*5c4a5fe1SAndy Fiddaman 		} else if (num <= 2 || num > 32768) {
665*5c4a5fe1SAndy Fiddaman 			fprintf(stderr,
666*5c4a5fe1SAndy Fiddaman 			    "viona: vqsize out of range", num);
667*5c4a5fe1SAndy Fiddaman 			err = -1;
668*5c4a5fe1SAndy Fiddaman 		} else if ((1 << (ffs(num) - 1)) != num) {
669*5c4a5fe1SAndy Fiddaman 			fprintf(stderr,
670*5c4a5fe1SAndy Fiddaman 			    "viona: vqsize must be power of 2", num);
671*5c4a5fe1SAndy Fiddaman 			err = -1;
672*5c4a5fe1SAndy Fiddaman 		} else {
673*5c4a5fe1SAndy Fiddaman 			sc->vsc_vq_size = num;
674*5c4a5fe1SAndy Fiddaman 		}
675*5c4a5fe1SAndy Fiddaman 	}
676*5c4a5fe1SAndy Fiddaman 
677*5c4a5fe1SAndy Fiddaman 	value = get_config_value_node(nvl, "vnic");
678*5c4a5fe1SAndy Fiddaman 	if (value == NULL) {
679*5c4a5fe1SAndy Fiddaman 		fprintf(stderr, "viona: vnic name required");
680*5c4a5fe1SAndy Fiddaman 		err = -1;
681*5c4a5fe1SAndy Fiddaman 	} else {
682*5c4a5fe1SAndy Fiddaman 		(void) strlcpy(sc->vsc_linkname, value, MAXLINKNAMELEN);
683*5c4a5fe1SAndy Fiddaman 	}
684*5c4a5fe1SAndy Fiddaman 
685*5c4a5fe1SAndy Fiddaman 	DPRINTF("viona=%p dev=%s vqsize=%x feature_mask=%x", sc,
686*5c4a5fe1SAndy Fiddaman 	    sc->vsc_linkname, sc->vsc_vq_size, sc->vsc_feature_mask);
687*5c4a5fe1SAndy Fiddaman 	return (err);
688*5c4a5fe1SAndy Fiddaman }
689*5c4a5fe1SAndy Fiddaman 
690*5c4a5fe1SAndy Fiddaman static int
pci_viona_init(struct pci_devinst * pi,nvlist_t * nvl)691*5c4a5fe1SAndy Fiddaman pci_viona_init(struct pci_devinst *pi, nvlist_t *nvl)
692*5c4a5fe1SAndy Fiddaman {
693*5c4a5fe1SAndy Fiddaman 	dladm_handle_t		handle;
694*5c4a5fe1SAndy Fiddaman 	dladm_status_t		status;
695*5c4a5fe1SAndy Fiddaman 	dladm_vnic_attr_t	attr;
696*5c4a5fe1SAndy Fiddaman 	char			errmsg[DLADM_STRSIZE];
697*5c4a5fe1SAndy Fiddaman 	char			tname[MAXCOMLEN + 1];
698*5c4a5fe1SAndy Fiddaman 	int error, i;
699*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc;
700*5c4a5fe1SAndy Fiddaman 	const char *vnic;
701*5c4a5fe1SAndy Fiddaman 	pthread_t tid;
702*5c4a5fe1SAndy Fiddaman 
703*5c4a5fe1SAndy Fiddaman 	if (get_config_bool_default("viona.debug", false))
704*5c4a5fe1SAndy Fiddaman 		pci_viona_debug = 1;
705*5c4a5fe1SAndy Fiddaman 
706*5c4a5fe1SAndy Fiddaman 	vnic = get_config_value_node(nvl, "vnic");
707*5c4a5fe1SAndy Fiddaman 	if (vnic == NULL) {
708*5c4a5fe1SAndy Fiddaman 		WPRINTF("virtio-viona: vnic required");
709*5c4a5fe1SAndy Fiddaman 		return (1);
710*5c4a5fe1SAndy Fiddaman 	}
711*5c4a5fe1SAndy Fiddaman 
712*5c4a5fe1SAndy Fiddaman 	sc = malloc(sizeof (struct pci_viona_softc));
713*5c4a5fe1SAndy Fiddaman 	memset(sc, 0, sizeof (struct pci_viona_softc));
714*5c4a5fe1SAndy Fiddaman 
715*5c4a5fe1SAndy Fiddaman 	if (pci_viona_parse_opts(sc, nvl) != 0) {
716*5c4a5fe1SAndy Fiddaman 		free(sc);
717*5c4a5fe1SAndy Fiddaman 		return (1);
718*5c4a5fe1SAndy Fiddaman 	}
719*5c4a5fe1SAndy Fiddaman 
720*5c4a5fe1SAndy Fiddaman 	if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
721*5c4a5fe1SAndy Fiddaman 		WPRINTF("could not open /dev/dld");
722*5c4a5fe1SAndy Fiddaman 		free(sc);
723*5c4a5fe1SAndy Fiddaman 		return (1);
724*5c4a5fe1SAndy Fiddaman 	}
725*5c4a5fe1SAndy Fiddaman 
726*5c4a5fe1SAndy Fiddaman 	if ((status = dladm_name2info(handle, sc->vsc_linkname, &sc->vsc_linkid,
727*5c4a5fe1SAndy Fiddaman 	    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
728*5c4a5fe1SAndy Fiddaman 		WPRINTF("dladm_name2info() for %s failed: %s", vnic,
729*5c4a5fe1SAndy Fiddaman 		    dladm_status2str(status, errmsg));
730*5c4a5fe1SAndy Fiddaman 		dladm_close(handle);
731*5c4a5fe1SAndy Fiddaman 		free(sc);
732*5c4a5fe1SAndy Fiddaman 		return (1);
733*5c4a5fe1SAndy Fiddaman 	}
734*5c4a5fe1SAndy Fiddaman 
735*5c4a5fe1SAndy Fiddaman 	if ((status = dladm_vnic_info(handle, sc->vsc_linkid, &attr,
736*5c4a5fe1SAndy Fiddaman 	    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
737*5c4a5fe1SAndy Fiddaman 		WPRINTF("dladm_vnic_info() for %s failed: %s", vnic,
738*5c4a5fe1SAndy Fiddaman 		    dladm_status2str(status, errmsg));
739*5c4a5fe1SAndy Fiddaman 		dladm_close(handle);
740*5c4a5fe1SAndy Fiddaman 		free(sc);
741*5c4a5fe1SAndy Fiddaman 		return (1);
742*5c4a5fe1SAndy Fiddaman 	}
743*5c4a5fe1SAndy Fiddaman 
744*5c4a5fe1SAndy Fiddaman 	memcpy(sc->vsc_macaddr, attr.va_mac_addr, ETHERADDRL);
745*5c4a5fe1SAndy Fiddaman 
746*5c4a5fe1SAndy Fiddaman 	dladm_close(handle);
747*5c4a5fe1SAndy Fiddaman 
748*5c4a5fe1SAndy Fiddaman 	error = pci_viona_viona_init(pi->pi_vmctx, sc);
749*5c4a5fe1SAndy Fiddaman 	if (error != 0) {
750*5c4a5fe1SAndy Fiddaman 		free(sc);
751*5c4a5fe1SAndy Fiddaman 		return (1);
752*5c4a5fe1SAndy Fiddaman 	}
753*5c4a5fe1SAndy Fiddaman 
754*5c4a5fe1SAndy Fiddaman 	error = pthread_create(&tid, NULL, pci_viona_poll_thread, sc);
755*5c4a5fe1SAndy Fiddaman 	assert(error == 0);
756*5c4a5fe1SAndy Fiddaman 	snprintf(tname, sizeof (tname), "vionapoll:%s", vnic);
757*5c4a5fe1SAndy Fiddaman 	pthread_set_name_np(tid, tname);
758*5c4a5fe1SAndy Fiddaman 
759*5c4a5fe1SAndy Fiddaman 	/* initialize config space */
760*5c4a5fe1SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET);
761*5c4a5fe1SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
762*5c4a5fe1SAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK);
763*5c4a5fe1SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_NETWORK);
764*5c4a5fe1SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
765*5c4a5fe1SAndy Fiddaman 
766*5c4a5fe1SAndy Fiddaman 	sc->vsc_consts = viona_vi_consts;
767*5c4a5fe1SAndy Fiddaman 	pthread_mutex_init(&sc->vsc_mtx, NULL);
768*5c4a5fe1SAndy Fiddaman 
769*5c4a5fe1SAndy Fiddaman 	/*
770*5c4a5fe1SAndy Fiddaman 	 * The RX and TX queues are handled in the kernel component of
771*5c4a5fe1SAndy Fiddaman 	 * viona; however The control queue is emulated in userspace.
772*5c4a5fe1SAndy Fiddaman 	 */
773*5c4a5fe1SAndy Fiddaman 	sc->vsc_queues[VIONA_CTLQ].vq_qsize = pci_viona_qsize(sc, VIONA_CTLQ);
774*5c4a5fe1SAndy Fiddaman 
775*5c4a5fe1SAndy Fiddaman 	vi_softc_linkup(&sc->vsc_vs, &sc->vsc_consts, sc, pi, sc->vsc_queues);
776*5c4a5fe1SAndy Fiddaman 	sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
777*5c4a5fe1SAndy Fiddaman 
778*5c4a5fe1SAndy Fiddaman 	/*
779*5c4a5fe1SAndy Fiddaman 	 * Guests that do not support CTRL_RX_MAC still generally need to
780*5c4a5fe1SAndy Fiddaman 	 * receive multicast packets. Guests that do support this feature will
781*5c4a5fe1SAndy Fiddaman 	 * end up setting this flag indirectly via messages on the control
782*5c4a5fe1SAndy Fiddaman 	 * queue but it does not hurt to default to multicast promiscuity here
783*5c4a5fe1SAndy Fiddaman 	 * and it is what older version of viona did.
784*5c4a5fe1SAndy Fiddaman 	 */
785*5c4a5fe1SAndy Fiddaman 	sc->vsc_promisc_mmac = true;
786*5c4a5fe1SAndy Fiddaman 	pci_viona_eval_promisc(sc);
787*5c4a5fe1SAndy Fiddaman 
788*5c4a5fe1SAndy Fiddaman 	/* MSI-X support */
789*5c4a5fe1SAndy Fiddaman 	for (i = 0; i < VIONA_MAXQ; i++)
790*5c4a5fe1SAndy Fiddaman 		sc->vsc_queues[i].vq_msix_idx = VIRTIO_MSI_NO_VECTOR;
791*5c4a5fe1SAndy Fiddaman 
792*5c4a5fe1SAndy Fiddaman 	/* BAR 1 used to map MSI-X table and PBA */
793*5c4a5fe1SAndy Fiddaman 	if (pci_emul_add_msixcap(pi, VIONA_MAXQ, 1)) {
794*5c4a5fe1SAndy Fiddaman 		free(sc);
795*5c4a5fe1SAndy Fiddaman 		return (1);
796*5c4a5fe1SAndy Fiddaman 	}
797*5c4a5fe1SAndy Fiddaman 
798*5c4a5fe1SAndy Fiddaman 	/* BAR 0 for legacy-style virtio register access. */
799*5c4a5fe1SAndy Fiddaman 	error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, VIONA_REGSZ);
800*5c4a5fe1SAndy Fiddaman 	if (error != 0) {
801*5c4a5fe1SAndy Fiddaman 		WPRINTF("could not allocate virtio BAR");
802*5c4a5fe1SAndy Fiddaman 		free(sc);
803*5c4a5fe1SAndy Fiddaman 		return (1);
804*5c4a5fe1SAndy Fiddaman 	}
805*5c4a5fe1SAndy Fiddaman 
806*5c4a5fe1SAndy Fiddaman 	/*
807*5c4a5fe1SAndy Fiddaman 	 * Need a legacy interrupt for virtio compliance, even though MSI-X
808*5c4a5fe1SAndy Fiddaman 	 * operation is _strongly_ suggested for adequate performance.
809*5c4a5fe1SAndy Fiddaman 	 */
810*5c4a5fe1SAndy Fiddaman 	pci_lintr_request(pi);
811*5c4a5fe1SAndy Fiddaman 
812*5c4a5fe1SAndy Fiddaman 	return (0);
813*5c4a5fe1SAndy Fiddaman }
814*5c4a5fe1SAndy Fiddaman 
815*5c4a5fe1SAndy Fiddaman static uint64_t
viona_adjust_offset(struct pci_devinst * pi,uint64_t offset)816*5c4a5fe1SAndy Fiddaman viona_adjust_offset(struct pci_devinst *pi, uint64_t offset)
817*5c4a5fe1SAndy Fiddaman {
818*5c4a5fe1SAndy Fiddaman 	/*
819*5c4a5fe1SAndy Fiddaman 	 * Device specific offsets used by guest would change based on
820*5c4a5fe1SAndy Fiddaman 	 * whether MSI-X capability is enabled or not
821*5c4a5fe1SAndy Fiddaman 	 */
822*5c4a5fe1SAndy Fiddaman 	if (!pci_msix_enabled(pi)) {
823*5c4a5fe1SAndy Fiddaman 		if (offset >= VIRTIO_PCI_CONFIG_OFF(0)) {
824*5c4a5fe1SAndy Fiddaman 			return (offset + (VIRTIO_PCI_CONFIG_OFF(1) -
825*5c4a5fe1SAndy Fiddaman 			    VIRTIO_PCI_CONFIG_OFF(0)));
826*5c4a5fe1SAndy Fiddaman 		}
827*5c4a5fe1SAndy Fiddaman 	}
828*5c4a5fe1SAndy Fiddaman 
829*5c4a5fe1SAndy Fiddaman 	return (offset);
830*5c4a5fe1SAndy Fiddaman }
831*5c4a5fe1SAndy Fiddaman 
832*5c4a5fe1SAndy Fiddaman static void
pci_viona_ring_set_msix(struct pci_devinst * pi,uint_t ring)833*5c4a5fe1SAndy Fiddaman pci_viona_ring_set_msix(struct pci_devinst *pi, uint_t ring)
834*5c4a5fe1SAndy Fiddaman {
835*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = pi->pi_arg;
836*5c4a5fe1SAndy Fiddaman 	struct msix_table_entry mte;
837*5c4a5fe1SAndy Fiddaman 	uint16_t tab_index;
838*5c4a5fe1SAndy Fiddaman 	vioc_ring_msi_t vrm;
839*5c4a5fe1SAndy Fiddaman 	int res;
840*5c4a5fe1SAndy Fiddaman 
841*5c4a5fe1SAndy Fiddaman 	if (ring == VIONA_CTLQ)
842*5c4a5fe1SAndy Fiddaman 		return;
843*5c4a5fe1SAndy Fiddaman 
844*5c4a5fe1SAndy Fiddaman 	assert(ring <= VIONA_VQ_TX);
845*5c4a5fe1SAndy Fiddaman 
846*5c4a5fe1SAndy Fiddaman 	vrm.rm_index = ring;
847*5c4a5fe1SAndy Fiddaman 	vrm.rm_addr = 0;
848*5c4a5fe1SAndy Fiddaman 	vrm.rm_msg = 0;
849*5c4a5fe1SAndy Fiddaman 	tab_index = sc->vsc_queues[ring].vq_msix_idx;
850*5c4a5fe1SAndy Fiddaman 
851*5c4a5fe1SAndy Fiddaman 	if (tab_index != VIRTIO_MSI_NO_VECTOR && sc->vsc_msix_active) {
852*5c4a5fe1SAndy Fiddaman 		mte = pi->pi_msix.table[tab_index];
853*5c4a5fe1SAndy Fiddaman 		if ((mte.vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
854*5c4a5fe1SAndy Fiddaman 			vrm.rm_addr = mte.addr;
855*5c4a5fe1SAndy Fiddaman 			vrm.rm_msg = mte.msg_data;
856*5c4a5fe1SAndy Fiddaman 		}
857*5c4a5fe1SAndy Fiddaman 	}
858*5c4a5fe1SAndy Fiddaman 
859*5c4a5fe1SAndy Fiddaman 	res = ioctl(sc->vsc_vnafd, VNA_IOC_RING_SET_MSI, &vrm);
860*5c4a5fe1SAndy Fiddaman 	if (res != 0) {
861*5c4a5fe1SAndy Fiddaman 		WPRINTF("ioctl viona set_msi %d failed %d", ring, errno);
862*5c4a5fe1SAndy Fiddaman 	}
863*5c4a5fe1SAndy Fiddaman }
864*5c4a5fe1SAndy Fiddaman 
865*5c4a5fe1SAndy Fiddaman static void
pci_viona_lintrupdate(struct pci_devinst * pi)866*5c4a5fe1SAndy Fiddaman pci_viona_lintrupdate(struct pci_devinst *pi)
867*5c4a5fe1SAndy Fiddaman {
868*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = pi->pi_arg;
869*5c4a5fe1SAndy Fiddaman 	bool msix_on = false;
870*5c4a5fe1SAndy Fiddaman 
871*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&sc->vsc_mtx);
872*5c4a5fe1SAndy Fiddaman 	msix_on = pci_msix_enabled(pi) && (pi->pi_msix.function_mask == 0);
873*5c4a5fe1SAndy Fiddaman 	if ((sc->vsc_msix_active && !msix_on) ||
874*5c4a5fe1SAndy Fiddaman 	    (msix_on && !sc->vsc_msix_active)) {
875*5c4a5fe1SAndy Fiddaman 		uint_t i;
876*5c4a5fe1SAndy Fiddaman 
877*5c4a5fe1SAndy Fiddaman 		sc->vsc_msix_active = msix_on;
878*5c4a5fe1SAndy Fiddaman 		/* Update in-kernel ring configs */
879*5c4a5fe1SAndy Fiddaman 		for (i = 0; i <= VIONA_VQ_TX; i++) {
880*5c4a5fe1SAndy Fiddaman 			pci_viona_ring_set_msix(pi, i);
881*5c4a5fe1SAndy Fiddaman 		}
882*5c4a5fe1SAndy Fiddaman 	}
883*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&sc->vsc_mtx);
884*5c4a5fe1SAndy Fiddaman }
885*5c4a5fe1SAndy Fiddaman 
886*5c4a5fe1SAndy Fiddaman static void
pci_viona_msix_update(struct pci_devinst * pi,uint64_t offset)887*5c4a5fe1SAndy Fiddaman pci_viona_msix_update(struct pci_devinst *pi, uint64_t offset)
888*5c4a5fe1SAndy Fiddaman {
889*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = pi->pi_arg;
890*5c4a5fe1SAndy Fiddaman 	uint_t tab_index, i;
891*5c4a5fe1SAndy Fiddaman 
892*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&sc->vsc_mtx);
893*5c4a5fe1SAndy Fiddaman 	if (!sc->vsc_msix_active) {
894*5c4a5fe1SAndy Fiddaman 		pthread_mutex_unlock(&sc->vsc_mtx);
895*5c4a5fe1SAndy Fiddaman 		return;
896*5c4a5fe1SAndy Fiddaman 	}
897*5c4a5fe1SAndy Fiddaman 
898*5c4a5fe1SAndy Fiddaman 	/*
899*5c4a5fe1SAndy Fiddaman 	 * Rather than update every possible MSI-X vector, cheat and use the
900*5c4a5fe1SAndy Fiddaman 	 * offset to calculate the entry within the table.  Since this should
901*5c4a5fe1SAndy Fiddaman 	 * only be called when a write to the table succeeds, the index should
902*5c4a5fe1SAndy Fiddaman 	 * be valid.
903*5c4a5fe1SAndy Fiddaman 	 */
904*5c4a5fe1SAndy Fiddaman 	tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
905*5c4a5fe1SAndy Fiddaman 
906*5c4a5fe1SAndy Fiddaman 	for (i = 0; i <= VIONA_VQ_TX; i++) {
907*5c4a5fe1SAndy Fiddaman 		if (sc->vsc_queues[i].vq_msix_idx != tab_index) {
908*5c4a5fe1SAndy Fiddaman 			continue;
909*5c4a5fe1SAndy Fiddaman 		}
910*5c4a5fe1SAndy Fiddaman 		pci_viona_ring_set_msix(pi, i);
911*5c4a5fe1SAndy Fiddaman 	}
912*5c4a5fe1SAndy Fiddaman 
913*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&sc->vsc_mtx);
914*5c4a5fe1SAndy Fiddaman }
915*5c4a5fe1SAndy Fiddaman 
916*5c4a5fe1SAndy Fiddaman static void
pci_viona_qnotify(struct pci_viona_softc * sc,int ring)917*5c4a5fe1SAndy Fiddaman pci_viona_qnotify(struct pci_viona_softc *sc, int ring)
918*5c4a5fe1SAndy Fiddaman {
919*5c4a5fe1SAndy Fiddaman 	int error;
920*5c4a5fe1SAndy Fiddaman 
921*5c4a5fe1SAndy Fiddaman 	switch (ring) {
922*5c4a5fe1SAndy Fiddaman 	case VIONA_TXQ:
923*5c4a5fe1SAndy Fiddaman 	case VIONA_RXQ:
924*5c4a5fe1SAndy Fiddaman 		error = ioctl(sc->vsc_vnafd, VNA_IOC_RING_KICK, ring);
925*5c4a5fe1SAndy Fiddaman 		if (error != 0) {
926*5c4a5fe1SAndy Fiddaman 			WPRINTF("ioctl viona ring %d kick failed %d",
927*5c4a5fe1SAndy Fiddaman 			    ring, errno);
928*5c4a5fe1SAndy Fiddaman 		}
929*5c4a5fe1SAndy Fiddaman 		break;
930*5c4a5fe1SAndy Fiddaman 	case VIONA_CTLQ: {
931*5c4a5fe1SAndy Fiddaman 		struct vqueue_info *vq = &sc->vsc_queues[VIONA_CTLQ];
932*5c4a5fe1SAndy Fiddaman 
933*5c4a5fe1SAndy Fiddaman 		if (vq_has_descs(vq))
934*5c4a5fe1SAndy Fiddaman 			pci_viona_process_ctrlq(vq);
935*5c4a5fe1SAndy Fiddaman 		break;
936*5c4a5fe1SAndy Fiddaman 	}
937*5c4a5fe1SAndy Fiddaman 	}
938*5c4a5fe1SAndy Fiddaman }
939*5c4a5fe1SAndy Fiddaman 
940*5c4a5fe1SAndy Fiddaman static void
pci_viona_baraddr(struct pci_devinst * pi,int baridx,int enabled,uint64_t address)941*5c4a5fe1SAndy Fiddaman pci_viona_baraddr(struct pci_devinst *pi, int baridx, int enabled,
942*5c4a5fe1SAndy Fiddaman     uint64_t address)
943*5c4a5fe1SAndy Fiddaman {
944*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = pi->pi_arg;
945*5c4a5fe1SAndy Fiddaman 	uint64_t ioport;
946*5c4a5fe1SAndy Fiddaman 	int error;
947*5c4a5fe1SAndy Fiddaman 
948*5c4a5fe1SAndy Fiddaman 	if (baridx != 0)
949*5c4a5fe1SAndy Fiddaman 		return;
950*5c4a5fe1SAndy Fiddaman 
951*5c4a5fe1SAndy Fiddaman 	if (enabled == 0) {
952*5c4a5fe1SAndy Fiddaman 		error = ioctl(sc->vsc_vnafd, VNA_IOC_SET_NOTIFY_IOP, 0);
953*5c4a5fe1SAndy Fiddaman 		if (error != 0)
954*5c4a5fe1SAndy Fiddaman 			WPRINTF("uninstall ioport hook failed %d", errno);
955*5c4a5fe1SAndy Fiddaman 		return;
956*5c4a5fe1SAndy Fiddaman 	}
957*5c4a5fe1SAndy Fiddaman 
958*5c4a5fe1SAndy Fiddaman 	/*
959*5c4a5fe1SAndy Fiddaman 	 * Install ioport hook for virtqueue notification.
960*5c4a5fe1SAndy Fiddaman 	 * This is part of the virtio common configuration area so the
961*5c4a5fe1SAndy Fiddaman 	 * address does not change with MSI-X status.
962*5c4a5fe1SAndy Fiddaman 	 */
963*5c4a5fe1SAndy Fiddaman 	ioport = address + VIRTIO_PCI_QUEUE_NOTIFY;
964*5c4a5fe1SAndy Fiddaman 	error = ioctl(sc->vsc_vnafd, VNA_IOC_SET_NOTIFY_IOP, ioport);
965*5c4a5fe1SAndy Fiddaman 	if (error != 0) {
966*5c4a5fe1SAndy Fiddaman 		WPRINTF("install ioport hook at %x failed %d",
967*5c4a5fe1SAndy Fiddaman 		    ioport, errno);
968*5c4a5fe1SAndy Fiddaman 	}
969*5c4a5fe1SAndy Fiddaman }
970*5c4a5fe1SAndy Fiddaman 
971*5c4a5fe1SAndy Fiddaman static void
pci_viona_write(struct pci_devinst * pi,int baridx,uint64_t offset,int size,uint64_t value)972*5c4a5fe1SAndy Fiddaman pci_viona_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
973*5c4a5fe1SAndy Fiddaman     uint64_t value)
974*5c4a5fe1SAndy Fiddaman {
975*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = pi->pi_arg;
976*5c4a5fe1SAndy Fiddaman 	void *ptr;
977*5c4a5fe1SAndy Fiddaman 	int err = 0;
978*5c4a5fe1SAndy Fiddaman 
979*5c4a5fe1SAndy Fiddaman 	if (baridx == pci_msix_table_bar(pi) ||
980*5c4a5fe1SAndy Fiddaman 	    baridx == pci_msix_pba_bar(pi)) {
981*5c4a5fe1SAndy Fiddaman 		if (pci_emul_msix_twrite(pi, offset, size, value) == 0) {
982*5c4a5fe1SAndy Fiddaman 			pci_viona_msix_update(pi, offset);
983*5c4a5fe1SAndy Fiddaman 		}
984*5c4a5fe1SAndy Fiddaman 		return;
985*5c4a5fe1SAndy Fiddaman 	}
986*5c4a5fe1SAndy Fiddaman 
987*5c4a5fe1SAndy Fiddaman 	assert(baridx == 0);
988*5c4a5fe1SAndy Fiddaman 
989*5c4a5fe1SAndy Fiddaman 	if (offset + size > pci_viona_iosize(pi)) {
990*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona_write: 2big, offset %ld size %d",
991*5c4a5fe1SAndy Fiddaman 		    offset, size);
992*5c4a5fe1SAndy Fiddaman 		return;
993*5c4a5fe1SAndy Fiddaman 	}
994*5c4a5fe1SAndy Fiddaman 
995*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&sc->vsc_mtx);
996*5c4a5fe1SAndy Fiddaman 
997*5c4a5fe1SAndy Fiddaman 	offset = viona_adjust_offset(pi, offset);
998*5c4a5fe1SAndy Fiddaman 
999*5c4a5fe1SAndy Fiddaman 	switch (offset) {
1000*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_GUEST_FEATURES:
1001*5c4a5fe1SAndy Fiddaman 		assert(size == 4);
1002*5c4a5fe1SAndy Fiddaman 		value &= ~(sc->vsc_feature_mask);
1003*5c4a5fe1SAndy Fiddaman 		err = ioctl(sc->vsc_vnafd, VNA_IOC_SET_FEATURES, &value);
1004*5c4a5fe1SAndy Fiddaman 		if (err != 0) {
1005*5c4a5fe1SAndy Fiddaman 			WPRINTF("ioctl feature negotiation returned err = %d",
1006*5c4a5fe1SAndy Fiddaman 			    errno);
1007*5c4a5fe1SAndy Fiddaman 		} else {
1008*5c4a5fe1SAndy Fiddaman 			sc->vsc_vs.vs_negotiated_caps = value;
1009*5c4a5fe1SAndy Fiddaman 		}
1010*5c4a5fe1SAndy Fiddaman 		break;
1011*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_PFN:
1012*5c4a5fe1SAndy Fiddaman 		assert(size == 4);
1013*5c4a5fe1SAndy Fiddaman 		pci_viona_ring_init(sc, value);
1014*5c4a5fe1SAndy Fiddaman 		break;
1015*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_SEL:
1016*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1017*5c4a5fe1SAndy Fiddaman 		assert(value < VIONA_MAXQ);
1018*5c4a5fe1SAndy Fiddaman 		sc->vsc_vs.vs_curq = value;
1019*5c4a5fe1SAndy Fiddaman 		break;
1020*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_NOTIFY:
1021*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1022*5c4a5fe1SAndy Fiddaman 		assert(value < VIONA_MAXQ);
1023*5c4a5fe1SAndy Fiddaman 		pci_viona_qnotify(sc, value);
1024*5c4a5fe1SAndy Fiddaman 		break;
1025*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_STATUS:
1026*5c4a5fe1SAndy Fiddaman 		assert(size == 1);
1027*5c4a5fe1SAndy Fiddaman 		pci_viona_update_status(sc, value);
1028*5c4a5fe1SAndy Fiddaman 		break;
1029*5c4a5fe1SAndy Fiddaman 	case VIRTIO_MSI_CONFIG_VECTOR:
1030*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1031*5c4a5fe1SAndy Fiddaman 		sc->vsc_vs.vs_msix_cfg_idx = value;
1032*5c4a5fe1SAndy Fiddaman 		break;
1033*5c4a5fe1SAndy Fiddaman 	case VIRTIO_MSI_QUEUE_VECTOR:
1034*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1035*5c4a5fe1SAndy Fiddaman 		assert(sc->vsc_vs.vs_curq < VIONA_MAXQ);
1036*5c4a5fe1SAndy Fiddaman 		sc->vsc_queues[sc->vsc_vs.vs_curq].vq_msix_idx = value;
1037*5c4a5fe1SAndy Fiddaman 		pci_viona_ring_set_msix(pi, sc->vsc_vs.vs_curq);
1038*5c4a5fe1SAndy Fiddaman 		break;
1039*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG0:
1040*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG1:
1041*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG2:
1042*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG3:
1043*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG4:
1044*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG5:
1045*5c4a5fe1SAndy Fiddaman 		assert((size + offset) <= (VIONA_R_CFG5 + 1));
1046*5c4a5fe1SAndy Fiddaman 		ptr = &sc->vsc_macaddr[offset - VIONA_R_CFG0];
1047*5c4a5fe1SAndy Fiddaman 		/*
1048*5c4a5fe1SAndy Fiddaman 		 * The driver is allowed to change the MAC address
1049*5c4a5fe1SAndy Fiddaman 		 */
1050*5c4a5fe1SAndy Fiddaman 		sc->vsc_macaddr[offset - VIONA_R_CFG0] = value;
1051*5c4a5fe1SAndy Fiddaman 		if (size == 1) {
1052*5c4a5fe1SAndy Fiddaman 			*(uint8_t *)ptr = value;
1053*5c4a5fe1SAndy Fiddaman 		} else if (size == 2) {
1054*5c4a5fe1SAndy Fiddaman 			*(uint16_t *)ptr = value;
1055*5c4a5fe1SAndy Fiddaman 		} else {
1056*5c4a5fe1SAndy Fiddaman 			*(uint32_t *)ptr = value;
1057*5c4a5fe1SAndy Fiddaman 		}
1058*5c4a5fe1SAndy Fiddaman 		break;
1059*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_HOST_FEATURES:
1060*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_NUM:
1061*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_ISR:
1062*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG6:
1063*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG7:
1064*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: write to readonly reg %ld", offset);
1065*5c4a5fe1SAndy Fiddaman 		break;
1066*5c4a5fe1SAndy Fiddaman 	default:
1067*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: unknown i/o write offset %ld", offset);
1068*5c4a5fe1SAndy Fiddaman 		value = 0;
1069*5c4a5fe1SAndy Fiddaman 		break;
1070*5c4a5fe1SAndy Fiddaman 	}
1071*5c4a5fe1SAndy Fiddaman 
1072*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&sc->vsc_mtx);
1073*5c4a5fe1SAndy Fiddaman }
1074*5c4a5fe1SAndy Fiddaman 
1075*5c4a5fe1SAndy Fiddaman static uint64_t
pci_viona_read(struct pci_devinst * pi,int baridx,uint64_t offset,int size)1076*5c4a5fe1SAndy Fiddaman pci_viona_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
1077*5c4a5fe1SAndy Fiddaman {
1078*5c4a5fe1SAndy Fiddaman 	struct pci_viona_softc *sc = pi->pi_arg;
1079*5c4a5fe1SAndy Fiddaman 	void *ptr;
1080*5c4a5fe1SAndy Fiddaman 	uint64_t value;
1081*5c4a5fe1SAndy Fiddaman 	int err = 0;
1082*5c4a5fe1SAndy Fiddaman 
1083*5c4a5fe1SAndy Fiddaman 	if (baridx == pci_msix_table_bar(pi) ||
1084*5c4a5fe1SAndy Fiddaman 	    baridx == pci_msix_pba_bar(pi)) {
1085*5c4a5fe1SAndy Fiddaman 		return (pci_emul_msix_tread(pi, offset, size));
1086*5c4a5fe1SAndy Fiddaman 	}
1087*5c4a5fe1SAndy Fiddaman 
1088*5c4a5fe1SAndy Fiddaman 	assert(baridx == 0);
1089*5c4a5fe1SAndy Fiddaman 
1090*5c4a5fe1SAndy Fiddaman 	if (offset + size > pci_viona_iosize(pi)) {
1091*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona_read: 2big, offset %ld size %d",
1092*5c4a5fe1SAndy Fiddaman 		    offset, size);
1093*5c4a5fe1SAndy Fiddaman 		return (0);
1094*5c4a5fe1SAndy Fiddaman 	}
1095*5c4a5fe1SAndy Fiddaman 
1096*5c4a5fe1SAndy Fiddaman 	pthread_mutex_lock(&sc->vsc_mtx);
1097*5c4a5fe1SAndy Fiddaman 
1098*5c4a5fe1SAndy Fiddaman 	offset = viona_adjust_offset(pi, offset);
1099*5c4a5fe1SAndy Fiddaman 
1100*5c4a5fe1SAndy Fiddaman 	switch (offset) {
1101*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_HOST_FEATURES:
1102*5c4a5fe1SAndy Fiddaman 		assert(size == 4);
1103*5c4a5fe1SAndy Fiddaman 		err = ioctl(sc->vsc_vnafd, VNA_IOC_GET_FEATURES, &value);
1104*5c4a5fe1SAndy Fiddaman 		if (err != 0) {
1105*5c4a5fe1SAndy Fiddaman 			WPRINTF("ioctl get host features returned err = %d",
1106*5c4a5fe1SAndy Fiddaman 			    errno);
1107*5c4a5fe1SAndy Fiddaman 		}
1108*5c4a5fe1SAndy Fiddaman 		value |= VIONA_S_HOSTCAPS_USERSPACE;
1109*5c4a5fe1SAndy Fiddaman 		value &= ~sc->vsc_feature_mask;
1110*5c4a5fe1SAndy Fiddaman 		sc->vsc_consts.vc_hv_caps = value;
1111*5c4a5fe1SAndy Fiddaman 		break;
1112*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_GUEST_FEATURES:
1113*5c4a5fe1SAndy Fiddaman 		assert(size == 4);
1114*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_vs.vs_negotiated_caps; /* XXX never read ? */
1115*5c4a5fe1SAndy Fiddaman 		break;
1116*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_PFN:
1117*5c4a5fe1SAndy Fiddaman 		assert(size == 4);
1118*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_queues[sc->vsc_vs.vs_curq].vq_pfn >> VRING_PFN;
1119*5c4a5fe1SAndy Fiddaman 		break;
1120*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_NUM:
1121*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1122*5c4a5fe1SAndy Fiddaman 		value = pci_viona_qsize(sc, sc->vsc_vs.vs_curq);
1123*5c4a5fe1SAndy Fiddaman 		break;
1124*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_SEL:
1125*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1126*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_vs.vs_curq;  /* XXX never read ? */
1127*5c4a5fe1SAndy Fiddaman 		break;
1128*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_QUEUE_NOTIFY:
1129*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1130*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_vs.vs_curq;  /* XXX never read ? */
1131*5c4a5fe1SAndy Fiddaman 		break;
1132*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_STATUS:
1133*5c4a5fe1SAndy Fiddaman 		assert(size == 1);
1134*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_vs.vs_status;
1135*5c4a5fe1SAndy Fiddaman 		break;
1136*5c4a5fe1SAndy Fiddaman 	case VIRTIO_PCI_ISR:
1137*5c4a5fe1SAndy Fiddaman 		assert(size == 1);
1138*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_vs.vs_isr;
1139*5c4a5fe1SAndy Fiddaman 		sc->vsc_vs.vs_isr = 0;	/* a read clears this flag */
1140*5c4a5fe1SAndy Fiddaman 		if (value != 0) {
1141*5c4a5fe1SAndy Fiddaman 			pci_lintr_deassert(pi);
1142*5c4a5fe1SAndy Fiddaman 		}
1143*5c4a5fe1SAndy Fiddaman 		break;
1144*5c4a5fe1SAndy Fiddaman 	case VIRTIO_MSI_CONFIG_VECTOR:
1145*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1146*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_vs.vs_msix_cfg_idx;
1147*5c4a5fe1SAndy Fiddaman 		break;
1148*5c4a5fe1SAndy Fiddaman 	case VIRTIO_MSI_QUEUE_VECTOR:
1149*5c4a5fe1SAndy Fiddaman 		assert(size == 2);
1150*5c4a5fe1SAndy Fiddaman 		assert(sc->vsc_vs.vs_curq < VIONA_MAXQ);
1151*5c4a5fe1SAndy Fiddaman 		value = sc->vsc_queues[sc->vsc_vs.vs_curq].vq_msix_idx;
1152*5c4a5fe1SAndy Fiddaman 		break;
1153*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG0:
1154*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG1:
1155*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG2:
1156*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG3:
1157*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG4:
1158*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG5:
1159*5c4a5fe1SAndy Fiddaman 		assert((size + offset) <= (VIONA_R_CFG5 + 1));
1160*5c4a5fe1SAndy Fiddaman 		ptr = &sc->vsc_macaddr[offset - VIONA_R_CFG0];
1161*5c4a5fe1SAndy Fiddaman 		if (size == 1) {
1162*5c4a5fe1SAndy Fiddaman 			value = *(uint8_t *)ptr;
1163*5c4a5fe1SAndy Fiddaman 		} else if (size == 2) {
1164*5c4a5fe1SAndy Fiddaman 			value = *(uint16_t *)ptr;
1165*5c4a5fe1SAndy Fiddaman 		} else {
1166*5c4a5fe1SAndy Fiddaman 			value = *(uint32_t *)ptr;
1167*5c4a5fe1SAndy Fiddaman 		}
1168*5c4a5fe1SAndy Fiddaman 		break;
1169*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG6:
1170*5c4a5fe1SAndy Fiddaman 		assert(size != 4);
1171*5c4a5fe1SAndy Fiddaman 		value = 0x01;	/* XXX link always up */
1172*5c4a5fe1SAndy Fiddaman 		break;
1173*5c4a5fe1SAndy Fiddaman 	case VIONA_R_CFG7:
1174*5c4a5fe1SAndy Fiddaman 		assert(size == 1);
1175*5c4a5fe1SAndy Fiddaman 		value = 0;	/* XXX link status in LSB */
1176*5c4a5fe1SAndy Fiddaman 		break;
1177*5c4a5fe1SAndy Fiddaman 	default:
1178*5c4a5fe1SAndy Fiddaman 		DPRINTF("viona: unknown i/o read offset %ld", offset);
1179*5c4a5fe1SAndy Fiddaman 		value = 0;
1180*5c4a5fe1SAndy Fiddaman 		break;
1181*5c4a5fe1SAndy Fiddaman 	}
1182*5c4a5fe1SAndy Fiddaman 
1183*5c4a5fe1SAndy Fiddaman 	pthread_mutex_unlock(&sc->vsc_mtx);
1184*5c4a5fe1SAndy Fiddaman 
1185*5c4a5fe1SAndy Fiddaman 	return (value);
1186*5c4a5fe1SAndy Fiddaman }
1187*5c4a5fe1SAndy Fiddaman 
1188*5c4a5fe1SAndy Fiddaman struct pci_devemu pci_de_viona = {
1189*5c4a5fe1SAndy Fiddaman 	.pe_emu =	"virtio-net-viona",
1190*5c4a5fe1SAndy Fiddaman 	.pe_init =	pci_viona_init,
1191*5c4a5fe1SAndy Fiddaman 	.pe_legacy_config = pci_viona_legacy_config,
1192*5c4a5fe1SAndy Fiddaman 	.pe_barwrite =	pci_viona_write,
1193*5c4a5fe1SAndy Fiddaman 	.pe_barread =	pci_viona_read,
1194*5c4a5fe1SAndy Fiddaman 	.pe_baraddr =	pci_viona_baraddr,
1195*5c4a5fe1SAndy Fiddaman 	.pe_lintrupdate = pci_viona_lintrupdate
1196*5c4a5fe1SAndy Fiddaman };
1197*5c4a5fe1SAndy Fiddaman PCI_EMUL_SET(pci_de_viona);
1198