]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/xen/console/console.c
change netfront to match xen31_6
[FreeBSD/FreeBSD.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/proc.h>
9 #include <sys/uio.h>
10 #include <sys/tty.h>
11 #include <sys/systm.h>
12 #include <sys/taskqueue.h>
13 #include <sys/conf.h>
14 #include <sys/kernel.h>
15 #include <sys/bus.h>
16 #include <machine/stdarg.h>
17 #include <machine/xen/xen-os.h>
18 #include <machine/xen/hypervisor.h>
19 #include <machine/xen/xen_intr.h>
20 #include <sys/cons.h>
21 #include <sys/priv.h>
22 #include <sys/proc.h>
23
24 #include <dev/xen/console/xencons_ring.h>
25 #include <xen/interface/io/console.h>
26
27
28 #include "opt_ddb.h"
29 #ifdef DDB
30 #include <ddb/ddb.h>
31 #endif
32
33 static char driver_name[] = "xc";
34 devclass_t xc_devclass; /* do not make static */
35 static void     xcoutwakeup(struct tty *);
36 static void     xc_timeout(void *);
37 static void __xencons_tx_flush(void);
38 static boolean_t xcons_putc(int c);
39
40 /* switch console so that shutdown can occur gracefully */
41 static void xc_shutdown(void *arg, int howto);
42 static int xc_mute;
43
44 static void xcons_force_flush(void);
45 static void xencons_priv_interrupt(void *);
46
47 static cn_probe_t       xccnprobe;
48 static cn_init_t        xccninit;
49 static cn_getc_t        xccngetc;
50 static cn_putc_t        xccnputc;
51 static cn_putc_t        xccnputc_dom0;
52 static cn_checkc_t      xccncheckc;
53
54 #define XC_POLLTIME     (hz/10)
55
56 CONS_DRIVER(xc, xccnprobe, xccninit, NULL, xccngetc, 
57             xccncheckc, xccnputc, NULL);
58
59 static int xen_console_up;
60 static boolean_t xc_start_needed;
61 static struct callout xc_callout;
62 struct mtx              cn_mtx;
63
64 #define RBUF_SIZE     1024
65 #define RBUF_MASK(_i) ((_i)&(RBUF_SIZE-1))
66 #define WBUF_SIZE     4096
67 #define WBUF_MASK(_i) ((_i)&(WBUF_SIZE-1))
68 static char wbuf[WBUF_SIZE];
69 static char rbuf[RBUF_SIZE];
70 static int rc, rp;
71 static unsigned int cnsl_evt_reg;
72 static unsigned int wc, wp; /* write_cons, write_prod */
73
74 #define CDEV_MAJOR 12
75 #define XCUNIT(x)       (minor(x))
76 #define ISTTYOPEN(tp)   ((tp) && ((tp)->t_state & TS_ISOPEN))
77 #define CN_LOCK_INIT(x, _name) \
78         mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE)
79
80 #define CN_LOCK(l)                                                                      \
81                 do {                                                                                    \
82                                 if (panicstr == NULL)                                   \
83                         mtx_lock_spin(&(l));                    \
84                 } while (0)
85 #define CN_UNLOCK(l)                                                            \
86                 do {                                                                                    \
87                                 if (panicstr == NULL)                                   \
88                         mtx_unlock_spin(&(l));                  \
89                 } while (0)
90 #define CN_LOCK_ASSERT(x)    mtx_assert(&x, MA_OWNED)
91 #define CN_LOCK_DESTROY(x)   mtx_destroy(&x)
92
93
94 static struct tty *xccons;
95
96 static tsw_open_t       xcopen;
97 static tsw_close_t      xcclose;
98
99 static struct ttydevsw xc_ttydevsw = {
100         .tsw_flags      = TF_NOPREFIX,
101         .tsw_open       = xcopen,
102         .tsw_close      = xcclose,
103         .tsw_outwakeup  = xcoutwakeup,
104 };
105
106 static void
107 xccnprobe(struct consdev *cp)
108 {
109         cp->cn_pri = CN_REMOTE;
110         cp->cn_tp = xccons;
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                         /* polling without sleeping in Xen doesn't work well. 
130                          * Sleeping gives other things like clock a chance to 
131                          * run
132                          */
133                         tsleep(&cn_mtx, PWAIT | PCATCH, "console sleep", 
134                                XC_POLLTIME);
135                 }
136         } while(c == -1);
137         return c;
138 }
139
140 int
141 xccncheckc(struct consdev *dev)
142 {
143         int ret = (xc_mute ? 0 : -1);
144         if (xencons_has_input()) 
145                         xencons_handle_input(NULL);
146         
147         CN_LOCK(cn_mtx);
148         if ((rp - rc)) {
149                 /* we need to return only one char */
150                 ret = (int)rbuf[RBUF_MASK(rc)];
151                 rc++;
152         }
153         CN_UNLOCK(cn_mtx);
154         return(ret);
155 }
156
157 static void
158 xccnputc(struct consdev *dev, int c)
159 {
160         xcons_putc(c);
161 }
162
163 static void
164 xccnputc_dom0(struct consdev *dev, int c)
165 {
166         HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c);
167 }
168
169 extern int db_active;
170 static boolean_t
171 xcons_putc(int c)
172 {
173         int force_flush = xc_mute ||
174 #ifdef DDB
175                 db_active ||
176 #endif
177                 panicstr;       /* we're not gonna recover, so force
178                                  * flush 
179                                  */
180
181         if ((wp-wc) < (WBUF_SIZE-1)) {
182                 if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') {
183                         wbuf[WBUF_MASK(wp++)] = '\r';
184 #ifdef notyet
185                         if (force_flush)
186                                 xcons_force_flush();
187 #endif
188                 }
189         } else if (force_flush) {
190 #ifdef notyet
191                 xcons_force_flush();
192 #endif          
193         }
194         if (cnsl_evt_reg)
195                 __xencons_tx_flush();
196         
197         /* inform start path that we're pretty full */
198         return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE;
199 }
200
201 static void
202 xc_identify(driver_t *driver, device_t parent)
203 {
204         device_t child;
205         child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
206         device_set_driver(child, driver);
207         device_set_desc(child, "Xen Console");
208 }
209
210 static int
211 xc_probe(device_t dev)
212 {
213
214         return (0);
215 }
216
217 static int
218 xc_attach(device_t dev) 
219 {
220
221         if (xen_start_info->flags & SIF_INITDOMAIN) {
222                 xc_consdev.cn_putc = xccnputc_dom0;
223         } 
224
225         xccons = tty_alloc(&xc_ttydevsw, NULL, NULL);
226         tty_makedev(xccons, NULL, "xc%r", 0);
227
228         callout_init(&xc_callout, 0);
229
230         xencons_ring_init();
231
232         cnsl_evt_reg = 1;
233         callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons);
234     
235         if (xen_start_info->flags & SIF_INITDOMAIN) {
236                 PANIC_IF(bind_virq_to_irqhandler(
237                                  VIRQ_CONSOLE,
238                                  0,
239                                  "console",
240                                  NULL,
241                                  xencons_priv_interrupt,
242                                  INTR_TYPE_TTY) < 0);
243                 
244         }
245
246
247         /* register handler to flush console on shutdown */
248         if ((EVENTHANDLER_REGISTER(shutdown_post_sync, xc_shutdown,
249                                    NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
250                 printf("xencons: shutdown event registration failed!\n");
251         
252         return (0);
253 }
254
255 /*
256  * return 0 for all console input, force flush all output.
257  */
258 static void
259 xc_shutdown(void *arg, int howto)
260 {
261         xc_mute = 1;
262         xcons_force_flush();
263 }
264
265 void 
266 xencons_rx(char *buf, unsigned len)
267 {
268         int           i;
269         struct tty *tp = xccons;
270
271         if (xen_console_up) {
272                 tty_lock(tp);
273                 for (i = 0; i < len; i++)
274                         ttydisc_rint(tp, buf[i], 0);
275                 ttydisc_rint_done(tp);
276                 tty_unlock(tp);
277         } else {
278                 for (i = 0; i < len; i++)
279                         rbuf[RBUF_MASK(rp++)] = buf[i];
280         }
281 }
282
283 static void 
284 __xencons_tx_flush(void)
285 {
286         int        sz;
287
288         CN_LOCK(cn_mtx);
289         while (wc != wp) {
290                 int sent;
291                 sz = wp - wc;
292                 if (sz > (WBUF_SIZE - WBUF_MASK(wc)))
293                         sz = WBUF_SIZE - WBUF_MASK(wc);
294                 if (xen_start_info->flags & SIF_INITDOMAIN) {
295                         HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]);
296                         wc += sz;
297                 } else {
298                         sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
299                         if (sent == 0) 
300                                 break;
301                         wc += sent;
302                 }
303         }
304         CN_UNLOCK(cn_mtx);
305 }
306
307 void
308 xencons_tx(void)
309 {
310         __xencons_tx_flush();
311 }
312
313 static void
314 xencons_priv_interrupt(void *arg)
315 {
316
317         static char rbuf[16];
318         int         l;
319
320         while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
321                 xencons_rx(rbuf, l);
322
323         xencons_tx();
324 }
325
326 static int
327 xcopen(struct tty *tp)
328 {
329
330         xen_console_up = 1;
331         return (0);
332 }
333
334 static void
335 xcclose(struct tty *tp)
336 {
337
338         xen_console_up = 0;
339 }
340
341 static inline int 
342 __xencons_put_char(int ch)
343 {
344         char _ch = (char)ch;
345         if ((wp - wc) == WBUF_SIZE)
346                 return 0;
347         wbuf[WBUF_MASK(wp++)] = _ch;
348         return 1;
349 }
350
351
352 static void
353 xcoutwakeup(struct tty *tp)
354 {
355         boolean_t cons_full = FALSE;
356         char c;
357
358         while (ttydisc_getc(tp, &c, 1) == 1 && !cons_full)
359                 cons_full = xcons_putc(c);
360
361         if (cons_full) {
362                 /* let the timeout kick us in a bit */
363                 xc_start_needed = TRUE;
364         }
365
366 }
367
368 static void
369 xc_timeout(void *v)
370 {
371         struct  tty *tp;
372         int     c;
373
374         tp = (struct tty *)v;
375
376         tty_lock(tp);
377         while ((c = xccncheckc(NULL)) != -1)
378                 ttydisc_rint(tp, c, 0);
379
380         if (xc_start_needed) {
381                 xc_start_needed = FALSE;
382                 xcoutwakeup(tp);
383         }
384         tty_unlock(tp);
385
386         callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, tp);
387 }
388
389 static device_method_t xc_methods[] = {
390         DEVMETHOD(device_identify, xc_identify),
391         DEVMETHOD(device_probe, xc_probe),
392         DEVMETHOD(device_attach, xc_attach),
393         {0, 0}
394 };
395
396 static driver_t xc_driver = {
397         driver_name,
398         xc_methods,
399         0,
400 };
401
402 /*** Forcibly flush console data before dying. ***/
403 void 
404 xcons_force_flush(void)
405 {
406         int        sz;
407
408         if (xen_start_info->flags & SIF_INITDOMAIN)
409                 return;
410
411         /* Spin until console data is flushed through to the domain controller. */
412         while (wc != wp) {
413                 int sent = 0;
414                 if ((sz = wp - wc) == 0)
415                         continue;
416                 
417                 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
418                 if (sent > 0)
419                         wc += sent;             
420         }
421 }
422
423 DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);
424 /*
425  * Local variables:
426  * mode: C
427  * c-set-style: "BSD"
428  * c-basic-offset: 8
429  * tab-width: 4
430  * indent-tabs-mode: t
431  * End:
432  */