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