Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177098714
D60895.1793800959.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
15 KB
Referenced Files
None
Subscribers
None
D60895.1793800959.diff
View Options
diff --git a/share/man/man4/virtio_console.4 b/share/man/man4/virtio_console.4
--- a/share/man/man4/virtio_console.4
+++ b/share/man/man4/virtio_console.4
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 22, 2014
+.Dd October 18, 2026
.Dt VIRTIO_CONSOLE 4
.Os
.Sh NAME
@@ -36,6 +36,7 @@
.Pp
In
.Xr loader.conf 5 :
+.Cd hw.vtcon.console= Ns Ar 0
.Cd virtio_console_load="YES"
.Sh DESCRIPTION
The
@@ -46,12 +47,40 @@
Each port is similar to a simple serial interface, and
each port is accessible through
.Xr tty 4 .
+.Pp
+A port the host designates as a console becomes a system console, usable
+for kernel messages,
+.Xr ddb 4 ,
+and a login session through
+.Xr getty 8 .
+Without multiport support, the host's only port is the console.
+Because the driver attaches during autoconfiguration rather than at
+.Fn cninit ,
+kernel messages from before it attaches are not shown there; they remain
+available through
+.Xr dmesg 8 .
+If the host offers the emergency write feature, the console falls back to
+it when the transmit virtqueue cannot be used, such as after the device has
+been reset at shutdown.
+.Sh LOADER TUNABLES
+Tunables can be set at the
+.Xr loader 8
+prompt before booting the kernel or stored in
+.Xr loader.conf 5 .
+.Bl -tag -width indent
+.It Va hw.vtcon.console
+Set to 0 to not use console ports as system consoles.
+The default value is 1.
+.El
.Sh FILES
.Bl -tag -width "/dev/ttyV?.??" -compact
.It Pa /dev/ttyV?.??
.El
.Sh SEE ALSO
+.Xr ddb 4 ,
.Xr tty 4 ,
-.Xr virtio 4
+.Xr virtio 4 ,
+.Xr ttys 5 ,
+.Xr getty 8
.Sh AUTHORS
.An Bryan Venteicher Aq Mt bryanv@FreeBSD.org
diff --git a/sys/dev/virtio/console/virtio_console.c b/sys/dev/virtio/console/virtio_console.c
--- a/sys/dev/virtio/console/virtio_console.c
+++ b/sys/dev/virtio/console/virtio_console.c
@@ -104,8 +104,16 @@
struct mtx vcsp_mtx;
int vcsp_flags;
#define VTCON_SCPORT_FLAG_TXSTOP 0x01
+#define VTCON_SCPORT_FLAG_TXWEDGED 0x02
+#define VTCON_SCPORT_FLAG_CONSOLE 0x04
+#define VTCON_SCPORT_FLAG_CNADDED 0x08
u_int vcsp_txbusy;
char vcsp_txbuf[VTCON_TX_NBUFS][VTCON_BULK_BUFSZ];
+ struct consdev vcsp_consdev;
+ u_int vcsp_cn_grab;
+ char *vcsp_cn_rxbuf;
+ uint32_t vcsp_cn_rxlen;
+ uint32_t vcsp_cn_rxpos;
};
#define VTCON_SCPORT_LOCK(_scport) mtx_lock_spin(&(_scport)->vcsp_mtx)
@@ -121,6 +129,7 @@
#define VTCON_FLAG_SIZE 0x02
#define VTCON_FLAG_MULTIPORT 0x04
#define VTCON_FLAG_READY 0x08
+#define VTCON_FLAG_EMERG_WRITE 0x10
/*
* Ports can be added and removed during runtime, but we have
@@ -152,7 +161,8 @@
#define VTCON_FEATURES \
(VIRTIO_CONSOLE_F_SIZE | \
- VIRTIO_CONSOLE_F_MULTIPORT)
+ VIRTIO_CONSOLE_F_MULTIPORT | \
+ VIRTIO_CONSOLE_F_EMERG_WRITE)
static struct virtio_feature_desc vtcon_feature_desc[] = {
{ VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" },
@@ -189,6 +199,14 @@
static int vtcon_scport_tx(struct vtcon_softc_port *, const void *,
int);
+static void vtcon_cn_register(struct vtcon_softc *, int);
+static void vtcon_cn_port_gone(struct vtcon_softc_port *);
+static void vtcon_cn_deregister(struct vtcon_softc *);
+static bool vtcon_cn_lock(struct vtcon_softc_port *);
+static void vtcon_cn_unlock(struct vtcon_softc_port *);
+static void vtcon_cn_rx_release(struct vtcon_softc_port *);
+static void vtcon_cn_emerg_putc(struct vtcon_softc *, int);
+
static int vtcon_ctrl_event_enqueue(struct vtcon_softc *,
struct virtio_console_control *);
static int vtcon_ctrl_event_create(struct vtcon_softc *);
@@ -215,7 +233,7 @@
static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t,
uint16_t, uint16_t);
-static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t);
+static int vtcon_port_enqueue_buf(struct virtqueue *, void *, size_t);
static int vtcon_port_create_buf(struct vtcon_port *);
static void vtcon_port_requeue_buf(struct vtcon_port *, void *);
static int vtcon_port_populate(struct vtcon_port *);
@@ -258,6 +276,13 @@
static int vtcon_pending_free;
+static SYSCTL_NODE(_hw, OID_AUTO, vtcon, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "VirtIO Console driver parameters");
+
+static int vtcon_console = 1;
+SYSCTL_INT(_hw_vtcon, OID_AUTO, console, CTLFLAG_RDTUN, &vtcon_console, 0,
+ "Use host-designated console ports as system consoles");
+
static struct ttydevsw vtcon_tty_class = {
.tsw_flags = 0,
.tsw_open = vtcon_tty_open,
@@ -266,6 +291,24 @@
.tsw_free = vtcon_tty_free,
};
+static cn_probe_t vtcon_cnprobe;
+static cn_init_t vtcon_cninit;
+static cn_term_t vtcon_cnterm;
+static cn_getc_t vtcon_cngetc;
+static cn_putc_t vtcon_cnputc;
+static cn_grab_t vtcon_cngrab;
+static cn_ungrab_t vtcon_cnungrab;
+
+static const struct consdev_ops vtcon_cnops = {
+ .cn_probe = vtcon_cnprobe,
+ .cn_init = vtcon_cninit,
+ .cn_term = vtcon_cnterm,
+ .cn_getc = vtcon_cngetc,
+ .cn_putc = vtcon_cnputc,
+ .cn_grab = vtcon_cngrab,
+ .cn_ungrab = vtcon_cnungrab,
+};
+
static device_method_t vtcon_methods[] = {
/* Device methods. */
DEVMETHOD(device_probe, vtcon_probe),
@@ -421,6 +464,7 @@
vtcon_ctrl_deinit(sc);
}
+ vtcon_cn_deregister(sc);
vtcon_destroy_ports(sc);
mtx_destroy(&sc->vtcon_mtx);
mtx_destroy(&sc->vtcon_ctrl_tx_mtx);
@@ -440,7 +484,9 @@
vtcon_attach_completed(device_t dev)
{
struct vtcon_softc *sc;
+ struct vtcon_softc_port *scport;
struct vtcon_port *port;
+ int i;
sc = device_get_softc(dev);
@@ -448,10 +494,16 @@
sc->vtcon_flags |= VTCON_FLAG_READY;
if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
virtqueue_notify(sc->vtcon_ctrl_rxvq);
- port = sc->vtcon_ports[0].vcsp_port;
+ scport = &sc->vtcon_ports[0];
+ port = scport->vcsp_port;
if (port != NULL) {
VTCON_PORT_LOCK(port);
+ /* Without multiport, the only port is the console. */
+ if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0)
+ port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
+ VTCON_SCPORT_LOCK(scport);
virtqueue_notify(port->vtcport_invq);
+ VTCON_SCPORT_UNLOCK(scport);
VTCON_PORT_UNLOCK(port);
}
VTCON_UNLOCK(sc);
@@ -459,6 +511,9 @@
vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID,
VIRTIO_CONSOLE_DEVICE_READY, 1);
+ for (i = 0; i < sc->vtcon_max_ports; i++)
+ vtcon_cn_register(sc, i);
+
return (0);
}
@@ -510,6 +565,8 @@
sc->vtcon_flags |= VTCON_FLAG_SIZE;
if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT))
sc->vtcon_flags |= VTCON_FLAG_MULTIPORT;
+ if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_EMERG_WRITE))
+ sc->vtcon_flags |= VTCON_FLAG_EMERG_WRITE;
return (0);
}
@@ -777,6 +834,287 @@
return (0);
}
+static void
+vtcon_cn_register(struct vtcon_softc *sc, int id)
+{
+ struct vtcon_softc_port *scport;
+ struct vtcon_port *port;
+ struct consdev *cp;
+ bool add;
+
+ if (vtcon_console == 0)
+ return;
+
+ scport = &sc->vtcon_ports[id];
+ cp = &scport->vcsp_consdev;
+
+ VTCON_LOCK(sc);
+ port = scport->vcsp_port;
+ if (port == NULL ||
+ (sc->vtcon_flags & (VTCON_FLAG_READY | VTCON_FLAG_DETACHED)) !=
+ VTCON_FLAG_READY) {
+ VTCON_UNLOCK(sc);
+ return;
+ }
+ VTCON_PORT_LOCK(port);
+ VTCON_UNLOCK(sc);
+
+ if ((port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) == 0) {
+ VTCON_PORT_UNLOCK(port);
+ return;
+ }
+
+ if (cp->cn_ops == NULL) {
+ cp->cn_ops = &vtcon_cnops;
+ cp->cn_arg = scport;
+ cp->cn_pri = CN_NORMAL;
+ strlcpy(cp->cn_name, tty_devname(port->vtcport_tty),
+ sizeof(cp->cn_name));
+ }
+
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &= ~VTCON_SCPORT_FLAG_TXWEDGED;
+ scport->vcsp_flags |= VTCON_SCPORT_FLAG_CONSOLE;
+ add = (scport->vcsp_flags & VTCON_SCPORT_FLAG_CNADDED) == 0;
+ scport->vcsp_flags |= VTCON_SCPORT_FLAG_CNADDED;
+ VTCON_SCPORT_UNLOCK(scport);
+ VTCON_PORT_UNLOCK(port);
+
+ if (!add)
+ return;
+
+ if (cnadd(cp) != 0) {
+ device_printf(sc->vtcon_dev, "cannot add console %s\n",
+ cp->cn_name);
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &=
+ ~(VTCON_SCPORT_FLAG_CONSOLE | VTCON_SCPORT_FLAG_CNADDED);
+ VTCON_SCPORT_UNLOCK(scport);
+ } else
+ device_printf(sc->vtcon_dev, "console %s\n", cp->cn_name);
+}
+
+static void
+vtcon_cn_port_gone(struct vtcon_softc_port *scport)
+{
+ char *buf;
+
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &= ~VTCON_SCPORT_FLAG_CONSOLE;
+ buf = scport->vcsp_cn_rxbuf;
+ scport->vcsp_cn_rxbuf = NULL;
+ VTCON_SCPORT_UNLOCK(scport);
+
+ free(buf, M_DEVBUF);
+}
+
+static void
+vtcon_cn_deregister(struct vtcon_softc *sc)
+{
+ struct vtcon_softc_port *scport;
+ int i;
+
+ if (sc->vtcon_ports == NULL)
+ return;
+
+ for (i = 0; i < sc->vtcon_max_ports; i++) {
+ scport = &sc->vtcon_ports[i];
+ if ((scport->vcsp_flags & VTCON_SCPORT_FLAG_CNADDED) == 0)
+ continue;
+
+ cnremove(&scport->vcsp_consdev);
+ vtcon_cn_port_gone(scport);
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &= ~VTCON_SCPORT_FLAG_CNADDED;
+ VTCON_SCPORT_UNLOCK(scport);
+ }
+}
+
+static bool
+vtcon_cn_lock(struct vtcon_softc_port *scport)
+{
+ struct mtx *m;
+
+ m = &scport->vcsp_mtx;
+
+ if (SCHEDULER_STOPPED())
+ return (mtx_owner(m) == NULL);
+ if (mtx_owned(m))
+ return (false);
+ if (kdb_active)
+ return (mtx_trylock_spin(m) != 0);
+ mtx_lock_spin(m);
+
+ return (true);
+}
+
+static void
+vtcon_cn_unlock(struct vtcon_softc_port *scport)
+{
+ if (!SCHEDULER_STOPPED())
+ mtx_unlock_spin(&scport->vcsp_mtx);
+}
+
+static void
+vtcon_cn_rx_release(struct vtcon_softc_port *scport)
+{
+ struct virtqueue *vq;
+ int error __diagused;
+
+ if (scport->vcsp_cn_rxbuf == NULL)
+ return;
+
+ vq = scport->vcsp_invq;
+ error = vtcon_port_enqueue_buf(vq, scport->vcsp_cn_rxbuf,
+ VTCON_BULK_BUFSZ);
+ KASSERT(error == 0,
+ ("%s: cannot requeue input buffer %d", __func__, error));
+ virtqueue_notify(vq);
+ scport->vcsp_cn_rxbuf = NULL;
+}
+
+static void
+vtcon_cn_emerg_putc(struct vtcon_softc *sc, int c)
+{
+ virtio_write_dev_config_4(sc->vtcon_dev,
+ offsetof(struct virtio_console_config, emerg_wr), (uint8_t)c);
+}
+
+static void
+vtcon_cnprobe(struct consdev *cp)
+{
+}
+
+static void
+vtcon_cninit(struct consdev *cp)
+{
+}
+
+static void
+vtcon_cnterm(struct consdev *cp)
+{
+}
+
+static int
+vtcon_cngetc(struct consdev *cp)
+{
+ struct vtcon_softc_port *scport;
+ struct virtqueue *vq;
+ char *buf;
+ uint32_t len;
+ int c, error __diagused;
+
+ scport = cp->cn_arg;
+ vq = scport->vcsp_invq;
+ c = -1;
+
+ if (!vtcon_cn_lock(scport))
+ return (c);
+
+ if ((scport->vcsp_flags & (VTCON_SCPORT_FLAG_CONSOLE |
+ VTCON_SCPORT_FLAG_TXSTOP)) != VTCON_SCPORT_FLAG_CONSOLE)
+ goto out;
+
+ while (scport->vcsp_cn_rxbuf == NULL) {
+ buf = virtqueue_dequeue(vq, &len);
+ if (buf == NULL)
+ goto out;
+ len = min(len, VTCON_BULK_BUFSZ);
+ if (len == 0) {
+ error = vtcon_port_enqueue_buf(vq, buf,
+ VTCON_BULK_BUFSZ);
+ KASSERT(error == 0, ("%s: cannot requeue input "
+ "buffer %d", __func__, error));
+ virtqueue_notify(vq);
+ continue;
+ }
+ scport->vcsp_cn_rxbuf = buf;
+ scport->vcsp_cn_rxlen = len;
+ scport->vcsp_cn_rxpos = 0;
+ }
+
+ c = (u_char)scport->vcsp_cn_rxbuf[scport->vcsp_cn_rxpos++];
+ if (scport->vcsp_cn_rxpos == scport->vcsp_cn_rxlen)
+ vtcon_cn_rx_release(scport);
+out:
+ vtcon_cn_unlock(scport);
+
+ return (c);
+}
+
+static void
+vtcon_cnputc(struct consdev *cp, int c)
+{
+ struct vtcon_softc_port *scport;
+ struct vtcon_softc *sc;
+ u_int timeout, us;
+ char ch;
+ int error;
+
+ scport = cp->cn_arg;
+ sc = scport->vcsp_sc;
+ ch = c;
+ error = ENXIO;
+
+ if (vtcon_cn_lock(scport)) {
+ if ((scport->vcsp_flags & (VTCON_SCPORT_FLAG_CONSOLE |
+ VTCON_SCPORT_FLAG_TXSTOP)) == VTCON_SCPORT_FLAG_CONSOLE) {
+ timeout = (scport->vcsp_flags &
+ VTCON_SCPORT_FLAG_TXWEDGED) != 0 ? 0 :
+ VTCON_TX_TIMEOUT;
+ for (us = 0;; us++) {
+ error = vtcon_scport_tx(scport, &ch, 1);
+ if (error != EBUSY || us >= timeout)
+ break;
+ DELAY(1);
+ }
+ if (error == EBUSY)
+ scport->vcsp_flags |=
+ VTCON_SCPORT_FLAG_TXWEDGED;
+ else
+ scport->vcsp_flags &=
+ ~VTCON_SCPORT_FLAG_TXWEDGED;
+ }
+ vtcon_cn_unlock(scport);
+ }
+
+ if (error != 0 && (sc->vtcon_flags & VTCON_FLAG_EMERG_WRITE) != 0)
+ vtcon_cn_emerg_putc(sc, ch);
+}
+
+static void
+vtcon_cngrab(struct consdev *cp)
+{
+ struct vtcon_softc_port *scport;
+
+ scport = cp->cn_arg;
+
+ if (!vtcon_cn_lock(scport))
+ return;
+ if (scport->vcsp_cn_grab++ == 0 &&
+ (scport->vcsp_flags & VTCON_SCPORT_FLAG_CONSOLE) != 0)
+ virtqueue_disable_intr(scport->vcsp_invq);
+ vtcon_cn_unlock(scport);
+}
+
+static void
+vtcon_cnungrab(struct consdev *cp)
+{
+ struct vtcon_softc_port *scport;
+
+ scport = cp->cn_arg;
+
+ if (!vtcon_cn_lock(scport))
+ return;
+ if (scport->vcsp_cn_grab > 0 && --scport->vcsp_cn_grab == 0 &&
+ (scport->vcsp_flags & (VTCON_SCPORT_FLAG_CONSOLE |
+ VTCON_SCPORT_FLAG_TXSTOP)) == VTCON_SCPORT_FLAG_CONSOLE) {
+ vtcon_cn_rx_release(scport);
+ (void)virtqueue_enable_intr(scport->vcsp_invq);
+ }
+ vtcon_cn_unlock(scport);
+}
+
static int
vtcon_ctrl_event_enqueue(struct vtcon_softc *sc,
struct virtio_console_control *control)
@@ -959,6 +1297,8 @@
port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
VTCON_PORT_UNLOCK(port);
+
+ vtcon_cn_register(sc, id);
}
static void
@@ -1219,15 +1559,12 @@
}
static int
-vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len)
+vtcon_port_enqueue_buf(struct virtqueue *vq, void *buf, size_t len)
{
struct sglist_seg segs[2];
struct sglist sg;
- struct virtqueue *vq;
int error;
- vq = port->vtcport_invq;
-
sglist_init(&sg, 2, segs);
error = sglist_append(&sg, buf, len);
KASSERT(error == 0,
@@ -1248,7 +1585,8 @@
if (buf == NULL)
return (ENOMEM);
- error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
+ error = vtcon_port_enqueue_buf(port->vtcport_invq, buf,
+ VTCON_BULK_BUFSZ);
if (error)
free(buf, M_DEVBUF);
@@ -1260,7 +1598,8 @@
{
int error __diagused;
- error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
+ error = vtcon_port_enqueue_buf(port->vtcport_invq, buf,
+ VTCON_BULK_BUFSZ);
KASSERT(error == 0,
("%s: cannot requeue input buffer %d", __func__, error));
}
@@ -1441,6 +1780,7 @@
tp = port->vtcport_tty;
port->vtcport_flags |= VTCON_PORT_FLAG_GONE;
+ vtcon_cn_port_gone(port->vtcport_scport);
if (tp != NULL) {
atomic_add_int(&vtcon_pending_free, 1);
@@ -1518,19 +1858,29 @@
static void
vtcon_port_in(struct vtcon_port *port)
{
+ struct vtcon_softc_port *scport;
struct virtqueue *vq;
struct tty *tp;
char *buf;
uint32_t len;
- int i, deq;
+ int i, deq, pending;
+ scport = port->vtcport_scport;
tp = port->vtcport_tty;
vq = port->vtcport_invq;
again:
deq = 0;
- while ((buf = virtqueue_dequeue(vq, &len)) != NULL) {
+ for (;;) {
+ VTCON_SCPORT_LOCK(scport);
+ buf = NULL;
+ if (scport->vcsp_cn_grab == 0)
+ buf = virtqueue_dequeue(vq, &len);
+ VTCON_SCPORT_UNLOCK(scport);
+ if (buf == NULL)
+ break;
+
len = min(len, VTCON_BULK_BUFSZ);
for (i = 0; i < len; i++) {
#if defined(KDB)
@@ -1540,15 +1890,23 @@
#endif
ttydisc_rint(tp, buf[i], 0);
}
+
+ VTCON_SCPORT_LOCK(scport);
vtcon_port_requeue_buf(port, buf);
+ VTCON_SCPORT_UNLOCK(scport);
deq++;
}
ttydisc_rint_done(tp);
+ VTCON_SCPORT_LOCK(scport);
if (deq > 0)
virtqueue_notify(vq);
+ pending = 0;
+ if (scport->vcsp_cn_grab == 0)
+ pending = virtqueue_enable_intr(vq);
+ VTCON_SCPORT_UNLOCK(scport);
- if (virtqueue_enable_intr(vq) != 0)
+ if (pending != 0)
goto again;
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 4, 2:02 PM (5 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41436404
Default Alt Text
D60895.1793800959.diff (15 KB)
Attached To
Mode
D60895: virtio_console: Add system console support
Attached
Detach File
Event Timeline