- 2.6.17 port work build breaks, but the patch set is relativly stable
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / console / console.c
1 /******************************************************************************
2  * console.c
3  * 
4  * Virtual console driver.
5  * 
6  * Copyright (c) 2002-2004, K A Fraser.
7  * 
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version 2
10  * as published by the Free Software Foundation; or, when distributed
11  * separately from the Linux kernel or incorporated into other
12  * software packages, subject to the following license:
13  * 
14  * Permission is hereby granted, free of charge, to any person obtaining a copy
15  * of this source file (the "Software"), to deal in the Software without
16  * restriction, including without limitation the rights to use, copy, modify,
17  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
18  * and to permit persons to whom the Software is furnished to do so, subject to
19  * the following conditions:
20  * 
21  * The above copyright notice and this permission notice shall be included in
22  * all copies or substantial portions of the Software.
23  * 
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * IN THE SOFTWARE.
31  */
32
33 #include <linux/config.h>
34 #include <linux/version.h>
35 #include <linux/module.h>
36 #include <linux/errno.h>
37 #include <linux/signal.h>
38 #include <linux/sched.h>
39 #include <linux/interrupt.h>
40 #include <linux/tty.h>
41 #include <linux/tty_flip.h>
42 #include <linux/serial.h>
43 #include <linux/major.h>
44 #include <linux/ptrace.h>
45 #include <linux/ioport.h>
46 #include <linux/mm.h>
47 #include <linux/slab.h>
48 #include <linux/init.h>
49 #include <linux/console.h>
50 #include <linux/bootmem.h>
51 #include <linux/sysrq.h>
52 #include <asm/io.h>
53 #include <asm/irq.h>
54 #include <asm/uaccess.h>
55 #include <xen/interface/xen.h>
56 #include <xen/interface/event_channel.h>
57 #include <asm/hypervisor.h>
58 #include <xen/evtchn.h>
59 #include <xen/xencons.h>
60
61 /*
62  * Modes:
63  *  'xencons=off'  [XC_OFF]:     Console is disabled.
64  *  'xencons=tty'  [XC_TTY]:     Console attached to '/dev/tty[0-9]+'.
65  *  'xencons=ttyS' [XC_SERIAL]:  Console attached to '/dev/ttyS[0-9]+'.
66  *                 [XC_DEFAULT]: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
67  * 
68  * NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses
69  * warnings from standard distro startup scripts.
70  */
71 static enum { XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL } xc_mode = XC_DEFAULT;
72 static int xc_num = -1;
73
74 #ifdef CONFIG_MAGIC_SYSRQ
75 static unsigned long sysrq_requested;
76 extern int sysrq_enabled;
77 #endif
78
79 static int __init xencons_setup(char *str)
80 {
81         char *q;
82         int n;
83
84         if (!strncmp(str, "ttyS", 4))
85                 xc_mode = XC_SERIAL;
86         else if (!strncmp(str, "tty", 3))
87                 xc_mode = XC_TTY;
88         else if (!strncmp(str, "off", 3))
89                 xc_mode = XC_OFF;
90
91         switch (xc_mode) {
92         case XC_SERIAL:
93                 n = simple_strtol(str+4, &q, 10);
94                 if (q > (str + 4))
95                         xc_num = n;
96                 break;
97         case XC_TTY:
98                 n = simple_strtol(str+3, &q, 10);
99                 if (q > (str + 3))
100                         xc_num = n;
101                 break;
102         default:
103                 break;
104         }
105
106         return 1;
107 }
108 __setup("xencons=", xencons_setup);
109
110 /* The kernel and user-land drivers share a common transmit buffer. */
111 static unsigned int wbuf_size = 4096;
112 #define WBUF_MASK(_i) ((_i)&(wbuf_size-1))
113 static char *wbuf;
114 static unsigned int wc, wp; /* write_cons, write_prod */
115
116 static int __init xencons_bufsz_setup(char *str)
117 {
118         unsigned int goal;
119         goal = simple_strtoul(str, NULL, 0);
120         while (wbuf_size < goal)
121                 wbuf_size <<= 1;
122         return 1;
123 }
124 __setup("xencons_bufsz=", xencons_bufsz_setup);
125
126 /* This lock protects accesses to the common transmit buffer. */
127 static spinlock_t xencons_lock = SPIN_LOCK_UNLOCKED;
128
129 /* Common transmit-kick routine. */
130 static void __xencons_tx_flush(void);
131
132 static struct tty_driver *xencons_driver;
133
134 /******************** Kernel console driver ********************************/
135
136 static void kcons_write(
137         struct console *c, const char *s, unsigned int count)
138 {
139         int           i = 0;
140         unsigned long flags;
141
142         spin_lock_irqsave(&xencons_lock, flags);
143
144         while (i < count) {
145                 for (; i < count; i++) {
146                         if ((wp - wc) >= (wbuf_size - 1))
147                                 break;
148                         if ((wbuf[WBUF_MASK(wp++)] = s[i]) == '\n')
149                                 wbuf[WBUF_MASK(wp++)] = '\r';
150                 }
151
152                 __xencons_tx_flush();
153         }
154
155         spin_unlock_irqrestore(&xencons_lock, flags);
156 }
157
158 static void kcons_write_dom0(
159         struct console *c, const char *s, unsigned int count)
160 {
161         int rc;
162
163         while ((count > 0) &&
164                ((rc = HYPERVISOR_console_io(
165                         CONSOLEIO_write, count, (char *)s)) > 0)) {
166                 count -= rc;
167                 s += rc;
168         }
169 }
170
171 static struct tty_driver *kcons_device(struct console *c, int *index)
172 {
173         *index = 0;
174         return xencons_driver;
175 }
176
177 static struct console kcons_info = {
178         .device = kcons_device,
179         .flags  = CON_PRINTBUFFER,
180         .index  = -1,
181 };
182
183 #define __RETCODE 0
184 static int __init xen_console_init(void)
185 {
186         if (xen_init() < 0)
187                 return __RETCODE;
188
189         if (xen_start_info->flags & SIF_INITDOMAIN) {
190                 if (xc_mode == XC_DEFAULT)
191                         xc_mode = XC_SERIAL;
192                 kcons_info.write = kcons_write_dom0;
193                 if (xc_mode == XC_SERIAL)
194                         kcons_info.flags |= CON_ENABLED;
195         } else {
196                 if (xc_mode == XC_DEFAULT)
197                         xc_mode = XC_TTY;
198                 kcons_info.write = kcons_write;
199         }
200
201         switch (xc_mode) {
202         case XC_SERIAL:
203                 strcpy(kcons_info.name, "ttyS");
204                 if (xc_num == -1)
205                         xc_num = 0;
206                 break;
207
208         case XC_TTY:
209                 strcpy(kcons_info.name, "tty");
210                 if (xc_num == -1)
211                         xc_num = 1;
212                 break;
213
214         default:
215                 return __RETCODE;
216         }
217
218         wbuf = alloc_bootmem(wbuf_size);
219
220         register_console(&kcons_info);
221
222         return __RETCODE;
223 }
224 console_initcall(xen_console_init);
225
226 /*** Useful function for console debugging -- goes straight to Xen. ***/
227 asmlinkage int xprintk(const char *fmt, ...)
228 {
229         va_list args;
230         int printk_len;
231         static char printk_buf[1024];
232
233         /* Emit the output into the temporary buffer */
234         va_start(args, fmt);
235         printk_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
236         va_end(args);
237
238         /* Send the processed output directly to Xen. */
239         kcons_write_dom0(NULL, printk_buf, printk_len);
240
241         return 0;
242 }
243
244 /*** Forcibly flush console data before dying. ***/
245 void xencons_force_flush(void)
246 {
247         int sz;
248
249         /* Emergency console is synchronous, so there's nothing to flush. */
250         if (xen_start_info->flags & SIF_INITDOMAIN)
251                 return;
252
253         /* Spin until console data is flushed through to the daemon. */
254         while (wc != wp) {
255                 int sent = 0;
256                 if ((sz = wp - wc) == 0)
257                         continue;
258                 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
259                 if (sent > 0)
260                         wc += sent;
261         }
262 }
263
264
265 /******************** User-space console driver (/dev/console) ************/
266
267 #define DRV(_d)         (_d)
268 #define TTY_INDEX(_tty) ((_tty)->index)
269
270 static struct termios *xencons_termios[MAX_NR_CONSOLES];
271 static struct termios *xencons_termios_locked[MAX_NR_CONSOLES];
272 static struct tty_struct *xencons_tty;
273 static int xencons_priv_irq;
274 static char x_char;
275
276 void xencons_rx(char *buf, unsigned len, struct pt_regs *regs)
277 {
278         int           i;
279         unsigned long flags;
280
281         spin_lock_irqsave(&xencons_lock, flags);
282         if (xencons_tty == NULL)
283                 goto out;
284
285         for (i = 0; i < len; i++) {
286 #ifdef CONFIG_MAGIC_SYSRQ
287                 if (sysrq_enabled) {
288                         if (buf[i] == '\x0f') { /* ^O */
289                                 sysrq_requested = jiffies;
290                                 continue; /* don't print the sysrq key */
291                         } else if (sysrq_requested) {
292                                 unsigned long sysrq_timeout =
293                                         sysrq_requested + HZ*2;
294                                 sysrq_requested = 0;
295                                 if (time_before(jiffies, sysrq_timeout)) {
296                                         spin_unlock_irqrestore(
297                                                 &xencons_lock, flags);
298                                         handle_sysrq(
299                                                 buf[i], regs, xencons_tty);
300                                         spin_lock_irqsave(
301                                                 &xencons_lock, flags);
302                                         continue;
303                                 }
304                         }
305                 }
306 #endif
307                 tty_insert_flip_char(xencons_tty, buf[i], 0);
308         }
309         tty_flip_buffer_push(xencons_tty);
310
311  out:
312         spin_unlock_irqrestore(&xencons_lock, flags);
313 }
314
315 static void __xencons_tx_flush(void)
316 {
317         int sent, sz, work_done = 0;
318
319         if (x_char) {
320                 if (xen_start_info->flags & SIF_INITDOMAIN)
321                         kcons_write_dom0(NULL, &x_char, 1);
322                 else
323                         while (x_char)
324                                 if (xencons_ring_send(&x_char, 1) == 1)
325                                         break;
326                 x_char = 0;
327                 work_done = 1;
328         }
329
330         while (wc != wp) {
331                 sz = wp - wc;
332                 if (sz > (wbuf_size - WBUF_MASK(wc)))
333                         sz = wbuf_size - WBUF_MASK(wc);
334                 if (xen_start_info->flags & SIF_INITDOMAIN) {
335                         kcons_write_dom0(NULL, &wbuf[WBUF_MASK(wc)], sz);
336                         wc += sz;
337                 } else {
338                         sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
339                         if (sent == 0)
340                                 break;
341                         wc += sent;
342                 }
343                 work_done = 1;
344         }
345
346         if (work_done && (xencons_tty != NULL)) {
347                 wake_up_interruptible(&xencons_tty->write_wait);
348                 if ((xencons_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
349                     (xencons_tty->ldisc.write_wakeup != NULL))
350                         (xencons_tty->ldisc.write_wakeup)(xencons_tty);
351         }
352 }
353
354 void xencons_tx(void)
355 {
356         unsigned long flags;
357
358         spin_lock_irqsave(&xencons_lock, flags);
359         __xencons_tx_flush();
360         spin_unlock_irqrestore(&xencons_lock, flags);
361 }
362
363 /* Privileged receive callback and transmit kicker. */
364 static irqreturn_t xencons_priv_interrupt(int irq, void *dev_id,
365                                           struct pt_regs *regs)
366 {
367         static char rbuf[16];
368         int         l;
369
370         while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
371                 xencons_rx(rbuf, l, regs);
372
373         xencons_tx();
374
375         return IRQ_HANDLED;
376 }
377
378 static int xencons_write_room(struct tty_struct *tty)
379 {
380         return wbuf_size - (wp - wc);
381 }
382
383 static int xencons_chars_in_buffer(struct tty_struct *tty)
384 {
385         return wp - wc;
386 }
387
388 static void xencons_send_xchar(struct tty_struct *tty, char ch)
389 {
390         unsigned long flags;
391
392         if (TTY_INDEX(tty) != 0)
393                 return;
394
395         spin_lock_irqsave(&xencons_lock, flags);
396         x_char = ch;
397         __xencons_tx_flush();
398         spin_unlock_irqrestore(&xencons_lock, flags);
399 }
400
401 static void xencons_throttle(struct tty_struct *tty)
402 {
403         if (TTY_INDEX(tty) != 0)
404                 return;
405
406         if (I_IXOFF(tty))
407                 xencons_send_xchar(tty, STOP_CHAR(tty));
408 }
409
410 static void xencons_unthrottle(struct tty_struct *tty)
411 {
412         if (TTY_INDEX(tty) != 0)
413                 return;
414
415         if (I_IXOFF(tty)) {
416                 if (x_char != 0)
417                         x_char = 0;
418                 else
419                         xencons_send_xchar(tty, START_CHAR(tty));
420         }
421 }
422
423 static void xencons_flush_buffer(struct tty_struct *tty)
424 {
425         unsigned long flags;
426
427         if (TTY_INDEX(tty) != 0)
428                 return;
429
430         spin_lock_irqsave(&xencons_lock, flags);
431         wc = wp = 0;
432         spin_unlock_irqrestore(&xencons_lock, flags);
433 }
434
435 static inline int __xencons_put_char(int ch)
436 {
437         char _ch = (char)ch;
438         if ((wp - wc) == wbuf_size)
439                 return 0;
440         wbuf[WBUF_MASK(wp++)] = _ch;
441         return 1;
442 }
443
444 static int xencons_write(
445         struct tty_struct *tty,
446         const unsigned char *buf,
447         int count)
448 {
449         int i;
450         unsigned long flags;
451
452         if (TTY_INDEX(tty) != 0)
453                 return count;
454
455         spin_lock_irqsave(&xencons_lock, flags);
456
457         for (i = 0; i < count; i++)
458                 if (!__xencons_put_char(buf[i]))
459                         break;
460
461         if (i != 0)
462                 __xencons_tx_flush();
463
464         spin_unlock_irqrestore(&xencons_lock, flags);
465
466         return i;
467 }
468
469 static void xencons_put_char(struct tty_struct *tty, u_char ch)
470 {
471         unsigned long flags;
472
473         if (TTY_INDEX(tty) != 0)
474                 return;
475
476         spin_lock_irqsave(&xencons_lock, flags);
477         (void)__xencons_put_char(ch);
478         spin_unlock_irqrestore(&xencons_lock, flags);
479 }
480
481 static void xencons_flush_chars(struct tty_struct *tty)
482 {
483         unsigned long flags;
484
485         if (TTY_INDEX(tty) != 0)
486                 return;
487
488         spin_lock_irqsave(&xencons_lock, flags);
489         __xencons_tx_flush();
490         spin_unlock_irqrestore(&xencons_lock, flags);
491 }
492
493 static void xencons_wait_until_sent(struct tty_struct *tty, int timeout)
494 {
495         unsigned long orig_jiffies = jiffies;
496
497         if (TTY_INDEX(tty) != 0)
498                 return;
499
500         while (DRV(tty->driver)->chars_in_buffer(tty)) {
501                 set_current_state(TASK_INTERRUPTIBLE);
502                 schedule_timeout(1);
503                 if (signal_pending(current))
504                         break;
505                 if (timeout && time_after(jiffies, orig_jiffies + timeout))
506                         break;
507         }
508
509         set_current_state(TASK_RUNNING);
510 }
511
512 static int xencons_open(struct tty_struct *tty, struct file *filp)
513 {
514         unsigned long flags;
515
516         if (TTY_INDEX(tty) != 0)
517                 return 0;
518
519         spin_lock_irqsave(&xencons_lock, flags);
520         tty->driver_data = NULL;
521         if (xencons_tty == NULL)
522                 xencons_tty = tty;
523         __xencons_tx_flush();
524         spin_unlock_irqrestore(&xencons_lock, flags);
525
526         return 0;
527 }
528
529 static void xencons_close(struct tty_struct *tty, struct file *filp)
530 {
531         unsigned long flags;
532
533         if (TTY_INDEX(tty) != 0)
534                 return;
535
536         if (tty->count == 1) {
537                 tty->closing = 1;
538                 tty_wait_until_sent(tty, 0);
539                 if (DRV(tty->driver)->flush_buffer != NULL)
540                         DRV(tty->driver)->flush_buffer(tty);
541                 if (tty->ldisc.flush_buffer != NULL)
542                         tty->ldisc.flush_buffer(tty);
543                 tty->closing = 0;
544                 spin_lock_irqsave(&xencons_lock, flags);
545                 xencons_tty = NULL;
546                 spin_unlock_irqrestore(&xencons_lock, flags);
547         }
548 }
549
550 static struct tty_operations xencons_ops = {
551         .open = xencons_open,
552         .close = xencons_close,
553         .write = xencons_write,
554         .write_room = xencons_write_room,
555         .put_char = xencons_put_char,
556         .flush_chars = xencons_flush_chars,
557         .chars_in_buffer = xencons_chars_in_buffer,
558         .send_xchar = xencons_send_xchar,
559         .flush_buffer = xencons_flush_buffer,
560         .throttle = xencons_throttle,
561         .unthrottle = xencons_unthrottle,
562         .wait_until_sent = xencons_wait_until_sent,
563 };
564
565 static int __init xencons_init(void)
566 {
567         int rc;
568
569         if (xen_init() < 0)
570                 return -ENODEV;
571
572         if (xc_mode == XC_OFF)
573                 return 0;
574
575         xencons_ring_init();
576
577         xencons_driver = alloc_tty_driver((xc_mode == XC_SERIAL) ?
578                                           1 : MAX_NR_CONSOLES);
579         if (xencons_driver == NULL)
580                 return -ENOMEM;
581
582         DRV(xencons_driver)->name            = "xencons";
583         DRV(xencons_driver)->major           = TTY_MAJOR;
584         DRV(xencons_driver)->type            = TTY_DRIVER_TYPE_SERIAL;
585         DRV(xencons_driver)->subtype         = SERIAL_TYPE_NORMAL;
586         DRV(xencons_driver)->init_termios    = tty_std_termios;
587         DRV(xencons_driver)->flags           =
588                 TTY_DRIVER_REAL_RAW |
589                 TTY_DRIVER_RESET_TERMIOS |
590                 TTY_DRIVER_NO_DEVFS;
591         DRV(xencons_driver)->termios         = xencons_termios;
592         DRV(xencons_driver)->termios_locked  = xencons_termios_locked;
593
594         if (xc_mode == XC_SERIAL) {
595                 DRV(xencons_driver)->name        = "ttyS";
596                 DRV(xencons_driver)->minor_start = 64 + xc_num;
597                 DRV(xencons_driver)->name_base   = 0 + xc_num;
598         } else {
599                 DRV(xencons_driver)->name        = "tty";
600                 DRV(xencons_driver)->minor_start = xc_num;
601                 DRV(xencons_driver)->name_base   = xc_num;
602         }
603
604         tty_set_operations(xencons_driver, &xencons_ops);
605
606         if ((rc = tty_register_driver(DRV(xencons_driver))) != 0) {
607                 printk("WARNING: Failed to register Xen virtual "
608                        "console driver as '%s%d'\n",
609                        DRV(xencons_driver)->name,
610                        DRV(xencons_driver)->name_base);
611                 put_tty_driver(xencons_driver);
612                 xencons_driver = NULL;
613                 return rc;
614         }
615
616         tty_register_device(xencons_driver, 0, NULL);
617
618         if (xen_start_info->flags & SIF_INITDOMAIN) {
619                 xencons_priv_irq = bind_virq_to_irqhandler(
620                         VIRQ_CONSOLE,
621                         0,
622                         xencons_priv_interrupt,
623                         0,
624                         "console",
625                         NULL);
626                 BUG_ON(xencons_priv_irq < 0);
627         }
628
629         printk("Xen virtual console successfully installed as %s%d\n",
630                DRV(xencons_driver)->name,
631                DRV(xencons_driver)->name_base );
632
633         return 0;
634 }
635
636 module_init(xencons_init);
637
638 MODULE_LICENSE("Dual BSD/GPL");
639
640 /*
641  * Local variables:
642  *  c-file-style: "linux"
643  *  indent-tabs-mode: t
644  *  c-indent-level: 8
645  *  c-basic-offset: 8
646  *  tab-width: 8
647  * End:
648  */