xref: /minix3/minix/drivers/net/virtio_net/virtio_net.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* virtio net driver for MINIX 3
2*433d6423SLionel Sambuc  *
3*433d6423SLionel Sambuc  * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
4*433d6423SLionel Sambuc  *
5*433d6423SLionel Sambuc  * This software is released under the BSD license. See the LICENSE file
6*433d6423SLionel Sambuc  * included in the main directory of this source distribution for the
7*433d6423SLionel Sambuc  * license terms and conditions.
8*433d6423SLionel Sambuc  */
9*433d6423SLionel Sambuc 
10*433d6423SLionel Sambuc #include <assert.h>
11*433d6423SLionel Sambuc #include <sys/types.h>
12*433d6423SLionel Sambuc 
13*433d6423SLionel Sambuc #include <net/gen/ether.h>
14*433d6423SLionel Sambuc #include <net/gen/eth_io.h>
15*433d6423SLionel Sambuc 
16*433d6423SLionel Sambuc #include <minix/drivers.h>
17*433d6423SLionel Sambuc #include <minix/netdriver.h>
18*433d6423SLionel Sambuc #include <minix/sysutil.h>
19*433d6423SLionel Sambuc #include <minix/virtio.h>
20*433d6423SLionel Sambuc 
21*433d6423SLionel Sambuc #include <sys/queue.h>
22*433d6423SLionel Sambuc 
23*433d6423SLionel Sambuc #include "virtio_net.h"
24*433d6423SLionel Sambuc 
25*433d6423SLionel Sambuc #define dput(s)		do { dprintf(s); printf("\n"); } while (0)
26*433d6423SLionel Sambuc #define dprintf(s) do {						\
27*433d6423SLionel Sambuc 	printf("%s: ", name);					\
28*433d6423SLionel Sambuc 	printf s;						\
29*433d6423SLionel Sambuc } while (0)
30*433d6423SLionel Sambuc 
31*433d6423SLionel Sambuc static struct virtio_device *net_dev;
32*433d6423SLionel Sambuc 
33*433d6423SLionel Sambuc static const char *const name = "virtio-net";
34*433d6423SLionel Sambuc 
35*433d6423SLionel Sambuc enum queue {RX_Q, TX_Q, CTRL_Q};
36*433d6423SLionel Sambuc 
37*433d6423SLionel Sambuc /* Number of packets to work with */
38*433d6423SLionel Sambuc /* TODO: This should be an argument to the driver and possibly also
39*433d6423SLionel Sambuc  *       depend on the queue sizes offered by this device.
40*433d6423SLionel Sambuc  */
41*433d6423SLionel Sambuc #define BUF_PACKETS		64
42*433d6423SLionel Sambuc /* Maximum size of a packet */
43*433d6423SLionel Sambuc #define MAX_PACK_SIZE		ETH_MAX_PACK_SIZE
44*433d6423SLionel Sambuc /* Buffer size needed for the payload of BUF_PACKETS */
45*433d6423SLionel Sambuc #define PACKET_BUF_SZ		(BUF_PACKETS * MAX_PACK_SIZE)
46*433d6423SLionel Sambuc 
47*433d6423SLionel Sambuc struct packet {
48*433d6423SLionel Sambuc 	int idx;
49*433d6423SLionel Sambuc 	struct virtio_net_hdr *vhdr;
50*433d6423SLionel Sambuc 	phys_bytes phdr;
51*433d6423SLionel Sambuc 	char *vdata;
52*433d6423SLionel Sambuc 	phys_bytes pdata;
53*433d6423SLionel Sambuc 	STAILQ_ENTRY(packet) next;
54*433d6423SLionel Sambuc };
55*433d6423SLionel Sambuc 
56*433d6423SLionel Sambuc /* Allocated data chunks */
57*433d6423SLionel Sambuc static char *data_vir;
58*433d6423SLionel Sambuc static phys_bytes data_phys;
59*433d6423SLionel Sambuc static struct virtio_net_hdr *hdrs_vir;
60*433d6423SLionel Sambuc static phys_bytes hdrs_phys;
61*433d6423SLionel Sambuc static struct packet *packets;
62*433d6423SLionel Sambuc static int in_rx;
63*433d6423SLionel Sambuc static int started;
64*433d6423SLionel Sambuc 
65*433d6423SLionel Sambuc /* Packets on this list can be given to the host */
66*433d6423SLionel Sambuc static STAILQ_HEAD(free_list, packet) free_list;
67*433d6423SLionel Sambuc 
68*433d6423SLionel Sambuc /* Packets on this list are to be given to inet */
69*433d6423SLionel Sambuc static STAILQ_HEAD(recv_list, packet) recv_list;
70*433d6423SLionel Sambuc 
71*433d6423SLionel Sambuc /* State about pending inet messages */
72*433d6423SLionel Sambuc static int rx_pending;
73*433d6423SLionel Sambuc static message pending_rx_msg;
74*433d6423SLionel Sambuc static int tx_pending;
75*433d6423SLionel Sambuc static message pending_tx_msg;
76*433d6423SLionel Sambuc 
77*433d6423SLionel Sambuc /* Various state data */
78*433d6423SLionel Sambuc static u8_t virtio_net_mac[6];
79*433d6423SLionel Sambuc static eth_stat_t virtio_net_stats;
80*433d6423SLionel Sambuc static int spurious_interrupt;
81*433d6423SLionel Sambuc 
82*433d6423SLionel Sambuc 
83*433d6423SLionel Sambuc /* Prototypes */
84*433d6423SLionel Sambuc static int virtio_net_probe(int skip);
85*433d6423SLionel Sambuc static int virtio_net_config(void);
86*433d6423SLionel Sambuc static int virtio_net_alloc_bufs(void);
87*433d6423SLionel Sambuc static void virtio_net_init_queues(void);
88*433d6423SLionel Sambuc 
89*433d6423SLionel Sambuc static void virtio_net_refill_rx_queue(void);
90*433d6423SLionel Sambuc static void virtio_net_check_queues(void);
91*433d6423SLionel Sambuc static void virtio_net_check_pending(void);
92*433d6423SLionel Sambuc 
93*433d6423SLionel Sambuc static void virtio_net_fetch_iovec(iovec_s_t *iov, message *m,
94*433d6423SLionel Sambuc 	cp_grant_id_t grant, size_t count);
95*433d6423SLionel Sambuc static int virtio_net_cpy_to_user(message *m);
96*433d6423SLionel Sambuc static int virtio_net_cpy_from_user(message *m);
97*433d6423SLionel Sambuc 
98*433d6423SLionel Sambuc static void virtio_net_intr(message *m);
99*433d6423SLionel Sambuc static void virtio_net_write(message *m);
100*433d6423SLionel Sambuc static void virtio_net_read(message *m);
101*433d6423SLionel Sambuc static void virtio_net_conf(message *m);
102*433d6423SLionel Sambuc static void virtio_net_getstat(message *m);
103*433d6423SLionel Sambuc 
104*433d6423SLionel Sambuc static void virtio_net_notify(message *m);
105*433d6423SLionel Sambuc static void virtio_net_msg(message *m);
106*433d6423SLionel Sambuc static void virtio_net_main_loop(void);
107*433d6423SLionel Sambuc 
108*433d6423SLionel Sambuc static void sef_local_startup(void);
109*433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *info);
110*433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo);
111*433d6423SLionel Sambuc 
112*433d6423SLionel Sambuc 
113*433d6423SLionel Sambuc /* TODO: Features are pretty much ignored */
114*433d6423SLionel Sambuc struct virtio_feature netf[] = {
115*433d6423SLionel Sambuc 	{ "partial csum",	VIRTIO_NET_F_CSUM,	0,	0	},
116*433d6423SLionel Sambuc 	{ "given mac",		VIRTIO_NET_F_MAC,	0,	1	},
117*433d6423SLionel Sambuc 	{ "status ",		VIRTIO_NET_F_STATUS,	0,	0	},
118*433d6423SLionel Sambuc 	{ "control channel",	VIRTIO_NET_F_CTRL_VQ,	0,	1	},
119*433d6423SLionel Sambuc 	{ "control channel rx",	VIRTIO_NET_F_CTRL_RX,	0,	0	}
120*433d6423SLionel Sambuc };
121*433d6423SLionel Sambuc 
122*433d6423SLionel Sambuc static int
123*433d6423SLionel Sambuc virtio_net_probe(int skip)
124*433d6423SLionel Sambuc {
125*433d6423SLionel Sambuc 	/* virtio-net has at least 2 queues */
126*433d6423SLionel Sambuc 	int queues = 2;
127*433d6423SLionel Sambuc 	net_dev= virtio_setup_device(0x00001, name, netf,
128*433d6423SLionel Sambuc 				     sizeof(netf) / sizeof(netf[0]),
129*433d6423SLionel Sambuc 				     1 /* threads */, skip);
130*433d6423SLionel Sambuc 	if (net_dev == NULL)
131*433d6423SLionel Sambuc 		return ENXIO;
132*433d6423SLionel Sambuc 
133*433d6423SLionel Sambuc 	/* If the host supports the control queue, allocate it as well */
134*433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
135*433d6423SLionel Sambuc 		queues += 1;
136*433d6423SLionel Sambuc 
137*433d6423SLionel Sambuc 	if (virtio_alloc_queues(net_dev, queues) != OK) {
138*433d6423SLionel Sambuc 		virtio_free_device(net_dev);
139*433d6423SLionel Sambuc 		return ENOMEM;
140*433d6423SLionel Sambuc 	}
141*433d6423SLionel Sambuc 
142*433d6423SLionel Sambuc 	return OK;
143*433d6423SLionel Sambuc }
144*433d6423SLionel Sambuc 
145*433d6423SLionel Sambuc static int
146*433d6423SLionel Sambuc virtio_net_config(void)
147*433d6423SLionel Sambuc {
148*433d6423SLionel Sambuc 	u32_t mac14;
149*433d6423SLionel Sambuc 	u32_t mac56;
150*433d6423SLionel Sambuc 	int i;
151*433d6423SLionel Sambuc 
152*433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
153*433d6423SLionel Sambuc 		dprintf(("Mac set by host: "));
154*433d6423SLionel Sambuc 		mac14 = virtio_sread32(net_dev, 0);
155*433d6423SLionel Sambuc 		mac56 = virtio_sread32(net_dev, 4);
156*433d6423SLionel Sambuc 		*(u32_t*)virtio_net_mac = mac14;
157*433d6423SLionel Sambuc 		*(u16_t*)(virtio_net_mac + 4) = mac56;
158*433d6423SLionel Sambuc 
159*433d6423SLionel Sambuc 		for (i = 0; i < 6; i++)
160*433d6423SLionel Sambuc 			printf("%02x%s", virtio_net_mac[i],
161*433d6423SLionel Sambuc 					 i == 5 ? "\n" : ":");
162*433d6423SLionel Sambuc 	} else {
163*433d6423SLionel Sambuc 		dput(("No mac"));
164*433d6423SLionel Sambuc 	}
165*433d6423SLionel Sambuc 
166*433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
167*433d6423SLionel Sambuc 		dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
168*433d6423SLionel Sambuc 	} else {
169*433d6423SLionel Sambuc 		dput(("No status"));
170*433d6423SLionel Sambuc 	}
171*433d6423SLionel Sambuc 
172*433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
173*433d6423SLionel Sambuc 		dput(("Host supports control channel"));
174*433d6423SLionel Sambuc 
175*433d6423SLionel Sambuc 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
176*433d6423SLionel Sambuc 		dput(("Host supports control channel for RX"));
177*433d6423SLionel Sambuc 
178*433d6423SLionel Sambuc 	return OK;
179*433d6423SLionel Sambuc }
180*433d6423SLionel Sambuc 
181*433d6423SLionel Sambuc static int
182*433d6423SLionel Sambuc virtio_net_alloc_bufs(void)
183*433d6423SLionel Sambuc {
184*433d6423SLionel Sambuc 	data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
185*433d6423SLionel Sambuc 
186*433d6423SLionel Sambuc 	if (!data_vir)
187*433d6423SLionel Sambuc 		return ENOMEM;
188*433d6423SLionel Sambuc 
189*433d6423SLionel Sambuc 	hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
190*433d6423SLionel Sambuc 				 0, &hdrs_phys);
191*433d6423SLionel Sambuc 
192*433d6423SLionel Sambuc 	if (!hdrs_vir) {
193*433d6423SLionel Sambuc 		free_contig(data_vir, PACKET_BUF_SZ);
194*433d6423SLionel Sambuc 		return ENOMEM;
195*433d6423SLionel Sambuc 	}
196*433d6423SLionel Sambuc 
197*433d6423SLionel Sambuc 	packets = malloc(BUF_PACKETS * sizeof(packets[0]));
198*433d6423SLionel Sambuc 
199*433d6423SLionel Sambuc 	if (!packets) {
200*433d6423SLionel Sambuc 		free_contig(data_vir, PACKET_BUF_SZ);
201*433d6423SLionel Sambuc 		free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
202*433d6423SLionel Sambuc 		return ENOMEM;
203*433d6423SLionel Sambuc 	}
204*433d6423SLionel Sambuc 
205*433d6423SLionel Sambuc 	memset(data_vir, 0, PACKET_BUF_SZ);
206*433d6423SLionel Sambuc 	memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
207*433d6423SLionel Sambuc 	memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
208*433d6423SLionel Sambuc 
209*433d6423SLionel Sambuc 	return OK;
210*433d6423SLionel Sambuc }
211*433d6423SLionel Sambuc 
212*433d6423SLionel Sambuc static void
213*433d6423SLionel Sambuc virtio_net_init_queues(void)
214*433d6423SLionel Sambuc {
215*433d6423SLionel Sambuc 	int i;
216*433d6423SLionel Sambuc 	STAILQ_INIT(&free_list);
217*433d6423SLionel Sambuc 	STAILQ_INIT(&recv_list);
218*433d6423SLionel Sambuc 
219*433d6423SLionel Sambuc 	for (i = 0; i < BUF_PACKETS; i++) {
220*433d6423SLionel Sambuc 		packets[i].idx = i;
221*433d6423SLionel Sambuc 		packets[i].vhdr = &hdrs_vir[i];
222*433d6423SLionel Sambuc 		packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
223*433d6423SLionel Sambuc 		packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
224*433d6423SLionel Sambuc 		packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
225*433d6423SLionel Sambuc 		STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
226*433d6423SLionel Sambuc 	}
227*433d6423SLionel Sambuc }
228*433d6423SLionel Sambuc 
229*433d6423SLionel Sambuc static void
230*433d6423SLionel Sambuc virtio_net_refill_rx_queue(void)
231*433d6423SLionel Sambuc {
232*433d6423SLionel Sambuc 	struct vumap_phys phys[2];
233*433d6423SLionel Sambuc 	struct packet *p;
234*433d6423SLionel Sambuc 
235*433d6423SLionel Sambuc 	while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
236*433d6423SLionel Sambuc 
237*433d6423SLionel Sambuc 		/* peek */
238*433d6423SLionel Sambuc 		p = STAILQ_FIRST(&free_list);
239*433d6423SLionel Sambuc 		/* remove */
240*433d6423SLionel Sambuc 		STAILQ_REMOVE_HEAD(&free_list, next);
241*433d6423SLionel Sambuc 
242*433d6423SLionel Sambuc 		phys[0].vp_addr = p->phdr;
243*433d6423SLionel Sambuc 		assert(!(phys[0].vp_addr & 1));
244*433d6423SLionel Sambuc 		phys[0].vp_size = sizeof(struct virtio_net_hdr);
245*433d6423SLionel Sambuc 
246*433d6423SLionel Sambuc 		phys[1].vp_addr = p->pdata;
247*433d6423SLionel Sambuc 		assert(!(phys[1].vp_addr & 1));
248*433d6423SLionel Sambuc 		phys[1].vp_size = MAX_PACK_SIZE;
249*433d6423SLionel Sambuc 
250*433d6423SLionel Sambuc 		/* RX queue needs write */
251*433d6423SLionel Sambuc 		phys[0].vp_addr |= 1;
252*433d6423SLionel Sambuc 		phys[1].vp_addr |= 1;
253*433d6423SLionel Sambuc 
254*433d6423SLionel Sambuc 		virtio_to_queue(net_dev, RX_Q, phys, 2, p);
255*433d6423SLionel Sambuc 		in_rx++;
256*433d6423SLionel Sambuc 
257*433d6423SLionel Sambuc 	}
258*433d6423SLionel Sambuc 
259*433d6423SLionel Sambuc 	if (in_rx == 0 && STAILQ_EMPTY(&free_list)) {
260*433d6423SLionel Sambuc 		dput(("warning: rx queue underflow!"));
261*433d6423SLionel Sambuc 		virtio_net_stats.ets_fifoUnder++;
262*433d6423SLionel Sambuc 	}
263*433d6423SLionel Sambuc }
264*433d6423SLionel Sambuc 
265*433d6423SLionel Sambuc static void
266*433d6423SLionel Sambuc virtio_net_check_queues(void)
267*433d6423SLionel Sambuc {
268*433d6423SLionel Sambuc 	struct packet *p;
269*433d6423SLionel Sambuc 
270*433d6423SLionel Sambuc 	/* Put the received packets into the recv list */
271*433d6423SLionel Sambuc 	while (virtio_from_queue(net_dev, RX_Q, (void **)&p) == 0) {
272*433d6423SLionel Sambuc 		STAILQ_INSERT_TAIL(&recv_list, p, next);
273*433d6423SLionel Sambuc 		in_rx--;
274*433d6423SLionel Sambuc 		virtio_net_stats.ets_packetR++;
275*433d6423SLionel Sambuc 	}
276*433d6423SLionel Sambuc 
277*433d6423SLionel Sambuc 	/* Packets from the TX queue just indicated they are free to
278*433d6423SLionel Sambuc 	 * be reused now. inet already knows about them as being sent.
279*433d6423SLionel Sambuc 	 */
280*433d6423SLionel Sambuc 	while (virtio_from_queue(net_dev, TX_Q, (void **)&p) == 0) {
281*433d6423SLionel Sambuc 		memset(p->vhdr, 0, sizeof(*p->vhdr));
282*433d6423SLionel Sambuc 		memset(p->vdata, 0, MAX_PACK_SIZE);
283*433d6423SLionel Sambuc 		STAILQ_INSERT_HEAD(&free_list, p, next);
284*433d6423SLionel Sambuc 		virtio_net_stats.ets_packetT++;
285*433d6423SLionel Sambuc 	}
286*433d6423SLionel Sambuc }
287*433d6423SLionel Sambuc 
288*433d6423SLionel Sambuc static void
289*433d6423SLionel Sambuc virtio_net_check_pending(void)
290*433d6423SLionel Sambuc {
291*433d6423SLionel Sambuc 	int dst = 0xDEAD;
292*433d6423SLionel Sambuc 	int r;
293*433d6423SLionel Sambuc 
294*433d6423SLionel Sambuc 	message reply;
295*433d6423SLionel Sambuc 	reply.m_type = DL_TASK_REPLY;
296*433d6423SLionel Sambuc 	reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS;
297*433d6423SLionel Sambuc 	reply.m_netdrv_net_dl_task.count = 0;
298*433d6423SLionel Sambuc 
299*433d6423SLionel Sambuc 	/* Pending read and something in recv_list? */
300*433d6423SLionel Sambuc 	if (!STAILQ_EMPTY(&recv_list) && rx_pending) {
301*433d6423SLionel Sambuc 		dst = pending_rx_msg.m_source;
302*433d6423SLionel Sambuc 		reply.m_netdrv_net_dl_task.count =
303*433d6423SLionel Sambuc 			virtio_net_cpy_to_user(&pending_rx_msg);
304*433d6423SLionel Sambuc 		reply.m_netdrv_net_dl_task.flags |= DL_PACK_RECV;
305*433d6423SLionel Sambuc 		rx_pending = 0;
306*433d6423SLionel Sambuc 	}
307*433d6423SLionel Sambuc 
308*433d6423SLionel Sambuc 	if (!STAILQ_EMPTY(&free_list) && tx_pending) {
309*433d6423SLionel Sambuc 		dst = pending_tx_msg.m_source;
310*433d6423SLionel Sambuc 		virtio_net_cpy_from_user(&pending_tx_msg);
311*433d6423SLionel Sambuc 		reply.m_netdrv_net_dl_task.flags |= DL_PACK_SEND;
312*433d6423SLionel Sambuc 		tx_pending = 0;
313*433d6423SLionel Sambuc 	}
314*433d6423SLionel Sambuc 
315*433d6423SLionel Sambuc 	/* Only reply if a pending request was handled */
316*433d6423SLionel Sambuc 	if (reply.m_netdrv_net_dl_task.flags != DL_NOFLAGS)
317*433d6423SLionel Sambuc 		if ((r = ipc_send(dst, &reply)) != OK)
318*433d6423SLionel Sambuc 			panic("%s: ipc_send to %d failed (%d)", name, dst, r);
319*433d6423SLionel Sambuc }
320*433d6423SLionel Sambuc 
321*433d6423SLionel Sambuc static void
322*433d6423SLionel Sambuc virtio_net_fetch_iovec(iovec_s_t *iov, message *m, cp_grant_id_t grant, size_t count)
323*433d6423SLionel Sambuc {
324*433d6423SLionel Sambuc 	int r;
325*433d6423SLionel Sambuc 	r = sys_safecopyfrom(m->m_source, grant, 0, (vir_bytes)iov,
326*433d6423SLionel Sambuc 		count * sizeof(iov[0]));
327*433d6423SLionel Sambuc 
328*433d6423SLionel Sambuc 	if (r != OK)
329*433d6423SLionel Sambuc 		panic("%s: iovec fail for %d (%d)", name, m->m_source, r);
330*433d6423SLionel Sambuc }
331*433d6423SLionel Sambuc 
332*433d6423SLionel Sambuc static int
333*433d6423SLionel Sambuc virtio_net_cpy_to_user(message *m)
334*433d6423SLionel Sambuc {
335*433d6423SLionel Sambuc 	/* Hmm, this looks so similar to cpy_from_user... TODO */
336*433d6423SLionel Sambuc 	int i, r, size, ivsz;
337*433d6423SLionel Sambuc 	int left = MAX_PACK_SIZE;	/* Try copying the whole packet */
338*433d6423SLionel Sambuc 	int bytes = 0;
339*433d6423SLionel Sambuc 	iovec_s_t iovec[NR_IOREQS];
340*433d6423SLionel Sambuc 	struct packet *p;
341*433d6423SLionel Sambuc 
342*433d6423SLionel Sambuc 	/* This should only be called if recv_list has some entries */
343*433d6423SLionel Sambuc 	assert(!STAILQ_EMPTY(&recv_list));
344*433d6423SLionel Sambuc 
345*433d6423SLionel Sambuc 	p = STAILQ_FIRST(&recv_list);
346*433d6423SLionel Sambuc 	STAILQ_REMOVE_HEAD(&recv_list, next);
347*433d6423SLionel Sambuc 
348*433d6423SLionel Sambuc 	virtio_net_fetch_iovec(iovec, m, m->m_net_netdrv_dl_readv_s.grant,
349*433d6423SLionel Sambuc 		 m->m_net_netdrv_dl_readv_s.count);
350*433d6423SLionel Sambuc 
351*433d6423SLionel Sambuc 	for (i = 0; i < m->m_net_netdrv_dl_readv_s.count && left > 0; i++) {
352*433d6423SLionel Sambuc 		ivsz = iovec[i].iov_size;
353*433d6423SLionel Sambuc 		size = left > ivsz ? ivsz : left;
354*433d6423SLionel Sambuc 		r = sys_safecopyto(m->m_source, iovec[i].iov_grant, 0,
355*433d6423SLionel Sambuc 				   (vir_bytes) p->vdata + bytes, size);
356*433d6423SLionel Sambuc 
357*433d6423SLionel Sambuc 		if (r != OK)
358*433d6423SLionel Sambuc 			panic("%s: copy to %d failed (%d)", name,
359*433d6423SLionel Sambuc 							    m->m_source,
360*433d6423SLionel Sambuc 							    r);
361*433d6423SLionel Sambuc 
362*433d6423SLionel Sambuc 		left -= size;
363*433d6423SLionel Sambuc 		bytes += size;
364*433d6423SLionel Sambuc 	}
365*433d6423SLionel Sambuc 
366*433d6423SLionel Sambuc 	if (left != 0)
367*433d6423SLionel Sambuc 		dput(("Uhm... left=%d", left));
368*433d6423SLionel Sambuc 
369*433d6423SLionel Sambuc 	/* Clean the packet */
370*433d6423SLionel Sambuc 	memset(p->vhdr, 0, sizeof(*p->vhdr));
371*433d6423SLionel Sambuc 	memset(p->vdata, 0, MAX_PACK_SIZE);
372*433d6423SLionel Sambuc 	STAILQ_INSERT_HEAD(&free_list, p, next);
373*433d6423SLionel Sambuc 
374*433d6423SLionel Sambuc 	return bytes;
375*433d6423SLionel Sambuc }
376*433d6423SLionel Sambuc 
377*433d6423SLionel Sambuc static int
378*433d6423SLionel Sambuc sys_easy_vsafecopy_from(endpoint_t src_proc, iovec_s_t *iov, int count,
379*433d6423SLionel Sambuc 			vir_bytes dst, size_t max, size_t *copied)
380*433d6423SLionel Sambuc {
381*433d6423SLionel Sambuc 	int i, r;
382*433d6423SLionel Sambuc 	size_t left = max;
383*433d6423SLionel Sambuc 	vir_bytes cur_off = 0;
384*433d6423SLionel Sambuc 	struct vscp_vec vv[NR_IOREQS];
385*433d6423SLionel Sambuc 
386*433d6423SLionel Sambuc 	for (i = 0; i < count && left > 0; i++) {
387*433d6423SLionel Sambuc 		vv[i].v_from = src_proc;
388*433d6423SLionel Sambuc 		vv[i].v_to = SELF;
389*433d6423SLionel Sambuc 		vv[i].v_gid = iov[i].iov_grant;
390*433d6423SLionel Sambuc 		vv[i].v_offset = 0;
391*433d6423SLionel Sambuc 		vv[i].v_addr = dst + cur_off;
392*433d6423SLionel Sambuc 		vv[i].v_bytes = iov[i].iov_size;
393*433d6423SLionel Sambuc 
394*433d6423SLionel Sambuc 		/* More data in iov than the buffer can hold, this should be
395*433d6423SLionel Sambuc 		 * manageable by the caller.
396*433d6423SLionel Sambuc 		 */
397*433d6423SLionel Sambuc 		if (left - vv[i].v_bytes > left) {
398*433d6423SLionel Sambuc 			printf("sys_easy_vsafecopy_from: buf too small!\n");
399*433d6423SLionel Sambuc 			return ENOMEM;
400*433d6423SLionel Sambuc 		}
401*433d6423SLionel Sambuc 
402*433d6423SLionel Sambuc 		left -= iov[i].iov_size;
403*433d6423SLionel Sambuc 		cur_off += iov[i].iov_size;
404*433d6423SLionel Sambuc 	}
405*433d6423SLionel Sambuc 
406*433d6423SLionel Sambuc 	/* Now that we prepared the vscp_vec, we can call vsafecopy() */
407*433d6423SLionel Sambuc 	if ((r = sys_vsafecopy(vv, count)) != OK)
408*433d6423SLionel Sambuc 		printf("sys_vsafecopy: failed: (%d)\n", r);
409*433d6423SLionel Sambuc 
410*433d6423SLionel Sambuc 	if (copied)
411*433d6423SLionel Sambuc 		*copied = cur_off;
412*433d6423SLionel Sambuc 
413*433d6423SLionel Sambuc 	return OK;
414*433d6423SLionel Sambuc }
415*433d6423SLionel Sambuc 
416*433d6423SLionel Sambuc static int
417*433d6423SLionel Sambuc virtio_net_cpy_from_user(message *m)
418*433d6423SLionel Sambuc {
419*433d6423SLionel Sambuc 	/* Put user bytes into a a free packet buffer and
420*433d6423SLionel Sambuc 	 * then forward this packet to the TX queue.
421*433d6423SLionel Sambuc 	 */
422*433d6423SLionel Sambuc 	int r;
423*433d6423SLionel Sambuc 	iovec_s_t iovec[NR_IOREQS];
424*433d6423SLionel Sambuc 	struct vumap_phys phys[2];
425*433d6423SLionel Sambuc 	struct packet *p;
426*433d6423SLionel Sambuc 	size_t bytes;
427*433d6423SLionel Sambuc 
428*433d6423SLionel Sambuc 	/* This should only be called if free_list has some entries */
429*433d6423SLionel Sambuc 	assert(!STAILQ_EMPTY(&free_list));
430*433d6423SLionel Sambuc 
431*433d6423SLionel Sambuc 	p = STAILQ_FIRST(&free_list);
432*433d6423SLionel Sambuc 	STAILQ_REMOVE_HEAD(&free_list, next);
433*433d6423SLionel Sambuc 
434*433d6423SLionel Sambuc 	virtio_net_fetch_iovec(iovec, m, m->m_net_netdrv_dl_writev_s.grant,
435*433d6423SLionel Sambuc 		 m->m_net_netdrv_dl_writev_s.count);
436*433d6423SLionel Sambuc 
437*433d6423SLionel Sambuc 	r = sys_easy_vsafecopy_from(m->m_source, iovec,
438*433d6423SLionel Sambuc 		m->m_net_netdrv_dl_writev_s.count, (vir_bytes)p->vdata,
439*433d6423SLionel Sambuc 		MAX_PACK_SIZE, &bytes);
440*433d6423SLionel Sambuc 
441*433d6423SLionel Sambuc 	if (r != OK)
442*433d6423SLionel Sambuc 		panic("%s: copy from %d failed", name, m->m_source);
443*433d6423SLionel Sambuc 
444*433d6423SLionel Sambuc 
445*433d6423SLionel Sambuc 	phys[0].vp_addr = p->phdr;
446*433d6423SLionel Sambuc 	assert(!(phys[0].vp_addr & 1));
447*433d6423SLionel Sambuc 	phys[0].vp_size = sizeof(struct virtio_net_hdr);
448*433d6423SLionel Sambuc 	phys[1].vp_addr = p->pdata;
449*433d6423SLionel Sambuc 	assert(!(phys[1].vp_addr & 1));
450*433d6423SLionel Sambuc 	phys[1].vp_size = bytes;
451*433d6423SLionel Sambuc 	virtio_to_queue(net_dev, TX_Q, phys, 2, p);
452*433d6423SLionel Sambuc 	return bytes;
453*433d6423SLionel Sambuc }
454*433d6423SLionel Sambuc 
455*433d6423SLionel Sambuc static void
456*433d6423SLionel Sambuc virtio_net_intr(message *m)
457*433d6423SLionel Sambuc {
458*433d6423SLionel Sambuc 	/* Check and clear interrupt flag */
459*433d6423SLionel Sambuc 	if (virtio_had_irq(net_dev)) {
460*433d6423SLionel Sambuc 		virtio_net_check_queues();
461*433d6423SLionel Sambuc 	} else {
462*433d6423SLionel Sambuc 		if (!spurious_interrupt)
463*433d6423SLionel Sambuc 			dput(("Spurious interrupt"));
464*433d6423SLionel Sambuc 
465*433d6423SLionel Sambuc 		spurious_interrupt = 1;
466*433d6423SLionel Sambuc 	}
467*433d6423SLionel Sambuc 
468*433d6423SLionel Sambuc 	virtio_net_check_pending();
469*433d6423SLionel Sambuc 
470*433d6423SLionel Sambuc 	virtio_irq_enable(net_dev);
471*433d6423SLionel Sambuc }
472*433d6423SLionel Sambuc 
473*433d6423SLionel Sambuc static void
474*433d6423SLionel Sambuc virtio_net_write(message *m)
475*433d6423SLionel Sambuc {
476*433d6423SLionel Sambuc 	int r;
477*433d6423SLionel Sambuc 	message reply;
478*433d6423SLionel Sambuc 
479*433d6423SLionel Sambuc 	reply.m_type = DL_TASK_REPLY;
480*433d6423SLionel Sambuc 	reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS;
481*433d6423SLionel Sambuc 	reply.m_netdrv_net_dl_task.count = 0;
482*433d6423SLionel Sambuc 
483*433d6423SLionel Sambuc 
484*433d6423SLionel Sambuc 	if (!STAILQ_EMPTY(&free_list)) {
485*433d6423SLionel Sambuc 		/* free_list contains at least one  packet, use it */
486*433d6423SLionel Sambuc 		reply.m_netdrv_net_dl_task.count = virtio_net_cpy_from_user(m);
487*433d6423SLionel Sambuc 		reply.m_netdrv_net_dl_task.flags = DL_PACK_SEND;
488*433d6423SLionel Sambuc 	} else {
489*433d6423SLionel Sambuc 		pending_tx_msg = *m;
490*433d6423SLionel Sambuc 		tx_pending = 1;
491*433d6423SLionel Sambuc 	}
492*433d6423SLionel Sambuc 
493*433d6423SLionel Sambuc 	if ((r = ipc_send(m->m_source, &reply)) != OK)
494*433d6423SLionel Sambuc 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
495*433d6423SLionel Sambuc }
496*433d6423SLionel Sambuc 
497*433d6423SLionel Sambuc static void
498*433d6423SLionel Sambuc virtio_net_read(message *m)
499*433d6423SLionel Sambuc {
500*433d6423SLionel Sambuc 	int r;
501*433d6423SLionel Sambuc 	message reply;
502*433d6423SLionel Sambuc 
503*433d6423SLionel Sambuc 	reply.m_type = DL_TASK_REPLY;
504*433d6423SLionel Sambuc 	reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS;
505*433d6423SLionel Sambuc 	reply.m_netdrv_net_dl_task.count = 0;
506*433d6423SLionel Sambuc 
507*433d6423SLionel Sambuc 	if (!STAILQ_EMPTY(&recv_list)) {
508*433d6423SLionel Sambuc 		/* recv_list contains at least one  packet, copy it */
509*433d6423SLionel Sambuc 		reply.m_netdrv_net_dl_task.count = virtio_net_cpy_to_user(m);
510*433d6423SLionel Sambuc 		reply.m_netdrv_net_dl_task.flags = DL_PACK_RECV;
511*433d6423SLionel Sambuc 	} else {
512*433d6423SLionel Sambuc 		rx_pending = 1;
513*433d6423SLionel Sambuc 		pending_rx_msg = *m;
514*433d6423SLionel Sambuc 	}
515*433d6423SLionel Sambuc 
516*433d6423SLionel Sambuc 	if ((r = ipc_send(m->m_source, &reply)) != OK)
517*433d6423SLionel Sambuc 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
518*433d6423SLionel Sambuc }
519*433d6423SLionel Sambuc 
520*433d6423SLionel Sambuc static void
521*433d6423SLionel Sambuc virtio_net_conf(message *m)
522*433d6423SLionel Sambuc {
523*433d6423SLionel Sambuc 	/* TODO: Add the multicast, broadcast filtering etc. */
524*433d6423SLionel Sambuc 	int i, r;
525*433d6423SLionel Sambuc 
526*433d6423SLionel Sambuc 	message reply;
527*433d6423SLionel Sambuc 
528*433d6423SLionel Sambuc 	/* If this is the first CONF message we see, fully initialize
529*433d6423SLionel Sambuc 	 * the device now.
530*433d6423SLionel Sambuc 	 */
531*433d6423SLionel Sambuc 	if (!started) {
532*433d6423SLionel Sambuc 		started = 1;
533*433d6423SLionel Sambuc 		virtio_device_ready(net_dev);
534*433d6423SLionel Sambuc 		virtio_irq_enable(net_dev);
535*433d6423SLionel Sambuc 	}
536*433d6423SLionel Sambuc 
537*433d6423SLionel Sambuc 	/* Prepare reply */
538*433d6423SLionel Sambuc 	memcpy(reply.m_netdrv_net_dl_conf.hw_addr, virtio_net_mac,
539*433d6423SLionel Sambuc 		sizeof(reply.m_netdrv_net_dl_conf.hw_addr));
540*433d6423SLionel Sambuc 
541*433d6423SLionel Sambuc 	reply.m_type = DL_CONF_REPLY;
542*433d6423SLionel Sambuc 	reply.m_netdrv_net_dl_conf.stat = OK;
543*433d6423SLionel Sambuc 
544*433d6423SLionel Sambuc 	if ((r = ipc_send(m->m_source, &reply)) != OK)
545*433d6423SLionel Sambuc 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
546*433d6423SLionel Sambuc }
547*433d6423SLionel Sambuc 
548*433d6423SLionel Sambuc static void
549*433d6423SLionel Sambuc virtio_net_getstat(message *m)
550*433d6423SLionel Sambuc {
551*433d6423SLionel Sambuc 	int r;
552*433d6423SLionel Sambuc 	message reply;
553*433d6423SLionel Sambuc 
554*433d6423SLionel Sambuc 	reply.m_type = DL_STAT_REPLY;
555*433d6423SLionel Sambuc 
556*433d6423SLionel Sambuc 	r = sys_safecopyto(m->m_source, m->m_net_netdrv_dl_getstat_s.grant, 0,
557*433d6423SLionel Sambuc 			   (vir_bytes)&virtio_net_stats,
558*433d6423SLionel Sambuc 			   sizeof(virtio_net_stats));
559*433d6423SLionel Sambuc 
560*433d6423SLionel Sambuc 	if (r != OK)
561*433d6423SLionel Sambuc 		panic("%s: copy to %d failed (%d)", name, m->m_source, r);
562*433d6423SLionel Sambuc 
563*433d6423SLionel Sambuc 	if ((r = ipc_send(m->m_source, &reply)) != OK)
564*433d6423SLionel Sambuc 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
565*433d6423SLionel Sambuc }
566*433d6423SLionel Sambuc 
567*433d6423SLionel Sambuc static void
568*433d6423SLionel Sambuc virtio_net_notify(message *m)
569*433d6423SLionel Sambuc {
570*433d6423SLionel Sambuc 	if (_ENDPOINT_P(m->m_source) == HARDWARE)
571*433d6423SLionel Sambuc 		virtio_net_intr(m);
572*433d6423SLionel Sambuc }
573*433d6423SLionel Sambuc 
574*433d6423SLionel Sambuc static void
575*433d6423SLionel Sambuc virtio_net_msg(message *m)
576*433d6423SLionel Sambuc {
577*433d6423SLionel Sambuc 	switch (m->m_type) {
578*433d6423SLionel Sambuc 	case DL_WRITEV_S:
579*433d6423SLionel Sambuc 		virtio_net_write(m);
580*433d6423SLionel Sambuc 		break;
581*433d6423SLionel Sambuc 	case DL_READV_S:
582*433d6423SLionel Sambuc 		virtio_net_read(m);
583*433d6423SLionel Sambuc 		break;
584*433d6423SLionel Sambuc 	case DL_CONF:
585*433d6423SLionel Sambuc 		virtio_net_conf(m);
586*433d6423SLionel Sambuc 		break;
587*433d6423SLionel Sambuc 	case DL_GETSTAT_S:
588*433d6423SLionel Sambuc 		virtio_net_getstat(m);
589*433d6423SLionel Sambuc 		break;
590*433d6423SLionel Sambuc 	default:
591*433d6423SLionel Sambuc 		panic("%s: illegal message: %d", name, m->m_type);
592*433d6423SLionel Sambuc 	}
593*433d6423SLionel Sambuc }
594*433d6423SLionel Sambuc 
595*433d6423SLionel Sambuc static void
596*433d6423SLionel Sambuc virtio_net_main_loop(void)
597*433d6423SLionel Sambuc {
598*433d6423SLionel Sambuc 	message m;
599*433d6423SLionel Sambuc 	int ipc_status;
600*433d6423SLionel Sambuc 	int r;
601*433d6423SLionel Sambuc 
602*433d6423SLionel Sambuc 	while (TRUE) {
603*433d6423SLionel Sambuc 
604*433d6423SLionel Sambuc 		virtio_net_refill_rx_queue();
605*433d6423SLionel Sambuc 
606*433d6423SLionel Sambuc 		if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK)
607*433d6423SLionel Sambuc 			panic("%s: netdriver_receive failed: %d", name, r);
608*433d6423SLionel Sambuc 
609*433d6423SLionel Sambuc 		if (is_ipc_notify(ipc_status))
610*433d6423SLionel Sambuc 			virtio_net_notify(&m);
611*433d6423SLionel Sambuc 		else
612*433d6423SLionel Sambuc 			virtio_net_msg(&m);
613*433d6423SLionel Sambuc 	}
614*433d6423SLionel Sambuc }
615*433d6423SLionel Sambuc 
616*433d6423SLionel Sambuc int
617*433d6423SLionel Sambuc main(int argc, char *argv[])
618*433d6423SLionel Sambuc {
619*433d6423SLionel Sambuc 	env_setargs(argc, argv);
620*433d6423SLionel Sambuc 	sef_local_startup();
621*433d6423SLionel Sambuc 
622*433d6423SLionel Sambuc 	virtio_net_main_loop();
623*433d6423SLionel Sambuc }
624*433d6423SLionel Sambuc 
625*433d6423SLionel Sambuc static void
626*433d6423SLionel Sambuc sef_local_startup()
627*433d6423SLionel Sambuc {
628*433d6423SLionel Sambuc 	sef_setcb_init_fresh(sef_cb_init_fresh);
629*433d6423SLionel Sambuc 	sef_setcb_init_lu(sef_cb_init_fresh);
630*433d6423SLionel Sambuc 	sef_setcb_init_restart(sef_cb_init_fresh);
631*433d6423SLionel Sambuc 
632*433d6423SLionel Sambuc 	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
633*433d6423SLionel Sambuc 	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
634*433d6423SLionel Sambuc 
635*433d6423SLionel Sambuc 	sef_setcb_signal_handler(sef_cb_signal_handler);
636*433d6423SLionel Sambuc 
637*433d6423SLionel Sambuc 	sef_startup();
638*433d6423SLionel Sambuc }
639*433d6423SLionel Sambuc 
640*433d6423SLionel Sambuc static int
641*433d6423SLionel Sambuc sef_cb_init_fresh(int type, sef_init_info_t *info)
642*433d6423SLionel Sambuc {
643*433d6423SLionel Sambuc 	long instance = 0;
644*433d6423SLionel Sambuc 	env_parse("instance", "d", 0, &instance, 0, 255);
645*433d6423SLionel Sambuc 
646*433d6423SLionel Sambuc 	if (virtio_net_probe((int)instance) != OK)
647*433d6423SLionel Sambuc 		panic("%s: No device found", name);
648*433d6423SLionel Sambuc 
649*433d6423SLionel Sambuc 	if (virtio_net_config() != OK)
650*433d6423SLionel Sambuc 		panic("%s: No device found", name);
651*433d6423SLionel Sambuc 
652*433d6423SLionel Sambuc 	if (virtio_net_alloc_bufs() != OK)
653*433d6423SLionel Sambuc 		panic("%s: Buffer allocation failed", name);
654*433d6423SLionel Sambuc 
655*433d6423SLionel Sambuc 	virtio_net_init_queues();
656*433d6423SLionel Sambuc 
657*433d6423SLionel Sambuc 	netdriver_announce();
658*433d6423SLionel Sambuc 
659*433d6423SLionel Sambuc 	return(OK);
660*433d6423SLionel Sambuc }
661*433d6423SLionel Sambuc 
662*433d6423SLionel Sambuc static void
663*433d6423SLionel Sambuc sef_cb_signal_handler(int signo)
664*433d6423SLionel Sambuc {
665*433d6423SLionel Sambuc 	if (signo != SIGTERM)
666*433d6423SLionel Sambuc 		return;
667*433d6423SLionel Sambuc 
668*433d6423SLionel Sambuc 	dput(("Terminating"));
669*433d6423SLionel Sambuc 
670*433d6423SLionel Sambuc 	free_contig(data_vir, PACKET_BUF_SZ);
671*433d6423SLionel Sambuc 	free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
672*433d6423SLionel Sambuc 	free(packets);
673*433d6423SLionel Sambuc 
674*433d6423SLionel Sambuc 	virtio_reset_device(net_dev);
675*433d6423SLionel Sambuc 	virtio_free_queues(net_dev);
676*433d6423SLionel Sambuc 	virtio_free_device(net_dev);
677*433d6423SLionel Sambuc 	net_dev = NULL;
678*433d6423SLionel Sambuc 
679*433d6423SLionel Sambuc 	exit(1);
680*433d6423SLionel Sambuc }
681