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