ia64/xen-unstable

annotate xen/drivers/char/console.c @ 16493:bb31c9325d5f

Fix string length check for vsnprintf() in debugtrace_printk().
Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Nov 28 12:50:24 2007 +0000 (2007-11-28)
parents d945240821e7
children a7ddd6bcd564
rev   line source
kaf24@1150 1 /******************************************************************************
kaf24@1150 2 * console.c
kaf24@1150 3 *
kaf24@1150 4 * Emergency console I/O for Xen and the domain-0 guest OS.
kaf24@1150 5 *
kaf24@1150 6 * Copyright (c) 2002-2004, K A Fraser.
kaf24@12030 7 *
kaf24@12030 8 * Added printf_ratelimit
kaf24@12030 9 * Taken from Linux - Author: Andi Kleen (net_ratelimit)
kaf24@12030 10 * Ported to Xen - Steven Rostedt - Red Hat
kaf24@1150 11 */
kaf24@1150 12
kfraser@11879 13 #include <xen/stdarg.h>
kaf24@1210 14 #include <xen/config.h>
kfraser@10890 15 #include <xen/version.h>
kaf24@3334 16 #include <xen/init.h>
kaf24@1210 17 #include <xen/lib.h>
kaf24@1210 18 #include <xen/errno.h>
kaf24@1210 19 #include <xen/event.h>
kaf24@1210 20 #include <xen/spinlock.h>
kaf24@1210 21 #include <xen/console.h>
kaf24@1210 22 #include <xen/serial.h>
kaf24@5327 23 #include <xen/softirq.h>
kaf24@1210 24 #include <xen/keyhandler.h>
kaf24@4839 25 #include <xen/mm.h>
cl349@5286 26 #include <xen/delay.h>
kaf24@9133 27 #include <xen/guest_access.h>
kaf24@10979 28 #include <xen/shutdown.h>
kfraser@11133 29 #include <xen/vga.h>
ian@12676 30 #include <xen/kexec.h>
cl349@5291 31 #include <asm/current.h>
sos22@3763 32 #include <asm/debugger.h>
kaf24@4253 33 #include <asm/io.h>
kaf24@12030 34 #include <asm/div64.h>
kfraser@15815 35 #include <xsm/xsm.h>
keir@16264 36 #include <public/sysctl.h>
kaf24@1150 37
kaf24@5322 38 /* console: comma-separated list of console outputs. */
riel@3992 39 static char opt_console[30] = OPT_CONSOLE_STR;
kaf24@3334 40 string_param("console", opt_console);
kaf24@3334 41
kaf24@5322 42 /* conswitch: a character pair controlling console switching. */
kaf24@3334 43 /* Char 1: CTRL+<char1> is used to switch console input between Xen and DOM0 */
kaf24@3334 44 /* Char 2: If this character is 'x', then do not auto-switch to DOM0 when it */
kaf24@3334 45 /* boots. Any other value, or omitting the char, enables auto-switch */
keir@16320 46 static unsigned char opt_conswitch[3] = "a";
kaf24@3334 47 string_param("conswitch", opt_conswitch);
kaf24@1769 48
kaf24@5322 49 /* sync_console: force synchronous console output (useful for debugging). */
kaf24@5322 50 static int opt_sync_console;
kaf24@5322 51 boolean_param("sync_console", opt_sync_console);
kaf24@5322 52
kfraser@14414 53 /* console_to_ring: send guest (incl. dom 0) console data to console ring. */
kfraser@14414 54 static int opt_console_to_ring;
kfraser@14414 55 boolean_param("console_to_ring", opt_console_to_ring);
kfraser@14414 56
keir@16071 57 /* console_timestamps: include a timestamp prefix on every Xen console line. */
keir@16071 58 static int opt_console_timestamps;
keir@16071 59 boolean_param("console_timestamps", opt_console_timestamps);
keir@16071 60
kaf24@5327 61 #define CONRING_SIZE 16384
kaf24@5327 62 #define CONRING_IDX_MASK(i) ((i)&(CONRING_SIZE-1))
kaf24@5327 63 static char conring[CONRING_SIZE];
keir@16264 64 static uint32_t conringc, conringp;
kaf24@1150 65
kaf24@1150 66 static int sercon_handle = -1;
kaf24@1150 67
kaf24@10288 68 static DEFINE_SPINLOCK(console_lock);
kaf24@1150 69
kaf24@12038 70 /*
kaf24@12038 71 * To control the amount of printing, thresholds are added.
kaf24@12038 72 * These thresholds correspond to the XENLOG logging levels.
kaf24@12038 73 * There's an upper and lower threshold for non-guest messages and for
kaf24@12038 74 * guest-provoked messages. This works as follows, for a given log level L:
kaf24@12038 75 *
kaf24@12038 76 * L < lower_threshold : always logged
kaf24@12038 77 * lower_threshold <= L < upper_threshold : rate-limited logging
kaf24@12038 78 * upper_threshold <= L : never logged
kaf24@12038 79 *
kaf24@12038 80 * Note, in the above algorithm, to disable rate limiting simply make
kaf24@12038 81 * the lower threshold equal to the upper.
kaf24@12038 82 */
kfraser@13897 83 #ifdef NDEBUG
kaf24@12038 84 #define XENLOG_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */
kaf24@12038 85 #define XENLOG_LOWER_THRESHOLD 2 /* Always print ERR and WARNING */
kaf24@12038 86 #define XENLOG_GUEST_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */
kaf24@12038 87 #define XENLOG_GUEST_LOWER_THRESHOLD 0 /* Rate-limit ERR and WARNING */
kfraser@13897 88 #else
kfraser@13897 89 #define XENLOG_UPPER_THRESHOLD 4 /* Do not discard anything */
kfraser@13897 90 #define XENLOG_LOWER_THRESHOLD 4 /* Print everything */
kfraser@13897 91 #define XENLOG_GUEST_UPPER_THRESHOLD 4 /* Do not discard anything */
kfraser@13897 92 #define XENLOG_GUEST_LOWER_THRESHOLD 4 /* Print everything */
kfraser@13897 93 #endif
kaf24@12038 94 /*
kaf24@12038 95 * The XENLOG_DEFAULT is the default given to printks that
kaf24@12038 96 * do not have any print level associated with them.
kaf24@12038 97 */
kaf24@12038 98 #define XENLOG_DEFAULT 1 /* XENLOG_WARNING */
kaf24@12038 99 #define XENLOG_GUEST_DEFAULT 1 /* XENLOG_WARNING */
kaf24@12038 100
kaf24@12232 101 static int xenlog_upper_thresh = XENLOG_UPPER_THRESHOLD;
kaf24@12232 102 static int xenlog_lower_thresh = XENLOG_LOWER_THRESHOLD;
kaf24@12232 103 static int xenlog_guest_upper_thresh = XENLOG_GUEST_UPPER_THRESHOLD;
kaf24@12232 104 static int xenlog_guest_lower_thresh = XENLOG_GUEST_LOWER_THRESHOLD;
kaf24@12232 105
kaf24@12232 106 static void parse_loglvl(char *s);
kaf24@12232 107 static void parse_guest_loglvl(char *s);
kaf24@12232 108
kaf24@12232 109 /*
kaf24@12232 110 * <lvl> := none|error|warning|info|debug|all
kaf24@12232 111 * loglvl=<lvl_print_always>[/<lvl_print_ratelimit>]
kaf24@12232 112 * <lvl_print_always>: log level which is always printed
kaf24@12232 113 * <lvl_print_rlimit>: log level which is rate-limit printed
kaf24@12232 114 * Similar definitions for guest_loglvl, but applies to guest tracing.
kaf24@12232 115 * Defaults: loglvl=warning ; guest_loglvl=none/warning
kaf24@12232 116 */
kaf24@12232 117 custom_param("loglvl", parse_loglvl);
kaf24@12232 118 custom_param("guest_loglvl", parse_guest_loglvl);
kaf24@12031 119
keir@13890 120 static atomic_t print_everything = ATOMIC_INIT(0);
kaf24@12031 121
kaf24@12232 122 #define ___parse_loglvl(s, ps, lvlstr, lvlnum) \
kaf24@12232 123 if ( !strncmp((s), (lvlstr), strlen(lvlstr)) ) { \
kaf24@12232 124 *(ps) = (s) + strlen(lvlstr); \
kaf24@12232 125 return (lvlnum); \
kaf24@12232 126 }
kaf24@12232 127
keir@15082 128 static int __init __parse_loglvl(char *s, char **ps)
kaf24@12232 129 {
kaf24@12232 130 ___parse_loglvl(s, ps, "none", 0);
kaf24@12232 131 ___parse_loglvl(s, ps, "error", 1);
kaf24@12232 132 ___parse_loglvl(s, ps, "warning", 2);
kaf24@12232 133 ___parse_loglvl(s, ps, "info", 3);
kaf24@12232 134 ___parse_loglvl(s, ps, "debug", 4);
kaf24@12232 135 ___parse_loglvl(s, ps, "all", 4);
kaf24@12232 136 return 2; /* sane fallback */
kaf24@12232 137 }
kaf24@12232 138
keir@15082 139 static void __init _parse_loglvl(char *s, int *lower, int *upper)
kaf24@12232 140 {
kaf24@12232 141 *lower = *upper = __parse_loglvl(s, &s);
kaf24@12232 142 if ( *s == '/' )
kaf24@12232 143 *upper = __parse_loglvl(s+1, &s);
kaf24@12232 144 if ( *upper < *lower )
kaf24@12232 145 *upper = *lower;
kaf24@12232 146 }
kaf24@12232 147
keir@15082 148 static void __init parse_loglvl(char *s)
kaf24@12232 149 {
kaf24@12232 150 _parse_loglvl(s, &xenlog_lower_thresh, &xenlog_upper_thresh);
kaf24@12232 151 }
kaf24@12232 152
keir@15082 153 static void __init parse_guest_loglvl(char *s)
kaf24@12232 154 {
kaf24@12232 155 _parse_loglvl(s, &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh);
kaf24@12232 156 }
kaf24@12232 157
keir@15082 158 static char * __init loglvl_str(int lvl)
kaf24@12232 159 {
kaf24@12232 160 switch ( lvl )
kaf24@12232 161 {
kaf24@12232 162 case 0: return "Nothing";
kaf24@12232 163 case 1: return "Errors";
kaf24@12232 164 case 2: return "Errors and warnings";
kaf24@12232 165 case 3: return "Errors, warnings and info";
kaf24@12232 166 case 4: return "All";
kaf24@12232 167 }
kaf24@12232 168 return "???";
kaf24@12232 169 }
kaf24@12232 170
kaf24@1150 171 /*
kaf24@1150 172 * ********************************************************
kaf24@1150 173 * *************** ACCESS TO CONSOLE RING *****************
kaf24@1150 174 * ********************************************************
kaf24@1150 175 */
kaf24@1150 176
kaf24@1150 177 static void putchar_console_ring(int c)
kaf24@1150 178 {
keir@16264 179 ASSERT(spin_is_locked(&console_lock));
kaf24@5327 180 conring[CONRING_IDX_MASK(conringp++)] = c;
keir@16264 181 if ( (uint32_t)(conringp - conringc) > CONRING_SIZE )
kaf24@5327 182 conringc = conringp - CONRING_SIZE;
kaf24@1150 183 }
kaf24@1150 184
keir@16264 185 long read_console_ring(struct xen_sysctl_readconsole *op)
kaf24@1150 186 {
keir@16264 187 XEN_GUEST_HANDLE(char) str;
keir@16264 188 uint32_t idx, len, max, sofar, c;
kaf24@5327 189
keir@16264 190 str = guest_handle_cast(op->buffer, char),
keir@16264 191 max = op->count;
kaf24@8859 192 sofar = 0;
kaf24@8859 193
kaf24@8859 194 c = conringc;
keir@16264 195 if ( op->incremental && ((int32_t)(op->index - c) < 0) )
keir@16264 196 c = op->index;
keir@16264 197
kaf24@10362 198 while ( (c != conringp) && (sofar < max) )
kaf24@8859 199 {
kaf24@8859 200 idx = CONRING_IDX_MASK(c);
kaf24@8859 201 len = conringp - c;
kaf24@8859 202 if ( (idx + len) > CONRING_SIZE )
kaf24@8859 203 len = CONRING_SIZE - idx;
kaf24@8859 204 if ( (sofar + len) > max )
kaf24@8859 205 len = max - sofar;
kaf24@9133 206 if ( copy_to_guest_offset(str, sofar, &conring[idx], len) )
kaf24@5327 207 return -EFAULT;
kaf24@8859 208 sofar += len;
kaf24@8859 209 c += len;
kaf24@8859 210 }
kaf24@1150 211
keir@16264 212 if ( op->clear )
kaf24@5327 213 {
keir@16264 214 spin_lock_irq(&console_lock);
keir@16264 215 if ( (uint32_t)(conringp - c) > CONRING_SIZE )
kaf24@10362 216 conringc = conringp - CONRING_SIZE;
kaf24@10362 217 else
kaf24@10362 218 conringc = c;
keir@16264 219 spin_unlock_irq(&console_lock);
kaf24@5327 220 }
kaf24@5327 221
keir@16264 222 op->count = sofar;
keir@16264 223 op->index = c;
keir@16264 224
kaf24@5327 225 return 0;
kaf24@1150 226 }
kaf24@1150 227
kaf24@1150 228
kaf24@1150 229 /*
kaf24@1150 230 * *******************************************************
kaf24@1150 231 * *************** ACCESS TO SERIAL LINE *****************
kaf24@1150 232 * *******************************************************
kaf24@1150 233 */
kaf24@1150 234
kaf24@1150 235 /* Characters received over the serial line are buffered for domain 0. */
kaf24@1150 236 #define SERIAL_RX_SIZE 128
kaf24@1150 237 #define SERIAL_RX_MASK(_i) ((_i)&(SERIAL_RX_SIZE-1))
kaf24@1150 238 static char serial_rx_ring[SERIAL_RX_SIZE];
kaf24@1150 239 static unsigned int serial_rx_cons, serial_rx_prod;
kaf24@1150 240
kfraser@11602 241 static void (*serial_steal_fn)(const char *);
kfraser@11602 242
kfraser@11602 243 int console_steal(int handle, void (*fn)(const char *))
kfraser@11602 244 {
kfraser@11602 245 if ( (handle == -1) || (handle != sercon_handle) )
kfraser@11602 246 return 0;
kfraser@11602 247
keir@13885 248 if ( serial_steal_fn != NULL )
kfraser@11602 249 return -EBUSY;
kfraser@11602 250
kfraser@11602 251 serial_steal_fn = fn;
kfraser@11602 252 return 1;
kfraser@11602 253 }
kfraser@11602 254
kfraser@11602 255 void console_giveback(int id)
kfraser@11602 256 {
kfraser@11602 257 if ( id == 1 )
kfraser@11602 258 serial_steal_fn = NULL;
kfraser@11602 259 }
kfraser@11602 260
kfraser@11602 261 static void sercon_puts(const char *s)
kfraser@11602 262 {
kfraser@11602 263 if ( serial_steal_fn != NULL )
kfraser@11602 264 (*serial_steal_fn)(s);
kfraser@11602 265 else
kfraser@11602 266 serial_puts(sercon_handle, s);
kfraser@11602 267 }
kfraser@11602 268
kaf24@1769 269 /* CTRL-<switch_char> switches input direction between Xen and DOM0. */
keir@16320 270 #define switch_code (opt_conswitch[0]-'a'+1)
kaf24@1175 271 static int xen_rx = 1; /* FALSE => serial input passed to domain 0. */
kaf24@1175 272
kaf24@1175 273 static void switch_serial_input(void)
kaf24@1175 274 {
kaf24@1175 275 static char *input_str[2] = { "DOM0", "Xen" };
kaf24@1175 276 xen_rx = !xen_rx;
keir@16320 277 printk("*** Serial input -> %s", input_str[xen_rx]);
keir@16320 278 if ( switch_code )
keir@16320 279 printk(" (type 'CTRL-%c' three times to switch input to %s)",
keir@16320 280 opt_conswitch[0], input_str[!xen_rx]);
keir@16320 281 printk("\n");
kaf24@1175 282 }
kaf24@1150 283
kaf24@5195 284 static void __serial_rx(char c, struct cpu_user_regs *regs)
kaf24@1150 285 {
kaf24@1175 286 if ( xen_rx )
kaf24@4988 287 return handle_keypress(c, regs);
kaf24@4988 288
kaf24@4988 289 /* Deliver input to guest buffer, unless it is already full. */
kaf24@4988 290 if ( (serial_rx_prod-serial_rx_cons) != SERIAL_RX_SIZE )
kaf24@4988 291 serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
kaf24@4988 292 /* Always notify the guest: prevents receive path from getting stuck. */
kaf24@9597 293 send_guest_global_virq(dom0, VIRQ_CONSOLE);
kaf24@1150 294 }
kaf24@1150 295
kaf24@5195 296 static void serial_rx(char c, struct cpu_user_regs *regs)
kaf24@1176 297 {
kaf24@1769 298 static int switch_code_count = 0;
kaf24@1176 299
keir@16320 300 if ( switch_code && (c == switch_code) )
kaf24@1176 301 {
kaf24@1769 302 /* We eat CTRL-<switch_char> in groups of 3 to switch console input. */
kaf24@1769 303 if ( ++switch_code_count == 3 )
kaf24@1176 304 {
kaf24@1176 305 switch_serial_input();
kaf24@1769 306 switch_code_count = 0;
kaf24@1176 307 }
keir@16320 308 return;
kaf24@1176 309 }
keir@16320 310
keir@16320 311 for ( ; switch_code_count != 0; switch_code_count-- )
keir@16320 312 __serial_rx(switch_code, regs);
kaf24@1248 313
kaf24@1248 314 /* Finally process the just-received character. */
kaf24@1248 315 __serial_rx(c, regs);
kaf24@1176 316 }
kaf24@1176 317
kaf24@9873 318 static long guest_console_write(XEN_GUEST_HANDLE(char) buffer, int count)
kaf24@5327 319 {
kaf24@8546 320 char kbuf[128], *kptr;
kaf24@5327 321 int kcount;
kaf24@5327 322
kaf24@5327 323 while ( count > 0 )
kaf24@5327 324 {
kaf24@5327 325 while ( serial_tx_space(sercon_handle) < (SERIAL_TXBUFSZ / 2) )
kaf24@5327 326 {
kaf24@5327 327 if ( hypercall_preempt_check() )
kaf24@5327 328 break;
kaf24@5327 329 cpu_relax();
kaf24@5327 330 }
kaf24@5327 331
kaf24@5327 332 if ( hypercall_preempt_check() )
kaf24@9068 333 return hypercall_create_continuation(
kaf24@9183 334 __HYPERVISOR_console_io, "iih",
kaf24@9068 335 CONSOLEIO_write, count, buffer);
kaf24@5327 336
kaf24@5327 337 kcount = min_t(int, count, sizeof(kbuf)-1);
kfraser@15404 338 if ( copy_from_guest(kbuf, buffer, kcount) )
kaf24@5327 339 return -EFAULT;
kaf24@5327 340 kbuf[kcount] = '\0';
kaf24@5327 341
keir@16264 342 spin_lock_irq(&console_lock);
keir@16264 343
kfraser@11602 344 sercon_puts(kbuf);
kfraser@15747 345 vga_puts(kbuf);
kaf24@5327 346
kfraser@15747 347 if ( opt_console_to_ring )
keir@16264 348 {
kfraser@15747 349 for ( kptr = kbuf; *kptr != '\0'; kptr++ )
kfraser@14414 350 putchar_console_ring(*kptr);
keir@16264 351 send_guest_global_virq(dom0, VIRQ_CON_RING);
keir@16264 352 }
kfraser@14414 353
keir@16264 354 spin_unlock_irq(&console_lock);
kaf24@8546 355
kaf24@9183 356 guest_handle_add_offset(buffer, kcount);
kaf24@9183 357 count -= kcount;
kaf24@5327 358 }
kaf24@5327 359
kaf24@5327 360 return 0;
kaf24@5327 361 }
kaf24@5327 362
kaf24@9873 363 long do_console_io(int cmd, int count, XEN_GUEST_HANDLE(char) buffer)
kaf24@1150 364 {
kaf24@5327 365 long rc;
kaf24@8859 366 unsigned int idx, len;
kaf24@1150 367
cl349@2210 368 #ifndef VERBOSE
kaf24@5327 369 /* Only domain 0 may access the emergency console. */
kaf24@4877 370 if ( current->domain->domain_id != 0 )
kaf24@1150 371 return -EPERM;
iap10@1358 372 #endif
kaf24@1150 373
kfraser@15815 374 rc = xsm_console_io(current->domain, cmd);
kfraser@15815 375 if ( rc )
kfraser@15815 376 return rc;
kfraser@15815 377
kaf24@1150 378 switch ( cmd )
kaf24@1150 379 {
kaf24@1171 380 case CONSOLEIO_write:
kaf24@5327 381 rc = guest_console_write(buffer, count);
kaf24@1150 382 break;
kaf24@1171 383 case CONSOLEIO_read:
kaf24@1150 384 rc = 0;
kaf24@1150 385 while ( (serial_rx_cons != serial_rx_prod) && (rc < count) )
kaf24@1150 386 {
kaf24@8859 387 idx = SERIAL_RX_MASK(serial_rx_cons);
kaf24@8859 388 len = serial_rx_prod - serial_rx_cons;
kaf24@8859 389 if ( (idx + len) > SERIAL_RX_SIZE )
kaf24@8859 390 len = SERIAL_RX_SIZE - idx;
kaf24@8859 391 if ( (rc + len) > count )
kaf24@8859 392 len = count - rc;
kaf24@9183 393 if ( copy_to_guest_offset(buffer, rc, &serial_rx_ring[idx], len) )
kaf24@1150 394 {
kaf24@1150 395 rc = -EFAULT;
kaf24@1150 396 break;
kaf24@1150 397 }
kaf24@8859 398 rc += len;
kaf24@8859 399 serial_rx_cons += len;
kaf24@1150 400 }
kaf24@1150 401 break;
kaf24@1150 402 default:
kaf24@1150 403 rc = -ENOSYS;
kaf24@1150 404 break;
kaf24@1150 405 }
kaf24@1150 406
kaf24@1150 407 return rc;
kaf24@1150 408 }
kaf24@1150 409
kaf24@1150 410
kaf24@1150 411 /*
kaf24@1150 412 * *****************************************************
kaf24@1150 413 * *************** GENERIC CONSOLE I/O *****************
kaf24@1150 414 * *****************************************************
kaf24@1150 415 */
kaf24@1150 416
kfraser@12239 417 static void __putstr(const char *str)
kaf24@1150 418 {
kaf24@1150 419 int c;
mafetter@3816 420
keir@16264 421 ASSERT(spin_is_locked(&console_lock));
keir@16264 422
kfraser@11602 423 sercon_puts(str);
kfraser@15747 424 vga_puts(str);
mafetter@3816 425
kaf24@1150 426 while ( (c = *str++) != '\0' )
kaf24@1150 427 putchar_console_ring(c);
kfraser@14307 428
kfraser@14307 429 send_guest_global_virq(dom0, VIRQ_CON_RING);
kaf24@1150 430 }
kaf24@1150 431
kfraser@12239 432 static int printk_prefix_check(char *p, char **pp)
kfraser@12239 433 {
kfraser@12239 434 int loglvl = -1;
kfraser@12239 435 int upper_thresh = xenlog_upper_thresh;
kfraser@12239 436 int lower_thresh = xenlog_lower_thresh;
kfraser@12239 437
kfraser@12239 438 while ( (p[0] == '<') && (p[1] != '\0') && (p[2] == '>') )
kfraser@12239 439 {
kfraser@12239 440 switch ( p[1] )
kfraser@12239 441 {
kfraser@12239 442 case 'G':
kfraser@12239 443 upper_thresh = xenlog_guest_upper_thresh;
kfraser@12239 444 lower_thresh = xenlog_guest_lower_thresh;
kfraser@12239 445 if ( loglvl == -1 )
kfraser@12239 446 loglvl = XENLOG_GUEST_DEFAULT;
kfraser@12239 447 break;
kfraser@12239 448 case '0' ... '3':
kfraser@12239 449 loglvl = p[1] - '0';
kfraser@12239 450 break;
kfraser@12239 451 }
kfraser@12239 452 p += 3;
kfraser@12239 453 }
kfraser@12239 454
kfraser@12239 455 if ( loglvl == -1 )
kfraser@12239 456 loglvl = XENLOG_DEFAULT;
kfraser@12239 457
kfraser@12239 458 *pp = p;
kfraser@12239 459
kfraser@12239 460 return ((atomic_read(&print_everything) != 0) ||
kfraser@12239 461 (loglvl < lower_thresh) ||
kfraser@12239 462 ((loglvl < upper_thresh) && printk_ratelimit()));
kfraser@12239 463 }
kfraser@12239 464
keir@16071 465 static void printk_start_of_line(void)
keir@16071 466 {
keir@16071 467 struct tm tm;
keir@16071 468 char tstr[32];
keir@16071 469
keir@16071 470 __putstr("(XEN) ");
keir@16071 471
keir@16071 472 if ( !opt_console_timestamps )
keir@16071 473 return;
keir@16071 474
keir@16071 475 tm = wallclock_time();
keir@16071 476 if ( tm.tm_mday == 0 )
keir@16071 477 return;
keir@16071 478
keir@16071 479 snprintf(tstr, sizeof(tstr), "[%04u-%02u-%02u %02u:%02u:%02u] ",
keir@16085 480 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday,
keir@16071 481 tm.tm_hour, tm.tm_min, tm.tm_sec);
keir@16071 482 __putstr(tstr);
keir@16071 483 }
keir@16071 484
kfraser@11947 485 void printk(const char *fmt, ...)
kaf24@1150 486 {
kaf24@1150 487 static char buf[1024];
kfraser@12239 488 static int start_of_line = 1, do_print;
kaf24@1150 489
kaf24@1150 490 va_list args;
kaf24@1150 491 char *p, *q;
kaf24@1150 492 unsigned long flags;
kaf24@1150 493
kfraser@12067 494 /* console_lock can be acquired recursively from __printk_ratelimit(). */
kfraser@12067 495 local_irq_save(flags);
kfraser@12067 496 spin_lock_recursive(&console_lock);
kaf24@1150 497
kaf24@1150 498 va_start(args, fmt);
kaf24@1150 499 (void)vsnprintf(buf, sizeof(buf), fmt, args);
kaf24@1150 500 va_end(args);
kaf24@1150 501
kaf24@1150 502 p = buf;
kaf24@12031 503
kaf24@1150 504 while ( (q = strchr(p, '\n')) != NULL )
kaf24@1150 505 {
kaf24@1150 506 *q = '\0';
kaf24@1150 507 if ( start_of_line )
kfraser@12239 508 do_print = printk_prefix_check(p, &p);
kfraser@12239 509 if ( do_print )
kfraser@12239 510 {
kfraser@12239 511 if ( start_of_line )
keir@16071 512 printk_start_of_line();
kfraser@12239 513 __putstr(p);
kfraser@12239 514 __putstr("\n");
kfraser@12239 515 }
kaf24@1150 516 start_of_line = 1;
kaf24@1150 517 p = q + 1;
kaf24@1150 518 }
kaf24@1150 519
kaf24@1150 520 if ( *p != '\0' )
kaf24@1150 521 {
kaf24@1150 522 if ( start_of_line )
kfraser@12239 523 do_print = printk_prefix_check(p, &p);
kfraser@12239 524 if ( do_print )
kfraser@12239 525 {
kfraser@12239 526 if ( start_of_line )
keir@16071 527 printk_start_of_line();
kfraser@12239 528 __putstr(p);
kfraser@12239 529 }
kaf24@1150 530 start_of_line = 0;
kaf24@1150 531 }
kaf24@1150 532
kfraser@12067 533 spin_unlock_recursive(&console_lock);
kfraser@12067 534 local_irq_restore(flags);
kaf24@1150 535 }
kaf24@1150 536
keir@15082 537 void __init init_console(void)
kaf24@1150 538 {
riel@3992 539 char *p;
kaf24@1150 540
kaf24@1150 541 /* Where should console output go? */
kaf24@1150 542 for ( p = opt_console; p != NULL; p = strchr(p, ',') )
kaf24@1150 543 {
kaf24@1150 544 if ( *p == ',' )
kaf24@1150 545 p++;
kaf24@1150 546 if ( strncmp(p, "com", 3) == 0 )
kaf24@5195 547 sercon_handle = serial_parse_handle(p);
kaf24@1150 548 else if ( strncmp(p, "vga", 3) == 0 )
kfraser@11177 549 vga_init();
kaf24@1150 550 }
kaf24@1150 551
kaf24@1150 552 serial_set_rx_handler(sercon_handle, serial_rx);
kaf24@3338 553
kaf24@3338 554 /* HELLO WORLD --- start-of-day banner text. */
keir@16264 555 spin_lock(&console_lock);
keir@16071 556 __putstr(xen_banner());
keir@16264 557 spin_unlock(&console_lock);
keir@16071 558 printk("Xen version %d.%d%s (%s@%s) (%s) %s\n",
kfraser@10890 559 xen_major_version(), xen_minor_version(), xen_extra_version(),
kfraser@10890 560 xen_compile_by(), xen_compile_domain(),
kfraser@10890 561 xen_compiler(), xen_compile_date());
keir@16071 562 printk("Latest ChangeSet: %s\n", xen_changeset());
kaf24@5322 563
kaf24@5322 564 if ( opt_sync_console )
kaf24@5322 565 {
kaf24@5322 566 serial_start_sync(sercon_handle);
kaf24@10500 567 add_taint(TAINT_SYNC_CONSOLE);
kaf24@5322 568 printk("Console output is synchronous.\n");
kaf24@5322 569 }
kaf24@1150 570 }
kaf24@1150 571
keir@15082 572 void __init console_endboot(void)
kaf24@1150 573 {
kaf24@10501 574 int i, j;
kaf24@10500 575
kaf24@12232 576 printk("Std. Loglevel: %s", loglvl_str(xenlog_lower_thresh));
kaf24@12232 577 if ( xenlog_upper_thresh != xenlog_lower_thresh )
kaf24@12232 578 printk(" (Rate-limited: %s)", loglvl_str(xenlog_upper_thresh));
kaf24@12232 579 printk("\nGuest Loglevel: %s", loglvl_str(xenlog_guest_lower_thresh));
kaf24@12232 580 if ( xenlog_guest_upper_thresh != xenlog_guest_lower_thresh )
kaf24@12232 581 printk(" (Rate-limited: %s)", loglvl_str(xenlog_guest_upper_thresh));
kaf24@12232 582 printk("\n");
kaf24@12232 583
kaf24@10500 584 if ( opt_sync_console )
kaf24@10500 585 {
kaf24@10500 586 printk("**********************************************\n");
kfraser@12239 587 printk("******* WARNING: CONSOLE OUTPUT IS SYNCHRONOUS\n");
kaf24@10500 588 printk("******* This option is intended to aid debugging "
kaf24@10500 589 "of Xen by ensuring\n");
kaf24@10500 590 printk("******* that all output is synchronously delivered "
kaf24@10500 591 "on the serial line.\n");
kaf24@10500 592 printk("******* However it can introduce SIGNIFICANT latencies "
kaf24@10500 593 "and affect\n");
kaf24@10500 594 printk("******* timekeeping. It is NOT recommended for "
kaf24@10500 595 "production use!\n");
kaf24@10500 596 printk("**********************************************\n");
kaf24@10500 597 for ( i = 0; i < 3; i++ )
kaf24@10500 598 {
kaf24@10500 599 printk("%d... ", 3-i);
kaf24@10501 600 for ( j = 0; j < 100; j++ )
kaf24@10501 601 {
kfraser@10620 602 process_pending_timers();
kaf24@10501 603 mdelay(10);
kaf24@10501 604 }
kaf24@10500 605 }
kaf24@10500 606 printk("\n");
kaf24@10500 607 }
kaf24@10500 608
kfraser@11177 609 vga_endboot();
kaf24@1769 610
kaf24@1769 611 /*
kaf24@1769 612 * If user specifies so, we fool the switch routine to redirect input
kaf24@1769 613 * straight back to Xen. I use this convoluted method so we still print
kaf24@1769 614 * a useful 'how to switch' message.
kaf24@1769 615 */
kaf24@1769 616 if ( opt_conswitch[1] == 'x' )
kaf24@1769 617 xen_rx = !xen_rx;
kaf24@1769 618
kaf24@1150 619 /* Serial input is directed to DOM0 by default. */
kaf24@1175 620 switch_serial_input();
kfraser@12239 621 }
kfraser@12239 622
keir@15974 623 int console_has(const char *device)
keir@15974 624 {
keir@15974 625 char *p;
keir@15974 626
keir@15974 627 for ( p = opt_console; p != NULL; p = strchr(p, ',') )
keir@15974 628 {
keir@15974 629 if ( *p == ',' )
keir@15974 630 p++;
keir@15974 631 if ( strncmp(p, device, strlen(device)) == 0 )
keir@15974 632 return 1;
keir@15974 633 }
keir@15974 634
keir@15974 635 return 0;
keir@15974 636 }
keir@15974 637
kfraser@12239 638 void console_start_log_everything(void)
kfraser@12239 639 {
kfraser@12239 640 atomic_inc(&print_everything);
kfraser@12239 641 }
kfraser@12239 642
kfraser@12239 643 void console_end_log_everything(void)
kfraser@12239 644 {
kfraser@12239 645 atomic_dec(&print_everything);
kaf24@1150 646 }
kaf24@1150 647
kaf24@2041 648 void console_force_unlock(void)
kaf24@2041 649 {
kfraser@11872 650 spin_lock_init(&console_lock);
kaf24@2041 651 serial_force_unlock(sercon_handle);
kaf24@9617 652 console_start_sync();
kaf24@2041 653 }
kaf24@2041 654
kaf24@2041 655 void console_force_lock(void)
kaf24@2041 656 {
kaf24@2041 657 spin_lock(&console_lock);
kaf24@2041 658 }
kaf24@2041 659
kaf24@5321 660 void console_start_sync(void)
kaf24@5321 661 {
kfraser@12239 662 console_start_log_everything();
kaf24@5321 663 serial_start_sync(sercon_handle);
kaf24@5321 664 }
kaf24@5321 665
kaf24@5321 666 void console_end_sync(void)
kaf24@5321 667 {
kaf24@5321 668 serial_end_sync(sercon_handle);
kfraser@12239 669 console_end_log_everything();
kaf24@5321 670 }
kaf24@5321 671
cl349@5204 672 void console_putc(char c)
cl349@5204 673 {
cl349@5204 674 serial_putc(sercon_handle, c);
cl349@5204 675 }
cl349@5204 676
cl349@5204 677 int console_getc(void)
cl349@5204 678 {
cl349@5204 679 return serial_getc(sercon_handle);
cl349@5204 680 }
cl349@5204 681
kaf24@12030 682 /*
kaf24@12030 683 * printk rate limiting, lifted from Linux.
kaf24@12030 684 *
kaf24@12030 685 * This enforces a rate limit: not more than one kernel message
kaf24@12030 686 * every printk_ratelimit_ms (millisecs).
kaf24@12030 687 */
kaf24@12030 688 int __printk_ratelimit(int ratelimit_ms, int ratelimit_burst)
kaf24@12030 689 {
kaf24@12030 690 static DEFINE_SPINLOCK(ratelimit_lock);
kaf24@12030 691 static unsigned long toks = 10 * 5 * 1000;
kaf24@12030 692 static unsigned long last_msg;
kaf24@12030 693 static int missed;
kaf24@12030 694 unsigned long flags;
kaf24@12030 695 unsigned long long now = NOW(); /* ns */
kaf24@12030 696 unsigned long ms;
kaf24@12030 697
kaf24@12030 698 do_div(now, 1000000);
kaf24@12030 699 ms = (unsigned long)now;
kaf24@12030 700
kaf24@12030 701 spin_lock_irqsave(&ratelimit_lock, flags);
kaf24@12030 702 toks += ms - last_msg;
kaf24@12030 703 last_msg = ms;
kaf24@12030 704 if ( toks > (ratelimit_burst * ratelimit_ms))
kaf24@12030 705 toks = ratelimit_burst * ratelimit_ms;
kaf24@12030 706 if ( toks >= ratelimit_ms )
kaf24@12030 707 {
kaf24@12030 708 int lost = missed;
kaf24@12030 709 missed = 0;
kaf24@12030 710 toks -= ratelimit_ms;
kfraser@12067 711 spin_unlock(&ratelimit_lock);
kaf24@12030 712 if ( lost )
kfraser@12067 713 {
kfraser@12067 714 char lost_str[8];
kfraser@12067 715 snprintf(lost_str, sizeof(lost_str), "%d", lost);
kfraser@12067 716 /* console_lock may already be acquired by printk(). */
kfraser@12067 717 spin_lock_recursive(&console_lock);
keir@16071 718 printk_start_of_line();
kfraser@12067 719 __putstr("printk: ");
kfraser@12067 720 __putstr(lost_str);
kfraser@12067 721 __putstr(" messages suppressed.\n");
kfraser@12067 722 spin_unlock_recursive(&console_lock);
kfraser@12067 723 }
kfraser@12067 724 local_irq_restore(flags);
kaf24@12030 725 return 1;
kaf24@12030 726 }
kaf24@12030 727 missed++;
kaf24@12030 728 spin_unlock_irqrestore(&ratelimit_lock, flags);
kaf24@12030 729 return 0;
kaf24@12030 730 }
kaf24@12030 731
kaf24@12030 732 /* minimum time in ms between messages */
kaf24@12030 733 int printk_ratelimit_ms = 5 * 1000;
kaf24@12030 734
kaf24@12030 735 /* number of messages we send before ratelimiting */
kaf24@12030 736 int printk_ratelimit_burst = 10;
kaf24@12030 737
kaf24@12030 738 int printk_ratelimit(void)
kaf24@12030 739 {
kfraser@12067 740 return __printk_ratelimit(printk_ratelimit_ms, printk_ratelimit_burst);
kaf24@12030 741 }
kaf24@1150 742
kaf24@1150 743 /*
kaf24@1150 744 * **************************************************************
kaf24@3827 745 * *************** Serial console ring buffer *******************
mafetter@3816 746 * **************************************************************
mafetter@3816 747 */
mafetter@3816 748
kfraser@11212 749 #ifdef DEBUG_TRACE_DUMP
kaf24@3827 750
kaf24@3875 751 /* Send output direct to console, or buffer it? */
tdeegan@11172 752 static volatile int debugtrace_send_to_console;
kaf24@3875 753
kaf24@4653 754 static char *debugtrace_buf; /* Debug-trace buffer */
kaf24@4653 755 static unsigned int debugtrace_prd; /* Producer index */
kaf24@4653 756 static unsigned int debugtrace_kilobytes = 128, debugtrace_bytes;
kaf24@4923 757 static unsigned int debugtrace_used;
kaf24@10288 758 static DEFINE_SPINLOCK(debugtrace_lock);
kaf24@3827 759 integer_param("debugtrace", debugtrace_kilobytes);
kaf24@3827 760
tdeegan@11172 761 static void debugtrace_dump_worker(void)
kaf24@3827 762 {
kaf24@4923 763 if ( (debugtrace_bytes == 0) || !debugtrace_used )
mafetter@3816 764 return;
mafetter@3816 765
mafetter@4139 766 printk("debugtrace_dump() starting\n");
mafetter@4139 767
kaf24@3827 768 /* Print oldest portion of the ring. */
mafetter@4139 769 ASSERT(debugtrace_buf[debugtrace_bytes - 1] == 0);
kfraser@11602 770 sercon_puts(&debugtrace_buf[debugtrace_prd]);
mafetter@3816 771
kaf24@3827 772 /* Print youngest portion of the ring. */
maf46@3864 773 debugtrace_buf[debugtrace_prd] = '\0';
kfraser@11602 774 sercon_puts(&debugtrace_buf[0]);
mafetter@3816 775
kaf24@3875 776 memset(debugtrace_buf, '\0', debugtrace_bytes);
maf46@3864 777
mafetter@4139 778 printk("debugtrace_dump() finished\n");
tdeegan@11172 779 }
tdeegan@11172 780
kfraser@11212 781 static void debugtrace_toggle(void)
tdeegan@11172 782 {
tdeegan@11172 783 unsigned long flags;
tdeegan@11172 784
tdeegan@11172 785 watchdog_disable();
tdeegan@11172 786 spin_lock_irqsave(&debugtrace_lock, flags);
tdeegan@11172 787
kfraser@12239 788 /*
kfraser@12239 789 * Dump the buffer *before* toggling, in case the act of dumping the
kfraser@12239 790 * buffer itself causes more printk() invocations.
kfraser@12239 791 */
tdeegan@11172 792 printk("debugtrace_printk now writing to %s.\n",
tdeegan@11172 793 !debugtrace_send_to_console ? "console": "buffer");
tdeegan@11172 794 if ( !debugtrace_send_to_console )
tdeegan@11172 795 debugtrace_dump_worker();
tdeegan@11172 796
tdeegan@11172 797 debugtrace_send_to_console = !debugtrace_send_to_console;
mafetter@4139 798
kaf24@3875 799 spin_unlock_irqrestore(&debugtrace_lock, flags);
tdeegan@11172 800 watchdog_enable();
mafetter@3816 801
tdeegan@11172 802 }
tdeegan@11172 803
tdeegan@11172 804 void debugtrace_dump(void)
tdeegan@11172 805 {
tdeegan@11172 806 unsigned long flags;
tdeegan@11172 807
tdeegan@11172 808 watchdog_disable();
tdeegan@11172 809 spin_lock_irqsave(&debugtrace_lock, flags);
tdeegan@11172 810
tdeegan@11172 811 debugtrace_dump_worker();
tdeegan@11172 812
tdeegan@11172 813 spin_unlock_irqrestore(&debugtrace_lock, flags);
kaf24@4926 814 watchdog_enable();
mafetter@3816 815 }
mafetter@3816 816
kaf24@3827 817 void debugtrace_printk(const char *fmt, ...)
mafetter@3816 818 {
kaf24@3875 819 static char buf[1024];
tdeegan@11172 820 static u32 count;
kaf24@3827 821
kaf24@4653 822 va_list args;
kaf24@4653 823 char *p;
kaf24@4653 824 unsigned long flags;
kaf24@3827 825
kaf24@3827 826 if ( debugtrace_bytes == 0 )
mafetter@3816 827 return;
kaf24@3827 828
kaf24@4923 829 debugtrace_used = 1;
kaf24@4923 830
maf46@3864 831 spin_lock_irqsave(&debugtrace_lock, flags);
mafetter@3816 832
mafetter@4139 833 ASSERT(debugtrace_buf[debugtrace_bytes - 1] == 0);
mafetter@4139 834
kfraser@13689 835 snprintf(buf, sizeof(buf), "%u ", ++count);
tdeegan@11172 836
kaf24@3827 837 va_start(args, fmt);
keir@16493 838 (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, args);
maf46@3865 839 va_end(args);
kaf24@3827 840
kaf24@3875 841 if ( debugtrace_send_to_console )
maf46@3865 842 {
maf46@3865 843 serial_puts(sercon_handle, buf);
maf46@3865 844 }
maf46@3865 845 else
maf46@3864 846 {
maf46@3865 847 for ( p = buf; *p != '\0'; p++ )
maf46@3865 848 {
kaf24@3875 849 debugtrace_buf[debugtrace_prd++] = *p;
kaf24@3875 850 /* Always leave a nul byte at the end of the buffer. */
kaf24@3875 851 if ( debugtrace_prd == (debugtrace_bytes - 1) )
maf46@3865 852 debugtrace_prd = 0;
maf46@3865 853 }
maf46@3864 854 }
maf46@3864 855
maf46@3864 856 spin_unlock_irqrestore(&debugtrace_lock, flags);
mafetter@3816 857 }
mafetter@3816 858
kfraser@11212 859 static void debugtrace_key(unsigned char key)
kfraser@11212 860 {
kfraser@11212 861 debugtrace_toggle();
kfraser@11212 862 }
kfraser@11212 863
kaf24@3827 864 static int __init debugtrace_init(void)
mafetter@3816 865 {
mafetter@3816 866 int order;
mafetter@4139 867 unsigned int kbytes, bytes;
mafetter@3816 868
kaf24@3827 869 /* Round size down to next power of two. */
kaf24@3827 870 while ( (kbytes = (debugtrace_kilobytes & (debugtrace_kilobytes-1))) != 0 )
kaf24@3827 871 debugtrace_kilobytes = kbytes;
kaf24@3827 872
mafetter@4139 873 bytes = debugtrace_kilobytes << 10;
mafetter@4139 874 if ( bytes == 0 )
mafetter@3816 875 return 0;
mafetter@3816 876
kaf24@6702 877 order = get_order_from_bytes(bytes);
kaf24@5398 878 debugtrace_buf = alloc_xenheap_pages(order);
kaf24@3827 879 ASSERT(debugtrace_buf != NULL);
mafetter@3816 880
mafetter@4139 881 memset(debugtrace_buf, '\0', bytes);
mafetter@4139 882
mafetter@4139 883 debugtrace_bytes = bytes;
mafetter@4139 884
kfraser@11212 885 register_keyhandler(
kfraser@11212 886 'T', debugtrace_key, "toggle debugtrace to console/buffer");
kfraser@11212 887
mafetter@3816 888 return 0;
mafetter@3816 889 }
kaf24@3827 890 __initcall(debugtrace_init);
mafetter@3816 891
kaf24@3827 892 #endif /* !NDEBUG */
kaf24@3827 893
mafetter@3816 894
mafetter@3816 895 /*
mafetter@3816 896 * **************************************************************
kaf24@1150 897 * *************** Debugging/tracing/error-report ***************
kaf24@1150 898 * **************************************************************
kaf24@1150 899 */
kaf24@1150 900
kaf24@1150 901 void panic(const char *fmt, ...)
kaf24@1150 902 {
kaf24@1150 903 va_list args;
kaf24@1150 904 unsigned long flags;
kaf24@10288 905 static DEFINE_SPINLOCK(lock);
keir@14687 906 static char buf[128];
kaf24@1150 907
kaf24@3875 908 debugtrace_dump();
maf46@3865 909
keir@14687 910 /* Protects buf[] and ensure multi-line message prints atomically. */
keir@14687 911 spin_lock_irqsave(&lock, flags);
keir@14687 912
kaf24@1150 913 va_start(args, fmt);
kaf24@1150 914 (void)vsnprintf(buf, sizeof(buf), fmt, args);
kaf24@1150 915 va_end(args);
sos22@1951 916
kaf24@9550 917 console_start_sync();
kaf24@6247 918 printk("\n****************************************\n");
kaf24@6247 919 printk("Panic on CPU %d:\n", smp_processor_id());
kaf24@6247 920 printk(buf);
kaf24@6247 921 printk("****************************************\n\n");
kaf24@10979 922 if ( opt_noreboot )
kaf24@10979 923 printk("Manual reset required ('noreboot' specified)\n");
kaf24@10979 924 else
kaf24@10979 925 printk("Reboot in five seconds...\n");
keir@14687 926
kaf24@6247 927 spin_unlock_irqrestore(&lock, flags);
kaf24@1150 928
kaf24@9285 929 debugger_trap_immediate();
kaf24@9285 930
kfraser@13029 931 kexec_crash();
ian@12676 932
kaf24@10979 933 if ( opt_noreboot )
kaf24@10979 934 {
kaf24@10979 935 machine_halt();
kaf24@10979 936 }
kaf24@10979 937 else
kaf24@10979 938 {
kaf24@10979 939 watchdog_disable();
kaf24@10979 940 mdelay(5000);
kfraser@15866 941 machine_restart();
kaf24@10979 942 }
kaf24@1150 943 }
kaf24@1150 944
kfraser@10577 945 void __bug(char *file, int line)
kfraser@10577 946 {
kfraser@10577 947 console_start_sync();
kfraser@14590 948 printk("Xen BUG at %s:%d\n", file, line);
kfraser@12280 949 dump_execution_state();
kfraser@14590 950 panic("Xen BUG at %s:%d\n", file, line);
kfraser@10577 951 for ( ; ; ) ;
kfraser@10577 952 }
kfraser@10577 953
kfraser@14590 954 void __warn(char *file, int line)
kfraser@14590 955 {
kfraser@14590 956 printk("Xen WARN at %s:%d\n", file, line);
kfraser@14590 957 dump_execution_state();
kfraser@14590 958 }
kfraser@14590 959
kfraser@15314 960
kfraser@15314 961 /*
kfraser@15314 962 * **************************************************************
kfraser@15314 963 * ****************** Console suspend/resume ********************
kfraser@15314 964 * **************************************************************
kfraser@15314 965 */
kfraser@15314 966
kfraser@15314 967 static void suspend_steal_fn(const char *str) { }
kfraser@15314 968 static int suspend_steal_id;
kfraser@15314 969
kfraser@15314 970 int console_suspend(void)
kfraser@15314 971 {
kfraser@15314 972 suspend_steal_id = console_steal(sercon_handle, suspend_steal_fn);
kfraser@15314 973 serial_suspend();
kfraser@15314 974 return 0;
kfraser@15314 975 }
kfraser@15314 976
kfraser@15314 977 int console_resume(void)
kfraser@15314 978 {
kfraser@15314 979 serial_resume();
kfraser@15314 980 console_giveback(suspend_steal_id);
kfraser@15314 981 return 0;
kfraser@15314 982 }
kfraser@15314 983
kaf24@3914 984 /*
kaf24@3914 985 * Local variables:
kaf24@3914 986 * mode: C
kaf24@3914 987 * c-set-style: "BSD"
kaf24@3914 988 * c-basic-offset: 4
kaf24@3914 989 * tab-width: 4
kaf24@3914 990 * indent-tabs-mode: nil
kaf24@3988 991 * End:
kaf24@3914 992 */
kaf24@1150 993