1/* $NetBSD: xencons.c,v 1.41 2014/07/25 08:10:35 dholland 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.41 2014/07/25 08:10:35 dholland 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/xen.h>
71#include <xen/hypervisor.h>
72#include <xen/evtchn.h>
73#include <uvm/uvm.h>
74#include <machine/pmap.h>
75#include <xen/xen-public/io/console.h>
76
77#include <dev/cons.h>
78
79#ifdef DDB
80#include <ddb/db_output.h> /* XXX for db_max_line */
81#endif
82
83#undef XENDEBUG
84
85#ifdef XENDEBUG
86#define XENPRINTK(x) printk x
87#else
88#define XENPRINTK(x)
89#endif
90
91static int xencons_isconsole = 0;
92static struct xencons_softc *xencons_console_device = NULL;
93
94#define XENCONS_UNIT(x) (minor(x))
95#define XENCONS_BURST 128
96
97int xencons_match(device_t, cfdata_t, void *);
98void xencons_attach(device_t, device_t, void *);
99int xencons_intr(void *);
100void xencons_tty_input(struct xencons_softc *, char*, int);
101
102
103struct xencons_softc {
104 device_t sc_dev;
105 struct tty *sc_tty;
106 int polling;
107};
108volatile struct xencons_interface *xencons_interface;
109
110CFATTACH_DECL_NEW(xencons, sizeof(struct xencons_softc),
111 xencons_match, xencons_attach, NULL, NULL);
112
113extern struct cfdriver xencons_cd;
114
115dev_type_open(xencons_open);
116dev_type_close(xencons_close);
117dev_type_read(xencons_read);
118dev_type_write(xencons_write);
119dev_type_ioctl(xencons_ioctl);
120dev_type_stop(xencons_stop);
121dev_type_tty(xencons_tty);
122dev_type_poll(xencons_poll);
123
124const struct cdevsw xencons_cdevsw = {
125 .d_open = xencons_open,
126 .d_close = xencons_close,
127 .d_read = xencons_read,
128 .d_write = xencons_write,
129 .d_ioctl = xencons_ioctl,
130 .d_stop = xencons_stop,
131 .d_tty = xencons_tty,
132 .d_poll = xencons_poll,
133 .d_mmap = NULL, /* XXX: is this safe? - dholland 20140315 */
134 .d_kqfilter = ttykqfilter,
135 .d_discard = nodiscard,
136 .d_flag = D_TTY
137};
138
139
140static int xencons_handler(void *);
141int xenconscn_getc(dev_t);
142void xenconscn_putc(dev_t, int);
143void 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
155void xencons_start (struct tty *);
156int xencons_param (struct tty *, struct termios *);
157
158int
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
168void
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 if (event_remove_handler(evtch, xencons_handler,
220 xencons_console_device) != 0) {
221 aprint_error_dev(dev,
222 "can't remove handler: xencons_handler\n");
223 }
224
225 aprint_verbose_dev(dev, "removed event channel %d\n", evtch);
226 }
227
228 return true;
229}
230
231static bool
232xencons_resume(device_t dev, const pmf_qual_t *qual) {
233
234 int evtch = -1;
235
236 if (xendomain_is_dom0()) {
237 /* dom0 console resume is required only during first start-up */
238 if (cold) {
239 evtch = bind_virq_to_evtch(VIRQ_CONSOLE);
240 event_set_handler(evtch, xencons_intr,
241 xencons_console_device, IPL_TTY, "xencons");
242 }
243 } else {
244 evtch = xen_start_info.console_evtchn;
245 event_set_handler(evtch, xencons_handler,
246 xencons_console_device, IPL_TTY, "xencons");
247 }
248
249 if (evtch != -1) {
250 aprint_verbose_dev(dev, "using event channel %d\n", evtch);
251 hypervisor_enable_event(evtch);
252 }
253
254 return true;
255}
256
257int
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
288int
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
305int
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
315int
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
325int
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
335struct 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
345int
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
371void
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
438void
439xencons_stop(struct tty *tp, int flag)
440{
441
442}
443
444
445/* Non-privileged console interrupt routine */
446static int
447xencons_handler(void *arg)
448{
449 struct xencons_softc *sc = arg;
450 XENCONS_RING_IDX cons, prod, len;
451 int s = spltty();
452
453 if (sc->polling) {
454 splx(s);
455 return 1;
456 }
457
458
459#define XNC_IN (xencons_interface->in)
460
461 cons = xencons_interface->in_cons;
462 prod = xencons_interface->in_prod;
463 xen_rmb();
464 while (cons != prod) {
465 if (MASK_XENCONS_IDX(cons, XNC_IN) <
466 MASK_XENCONS_IDX(prod, XNC_IN))
467 len = MASK_XENCONS_IDX(prod, XNC_IN) -
468 MASK_XENCONS_IDX(cons, XNC_IN);
469 else
470 len = sizeof(XNC_IN) - MASK_XENCONS_IDX(cons, XNC_IN);
471
472 xencons_tty_input(sc, __UNVOLATILE(
473 &XNC_IN[MASK_XENCONS_IDX(cons, XNC_IN)]), len);
474 if (__predict_false(xencons_interface->in_cons != cons)) {
475 /* catch up with xenconscn_getc() */
476 cons = xencons_interface->in_cons;
477 prod = xencons_interface->in_prod;
478 xen_rmb();
479 } else {
480 cons += len;
481 xen_wmb();
482 xencons_interface->in_cons = cons;
483 xen_wmb();
484 }
485 }
486 hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn);
487 splx(s);
488 return 1;
489#undef XNC_IN
490}
491
492
493void
494xencons_tty_input(struct xencons_softc *sc, char* buf, int len)
495{
496 struct tty *tp;
497 int i;
498
499 tp = sc->sc_tty;
500 if (tp == NULL)
501 return;
502
503 for (i = 0; i < len; i++) {
504 cn_check_magic(sc->sc_tty->t_dev, buf[i], xencons_cnm_state);
505 (*tp->t_linesw->l_rint)(buf[i], tp);
506 }
507}
508
509/* privileged receive callback */
510int
511xencons_intr(void *p)
512{
513 static char rbuf[16];
514 int len;
515 struct xencons_softc *sc = p;
516
517 if (sc == NULL)
518 /* Interrupt may happen during resume */
519 return 1;
520
521 if (sc->polling)
522 return 1;
523
524 while ((len =
525 HYPERVISOR_console_io(CONSOLEIO_read, sizeof(rbuf), rbuf)) > 0) {
526 xencons_tty_input(sc, rbuf, len);
527 }
528 return 1;
529}
530
531void
532xenconscn_attach(void)
533{
534
535 cn_tab = &xencons;
536
537 /* console ring mapped in locore.S */
538
539 cn_init_magic(&xencons_cnm_state);
540 cn_set_magic("+++++");
541
542 xencons_isconsole = 1;
543}
544
545int
546xenconscn_getc(dev_t dev)
547{
548 char c;
549 int s = spltty();
550 XENCONS_RING_IDX cons, prod;
551
552 if (xencons_console_device && xencons_console_device->polling == 0) {
553 printf("xenconscn_getc() but not polling\n");
554 splx(s);
555 return 0;
556 }
557 if (xendomain_is_dom0()) {
558 while (HYPERVISOR_console_io(CONSOLEIO_read, 1, &c) == 0)
559 ;
560 cn_check_magic(dev, c, xencons_cnm_state);
561 splx(s);
562 return c;
563 }
564 if (xencons_console_device == NULL) {
565 printf("xenconscn_getc(): not console\n");
566 while (1)
567 ; /* loop here instead of in ddb */
568 splx(s);
569 return 0;
570 }
571
572 if (xencons_console_device->polling == 0) {
573 printf("xenconscn_getc() but not polling\n");
574 splx(s);
575 return 0;
576 }
577
578 cons = xencons_interface->in_cons;
579 prod = xencons_interface->in_prod;
580 xen_rmb();
581 while (cons == prod) {
582 HYPERVISOR_yield();
583 prod = xencons_interface->in_prod;
584 }
585 xen_rmb();
586 c = xencons_interface->in[MASK_XENCONS_IDX(xencons_interface->in_cons,
587 xencons_interface->in)];
588 xen_rmb();
589 xencons_interface->in_cons = cons + 1;
590 cn_check_magic(dev, c, xencons_cnm_state);
591 splx(s);
592 return c;
593}
594
595void
596xenconscn_putc(dev_t dev, int c)
597{
598 int s = spltty();
599 XENCONS_RING_IDX cons, prod;
600
601 if (xendomain_is_dom0()) {
602 u_char buf[1];
603
604 buf[0] = c;
605 (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, buf);
606 } else {
607 XENPRINTK(("xenconscn_putc(%c)\n", c));
608
609 cons = xencons_interface->out_cons;
610 prod = xencons_interface->out_prod;
611 xen_rmb();
612 while (prod == cons + sizeof(xencons_interface->out)) {
613 cons = xencons_interface->out_cons;
614 prod = xencons_interface->out_prod;
615 xen_rmb();
616 }
617 xencons_interface->out[MASK_XENCONS_IDX(xencons_interface->out_prod,
618 xencons_interface->out)] = c;
619 xen_rmb();
620 xencons_interface->out_prod++;
621 xen_rmb();
622 hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn);
623 splx(s);
624 }
625}
626
627void
628xenconscn_pollc(dev_t dev, int on)
629{
630 if (xencons_console_device)
631 xencons_console_device->polling = on;
632}
633
634/*
635 * Set line parameters.
636 */
637int
638xencons_param(struct tty *tp, struct termios *t)
639{
640
641 tp->t_ispeed = t->c_ispeed;
642 tp->t_ospeed = t->c_ospeed;
643 tp->t_cflag = t->c_cflag;
644 return (0);
645}
646