- patches.rt/0001-sched-count-of-queued-RT-tasks.patch: Delete.
[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/version.h>
34 #include <linux/module.h>
35 #include <linux/errno.h>
36 #include <linux/signal.h>
37 #include <linux/sched.h>
38 #include <linux/interrupt.h>
39 #include <linux/tty.h>
40 #include <linux/tty_flip.h>
41 #include <linux/serial.h>
42 #include <linux/major.h>
43 #include <linux/ptrace.h>
44 #include <linux/ioport.h>
45 #include <linux/mm.h>
46 #include <linux/slab.h>
47 #include <linux/init.h>
48 #include <linux/console.h>
49 #include <linux/bootmem.h>
50 #include <linux/sysrq.h>
51 #include <linux/screen_info.h>
52 #include <linux/vt.h>
53 #include <asm/io.h>
54 #include <asm/irq.h>
55 #include <asm/uaccess.h>
56 #include <xen/interface/xen.h>
57 #include <xen/interface/event_channel.h>
58 #include <asm/hypervisor.h>
59 #include <xen/evtchn.h>
60 #include <xen/xenbus.h>
61 #include <xen/xencons.h>
62
63 /*
64  * Modes:
65  *  'xencons=off'  [XC_OFF]:     Console is disabled.
66  *  'xencons=tty'  [XC_TTY]:     Console attached to '/dev/tty[0-9]+'.
67  *  'xencons=ttyS' [XC_SERIAL]:  Console attached to '/dev/ttyS[0-9]+'.
68  *  'xencons=xvc'  [XC_XVC]:     Console attached to '/dev/xvc0'.
69  *  default:                     XC_XVC
70  * 
71  * NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses
72  * warnings from standard distro startup scripts.
73  */
74 static enum {
75         XC_OFF, XC_TTY, XC_SERIAL, XC_XVC
76 } xc_mode = XC_XVC;
77 static int xc_num = -1;
78
79 /* /dev/xvc0 device number allocated by lanana.org. */
80 #define XEN_XVC_MAJOR 204
81 #define XEN_XVC_MINOR 191
82
83 static int __init xencons_setup(char *str)
84 {
85         char *q;
86         int n;
87         extern int console_use_vt;
88
89         console_use_vt = 1;
90         if (!strncmp(str, "ttyS", 4)) {
91                 xc_mode = XC_SERIAL;
92                 str += 4;
93         } else if (!strncmp(str, "tty", 3)) {
94                 xc_mode = XC_TTY;
95                 str += 3;
96                 console_use_vt = 0;
97         } else if (!strncmp(str, "xvc", 3)) {
98                 xc_mode = XC_XVC;
99                 str += 3;
100         } else if (!strncmp(str, "off", 3)) {
101                 xc_mode = XC_OFF;
102                 str += 3;
103         }
104
105         n = simple_strtol(str, &q, 10);
106         if (q != str)
107                 xc_num = n;
108
109         return 1;
110 }
111 __setup("xencons=", xencons_setup);
112
113 /* The kernel and user-land drivers share a common transmit buffer. */
114 static unsigned int wbuf_size = 4096;
115 #define WBUF_MASK(_i) ((_i)&(wbuf_size-1))
116 static char *wbuf;
117 static unsigned int wc, wp; /* write_cons, write_prod */
118
119 static int __init xencons_bufsz_setup(char *str)
120 {
121         unsigned int goal;
122         goal = simple_strtoul(str, NULL, 0);
123         if (goal) {
124                 goal = roundup_pow_of_two(goal);
125                 if (wbuf_size < goal)
126                         wbuf_size = goal;
127         }
128         return 1;
129 }
130 __setup("xencons_bufsz=", xencons_bufsz_setup);
131
132 /* This lock protects accesses to the common transmit buffer. */
133 static DEFINE_SPINLOCK(xencons_lock);
134
135 /* Common transmit-kick routine. */
136 static void __xencons_tx_flush(void);
137
138 static struct tty_driver *xencons_driver;
139
140 /******************** Kernel console driver ********************************/
141
142 static void kcons_write(struct console *c, const char *s, unsigned int count)
143 {
144         int           i = 0;
145         unsigned long flags;
146
147         spin_lock_irqsave(&xencons_lock, flags);
148
149         while (i < count) {
150                 for (; i < count; i++) {
151                         if ((wp - wc) >= (wbuf_size - 1))
152                                 break;
153                         if ((wbuf[WBUF_MASK(wp++)] = s[i]) == '\n')
154                                 wbuf[WBUF_MASK(wp++)] = '\r';
155                 }
156
157                 __xencons_tx_flush();
158         }
159
160         spin_unlock_irqrestore(&xencons_lock, flags);
161 }
162
163 static void kcons_write_dom0(struct console *c, const char *s, unsigned int count)
164 {
165
166         while (count > 0) {
167                 int rc;
168                 rc = HYPERVISOR_console_io( CONSOLEIO_write, count, (char *)s);
169                 if (rc <= 0)
170                         break;
171                 count -= rc;
172                 s += rc;
173         }
174 }
175
176 static struct tty_driver *kcons_device(struct console *c, int *index)
177 {
178         *index = 0;
179         return xencons_driver;
180 }
181
182 static struct console kcons_info = {
183         .device = kcons_device,
184         .flags  = CON_PRINTBUFFER | CON_ENABLED,
185         .index  = -1,
186 };
187
188 static int __init xen_console_init(void)
189 {
190         if (!is_running_on_xen())
191                 goto out;
192
193         if (is_initial_xendomain()) {
194                 kcons_info.write = kcons_write_dom0;
195         } else {
196                 if (!xen_start_info->console.domU.evtchn)
197                         goto out;
198                 kcons_info.write = kcons_write;
199         }
200
201         switch (xc_mode) {
202         case XC_XVC:
203                 strcpy(kcons_info.name, "xvc");
204                 if (xc_num == -1)
205                         xc_num = 0;
206                 break;
207
208         case XC_SERIAL:
209                 strcpy(kcons_info.name, "ttyS");
210                 if (xc_num == -1)
211                         xc_num = 0;
212                 break;
213
214         case XC_TTY:
215                 strcpy(kcons_info.name, "tty");
216                 if (xc_num == -1)
217                         xc_num = 1;
218                 break;
219
220         default:
221                 goto out;
222         }
223
224         wbuf = alloc_bootmem(wbuf_size);
225
226         register_console(&kcons_info);
227
228  out:
229         return 0;
230 }
231 console_initcall(xen_console_init);
232
233 /*** Useful function for console debugging -- goes straight to Xen. ***/
234 asmlinkage int xprintk(const char *fmt, ...)
235 {
236         va_list args;
237         int printk_len;
238         static char printk_buf[1024];
239
240         /* Emit the output into the temporary buffer */
241         va_start(args, fmt);
242         printk_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
243         va_end(args);
244
245         /* Send the processed output directly to Xen. */
246         kcons_write_dom0(NULL, printk_buf, printk_len);
247
248         return 0;
249 }
250
251 /*** Forcibly flush console data before dying. ***/
252 void xencons_force_flush(void)
253 {
254         int sz;
255
256         /* Emergency console is synchronous, so there's nothing to flush. */
257         if (!is_running_on_xen() ||
258             is_initial_xendomain() ||
259             !xen_start_info->console.domU.evtchn)
260                 return;
261
262         /* Spin until console data is flushed through to the daemon. */
263         while (wc != wp) {
264                 int sent = 0;
265                 if ((sz = wp - wc) == 0)
266                         continue;
267                 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
268                 if (sent > 0)
269                         wc += sent;
270         }
271 }
272
273
274 /******************** User-space console driver (/dev/console) ************/
275
276 #define DRV(_d)         (_d)
277 #define DUMMY_TTY(_tty) ((xc_mode == XC_TTY) &&         \
278                          ((_tty)->index != (xc_num - 1)))
279
280 static struct ktermios *xencons_termios[MAX_NR_CONSOLES];
281 static struct ktermios *xencons_termios_locked[MAX_NR_CONSOLES];
282 static struct tty_struct *xencons_tty;
283 static int xencons_priv_irq;
284 static char x_char;
285
286 void xencons_rx(char *buf, unsigned len)
287 {
288         int           i;
289         unsigned long flags;
290
291         spin_lock_irqsave(&xencons_lock, flags);
292         if (xencons_tty == NULL)
293                 goto out;
294
295         for (i = 0; i < len; i++) {
296 #ifdef CONFIG_MAGIC_SYSRQ
297                 if (sysrq_on()) {
298                         static unsigned long sysrq_requested;
299
300                         if (buf[i] == '\x0f') { /* ^O */
301                                 if (!sysrq_requested) {
302                                         sysrq_requested = jiffies;
303                                         continue; /* don't print sysrq key */
304                                 }
305                                 sysrq_requested = 0;
306                         } else if (sysrq_requested) {
307                                 unsigned long sysrq_timeout =
308                                         sysrq_requested + HZ*2;
309                                 sysrq_requested = 0;
310                                 if (time_before(jiffies, sysrq_timeout)) {
311                                         spin_unlock_irqrestore(
312                                                 &xencons_lock, flags);
313                                         handle_sysrq(buf[i], xencons_tty);
314                                         spin_lock_irqsave(
315                                                 &xencons_lock, flags);
316                                         continue;
317                                 }
318                         }
319                 }
320 #endif
321                 tty_insert_flip_char(xencons_tty, buf[i], 0);
322         }
323         tty_flip_buffer_push(xencons_tty);
324
325  out:
326         spin_unlock_irqrestore(&xencons_lock, flags);
327 }
328
329 static void __xencons_tx_flush(void)
330 {
331         int sent, sz, work_done = 0;
332
333         if (x_char) {
334                 if (is_initial_xendomain())
335                         kcons_write_dom0(NULL, &x_char, 1);
336                 else
337                         while (x_char)
338                                 if (xencons_ring_send(&x_char, 1) == 1)
339                                         break;
340                 x_char = 0;
341                 work_done = 1;
342         }
343
344         while (wc != wp) {
345                 sz = wp - wc;
346                 if (sz > (wbuf_size - WBUF_MASK(wc)))
347                         sz = wbuf_size - WBUF_MASK(wc);
348                 if (is_initial_xendomain()) {
349                         kcons_write_dom0(NULL, &wbuf[WBUF_MASK(wc)], sz);
350                         wc += sz;
351                 } else {
352                         sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
353                         if (sent == 0)
354                                 break;
355                         wc += sent;
356                 }
357                 work_done = 1;
358         }
359
360         if (work_done && (xencons_tty != NULL)) {
361                 wake_up_interruptible(&xencons_tty->write_wait);
362                 if ((xencons_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
363                     (xencons_tty->ldisc.write_wakeup != NULL))
364                         (xencons_tty->ldisc.write_wakeup)(xencons_tty);
365         }
366 }
367
368 void xencons_tx(void)
369 {
370         unsigned long flags;
371
372         spin_lock_irqsave(&xencons_lock, flags);
373         __xencons_tx_flush();
374         spin_unlock_irqrestore(&xencons_lock, flags);
375 }
376
377 /* Privileged receive callback and transmit kicker. */
378 static irqreturn_t xencons_priv_interrupt(int irq, void *dev_id)
379 {
380         static char rbuf[16];
381         int         l;
382
383         while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
384                 xencons_rx(rbuf, l);
385
386         xencons_tx();
387
388         return IRQ_HANDLED;
389 }
390
391 static int xencons_write_room(struct tty_struct *tty)
392 {
393         return wbuf_size - (wp - wc);
394 }
395
396 static int xencons_chars_in_buffer(struct tty_struct *tty)
397 {
398         return wp - wc;
399 }
400
401 static void xencons_send_xchar(struct tty_struct *tty, char ch)
402 {
403         unsigned long flags;
404
405         if (DUMMY_TTY(tty))
406                 return;
407
408         spin_lock_irqsave(&xencons_lock, flags);
409         x_char = ch;
410         __xencons_tx_flush();
411         spin_unlock_irqrestore(&xencons_lock, flags);
412 }
413
414 static void xencons_throttle(struct tty_struct *tty)
415 {
416         if (DUMMY_TTY(tty))
417                 return;
418
419         if (I_IXOFF(tty))
420                 xencons_send_xchar(tty, STOP_CHAR(tty));
421 }
422
423 static void xencons_unthrottle(struct tty_struct *tty)
424 {
425         if (DUMMY_TTY(tty))
426                 return;
427
428         if (I_IXOFF(tty)) {
429                 if (x_char != 0)
430                         x_char = 0;
431                 else
432                         xencons_send_xchar(tty, START_CHAR(tty));
433         }
434 }
435
436 static void xencons_flush_buffer(struct tty_struct *tty)
437 {
438         unsigned long flags;
439
440         if (DUMMY_TTY(tty))
441                 return;
442
443         spin_lock_irqsave(&xencons_lock, flags);
444         wc = wp = 0;
445         spin_unlock_irqrestore(&xencons_lock, flags);
446 }
447
448 static inline int __xencons_put_char(int ch)
449 {
450         char _ch = (char)ch;
451         if ((wp - wc) == wbuf_size)
452                 return 0;
453         wbuf[WBUF_MASK(wp++)] = _ch;
454         return 1;
455 }
456
457 static int xencons_write(
458         struct tty_struct *tty,
459         const unsigned char *buf,
460         int count)
461 {
462         int i;
463         unsigned long flags;
464
465         if (DUMMY_TTY(tty))
466                 return count;
467
468         spin_lock_irqsave(&xencons_lock, flags);
469
470         for (i = 0; i < count; i++)
471                 if (!__xencons_put_char(buf[i]))
472                         break;
473
474         if (i != 0)
475                 __xencons_tx_flush();
476
477         spin_unlock_irqrestore(&xencons_lock, flags);
478
479         return i;
480 }
481
482 static void xencons_put_char(struct tty_struct *tty, u_char ch)
483 {
484         unsigned long flags;
485
486         if (DUMMY_TTY(tty))
487                 return;
488
489         spin_lock_irqsave(&xencons_lock, flags);
490         (void)__xencons_put_char(ch);
491         spin_unlock_irqrestore(&xencons_lock, flags);
492 }
493
494 static void xencons_flush_chars(struct tty_struct *tty)
495 {
496         unsigned long flags;
497
498         if (DUMMY_TTY(tty))
499                 return;
500
501         spin_lock_irqsave(&xencons_lock, flags);
502         __xencons_tx_flush();
503         spin_unlock_irqrestore(&xencons_lock, flags);
504 }
505
506 static void xencons_wait_until_sent(struct tty_struct *tty, int timeout)
507 {
508         unsigned long orig_jiffies = jiffies;
509
510         if (DUMMY_TTY(tty))
511                 return;
512
513         while (DRV(tty->driver)->chars_in_buffer(tty)) {
514                 set_current_state(TASK_INTERRUPTIBLE);
515                 schedule_timeout(1);
516                 if (signal_pending(current))
517                         break;
518                 if (timeout && time_after(jiffies, orig_jiffies + timeout))
519                         break;
520         }
521
522         set_current_state(TASK_RUNNING);
523 }
524
525 static int xencons_open(struct tty_struct *tty, struct file *filp)
526 {
527         unsigned long flags;
528
529         if (DUMMY_TTY(tty))
530                 return 0;
531
532         spin_lock_irqsave(&xencons_lock, flags);
533         tty->driver_data = NULL;
534         if (xencons_tty == NULL)
535                 xencons_tty = tty;
536         __xencons_tx_flush();
537         spin_unlock_irqrestore(&xencons_lock, flags);
538
539         return 0;
540 }
541
542 static void xencons_close(struct tty_struct *tty, struct file *filp)
543 {
544         unsigned long flags;
545
546         if (DUMMY_TTY(tty))
547                 return;
548
549         mutex_lock(&tty_mutex);
550
551         if (tty->count != 1) {
552                 mutex_unlock(&tty_mutex);
553                 return;
554         }
555
556         /* Prevent other threads from re-opening this tty. */
557         set_bit(TTY_CLOSING, &tty->flags);
558         mutex_unlock(&tty_mutex);
559
560         tty->closing = 1;
561         tty_wait_until_sent(tty, 0);
562         if (DRV(tty->driver)->flush_buffer != NULL)
563                 DRV(tty->driver)->flush_buffer(tty);
564         if (tty->ldisc.flush_buffer != NULL)
565                 tty->ldisc.flush_buffer(tty);
566         tty->closing = 0;
567         spin_lock_irqsave(&xencons_lock, flags);
568         xencons_tty = NULL;
569         spin_unlock_irqrestore(&xencons_lock, flags);
570 }
571
572 static struct tty_operations xencons_ops = {
573         .open = xencons_open,
574         .close = xencons_close,
575         .write = xencons_write,
576         .write_room = xencons_write_room,
577         .put_char = xencons_put_char,
578         .flush_chars = xencons_flush_chars,
579         .chars_in_buffer = xencons_chars_in_buffer,
580         .send_xchar = xencons_send_xchar,
581         .flush_buffer = xencons_flush_buffer,
582         .throttle = xencons_throttle,
583         .unthrottle = xencons_unthrottle,
584         .wait_until_sent = xencons_wait_until_sent,
585 };
586
587 static int __init xencons_init(void)
588 {
589         int rc;
590
591         if (!is_running_on_xen())
592                 return -ENODEV;
593
594         if (xc_mode == XC_OFF)
595                 return 0;
596
597         if (!is_initial_xendomain()) {
598                 rc = xencons_ring_init();
599                 if (rc)
600                         return rc;
601         }
602
603         xencons_driver = alloc_tty_driver((xc_mode == XC_TTY) ?
604                                           MAX_NR_CONSOLES : 1);
605         if (xencons_driver == NULL)
606                 return -ENOMEM;
607
608         DRV(xencons_driver)->name            = "xencons";
609         DRV(xencons_driver)->major           = TTY_MAJOR;
610         DRV(xencons_driver)->type            = TTY_DRIVER_TYPE_SERIAL;
611         DRV(xencons_driver)->subtype         = SERIAL_TYPE_NORMAL;
612         DRV(xencons_driver)->init_termios    = tty_std_termios;
613         DRV(xencons_driver)->flags           =
614                 TTY_DRIVER_REAL_RAW |
615                 TTY_DRIVER_RESET_TERMIOS;
616         DRV(xencons_driver)->termios         = xencons_termios;
617         DRV(xencons_driver)->termios_locked  = xencons_termios_locked;
618
619         switch (xc_mode) {
620         case XC_XVC:
621                 DRV(xencons_driver)->name        = "xvc";
622                 DRV(xencons_driver)->major       = XEN_XVC_MAJOR;
623                 DRV(xencons_driver)->minor_start = XEN_XVC_MINOR;
624                 DRV(xencons_driver)->name_base   = xc_num;
625                 break;
626         case XC_SERIAL:
627                 DRV(xencons_driver)->name        = "ttyS";
628                 DRV(xencons_driver)->minor_start = 64 + xc_num;
629                 DRV(xencons_driver)->name_base   = xc_num;
630                 break;
631         default:
632                 DRV(xencons_driver)->name        = "tty";
633                 DRV(xencons_driver)->minor_start = 1;
634                 DRV(xencons_driver)->name_base   = 1;
635                 break;
636         }
637
638         tty_set_operations(xencons_driver, &xencons_ops);
639
640         if ((rc = tty_register_driver(DRV(xencons_driver))) != 0) {
641                 printk("WARNING: Failed to register Xen virtual "
642                        "console driver as '%s%d'\n",
643                        DRV(xencons_driver)->name,
644                        DRV(xencons_driver)->name_base);
645                 put_tty_driver(xencons_driver);
646                 xencons_driver = NULL;
647                 return rc;
648         }
649
650         if (is_initial_xendomain()) {
651                 xencons_priv_irq = bind_virq_to_irqhandler(
652                         VIRQ_CONSOLE,
653                         0,
654                         xencons_priv_interrupt,
655                         0,
656                         "console",
657                         NULL);
658                 BUG_ON(xencons_priv_irq < 0);
659         }
660
661         printk("Xen virtual console successfully installed as %s%d\n",
662                DRV(xencons_driver)->name, xc_num);
663
664         return 0;
665 }
666
667 module_init(xencons_init);
668
669 MODULE_LICENSE("Dual BSD/GPL");