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