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