console.c revision 265999
1#include <sys/cdefs.h>
2__FBSDID("$FreeBSD: stable/10/sys/dev/xen/console/console.c 265999 2014-05-14 01:35:43Z ian $");
3
4#include <sys/param.h>
5#include <sys/module.h>
6#include <sys/systm.h>
7#include <sys/consio.h>
8#include <sys/priv.h>
9#include <sys/proc.h>
10#include <sys/uio.h>
11#include <sys/tty.h>
12#include <sys/systm.h>
13#include <sys/taskqueue.h>
14#include <sys/conf.h>
15#include <sys/kernel.h>
16#include <sys/bus.h>
17#include <machine/stdarg.h>
18#include <xen/xen-os.h>
19#include <xen/hypervisor.h>
20#include <xen/xen_intr.h>
21#include <sys/cons.h>
22#include <sys/kdb.h>
23#include <sys/proc.h>
24
25#include <dev/xen/console/xencons_ring.h>
26#include <xen/interface/io/console.h>
27
28
29#include "opt_ddb.h"
30#ifdef DDB
31#include <ddb/ddb.h>
32#endif
33
34static char driver_name[] = "xc";
35devclass_t xc_devclass; /* do not make static */
36static void	xcoutwakeup(struct tty *);
37static void	xc_timeout(void *);
38static void __xencons_tx_flush(void);
39static boolean_t xcons_putc(int c);
40
41/* switch console so that shutdown can occur gracefully */
42static void xc_shutdown(void *arg, int howto);
43static int xc_mute;
44
45static void xcons_force_flush(void);
46static void xencons_priv_interrupt(void *);
47
48static cn_probe_t       xc_cnprobe;
49static cn_init_t        xc_cninit;
50static cn_term_t        xc_cnterm;
51static cn_getc_t        xc_cngetc;
52static cn_putc_t        xc_cnputc;
53static cn_grab_t        xc_cngrab;
54static cn_ungrab_t      xc_cnungrab;
55
56#define XC_POLLTIME 	(hz/10)
57
58CONSOLE_DRIVER(xc);
59
60static int xen_console_up;
61static boolean_t xc_start_needed;
62static struct callout xc_callout;
63struct mtx              cn_mtx;
64
65#define RBUF_SIZE     1024
66#define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1))
67#define WBUF_SIZE     4096
68#define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1))
69static char wbuf[WBUF_SIZE];
70static char rbuf[RBUF_SIZE];
71static int rc, rp;
72static unsigned int cnsl_evt_reg;
73static unsigned int wc, wp; /* write_cons, write_prod */
74xen_intr_handle_t xen_intr_handle;
75device_t xencons_dev;
76
77#ifdef KDB
78static int	xc_altbrk;
79#endif
80
81#define CDEV_MAJOR 12
82#define	XCUNIT(x)	(dev2unit(x))
83#define ISTTYOPEN(tp)	((tp) && ((tp)->t_state & TS_ISOPEN))
84#define CN_LOCK_INIT(x, _name) \
85        mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE)
86
87#define CN_LOCK(l)        								\
88		do {											\
89				if (panicstr == NULL)					\
90                        mtx_lock_spin(&(l));			\
91		} while (0)
92#define CN_UNLOCK(l)        							\
93		do {											\
94				if (panicstr == NULL)					\
95                        mtx_unlock_spin(&(l));			\
96		} while (0)
97#define CN_LOCK_ASSERT(x)    mtx_assert(&x, MA_OWNED)
98#define CN_LOCK_DESTROY(x)   mtx_destroy(&x)
99
100
101static struct tty *xccons;
102
103static tsw_open_t	xcopen;
104static tsw_close_t	xcclose;
105
106static struct ttydevsw xc_ttydevsw = {
107        .tsw_flags	= TF_NOPREFIX,
108        .tsw_open	= xcopen,
109        .tsw_close	= xcclose,
110        .tsw_outwakeup	= xcoutwakeup,
111};
112
113static void
114xc_cnprobe(struct consdev *cp)
115{
116	cp->cn_pri = CN_REMOTE;
117	sprintf(cp->cn_name, "%s0", driver_name);
118}
119
120
121static void
122xc_cninit(struct consdev *cp)
123{
124	CN_LOCK_INIT(cn_mtx,"XCONS LOCK");
125
126}
127
128static void
129xc_cnterm(struct consdev *cp)
130{
131}
132
133static void
134xc_cngrab(struct consdev *cp)
135{
136}
137
138static void
139xc_cnungrab(struct consdev *cp)
140{
141}
142
143static int
144xc_cngetc(struct consdev *dev)
145{
146	int ret;
147
148	if (xencons_has_input())
149		xencons_handle_input(NULL);
150
151	CN_LOCK(cn_mtx);
152	if ((rp - rc) && !xc_mute) {
153		/* we need to return only one char */
154		ret = (int)rbuf[RBUF_MASK(rc)];
155		rc++;
156	} else
157		ret = -1;
158	CN_UNLOCK(cn_mtx);
159	return(ret);
160}
161
162static void
163xc_cnputc_domu(struct consdev *dev, int c)
164{
165	xcons_putc(c);
166}
167
168static void
169xc_cnputc_dom0(struct consdev *dev, int c)
170{
171	HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c);
172}
173
174static void
175xc_cnputc(struct consdev *dev, int c)
176{
177
178	if (xen_start_info->flags & SIF_INITDOMAIN)
179		xc_cnputc_dom0(dev, c);
180	else
181		xc_cnputc_domu(dev, c);
182}
183
184extern int db_active;
185static boolean_t
186xcons_putc(int c)
187{
188	int force_flush = xc_mute ||
189#ifdef DDB
190		db_active ||
191#endif
192		panicstr;	/* we're not gonna recover, so force
193				 * flush
194				 */
195
196	if ((wp-wc) < (WBUF_SIZE-1)) {
197		if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') {
198        		wbuf[WBUF_MASK(wp++)] = '\r';
199#ifdef notyet
200			if (force_flush)
201				xcons_force_flush();
202#endif
203		}
204	} else if (force_flush) {
205#ifdef notyet
206		xcons_force_flush();
207#endif
208	}
209	if (cnsl_evt_reg)
210		__xencons_tx_flush();
211
212	/* inform start path that we're pretty full */
213	return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE;
214}
215
216static void
217xc_identify(driver_t *driver, device_t parent)
218{
219	device_t child;
220	child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
221	device_set_driver(child, driver);
222	device_set_desc(child, "Xen Console");
223}
224
225static int
226xc_probe(device_t dev)
227{
228
229	return (BUS_PROBE_NOWILDCARD);
230}
231
232static int
233xc_attach(device_t dev)
234{
235	int error;
236
237	xencons_dev = dev;
238	xccons = tty_alloc(&xc_ttydevsw, NULL);
239	tty_makedev(xccons, NULL, "xc%r", 0);
240
241	callout_init(&xc_callout, 0);
242
243	xencons_ring_init();
244
245	cnsl_evt_reg = 1;
246	callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons);
247
248	if (xen_start_info->flags & SIF_INITDOMAIN) {
249		error = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL,
250		                           xencons_priv_interrupt, NULL,
251		                           INTR_TYPE_TTY, &xen_intr_handle);
252		KASSERT(error >= 0, ("can't register console interrupt"));
253	}
254
255	/* register handler to flush console on shutdown */
256	if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xc_shutdown,
257				   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
258		printf("xencons: shutdown event registration failed!\n");
259
260	return (0);
261}
262
263/*
264 * return 0 for all console input, force flush all output.
265 */
266static void
267xc_shutdown(void *arg, int howto)
268{
269	xc_mute = 1;
270	xcons_force_flush();
271}
272
273void
274xencons_rx(char *buf, unsigned len)
275{
276	int           i;
277	struct tty *tp = xccons;
278
279	if (xen_console_up
280#ifdef DDB
281	    && !kdb_active
282#endif
283		) {
284		tty_lock(tp);
285		for (i = 0; i < len; i++) {
286#ifdef KDB
287			kdb_alt_break(buf[i], &xc_altbrk);
288#endif
289			ttydisc_rint(tp, buf[i], 0);
290		}
291		ttydisc_rint_done(tp);
292		tty_unlock(tp);
293	} else {
294		CN_LOCK(cn_mtx);
295		for (i = 0; i < len; i++)
296			rbuf[RBUF_MASK(rp++)] = buf[i];
297		CN_UNLOCK(cn_mtx);
298	}
299}
300
301static void
302__xencons_tx_flush(void)
303{
304	int        sz;
305
306	CN_LOCK(cn_mtx);
307	while (wc != wp) {
308		int sent;
309		sz = wp - wc;
310		if (sz > (WBUF_SIZE - WBUF_MASK(wc)))
311			sz = WBUF_SIZE - WBUF_MASK(wc);
312		if (xen_start_info->flags & SIF_INITDOMAIN) {
313			HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]);
314			wc += sz;
315		} else {
316			sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
317			if (sent == 0)
318				break;
319			wc += sent;
320		}
321	}
322	CN_UNLOCK(cn_mtx);
323}
324
325void
326xencons_tx(void)
327{
328	__xencons_tx_flush();
329}
330
331static void
332xencons_priv_interrupt(void *arg)
333{
334
335	static char rbuf[16];
336	int         l;
337
338	while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
339		xencons_rx(rbuf, l);
340
341	xencons_tx();
342}
343
344static int
345xcopen(struct tty *tp)
346{
347
348	xen_console_up = 1;
349	return (0);
350}
351
352static void
353xcclose(struct tty *tp)
354{
355
356	xen_console_up = 0;
357}
358
359#if 0
360static inline int
361__xencons_put_char(int ch)
362{
363	char _ch = (char)ch;
364	if ((wp - wc) == WBUF_SIZE)
365		return 0;
366	wbuf[WBUF_MASK(wp++)] = _ch;
367	return 1;
368}
369#endif
370
371
372static void
373xcoutwakeup(struct tty *tp)
374{
375	boolean_t cons_full = FALSE;
376	char c;
377
378	while (ttydisc_getc(tp, &c, 1) == 1 && !cons_full)
379		cons_full = xcons_putc(c);
380
381	if (cons_full) {
382	    	/* let the timeout kick us in a bit */
383	    	xc_start_needed = TRUE;
384	}
385
386}
387
388static void
389xc_timeout(void *v)
390{
391	struct	tty *tp;
392	int 	c;
393
394	tp = (struct tty *)v;
395
396	tty_lock(tp);
397	while ((c = xc_cngetc(NULL)) != -1)
398		ttydisc_rint(tp, c, 0);
399
400	if (xc_start_needed) {
401	    	xc_start_needed = FALSE;
402		xcoutwakeup(tp);
403	}
404	tty_unlock(tp);
405
406	callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, tp);
407}
408
409static device_method_t xc_methods[] = {
410	DEVMETHOD(device_identify, xc_identify),
411	DEVMETHOD(device_probe, xc_probe),
412	DEVMETHOD(device_attach, xc_attach),
413
414	DEVMETHOD_END
415};
416
417static driver_t xc_driver = {
418	driver_name,
419	xc_methods,
420	0,
421};
422
423/*** Forcibly flush console data before dying. ***/
424void
425xcons_force_flush(void)
426{
427	int        sz;
428
429	if (xen_start_info->flags & SIF_INITDOMAIN)
430		return;
431
432	/* Spin until console data is flushed through to the domain controller. */
433	while (wc != wp) {
434		int sent = 0;
435		if ((sz = wp - wc) == 0)
436			continue;
437
438		sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
439		if (sent > 0)
440			wc += sent;
441	}
442}
443
444DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);
445