xref: /freebsd/usr.sbin/bhyve/pci_virtio_console.c (revision b34a4edefb0a40ced9b17ffd640f52fe55edc1f5)
113ee8ddeSJakub Wojciech Klama /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3ce80faa4SMarcelo Araujo  *
413ee8ddeSJakub Wojciech Klama  * Copyright (c) 2016 iXsystems Inc.
513ee8ddeSJakub Wojciech Klama  * All rights reserved.
613ee8ddeSJakub Wojciech Klama  *
713ee8ddeSJakub Wojciech Klama  * This software was developed by Jakub Klama <jceel@FreeBSD.org>
813ee8ddeSJakub Wojciech Klama  * under sponsorship from iXsystems Inc.
913ee8ddeSJakub Wojciech Klama  *
1013ee8ddeSJakub Wojciech Klama  * Redistribution and use in source and binary forms, with or without
1113ee8ddeSJakub Wojciech Klama  * modification, are permitted provided that the following conditions
1213ee8ddeSJakub Wojciech Klama  * are met:
1313ee8ddeSJakub Wojciech Klama  * 1. Redistributions of source code must retain the above copyright
1413ee8ddeSJakub Wojciech Klama  *    notice, this list of conditions and the following disclaimer
1513ee8ddeSJakub Wojciech Klama  *    in this position and unchanged.
1613ee8ddeSJakub Wojciech Klama  * 2. Redistributions in binary form must reproduce the above copyright
1713ee8ddeSJakub Wojciech Klama  *    notice, this list of conditions and the following disclaimer in the
1813ee8ddeSJakub Wojciech Klama  *    documentation and/or other materials provided with the distribution.
1913ee8ddeSJakub Wojciech Klama  *
2013ee8ddeSJakub Wojciech Klama  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2113ee8ddeSJakub Wojciech Klama  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2213ee8ddeSJakub Wojciech Klama  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2313ee8ddeSJakub Wojciech Klama  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2413ee8ddeSJakub Wojciech Klama  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2513ee8ddeSJakub Wojciech Klama  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2613ee8ddeSJakub Wojciech Klama  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2713ee8ddeSJakub Wojciech Klama  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2813ee8ddeSJakub Wojciech Klama  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2913ee8ddeSJakub Wojciech Klama  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3013ee8ddeSJakub Wojciech Klama  * SUCH DAMAGE.
3113ee8ddeSJakub Wojciech Klama  */
3213ee8ddeSJakub Wojciech Klama 
3313ee8ddeSJakub Wojciech Klama #include <sys/param.h>
3400ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM
3500ef17beSBartek Rutkowski #include <sys/capsicum.h>
3600ef17beSBartek Rutkowski #endif
3713ee8ddeSJakub Wojciech Klama #include <sys/linker_set.h>
3813ee8ddeSJakub Wojciech Klama #include <sys/uio.h>
3913ee8ddeSJakub Wojciech Klama #include <sys/types.h>
4013ee8ddeSJakub Wojciech Klama #include <sys/socket.h>
4113ee8ddeSJakub Wojciech Klama #include <sys/un.h>
4213ee8ddeSJakub Wojciech Klama 
43abfa3c39SMarcelo Araujo #ifndef WITHOUT_CAPSICUM
44abfa3c39SMarcelo Araujo #include <capsicum_helpers.h>
45abfa3c39SMarcelo Araujo #endif
4600ef17beSBartek Rutkowski #include <err.h>
4713ee8ddeSJakub Wojciech Klama #include <errno.h>
4813ee8ddeSJakub Wojciech Klama #include <fcntl.h>
4913ee8ddeSJakub Wojciech Klama #include <stdio.h>
5013ee8ddeSJakub Wojciech Klama #include <stdlib.h>
5113ee8ddeSJakub Wojciech Klama #include <stdbool.h>
5213ee8ddeSJakub Wojciech Klama #include <string.h>
5313ee8ddeSJakub Wojciech Klama #include <unistd.h>
5413ee8ddeSJakub Wojciech Klama #include <assert.h>
5513ee8ddeSJakub Wojciech Klama #include <pthread.h>
5613ee8ddeSJakub Wojciech Klama #include <libgen.h>
5700ef17beSBartek Rutkowski #include <sysexits.h>
5813ee8ddeSJakub Wojciech Klama 
5913ee8ddeSJakub Wojciech Klama #include "bhyverun.h"
60621b5090SJohn Baldwin #include "config.h"
61332eff95SVincenzo Maffione #include "debug.h"
6213ee8ddeSJakub Wojciech Klama #include "pci_emul.h"
6313ee8ddeSJakub Wojciech Klama #include "virtio.h"
6413ee8ddeSJakub Wojciech Klama #include "mevent.h"
65d286418eSJakub Wojciech Klama #include "sockstream.h"
6613ee8ddeSJakub Wojciech Klama 
6713ee8ddeSJakub Wojciech Klama #define	VTCON_RINGSZ	64
6813ee8ddeSJakub Wojciech Klama #define	VTCON_MAXPORTS	16
6913ee8ddeSJakub Wojciech Klama #define	VTCON_MAXQ	(VTCON_MAXPORTS * 2 + 2)
7013ee8ddeSJakub Wojciech Klama 
7113ee8ddeSJakub Wojciech Klama #define	VTCON_DEVICE_READY	0
7213ee8ddeSJakub Wojciech Klama #define	VTCON_DEVICE_ADD	1
7313ee8ddeSJakub Wojciech Klama #define	VTCON_DEVICE_REMOVE	2
7413ee8ddeSJakub Wojciech Klama #define	VTCON_PORT_READY	3
7513ee8ddeSJakub Wojciech Klama #define	VTCON_CONSOLE_PORT	4
7613ee8ddeSJakub Wojciech Klama #define	VTCON_CONSOLE_RESIZE	5
7713ee8ddeSJakub Wojciech Klama #define	VTCON_PORT_OPEN		6
7813ee8ddeSJakub Wojciech Klama #define	VTCON_PORT_NAME		7
7913ee8ddeSJakub Wojciech Klama 
8013ee8ddeSJakub Wojciech Klama #define	VTCON_F_SIZE		0
8113ee8ddeSJakub Wojciech Klama #define	VTCON_F_MULTIPORT	1
8213ee8ddeSJakub Wojciech Klama #define	VTCON_F_EMERG_WRITE	2
8313ee8ddeSJakub Wojciech Klama #define	VTCON_S_HOSTCAPS	\
8413ee8ddeSJakub Wojciech Klama     (VTCON_F_SIZE | VTCON_F_MULTIPORT | VTCON_F_EMERG_WRITE)
8513ee8ddeSJakub Wojciech Klama 
8613ee8ddeSJakub Wojciech Klama static int pci_vtcon_debug;
87332eff95SVincenzo Maffione #define DPRINTF(params) if (pci_vtcon_debug) PRINTLN params
88332eff95SVincenzo Maffione #define WPRINTF(params) PRINTLN params
8913ee8ddeSJakub Wojciech Klama 
9013ee8ddeSJakub Wojciech Klama struct pci_vtcon_softc;
9113ee8ddeSJakub Wojciech Klama struct pci_vtcon_port;
9213ee8ddeSJakub Wojciech Klama struct pci_vtcon_config;
9313ee8ddeSJakub Wojciech Klama typedef void (pci_vtcon_cb_t)(struct pci_vtcon_port *, void *, struct iovec *,
9413ee8ddeSJakub Wojciech Klama     int);
9513ee8ddeSJakub Wojciech Klama 
9613ee8ddeSJakub Wojciech Klama struct pci_vtcon_port {
9713ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc * vsp_sc;
9813ee8ddeSJakub Wojciech Klama 	int                      vsp_id;
9913ee8ddeSJakub Wojciech Klama 	const char *             vsp_name;
10013ee8ddeSJakub Wojciech Klama 	bool                     vsp_enabled;
10113ee8ddeSJakub Wojciech Klama 	bool                     vsp_console;
10213ee8ddeSJakub Wojciech Klama 	bool                     vsp_rx_ready;
103962094d5SJakub Wojciech Klama 	bool                     vsp_open;
10413ee8ddeSJakub Wojciech Klama 	int                      vsp_rxq;
10513ee8ddeSJakub Wojciech Klama 	int                      vsp_txq;
10613ee8ddeSJakub Wojciech Klama 	void *                   vsp_arg;
10713ee8ddeSJakub Wojciech Klama 	pci_vtcon_cb_t *         vsp_cb;
10813ee8ddeSJakub Wojciech Klama };
10913ee8ddeSJakub Wojciech Klama 
11013ee8ddeSJakub Wojciech Klama struct pci_vtcon_sock
11113ee8ddeSJakub Wojciech Klama {
11213ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_port *  vss_port;
11313ee8ddeSJakub Wojciech Klama 	const char *             vss_path;
11413ee8ddeSJakub Wojciech Klama 	struct mevent *          vss_server_evp;
11513ee8ddeSJakub Wojciech Klama 	struct mevent *          vss_conn_evp;
11613ee8ddeSJakub Wojciech Klama 	int                      vss_server_fd;
11713ee8ddeSJakub Wojciech Klama 	int                      vss_conn_fd;
11813ee8ddeSJakub Wojciech Klama 	bool                     vss_open;
11913ee8ddeSJakub Wojciech Klama };
12013ee8ddeSJakub Wojciech Klama 
12113ee8ddeSJakub Wojciech Klama struct pci_vtcon_softc {
12213ee8ddeSJakub Wojciech Klama 	struct virtio_softc      vsc_vs;
12313ee8ddeSJakub Wojciech Klama 	struct vqueue_info       vsc_queues[VTCON_MAXQ];
12413ee8ddeSJakub Wojciech Klama 	pthread_mutex_t          vsc_mtx;
12513ee8ddeSJakub Wojciech Klama 	uint64_t                 vsc_cfg;
12613ee8ddeSJakub Wojciech Klama 	uint64_t                 vsc_features;
12713ee8ddeSJakub Wojciech Klama 	char *                   vsc_rootdir;
12813ee8ddeSJakub Wojciech Klama 	int                      vsc_kq;
129962094d5SJakub Wojciech Klama 	bool                     vsc_ready;
13013ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_port    vsc_control_port;
13113ee8ddeSJakub Wojciech Klama  	struct pci_vtcon_port    vsc_ports[VTCON_MAXPORTS];
13213ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_config *vsc_config;
13313ee8ddeSJakub Wojciech Klama };
13413ee8ddeSJakub Wojciech Klama 
13513ee8ddeSJakub Wojciech Klama struct pci_vtcon_config {
13613ee8ddeSJakub Wojciech Klama 	uint16_t cols;
13713ee8ddeSJakub Wojciech Klama 	uint16_t rows;
13813ee8ddeSJakub Wojciech Klama 	uint32_t max_nr_ports;
13913ee8ddeSJakub Wojciech Klama 	uint32_t emerg_wr;
14013ee8ddeSJakub Wojciech Klama } __attribute__((packed));
14113ee8ddeSJakub Wojciech Klama 
14213ee8ddeSJakub Wojciech Klama struct pci_vtcon_control {
14313ee8ddeSJakub Wojciech Klama 	uint32_t id;
14413ee8ddeSJakub Wojciech Klama 	uint16_t event;
14513ee8ddeSJakub Wojciech Klama 	uint16_t value;
14613ee8ddeSJakub Wojciech Klama } __attribute__((packed));
14713ee8ddeSJakub Wojciech Klama 
14813ee8ddeSJakub Wojciech Klama struct pci_vtcon_console_resize {
14913ee8ddeSJakub Wojciech Klama 	uint16_t cols;
15013ee8ddeSJakub Wojciech Klama 	uint16_t rows;
15113ee8ddeSJakub Wojciech Klama } __attribute__((packed));
15213ee8ddeSJakub Wojciech Klama 
15313ee8ddeSJakub Wojciech Klama static void pci_vtcon_reset(void *);
15413ee8ddeSJakub Wojciech Klama static void pci_vtcon_notify_rx(void *, struct vqueue_info *);
15513ee8ddeSJakub Wojciech Klama static void pci_vtcon_notify_tx(void *, struct vqueue_info *);
15613ee8ddeSJakub Wojciech Klama static int pci_vtcon_cfgread(void *, int, int, uint32_t *);
15713ee8ddeSJakub Wojciech Klama static int pci_vtcon_cfgwrite(void *, int, int, uint32_t);
15813ee8ddeSJakub Wojciech Klama static void pci_vtcon_neg_features(void *, uint64_t);
15913ee8ddeSJakub Wojciech Klama static void pci_vtcon_sock_accept(int, enum ev_type,  void *);
16013ee8ddeSJakub Wojciech Klama static void pci_vtcon_sock_rx(int, enum ev_type, void *);
16113ee8ddeSJakub Wojciech Klama static void pci_vtcon_sock_tx(struct pci_vtcon_port *, void *, struct iovec *,
16213ee8ddeSJakub Wojciech Klama     int);
16313ee8ddeSJakub Wojciech Klama static void pci_vtcon_control_send(struct pci_vtcon_softc *,
16413ee8ddeSJakub Wojciech Klama     struct pci_vtcon_control *, const void *, size_t);
16513ee8ddeSJakub Wojciech Klama static void pci_vtcon_announce_port(struct pci_vtcon_port *);
16613ee8ddeSJakub Wojciech Klama static void pci_vtcon_open_port(struct pci_vtcon_port *, bool);
16713ee8ddeSJakub Wojciech Klama 
16813ee8ddeSJakub Wojciech Klama static struct virtio_consts vtcon_vi_consts = {
1696cb26162SMark Johnston 	.vc_name =	"vtcon",
1706cb26162SMark Johnston 	.vc_nvq =	VTCON_MAXQ,
1716cb26162SMark Johnston 	.vc_cfgsize =	sizeof(struct pci_vtcon_config),
1726cb26162SMark Johnston 	.vc_reset =	pci_vtcon_reset,
1736cb26162SMark Johnston 	.vc_cfgread =	pci_vtcon_cfgread,
1746cb26162SMark Johnston 	.vc_cfgwrite =	pci_vtcon_cfgwrite,
1756cb26162SMark Johnston 	.vc_apply_features = pci_vtcon_neg_features,
1766cb26162SMark Johnston 	.vc_hv_caps =	VTCON_S_HOSTCAPS,
17713ee8ddeSJakub Wojciech Klama };
17813ee8ddeSJakub Wojciech Klama 
17913ee8ddeSJakub Wojciech Klama static void
pci_vtcon_reset(void * vsc)18013ee8ddeSJakub Wojciech Klama pci_vtcon_reset(void *vsc)
18113ee8ddeSJakub Wojciech Klama {
18213ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc *sc;
18313ee8ddeSJakub Wojciech Klama 
18413ee8ddeSJakub Wojciech Klama 	sc = vsc;
18513ee8ddeSJakub Wojciech Klama 
186332eff95SVincenzo Maffione 	DPRINTF(("vtcon: device reset requested!"));
18713ee8ddeSJakub Wojciech Klama 	vi_reset_dev(&sc->vsc_vs);
18813ee8ddeSJakub Wojciech Klama }
18913ee8ddeSJakub Wojciech Klama 
19013ee8ddeSJakub Wojciech Klama static void
pci_vtcon_neg_features(void * vsc,uint64_t negotiated_features)19113ee8ddeSJakub Wojciech Klama pci_vtcon_neg_features(void *vsc, uint64_t negotiated_features)
19213ee8ddeSJakub Wojciech Klama {
19313ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc *sc = vsc;
19413ee8ddeSJakub Wojciech Klama 
19513ee8ddeSJakub Wojciech Klama 	sc->vsc_features = negotiated_features;
19613ee8ddeSJakub Wojciech Klama }
19713ee8ddeSJakub Wojciech Klama 
19813ee8ddeSJakub Wojciech Klama static int
pci_vtcon_cfgread(void * vsc,int offset,int size,uint32_t * retval)19913ee8ddeSJakub Wojciech Klama pci_vtcon_cfgread(void *vsc, int offset, int size, uint32_t *retval)
20013ee8ddeSJakub Wojciech Klama {
20113ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc *sc = vsc;
20213ee8ddeSJakub Wojciech Klama 	void *ptr;
20313ee8ddeSJakub Wojciech Klama 
20413ee8ddeSJakub Wojciech Klama 	ptr = (uint8_t *)sc->vsc_config + offset;
20513ee8ddeSJakub Wojciech Klama 	memcpy(retval, ptr, size);
20613ee8ddeSJakub Wojciech Klama 	return (0);
20713ee8ddeSJakub Wojciech Klama }
20813ee8ddeSJakub Wojciech Klama 
20913ee8ddeSJakub Wojciech Klama static int
pci_vtcon_cfgwrite(void * vsc __unused,int offset __unused,int size __unused,uint32_t val __unused)21098d920d9SMark Johnston pci_vtcon_cfgwrite(void *vsc __unused, int offset __unused, int size __unused,
21198d920d9SMark Johnston     uint32_t val __unused)
21213ee8ddeSJakub Wojciech Klama {
21313ee8ddeSJakub Wojciech Klama 	return (0);
21413ee8ddeSJakub Wojciech Klama }
21513ee8ddeSJakub Wojciech Klama 
21613ee8ddeSJakub Wojciech Klama static inline struct pci_vtcon_port *
pci_vtcon_vq_to_port(struct pci_vtcon_softc * sc,struct vqueue_info * vq)21713ee8ddeSJakub Wojciech Klama pci_vtcon_vq_to_port(struct pci_vtcon_softc *sc, struct vqueue_info *vq)
21813ee8ddeSJakub Wojciech Klama {
21913ee8ddeSJakub Wojciech Klama 	uint16_t num = vq->vq_num;
22013ee8ddeSJakub Wojciech Klama 
22113ee8ddeSJakub Wojciech Klama 	if (num == 0 || num == 1)
22213ee8ddeSJakub Wojciech Klama 		return (&sc->vsc_ports[0]);
22313ee8ddeSJakub Wojciech Klama 
22413ee8ddeSJakub Wojciech Klama 	if (num == 2 || num == 3)
22513ee8ddeSJakub Wojciech Klama 		return (&sc->vsc_control_port);
22613ee8ddeSJakub Wojciech Klama 
22713ee8ddeSJakub Wojciech Klama 	return (&sc->vsc_ports[(num / 2) - 1]);
22813ee8ddeSJakub Wojciech Klama }
22913ee8ddeSJakub Wojciech Klama 
23013ee8ddeSJakub Wojciech Klama static inline struct vqueue_info *
pci_vtcon_port_to_vq(struct pci_vtcon_port * port,bool tx_queue)23113ee8ddeSJakub Wojciech Klama pci_vtcon_port_to_vq(struct pci_vtcon_port *port, bool tx_queue)
23213ee8ddeSJakub Wojciech Klama {
23313ee8ddeSJakub Wojciech Klama 	int qnum;
23413ee8ddeSJakub Wojciech Klama 
23513ee8ddeSJakub Wojciech Klama 	qnum = tx_queue ? port->vsp_txq : port->vsp_rxq;
23613ee8ddeSJakub Wojciech Klama 	return (&port->vsp_sc->vsc_queues[qnum]);
23713ee8ddeSJakub Wojciech Klama }
23813ee8ddeSJakub Wojciech Klama 
23913ee8ddeSJakub Wojciech Klama static struct pci_vtcon_port *
pci_vtcon_port_add(struct pci_vtcon_softc * sc,int port_id,const char * name,pci_vtcon_cb_t * cb,void * arg)240621b5090SJohn Baldwin pci_vtcon_port_add(struct pci_vtcon_softc *sc, int port_id, const char *name,
24113ee8ddeSJakub Wojciech Klama     pci_vtcon_cb_t *cb, void *arg)
24213ee8ddeSJakub Wojciech Klama {
24313ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_port *port;
24413ee8ddeSJakub Wojciech Klama 
245621b5090SJohn Baldwin 	port = &sc->vsc_ports[port_id];
246621b5090SJohn Baldwin 	if (port->vsp_enabled) {
24713ee8ddeSJakub Wojciech Klama 		errno = EBUSY;
24813ee8ddeSJakub Wojciech Klama 		return (NULL);
24913ee8ddeSJakub Wojciech Klama 	}
250621b5090SJohn Baldwin 	port->vsp_id = port_id;
25113ee8ddeSJakub Wojciech Klama 	port->vsp_sc = sc;
25213ee8ddeSJakub Wojciech Klama 	port->vsp_name = name;
25313ee8ddeSJakub Wojciech Klama 	port->vsp_cb = cb;
25413ee8ddeSJakub Wojciech Klama 	port->vsp_arg = arg;
25513ee8ddeSJakub Wojciech Klama 
25613ee8ddeSJakub Wojciech Klama 	if (port->vsp_id == 0) {
25713ee8ddeSJakub Wojciech Klama 		/* port0 */
25813ee8ddeSJakub Wojciech Klama 		port->vsp_txq = 0;
25913ee8ddeSJakub Wojciech Klama 		port->vsp_rxq = 1;
26013ee8ddeSJakub Wojciech Klama 	} else {
261621b5090SJohn Baldwin 		port->vsp_txq = (port_id + 1) * 2;
26213ee8ddeSJakub Wojciech Klama 		port->vsp_rxq = port->vsp_txq + 1;
26313ee8ddeSJakub Wojciech Klama 	}
26413ee8ddeSJakub Wojciech Klama 
26513ee8ddeSJakub Wojciech Klama 	port->vsp_enabled = true;
26613ee8ddeSJakub Wojciech Klama 	return (port);
26713ee8ddeSJakub Wojciech Klama }
26813ee8ddeSJakub Wojciech Klama 
26913ee8ddeSJakub Wojciech Klama static int
pci_vtcon_sock_add(struct pci_vtcon_softc * sc,const char * port_name,const nvlist_t * nvl)270621b5090SJohn Baldwin pci_vtcon_sock_add(struct pci_vtcon_softc *sc, const char *port_name,
271621b5090SJohn Baldwin     const nvlist_t *nvl)
27213ee8ddeSJakub Wojciech Klama {
273bc928800SJohn Baldwin 	struct pci_vtcon_sock *sock = NULL;
27413ee8ddeSJakub Wojciech Klama 	struct sockaddr_un sun;
275621b5090SJohn Baldwin 	const char *name, *path;
276621b5090SJohn Baldwin 	char *cp, *pathcopy;
277621b5090SJohn Baldwin 	long port;
27813ee8ddeSJakub Wojciech Klama 	int s = -1, fd = -1, error = 0;
27900ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM
28000ef17beSBartek Rutkowski 	cap_rights_t rights;
28100ef17beSBartek Rutkowski #endif
28213ee8ddeSJakub Wojciech Klama 
283621b5090SJohn Baldwin 	port = strtol(port_name, &cp, 0);
284621b5090SJohn Baldwin 	if (*cp != '\0' || port < 0 || port >= VTCON_MAXPORTS) {
285621b5090SJohn Baldwin 		EPRINTLN("vtcon: Invalid port %s", port_name);
286621b5090SJohn Baldwin 		error = -1;
287621b5090SJohn Baldwin 		goto out;
288621b5090SJohn Baldwin 	}
289621b5090SJohn Baldwin 
290621b5090SJohn Baldwin 	path = get_config_value_node(nvl, "path");
291621b5090SJohn Baldwin 	if (path == NULL) {
292621b5090SJohn Baldwin 		EPRINTLN("vtcon: required path missing for port %ld", port);
293621b5090SJohn Baldwin 		error = -1;
294621b5090SJohn Baldwin 		goto out;
295621b5090SJohn Baldwin 	}
296621b5090SJohn Baldwin 
29713ee8ddeSJakub Wojciech Klama 	sock = calloc(1, sizeof(struct pci_vtcon_sock));
29813ee8ddeSJakub Wojciech Klama 	if (sock == NULL) {
29913ee8ddeSJakub Wojciech Klama 		error = -1;
30013ee8ddeSJakub Wojciech Klama 		goto out;
30113ee8ddeSJakub Wojciech Klama 	}
30213ee8ddeSJakub Wojciech Klama 
30313ee8ddeSJakub Wojciech Klama 	s = socket(AF_UNIX, SOCK_STREAM, 0);
30413ee8ddeSJakub Wojciech Klama 	if (s < 0) {
30513ee8ddeSJakub Wojciech Klama 		error = -1;
30613ee8ddeSJakub Wojciech Klama 		goto out;
30713ee8ddeSJakub Wojciech Klama 	}
30813ee8ddeSJakub Wojciech Klama 
309ecc28863SEd Schouten 	pathcopy = strdup(path);
310ecc28863SEd Schouten 	if (pathcopy == NULL) {
311ecc28863SEd Schouten 		error = -1;
312ecc28863SEd Schouten 		goto out;
313ecc28863SEd Schouten 	}
314ecc28863SEd Schouten 
315ecc28863SEd Schouten 	fd = open(dirname(pathcopy), O_RDONLY | O_DIRECTORY);
31613ee8ddeSJakub Wojciech Klama 	if (fd < 0) {
317ecc28863SEd Schouten 		free(pathcopy);
31813ee8ddeSJakub Wojciech Klama 		error = -1;
31913ee8ddeSJakub Wojciech Klama 		goto out;
32013ee8ddeSJakub Wojciech Klama 	}
32113ee8ddeSJakub Wojciech Klama 
32213ee8ddeSJakub Wojciech Klama 	sun.sun_family = AF_UNIX;
32313ee8ddeSJakub Wojciech Klama 	sun.sun_len = sizeof(struct sockaddr_un);
324ecc28863SEd Schouten 	strcpy(pathcopy, path);
3258a114a66SEric van Gyzen 	strlcpy(sun.sun_path, basename(pathcopy), sizeof(sun.sun_path));
326ecc28863SEd Schouten 	free(pathcopy);
32713ee8ddeSJakub Wojciech Klama 
32813ee8ddeSJakub Wojciech Klama 	if (bindat(fd, s, (struct sockaddr *)&sun, sun.sun_len) < 0) {
32913ee8ddeSJakub Wojciech Klama 		error = -1;
33013ee8ddeSJakub Wojciech Klama 		goto out;
33113ee8ddeSJakub Wojciech Klama 	}
33213ee8ddeSJakub Wojciech Klama 
33313ee8ddeSJakub Wojciech Klama 	if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
33413ee8ddeSJakub Wojciech Klama 		error = -1;
33513ee8ddeSJakub Wojciech Klama 		goto out;
33613ee8ddeSJakub Wojciech Klama 	}
33713ee8ddeSJakub Wojciech Klama 
33813ee8ddeSJakub Wojciech Klama 	if (listen(s, 1) < 0) {
33913ee8ddeSJakub Wojciech Klama 		error = -1;
34013ee8ddeSJakub Wojciech Klama 		goto out;
34113ee8ddeSJakub Wojciech Klama 	}
34213ee8ddeSJakub Wojciech Klama 
34300ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM
34400ef17beSBartek Rutkowski 	cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE);
345abfa3c39SMarcelo Araujo 	if (caph_rights_limit(s, &rights) == -1)
34600ef17beSBartek Rutkowski 		errx(EX_OSERR, "Unable to apply rights for sandbox");
34700ef17beSBartek Rutkowski #endif
34813ee8ddeSJakub Wojciech Klama 
349621b5090SJohn Baldwin 	name = get_config_value_node(nvl, "name");
350621b5090SJohn Baldwin 	if (name == NULL) {
351621b5090SJohn Baldwin 		EPRINTLN("vtcon: required name missing for port %ld", port);
352621b5090SJohn Baldwin 		error = -1;
353621b5090SJohn Baldwin 		goto out;
354621b5090SJohn Baldwin 	}
355621b5090SJohn Baldwin 	sock->vss_port = pci_vtcon_port_add(sc, port, name, pci_vtcon_sock_tx, sock);
35613ee8ddeSJakub Wojciech Klama 	if (sock->vss_port == NULL) {
35713ee8ddeSJakub Wojciech Klama 		error = -1;
35813ee8ddeSJakub Wojciech Klama 		goto out;
35913ee8ddeSJakub Wojciech Klama 	}
36013ee8ddeSJakub Wojciech Klama 
36113ee8ddeSJakub Wojciech Klama 	sock->vss_open = false;
36213ee8ddeSJakub Wojciech Klama 	sock->vss_conn_fd = -1;
36313ee8ddeSJakub Wojciech Klama 	sock->vss_server_fd = s;
36413ee8ddeSJakub Wojciech Klama 	sock->vss_server_evp = mevent_add(s, EVF_READ, pci_vtcon_sock_accept,
36513ee8ddeSJakub Wojciech Klama 	    sock);
36613ee8ddeSJakub Wojciech Klama 
36713ee8ddeSJakub Wojciech Klama 	if (sock->vss_server_evp == NULL) {
36813ee8ddeSJakub Wojciech Klama 		error = -1;
36913ee8ddeSJakub Wojciech Klama 		goto out;
37013ee8ddeSJakub Wojciech Klama 	}
37113ee8ddeSJakub Wojciech Klama 
37213ee8ddeSJakub Wojciech Klama out:
37313ee8ddeSJakub Wojciech Klama 	if (fd != -1)
37413ee8ddeSJakub Wojciech Klama 		close(fd);
37513ee8ddeSJakub Wojciech Klama 
376ba1ca6d2SSean Chittenden 	if (error != 0) {
377ba1ca6d2SSean Chittenden 		if (s != -1)
37813ee8ddeSJakub Wojciech Klama 			close(s);
379ba1ca6d2SSean Chittenden 		free(sock);
380ba1ca6d2SSean Chittenden 	}
38113ee8ddeSJakub Wojciech Klama 
38213ee8ddeSJakub Wojciech Klama 	return (error);
38313ee8ddeSJakub Wojciech Klama }
38413ee8ddeSJakub Wojciech Klama 
38513ee8ddeSJakub Wojciech Klama static void
pci_vtcon_sock_accept(int fd __unused,enum ev_type t __unused,void * arg)38613ee8ddeSJakub Wojciech Klama pci_vtcon_sock_accept(int fd __unused, enum ev_type t __unused, void *arg)
38713ee8ddeSJakub Wojciech Klama {
38813ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg;
38913ee8ddeSJakub Wojciech Klama 	int s;
39013ee8ddeSJakub Wojciech Klama 
39113ee8ddeSJakub Wojciech Klama 	s = accept(sock->vss_server_fd, NULL, NULL);
39213ee8ddeSJakub Wojciech Klama 	if (s < 0)
39313ee8ddeSJakub Wojciech Klama 		return;
39413ee8ddeSJakub Wojciech Klama 
39513ee8ddeSJakub Wojciech Klama 	if (sock->vss_open) {
39613ee8ddeSJakub Wojciech Klama 		close(s);
39713ee8ddeSJakub Wojciech Klama 		return;
39813ee8ddeSJakub Wojciech Klama 	}
39913ee8ddeSJakub Wojciech Klama 
40013ee8ddeSJakub Wojciech Klama 	sock->vss_open = true;
40113ee8ddeSJakub Wojciech Klama 	sock->vss_conn_fd = s;
40213ee8ddeSJakub Wojciech Klama 	sock->vss_conn_evp = mevent_add(s, EVF_READ, pci_vtcon_sock_rx, sock);
403962094d5SJakub Wojciech Klama 
40413ee8ddeSJakub Wojciech Klama 	pci_vtcon_open_port(sock->vss_port, true);
40513ee8ddeSJakub Wojciech Klama }
40613ee8ddeSJakub Wojciech Klama 
40713ee8ddeSJakub Wojciech Klama static void
pci_vtcon_sock_rx(int fd __unused,enum ev_type t __unused,void * arg)40813ee8ddeSJakub Wojciech Klama pci_vtcon_sock_rx(int fd __unused, enum ev_type t __unused, void *arg)
40913ee8ddeSJakub Wojciech Klama {
41013ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_port *port;
41113ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_sock *sock = (struct pci_vtcon_sock *)arg;
41213ee8ddeSJakub Wojciech Klama 	struct vqueue_info *vq;
413b0139127SKa Ho Ng 	struct vi_req req;
41413ee8ddeSJakub Wojciech Klama 	struct iovec iov;
41513ee8ddeSJakub Wojciech Klama 	static char dummybuf[2048];
41613ee8ddeSJakub Wojciech Klama 	int len, n;
41713ee8ddeSJakub Wojciech Klama 
41813ee8ddeSJakub Wojciech Klama 	port = sock->vss_port;
41913ee8ddeSJakub Wojciech Klama 	vq = pci_vtcon_port_to_vq(port, true);
42013ee8ddeSJakub Wojciech Klama 
42113ee8ddeSJakub Wojciech Klama 	if (!sock->vss_open || !port->vsp_rx_ready) {
42213ee8ddeSJakub Wojciech Klama 		len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf));
42313ee8ddeSJakub Wojciech Klama 		if (len == 0)
42413ee8ddeSJakub Wojciech Klama 			goto close;
42513ee8ddeSJakub Wojciech Klama 
42613ee8ddeSJakub Wojciech Klama 		return;
42713ee8ddeSJakub Wojciech Klama 	}
42813ee8ddeSJakub Wojciech Klama 
42913ee8ddeSJakub Wojciech Klama 	if (!vq_has_descs(vq)) {
43013ee8ddeSJakub Wojciech Klama 		len = read(sock->vss_conn_fd, dummybuf, sizeof(dummybuf));
43113ee8ddeSJakub Wojciech Klama 		vq_endchains(vq, 1);
43213ee8ddeSJakub Wojciech Klama 		if (len == 0)
43313ee8ddeSJakub Wojciech Klama 			goto close;
43413ee8ddeSJakub Wojciech Klama 
43513ee8ddeSJakub Wojciech Klama 		return;
43613ee8ddeSJakub Wojciech Klama 	}
43713ee8ddeSJakub Wojciech Klama 
43813ee8ddeSJakub Wojciech Klama 	do {
439b0139127SKa Ho Ng 		n = vq_getchain(vq, &iov, 1, &req);
44071fbc6faSMark Johnston 		assert(n == 1);
44113ee8ddeSJakub Wojciech Klama 		len = readv(sock->vss_conn_fd, &iov, n);
44213ee8ddeSJakub Wojciech Klama 
44313ee8ddeSJakub Wojciech Klama 		if (len == 0 || (len < 0 && errno == EWOULDBLOCK)) {
444d55e0373SVincenzo Maffione 			vq_retchains(vq, 1);
44513ee8ddeSJakub Wojciech Klama 			vq_endchains(vq, 0);
44613ee8ddeSJakub Wojciech Klama 			if (len == 0)
44713ee8ddeSJakub Wojciech Klama 				goto close;
44813ee8ddeSJakub Wojciech Klama 
44913ee8ddeSJakub Wojciech Klama 			return;
45013ee8ddeSJakub Wojciech Klama 		}
45113ee8ddeSJakub Wojciech Klama 
452b0139127SKa Ho Ng 		vq_relchain(vq, req.idx, len);
45313ee8ddeSJakub Wojciech Klama 	} while (vq_has_descs(vq));
45413ee8ddeSJakub Wojciech Klama 
45513ee8ddeSJakub Wojciech Klama 	vq_endchains(vq, 1);
45613ee8ddeSJakub Wojciech Klama 
45713ee8ddeSJakub Wojciech Klama close:
45813ee8ddeSJakub Wojciech Klama 	mevent_delete_close(sock->vss_conn_evp);
45913ee8ddeSJakub Wojciech Klama 	sock->vss_conn_fd = -1;
46013ee8ddeSJakub Wojciech Klama 	sock->vss_open = false;
46113ee8ddeSJakub Wojciech Klama }
46213ee8ddeSJakub Wojciech Klama 
46313ee8ddeSJakub Wojciech Klama static void
pci_vtcon_sock_tx(struct pci_vtcon_port * port __unused,void * arg __unused,struct iovec * iov,int niov)46498d920d9SMark Johnston pci_vtcon_sock_tx(struct pci_vtcon_port *port __unused, void *arg __unused,
46598d920d9SMark Johnston     struct iovec *iov, int niov)
46613ee8ddeSJakub Wojciech Klama {
46713ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_sock *sock;
468d286418eSJakub Wojciech Klama 	int i, ret;
46913ee8ddeSJakub Wojciech Klama 
47013ee8ddeSJakub Wojciech Klama 	sock = (struct pci_vtcon_sock *)arg;
47113ee8ddeSJakub Wojciech Klama 
47213ee8ddeSJakub Wojciech Klama 	if (sock->vss_conn_fd == -1)
47313ee8ddeSJakub Wojciech Klama 		return;
47413ee8ddeSJakub Wojciech Klama 
475d286418eSJakub Wojciech Klama 	for (i = 0; i < niov; i++) {
476d286418eSJakub Wojciech Klama 		ret = stream_write(sock->vss_conn_fd, iov[i].iov_base,
477d286418eSJakub Wojciech Klama 		    iov[i].iov_len);
478d286418eSJakub Wojciech Klama 		if (ret <= 0)
479d286418eSJakub Wojciech Klama 			break;
480d286418eSJakub Wojciech Klama 	}
48113ee8ddeSJakub Wojciech Klama 
482d286418eSJakub Wojciech Klama 	if (ret <= 0) {
48313ee8ddeSJakub Wojciech Klama 		mevent_delete_close(sock->vss_conn_evp);
48413ee8ddeSJakub Wojciech Klama 		sock->vss_conn_fd = -1;
48513ee8ddeSJakub Wojciech Klama 		sock->vss_open = false;
48613ee8ddeSJakub Wojciech Klama 	}
48713ee8ddeSJakub Wojciech Klama }
48813ee8ddeSJakub Wojciech Klama 
48913ee8ddeSJakub Wojciech Klama static void
pci_vtcon_control_tx(struct pci_vtcon_port * port,void * arg __unused,struct iovec * iov,int niov)49098d920d9SMark Johnston pci_vtcon_control_tx(struct pci_vtcon_port *port, void *arg __unused,
49198d920d9SMark Johnston     struct iovec *iov, int niov)
49213ee8ddeSJakub Wojciech Klama {
49313ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc *sc;
49413ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_port *tmp;
49513ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_control resp, *ctrl;
49613ee8ddeSJakub Wojciech Klama 	int i;
49713ee8ddeSJakub Wojciech Klama 
49813ee8ddeSJakub Wojciech Klama 	assert(niov == 1);
49913ee8ddeSJakub Wojciech Klama 
50013ee8ddeSJakub Wojciech Klama 	sc = port->vsp_sc;
50113ee8ddeSJakub Wojciech Klama 	ctrl = (struct pci_vtcon_control *)iov->iov_base;
50213ee8ddeSJakub Wojciech Klama 
50313ee8ddeSJakub Wojciech Klama 	switch (ctrl->event) {
50413ee8ddeSJakub Wojciech Klama 	case VTCON_DEVICE_READY:
505962094d5SJakub Wojciech Klama 		sc->vsc_ready = true;
50613ee8ddeSJakub Wojciech Klama 		/* set port ready events for registered ports */
50713ee8ddeSJakub Wojciech Klama 		for (i = 0; i < VTCON_MAXPORTS; i++) {
50813ee8ddeSJakub Wojciech Klama 			tmp = &sc->vsc_ports[i];
50913ee8ddeSJakub Wojciech Klama 			if (tmp->vsp_enabled)
51013ee8ddeSJakub Wojciech Klama 				pci_vtcon_announce_port(tmp);
511962094d5SJakub Wojciech Klama 
512962094d5SJakub Wojciech Klama 			if (tmp->vsp_open)
513962094d5SJakub Wojciech Klama 				pci_vtcon_open_port(tmp, true);
51413ee8ddeSJakub Wojciech Klama 		}
51513ee8ddeSJakub Wojciech Klama 		break;
51613ee8ddeSJakub Wojciech Klama 
51713ee8ddeSJakub Wojciech Klama 	case VTCON_PORT_READY:
518621b5090SJohn Baldwin 		tmp = &sc->vsc_ports[ctrl->id];
519621b5090SJohn Baldwin 		if (ctrl->id >= VTCON_MAXPORTS || !tmp->vsp_enabled) {
520332eff95SVincenzo Maffione 			WPRINTF(("VTCON_PORT_READY event for unknown port %d",
52113ee8ddeSJakub Wojciech Klama 			    ctrl->id));
52213ee8ddeSJakub Wojciech Klama 			return;
52313ee8ddeSJakub Wojciech Klama 		}
52413ee8ddeSJakub Wojciech Klama 
52513ee8ddeSJakub Wojciech Klama 		if (tmp->vsp_console) {
52613ee8ddeSJakub Wojciech Klama 			resp.event = VTCON_CONSOLE_PORT;
52713ee8ddeSJakub Wojciech Klama 			resp.id = ctrl->id;
52813ee8ddeSJakub Wojciech Klama 			resp.value = 1;
52913ee8ddeSJakub Wojciech Klama 			pci_vtcon_control_send(sc, &resp, NULL, 0);
53013ee8ddeSJakub Wojciech Klama 		}
53113ee8ddeSJakub Wojciech Klama 		break;
53213ee8ddeSJakub Wojciech Klama 	}
53313ee8ddeSJakub Wojciech Klama }
53413ee8ddeSJakub Wojciech Klama 
53513ee8ddeSJakub Wojciech Klama static void
pci_vtcon_announce_port(struct pci_vtcon_port * port)53613ee8ddeSJakub Wojciech Klama pci_vtcon_announce_port(struct pci_vtcon_port *port)
53713ee8ddeSJakub Wojciech Klama {
53813ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_control event;
53913ee8ddeSJakub Wojciech Klama 
54013ee8ddeSJakub Wojciech Klama 	event.id = port->vsp_id;
54113ee8ddeSJakub Wojciech Klama 	event.event = VTCON_DEVICE_ADD;
54213ee8ddeSJakub Wojciech Klama 	event.value = 1;
54313ee8ddeSJakub Wojciech Klama 	pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0);
54413ee8ddeSJakub Wojciech Klama 
54513ee8ddeSJakub Wojciech Klama 	event.event = VTCON_PORT_NAME;
54613ee8ddeSJakub Wojciech Klama 	pci_vtcon_control_send(port->vsp_sc, &event, port->vsp_name,
54713ee8ddeSJakub Wojciech Klama 	    strlen(port->vsp_name));
54813ee8ddeSJakub Wojciech Klama }
54913ee8ddeSJakub Wojciech Klama 
55013ee8ddeSJakub Wojciech Klama static void
pci_vtcon_open_port(struct pci_vtcon_port * port,bool open)55113ee8ddeSJakub Wojciech Klama pci_vtcon_open_port(struct pci_vtcon_port *port, bool open)
55213ee8ddeSJakub Wojciech Klama {
55313ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_control event;
55413ee8ddeSJakub Wojciech Klama 
555962094d5SJakub Wojciech Klama 	if (!port->vsp_sc->vsc_ready) {
556962094d5SJakub Wojciech Klama 		port->vsp_open = true;
557962094d5SJakub Wojciech Klama 		return;
558962094d5SJakub Wojciech Klama 	}
559962094d5SJakub Wojciech Klama 
56013ee8ddeSJakub Wojciech Klama 	event.id = port->vsp_id;
56113ee8ddeSJakub Wojciech Klama 	event.event = VTCON_PORT_OPEN;
56213ee8ddeSJakub Wojciech Klama 	event.value = (int)open;
56313ee8ddeSJakub Wojciech Klama 	pci_vtcon_control_send(port->vsp_sc, &event, NULL, 0);
56413ee8ddeSJakub Wojciech Klama }
56513ee8ddeSJakub Wojciech Klama 
56613ee8ddeSJakub Wojciech Klama static void
pci_vtcon_control_send(struct pci_vtcon_softc * sc,struct pci_vtcon_control * ctrl,const void * payload,size_t len)56713ee8ddeSJakub Wojciech Klama pci_vtcon_control_send(struct pci_vtcon_softc *sc,
56813ee8ddeSJakub Wojciech Klama     struct pci_vtcon_control *ctrl, const void *payload, size_t len)
56913ee8ddeSJakub Wojciech Klama {
57013ee8ddeSJakub Wojciech Klama 	struct vqueue_info *vq;
571b0139127SKa Ho Ng 	struct vi_req req;
57213ee8ddeSJakub Wojciech Klama 	struct iovec iov;
57313ee8ddeSJakub Wojciech Klama 	int n;
57413ee8ddeSJakub Wojciech Klama 
575*b34a4edeSPierre Pronchery 	if (len > SIZE_T_MAX - sizeof(struct pci_vtcon_control))
576*b34a4edeSPierre Pronchery 		return;
577*b34a4edeSPierre Pronchery 
57813ee8ddeSJakub Wojciech Klama 	vq = pci_vtcon_port_to_vq(&sc->vsc_control_port, true);
57913ee8ddeSJakub Wojciech Klama 
58013ee8ddeSJakub Wojciech Klama 	if (!vq_has_descs(vq))
58113ee8ddeSJakub Wojciech Klama 		return;
58213ee8ddeSJakub Wojciech Klama 
583b0139127SKa Ho Ng 	n = vq_getchain(vq, &iov, 1, &req);
58413ee8ddeSJakub Wojciech Klama 	assert(n == 1);
58513ee8ddeSJakub Wojciech Klama 
586*b34a4edeSPierre Pronchery 	if (iov.iov_len < sizeof(struct pci_vtcon_control) + len)
58789340029SPierre Pronchery 		goto out;
58889340029SPierre Pronchery 
58913ee8ddeSJakub Wojciech Klama 	memcpy(iov.iov_base, ctrl, sizeof(struct pci_vtcon_control));
590*b34a4edeSPierre Pronchery 	if (len > 0)
59163898728SMark Johnston 		memcpy((uint8_t *)iov.iov_base +
59263898728SMark Johnston 		    sizeof(struct pci_vtcon_control), payload, len);
59313ee8ddeSJakub Wojciech Klama 
59489340029SPierre Pronchery out:
595b0139127SKa Ho Ng 	vq_relchain(vq, req.idx, sizeof(struct pci_vtcon_control) + len);
59613ee8ddeSJakub Wojciech Klama 	vq_endchains(vq, 1);
59713ee8ddeSJakub Wojciech Klama }
59813ee8ddeSJakub Wojciech Klama 
59913ee8ddeSJakub Wojciech Klama 
60013ee8ddeSJakub Wojciech Klama static void
pci_vtcon_notify_tx(void * vsc,struct vqueue_info * vq)60113ee8ddeSJakub Wojciech Klama pci_vtcon_notify_tx(void *vsc, struct vqueue_info *vq)
60213ee8ddeSJakub Wojciech Klama {
60313ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc *sc;
60413ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_port *port;
60513ee8ddeSJakub Wojciech Klama 	struct iovec iov[1];
606b0139127SKa Ho Ng 	struct vi_req req;
60771fbc6faSMark Johnston 	int n;
60813ee8ddeSJakub Wojciech Klama 
60913ee8ddeSJakub Wojciech Klama 	sc = vsc;
61013ee8ddeSJakub Wojciech Klama 	port = pci_vtcon_vq_to_port(sc, vq);
61113ee8ddeSJakub Wojciech Klama 
61213ee8ddeSJakub Wojciech Klama 	while (vq_has_descs(vq)) {
613b0139127SKa Ho Ng 		n = vq_getchain(vq, iov, 1, &req);
61471fbc6faSMark Johnston 		assert(n == 1);
61513ee8ddeSJakub Wojciech Klama 		if (port != NULL)
61613ee8ddeSJakub Wojciech Klama 			port->vsp_cb(port, port->vsp_arg, iov, 1);
61713ee8ddeSJakub Wojciech Klama 
61813ee8ddeSJakub Wojciech Klama 		/*
61913ee8ddeSJakub Wojciech Klama 		 * Release this chain and handle more
62013ee8ddeSJakub Wojciech Klama 		 */
621b0139127SKa Ho Ng 		vq_relchain(vq, req.idx, 0);
62213ee8ddeSJakub Wojciech Klama 	}
62313ee8ddeSJakub Wojciech Klama 	vq_endchains(vq, 1);	/* Generate interrupt if appropriate. */
62413ee8ddeSJakub Wojciech Klama }
62513ee8ddeSJakub Wojciech Klama 
62613ee8ddeSJakub Wojciech Klama static void
pci_vtcon_notify_rx(void * vsc,struct vqueue_info * vq)62713ee8ddeSJakub Wojciech Klama pci_vtcon_notify_rx(void *vsc, struct vqueue_info *vq)
62813ee8ddeSJakub Wojciech Klama {
62913ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc *sc;
63013ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_port *port;
63113ee8ddeSJakub Wojciech Klama 
63213ee8ddeSJakub Wojciech Klama 	sc = vsc;
63313ee8ddeSJakub Wojciech Klama 	port = pci_vtcon_vq_to_port(sc, vq);
63413ee8ddeSJakub Wojciech Klama 
63513ee8ddeSJakub Wojciech Klama 	if (!port->vsp_rx_ready) {
63613ee8ddeSJakub Wojciech Klama 		port->vsp_rx_ready = 1;
63717e9052cSVincenzo Maffione 		vq_kick_disable(vq);
63813ee8ddeSJakub Wojciech Klama 	}
63913ee8ddeSJakub Wojciech Klama }
64013ee8ddeSJakub Wojciech Klama 
641621b5090SJohn Baldwin /*
642621b5090SJohn Baldwin  * Each console device has a "port" node which contains nodes for
643621b5090SJohn Baldwin  * each port.  Ports are numbered starting at 0.
644621b5090SJohn Baldwin  */
64513ee8ddeSJakub Wojciech Klama static int
pci_vtcon_legacy_config_port(nvlist_t * nvl,int port,char * opt)646621b5090SJohn Baldwin pci_vtcon_legacy_config_port(nvlist_t *nvl, int port, char *opt)
647621b5090SJohn Baldwin {
648621b5090SJohn Baldwin 	char *name, *path;
649621b5090SJohn Baldwin 	char node_name[sizeof("XX")];
650621b5090SJohn Baldwin 	nvlist_t *port_nvl;
651621b5090SJohn Baldwin 
652621b5090SJohn Baldwin 	name = strsep(&opt, "=");
653621b5090SJohn Baldwin 	path = opt;
654621b5090SJohn Baldwin 	if (path == NULL) {
655621b5090SJohn Baldwin 		EPRINTLN("vtcon: port %s requires a path", name);
656621b5090SJohn Baldwin 		return (-1);
657621b5090SJohn Baldwin 	}
658621b5090SJohn Baldwin 	if (port >= VTCON_MAXPORTS) {
659621b5090SJohn Baldwin 		EPRINTLN("vtcon: too many ports");
660621b5090SJohn Baldwin 		return (-1);
661621b5090SJohn Baldwin 	}
662621b5090SJohn Baldwin 	snprintf(node_name, sizeof(node_name), "%d", port);
663621b5090SJohn Baldwin 	port_nvl = create_relative_config_node(nvl, node_name);
664621b5090SJohn Baldwin 	set_config_value_node(port_nvl, "name", name);
665621b5090SJohn Baldwin 	set_config_value_node(port_nvl, "path", path);
666621b5090SJohn Baldwin 	return (0);
667621b5090SJohn Baldwin }
668621b5090SJohn Baldwin 
669621b5090SJohn Baldwin static int
pci_vtcon_legacy_config(nvlist_t * nvl,const char * opts)670621b5090SJohn Baldwin pci_vtcon_legacy_config(nvlist_t *nvl, const char *opts)
671621b5090SJohn Baldwin {
672621b5090SJohn Baldwin 	char *opt, *str, *tofree;
673621b5090SJohn Baldwin 	nvlist_t *ports_nvl;
674621b5090SJohn Baldwin 	int error, port;
675621b5090SJohn Baldwin 
676621b5090SJohn Baldwin 	ports_nvl = create_relative_config_node(nvl, "port");
677621b5090SJohn Baldwin 	tofree = str = strdup(opts);
678621b5090SJohn Baldwin 	error = 0;
679621b5090SJohn Baldwin 	port = 0;
680621b5090SJohn Baldwin 	while ((opt = strsep(&str, ",")) != NULL) {
681621b5090SJohn Baldwin 		error = pci_vtcon_legacy_config_port(ports_nvl, port, opt);
682621b5090SJohn Baldwin 		if (error)
683621b5090SJohn Baldwin 			break;
684621b5090SJohn Baldwin 		port++;
685621b5090SJohn Baldwin 	}
686621b5090SJohn Baldwin 	free(tofree);
687621b5090SJohn Baldwin 	return (error);
688621b5090SJohn Baldwin }
689621b5090SJohn Baldwin 
690621b5090SJohn Baldwin static int
pci_vtcon_init(struct pci_devinst * pi,nvlist_t * nvl)6916a284cacSJohn Baldwin pci_vtcon_init(struct pci_devinst *pi, nvlist_t *nvl)
69213ee8ddeSJakub Wojciech Klama {
69313ee8ddeSJakub Wojciech Klama 	struct pci_vtcon_softc *sc;
694621b5090SJohn Baldwin 	nvlist_t *ports_nvl;
69513ee8ddeSJakub Wojciech Klama 	int i;
69613ee8ddeSJakub Wojciech Klama 
69713ee8ddeSJakub Wojciech Klama 	sc = calloc(1, sizeof(struct pci_vtcon_softc));
69813ee8ddeSJakub Wojciech Klama 	sc->vsc_config = calloc(1, sizeof(struct pci_vtcon_config));
69913ee8ddeSJakub Wojciech Klama 	sc->vsc_config->max_nr_ports = VTCON_MAXPORTS;
70013ee8ddeSJakub Wojciech Klama 	sc->vsc_config->cols = 80;
70113ee8ddeSJakub Wojciech Klama 	sc->vsc_config->rows = 25;
70213ee8ddeSJakub Wojciech Klama 
703f6f357efSAndy Fiddaman 	pthread_mutex_init(&sc->vsc_mtx, NULL);
704f6f357efSAndy Fiddaman 
70513ee8ddeSJakub Wojciech Klama 	vi_softc_linkup(&sc->vsc_vs, &vtcon_vi_consts, sc, pi, sc->vsc_queues);
70613ee8ddeSJakub Wojciech Klama 	sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
70713ee8ddeSJakub Wojciech Klama 
70813ee8ddeSJakub Wojciech Klama 	for (i = 0; i < VTCON_MAXQ; i++) {
70913ee8ddeSJakub Wojciech Klama 		sc->vsc_queues[i].vq_qsize = VTCON_RINGSZ;
71013ee8ddeSJakub Wojciech Klama 		sc->vsc_queues[i].vq_notify = i % 2 == 0
71113ee8ddeSJakub Wojciech Klama 		    ? pci_vtcon_notify_rx
71213ee8ddeSJakub Wojciech Klama 		    : pci_vtcon_notify_tx;
71313ee8ddeSJakub Wojciech Klama 	}
71413ee8ddeSJakub Wojciech Klama 
71513ee8ddeSJakub Wojciech Klama 	/* initialize config space */
71613ee8ddeSJakub Wojciech Klama 	pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_CONSOLE);
71713ee8ddeSJakub Wojciech Klama 	pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
71813ee8ddeSJakub Wojciech Klama 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM);
71954ac6f72SKa Ho Ng 	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_CONSOLE);
72013ee8ddeSJakub Wojciech Klama 	pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
72113ee8ddeSJakub Wojciech Klama 
72213ee8ddeSJakub Wojciech Klama 	if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
72313ee8ddeSJakub Wojciech Klama 		return (1);
72413ee8ddeSJakub Wojciech Klama 	vi_set_io_bar(&sc->vsc_vs, 0);
72513ee8ddeSJakub Wojciech Klama 
72613ee8ddeSJakub Wojciech Klama 	/* create control port */
72713ee8ddeSJakub Wojciech Klama 	sc->vsc_control_port.vsp_sc = sc;
72813ee8ddeSJakub Wojciech Klama 	sc->vsc_control_port.vsp_txq = 2;
72913ee8ddeSJakub Wojciech Klama 	sc->vsc_control_port.vsp_rxq = 3;
73013ee8ddeSJakub Wojciech Klama 	sc->vsc_control_port.vsp_cb = pci_vtcon_control_tx;
73113ee8ddeSJakub Wojciech Klama 	sc->vsc_control_port.vsp_enabled = true;
73213ee8ddeSJakub Wojciech Klama 
733621b5090SJohn Baldwin 	ports_nvl = find_relative_config_node(nvl, "port");
734621b5090SJohn Baldwin 	if (ports_nvl != NULL) {
735621b5090SJohn Baldwin 		const char *name;
736621b5090SJohn Baldwin 		void *cookie;
737621b5090SJohn Baldwin 		int type;
73813ee8ddeSJakub Wojciech Klama 
739621b5090SJohn Baldwin 		cookie = NULL;
740621b5090SJohn Baldwin 		while ((name = nvlist_next(ports_nvl, &type, &cookie)) !=
741621b5090SJohn Baldwin 		    NULL) {
742621b5090SJohn Baldwin 			if (type != NV_TYPE_NVLIST)
743621b5090SJohn Baldwin 				continue;
744621b5090SJohn Baldwin 
745621b5090SJohn Baldwin 			if (pci_vtcon_sock_add(sc, name,
746621b5090SJohn Baldwin 			    nvlist_get_nvlist(ports_nvl, name)) < 0) {
747332eff95SVincenzo Maffione 				EPRINTLN("cannot create port %s: %s",
748621b5090SJohn Baldwin 				    name, strerror(errno));
74913ee8ddeSJakub Wojciech Klama 				return (1);
75013ee8ddeSJakub Wojciech Klama 			}
75113ee8ddeSJakub Wojciech Klama 		}
752621b5090SJohn Baldwin 	}
75313ee8ddeSJakub Wojciech Klama 
75413ee8ddeSJakub Wojciech Klama 	return (0);
75513ee8ddeSJakub Wojciech Klama }
75613ee8ddeSJakub Wojciech Klama 
75737045dfaSMark Johnston static const struct pci_devemu pci_de_vcon = {
75813ee8ddeSJakub Wojciech Klama 	.pe_emu =	"virtio-console",
75913ee8ddeSJakub Wojciech Klama 	.pe_init =	pci_vtcon_init,
76013ee8ddeSJakub Wojciech Klama 	.pe_barwrite =	vi_pci_write,
7613cdfaefaSYan Ka Chiu 	.pe_barread =	vi_pci_read,
7623cdfaefaSYan Ka Chiu 	.pe_legacy_config = pci_vtcon_legacy_config,
76313ee8ddeSJakub Wojciech Klama };
76413ee8ddeSJakub Wojciech Klama PCI_EMUL_SET(pci_de_vcon);
765