1433d6423SLionel Sambuc /* virtio net driver for MINIX 3 2433d6423SLionel Sambuc * 3433d6423SLionel Sambuc * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com> 4433d6423SLionel Sambuc * 5433d6423SLionel Sambuc * This software is released under the BSD license. See the LICENSE file 6433d6423SLionel Sambuc * included in the main directory of this source distribution for the 7433d6423SLionel Sambuc * license terms and conditions. 8433d6423SLionel Sambuc */ 9433d6423SLionel Sambuc 10433d6423SLionel Sambuc #include <assert.h> 11433d6423SLionel Sambuc #include <sys/types.h> 12433d6423SLionel Sambuc 13433d6423SLionel Sambuc #include <net/gen/ether.h> 14433d6423SLionel Sambuc #include <net/gen/eth_io.h> 15433d6423SLionel Sambuc 16433d6423SLionel Sambuc #include <minix/drivers.h> 17433d6423SLionel Sambuc #include <minix/netdriver.h> 18433d6423SLionel Sambuc #include <minix/sysutil.h> 19433d6423SLionel Sambuc #include <minix/virtio.h> 20433d6423SLionel Sambuc 21433d6423SLionel Sambuc #include <sys/queue.h> 22433d6423SLionel Sambuc 23433d6423SLionel Sambuc #include "virtio_net.h" 24433d6423SLionel Sambuc 25433d6423SLionel Sambuc #define dput(s) do { dprintf(s); printf("\n"); } while (0) 26433d6423SLionel Sambuc #define dprintf(s) do { \ 27433d6423SLionel Sambuc printf("%s: ", name); \ 28433d6423SLionel Sambuc printf s; \ 29433d6423SLionel Sambuc } while (0) 30433d6423SLionel Sambuc 31433d6423SLionel Sambuc static struct virtio_device *net_dev; 32433d6423SLionel Sambuc 33433d6423SLionel Sambuc static const char *const name = "virtio-net"; 34433d6423SLionel Sambuc 35433d6423SLionel Sambuc enum queue {RX_Q, TX_Q, CTRL_Q}; 36433d6423SLionel Sambuc 37433d6423SLionel Sambuc /* Number of packets to work with */ 38433d6423SLionel Sambuc /* TODO: This should be an argument to the driver and possibly also 39433d6423SLionel Sambuc * depend on the queue sizes offered by this device. 40433d6423SLionel Sambuc */ 41433d6423SLionel Sambuc #define BUF_PACKETS 64 42433d6423SLionel Sambuc /* Maximum size of a packet */ 43433d6423SLionel Sambuc #define MAX_PACK_SIZE ETH_MAX_PACK_SIZE 44433d6423SLionel Sambuc /* Buffer size needed for the payload of BUF_PACKETS */ 45433d6423SLionel Sambuc #define PACKET_BUF_SZ (BUF_PACKETS * MAX_PACK_SIZE) 46433d6423SLionel Sambuc 47433d6423SLionel Sambuc struct packet { 48433d6423SLionel Sambuc int idx; 49433d6423SLionel Sambuc struct virtio_net_hdr *vhdr; 50433d6423SLionel Sambuc phys_bytes phdr; 51433d6423SLionel Sambuc char *vdata; 52433d6423SLionel Sambuc phys_bytes pdata; 53433d6423SLionel Sambuc STAILQ_ENTRY(packet) next; 54433d6423SLionel Sambuc }; 55433d6423SLionel Sambuc 56433d6423SLionel Sambuc /* Allocated data chunks */ 57433d6423SLionel Sambuc static char *data_vir; 58433d6423SLionel Sambuc static phys_bytes data_phys; 59433d6423SLionel Sambuc static struct virtio_net_hdr *hdrs_vir; 60433d6423SLionel Sambuc static phys_bytes hdrs_phys; 61433d6423SLionel Sambuc static struct packet *packets; 62433d6423SLionel Sambuc static int in_rx; 63433d6423SLionel Sambuc static int started; 64433d6423SLionel Sambuc 65433d6423SLionel Sambuc /* Packets on this list can be given to the host */ 66433d6423SLionel Sambuc static STAILQ_HEAD(free_list, packet) free_list; 67433d6423SLionel Sambuc 68433d6423SLionel Sambuc /* Packets on this list are to be given to inet */ 69433d6423SLionel Sambuc static STAILQ_HEAD(recv_list, packet) recv_list; 70433d6423SLionel Sambuc 71433d6423SLionel Sambuc /* State about pending inet messages */ 72433d6423SLionel Sambuc static int rx_pending; 73433d6423SLionel Sambuc static message pending_rx_msg; 74433d6423SLionel Sambuc static int tx_pending; 75433d6423SLionel Sambuc static message pending_tx_msg; 76433d6423SLionel Sambuc 77433d6423SLionel Sambuc /* Various state data */ 78433d6423SLionel Sambuc static u8_t virtio_net_mac[6]; 79433d6423SLionel Sambuc static eth_stat_t virtio_net_stats; 80433d6423SLionel Sambuc static int spurious_interrupt; 81433d6423SLionel Sambuc 82433d6423SLionel Sambuc 83433d6423SLionel Sambuc /* Prototypes */ 84433d6423SLionel Sambuc static int virtio_net_probe(int skip); 85433d6423SLionel Sambuc static int virtio_net_config(void); 86433d6423SLionel Sambuc static int virtio_net_alloc_bufs(void); 87433d6423SLionel Sambuc static void virtio_net_init_queues(void); 88433d6423SLionel Sambuc 89433d6423SLionel Sambuc static void virtio_net_refill_rx_queue(void); 90433d6423SLionel Sambuc static void virtio_net_check_queues(void); 91433d6423SLionel Sambuc static void virtio_net_check_pending(void); 92433d6423SLionel Sambuc 93433d6423SLionel Sambuc static void virtio_net_fetch_iovec(iovec_s_t *iov, message *m, 94433d6423SLionel Sambuc cp_grant_id_t grant, size_t count); 95433d6423SLionel Sambuc static int virtio_net_cpy_to_user(message *m); 96433d6423SLionel Sambuc static int virtio_net_cpy_from_user(message *m); 97433d6423SLionel Sambuc 98433d6423SLionel Sambuc static void virtio_net_intr(message *m); 99433d6423SLionel Sambuc static void virtio_net_write(message *m); 100433d6423SLionel Sambuc static void virtio_net_read(message *m); 101433d6423SLionel Sambuc static void virtio_net_conf(message *m); 102433d6423SLionel Sambuc static void virtio_net_getstat(message *m); 103433d6423SLionel Sambuc 104433d6423SLionel Sambuc static void virtio_net_notify(message *m); 105433d6423SLionel Sambuc static void virtio_net_msg(message *m); 106433d6423SLionel Sambuc static void virtio_net_main_loop(void); 107433d6423SLionel Sambuc 108433d6423SLionel Sambuc static void sef_local_startup(void); 109433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *info); 110433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo); 111433d6423SLionel Sambuc 112433d6423SLionel Sambuc 113433d6423SLionel Sambuc /* TODO: Features are pretty much ignored */ 114433d6423SLionel Sambuc struct virtio_feature netf[] = { 115433d6423SLionel Sambuc { "partial csum", VIRTIO_NET_F_CSUM, 0, 0 }, 116433d6423SLionel Sambuc { "given mac", VIRTIO_NET_F_MAC, 0, 1 }, 117433d6423SLionel Sambuc { "status ", VIRTIO_NET_F_STATUS, 0, 0 }, 118433d6423SLionel Sambuc { "control channel", VIRTIO_NET_F_CTRL_VQ, 0, 1 }, 119433d6423SLionel Sambuc { "control channel rx", VIRTIO_NET_F_CTRL_RX, 0, 0 } 120433d6423SLionel Sambuc }; 121433d6423SLionel Sambuc 122433d6423SLionel Sambuc static int 123433d6423SLionel Sambuc virtio_net_probe(int skip) 124433d6423SLionel Sambuc { 125433d6423SLionel Sambuc /* virtio-net has at least 2 queues */ 126433d6423SLionel Sambuc int queues = 2; 127433d6423SLionel Sambuc net_dev= virtio_setup_device(0x00001, name, netf, 128433d6423SLionel Sambuc sizeof(netf) / sizeof(netf[0]), 129433d6423SLionel Sambuc 1 /* threads */, skip); 130433d6423SLionel Sambuc if (net_dev == NULL) 131433d6423SLionel Sambuc return ENXIO; 132433d6423SLionel Sambuc 133433d6423SLionel Sambuc /* If the host supports the control queue, allocate it as well */ 134433d6423SLionel Sambuc if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ)) 135433d6423SLionel Sambuc queues += 1; 136433d6423SLionel Sambuc 137433d6423SLionel Sambuc if (virtio_alloc_queues(net_dev, queues) != OK) { 138433d6423SLionel Sambuc virtio_free_device(net_dev); 139433d6423SLionel Sambuc return ENOMEM; 140433d6423SLionel Sambuc } 141433d6423SLionel Sambuc 142433d6423SLionel Sambuc return OK; 143433d6423SLionel Sambuc } 144433d6423SLionel Sambuc 145433d6423SLionel Sambuc static int 146433d6423SLionel Sambuc virtio_net_config(void) 147433d6423SLionel Sambuc { 148433d6423SLionel Sambuc u32_t mac14; 149433d6423SLionel Sambuc u32_t mac56; 150433d6423SLionel Sambuc int i; 151433d6423SLionel Sambuc 152433d6423SLionel Sambuc if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) { 153433d6423SLionel Sambuc dprintf(("Mac set by host: ")); 154433d6423SLionel Sambuc mac14 = virtio_sread32(net_dev, 0); 155433d6423SLionel Sambuc mac56 = virtio_sread32(net_dev, 4); 156433d6423SLionel Sambuc *(u32_t*)virtio_net_mac = mac14; 157433d6423SLionel Sambuc *(u16_t*)(virtio_net_mac + 4) = mac56; 158433d6423SLionel Sambuc 159433d6423SLionel Sambuc for (i = 0; i < 6; i++) 160433d6423SLionel Sambuc printf("%02x%s", virtio_net_mac[i], 161433d6423SLionel Sambuc i == 5 ? "\n" : ":"); 162433d6423SLionel Sambuc } else { 163433d6423SLionel Sambuc dput(("No mac")); 164433d6423SLionel Sambuc } 165433d6423SLionel Sambuc 166433d6423SLionel Sambuc if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) { 167433d6423SLionel Sambuc dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6))); 168433d6423SLionel Sambuc } else { 169433d6423SLionel Sambuc dput(("No status")); 170433d6423SLionel Sambuc } 171433d6423SLionel Sambuc 172433d6423SLionel Sambuc if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ)) 173433d6423SLionel Sambuc dput(("Host supports control channel")); 174433d6423SLionel Sambuc 175433d6423SLionel Sambuc if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX)) 176433d6423SLionel Sambuc dput(("Host supports control channel for RX")); 177433d6423SLionel Sambuc 178433d6423SLionel Sambuc return OK; 179433d6423SLionel Sambuc } 180433d6423SLionel Sambuc 181433d6423SLionel Sambuc static int 182433d6423SLionel Sambuc virtio_net_alloc_bufs(void) 183433d6423SLionel Sambuc { 184433d6423SLionel Sambuc data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys); 185433d6423SLionel Sambuc 186433d6423SLionel Sambuc if (!data_vir) 187433d6423SLionel Sambuc return ENOMEM; 188433d6423SLionel Sambuc 189433d6423SLionel Sambuc hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]), 190433d6423SLionel Sambuc 0, &hdrs_phys); 191433d6423SLionel Sambuc 192433d6423SLionel Sambuc if (!hdrs_vir) { 193433d6423SLionel Sambuc free_contig(data_vir, PACKET_BUF_SZ); 194433d6423SLionel Sambuc return ENOMEM; 195433d6423SLionel Sambuc } 196433d6423SLionel Sambuc 197433d6423SLionel Sambuc packets = malloc(BUF_PACKETS * sizeof(packets[0])); 198433d6423SLionel Sambuc 199433d6423SLionel Sambuc if (!packets) { 200433d6423SLionel Sambuc free_contig(data_vir, PACKET_BUF_SZ); 201433d6423SLionel Sambuc free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0])); 202433d6423SLionel Sambuc return ENOMEM; 203433d6423SLionel Sambuc } 204433d6423SLionel Sambuc 205433d6423SLionel Sambuc memset(data_vir, 0, PACKET_BUF_SZ); 206433d6423SLionel Sambuc memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0])); 207433d6423SLionel Sambuc memset(packets, 0, BUF_PACKETS * sizeof(packets[0])); 208433d6423SLionel Sambuc 209433d6423SLionel Sambuc return OK; 210433d6423SLionel Sambuc } 211433d6423SLionel Sambuc 212433d6423SLionel Sambuc static void 213433d6423SLionel Sambuc virtio_net_init_queues(void) 214433d6423SLionel Sambuc { 215433d6423SLionel Sambuc int i; 216433d6423SLionel Sambuc STAILQ_INIT(&free_list); 217433d6423SLionel Sambuc STAILQ_INIT(&recv_list); 218433d6423SLionel Sambuc 219433d6423SLionel Sambuc for (i = 0; i < BUF_PACKETS; i++) { 220433d6423SLionel Sambuc packets[i].idx = i; 221433d6423SLionel Sambuc packets[i].vhdr = &hdrs_vir[i]; 222433d6423SLionel Sambuc packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]); 223433d6423SLionel Sambuc packets[i].vdata = data_vir + i * MAX_PACK_SIZE; 224433d6423SLionel Sambuc packets[i].pdata = data_phys + i * MAX_PACK_SIZE; 225433d6423SLionel Sambuc STAILQ_INSERT_HEAD(&free_list, &packets[i], next); 226433d6423SLionel Sambuc } 227433d6423SLionel Sambuc } 228433d6423SLionel Sambuc 229433d6423SLionel Sambuc static void 230433d6423SLionel Sambuc virtio_net_refill_rx_queue(void) 231433d6423SLionel Sambuc { 232433d6423SLionel Sambuc struct vumap_phys phys[2]; 233433d6423SLionel Sambuc struct packet *p; 234433d6423SLionel Sambuc 235433d6423SLionel Sambuc while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) { 236433d6423SLionel Sambuc 237433d6423SLionel Sambuc /* peek */ 238433d6423SLionel Sambuc p = STAILQ_FIRST(&free_list); 239433d6423SLionel Sambuc /* remove */ 240433d6423SLionel Sambuc STAILQ_REMOVE_HEAD(&free_list, next); 241433d6423SLionel Sambuc 242433d6423SLionel Sambuc phys[0].vp_addr = p->phdr; 243433d6423SLionel Sambuc assert(!(phys[0].vp_addr & 1)); 244433d6423SLionel Sambuc phys[0].vp_size = sizeof(struct virtio_net_hdr); 245433d6423SLionel Sambuc 246433d6423SLionel Sambuc phys[1].vp_addr = p->pdata; 247433d6423SLionel Sambuc assert(!(phys[1].vp_addr & 1)); 248433d6423SLionel Sambuc phys[1].vp_size = MAX_PACK_SIZE; 249433d6423SLionel Sambuc 250433d6423SLionel Sambuc /* RX queue needs write */ 251433d6423SLionel Sambuc phys[0].vp_addr |= 1; 252433d6423SLionel Sambuc phys[1].vp_addr |= 1; 253433d6423SLionel Sambuc 254433d6423SLionel Sambuc virtio_to_queue(net_dev, RX_Q, phys, 2, p); 255433d6423SLionel Sambuc in_rx++; 256433d6423SLionel Sambuc 257433d6423SLionel Sambuc } 258433d6423SLionel Sambuc 259433d6423SLionel Sambuc if (in_rx == 0 && STAILQ_EMPTY(&free_list)) { 260433d6423SLionel Sambuc dput(("warning: rx queue underflow!")); 261433d6423SLionel Sambuc virtio_net_stats.ets_fifoUnder++; 262433d6423SLionel Sambuc } 263433d6423SLionel Sambuc } 264433d6423SLionel Sambuc 265433d6423SLionel Sambuc static void 266433d6423SLionel Sambuc virtio_net_check_queues(void) 267433d6423SLionel Sambuc { 268433d6423SLionel Sambuc struct packet *p; 269433d6423SLionel Sambuc 270433d6423SLionel Sambuc /* Put the received packets into the recv list */ 271*d1db724fSDavid van Moolenbroek while (virtio_from_queue(net_dev, RX_Q, (void **)&p, NULL) == 0) { 272433d6423SLionel Sambuc STAILQ_INSERT_TAIL(&recv_list, p, next); 273433d6423SLionel Sambuc in_rx--; 274433d6423SLionel Sambuc virtio_net_stats.ets_packetR++; 275433d6423SLionel Sambuc } 276433d6423SLionel Sambuc 277433d6423SLionel Sambuc /* Packets from the TX queue just indicated they are free to 278433d6423SLionel Sambuc * be reused now. inet already knows about them as being sent. 279433d6423SLionel Sambuc */ 280*d1db724fSDavid van Moolenbroek while (virtio_from_queue(net_dev, TX_Q, (void **)&p, NULL) == 0) { 281433d6423SLionel Sambuc memset(p->vhdr, 0, sizeof(*p->vhdr)); 282433d6423SLionel Sambuc memset(p->vdata, 0, MAX_PACK_SIZE); 283433d6423SLionel Sambuc STAILQ_INSERT_HEAD(&free_list, p, next); 284433d6423SLionel Sambuc virtio_net_stats.ets_packetT++; 285433d6423SLionel Sambuc } 286433d6423SLionel Sambuc } 287433d6423SLionel Sambuc 288433d6423SLionel Sambuc static void 289433d6423SLionel Sambuc virtio_net_check_pending(void) 290433d6423SLionel Sambuc { 291433d6423SLionel Sambuc int dst = 0xDEAD; 292433d6423SLionel Sambuc int r; 293433d6423SLionel Sambuc 294433d6423SLionel Sambuc message reply; 295433d6423SLionel Sambuc reply.m_type = DL_TASK_REPLY; 296433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS; 297433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.count = 0; 298433d6423SLionel Sambuc 299433d6423SLionel Sambuc /* Pending read and something in recv_list? */ 300433d6423SLionel Sambuc if (!STAILQ_EMPTY(&recv_list) && rx_pending) { 301433d6423SLionel Sambuc dst = pending_rx_msg.m_source; 302433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.count = 303433d6423SLionel Sambuc virtio_net_cpy_to_user(&pending_rx_msg); 304433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.flags |= DL_PACK_RECV; 305433d6423SLionel Sambuc rx_pending = 0; 306433d6423SLionel Sambuc } 307433d6423SLionel Sambuc 308433d6423SLionel Sambuc if (!STAILQ_EMPTY(&free_list) && tx_pending) { 309433d6423SLionel Sambuc dst = pending_tx_msg.m_source; 310433d6423SLionel Sambuc virtio_net_cpy_from_user(&pending_tx_msg); 311433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.flags |= DL_PACK_SEND; 312433d6423SLionel Sambuc tx_pending = 0; 313433d6423SLionel Sambuc } 314433d6423SLionel Sambuc 315433d6423SLionel Sambuc /* Only reply if a pending request was handled */ 316433d6423SLionel Sambuc if (reply.m_netdrv_net_dl_task.flags != DL_NOFLAGS) 317433d6423SLionel Sambuc if ((r = ipc_send(dst, &reply)) != OK) 318433d6423SLionel Sambuc panic("%s: ipc_send to %d failed (%d)", name, dst, r); 319433d6423SLionel Sambuc } 320433d6423SLionel Sambuc 321433d6423SLionel Sambuc static void 322433d6423SLionel Sambuc virtio_net_fetch_iovec(iovec_s_t *iov, message *m, cp_grant_id_t grant, size_t count) 323433d6423SLionel Sambuc { 324433d6423SLionel Sambuc int r; 325433d6423SLionel Sambuc r = sys_safecopyfrom(m->m_source, grant, 0, (vir_bytes)iov, 326433d6423SLionel Sambuc count * sizeof(iov[0])); 327433d6423SLionel Sambuc 328433d6423SLionel Sambuc if (r != OK) 329433d6423SLionel Sambuc panic("%s: iovec fail for %d (%d)", name, m->m_source, r); 330433d6423SLionel Sambuc } 331433d6423SLionel Sambuc 332433d6423SLionel Sambuc static int 333433d6423SLionel Sambuc virtio_net_cpy_to_user(message *m) 334433d6423SLionel Sambuc { 335433d6423SLionel Sambuc /* Hmm, this looks so similar to cpy_from_user... TODO */ 336433d6423SLionel Sambuc int i, r, size, ivsz; 337433d6423SLionel Sambuc int left = MAX_PACK_SIZE; /* Try copying the whole packet */ 338433d6423SLionel Sambuc int bytes = 0; 339433d6423SLionel Sambuc iovec_s_t iovec[NR_IOREQS]; 340433d6423SLionel Sambuc struct packet *p; 341433d6423SLionel Sambuc 342433d6423SLionel Sambuc /* This should only be called if recv_list has some entries */ 343433d6423SLionel Sambuc assert(!STAILQ_EMPTY(&recv_list)); 344433d6423SLionel Sambuc 345433d6423SLionel Sambuc p = STAILQ_FIRST(&recv_list); 346433d6423SLionel Sambuc STAILQ_REMOVE_HEAD(&recv_list, next); 347433d6423SLionel Sambuc 348433d6423SLionel Sambuc virtio_net_fetch_iovec(iovec, m, m->m_net_netdrv_dl_readv_s.grant, 349433d6423SLionel Sambuc m->m_net_netdrv_dl_readv_s.count); 350433d6423SLionel Sambuc 351433d6423SLionel Sambuc for (i = 0; i < m->m_net_netdrv_dl_readv_s.count && left > 0; i++) { 352433d6423SLionel Sambuc ivsz = iovec[i].iov_size; 353433d6423SLionel Sambuc size = left > ivsz ? ivsz : left; 354433d6423SLionel Sambuc r = sys_safecopyto(m->m_source, iovec[i].iov_grant, 0, 355433d6423SLionel Sambuc (vir_bytes) p->vdata + bytes, size); 356433d6423SLionel Sambuc 357433d6423SLionel Sambuc if (r != OK) 358433d6423SLionel Sambuc panic("%s: copy to %d failed (%d)", name, 359433d6423SLionel Sambuc m->m_source, 360433d6423SLionel Sambuc r); 361433d6423SLionel Sambuc 362433d6423SLionel Sambuc left -= size; 363433d6423SLionel Sambuc bytes += size; 364433d6423SLionel Sambuc } 365433d6423SLionel Sambuc 366433d6423SLionel Sambuc if (left != 0) 367433d6423SLionel Sambuc dput(("Uhm... left=%d", left)); 368433d6423SLionel Sambuc 369433d6423SLionel Sambuc /* Clean the packet */ 370433d6423SLionel Sambuc memset(p->vhdr, 0, sizeof(*p->vhdr)); 371433d6423SLionel Sambuc memset(p->vdata, 0, MAX_PACK_SIZE); 372433d6423SLionel Sambuc STAILQ_INSERT_HEAD(&free_list, p, next); 373433d6423SLionel Sambuc 374433d6423SLionel Sambuc return bytes; 375433d6423SLionel Sambuc } 376433d6423SLionel Sambuc 377433d6423SLionel Sambuc static int 378433d6423SLionel Sambuc sys_easy_vsafecopy_from(endpoint_t src_proc, iovec_s_t *iov, int count, 379433d6423SLionel Sambuc vir_bytes dst, size_t max, size_t *copied) 380433d6423SLionel Sambuc { 381433d6423SLionel Sambuc int i, r; 382433d6423SLionel Sambuc size_t left = max; 383433d6423SLionel Sambuc vir_bytes cur_off = 0; 384433d6423SLionel Sambuc struct vscp_vec vv[NR_IOREQS]; 385433d6423SLionel Sambuc 386433d6423SLionel Sambuc for (i = 0; i < count && left > 0; i++) { 387433d6423SLionel Sambuc vv[i].v_from = src_proc; 388433d6423SLionel Sambuc vv[i].v_to = SELF; 389433d6423SLionel Sambuc vv[i].v_gid = iov[i].iov_grant; 390433d6423SLionel Sambuc vv[i].v_offset = 0; 391433d6423SLionel Sambuc vv[i].v_addr = dst + cur_off; 392433d6423SLionel Sambuc vv[i].v_bytes = iov[i].iov_size; 393433d6423SLionel Sambuc 394433d6423SLionel Sambuc /* More data in iov than the buffer can hold, this should be 395433d6423SLionel Sambuc * manageable by the caller. 396433d6423SLionel Sambuc */ 397433d6423SLionel Sambuc if (left - vv[i].v_bytes > left) { 398433d6423SLionel Sambuc printf("sys_easy_vsafecopy_from: buf too small!\n"); 399433d6423SLionel Sambuc return ENOMEM; 400433d6423SLionel Sambuc } 401433d6423SLionel Sambuc 402433d6423SLionel Sambuc left -= iov[i].iov_size; 403433d6423SLionel Sambuc cur_off += iov[i].iov_size; 404433d6423SLionel Sambuc } 405433d6423SLionel Sambuc 406433d6423SLionel Sambuc /* Now that we prepared the vscp_vec, we can call vsafecopy() */ 407433d6423SLionel Sambuc if ((r = sys_vsafecopy(vv, count)) != OK) 408433d6423SLionel Sambuc printf("sys_vsafecopy: failed: (%d)\n", r); 409433d6423SLionel Sambuc 410433d6423SLionel Sambuc if (copied) 411433d6423SLionel Sambuc *copied = cur_off; 412433d6423SLionel Sambuc 413433d6423SLionel Sambuc return OK; 414433d6423SLionel Sambuc } 415433d6423SLionel Sambuc 416433d6423SLionel Sambuc static int 417433d6423SLionel Sambuc virtio_net_cpy_from_user(message *m) 418433d6423SLionel Sambuc { 419433d6423SLionel Sambuc /* Put user bytes into a a free packet buffer and 420433d6423SLionel Sambuc * then forward this packet to the TX queue. 421433d6423SLionel Sambuc */ 422433d6423SLionel Sambuc int r; 423433d6423SLionel Sambuc iovec_s_t iovec[NR_IOREQS]; 424433d6423SLionel Sambuc struct vumap_phys phys[2]; 425433d6423SLionel Sambuc struct packet *p; 426433d6423SLionel Sambuc size_t bytes; 427433d6423SLionel Sambuc 428433d6423SLionel Sambuc /* This should only be called if free_list has some entries */ 429433d6423SLionel Sambuc assert(!STAILQ_EMPTY(&free_list)); 430433d6423SLionel Sambuc 431433d6423SLionel Sambuc p = STAILQ_FIRST(&free_list); 432433d6423SLionel Sambuc STAILQ_REMOVE_HEAD(&free_list, next); 433433d6423SLionel Sambuc 434433d6423SLionel Sambuc virtio_net_fetch_iovec(iovec, m, m->m_net_netdrv_dl_writev_s.grant, 435433d6423SLionel Sambuc m->m_net_netdrv_dl_writev_s.count); 436433d6423SLionel Sambuc 437433d6423SLionel Sambuc r = sys_easy_vsafecopy_from(m->m_source, iovec, 438433d6423SLionel Sambuc m->m_net_netdrv_dl_writev_s.count, (vir_bytes)p->vdata, 439433d6423SLionel Sambuc MAX_PACK_SIZE, &bytes); 440433d6423SLionel Sambuc 441433d6423SLionel Sambuc if (r != OK) 442433d6423SLionel Sambuc panic("%s: copy from %d failed", name, m->m_source); 443433d6423SLionel Sambuc 444433d6423SLionel Sambuc 445433d6423SLionel Sambuc phys[0].vp_addr = p->phdr; 446433d6423SLionel Sambuc assert(!(phys[0].vp_addr & 1)); 447433d6423SLionel Sambuc phys[0].vp_size = sizeof(struct virtio_net_hdr); 448433d6423SLionel Sambuc phys[1].vp_addr = p->pdata; 449433d6423SLionel Sambuc assert(!(phys[1].vp_addr & 1)); 450433d6423SLionel Sambuc phys[1].vp_size = bytes; 451433d6423SLionel Sambuc virtio_to_queue(net_dev, TX_Q, phys, 2, p); 452433d6423SLionel Sambuc return bytes; 453433d6423SLionel Sambuc } 454433d6423SLionel Sambuc 455433d6423SLionel Sambuc static void 456433d6423SLionel Sambuc virtio_net_intr(message *m) 457433d6423SLionel Sambuc { 458433d6423SLionel Sambuc /* Check and clear interrupt flag */ 459433d6423SLionel Sambuc if (virtio_had_irq(net_dev)) { 460433d6423SLionel Sambuc virtio_net_check_queues(); 461433d6423SLionel Sambuc } else { 462433d6423SLionel Sambuc if (!spurious_interrupt) 463433d6423SLionel Sambuc dput(("Spurious interrupt")); 464433d6423SLionel Sambuc 465433d6423SLionel Sambuc spurious_interrupt = 1; 466433d6423SLionel Sambuc } 467433d6423SLionel Sambuc 468433d6423SLionel Sambuc virtio_net_check_pending(); 469433d6423SLionel Sambuc 470433d6423SLionel Sambuc virtio_irq_enable(net_dev); 471433d6423SLionel Sambuc } 472433d6423SLionel Sambuc 473433d6423SLionel Sambuc static void 474433d6423SLionel Sambuc virtio_net_write(message *m) 475433d6423SLionel Sambuc { 476433d6423SLionel Sambuc int r; 477433d6423SLionel Sambuc message reply; 478433d6423SLionel Sambuc 479433d6423SLionel Sambuc reply.m_type = DL_TASK_REPLY; 480433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS; 481433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.count = 0; 482433d6423SLionel Sambuc 483433d6423SLionel Sambuc 484433d6423SLionel Sambuc if (!STAILQ_EMPTY(&free_list)) { 485433d6423SLionel Sambuc /* free_list contains at least one packet, use it */ 486433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.count = virtio_net_cpy_from_user(m); 487433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.flags = DL_PACK_SEND; 488433d6423SLionel Sambuc } else { 489433d6423SLionel Sambuc pending_tx_msg = *m; 490433d6423SLionel Sambuc tx_pending = 1; 491433d6423SLionel Sambuc } 492433d6423SLionel Sambuc 493433d6423SLionel Sambuc if ((r = ipc_send(m->m_source, &reply)) != OK) 494433d6423SLionel Sambuc panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r); 495433d6423SLionel Sambuc } 496433d6423SLionel Sambuc 497433d6423SLionel Sambuc static void 498433d6423SLionel Sambuc virtio_net_read(message *m) 499433d6423SLionel Sambuc { 500433d6423SLionel Sambuc int r; 501433d6423SLionel Sambuc message reply; 502433d6423SLionel Sambuc 503433d6423SLionel Sambuc reply.m_type = DL_TASK_REPLY; 504433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS; 505433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.count = 0; 506433d6423SLionel Sambuc 507433d6423SLionel Sambuc if (!STAILQ_EMPTY(&recv_list)) { 508433d6423SLionel Sambuc /* recv_list contains at least one packet, copy it */ 509433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.count = virtio_net_cpy_to_user(m); 510433d6423SLionel Sambuc reply.m_netdrv_net_dl_task.flags = DL_PACK_RECV; 511433d6423SLionel Sambuc } else { 512433d6423SLionel Sambuc rx_pending = 1; 513433d6423SLionel Sambuc pending_rx_msg = *m; 514433d6423SLionel Sambuc } 515433d6423SLionel Sambuc 516433d6423SLionel Sambuc if ((r = ipc_send(m->m_source, &reply)) != OK) 517433d6423SLionel Sambuc panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r); 518433d6423SLionel Sambuc } 519433d6423SLionel Sambuc 520433d6423SLionel Sambuc static void 521433d6423SLionel Sambuc virtio_net_conf(message *m) 522433d6423SLionel Sambuc { 523433d6423SLionel Sambuc /* TODO: Add the multicast, broadcast filtering etc. */ 524433d6423SLionel Sambuc int i, r; 525433d6423SLionel Sambuc 526433d6423SLionel Sambuc message reply; 527433d6423SLionel Sambuc 528433d6423SLionel Sambuc /* If this is the first CONF message we see, fully initialize 529433d6423SLionel Sambuc * the device now. 530433d6423SLionel Sambuc */ 531433d6423SLionel Sambuc if (!started) { 532433d6423SLionel Sambuc started = 1; 533433d6423SLionel Sambuc virtio_device_ready(net_dev); 534433d6423SLionel Sambuc virtio_irq_enable(net_dev); 535433d6423SLionel Sambuc } 536433d6423SLionel Sambuc 537433d6423SLionel Sambuc /* Prepare reply */ 538433d6423SLionel Sambuc memcpy(reply.m_netdrv_net_dl_conf.hw_addr, virtio_net_mac, 539433d6423SLionel Sambuc sizeof(reply.m_netdrv_net_dl_conf.hw_addr)); 540433d6423SLionel Sambuc 541433d6423SLionel Sambuc reply.m_type = DL_CONF_REPLY; 542433d6423SLionel Sambuc reply.m_netdrv_net_dl_conf.stat = OK; 543433d6423SLionel Sambuc 544433d6423SLionel Sambuc if ((r = ipc_send(m->m_source, &reply)) != OK) 545433d6423SLionel Sambuc panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r); 546433d6423SLionel Sambuc } 547433d6423SLionel Sambuc 548433d6423SLionel Sambuc static void 549433d6423SLionel Sambuc virtio_net_getstat(message *m) 550433d6423SLionel Sambuc { 551433d6423SLionel Sambuc int r; 552433d6423SLionel Sambuc message reply; 553433d6423SLionel Sambuc 554433d6423SLionel Sambuc reply.m_type = DL_STAT_REPLY; 555433d6423SLionel Sambuc 556433d6423SLionel Sambuc r = sys_safecopyto(m->m_source, m->m_net_netdrv_dl_getstat_s.grant, 0, 557433d6423SLionel Sambuc (vir_bytes)&virtio_net_stats, 558433d6423SLionel Sambuc sizeof(virtio_net_stats)); 559433d6423SLionel Sambuc 560433d6423SLionel Sambuc if (r != OK) 561433d6423SLionel Sambuc panic("%s: copy to %d failed (%d)", name, m->m_source, r); 562433d6423SLionel Sambuc 563433d6423SLionel Sambuc if ((r = ipc_send(m->m_source, &reply)) != OK) 564433d6423SLionel Sambuc panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r); 565433d6423SLionel Sambuc } 566433d6423SLionel Sambuc 567433d6423SLionel Sambuc static void 568433d6423SLionel Sambuc virtio_net_notify(message *m) 569433d6423SLionel Sambuc { 570433d6423SLionel Sambuc if (_ENDPOINT_P(m->m_source) == HARDWARE) 571433d6423SLionel Sambuc virtio_net_intr(m); 572433d6423SLionel Sambuc } 573433d6423SLionel Sambuc 574433d6423SLionel Sambuc static void 575433d6423SLionel Sambuc virtio_net_msg(message *m) 576433d6423SLionel Sambuc { 577433d6423SLionel Sambuc switch (m->m_type) { 578433d6423SLionel Sambuc case DL_WRITEV_S: 579433d6423SLionel Sambuc virtio_net_write(m); 580433d6423SLionel Sambuc break; 581433d6423SLionel Sambuc case DL_READV_S: 582433d6423SLionel Sambuc virtio_net_read(m); 583433d6423SLionel Sambuc break; 584433d6423SLionel Sambuc case DL_CONF: 585433d6423SLionel Sambuc virtio_net_conf(m); 586433d6423SLionel Sambuc break; 587433d6423SLionel Sambuc case DL_GETSTAT_S: 588433d6423SLionel Sambuc virtio_net_getstat(m); 589433d6423SLionel Sambuc break; 590433d6423SLionel Sambuc default: 591433d6423SLionel Sambuc panic("%s: illegal message: %d", name, m->m_type); 592433d6423SLionel Sambuc } 593433d6423SLionel Sambuc } 594433d6423SLionel Sambuc 595433d6423SLionel Sambuc static void 596433d6423SLionel Sambuc virtio_net_main_loop(void) 597433d6423SLionel Sambuc { 598433d6423SLionel Sambuc message m; 599433d6423SLionel Sambuc int ipc_status; 600433d6423SLionel Sambuc int r; 601433d6423SLionel Sambuc 602433d6423SLionel Sambuc while (TRUE) { 603433d6423SLionel Sambuc 604433d6423SLionel Sambuc virtio_net_refill_rx_queue(); 605433d6423SLionel Sambuc 606433d6423SLionel Sambuc if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK) 607433d6423SLionel Sambuc panic("%s: netdriver_receive failed: %d", name, r); 608433d6423SLionel Sambuc 609433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) 610433d6423SLionel Sambuc virtio_net_notify(&m); 611433d6423SLionel Sambuc else 612433d6423SLionel Sambuc virtio_net_msg(&m); 613433d6423SLionel Sambuc } 614433d6423SLionel Sambuc } 615433d6423SLionel Sambuc 616433d6423SLionel Sambuc int 617433d6423SLionel Sambuc main(int argc, char *argv[]) 618433d6423SLionel Sambuc { 619433d6423SLionel Sambuc env_setargs(argc, argv); 620433d6423SLionel Sambuc sef_local_startup(); 621433d6423SLionel Sambuc 622433d6423SLionel Sambuc virtio_net_main_loop(); 623433d6423SLionel Sambuc } 624433d6423SLionel Sambuc 625433d6423SLionel Sambuc static void 626433d6423SLionel Sambuc sef_local_startup() 627433d6423SLionel Sambuc { 628433d6423SLionel Sambuc sef_setcb_init_fresh(sef_cb_init_fresh); 629433d6423SLionel Sambuc sef_setcb_init_lu(sef_cb_init_fresh); 630433d6423SLionel Sambuc sef_setcb_init_restart(sef_cb_init_fresh); 631433d6423SLionel Sambuc 632433d6423SLionel Sambuc sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready); 633433d6423SLionel Sambuc sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree); 634433d6423SLionel Sambuc 635433d6423SLionel Sambuc sef_setcb_signal_handler(sef_cb_signal_handler); 636433d6423SLionel Sambuc 637433d6423SLionel Sambuc sef_startup(); 638433d6423SLionel Sambuc } 639433d6423SLionel Sambuc 640433d6423SLionel Sambuc static int 641433d6423SLionel Sambuc sef_cb_init_fresh(int type, sef_init_info_t *info) 642433d6423SLionel Sambuc { 643433d6423SLionel Sambuc long instance = 0; 644433d6423SLionel Sambuc env_parse("instance", "d", 0, &instance, 0, 255); 645433d6423SLionel Sambuc 646433d6423SLionel Sambuc if (virtio_net_probe((int)instance) != OK) 647433d6423SLionel Sambuc panic("%s: No device found", name); 648433d6423SLionel Sambuc 649433d6423SLionel Sambuc if (virtio_net_config() != OK) 650433d6423SLionel Sambuc panic("%s: No device found", name); 651433d6423SLionel Sambuc 652433d6423SLionel Sambuc if (virtio_net_alloc_bufs() != OK) 653433d6423SLionel Sambuc panic("%s: Buffer allocation failed", name); 654433d6423SLionel Sambuc 655433d6423SLionel Sambuc virtio_net_init_queues(); 656433d6423SLionel Sambuc 657433d6423SLionel Sambuc netdriver_announce(); 658433d6423SLionel Sambuc 659433d6423SLionel Sambuc return(OK); 660433d6423SLionel Sambuc } 661433d6423SLionel Sambuc 662433d6423SLionel Sambuc static void 663433d6423SLionel Sambuc sef_cb_signal_handler(int signo) 664433d6423SLionel Sambuc { 665433d6423SLionel Sambuc if (signo != SIGTERM) 666433d6423SLionel Sambuc return; 667433d6423SLionel Sambuc 668433d6423SLionel Sambuc dput(("Terminating")); 669433d6423SLionel Sambuc 670433d6423SLionel Sambuc free_contig(data_vir, PACKET_BUF_SZ); 671433d6423SLionel Sambuc free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0])); 672433d6423SLionel Sambuc free(packets); 673433d6423SLionel Sambuc 674433d6423SLionel Sambuc virtio_reset_device(net_dev); 675433d6423SLionel Sambuc virtio_free_queues(net_dev); 676433d6423SLionel Sambuc virtio_free_device(net_dev); 677433d6423SLionel Sambuc net_dev = NULL; 678433d6423SLionel Sambuc 679433d6423SLionel Sambuc exit(1); 680433d6423SLionel Sambuc } 681