xref: /freebsd/sys/powerpc/pseries/phyp_console.c (revision 02e9120893770924227138ba49df1edb3896112a)
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 }
459