]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/xen/console/console.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.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 <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 static cn_grab_t        xc_cngrab;
54 static cn_ungrab_t      xc_cnungrab;
55
56 #define XC_POLLTIME     (hz/10)
57
58 CONSOLE_DRIVER(xc);
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 xen_intr_handle_t xen_intr_handle;
75 device_t xencons_dev;
76
77 #ifdef KDB
78 static int      xc_altbrk;
79 #endif
80
81 #define CDEV_MAJOR 12
82 #define XCUNIT(x)       (dev2unit(x))
83 #define ISTTYOPEN(tp)   ((tp) && ((tp)->t_state & TS_ISOPEN))
84 #define CN_LOCK_INIT(x, _name) \
85         mtx_init(&x, _name, NULL, MTX_SPIN|MTX_RECURSE)
86
87 #define CN_LOCK(l)                                                                      \
88                 do {                                                                                    \
89                                 if (panicstr == NULL)                                   \
90                         mtx_lock_spin(&(l));                    \
91                 } while (0)
92 #define CN_UNLOCK(l)                                                            \
93                 do {                                                                                    \
94                                 if (panicstr == NULL)                                   \
95                         mtx_unlock_spin(&(l));                  \
96                 } while (0)
97 #define CN_LOCK_ASSERT(x)    mtx_assert(&x, MA_OWNED)
98 #define CN_LOCK_DESTROY(x)   mtx_destroy(&x)
99
100
101 static struct tty *xccons;
102
103 static tsw_open_t       xcopen;
104 static tsw_close_t      xcclose;
105
106 static struct ttydevsw xc_ttydevsw = {
107         .tsw_flags      = TF_NOPREFIX,
108         .tsw_open       = xcopen,
109         .tsw_close      = xcclose,
110         .tsw_outwakeup  = xcoutwakeup,
111 };
112
113 static void
114 xc_cnprobe(struct consdev *cp)
115 {
116         cp->cn_pri = CN_REMOTE;
117         sprintf(cp->cn_name, "%s0", driver_name);
118 }
119
120
121 static void
122 xc_cninit(struct consdev *cp)
123
124         CN_LOCK_INIT(cn_mtx,"XCONS LOCK");
125
126 }
127
128 static void
129 xc_cnterm(struct consdev *cp)
130
131 }
132
133 static void
134 xc_cngrab(struct consdev *cp)
135 {
136 }
137
138 static void
139 xc_cnungrab(struct consdev *cp)
140 {
141 }
142
143 static int
144 xc_cngetc(struct consdev *dev)
145 {
146         int ret;
147
148         if (xencons_has_input())
149                 xencons_handle_input(NULL);
150         
151         CN_LOCK(cn_mtx);
152         if ((rp - rc) && !xc_mute) {
153                 /* we need to return only one char */
154                 ret = (int)rbuf[RBUF_MASK(rc)];
155                 rc++;
156         } else
157                 ret = -1;
158         CN_UNLOCK(cn_mtx);
159         return(ret);
160 }
161
162 static void
163 xc_cnputc_domu(struct consdev *dev, int c)
164 {
165         xcons_putc(c);
166 }
167
168 static void
169 xc_cnputc_dom0(struct consdev *dev, int c)
170 {
171         HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&c);
172 }
173
174 static void
175 xc_cnputc(struct consdev *dev, int c)
176 {
177
178         if (xen_start_info->flags & SIF_INITDOMAIN)
179                 xc_cnputc_dom0(dev, c);
180         else
181                 xc_cnputc_domu(dev, c);
182 }
183
184 extern int db_active;
185 static boolean_t
186 xcons_putc(int c)
187 {
188         int force_flush = xc_mute ||
189 #ifdef DDB
190                 db_active ||
191 #endif
192                 panicstr;       /* we're not gonna recover, so force
193                                  * flush 
194                                  */
195
196         if ((wp-wc) < (WBUF_SIZE-1)) {
197                 if ((wbuf[WBUF_MASK(wp++)] = c) == '\n') {
198                         wbuf[WBUF_MASK(wp++)] = '\r';
199 #ifdef notyet
200                         if (force_flush)
201                                 xcons_force_flush();
202 #endif
203                 }
204         } else if (force_flush) {
205 #ifdef notyet
206                 xcons_force_flush();
207 #endif          
208         }
209         if (cnsl_evt_reg)
210                 __xencons_tx_flush();
211         
212         /* inform start path that we're pretty full */
213         return ((wp - wc) >= WBUF_SIZE - 100) ? TRUE : FALSE;
214 }
215
216 static void
217 xc_identify(driver_t *driver, device_t parent)
218 {
219         device_t child;
220         child = BUS_ADD_CHILD(parent, 0, driver_name, 0);
221         device_set_driver(child, driver);
222         device_set_desc(child, "Xen Console");
223 }
224
225 static int
226 xc_probe(device_t dev)
227 {
228
229         return (BUS_PROBE_NOWILDCARD);
230 }
231
232 static int
233 xc_attach(device_t dev) 
234 {
235         int error;
236
237         xencons_dev = dev;
238         xccons = tty_alloc(&xc_ttydevsw, NULL);
239         tty_makedev(xccons, NULL, "xc%r", 0);
240
241         callout_init(&xc_callout, 0);
242
243         xencons_ring_init();
244
245         cnsl_evt_reg = 1;
246         callout_reset(&xc_callout, XC_POLLTIME, xc_timeout, xccons);
247     
248         if (xen_start_info->flags & SIF_INITDOMAIN) {
249                 error = xen_intr_bind_virq(dev, VIRQ_CONSOLE, 0, NULL,
250                                            xencons_priv_interrupt, NULL,
251                                            INTR_TYPE_TTY, &xen_intr_handle);
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 #ifdef KDB
287                         kdb_alt_break(buf[i], &xc_altbrk);
288 #endif
289                         ttydisc_rint(tp, buf[i], 0);
290                 }
291                 ttydisc_rint_done(tp);
292                 tty_unlock(tp);
293         } else {
294                 CN_LOCK(cn_mtx);
295                 for (i = 0; i < len; i++)
296                         rbuf[RBUF_MASK(rp++)] = buf[i];
297                 CN_UNLOCK(cn_mtx);
298         }
299 }
300
301 static void 
302 __xencons_tx_flush(void)
303 {
304         int        sz;
305
306         CN_LOCK(cn_mtx);
307         while (wc != wp) {
308                 int sent;
309                 sz = wp - wc;
310                 if (sz > (WBUF_SIZE - WBUF_MASK(wc)))
311                         sz = WBUF_SIZE - WBUF_MASK(wc);
312                 if (xen_start_info->flags & SIF_INITDOMAIN) {
313                         HYPERVISOR_console_io(CONSOLEIO_write, sz, &wbuf[WBUF_MASK(wc)]);
314                         wc += sz;
315                 } else {
316                         sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
317                         if (sent == 0) 
318                                 break;
319                         wc += sent;
320                 }
321         }
322         CN_UNLOCK(cn_mtx);
323 }
324
325 void
326 xencons_tx(void)
327 {
328         __xencons_tx_flush();
329 }
330
331 static void
332 xencons_priv_interrupt(void *arg)
333 {
334
335         static char rbuf[16];
336         int         l;
337
338         while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
339                 xencons_rx(rbuf, l);
340
341         xencons_tx();
342 }
343
344 static int
345 xcopen(struct tty *tp)
346 {
347
348         xen_console_up = 1;
349         return (0);
350 }
351
352 static void
353 xcclose(struct tty *tp)
354 {
355
356         xen_console_up = 0;
357 }
358
359 #if 0
360 static inline int 
361 __xencons_put_char(int ch)
362 {
363         char _ch = (char)ch;
364         if ((wp - wc) == WBUF_SIZE)
365                 return 0;
366         wbuf[WBUF_MASK(wp++)] = _ch;
367         return 1;
368 }
369 #endif
370
371
372 static void
373 xcoutwakeup(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
388 static void
389 xc_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
409 static device_method_t xc_methods[] = {
410         DEVMETHOD(device_identify, xc_identify),
411         DEVMETHOD(device_probe, xc_probe),
412         DEVMETHOD(device_attach, xc_attach),
413
414         DEVMETHOD_END
415 };
416
417 static driver_t xc_driver = {
418         driver_name,
419         xc_methods,
420         0,
421 };
422
423 /*** Forcibly flush console data before dying. ***/
424 void 
425 xcons_force_flush(void)
426 {
427         int        sz;
428
429         if (xen_start_info->flags & SIF_INITDOMAIN)
430                 return;
431
432         /* Spin until console data is flushed through to the domain controller. */
433         while (wc != wp) {
434                 int sent = 0;
435                 if ((sz = wp - wc) == 0)
436                         continue;
437                 
438                 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
439                 if (sent > 0)
440                         wc += sent;             
441         }
442 }
443
444 DRIVER_MODULE(xc, nexus, xc_driver, xc_devclass, 0, 0);