virtio.c revision 276031
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 mmio backend common methods
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/sys/dev/beri/virtio/virtio.c 276031 2014-12-21 21:24:19Z ian $");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/bus.h>
41#include <sys/cdefs.h>
42#include <sys/kernel.h>
43#include <sys/module.h>
44#include <sys/malloc.h>
45#include <sys/rman.h>
46#include <sys/timeet.h>
47#include <sys/timetc.h>
48#include <sys/conf.h>
49#include <sys/uio.h>
50#include <sys/stat.h>
51#include <sys/time.h>
52#include <sys/event.h>
53#include <sys/selinfo.h>
54#include <sys/endian.h>
55#include <sys/rwlock.h>
56
57#include <machine/bus.h>
58#include <machine/fdt.h>
59#include <machine/cpu.h>
60#include <machine/intr.h>
61
62#include <dev/fdt/fdt_common.h>
63#include <dev/ofw/openfirm.h>
64#include <dev/ofw/ofw_bus.h>
65#include <dev/ofw/ofw_bus_subr.h>
66
67#include <dev/beri/virtio/virtio.h>
68#include <dev/virtio/virtio.h>
69#include <dev/virtio/virtqueue.h>
70#include <dev/virtio/virtio_ring.h>
71#include <dev/altera/pio/pio.h>
72
73#include "pio_if.h"
74
75int
76vq_ring_ready(struct vqueue_info *vq)
77{
78
79	return (vq->vq_flags & VQ_ALLOC);
80}
81
82int
83vq_has_descs(struct vqueue_info *vq)
84{
85
86	return (vq_ring_ready(vq) && vq->vq_last_avail !=
87		be16toh(vq->vq_avail->idx));
88}
89
90void *
91paddr_map(uint32_t offset, uint32_t phys, uint32_t size)
92{
93	bus_space_handle_t bsh;
94
95	if (bus_space_map(fdtbus_bs_tag, (phys + offset),
96			size, 0, &bsh) != 0) {
97		panic("Couldn't map 0x%08x\n", (phys + offset));
98	}
99
100	return (void *)(bsh);
101}
102
103void
104paddr_unmap(void *phys, uint32_t size)
105{
106
107	bus_space_unmap(fdtbus_bs_tag, (bus_space_handle_t)phys, size);
108}
109
110static inline void
111_vq_record(uint32_t offs, int i, volatile struct vring_desc *vd,
112	struct iovec *iov, int n_iov, uint16_t *flags) {
113
114	if (i >= n_iov)
115		return;
116
117	iov[i].iov_base = paddr_map(offs, be64toh(vd->addr),
118				be32toh(vd->len));
119	iov[i].iov_len = be32toh(vd->len);
120	if (flags != NULL)
121		flags[i] = be16toh(vd->flags);
122}
123
124int
125vq_getchain(uint32_t offs, struct vqueue_info *vq,
126	struct iovec *iov, int n_iov, uint16_t *flags)
127{
128	volatile struct vring_desc *vdir, *vindir, *vp;
129	int idx, ndesc, n_indir;
130	int head, next;
131	int i;
132
133	idx = vq->vq_last_avail;
134	ndesc = (be16toh(vq->vq_avail->idx) - idx);
135	if (ndesc == 0)
136		return (0);
137
138	head = be16toh(vq->vq_avail->ring[idx & (vq->vq_qsize - 1)]);
139	next = head;
140
141	for (i = 0; i < VQ_MAX_DESCRIPTORS; next = be16toh(vdir->next)) {
142		vdir = &vq->vq_desc[next];
143		if ((be16toh(vdir->flags) & VRING_DESC_F_INDIRECT) == 0) {
144			_vq_record(offs, i, vdir, iov, n_iov, flags);
145			i++;
146		} else {
147			n_indir = be32toh(vdir->len) / 16;
148			vindir = paddr_map(offs, be64toh(vdir->addr),
149					be32toh(vdir->len));
150			next = 0;
151			for (;;) {
152				vp = &vindir[next];
153				_vq_record(offs, i, vp, iov, n_iov, flags);
154				i+=1;
155				if ((be16toh(vp->flags) & \
156					VRING_DESC_F_NEXT) == 0)
157					break;
158				next = be16toh(vp->next);
159			}
160			paddr_unmap(__DEVOLATILE(void *, vindir), be32toh(vdir->len));
161		}
162
163		if ((be16toh(vdir->flags) & VRING_DESC_F_NEXT) == 0)
164			return (i);
165	}
166
167	return (i);
168}
169
170void
171vq_relchain(struct vqueue_info *vq, struct iovec *iov, int n, uint32_t iolen)
172{
173	volatile struct vring_used_elem *vue;
174	volatile struct vring_used *vu;
175	uint16_t head, uidx, mask;
176	int i;
177
178	mask = vq->vq_qsize - 1;
179	vu = vq->vq_used;
180	head = be16toh(vq->vq_avail->ring[vq->vq_last_avail++ & mask]);
181
182	uidx = be16toh(vu->idx);
183	vue = &vu->ring[uidx++ & mask];
184	vue->id = htobe32(head);
185
186	vue->len = htobe32(iolen);
187	vu->idx = htobe16(uidx);
188
189	/* Clean up */
190	for (i = 1; i < (n-1); i++) {
191		paddr_unmap((void *)iov[i].iov_base, iov[i].iov_len);
192	}
193}
194
195int
196setup_pio(device_t dev, char *name, device_t *pio_dev)
197{
198	phandle_t pio_node;
199	struct fdt_ic *ic;
200	phandle_t xref;
201	phandle_t node;
202
203	if ((node = ofw_bus_get_node(dev)) == -1)
204		return (ENXIO);
205
206	if (OF_searchencprop(node, name, &xref,
207		sizeof(xref)) == -1) {
208		return (ENXIO);
209	}
210
211	pio_node = OF_node_from_xref(xref);
212	SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) {
213		if (ic->iph == pio_node) {
214			*pio_dev = ic->dev;
215			PIO_CONFIGURE(*pio_dev, PIO_OUT_ALL,
216					PIO_UNMASK_ALL);
217			return (0);
218		}
219	}
220
221	return (ENXIO);
222}
223
224int
225setup_offset(device_t dev, uint32_t *offset)
226{
227	pcell_t dts_value[2];
228	phandle_t mem_node;
229	phandle_t xref;
230	phandle_t node;
231	int len;
232
233	if ((node = ofw_bus_get_node(dev)) == -1)
234		return (ENXIO);
235
236	if (OF_searchencprop(node, "beri-mem", &xref,
237		sizeof(xref)) == -1) {
238		return (ENXIO);
239	}
240
241	mem_node = OF_node_from_xref(xref);
242	if ((len = OF_getproplen(mem_node, "reg")) <= 0)
243		return (ENXIO);
244	OF_getencprop(mem_node, "reg", dts_value, len);
245	*offset = dts_value[0];
246
247	return (0);
248}
249
250