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 && s != -1) 379 close(s); 380 381 return (error); 382 } 383 384 static void 385 pci_vtcon_sock_accept(int fd __unused, enum ev_type t __unused, void *arg) 386 { 387 struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg; 388 int s; 389 390 s = accept(sock->vss_server_fd, NULL, NULL); 391 if (s < 0) 392 return; 393 394 if (sock->vss_open) { 395 close(s); 396 return; 397 } 398 399 sock->vss_open = true; 400 sock->vss_conn_fd = s; 401 sock->vss_conn_evp = mevent_add(s, EVF_READ, pci_vtcon_sock_rx, sock); 402 403 pci_vtcon_open_port(sock->vss_port, true); 404 } 405 406 static void 407 pci_vtcon_sock_rx(int fd __unused, enum ev_type t __unused, void *arg) 408 { 409 struct pci_vtcon_port *port; 410 struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg; 411 struct vqueue_info *vq; 412 struct iovec iov; 413 static char dummybuf[2048]; 414 int len, n; 415 uint16_t idx; 416 417 port = sock->vss_port; 418 vq = pci_vtcon_port_to_vq(port, true); 419 420 if (!sock->vss_open || !port->vsp_rx_ready) { 421 len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf)); 422 if (len == 0) 423 goto close; 424 425 return; 426 } 427 428 if (!vq_has_descs(vq)) { 429 len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf)); 430 vq_endchains(vq, 1); 431 if (len == 0) 432 goto close; 433 434 return; 435 } 436 437 do { 438 n = vq_getchain(vq, &idx, &iov, 1, NULL); 439 len = readv(sock->vss_conn_fd, &iov, n); 440 441 if (len == 0 || (len < 0 && errno == EWOULDBLOCK)) { 442 vq_retchain(vq); 443 vq_endchains(vq, 0); 444 if (len == 0) 445 goto close; 446 447 return; 448 } 449 450 vq_relchain(vq, idx, len); 451 } while (vq_has_descs(vq)); 452 453 vq_endchains(vq, 1); 454 455 close: 456 mevent_delete_close(sock->vss_conn_evp); 457 sock->vss_conn_fd = -1; 458 sock->vss_open = false; 459 } 460 461 static void 462 pci_vtcon_sock_tx(struct pci_vtcon_port *port, void *arg, struct iovec *iov, 463 int niov) 464 { 465 struct pci_vtcon_sock *sock; 466 #ifdef __FreeBSD__ 467 int i, ret; 468 #else 469 int i, ret = 0; 470 #endif 471 472 sock = (struct pci_vtcon_sock *)arg; 473 474 if (sock->vss_conn_fd == -1) 475 return; 476 477 for (i = 0; i < niov; i++) { 478 ret = stream_write(sock->vss_conn_fd, iov[i].iov_base, 479 iov[i].iov_len); 480 if (ret <= 0) 481 break; 482 } 483 484 if (ret <= 0) { 485 mevent_delete_close(sock->vss_conn_evp); 486 sock->vss_conn_fd = -1; 487 sock->vss_open = false; 488 } 489 } 490 491 static void 492 pci_vtcon_control_tx(struct pci_vtcon_port *port, void *arg, struct iovec *iov, 493 int niov) 494 { 495 struct pci_vtcon_softc *sc; 496 struct pci_vtcon_port *tmp; 497 struct pci_vtcon_control resp, *ctrl; 498 int i; 499 500 assert(niov == 1); 501 502 sc = port->vsp_sc; 503 ctrl = (struct pci_vtcon_control *)iov->iov_base; 504 505 switch (ctrl->event) { 506 case VTCON_DEVICE_READY: 507 sc->vsc_ready = true; 508 /* set port ready events for registered ports */ 509 for (i = 0; i < VTCON_MAXPORTS; i++) { 510 tmp = &sc->vsc_ports[i]; 511 if (tmp->vsp_enabled) 512 pci_vtcon_announce_port(tmp); 513 514 if (tmp->vsp_open) 515 pci_vtcon_open_port(tmp, true); 516 } 517 break; 518 519 case VTCON_PORT_READY: 520 if (ctrl->id >= sc->vsc_nports) { 521 WPRINTF(("VTCON_PORT_READY event for unknown port %d\n", 522 ctrl->id)); 523 return; 524 } 525 526 tmp = &sc->vsc_ports[ctrl->id]; 527 if (tmp->vsp_console) { 528 resp.event = VTCON_CONSOLE_PORT; 529 resp.id = ctrl->id; 530 resp.value = 1; 531 pci_vtcon_control_send(sc, &resp, NULL, 0); 532 } 533 break; 534 } 535 } 536 537 static void 538 pci_vtcon_announce_port(struct pci_vtcon_port *port) 539 { 540 struct pci_vtcon_control event; 541 542 event.id = port->vsp_id; 543 event.event = VTCON_DEVICE_ADD; 544 event.value = 1; 545 pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0); 546 547 event.event = VTCON_PORT_NAME; 548 pci_vtcon_control_send(port->vsp_sc, &event, port->vsp_name, 549 strlen(port->vsp_name)); 550 } 551 552 static void 553 pci_vtcon_open_port(struct pci_vtcon_port *port, bool open) 554 { 555 struct pci_vtcon_control event; 556 557 if (!port->vsp_sc->vsc_ready) { 558 port->vsp_open = true; 559 return; 560 } 561 562 event.id = port->vsp_id; 563 event.event = VTCON_PORT_OPEN; 564 event.value = (int)open; 565 pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0); 566 } 567 568 static void 569 pci_vtcon_control_send(struct pci_vtcon_softc *sc, 570 struct pci_vtcon_control *ctrl, const void *payload, size_t len) 571 { 572 struct vqueue_info *vq; 573 struct iovec iov; 574 uint16_t idx; 575 int n; 576 577 vq = pci_vtcon_port_to_vq(&sc->vsc_control_port, true); 578 579 if (!vq_has_descs(vq)) 580 return; 581 582 n = vq_getchain(vq, &idx, &iov, 1, NULL); 583 584 assert(n == 1); 585 586 memcpy(iov.iov_base, ctrl, sizeof(struct pci_vtcon_control)); 587 if (payload != NULL && len > 0) 588 memcpy(iov.iov_base + sizeof(struct pci_vtcon_control), 589 payload, len); 590 591 vq_relchain(vq, idx, sizeof(struct pci_vtcon_control) + len); 592 vq_endchains(vq, 1); 593 } 594 595 596 static void 597 pci_vtcon_notify_tx(void *vsc, struct vqueue_info *vq) 598 { 599 struct pci_vtcon_softc *sc; 600 struct pci_vtcon_port *port; 601 struct iovec iov[1]; 602 uint16_t idx, n; 603 uint16_t flags[8]; 604 605 sc = vsc; 606 port = pci_vtcon_vq_to_port(sc, vq); 607 608 while (vq_has_descs(vq)) { 609 n = vq_getchain(vq, &idx, iov, 1, flags); 610 assert(n >= 1); 611 if (port != NULL) 612 port->vsp_cb(port, port->vsp_arg, iov, 1); 613 614 /* 615 * Release this chain and handle more 616 */ 617 vq_relchain(vq, idx, 0); 618 } 619 vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ 620 } 621 622 static void 623 pci_vtcon_notify_rx(void *vsc, struct vqueue_info *vq) 624 { 625 struct pci_vtcon_softc *sc; 626 struct pci_vtcon_port *port; 627 628 sc = vsc; 629 port = pci_vtcon_vq_to_port(sc, vq); 630 631 if (!port->vsp_rx_ready) { 632 port->vsp_rx_ready = 1; 633 vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY; 634 } 635 } 636 637 static int 638 pci_vtcon_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 639 { 640 struct pci_vtcon_softc *sc; 641 char *portname = NULL; 642 char *portpath = NULL; 643 char *opt; 644 int i; 645 646 sc = calloc(1, sizeof(struct pci_vtcon_softc)); 647 sc->vsc_config = calloc(1, sizeof(struct pci_vtcon_config)); 648 sc->vsc_config->max_nr_ports = VTCON_MAXPORTS; 649 sc->vsc_config->cols = 80; 650 sc->vsc_config->rows = 25; 651 652 vi_softc_linkup(&sc->vsc_vs, &vtcon_vi_consts, sc, pi, sc->vsc_queues); 653 sc->vsc_vs.vs_mtx = &sc->vsc_mtx; 654 655 for (i = 0; i < VTCON_MAXQ; i++) { 656 sc->vsc_queues[i].vq_qsize = VTCON_RINGSZ; 657 sc->vsc_queues[i].vq_notify = i % 2 == 0 658 ? pci_vtcon_notify_rx 659 : pci_vtcon_notify_tx; 660 } 661 662 /* initialize config space */ 663 pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_CONSOLE); 664 pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 665 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM); 666 pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_CONSOLE); 667 pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR); 668 669 if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix())) 670 return (1); 671 vi_set_io_bar(&sc->vsc_vs, 0); 672 673 /* create control port */ 674 sc->vsc_control_port.vsp_sc = sc; 675 sc->vsc_control_port.vsp_txq = 2; 676 sc->vsc_control_port.vsp_rxq = 3; 677 sc->vsc_control_port.vsp_cb = pci_vtcon_control_tx; 678 sc->vsc_control_port.vsp_enabled = true; 679 680 while ((opt = strsep(&opts, ",")) != NULL) { 681 portname = strsep(&opt, "="); 682 portpath = opt; 683 684 /* create port */ 685 if (pci_vtcon_sock_add(sc, portname, portpath) < 0) { 686 fprintf(stderr, "cannot create port %s: %s\n", 687 portname, strerror(errno)); 688 return (1); 689 } 690 } 691 692 return (0); 693 } 694 695 struct pci_devemu pci_de_vcon = { 696 .pe_emu = "virtio-console", 697 .pe_init = pci_vtcon_init, 698 .pe_barwrite = vi_pci_write, 699 .pe_barread = vi_pci_read 700 }; 701 PCI_EMUL_SET(pci_de_vcon); 702