Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_virtio_console.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <libgen.h> | #include <libgen.h> | ||||
#include <sysexits.h> | #include <sysexits.h> | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "config.h" | |||||
#include "debug.h" | #include "debug.h" | ||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
#include "virtio.h" | #include "virtio.h" | ||||
#include "mevent.h" | #include "mevent.h" | ||||
#include "sockstream.h" | #include "sockstream.h" | ||||
#define VTCON_RINGSZ 64 | #define VTCON_RINGSZ 64 | ||||
#define VTCON_MAXPORTS 16 | #define VTCON_MAXPORTS 16 | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
struct pci_vtcon_softc { | struct pci_vtcon_softc { | ||||
struct virtio_softc vsc_vs; | struct virtio_softc vsc_vs; | ||||
struct vqueue_info vsc_queues[VTCON_MAXQ]; | struct vqueue_info vsc_queues[VTCON_MAXQ]; | ||||
pthread_mutex_t vsc_mtx; | pthread_mutex_t vsc_mtx; | ||||
uint64_t vsc_cfg; | uint64_t vsc_cfg; | ||||
uint64_t vsc_features; | uint64_t vsc_features; | ||||
char * vsc_rootdir; | char * vsc_rootdir; | ||||
int vsc_kq; | int vsc_kq; | ||||
int vsc_nports; | |||||
bool vsc_ready; | bool vsc_ready; | ||||
struct pci_vtcon_port vsc_control_port; | struct pci_vtcon_port vsc_control_port; | ||||
struct pci_vtcon_port vsc_ports[VTCON_MAXPORTS]; | struct pci_vtcon_port vsc_ports[VTCON_MAXPORTS]; | ||||
struct pci_vtcon_config *vsc_config; | struct pci_vtcon_config *vsc_config; | ||||
}; | }; | ||||
struct pci_vtcon_config { | struct pci_vtcon_config { | ||||
uint16_t cols; | uint16_t cols; | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
int qnum; | int qnum; | ||||
qnum = tx_queue ? port->vsp_txq : port->vsp_rxq; | qnum = tx_queue ? port->vsp_txq : port->vsp_rxq; | ||||
return (&port->vsp_sc->vsc_queues[qnum]); | return (&port->vsp_sc->vsc_queues[qnum]); | ||||
} | } | ||||
static struct pci_vtcon_port * | static struct pci_vtcon_port * | ||||
pci_vtcon_port_add(struct pci_vtcon_softc *sc, const char *name, | pci_vtcon_port_add(struct pci_vtcon_softc *sc, int port_id, const char *name, | ||||
pci_vtcon_cb_t *cb, void *arg) | pci_vtcon_cb_t *cb, void *arg) | ||||
{ | { | ||||
struct pci_vtcon_port *port; | struct pci_vtcon_port *port; | ||||
if (sc->vsc_nports == VTCON_MAXPORTS) { | port = &sc->vsc_ports[port_id]; | ||||
if (port->vsp_enabled) { | |||||
errno = EBUSY; | errno = EBUSY; | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
port->vsp_id = port_id; | |||||
port = &sc->vsc_ports[sc->vsc_nports++]; | |||||
port->vsp_id = sc->vsc_nports - 1; | |||||
port->vsp_sc = sc; | port->vsp_sc = sc; | ||||
port->vsp_name = name; | port->vsp_name = name; | ||||
port->vsp_cb = cb; | port->vsp_cb = cb; | ||||
port->vsp_arg = arg; | port->vsp_arg = arg; | ||||
if (port->vsp_id == 0) { | if (port->vsp_id == 0) { | ||||
/* port0 */ | /* port0 */ | ||||
port->vsp_txq = 0; | port->vsp_txq = 0; | ||||
port->vsp_rxq = 1; | port->vsp_rxq = 1; | ||||
} else { | } else { | ||||
port->vsp_txq = sc->vsc_nports * 2; | port->vsp_txq = (port_id + 1) * 2; | ||||
port->vsp_rxq = port->vsp_txq + 1; | port->vsp_rxq = port->vsp_txq + 1; | ||||
} | } | ||||
port->vsp_enabled = true; | port->vsp_enabled = true; | ||||
return (port); | return (port); | ||||
} | } | ||||
static int | static int | ||||
pci_vtcon_sock_add(struct pci_vtcon_softc *sc, const char *name, | pci_vtcon_sock_add(struct pci_vtcon_softc *sc, const char *port_name, | ||||
const char *path) | const nvlist_t *nvl) | ||||
{ | { | ||||
struct pci_vtcon_sock *sock; | struct pci_vtcon_sock *sock; | ||||
struct sockaddr_un sun; | struct sockaddr_un sun; | ||||
char *pathcopy; | const char *name, *path; | ||||
char *cp, *pathcopy; | |||||
long port; | |||||
int s = -1, fd = -1, error = 0; | int s = -1, fd = -1, error = 0; | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
#endif | #endif | ||||
port = strtol(port_name, &cp, 0); | |||||
if (*cp != '\0' || port < 0 || port >= VTCON_MAXPORTS) { | |||||
EPRINTLN("vtcon: Invalid port %s", port_name); | |||||
error = -1; | |||||
goto out; | |||||
} | |||||
path = get_config_value_node(nvl, "path"); | |||||
if (path == NULL) { | |||||
EPRINTLN("vtcon: required path missing for port %ld", port); | |||||
error = -1; | |||||
goto out; | |||||
} | |||||
sock = calloc(1, sizeof(struct pci_vtcon_sock)); | sock = calloc(1, sizeof(struct pci_vtcon_sock)); | ||||
if (sock == NULL) { | if (sock == NULL) { | ||||
error = -1; | error = -1; | ||||
goto out; | goto out; | ||||
} | } | ||||
s = socket(AF_UNIX, SOCK_STREAM, 0); | s = socket(AF_UNIX, SOCK_STREAM, 0); | ||||
if (s < 0) { | if (s < 0) { | ||||
Show All 36 Lines | #endif | ||||
} | } | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE); | cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE); | ||||
if (caph_rights_limit(s, &rights) == -1) | if (caph_rights_limit(s, &rights) == -1) | ||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | errx(EX_OSERR, "Unable to apply rights for sandbox"); | ||||
#endif | #endif | ||||
sock->vss_port = pci_vtcon_port_add(sc, name, pci_vtcon_sock_tx, sock); | name = get_config_value_node(nvl, "name"); | ||||
if (name == NULL) { | |||||
EPRINTLN("vtcon: required name missing for port %ld", port); | |||||
error = -1; | |||||
goto out; | |||||
} | |||||
sock->vss_port = pci_vtcon_port_add(sc, port, name, pci_vtcon_sock_tx, sock); | |||||
if (sock->vss_port == NULL) { | if (sock->vss_port == NULL) { | ||||
error = -1; | error = -1; | ||||
goto out; | goto out; | ||||
} | } | ||||
sock->vss_open = false; | sock->vss_open = false; | ||||
sock->vss_conn_fd = -1; | sock->vss_conn_fd = -1; | ||||
sock->vss_server_fd = s; | sock->vss_server_fd = s; | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | for (i = 0; i < VTCON_MAXPORTS; i++) { | ||||
pci_vtcon_announce_port(tmp); | pci_vtcon_announce_port(tmp); | ||||
if (tmp->vsp_open) | if (tmp->vsp_open) | ||||
pci_vtcon_open_port(tmp, true); | pci_vtcon_open_port(tmp, true); | ||||
} | } | ||||
break; | break; | ||||
case VTCON_PORT_READY: | case VTCON_PORT_READY: | ||||
if (ctrl->id >= sc->vsc_nports) { | tmp = &sc->vsc_ports[ctrl->id]; | ||||
if (ctrl->id >= VTCON_MAXPORTS || !tmp->vsp_enabled) { | |||||
WPRINTF(("VTCON_PORT_READY event for unknown port %d", | WPRINTF(("VTCON_PORT_READY event for unknown port %d", | ||||
ctrl->id)); | ctrl->id)); | ||||
return; | return; | ||||
} | } | ||||
tmp = &sc->vsc_ports[ctrl->id]; | |||||
if (tmp->vsp_console) { | if (tmp->vsp_console) { | ||||
resp.event = VTCON_CONSOLE_PORT; | resp.event = VTCON_CONSOLE_PORT; | ||||
resp.id = ctrl->id; | resp.id = ctrl->id; | ||||
resp.value = 1; | resp.value = 1; | ||||
pci_vtcon_control_send(sc, &resp, NULL, 0); | pci_vtcon_control_send(sc, &resp, NULL, 0); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | pci_vtcon_notify_rx(void *vsc, struct vqueue_info *vq) | ||||
port = pci_vtcon_vq_to_port(sc, vq); | port = pci_vtcon_vq_to_port(sc, vq); | ||||
if (!port->vsp_rx_ready) { | if (!port->vsp_rx_ready) { | ||||
port->vsp_rx_ready = 1; | port->vsp_rx_ready = 1; | ||||
vq_kick_disable(vq); | vq_kick_disable(vq); | ||||
} | } | ||||
} | } | ||||
/* | |||||
* Each console device has a "port" node which contains nodes for | |||||
* each port. Ports are numbered starting at 0. | |||||
*/ | |||||
static int | static int | ||||
pci_vtcon_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | pci_vtcon_legacy_config_port(nvlist_t *nvl, int port, char *opt) | ||||
{ | { | ||||
char *name, *path; | |||||
char node_name[sizeof("XX")]; | |||||
nvlist_t *port_nvl; | |||||
name = strsep(&opt, "="); | |||||
path = opt; | |||||
if (path == NULL) { | |||||
EPRINTLN("vtcon: port %s requires a path", name); | |||||
return (-1); | |||||
} | |||||
if (port >= VTCON_MAXPORTS) { | |||||
EPRINTLN("vtcon: too many ports"); | |||||
return (-1); | |||||
} | |||||
snprintf(node_name, sizeof(node_name), "%d", port); | |||||
port_nvl = create_relative_config_node(nvl, node_name); | |||||
set_config_value_node(port_nvl, "name", name); | |||||
set_config_value_node(port_nvl, "path", path); | |||||
return (0); | |||||
} | |||||
static int | |||||
pci_vtcon_legacy_config(nvlist_t *nvl, const char *opts) | |||||
{ | |||||
char *opt, *str, *tofree; | |||||
nvlist_t *ports_nvl; | |||||
int error, port; | |||||
ports_nvl = create_relative_config_node(nvl, "port"); | |||||
tofree = str = strdup(opts); | |||||
error = 0; | |||||
port = 0; | |||||
while ((opt = strsep(&str, ",")) != NULL) { | |||||
error = pci_vtcon_legacy_config_port(ports_nvl, port, opt); | |||||
if (error) | |||||
break; | |||||
port++; | |||||
} | |||||
free(tofree); | |||||
return (error); | |||||
} | |||||
static int | |||||
pci_vtcon_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | |||||
{ | |||||
struct pci_vtcon_softc *sc; | struct pci_vtcon_softc *sc; | ||||
char *portname = NULL; | nvlist_t *ports_nvl; | ||||
char *portpath = NULL; | |||||
char *opt; | |||||
int i; | int i; | ||||
sc = calloc(1, sizeof(struct pci_vtcon_softc)); | sc = calloc(1, sizeof(struct pci_vtcon_softc)); | ||||
sc->vsc_config = calloc(1, sizeof(struct pci_vtcon_config)); | sc->vsc_config = calloc(1, sizeof(struct pci_vtcon_config)); | ||||
sc->vsc_config->max_nr_ports = VTCON_MAXPORTS; | sc->vsc_config->max_nr_ports = VTCON_MAXPORTS; | ||||
sc->vsc_config->cols = 80; | sc->vsc_config->cols = 80; | ||||
sc->vsc_config->rows = 25; | sc->vsc_config->rows = 25; | ||||
vi_softc_linkup(&sc->vsc_vs, &vtcon_vi_consts, sc, pi, sc->vsc_queues); | vi_softc_linkup(&sc->vsc_vs, &vtcon_vi_consts, sc, pi, sc->vsc_queues); | ||||
Show All 19 Lines | pci_vtcon_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | ||||
/* create control port */ | /* create control port */ | ||||
sc->vsc_control_port.vsp_sc = sc; | sc->vsc_control_port.vsp_sc = sc; | ||||
sc->vsc_control_port.vsp_txq = 2; | sc->vsc_control_port.vsp_txq = 2; | ||||
sc->vsc_control_port.vsp_rxq = 3; | sc->vsc_control_port.vsp_rxq = 3; | ||||
sc->vsc_control_port.vsp_cb = pci_vtcon_control_tx; | sc->vsc_control_port.vsp_cb = pci_vtcon_control_tx; | ||||
sc->vsc_control_port.vsp_enabled = true; | sc->vsc_control_port.vsp_enabled = true; | ||||
while ((opt = strsep(&opts, ",")) != NULL) { | ports_nvl = find_relative_config_node(nvl, "port"); | ||||
portname = strsep(&opt, "="); | if (ports_nvl != NULL) { | ||||
portpath = opt; | const char *name; | ||||
void *cookie; | |||||
int type; | |||||
/* create port */ | cookie = NULL; | ||||
if (pci_vtcon_sock_add(sc, portname, portpath) < 0) { | while ((name = nvlist_next(ports_nvl, &type, &cookie)) != | ||||
NULL) { | |||||
if (type != NV_TYPE_NVLIST) | |||||
continue; | |||||
if (pci_vtcon_sock_add(sc, name, | |||||
nvlist_get_nvlist(ports_nvl, name)) < 0) { | |||||
EPRINTLN("cannot create port %s: %s", | EPRINTLN("cannot create port %s: %s", | ||||
portname, strerror(errno)); | name, strerror(errno)); | ||||
return (1); | return (1); | ||||
} | |||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
struct pci_devemu pci_de_vcon = { | struct pci_devemu pci_de_vcon = { | ||||
.pe_emu = "virtio-console", | .pe_emu = "virtio-console", | ||||
.pe_init = pci_vtcon_init, | .pe_init = pci_vtcon_init, | ||||
.pe_barwrite = vi_pci_write, | .pe_barwrite = vi_pci_write, | ||||
.pe_barread = vi_pci_read | .pe_barread = vi_pci_read | ||||
}; | }; | ||||
PCI_EMUL_SET(pci_de_vcon); | PCI_EMUL_SET(pci_de_vcon); |