1 /* 2 * Copyright (c) 2011 NetApp, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 /* 27 * This file and its contents are supplied under the terms of the 28 * Common Development and Distribution License ("CDDL"), version 1.0. 29 * You may only use this file in accordance with the terms of version 30 * 1.0 of the CDDL. 31 * 32 * A full copy of the text of the CDDL should have accompanied this 33 * source. A copy of the CDDL is also available via the Internet at 34 * http://www.illumos.org/license/CDDL. 35 * 36 * Copyright 2015 Pluribus Networks Inc. 37 */ 38 39 #include <sys/cdefs.h> 40 41 #include <sys/param.h> 42 #include <sys/linker_set.h> 43 #include <sys/ioctl.h> 44 #include <sys/viona_io.h> 45 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <stdint.h> 51 #include <string.h> 52 #include <strings.h> 53 #include <unistd.h> 54 #include <assert.h> 55 #include <pthread.h> 56 #include <signal.h> 57 #include <poll.h> 58 #include <libdladm.h> 59 #include <libdllink.h> 60 #include <libdlvnic.h> 61 62 #include <machine/vmm.h> 63 #include <vmmapi.h> 64 65 #include "bhyverun.h" 66 #include "pci_emul.h" 67 #include "virtio.h" 68 69 #define VIONA_RINGSZ 1024 70 71 /* 72 * PCI config-space register offsets 73 */ 74 #define VIONA_R_CFG0 24 75 #define VIONA_R_CFG1 25 76 #define VIONA_R_CFG2 26 77 #define VIONA_R_CFG3 27 78 #define VIONA_R_CFG4 28 79 #define VIONA_R_CFG5 29 80 #define VIONA_R_CFG6 30 81 #define VIONA_R_CFG7 31 82 #define VIONA_R_MAX 31 83 84 #define VIONA_REGSZ VIONA_R_MAX+1 85 86 /* 87 * Host capabilities 88 */ 89 #define VIRTIO_NET_F_MAC (1 << 5) /* host supplies MAC */ 90 #define VIRTIO_NET_F_MRG_RXBUF (1 << 15) /* host can merge RX buffers */ 91 #define VIRTIO_NET_F_STATUS (1 << 16) /* config status field available */ 92 93 #define VIONA_S_HOSTCAPS \ 94 (VIRTIO_NET_F_MAC | \ 95 VIRTIO_NET_F_MRG_RXBUF | \ 96 VIRTIO_NET_F_STATUS) 97 98 /* 99 * Queue definitions. 100 */ 101 #define VIONA_RXQ 0 102 #define VIONA_TXQ 1 103 #define VIONA_CTLQ 2 104 105 #define VIONA_MAXQ 3 106 107 /* 108 * Debug printf 109 */ 110 static int pci_viona_debug; 111 #define DPRINTF(params) if (pci_viona_debug) printf params 112 #define WPRINTF(params) printf params 113 114 /* 115 * Per-device softc 116 */ 117 struct pci_viona_softc { 118 struct pci_devinst *vsc_pi; 119 pthread_mutex_t vsc_mtx; 120 121 int vsc_curq; 122 int vsc_status; 123 int vsc_isr; 124 125 datalink_id_t vsc_linkid; 126 char vsc_linkname[MAXLINKNAMELEN]; 127 int vsc_vnafd; 128 129 uint32_t vsc_features; 130 uint8_t vsc_macaddr[6]; 131 132 uint64_t vsc_pfn[VIONA_MAXQ]; 133 uint16_t vsc_msix_table_idx[VIONA_MAXQ]; 134 /* 135 * Flag to see if host is already sending data out. 136 * If it is, no need to wait for lock and send interrupt to host 137 * for new data. 138 */ 139 boolean_t vsc_tx_kick_lock_held; 140 141 pthread_t tx_tid; 142 pthread_mutex_t tx_mtx; 143 pthread_cond_t tx_cond; 144 }; 145 #define viona_ctx(sc) ((sc)->vsc_pi->pi_vmctx) 146 147 /* 148 * Return the size of IO BAR that maps virtio header and device specific 149 * region. The size would vary depending on whether MSI-X is enabled or 150 * not. 151 */ 152 static uint64_t 153 pci_viona_iosize(struct pci_devinst *pi) 154 { 155 if (pci_msix_enabled(pi)) 156 return (VIONA_REGSZ); 157 else 158 return (VIONA_REGSZ - (VTCFG_R_CFG1 - VTCFG_R_MSIX)); 159 } 160 161 static uint16_t 162 pci_viona_qsize(int qnum) 163 { 164 /* XXX no ctl queue currently */ 165 if (qnum == VIONA_CTLQ) { 166 return (0); 167 } 168 169 /* XXX fixed currently. Maybe different for tx/rx/ctl */ 170 return (VIONA_RINGSZ); 171 } 172 173 static void 174 pci_viona_ring_reset(struct pci_viona_softc *sc, int ring) 175 { 176 int error; 177 178 assert(ring < VIONA_MAXQ); 179 180 switch (ring) { 181 case VIONA_RXQ: 182 error = ioctl(sc->vsc_vnafd, VNA_IOC_RX_RING_RESET); 183 if (error != 0) { 184 WPRINTF(("ioctl viona rx ring reset failed %d\n", 185 error)); 186 } else { 187 sc->vsc_pfn[VIONA_RXQ] = 0; 188 } 189 break; 190 case VIONA_TXQ: 191 error = ioctl(sc->vsc_vnafd, VNA_IOC_TX_RING_RESET); 192 if (error != 0) { 193 WPRINTF(("ioctl viona tx ring reset failed %d\n", 194 error)); 195 } else { 196 sc->vsc_pfn[VIONA_TXQ] = 0; 197 } 198 break; 199 case VIONA_CTLQ: 200 default: 201 break; 202 } 203 } 204 205 static void 206 pci_viona_update_status(struct pci_viona_softc *sc, uint32_t value) 207 { 208 209 if (value == 0) { 210 DPRINTF(("viona: device reset requested !\n")); 211 pci_viona_ring_reset(sc, VIONA_RXQ); 212 pci_viona_ring_reset(sc, VIONA_TXQ); 213 } 214 215 sc->vsc_status = value; 216 } 217 218 static void * 219 pci_viona_poll_thread(void *param) 220 { 221 struct pci_viona_softc *sc = param; 222 pollfd_t pollset; 223 int error; 224 225 pollset.fd = sc->vsc_vnafd; 226 pollset.events = POLLIN | POLLOUT; 227 228 for (;;) { 229 if (poll(&pollset, 1, -1) < 0) { 230 if (errno == EINTR || errno == EAGAIN) { 231 continue; 232 } else { 233 WPRINTF(("pci_viona_poll_thread poll()" 234 "error %d\n", errno)); 235 break; 236 } 237 } 238 if (pollset.revents & POLLIN) { 239 pci_generate_msix(sc->vsc_pi, 240 sc->vsc_msix_table_idx[VIONA_RXQ]); 241 error = ioctl(sc->vsc_vnafd, VNA_IOC_RX_INTR_CLR); 242 if (error != 0) { 243 WPRINTF(("ioctl viona rx intr clear failed" 244 " %d\n", error)); 245 } 246 } 247 248 if (pollset.revents & POLLOUT) { 249 pci_generate_msix(sc->vsc_pi, 250 sc->vsc_msix_table_idx[VIONA_TXQ]); 251 error = ioctl(sc->vsc_vnafd, VNA_IOC_TX_INTR_CLR); 252 if (error != 0) { 253 WPRINTF(("ioctl viona tx intr clear failed" 254 " %d\n", error)); 255 } 256 } 257 } 258 259 pthread_exit(NULL); 260 } 261 262 static void 263 pci_viona_ping_rxq(struct pci_viona_softc *sc) 264 { 265 int error; 266 267 error = ioctl(sc->vsc_vnafd, VNA_IOC_RX_RING_KICK); 268 if (error != 0) { 269 WPRINTF(("ioctl viona rx ring kick failed %d\n", error)); 270 } 271 } 272 273 static void * 274 pci_viona_tx_thread(void *param) 275 { 276 struct pci_viona_softc *sc = (struct pci_viona_softc *)param; 277 int error; 278 279 pthread_mutex_lock(&sc->tx_mtx); 280 for (;;) { 281 error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx); 282 assert(error == 0); 283 sc->vsc_tx_kick_lock_held = B_TRUE; 284 error = ioctl(sc->vsc_vnafd, VNA_IOC_TX_RING_KICK); 285 if (error != 0) { 286 WPRINTF(("ioctl viona tx ring kick failed %d\n", 287 error)); 288 } 289 sc->vsc_tx_kick_lock_held = B_FALSE; 290 } 291 pthread_mutex_unlock(&sc->tx_mtx); 292 } 293 294 static void 295 pci_viona_ping_txq(struct pci_viona_softc *sc) 296 { 297 /* Signal the tx thread for processing */ 298 if (sc->vsc_tx_kick_lock_held) 299 return; 300 pthread_mutex_lock(&sc->tx_mtx); 301 pthread_cond_signal(&sc->tx_cond); 302 pthread_mutex_unlock(&sc->tx_mtx); 303 } 304 305 static void 306 pci_viona_ping_ctlq(struct pci_viona_softc *sc) 307 { 308 DPRINTF(("viona: control qnotify!\n\r")); 309 } 310 311 static void 312 pci_viona_ring_init(struct pci_viona_softc *sc, uint64_t pfn) 313 { 314 int qnum = sc->vsc_curq; 315 vioc_ring_init_t vna_ri; 316 int error; 317 318 assert(qnum < VIONA_MAXQ); 319 320 sc->vsc_pfn[qnum] = (pfn << VRING_PFN); 321 322 vna_ri.ri_qsize = pci_viona_qsize(qnum); 323 vna_ri.ri_qaddr = (pfn << VRING_PFN); 324 325 switch (qnum) { 326 case VIONA_RXQ: 327 error = ioctl(sc->vsc_vnafd, VNA_IOC_RX_RING_INIT, &vna_ri); 328 if (error != 0) { 329 WPRINTF(("ioctl viona rx ring init failed %d\n", 330 error)); 331 } 332 break; 333 case VIONA_TXQ: 334 error = ioctl(sc->vsc_vnafd, VNA_IOC_TX_RING_INIT, &vna_ri); 335 if (error != 0) { 336 WPRINTF(("ioctl viona tx ring init failed %d\n", 337 error)); 338 } 339 break; 340 case VIONA_CTLQ: 341 default: 342 break; 343 } 344 } 345 346 static int 347 pci_viona_viona_init(struct vmctx *ctx, struct pci_viona_softc *sc) 348 { 349 vioc_create_t vna_create; 350 char devname[MAXNAMELEN]; 351 int ctlfd; 352 int error; 353 354 sc->vsc_vnafd = open("/devices/pseudo/viona@0:ctl", O_RDWR | O_EXCL); 355 if (sc->vsc_vnafd == -1) { 356 WPRINTF(("open viona ctl failed\n")); 357 return (-1); 358 } 359 360 vna_create.c_linkid = sc->vsc_linkid; 361 strlcpy(vna_create.c_vmname, vmname, 362 sizeof (vna_create.c_vmname)); 363 vm_get_memory_seg(ctx, 1 * (1024 * 1024UL), &vna_create.c_lomem_size, 364 NULL); 365 vm_get_memory_seg(ctx, 4 * (1024 * 1024 * 1024UL), 366 &vna_create.c_himem_size, NULL); 367 error = ioctl(sc->vsc_vnafd, VNA_IOC_CREATE, &vna_create); 368 if (error != 0) { 369 WPRINTF(("ioctl viona create failed %d\n", error)); 370 return (-1); 371 } 372 373 return (0); 374 } 375 376 static int 377 pci_viona_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 378 { 379 dladm_handle_t handle; 380 dladm_status_t status; 381 dladm_vnic_attr_t attr; 382 char errmsg[DLADM_STRSIZE]; 383 int error; 384 struct pci_viona_softc *sc; 385 int i; 386 387 if (opts == NULL) { 388 printf("virtio-viona: vnic required\n"); 389 return (1); 390 } 391 392 sc = malloc(sizeof (struct pci_viona_softc)); 393 memset(sc, 0, sizeof (struct pci_viona_softc)); 394 395 pi->pi_arg = sc; 396 sc->vsc_pi = pi; 397 398 pthread_mutex_init(&sc->vsc_mtx, NULL); 399 400 strlcpy(sc->vsc_linkname, opts, MAXLINKNAMELEN); 401 402 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) { 403 WPRINTF(("could not open /dev/dld")); 404 free(sc); 405 return (1); 406 } 407 408 if (dladm_name2info(handle, sc->vsc_linkname, &sc->vsc_linkid, 409 NULL, NULL, NULL) != DLADM_STATUS_OK) { 410 WPRINTF(("dladm_name2info() for %s failed: %s\n", opts, 411 dladm_status2str(status, errmsg))); 412 dladm_close(handle); 413 free(sc); 414 return (1); 415 } 416 417 if (dladm_vnic_info(handle, sc->vsc_linkid, &attr, 418 DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) { 419 WPRINTF(("dladm_vnic_info() for %s failed: %s\n", opts, 420 dladm_status2str(status, errmsg))); 421 dladm_close(handle); 422 free(sc); 423 return (1); 424 } 425 426 sc->vsc_tx_kick_lock_held = B_FALSE; 427 memcpy(sc->vsc_macaddr, attr.va_mac_addr, ETHERADDRL); 428 429 dladm_close(handle); 430 431 error = pci_viona_viona_init(ctx, sc); 432 if (error != 0) { 433 free(sc); 434 return (1); 435 } 436 437 error = pthread_create(NULL, NULL, pci_viona_poll_thread, sc); 438 assert(error == 0); 439 440 /* initialize config space */ 441 pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET); 442 pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 443 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK); 444 pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET); 445 446 /* MSI-X support */ 447 for (i = 0; i < VIONA_MAXQ; i++) 448 sc->vsc_msix_table_idx[i] = VIRTIO_MSI_NO_VECTOR; 449 450 /* 451 * BAR 1 used to map MSI-X table and PBA 452 */ 453 if (pci_emul_add_msixcap(pi, VIONA_MAXQ, 1)) { 454 free(sc); 455 return (1); 456 } 457 458 pci_emul_alloc_bar(pi, 0, PCIBAR_IO, VIONA_REGSZ); 459 460 /* 461 * Initialize tx semaphore & spawn TX processing thread 462 * As of now, only one thread for TX desc processing is 463 * spawned. 464 */ 465 pthread_mutex_init(&sc->tx_mtx, NULL); 466 pthread_cond_init(&sc->tx_cond, NULL); 467 pthread_create(&sc->tx_tid, NULL, pci_viona_tx_thread, (void *)sc); 468 469 return (0); 470 } 471 472 /* 473 * Function pointer array to handle queue notifications 474 */ 475 static void (*pci_viona_qnotify[VIONA_MAXQ])(struct pci_viona_softc *) = { 476 pci_viona_ping_rxq, 477 pci_viona_ping_txq, 478 pci_viona_ping_ctlq 479 }; 480 481 static uint64_t 482 viona_adjust_offset(struct pci_devinst *pi, uint64_t offset) 483 { 484 /* 485 * Device specific offsets used by guest would change based on 486 * whether MSI-X capability is enabled or not 487 */ 488 if (!pci_msix_enabled(pi)) { 489 if (offset >= VTCFG_R_MSIX) 490 return (offset + (VTCFG_R_CFG1 - VTCFG_R_MSIX)); 491 } 492 493 return (offset); 494 } 495 496 static void 497 pci_viona_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 498 int baridx, uint64_t offset, int size, uint64_t value) 499 { 500 struct pci_viona_softc *sc = pi->pi_arg; 501 void *ptr; 502 int err = 0; 503 504 if (baridx == pci_msix_table_bar(pi) || 505 baridx == pci_msix_pba_bar(pi)) { 506 pci_emul_msix_twrite(pi, offset, size, value); 507 return; 508 } 509 510 assert(baridx == 0); 511 512 if (offset + size > pci_viona_iosize(pi)) { 513 DPRINTF(("viona_write: 2big, offset %ld size %d\n", 514 offset, size)); 515 return; 516 } 517 518 pthread_mutex_lock(&sc->vsc_mtx); 519 520 offset = viona_adjust_offset(pi, offset); 521 522 switch (offset) { 523 case VTCFG_R_GUESTCAP: 524 assert(size == 4); 525 err = ioctl(sc->vsc_vnafd, VNA_IOC_SET_FEATURES, &value); 526 if (err != 0) 527 WPRINTF(("ioctl feature negotiation returned" 528 " err = %d\n", err)); 529 break; 530 case VTCFG_R_PFN: 531 assert(size == 4); 532 pci_viona_ring_init(sc, value); 533 break; 534 case VTCFG_R_QSEL: 535 assert(size == 2); 536 assert(value < VIONA_MAXQ); 537 sc->vsc_curq = value; 538 break; 539 case VTCFG_R_QNOTIFY: 540 assert(size == 2); 541 assert(value < VIONA_MAXQ); 542 (*pci_viona_qnotify[value])(sc); 543 break; 544 case VTCFG_R_STATUS: 545 assert(size == 1); 546 pci_viona_update_status(sc, value); 547 break; 548 case VTCFG_R_CFGVEC: 549 assert(size == 2); 550 sc->vsc_msix_table_idx[VIONA_CTLQ] = value; 551 break; 552 case VTCFG_R_QVEC: 553 assert(size == 2); 554 assert(sc->vsc_curq != VIONA_CTLQ); 555 sc->vsc_msix_table_idx[sc->vsc_curq] = value; 556 break; 557 case VIONA_R_CFG0: 558 case VIONA_R_CFG1: 559 case VIONA_R_CFG2: 560 case VIONA_R_CFG3: 561 case VIONA_R_CFG4: 562 case VIONA_R_CFG5: 563 assert((size + offset) <= (VIONA_R_CFG5 + 1)); 564 ptr = &sc->vsc_macaddr[offset - VIONA_R_CFG0]; 565 /* 566 * The driver is allowed to change the MAC address 567 */ 568 sc->vsc_macaddr[offset - VIONA_R_CFG0] = value; 569 if (size == 1) { 570 *(uint8_t *)ptr = value; 571 } else if (size == 2) { 572 *(uint16_t *)ptr = value; 573 } else { 574 *(uint32_t *)ptr = value; 575 } 576 break; 577 case VTCFG_R_HOSTCAP: 578 case VTCFG_R_QNUM: 579 case VTCFG_R_ISR: 580 case VIONA_R_CFG6: 581 case VIONA_R_CFG7: 582 DPRINTF(("viona: write to readonly reg %ld\n\r", offset)); 583 break; 584 default: 585 DPRINTF(("viona: unknown i/o write offset %ld\n\r", offset)); 586 value = 0; 587 break; 588 } 589 590 pthread_mutex_unlock(&sc->vsc_mtx); 591 } 592 593 uint64_t 594 pci_viona_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 595 int baridx, uint64_t offset, int size) 596 { 597 struct pci_viona_softc *sc = pi->pi_arg; 598 void *ptr; 599 uint64_t value; 600 int err = 0; 601 602 if (baridx == pci_msix_table_bar(pi) || 603 baridx == pci_msix_pba_bar(pi)) { 604 return (pci_emul_msix_tread(pi, offset, size)); 605 } 606 607 assert(baridx == 0); 608 609 if (offset + size > pci_viona_iosize(pi)) { 610 DPRINTF(("viona_read: 2big, offset %ld size %d\n", 611 offset, size)); 612 return (0); 613 } 614 615 pthread_mutex_lock(&sc->vsc_mtx); 616 617 offset = viona_adjust_offset(pi, offset); 618 619 switch (offset) { 620 case VTCFG_R_HOSTCAP: 621 assert(size == 4); 622 err = ioctl(sc->vsc_vnafd, VNA_IOC_GET_FEATURES, &value); 623 if (err != 0) 624 WPRINTF(("ioctl get host features returned" 625 " err = %d\n", err)); 626 break; 627 case VTCFG_R_GUESTCAP: 628 assert(size == 4); 629 value = sc->vsc_features; /* XXX never read ? */ 630 break; 631 case VTCFG_R_PFN: 632 assert(size == 4); 633 value = sc->vsc_pfn[sc->vsc_curq] >> VRING_PFN; 634 break; 635 case VTCFG_R_QNUM: 636 assert(size == 2); 637 value = pci_viona_qsize(sc->vsc_curq); 638 break; 639 case VTCFG_R_QSEL: 640 assert(size == 2); 641 value = sc->vsc_curq; /* XXX never read ? */ 642 break; 643 case VTCFG_R_QNOTIFY: 644 assert(size == 2); 645 value = sc->vsc_curq; /* XXX never read ? */ 646 break; 647 case VTCFG_R_STATUS: 648 assert(size == 1); 649 value = sc->vsc_status; 650 break; 651 case VTCFG_R_ISR: 652 assert(size == 1); 653 value = sc->vsc_isr; 654 sc->vsc_isr = 0; /* a read clears this flag */ 655 break; 656 case VTCFG_R_CFGVEC: 657 assert(size == 2); 658 value = sc->vsc_msix_table_idx[VIONA_CTLQ]; 659 break; 660 case VTCFG_R_QVEC: 661 assert(size == 2); 662 assert(sc->vsc_curq != VIONA_CTLQ); 663 value = sc->vsc_msix_table_idx[sc->vsc_curq]; 664 break; 665 case VIONA_R_CFG0: 666 case VIONA_R_CFG1: 667 case VIONA_R_CFG2: 668 case VIONA_R_CFG3: 669 case VIONA_R_CFG4: 670 case VIONA_R_CFG5: 671 assert((size + offset) <= (VIONA_R_CFG5 + 1)); 672 ptr = &sc->vsc_macaddr[offset - VIONA_R_CFG0]; 673 if (size == 1) { 674 value = *(uint8_t *)ptr; 675 } else if (size == 2) { 676 value = *(uint16_t *)ptr; 677 } else { 678 value = *(uint32_t *)ptr; 679 } 680 break; 681 case VIONA_R_CFG6: 682 assert(size != 4); 683 value = 0x01; /* XXX link always up */ 684 break; 685 case VIONA_R_CFG7: 686 assert(size == 1); 687 value = 0; /* XXX link status in LSB */ 688 break; 689 default: 690 DPRINTF(("viona: unknown i/o read offset %ld\n\r", offset)); 691 value = 0; 692 break; 693 } 694 695 pthread_mutex_unlock(&sc->vsc_mtx); 696 697 return (value); 698 } 699 700 struct pci_devemu pci_de_viona = { 701 .pe_emu = "virtio-net-viona", 702 .pe_init = pci_viona_init, 703 .pe_barwrite = pci_viona_write, 704 .pe_barread = pci_viona_read 705 }; 706 PCI_EMUL_SET(pci_de_viona); 707