1/*	$NetBSD: xencons.c,v 1.53 2023/02/25 00:35:40 riastradh Exp $	*/
2
3/*
4 * Copyright (c) 2006 Manuel Bouyer.
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 THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28/*
29 *
30 * Copyright (c) 2004 Christian Limpach.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 *    notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 *    notice, this list of conditions and the following disclaimer in the
40 *    documentation and/or other materials provided with the distribution.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
43 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
44 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
45 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
46 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
48 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
49 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
50 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
51 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 */
53
54
55#include <sys/cdefs.h>
56__KERNEL_RCSID(0, "$NetBSD: xencons.c,v 1.53 2023/02/25 00:35:40 riastradh Exp $");
57
58#include "opt_xen.h"
59
60#include <sys/param.h>
61#include <sys/ioctl.h>
62#include <sys/proc.h>
63#include <sys/tty.h>
64#include <sys/systm.h>
65#include <sys/device.h>
66#include <sys/conf.h>
67#include <sys/kauth.h>
68#include <sys/kernel.h>
69
70#include <xen/intr.h>
71#include <xen/xen.h>
72#include <xen/hypervisor.h>
73#include <xen/evtchn.h>
74#include <uvm/uvm.h>
75#include <machine/pmap.h>
76#include <xen/include/public/io/console.h>
77
78#include <dev/cons.h>
79
80#ifdef DDB
81#include <ddb/db_output.h>	/* XXX for db_max_line */
82#endif
83
84#undef XENDEBUG
85
86#ifdef XENDEBUG
87#define XENPRINTK(x) printk x
88#else
89#define XENPRINTK(x)
90#endif
91
92static int xencons_isconsole = 0;
93static struct xencons_softc *xencons_console_device = NULL;
94static struct intrhand *ih;
95
96#define	XENCONS_UNIT(x)	(minor(x))
97#define XENCONS_BURST 128
98
99static int xencons_match(device_t, cfdata_t, void *);
100static void xencons_attach(device_t, device_t, void *);
101static int xencons_intr(void *);
102static void xencons_tty_input(struct xencons_softc *, char*, int);
103
104struct xencons_softc {
105	device_t sc_dev;
106	struct	tty *sc_tty;
107	int polling;
108};
109volatile struct xencons_interface *xencons_interface;
110
111CFATTACH_DECL_NEW(xencons, sizeof(struct xencons_softc),
112    xencons_match, xencons_attach, NULL, NULL);
113
114extern struct cfdriver xencons_cd;
115
116static dev_type_open(xencons_open);
117static dev_type_close(xencons_close);
118static dev_type_read(xencons_read);
119static dev_type_write(xencons_write);
120static dev_type_ioctl(xencons_ioctl);
121static dev_type_stop(xencons_stop);
122static dev_type_tty(xencons_tty);
123static dev_type_poll(xencons_poll);
124
125const struct cdevsw xencons_cdevsw = {
126	.d_open = xencons_open,
127	.d_close = xencons_close,
128	.d_read = xencons_read,
129	.d_write = xencons_write,
130	.d_ioctl = xencons_ioctl,
131	.d_stop = xencons_stop,
132	.d_tty = xencons_tty,
133	.d_poll = xencons_poll,
134	.d_mmap = NULL,	/* XXX: is this safe? - dholland 20140315 */
135	.d_kqfilter = ttykqfilter,
136	.d_discard = nodiscard,
137	.d_flag = D_TTY
138};
139
140static int xencons_handler(void *);
141static int xenconscn_getc(dev_t);
142static void xenconscn_putc(dev_t, int);
143static void xenconscn_pollc(dev_t, int);
144
145static bool xencons_suspend(device_t, const pmf_qual_t *);
146static bool xencons_resume(device_t, const pmf_qual_t *);
147
148static struct consdev xencons = {
149	NULL, NULL, xenconscn_getc, xenconscn_putc, xenconscn_pollc,
150	NULL, NULL, NULL, NODEV, CN_NORMAL
151};
152
153static struct cnm_state xencons_cnm_state;
154
155static void xencons_start(struct tty *);
156static int xencons_param(struct tty *, struct termios *);
157
158static int
159xencons_match(device_t parent, cfdata_t match, void *aux)
160{
161	struct xencons_attach_args *xa = (struct xencons_attach_args *)aux;
162
163	if (strcmp(xa->xa_device, "xencons") == 0)
164		return 1;
165	return 0;
166}
167
168static void
169xencons_attach(device_t parent, device_t self, void *aux)
170{
171	struct xencons_softc *sc = device_private(self);
172
173	aprint_normal(": Xen Virtual Console Driver\n");
174
175	sc->sc_dev = self;
176	sc->sc_tty = tty_alloc();
177	tty_attach(sc->sc_tty);
178	sc->sc_tty->t_oproc = xencons_start;
179	sc->sc_tty->t_param = xencons_param;
180
181	if (xencons_isconsole) {
182		int maj;
183
184		/* Locate the major number. */
185		maj = cdevsw_lookup_major(&xencons_cdevsw);
186
187		/* There can be only one, but it can have any unit number. */
188		cn_tab->cn_dev = makedev(maj, device_unit(self));
189
190		aprint_verbose_dev(self, "console major %d, unit %d\n",
191		    maj, device_unit(self));
192
193		sc->sc_tty->t_dev = cn_tab->cn_dev;
194
195#ifdef DDB
196		/* Set db_max_line to avoid paging. */
197		db_max_line = 0x7fffffff;
198#endif
199
200		xencons_console_device = sc;
201
202		xencons_resume(self, PMF_Q_NONE);
203	}
204	sc->polling = 0;
205
206	if (!pmf_device_register(self, xencons_suspend, xencons_resume))
207		aprint_error_dev(self, "couldn't establish power handler\n");
208}
209
210static bool
211xencons_suspend(device_t dev, const pmf_qual_t *qual) {
212
213	int evtch;
214
215	/* dom0 console should not be suspended */
216	if (!xendomain_is_dom0()) {
217		evtch = xen_start_info.console_evtchn;
218		hypervisor_mask_event(evtch);
219		xen_intr_disestablish(ih);
220		aprint_verbose_dev(dev, "removed event channel %d\n", ih->ih_pin);
221	}
222
223	return true;
224}
225
226static bool
227xencons_resume(device_t dev, const pmf_qual_t *qual) {
228
229	int evtch = -1;
230
231	if (xendomain_is_dom0()) {
232		/* dom0 console resume is required only during first start-up */
233		if (cold) {
234			evtch = bind_virq_to_evtch(VIRQ_CONSOLE);
235			ih = xen_intr_establish_xname(-1, &xen_pic, evtch,
236			    IST_LEVEL, IPL_TTY, xencons_intr,
237			    xencons_console_device, false,
238			    device_xname(dev));
239			KASSERT(ih != NULL);
240		}
241	} else {
242		evtch = xen_start_info.console_evtchn;
243		ih = xen_intr_establish_xname(-1, &xen_pic, evtch,
244		    IST_LEVEL, IPL_TTY, xencons_handler,
245		    xencons_console_device, false, device_xname(dev));
246		KASSERT(ih != NULL);
247	}
248
249	if (evtch != -1) {
250		aprint_verbose_dev(dev, "using event channel %d\n", evtch);
251		hypervisor_unmask_event(evtch);
252	}
253
254	return true;
255}
256
257static int
258xencons_open(dev_t dev, int flag, int mode, struct lwp *l)
259{
260	struct xencons_softc *sc;
261	struct tty *tp;
262
263	sc = device_lookup_private(&xencons_cd, XENCONS_UNIT(dev));
264	if (sc == NULL)
265		return (ENXIO);
266
267	tp = sc->sc_tty;
268
269	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
270		return (EBUSY);
271
272	if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
273		tp->t_dev = dev;
274		ttychars(tp);
275		tp->t_iflag = TTYDEF_IFLAG;
276		tp->t_oflag = TTYDEF_OFLAG;
277		tp->t_cflag = TTYDEF_CFLAG;
278		tp->t_lflag = TTYDEF_LFLAG;
279		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
280		xencons_param(tp, &tp->t_termios);
281		ttsetwater(tp);
282	}
283	tp->t_state |= TS_CARR_ON;
284
285	return ((*tp->t_linesw->l_open)(dev, tp));
286}
287
288static int
289xencons_close(dev_t dev, int flag, int mode, struct lwp *l)
290{
291	struct xencons_softc *sc = device_lookup_private(&xencons_cd,
292	    XENCONS_UNIT(dev));
293	struct tty *tp = sc->sc_tty;
294
295	if (tp == NULL)
296		return (0);
297	(*tp->t_linesw->l_close)(tp, flag);
298	ttyclose(tp);
299#ifdef notyet /* XXX */
300	tty_free(tp);
301#endif
302	return (0);
303}
304
305static int
306xencons_read(dev_t dev, struct uio *uio, int flag)
307{
308	struct xencons_softc *sc = device_lookup_private(&xencons_cd,
309	    XENCONS_UNIT(dev));
310	struct tty *tp = sc->sc_tty;
311
312	return ((*tp->t_linesw->l_read)(tp, uio, flag));
313}
314
315static int
316xencons_write(dev_t dev, struct uio *uio, int flag)
317{
318	struct xencons_softc *sc = device_lookup_private(&xencons_cd,
319	    XENCONS_UNIT(dev));
320	struct tty *tp = sc->sc_tty;
321
322	return ((*tp->t_linesw->l_write)(tp, uio, flag));
323}
324
325static int
326xencons_poll(dev_t dev, int events, struct lwp *l)
327{
328	struct xencons_softc *sc = device_lookup_private(&xencons_cd,
329	    XENCONS_UNIT(dev));
330	struct tty *tp = sc->sc_tty;
331
332	return ((*tp->t_linesw->l_poll)(tp, events, l));
333}
334
335static struct tty *
336xencons_tty(dev_t dev)
337{
338	struct xencons_softc *sc = device_lookup_private(&xencons_cd,
339	    XENCONS_UNIT(dev));
340	struct tty *tp = sc->sc_tty;
341
342	return (tp);
343}
344
345static int
346xencons_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
347{
348	struct xencons_softc *sc = device_lookup_private(&xencons_cd,
349	    XENCONS_UNIT(dev));
350	struct tty *tp = sc->sc_tty;
351	int error;
352
353	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
354	if (error != EPASSTHROUGH)
355		return (error);
356
357	error = ttioctl(tp, cmd, data, flag, l);
358	if (error != EPASSTHROUGH)
359		return (error);
360
361	switch (cmd) {
362	default:
363		return (EPASSTHROUGH);
364	}
365
366#ifdef DIAGNOSTIC
367	panic("xencons_ioctl: impossible");
368#endif
369}
370
371static void
372xencons_start(struct tty *tp)
373{
374	struct clist *cl;
375	int s;
376
377	s = spltty();
378	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
379		goto out;
380	tp->t_state |= TS_BUSY;
381	splx(s);
382
383	/*
384	 * We need to do this outside spl since it could be fairly
385	 * expensive and we don't want our serial ports to overflow.
386	 */
387	cl = &tp->t_outq;
388	if (xendomain_is_dom0()) {
389		int len, r;
390		u_char buf[XENCONS_BURST+1];
391
392		len = q_to_b(cl, buf, XENCONS_BURST);
393		while (len > 0) {
394			r = HYPERVISOR_console_io(CONSOLEIO_write, len, buf);
395			if (r <= 0)
396				break;
397			len -= r;
398		}
399	} else {
400		XENCONS_RING_IDX cons, prod, len;
401
402#define XNC_OUT (xencons_interface->out)
403		cons = xencons_interface->out_cons;
404		prod = xencons_interface->out_prod;
405		xen_rmb();
406		while (prod != cons + sizeof(xencons_interface->out)) {
407			if (MASK_XENCONS_IDX(prod, XNC_OUT) <
408			    MASK_XENCONS_IDX(cons, XNC_OUT)) {
409				len = MASK_XENCONS_IDX(cons, XNC_OUT) -
410				    MASK_XENCONS_IDX(prod, XNC_OUT);
411			} else {
412				len = sizeof(XNC_OUT) -
413				    MASK_XENCONS_IDX(prod, XNC_OUT);
414			}
415			len = q_to_b(cl, __UNVOLATILE(
416			    &XNC_OUT[MASK_XENCONS_IDX(prod, XNC_OUT)]), len);
417			if (len == 0)
418				break;
419			prod = prod + len;
420		}
421		xen_wmb();
422		xencons_interface->out_prod = prod;
423		xen_wmb();
424		hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn);
425#undef XNC_OUT
426	}
427
428	s = spltty();
429	tp->t_state &= ~TS_BUSY;
430	if (ttypull(tp)) {
431		tp->t_state |= TS_TIMEOUT;
432		callout_schedule(&tp->t_rstrt_ch, 1);
433	}
434out:
435	splx(s);
436}
437
438static void
439xencons_stop(struct tty *tp, int flag)
440{
441
442}
443
444/* Non-privileged console interrupt routine */
445static int
446xencons_handler(void *arg)
447{
448	struct xencons_softc *sc = arg;
449	XENCONS_RING_IDX cons, prod, len;
450	int s = spltty();
451
452	if (sc->polling) {
453		splx(s);
454		return 1;
455	}
456
457
458#define XNC_IN (xencons_interface->in)
459
460	cons = xencons_interface->in_cons;
461	prod = xencons_interface->in_prod;
462	xen_rmb();
463	while (cons != prod) {
464		if (MASK_XENCONS_IDX(cons, XNC_IN) <
465		    MASK_XENCONS_IDX(prod, XNC_IN))
466			len = MASK_XENCONS_IDX(prod, XNC_IN) -
467			    MASK_XENCONS_IDX(cons, XNC_IN);
468		else
469			len = sizeof(XNC_IN) - MASK_XENCONS_IDX(cons, XNC_IN);
470
471		xencons_tty_input(sc, __UNVOLATILE(
472		    &XNC_IN[MASK_XENCONS_IDX(cons, XNC_IN)]), len);
473		if (__predict_false(xencons_interface->in_cons != cons)) {
474			/* catch up with xenconscn_getc() */
475			cons = xencons_interface->in_cons;
476			prod = xencons_interface->in_prod;
477			xen_rmb();
478		} else {
479			cons += len;
480			xen_wmb();
481			xencons_interface->in_cons = cons;
482		}
483	}
484	xen_wmb();
485	hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn);
486	splx(s);
487	return 1;
488#undef XNC_IN
489}
490
491static void
492xencons_tty_input(struct xencons_softc *sc, char* buf, int len)
493{
494	struct tty *tp;
495	int i;
496
497	tp = sc->sc_tty;
498	if (tp == NULL)
499		return;
500
501	for (i = 0; i < len; i++) {
502		cn_check_magic(sc->sc_tty->t_dev, buf[i], xencons_cnm_state);
503		(*tp->t_linesw->l_rint)(buf[i], tp);
504	}
505}
506
507/* privileged receive callback */
508static int
509xencons_intr(void *p)
510{
511	static char rbuf[16];
512	int len;
513	struct xencons_softc *sc = p;
514
515	if (sc == NULL)
516		/* Interrupt may happen during resume */
517		return 1;
518
519	if (sc->polling)
520		return 1;
521
522	while ((len =
523	    HYPERVISOR_console_io(CONSOLEIO_read, sizeof(rbuf), rbuf)) > 0) {
524		xencons_tty_input(sc, rbuf, len);
525	}
526	return 1;
527}
528
529void
530xenconscn_attach(void)
531{
532
533	cn_tab = &xencons;
534
535	/* console ring mapped in locore.S */
536
537	cn_init_magic(&xencons_cnm_state);
538	cn_set_magic("+++++");
539
540	xencons_isconsole = 1;
541}
542
543static int
544xenconscn_getc(dev_t dev)
545{
546	char c;
547	int s = spltty();
548	XENCONS_RING_IDX cons, prod;
549
550	if (xencons_console_device && xencons_console_device->polling == 0) {
551		printf("xenconscn_getc() but not polling\n");
552		splx(s);
553		return 0;
554	}
555	if (xendomain_is_dom0()) {
556		while (HYPERVISOR_console_io(CONSOLEIO_read, 1, &c) == 0)
557			;
558		cn_check_magic(dev, c, xencons_cnm_state);
559		splx(s);
560		return c;
561	}
562	if (xencons_console_device == NULL) {
563		printf("xenconscn_getc(): not console\n");
564		while (1)
565			;  /* loop here instead of in ddb */
566		splx(s);
567		return 0;
568	}
569
570	if (xencons_console_device->polling == 0) {
571		printf("xenconscn_getc() but not polling\n");
572		splx(s);
573		return 0;
574	}
575
576	cons = xencons_interface->in_cons;
577	prod = xencons_interface->in_prod;
578	while (cons == prod) {
579		HYPERVISOR_yield();
580		prod = xencons_interface->in_prod;
581	}
582	xen_rmb();
583	c = xencons_interface->in[MASK_XENCONS_IDX(xencons_interface->in_cons,
584	    xencons_interface->in)];
585	xen_wmb();
586	xencons_interface->in_cons = cons + 1;
587	cn_check_magic(dev, c, xencons_cnm_state);
588	splx(s);
589	return c;
590}
591
592static void
593xenconscn_putc(dev_t dev, int c)
594{
595	int s = spltty();
596	XENCONS_RING_IDX cons, prod;
597
598	if (xendomain_is_dom0()) {
599		u_char buf[1];
600
601		buf[0] = c;
602		(void)HYPERVISOR_console_io(CONSOLEIO_write, 1, buf);
603	} else {
604		XENPRINTK(("xenconscn_putc(%c)\n", c));
605
606		cons = xencons_interface->out_cons;
607		prod = xencons_interface->out_prod;
608		xen_rmb();
609		while (prod == cons + sizeof(xencons_interface->out)) {
610			cons = xencons_interface->out_cons;
611			prod = xencons_interface->out_prod;
612			xen_rmb();
613		}
614		xencons_interface->out[MASK_XENCONS_IDX(xencons_interface->out_prod,
615		    xencons_interface->out)] = c;
616		xen_wmb();
617		xencons_interface->out_prod++;
618		xen_wmb();
619		hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn);
620		splx(s);
621	}
622}
623
624static void
625xenconscn_pollc(dev_t dev, int on)
626{
627	if (xencons_console_device)
628		xencons_console_device->polling = on;
629}
630
631/*
632 * Set line parameters.
633 */
634static int
635xencons_param(struct tty *tp, struct termios *t)
636{
637
638	tp->t_ispeed = t->c_ispeed;
639	tp->t_ospeed = t->c_ospeed;
640	tp->t_cflag = t->c_cflag;
641	return (0);
642}
643