ia64/xen-unstable

view linux-2.6-xen-sparse/drivers/xen/console/console.c @ 10638:9fd92e7e91a5

[LINUX] Fix booting with e.g. xencons=tty3, so that tty1/tty2 are valid (dummy) devices.

Attached patch makes it always create valid devices on tty1-tty63, but
all but the one specified by xencons are dummy devices.

From: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@dhcp93.uk.xensource.com
date Mon Jul 03 08:52:27 2006 +0100 (2006-07-03)
parents cede50ca1704
children 09b8041dc2fd
line source
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 */
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>
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;
74 #ifdef CONFIG_MAGIC_SYSRQ
75 static unsigned long sysrq_requested;
76 extern int sysrq_enabled;
77 #endif
79 static int __init xencons_setup(char *str)
80 {
81 char *q;
82 int n;
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;
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 }
106 return 1;
107 }
108 __setup("xencons=", xencons_setup);
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 */
116 static int __init xencons_bufsz_setup(char *str)
117 {
118 unsigned int goal;
119 goal = simple_strtoul(str, NULL, 0);
120 if (goal) {
121 goal = roundup_pow_of_two(goal);
122 if (wbuf_size < goal)
123 wbuf_size = goal;
124 }
125 return 1;
126 }
127 __setup("xencons_bufsz=", xencons_bufsz_setup);
129 /* This lock protects accesses to the common transmit buffer. */
130 static DEFINE_SPINLOCK(xencons_lock);
132 /* Common transmit-kick routine. */
133 static void __xencons_tx_flush(void);
135 static struct tty_driver *xencons_driver;
137 /******************** Kernel console driver ********************************/
139 static void kcons_write(struct console *c, const char *s, unsigned int count)
140 {
141 int i = 0;
142 unsigned long flags;
144 spin_lock_irqsave(&xencons_lock, flags);
146 while (i < count) {
147 for (; i < count; i++) {
148 if ((wp - wc) >= (wbuf_size - 1))
149 break;
150 if ((wbuf[WBUF_MASK(wp++)] = s[i]) == '\n')
151 wbuf[WBUF_MASK(wp++)] = '\r';
152 }
154 __xencons_tx_flush();
155 }
157 spin_unlock_irqrestore(&xencons_lock, flags);
158 }
160 static void kcons_write_dom0(struct console *c, const char *s, unsigned int count)
161 {
163 while (count > 0) {
164 int rc;
165 rc = HYPERVISOR_console_io( CONSOLEIO_write, count, (char *)s);
166 if (rc <= 0)
167 break;
168 count -= rc;
169 s += rc;
170 }
171 }
173 static struct tty_driver *kcons_device(struct console *c, int *index)
174 {
175 *index = 0;
176 return xencons_driver;
177 }
179 static struct console kcons_info = {
180 .device = kcons_device,
181 .flags = CON_PRINTBUFFER,
182 .index = -1,
183 };
185 #define __RETCODE 0
186 static int __init xen_console_init(void)
187 {
188 if (!is_running_on_xen())
189 return __RETCODE;
191 if (xen_start_info->flags & SIF_INITDOMAIN) {
192 if (xc_mode == XC_DEFAULT)
193 xc_mode = XC_SERIAL;
194 kcons_info.write = kcons_write_dom0;
195 if (xc_mode == XC_SERIAL)
196 kcons_info.flags |= CON_ENABLED;
197 } else {
198 if (xc_mode == XC_DEFAULT)
199 xc_mode = XC_TTY;
200 kcons_info.write = kcons_write;
201 }
203 switch (xc_mode) {
204 case XC_SERIAL:
205 strcpy(kcons_info.name, "ttyS");
206 if (xc_num == -1)
207 xc_num = 0;
208 break;
210 case XC_TTY:
211 strcpy(kcons_info.name, "tty");
212 if (xc_num == -1)
213 xc_num = 1;
214 break;
216 default:
217 return __RETCODE;
218 }
220 wbuf = alloc_bootmem(wbuf_size);
222 register_console(&kcons_info);
224 return __RETCODE;
225 }
226 console_initcall(xen_console_init);
228 /*** Useful function for console debugging -- goes straight to Xen. ***/
229 asmlinkage int xprintk(const char *fmt, ...)
230 {
231 va_list args;
232 int printk_len;
233 static char printk_buf[1024];
235 /* Emit the output into the temporary buffer */
236 va_start(args, fmt);
237 printk_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
238 va_end(args);
240 /* Send the processed output directly to Xen. */
241 kcons_write_dom0(NULL, printk_buf, printk_len);
243 return 0;
244 }
246 /*** Forcibly flush console data before dying. ***/
247 void xencons_force_flush(void)
248 {
249 int sz;
251 /* Emergency console is synchronous, so there's nothing to flush. */
252 if (xen_start_info->flags & SIF_INITDOMAIN)
253 return;
255 /* Spin until console data is flushed through to the daemon. */
256 while (wc != wp) {
257 int sent = 0;
258 if ((sz = wp - wc) == 0)
259 continue;
260 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
261 if (sent > 0)
262 wc += sent;
263 }
264 }
267 /******************** User-space console driver (/dev/console) ************/
269 #define DRV(_d) (_d)
270 #define DUMMY_TTY(_tty) ((xc_mode != XC_SERIAL) && \
271 ((_tty)->index != (xc_num - 1)))
273 static struct termios *xencons_termios[MAX_NR_CONSOLES];
274 static struct termios *xencons_termios_locked[MAX_NR_CONSOLES];
275 static struct tty_struct *xencons_tty;
276 static int xencons_priv_irq;
277 static char x_char;
279 void xencons_rx(char *buf, unsigned len, struct pt_regs *regs)
280 {
281 int i;
282 unsigned long flags;
284 spin_lock_irqsave(&xencons_lock, flags);
285 if (xencons_tty == NULL)
286 goto out;
288 for (i = 0; i < len; i++) {
289 #ifdef CONFIG_MAGIC_SYSRQ
290 if (sysrq_enabled) {
291 if (buf[i] == '\x0f') { /* ^O */
292 sysrq_requested = jiffies;
293 continue; /* don't print the sysrq key */
294 } else if (sysrq_requested) {
295 unsigned long sysrq_timeout =
296 sysrq_requested + HZ*2;
297 sysrq_requested = 0;
298 if (time_before(jiffies, sysrq_timeout)) {
299 spin_unlock_irqrestore(
300 &xencons_lock, flags);
301 handle_sysrq(
302 buf[i], regs, xencons_tty);
303 spin_lock_irqsave(
304 &xencons_lock, flags);
305 continue;
306 }
307 }
308 }
309 #endif
310 tty_insert_flip_char(xencons_tty, buf[i], 0);
311 }
312 tty_flip_buffer_push(xencons_tty);
314 out:
315 spin_unlock_irqrestore(&xencons_lock, flags);
316 }
318 static void __xencons_tx_flush(void)
319 {
320 int sent, sz, work_done = 0;
322 if (x_char) {
323 if (xen_start_info->flags & SIF_INITDOMAIN)
324 kcons_write_dom0(NULL, &x_char, 1);
325 else
326 while (x_char)
327 if (xencons_ring_send(&x_char, 1) == 1)
328 break;
329 x_char = 0;
330 work_done = 1;
331 }
333 while (wc != wp) {
334 sz = wp - wc;
335 if (sz > (wbuf_size - WBUF_MASK(wc)))
336 sz = wbuf_size - WBUF_MASK(wc);
337 if (xen_start_info->flags & SIF_INITDOMAIN) {
338 kcons_write_dom0(NULL, &wbuf[WBUF_MASK(wc)], sz);
339 wc += sz;
340 } else {
341 sent = xencons_ring_send(&wbuf[WBUF_MASK(wc)], sz);
342 if (sent == 0)
343 break;
344 wc += sent;
345 }
346 work_done = 1;
347 }
349 if (work_done && (xencons_tty != NULL)) {
350 wake_up_interruptible(&xencons_tty->write_wait);
351 if ((xencons_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
352 (xencons_tty->ldisc.write_wakeup != NULL))
353 (xencons_tty->ldisc.write_wakeup)(xencons_tty);
354 }
355 }
357 void xencons_tx(void)
358 {
359 unsigned long flags;
361 spin_lock_irqsave(&xencons_lock, flags);
362 __xencons_tx_flush();
363 spin_unlock_irqrestore(&xencons_lock, flags);
364 }
366 /* Privileged receive callback and transmit kicker. */
367 static irqreturn_t xencons_priv_interrupt(int irq, void *dev_id,
368 struct pt_regs *regs)
369 {
370 static char rbuf[16];
371 int l;
373 while ((l = HYPERVISOR_console_io(CONSOLEIO_read, 16, rbuf)) > 0)
374 xencons_rx(rbuf, l, regs);
376 xencons_tx();
378 return IRQ_HANDLED;
379 }
381 static int xencons_write_room(struct tty_struct *tty)
382 {
383 return wbuf_size - (wp - wc);
384 }
386 static int xencons_chars_in_buffer(struct tty_struct *tty)
387 {
388 return wp - wc;
389 }
391 static void xencons_send_xchar(struct tty_struct *tty, char ch)
392 {
393 unsigned long flags;
395 if (DUMMY_TTY(tty))
396 return;
398 spin_lock_irqsave(&xencons_lock, flags);
399 x_char = ch;
400 __xencons_tx_flush();
401 spin_unlock_irqrestore(&xencons_lock, flags);
402 }
404 static void xencons_throttle(struct tty_struct *tty)
405 {
406 if (DUMMY_TTY(tty))
407 return;
409 if (I_IXOFF(tty))
410 xencons_send_xchar(tty, STOP_CHAR(tty));
411 }
413 static void xencons_unthrottle(struct tty_struct *tty)
414 {
415 if (DUMMY_TTY(tty))
416 return;
418 if (I_IXOFF(tty)) {
419 if (x_char != 0)
420 x_char = 0;
421 else
422 xencons_send_xchar(tty, START_CHAR(tty));
423 }
424 }
426 static void xencons_flush_buffer(struct tty_struct *tty)
427 {
428 unsigned long flags;
430 if (DUMMY_TTY(tty))
431 return;
433 spin_lock_irqsave(&xencons_lock, flags);
434 wc = wp = 0;
435 spin_unlock_irqrestore(&xencons_lock, flags);
436 }
438 static inline int __xencons_put_char(int ch)
439 {
440 char _ch = (char)ch;
441 if ((wp - wc) == wbuf_size)
442 return 0;
443 wbuf[WBUF_MASK(wp++)] = _ch;
444 return 1;
445 }
447 static int xencons_write(
448 struct tty_struct *tty,
449 const unsigned char *buf,
450 int count)
451 {
452 int i;
453 unsigned long flags;
455 if (DUMMY_TTY(tty))
456 return count;
458 spin_lock_irqsave(&xencons_lock, flags);
460 for (i = 0; i < count; i++)
461 if (!__xencons_put_char(buf[i]))
462 break;
464 if (i != 0)
465 __xencons_tx_flush();
467 spin_unlock_irqrestore(&xencons_lock, flags);
469 return i;
470 }
472 static void xencons_put_char(struct tty_struct *tty, u_char ch)
473 {
474 unsigned long flags;
476 if (DUMMY_TTY(tty))
477 return;
479 spin_lock_irqsave(&xencons_lock, flags);
480 (void)__xencons_put_char(ch);
481 spin_unlock_irqrestore(&xencons_lock, flags);
482 }
484 static void xencons_flush_chars(struct tty_struct *tty)
485 {
486 unsigned long flags;
488 if (DUMMY_TTY(tty))
489 return;
491 spin_lock_irqsave(&xencons_lock, flags);
492 __xencons_tx_flush();
493 spin_unlock_irqrestore(&xencons_lock, flags);
494 }
496 static void xencons_wait_until_sent(struct tty_struct *tty, int timeout)
497 {
498 unsigned long orig_jiffies = jiffies;
500 if (DUMMY_TTY(tty))
501 return;
503 while (DRV(tty->driver)->chars_in_buffer(tty)) {
504 set_current_state(TASK_INTERRUPTIBLE);
505 schedule_timeout(1);
506 if (signal_pending(current))
507 break;
508 if (timeout && time_after(jiffies, orig_jiffies + timeout))
509 break;
510 }
512 set_current_state(TASK_RUNNING);
513 }
515 static int xencons_open(struct tty_struct *tty, struct file *filp)
516 {
517 unsigned long flags;
519 if (DUMMY_TTY(tty))
520 return 0;
522 spin_lock_irqsave(&xencons_lock, flags);
523 tty->driver_data = NULL;
524 if (xencons_tty == NULL)
525 xencons_tty = tty;
526 __xencons_tx_flush();
527 spin_unlock_irqrestore(&xencons_lock, flags);
529 return 0;
530 }
532 static void xencons_close(struct tty_struct *tty, struct file *filp)
533 {
534 unsigned long flags;
536 if (DUMMY_TTY(tty))
537 return;
539 if (tty->count == 1) {
540 tty->closing = 1;
541 tty_wait_until_sent(tty, 0);
542 if (DRV(tty->driver)->flush_buffer != NULL)
543 DRV(tty->driver)->flush_buffer(tty);
544 if (tty->ldisc.flush_buffer != NULL)
545 tty->ldisc.flush_buffer(tty);
546 tty->closing = 0;
547 spin_lock_irqsave(&xencons_lock, flags);
548 xencons_tty = NULL;
549 spin_unlock_irqrestore(&xencons_lock, flags);
550 }
551 }
553 static struct tty_operations xencons_ops = {
554 .open = xencons_open,
555 .close = xencons_close,
556 .write = xencons_write,
557 .write_room = xencons_write_room,
558 .put_char = xencons_put_char,
559 .flush_chars = xencons_flush_chars,
560 .chars_in_buffer = xencons_chars_in_buffer,
561 .send_xchar = xencons_send_xchar,
562 .flush_buffer = xencons_flush_buffer,
563 .throttle = xencons_throttle,
564 .unthrottle = xencons_unthrottle,
565 .wait_until_sent = xencons_wait_until_sent,
566 };
568 static int __init xencons_init(void)
569 {
570 int rc;
572 if (!is_running_on_xen())
573 return -ENODEV;
575 if (xc_mode == XC_OFF)
576 return 0;
578 xencons_ring_init();
580 xencons_driver = alloc_tty_driver((xc_mode == XC_SERIAL) ?
581 1 : MAX_NR_CONSOLES);
582 if (xencons_driver == NULL)
583 return -ENOMEM;
585 DRV(xencons_driver)->name = "xencons";
586 DRV(xencons_driver)->major = TTY_MAJOR;
587 DRV(xencons_driver)->type = TTY_DRIVER_TYPE_SERIAL;
588 DRV(xencons_driver)->subtype = SERIAL_TYPE_NORMAL;
589 DRV(xencons_driver)->init_termios = tty_std_termios;
590 DRV(xencons_driver)->flags =
591 TTY_DRIVER_REAL_RAW |
592 TTY_DRIVER_RESET_TERMIOS;
593 DRV(xencons_driver)->termios = xencons_termios;
594 DRV(xencons_driver)->termios_locked = xencons_termios_locked;
596 if (xc_mode == XC_SERIAL) {
597 DRV(xencons_driver)->name = "ttyS";
598 DRV(xencons_driver)->minor_start = 64 + xc_num;
599 DRV(xencons_driver)->name_base = 0 + xc_num;
600 } else {
601 DRV(xencons_driver)->name = "tty";
602 DRV(xencons_driver)->minor_start = 1;
603 DRV(xencons_driver)->name_base = 1;
604 }
606 tty_set_operations(xencons_driver, &xencons_ops);
608 if ((rc = tty_register_driver(DRV(xencons_driver))) != 0) {
609 printk("WARNING: Failed to register Xen virtual "
610 "console driver as '%s%d'\n",
611 DRV(xencons_driver)->name,
612 DRV(xencons_driver)->name_base);
613 put_tty_driver(xencons_driver);
614 xencons_driver = NULL;
615 return rc;
616 }
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 }
629 printk("Xen virtual console successfully installed as %s%d\n",
630 DRV(xencons_driver)->name, xc_num);
632 return 0;
633 }
635 module_init(xencons_init);
637 MODULE_LICENSE("Dual BSD/GPL");