1181643Skmacy#include <sys/cdefs.h>
2181643Skmacy__FBSDID("$FreeBSD$");
3181643Skmacy
4181643Skmacy#include <sys/param.h>
5181643Skmacy#include <sys/module.h>
6181643Skmacy#include <sys/systm.h>
7181643Skmacy#include <sys/consio.h>
8189699Sdfr#include <sys/priv.h>
9181643Skmacy#include <sys/proc.h>
10181643Skmacy#include <sys/uio.h>
11181643Skmacy#include <sys/tty.h>
12181643Skmacy#include <sys/systm.h>
13181643Skmacy#include <sys/taskqueue.h>
14181643Skmacy#include <sys/conf.h>
15181643Skmacy#include <sys/kernel.h>
16181643Skmacy#include <sys/bus.h>
17181643Skmacy#include <machine/stdarg.h>
18255040Sgibbs#include <xen/xen-os.h>
19186557Skmacy#include <xen/hypervisor.h>
20186557Skmacy#include <xen/xen_intr.h>
21181643Skmacy#include <sys/cons.h>
22189699Sdfr#include <sys/kdb.h>
23181643Skmacy#include <sys/proc.h>
24181643Skmacy
25181643Skmacy#include <dev/xen/console/xencons_ring.h>
26181643Skmacy#include <xen/interface/io/console.h>
27181643Skmacy
28181643Skmacy
29181643Skmacy#include "opt_ddb.h"
30181643Skmacy#ifdef DDB
31181643Skmacy#include <ddb/ddb.h>
32181643Skmacy#endif
33181643Skmacy
34181643Skmacystatic char driver_name[] = "xc";
35181643Skmacydevclass_t xc_devclass; /* do not make static */
36181908Sedstatic void	xcoutwakeup(struct tty *);
37181643Skmacystatic void	xc_timeout(void *);
38181643Skmacystatic void __xencons_tx_flush(void);
39181643Skmacystatic boolean_t xcons_putc(int c);
40181643Skmacy
41181643Skmacy/* switch console so that shutdown can occur gracefully */
42181643Skmacystatic void xc_shutdown(void *arg, int howto);
43181643Skmacystatic int xc_mute;
44181643Skmacy
45181643Skmacystatic void xcons_force_flush(void);
46181643Skmacystatic void xencons_priv_interrupt(void *);
47181643Skmacy
48196499Sedstatic cn_probe_t       xc_cnprobe;
49196499Sedstatic cn_init_t        xc_cninit;
50196499Sedstatic cn_term_t        xc_cnterm;
51196499Sedstatic cn_getc_t        xc_cngetc;
52196499Sedstatic cn_putc_t        xc_cnputc;
53228631Savgstatic cn_grab_t        xc_cngrab;
54228631Savgstatic cn_ungrab_t      xc_cnungrab;
55181643Skmacy
56181643Skmacy#define XC_POLLTIME 	(hz/10)
57181643Skmacy
58196499SedCONSOLE_DRIVER(xc);
59181643Skmacy
60181643Skmacystatic int xen_console_up;
61181643Skmacystatic boolean_t xc_start_needed;
62181643Skmacystatic struct callout xc_callout;
63181643Skmacystruct mtx              cn_mtx;
64181643Skmacy
65181643Skmacy#define RBUF_SIZE     1024
66181643Skmacy#define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1))
67181643Skmacy#define WBUF_SIZE     4096
68181643Skmacy#define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1))
69181643Skmacystatic char wbuf[WBUF_SIZE];
70181643Skmacystatic char rbuf[RBUF_SIZE];
71181643Skmacystatic int rc, rp;
72181643Skmacystatic unsigned int cnsl_evt_reg;
73181643Skmacystatic unsigned int wc, wp; /* write_cons, write_prod */
74255040Sgibbsxen_intr_handle_t xen_intr_handle;
75255040Sgibbsdevice_t xencons_dev;
76181643Skmacy
77225343Srwatson#ifdef KDB
78225343Srwatsonstatic int	xc_altbrk;
79225343Srwatson#endif
80225343Srwatson
81181643Skmacy#define CDEV_MAJOR 12
82183397Sed#define	XCUNIT(x)	(dev2unit(x))
83181643Skmacy#define ISTTYOPEN(tp)	((tp) && ((tp)->t_state & TS_ISOPEN))
84181643Skmacy#define CN_LOCK_INIT(x, _name) \
85192003Skmacy        mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE)
86181643Skmacy
87181643Skmacy#define CN_LOCK(l)        								\
88181643Skmacy		do {											\
89181643Skmacy				if (panicstr == NULL)					\
90192003Skmacy                        mtx_lock_spin(&(l));			\
91181643Skmacy		} while (0)
92181643Skmacy#define CN_UNLOCK(l)        							\
93181643Skmacy		do {											\
94181643Skmacy				if (panicstr == NULL)					\
95192003Skmacy                        mtx_unlock_spin(&(l));			\
96181643Skmacy		} while (0)
97181643Skmacy#define CN_LOCK_ASSERT(x)    mtx_assert(&x, MA_OWNED)
98181643Skmacy#define CN_LOCK_DESTROY(x)   mtx_destroy(&x)
99181643Skmacy
100181643Skmacy
101181643Skmacystatic struct tty *xccons;
102181643Skmacy
103181908Sedstatic tsw_open_t	xcopen;
104181908Sedstatic tsw_close_t	xcclose;
105181643Skmacy
106181908Sedstatic struct ttydevsw xc_ttydevsw = {
107181908Sed        .tsw_flags	= TF_NOPREFIX,
108181908Sed        .tsw_open	= xcopen,
109181908Sed        .tsw_close	= xcclose,
110181908Sed        .tsw_outwakeup	= xcoutwakeup,
111181643Skmacy};
112181643Skmacy
113181643Skmacystatic void
114196499Sedxc_cnprobe(struct consdev *cp)
115181643Skmacy{
116181643Skmacy	cp->cn_pri = CN_REMOTE;
117181643Skmacy	sprintf(cp->cn_name, "%s0", driver_name);
118181643Skmacy}
119181643Skmacy
120181643Skmacy
121181643Skmacystatic void
122196499Sedxc_cninit(struct consdev *cp)
123181643Skmacy{
124181643Skmacy	CN_LOCK_INIT(cn_mtx,"XCONS LOCK");
125181643Skmacy
126181643Skmacy}
127196499Sed
128196499Sedstatic void
129196499Sedxc_cnterm(struct consdev *cp)
130196499Sed{
131181643Skmacy}
132181643Skmacy
133228631Savgstatic void
134228631Savgxc_cngrab(struct consdev *cp)
135228631Savg{
136228631Savg}
137228631Savg
138228631Savgstatic void
139228631Savgxc_cnungrab(struct consdev *cp)
140228631Savg{
141228631Savg}
142228631Savg
143196499Sedstatic int
144196499Sedxc_cngetc(struct consdev *dev)
145181643Skmacy{
146216790Scperciva	int ret;
147189699Sdfr
148189699Sdfr	if (xencons_has_input())
149189699Sdfr		xencons_handle_input(NULL);
150181643Skmacy
151181643Skmacy	CN_LOCK(cn_mtx);
152216790Scperciva	if ((rp - rc) && !xc_mute) {
153181643Skmacy		/* we need to return only one char */
154181643Skmacy		ret = (int)rbuf[RBUF_MASK(rc)];
155181643Skmacy		rc++;
156216790Scperciva	} else
157216790Scperciva		ret = -1;
158181643Skmacy	CN_UNLOCK(cn_mtx);
159181643Skmacy	return(ret);
160181643Skmacy}
161181643Skmacy
162181643Skmacystatic void
163196499Sedxc_cnputc_domu(struct consdev *dev, int c)
164181643Skmacy{
165181643Skmacy	xcons_putc(c);
166181643Skmacy}
167181643Skmacy
168181643Skmacystatic void
169196499Sedxc_cnputc_dom0(struct consdev *dev, int c)
170181643Skmacy{
171181643Skmacy	HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c);
172181643Skmacy}
173181643Skmacy
174196499Sedstatic void
175196499Sedxc_cnputc(struct consdev *dev, int c)
176196499Sed{
177196499Sed
178196499Sed	if (xen_start_info->flags & SIF_INITDOMAIN)
179196499Sed		xc_cnputc_dom0(dev, c);
180196499Sed	else
181196499Sed		xc_cnputc_domu(dev, c);
182196499Sed}
183196499Sed
184181643Skmacyextern int db_active;
185181643Skmacystatic boolean_t
186181643Skmacyxcons_putc(int c)
187181643Skmacy{
188181643Skmacy	int force_flush = xc_mute ||
189181643Skmacy#ifdef DDB
190181643Skmacy		db_active ||
191181643Skmacy#endif
192181643Skmacy		panicstr;	/* we're not gonna recover, so force
193181643Skmacy				 * flush
194181643Skmacy				 */
195181643Skmacy
196181643Skmacy	if ((wp-wc) < (WBUF_SIZE-1)) {
197181643Skmacy		if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') {
198181643Skmacy        		wbuf[WBUF_MASK(wp++)] = '\r';
199181643Skmacy#ifdef notyet
200181643Skmacy			if (force_flush)
201181643Skmacy				xcons_force_flush();
202181643Skmacy#endif
203181643Skmacy		}
204181643Skmacy	} else if (force_flush) {
205181643Skmacy#ifdef notyet
206181643Skmacy		xcons_force_flush();
207181643Skmacy#endif
208181643Skmacy	}
209181643Skmacy	if (cnsl_evt_reg)
210181643Skmacy		__xencons_tx_flush();
211181643Skmacy
212181643Skmacy	/* inform start path that we're pretty full */
213181643Skmacy	return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE;
214181643Skmacy}
215181643Skmacy
216181643Skmacystatic void
217181643Skmacyxc_identify(driver_t *driver, device_t parent)
218181643Skmacy{
219181643Skmacy	device_t child;
220181643Skmacy	child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
221181643Skmacy	device_set_driver(child, driver);
222181643Skmacy	device_set_desc(child, "Xen Console");
223181643Skmacy}
224181643Skmacy
225181643Skmacystatic int
226181643Skmacyxc_probe(device_t dev)
227181643Skmacy{
228181643Skmacy
229181643Skmacy	return (0);
230181643Skmacy}
231181643Skmacy
232181643Skmacystatic int
233181643Skmacyxc_attach(device_t dev)
234181643Skmacy{
235186557Skmacy	int error;
236181643Skmacy
237255040Sgibbs	xencons_dev = dev;
238193018Sed	xccons = tty_alloc(&xc_ttydevsw, NULL);
239181908Sed	tty_makedev(xccons, NULL, "xc%r", 0);
240181643Skmacy
241181643Skmacy	callout_init(&xc_callout, 0);
242181643Skmacy
243181643Skmacy	xencons_ring_init();
244181643Skmacy
245181643Skmacy	cnsl_evt_reg = 1;
246181643Skmacy	callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons);
247181643Skmacy
248181643Skmacy	if (xen_start_info->flags & SIF_INITDOMAIN) {
249255040Sgibbs		error = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL,
250255040Sgibbs		                           xencons_priv_interrupt, NULL,
251255040Sgibbs		                           INTR_TYPE_TTY, &xen_intr_handle);
252255040Sgibbs		KASSERT(error >= 0, ("can't register console interrupt"));
253181643Skmacy	}
254181643Skmacy
255181643Skmacy	/* register handler to flush console on shutdown */
256181643Skmacy	if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xc_shutdown,
257181643Skmacy				   NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
258181643Skmacy		printf("xencons: shutdown event registration failed!\n");
259181643Skmacy
260181643Skmacy	return (0);
261181643Skmacy}
262181643Skmacy
263181643Skmacy/*
264181643Skmacy * return 0 for all console input, force flush all output.
265181643Skmacy */
266181643Skmacystatic void
267181643Skmacyxc_shutdown(void *arg, int howto)
268181643Skmacy{
269181643Skmacy	xc_mute = 1;
270181643Skmacy	xcons_force_flush();
271181643Skmacy}
272181643Skmacy
273181643Skmacyvoid
274181643Skmacyxencons_rx(char *buf, unsigned len)
275181643Skmacy{
276181643Skmacy	int           i;
277181643Skmacy	struct tty *tp = xccons;
278181908Sed
279189699Sdfr	if (xen_console_up
280189699Sdfr#ifdef DDB
281189699Sdfr	    && !kdb_active
282189699Sdfr#endif
283189699Sdfr		) {
284181908Sed		tty_lock(tp);
285225343Srwatson		for (i = 0; i < len; i++) {
286225343Srwatson#ifdef KDB
287225343Srwatson			kdb_alt_break(buf[i], &xc_altbrk);
288225343Srwatson#endif
289181908Sed			ttydisc_rint(tp, buf[i], 0);
290225343Srwatson		}
291181908Sed		ttydisc_rint_done(tp);
292181908Sed		tty_unlock(tp);
293181908Sed	} else {
294192004Skmacy		CN_LOCK(cn_mtx);
295181908Sed		for (i = 0; i < len; i++)
296181643Skmacy			rbuf[RBUF_MASK(rp++)] = buf[i];
297192004Skmacy		CN_UNLOCK(cn_mtx);
298181643Skmacy	}
299181643Skmacy}
300181643Skmacy
301181643Skmacystatic void
302181643Skmacy__xencons_tx_flush(void)
303181643Skmacy{
304181908Sed	int        sz;
305181643Skmacy
306181643Skmacy	CN_LOCK(cn_mtx);
307181643Skmacy	while (wc != wp) {
308181643Skmacy		int sent;
309181643Skmacy		sz = wp - wc;
310181643Skmacy		if (sz > (WBUF_SIZE - WBUF_MASK(wc)))
311181643Skmacy			sz = WBUF_SIZE - WBUF_MASK(wc);
312181643Skmacy		if (xen_start_info->flags & SIF_INITDOMAIN) {
313181643Skmacy			HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]);
314181643Skmacy			wc += sz;
315181643Skmacy		} else {
316181643Skmacy			sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
317181643Skmacy			if (sent == 0)
318181643Skmacy				break;
319181643Skmacy			wc += sent;
320181643Skmacy		}
321181643Skmacy	}
322181643Skmacy	CN_UNLOCK(cn_mtx);
323181643Skmacy}
324181643Skmacy
325181643Skmacyvoid
326181643Skmacyxencons_tx(void)
327181643Skmacy{
328181643Skmacy	__xencons_tx_flush();
329181643Skmacy}
330181643Skmacy
331181643Skmacystatic void
332181643Skmacyxencons_priv_interrupt(void *arg)
333181643Skmacy{
334181643Skmacy
335181643Skmacy	static char rbuf[16];
336181643Skmacy	int         l;
337181643Skmacy
338181643Skmacy	while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
339181643Skmacy		xencons_rx(rbuf, l);
340181643Skmacy
341181643Skmacy	xencons_tx();
342181643Skmacy}
343181643Skmacy
344181908Sedstatic int
345181908Sedxcopen(struct tty *tp)
346181643Skmacy{
347181643Skmacy
348181643Skmacy	xen_console_up = 1;
349181643Skmacy	return (0);
350181643Skmacy}
351181643Skmacy
352181908Sedstatic void
353181908Sedxcclose(struct tty *tp)
354181643Skmacy{
355181643Skmacy
356181908Sed	xen_console_up = 0;
357181643Skmacy}
358181643Skmacy
359181643Skmacystatic inline int
360181643Skmacy__xencons_put_char(int ch)
361181643Skmacy{
362181643Skmacy	char _ch = (char)ch;
363181643Skmacy	if ((wp - wc) == WBUF_SIZE)
364181643Skmacy		return 0;
365181643Skmacy	wbuf[WBUF_MASK(wp++)] = _ch;
366181643Skmacy	return 1;
367181643Skmacy}
368181643Skmacy
369181643Skmacy
370181643Skmacystatic void
371181908Sedxcoutwakeup(struct tty *tp)
372181643Skmacy{
373181643Skmacy	boolean_t cons_full = FALSE;
374181908Sed	char c;
375181643Skmacy
376181908Sed	while (ttydisc_getc(tp, &c, 1) == 1 && !cons_full)
377181908Sed		cons_full = xcons_putc(c);
378181643Skmacy
379181908Sed	if (cons_full) {
380181643Skmacy	    	/* let the timeout kick us in a bit */
381181643Skmacy	    	xc_start_needed = TRUE;
382181643Skmacy	}
383181643Skmacy
384181643Skmacy}
385181643Skmacy
386181643Skmacystatic void
387181643Skmacyxc_timeout(void *v)
388181643Skmacy{
389181643Skmacy	struct	tty *tp;
390181643Skmacy	int 	c;
391181643Skmacy
392181643Skmacy	tp = (struct tty *)v;
393181643Skmacy
394181908Sed	tty_lock(tp);
395196499Sed	while ((c = xc_cngetc(NULL)) != -1)
396181908Sed		ttydisc_rint(tp, c, 0);
397181643Skmacy
398181643Skmacy	if (xc_start_needed) {
399181643Skmacy	    	xc_start_needed = FALSE;
400181908Sed		xcoutwakeup(tp);
401181643Skmacy	}
402181916Skmacy	tty_unlock(tp);
403181643Skmacy
404181643Skmacy	callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, tp);
405181643Skmacy}
406181643Skmacy
407181643Skmacystatic device_method_t xc_methods[] = {
408181643Skmacy	DEVMETHOD(device_identify, xc_identify),
409181643Skmacy	DEVMETHOD(device_probe, xc_probe),
410181643Skmacy	DEVMETHOD(device_attach, xc_attach),
411246128Ssbz
412246128Ssbz	DEVMETHOD_END
413181643Skmacy};
414181643Skmacy
415181643Skmacystatic driver_t xc_driver = {
416181643Skmacy	driver_name,
417181643Skmacy	xc_methods,
418181908Sed	0,
419181643Skmacy};
420181643Skmacy
421181643Skmacy/*** Forcibly flush console data before dying. ***/
422181643Skmacyvoid
423181643Skmacyxcons_force_flush(void)
424181643Skmacy{
425181643Skmacy	int        sz;
426181643Skmacy
427181643Skmacy	if (xen_start_info->flags & SIF_INITDOMAIN)
428181643Skmacy		return;
429181643Skmacy
430181643Skmacy	/* Spin until console data is flushed through to the domain controller. */
431181643Skmacy	while (wc != wp) {
432181643Skmacy		int sent = 0;
433181643Skmacy		if ((sz = wp - wc) == 0)
434181643Skmacy			continue;
435181643Skmacy
436181643Skmacy		sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
437181643Skmacy		if (sent > 0)
438181643Skmacy			wc += sent;
439181643Skmacy	}
440181643Skmacy}
441181643Skmacy
442181643SkmacyDRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);
443