1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2016 iXsystems Inc. 5 * All rights reserved. 6 * 7 * This software was developed by Jakub Klama <jceel@FreeBSD.org> 8 * under sponsorship from iXsystems Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer 15 * in this position and unchanged. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright 2018 Joyent, Inc. 35 */ 36 37 #include <sys/cdefs.h> 38 __FBSDID("$FreeBSD$"); 39 40 #include <sys/param.h> 41 #ifndef WITHOUT_CAPSICUM 42 #include <sys/capsicum.h> 43 #endif 44 #include <sys/linker_set.h> 45 #include <sys/uio.h> 46 #include <sys/types.h> 47 #include <sys/socket.h> 48 #include <sys/un.h> 49 50 #ifndef WITHOUT_CAPSICUM 51 #include <capsicum_helpers.h> 52 #endif 53 #include <err.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <stdbool.h> 59 #include <string.h> 60 #include <unistd.h> 61 #include <assert.h> 62 #include <pthread.h> 63 #include <libgen.h> 64 #include <sysexits.h> 65 66 #include "bhyverun.h" 67 #include "pci_emul.h" 68 #include "virtio.h" 69 #include "mevent.h" 70 #include "sockstream.h" 71 72 #define VTCON_RINGSZ 64 73 #define VTCON_MAXPORTS 16 74 #define VTCON_MAXQ (VTCON_MAXPORTS * 2 + 2) 75 76 #define VTCON_DEVICE_READY 0 77 #define VTCON_DEVICE_ADD 1 78 #define VTCON_DEVICE_REMOVE 2 79 #define VTCON_PORT_READY 3 80 #define VTCON_CONSOLE_PORT 4 81 #define VTCON_CONSOLE_RESIZE 5 82 #define VTCON_PORT_OPEN 6 83 #define VTCON_PORT_NAME 7 84 85 #define VTCON_F_SIZE 0 86 #define VTCON_F_MULTIPORT 1 87 #define VTCON_F_EMERG_WRITE 2 88 #define VTCON_S_HOSTCAPS \ 89 (VTCON_F_SIZE | VTCON_F_MULTIPORT | VTCON_F_EMERG_WRITE) 90 91 static int pci_vtcon_debug; 92 #define DPRINTF(params) if (pci_vtcon_debug) printf params 93 #define WPRINTF(params) printf params 94 95 struct pci_vtcon_softc; 96 struct pci_vtcon_port; 97 struct pci_vtcon_config; 98 typedef void (pci_vtcon_cb_t)(struct pci_vtcon_port *, void *, struct iovec *, 99 int); 100 101 struct pci_vtcon_port { 102 struct pci_vtcon_softc * vsp_sc; 103 int vsp_id; 104 const char * vsp_name; 105 bool vsp_enabled; 106 bool vsp_console; 107 bool vsp_rx_ready; 108 bool vsp_open; 109 int vsp_rxq; 110 int vsp_txq; 111 void * vsp_arg; 112 pci_vtcon_cb_t * vsp_cb; 113 }; 114 115 struct pci_vtcon_sock 116 { 117 struct pci_vtcon_port * vss_port; 118 const char * vss_path; 119 struct mevent * vss_server_evp; 120 struct mevent * vss_conn_evp; 121 int vss_server_fd; 122 int vss_conn_fd; 123 bool vss_open; 124 }; 125 126 struct pci_vtcon_softc { 127 struct virtio_softc vsc_vs; 128 struct vqueue_info vsc_queues[VTCON_MAXQ]; 129 pthread_mutex_t vsc_mtx; 130 uint64_t vsc_cfg; 131 uint64_t vsc_features; 132 char * vsc_rootdir; 133 int vsc_kq; 134 int vsc_nports; 135 bool vsc_ready; 136 struct pci_vtcon_port vsc_control_port; 137 struct pci_vtcon_port vsc_ports[VTCON_MAXPORTS]; 138 struct pci_vtcon_config *vsc_config; 139 }; 140 141 struct pci_vtcon_config { 142 uint16_t cols; 143 uint16_t rows; 144 uint32_t max_nr_ports; 145 uint32_t emerg_wr; 146 } __attribute__((packed)); 147 148 struct pci_vtcon_control { 149 uint32_t id; 150 uint16_t event; 151 uint16_t value; 152 } __attribute__((packed)); 153 154 struct pci_vtcon_console_resize { 155 uint16_t cols; 156 uint16_t rows; 157 } __attribute__((packed)); 158 159 static void pci_vtcon_reset(void *); 160 static void pci_vtcon_notify_rx(void *, struct vqueue_info *); 161 static void pci_vtcon_notify_tx(void *, struct vqueue_info *); 162 static int pci_vtcon_cfgread(void *, int, int, uint32_t *); 163 static int pci_vtcon_cfgwrite(void *, int, int, uint32_t); 164 static void pci_vtcon_neg_features(void *, uint64_t); 165 static void pci_vtcon_sock_accept(int, enum ev_type, void *); 166 static void pci_vtcon_sock_rx(int, enum ev_type, void *); 167 static void pci_vtcon_sock_tx(struct pci_vtcon_port *, void *, struct iovec *, 168 int); 169 static void pci_vtcon_control_send(struct pci_vtcon_softc *, 170 struct pci_vtcon_control *, const void *, size_t); 171 static void pci_vtcon_announce_port(struct pci_vtcon_port *); 172 static void pci_vtcon_open_port(struct pci_vtcon_port *, bool); 173 174 static struct virtio_consts vtcon_vi_consts = { 175 "vtcon", /* our name */ 176 VTCON_MAXQ, /* we support VTCON_MAXQ virtqueues */ 177 sizeof(struct pci_vtcon_config), /* config reg size */ 178 pci_vtcon_reset, /* reset */ 179 NULL, /* device-wide qnotify */ 180 pci_vtcon_cfgread, /* read virtio config */ 181 pci_vtcon_cfgwrite, /* write virtio config */ 182 pci_vtcon_neg_features, /* apply negotiated features */ 183 VTCON_S_HOSTCAPS, /* our capabilities */ 184 }; 185 186 187 static void 188 pci_vtcon_reset(void *vsc) 189 { 190 struct pci_vtcon_softc *sc; 191 192 sc = vsc; 193 194 DPRINTF(("vtcon: device reset requested!\n")); 195 vi_reset_dev(&sc->vsc_vs); 196 } 197 198 static void 199 pci_vtcon_neg_features(void *vsc, uint64_t negotiated_features) 200 { 201 struct pci_vtcon_softc *sc = vsc; 202 203 sc->vsc_features = negotiated_features; 204 } 205 206 static int 207 pci_vtcon_cfgread(void *vsc, int offset, int size, uint32_t *retval) 208 { 209 struct pci_vtcon_softc *sc = vsc; 210 void *ptr; 211 212 ptr = (uint8_t *)sc->vsc_config + offset; 213 memcpy(retval, ptr, size); 214 return (0); 215 } 216 217 static int 218 pci_vtcon_cfgwrite(void *vsc, int offset, int size, uint32_t val) 219 { 220 221 return (0); 222 } 223 224 static inline struct pci_vtcon_port * 225 pci_vtcon_vq_to_port(struct pci_vtcon_softc *sc, struct vqueue_info *vq) 226 { 227 uint16_t num = vq->vq_num; 228 229 if (num == 0 || num == 1) 230 return (&sc->vsc_ports[0]); 231 232 if (num == 2 || num == 3) 233 return (&sc->vsc_control_port); 234 235 return (&sc->vsc_ports[(num / 2) - 1]); 236 } 237 238 static inline struct vqueue_info * 239 pci_vtcon_port_to_vq(struct pci_vtcon_port *port, bool tx_queue) 240 { 241 int qnum; 242 243 qnum = tx_queue ? port->vsp_txq : port->vsp_rxq; 244 return (&port->vsp_sc->vsc_queues[qnum]); 245 } 246 247 static struct pci_vtcon_port * 248 pci_vtcon_port_add(struct pci_vtcon_softc *sc, const char *name, 249 pci_vtcon_cb_t *cb, void *arg) 250 { 251 struct pci_vtcon_port *port; 252 253 if (sc->vsc_nports == VTCON_MAXPORTS) { 254 errno = EBUSY; 255 return (NULL); 256 } 257 258 port = &sc->vsc_ports[sc->vsc_nports++]; 259 port->vsp_id = sc->vsc_nports - 1; 260 port->vsp_sc = sc; 261 port->vsp_name = name; 262 port->vsp_cb = cb; 263 port->vsp_arg = arg; 264 265 if (port->vsp_id == 0) { 266 /* port0 */ 267 port->vsp_txq = 0; 268 port->vsp_rxq = 1; 269 } else { 270 port->vsp_txq = sc->vsc_nports * 2; 271 port->vsp_rxq = port->vsp_txq + 1; 272 } 273 274 port->vsp_enabled = true; 275 return (port); 276 } 277 278 static int 279 pci_vtcon_sock_add(struct pci_vtcon_softc *sc, const char *name, 280 const char *path) 281 { 282 struct pci_vtcon_sock *sock; 283 #ifdef __FreeBSD__ 284 struct sockaddr_un sun; 285 char *pathcopy; 286 #else 287 /* Our compiler #defines 'sun' as '1'. Awesome. */ 288 struct sockaddr_un addr; 289 #endif 290 int s = -1, fd = -1, error = 0; 291 #ifndef WITHOUT_CAPSICUM 292 cap_rights_t rights; 293 #endif 294 295 sock = calloc(1, sizeof(struct pci_vtcon_sock)); 296 if (sock == NULL) { 297 error = -1; 298 goto out; 299 } 300 301 s = socket(AF_UNIX, SOCK_STREAM, 0); 302 if (s < 0) { 303 error = -1; 304 goto out; 305 } 306 307 #ifdef __FreeBSD__ 308 pathcopy = strdup(path); 309 if (pathcopy == NULL) { 310 error = -1; 311 goto out; 312 } 313 314 fd = open(dirname(pathcopy), O_RDONLY | O_DIRECTORY); 315 if (fd < 0) { 316 free(pathcopy); 317 error = -1; 318 goto out; 319 } 320 321 sun.sun_family = AF_UNIX; 322 sun.sun_len = sizeof(struct sockaddr_un); 323 strcpy(pathcopy, path); 324 strlcpy(sun.sun_path, basename(pathcopy), sizeof(sun.sun_path)); 325 free(pathcopy); 326 327 if (bindat(fd, s, (struct sockaddr *)&sun, sun.sun_len) < 0) { 328 error = -1; 329 goto out; 330 } 331 #else /* __FreeBSD__ */ 332 /* Do a simple bind rather than the FreeBSD bindat() */ 333 addr.sun_family = AF_UNIX; 334 (void) strlcpy(addr.sun_path, path, sizeof (addr.sun_path)); 335 if (bind(fd, (struct sockaddr *)&addr, sizeof (addr)) < 0) { 336 error = -1; 337 goto out; 338 } 339 #endif /* __FreeBSD__ */ 340 341 if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { 342 error = -1; 343 goto out; 344 } 345 346 if (listen(s, 1) < 0) { 347 error = -1; 348 goto out; 349 } 350 351 #ifndef WITHOUT_CAPSICUM 352 cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE); 353 if (caph_rights_limit(s, &rights) == -1) 354 errx(EX_OSERR, "Unable to apply rights for sandbox"); 355 #endif 356 357 sock->vss_port = pci_vtcon_port_add(sc, name, pci_vtcon_sock_tx, sock); 358 if (sock->vss_port == NULL) { 359 error = -1; 360 goto out; 361 } 362 363 sock->vss_open = false; 364 sock->vss_conn_fd = -1; 365 sock->vss_server_fd = s; 366 sock->vss_server_evp = mevent_add(s, EVF_READ, pci_vtcon_sock_accept, 367 sock); 368 369 if (sock->vss_server_evp == NULL) { 370 error = -1; 371 goto out; 372 } 373 374 out: 375 if (fd != -1) 376 close(fd); 377 378 if (error != 0) { 379 if (s != -1) 380 close(s); 381 free(sock); 382 } 383 384 return (error); 385 } 386 387 static void 388 pci_vtcon_sock_accept(int fd __unused, enum ev_type t __unused, void *arg) 389 { 390 struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg; 391 int s; 392 393 s = accept(sock->vss_server_fd, NULL, NULL); 394 if (s < 0) 395 return; 396 397 if (sock->vss_open) { 398 close(s); 399 return; 400 } 401 402 sock->vss_open = true; 403 sock->vss_conn_fd = s; 404 sock->vss_conn_evp = mevent_add(s, EVF_READ, pci_vtcon_sock_rx, sock); 405 406 pci_vtcon_open_port(sock->vss_port, true); 407 } 408 409 static void 410 pci_vtcon_sock_rx(int fd __unused, enum ev_type t __unused, void *arg) 411 { 412 struct pci_vtcon_port *port; 413 struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg; 414 struct vqueue_info *vq; 415 struct iovec iov; 416 static char dummybuf[2048]; 417 int len, n; 418 uint16_t idx; 419 420 port = sock->vss_port; 421 vq = pci_vtcon_port_to_vq(port, true); 422 423 if (!sock->vss_open || !port->vsp_rx_ready) { 424 len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf)); 425 if (len == 0) 426 goto close; 427 428 return; 429 } 430 431 if (!vq_has_descs(vq)) { 432 len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf)); 433 vq_endchains(vq, 1); 434 if (len == 0) 435 goto close; 436 437 return; 438 } 439 440 do { 441 n = vq_getchain(vq, &idx, &iov, 1, NULL); 442 len = readv(sock->vss_conn_fd, &iov, n); 443 444 if (len == 0 || (len < 0 && errno == EWOULDBLOCK)) { 445 vq_retchain(vq); 446 vq_endchains(vq, 0); 447 if (len == 0) 448 goto close; 449 450 return; 451 } 452 453 vq_relchain(vq, idx, len); 454 } while (vq_has_descs(vq)); 455 456 vq_endchains(vq, 1); 457 458 close: 459 mevent_delete_close(sock->vss_conn_evp); 460 sock->vss_conn_fd = -1; 461 sock->vss_open = false; 462 } 463 464 static void 465 pci_vtcon_sock_tx(struct pci_vtcon_port *port, void *arg, struct iovec *iov, 466 int niov) 467 { 468 struct pci_vtcon_sock *sock; 469 #ifdef __FreeBSD__ 470 int i, ret; 471 #else 472 int i, ret = 0; 473 #endif 474 475 sock = (struct pci_vtcon_sock *)arg; 476 477 if (sock->vss_conn_fd == -1) 478 return; 479 480 for (i = 0; i < niov; i++) { 481 ret = stream_write(sock->vss_conn_fd, iov[i].iov_base, 482 iov[i].iov_len); 483 if (ret <= 0) 484 break; 485 } 486 487 if (ret <= 0) { 488 mevent_delete_close(sock->vss_conn_evp); 489 sock->vss_conn_fd = -1; 490 sock->vss_open = false; 491 } 492 } 493 494 static void 495 pci_vtcon_control_tx(struct pci_vtcon_port *port, void *arg, struct iovec *iov, 496 int niov) 497 { 498 struct pci_vtcon_softc *sc; 499 struct pci_vtcon_port *tmp; 500 struct pci_vtcon_control resp, *ctrl; 501 int i; 502 503 assert(niov == 1); 504 505 sc = port->vsp_sc; 506 ctrl = (struct pci_vtcon_control *)iov->iov_base; 507 508 switch (ctrl->event) { 509 case VTCON_DEVICE_READY: 510 sc->vsc_ready = true; 511 /* set port ready events for registered ports */ 512 for (i = 0; i < VTCON_MAXPORTS; i++) { 513 tmp = &sc->vsc_ports[i]; 514 if (tmp->vsp_enabled) 515 pci_vtcon_announce_port(tmp); 516 517 if (tmp->vsp_open) 518 pci_vtcon_open_port(tmp, true); 519 } 520 break; 521 522 case VTCON_PORT_READY: 523 if (ctrl->id >= sc->vsc_nports) { 524 WPRINTF(("VTCON_PORT_READY event for unknown port %d\n", 525 ctrl->id)); 526 return; 527 } 528 529 tmp = &sc->vsc_ports[ctrl->id]; 530 if (tmp->vsp_console) { 531 resp.event = VTCON_CONSOLE_PORT; 532 resp.id = ctrl->id; 533 resp.value = 1; 534 pci_vtcon_control_send(sc, &resp, NULL, 0); 535 } 536 break; 537 } 538 } 539 540 static void 541 pci_vtcon_announce_port(struct pci_vtcon_port *port) 542 { 543 struct pci_vtcon_control event; 544 545 event.id = port->vsp_id; 546 event.event = VTCON_DEVICE_ADD; 547 event.value = 1; 548 pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0); 549 550 event.event = VTCON_PORT_NAME; 551 pci_vtcon_control_send(port->vsp_sc, &event, port->vsp_name, 552 strlen(port->vsp_name)); 553 } 554 555 static void 556 pci_vtcon_open_port(struct pci_vtcon_port *port, bool open) 557 { 558 struct pci_vtcon_control event; 559 560 if (!port->vsp_sc->vsc_ready) { 561 port->vsp_open = true; 562 return; 563 } 564 565 event.id = port->vsp_id; 566 event.event = VTCON_PORT_OPEN; 567 event.value = (int)open; 568 pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0); 569 } 570 571 static void 572 pci_vtcon_control_send(struct pci_vtcon_softc *sc, 573 struct pci_vtcon_control *ctrl, const void *payload, size_t len) 574 { 575 struct vqueue_info *vq; 576 struct iovec iov; 577 uint16_t idx; 578 int n; 579 580 vq = pci_vtcon_port_to_vq(&sc->vsc_control_port, true); 581 582 if (!vq_has_descs(vq)) 583 return; 584 585 n = vq_getchain(vq, &idx, &iov, 1, NULL); 586 587 assert(n == 1); 588 589 memcpy(iov.iov_base, ctrl, sizeof(struct pci_vtcon_control)); 590 if (payload != NULL && len > 0) 591 memcpy(iov.iov_base + sizeof(struct pci_vtcon_control), 592 payload, len); 593 594 vq_relchain(vq, idx, sizeof(struct pci_vtcon_control) + len); 595 vq_endchains(vq, 1); 596 } 597 598 599 static void 600 pci_vtcon_notify_tx(void *vsc, struct vqueue_info *vq) 601 { 602 struct pci_vtcon_softc *sc; 603 struct pci_vtcon_port *port; 604 struct iovec iov[1]; 605 uint16_t idx, n; 606 uint16_t flags[8]; 607 608 sc = vsc; 609 port = pci_vtcon_vq_to_port(sc, vq); 610 611 while (vq_has_descs(vq)) { 612 n = vq_getchain(vq, &idx, iov, 1, flags); 613 assert(n >= 1); 614 if (port != NULL) 615 port->vsp_cb(port, port->vsp_arg, iov, 1); 616 617 /* 618 * Release this chain and handle more 619 */ 620 vq_relchain(vq, idx, 0); 621 } 622 vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ 623 } 624 625 static void 626 pci_vtcon_notify_rx(void *vsc, struct vqueue_info *vq) 627 { 628 struct pci_vtcon_softc *sc; 629 struct pci_vtcon_port *port; 630 631 sc = vsc; 632 port = pci_vtcon_vq_to_port(sc, vq); 633 634 if (!port->vsp_rx_ready) { 635 port->vsp_rx_ready = 1; 636 vq_kick_disable(vq); 637 } 638 } 639 640 static int 641 pci_vtcon_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 642 { 643 struct pci_vtcon_softc *sc; 644 char *portname = NULL; 645 char *portpath = NULL; 646 char *opt; 647 int i; 648 649 sc = calloc(1, sizeof(struct pci_vtcon_softc)); 650 sc->vsc_config = calloc(1, sizeof(struct pci_vtcon_config)); 651 sc->vsc_config->max_nr_ports = VTCON_MAXPORTS; 652 sc->vsc_config->cols = 80; 653 sc->vsc_config->rows = 25; 654 655 vi_softc_linkup(&sc->vsc_vs, &vtcon_vi_consts, sc, pi, sc->vsc_queues); 656 sc->vsc_vs.vs_mtx = &sc->vsc_mtx; 657 658 for (i = 0; i < VTCON_MAXQ; i++) { 659 sc->vsc_queues[i].vq_qsize = VTCON_RINGSZ; 660 sc->vsc_queues[i].vq_notify = i % 2 == 0 661 ? pci_vtcon_notify_rx 662 : pci_vtcon_notify_tx; 663 } 664 665 /* initialize config space */ 666 pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_CONSOLE); 667 pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 668 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM); 669 pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_CONSOLE); 670 pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR); 671 672 if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix())) 673 return (1); 674 vi_set_io_bar(&sc->vsc_vs, 0); 675 676 /* create control port */ 677 sc->vsc_control_port.vsp_sc = sc; 678 sc->vsc_control_port.vsp_txq = 2; 679 sc->vsc_control_port.vsp_rxq = 3; 680 sc->vsc_control_port.vsp_cb = pci_vtcon_control_tx; 681 sc->vsc_control_port.vsp_enabled = true; 682 683 while ((opt = strsep(&opts, ",")) != NULL) { 684 portname = strsep(&opt, "="); 685 portpath = opt; 686 687 /* create port */ 688 if (pci_vtcon_sock_add(sc, portname, portpath) < 0) { 689 fprintf(stderr, "cannot create port %s: %s\n", 690 portname, strerror(errno)); 691 return (1); 692 } 693 } 694 695 return (0); 696 } 697 698 struct pci_devemu pci_de_vcon = { 699 .pe_emu = "virtio-console", 700 .pe_init = pci_vtcon_init, 701 .pe_barwrite = vi_pci_write, 702 .pe_barread = vi_pci_read 703 }; 704 PCI_EMUL_SET(pci_de_vcon); 705