The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/powerpc/pseries/phyp_console.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (C) 2011 by Nathan Whitehorn. All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include <sys/endian.h>
   31 #include <sys/param.h>
   32 #include <sys/kdb.h>
   33 #include <sys/kernel.h>
   34 #include <sys/priv.h>
   35 #include <sys/systm.h>
   36 #include <sys/module.h>
   37 #include <sys/types.h>
   38 #include <sys/conf.h>
   39 #include <sys/cons.h>
   40 #include <sys/tty.h>
   41 #include <machine/bus.h>
   42 
   43 #include <dev/ofw/openfirm.h>
   44 #include <dev/ofw/ofw_bus.h>
   45 #include <dev/ofw/ofw_bus_subr.h>
   46 #include <dev/uart/uart.h>
   47 #include <dev/uart/uart_cpu.h>
   48 #include <dev/uart/uart_bus.h>
   49 
   50 #include "phyp-hvcall.h"
   51 #include "uart_if.h"
   52 
   53 struct uart_phyp_softc {
   54         device_t dev;
   55         phandle_t node;
   56         int vtermid;
   57 
   58         struct tty *tp;
   59         struct resource *irqres;
   60         int irqrid;
   61         struct callout callout;
   62         void *sc_icookie;
   63         int polltime;
   64 
   65         struct mtx sc_mtx;
   66         int protocol;
   67 
   68         union {
   69                 uint64_t u64[2];
   70                 char str[16];
   71         } phyp_inbuf;
   72         uint64_t inbuflen;
   73         uint8_t outseqno;
   74 };
   75 
   76 static struct uart_phyp_softc   *console_sc = NULL;
   77 #if defined(KDB)
   78 static int                      alt_break_state;
   79 #endif
   80 
   81 enum {
   82         HVTERM1, HVTERMPROT
   83 };
   84 
   85 #define VS_DATA_PACKET_HEADER           0xff
   86 #define VS_CONTROL_PACKET_HEADER        0xfe
   87 #define  VSV_SET_MODEM_CTL              0x01
   88 #define  VSV_MODEM_CTL_UPDATE           0x02
   89 #define  VSV_RENEGOTIATE_CONNECTION     0x03
   90 #define VS_QUERY_PACKET_HEADER          0xfd
   91 #define  VSV_SEND_VERSION_NUMBER        0x01
   92 #define  VSV_SEND_MODEM_CTL_STATUS      0x02
   93 #define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc
   94 
   95 static int uart_phyp_probe(device_t dev);
   96 static int uart_phyp_attach(device_t dev);
   97 static void uart_phyp_intr(void *v);
   98 
   99 static device_method_t uart_phyp_methods[] = {
  100         /* Device interface */
  101         DEVMETHOD(device_probe,         uart_phyp_probe),
  102         DEVMETHOD(device_attach,        uart_phyp_attach),
  103 
  104         DEVMETHOD_END
  105 };
  106 
  107 static driver_t uart_phyp_driver = {
  108         "uart",
  109         uart_phyp_methods,
  110         sizeof(struct uart_phyp_softc),
  111 };
  112 
  113 DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, 0, 0);
  114 
  115 static cn_probe_t uart_phyp_cnprobe;
  116 static cn_init_t uart_phyp_cninit;
  117 static cn_term_t uart_phyp_cnterm;
  118 static cn_getc_t uart_phyp_cngetc;
  119 static cn_putc_t uart_phyp_cnputc;
  120 static cn_grab_t uart_phyp_cngrab;
  121 static cn_ungrab_t uart_phyp_cnungrab;
  122 
  123 CONSOLE_DRIVER(uart_phyp);
  124 
  125 static void uart_phyp_ttyoutwakeup(struct tty *tp);
  126 
  127 static struct ttydevsw uart_phyp_tty_class = {
  128         .tsw_flags      = TF_INITLOCK|TF_CALLOUT,
  129         .tsw_outwakeup  = uart_phyp_ttyoutwakeup,
  130 };
  131 
  132 static int
  133 uart_phyp_probe_node(struct uart_phyp_softc *sc)
  134 {
  135         phandle_t node = sc->node;
  136         uint32_t reg;
  137         char buf[64];
  138 
  139         sc->inbuflen = 0;
  140         sc->outseqno = 0;
  141 
  142         if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
  143                 return (ENXIO);
  144         if (strcmp(buf, "vty") != 0)
  145                 return (ENXIO);
  146 
  147         if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)
  148                 return (ENXIO);
  149         if (strcmp(buf, "serial") != 0)
  150                 return (ENXIO);
  151 
  152         reg = -1;
  153         OF_getencprop(node, "reg", &reg, sizeof(reg));
  154         if (reg == -1)
  155                 return (ENXIO);
  156         sc->vtermid = reg;
  157         sc->node = node;
  158 
  159         if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)
  160                 return (ENXIO);
  161         if (strcmp(buf, "hvterm1") == 0) {
  162                 sc->protocol = HVTERM1;
  163                 return (0);
  164         } else if (strcmp(buf, "hvterm-protocol") == 0) {
  165                 sc->protocol = HVTERMPROT;
  166                 return (0);
  167         }
  168 
  169         return (ENXIO);
  170 }
  171 
  172 static int
  173 uart_phyp_probe(device_t dev)
  174 {
  175         const char *name;
  176         struct uart_phyp_softc sc;
  177         int err;
  178 
  179         name = ofw_bus_get_name(dev);
  180         if (name == NULL || strcmp(name, "vty") != 0)
  181                 return (ENXIO);
  182 
  183         sc.node = ofw_bus_get_node(dev);
  184         err = uart_phyp_probe_node(&sc);
  185         if (err != 0)
  186                 return (err);
  187 
  188         device_set_desc(dev, "POWER Hypervisor Virtual Serial Port");
  189 
  190         return (err);
  191 }
  192 
  193 static void
  194 uart_phyp_cnprobe(struct consdev *cp)
  195 {
  196         char buf[64];
  197         ihandle_t stdout;
  198         phandle_t input, chosen;
  199         static struct uart_phyp_softc sc;
  200 
  201         if ((chosen = OF_finddevice("/chosen")) == -1)
  202                 goto fail;
  203 
  204         /* Check if OF has an active stdin/stdout */
  205         input = -1;
  206         if (OF_getencprop(chosen, "stdout", &stdout,
  207             sizeof(stdout)) == sizeof(stdout) && stdout != 0)
  208                 input = OF_instance_to_package(stdout);
  209         if (input == -1)
  210                 goto fail;
  211 
  212         if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1)
  213                 goto fail;
  214         if (strcmp(buf, "serial") != 0)
  215                 goto fail;
  216 
  217         sc.node = input;
  218         if (uart_phyp_probe_node(&sc) != 0)
  219                 goto fail;
  220         mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET |
  221             MTX_NOWITNESS);
  222 
  223         cp->cn_pri = CN_NORMAL;
  224         console_sc = &sc;
  225         return;
  226 
  227 fail:
  228         cp->cn_pri = CN_DEAD;
  229         return;
  230 }
  231 
  232 static int
  233 uart_phyp_attach(device_t dev)
  234 {
  235         struct uart_phyp_softc *sc;
  236         int unit;
  237 
  238         sc = device_get_softc(dev);
  239         sc->dev = dev;
  240         sc->node = ofw_bus_get_node(dev);
  241         uart_phyp_probe_node(sc);
  242 
  243         unit = device_get_unit(dev);
  244         sc->tp = tty_alloc(&uart_phyp_tty_class, sc);
  245         mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,
  246             MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);
  247 
  248         if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {
  249                 sc->outseqno = console_sc->outseqno;
  250                 console_sc = sc;
  251                 sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit);
  252                 tty_init_console(sc->tp, 0);
  253         }
  254 
  255         sc->irqrid = 0;
  256         sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,
  257             RF_ACTIVE | RF_SHAREABLE);
  258         if (sc->irqres != NULL) {
  259                 bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,
  260                     NULL, uart_phyp_intr, sc, &sc->sc_icookie);
  261         } else {
  262                 callout_init(&sc->callout, 1);
  263                 sc->polltime = hz / 20;
  264                 if (sc->polltime < 1)
  265                         sc->polltime = 1;
  266                 callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
  267         }
  268 
  269         tty_makedev(sc->tp, NULL, "u%r", unit);
  270 
  271         return (0);
  272 }
  273 
  274 static void
  275 uart_phyp_cninit(struct consdev *cp)
  276 {
  277 
  278         strcpy(cp->cn_name, "phypcons");
  279 }
  280 
  281 static void
  282 uart_phyp_cnterm(struct consdev *cp)
  283 {
  284 }
  285 
  286 static int
  287 uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
  288 {
  289         int err;
  290         int hdr = 0;
  291         uint64_t i, j;
  292 
  293         uart_lock(&sc->sc_mtx);
  294         if (sc->inbuflen == 0) {
  295                 err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid,
  296                     0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0],
  297                     &sc->phyp_inbuf.u64[1]);
  298 #if BYTE_ORDER == LITTLE_ENDIAN
  299                 sc->phyp_inbuf.u64[0] = be64toh(sc->phyp_inbuf.u64[0]);
  300                 sc->phyp_inbuf.u64[1] = be64toh(sc->phyp_inbuf.u64[1]);
  301 #endif
  302                 if (err != H_SUCCESS) {
  303                         uart_unlock(&sc->sc_mtx);
  304                         return (-1);
  305                 }
  306                 hdr = 1;
  307         }
  308 
  309         if (sc->inbuflen == 0) {
  310                 uart_unlock(&sc->sc_mtx);
  311                 return (0);
  312         }
  313 
  314         if ((sc->protocol == HVTERMPROT) && (hdr == 1)) {
  315                 sc->inbuflen = sc->inbuflen - 4;
  316                 /* The VTERM protocol has a 4 byte header, skip it here. */
  317                 memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4],
  318                     sc->inbuflen);
  319         }
  320 
  321         /*
  322          * Since version 2.11.0, QEMU became bug-compatible with
  323          * PowerVM's vty implementation, by inserting a \0 after
  324          * every \r going to the guest. Guests are expected to
  325          * workaround this issue by removing every \0 immediately
  326          * following a \r.
  327          */
  328         if (hdr == 1) {
  329                 for (i = 0, j = 0; i < sc->inbuflen; i++, j++) {
  330                         if (i > j)
  331                                 sc->phyp_inbuf.str[j] = sc->phyp_inbuf.str[i];
  332 
  333                         if (sc->phyp_inbuf.str[i] == '\r' &&
  334                             i < sc->inbuflen - 1 &&
  335                             sc->phyp_inbuf.str[i + 1] == '\0')
  336                                 i++;
  337                 }
  338                 sc->inbuflen -= i - j;
  339         }
  340 
  341         if (bufsize > sc->inbuflen)
  342                 bufsize = sc->inbuflen;
  343 
  344         memcpy(buffer, sc->phyp_inbuf.str, bufsize);
  345         sc->inbuflen -= bufsize;
  346         if (sc->inbuflen > 0)
  347                 memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize],
  348                     sc->inbuflen);
  349 
  350         uart_unlock(&sc->sc_mtx);
  351         return (bufsize);
  352 }
  353 
  354 static int
  355 uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
  356 {
  357         uint16_t seqno;
  358         uint64_t len = 0;
  359         int     err;
  360 
  361         union {
  362                 uint64_t u64[2];
  363                 char bytes[16];
  364         } cbuf;
  365 
  366         uart_lock(&sc->sc_mtx);
  367         switch (sc->protocol) {
  368         case HVTERM1:
  369                 if (bufsize > 16)
  370                         bufsize = 16;
  371                 memcpy(&cbuf, buffer, bufsize);
  372                 len = bufsize;
  373                 break;
  374         case HVTERMPROT:
  375                 if (bufsize > 12)
  376                         bufsize = 12;
  377                 seqno = sc->outseqno++;
  378                 cbuf.bytes[0] = VS_DATA_PACKET_HEADER;
  379                 cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */
  380                 cbuf.bytes[2] = (seqno >> 8) & 0xff;
  381                 cbuf.bytes[3] = seqno & 0xff;
  382                 memcpy(&cbuf.bytes[4], buffer, bufsize);
  383                 len = 4 + bufsize;
  384                 break;
  385         }
  386 
  387         do {
  388             err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, htobe64(cbuf.u64[0]),
  389                             htobe64(cbuf.u64[1]));
  390                 DELAY(100);
  391         } while (err == H_BUSY);
  392 
  393         uart_unlock(&sc->sc_mtx);
  394 
  395         return (bufsize);
  396 }
  397 
  398 static int
  399 uart_phyp_cngetc(struct consdev *cp)
  400 {
  401         unsigned char c;
  402         int retval;
  403 
  404         retval = uart_phyp_get(console_sc, &c, 1);
  405         if (retval != 1)
  406                 return (-1);
  407 #if defined(KDB)
  408         kdb_alt_break(c, &alt_break_state);
  409 #endif
  410 
  411         return (c);
  412 }
  413 
  414 static void
  415 uart_phyp_cnputc(struct consdev *cp, int c)
  416 {
  417         unsigned char ch = c;
  418         uart_phyp_put(console_sc, &ch, 1);
  419 }
  420 
  421 static void
  422 uart_phyp_cngrab(struct consdev *cp)
  423 {
  424 }
  425 
  426 static void
  427 uart_phyp_cnungrab(struct consdev *cp)
  428 {
  429 }
  430 
  431 static void
  432 uart_phyp_ttyoutwakeup(struct tty *tp)
  433 {
  434         struct uart_phyp_softc *sc;
  435         char buffer[8];
  436         int len;
  437 
  438         sc = tty_softc(tp);
  439 
  440         while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0)
  441                 uart_phyp_put(sc, buffer, len);
  442 }
  443 
  444 static void
  445 uart_phyp_intr(void *v)
  446 {
  447         struct uart_phyp_softc *sc = v;
  448         struct tty *tp = sc->tp;
  449         unsigned char c;
  450         int len;
  451 
  452         tty_lock(tp);
  453         while ((len = uart_phyp_get(sc, &c, 1)) > 0)
  454                 ttydisc_rint(tp, c, 0);
  455         ttydisc_rint_done(tp);
  456         tty_unlock(tp);
  457 
  458         if (sc->irqres == NULL)
  459                 callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
  460 }

Cache object: c3a53c81daf6bdcbebb006592d1df0ce


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.