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