xref: /freebsd/sys/dev/beri/virtio/network/if_vtbe.c (revision d6b92ffa)
1 /*-
2  * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  *
5  * This software was developed by SRI International and the University of
6  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7  * ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 /*
32  * BERI Virtio Networking Frontend
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43 #include <sys/malloc.h>
44 #include <sys/rman.h>
45 #include <sys/timeet.h>
46 #include <sys/timetc.h>
47 #include <sys/endian.h>
48 #include <sys/lock.h>
49 #include <sys/mbuf.h>
50 #include <sys/mutex.h>
51 #include <sys/socket.h>
52 #include <sys/sockio.h>
53 #include <sys/sysctl.h>
54 #include <sys/mdioctl.h>
55 #include <sys/conf.h>
56 #include <sys/stat.h>
57 #include <sys/uio.h>
58 
59 #include <dev/fdt/fdt_common.h>
60 #include <dev/ofw/openfirm.h>
61 #include <dev/ofw/ofw_bus.h>
62 #include <dev/ofw/ofw_bus_subr.h>
63 
64 #include <net/bpf.h>
65 #include <net/if.h>
66 #include <net/ethernet.h>
67 #include <net/if_dl.h>
68 #include <net/if_media.h>
69 #include <net/if_types.h>
70 #include <net/if_var.h>
71 #include <net/if_vlan_var.h>
72 
73 #include <machine/bus.h>
74 #include <machine/fdt.h>
75 #include <machine/cpu.h>
76 #include <machine/intr.h>
77 
78 #include <dev/beri/virtio/virtio.h>
79 #include <dev/beri/virtio/virtio_mmio_platform.h>
80 
81 #include <dev/altera/pio/pio.h>
82 
83 #include <dev/virtio/mmio/virtio_mmio.h>
84 #include <dev/virtio/network/virtio_net.h>
85 #include <dev/virtio/virtio_ids.h>
86 #include <dev/virtio/virtio_config.h>
87 #include <dev/virtio/virtio_ring.h>
88 
89 #include "pio_if.h"
90 
91 #define	DPRINTF(fmt, args...)	printf(fmt, ##args)
92 
93 #define	READ4(_sc, _reg) \
94 	bus_read_4((_sc)->res[0], _reg)
95 #define	WRITE4(_sc, _reg, _val) \
96 	bus_write_4((_sc)->res[0], _reg, _val)
97 
98 #define	VTBE_LOCK(sc)			mtx_lock(&(sc)->mtx)
99 #define	VTBE_UNLOCK(sc)			mtx_unlock(&(sc)->mtx)
100 #define	VTBE_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->mtx, MA_OWNED);
101 #define	VTBE_ASSERT_UNLOCKED(sc)	mtx_assert(&(sc)->mtx, MA_NOTOWNED);
102 
103 /*
104  * Driver data and defines.
105  */
106 #define	DESC_COUNT	256
107 
108 struct vtbe_softc {
109 	struct resource		*res[2];
110 	bus_space_tag_t		bst;
111 	bus_space_handle_t	bsh;
112 	device_t		dev;
113 	struct ifnet		*ifp;
114 	int			if_flags;
115 	struct mtx		mtx;
116 	boolean_t		is_attached;
117 
118 	int			beri_mem_offset;
119 	device_t		pio_send;
120 	device_t		pio_recv;
121 	int			opened;
122 
123 	struct vqueue_info	vs_queues[2];
124 	int			vs_curq;
125 	int			hdrsize;
126 };
127 
128 static struct resource_spec vtbe_spec[] = {
129 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
130 	{ -1, 0 }
131 };
132 
133 static void vtbe_txfinish_locked(struct vtbe_softc *sc);
134 static void vtbe_rxfinish_locked(struct vtbe_softc *sc);
135 static void vtbe_stop_locked(struct vtbe_softc *sc);
136 static int pio_enable_irq(struct vtbe_softc *sc, int enable);
137 
138 static void
139 vtbe_txstart_locked(struct vtbe_softc *sc)
140 {
141 	struct iovec iov[DESC_COUNT];
142 	struct virtio_net_hdr *vnh;
143 	struct vqueue_info *vq;
144 	struct iovec *tiov;
145 	struct ifnet *ifp;
146 	struct mbuf *m;
147 	struct uio uio;
148 	int enqueued;
149 	int iolen;
150 	int error;
151 	int reg;
152 	int len;
153 	int n;
154 
155 	VTBE_ASSERT_LOCKED(sc);
156 
157 	/* RX queue */
158 	vq = &sc->vs_queues[0];
159 	if (!vq_has_descs(vq)) {
160 		return;
161 	}
162 
163 	ifp = sc->ifp;
164 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
165 		return;
166 	}
167 
168 	enqueued = 0;
169 
170 	if (!vq_ring_ready(vq))
171 		return;
172 
173 	vq->vq_save_used = be16toh(vq->vq_used->idx);
174 
175 	for (;;) {
176 		if (!vq_has_descs(vq)) {
177 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
178 			break;
179 		}
180 
181 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
182 		if (m == NULL) {
183 			break;
184 		}
185 
186 		n = vq_getchain(sc->beri_mem_offset, vq, iov,
187 			DESC_COUNT, NULL);
188 		KASSERT(n == 2,
189 			("Unexpected amount of descriptors (%d)", n));
190 
191 		tiov = getcopy(iov, n);
192 		vnh = iov[0].iov_base;
193 		memset(vnh, 0, sc->hdrsize);
194 
195 		len = iov[1].iov_len;
196 		uio.uio_resid = len;
197 		uio.uio_iov = &tiov[1];
198 		uio.uio_segflg = UIO_SYSSPACE;
199 		uio.uio_iovcnt = 1;
200 		uio.uio_offset = 0;
201 		uio.uio_rw = UIO_READ;
202 
203 		error = m_mbuftouio(&uio, m, 0);
204 		if (error)
205 			panic("m_mbuftouio failed\n");
206 
207 		iolen = (len - uio.uio_resid + sc->hdrsize);
208 
209 		free(tiov, M_DEVBUF);
210 		vq_relchain(vq, iov, n, iolen);
211 
212 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
213 
214 		BPF_MTAP(ifp, m);
215 		m_freem(m);
216 
217 		++enqueued;
218 	}
219 
220 	if (enqueued != 0) {
221 		reg = htobe32(VIRTIO_MMIO_INT_VRING);
222 		WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg);
223 
224 		PIO_SET(sc->pio_send, Q_INTR, 1);
225 	}
226 }
227 
228 static void
229 vtbe_txstart(struct ifnet *ifp)
230 {
231 	struct vtbe_softc *sc = ifp->if_softc;
232 
233 	VTBE_LOCK(sc);
234 	vtbe_txstart_locked(sc);
235 	VTBE_UNLOCK(sc);
236 }
237 
238 static void
239 vtbe_stop_locked(struct vtbe_softc *sc)
240 {
241 	struct ifnet *ifp;
242 
243 	VTBE_ASSERT_LOCKED(sc);
244 
245 	ifp = sc->ifp;
246 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
247 }
248 
249 static void
250 vtbe_init_locked(struct vtbe_softc *sc)
251 {
252 	struct ifnet *ifp = sc->ifp;
253 
254 	VTBE_ASSERT_LOCKED(sc);
255 
256 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
257 		return;
258 
259 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
260 }
261 
262 static void
263 vtbe_init(void *if_softc)
264 {
265 	struct vtbe_softc *sc = if_softc;
266 
267 	VTBE_LOCK(sc);
268 	vtbe_init_locked(sc);
269 	VTBE_UNLOCK(sc);
270 }
271 
272 static int
273 vtbe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
274 {
275 	struct ifmediareq *ifmr;
276 	struct vtbe_softc *sc;
277 	struct ifreq *ifr;
278 	int mask, error;
279 
280 	sc = ifp->if_softc;
281 	ifr = (struct ifreq *)data;
282 
283 	error = 0;
284 	switch (cmd) {
285 	case SIOCSIFFLAGS:
286 		VTBE_LOCK(sc);
287 		if (ifp->if_flags & IFF_UP) {
288 			pio_enable_irq(sc, 1);
289 
290 			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
291 				vtbe_init_locked(sc);
292 			}
293 		} else {
294 			pio_enable_irq(sc, 0);
295 
296 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
297 				vtbe_stop_locked(sc);
298 			}
299 		}
300 		sc->if_flags = ifp->if_flags;
301 		VTBE_UNLOCK(sc);
302 		break;
303 	case SIOCADDMULTI:
304 	case SIOCDELMULTI:
305 		break;
306 	case SIOCSIFMEDIA:
307 	case SIOCGIFMEDIA:
308 		ifmr = (struct ifmediareq *)data;
309 		ifmr->ifm_count = 1;
310 		ifmr->ifm_status = (IFM_AVALID | IFM_ACTIVE);
311 		ifmr->ifm_active = (IFM_ETHER | IFM_10G_T | IFM_FDX);
312 		ifmr->ifm_current = ifmr->ifm_active;
313 		break;
314 	case SIOCSIFCAP:
315 		mask = ifp->if_capenable ^ ifr->ifr_reqcap;
316 		if (mask & IFCAP_VLAN_MTU) {
317 			ifp->if_capenable ^= IFCAP_VLAN_MTU;
318 		}
319 		break;
320 
321 	case SIOCSIFADDR:
322 		pio_enable_irq(sc, 1);
323 	default:
324 		error = ether_ioctl(ifp, cmd, data);
325 		break;
326 	}
327 
328 	return (error);
329 }
330 
331 static void
332 vtbe_txfinish_locked(struct vtbe_softc *sc)
333 {
334 	struct ifnet *ifp;
335 
336 	VTBE_ASSERT_LOCKED(sc);
337 
338 	ifp = sc->ifp;
339 }
340 
341 static int
342 vq_init(struct vtbe_softc *sc)
343 {
344 	struct vqueue_info *vq;
345 	uint8_t *base;
346 	int size;
347 	int reg;
348 	int pfn;
349 
350 	vq = &sc->vs_queues[sc->vs_curq];
351 	vq->vq_qsize = DESC_COUNT;
352 
353 	reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN);
354 	pfn = be32toh(reg);
355 	vq->vq_pfn = pfn;
356 
357 	size = vring_size(vq->vq_qsize, VRING_ALIGN);
358 	base = paddr_map(sc->beri_mem_offset,
359 		(pfn << PAGE_SHIFT), size);
360 
361 	/* First pages are descriptors */
362 	vq->vq_desc = (struct vring_desc *)base;
363 	base += vq->vq_qsize * sizeof(struct vring_desc);
364 
365 	/* Then avail ring */
366 	vq->vq_avail = (struct vring_avail *)base;
367 	base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t);
368 
369 	/* Then it's rounded up to the next page */
370 	base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN);
371 
372 	/* And the last pages are the used ring */
373 	vq->vq_used = (struct vring_used *)base;
374 
375 	/* Mark queue as allocated, and start at 0 when we use it. */
376 	vq->vq_flags = VQ_ALLOC;
377 	vq->vq_last_avail = 0;
378 
379 	return (0);
380 }
381 
382 static void
383 vtbe_proc_rx(struct vtbe_softc *sc, struct vqueue_info *vq)
384 {
385 	struct iovec iov[DESC_COUNT];
386 	struct iovec *tiov;
387 	struct ifnet *ifp;
388 	struct uio uio;
389 	struct mbuf *m;
390 	int iolen;
391 	int i;
392 	int n;
393 
394 	ifp = sc->ifp;
395 
396 	n = vq_getchain(sc->beri_mem_offset, vq, iov,
397 		DESC_COUNT, NULL);
398 
399 	KASSERT(n >= 1 && n <= DESC_COUNT,
400 		("wrong n %d", n));
401 
402 	tiov = getcopy(iov, n);
403 
404 	iolen = 0;
405 	for (i = 1; i < n; i++) {
406 		iolen += iov[i].iov_len;
407 	}
408 
409 	uio.uio_resid = iolen;
410 	uio.uio_iov = &tiov[1];
411 	uio.uio_segflg = UIO_SYSSPACE;
412 	uio.uio_iovcnt = (n - 1);
413 	uio.uio_rw = UIO_WRITE;
414 
415 	if ((m = m_uiotombuf(&uio, M_NOWAIT, 0, ETHER_ALIGN,
416 	    M_PKTHDR)) == NULL) {
417 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
418 		goto done;
419 	}
420 
421 	m->m_pkthdr.rcvif = ifp;
422 
423 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
424 
425 	CURVNET_SET(ifp->if_vnet);
426 	VTBE_UNLOCK(sc);
427 	(*ifp->if_input)(ifp, m);
428 	VTBE_LOCK(sc);
429 	CURVNET_RESTORE();
430 
431 done:
432 	free(tiov, M_DEVBUF);
433 	vq_relchain(vq, iov, n, iolen + sc->hdrsize);
434 }
435 
436 static void
437 vtbe_rxfinish_locked(struct vtbe_softc *sc)
438 {
439 	struct vqueue_info *vq;
440 	int reg;
441 
442 	/* TX queue */
443 	vq = &sc->vs_queues[1];
444 	if (!vq_ring_ready(vq))
445 		return;
446 
447 	/* Process new descriptors */
448 	vq->vq_save_used = be16toh(vq->vq_used->idx);
449 
450 	while (vq_has_descs(vq)) {
451 		vtbe_proc_rx(sc, vq);
452 	}
453 
454 	/* Interrupt the other side */
455 	reg = htobe32(VIRTIO_MMIO_INT_VRING);
456 	WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg);
457 
458 	PIO_SET(sc->pio_send, Q_INTR, 1);
459 }
460 
461 static void
462 vtbe_intr(void *arg)
463 {
464 	struct vtbe_softc *sc;
465 	int pending;
466 	uint32_t reg;
467 
468 	sc = arg;
469 
470 	VTBE_LOCK(sc);
471 
472 	reg = PIO_READ(sc->pio_recv);
473 
474 	/* Ack */
475 	PIO_SET(sc->pio_recv, reg, 0);
476 
477 	pending = htobe32(reg);
478 	if (pending & Q_SEL) {
479 		reg = READ4(sc, VIRTIO_MMIO_QUEUE_SEL);
480 		sc->vs_curq = be32toh(reg);
481 	}
482 
483 	if (pending & Q_PFN) {
484 		vq_init(sc);
485 	}
486 
487 	if (pending & Q_NOTIFY) {
488 		/* beri rx / arm tx notify */
489 		vtbe_txfinish_locked(sc);
490 	}
491 
492 	if (pending & Q_NOTIFY1) {
493 		vtbe_rxfinish_locked(sc);
494 	}
495 
496 	VTBE_UNLOCK(sc);
497 }
498 
499 static int
500 vtbe_get_hwaddr(struct vtbe_softc *sc, uint8_t *hwaddr)
501 {
502 	int rnd;
503 
504 	/*
505 	 * Generate MAC address, use 'bsd' + random 24 low-order bits.
506 	 */
507 
508 	rnd = arc4random() & 0x00ffffff;
509 
510 	hwaddr[0] = 'b';
511 	hwaddr[1] = 's';
512 	hwaddr[2] = 'd';
513 	hwaddr[3] = rnd >> 16;
514 	hwaddr[4] = rnd >>  8;
515 	hwaddr[5] = rnd >>  0;
516 
517 	return (0);
518 }
519 
520 static int
521 pio_enable_irq(struct vtbe_softc *sc, int enable)
522 {
523 
524 	/*
525 	 * IRQ lines should be disabled while reprogram FPGA core.
526 	 */
527 
528 	if (enable) {
529 		if (sc->opened == 0) {
530 			sc->opened = 1;
531 			PIO_SETUP_IRQ(sc->pio_recv, vtbe_intr, sc);
532 		}
533 	} else {
534 		if (sc->opened == 1) {
535 			PIO_TEARDOWN_IRQ(sc->pio_recv);
536 			sc->opened = 0;
537 		}
538 	}
539 
540 	return (0);
541 }
542 
543 static int
544 vtbe_probe(device_t dev)
545 {
546 
547 	if (!ofw_bus_status_okay(dev))
548 		return (ENXIO);
549 
550 	if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtnet"))
551 		return (ENXIO);
552 
553 	device_set_desc(dev, "Virtio BERI Ethernet Controller");
554 	return (BUS_PROBE_DEFAULT);
555 }
556 
557 static int
558 vtbe_attach(device_t dev)
559 {
560 	uint8_t macaddr[ETHER_ADDR_LEN];
561 	struct vtbe_softc *sc;
562 	struct ifnet *ifp;
563 	int reg;
564 
565 	sc = device_get_softc(dev);
566 	sc->dev = dev;
567 
568 	sc->hdrsize = sizeof(struct virtio_net_hdr);
569 
570 	if (bus_alloc_resources(dev, vtbe_spec, sc->res)) {
571 		device_printf(dev, "could not allocate resources\n");
572 		return (ENXIO);
573 	}
574 
575 	/* Memory interface */
576 	sc->bst = rman_get_bustag(sc->res[0]);
577 	sc->bsh = rman_get_bushandle(sc->res[0]);
578 
579 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
580 	    MTX_NETWORK_LOCK, MTX_DEF);
581 
582 	if (setup_offset(dev, &sc->beri_mem_offset) != 0)
583 		return (ENXIO);
584 	if (setup_pio(dev, "pio-send", &sc->pio_send) != 0)
585 		return (ENXIO);
586 	if (setup_pio(dev, "pio-recv", &sc->pio_recv) != 0)
587 		return (ENXIO);
588 
589 	/* Setup MMIO */
590 
591 	/* Specify that we provide network device */
592 	reg = htobe32(VIRTIO_ID_NETWORK);
593 	WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg);
594 
595 	/* The number of desc we support */
596 	reg = htobe32(DESC_COUNT);
597 	WRITE4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX, reg);
598 
599 	/* Our features */
600 	reg = htobe32(VIRTIO_NET_F_MAC |
601     			VIRTIO_F_NOTIFY_ON_EMPTY);
602 	WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg);
603 
604 	/* Get MAC */
605 	if (vtbe_get_hwaddr(sc, macaddr)) {
606 		device_printf(sc->dev, "can't get mac\n");
607 		return (ENXIO);
608 	}
609 
610 	/* Set up the ethernet interface. */
611 	sc->ifp = ifp = if_alloc(IFT_ETHER);
612 	ifp->if_baudrate = IF_Gbps(10);
613 	ifp->if_softc = sc;
614 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
615 	ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX |
616 			 IFF_MULTICAST | IFF_PROMISC);
617 	ifp->if_capabilities = IFCAP_VLAN_MTU;
618 	ifp->if_capenable = ifp->if_capabilities;
619 	ifp->if_start = vtbe_txstart;
620 	ifp->if_ioctl = vtbe_ioctl;
621 	ifp->if_init = vtbe_init;
622 	IFQ_SET_MAXLEN(&ifp->if_snd, DESC_COUNT - 1);
623 	ifp->if_snd.ifq_drv_maxlen = DESC_COUNT - 1;
624 	IFQ_SET_READY(&ifp->if_snd);
625 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
626 
627 	/* All ready to run, attach the ethernet interface. */
628 	ether_ifattach(ifp, macaddr);
629 
630 	sc->is_attached = true;
631 
632 	return (0);
633 }
634 
635 static device_method_t vtbe_methods[] = {
636 	DEVMETHOD(device_probe,		vtbe_probe),
637 	DEVMETHOD(device_attach,	vtbe_attach),
638 
639 	{ 0, 0 }
640 };
641 
642 static driver_t vtbe_driver = {
643 	"vtbe",
644 	vtbe_methods,
645 	sizeof(struct vtbe_softc),
646 };
647 
648 static devclass_t vtbe_devclass;
649 
650 DRIVER_MODULE(vtbe, simplebus, vtbe_driver, vtbe_devclass, 0, 0);
651 MODULE_DEPEND(vtbe, ether, 1, 1, 1);
652