Path: blob/main/sys/powerpc/pseries/phyp_console.c
39507 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (C) 2011 by Nathan Whitehorn. All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,18* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,19* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;20* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,21* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR22* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF23* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526#include <sys/cdefs.h>27#include <sys/endian.h>28#include <sys/param.h>29#include <sys/kdb.h>30#include <sys/kernel.h>31#include <sys/priv.h>32#include <sys/systm.h>33#include <sys/module.h>34#include <sys/types.h>35#include <sys/conf.h>36#include <sys/cons.h>37#include <sys/tty.h>38#include <machine/bus.h>3940#include <dev/ofw/openfirm.h>41#include <dev/ofw/ofw_bus.h>42#include <dev/ofw/ofw_bus_subr.h>43#include <dev/uart/uart.h>44#include <dev/uart/uart_cpu.h>45#include <dev/uart/uart_bus.h>4647#include "phyp-hvcall.h"48#include "uart_if.h"4950struct uart_phyp_softc {51device_t dev;52phandle_t node;53int vtermid;5455struct tty *tp;56struct resource *irqres;57int irqrid;58struct callout callout;59void *sc_icookie;60int polltime;6162struct mtx sc_mtx;63int protocol;6465union {66uint64_t u64[2];67char str[16];68} phyp_inbuf;69uint64_t inbuflen;70uint8_t outseqno;71};7273static struct uart_phyp_softc *console_sc = NULL;74#if defined(KDB)75static int alt_break_state;76#endif7778enum {79HVTERM1, HVTERMPROT80};8182#define VS_DATA_PACKET_HEADER 0xff83#define VS_CONTROL_PACKET_HEADER 0xfe84#define VSV_SET_MODEM_CTL 0x0185#define VSV_MODEM_CTL_UPDATE 0x0286#define VSV_RENEGOTIATE_CONNECTION 0x0387#define VS_QUERY_PACKET_HEADER 0xfd88#define VSV_SEND_VERSION_NUMBER 0x0189#define VSV_SEND_MODEM_CTL_STATUS 0x0290#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc9192static int uart_phyp_probe(device_t dev);93static int uart_phyp_attach(device_t dev);94static void uart_phyp_intr(void *v);9596static device_method_t uart_phyp_methods[] = {97/* Device interface */98DEVMETHOD(device_probe, uart_phyp_probe),99DEVMETHOD(device_attach, uart_phyp_attach),100101DEVMETHOD_END102};103104static driver_t uart_phyp_driver = {105"uart",106uart_phyp_methods,107sizeof(struct uart_phyp_softc),108};109110DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, 0, 0);111112static cn_probe_t uart_phyp_cnprobe;113static cn_init_t uart_phyp_cninit;114static cn_term_t uart_phyp_cnterm;115static cn_getc_t uart_phyp_cngetc;116static cn_putc_t uart_phyp_cnputc;117static cn_grab_t uart_phyp_cngrab;118static cn_ungrab_t uart_phyp_cnungrab;119120CONSOLE_DRIVER(uart_phyp);121122static void uart_phyp_ttyoutwakeup(struct tty *tp);123124static struct ttydevsw uart_phyp_tty_class = {125.tsw_flags = TF_INITLOCK|TF_CALLOUT,126.tsw_outwakeup = uart_phyp_ttyoutwakeup,127};128129static int130uart_phyp_probe_node(struct uart_phyp_softc *sc)131{132phandle_t node = sc->node;133uint32_t reg;134char buf[64];135136sc->inbuflen = 0;137sc->outseqno = 0;138139if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)140return (ENXIO);141if (strcmp(buf, "vty") != 0)142return (ENXIO);143144if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)145return (ENXIO);146if (strcmp(buf, "serial") != 0)147return (ENXIO);148149reg = -1;150OF_getencprop(node, "reg", ®, sizeof(reg));151if (reg == -1)152return (ENXIO);153sc->vtermid = reg;154sc->node = node;155156if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)157return (ENXIO);158if (strcmp(buf, "hvterm1") == 0) {159sc->protocol = HVTERM1;160return (0);161} else if (strcmp(buf, "hvterm-protocol") == 0) {162sc->protocol = HVTERMPROT;163return (0);164}165166return (ENXIO);167}168169static int170uart_phyp_probe(device_t dev)171{172const char *name;173struct uart_phyp_softc sc;174int err;175176name = ofw_bus_get_name(dev);177if (name == NULL || strcmp(name, "vty") != 0)178return (ENXIO);179180sc.node = ofw_bus_get_node(dev);181err = uart_phyp_probe_node(&sc);182if (err != 0)183return (err);184185device_set_desc(dev, "POWER Hypervisor Virtual Serial Port");186187return (err);188}189190static void191uart_phyp_cnprobe(struct consdev *cp)192{193char buf[64];194ihandle_t stdout;195phandle_t input, chosen;196static struct uart_phyp_softc sc;197198if ((chosen = OF_finddevice("/chosen")) == -1)199goto fail;200201/* Check if OF has an active stdin/stdout */202input = -1;203if (OF_getencprop(chosen, "stdout", &stdout,204sizeof(stdout)) == sizeof(stdout) && stdout != 0)205input = OF_instance_to_package(stdout);206if (input == -1)207goto fail;208209if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1)210goto fail;211if (strcmp(buf, "serial") != 0)212goto fail;213214sc.node = input;215if (uart_phyp_probe_node(&sc) != 0)216goto fail;217mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET |218MTX_NOWITNESS);219220cp->cn_pri = CN_NORMAL;221console_sc = ≻222return;223224fail:225cp->cn_pri = CN_DEAD;226return;227}228229static int230uart_phyp_attach(device_t dev)231{232struct uart_phyp_softc *sc;233int unit;234235sc = device_get_softc(dev);236sc->dev = dev;237sc->node = ofw_bus_get_node(dev);238uart_phyp_probe_node(sc);239240unit = device_get_unit(dev);241sc->tp = tty_alloc(&uart_phyp_tty_class, sc);242mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,243MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);244245if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {246sc->outseqno = console_sc->outseqno;247console_sc = sc;248sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit);249tty_init_console(sc->tp, 0);250}251252sc->irqrid = 0;253sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,254RF_ACTIVE | RF_SHAREABLE);255if (sc->irqres != NULL) {256bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,257NULL, uart_phyp_intr, sc, &sc->sc_icookie);258} else {259callout_init(&sc->callout, 1);260sc->polltime = hz / 20;261if (sc->polltime < 1)262sc->polltime = 1;263callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);264}265266tty_makedev(sc->tp, NULL, "u%r", unit);267268return (0);269}270271static void272uart_phyp_cninit(struct consdev *cp)273{274275strcpy(cp->cn_name, "phypcons");276}277278static void279uart_phyp_cnterm(struct consdev *cp)280{281}282283static int284uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)285{286int err;287int hdr = 0;288uint64_t i, j;289290uart_lock(&sc->sc_mtx);291if (sc->inbuflen == 0) {292err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid,2930, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0],294&sc->phyp_inbuf.u64[1]);295#if BYTE_ORDER == LITTLE_ENDIAN296sc->phyp_inbuf.u64[0] = be64toh(sc->phyp_inbuf.u64[0]);297sc->phyp_inbuf.u64[1] = be64toh(sc->phyp_inbuf.u64[1]);298#endif299if (err != H_SUCCESS) {300uart_unlock(&sc->sc_mtx);301return (-1);302}303hdr = 1;304}305306if (sc->inbuflen == 0) {307uart_unlock(&sc->sc_mtx);308return (0);309}310311if ((sc->protocol == HVTERMPROT) && (hdr == 1)) {312sc->inbuflen = sc->inbuflen - 4;313/* The VTERM protocol has a 4 byte header, skip it here. */314memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4],315sc->inbuflen);316}317318/*319* Since version 2.11.0, QEMU became bug-compatible with320* PowerVM's vty implementation, by inserting a \0 after321* every \r going to the guest. Guests are expected to322* workaround this issue by removing every \0 immediately323* following a \r.324*/325if (hdr == 1) {326for (i = 0, j = 0; i < sc->inbuflen; i++, j++) {327if (i > j)328sc->phyp_inbuf.str[j] = sc->phyp_inbuf.str[i];329330if (sc->phyp_inbuf.str[i] == '\r' &&331i < sc->inbuflen - 1 &&332sc->phyp_inbuf.str[i + 1] == '\0')333i++;334}335sc->inbuflen -= i - j;336}337338if (bufsize > sc->inbuflen)339bufsize = sc->inbuflen;340341memcpy(buffer, sc->phyp_inbuf.str, bufsize);342sc->inbuflen -= bufsize;343if (sc->inbuflen > 0)344memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize],345sc->inbuflen);346347uart_unlock(&sc->sc_mtx);348return (bufsize);349}350351static int352uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)353{354uint16_t seqno;355uint64_t len = 0;356int err;357358union {359uint64_t u64[2];360char bytes[16];361} cbuf;362363uart_lock(&sc->sc_mtx);364switch (sc->protocol) {365case HVTERM1:366if (bufsize > 16)367bufsize = 16;368memcpy(&cbuf, buffer, bufsize);369len = bufsize;370break;371case HVTERMPROT:372if (bufsize > 12)373bufsize = 12;374seqno = sc->outseqno++;375cbuf.bytes[0] = VS_DATA_PACKET_HEADER;376cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */377cbuf.bytes[2] = (seqno >> 8) & 0xff;378cbuf.bytes[3] = seqno & 0xff;379memcpy(&cbuf.bytes[4], buffer, bufsize);380len = 4 + bufsize;381break;382}383384do {385err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, htobe64(cbuf.u64[0]),386htobe64(cbuf.u64[1]));387DELAY(100);388} while (err == H_BUSY);389390uart_unlock(&sc->sc_mtx);391392return (bufsize);393}394395static int396uart_phyp_cngetc(struct consdev *cp)397{398unsigned char c;399int retval;400401retval = uart_phyp_get(console_sc, &c, 1);402if (retval != 1)403return (-1);404#if defined(KDB)405kdb_alt_break(c, &alt_break_state);406#endif407408return (c);409}410411static void412uart_phyp_cnputc(struct consdev *cp, int c)413{414unsigned char ch = c;415uart_phyp_put(console_sc, &ch, 1);416}417418static void419uart_phyp_cngrab(struct consdev *cp)420{421}422423static void424uart_phyp_cnungrab(struct consdev *cp)425{426}427428static void429uart_phyp_ttyoutwakeup(struct tty *tp)430{431struct uart_phyp_softc *sc;432char buffer[8];433int len;434435sc = tty_softc(tp);436437while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0)438uart_phyp_put(sc, buffer, len);439}440441static void442uart_phyp_intr(void *v)443{444struct uart_phyp_softc *sc = v;445struct tty *tp = sc->tp;446unsigned char c;447int len;448449tty_lock(tp);450while ((len = uart_phyp_get(sc, &c, 1)) > 0)451ttydisc_rint(tp, c, 0);452ttydisc_rint_done(tp);453tty_unlock(tp);454455if (sc->irqres == NULL)456callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);457}458459460