xref: /freebsd/sys/powerpc/pseries/phyp_console.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
17a8d25c0SNathan Whitehorn /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
371e3c308SPedro F. Giffuni  *
47a8d25c0SNathan Whitehorn  * Copyright (C) 2011 by Nathan Whitehorn. All rights reserved.
57a8d25c0SNathan Whitehorn  *
67a8d25c0SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
77a8d25c0SNathan Whitehorn  * modification, are permitted provided that the following conditions
87a8d25c0SNathan Whitehorn  * are met:
97a8d25c0SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
107a8d25c0SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
117a8d25c0SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
127a8d25c0SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
137a8d25c0SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
147a8d25c0SNathan Whitehorn  *
157a8d25c0SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
167a8d25c0SNathan Whitehorn  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
177a8d25c0SNathan Whitehorn  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
187a8d25c0SNathan Whitehorn  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
197a8d25c0SNathan Whitehorn  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
207a8d25c0SNathan Whitehorn  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
217a8d25c0SNathan Whitehorn  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
227a8d25c0SNathan Whitehorn  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
237a8d25c0SNathan Whitehorn  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
247a8d25c0SNathan Whitehorn  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
257a8d25c0SNathan Whitehorn  */
267a8d25c0SNathan Whitehorn 
277a8d25c0SNathan Whitehorn #include <sys/cdefs.h>
2815be37cbSBrandon Bergren #include <sys/endian.h>
297a8d25c0SNathan Whitehorn #include <sys/param.h>
307a8d25c0SNathan Whitehorn #include <sys/kdb.h>
317a8d25c0SNathan Whitehorn #include <sys/kernel.h>
327a8d25c0SNathan Whitehorn #include <sys/priv.h>
337a8d25c0SNathan Whitehorn #include <sys/systm.h>
347a8d25c0SNathan Whitehorn #include <sys/module.h>
357a8d25c0SNathan Whitehorn #include <sys/types.h>
367a8d25c0SNathan Whitehorn #include <sys/conf.h>
377a8d25c0SNathan Whitehorn #include <sys/cons.h>
387a8d25c0SNathan Whitehorn #include <sys/tty.h>
397a8d25c0SNathan Whitehorn #include <machine/bus.h>
407a8d25c0SNathan Whitehorn 
417a8d25c0SNathan Whitehorn #include <dev/ofw/openfirm.h>
427a8d25c0SNathan Whitehorn #include <dev/ofw/ofw_bus.h>
437a8d25c0SNathan Whitehorn #include <dev/ofw/ofw_bus_subr.h>
447a8d25c0SNathan Whitehorn #include <dev/uart/uart.h>
457a8d25c0SNathan Whitehorn #include <dev/uart/uart_cpu.h>
467a8d25c0SNathan Whitehorn #include <dev/uart/uart_bus.h>
477a8d25c0SNathan Whitehorn 
487a8d25c0SNathan Whitehorn #include "phyp-hvcall.h"
497a8d25c0SNathan Whitehorn #include "uart_if.h"
507a8d25c0SNathan Whitehorn 
517a8d25c0SNathan Whitehorn struct uart_phyp_softc {
527a8d25c0SNathan Whitehorn 	device_t dev;
537a8d25c0SNathan Whitehorn 	phandle_t node;
547a8d25c0SNathan Whitehorn 	int vtermid;
557a8d25c0SNathan Whitehorn 
567a8d25c0SNathan Whitehorn 	struct tty *tp;
577a8d25c0SNathan Whitehorn 	struct resource *irqres;
587a8d25c0SNathan Whitehorn 	int irqrid;
597a8d25c0SNathan Whitehorn 	struct callout callout;
607a8d25c0SNathan Whitehorn 	void *sc_icookie;
617a8d25c0SNathan Whitehorn 	int polltime;
627a8d25c0SNathan Whitehorn 
637a8d25c0SNathan Whitehorn 	struct mtx sc_mtx;
647a8d25c0SNathan Whitehorn 	int protocol;
657a8d25c0SNathan Whitehorn 
667a8d25c0SNathan Whitehorn 	union {
677a8d25c0SNathan Whitehorn 		uint64_t u64[2];
687a8d25c0SNathan Whitehorn 		char str[16];
697a8d25c0SNathan Whitehorn 	} phyp_inbuf;
707a8d25c0SNathan Whitehorn 	uint64_t inbuflen;
717a8d25c0SNathan Whitehorn 	uint8_t outseqno;
727a8d25c0SNathan Whitehorn };
737a8d25c0SNathan Whitehorn 
747a8d25c0SNathan Whitehorn static struct uart_phyp_softc	*console_sc = NULL;
757a8d25c0SNathan Whitehorn #if defined(KDB)
767a8d25c0SNathan Whitehorn static int			alt_break_state;
777a8d25c0SNathan Whitehorn #endif
787a8d25c0SNathan Whitehorn 
797a8d25c0SNathan Whitehorn enum {
807a8d25c0SNathan Whitehorn 	HVTERM1, HVTERMPROT
817a8d25c0SNathan Whitehorn };
827a8d25c0SNathan Whitehorn 
837a8d25c0SNathan Whitehorn #define VS_DATA_PACKET_HEADER		0xff
847a8d25c0SNathan Whitehorn #define VS_CONTROL_PACKET_HEADER	0xfe
857a8d25c0SNathan Whitehorn #define  VSV_SET_MODEM_CTL		0x01
867a8d25c0SNathan Whitehorn #define  VSV_MODEM_CTL_UPDATE		0x02
877a8d25c0SNathan Whitehorn #define  VSV_RENEGOTIATE_CONNECTION	0x03
887a8d25c0SNathan Whitehorn #define VS_QUERY_PACKET_HEADER		0xfd
897a8d25c0SNathan Whitehorn #define  VSV_SEND_VERSION_NUMBER	0x01
907a8d25c0SNathan Whitehorn #define  VSV_SEND_MODEM_CTL_STATUS	0x02
917a8d25c0SNathan Whitehorn #define VS_QUERY_RESPONSE_PACKET_HEADER	0xfc
927a8d25c0SNathan Whitehorn 
937a8d25c0SNathan Whitehorn static int uart_phyp_probe(device_t dev);
947a8d25c0SNathan Whitehorn static int uart_phyp_attach(device_t dev);
957a8d25c0SNathan Whitehorn static void uart_phyp_intr(void *v);
967a8d25c0SNathan Whitehorn 
977a8d25c0SNathan Whitehorn static device_method_t uart_phyp_methods[] = {
987a8d25c0SNathan Whitehorn 	/* Device interface */
997a8d25c0SNathan Whitehorn 	DEVMETHOD(device_probe,		uart_phyp_probe),
1007a8d25c0SNathan Whitehorn 	DEVMETHOD(device_attach,	uart_phyp_attach),
1017a8d25c0SNathan Whitehorn 
1027a8d25c0SNathan Whitehorn 	DEVMETHOD_END
1037a8d25c0SNathan Whitehorn };
1047a8d25c0SNathan Whitehorn 
1057a8d25c0SNathan Whitehorn static driver_t uart_phyp_driver = {
1067a8d25c0SNathan Whitehorn 	"uart",
1077a8d25c0SNathan Whitehorn 	uart_phyp_methods,
1087a8d25c0SNathan Whitehorn 	sizeof(struct uart_phyp_softc),
1097a8d25c0SNathan Whitehorn };
1107a8d25c0SNathan Whitehorn 
111c90ea831SJohn Baldwin DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, 0, 0);
1127a8d25c0SNathan Whitehorn 
1137a8d25c0SNathan Whitehorn static cn_probe_t uart_phyp_cnprobe;
1147a8d25c0SNathan Whitehorn static cn_init_t uart_phyp_cninit;
1157a8d25c0SNathan Whitehorn static cn_term_t uart_phyp_cnterm;
1167a8d25c0SNathan Whitehorn static cn_getc_t uart_phyp_cngetc;
1177a8d25c0SNathan Whitehorn static cn_putc_t uart_phyp_cnputc;
1187a8d25c0SNathan Whitehorn static cn_grab_t uart_phyp_cngrab;
1197a8d25c0SNathan Whitehorn static cn_ungrab_t uart_phyp_cnungrab;
1207a8d25c0SNathan Whitehorn 
1217a8d25c0SNathan Whitehorn CONSOLE_DRIVER(uart_phyp);
1227a8d25c0SNathan Whitehorn 
1237a8d25c0SNathan Whitehorn static void uart_phyp_ttyoutwakeup(struct tty *tp);
1247a8d25c0SNathan Whitehorn 
1257a8d25c0SNathan Whitehorn static struct ttydevsw uart_phyp_tty_class = {
1267a8d25c0SNathan Whitehorn 	.tsw_flags	= TF_INITLOCK|TF_CALLOUT,
1277a8d25c0SNathan Whitehorn 	.tsw_outwakeup	= uart_phyp_ttyoutwakeup,
1287a8d25c0SNathan Whitehorn };
1297a8d25c0SNathan Whitehorn 
1307a8d25c0SNathan Whitehorn static int
uart_phyp_probe_node(struct uart_phyp_softc * sc)1317a8d25c0SNathan Whitehorn uart_phyp_probe_node(struct uart_phyp_softc *sc)
1327a8d25c0SNathan Whitehorn {
1337a8d25c0SNathan Whitehorn 	phandle_t node = sc->node;
1347a8d25c0SNathan Whitehorn 	uint32_t reg;
1357a8d25c0SNathan Whitehorn 	char buf[64];
1367a8d25c0SNathan Whitehorn 
1377a8d25c0SNathan Whitehorn 	sc->inbuflen = 0;
1387a8d25c0SNathan Whitehorn 	sc->outseqno = 0;
1397a8d25c0SNathan Whitehorn 
1407a8d25c0SNathan Whitehorn 	if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
1417a8d25c0SNathan Whitehorn 		return (ENXIO);
1427a8d25c0SNathan Whitehorn 	if (strcmp(buf, "vty") != 0)
1437a8d25c0SNathan Whitehorn 		return (ENXIO);
1447a8d25c0SNathan Whitehorn 
1457a8d25c0SNathan Whitehorn 	if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)
1467a8d25c0SNathan Whitehorn 		return (ENXIO);
1477a8d25c0SNathan Whitehorn 	if (strcmp(buf, "serial") != 0)
1487a8d25c0SNathan Whitehorn 		return (ENXIO);
1497a8d25c0SNathan Whitehorn 
1507a8d25c0SNathan Whitehorn 	reg = -1;
151509142e1SNathan Whitehorn 	OF_getencprop(node, "reg", &reg, sizeof(reg));
1527a8d25c0SNathan Whitehorn 	if (reg == -1)
1537a8d25c0SNathan Whitehorn 		return (ENXIO);
154145341e9SNathan Whitehorn 	sc->vtermid = reg;
1557a8d25c0SNathan Whitehorn 	sc->node = node;
1567a8d25c0SNathan Whitehorn 
1577a8d25c0SNathan Whitehorn 	if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)
1587a8d25c0SNathan Whitehorn 		return (ENXIO);
1597a8d25c0SNathan Whitehorn 	if (strcmp(buf, "hvterm1") == 0) {
1607a8d25c0SNathan Whitehorn 		sc->protocol = HVTERM1;
1617a8d25c0SNathan Whitehorn 		return (0);
1627a8d25c0SNathan Whitehorn 	} else if (strcmp(buf, "hvterm-protocol") == 0) {
1637a8d25c0SNathan Whitehorn 		sc->protocol = HVTERMPROT;
1647a8d25c0SNathan Whitehorn 		return (0);
1657a8d25c0SNathan Whitehorn 	}
1667a8d25c0SNathan Whitehorn 
1677a8d25c0SNathan Whitehorn 	return (ENXIO);
1687a8d25c0SNathan Whitehorn }
1697a8d25c0SNathan Whitehorn 
1707a8d25c0SNathan Whitehorn static int
uart_phyp_probe(device_t dev)1717a8d25c0SNathan Whitehorn uart_phyp_probe(device_t dev)
1727a8d25c0SNathan Whitehorn {
1737a8d25c0SNathan Whitehorn 	const char *name;
1747a8d25c0SNathan Whitehorn 	struct uart_phyp_softc sc;
1757a8d25c0SNathan Whitehorn 	int err;
1767a8d25c0SNathan Whitehorn 
1777a8d25c0SNathan Whitehorn 	name = ofw_bus_get_name(dev);
1787a8d25c0SNathan Whitehorn 	if (name == NULL || strcmp(name, "vty") != 0)
1797a8d25c0SNathan Whitehorn 		return (ENXIO);
1807a8d25c0SNathan Whitehorn 
1817a8d25c0SNathan Whitehorn 	sc.node = ofw_bus_get_node(dev);
1827a8d25c0SNathan Whitehorn 	err = uart_phyp_probe_node(&sc);
1837a8d25c0SNathan Whitehorn 	if (err != 0)
1847a8d25c0SNathan Whitehorn 		return (err);
1857a8d25c0SNathan Whitehorn 
1867a8d25c0SNathan Whitehorn 	device_set_desc(dev, "POWER Hypervisor Virtual Serial Port");
1877a8d25c0SNathan Whitehorn 
1887a8d25c0SNathan Whitehorn 	return (err);
1897a8d25c0SNathan Whitehorn }
1907a8d25c0SNathan Whitehorn 
1917a8d25c0SNathan Whitehorn static void
uart_phyp_cnprobe(struct consdev * cp)1927a8d25c0SNathan Whitehorn uart_phyp_cnprobe(struct consdev *cp)
1937a8d25c0SNathan Whitehorn {
1947a8d25c0SNathan Whitehorn 	char buf[64];
1957a8d25c0SNathan Whitehorn 	ihandle_t stdout;
1969b00150bSNathan Whitehorn 	phandle_t input, chosen;
1977a8d25c0SNathan Whitehorn 	static struct uart_phyp_softc sc;
1987a8d25c0SNathan Whitehorn 
1997a8d25c0SNathan Whitehorn 	if ((chosen = OF_finddevice("/chosen")) == -1)
2007a8d25c0SNathan Whitehorn 		goto fail;
2017a8d25c0SNathan Whitehorn 
2027a8d25c0SNathan Whitehorn 	/* Check if OF has an active stdin/stdout */
2037a8d25c0SNathan Whitehorn 	input = -1;
204509142e1SNathan Whitehorn 	if (OF_getencprop(chosen, "stdout", &stdout,
2057a8d25c0SNathan Whitehorn 	    sizeof(stdout)) == sizeof(stdout) && stdout != 0)
2067a8d25c0SNathan Whitehorn 		input = OF_instance_to_package(stdout);
2077a8d25c0SNathan Whitehorn 	if (input == -1)
2087a8d25c0SNathan Whitehorn 		goto fail;
2097a8d25c0SNathan Whitehorn 
2107a8d25c0SNathan Whitehorn 	if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1)
2117a8d25c0SNathan Whitehorn 		goto fail;
2127a8d25c0SNathan Whitehorn 	if (strcmp(buf, "serial") != 0)
2137a8d25c0SNathan Whitehorn 		goto fail;
2147a8d25c0SNathan Whitehorn 
2157a8d25c0SNathan Whitehorn 	sc.node = input;
2167a8d25c0SNathan Whitehorn 	if (uart_phyp_probe_node(&sc) != 0)
2177a8d25c0SNathan Whitehorn 		goto fail;
2187a8d25c0SNathan Whitehorn 	mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET |
2197a8d25c0SNathan Whitehorn 	    MTX_NOWITNESS);
2207a8d25c0SNathan Whitehorn 
2217a8d25c0SNathan Whitehorn 	cp->cn_pri = CN_NORMAL;
2227a8d25c0SNathan Whitehorn 	console_sc = &sc;
2237a8d25c0SNathan Whitehorn 	return;
2247a8d25c0SNathan Whitehorn 
2257a8d25c0SNathan Whitehorn fail:
2267a8d25c0SNathan Whitehorn 	cp->cn_pri = CN_DEAD;
2277a8d25c0SNathan Whitehorn 	return;
2287a8d25c0SNathan Whitehorn }
2297a8d25c0SNathan Whitehorn 
2307a8d25c0SNathan Whitehorn static int
uart_phyp_attach(device_t dev)2317a8d25c0SNathan Whitehorn uart_phyp_attach(device_t dev)
2327a8d25c0SNathan Whitehorn {
2337a8d25c0SNathan Whitehorn 	struct uart_phyp_softc *sc;
2347a8d25c0SNathan Whitehorn 	int unit;
2357a8d25c0SNathan Whitehorn 
2367a8d25c0SNathan Whitehorn 	sc = device_get_softc(dev);
2377a8d25c0SNathan Whitehorn 	sc->dev = dev;
2387a8d25c0SNathan Whitehorn 	sc->node = ofw_bus_get_node(dev);
2397a8d25c0SNathan Whitehorn 	uart_phyp_probe_node(sc);
2407a8d25c0SNathan Whitehorn 
2417a8d25c0SNathan Whitehorn 	unit = device_get_unit(dev);
2427a8d25c0SNathan Whitehorn 	sc->tp = tty_alloc(&uart_phyp_tty_class, sc);
2437a8d25c0SNathan Whitehorn 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,
2447a8d25c0SNathan Whitehorn 	    MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);
2457a8d25c0SNathan Whitehorn 
2467a8d25c0SNathan Whitehorn 	if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {
2477a8d25c0SNathan Whitehorn 		sc->outseqno = console_sc->outseqno;
2487a8d25c0SNathan Whitehorn 		console_sc = sc;
2497a8d25c0SNathan Whitehorn 		sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit);
2507a8d25c0SNathan Whitehorn 		tty_init_console(sc->tp, 0);
2517a8d25c0SNathan Whitehorn 	}
2527a8d25c0SNathan Whitehorn 
2537a8d25c0SNathan Whitehorn 	sc->irqrid = 0;
2547a8d25c0SNathan Whitehorn 	sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,
2557a8d25c0SNathan Whitehorn 	    RF_ACTIVE | RF_SHAREABLE);
2567a8d25c0SNathan Whitehorn 	if (sc->irqres != NULL) {
2577a8d25c0SNathan Whitehorn 		bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,
2587a8d25c0SNathan Whitehorn 		    NULL, uart_phyp_intr, sc, &sc->sc_icookie);
2597a8d25c0SNathan Whitehorn 	} else {
260fd90e2edSJung-uk Kim 		callout_init(&sc->callout, 1);
2617a8d25c0SNathan Whitehorn 		sc->polltime = hz / 20;
2627a8d25c0SNathan Whitehorn 		if (sc->polltime < 1)
2637a8d25c0SNathan Whitehorn 			sc->polltime = 1;
2647a8d25c0SNathan Whitehorn 		callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
2657a8d25c0SNathan Whitehorn 	}
2667a8d25c0SNathan Whitehorn 
2677a8d25c0SNathan Whitehorn 	tty_makedev(sc->tp, NULL, "u%r", unit);
2687a8d25c0SNathan Whitehorn 
2697a8d25c0SNathan Whitehorn 	return (0);
2707a8d25c0SNathan Whitehorn }
2717a8d25c0SNathan Whitehorn 
2727a8d25c0SNathan Whitehorn static void
uart_phyp_cninit(struct consdev * cp)2737a8d25c0SNathan Whitehorn uart_phyp_cninit(struct consdev *cp)
2747a8d25c0SNathan Whitehorn {
2757a8d25c0SNathan Whitehorn 
2767a8d25c0SNathan Whitehorn 	strcpy(cp->cn_name, "phypcons");
2777a8d25c0SNathan Whitehorn }
2787a8d25c0SNathan Whitehorn 
2797a8d25c0SNathan Whitehorn static void
uart_phyp_cnterm(struct consdev * cp)2807a8d25c0SNathan Whitehorn uart_phyp_cnterm(struct consdev *cp)
2817a8d25c0SNathan Whitehorn {
2827a8d25c0SNathan Whitehorn }
2837a8d25c0SNathan Whitehorn 
2847a8d25c0SNathan Whitehorn static int
uart_phyp_get(struct uart_phyp_softc * sc,void * buffer,size_t bufsize)2857a8d25c0SNathan Whitehorn uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
2867a8d25c0SNathan Whitehorn {
2877a8d25c0SNathan Whitehorn 	int err;
28890653c1cSAndreas Tobler 	int hdr = 0;
2890b0c23feSLeandro Lupori 	uint64_t i, j;
2907a8d25c0SNathan Whitehorn 
2917a8d25c0SNathan Whitehorn 	uart_lock(&sc->sc_mtx);
2927a8d25c0SNathan Whitehorn 	if (sc->inbuflen == 0) {
2937a8d25c0SNathan Whitehorn 		err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid,
2947a8d25c0SNathan Whitehorn 		    0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0],
2957a8d25c0SNathan Whitehorn 		    &sc->phyp_inbuf.u64[1]);
2965001c579SBrandon Bergren #if BYTE_ORDER == LITTLE_ENDIAN
2975001c579SBrandon Bergren 		sc->phyp_inbuf.u64[0] = be64toh(sc->phyp_inbuf.u64[0]);
2985001c579SBrandon Bergren 		sc->phyp_inbuf.u64[1] = be64toh(sc->phyp_inbuf.u64[1]);
2995001c579SBrandon Bergren #endif
3007a8d25c0SNathan Whitehorn 		if (err != H_SUCCESS) {
3017a8d25c0SNathan Whitehorn 			uart_unlock(&sc->sc_mtx);
3027a8d25c0SNathan Whitehorn 			return (-1);
3037a8d25c0SNathan Whitehorn 		}
30490653c1cSAndreas Tobler 		hdr = 1;
3057a8d25c0SNathan Whitehorn 	}
3067a8d25c0SNathan Whitehorn 
3077a8d25c0SNathan Whitehorn 	if (sc->inbuflen == 0) {
3087a8d25c0SNathan Whitehorn 		uart_unlock(&sc->sc_mtx);
3097a8d25c0SNathan Whitehorn 		return (0);
3107a8d25c0SNathan Whitehorn 	}
3117a8d25c0SNathan Whitehorn 
31290653c1cSAndreas Tobler 	if ((sc->protocol == HVTERMPROT) && (hdr == 1)) {
31390653c1cSAndreas Tobler 		sc->inbuflen = sc->inbuflen - 4;
31490653c1cSAndreas Tobler 		/* The VTERM protocol has a 4 byte header, skip it here. */
31590653c1cSAndreas Tobler 		memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4],
31690653c1cSAndreas Tobler 		    sc->inbuflen);
31790653c1cSAndreas Tobler 	}
31890653c1cSAndreas Tobler 
3190b0c23feSLeandro Lupori 	/*
3200b0c23feSLeandro Lupori 	 * Since version 2.11.0, QEMU became bug-compatible with
3210b0c23feSLeandro Lupori 	 * PowerVM's vty implementation, by inserting a \0 after
3220b0c23feSLeandro Lupori 	 * every \r going to the guest. Guests are expected to
3230b0c23feSLeandro Lupori 	 * workaround this issue by removing every \0 immediately
3240b0c23feSLeandro Lupori 	 * following a \r.
3250b0c23feSLeandro Lupori 	 */
3260b0c23feSLeandro Lupori 	if (hdr == 1) {
3270b0c23feSLeandro Lupori 		for (i = 0, j = 0; i < sc->inbuflen; i++, j++) {
3280b0c23feSLeandro Lupori 			if (i > j)
3290b0c23feSLeandro Lupori 				sc->phyp_inbuf.str[j] = sc->phyp_inbuf.str[i];
3300b0c23feSLeandro Lupori 
3310b0c23feSLeandro Lupori 			if (sc->phyp_inbuf.str[i] == '\r' &&
3320b0c23feSLeandro Lupori 			    i < sc->inbuflen - 1 &&
3330b0c23feSLeandro Lupori 			    sc->phyp_inbuf.str[i + 1] == '\0')
3340b0c23feSLeandro Lupori 				i++;
3350b0c23feSLeandro Lupori 		}
3360b0c23feSLeandro Lupori 		sc->inbuflen -= i - j;
3370b0c23feSLeandro Lupori 	}
3380b0c23feSLeandro Lupori 
3390b0c23feSLeandro Lupori 	if (bufsize > sc->inbuflen)
3400b0c23feSLeandro Lupori 		bufsize = sc->inbuflen;
3410b0c23feSLeandro Lupori 
3427a8d25c0SNathan Whitehorn 	memcpy(buffer, sc->phyp_inbuf.str, bufsize);
3437a8d25c0SNathan Whitehorn 	sc->inbuflen -= bufsize;
3447a8d25c0SNathan Whitehorn 	if (sc->inbuflen > 0)
3457a8d25c0SNathan Whitehorn 		memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize],
3467a8d25c0SNathan Whitehorn 		    sc->inbuflen);
3477a8d25c0SNathan Whitehorn 
3487a8d25c0SNathan Whitehorn 	uart_unlock(&sc->sc_mtx);
3497a8d25c0SNathan Whitehorn 	return (bufsize);
3507a8d25c0SNathan Whitehorn }
3517a8d25c0SNathan Whitehorn 
3527a8d25c0SNathan Whitehorn static int
uart_phyp_put(struct uart_phyp_softc * sc,void * buffer,size_t bufsize)3537a8d25c0SNathan Whitehorn uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
3547a8d25c0SNathan Whitehorn {
3557a8d25c0SNathan Whitehorn 	uint16_t seqno;
3567a8d25c0SNathan Whitehorn 	uint64_t len = 0;
35790653c1cSAndreas Tobler 	int	err;
35890653c1cSAndreas Tobler 
3597a8d25c0SNathan Whitehorn 	union {
36090653c1cSAndreas Tobler 		uint64_t u64[2];
36190653c1cSAndreas Tobler 		char bytes[16];
3627a8d25c0SNathan Whitehorn 	} cbuf;
3637a8d25c0SNathan Whitehorn 
3647a8d25c0SNathan Whitehorn 	uart_lock(&sc->sc_mtx);
3657a8d25c0SNathan Whitehorn 	switch (sc->protocol) {
3667a8d25c0SNathan Whitehorn 	case HVTERM1:
36790653c1cSAndreas Tobler 		if (bufsize > 16)
36890653c1cSAndreas Tobler 			bufsize = 16;
3697a8d25c0SNathan Whitehorn 		memcpy(&cbuf, buffer, bufsize);
3707a8d25c0SNathan Whitehorn 		len = bufsize;
3717a8d25c0SNathan Whitehorn 		break;
3727a8d25c0SNathan Whitehorn 	case HVTERMPROT:
37390653c1cSAndreas Tobler 		if (bufsize > 12)
37490653c1cSAndreas Tobler 			bufsize = 12;
3757a8d25c0SNathan Whitehorn 		seqno = sc->outseqno++;
3767a8d25c0SNathan Whitehorn 		cbuf.bytes[0] = VS_DATA_PACKET_HEADER;
37790653c1cSAndreas Tobler 		cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */
3787a8d25c0SNathan Whitehorn 		cbuf.bytes[2] = (seqno >> 8) & 0xff;
3797a8d25c0SNathan Whitehorn 		cbuf.bytes[3] = seqno & 0xff;
3807a8d25c0SNathan Whitehorn 		memcpy(&cbuf.bytes[4], buffer, bufsize);
3817a8d25c0SNathan Whitehorn 		len = 4 + bufsize;
3827a8d25c0SNathan Whitehorn 		break;
3837a8d25c0SNathan Whitehorn 	}
38490653c1cSAndreas Tobler 
38590653c1cSAndreas Tobler 	do {
38615be37cbSBrandon Bergren 	    err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, htobe64(cbuf.u64[0]),
38715be37cbSBrandon Bergren 			    htobe64(cbuf.u64[1]));
38890653c1cSAndreas Tobler 		DELAY(100);
38990653c1cSAndreas Tobler 	} while (err == H_BUSY);
39090653c1cSAndreas Tobler 
3917a8d25c0SNathan Whitehorn 	uart_unlock(&sc->sc_mtx);
3927a8d25c0SNathan Whitehorn 
3937a8d25c0SNathan Whitehorn 	return (bufsize);
3947a8d25c0SNathan Whitehorn }
3957a8d25c0SNathan Whitehorn 
3967a8d25c0SNathan Whitehorn static int
uart_phyp_cngetc(struct consdev * cp)3977a8d25c0SNathan Whitehorn uart_phyp_cngetc(struct consdev *cp)
3987a8d25c0SNathan Whitehorn {
3997a8d25c0SNathan Whitehorn 	unsigned char c;
4007a8d25c0SNathan Whitehorn 	int retval;
4017a8d25c0SNathan Whitehorn 
4027a8d25c0SNathan Whitehorn 	retval = uart_phyp_get(console_sc, &c, 1);
4037a8d25c0SNathan Whitehorn 	if (retval != 1)
4047a8d25c0SNathan Whitehorn 		return (-1);
4057a8d25c0SNathan Whitehorn #if defined(KDB)
4067a8d25c0SNathan Whitehorn 	kdb_alt_break(c, &alt_break_state);
4077a8d25c0SNathan Whitehorn #endif
4087a8d25c0SNathan Whitehorn 
4097a8d25c0SNathan Whitehorn 	return (c);
4107a8d25c0SNathan Whitehorn }
4117a8d25c0SNathan Whitehorn 
4127a8d25c0SNathan Whitehorn static void
uart_phyp_cnputc(struct consdev * cp,int c)4137a8d25c0SNathan Whitehorn uart_phyp_cnputc(struct consdev *cp, int c)
4147a8d25c0SNathan Whitehorn {
4157a8d25c0SNathan Whitehorn 	unsigned char ch = c;
4167a8d25c0SNathan Whitehorn 	uart_phyp_put(console_sc, &ch, 1);
4177a8d25c0SNathan Whitehorn }
4187a8d25c0SNathan Whitehorn 
4197a8d25c0SNathan Whitehorn static void
uart_phyp_cngrab(struct consdev * cp)4207a8d25c0SNathan Whitehorn uart_phyp_cngrab(struct consdev *cp)
4217a8d25c0SNathan Whitehorn {
4227a8d25c0SNathan Whitehorn }
4237a8d25c0SNathan Whitehorn 
4247a8d25c0SNathan Whitehorn static void
uart_phyp_cnungrab(struct consdev * cp)4257a8d25c0SNathan Whitehorn uart_phyp_cnungrab(struct consdev *cp)
4267a8d25c0SNathan Whitehorn {
4277a8d25c0SNathan Whitehorn }
4287a8d25c0SNathan Whitehorn 
4297a8d25c0SNathan Whitehorn static void
uart_phyp_ttyoutwakeup(struct tty * tp)4307a8d25c0SNathan Whitehorn uart_phyp_ttyoutwakeup(struct tty *tp)
4317a8d25c0SNathan Whitehorn {
4327a8d25c0SNathan Whitehorn 	struct uart_phyp_softc *sc;
4337a8d25c0SNathan Whitehorn 	char buffer[8];
4347a8d25c0SNathan Whitehorn 	int len;
4357a8d25c0SNathan Whitehorn 
4367a8d25c0SNathan Whitehorn 	sc = tty_softc(tp);
4377a8d25c0SNathan Whitehorn 
4387a8d25c0SNathan Whitehorn 	while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0)
4397a8d25c0SNathan Whitehorn 		uart_phyp_put(sc, buffer, len);
4407a8d25c0SNathan Whitehorn }
4417a8d25c0SNathan Whitehorn 
4427a8d25c0SNathan Whitehorn static void
uart_phyp_intr(void * v)4437a8d25c0SNathan Whitehorn uart_phyp_intr(void *v)
4447a8d25c0SNathan Whitehorn {
4457a8d25c0SNathan Whitehorn 	struct uart_phyp_softc *sc = v;
4467a8d25c0SNathan Whitehorn 	struct tty *tp = sc->tp;
4477a8d25c0SNathan Whitehorn 	unsigned char c;
4487a8d25c0SNathan Whitehorn 	int len;
4497a8d25c0SNathan Whitehorn 
4507a8d25c0SNathan Whitehorn 	tty_lock(tp);
4517a8d25c0SNathan Whitehorn 	while ((len = uart_phyp_get(sc, &c, 1)) > 0)
4527a8d25c0SNathan Whitehorn 		ttydisc_rint(tp, c, 0);
4537a8d25c0SNathan Whitehorn 	ttydisc_rint_done(tp);
4547a8d25c0SNathan Whitehorn 	tty_unlock(tp);
4557a8d25c0SNathan Whitehorn 
4567a8d25c0SNathan Whitehorn 	if (sc->irqres == NULL)
4577a8d25c0SNathan Whitehorn 		callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
4587a8d25c0SNathan Whitehorn }
459