Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/pseries/phyp_console.c
39507 views
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
460