1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD: stable/10/usr.sbin/bhyve/pci_virtio_net.c 307183 2016-10-13 06:32:21Z np $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pci_virtio_net.c 307183 2016-10-13 06:32:21Z np $"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34221828Sgrehan#include <sys/select.h> 35221828Sgrehan#include <sys/uio.h> 36221828Sgrehan#include <sys/ioctl.h> 37282839Smav#include <machine/atomic.h> 38252682Sgrehan#include <net/ethernet.h> 39294294Sgnn#ifndef NETMAP_WITH_LIBS 40294294Sgnn#define NETMAP_WITH_LIBS 41294294Sgnn#endif 42294294Sgnn#include <net/netmap_user.h> 43221828Sgrehan 44221828Sgrehan#include <errno.h> 45221828Sgrehan#include <fcntl.h> 46221828Sgrehan#include <stdio.h> 47221828Sgrehan#include <stdlib.h> 48221828Sgrehan#include <stdint.h> 49221828Sgrehan#include <string.h> 50221828Sgrehan#include <strings.h> 51221828Sgrehan#include <unistd.h> 52221828Sgrehan#include <assert.h> 53221828Sgrehan#include <md5.h> 54221828Sgrehan#include <pthread.h> 55249917Sgrehan#include <pthread_np.h> 56221828Sgrehan 57244167Sgrehan#include "bhyverun.h" 58221828Sgrehan#include "pci_emul.h" 59221828Sgrehan#include "mevent.h" 60221828Sgrehan#include "virtio.h" 61221828Sgrehan 62249917Sgrehan#define VTNET_RINGSZ 1024 63221828Sgrehan 64295124Sgrehan#define VTNET_MAXSEGS 256 65221828Sgrehan 66221828Sgrehan/* 67253440Sgrehan * Host capabilities. Note that we only offer a few of these. 68221828Sgrehan */ 69253440Sgrehan#define VIRTIO_NET_F_CSUM (1 << 0) /* host handles partial cksum */ 70253440Sgrehan#define VIRTIO_NET_F_GUEST_CSUM (1 << 1) /* guest handles partial cksum */ 71253440Sgrehan#define VIRTIO_NET_F_MAC (1 << 5) /* host supplies MAC */ 72253440Sgrehan#define VIRTIO_NET_F_GSO_DEPREC (1 << 6) /* deprecated: host handles GSO */ 73253440Sgrehan#define VIRTIO_NET_F_GUEST_TSO4 (1 << 7) /* guest can rcv TSOv4 */ 74253440Sgrehan#define VIRTIO_NET_F_GUEST_TSO6 (1 << 8) /* guest can rcv TSOv6 */ 75253440Sgrehan#define VIRTIO_NET_F_GUEST_ECN (1 << 9) /* guest can rcv TSO with ECN */ 76253440Sgrehan#define VIRTIO_NET_F_GUEST_UFO (1 << 10) /* guest can rcv UFO */ 77253440Sgrehan#define VIRTIO_NET_F_HOST_TSO4 (1 << 11) /* host can rcv TSOv4 */ 78253440Sgrehan#define VIRTIO_NET_F_HOST_TSO6 (1 << 12) /* host can rcv TSOv6 */ 79253440Sgrehan#define VIRTIO_NET_F_HOST_ECN (1 << 13) /* host can rcv TSO with ECN */ 80253440Sgrehan#define VIRTIO_NET_F_HOST_UFO (1 << 14) /* host can rcv UFO */ 81253440Sgrehan#define VIRTIO_NET_F_MRG_RXBUF (1 << 15) /* host can merge RX buffers */ 82253440Sgrehan#define VIRTIO_NET_F_STATUS (1 << 16) /* config status field available */ 83253440Sgrehan#define VIRTIO_NET_F_CTRL_VQ (1 << 17) /* control channel available */ 84253440Sgrehan#define VIRTIO_NET_F_CTRL_RX (1 << 18) /* control channel RX mode support */ 85253440Sgrehan#define VIRTIO_NET_F_CTRL_VLAN (1 << 19) /* control channel VLAN filtering */ 86253440Sgrehan#define VIRTIO_NET_F_GUEST_ANNOUNCE \ 87253440Sgrehan (1 << 21) /* guest can send gratuitous pkts */ 88221828Sgrehan 89253440Sgrehan#define VTNET_S_HOSTCAPS \ 90253440Sgrehan ( VIRTIO_NET_F_MAC | VIRTIO_NET_F_MRG_RXBUF | VIRTIO_NET_F_STATUS | \ 91295124Sgrehan VIRTIO_F_NOTIFY_ON_EMPTY | VIRTIO_RING_F_INDIRECT_DESC) 92221828Sgrehan 93221828Sgrehan/* 94253440Sgrehan * PCI config-space "registers" 95221828Sgrehan */ 96253440Sgrehanstruct virtio_net_config { 97253440Sgrehan uint8_t mac[6]; 98253440Sgrehan uint16_t status; 99253440Sgrehan} __packed; 100221828Sgrehan 101221828Sgrehan/* 102221828Sgrehan * Queue definitions. 103221828Sgrehan */ 104221828Sgrehan#define VTNET_RXQ 0 105221828Sgrehan#define VTNET_TXQ 1 106253440Sgrehan#define VTNET_CTLQ 2 /* NB: not yet supported */ 107221828Sgrehan 108221828Sgrehan#define VTNET_MAXQ 3 109221828Sgrehan 110221828Sgrehan/* 111221828Sgrehan * Fixed network header size 112221828Sgrehan */ 113221828Sgrehanstruct virtio_net_rxhdr { 114221828Sgrehan uint8_t vrh_flags; 115221828Sgrehan uint8_t vrh_gso_type; 116221828Sgrehan uint16_t vrh_hdr_len; 117221828Sgrehan uint16_t vrh_gso_size; 118221828Sgrehan uint16_t vrh_csum_start; 119221828Sgrehan uint16_t vrh_csum_offset; 120221828Sgrehan uint16_t vrh_bufs; 121221828Sgrehan} __packed; 122221828Sgrehan 123221828Sgrehan/* 124221828Sgrehan * Debug printf 125221828Sgrehan */ 126221828Sgrehanstatic int pci_vtnet_debug; 127221828Sgrehan#define DPRINTF(params) if (pci_vtnet_debug) printf params 128221828Sgrehan#define WPRINTF(params) printf params 129221828Sgrehan 130221828Sgrehan/* 131221828Sgrehan * Per-device softc 132221828Sgrehan */ 133221828Sgrehanstruct pci_vtnet_softc { 134253440Sgrehan struct virtio_softc vsc_vs; 135253440Sgrehan struct vqueue_info vsc_queues[VTNET_MAXQ - 1]; 136221828Sgrehan pthread_mutex_t vsc_mtx; 137221828Sgrehan struct mevent *vsc_mevp; 138221828Sgrehan 139221828Sgrehan int vsc_tapfd; 140294294Sgnn struct nm_desc *vsc_nmd; 141294294Sgnn 142221828Sgrehan int vsc_rx_ready; 143253440Sgrehan volatile int resetting; /* set and checked outside lock */ 144221828Sgrehan 145271685Sgrehan uint64_t vsc_features; /* negotiated features */ 146271685Sgrehan 147253440Sgrehan struct virtio_net_config vsc_config; 148221828Sgrehan 149250083Sneel pthread_mutex_t rx_mtx; 150250083Sneel int rx_in_progress; 151271685Sgrehan int rx_vhdrlen; 152271685Sgrehan int rx_merge; /* merged rx bufs in use */ 153250083Sneel 154249917Sgrehan pthread_t tx_tid; 155249917Sgrehan pthread_mutex_t tx_mtx; 156249917Sgrehan pthread_cond_t tx_cond; 157250083Sneel int tx_in_progress; 158294294Sgnn 159294294Sgnn void (*pci_vtnet_rx)(struct pci_vtnet_softc *sc); 160294294Sgnn void (*pci_vtnet_tx)(struct pci_vtnet_softc *sc, struct iovec *iov, 161294294Sgnn int iovcnt, int len); 162221828Sgrehan}; 163221828Sgrehan 164253440Sgrehanstatic void pci_vtnet_reset(void *); 165253440Sgrehan/* static void pci_vtnet_notify(void *, struct vqueue_info *); */ 166253440Sgrehanstatic int pci_vtnet_cfgread(void *, int, int, uint32_t *); 167253440Sgrehanstatic int pci_vtnet_cfgwrite(void *, int, int, uint32_t); 168271685Sgrehanstatic void pci_vtnet_neg_features(void *, uint64_t); 169246109Sneel 170253440Sgrehanstatic struct virtio_consts vtnet_vi_consts = { 171253440Sgrehan "vtnet", /* our name */ 172253440Sgrehan VTNET_MAXQ - 1, /* we currently support 2 virtqueues */ 173253440Sgrehan sizeof(struct virtio_net_config), /* config reg size */ 174253440Sgrehan pci_vtnet_reset, /* reset */ 175253440Sgrehan NULL, /* device-wide qnotify -- not used */ 176253440Sgrehan pci_vtnet_cfgread, /* read PCI config */ 177253440Sgrehan pci_vtnet_cfgwrite, /* write PCI config */ 178271685Sgrehan pci_vtnet_neg_features, /* apply negotiated features */ 179253440Sgrehan VTNET_S_HOSTCAPS, /* our capabilities */ 180253440Sgrehan}; 181221828Sgrehan 182250083Sneel/* 183250083Sneel * If the transmit thread is active then stall until it is done. 184250083Sneel */ 185244160Sgrehanstatic void 186250083Sneelpci_vtnet_txwait(struct pci_vtnet_softc *sc) 187250083Sneel{ 188250083Sneel 189250083Sneel pthread_mutex_lock(&sc->tx_mtx); 190250083Sneel while (sc->tx_in_progress) { 191250083Sneel pthread_mutex_unlock(&sc->tx_mtx); 192250083Sneel usleep(10000); 193250083Sneel pthread_mutex_lock(&sc->tx_mtx); 194250083Sneel } 195250083Sneel pthread_mutex_unlock(&sc->tx_mtx); 196250083Sneel} 197250083Sneel 198250083Sneel/* 199250083Sneel * If the receive thread is active then stall until it is done. 200250083Sneel */ 201250083Sneelstatic void 202250083Sneelpci_vtnet_rxwait(struct pci_vtnet_softc *sc) 203250083Sneel{ 204250083Sneel 205250083Sneel pthread_mutex_lock(&sc->rx_mtx); 206250083Sneel while (sc->rx_in_progress) { 207250083Sneel pthread_mutex_unlock(&sc->rx_mtx); 208250083Sneel usleep(10000); 209250083Sneel pthread_mutex_lock(&sc->rx_mtx); 210250083Sneel } 211250083Sneel pthread_mutex_unlock(&sc->rx_mtx); 212250083Sneel} 213250083Sneel 214250083Sneelstatic void 215253440Sgrehanpci_vtnet_reset(void *vsc) 216221828Sgrehan{ 217253440Sgrehan struct pci_vtnet_softc *sc = vsc; 218244160Sgrehan 219253440Sgrehan DPRINTF(("vtnet: device reset requested !\n")); 220249917Sgrehan 221253440Sgrehan sc->resetting = 1; 222250083Sneel 223253440Sgrehan /* 224253440Sgrehan * Wait for the transmit and receive threads to finish their 225253440Sgrehan * processing. 226253440Sgrehan */ 227253440Sgrehan pci_vtnet_txwait(sc); 228253440Sgrehan pci_vtnet_rxwait(sc); 229250083Sneel 230253440Sgrehan sc->vsc_rx_ready = 0; 231271685Sgrehan sc->rx_merge = 1; 232271685Sgrehan sc->rx_vhdrlen = sizeof(struct virtio_net_rxhdr); 233250086Sneel 234253440Sgrehan /* now reset rings, MSI-X vectors, and negotiated capabilities */ 235253440Sgrehan vi_reset_dev(&sc->vsc_vs); 236250086Sneel 237253440Sgrehan sc->resetting = 0; 238221828Sgrehan} 239221828Sgrehan 240221828Sgrehan/* 241221828Sgrehan * Called to send a buffer chain out to the tap device 242221828Sgrehan */ 243221828Sgrehanstatic void 244221828Sgrehanpci_vtnet_tap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt, 245221828Sgrehan int len) 246221828Sgrehan{ 247253440Sgrehan static char pad[60]; /* all zero bytes */ 248221828Sgrehan 249221828Sgrehan if (sc->vsc_tapfd == -1) 250221828Sgrehan return; 251221828Sgrehan 252221828Sgrehan /* 253221828Sgrehan * If the length is < 60, pad out to that and add the 254221828Sgrehan * extra zero'd segment to the iov. It is guaranteed that 255221828Sgrehan * there is always an extra iov available by the caller. 256221828Sgrehan */ 257221828Sgrehan if (len < 60) { 258221828Sgrehan iov[iovcnt].iov_base = pad; 259221828Sgrehan iov[iovcnt].iov_len = 60 - len; 260221828Sgrehan iovcnt++; 261221828Sgrehan } 262221828Sgrehan (void) writev(sc->vsc_tapfd, iov, iovcnt); 263221828Sgrehan} 264221828Sgrehan 265221828Sgrehan/* 266221828Sgrehan * Called when there is read activity on the tap file descriptor. 267221828Sgrehan * Each buffer posted by the guest is assumed to be able to contain 268221828Sgrehan * an entire ethernet frame + rx header. 269221828Sgrehan * MP note: the dummybuf is only used for discarding frames, so there 270221828Sgrehan * is no need for it to be per-vtnet or locked. 271221828Sgrehan */ 272221828Sgrehanstatic uint8_t dummybuf[2048]; 273221828Sgrehan 274271685Sgrehanstatic __inline struct iovec * 275271685Sgrehanrx_iov_trim(struct iovec *iov, int *niov, int tlen) 276271685Sgrehan{ 277271685Sgrehan struct iovec *riov; 278271685Sgrehan 279271685Sgrehan /* XXX short-cut: assume first segment is >= tlen */ 280271685Sgrehan assert(iov[0].iov_len >= tlen); 281271685Sgrehan 282271685Sgrehan iov[0].iov_len -= tlen; 283271685Sgrehan if (iov[0].iov_len == 0) { 284271685Sgrehan assert(*niov > 1); 285271685Sgrehan *niov -= 1; 286271685Sgrehan riov = &iov[1]; 287271685Sgrehan } else { 288271685Sgrehan iov[0].iov_base = (void *)((uintptr_t)iov[0].iov_base + tlen); 289271685Sgrehan riov = &iov[0]; 290271685Sgrehan } 291271685Sgrehan 292271685Sgrehan return (riov); 293271685Sgrehan} 294271685Sgrehan 295221828Sgrehanstatic void 296221828Sgrehanpci_vtnet_tap_rx(struct pci_vtnet_softc *sc) 297221828Sgrehan{ 298271685Sgrehan struct iovec iov[VTNET_MAXSEGS], *riov; 299253440Sgrehan struct vqueue_info *vq; 300271685Sgrehan void *vrx; 301271685Sgrehan int len, n; 302280743Smav uint16_t idx; 303221828Sgrehan 304221828Sgrehan /* 305221828Sgrehan * Should never be called without a valid tap fd 306221828Sgrehan */ 307221828Sgrehan assert(sc->vsc_tapfd != -1); 308221828Sgrehan 309221828Sgrehan /* 310221828Sgrehan * But, will be called when the rx ring hasn't yet 311250083Sneel * been set up or the guest is resetting the device. 312221828Sgrehan */ 313250083Sneel if (!sc->vsc_rx_ready || sc->resetting) { 314221828Sgrehan /* 315221828Sgrehan * Drop the packet and try later. 316221828Sgrehan */ 317221828Sgrehan (void) read(sc->vsc_tapfd, dummybuf, sizeof(dummybuf)); 318221828Sgrehan return; 319221828Sgrehan } 320221828Sgrehan 321221828Sgrehan /* 322253440Sgrehan * Check for available rx buffers 323221828Sgrehan */ 324253440Sgrehan vq = &sc->vsc_queues[VTNET_RXQ]; 325253440Sgrehan if (!vq_has_descs(vq)) { 326221828Sgrehan /* 327253440Sgrehan * Drop the packet and try later. Interrupt on 328253440Sgrehan * empty, if that's negotiated. 329221828Sgrehan */ 330221828Sgrehan (void) read(sc->vsc_tapfd, dummybuf, sizeof(dummybuf)); 331253440Sgrehan vq_endchains(vq, 1); 332221828Sgrehan return; 333221828Sgrehan } 334221828Sgrehan 335253440Sgrehan do { 336221828Sgrehan /* 337271685Sgrehan * Get descriptor chain. 338221828Sgrehan */ 339280743Smav n = vq_getchain(vq, &idx, iov, VTNET_MAXSEGS, NULL); 340271685Sgrehan assert(n >= 1 && n <= VTNET_MAXSEGS); 341221828Sgrehan 342221828Sgrehan /* 343221828Sgrehan * Get a pointer to the rx header, and use the 344221828Sgrehan * data immediately following it for the packet buffer. 345221828Sgrehan */ 346271685Sgrehan vrx = iov[0].iov_base; 347271685Sgrehan riov = rx_iov_trim(iov, &n, sc->rx_vhdrlen); 348221828Sgrehan 349271685Sgrehan len = readv(sc->vsc_tapfd, riov, n); 350221828Sgrehan 351221828Sgrehan if (len < 0 && errno == EWOULDBLOCK) { 352253440Sgrehan /* 353253440Sgrehan * No more packets, but still some avail ring 354253440Sgrehan * entries. Interrupt if needed/appropriate. 355253440Sgrehan */ 356280743Smav vq_retchain(vq); 357253440Sgrehan vq_endchains(vq, 0); 358253440Sgrehan return; 359221828Sgrehan } 360221828Sgrehan 361221828Sgrehan /* 362221828Sgrehan * The only valid field in the rx packet header is the 363271685Sgrehan * number of buffers if merged rx bufs were negotiated. 364221828Sgrehan */ 365271685Sgrehan memset(vrx, 0, sc->rx_vhdrlen); 366221828Sgrehan 367271685Sgrehan if (sc->rx_merge) { 368271685Sgrehan struct virtio_net_rxhdr *vrxh; 369271685Sgrehan 370271685Sgrehan vrxh = vrx; 371271685Sgrehan vrxh->vrh_bufs = 1; 372271685Sgrehan } 373271685Sgrehan 374221828Sgrehan /* 375253440Sgrehan * Release this chain and handle more chains. 376221828Sgrehan */ 377280743Smav vq_relchain(vq, idx, len + sc->rx_vhdrlen); 378253440Sgrehan } while (vq_has_descs(vq)); 379221828Sgrehan 380253440Sgrehan /* Interrupt if needed, including for NOTIFY_ON_EMPTY. */ 381253440Sgrehan vq_endchains(vq, 1); 382221828Sgrehan} 383221828Sgrehan 384294294Sgnnstatic int 385294294Sgnnpci_vtnet_netmap_writev(struct nm_desc *nmd, struct iovec *iov, int iovcnt) 386294294Sgnn{ 387294294Sgnn int r, i; 388294294Sgnn int len = 0; 389294294Sgnn 390294294Sgnn for (r = nmd->cur_tx_ring; ; ) { 391294294Sgnn struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, r); 392294294Sgnn uint32_t cur, idx; 393294294Sgnn char *buf; 394294294Sgnn 395294294Sgnn if (nm_ring_empty(ring)) { 396294294Sgnn r++; 397294294Sgnn if (r > nmd->last_tx_ring) 398294294Sgnn r = nmd->first_tx_ring; 399294294Sgnn if (r == nmd->cur_rx_ring) 400294294Sgnn break; 401294294Sgnn continue; 402294294Sgnn } 403294294Sgnn cur = ring->cur; 404294294Sgnn idx = ring->slot[cur].buf_idx; 405294294Sgnn buf = NETMAP_BUF(ring, idx); 406294294Sgnn 407294294Sgnn for (i = 0; i < iovcnt; i++) { 408294294Sgnn memcpy(&buf[len], iov[i].iov_base, iov[i].iov_len); 409294294Sgnn len += iov[i].iov_len; 410294294Sgnn } 411294294Sgnn ring->slot[cur].len = len; 412294294Sgnn ring->head = ring->cur = nm_ring_next(ring, cur); 413294294Sgnn nmd->cur_tx_ring = r; 414294294Sgnn ioctl(nmd->fd, NIOCTXSYNC, NULL); 415294294Sgnn break; 416294294Sgnn } 417294294Sgnn 418294294Sgnn return (len); 419294294Sgnn} 420294294Sgnn 421294294Sgnnstatic inline int 422294294Sgnnpci_vtnet_netmap_readv(struct nm_desc *nmd, struct iovec *iov, int iovcnt) 423294294Sgnn{ 424294294Sgnn int len = 0; 425294294Sgnn int i = 0; 426294294Sgnn int r; 427294294Sgnn 428294294Sgnn for (r = nmd->cur_rx_ring; ; ) { 429294294Sgnn struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, r); 430294294Sgnn uint32_t cur, idx; 431294294Sgnn char *buf; 432294294Sgnn size_t left; 433294294Sgnn 434294294Sgnn if (nm_ring_empty(ring)) { 435294294Sgnn r++; 436294294Sgnn if (r > nmd->last_rx_ring) 437294294Sgnn r = nmd->first_rx_ring; 438294294Sgnn if (r == nmd->cur_rx_ring) 439294294Sgnn break; 440294294Sgnn continue; 441294294Sgnn } 442294294Sgnn cur = ring->cur; 443294294Sgnn idx = ring->slot[cur].buf_idx; 444294294Sgnn buf = NETMAP_BUF(ring, idx); 445294294Sgnn left = ring->slot[cur].len; 446294294Sgnn 447294294Sgnn for (i = 0; i < iovcnt && left > 0; i++) { 448294294Sgnn if (iov[i].iov_len > left) 449294294Sgnn iov[i].iov_len = left; 450294294Sgnn memcpy(iov[i].iov_base, &buf[len], iov[i].iov_len); 451294294Sgnn len += iov[i].iov_len; 452294294Sgnn left -= iov[i].iov_len; 453294294Sgnn } 454294294Sgnn ring->head = ring->cur = nm_ring_next(ring, cur); 455294294Sgnn nmd->cur_rx_ring = r; 456294294Sgnn ioctl(nmd->fd, NIOCRXSYNC, NULL); 457294294Sgnn break; 458294294Sgnn } 459294294Sgnn for (; i < iovcnt; i++) 460294294Sgnn iov[i].iov_len = 0; 461294294Sgnn 462294294Sgnn return (len); 463294294Sgnn} 464294294Sgnn 465294294Sgnn/* 466294294Sgnn * Called to send a buffer chain out to the vale port 467294294Sgnn */ 468221828Sgrehanstatic void 469294294Sgnnpci_vtnet_netmap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt, 470294294Sgnn int len) 471221828Sgrehan{ 472294294Sgnn static char pad[60]; /* all zero bytes */ 473294294Sgnn 474294294Sgnn if (sc->vsc_nmd == NULL) 475294294Sgnn return; 476294294Sgnn 477294294Sgnn /* 478294294Sgnn * If the length is < 60, pad out to that and add the 479294294Sgnn * extra zero'd segment to the iov. It is guaranteed that 480294294Sgnn * there is always an extra iov available by the caller. 481294294Sgnn */ 482294294Sgnn if (len < 60) { 483294294Sgnn iov[iovcnt].iov_base = pad; 484294294Sgnn iov[iovcnt].iov_len = 60 - len; 485294294Sgnn iovcnt++; 486294294Sgnn } 487294294Sgnn (void) pci_vtnet_netmap_writev(sc->vsc_nmd, iov, iovcnt); 488294294Sgnn} 489294294Sgnn 490294294Sgnnstatic void 491294294Sgnnpci_vtnet_netmap_rx(struct pci_vtnet_softc *sc) 492294294Sgnn{ 493294294Sgnn struct iovec iov[VTNET_MAXSEGS], *riov; 494294294Sgnn struct vqueue_info *vq; 495294294Sgnn void *vrx; 496294294Sgnn int len, n; 497294294Sgnn uint16_t idx; 498294294Sgnn 499294294Sgnn /* 500294294Sgnn * Should never be called without a valid netmap descriptor 501294294Sgnn */ 502294294Sgnn assert(sc->vsc_nmd != NULL); 503294294Sgnn 504294294Sgnn /* 505294294Sgnn * But, will be called when the rx ring hasn't yet 506294294Sgnn * been set up or the guest is resetting the device. 507294294Sgnn */ 508294294Sgnn if (!sc->vsc_rx_ready || sc->resetting) { 509294294Sgnn /* 510294294Sgnn * Drop the packet and try later. 511294294Sgnn */ 512294294Sgnn (void) nm_nextpkt(sc->vsc_nmd, (void *)dummybuf); 513294294Sgnn return; 514294294Sgnn } 515294294Sgnn 516294294Sgnn /* 517294294Sgnn * Check for available rx buffers 518294294Sgnn */ 519294294Sgnn vq = &sc->vsc_queues[VTNET_RXQ]; 520294294Sgnn if (!vq_has_descs(vq)) { 521294294Sgnn /* 522294294Sgnn * Drop the packet and try later. Interrupt on 523294294Sgnn * empty, if that's negotiated. 524294294Sgnn */ 525294294Sgnn (void) nm_nextpkt(sc->vsc_nmd, (void *)dummybuf); 526294294Sgnn vq_endchains(vq, 1); 527294294Sgnn return; 528294294Sgnn } 529294294Sgnn 530294294Sgnn do { 531294294Sgnn /* 532294294Sgnn * Get descriptor chain. 533294294Sgnn */ 534294294Sgnn n = vq_getchain(vq, &idx, iov, VTNET_MAXSEGS, NULL); 535294294Sgnn assert(n >= 1 && n <= VTNET_MAXSEGS); 536294294Sgnn 537294294Sgnn /* 538294294Sgnn * Get a pointer to the rx header, and use the 539294294Sgnn * data immediately following it for the packet buffer. 540294294Sgnn */ 541294294Sgnn vrx = iov[0].iov_base; 542294294Sgnn riov = rx_iov_trim(iov, &n, sc->rx_vhdrlen); 543294294Sgnn 544294294Sgnn len = pci_vtnet_netmap_readv(sc->vsc_nmd, riov, n); 545294294Sgnn 546294294Sgnn if (len == 0) { 547294294Sgnn /* 548294294Sgnn * No more packets, but still some avail ring 549294294Sgnn * entries. Interrupt if needed/appropriate. 550294294Sgnn */ 551294294Sgnn vq_endchains(vq, 0); 552294294Sgnn return; 553294294Sgnn } 554294294Sgnn 555294294Sgnn /* 556294294Sgnn * The only valid field in the rx packet header is the 557294294Sgnn * number of buffers if merged rx bufs were negotiated. 558294294Sgnn */ 559294294Sgnn memset(vrx, 0, sc->rx_vhdrlen); 560294294Sgnn 561294294Sgnn if (sc->rx_merge) { 562294294Sgnn struct virtio_net_rxhdr *vrxh; 563294294Sgnn 564294294Sgnn vrxh = vrx; 565294294Sgnn vrxh->vrh_bufs = 1; 566294294Sgnn } 567294294Sgnn 568294294Sgnn /* 569294294Sgnn * Release this chain and handle more chains. 570294294Sgnn */ 571294294Sgnn vq_relchain(vq, idx, len + sc->rx_vhdrlen); 572294294Sgnn } while (vq_has_descs(vq)); 573294294Sgnn 574294294Sgnn /* Interrupt if needed, including for NOTIFY_ON_EMPTY. */ 575294294Sgnn vq_endchains(vq, 1); 576294294Sgnn} 577294294Sgnn 578294294Sgnnstatic void 579294294Sgnnpci_vtnet_rx_callback(int fd, enum ev_type type, void *param) 580294294Sgnn{ 581221828Sgrehan struct pci_vtnet_softc *sc = param; 582221828Sgrehan 583250083Sneel pthread_mutex_lock(&sc->rx_mtx); 584250083Sneel sc->rx_in_progress = 1; 585294294Sgnn sc->pci_vtnet_rx(sc); 586250083Sneel sc->rx_in_progress = 0; 587250083Sneel pthread_mutex_unlock(&sc->rx_mtx); 588221828Sgrehan 589221828Sgrehan} 590221828Sgrehan 591221828Sgrehanstatic void 592253440Sgrehanpci_vtnet_ping_rxq(void *vsc, struct vqueue_info *vq) 593221828Sgrehan{ 594253440Sgrehan struct pci_vtnet_softc *sc = vsc; 595253440Sgrehan 596221828Sgrehan /* 597221828Sgrehan * A qnotify means that the rx process can now begin 598221828Sgrehan */ 599221828Sgrehan if (sc->vsc_rx_ready == 0) { 600221828Sgrehan sc->vsc_rx_ready = 1; 601282839Smav vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY; 602221828Sgrehan } 603221828Sgrehan} 604221828Sgrehan 605221828Sgrehanstatic void 606253440Sgrehanpci_vtnet_proctx(struct pci_vtnet_softc *sc, struct vqueue_info *vq) 607221828Sgrehan{ 608221828Sgrehan struct iovec iov[VTNET_MAXSEGS + 1]; 609253440Sgrehan int i, n; 610253440Sgrehan int plen, tlen; 611280743Smav uint16_t idx; 612221828Sgrehan 613221828Sgrehan /* 614253440Sgrehan * Obtain chain of descriptors. The first one is 615253440Sgrehan * really the header descriptor, so we need to sum 616253440Sgrehan * up two lengths: packet length and transfer length. 617221828Sgrehan */ 618280743Smav n = vq_getchain(vq, &idx, iov, VTNET_MAXSEGS, NULL); 619253440Sgrehan assert(n >= 1 && n <= VTNET_MAXSEGS); 620253440Sgrehan plen = 0; 621253440Sgrehan tlen = iov[0].iov_len; 622253440Sgrehan for (i = 1; i < n; i++) { 623253440Sgrehan plen += iov[i].iov_len; 624253440Sgrehan tlen += iov[i].iov_len; 625221828Sgrehan } 626221828Sgrehan 627253440Sgrehan DPRINTF(("virtio: packet send, %d bytes, %d segs\n\r", plen, n)); 628294294Sgnn sc->pci_vtnet_tx(sc, &iov[1], n - 1, plen); 629221828Sgrehan 630253440Sgrehan /* chain is processed, release it and set tlen */ 631280743Smav vq_relchain(vq, idx, tlen); 632221828Sgrehan} 633221828Sgrehan 634221828Sgrehanstatic void 635253440Sgrehanpci_vtnet_ping_txq(void *vsc, struct vqueue_info *vq) 636221828Sgrehan{ 637253440Sgrehan struct pci_vtnet_softc *sc = vsc; 638221828Sgrehan 639221828Sgrehan /* 640253440Sgrehan * Any ring entries to process? 641221828Sgrehan */ 642253440Sgrehan if (!vq_has_descs(vq)) 643221828Sgrehan return; 644221828Sgrehan 645249917Sgrehan /* Signal the tx thread for processing */ 646249917Sgrehan pthread_mutex_lock(&sc->tx_mtx); 647282839Smav vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY; 648249917Sgrehan if (sc->tx_in_progress == 0) 649249917Sgrehan pthread_cond_signal(&sc->tx_cond); 650249917Sgrehan pthread_mutex_unlock(&sc->tx_mtx); 651221828Sgrehan} 652221828Sgrehan 653249917Sgrehan/* 654249917Sgrehan * Thread which will handle processing of TX desc 655249917Sgrehan */ 656249917Sgrehanstatic void * 657249917Sgrehanpci_vtnet_tx_thread(void *param) 658249917Sgrehan{ 659253440Sgrehan struct pci_vtnet_softc *sc = param; 660253440Sgrehan struct vqueue_info *vq; 661282839Smav int error; 662253440Sgrehan 663253440Sgrehan vq = &sc->vsc_queues[VTNET_TXQ]; 664253440Sgrehan 665253440Sgrehan /* 666253440Sgrehan * Let us wait till the tx queue pointers get initialised & 667253440Sgrehan * first tx signaled 668249917Sgrehan */ 669249917Sgrehan pthread_mutex_lock(&sc->tx_mtx); 670249917Sgrehan error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx); 671249917Sgrehan assert(error == 0); 672253440Sgrehan 673249917Sgrehan for (;;) { 674253440Sgrehan /* note - tx mutex is locked here */ 675282839Smav while (sc->resetting || !vq_has_descs(vq)) { 676282839Smav vq->vq_used->vu_flags &= ~VRING_USED_F_NO_NOTIFY; 677282839Smav mb(); 678282839Smav if (!sc->resetting && vq_has_descs(vq)) 679282839Smav break; 680250197Sneel 681282839Smav sc->tx_in_progress = 0; 682282839Smav error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx); 683282839Smav assert(error == 0); 684282839Smav } 685282839Smav vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY; 686249917Sgrehan sc->tx_in_progress = 1; 687249917Sgrehan pthread_mutex_unlock(&sc->tx_mtx); 688249917Sgrehan 689253440Sgrehan do { 690249917Sgrehan /* 691253440Sgrehan * Run through entries, placing them into 692253440Sgrehan * iovecs and sending when an end-of-packet 693253440Sgrehan * is found 694249917Sgrehan */ 695253440Sgrehan pci_vtnet_proctx(sc, vq); 696253440Sgrehan } while (vq_has_descs(vq)); 697250197Sneel 698250197Sneel /* 699250197Sneel * Generate an interrupt if needed. 700250197Sneel */ 701253440Sgrehan vq_endchains(vq, 1); 702253440Sgrehan 703253440Sgrehan pthread_mutex_lock(&sc->tx_mtx); 704249917Sgrehan } 705221828Sgrehan} 706221828Sgrehan 707253440Sgrehan#ifdef notyet 708221828Sgrehanstatic void 709253440Sgrehanpci_vtnet_ping_ctlq(void *vsc, struct vqueue_info *vq) 710221828Sgrehan{ 711221828Sgrehan 712253440Sgrehan DPRINTF(("vtnet: control qnotify!\n\r")); 713221828Sgrehan} 714253440Sgrehan#endif 715221828Sgrehan 716221828Sgrehanstatic int 717252682Sgrehanpci_vtnet_parsemac(char *mac_str, uint8_t *mac_addr) 718252682Sgrehan{ 719252682Sgrehan struct ether_addr *ea; 720252682Sgrehan char *tmpstr; 721252682Sgrehan char zero_addr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }; 722252682Sgrehan 723252682Sgrehan tmpstr = strsep(&mac_str,"="); 724252682Sgrehan 725252682Sgrehan if ((mac_str != NULL) && (!strcmp(tmpstr,"mac"))) { 726252682Sgrehan ea = ether_aton(mac_str); 727252682Sgrehan 728252682Sgrehan if (ea == NULL || ETHER_IS_MULTICAST(ea->octet) || 729252682Sgrehan memcmp(ea->octet, zero_addr, ETHER_ADDR_LEN) == 0) { 730252682Sgrehan fprintf(stderr, "Invalid MAC %s\n", mac_str); 731252682Sgrehan return (EINVAL); 732252682Sgrehan } else 733252682Sgrehan memcpy(mac_addr, ea->octet, ETHER_ADDR_LEN); 734252682Sgrehan } 735252682Sgrehan 736252682Sgrehan return (0); 737252682Sgrehan} 738252682Sgrehan 739294294Sgnnstatic void 740294294Sgnnpci_vtnet_tap_setup(struct pci_vtnet_softc *sc, char *devname) 741294294Sgnn{ 742294294Sgnn char tbuf[80]; 743252682Sgrehan 744294294Sgnn strcpy(tbuf, "/dev/"); 745294294Sgnn strlcat(tbuf, devname, sizeof(tbuf)); 746294294Sgnn 747294294Sgnn sc->pci_vtnet_rx = pci_vtnet_tap_rx; 748294294Sgnn sc->pci_vtnet_tx = pci_vtnet_tap_tx; 749294294Sgnn 750294294Sgnn sc->vsc_tapfd = open(tbuf, O_RDWR); 751294294Sgnn if (sc->vsc_tapfd == -1) { 752294294Sgnn WPRINTF(("open of tap device %s failed\n", tbuf)); 753294294Sgnn return; 754294294Sgnn } 755294294Sgnn 756294294Sgnn /* 757294294Sgnn * Set non-blocking and register for read 758294294Sgnn * notifications with the event loop 759294294Sgnn */ 760294294Sgnn int opt = 1; 761294294Sgnn if (ioctl(sc->vsc_tapfd, FIONBIO, &opt) < 0) { 762294294Sgnn WPRINTF(("tap device O_NONBLOCK failed\n")); 763294294Sgnn close(sc->vsc_tapfd); 764294294Sgnn sc->vsc_tapfd = -1; 765294294Sgnn } 766294294Sgnn 767294294Sgnn sc->vsc_mevp = mevent_add(sc->vsc_tapfd, 768294294Sgnn EVF_READ, 769294294Sgnn pci_vtnet_rx_callback, 770294294Sgnn sc); 771294294Sgnn if (sc->vsc_mevp == NULL) { 772294294Sgnn WPRINTF(("Could not register event\n")); 773294294Sgnn close(sc->vsc_tapfd); 774294294Sgnn sc->vsc_tapfd = -1; 775294294Sgnn } 776294294Sgnn} 777294294Sgnn 778294294Sgnnstatic void 779294294Sgnnpci_vtnet_netmap_setup(struct pci_vtnet_softc *sc, char *ifname) 780294294Sgnn{ 781294294Sgnn sc->pci_vtnet_rx = pci_vtnet_netmap_rx; 782294294Sgnn sc->pci_vtnet_tx = pci_vtnet_netmap_tx; 783294294Sgnn 784294294Sgnn sc->vsc_nmd = nm_open(ifname, NULL, 0, 0); 785294294Sgnn if (sc->vsc_nmd == NULL) { 786294294Sgnn WPRINTF(("open of netmap device %s failed\n", ifname)); 787294294Sgnn return; 788294294Sgnn } 789294294Sgnn 790294294Sgnn sc->vsc_mevp = mevent_add(sc->vsc_nmd->fd, 791294294Sgnn EVF_READ, 792294294Sgnn pci_vtnet_rx_callback, 793294294Sgnn sc); 794294294Sgnn if (sc->vsc_mevp == NULL) { 795294294Sgnn WPRINTF(("Could not register event\n")); 796294294Sgnn nm_close(sc->vsc_nmd); 797294294Sgnn sc->vsc_nmd = NULL; 798294294Sgnn } 799294294Sgnn} 800294294Sgnn 801252682Sgrehanstatic int 802221828Sgrehanpci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 803221828Sgrehan{ 804221828Sgrehan MD5_CTX mdctx; 805221828Sgrehan unsigned char digest[16]; 806221828Sgrehan char nstr[80]; 807249917Sgrehan char tname[MAXCOMLEN + 1]; 808221828Sgrehan struct pci_vtnet_softc *sc; 809252682Sgrehan char *devname; 810252682Sgrehan char *vtopts; 811252682Sgrehan int mac_provided; 812221828Sgrehan 813268953Sjhb sc = calloc(1, sizeof(struct pci_vtnet_softc)); 814221828Sgrehan 815253440Sgrehan pthread_mutex_init(&sc->vsc_mtx, NULL); 816221828Sgrehan 817253440Sgrehan vi_softc_linkup(&sc->vsc_vs, &vtnet_vi_consts, sc, pi, sc->vsc_queues); 818267393Sjhb sc->vsc_vs.vs_mtx = &sc->vsc_mtx; 819267393Sjhb 820253440Sgrehan sc->vsc_queues[VTNET_RXQ].vq_qsize = VTNET_RINGSZ; 821253440Sgrehan sc->vsc_queues[VTNET_RXQ].vq_notify = pci_vtnet_ping_rxq; 822253440Sgrehan sc->vsc_queues[VTNET_TXQ].vq_qsize = VTNET_RINGSZ; 823253440Sgrehan sc->vsc_queues[VTNET_TXQ].vq_notify = pci_vtnet_ping_txq; 824253440Sgrehan#ifdef notyet 825253440Sgrehan sc->vsc_queues[VTNET_CTLQ].vq_qsize = VTNET_RINGSZ; 826253440Sgrehan sc->vsc_queues[VTNET_CTLQ].vq_notify = pci_vtnet_ping_ctlq; 827253440Sgrehan#endif 828246109Sneel 829246109Sneel /* 830252682Sgrehan * Attempt to open the tap device and read the MAC address 831252682Sgrehan * if specified 832221828Sgrehan */ 833252682Sgrehan mac_provided = 0; 834221828Sgrehan sc->vsc_tapfd = -1; 835294294Sgnn sc->vsc_nmd = NULL; 836221828Sgrehan if (opts != NULL) { 837252682Sgrehan int err; 838221828Sgrehan 839252682Sgrehan devname = vtopts = strdup(opts); 840252682Sgrehan (void) strsep(&vtopts, ","); 841252682Sgrehan 842252682Sgrehan if (vtopts != NULL) { 843253440Sgrehan err = pci_vtnet_parsemac(vtopts, sc->vsc_config.mac); 844252682Sgrehan if (err != 0) { 845252682Sgrehan free(devname); 846252682Sgrehan return (err); 847252682Sgrehan } 848252682Sgrehan mac_provided = 1; 849252682Sgrehan } 850252682Sgrehan 851294294Sgnn if (strncmp(devname, "vale", 4) == 0) 852294294Sgnn pci_vtnet_netmap_setup(sc, devname); 853294294Sgnn if ((strncmp(devname, "tap", 3) == 0) || 854307183Snp (strncmp(devname, "vmnet", 5) == 0)) 855294294Sgnn pci_vtnet_tap_setup(sc, devname); 856221828Sgrehan 857252682Sgrehan free(devname); 858221828Sgrehan } 859221828Sgrehan 860221828Sgrehan /* 861252682Sgrehan * The default MAC address is the standard NetApp OUI of 00-a0-98, 862252682Sgrehan * followed by an MD5 of the PCI slot/func number and dev name 863221828Sgrehan */ 864252682Sgrehan if (!mac_provided) { 865244159Sgrehan snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot, 866259301Sgrehan pi->pi_func, vmname); 867221828Sgrehan 868252682Sgrehan MD5Init(&mdctx); 869252682Sgrehan MD5Update(&mdctx, nstr, strlen(nstr)); 870252682Sgrehan MD5Final(digest, &mdctx); 871221828Sgrehan 872253440Sgrehan sc->vsc_config.mac[0] = 0x00; 873253440Sgrehan sc->vsc_config.mac[1] = 0xa0; 874253440Sgrehan sc->vsc_config.mac[2] = 0x98; 875253440Sgrehan sc->vsc_config.mac[3] = digest[0]; 876253440Sgrehan sc->vsc_config.mac[4] = digest[1]; 877253440Sgrehan sc->vsc_config.mac[5] = digest[2]; 878252682Sgrehan } 879221828Sgrehan 880221828Sgrehan /* initialize config space */ 881221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET); 882221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 883221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK); 884221828Sgrehan pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET); 885284900Sneel pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR); 886253440Sgrehan 887282840Smav /* Link is up if we managed to open tap device. */ 888282840Smav sc->vsc_config.status = (opts == NULL || sc->vsc_tapfd >= 0); 889246109Sneel 890253440Sgrehan /* use BAR 1 to map MSI-X table and PBA, if we're using MSI-X */ 891256755Sgrehan if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix())) 892253440Sgrehan return (1); 893246109Sneel 894253440Sgrehan /* use BAR 0 to map config regs in IO space */ 895253440Sgrehan vi_set_io_bar(&sc->vsc_vs, 0); 896246109Sneel 897250083Sneel sc->resetting = 0; 898250083Sneel 899271685Sgrehan sc->rx_merge = 1; 900271685Sgrehan sc->rx_vhdrlen = sizeof(struct virtio_net_rxhdr); 901250083Sneel sc->rx_in_progress = 0; 902250083Sneel pthread_mutex_init(&sc->rx_mtx, NULL); 903250083Sneel 904249917Sgrehan /* 905253440Sgrehan * Initialize tx semaphore & spawn TX processing thread. 906249917Sgrehan * As of now, only one thread for TX desc processing is 907249917Sgrehan * spawned. 908249917Sgrehan */ 909249917Sgrehan sc->tx_in_progress = 0; 910249917Sgrehan pthread_mutex_init(&sc->tx_mtx, NULL); 911249917Sgrehan pthread_cond_init(&sc->tx_cond, NULL); 912249917Sgrehan pthread_create(&sc->tx_tid, NULL, pci_vtnet_tx_thread, (void *)sc); 913259301Sgrehan snprintf(tname, sizeof(tname), "vtnet-%d:%d tx", pi->pi_slot, 914259301Sgrehan pi->pi_func); 915249917Sgrehan pthread_set_name_np(sc->tx_tid, tname); 916221828Sgrehan 917221828Sgrehan return (0); 918221828Sgrehan} 919221828Sgrehan 920253440Sgrehanstatic int 921253440Sgrehanpci_vtnet_cfgwrite(void *vsc, int offset, int size, uint32_t value) 922246109Sneel{ 923253440Sgrehan struct pci_vtnet_softc *sc = vsc; 924222830Sgrehan void *ptr; 925222830Sgrehan 926253440Sgrehan if (offset < 6) { 927253440Sgrehan assert(offset + size <= 6); 928221828Sgrehan /* 929221828Sgrehan * The driver is allowed to change the MAC address 930221828Sgrehan */ 931253440Sgrehan ptr = &sc->vsc_config.mac[offset]; 932253440Sgrehan memcpy(ptr, &value, size); 933253440Sgrehan } else { 934271685Sgrehan /* silently ignore other writes */ 935253440Sgrehan DPRINTF(("vtnet: write to readonly reg %d\n\r", offset)); 936221828Sgrehan } 937271685Sgrehan 938253440Sgrehan return (0); 939221828Sgrehan} 940221828Sgrehan 941253440Sgrehanstatic int 942253440Sgrehanpci_vtnet_cfgread(void *vsc, int offset, int size, uint32_t *retval) 943221828Sgrehan{ 944253440Sgrehan struct pci_vtnet_softc *sc = vsc; 945222830Sgrehan void *ptr; 946221828Sgrehan 947253440Sgrehan ptr = (uint8_t *)&sc->vsc_config + offset; 948253440Sgrehan memcpy(retval, ptr, size); 949253440Sgrehan return (0); 950221828Sgrehan} 951221828Sgrehan 952271685Sgrehanstatic void 953271685Sgrehanpci_vtnet_neg_features(void *vsc, uint64_t negotiated_features) 954271685Sgrehan{ 955271685Sgrehan struct pci_vtnet_softc *sc = vsc; 956271685Sgrehan 957271685Sgrehan sc->vsc_features = negotiated_features; 958271685Sgrehan 959271685Sgrehan if (!(sc->vsc_features & VIRTIO_NET_F_MRG_RXBUF)) { 960271685Sgrehan sc->rx_merge = 0; 961271685Sgrehan /* non-merge rx header is 2 bytes shorter */ 962271685Sgrehan sc->rx_vhdrlen -= 2; 963271685Sgrehan } 964271685Sgrehan} 965271685Sgrehan 966221828Sgrehanstruct pci_devemu pci_de_vnet = { 967241744Sgrehan .pe_emu = "virtio-net", 968241744Sgrehan .pe_init = pci_vtnet_init, 969253440Sgrehan .pe_barwrite = vi_pci_write, 970253440Sgrehan .pe_barread = vi_pci_read 971221828Sgrehan}; 972221828SgrehanPCI_EMUL_SET(pci_de_vnet); 973