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 "config.h" 68 #include "debug.h" 69 #include "pci_emul.h" 70 #include "virtio.h" 71 #include "mevent.h" 72 #include "sockstream.h" 73 74 #define VTCON_RINGSZ 64 75 #define VTCON_MAXPORTS 16 76 #define VTCON_MAXQ (VTCON_MAXPORTS * 2 + 2) 77 78 #define VTCON_DEVICE_READY 0 79 #define VTCON_DEVICE_ADD 1 80 #define VTCON_DEVICE_REMOVE 2 81 #define VTCON_PORT_READY 3 82 #define VTCON_CONSOLE_PORT 4 83 #define VTCON_CONSOLE_RESIZE 5 84 #define VTCON_PORT_OPEN 6 85 #define VTCON_PORT_NAME 7 86 87 #define VTCON_F_SIZE 0 88 #define VTCON_F_MULTIPORT 1 89 #define VTCON_F_EMERG_WRITE 2 90 #define VTCON_S_HOSTCAPS \ 91 (VTCON_F_SIZE | VTCON_F_MULTIPORT | VTCON_F_EMERG_WRITE) 92 93 static int pci_vtcon_debug; 94 #define DPRINTF(params) if (pci_vtcon_debug) PRINTLN params 95 #define WPRINTF(params) PRINTLN params 96 97 struct pci_vtcon_softc; 98 struct pci_vtcon_port; 99 struct pci_vtcon_config; 100 typedef void (pci_vtcon_cb_t)(struct pci_vtcon_port *, void *, struct iovec *, 101 int); 102 103 struct pci_vtcon_port { 104 struct pci_vtcon_softc * vsp_sc; 105 int vsp_id; 106 const char * vsp_name; 107 bool vsp_enabled; 108 bool vsp_console; 109 bool vsp_rx_ready; 110 bool vsp_open; 111 int vsp_rxq; 112 int vsp_txq; 113 void * vsp_arg; 114 pci_vtcon_cb_t * vsp_cb; 115 }; 116 117 struct pci_vtcon_sock 118 { 119 struct pci_vtcon_port * vss_port; 120 const char * vss_path; 121 struct mevent * vss_server_evp; 122 struct mevent * vss_conn_evp; 123 int vss_server_fd; 124 int vss_conn_fd; 125 bool vss_open; 126 }; 127 128 struct pci_vtcon_softc { 129 struct virtio_softc vsc_vs; 130 struct vqueue_info vsc_queues[VTCON_MAXQ]; 131 pthread_mutex_t vsc_mtx; 132 uint64_t vsc_cfg; 133 uint64_t vsc_features; 134 char * vsc_rootdir; 135 int vsc_kq; 136 bool vsc_ready; 137 struct pci_vtcon_port vsc_control_port; 138 struct pci_vtcon_port vsc_ports[VTCON_MAXPORTS]; 139 struct pci_vtcon_config *vsc_config; 140 }; 141 142 struct pci_vtcon_config { 143 uint16_t cols; 144 uint16_t rows; 145 uint32_t max_nr_ports; 146 uint32_t emerg_wr; 147 } __attribute__((packed)); 148 149 struct pci_vtcon_control { 150 uint32_t id; 151 uint16_t event; 152 uint16_t value; 153 } __attribute__((packed)); 154 155 struct pci_vtcon_console_resize { 156 uint16_t cols; 157 uint16_t rows; 158 } __attribute__((packed)); 159 160 static void pci_vtcon_reset(void *); 161 static void pci_vtcon_notify_rx(void *, struct vqueue_info *); 162 static void pci_vtcon_notify_tx(void *, struct vqueue_info *); 163 static int pci_vtcon_cfgread(void *, int, int, uint32_t *); 164 static int pci_vtcon_cfgwrite(void *, int, int, uint32_t); 165 static void pci_vtcon_neg_features(void *, uint64_t); 166 static void pci_vtcon_sock_accept(int, enum ev_type, void *); 167 static void pci_vtcon_sock_rx(int, enum ev_type, void *); 168 static void pci_vtcon_sock_tx(struct pci_vtcon_port *, void *, struct iovec *, 169 int); 170 static void pci_vtcon_control_send(struct pci_vtcon_softc *, 171 struct pci_vtcon_control *, const void *, size_t); 172 static void pci_vtcon_announce_port(struct pci_vtcon_port *); 173 static void pci_vtcon_open_port(struct pci_vtcon_port *, bool); 174 175 static struct virtio_consts vtcon_vi_consts = { 176 .vc_name = "vtcon", 177 .vc_nvq = VTCON_MAXQ, 178 .vc_cfgsize = sizeof(struct pci_vtcon_config), 179 .vc_reset = pci_vtcon_reset, 180 .vc_cfgread = pci_vtcon_cfgread, 181 .vc_cfgwrite = pci_vtcon_cfgwrite, 182 .vc_apply_features = pci_vtcon_neg_features, 183 .vc_hv_caps = VTCON_S_HOSTCAPS, 184 }; 185 186 static void 187 pci_vtcon_reset(void *vsc) 188 { 189 struct pci_vtcon_softc *sc; 190 191 sc = vsc; 192 193 DPRINTF(("vtcon: device reset requested!")); 194 vi_reset_dev(&sc->vsc_vs); 195 } 196 197 static void 198 pci_vtcon_neg_features(void *vsc, uint64_t negotiated_features) 199 { 200 struct pci_vtcon_softc *sc = vsc; 201 202 sc->vsc_features = negotiated_features; 203 } 204 205 static int 206 pci_vtcon_cfgread(void *vsc, int offset, int size, uint32_t *retval) 207 { 208 struct pci_vtcon_softc *sc = vsc; 209 void *ptr; 210 211 ptr = (uint8_t *)sc->vsc_config + offset; 212 memcpy(retval, ptr, size); 213 return (0); 214 } 215 216 static int 217 pci_vtcon_cfgwrite(void *vsc __unused, int offset __unused, int size __unused, 218 uint32_t val __unused) 219 { 220 return (0); 221 } 222 223 static inline struct pci_vtcon_port * 224 pci_vtcon_vq_to_port(struct pci_vtcon_softc *sc, struct vqueue_info *vq) 225 { 226 uint16_t num = vq->vq_num; 227 228 if (num == 0 || num == 1) 229 return (&sc->vsc_ports[0]); 230 231 if (num == 2 || num == 3) 232 return (&sc->vsc_control_port); 233 234 return (&sc->vsc_ports[(num / 2) - 1]); 235 } 236 237 static inline struct vqueue_info * 238 pci_vtcon_port_to_vq(struct pci_vtcon_port *port, bool tx_queue) 239 { 240 int qnum; 241 242 qnum = tx_queue ? port->vsp_txq : port->vsp_rxq; 243 return (&port->vsp_sc->vsc_queues[qnum]); 244 } 245 246 static struct pci_vtcon_port * 247 pci_vtcon_port_add(struct pci_vtcon_softc *sc, int port_id, const char *name, 248 pci_vtcon_cb_t *cb, void *arg) 249 { 250 struct pci_vtcon_port *port; 251 252 port = &sc->vsc_ports[port_id]; 253 if (port->vsp_enabled) { 254 errno = EBUSY; 255 return (NULL); 256 } 257 port->vsp_id = port_id; 258 port->vsp_sc = sc; 259 port->vsp_name = name; 260 port->vsp_cb = cb; 261 port->vsp_arg = arg; 262 263 if (port->vsp_id == 0) { 264 /* port0 */ 265 port->vsp_txq = 0; 266 port->vsp_rxq = 1; 267 } else { 268 port->vsp_txq = (port_id + 1) * 2; 269 port->vsp_rxq = port->vsp_txq + 1; 270 } 271 272 port->vsp_enabled = true; 273 return (port); 274 } 275 276 static int 277 pci_vtcon_sock_add(struct pci_vtcon_softc *sc, const char *port_name, 278 const nvlist_t *nvl) 279 { 280 struct pci_vtcon_sock *sock = NULL; 281 #ifdef __FreeBSD__ 282 struct sockaddr_un sun; 283 #else 284 /* Our compiler #defines 'sun' as '1'. Awesome. */ 285 struct sockaddr_un addr; 286 #endif 287 const char *name, *path; 288 char *cp, *pathcopy; 289 long port; 290 int s = -1, fd = -1, error = 0; 291 #ifndef WITHOUT_CAPSICUM 292 cap_rights_t rights; 293 #endif 294 295 port = strtol(port_name, &cp, 0); 296 if (*cp != '\0' || port < 0 || port >= VTCON_MAXPORTS) { 297 EPRINTLN("vtcon: Invalid port %s", port_name); 298 error = -1; 299 goto out; 300 } 301 302 path = get_config_value_node(nvl, "path"); 303 if (path == NULL) { 304 EPRINTLN("vtcon: required path missing for port %ld", port); 305 error = -1; 306 goto out; 307 } 308 309 sock = calloc(1, sizeof(struct pci_vtcon_sock)); 310 if (sock == NULL) { 311 error = -1; 312 goto out; 313 } 314 315 s = socket(AF_UNIX, SOCK_STREAM, 0); 316 if (s < 0) { 317 error = -1; 318 goto out; 319 } 320 321 #ifdef __FreeBSD__ 322 pathcopy = strdup(path); 323 if (pathcopy == NULL) { 324 error = -1; 325 goto out; 326 } 327 328 fd = open(dirname(pathcopy), O_RDONLY | O_DIRECTORY); 329 if (fd < 0) { 330 free(pathcopy); 331 error = -1; 332 goto out; 333 } 334 335 sun.sun_family = AF_UNIX; 336 sun.sun_len = sizeof(struct sockaddr_un); 337 strcpy(pathcopy, path); 338 strlcpy(sun.sun_path, basename(pathcopy), sizeof(sun.sun_path)); 339 free(pathcopy); 340 341 if (bindat(fd, s, (struct sockaddr *)&sun, sun.sun_len) < 0) { 342 error = -1; 343 goto out; 344 } 345 #else /* __FreeBSD__ */ 346 /* Do a simple bind rather than the FreeBSD bindat() */ 347 pathcopy = (char *)path; 348 addr.sun_family = AF_UNIX; 349 (void) strlcpy(addr.sun_path, pathcopy, sizeof (addr.sun_path)); 350 if (bind(fd, (struct sockaddr *)&addr, sizeof (addr)) < 0) { 351 error = -1; 352 goto out; 353 } 354 #endif /* __FreeBSD__ */ 355 356 if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { 357 error = -1; 358 goto out; 359 } 360 361 if (listen(s, 1) < 0) { 362 error = -1; 363 goto out; 364 } 365 366 #ifndef WITHOUT_CAPSICUM 367 cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE); 368 if (caph_rights_limit(s, &rights) == -1) 369 errx(EX_OSERR, "Unable to apply rights for sandbox"); 370 #endif 371 372 name = get_config_value_node(nvl, "name"); 373 if (name == NULL) { 374 EPRINTLN("vtcon: required name missing for port %ld", port); 375 error = -1; 376 goto out; 377 } 378 sock->vss_port = pci_vtcon_port_add(sc, port, name, pci_vtcon_sock_tx, sock); 379 if (sock->vss_port == NULL) { 380 error = -1; 381 goto out; 382 } 383 384 sock->vss_open = false; 385 sock->vss_conn_fd = -1; 386 sock->vss_server_fd = s; 387 sock->vss_server_evp = mevent_add(s, EVF_READ, pci_vtcon_sock_accept, 388 sock); 389 390 if (sock->vss_server_evp == NULL) { 391 error = -1; 392 goto out; 393 } 394 395 out: 396 if (fd != -1) 397 close(fd); 398 399 if (error != 0) { 400 if (s != -1) 401 close(s); 402 free(sock); 403 } 404 405 return (error); 406 } 407 408 static void 409 pci_vtcon_sock_accept(int fd __unused, enum ev_type t __unused, void *arg) 410 { 411 struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg; 412 int s; 413 414 s = accept(sock->vss_server_fd, NULL, NULL); 415 if (s < 0) 416 return; 417 418 if (sock->vss_open) { 419 close(s); 420 return; 421 } 422 423 sock->vss_open = true; 424 sock->vss_conn_fd = s; 425 sock->vss_conn_evp = mevent_add(s, EVF_READ, pci_vtcon_sock_rx, sock); 426 427 pci_vtcon_open_port(sock->vss_port, true); 428 } 429 430 static void 431 pci_vtcon_sock_rx(int fd __unused, enum ev_type t __unused, void *arg) 432 { 433 struct pci_vtcon_port *port; 434 struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg; 435 struct vqueue_info *vq; 436 struct vi_req req; 437 struct iovec iov; 438 static char dummybuf[2048]; 439 int len, n; 440 441 port = sock->vss_port; 442 vq = pci_vtcon_port_to_vq(port, true); 443 444 if (!sock->vss_open || !port->vsp_rx_ready) { 445 len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf)); 446 if (len == 0) 447 goto close; 448 449 return; 450 } 451 452 if (!vq_has_descs(vq)) { 453 len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf)); 454 vq_endchains(vq, 1); 455 if (len == 0) 456 goto close; 457 458 return; 459 } 460 461 do { 462 n = vq_getchain(vq, &iov, 1, &req); 463 assert(n == 1); 464 len = readv(sock->vss_conn_fd, &iov, n); 465 466 if (len == 0 || (len < 0 && errno == EWOULDBLOCK)) { 467 vq_retchains(vq, 1); 468 vq_endchains(vq, 0); 469 if (len == 0) 470 goto close; 471 472 return; 473 } 474 475 vq_relchain(vq, req.idx, len); 476 } while (vq_has_descs(vq)); 477 478 vq_endchains(vq, 1); 479 480 close: 481 mevent_delete_close(sock->vss_conn_evp); 482 sock->vss_conn_fd = -1; 483 sock->vss_open = false; 484 } 485 486 static void 487 pci_vtcon_sock_tx(struct pci_vtcon_port *port __unused, void *arg __unused, 488 struct iovec *iov, int niov) 489 { 490 struct pci_vtcon_sock *sock; 491 int i, ret; 492 493 #ifndef __FreeBSD__ 494 ret = 0; 495 #endif 496 497 sock = (struct pci_vtcon_sock *)arg; 498 499 if (sock->vss_conn_fd == -1) 500 return; 501 502 for (i = 0; i < niov; i++) { 503 ret = stream_write(sock->vss_conn_fd, iov[i].iov_base, 504 iov[i].iov_len); 505 if (ret <= 0) 506 break; 507 } 508 509 if (ret <= 0) { 510 mevent_delete_close(sock->vss_conn_evp); 511 sock->vss_conn_fd = -1; 512 sock->vss_open = false; 513 } 514 } 515 516 static void 517 pci_vtcon_control_tx(struct pci_vtcon_port *port, void *arg __unused, 518 struct iovec *iov, int niov) 519 { 520 struct pci_vtcon_softc *sc; 521 struct pci_vtcon_port *tmp; 522 struct pci_vtcon_control resp, *ctrl; 523 int i; 524 525 assert(niov == 1); 526 527 sc = port->vsp_sc; 528 ctrl = (struct pci_vtcon_control *)iov->iov_base; 529 530 switch (ctrl->event) { 531 case VTCON_DEVICE_READY: 532 sc->vsc_ready = true; 533 /* set port ready events for registered ports */ 534 for (i = 0; i < VTCON_MAXPORTS; i++) { 535 tmp = &sc->vsc_ports[i]; 536 if (tmp->vsp_enabled) 537 pci_vtcon_announce_port(tmp); 538 539 if (tmp->vsp_open) 540 pci_vtcon_open_port(tmp, true); 541 } 542 break; 543 544 case VTCON_PORT_READY: 545 tmp = &sc->vsc_ports[ctrl->id]; 546 if (ctrl->id >= VTCON_MAXPORTS || !tmp->vsp_enabled) { 547 WPRINTF(("VTCON_PORT_READY event for unknown port %d", 548 ctrl->id)); 549 return; 550 } 551 552 if (tmp->vsp_console) { 553 resp.event = VTCON_CONSOLE_PORT; 554 resp.id = ctrl->id; 555 resp.value = 1; 556 pci_vtcon_control_send(sc, &resp, NULL, 0); 557 } 558 break; 559 } 560 } 561 562 static void 563 pci_vtcon_announce_port(struct pci_vtcon_port *port) 564 { 565 struct pci_vtcon_control event; 566 567 event.id = port->vsp_id; 568 event.event = VTCON_DEVICE_ADD; 569 event.value = 1; 570 pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0); 571 572 event.event = VTCON_PORT_NAME; 573 pci_vtcon_control_send(port->vsp_sc, &event, port->vsp_name, 574 strlen(port->vsp_name)); 575 } 576 577 static void 578 pci_vtcon_open_port(struct pci_vtcon_port *port, bool open) 579 { 580 struct pci_vtcon_control event; 581 582 if (!port->vsp_sc->vsc_ready) { 583 port->vsp_open = true; 584 return; 585 } 586 587 event.id = port->vsp_id; 588 event.event = VTCON_PORT_OPEN; 589 event.value = (int)open; 590 pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0); 591 } 592 593 static void 594 pci_vtcon_control_send(struct pci_vtcon_softc *sc, 595 struct pci_vtcon_control *ctrl, const void *payload, size_t len) 596 { 597 struct vqueue_info *vq; 598 struct vi_req req; 599 struct iovec iov; 600 int n; 601 602 vq = pci_vtcon_port_to_vq(&sc->vsc_control_port, true); 603 604 if (!vq_has_descs(vq)) 605 return; 606 607 n = vq_getchain(vq, &iov, 1, &req); 608 assert(n == 1); 609 610 memcpy(iov.iov_base, ctrl, sizeof(struct pci_vtcon_control)); 611 if (payload != NULL && len > 0) 612 memcpy((uint8_t *)iov.iov_base + 613 sizeof(struct pci_vtcon_control), payload, len); 614 615 vq_relchain(vq, req.idx, sizeof(struct pci_vtcon_control) + len); 616 vq_endchains(vq, 1); 617 } 618 619 620 static void 621 pci_vtcon_notify_tx(void *vsc, struct vqueue_info *vq) 622 { 623 struct pci_vtcon_softc *sc; 624 struct pci_vtcon_port *port; 625 struct iovec iov[1]; 626 struct vi_req req; 627 int n; 628 629 sc = vsc; 630 port = pci_vtcon_vq_to_port(sc, vq); 631 632 while (vq_has_descs(vq)) { 633 n = vq_getchain(vq, iov, 1, &req); 634 assert(n == 1); 635 if (port != NULL) 636 port->vsp_cb(port, port->vsp_arg, iov, 1); 637 638 /* 639 * Release this chain and handle more 640 */ 641 vq_relchain(vq, req.idx, 0); 642 } 643 vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ 644 } 645 646 static void 647 pci_vtcon_notify_rx(void *vsc, struct vqueue_info *vq) 648 { 649 struct pci_vtcon_softc *sc; 650 struct pci_vtcon_port *port; 651 652 sc = vsc; 653 port = pci_vtcon_vq_to_port(sc, vq); 654 655 if (!port->vsp_rx_ready) { 656 port->vsp_rx_ready = 1; 657 vq_kick_disable(vq); 658 } 659 } 660 661 /* 662 * Each console device has a "port" node which contains nodes for 663 * each port. Ports are numbered starting at 0. 664 */ 665 static int 666 pci_vtcon_legacy_config_port(nvlist_t *nvl, int port, char *opt) 667 { 668 char *name, *path; 669 char node_name[sizeof("XX")]; 670 nvlist_t *port_nvl; 671 672 name = strsep(&opt, "="); 673 path = opt; 674 if (path == NULL) { 675 EPRINTLN("vtcon: port %s requires a path", name); 676 return (-1); 677 } 678 if (port >= VTCON_MAXPORTS) { 679 EPRINTLN("vtcon: too many ports"); 680 return (-1); 681 } 682 snprintf(node_name, sizeof(node_name), "%d", port); 683 port_nvl = create_relative_config_node(nvl, node_name); 684 set_config_value_node(port_nvl, "name", name); 685 set_config_value_node(port_nvl, "path", path); 686 return (0); 687 } 688 689 static int 690 pci_vtcon_legacy_config(nvlist_t *nvl, const char *opts) 691 { 692 char *opt, *str, *tofree; 693 nvlist_t *ports_nvl; 694 int error, port; 695 696 ports_nvl = create_relative_config_node(nvl, "port"); 697 tofree = str = strdup(opts); 698 error = 0; 699 port = 0; 700 while ((opt = strsep(&str, ",")) != NULL) { 701 error = pci_vtcon_legacy_config_port(ports_nvl, port, opt); 702 if (error) 703 break; 704 port++; 705 } 706 free(tofree); 707 return (error); 708 } 709 710 static int 711 pci_vtcon_init(struct vmctx *ctx __unused, struct pci_devinst *pi, 712 nvlist_t *nvl) 713 { 714 struct pci_vtcon_softc *sc; 715 nvlist_t *ports_nvl; 716 int i; 717 718 sc = calloc(1, sizeof(struct pci_vtcon_softc)); 719 sc->vsc_config = calloc(1, sizeof(struct pci_vtcon_config)); 720 sc->vsc_config->max_nr_ports = VTCON_MAXPORTS; 721 sc->vsc_config->cols = 80; 722 sc->vsc_config->rows = 25; 723 724 pthread_mutex_init(&sc->vsc_mtx, NULL); 725 726 vi_softc_linkup(&sc->vsc_vs, &vtcon_vi_consts, sc, pi, sc->vsc_queues); 727 sc->vsc_vs.vs_mtx = &sc->vsc_mtx; 728 729 for (i = 0; i < VTCON_MAXQ; i++) { 730 sc->vsc_queues[i].vq_qsize = VTCON_RINGSZ; 731 sc->vsc_queues[i].vq_notify = i % 2 == 0 732 ? pci_vtcon_notify_rx 733 : pci_vtcon_notify_tx; 734 } 735 736 /* initialize config space */ 737 pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_CONSOLE); 738 pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 739 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM); 740 pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_CONSOLE); 741 pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR); 742 743 if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix())) 744 return (1); 745 vi_set_io_bar(&sc->vsc_vs, 0); 746 747 /* create control port */ 748 sc->vsc_control_port.vsp_sc = sc; 749 sc->vsc_control_port.vsp_txq = 2; 750 sc->vsc_control_port.vsp_rxq = 3; 751 sc->vsc_control_port.vsp_cb = pci_vtcon_control_tx; 752 sc->vsc_control_port.vsp_enabled = true; 753 754 ports_nvl = find_relative_config_node(nvl, "port"); 755 if (ports_nvl != NULL) { 756 const char *name; 757 void *cookie; 758 int type; 759 760 cookie = NULL; 761 while ((name = nvlist_next(ports_nvl, &type, &cookie)) != 762 NULL) { 763 if (type != NV_TYPE_NVLIST) 764 continue; 765 766 if (pci_vtcon_sock_add(sc, name, 767 nvlist_get_nvlist(ports_nvl, name)) < 0) { 768 EPRINTLN("cannot create port %s: %s", 769 name, strerror(errno)); 770 return (1); 771 } 772 } 773 } 774 775 return (0); 776 } 777 778 static const struct pci_devemu pci_de_vcon = { 779 .pe_emu = "virtio-console", 780 .pe_init = pci_vtcon_init, 781 .pe_barwrite = vi_pci_write, 782 .pe_barread = vi_pci_read, 783 .pe_legacy_config = pci_vtcon_legacy_config, 784 }; 785 PCI_EMUL_SET(pci_de_vcon); 786