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