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