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