pci_virtio_net.c revision 250086
1221828Sgrehan/*-
2221828Sgrehan * Copyright (c) 2011 NetApp, Inc.
3221828Sgrehan * All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24221828Sgrehan * SUCH DAMAGE.
25221828Sgrehan *
26221828Sgrehan * $FreeBSD: head/usr.sbin/bhyve/pci_virtio_net.c 250086 2013-04-30 01:14:54Z neel $
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_net.c 250086 2013-04-30 01:14:54Z neel $");
31221828Sgrehan
32221828Sgrehan#include <sys/param.h>
33221828Sgrehan#include <sys/linker_set.h>
34221828Sgrehan#include <sys/select.h>
35221828Sgrehan#include <sys/uio.h>
36221828Sgrehan#include <sys/ioctl.h>
37221828Sgrehan
38221828Sgrehan#include <errno.h>
39221828Sgrehan#include <fcntl.h>
40221828Sgrehan#include <stdio.h>
41221828Sgrehan#include <stdlib.h>
42221828Sgrehan#include <stdint.h>
43221828Sgrehan#include <string.h>
44221828Sgrehan#include <strings.h>
45221828Sgrehan#include <unistd.h>
46221828Sgrehan#include <assert.h>
47221828Sgrehan#include <md5.h>
48221828Sgrehan#include <pthread.h>
49249917Sgrehan#include <pthread_np.h>
50221828Sgrehan
51244167Sgrehan#include "bhyverun.h"
52221828Sgrehan#include "pci_emul.h"
53221828Sgrehan#include "mevent.h"
54221828Sgrehan#include "virtio.h"
55221828Sgrehan
56249917Sgrehan#define VTNET_RINGSZ	1024
57221828Sgrehan
58221828Sgrehan#define VTNET_MAXSEGS	32
59221828Sgrehan
60221828Sgrehan/*
61221828Sgrehan * PCI config-space register offsets
62221828Sgrehan */
63246109Sneel#define VTNET_R_CFG0	24
64246109Sneel#define VTNET_R_CFG1	25
65246109Sneel#define VTNET_R_CFG2	26
66246109Sneel#define VTNET_R_CFG3	27
67246109Sneel#define VTNET_R_CFG4	28
68246109Sneel#define VTNET_R_CFG5	29
69246109Sneel#define VTNET_R_CFG6	30
70246109Sneel#define VTNET_R_CFG7	31
71246109Sneel#define VTNET_R_MAX	31
72221828Sgrehan
73246109Sneel#define VTNET_REGSZ	VTNET_R_MAX+1
74221828Sgrehan
75221828Sgrehan/*
76221828Sgrehan * Host capabilities
77221828Sgrehan */
78221828Sgrehan#define VTNET_S_HOSTCAPS      \
79221828Sgrehan  ( 0x00000020 |	/* host supplies MAC */ \
80221828Sgrehan    0x00008000 |	/* host can merge Rx buffers */ \
81221828Sgrehan    0x00010000 )	/* config status available */
82221828Sgrehan
83221828Sgrehan/*
84221828Sgrehan * Queue definitions.
85221828Sgrehan */
86221828Sgrehan#define VTNET_RXQ	0
87221828Sgrehan#define VTNET_TXQ	1
88221828Sgrehan#define VTNET_CTLQ	2
89221828Sgrehan
90221828Sgrehan#define VTNET_MAXQ	3
91221828Sgrehan
92246109Sneelstatic int use_msix = 1;
93246109Sneel
94221828Sgrehanstruct vring_hqueue {
95221828Sgrehan	/* Internal state */
96221828Sgrehan	uint16_t	hq_size;
97221828Sgrehan	uint16_t	hq_cur_aidx;		/* trails behind 'avail_idx' */
98221828Sgrehan
99221828Sgrehan	 /* Host-context pointers to the queue */
100221828Sgrehan	struct virtio_desc *hq_dtable;
101221828Sgrehan	uint16_t	*hq_avail_flags;
102221828Sgrehan	uint16_t	*hq_avail_idx;		/* monotonically increasing */
103221828Sgrehan	uint16_t	*hq_avail_ring;
104221828Sgrehan
105221828Sgrehan	uint16_t	*hq_used_flags;
106221828Sgrehan	uint16_t	*hq_used_idx;		/* monotonically increasing */
107221828Sgrehan	struct virtio_used *hq_used_ring;
108221828Sgrehan};
109221828Sgrehan
110221828Sgrehan/*
111221828Sgrehan * Fixed network header size
112221828Sgrehan */
113221828Sgrehanstruct virtio_net_rxhdr {
114221828Sgrehan	uint8_t		vrh_flags;
115221828Sgrehan	uint8_t		vrh_gso_type;
116221828Sgrehan	uint16_t	vrh_hdr_len;
117221828Sgrehan	uint16_t	vrh_gso_size;
118221828Sgrehan	uint16_t	vrh_csum_start;
119221828Sgrehan	uint16_t	vrh_csum_offset;
120221828Sgrehan	uint16_t	vrh_bufs;
121221828Sgrehan} __packed;
122221828Sgrehan
123221828Sgrehan/*
124221828Sgrehan * Debug printf
125221828Sgrehan */
126221828Sgrehanstatic int pci_vtnet_debug;
127221828Sgrehan#define DPRINTF(params) if (pci_vtnet_debug) printf params
128221828Sgrehan#define WPRINTF(params) printf params
129221828Sgrehan
130221828Sgrehan/*
131221828Sgrehan * Per-device softc
132221828Sgrehan */
133221828Sgrehanstruct pci_vtnet_softc {
134221828Sgrehan	struct pci_devinst *vsc_pi;
135221828Sgrehan	pthread_mutex_t vsc_mtx;
136221828Sgrehan	struct mevent	*vsc_mevp;
137221828Sgrehan
138221828Sgrehan	int		vsc_curq;
139221828Sgrehan	int		vsc_status;
140221828Sgrehan	int		vsc_isr;
141221828Sgrehan	int		vsc_tapfd;
142221828Sgrehan	int		vsc_rx_ready;
143249917Sgrehan	int		resetting;
144221828Sgrehan
145221828Sgrehan	uint32_t	vsc_features;
146221828Sgrehan	uint8_t		vsc_macaddr[6];
147221828Sgrehan
148221828Sgrehan	uint64_t	vsc_pfn[VTNET_MAXQ];
149221828Sgrehan	struct	vring_hqueue vsc_hq[VTNET_MAXQ];
150246109Sneel	uint16_t	vsc_msix_table_idx[VTNET_MAXQ];
151250083Sneel
152250083Sneel	pthread_mutex_t	rx_mtx;
153250083Sneel	int		rx_in_progress;
154250083Sneel
155249917Sgrehan	pthread_t 	tx_tid;
156249917Sgrehan	pthread_mutex_t	tx_mtx;
157249917Sgrehan	pthread_cond_t	tx_cond;
158250083Sneel	int		tx_in_progress;
159221828Sgrehan};
160248477Sneel#define	vtnet_ctx(sc)	((sc)->vsc_pi->pi_vmctx)
161221828Sgrehan
162221828Sgrehan/*
163246109Sneel * Return the size of IO BAR that maps virtio header and device specific
164246109Sneel * region. The size would vary depending on whether MSI-X is enabled or
165246109Sneel * not.
166246109Sneel */
167246109Sneelstatic uint64_t
168246109Sneelpci_vtnet_iosize(struct pci_devinst *pi)
169246109Sneel{
170246109Sneel	if (pci_msix_enabled(pi))
171246109Sneel		return (VTNET_REGSZ);
172246109Sneel	else
173246109Sneel		return (VTNET_REGSZ - (VTCFG_R_CFG1 - VTCFG_R_MSIX));
174246109Sneel}
175246109Sneel
176246109Sneel/*
177221828Sgrehan * Return the number of available descriptors in the vring taking care
178221828Sgrehan * of the 16-bit index wraparound.
179221828Sgrehan */
180221828Sgrehanstatic int
181221828Sgrehanhq_num_avail(struct vring_hqueue *hq)
182221828Sgrehan{
183248368Sneel	uint16_t ndesc;
184221828Sgrehan
185247871Sgrehan	/*
186249917Sgrehan	 * We're just computing (a-b) mod 2^16
187247871Sgrehan	 *
188247871Sgrehan	 * The only glitch here is that in standard C,
189247871Sgrehan	 * uint16_t promotes to (signed) int when int has
190247871Sgrehan	 * more than 16 bits (pretty much always now), so
191247871Sgrehan	 * we have to force it back to unsigned.
192247871Sgrehan	 */
193247871Sgrehan	ndesc = (unsigned)*hq->hq_avail_idx - (unsigned)hq->hq_cur_aidx;
194221828Sgrehan
195247871Sgrehan	assert(ndesc <= hq->hq_size);
196221828Sgrehan
197221828Sgrehan	return (ndesc);
198221828Sgrehan}
199221828Sgrehan
200221828Sgrehanstatic uint16_t
201221828Sgrehanpci_vtnet_qsize(int qnum)
202221828Sgrehan{
203221828Sgrehan	/* XXX no ctl queue currently */
204221828Sgrehan	if (qnum == VTNET_CTLQ) {
205221828Sgrehan		return (0);
206221828Sgrehan	}
207221828Sgrehan
208221828Sgrehan	/* XXX fixed currently. Maybe different for tx/rx/ctl */
209221828Sgrehan	return (VTNET_RINGSZ);
210221828Sgrehan}
211221828Sgrehan
212221828Sgrehanstatic void
213244160Sgrehanpci_vtnet_ring_reset(struct pci_vtnet_softc *sc, int ring)
214244160Sgrehan{
215244160Sgrehan	struct vring_hqueue *hq;
216244160Sgrehan
217244160Sgrehan	assert(ring < VTNET_MAXQ);
218244160Sgrehan
219244160Sgrehan	hq = &sc->vsc_hq[ring];
220244160Sgrehan
221244160Sgrehan	/*
222244160Sgrehan	 * Reset all soft state
223244160Sgrehan	 */
224244160Sgrehan	hq->hq_cur_aidx = 0;
225244160Sgrehan}
226244160Sgrehan
227250083Sneel/*
228250083Sneel * If the transmit thread is active then stall until it is done.
229250083Sneel */
230244160Sgrehanstatic void
231250083Sneelpci_vtnet_txwait(struct pci_vtnet_softc *sc)
232250083Sneel{
233250083Sneel
234250083Sneel	pthread_mutex_lock(&sc->tx_mtx);
235250083Sneel	while (sc->tx_in_progress) {
236250083Sneel		pthread_mutex_unlock(&sc->tx_mtx);
237250083Sneel		usleep(10000);
238250083Sneel		pthread_mutex_lock(&sc->tx_mtx);
239250083Sneel	}
240250083Sneel	pthread_mutex_unlock(&sc->tx_mtx);
241250083Sneel}
242250083Sneel
243250083Sneel/*
244250083Sneel * If the receive thread is active then stall until it is done.
245250083Sneel */
246250083Sneelstatic void
247250083Sneelpci_vtnet_rxwait(struct pci_vtnet_softc *sc)
248250083Sneel{
249250083Sneel
250250083Sneel	pthread_mutex_lock(&sc->rx_mtx);
251250083Sneel	while (sc->rx_in_progress) {
252250083Sneel		pthread_mutex_unlock(&sc->rx_mtx);
253250083Sneel		usleep(10000);
254250083Sneel		pthread_mutex_lock(&sc->rx_mtx);
255250083Sneel	}
256250083Sneel	pthread_mutex_unlock(&sc->rx_mtx);
257250083Sneel}
258250083Sneel
259250083Sneelstatic void
260221828Sgrehanpci_vtnet_update_status(struct pci_vtnet_softc *sc, uint32_t value)
261221828Sgrehan{
262250086Sneel	int i;
263244160Sgrehan
264221828Sgrehan	if (value == 0) {
265221828Sgrehan		DPRINTF(("vtnet: device reset requested !\n"));
266249917Sgrehan
267249917Sgrehan		sc->resetting = 1;
268249917Sgrehan
269250083Sneel		/*
270250083Sneel		 * Wait for the transmit and receive threads to finish their
271250083Sneel		 * processing.
272250083Sneel		 */
273250083Sneel		pci_vtnet_txwait(sc);
274250083Sneel		pci_vtnet_rxwait(sc);
275250083Sneel
276250083Sneel		sc->vsc_rx_ready = 0;
277244160Sgrehan		pci_vtnet_ring_reset(sc, VTNET_RXQ);
278244160Sgrehan		pci_vtnet_ring_reset(sc, VTNET_TXQ);
279250083Sneel
280250086Sneel		for (i = 0; i < VTNET_MAXQ; i++)
281250086Sneel			sc->vsc_msix_table_idx[i] = VIRTIO_MSI_NO_VECTOR;
282250086Sneel
283250086Sneel		sc->vsc_isr = 0;
284250086Sneel		sc->vsc_features = 0;
285250086Sneel
286249917Sgrehan		sc->resetting = 0;
287221828Sgrehan	}
288221828Sgrehan
289221828Sgrehan	sc->vsc_status = value;
290221828Sgrehan}
291221828Sgrehan
292221828Sgrehan/*
293221828Sgrehan * Called to send a buffer chain out to the tap device
294221828Sgrehan */
295221828Sgrehanstatic void
296221828Sgrehanpci_vtnet_tap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt,
297221828Sgrehan		 int len)
298221828Sgrehan{
299221828Sgrehan	char pad[60];
300221828Sgrehan
301221828Sgrehan	if (sc->vsc_tapfd == -1)
302221828Sgrehan		return;
303221828Sgrehan
304221828Sgrehan	/*
305221828Sgrehan	 * If the length is < 60, pad out to that and add the
306221828Sgrehan	 * extra zero'd segment to the iov. It is guaranteed that
307221828Sgrehan	 * there is always an extra iov available by the caller.
308221828Sgrehan	 */
309221828Sgrehan	if (len < 60) {
310221828Sgrehan		memset(pad, 0, 60 - len);
311221828Sgrehan		iov[iovcnt].iov_base = pad;
312221828Sgrehan		iov[iovcnt].iov_len = 60 - len;
313221828Sgrehan		iovcnt++;
314221828Sgrehan	}
315221828Sgrehan	(void) writev(sc->vsc_tapfd, iov, iovcnt);
316221828Sgrehan}
317221828Sgrehan
318221828Sgrehan/*
319221828Sgrehan *  Called when there is read activity on the tap file descriptor.
320221828Sgrehan * Each buffer posted by the guest is assumed to be able to contain
321221828Sgrehan * an entire ethernet frame + rx header.
322221828Sgrehan *  MP note: the dummybuf is only used for discarding frames, so there
323221828Sgrehan * is no need for it to be per-vtnet or locked.
324221828Sgrehan */
325221828Sgrehanstatic uint8_t dummybuf[2048];
326221828Sgrehan
327221828Sgrehanstatic void
328221828Sgrehanpci_vtnet_tap_rx(struct pci_vtnet_softc *sc)
329221828Sgrehan{
330221828Sgrehan	struct virtio_desc *vd;
331221828Sgrehan	struct virtio_used *vu;
332221828Sgrehan	struct vring_hqueue *hq;
333221828Sgrehan	struct virtio_net_rxhdr *vrx;
334221828Sgrehan	uint8_t *buf;
335221828Sgrehan	int i;
336221828Sgrehan	int len;
337221828Sgrehan	int ndescs;
338221828Sgrehan	int didx, uidx, aidx;	/* descriptor, avail and used index */
339221828Sgrehan
340221828Sgrehan	/*
341221828Sgrehan	 * Should never be called without a valid tap fd
342221828Sgrehan	 */
343221828Sgrehan	assert(sc->vsc_tapfd != -1);
344221828Sgrehan
345221828Sgrehan	/*
346221828Sgrehan	 * But, will be called when the rx ring hasn't yet
347250083Sneel	 * been set up or the guest is resetting the device.
348221828Sgrehan	 */
349250083Sneel	if (!sc->vsc_rx_ready || sc->resetting) {
350221828Sgrehan		/*
351221828Sgrehan		 * Drop the packet and try later.
352221828Sgrehan		 */
353221828Sgrehan		(void) read(sc->vsc_tapfd, dummybuf, sizeof(dummybuf));
354221828Sgrehan		return;
355221828Sgrehan	}
356221828Sgrehan
357221828Sgrehan	/*
358221828Sgrehan	 * Calculate the number of available rx buffers
359221828Sgrehan	 */
360221828Sgrehan	hq = &sc->vsc_hq[VTNET_RXQ];
361221828Sgrehan
362221828Sgrehan	ndescs = hq_num_avail(hq);
363221828Sgrehan
364221828Sgrehan	if (ndescs == 0) {
365221828Sgrehan		/*
366221828Sgrehan		 * Drop the packet and try later
367221828Sgrehan		 */
368221828Sgrehan		(void) read(sc->vsc_tapfd, dummybuf, sizeof(dummybuf));
369221828Sgrehan		return;
370221828Sgrehan	}
371221828Sgrehan
372221828Sgrehan	aidx = hq->hq_cur_aidx;
373221828Sgrehan	uidx = *hq->hq_used_idx;
374221828Sgrehan	for (i = 0; i < ndescs; i++) {
375221828Sgrehan		/*
376221828Sgrehan		 * 'aidx' indexes into the an array of descriptor indexes
377221828Sgrehan		 */
378221828Sgrehan		didx = hq->hq_avail_ring[aidx % hq->hq_size];
379221828Sgrehan		assert(didx >= 0 && didx < hq->hq_size);
380221828Sgrehan
381221828Sgrehan		vd = &hq->hq_dtable[didx];
382221828Sgrehan
383221828Sgrehan		/*
384221828Sgrehan		 * Get a pointer to the rx header, and use the
385221828Sgrehan		 * data immediately following it for the packet buffer.
386221828Sgrehan		 */
387248477Sneel		vrx = paddr_guest2host(vtnet_ctx(sc), vd->vd_addr, vd->vd_len);
388221828Sgrehan		buf = (uint8_t *)(vrx + 1);
389221828Sgrehan
390221828Sgrehan		len = read(sc->vsc_tapfd, buf,
391221828Sgrehan			   vd->vd_len - sizeof(struct virtio_net_rxhdr));
392221828Sgrehan
393221828Sgrehan		if (len < 0 && errno == EWOULDBLOCK) {
394221828Sgrehan			break;
395221828Sgrehan		}
396221828Sgrehan
397221828Sgrehan		/*
398221828Sgrehan		 * The only valid field in the rx packet header is the
399221828Sgrehan		 * number of buffers, which is always 1 without TSO
400221828Sgrehan		 * support.
401221828Sgrehan		 */
402221828Sgrehan		memset(vrx, 0, sizeof(struct virtio_net_rxhdr));
403221828Sgrehan		vrx->vrh_bufs = 1;
404221828Sgrehan
405221828Sgrehan		/*
406221828Sgrehan		 * Write this descriptor into the used ring
407221828Sgrehan		 */
408221828Sgrehan		vu = &hq->hq_used_ring[uidx % hq->hq_size];
409221828Sgrehan		vu->vu_idx = didx;
410221828Sgrehan		vu->vu_tlen = len + sizeof(struct virtio_net_rxhdr);
411221828Sgrehan		uidx++;
412221828Sgrehan		aidx++;
413221828Sgrehan	}
414221828Sgrehan
415221828Sgrehan	/*
416221828Sgrehan	 * Update the used pointer, and signal an interrupt if allowed
417221828Sgrehan	 */
418221828Sgrehan	*hq->hq_used_idx = uidx;
419221828Sgrehan	hq->hq_cur_aidx = aidx;
420221828Sgrehan
421221828Sgrehan	if ((*hq->hq_avail_flags & VRING_AVAIL_F_NO_INTERRUPT) == 0) {
422246109Sneel		if (use_msix) {
423246109Sneel			pci_generate_msix(sc->vsc_pi,
424246109Sneel					  sc->vsc_msix_table_idx[VTNET_RXQ]);
425246109Sneel		} else {
426246109Sneel			sc->vsc_isr |= 1;
427246109Sneel			pci_generate_msi(sc->vsc_pi, 0);
428246109Sneel		}
429221828Sgrehan	}
430221828Sgrehan}
431221828Sgrehan
432221828Sgrehanstatic void
433221828Sgrehanpci_vtnet_tap_callback(int fd, enum ev_type type, void *param)
434221828Sgrehan{
435221828Sgrehan	struct pci_vtnet_softc *sc = param;
436221828Sgrehan
437250083Sneel	pthread_mutex_lock(&sc->rx_mtx);
438250083Sneel	sc->rx_in_progress = 1;
439221828Sgrehan	pci_vtnet_tap_rx(sc);
440250083Sneel	sc->rx_in_progress = 0;
441250083Sneel	pthread_mutex_unlock(&sc->rx_mtx);
442221828Sgrehan
443221828Sgrehan}
444221828Sgrehan
445221828Sgrehanstatic void
446221828Sgrehanpci_vtnet_ping_rxq(struct pci_vtnet_softc *sc)
447221828Sgrehan{
448221828Sgrehan	/*
449221828Sgrehan	 * A qnotify means that the rx process can now begin
450221828Sgrehan	 */
451221828Sgrehan	if (sc->vsc_rx_ready == 0) {
452221828Sgrehan		sc->vsc_rx_ready = 1;
453221828Sgrehan	}
454221828Sgrehan}
455221828Sgrehan
456221828Sgrehanstatic void
457221828Sgrehanpci_vtnet_proctx(struct pci_vtnet_softc *sc, struct vring_hqueue *hq)
458221828Sgrehan{
459221828Sgrehan	struct iovec iov[VTNET_MAXSEGS + 1];
460221828Sgrehan	struct virtio_desc *vd;
461221828Sgrehan	struct virtio_used *vu;
462221828Sgrehan	int i;
463221828Sgrehan	int plen;
464221828Sgrehan	int tlen;
465221828Sgrehan	int uidx, aidx, didx;
466221828Sgrehan
467221828Sgrehan	uidx = *hq->hq_used_idx;
468221828Sgrehan	aidx = hq->hq_cur_aidx;
469221828Sgrehan	didx = hq->hq_avail_ring[aidx % hq->hq_size];
470221828Sgrehan	assert(didx >= 0 && didx < hq->hq_size);
471221828Sgrehan
472221828Sgrehan	vd = &hq->hq_dtable[didx];
473221828Sgrehan
474221828Sgrehan	/*
475221828Sgrehan	 * Run through the chain of descriptors, ignoring the
476221828Sgrehan	 * first header descriptor. However, include the header
477221828Sgrehan	 * length in the total length that will be put into the
478221828Sgrehan	 * used queue.
479221828Sgrehan	 */
480221828Sgrehan	tlen = vd->vd_len;
481221828Sgrehan	vd = &hq->hq_dtable[vd->vd_next];
482221828Sgrehan
483221828Sgrehan	for (i = 0, plen = 0;
484221828Sgrehan	     i < VTNET_MAXSEGS;
485221828Sgrehan	     i++, vd = &hq->hq_dtable[vd->vd_next]) {
486248477Sneel		iov[i].iov_base = paddr_guest2host(vtnet_ctx(sc),
487248477Sneel						   vd->vd_addr, vd->vd_len);
488221828Sgrehan		iov[i].iov_len = vd->vd_len;
489221828Sgrehan		plen += vd->vd_len;
490221828Sgrehan		tlen += vd->vd_len;
491221828Sgrehan
492221828Sgrehan		if ((vd->vd_flags & VRING_DESC_F_NEXT) == 0)
493221828Sgrehan			break;
494221828Sgrehan	}
495221828Sgrehan	assert(i < VTNET_MAXSEGS);
496221828Sgrehan
497221828Sgrehan	DPRINTF(("virtio: packet send, %d bytes, %d segs\n\r", plen, i + 1));
498221828Sgrehan	pci_vtnet_tap_tx(sc, iov, i + 1, plen);
499221828Sgrehan
500221828Sgrehan	/*
501221828Sgrehan	 * Return this chain back to the host
502221828Sgrehan	 */
503221828Sgrehan	vu = &hq->hq_used_ring[uidx % hq->hq_size];
504221828Sgrehan	vu->vu_idx = didx;
505221828Sgrehan	vu->vu_tlen = tlen;
506221828Sgrehan	hq->hq_cur_aidx = aidx + 1;
507221828Sgrehan	*hq->hq_used_idx = uidx + 1;
508221828Sgrehan}
509221828Sgrehan
510221828Sgrehanstatic void
511221828Sgrehanpci_vtnet_ping_txq(struct pci_vtnet_softc *sc)
512221828Sgrehan{
513221828Sgrehan	struct vring_hqueue *hq = &sc->vsc_hq[VTNET_TXQ];
514221828Sgrehan	int ndescs;
515221828Sgrehan
516221828Sgrehan	/*
517221828Sgrehan	 * Calculate number of ring entries to process
518221828Sgrehan	 */
519221828Sgrehan	ndescs = hq_num_avail(hq);
520221828Sgrehan
521221828Sgrehan	if (ndescs == 0)
522221828Sgrehan		return;
523221828Sgrehan
524249917Sgrehan	/* Signal the tx thread for processing */
525249917Sgrehan	pthread_mutex_lock(&sc->tx_mtx);
526249917Sgrehan	if (sc->tx_in_progress == 0)
527249917Sgrehan		pthread_cond_signal(&sc->tx_cond);
528249917Sgrehan	pthread_mutex_unlock(&sc->tx_mtx);
529221828Sgrehan}
530221828Sgrehan
531249917Sgrehan/*
532249917Sgrehan * Thread which will handle processing of TX desc
533249917Sgrehan */
534249917Sgrehanstatic void *
535249917Sgrehanpci_vtnet_tx_thread(void *param)
536249917Sgrehan{
537249917Sgrehan	struct pci_vtnet_softc *sc = (struct pci_vtnet_softc *) param;
538249917Sgrehan	struct vring_hqueue *hq;
539249917Sgrehan	int i, ndescs, needintr,error;
540249917Sgrehan
541249917Sgrehan	needintr = 0;
542249917Sgrehan	hq = &sc->vsc_hq[VTNET_TXQ];
543249917Sgrehan
544249917Sgrehan	/*
545249917Sgrehan	 * Let us wait till the tx queue pointers get initialised &
546249917Sgrehan	 * first tx signaled
547249917Sgrehan	 */
548249917Sgrehan	pthread_mutex_lock(&sc->tx_mtx);
549249917Sgrehan	error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx);
550249917Sgrehan	assert(error == 0);
551249917Sgrehan
552249917Sgrehan	for (;;) {
553249917Sgrehan		pthread_mutex_lock(&sc->tx_mtx);
554249917Sgrehan		for (;;) {
555249917Sgrehan			if (sc->resetting) {
556249917Sgrehan				ndescs = 0;
557249917Sgrehan				needintr = 0;
558249917Sgrehan			} else
559249917Sgrehan				ndescs = hq_num_avail(hq);
560249917Sgrehan
561249917Sgrehan			if (ndescs != 0)
562249917Sgrehan				break;
563249917Sgrehan
564249917Sgrehan			if (needintr) {
565249917Sgrehan				/*
566249917Sgrehan				 * Generate an interrupt if able
567249917Sgrehan				 */
568249917Sgrehan				if ((*hq->hq_avail_flags &
569249917Sgrehan				     VRING_AVAIL_F_NO_INTERRUPT) == 0) {
570249917Sgrehan					if (use_msix) {
571249917Sgrehan						pci_generate_msix(sc->vsc_pi,
572249917Sgrehan						     sc->vsc_msix_table_idx[VTNET_TXQ]);
573249917Sgrehan					} else {
574249917Sgrehan						sc->vsc_isr |= 1;
575249917Sgrehan						pci_generate_msi(sc->vsc_pi, 0);
576249917Sgrehan					}
577249917Sgrehan				}
578249917Sgrehan			}
579249917Sgrehan			needintr = 0;
580249917Sgrehan			sc->tx_in_progress = 0;
581249917Sgrehan			error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx);
582249917Sgrehan			assert(error == 0);
583249917Sgrehan		}
584249917Sgrehan		sc->tx_in_progress = 1;
585249917Sgrehan		pthread_mutex_unlock(&sc->tx_mtx);
586249917Sgrehan
587249917Sgrehan		while (ndescs > 0) {
588249917Sgrehan			/*
589249917Sgrehan			 * Run through all the entries, placing them into
590249917Sgrehan			 * iovecs and sending when an end-of-packet is found
591249917Sgrehan			 */
592249917Sgrehan			for (i = 0; i < ndescs; i++)
593249917Sgrehan				pci_vtnet_proctx(sc, hq);
594249917Sgrehan			needintr = 1;
595249917Sgrehan			ndescs = hq_num_avail(hq);
596249917Sgrehan		}
597249917Sgrehan	}
598249917Sgrehan}
599249917Sgrehan
600221828Sgrehanstatic void
601221828Sgrehanpci_vtnet_ping_ctlq(struct pci_vtnet_softc *sc)
602221828Sgrehan{
603221828Sgrehan
604221828Sgrehan	DPRINTF(("vtnet: control qnotify!\n\r"));
605221828Sgrehan}
606221828Sgrehan
607221828Sgrehanstatic void
608221828Sgrehanpci_vtnet_ring_init(struct pci_vtnet_softc *sc, uint64_t pfn)
609221828Sgrehan{
610221828Sgrehan	struct vring_hqueue *hq;
611221828Sgrehan	int qnum = sc->vsc_curq;
612221828Sgrehan
613221828Sgrehan	assert(qnum < VTNET_MAXQ);
614221828Sgrehan
615221828Sgrehan	sc->vsc_pfn[qnum] = pfn << VRING_PFN;
616221828Sgrehan
617221828Sgrehan	/*
618221828Sgrehan	 * Set up host pointers to the various parts of the
619221828Sgrehan	 * queue
620221828Sgrehan	 */
621221828Sgrehan	hq = &sc->vsc_hq[qnum];
622221828Sgrehan	hq->hq_size = pci_vtnet_qsize(qnum);
623221828Sgrehan
624248477Sneel	hq->hq_dtable = paddr_guest2host(vtnet_ctx(sc), pfn << VRING_PFN,
625247523Sneel					 vring_size(hq->hq_size));
626221828Sgrehan	hq->hq_avail_flags =  (uint16_t *)(hq->hq_dtable + hq->hq_size);
627221828Sgrehan	hq->hq_avail_idx = hq->hq_avail_flags + 1;
628221828Sgrehan	hq->hq_avail_ring = hq->hq_avail_flags + 2;
629221828Sgrehan	hq->hq_used_flags = (uint16_t *)roundup2((uintptr_t)hq->hq_avail_ring,
630221828Sgrehan						 VRING_ALIGN);
631221828Sgrehan	hq->hq_used_idx = hq->hq_used_flags + 1;
632221828Sgrehan	hq->hq_used_ring = (struct virtio_used *)(hq->hq_used_flags + 2);
633221828Sgrehan
634221828Sgrehan	/*
635221828Sgrehan	 * Initialize queue indexes
636221828Sgrehan	 */
637221828Sgrehan	hq->hq_cur_aidx = 0;
638221828Sgrehan}
639221828Sgrehan
640221828Sgrehanstatic int
641221828Sgrehanpci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
642221828Sgrehan{
643221828Sgrehan	MD5_CTX mdctx;
644221828Sgrehan	unsigned char digest[16];
645221828Sgrehan	char nstr[80];
646249917Sgrehan	char tname[MAXCOMLEN + 1];
647221828Sgrehan	struct pci_vtnet_softc *sc;
648246109Sneel	const char *env_msi;
649221828Sgrehan
650221828Sgrehan	sc = malloc(sizeof(struct pci_vtnet_softc));
651221828Sgrehan	memset(sc, 0, sizeof(struct pci_vtnet_softc));
652221828Sgrehan
653221828Sgrehan	pi->pi_arg = sc;
654221828Sgrehan	sc->vsc_pi = pi;
655221828Sgrehan
656221828Sgrehan	pthread_mutex_init(&sc->vsc_mtx, NULL);
657246109Sneel
658246109Sneel	/*
659246109Sneel	 * Use MSI if set by user
660246109Sneel	 */
661246109Sneel	if ((env_msi = getenv("BHYVE_USE_MSI")) != NULL) {
662246109Sneel		if (strcasecmp(env_msi, "yes") == 0)
663246109Sneel			use_msix = 0;
664246109Sneel	}
665221828Sgrehan
666221828Sgrehan	/*
667221828Sgrehan	 * Attempt to open the tap device
668221828Sgrehan	 */
669221828Sgrehan	sc->vsc_tapfd = -1;
670221828Sgrehan	if (opts != NULL) {
671221828Sgrehan		char tbuf[80];
672221828Sgrehan
673221828Sgrehan		strcpy(tbuf, "/dev/");
674242882Sneel		strlcat(tbuf, opts, sizeof(tbuf));
675221828Sgrehan
676221828Sgrehan		sc->vsc_tapfd = open(tbuf, O_RDWR);
677221828Sgrehan		if (sc->vsc_tapfd == -1) {
678221828Sgrehan			WPRINTF(("open of tap device %s failed\n", tbuf));
679221828Sgrehan		} else {
680221828Sgrehan			/*
681221828Sgrehan			 * Set non-blocking and register for read
682221828Sgrehan			 * notifications with the event loop
683221828Sgrehan			 */
684221828Sgrehan			int opt = 1;
685221828Sgrehan			if (ioctl(sc->vsc_tapfd, FIONBIO, &opt) < 0) {
686221828Sgrehan				WPRINTF(("tap device O_NONBLOCK failed\n"));
687221828Sgrehan				close(sc->vsc_tapfd);
688221828Sgrehan				sc->vsc_tapfd = -1;
689221828Sgrehan			}
690221828Sgrehan
691221828Sgrehan			sc->vsc_mevp = mevent_add(sc->vsc_tapfd,
692221828Sgrehan						  EVF_READ,
693221828Sgrehan						  pci_vtnet_tap_callback,
694221828Sgrehan						  sc);
695221828Sgrehan			if (sc->vsc_mevp == NULL) {
696221828Sgrehan				WPRINTF(("Could not register event\n"));
697221828Sgrehan				close(sc->vsc_tapfd);
698221828Sgrehan				sc->vsc_tapfd = -1;
699221828Sgrehan			}
700221828Sgrehan		}
701221828Sgrehan	}
702221828Sgrehan
703221828Sgrehan	/*
704221828Sgrehan	 * The MAC address is the standard NetApp OUI of 00-a0-98,
705244159Sgrehan	 * followed by an MD5 of the vm name. The slot/func number is
706244159Sgrehan	 * prepended to this for slots other than 1:0, so that
707244159Sgrehan	 * a bootloader can netboot from the equivalent of slot 1.
708221828Sgrehan	 */
709244159Sgrehan	if (pi->pi_slot == 1 && pi->pi_func == 0) {
710221828Sgrehan		strncpy(nstr, vmname, sizeof(nstr));
711221828Sgrehan	} else {
712244159Sgrehan		snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot,
713244159Sgrehan		    pi->pi_func, vmname);
714221828Sgrehan	}
715221828Sgrehan
716221828Sgrehan	MD5Init(&mdctx);
717221828Sgrehan	MD5Update(&mdctx, nstr, strlen(nstr));
718221828Sgrehan	MD5Final(digest, &mdctx);
719221828Sgrehan
720221828Sgrehan	sc->vsc_macaddr[0] = 0x00;
721221828Sgrehan	sc->vsc_macaddr[1] = 0xa0;
722221828Sgrehan	sc->vsc_macaddr[2] = 0x98;
723221828Sgrehan	sc->vsc_macaddr[3] = digest[0];
724221828Sgrehan	sc->vsc_macaddr[4] = digest[1];
725221828Sgrehan	sc->vsc_macaddr[5] = digest[2];
726221828Sgrehan
727221828Sgrehan	/* initialize config space */
728221828Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET);
729221828Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
730221828Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK);
731221828Sgrehan	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET);
732246109Sneel
733246109Sneel	if (use_msix) {
734246109Sneel		/* MSI-X support */
735246109Sneel		int i;
736246109Sneel
737246109Sneel		for (i = 0; i < VTNET_MAXQ; i++)
738246109Sneel			sc->vsc_msix_table_idx[i] = VIRTIO_MSI_NO_VECTOR;
739246109Sneel
740246109Sneel		/*
741246109Sneel		 * BAR 1 used to map MSI-X table and PBA
742246109Sneel		 */
743246109Sneel		if (pci_emul_add_msixcap(pi, VTNET_MAXQ, 1))
744246109Sneel			return (1);
745246109Sneel	} else {
746246109Sneel		/* MSI support */
747246109Sneel		pci_emul_add_msicap(pi, 1);
748246109Sneel	}
749246109Sneel
750241744Sgrehan	pci_emul_alloc_bar(pi, 0, PCIBAR_IO, VTNET_REGSZ);
751250083Sneel
752250083Sneel	sc->resetting = 0;
753250083Sneel
754250083Sneel	sc->rx_in_progress = 0;
755250083Sneel	pthread_mutex_init(&sc->rx_mtx, NULL);
756250083Sneel
757249917Sgrehan	/*
758249917Sgrehan	 * Initialize tx semaphore & spawn TX processing thread
759249917Sgrehan	 * As of now, only one thread for TX desc processing is
760249917Sgrehan	 * spawned.
761249917Sgrehan	 */
762249917Sgrehan	sc->tx_in_progress = 0;
763249917Sgrehan	pthread_mutex_init(&sc->tx_mtx, NULL);
764249917Sgrehan	pthread_cond_init(&sc->tx_cond, NULL);
765249917Sgrehan	pthread_create(&sc->tx_tid, NULL, pci_vtnet_tx_thread, (void *)sc);
766249917Sgrehan        snprintf(tname, sizeof(tname), "%s vtnet%d tx", vmname, pi->pi_slot);
767249917Sgrehan        pthread_set_name_np(sc->tx_tid, tname);
768221828Sgrehan
769221828Sgrehan	return (0);
770221828Sgrehan}
771221828Sgrehan
772221828Sgrehan/*
773221828Sgrehan * Function pointer array to handle queue notifications
774221828Sgrehan */
775221828Sgrehanstatic void (*pci_vtnet_qnotify[VTNET_MAXQ])(struct pci_vtnet_softc *) = {
776221828Sgrehan	pci_vtnet_ping_rxq,
777221828Sgrehan	pci_vtnet_ping_txq,
778221828Sgrehan	pci_vtnet_ping_ctlq
779221828Sgrehan};
780221828Sgrehan
781246109Sneelstatic uint64_t
782246109Sneelvtnet_adjust_offset(struct pci_devinst *pi, uint64_t offset)
783246109Sneel{
784246109Sneel	/*
785246109Sneel	 * Device specific offsets used by guest would change based on
786246109Sneel	 * whether MSI-X capability is enabled or not
787246109Sneel	 */
788246109Sneel	if (!pci_msix_enabled(pi)) {
789246109Sneel		if (offset >= VTCFG_R_MSIX)
790246109Sneel			return (offset + (VTCFG_R_CFG1 - VTCFG_R_MSIX));
791246109Sneel	}
792246109Sneel
793246109Sneel	return (offset);
794246109Sneel}
795246109Sneel
796221828Sgrehanstatic void
797241744Sgrehanpci_vtnet_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
798241744Sgrehan		int baridx, uint64_t offset, int size, uint64_t value)
799221828Sgrehan{
800221828Sgrehan	struct pci_vtnet_softc *sc = pi->pi_arg;
801222830Sgrehan	void *ptr;
802222830Sgrehan
803246109Sneel	if (use_msix) {
804246190Sneel		if (baridx == pci_msix_table_bar(pi) ||
805246190Sneel		    baridx == pci_msix_pba_bar(pi)) {
806246109Sneel			pci_emul_msix_twrite(pi, offset, size, value);
807246109Sneel			return;
808246109Sneel		}
809246109Sneel	}
810246109Sneel
811241744Sgrehan	assert(baridx == 0);
812241744Sgrehan
813246109Sneel	if (offset + size > pci_vtnet_iosize(pi)) {
814241744Sgrehan		DPRINTF(("vtnet_write: 2big, offset %ld size %d\n",
815221828Sgrehan			 offset, size));
816221828Sgrehan		return;
817221828Sgrehan	}
818221828Sgrehan
819221828Sgrehan	pthread_mutex_lock(&sc->vsc_mtx);
820221828Sgrehan
821246109Sneel	offset = vtnet_adjust_offset(pi, offset);
822246109Sneel
823221828Sgrehan	switch (offset) {
824221828Sgrehan	case VTCFG_R_GUESTCAP:
825221828Sgrehan		assert(size == 4);
826221828Sgrehan		sc->vsc_features = value & VTNET_S_HOSTCAPS;
827221828Sgrehan		break;
828221828Sgrehan	case VTCFG_R_PFN:
829221828Sgrehan		assert(size == 4);
830221828Sgrehan		pci_vtnet_ring_init(sc, value);
831221828Sgrehan		break;
832221828Sgrehan	case VTCFG_R_QSEL:
833221828Sgrehan		assert(size == 2);
834221828Sgrehan		assert(value < VTNET_MAXQ);
835221828Sgrehan		sc->vsc_curq = value;
836221828Sgrehan		break;
837221828Sgrehan	case VTCFG_R_QNOTIFY:
838221828Sgrehan		assert(size == 2);
839221828Sgrehan		assert(value < VTNET_MAXQ);
840221828Sgrehan		(*pci_vtnet_qnotify[value])(sc);
841221828Sgrehan		break;
842221828Sgrehan	case VTCFG_R_STATUS:
843221828Sgrehan		assert(size == 1);
844221828Sgrehan		pci_vtnet_update_status(sc, value);
845221828Sgrehan		break;
846246109Sneel	case VTCFG_R_CFGVEC:
847246109Sneel		assert(size == 2);
848246109Sneel		sc->vsc_msix_table_idx[VTNET_CTLQ] = value;
849246109Sneel		break;
850246109Sneel	case VTCFG_R_QVEC:
851246109Sneel		assert(size == 2);
852246109Sneel		assert(sc->vsc_curq != VTNET_CTLQ);
853246109Sneel		sc->vsc_msix_table_idx[sc->vsc_curq] = value;
854246109Sneel		break;
855221828Sgrehan	case VTNET_R_CFG0:
856221828Sgrehan	case VTNET_R_CFG1:
857221828Sgrehan	case VTNET_R_CFG2:
858221828Sgrehan	case VTNET_R_CFG3:
859221828Sgrehan	case VTNET_R_CFG4:
860221828Sgrehan	case VTNET_R_CFG5:
861222830Sgrehan		assert((size + offset) <= (VTNET_R_CFG5 + 1));
862222830Sgrehan		ptr = &sc->vsc_macaddr[offset - VTNET_R_CFG0];
863221828Sgrehan		/*
864221828Sgrehan		 * The driver is allowed to change the MAC address
865221828Sgrehan		 */
866221828Sgrehan		sc->vsc_macaddr[offset - VTNET_R_CFG0] = value;
867222830Sgrehan		if (size == 1) {
868222830Sgrehan			*(uint8_t *) ptr = value;
869222830Sgrehan		} else if (size == 2) {
870222830Sgrehan			*(uint16_t *) ptr = value;
871222830Sgrehan		} else {
872222830Sgrehan			*(uint32_t *) ptr = value;
873222830Sgrehan		}
874221828Sgrehan		break;
875221828Sgrehan	case VTCFG_R_HOSTCAP:
876221828Sgrehan	case VTCFG_R_QNUM:
877221828Sgrehan	case VTCFG_R_ISR:
878221828Sgrehan	case VTNET_R_CFG6:
879221828Sgrehan	case VTNET_R_CFG7:
880241744Sgrehan		DPRINTF(("vtnet: write to readonly reg %ld\n\r", offset));
881221828Sgrehan		break;
882221828Sgrehan	default:
883241744Sgrehan		DPRINTF(("vtnet: unknown i/o write offset %ld\n\r", offset));
884221828Sgrehan		value = 0;
885221828Sgrehan		break;
886221828Sgrehan	}
887221828Sgrehan
888221828Sgrehan	pthread_mutex_unlock(&sc->vsc_mtx);
889221828Sgrehan}
890221828Sgrehan
891241744Sgrehanuint64_t
892241744Sgrehanpci_vtnet_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
893241744Sgrehan	       int baridx, uint64_t offset, int size)
894221828Sgrehan{
895221828Sgrehan	struct pci_vtnet_softc *sc = pi->pi_arg;
896222830Sgrehan	void *ptr;
897241744Sgrehan	uint64_t value;
898221828Sgrehan
899246109Sneel	if (use_msix) {
900246190Sneel		if (baridx == pci_msix_table_bar(pi) ||
901246190Sneel		    baridx == pci_msix_pba_bar(pi)) {
902246109Sneel			return (pci_emul_msix_tread(pi, offset, size));
903246109Sneel		}
904246109Sneel	}
905246109Sneel
906241744Sgrehan	assert(baridx == 0);
907241744Sgrehan
908246109Sneel	if (offset + size > pci_vtnet_iosize(pi)) {
909241744Sgrehan		DPRINTF(("vtnet_read: 2big, offset %ld size %d\n",
910221828Sgrehan			 offset, size));
911221828Sgrehan		return (0);
912221828Sgrehan	}
913221828Sgrehan
914221828Sgrehan	pthread_mutex_lock(&sc->vsc_mtx);
915221828Sgrehan
916246109Sneel	offset = vtnet_adjust_offset(pi, offset);
917246109Sneel
918221828Sgrehan	switch (offset) {
919221828Sgrehan	case VTCFG_R_HOSTCAP:
920221828Sgrehan		assert(size == 4);
921221828Sgrehan		value = VTNET_S_HOSTCAPS;
922221828Sgrehan		break;
923221828Sgrehan	case VTCFG_R_GUESTCAP:
924221828Sgrehan		assert(size == 4);
925221828Sgrehan		value = sc->vsc_features; /* XXX never read ? */
926221828Sgrehan		break;
927221828Sgrehan	case VTCFG_R_PFN:
928221828Sgrehan		assert(size == 4);
929221828Sgrehan		value = sc->vsc_pfn[sc->vsc_curq] >> VRING_PFN;
930221828Sgrehan		break;
931221828Sgrehan	case VTCFG_R_QNUM:
932221828Sgrehan		assert(size == 2);
933221828Sgrehan		value = pci_vtnet_qsize(sc->vsc_curq);
934221828Sgrehan		break;
935221828Sgrehan	case VTCFG_R_QSEL:
936221828Sgrehan		assert(size == 2);
937221828Sgrehan		value = sc->vsc_curq;  /* XXX never read ? */
938221828Sgrehan		break;
939221828Sgrehan	case VTCFG_R_QNOTIFY:
940221828Sgrehan		assert(size == 2);
941221828Sgrehan		value = sc->vsc_curq;  /* XXX never read ? */
942221828Sgrehan		break;
943221828Sgrehan	case VTCFG_R_STATUS:
944221828Sgrehan		assert(size == 1);
945221828Sgrehan		value = sc->vsc_status;
946221828Sgrehan		break;
947221828Sgrehan	case VTCFG_R_ISR:
948221828Sgrehan		assert(size == 1);
949221828Sgrehan		value = sc->vsc_isr;
950221828Sgrehan		sc->vsc_isr = 0;     /* a read clears this flag */
951221828Sgrehan		break;
952246109Sneel	case VTCFG_R_CFGVEC:
953246109Sneel		assert(size == 2);
954246109Sneel		value = sc->vsc_msix_table_idx[VTNET_CTLQ];
955246109Sneel		break;
956246109Sneel	case VTCFG_R_QVEC:
957246109Sneel		assert(size == 2);
958246109Sneel		assert(sc->vsc_curq != VTNET_CTLQ);
959246109Sneel		value = sc->vsc_msix_table_idx[sc->vsc_curq];
960246109Sneel		break;
961221828Sgrehan	case VTNET_R_CFG0:
962221828Sgrehan	case VTNET_R_CFG1:
963221828Sgrehan	case VTNET_R_CFG2:
964221828Sgrehan	case VTNET_R_CFG3:
965221828Sgrehan	case VTNET_R_CFG4:
966221828Sgrehan	case VTNET_R_CFG5:
967246109Sneel		assert((size + offset) <= (VTNET_R_CFG5 + 1));
968246109Sneel		ptr = &sc->vsc_macaddr[offset - VTNET_R_CFG0];
969246109Sneel		if (size == 1) {
970246109Sneel			value = *(uint8_t *) ptr;
971246109Sneel		} else if (size == 2) {
972246109Sneel			value = *(uint16_t *) ptr;
973246109Sneel		} else {
974246109Sneel			value = *(uint32_t *) ptr;
975246109Sneel		}
976221828Sgrehan		break;
977221828Sgrehan	case VTNET_R_CFG6:
978222830Sgrehan		assert(size != 4);
979222830Sgrehan		value = 0x01; /* XXX link always up */
980221828Sgrehan		break;
981221828Sgrehan	case VTNET_R_CFG7:
982221828Sgrehan		assert(size == 1);
983222830Sgrehan		value = 0; /* XXX link status in LSB */
984221828Sgrehan		break;
985221828Sgrehan	default:
986241744Sgrehan		DPRINTF(("vtnet: unknown i/o read offset %ld\n\r", offset));
987221828Sgrehan		value = 0;
988221828Sgrehan		break;
989221828Sgrehan	}
990221828Sgrehan
991221828Sgrehan	pthread_mutex_unlock(&sc->vsc_mtx);
992221828Sgrehan
993221828Sgrehan	return (value);
994221828Sgrehan}
995221828Sgrehan
996221828Sgrehanstruct pci_devemu pci_de_vnet = {
997241744Sgrehan	.pe_emu = 	"virtio-net",
998241744Sgrehan	.pe_init =	pci_vtnet_init,
999241744Sgrehan	.pe_barwrite =	pci_vtnet_write,
1000241744Sgrehan	.pe_barread =	pci_vtnet_read
1001221828Sgrehan};
1002221828SgrehanPCI_EMUL_SET(pci_de_vnet);
1003