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