]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/pseries/phyp_console.c
MFV r368207:
[FreeBSD/FreeBSD.git] / sys / powerpc / pseries / phyp_console.c
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, uart_devclass, 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 (err != H_SUCCESS) {
299                         uart_unlock(&sc->sc_mtx);
300                         return (-1);
301                 }
302                 hdr = 1;
303         }
304
305         if (sc->inbuflen == 0) {
306                 uart_unlock(&sc->sc_mtx);
307                 return (0);
308         }
309
310 #if BYTE_ORDER == LITTLE_ENDIAN
311         sc->phyp_inbuf.u64[0] = be64toh(sc->phyp_inbuf.u64[0]);
312         sc->phyp_inbuf.u64[1] = be64toh(sc->phyp_inbuf.u64[1]);
313 #endif
314
315         if ((sc->protocol == HVTERMPROT) && (hdr == 1)) {
316                 sc->inbuflen = sc->inbuflen - 4;
317                 /* The VTERM protocol has a 4 byte header, skip it here. */
318                 memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4],
319                     sc->inbuflen);
320         }
321
322         /*
323          * Since version 2.11.0, QEMU became bug-compatible with
324          * PowerVM's vty implementation, by inserting a \0 after
325          * every \r going to the guest. Guests are expected to
326          * workaround this issue by removing every \0 immediately
327          * following a \r.
328          */
329         if (hdr == 1) {
330                 for (i = 0, j = 0; i < sc->inbuflen; i++, j++) {
331                         if (i > j)
332                                 sc->phyp_inbuf.str[j] = sc->phyp_inbuf.str[i];
333
334                         if (sc->phyp_inbuf.str[i] == '\r' &&
335                             i < sc->inbuflen - 1 &&
336                             sc->phyp_inbuf.str[i + 1] == '\0')
337                                 i++;
338                 }
339                 sc->inbuflen -= i - j;
340         }
341
342         if (bufsize > sc->inbuflen)
343                 bufsize = sc->inbuflen;
344
345         memcpy(buffer, sc->phyp_inbuf.str, bufsize);
346         sc->inbuflen -= bufsize;
347         if (sc->inbuflen > 0)
348                 memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize],
349                     sc->inbuflen);
350
351         uart_unlock(&sc->sc_mtx);
352         return (bufsize);
353 }
354
355 static int
356 uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
357 {
358         uint16_t seqno;
359         uint64_t len = 0;
360         int     err;
361
362         union {
363                 uint64_t u64[2];
364                 char bytes[16];
365         } cbuf;
366
367         uart_lock(&sc->sc_mtx);
368         switch (sc->protocol) {
369         case HVTERM1:
370                 if (bufsize > 16)
371                         bufsize = 16;
372                 memcpy(&cbuf, buffer, bufsize);
373                 len = bufsize;
374                 break;
375         case HVTERMPROT:
376                 if (bufsize > 12)
377                         bufsize = 12;
378                 seqno = sc->outseqno++;
379                 cbuf.bytes[0] = VS_DATA_PACKET_HEADER;
380                 cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */
381                 cbuf.bytes[2] = (seqno >> 8) & 0xff;
382                 cbuf.bytes[3] = seqno & 0xff;
383                 memcpy(&cbuf.bytes[4], buffer, bufsize);
384                 len = 4 + bufsize;
385                 break;
386         }
387
388         do {
389             err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, htobe64(cbuf.u64[0]),
390                             htobe64(cbuf.u64[1]));
391                 DELAY(100);
392         } while (err == H_BUSY);
393
394         uart_unlock(&sc->sc_mtx);
395
396         return (bufsize);
397 }
398
399 static int
400 uart_phyp_cngetc(struct consdev *cp)
401 {
402         unsigned char c;
403         int retval;
404
405         retval = uart_phyp_get(console_sc, &c, 1);
406         if (retval != 1)
407                 return (-1);
408 #if defined(KDB)
409         kdb_alt_break(c, &alt_break_state);
410 #endif
411
412         return (c);
413 }
414
415 static void
416 uart_phyp_cnputc(struct consdev *cp, int c)
417 {
418         unsigned char ch = c;
419         uart_phyp_put(console_sc, &ch, 1);
420 }
421
422 static void
423 uart_phyp_cngrab(struct consdev *cp)
424 {
425 }
426
427 static void
428 uart_phyp_cnungrab(struct consdev *cp)
429 {
430 }
431
432 static void
433 uart_phyp_ttyoutwakeup(struct tty *tp)
434 {
435         struct uart_phyp_softc *sc;
436         char buffer[8];
437         int len;
438
439         sc = tty_softc(tp);
440
441         while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0)
442                 uart_phyp_put(sc, buffer, len);
443 }
444
445 static void
446 uart_phyp_intr(void *v)
447 {
448         struct uart_phyp_softc *sc = v;
449         struct tty *tp = sc->tp;
450         unsigned char c;
451         int len;
452
453         tty_lock(tp);
454         while ((len = uart_phyp_get(sc, &c, 1)) > 0)
455                 ttydisc_rint(tp, c, 0);
456         ttydisc_rint_done(tp);
457         tty_unlock(tp);
458
459         if (sc->irqres == NULL)
460                 callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
461 }