]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/dev/xen/console/console.c
MFS r217053: Fix the Xen console to not spew \0 bytes when there is no
[FreeBSD/releng/8.2.git] / sys / dev / xen / console / console.c
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
34 static char driver_name[] = "xc";
35 devclass_t xc_devclass; /* do not make static */
36 static void     xcoutwakeup(struct tty *);
37 static void     xc_timeout(void *);
38 static void __xencons_tx_flush(void);
39 static boolean_t xcons_putc(int c);
40
41 /* switch console so that shutdown can occur gracefully */
42 static void xc_shutdown(void *arg, int howto);
43 static int xc_mute;
44
45 static void xcons_force_flush(void);
46 static void xencons_priv_interrupt(void *);
47
48 static cn_probe_t       xccnprobe;
49 static cn_init_t        xccninit;
50 static cn_getc_t        xccngetc;
51 static cn_putc_t        xccnputc;
52 static cn_putc_t        xccnputc_dom0;
53 static cn_checkc_t      xccncheckc;
54
55 #define XC_POLLTIME     (hz/10)
56
57 CONS_DRIVER(xc, xccnprobe, xccninit, NULL, xccngetc, 
58             xccncheckc, xccnputc, NULL);
59
60 static int xen_console_up;
61 static boolean_t xc_start_needed;
62 static struct callout xc_callout;
63 struct 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))
69 static char wbuf[WBUF_SIZE];
70 static char rbuf[RBUF_SIZE];
71 static int rc, rp;
72 static unsigned int cnsl_evt_reg;
73 static 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
95 static struct tty *xccons;
96
97 static tsw_open_t       xcopen;
98 static tsw_close_t      xcclose;
99
100 static struct ttydevsw xc_ttydevsw = {
101         .tsw_flags      = TF_NOPREFIX,
102         .tsw_open       = xcopen,
103         .tsw_close      = xcclose,
104         .tsw_outwakeup  = xcoutwakeup,
105 };
106
107 static void
108 xccnprobe(struct consdev *cp)
109 {
110         cp->cn_pri = CN_REMOTE;
111         sprintf(cp->cn_name, "%s0", driver_name);
112 }
113
114
115 static void
116 xccninit(struct consdev *cp)
117
118         CN_LOCK_INIT(cn_mtx,"XCONS LOCK");
119
120 }
121 int
122 xccngetc(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
145 int
146 xccncheckc(struct consdev *dev)
147 {
148         int ret;
149
150         if (xencons_has_input())
151                 xencons_handle_input(NULL);
152         
153         CN_LOCK(cn_mtx);
154         if ((rp - rc) && !xc_mute) {
155                 /* we need to return only one char */
156                 ret = (int)rbuf[RBUF_MASK(rc)];
157                 rc++;
158         } else
159                 ret = -1;
160         CN_UNLOCK(cn_mtx);
161         return(ret);
162 }
163
164 static void
165 xccnputc(struct consdev *dev, int c)
166 {
167         xcons_putc(c);
168 }
169
170 static void
171 xccnputc_dom0(struct consdev *dev, int c)
172 {
173         HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c);
174 }
175
176 extern int db_active;
177 static boolean_t
178 xcons_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
208 static void
209 xc_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
217 static int
218 xc_probe(device_t dev)
219 {
220
221         return (0);
222 }
223
224 static int
225 xc_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);
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  */
266 static void
267 xc_shutdown(void *arg, int howto)
268 {
269         xc_mute = 1;
270         xcons_force_flush();
271 }
272
273 void 
274 xencons_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
297 static 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
321 void
322 xencons_tx(void)
323 {
324         __xencons_tx_flush();
325 }
326
327 static void
328 xencons_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
340 static int
341 xcopen(struct tty *tp)
342 {
343
344         xen_console_up = 1;
345         return (0);
346 }
347
348 static void
349 xcclose(struct tty *tp)
350 {
351
352         xen_console_up = 0;
353 }
354
355 static 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
366 static void
367 xcoutwakeup(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
382 static void
383 xc_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
403 static 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
410 static driver_t xc_driver = {
411         driver_name,
412         xc_methods,
413         0,
414 };
415
416 /*** Forcibly flush console data before dying. ***/
417 void 
418 xcons_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
437 DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);