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