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