Deleted Added
sdiff udiff text old ( 225343 ) new ( 228631 )
full compact
1#include <sys/cdefs.h>
2__FBSDID("$FreeBSD: head/sys/dev/xen/console/console.c 228631 2011-12-17 15:08:43Z avg $");
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
361static inline int
362__xencons_put_char(int ch)
363{
364 char _ch = (char)ch;
365 if ((wp - wc) == WBUF_SIZE)
366 return 0;
367 wbuf[WBUF_MASK(wp++)] = _ch;
368 return 1;
369}
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 {0, 0}
414};
415
416static driver_t xc_driver = {
417 driver_name,
418 xc_methods,
419 0,
420};
421
422/*** Forcibly flush console data before dying. ***/
423void
424xcons_force_flush(void)
425{
426 int sz;
427
428 if (xen_start_info->flags & SIF_INITDOMAIN)
429 return;
430
431 /* Spin until console data is flushed through to the domain controller. */
432 while (wc != wp) {
433 int sent = 0;
434 if ((sz = wp - wc) == 0)
435 continue;
436
437 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
438 if (sent > 0)
439 wc += sent;
440 }
441}
442
443DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);