ia64/xen-unstable

view linux-2.6-xen-sparse/drivers/xen/console/console.c @ 8166:d84ffe8f32ae

fix the console when only XEN_PHYSDEV_ACCESS is Yes.

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