ARM divsi3.S: raise(SIGFPE) when called for
[minix.git] / drivers / virtio_net / virtio_net.c
blob9f17495325bcf9fe85cee8beafc807382e92a47e
1 /* virtio net driver for MINIX 3
3 * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
5 * This software is released under the BSD license. See the LICENSE file
6 * included in the main directory of this source distribution for the
7 * license terms and conditions.
8 */
10 #include <assert.h>
11 #include <sys/types.h>
13 #include <net/gen/ether.h>
14 #include <net/gen/eth_io.h>
16 #include <minix/drivers.h>
17 #include <minix/netdriver.h>
18 #include <minix/sysutil.h>
19 #include <minix/virtio.h>
21 #include <sys/queue.h>
23 #include "virtio_net.h"
25 #define dput(s) do { dprintf(s); printf("\n"); } while (0)
26 #define dprintf(s) do { \
27 printf("%s: ", name); \
28 printf s; \
29 } while (0)
31 static struct virtio_device *net_dev;
33 static const char *const name = "virtio-net";
35 enum queue {RX_Q, TX_Q, CTRL_Q};
37 /* Number of packets to work with */
38 /* TODO: This should be an argument to the driver and possibly also
39 * depend on the queue sizes offered by this device.
41 #define BUF_PACKETS 64
42 /* Maximum size of a packet */
43 #define MAX_PACK_SIZE ETH_MAX_PACK_SIZE
44 /* Buffer size needed for the payload of BUF_PACKETS */
45 #define PACKET_BUF_SZ (BUF_PACKETS * MAX_PACK_SIZE)
47 struct packet {
48 int idx;
49 struct virtio_net_hdr *vhdr;
50 phys_bytes phdr;
51 char *vdata;
52 phys_bytes pdata;
53 STAILQ_ENTRY(packet) next;
56 /* Allocated data chunks */
57 static char *data_vir;
58 static phys_bytes data_phys;
59 static struct virtio_net_hdr *hdrs_vir;
60 static phys_bytes hdrs_phys;
61 static struct packet *packets;
62 static int in_rx;
63 static int started;
65 /* Packets on this list can be given to the host */
66 static STAILQ_HEAD(free_list, packet) free_list;
68 /* Packets on this list are to be given to inet */
69 static STAILQ_HEAD(recv_list, packet) recv_list;
71 /* State about pending inet messages */
72 static int rx_pending;
73 static message pending_rx_msg;
74 static int tx_pending;
75 static message pending_tx_msg;
77 /* Various state data */
78 static u8_t virtio_net_mac[6];
79 static eth_stat_t virtio_net_stats;
80 static int spurious_interrupt;
83 /* Prototypes */
84 static int virtio_net_probe(int skip);
85 static int virtio_net_config(void);
86 static int virtio_net_alloc_bufs(void);
87 static void virtio_net_init_queues(void);
89 static void virtio_net_refill_rx_queue(void);
90 static void virtio_net_check_queues(void);
91 static void virtio_net_check_pending(void);
93 static void virtio_net_fetch_iovec(iovec_s_t *iov, message *m);
94 static int virtio_net_cpy_to_user(message *m);
95 static int virtio_net_cpy_from_user(message *m);
97 static void virtio_net_intr(message *m);
98 static void virtio_net_write(message *m);
99 static void virtio_net_read(message *m);
100 static void virtio_net_conf(message *m);
101 static void virtio_net_getstat(message *m);
103 static void virtio_net_notify(message *m);
104 static void virtio_net_msg(message *m);
105 static void virtio_net_main_loop(void);
107 static void sef_local_startup(void);
108 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
109 static void sef_cb_signal_handler(int signo);
112 /* TODO: Features are pretty much ignored */
113 struct virtio_feature netf[] = {
114 { "partial csum", VIRTIO_NET_F_CSUM, 0, 0 },
115 { "given mac", VIRTIO_NET_F_MAC, 0, 0 },
116 { "status ", VIRTIO_NET_F_STATUS, 0, 0 },
117 { "control channel", VIRTIO_NET_F_CTRL_VQ, 0, 0 },
118 { "control channel rx", VIRTIO_NET_F_CTRL_RX, 0, 0 }
121 static int
122 virtio_net_probe(int skip)
124 /* virtio-net has at least 2 queues */
125 int queues = 2;
126 net_dev= virtio_setup_device(0x00001, name, netf,
127 sizeof(netf) / sizeof(netf[0]),
128 1 /* threads */, skip);
129 if (net_dev == NULL)
130 return ENXIO;
132 /* If the host supports the control queue, allocate it as well */
133 if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
134 queues += 1;
136 if (virtio_alloc_queues(net_dev, queues) != OK) {
137 virtio_free_device(net_dev);
138 return ENOMEM;
141 return OK;
144 static int
145 virtio_net_config(void)
147 u32_t mac14;
148 u32_t mac56;
149 int i;
151 if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
152 dprintf(("Mac set by host: "));
153 mac14 = virtio_sread32(net_dev, 0);
154 mac56 = virtio_sread32(net_dev, 4);
155 *(u32_t*)virtio_net_mac = mac14;
156 *(u16_t*)(virtio_net_mac + 4) = mac56;
158 for (i = 0; i < 6; i++)
159 printf("%02x%s", virtio_net_mac[i],
160 i == 5 ? "\n" : ":");
161 } else {
162 dput(("No mac"));
165 if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
166 dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
167 } else {
168 dput(("No status"));
171 if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
172 dput(("Host supports control channel"));
174 if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
175 dput(("Host supports control channel for RX"));
177 return OK;
180 static int
181 virtio_net_alloc_bufs(void)
183 data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
185 if (!data_vir)
186 return ENOMEM;
188 hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
189 0, &hdrs_phys);
191 if (!hdrs_vir) {
192 free_contig(data_vir, PACKET_BUF_SZ);
193 return ENOMEM;
196 packets = malloc(BUF_PACKETS * sizeof(packets[0]));
198 if (!packets) {
199 free_contig(data_vir, PACKET_BUF_SZ);
200 free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
201 return ENOMEM;
204 memset(data_vir, 0, PACKET_BUF_SZ);
205 memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
206 memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
208 return OK;
211 static void
212 virtio_net_init_queues(void)
214 int i;
215 STAILQ_INIT(&free_list);
216 STAILQ_INIT(&recv_list);
218 for (i = 0; i < BUF_PACKETS; i++) {
219 packets[i].idx = i;
220 packets[i].vhdr = &hdrs_vir[i];
221 packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
222 packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
223 packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
224 STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
228 static void
229 virtio_net_refill_rx_queue(void)
231 struct vumap_phys phys[2];
232 struct packet *p;
234 while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
236 /* peek */
237 p = STAILQ_FIRST(&free_list);
238 /* remove */
239 STAILQ_REMOVE_HEAD(&free_list, next);
241 phys[0].vp_addr = p->phdr;
242 assert(!(phys[0].vp_addr & 1));
243 phys[0].vp_size = sizeof(struct virtio_net_hdr);
245 phys[1].vp_addr = p->pdata;
246 assert(!(phys[1].vp_addr & 1));
247 phys[1].vp_size = MAX_PACK_SIZE;
249 /* RX queue needs write */
250 phys[0].vp_addr |= 1;
251 phys[1].vp_addr |= 1;
253 virtio_to_queue(net_dev, RX_Q, phys, 2, p);
254 in_rx++;
258 if (in_rx == 0 && STAILQ_EMPTY(&free_list)) {
259 dput(("warning: rx queue underflow!"));
260 virtio_net_stats.ets_fifoUnder++;
264 static void
265 virtio_net_check_queues(void)
267 struct packet *p;
269 /* Put the received packets into the recv list */
270 while (virtio_from_queue(net_dev, RX_Q, (void **)&p) == 0) {
271 STAILQ_INSERT_TAIL(&recv_list, p, next);
272 in_rx--;
273 virtio_net_stats.ets_packetR++;
276 /* Packets from the TX queue just indicated they are free to
277 * be reused now. inet already knows about them as being sent.
279 while (virtio_from_queue(net_dev, TX_Q, (void **)&p) == 0) {
280 memset(p->vhdr, 0, sizeof(*p->vhdr));
281 memset(p->vdata, 0, MAX_PACK_SIZE);
282 STAILQ_INSERT_HEAD(&free_list, p, next);
283 virtio_net_stats.ets_packetT++;
287 static void
288 virtio_net_check_pending(void)
290 int dst = 0xDEAD;
291 int r;
293 message reply;
294 reply.m_type = DL_TASK_REPLY;
295 reply.DL_FLAGS = DL_NOFLAGS;
296 reply.DL_COUNT = 0;
298 /* Pending read and something in recv_list? */
299 if (!STAILQ_EMPTY(&recv_list) && rx_pending) {
300 dst = pending_rx_msg.m_source;
301 reply.DL_COUNT = virtio_net_cpy_to_user(&pending_rx_msg);
302 reply.DL_FLAGS |= DL_PACK_RECV;
303 rx_pending = 0;
306 if (!STAILQ_EMPTY(&free_list) && tx_pending) {
307 dst = pending_tx_msg.m_source;
308 virtio_net_cpy_from_user(&pending_tx_msg);
309 reply.DL_FLAGS |= DL_PACK_SEND;
310 tx_pending = 0;
313 /* Only reply if a pending request was handled */
314 if (reply.DL_FLAGS != DL_NOFLAGS)
315 if ((r = send(dst, &reply)) != OK)
316 panic("%s: send to %d failed (%d)", name, dst, r);
319 static void
320 virtio_net_fetch_iovec(iovec_s_t *iov, message *m)
322 int r;
323 r = sys_safecopyfrom(m->m_source, m->DL_GRANT, 0, (vir_bytes)iov,
324 m->DL_COUNT * sizeof(iov[0]));
326 if (r != OK)
327 panic("%s: iovec fail for %d (%d)", name, m->m_source, r);
330 static int
331 virtio_net_cpy_to_user(message *m)
333 /* Hmm, this looks so similar to cpy_from_user... TODO */
334 int i, r, size, ivsz;
335 int left = MAX_PACK_SIZE; /* Try copying the whole packet */
336 int bytes = 0;
337 iovec_s_t iovec[NR_IOREQS];
338 struct packet *p;
340 /* This should only be called if recv_list has some entries */
341 assert(!STAILQ_EMPTY(&recv_list));
343 p = STAILQ_FIRST(&recv_list);
344 STAILQ_REMOVE_HEAD(&recv_list, next);
346 virtio_net_fetch_iovec(iovec, m);
348 for (i = 0; i < m->DL_COUNT && left > 0; i++) {
349 ivsz = iovec[i].iov_size;
350 size = left > ivsz ? ivsz : left;
351 r = sys_safecopyto(m->m_source, iovec[i].iov_grant, 0,
352 (vir_bytes) p->vdata + bytes, size);
354 if (r != OK)
355 panic("%s: copy to %d failed (%d)", name,
356 m->m_source,
359 left -= size;
360 bytes += size;
363 if (left != 0)
364 dput(("Uhm... left=%d", left));
366 /* Clean the packet */
367 memset(p->vhdr, 0, sizeof(*p->vhdr));
368 memset(p->vdata, 0, MAX_PACK_SIZE);
369 STAILQ_INSERT_HEAD(&free_list, p, next);
371 return bytes;
374 static int
375 sys_easy_vsafecopy_from(endpoint_t src_proc, iovec_s_t *iov, int count,
376 vir_bytes dst, size_t max, size_t *copied)
378 int i, r;
379 size_t left = max;
380 vir_bytes cur_off = 0;
381 struct vscp_vec vv[NR_IOREQS];
383 for (i = 0; i < count && left > 0; i++) {
384 vv[i].v_from = src_proc;
385 vv[i].v_to = SELF;
386 vv[i].v_gid = iov[i].iov_grant;
387 vv[i].v_offset = 0;
388 vv[i].v_addr = dst + cur_off;
389 vv[i].v_bytes = iov[i].iov_size;
391 /* More data in iov than the buffer can hold, this should be
392 * manageable by the caller.
394 if (left - vv[i].v_bytes > left) {
395 printf("sys_easy_vsafecopy_from: buf too small!\n");
396 return ENOMEM;
399 left -= iov[i].iov_size;
400 cur_off += iov[i].iov_size;
403 /* Now that we prepared the vscp_vec, we can call vsafecopy() */
404 if ((r = sys_vsafecopy(vv, count)) != OK)
405 printf("sys_vsafecopy: failed: (%d)\n", r);
407 if (copied)
408 *copied = cur_off;
410 return OK;
413 static int
414 virtio_net_cpy_from_user(message *m)
416 /* Put user bytes into a a free packet buffer and
417 * then forward this packet to the TX queue.
419 int r;
420 iovec_s_t iovec[NR_IOREQS];
421 struct vumap_phys phys[2];
422 struct packet *p;
423 size_t bytes;
425 /* This should only be called if free_list has some entries */
426 assert(!STAILQ_EMPTY(&free_list));
428 p = STAILQ_FIRST(&free_list);
429 STAILQ_REMOVE_HEAD(&free_list, next);
431 virtio_net_fetch_iovec(iovec, m);
433 r = sys_easy_vsafecopy_from(m->m_source, iovec, m->DL_COUNT,
434 (vir_bytes)p->vdata, MAX_PACK_SIZE,
435 &bytes);
437 if (r != OK)
438 panic("%s: copy from %d failed", name, m->m_source);
441 phys[0].vp_addr = p->phdr;
442 assert(!(phys[0].vp_addr & 1));
443 phys[0].vp_size = sizeof(struct virtio_net_hdr);
444 phys[1].vp_addr = p->pdata;
445 assert(!(phys[1].vp_addr & 1));
446 phys[1].vp_size = bytes;
447 virtio_to_queue(net_dev, TX_Q, phys, 2, p);
448 return bytes;
451 static void
452 virtio_net_intr(message *m)
454 /* Check and clear interrupt flag */
455 if (virtio_had_irq(net_dev)) {
456 virtio_net_check_queues();
457 } else {
458 if (!spurious_interrupt)
459 dput(("Spurious interrupt"));
461 spurious_interrupt = 1;
464 virtio_net_check_pending();
466 virtio_irq_enable(net_dev);
469 static void
470 virtio_net_write(message *m)
472 int r;
473 message reply;
475 reply.m_type = DL_TASK_REPLY;
476 reply.DL_FLAGS = DL_NOFLAGS;
477 reply.DL_COUNT = 0;
480 if (!STAILQ_EMPTY(&free_list)) {
481 /* free_list contains at least one packet, use it */
482 reply.DL_COUNT = virtio_net_cpy_from_user(m);
483 reply.DL_FLAGS = DL_PACK_SEND;
484 } else {
485 pending_tx_msg = *m;
486 tx_pending = 1;
489 if ((r = send(m->m_source, &reply)) != OK)
490 panic("%s: send to %d failed (%d)", name, m->m_source, r);
493 static void
494 virtio_net_read(message *m)
496 int r;
497 message reply;
499 reply.m_type = DL_TASK_REPLY;
500 reply.DL_FLAGS = DL_NOFLAGS;
501 reply.DL_COUNT = 0;
503 if (!STAILQ_EMPTY(&recv_list)) {
504 /* recv_list contains at least one packet, copy it */
505 reply.DL_COUNT = virtio_net_cpy_to_user(m);
506 reply.DL_FLAGS = DL_PACK_RECV;
507 } else {
508 rx_pending = 1;
509 pending_rx_msg = *m;
512 if ((r = send(m->m_source, &reply)) != OK)
513 panic("%s: send to %d failed (%d)", name, m->m_source, r);
516 static void
517 virtio_net_conf(message *m)
519 /* TODO: Add the multicast, broadcast filtering etc. */
520 int i, r;
522 message reply;
524 /* If this is the first CONF message we see, fully initialize
525 * the device now.
527 if (!started) {
528 started = 1;
529 virtio_device_ready(net_dev);
530 virtio_irq_enable(net_dev);
533 /* Prepare reply */
534 for (i = 0; i < sizeof(virtio_net_mac); i++)
535 ((u8_t*)reply.DL_HWADDR)[i] = virtio_net_mac[i];
537 reply.m_type = DL_CONF_REPLY;
538 reply.DL_STAT = OK;
539 reply.DL_COUNT = 0;
541 if ((r = send(m->m_source, &reply)) != OK)
542 panic("%s: send to %d failed (%d)", name, m->m_source, r);
545 static void
546 virtio_net_getstat(message *m)
548 int r;
549 message reply;
551 reply.m_type = DL_STAT_REPLY;
552 reply.DL_STAT = OK;
553 reply.DL_COUNT = 0;
556 r = sys_safecopyto(m->m_source, m->DL_GRANT, 0,
557 (vir_bytes)&virtio_net_stats,
558 sizeof(virtio_net_stats));
560 if (r != OK)
561 panic("%s: copy to %d failed (%d)", name, m->m_source, r);
563 if ((r = send(m->m_source, &reply)) != OK)
564 panic("%s: send to %d failed (%d)", name, m->m_source, r);
567 static void
568 virtio_net_notify(message *m)
570 if (_ENDPOINT_P(m->m_source) == HARDWARE)
571 virtio_net_intr(m);
574 static void
575 virtio_net_msg(message *m)
577 switch (m->m_type) {
578 case DL_WRITEV_S:
579 virtio_net_write(m);
580 break;
581 case DL_READV_S:
582 virtio_net_read(m);
583 break;
584 case DL_CONF:
585 virtio_net_conf(m);
586 break;
587 case DL_GETSTAT_S:
588 virtio_net_getstat(m);
589 break;
590 default:
591 panic("%s: illegal message: %d", name, m->m_type);
595 static void
596 virtio_net_main_loop(void)
598 message m;
599 int ipc_status;
600 int r;
602 while (TRUE) {
604 virtio_net_refill_rx_queue();
606 if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK)
607 panic("%s: netdriver_receive failed: %d", name, r);
609 if (is_ipc_notify(ipc_status))
610 virtio_net_notify(&m);
611 else
612 virtio_net_msg(&m);
617 main(int argc, char *argv[])
619 env_setargs(argc, argv);
620 sef_local_startup();
622 virtio_net_main_loop();
625 static void
626 sef_local_startup()
628 sef_setcb_init_fresh(sef_cb_init_fresh);
629 sef_setcb_init_lu(sef_cb_init_fresh);
630 sef_setcb_init_restart(sef_cb_init_fresh);
632 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
633 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
635 sef_setcb_signal_handler(sef_cb_signal_handler);
637 sef_startup();
640 static int
641 sef_cb_init_fresh(int type, sef_init_info_t *info)
643 long instance = 0;
644 env_parse("instance", "d", 0, &instance, 0, 255);
646 if (virtio_net_probe((int)instance) != OK)
647 panic("%s: No device found", name);
649 if (virtio_net_config() != OK)
650 panic("%s: No device found", name);
652 if (virtio_net_alloc_bufs() != OK)
653 panic("%s: Buffer allocation failed", name);
655 virtio_net_init_queues();
657 netdriver_announce();
659 return(OK);
662 static void
663 sef_cb_signal_handler(int signo)
665 if (signo != SIGTERM)
666 return;
668 dput(("Terminating"));
670 free_contig(data_vir, PACKET_BUF_SZ);
671 free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
672 free(packets);
674 virtio_reset_device(net_dev);
675 virtio_free_queues(net_dev);
676 virtio_free_device(net_dev);
677 net_dev = NULL;
679 exit(1);