pci_virtio_net.c revision 250086
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: head/usr.sbin/bhyve/pci_virtio_net.c 250086 2013-04-30 01:14:54Z neel $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_net.c 250086 2013-04-30 01:14:54Z neel $"); 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> 37221828Sgrehan 38221828Sgrehan#include <errno.h> 39221828Sgrehan#include <fcntl.h> 40221828Sgrehan#include <stdio.h> 41221828Sgrehan#include <stdlib.h> 42221828Sgrehan#include <stdint.h> 43221828Sgrehan#include <string.h> 44221828Sgrehan#include <strings.h> 45221828Sgrehan#include <unistd.h> 46221828Sgrehan#include <assert.h> 47221828Sgrehan#include <md5.h> 48221828Sgrehan#include <pthread.h> 49249917Sgrehan#include <pthread_np.h> 50221828Sgrehan 51244167Sgrehan#include "bhyverun.h" 52221828Sgrehan#include "pci_emul.h" 53221828Sgrehan#include "mevent.h" 54221828Sgrehan#include "virtio.h" 55221828Sgrehan 56249917Sgrehan#define VTNET_RINGSZ 1024 57221828Sgrehan 58221828Sgrehan#define VTNET_MAXSEGS 32 59221828Sgrehan 60221828Sgrehan/* 61221828Sgrehan * PCI config-space register offsets 62221828Sgrehan */ 63246109Sneel#define VTNET_R_CFG0 24 64246109Sneel#define VTNET_R_CFG1 25 65246109Sneel#define VTNET_R_CFG2 26 66246109Sneel#define VTNET_R_CFG3 27 67246109Sneel#define VTNET_R_CFG4 28 68246109Sneel#define VTNET_R_CFG5 29 69246109Sneel#define VTNET_R_CFG6 30 70246109Sneel#define VTNET_R_CFG7 31 71246109Sneel#define VTNET_R_MAX 31 72221828Sgrehan 73246109Sneel#define VTNET_REGSZ VTNET_R_MAX+1 74221828Sgrehan 75221828Sgrehan/* 76221828Sgrehan * Host capabilities 77221828Sgrehan */ 78221828Sgrehan#define VTNET_S_HOSTCAPS \ 79221828Sgrehan ( 0x00000020 | /* host supplies MAC */ \ 80221828Sgrehan 0x00008000 | /* host can merge Rx buffers */ \ 81221828Sgrehan 0x00010000 ) /* config status available */ 82221828Sgrehan 83221828Sgrehan/* 84221828Sgrehan * Queue definitions. 85221828Sgrehan */ 86221828Sgrehan#define VTNET_RXQ 0 87221828Sgrehan#define VTNET_TXQ 1 88221828Sgrehan#define VTNET_CTLQ 2 89221828Sgrehan 90221828Sgrehan#define VTNET_MAXQ 3 91221828Sgrehan 92246109Sneelstatic int use_msix = 1; 93246109Sneel 94221828Sgrehanstruct vring_hqueue { 95221828Sgrehan /* Internal state */ 96221828Sgrehan uint16_t hq_size; 97221828Sgrehan uint16_t hq_cur_aidx; /* trails behind 'avail_idx' */ 98221828Sgrehan 99221828Sgrehan /* Host-context pointers to the queue */ 100221828Sgrehan struct virtio_desc *hq_dtable; 101221828Sgrehan uint16_t *hq_avail_flags; 102221828Sgrehan uint16_t *hq_avail_idx; /* monotonically increasing */ 103221828Sgrehan uint16_t *hq_avail_ring; 104221828Sgrehan 105221828Sgrehan uint16_t *hq_used_flags; 106221828Sgrehan uint16_t *hq_used_idx; /* monotonically increasing */ 107221828Sgrehan struct virtio_used *hq_used_ring; 108221828Sgrehan}; 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 { 134221828Sgrehan struct pci_devinst *vsc_pi; 135221828Sgrehan pthread_mutex_t vsc_mtx; 136221828Sgrehan struct mevent *vsc_mevp; 137221828Sgrehan 138221828Sgrehan int vsc_curq; 139221828Sgrehan int vsc_status; 140221828Sgrehan int vsc_isr; 141221828Sgrehan int vsc_tapfd; 142221828Sgrehan int vsc_rx_ready; 143249917Sgrehan int resetting; 144221828Sgrehan 145221828Sgrehan uint32_t vsc_features; 146221828Sgrehan uint8_t vsc_macaddr[6]; 147221828Sgrehan 148221828Sgrehan uint64_t vsc_pfn[VTNET_MAXQ]; 149221828Sgrehan struct vring_hqueue vsc_hq[VTNET_MAXQ]; 150246109Sneel uint16_t vsc_msix_table_idx[VTNET_MAXQ]; 151250083Sneel 152250083Sneel pthread_mutex_t rx_mtx; 153250083Sneel int rx_in_progress; 154250083Sneel 155249917Sgrehan pthread_t tx_tid; 156249917Sgrehan pthread_mutex_t tx_mtx; 157249917Sgrehan pthread_cond_t tx_cond; 158250083Sneel int tx_in_progress; 159221828Sgrehan}; 160248477Sneel#define vtnet_ctx(sc) ((sc)->vsc_pi->pi_vmctx) 161221828Sgrehan 162221828Sgrehan/* 163246109Sneel * Return the size of IO BAR that maps virtio header and device specific 164246109Sneel * region. The size would vary depending on whether MSI-X is enabled or 165246109Sneel * not. 166246109Sneel */ 167246109Sneelstatic uint64_t 168246109Sneelpci_vtnet_iosize(struct pci_devinst *pi) 169246109Sneel{ 170246109Sneel if (pci_msix_enabled(pi)) 171246109Sneel return (VTNET_REGSZ); 172246109Sneel else 173246109Sneel return (VTNET_REGSZ - (VTCFG_R_CFG1 - VTCFG_R_MSIX)); 174246109Sneel} 175246109Sneel 176246109Sneel/* 177221828Sgrehan * Return the number of available descriptors in the vring taking care 178221828Sgrehan * of the 16-bit index wraparound. 179221828Sgrehan */ 180221828Sgrehanstatic int 181221828Sgrehanhq_num_avail(struct vring_hqueue *hq) 182221828Sgrehan{ 183248368Sneel uint16_t ndesc; 184221828Sgrehan 185247871Sgrehan /* 186249917Sgrehan * We're just computing (a-b) mod 2^16 187247871Sgrehan * 188247871Sgrehan * The only glitch here is that in standard C, 189247871Sgrehan * uint16_t promotes to (signed) int when int has 190247871Sgrehan * more than 16 bits (pretty much always now), so 191247871Sgrehan * we have to force it back to unsigned. 192247871Sgrehan */ 193247871Sgrehan ndesc = (unsigned)*hq->hq_avail_idx - (unsigned)hq->hq_cur_aidx; 194221828Sgrehan 195247871Sgrehan assert(ndesc <= hq->hq_size); 196221828Sgrehan 197221828Sgrehan return (ndesc); 198221828Sgrehan} 199221828Sgrehan 200221828Sgrehanstatic uint16_t 201221828Sgrehanpci_vtnet_qsize(int qnum) 202221828Sgrehan{ 203221828Sgrehan /* XXX no ctl queue currently */ 204221828Sgrehan if (qnum == VTNET_CTLQ) { 205221828Sgrehan return (0); 206221828Sgrehan } 207221828Sgrehan 208221828Sgrehan /* XXX fixed currently. Maybe different for tx/rx/ctl */ 209221828Sgrehan return (VTNET_RINGSZ); 210221828Sgrehan} 211221828Sgrehan 212221828Sgrehanstatic void 213244160Sgrehanpci_vtnet_ring_reset(struct pci_vtnet_softc *sc, int ring) 214244160Sgrehan{ 215244160Sgrehan struct vring_hqueue *hq; 216244160Sgrehan 217244160Sgrehan assert(ring < VTNET_MAXQ); 218244160Sgrehan 219244160Sgrehan hq = &sc->vsc_hq[ring]; 220244160Sgrehan 221244160Sgrehan /* 222244160Sgrehan * Reset all soft state 223244160Sgrehan */ 224244160Sgrehan hq->hq_cur_aidx = 0; 225244160Sgrehan} 226244160Sgrehan 227250083Sneel/* 228250083Sneel * If the transmit thread is active then stall until it is done. 229250083Sneel */ 230244160Sgrehanstatic void 231250083Sneelpci_vtnet_txwait(struct pci_vtnet_softc *sc) 232250083Sneel{ 233250083Sneel 234250083Sneel pthread_mutex_lock(&sc->tx_mtx); 235250083Sneel while (sc->tx_in_progress) { 236250083Sneel pthread_mutex_unlock(&sc->tx_mtx); 237250083Sneel usleep(10000); 238250083Sneel pthread_mutex_lock(&sc->tx_mtx); 239250083Sneel } 240250083Sneel pthread_mutex_unlock(&sc->tx_mtx); 241250083Sneel} 242250083Sneel 243250083Sneel/* 244250083Sneel * If the receive thread is active then stall until it is done. 245250083Sneel */ 246250083Sneelstatic void 247250083Sneelpci_vtnet_rxwait(struct pci_vtnet_softc *sc) 248250083Sneel{ 249250083Sneel 250250083Sneel pthread_mutex_lock(&sc->rx_mtx); 251250083Sneel while (sc->rx_in_progress) { 252250083Sneel pthread_mutex_unlock(&sc->rx_mtx); 253250083Sneel usleep(10000); 254250083Sneel pthread_mutex_lock(&sc->rx_mtx); 255250083Sneel } 256250083Sneel pthread_mutex_unlock(&sc->rx_mtx); 257250083Sneel} 258250083Sneel 259250083Sneelstatic void 260221828Sgrehanpci_vtnet_update_status(struct pci_vtnet_softc *sc, uint32_t value) 261221828Sgrehan{ 262250086Sneel int i; 263244160Sgrehan 264221828Sgrehan if (value == 0) { 265221828Sgrehan DPRINTF(("vtnet: device reset requested !\n")); 266249917Sgrehan 267249917Sgrehan sc->resetting = 1; 268249917Sgrehan 269250083Sneel /* 270250083Sneel * Wait for the transmit and receive threads to finish their 271250083Sneel * processing. 272250083Sneel */ 273250083Sneel pci_vtnet_txwait(sc); 274250083Sneel pci_vtnet_rxwait(sc); 275250083Sneel 276250083Sneel sc->vsc_rx_ready = 0; 277244160Sgrehan pci_vtnet_ring_reset(sc, VTNET_RXQ); 278244160Sgrehan pci_vtnet_ring_reset(sc, VTNET_TXQ); 279250083Sneel 280250086Sneel for (i = 0; i < VTNET_MAXQ; i++) 281250086Sneel sc->vsc_msix_table_idx[i] = VIRTIO_MSI_NO_VECTOR; 282250086Sneel 283250086Sneel sc->vsc_isr = 0; 284250086Sneel sc->vsc_features = 0; 285250086Sneel 286249917Sgrehan sc->resetting = 0; 287221828Sgrehan } 288221828Sgrehan 289221828Sgrehan sc->vsc_status = value; 290221828Sgrehan} 291221828Sgrehan 292221828Sgrehan/* 293221828Sgrehan * Called to send a buffer chain out to the tap device 294221828Sgrehan */ 295221828Sgrehanstatic void 296221828Sgrehanpci_vtnet_tap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt, 297221828Sgrehan int len) 298221828Sgrehan{ 299221828Sgrehan char pad[60]; 300221828Sgrehan 301221828Sgrehan if (sc->vsc_tapfd == -1) 302221828Sgrehan return; 303221828Sgrehan 304221828Sgrehan /* 305221828Sgrehan * If the length is < 60, pad out to that and add the 306221828Sgrehan * extra zero'd segment to the iov. It is guaranteed that 307221828Sgrehan * there is always an extra iov available by the caller. 308221828Sgrehan */ 309221828Sgrehan if (len < 60) { 310221828Sgrehan memset(pad, 0, 60 - len); 311221828Sgrehan iov[iovcnt].iov_base = pad; 312221828Sgrehan iov[iovcnt].iov_len = 60 - len; 313221828Sgrehan iovcnt++; 314221828Sgrehan } 315221828Sgrehan (void) writev(sc->vsc_tapfd, iov, iovcnt); 316221828Sgrehan} 317221828Sgrehan 318221828Sgrehan/* 319221828Sgrehan * Called when there is read activity on the tap file descriptor. 320221828Sgrehan * Each buffer posted by the guest is assumed to be able to contain 321221828Sgrehan * an entire ethernet frame + rx header. 322221828Sgrehan * MP note: the dummybuf is only used for discarding frames, so there 323221828Sgrehan * is no need for it to be per-vtnet or locked. 324221828Sgrehan */ 325221828Sgrehanstatic uint8_t dummybuf[2048]; 326221828Sgrehan 327221828Sgrehanstatic void 328221828Sgrehanpci_vtnet_tap_rx(struct pci_vtnet_softc *sc) 329221828Sgrehan{ 330221828Sgrehan struct virtio_desc *vd; 331221828Sgrehan struct virtio_used *vu; 332221828Sgrehan struct vring_hqueue *hq; 333221828Sgrehan struct virtio_net_rxhdr *vrx; 334221828Sgrehan uint8_t *buf; 335221828Sgrehan int i; 336221828Sgrehan int len; 337221828Sgrehan int ndescs; 338221828Sgrehan int didx, uidx, aidx; /* descriptor, avail and used index */ 339221828Sgrehan 340221828Sgrehan /* 341221828Sgrehan * Should never be called without a valid tap fd 342221828Sgrehan */ 343221828Sgrehan assert(sc->vsc_tapfd != -1); 344221828Sgrehan 345221828Sgrehan /* 346221828Sgrehan * But, will be called when the rx ring hasn't yet 347250083Sneel * been set up or the guest is resetting the device. 348221828Sgrehan */ 349250083Sneel if (!sc->vsc_rx_ready || sc->resetting) { 350221828Sgrehan /* 351221828Sgrehan * Drop the packet and try later. 352221828Sgrehan */ 353221828Sgrehan (void) read(sc->vsc_tapfd, dummybuf, sizeof(dummybuf)); 354221828Sgrehan return; 355221828Sgrehan } 356221828Sgrehan 357221828Sgrehan /* 358221828Sgrehan * Calculate the number of available rx buffers 359221828Sgrehan */ 360221828Sgrehan hq = &sc->vsc_hq[VTNET_RXQ]; 361221828Sgrehan 362221828Sgrehan ndescs = hq_num_avail(hq); 363221828Sgrehan 364221828Sgrehan if (ndescs == 0) { 365221828Sgrehan /* 366221828Sgrehan * Drop the packet and try later 367221828Sgrehan */ 368221828Sgrehan (void) read(sc->vsc_tapfd, dummybuf, sizeof(dummybuf)); 369221828Sgrehan return; 370221828Sgrehan } 371221828Sgrehan 372221828Sgrehan aidx = hq->hq_cur_aidx; 373221828Sgrehan uidx = *hq->hq_used_idx; 374221828Sgrehan for (i = 0; i < ndescs; i++) { 375221828Sgrehan /* 376221828Sgrehan * 'aidx' indexes into the an array of descriptor indexes 377221828Sgrehan */ 378221828Sgrehan didx = hq->hq_avail_ring[aidx % hq->hq_size]; 379221828Sgrehan assert(didx >= 0 && didx < hq->hq_size); 380221828Sgrehan 381221828Sgrehan vd = &hq->hq_dtable[didx]; 382221828Sgrehan 383221828Sgrehan /* 384221828Sgrehan * Get a pointer to the rx header, and use the 385221828Sgrehan * data immediately following it for the packet buffer. 386221828Sgrehan */ 387248477Sneel vrx = paddr_guest2host(vtnet_ctx(sc), vd->vd_addr, vd->vd_len); 388221828Sgrehan buf = (uint8_t *)(vrx + 1); 389221828Sgrehan 390221828Sgrehan len = read(sc->vsc_tapfd, buf, 391221828Sgrehan vd->vd_len - sizeof(struct virtio_net_rxhdr)); 392221828Sgrehan 393221828Sgrehan if (len < 0 && errno == EWOULDBLOCK) { 394221828Sgrehan break; 395221828Sgrehan } 396221828Sgrehan 397221828Sgrehan /* 398221828Sgrehan * The only valid field in the rx packet header is the 399221828Sgrehan * number of buffers, which is always 1 without TSO 400221828Sgrehan * support. 401221828Sgrehan */ 402221828Sgrehan memset(vrx, 0, sizeof(struct virtio_net_rxhdr)); 403221828Sgrehan vrx->vrh_bufs = 1; 404221828Sgrehan 405221828Sgrehan /* 406221828Sgrehan * Write this descriptor into the used ring 407221828Sgrehan */ 408221828Sgrehan vu = &hq->hq_used_ring[uidx % hq->hq_size]; 409221828Sgrehan vu->vu_idx = didx; 410221828Sgrehan vu->vu_tlen = len + sizeof(struct virtio_net_rxhdr); 411221828Sgrehan uidx++; 412221828Sgrehan aidx++; 413221828Sgrehan } 414221828Sgrehan 415221828Sgrehan /* 416221828Sgrehan * Update the used pointer, and signal an interrupt if allowed 417221828Sgrehan */ 418221828Sgrehan *hq->hq_used_idx = uidx; 419221828Sgrehan hq->hq_cur_aidx = aidx; 420221828Sgrehan 421221828Sgrehan if ((*hq->hq_avail_flags & VRING_AVAIL_F_NO_INTERRUPT) == 0) { 422246109Sneel if (use_msix) { 423246109Sneel pci_generate_msix(sc->vsc_pi, 424246109Sneel sc->vsc_msix_table_idx[VTNET_RXQ]); 425246109Sneel } else { 426246109Sneel sc->vsc_isr |= 1; 427246109Sneel pci_generate_msi(sc->vsc_pi, 0); 428246109Sneel } 429221828Sgrehan } 430221828Sgrehan} 431221828Sgrehan 432221828Sgrehanstatic void 433221828Sgrehanpci_vtnet_tap_callback(int fd, enum ev_type type, void *param) 434221828Sgrehan{ 435221828Sgrehan struct pci_vtnet_softc *sc = param; 436221828Sgrehan 437250083Sneel pthread_mutex_lock(&sc->rx_mtx); 438250083Sneel sc->rx_in_progress = 1; 439221828Sgrehan pci_vtnet_tap_rx(sc); 440250083Sneel sc->rx_in_progress = 0; 441250083Sneel pthread_mutex_unlock(&sc->rx_mtx); 442221828Sgrehan 443221828Sgrehan} 444221828Sgrehan 445221828Sgrehanstatic void 446221828Sgrehanpci_vtnet_ping_rxq(struct pci_vtnet_softc *sc) 447221828Sgrehan{ 448221828Sgrehan /* 449221828Sgrehan * A qnotify means that the rx process can now begin 450221828Sgrehan */ 451221828Sgrehan if (sc->vsc_rx_ready == 0) { 452221828Sgrehan sc->vsc_rx_ready = 1; 453221828Sgrehan } 454221828Sgrehan} 455221828Sgrehan 456221828Sgrehanstatic void 457221828Sgrehanpci_vtnet_proctx(struct pci_vtnet_softc *sc, struct vring_hqueue *hq) 458221828Sgrehan{ 459221828Sgrehan struct iovec iov[VTNET_MAXSEGS + 1]; 460221828Sgrehan struct virtio_desc *vd; 461221828Sgrehan struct virtio_used *vu; 462221828Sgrehan int i; 463221828Sgrehan int plen; 464221828Sgrehan int tlen; 465221828Sgrehan int uidx, aidx, didx; 466221828Sgrehan 467221828Sgrehan uidx = *hq->hq_used_idx; 468221828Sgrehan aidx = hq->hq_cur_aidx; 469221828Sgrehan didx = hq->hq_avail_ring[aidx % hq->hq_size]; 470221828Sgrehan assert(didx >= 0 && didx < hq->hq_size); 471221828Sgrehan 472221828Sgrehan vd = &hq->hq_dtable[didx]; 473221828Sgrehan 474221828Sgrehan /* 475221828Sgrehan * Run through the chain of descriptors, ignoring the 476221828Sgrehan * first header descriptor. However, include the header 477221828Sgrehan * length in the total length that will be put into the 478221828Sgrehan * used queue. 479221828Sgrehan */ 480221828Sgrehan tlen = vd->vd_len; 481221828Sgrehan vd = &hq->hq_dtable[vd->vd_next]; 482221828Sgrehan 483221828Sgrehan for (i = 0, plen = 0; 484221828Sgrehan i < VTNET_MAXSEGS; 485221828Sgrehan i++, vd = &hq->hq_dtable[vd->vd_next]) { 486248477Sneel iov[i].iov_base = paddr_guest2host(vtnet_ctx(sc), 487248477Sneel vd->vd_addr, vd->vd_len); 488221828Sgrehan iov[i].iov_len = vd->vd_len; 489221828Sgrehan plen += vd->vd_len; 490221828Sgrehan tlen += vd->vd_len; 491221828Sgrehan 492221828Sgrehan if ((vd->vd_flags & VRING_DESC_F_NEXT) == 0) 493221828Sgrehan break; 494221828Sgrehan } 495221828Sgrehan assert(i < VTNET_MAXSEGS); 496221828Sgrehan 497221828Sgrehan DPRINTF(("virtio: packet send, %d bytes, %d segs\n\r", plen, i + 1)); 498221828Sgrehan pci_vtnet_tap_tx(sc, iov, i + 1, plen); 499221828Sgrehan 500221828Sgrehan /* 501221828Sgrehan * Return this chain back to the host 502221828Sgrehan */ 503221828Sgrehan vu = &hq->hq_used_ring[uidx % hq->hq_size]; 504221828Sgrehan vu->vu_idx = didx; 505221828Sgrehan vu->vu_tlen = tlen; 506221828Sgrehan hq->hq_cur_aidx = aidx + 1; 507221828Sgrehan *hq->hq_used_idx = uidx + 1; 508221828Sgrehan} 509221828Sgrehan 510221828Sgrehanstatic void 511221828Sgrehanpci_vtnet_ping_txq(struct pci_vtnet_softc *sc) 512221828Sgrehan{ 513221828Sgrehan struct vring_hqueue *hq = &sc->vsc_hq[VTNET_TXQ]; 514221828Sgrehan int ndescs; 515221828Sgrehan 516221828Sgrehan /* 517221828Sgrehan * Calculate number of ring entries to process 518221828Sgrehan */ 519221828Sgrehan ndescs = hq_num_avail(hq); 520221828Sgrehan 521221828Sgrehan if (ndescs == 0) 522221828Sgrehan return; 523221828Sgrehan 524249917Sgrehan /* Signal the tx thread for processing */ 525249917Sgrehan pthread_mutex_lock(&sc->tx_mtx); 526249917Sgrehan if (sc->tx_in_progress == 0) 527249917Sgrehan pthread_cond_signal(&sc->tx_cond); 528249917Sgrehan pthread_mutex_unlock(&sc->tx_mtx); 529221828Sgrehan} 530221828Sgrehan 531249917Sgrehan/* 532249917Sgrehan * Thread which will handle processing of TX desc 533249917Sgrehan */ 534249917Sgrehanstatic void * 535249917Sgrehanpci_vtnet_tx_thread(void *param) 536249917Sgrehan{ 537249917Sgrehan struct pci_vtnet_softc *sc = (struct pci_vtnet_softc *) param; 538249917Sgrehan struct vring_hqueue *hq; 539249917Sgrehan int i, ndescs, needintr,error; 540249917Sgrehan 541249917Sgrehan needintr = 0; 542249917Sgrehan hq = &sc->vsc_hq[VTNET_TXQ]; 543249917Sgrehan 544249917Sgrehan /* 545249917Sgrehan * Let us wait till the tx queue pointers get initialised & 546249917Sgrehan * first tx signaled 547249917Sgrehan */ 548249917Sgrehan pthread_mutex_lock(&sc->tx_mtx); 549249917Sgrehan error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx); 550249917Sgrehan assert(error == 0); 551249917Sgrehan 552249917Sgrehan for (;;) { 553249917Sgrehan pthread_mutex_lock(&sc->tx_mtx); 554249917Sgrehan for (;;) { 555249917Sgrehan if (sc->resetting) { 556249917Sgrehan ndescs = 0; 557249917Sgrehan needintr = 0; 558249917Sgrehan } else 559249917Sgrehan ndescs = hq_num_avail(hq); 560249917Sgrehan 561249917Sgrehan if (ndescs != 0) 562249917Sgrehan break; 563249917Sgrehan 564249917Sgrehan if (needintr) { 565249917Sgrehan /* 566249917Sgrehan * Generate an interrupt if able 567249917Sgrehan */ 568249917Sgrehan if ((*hq->hq_avail_flags & 569249917Sgrehan VRING_AVAIL_F_NO_INTERRUPT) == 0) { 570249917Sgrehan if (use_msix) { 571249917Sgrehan pci_generate_msix(sc->vsc_pi, 572249917Sgrehan sc->vsc_msix_table_idx[VTNET_TXQ]); 573249917Sgrehan } else { 574249917Sgrehan sc->vsc_isr |= 1; 575249917Sgrehan pci_generate_msi(sc->vsc_pi, 0); 576249917Sgrehan } 577249917Sgrehan } 578249917Sgrehan } 579249917Sgrehan needintr = 0; 580249917Sgrehan sc->tx_in_progress = 0; 581249917Sgrehan error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx); 582249917Sgrehan assert(error == 0); 583249917Sgrehan } 584249917Sgrehan sc->tx_in_progress = 1; 585249917Sgrehan pthread_mutex_unlock(&sc->tx_mtx); 586249917Sgrehan 587249917Sgrehan while (ndescs > 0) { 588249917Sgrehan /* 589249917Sgrehan * Run through all the entries, placing them into 590249917Sgrehan * iovecs and sending when an end-of-packet is found 591249917Sgrehan */ 592249917Sgrehan for (i = 0; i < ndescs; i++) 593249917Sgrehan pci_vtnet_proctx(sc, hq); 594249917Sgrehan needintr = 1; 595249917Sgrehan ndescs = hq_num_avail(hq); 596249917Sgrehan } 597249917Sgrehan } 598249917Sgrehan} 599249917Sgrehan 600221828Sgrehanstatic void 601221828Sgrehanpci_vtnet_ping_ctlq(struct pci_vtnet_softc *sc) 602221828Sgrehan{ 603221828Sgrehan 604221828Sgrehan DPRINTF(("vtnet: control qnotify!\n\r")); 605221828Sgrehan} 606221828Sgrehan 607221828Sgrehanstatic void 608221828Sgrehanpci_vtnet_ring_init(struct pci_vtnet_softc *sc, uint64_t pfn) 609221828Sgrehan{ 610221828Sgrehan struct vring_hqueue *hq; 611221828Sgrehan int qnum = sc->vsc_curq; 612221828Sgrehan 613221828Sgrehan assert(qnum < VTNET_MAXQ); 614221828Sgrehan 615221828Sgrehan sc->vsc_pfn[qnum] = pfn << VRING_PFN; 616221828Sgrehan 617221828Sgrehan /* 618221828Sgrehan * Set up host pointers to the various parts of the 619221828Sgrehan * queue 620221828Sgrehan */ 621221828Sgrehan hq = &sc->vsc_hq[qnum]; 622221828Sgrehan hq->hq_size = pci_vtnet_qsize(qnum); 623221828Sgrehan 624248477Sneel hq->hq_dtable = paddr_guest2host(vtnet_ctx(sc), pfn << VRING_PFN, 625247523Sneel vring_size(hq->hq_size)); 626221828Sgrehan hq->hq_avail_flags = (uint16_t *)(hq->hq_dtable + hq->hq_size); 627221828Sgrehan hq->hq_avail_idx = hq->hq_avail_flags + 1; 628221828Sgrehan hq->hq_avail_ring = hq->hq_avail_flags + 2; 629221828Sgrehan hq->hq_used_flags = (uint16_t *)roundup2((uintptr_t)hq->hq_avail_ring, 630221828Sgrehan VRING_ALIGN); 631221828Sgrehan hq->hq_used_idx = hq->hq_used_flags + 1; 632221828Sgrehan hq->hq_used_ring = (struct virtio_used *)(hq->hq_used_flags + 2); 633221828Sgrehan 634221828Sgrehan /* 635221828Sgrehan * Initialize queue indexes 636221828Sgrehan */ 637221828Sgrehan hq->hq_cur_aidx = 0; 638221828Sgrehan} 639221828Sgrehan 640221828Sgrehanstatic int 641221828Sgrehanpci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 642221828Sgrehan{ 643221828Sgrehan MD5_CTX mdctx; 644221828Sgrehan unsigned char digest[16]; 645221828Sgrehan char nstr[80]; 646249917Sgrehan char tname[MAXCOMLEN + 1]; 647221828Sgrehan struct pci_vtnet_softc *sc; 648246109Sneel const char *env_msi; 649221828Sgrehan 650221828Sgrehan sc = malloc(sizeof(struct pci_vtnet_softc)); 651221828Sgrehan memset(sc, 0, sizeof(struct pci_vtnet_softc)); 652221828Sgrehan 653221828Sgrehan pi->pi_arg = sc; 654221828Sgrehan sc->vsc_pi = pi; 655221828Sgrehan 656221828Sgrehan pthread_mutex_init(&sc->vsc_mtx, NULL); 657246109Sneel 658246109Sneel /* 659246109Sneel * Use MSI if set by user 660246109Sneel */ 661246109Sneel if ((env_msi = getenv("BHYVE_USE_MSI")) != NULL) { 662246109Sneel if (strcasecmp(env_msi, "yes") == 0) 663246109Sneel use_msix = 0; 664246109Sneel } 665221828Sgrehan 666221828Sgrehan /* 667221828Sgrehan * Attempt to open the tap device 668221828Sgrehan */ 669221828Sgrehan sc->vsc_tapfd = -1; 670221828Sgrehan if (opts != NULL) { 671221828Sgrehan char tbuf[80]; 672221828Sgrehan 673221828Sgrehan strcpy(tbuf, "/dev/"); 674242882Sneel strlcat(tbuf, opts, sizeof(tbuf)); 675221828Sgrehan 676221828Sgrehan sc->vsc_tapfd = open(tbuf, O_RDWR); 677221828Sgrehan if (sc->vsc_tapfd == -1) { 678221828Sgrehan WPRINTF(("open of tap device %s failed\n", tbuf)); 679221828Sgrehan } else { 680221828Sgrehan /* 681221828Sgrehan * Set non-blocking and register for read 682221828Sgrehan * notifications with the event loop 683221828Sgrehan */ 684221828Sgrehan int opt = 1; 685221828Sgrehan if (ioctl(sc->vsc_tapfd, FIONBIO, &opt) < 0) { 686221828Sgrehan WPRINTF(("tap device O_NONBLOCK failed\n")); 687221828Sgrehan close(sc->vsc_tapfd); 688221828Sgrehan sc->vsc_tapfd = -1; 689221828Sgrehan } 690221828Sgrehan 691221828Sgrehan sc->vsc_mevp = mevent_add(sc->vsc_tapfd, 692221828Sgrehan EVF_READ, 693221828Sgrehan pci_vtnet_tap_callback, 694221828Sgrehan sc); 695221828Sgrehan if (sc->vsc_mevp == NULL) { 696221828Sgrehan WPRINTF(("Could not register event\n")); 697221828Sgrehan close(sc->vsc_tapfd); 698221828Sgrehan sc->vsc_tapfd = -1; 699221828Sgrehan } 700221828Sgrehan } 701221828Sgrehan } 702221828Sgrehan 703221828Sgrehan /* 704221828Sgrehan * The MAC address is the standard NetApp OUI of 00-a0-98, 705244159Sgrehan * followed by an MD5 of the vm name. The slot/func number is 706244159Sgrehan * prepended to this for slots other than 1:0, so that 707244159Sgrehan * a bootloader can netboot from the equivalent of slot 1. 708221828Sgrehan */ 709244159Sgrehan if (pi->pi_slot == 1 && pi->pi_func == 0) { 710221828Sgrehan strncpy(nstr, vmname, sizeof(nstr)); 711221828Sgrehan } else { 712244159Sgrehan snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot, 713244159Sgrehan pi->pi_func, vmname); 714221828Sgrehan } 715221828Sgrehan 716221828Sgrehan MD5Init(&mdctx); 717221828Sgrehan MD5Update(&mdctx, nstr, strlen(nstr)); 718221828Sgrehan MD5Final(digest, &mdctx); 719221828Sgrehan 720221828Sgrehan sc->vsc_macaddr[0] = 0x00; 721221828Sgrehan sc->vsc_macaddr[1] = 0xa0; 722221828Sgrehan sc->vsc_macaddr[2] = 0x98; 723221828Sgrehan sc->vsc_macaddr[3] = digest[0]; 724221828Sgrehan sc->vsc_macaddr[4] = digest[1]; 725221828Sgrehan sc->vsc_macaddr[5] = digest[2]; 726221828Sgrehan 727221828Sgrehan /* initialize config space */ 728221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET); 729221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 730221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK); 731221828Sgrehan pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET); 732246109Sneel 733246109Sneel if (use_msix) { 734246109Sneel /* MSI-X support */ 735246109Sneel int i; 736246109Sneel 737246109Sneel for (i = 0; i < VTNET_MAXQ; i++) 738246109Sneel sc->vsc_msix_table_idx[i] = VIRTIO_MSI_NO_VECTOR; 739246109Sneel 740246109Sneel /* 741246109Sneel * BAR 1 used to map MSI-X table and PBA 742246109Sneel */ 743246109Sneel if (pci_emul_add_msixcap(pi, VTNET_MAXQ, 1)) 744246109Sneel return (1); 745246109Sneel } else { 746246109Sneel /* MSI support */ 747246109Sneel pci_emul_add_msicap(pi, 1); 748246109Sneel } 749246109Sneel 750241744Sgrehan pci_emul_alloc_bar(pi, 0, PCIBAR_IO, VTNET_REGSZ); 751250083Sneel 752250083Sneel sc->resetting = 0; 753250083Sneel 754250083Sneel sc->rx_in_progress = 0; 755250083Sneel pthread_mutex_init(&sc->rx_mtx, NULL); 756250083Sneel 757249917Sgrehan /* 758249917Sgrehan * Initialize tx semaphore & spawn TX processing thread 759249917Sgrehan * As of now, only one thread for TX desc processing is 760249917Sgrehan * spawned. 761249917Sgrehan */ 762249917Sgrehan sc->tx_in_progress = 0; 763249917Sgrehan pthread_mutex_init(&sc->tx_mtx, NULL); 764249917Sgrehan pthread_cond_init(&sc->tx_cond, NULL); 765249917Sgrehan pthread_create(&sc->tx_tid, NULL, pci_vtnet_tx_thread, (void *)sc); 766249917Sgrehan snprintf(tname, sizeof(tname), "%s vtnet%d tx", vmname, pi->pi_slot); 767249917Sgrehan pthread_set_name_np(sc->tx_tid, tname); 768221828Sgrehan 769221828Sgrehan return (0); 770221828Sgrehan} 771221828Sgrehan 772221828Sgrehan/* 773221828Sgrehan * Function pointer array to handle queue notifications 774221828Sgrehan */ 775221828Sgrehanstatic void (*pci_vtnet_qnotify[VTNET_MAXQ])(struct pci_vtnet_softc *) = { 776221828Sgrehan pci_vtnet_ping_rxq, 777221828Sgrehan pci_vtnet_ping_txq, 778221828Sgrehan pci_vtnet_ping_ctlq 779221828Sgrehan}; 780221828Sgrehan 781246109Sneelstatic uint64_t 782246109Sneelvtnet_adjust_offset(struct pci_devinst *pi, uint64_t offset) 783246109Sneel{ 784246109Sneel /* 785246109Sneel * Device specific offsets used by guest would change based on 786246109Sneel * whether MSI-X capability is enabled or not 787246109Sneel */ 788246109Sneel if (!pci_msix_enabled(pi)) { 789246109Sneel if (offset >= VTCFG_R_MSIX) 790246109Sneel return (offset + (VTCFG_R_CFG1 - VTCFG_R_MSIX)); 791246109Sneel } 792246109Sneel 793246109Sneel return (offset); 794246109Sneel} 795246109Sneel 796221828Sgrehanstatic void 797241744Sgrehanpci_vtnet_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 798241744Sgrehan int baridx, uint64_t offset, int size, uint64_t value) 799221828Sgrehan{ 800221828Sgrehan struct pci_vtnet_softc *sc = pi->pi_arg; 801222830Sgrehan void *ptr; 802222830Sgrehan 803246109Sneel if (use_msix) { 804246190Sneel if (baridx == pci_msix_table_bar(pi) || 805246190Sneel baridx == pci_msix_pba_bar(pi)) { 806246109Sneel pci_emul_msix_twrite(pi, offset, size, value); 807246109Sneel return; 808246109Sneel } 809246109Sneel } 810246109Sneel 811241744Sgrehan assert(baridx == 0); 812241744Sgrehan 813246109Sneel if (offset + size > pci_vtnet_iosize(pi)) { 814241744Sgrehan DPRINTF(("vtnet_write: 2big, offset %ld size %d\n", 815221828Sgrehan offset, size)); 816221828Sgrehan return; 817221828Sgrehan } 818221828Sgrehan 819221828Sgrehan pthread_mutex_lock(&sc->vsc_mtx); 820221828Sgrehan 821246109Sneel offset = vtnet_adjust_offset(pi, offset); 822246109Sneel 823221828Sgrehan switch (offset) { 824221828Sgrehan case VTCFG_R_GUESTCAP: 825221828Sgrehan assert(size == 4); 826221828Sgrehan sc->vsc_features = value & VTNET_S_HOSTCAPS; 827221828Sgrehan break; 828221828Sgrehan case VTCFG_R_PFN: 829221828Sgrehan assert(size == 4); 830221828Sgrehan pci_vtnet_ring_init(sc, value); 831221828Sgrehan break; 832221828Sgrehan case VTCFG_R_QSEL: 833221828Sgrehan assert(size == 2); 834221828Sgrehan assert(value < VTNET_MAXQ); 835221828Sgrehan sc->vsc_curq = value; 836221828Sgrehan break; 837221828Sgrehan case VTCFG_R_QNOTIFY: 838221828Sgrehan assert(size == 2); 839221828Sgrehan assert(value < VTNET_MAXQ); 840221828Sgrehan (*pci_vtnet_qnotify[value])(sc); 841221828Sgrehan break; 842221828Sgrehan case VTCFG_R_STATUS: 843221828Sgrehan assert(size == 1); 844221828Sgrehan pci_vtnet_update_status(sc, value); 845221828Sgrehan break; 846246109Sneel case VTCFG_R_CFGVEC: 847246109Sneel assert(size == 2); 848246109Sneel sc->vsc_msix_table_idx[VTNET_CTLQ] = value; 849246109Sneel break; 850246109Sneel case VTCFG_R_QVEC: 851246109Sneel assert(size == 2); 852246109Sneel assert(sc->vsc_curq != VTNET_CTLQ); 853246109Sneel sc->vsc_msix_table_idx[sc->vsc_curq] = value; 854246109Sneel break; 855221828Sgrehan case VTNET_R_CFG0: 856221828Sgrehan case VTNET_R_CFG1: 857221828Sgrehan case VTNET_R_CFG2: 858221828Sgrehan case VTNET_R_CFG3: 859221828Sgrehan case VTNET_R_CFG4: 860221828Sgrehan case VTNET_R_CFG5: 861222830Sgrehan assert((size + offset) <= (VTNET_R_CFG5 + 1)); 862222830Sgrehan ptr = &sc->vsc_macaddr[offset - VTNET_R_CFG0]; 863221828Sgrehan /* 864221828Sgrehan * The driver is allowed to change the MAC address 865221828Sgrehan */ 866221828Sgrehan sc->vsc_macaddr[offset - VTNET_R_CFG0] = value; 867222830Sgrehan if (size == 1) { 868222830Sgrehan *(uint8_t *) ptr = value; 869222830Sgrehan } else if (size == 2) { 870222830Sgrehan *(uint16_t *) ptr = value; 871222830Sgrehan } else { 872222830Sgrehan *(uint32_t *) ptr = value; 873222830Sgrehan } 874221828Sgrehan break; 875221828Sgrehan case VTCFG_R_HOSTCAP: 876221828Sgrehan case VTCFG_R_QNUM: 877221828Sgrehan case VTCFG_R_ISR: 878221828Sgrehan case VTNET_R_CFG6: 879221828Sgrehan case VTNET_R_CFG7: 880241744Sgrehan DPRINTF(("vtnet: write to readonly reg %ld\n\r", offset)); 881221828Sgrehan break; 882221828Sgrehan default: 883241744Sgrehan DPRINTF(("vtnet: unknown i/o write offset %ld\n\r", offset)); 884221828Sgrehan value = 0; 885221828Sgrehan break; 886221828Sgrehan } 887221828Sgrehan 888221828Sgrehan pthread_mutex_unlock(&sc->vsc_mtx); 889221828Sgrehan} 890221828Sgrehan 891241744Sgrehanuint64_t 892241744Sgrehanpci_vtnet_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 893241744Sgrehan int baridx, uint64_t offset, int size) 894221828Sgrehan{ 895221828Sgrehan struct pci_vtnet_softc *sc = pi->pi_arg; 896222830Sgrehan void *ptr; 897241744Sgrehan uint64_t value; 898221828Sgrehan 899246109Sneel if (use_msix) { 900246190Sneel if (baridx == pci_msix_table_bar(pi) || 901246190Sneel baridx == pci_msix_pba_bar(pi)) { 902246109Sneel return (pci_emul_msix_tread(pi, offset, size)); 903246109Sneel } 904246109Sneel } 905246109Sneel 906241744Sgrehan assert(baridx == 0); 907241744Sgrehan 908246109Sneel if (offset + size > pci_vtnet_iosize(pi)) { 909241744Sgrehan DPRINTF(("vtnet_read: 2big, offset %ld size %d\n", 910221828Sgrehan offset, size)); 911221828Sgrehan return (0); 912221828Sgrehan } 913221828Sgrehan 914221828Sgrehan pthread_mutex_lock(&sc->vsc_mtx); 915221828Sgrehan 916246109Sneel offset = vtnet_adjust_offset(pi, offset); 917246109Sneel 918221828Sgrehan switch (offset) { 919221828Sgrehan case VTCFG_R_HOSTCAP: 920221828Sgrehan assert(size == 4); 921221828Sgrehan value = VTNET_S_HOSTCAPS; 922221828Sgrehan break; 923221828Sgrehan case VTCFG_R_GUESTCAP: 924221828Sgrehan assert(size == 4); 925221828Sgrehan value = sc->vsc_features; /* XXX never read ? */ 926221828Sgrehan break; 927221828Sgrehan case VTCFG_R_PFN: 928221828Sgrehan assert(size == 4); 929221828Sgrehan value = sc->vsc_pfn[sc->vsc_curq] >> VRING_PFN; 930221828Sgrehan break; 931221828Sgrehan case VTCFG_R_QNUM: 932221828Sgrehan assert(size == 2); 933221828Sgrehan value = pci_vtnet_qsize(sc->vsc_curq); 934221828Sgrehan break; 935221828Sgrehan case VTCFG_R_QSEL: 936221828Sgrehan assert(size == 2); 937221828Sgrehan value = sc->vsc_curq; /* XXX never read ? */ 938221828Sgrehan break; 939221828Sgrehan case VTCFG_R_QNOTIFY: 940221828Sgrehan assert(size == 2); 941221828Sgrehan value = sc->vsc_curq; /* XXX never read ? */ 942221828Sgrehan break; 943221828Sgrehan case VTCFG_R_STATUS: 944221828Sgrehan assert(size == 1); 945221828Sgrehan value = sc->vsc_status; 946221828Sgrehan break; 947221828Sgrehan case VTCFG_R_ISR: 948221828Sgrehan assert(size == 1); 949221828Sgrehan value = sc->vsc_isr; 950221828Sgrehan sc->vsc_isr = 0; /* a read clears this flag */ 951221828Sgrehan break; 952246109Sneel case VTCFG_R_CFGVEC: 953246109Sneel assert(size == 2); 954246109Sneel value = sc->vsc_msix_table_idx[VTNET_CTLQ]; 955246109Sneel break; 956246109Sneel case VTCFG_R_QVEC: 957246109Sneel assert(size == 2); 958246109Sneel assert(sc->vsc_curq != VTNET_CTLQ); 959246109Sneel value = sc->vsc_msix_table_idx[sc->vsc_curq]; 960246109Sneel break; 961221828Sgrehan case VTNET_R_CFG0: 962221828Sgrehan case VTNET_R_CFG1: 963221828Sgrehan case VTNET_R_CFG2: 964221828Sgrehan case VTNET_R_CFG3: 965221828Sgrehan case VTNET_R_CFG4: 966221828Sgrehan case VTNET_R_CFG5: 967246109Sneel assert((size + offset) <= (VTNET_R_CFG5 + 1)); 968246109Sneel ptr = &sc->vsc_macaddr[offset - VTNET_R_CFG0]; 969246109Sneel if (size == 1) { 970246109Sneel value = *(uint8_t *) ptr; 971246109Sneel } else if (size == 2) { 972246109Sneel value = *(uint16_t *) ptr; 973246109Sneel } else { 974246109Sneel value = *(uint32_t *) ptr; 975246109Sneel } 976221828Sgrehan break; 977221828Sgrehan case VTNET_R_CFG6: 978222830Sgrehan assert(size != 4); 979222830Sgrehan value = 0x01; /* XXX link always up */ 980221828Sgrehan break; 981221828Sgrehan case VTNET_R_CFG7: 982221828Sgrehan assert(size == 1); 983222830Sgrehan value = 0; /* XXX link status in LSB */ 984221828Sgrehan break; 985221828Sgrehan default: 986241744Sgrehan DPRINTF(("vtnet: unknown i/o read offset %ld\n\r", offset)); 987221828Sgrehan value = 0; 988221828Sgrehan break; 989221828Sgrehan } 990221828Sgrehan 991221828Sgrehan pthread_mutex_unlock(&sc->vsc_mtx); 992221828Sgrehan 993221828Sgrehan return (value); 994221828Sgrehan} 995221828Sgrehan 996221828Sgrehanstruct pci_devemu pci_de_vnet = { 997241744Sgrehan .pe_emu = "virtio-net", 998241744Sgrehan .pe_init = pci_vtnet_init, 999241744Sgrehan .pe_barwrite = pci_vtnet_write, 1000241744Sgrehan .pe_barread = pci_vtnet_read 1001221828Sgrehan}; 1002221828SgrehanPCI_EMUL_SET(pci_de_vnet); 1003