📄 console.c
字号:
/****************************************************************************** * console.c * * Emergency console I/O for Xen and the domain-0 guest OS. * * Copyright (c) 2002-2004, K A Fraser. * * Added printf_ratelimit * Taken from Linux - Author: Andi Kleen (net_ratelimit) * Ported to Xen - Steven Rostedt - Red Hat */#include <xen/stdarg.h>#include <xen/config.h>#include <xen/version.h>#include <xen/init.h>#include <xen/lib.h>#include <xen/errno.h>#include <xen/event.h>#include <xen/spinlock.h>#include <xen/console.h>#include <xen/serial.h>#include <xen/softirq.h>#include <xen/keyhandler.h>#include <xen/mm.h>#include <xen/delay.h>#include <xen/guest_access.h>#include <xen/shutdown.h>#include <xen/vga.h>#include <xen/kexec.h>#include <asm/current.h>#include <asm/debugger.h>#include <asm/io.h>#include <asm/div64.h>#include <xsm/xsm.h>#include <public/sysctl.h>/* console: comma-separated list of console outputs. */static char opt_console[30] = OPT_CONSOLE_STR;string_param("console", opt_console);/* conswitch: a character pair controlling console switching. *//* Char 1: CTRL+<char1> is used to switch console input between Xen and DOM0 *//* Char 2: If this character is 'x', then do not auto-switch to DOM0 when it *//* boots. Any other value, or omitting the char, enables auto-switch */static unsigned char opt_conswitch[3] = "a";string_param("conswitch", opt_conswitch);/* sync_console: force synchronous console output (useful for debugging). */static int opt_sync_console;boolean_param("sync_console", opt_sync_console);/* console_to_ring: send guest (incl. dom 0) console data to console ring. */static int opt_console_to_ring;boolean_param("console_to_ring", opt_console_to_ring);/* console_timestamps: include a timestamp prefix on every Xen console line. */static int opt_console_timestamps;boolean_param("console_timestamps", opt_console_timestamps);#define CONRING_SIZE 16384#define CONRING_IDX_MASK(i) ((i)&(CONRING_SIZE-1))static char conring[CONRING_SIZE];static uint32_t conringc, conringp;static int sercon_handle = -1;static DEFINE_SPINLOCK(console_lock);/* * To control the amount of printing, thresholds are added. * These thresholds correspond to the XENLOG logging levels. * There's an upper and lower threshold for non-guest messages and for * guest-provoked messages. This works as follows, for a given log level L: * * L < lower_threshold : always logged * lower_threshold <= L < upper_threshold : rate-limited logging * upper_threshold <= L : never logged * * Note, in the above algorithm, to disable rate limiting simply make * the lower threshold equal to the upper. */#ifdef NDEBUG#define XENLOG_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */#define XENLOG_LOWER_THRESHOLD 2 /* Always print ERR and WARNING */#define XENLOG_GUEST_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */#define XENLOG_GUEST_LOWER_THRESHOLD 0 /* Rate-limit ERR and WARNING */#else#define XENLOG_UPPER_THRESHOLD 4 /* Do not discard anything */#define XENLOG_LOWER_THRESHOLD 4 /* Print everything */#define XENLOG_GUEST_UPPER_THRESHOLD 4 /* Do not discard anything */#define XENLOG_GUEST_LOWER_THRESHOLD 4 /* Print everything */#endif/* * The XENLOG_DEFAULT is the default given to printks that * do not have any print level associated with them. */#define XENLOG_DEFAULT 1 /* XENLOG_WARNING */#define XENLOG_GUEST_DEFAULT 1 /* XENLOG_WARNING */static int xenlog_upper_thresh = XENLOG_UPPER_THRESHOLD;static int xenlog_lower_thresh = XENLOG_LOWER_THRESHOLD;static int xenlog_guest_upper_thresh = XENLOG_GUEST_UPPER_THRESHOLD;static int xenlog_guest_lower_thresh = XENLOG_GUEST_LOWER_THRESHOLD;static void parse_loglvl(char *s);static void parse_guest_loglvl(char *s);/* * <lvl> := none|error|warning|info|debug|all * loglvl=<lvl_print_always>[/<lvl_print_ratelimit>] * <lvl_print_always>: log level which is always printed * <lvl_print_rlimit>: log level which is rate-limit printed * Similar definitions for guest_loglvl, but applies to guest tracing. * Defaults: loglvl=warning ; guest_loglvl=none/warning */custom_param("loglvl", parse_loglvl);custom_param("guest_loglvl", parse_guest_loglvl);static atomic_t print_everything = ATOMIC_INIT(0);#define ___parse_loglvl(s, ps, lvlstr, lvlnum) \ if ( !strncmp((s), (lvlstr), strlen(lvlstr)) ) { \ *(ps) = (s) + strlen(lvlstr); \ return (lvlnum); \ }static int __init __parse_loglvl(char *s, char **ps){ ___parse_loglvl(s, ps, "none", 0); ___parse_loglvl(s, ps, "error", 1); ___parse_loglvl(s, ps, "warning", 2); ___parse_loglvl(s, ps, "info", 3); ___parse_loglvl(s, ps, "debug", 4); ___parse_loglvl(s, ps, "all", 4); return 2; /* sane fallback */}static void __init _parse_loglvl(char *s, int *lower, int *upper){ *lower = *upper = __parse_loglvl(s, &s); if ( *s == '/' ) *upper = __parse_loglvl(s+1, &s); if ( *upper < *lower ) *upper = *lower;}static void __init parse_loglvl(char *s){ _parse_loglvl(s, &xenlog_lower_thresh, &xenlog_upper_thresh);}static void __init parse_guest_loglvl(char *s){ _parse_loglvl(s, &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh);}static char * __init loglvl_str(int lvl){ switch ( lvl ) { case 0: return "Nothing"; case 1: return "Errors"; case 2: return "Errors and warnings"; case 3: return "Errors, warnings and info"; case 4: return "All"; } return "???";}/* * ******************************************************** * *************** ACCESS TO CONSOLE RING ***************** * ******************************************************** */static void putchar_console_ring(int c){ ASSERT(spin_is_locked(&console_lock)); conring[CONRING_IDX_MASK(conringp++)] = c; if ( (uint32_t)(conringp - conringc) > CONRING_SIZE ) conringc = conringp - CONRING_SIZE;}long read_console_ring(struct xen_sysctl_readconsole *op){ XEN_GUEST_HANDLE(char) str; uint32_t idx, len, max, sofar, c; str = guest_handle_cast(op->buffer, char), max = op->count; sofar = 0; c = conringc; if ( op->incremental && ((int32_t)(op->index - c) < 0) ) c = op->index; while ( (c != conringp) && (sofar < max) ) { idx = CONRING_IDX_MASK(c); len = conringp - c; if ( (idx + len) > CONRING_SIZE ) len = CONRING_SIZE - idx; if ( (sofar + len) > max ) len = max - sofar; if ( copy_to_guest_offset(str, sofar, &conring[idx], len) ) return -EFAULT; sofar += len; c += len; } if ( op->clear ) { spin_lock_irq(&console_lock); if ( (uint32_t)(conringp - c) > CONRING_SIZE ) conringc = conringp - CONRING_SIZE; else conringc = c; spin_unlock_irq(&console_lock); } op->count = sofar; op->index = c; return 0;}/* * ******************************************************* * *************** ACCESS TO SERIAL LINE ***************** * ******************************************************* *//* Characters received over the serial line are buffered for domain 0. */#define SERIAL_RX_SIZE 128#define SERIAL_RX_MASK(_i) ((_i)&(SERIAL_RX_SIZE-1))static char serial_rx_ring[SERIAL_RX_SIZE];static unsigned int serial_rx_cons, serial_rx_prod;static void (*serial_steal_fn)(const char *);int console_steal(int handle, void (*fn)(const char *)){ if ( (handle == -1) || (handle != sercon_handle) ) return 0; if ( serial_steal_fn != NULL ) return -EBUSY; serial_steal_fn = fn; return 1;}void console_giveback(int id){ if ( id == 1 ) serial_steal_fn = NULL;}static void sercon_puts(const char *s){ if ( serial_steal_fn != NULL ) (*serial_steal_fn)(s); else serial_puts(sercon_handle, s);}/* CTRL-<switch_char> switches input direction between Xen and DOM0. */#define switch_code (opt_conswitch[0]-'a'+1)static int xen_rx = 1; /* FALSE => serial input passed to domain 0. */static void switch_serial_input(void){ static char *input_str[2] = { "DOM0", "Xen" }; xen_rx = !xen_rx; printk("*** Serial input -> %s", input_str[xen_rx]); if ( switch_code ) printk(" (type 'CTRL-%c' three times to switch input to %s)", opt_conswitch[0], input_str[!xen_rx]); printk("\n");}static void __serial_rx(char c, struct cpu_user_regs *regs){ if ( xen_rx ) return handle_keypress(c, regs); /* Deliver input to guest buffer, unless it is already full. */ if ( (serial_rx_prod-serial_rx_cons) != SERIAL_RX_SIZE ) serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c; /* Always notify the guest: prevents receive path from getting stuck. */ send_guest_global_virq(dom0, VIRQ_CONSOLE);}static void serial_rx(char c, struct cpu_user_regs *regs){ static int switch_code_count = 0; if ( switch_code && (c == switch_code) ) { /* We eat CTRL-<switch_char> in groups of 3 to switch console input. */ if ( ++switch_code_count == 3 ) { switch_serial_input(); switch_code_count = 0; } return; } for ( ; switch_code_count != 0; switch_code_count-- ) __serial_rx(switch_code, regs); /* Finally process the just-received character. */ __serial_rx(c, regs);}static long guest_console_write(XEN_GUEST_HANDLE(char) buffer, int count){ char kbuf[128], *kptr; int kcount; while ( count > 0 ) { while ( serial_tx_space(sercon_handle) < (serial_txbufsz / 2) ) { if ( hypercall_preempt_check() ) break; cpu_relax(); } if ( hypercall_preempt_check() ) return hypercall_create_continuation( __HYPERVISOR_console_io, "iih", CONSOLEIO_write, count, buffer); kcount = min_t(int, count, sizeof(kbuf)-1); if ( copy_from_guest(kbuf, buffer, kcount) ) return -EFAULT; kbuf[kcount] = '\0'; spin_lock_irq(&console_lock); sercon_puts(kbuf); vga_puts(kbuf); if ( opt_console_to_ring ) { for ( kptr = kbuf; *kptr != '\0'; kptr++ ) putchar_console_ring(*kptr); send_guest_global_virq(dom0, VIRQ_CON_RING); } spin_unlock_irq(&console_lock); guest_handle_add_offset(buffer, kcount); count -= kcount; } return 0;}long do_console_io(int cmd, int count, XEN_GUEST_HANDLE(char) buffer){ long rc; unsigned int idx, len;#ifndef VERBOSE /* Only domain 0 may access the emergency console. */ if ( current->domain->domain_id != 0 ) return -EPERM;#endif rc = xsm_console_io(current->domain, cmd); if ( rc ) return rc; switch ( cmd ) { case CONSOLEIO_write: rc = guest_console_write(buffer, count); break; case CONSOLEIO_read: rc = 0; while ( (serial_rx_cons != serial_rx_prod) && (rc < count) ) { idx = SERIAL_RX_MASK(serial_rx_cons); len = serial_rx_prod - serial_rx_cons; if ( (idx + len) > SERIAL_RX_SIZE ) len = SERIAL_RX_SIZE - idx; if ( (rc + len) > count ) len = count - rc; if ( copy_to_guest_offset(buffer, rc, &serial_rx_ring[idx], len) ) { rc = -EFAULT; break; } rc += len; serial_rx_cons += len; } break; default: rc = -ENOSYS; break; } return rc;}/* * ***************************************************** * *************** GENERIC CONSOLE I/O ***************** * ***************************************************** */static void __putstr(const char *str){ int c; ASSERT(spin_is_locked(&console_lock)); sercon_puts(str); vga_puts(str); while ( (c = *str++) != '\0' ) putchar_console_ring(c); send_guest_global_virq(dom0, VIRQ_CON_RING);}static int printk_prefix_check(char *p, char **pp){ int loglvl = -1; int upper_thresh = xenlog_upper_thresh; int lower_thresh = xenlog_lower_thresh; while ( (p[0] == '<') && (p[1] != '\0') && (p[2] == '>') ) { switch ( p[1] ) { case 'G': upper_thresh = xenlog_guest_upper_thresh; lower_thresh = xenlog_guest_lower_thresh; if ( loglvl == -1 ) loglvl = XENLOG_GUEST_DEFAULT; break; case '0' ... '3': loglvl = p[1] - '0'; break; } p += 3; } if ( loglvl == -1 ) loglvl = XENLOG_DEFAULT; *pp = p; return ((atomic_read(&print_everything) != 0) || (loglvl < lower_thresh) || ((loglvl < upper_thresh) && printk_ratelimit()));} static void printk_start_of_line(void){ struct tm tm; char tstr[32]; __putstr("(XEN) "); if ( !opt_console_timestamps ) return; tm = wallclock_time(); if ( tm.tm_mday == 0 ) return; snprintf(tstr, sizeof(tstr), "[%04u-%02u-%02u %02u:%02u:%02u] ", 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); __putstr(tstr);}void printk(const char *fmt, ...){ static char buf[1024]; static int start_of_line = 1, do_print; va_list args; char *p, *q; unsigned long flags; /* console_lock can be acquired recursively from __printk_ratelimit(). */ local_irq_save(flags); spin_lock_recursive(&console_lock); va_start(args, fmt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -