console.c revision 192004
1#include <sys/cdefs.h>
2__FBSDID("$FreeBSD: head/sys/dev/xen/console/console.c 192004 2009-05-11 23:03:15Z kmacy $");
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 <machine/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       xccnprobe;
49static cn_init_t        xccninit;
50static cn_getc_t        xccngetc;
51static cn_putc_t        xccnputc;
52static cn_putc_t        xccnputc_dom0;
53static cn_checkc_t      xccncheckc;
54
55#define XC_POLLTIME 	(hz/10)
56
57CONS_DRIVER(xc, xccnprobe, xccninit, NULL, xccngetc,
58	    xccncheckc, xccnputc, NULL);
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 */
74
75#define CDEV_MAJOR 12
76#define	XCUNIT(x)	(dev2unit(x))
77#define ISTTYOPEN(tp)	((tp) && ((tp)->t_state & TS_ISOPEN))
78#define CN_LOCK_INIT(x, _name) \
79        mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE)
80
81#define CN_LOCK(l)        								\
82		do {											\
83				if (panicstr == NULL)					\
84                        mtx_lock_spin(&(l));			\
85		} while (0)
86#define CN_UNLOCK(l)        							\
87		do {											\
88				if (panicstr == NULL)					\
89                        mtx_unlock_spin(&(l));			\
90		} while (0)
91#define CN_LOCK_ASSERT(x)    mtx_assert(&x, MA_OWNED)
92#define CN_LOCK_DESTROY(x)   mtx_destroy(&x)
93
94
95static struct tty *xccons;
96
97static tsw_open_t	xcopen;
98static tsw_close_t	xcclose;
99
100static struct ttydevsw xc_ttydevsw = {
101        .tsw_flags	= TF_NOPREFIX,
102        .tsw_open	= xcopen,
103        .tsw_close	= xcclose,
104        .tsw_outwakeup	= xcoutwakeup,
105};
106
107static void
108xccnprobe(struct consdev *cp)
109{
110	cp->cn_pri = CN_REMOTE;
111	sprintf(cp->cn_name, "%s0", driver_name);
112}
113
114
115static void
116xccninit(struct consdev *cp)
117{
118	CN_LOCK_INIT(cn_mtx,"XCONS LOCK");
119
120}
121int
122xccngetc(struct consdev *dev)
123{
124	int c;
125	if (xc_mute)
126	    	return 0;
127	do {
128		if ((c = xccncheckc(dev)) == -1) {
129#ifdef KDB
130			if (!kdb_active)
131#endif
132				/*
133				 * Polling without sleeping in Xen
134				 * doesn't work well.  Sleeping gives
135				 * other things like clock a chance to
136				 * run
137				 */
138				tsleep(&cn_mtx, PWAIT | PCATCH,
139				    "console sleep", XC_POLLTIME);
140		}
141	} while(c == -1);
142	return c;
143}
144
145int
146xccncheckc(struct consdev *dev)
147{
148	int ret = (xc_mute ? 0 : -1);
149
150	if (xencons_has_input())
151		xencons_handle_input(NULL);
152
153	CN_LOCK(cn_mtx);
154	if ((rp - rc)) {
155		if (kdb_active) printf("%s:%d\n", __func__, __LINE__);
156		/* we need to return only one char */
157		ret = (int)rbuf[RBUF_MASK(rc)];
158		rc++;
159	}
160	CN_UNLOCK(cn_mtx);
161	return(ret);
162}
163
164static void
165xccnputc(struct consdev *dev, int c)
166{
167	xcons_putc(c);
168}
169
170static void
171xccnputc_dom0(struct consdev *dev, int c)
172{
173	HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c);
174}
175
176extern int db_active;
177static boolean_t
178xcons_putc(int c)
179{
180	int force_flush = xc_mute ||
181#ifdef DDB
182		db_active ||
183#endif
184		panicstr;	/* we're not gonna recover, so force
185				 * flush
186				 */
187
188	if ((wp-wc) < (WBUF_SIZE-1)) {
189		if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') {
190        		wbuf[WBUF_MASK(wp++)] = '\r';
191#ifdef notyet
192			if (force_flush)
193				xcons_force_flush();
194#endif
195		}
196	} else if (force_flush) {
197#ifdef notyet
198		xcons_force_flush();
199#endif
200	}
201	if (cnsl_evt_reg)
202		__xencons_tx_flush();
203
204	/* inform start path that we're pretty full */
205	return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE;
206}
207
208static void
209xc_identify(driver_t *driver, device_t parent)
210{
211	device_t child;
212	child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
213	device_set_driver(child, driver);
214	device_set_desc(child, "Xen Console");
215}
216
217static int
218xc_probe(device_t dev)
219{
220
221	return (0);
222}
223
224static int
225xc_attach(device_t dev)
226{
227	int error;
228
229	if (xen_start_info->flags & SIF_INITDOMAIN) {
230		xc_consdev.cn_putc = xccnputc_dom0;
231	}
232
233	xccons = tty_alloc(&xc_ttydevsw, NULL, NULL);
234	tty_makedev(xccons, NULL, "xc%r", 0);
235
236	callout_init(&xc_callout, 0);
237
238	xencons_ring_init();
239
240	cnsl_evt_reg = 1;
241	callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons);
242
243	if (xen_start_info->flags & SIF_INITDOMAIN) {
244			error = bind_virq_to_irqhandler(
245				 VIRQ_CONSOLE,
246				 0,
247				 "console",
248				 NULL,
249				 xencons_priv_interrupt, NULL,
250				 INTR_TYPE_TTY, NULL);
251
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			ttydisc_rint(tp, buf[i], 0);
287		ttydisc_rint_done(tp);
288		tty_unlock(tp);
289	} else {
290		CN_LOCK(cn_mtx);
291		for (i = 0; i < len; i++)
292			rbuf[RBUF_MASK(rp++)] = buf[i];
293		CN_UNLOCK(cn_mtx);
294	}
295}
296
297static void
298__xencons_tx_flush(void)
299{
300	int        sz;
301
302	CN_LOCK(cn_mtx);
303	while (wc != wp) {
304		int sent;
305		sz = wp - wc;
306		if (sz > (WBUF_SIZE - WBUF_MASK(wc)))
307			sz = WBUF_SIZE - WBUF_MASK(wc);
308		if (xen_start_info->flags & SIF_INITDOMAIN) {
309			HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]);
310			wc += sz;
311		} else {
312			sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
313			if (sent == 0)
314				break;
315			wc += sent;
316		}
317	}
318	CN_UNLOCK(cn_mtx);
319}
320
321void
322xencons_tx(void)
323{
324	__xencons_tx_flush();
325}
326
327static void
328xencons_priv_interrupt(void *arg)
329{
330
331	static char rbuf[16];
332	int         l;
333
334	while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
335		xencons_rx(rbuf, l);
336
337	xencons_tx();
338}
339
340static int
341xcopen(struct tty *tp)
342{
343
344	xen_console_up = 1;
345	return (0);
346}
347
348static void
349xcclose(struct tty *tp)
350{
351
352	xen_console_up = 0;
353}
354
355static inline int
356__xencons_put_char(int ch)
357{
358	char _ch = (char)ch;
359	if ((wp - wc) == WBUF_SIZE)
360		return 0;
361	wbuf[WBUF_MASK(wp++)] = _ch;
362	return 1;
363}
364
365
366static void
367xcoutwakeup(struct tty *tp)
368{
369	boolean_t cons_full = FALSE;
370	char c;
371
372	while (ttydisc_getc(tp, &c, 1) == 1 && !cons_full)
373		cons_full = xcons_putc(c);
374
375	if (cons_full) {
376	    	/* let the timeout kick us in a bit */
377	    	xc_start_needed = TRUE;
378	}
379
380}
381
382static void
383xc_timeout(void *v)
384{
385	struct	tty *tp;
386	int 	c;
387
388	tp = (struct tty *)v;
389
390	tty_lock(tp);
391	while ((c = xccncheckc(NULL)) != -1)
392		ttydisc_rint(tp, c, 0);
393
394	if (xc_start_needed) {
395	    	xc_start_needed = FALSE;
396		xcoutwakeup(tp);
397	}
398	tty_unlock(tp);
399
400	callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, tp);
401}
402
403static device_method_t xc_methods[] = {
404	DEVMETHOD(device_identify, xc_identify),
405	DEVMETHOD(device_probe, xc_probe),
406	DEVMETHOD(device_attach, xc_attach),
407	{0, 0}
408};
409
410static driver_t xc_driver = {
411	driver_name,
412	xc_methods,
413	0,
414};
415
416/*** Forcibly flush console data before dying. ***/
417void
418xcons_force_flush(void)
419{
420	int        sz;
421
422	if (xen_start_info->flags & SIF_INITDOMAIN)
423		return;
424
425	/* Spin until console data is flushed through to the domain controller. */
426	while (wc != wp) {
427		int sent = 0;
428		if ((sz = wp - wc) == 0)
429			continue;
430
431		sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
432		if (sent > 0)
433			wc += sent;
434	}
435}
436
437DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);
438