/root/src/xen/xen/drivers/char/console.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * console.c |
3 | | * |
4 | | * Emergency console I/O for Xen and the domain-0 guest OS. |
5 | | * |
6 | | * Copyright (c) 2002-2004, K A Fraser. |
7 | | * |
8 | | * Added printf_ratelimit |
9 | | * Taken from Linux - Author: Andi Kleen (net_ratelimit) |
10 | | * Ported to Xen - Steven Rostedt - Red Hat |
11 | | */ |
12 | | |
13 | | #include <xen/version.h> |
14 | | #include <xen/lib.h> |
15 | | #include <xen/init.h> |
16 | | #include <xen/event.h> |
17 | | #include <xen/console.h> |
18 | | #include <xen/serial.h> |
19 | | #include <xen/softirq.h> |
20 | | #include <xen/keyhandler.h> |
21 | | #include <xen/guest_access.h> |
22 | | #include <xen/watchdog.h> |
23 | | #include <xen/shutdown.h> |
24 | | #include <xen/video.h> |
25 | | #include <xen/kexec.h> |
26 | | #include <xen/ctype.h> |
27 | | #include <xen/warning.h> |
28 | | #include <asm/debugger.h> |
29 | | #include <asm/div64.h> |
30 | | #include <xen/hypercall.h> /* for do_console_io */ |
31 | | #include <xen/early_printk.h> |
32 | | #include <xen/warning.h> |
33 | | |
34 | | /* console: comma-separated list of console outputs. */ |
35 | | static char __initdata opt_console[30] = OPT_CONSOLE_STR; |
36 | | string_param("console", opt_console); |
37 | | |
38 | | /* conswitch: a character pair controlling console switching. */ |
39 | | /* Char 1: CTRL+<char1> is used to switch console input between Xen and DOM0 */ |
40 | | /* Char 2: If this character is 'x', then do not auto-switch to DOM0 when it */ |
41 | | /* boots. Any other value, or omitting the char, enables auto-switch */ |
42 | | static unsigned char __read_mostly opt_conswitch[3] = "a"; |
43 | | string_runtime_param("conswitch", opt_conswitch); |
44 | | |
45 | | /* sync_console: force synchronous console output (useful for debugging). */ |
46 | | static bool_t __initdata opt_sync_console; |
47 | | boolean_param("sync_console", opt_sync_console); |
48 | | static const char __initconst warning_sync_console[] = |
49 | | "WARNING: CONSOLE OUTPUT IS SYNCHRONOUS\n" |
50 | | "This option is intended to aid debugging of Xen by ensuring\n" |
51 | | "that all output is synchronously delivered on the serial line.\n" |
52 | | "However it can introduce SIGNIFICANT latencies and affect\n" |
53 | | "timekeeping. It is NOT recommended for production use!\n"; |
54 | | |
55 | | /* console_to_ring: send guest (incl. dom 0) console data to console ring. */ |
56 | | static bool_t __read_mostly opt_console_to_ring; |
57 | | boolean_param("console_to_ring", opt_console_to_ring); |
58 | | |
59 | | /* console_timestamps: include a timestamp prefix on every Xen console line. */ |
60 | | enum con_timestamp_mode |
61 | | { |
62 | | TSM_NONE, /* No timestamps */ |
63 | | TSM_DATE, /* [YYYY-MM-DD HH:MM:SS] */ |
64 | | TSM_DATE_MS, /* [YYYY-MM-DD HH:MM:SS.mmm] */ |
65 | | TSM_BOOT /* [SSSSSS.uuuuuu] */ |
66 | | }; |
67 | | |
68 | | static enum con_timestamp_mode __read_mostly opt_con_timestamp_mode = TSM_NONE; |
69 | | |
70 | | static int parse_console_timestamps(const char *s); |
71 | | custom_runtime_param("console_timestamps", parse_console_timestamps); |
72 | | |
73 | | /* conring_size: allows a large console ring than default (16kB). */ |
74 | | static uint32_t __initdata opt_conring_size; |
75 | | size_param("conring_size", opt_conring_size); |
76 | | |
77 | | #define _CONRING_SIZE 16384 |
78 | 15.5k | #define CONRING_IDX_MASK(i) ((i)&(conring_size-1)) |
79 | | static char __initdata _conring[_CONRING_SIZE]; |
80 | | static char *__read_mostly conring = _conring; |
81 | | static uint32_t __read_mostly conring_size = _CONRING_SIZE; |
82 | | static uint32_t conringc, conringp; |
83 | | |
84 | | static int __read_mostly sercon_handle = -1; |
85 | | |
86 | | static DEFINE_SPINLOCK(console_lock); |
87 | | |
88 | | /* |
89 | | * To control the amount of printing, thresholds are added. |
90 | | * These thresholds correspond to the XENLOG logging levels. |
91 | | * There's an upper and lower threshold for non-guest messages and for |
92 | | * guest-provoked messages. This works as follows, for a given log level L: |
93 | | * |
94 | | * L < lower_threshold : always logged |
95 | | * lower_threshold <= L < upper_threshold : rate-limited logging |
96 | | * upper_threshold <= L : never logged |
97 | | * |
98 | | * Note, in the above algorithm, to disable rate limiting simply make |
99 | | * the lower threshold equal to the upper. |
100 | | */ |
101 | | #ifdef NDEBUG |
102 | | #define XENLOG_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */ |
103 | | #define XENLOG_LOWER_THRESHOLD 2 /* Always print ERR and WARNING */ |
104 | | #define XENLOG_GUEST_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */ |
105 | | #define XENLOG_GUEST_LOWER_THRESHOLD 0 /* Rate-limit ERR and WARNING */ |
106 | | #else |
107 | | #define XENLOG_UPPER_THRESHOLD 4 /* Do not discard anything */ |
108 | | #define XENLOG_LOWER_THRESHOLD 4 /* Print everything */ |
109 | | #define XENLOG_GUEST_UPPER_THRESHOLD 4 /* Do not discard anything */ |
110 | | #define XENLOG_GUEST_LOWER_THRESHOLD 4 /* Print everything */ |
111 | | #endif |
112 | | /* |
113 | | * The XENLOG_DEFAULT is the default given to printks that |
114 | | * do not have any print level associated with them. |
115 | | */ |
116 | 194 | #define XENLOG_DEFAULT 1 /* XENLOG_WARNING */ |
117 | 41 | #define XENLOG_GUEST_DEFAULT 1 /* XENLOG_WARNING */ |
118 | | |
119 | | static int __read_mostly xenlog_upper_thresh = XENLOG_UPPER_THRESHOLD; |
120 | | static int __read_mostly xenlog_lower_thresh = XENLOG_LOWER_THRESHOLD; |
121 | | static int __read_mostly xenlog_guest_upper_thresh = |
122 | | XENLOG_GUEST_UPPER_THRESHOLD; |
123 | | static int __read_mostly xenlog_guest_lower_thresh = |
124 | | XENLOG_GUEST_LOWER_THRESHOLD; |
125 | | |
126 | | static int parse_loglvl(const char *s); |
127 | | static int parse_guest_loglvl(const char *s); |
128 | | |
129 | | /* |
130 | | * <lvl> := none|error|warning|info|debug|all |
131 | | * loglvl=<lvl_print_always>[/<lvl_print_ratelimit>] |
132 | | * <lvl_print_always>: log level which is always printed |
133 | | * <lvl_print_rlimit>: log level which is rate-limit printed |
134 | | * Similar definitions for guest_loglvl, but applies to guest tracing. |
135 | | * Defaults: loglvl=warning ; guest_loglvl=none/warning |
136 | | */ |
137 | | custom_runtime_param("loglvl", parse_loglvl); |
138 | | custom_runtime_param("guest_loglvl", parse_guest_loglvl); |
139 | | |
140 | | static atomic_t print_everything = ATOMIC_INIT(0); |
141 | | |
142 | | #define ___parse_loglvl(s, ps, lvlstr, lvlnum) \ |
143 | 12 | if ( !strncmp((s), (lvlstr), strlen(lvlstr)) ) { \ |
144 | 2 | *(ps) = (s) + strlen(lvlstr); \ |
145 | 2 | return (lvlnum); \ |
146 | 2 | } |
147 | | |
148 | | static int __parse_loglvl(const char *s, const char **ps) |
149 | 2 | { |
150 | 2 | ___parse_loglvl(s, ps, "none", 0); |
151 | 2 | ___parse_loglvl(s, ps, "error", 1); |
152 | 2 | ___parse_loglvl(s, ps, "warning", 2); |
153 | 2 | ___parse_loglvl(s, ps, "info", 3); |
154 | 2 | ___parse_loglvl(s, ps, "debug", 4); |
155 | 2 | ___parse_loglvl(s, ps, "all", 4); |
156 | 0 | return 2; /* sane fallback */ |
157 | 2 | } |
158 | | |
159 | | static int _parse_loglvl(const char *s, int *lower, int *upper) |
160 | 2 | { |
161 | 2 | *lower = *upper = __parse_loglvl(s, &s); |
162 | 2 | if ( *s == '/' ) |
163 | 0 | *upper = __parse_loglvl(s+1, &s); |
164 | 2 | if ( *upper < *lower ) |
165 | 0 | *upper = *lower; |
166 | 2 | |
167 | 2 | return *s ? -EINVAL : 0; |
168 | 2 | } |
169 | | |
170 | | static int parse_loglvl(const char *s) |
171 | 1 | { |
172 | 1 | return _parse_loglvl(s, &xenlog_lower_thresh, &xenlog_upper_thresh); |
173 | 1 | } |
174 | | |
175 | | static int parse_guest_loglvl(const char *s) |
176 | 1 | { |
177 | 1 | return _parse_loglvl(s, &xenlog_guest_lower_thresh, |
178 | 1 | &xenlog_guest_upper_thresh); |
179 | 1 | } |
180 | | |
181 | | static char *loglvl_str(int lvl) |
182 | 2 | { |
183 | 2 | switch ( lvl ) |
184 | 2 | { |
185 | 0 | case 0: return "Nothing"; |
186 | 0 | case 1: return "Errors"; |
187 | 0 | case 2: return "Errors and warnings"; |
188 | 0 | case 3: return "Errors, warnings and info"; |
189 | 2 | case 4: return "All"; |
190 | 2 | } |
191 | 0 | return "???"; |
192 | 2 | } |
193 | | |
194 | | static int *__read_mostly upper_thresh_adj = &xenlog_upper_thresh; |
195 | | static int *__read_mostly lower_thresh_adj = &xenlog_lower_thresh; |
196 | | static const char *__read_mostly thresh_adj = "standard"; |
197 | | |
198 | | static void do_toggle_guest(unsigned char key, struct cpu_user_regs *regs) |
199 | 0 | { |
200 | 0 | if ( upper_thresh_adj == &xenlog_upper_thresh ) |
201 | 0 | { |
202 | 0 | upper_thresh_adj = &xenlog_guest_upper_thresh; |
203 | 0 | lower_thresh_adj = &xenlog_guest_lower_thresh; |
204 | 0 | thresh_adj = "guest"; |
205 | 0 | } |
206 | 0 | else |
207 | 0 | { |
208 | 0 | upper_thresh_adj = &xenlog_upper_thresh; |
209 | 0 | lower_thresh_adj = &xenlog_lower_thresh; |
210 | 0 | thresh_adj = "standard"; |
211 | 0 | } |
212 | 0 | printk("'%c' pressed -> %s log level adjustments enabled\n", |
213 | 0 | key, thresh_adj); |
214 | 0 | } |
215 | | |
216 | | static void do_adj_thresh(unsigned char key) |
217 | 0 | { |
218 | 0 | if ( *upper_thresh_adj < *lower_thresh_adj ) |
219 | 0 | *upper_thresh_adj = *lower_thresh_adj; |
220 | 0 | printk("'%c' pressed -> %s log level: %s (rate limited %s)\n", |
221 | 0 | key, thresh_adj, loglvl_str(*lower_thresh_adj), |
222 | 0 | loglvl_str(*upper_thresh_adj)); |
223 | 0 | } |
224 | | |
225 | | static void do_inc_thresh(unsigned char key, struct cpu_user_regs *regs) |
226 | 0 | { |
227 | 0 | ++*lower_thresh_adj; |
228 | 0 | do_adj_thresh(key); |
229 | 0 | } |
230 | | |
231 | | static void do_dec_thresh(unsigned char key, struct cpu_user_regs *regs) |
232 | 0 | { |
233 | 0 | if ( *lower_thresh_adj ) |
234 | 0 | --*lower_thresh_adj; |
235 | 0 | do_adj_thresh(key); |
236 | 0 | } |
237 | | |
238 | | /* |
239 | | * ******************************************************** |
240 | | * *************** ACCESS TO CONSOLE RING ***************** |
241 | | * ******************************************************** |
242 | | */ |
243 | | |
244 | | static void conring_puts(const char *str) |
245 | 1.02k | { |
246 | 1.02k | char c; |
247 | 1.02k | |
248 | 1.02k | ASSERT(spin_is_locked(&console_lock)); |
249 | 1.02k | |
250 | 16.5k | while ( (c = *str++) != '\0' ) |
251 | 15.5k | conring[CONRING_IDX_MASK(conringp++)] = c; |
252 | 1.02k | |
253 | 1.02k | if ( (uint32_t)(conringp - conringc) > conring_size ) |
254 | 0 | conringc = conringp - conring_size; |
255 | 1.02k | } |
256 | | |
257 | | long read_console_ring(struct xen_sysctl_readconsole *op) |
258 | 0 | { |
259 | 0 | XEN_GUEST_HANDLE_PARAM(char) str; |
260 | 0 | uint32_t idx, len, max, sofar, c, p; |
261 | 0 |
|
262 | 0 | str = guest_handle_cast(op->buffer, char), |
263 | 0 | max = op->count; |
264 | 0 | sofar = 0; |
265 | 0 |
|
266 | 0 | c = read_atomic(&conringc); |
267 | 0 | p = read_atomic(&conringp); |
268 | 0 | if ( op->incremental && |
269 | 0 | (c <= p ? c < op->index && op->index <= p |
270 | 0 | : c < op->index || op->index <= p) ) |
271 | 0 | c = op->index; |
272 | 0 |
|
273 | 0 | while ( (c != p) && (sofar < max) ) |
274 | 0 | { |
275 | 0 | idx = CONRING_IDX_MASK(c); |
276 | 0 | len = p - c; |
277 | 0 | if ( (idx + len) > conring_size ) |
278 | 0 | len = conring_size - idx; |
279 | 0 | if ( (sofar + len) > max ) |
280 | 0 | len = max - sofar; |
281 | 0 | if ( copy_to_guest_offset(str, sofar, &conring[idx], len) ) |
282 | 0 | return -EFAULT; |
283 | 0 | sofar += len; |
284 | 0 | c += len; |
285 | 0 | } |
286 | 0 |
|
287 | 0 | if ( op->clear ) |
288 | 0 | { |
289 | 0 | spin_lock_irq(&console_lock); |
290 | 0 | conringc = p - c > conring_size ? p - conring_size : c; |
291 | 0 | spin_unlock_irq(&console_lock); |
292 | 0 | } |
293 | 0 |
|
294 | 0 | op->count = sofar; |
295 | 0 | op->index = c; |
296 | 0 |
|
297 | 0 | return 0; |
298 | 0 | } |
299 | | |
300 | | |
301 | | /* |
302 | | * ******************************************************* |
303 | | * *************** ACCESS TO SERIAL LINE ***************** |
304 | | * ******************************************************* |
305 | | */ |
306 | | |
307 | | /* Characters received over the serial line are buffered for domain 0. */ |
308 | 0 | #define SERIAL_RX_SIZE 128 |
309 | 0 | #define SERIAL_RX_MASK(_i) ((_i)&(SERIAL_RX_SIZE-1)) |
310 | | static char serial_rx_ring[SERIAL_RX_SIZE]; |
311 | | static unsigned int serial_rx_cons, serial_rx_prod; |
312 | | |
313 | | static void (*serial_steal_fn)(const char *) = early_puts; |
314 | | |
315 | | int console_steal(int handle, void (*fn)(const char *)) |
316 | 0 | { |
317 | 0 | if ( (handle == -1) || (handle != sercon_handle) ) |
318 | 0 | return 0; |
319 | 0 |
|
320 | 0 | if ( serial_steal_fn != NULL ) |
321 | 0 | return -EBUSY; |
322 | 0 |
|
323 | 0 | serial_steal_fn = fn; |
324 | 0 | return 1; |
325 | 0 | } |
326 | | |
327 | | void console_giveback(int id) |
328 | 0 | { |
329 | 0 | if ( id == 1 ) |
330 | 0 | serial_steal_fn = NULL; |
331 | 0 | } |
332 | | |
333 | | static void sercon_puts(const char *s) |
334 | 119k | { |
335 | 119k | if ( serial_steal_fn != NULL ) |
336 | 0 | (*serial_steal_fn)(s); |
337 | 119k | else |
338 | 119k | serial_puts(sercon_handle, s); |
339 | 119k | } |
340 | | |
341 | | static void dump_console_ring_key(unsigned char key) |
342 | 0 | { |
343 | 0 | uint32_t idx, len, sofar, c; |
344 | 0 | unsigned int order; |
345 | 0 | char *buf; |
346 | 0 |
|
347 | 0 | printk("'%c' pressed -> dumping console ring buffer (dmesg)\n", key); |
348 | 0 |
|
349 | 0 | /* create a buffer in which we'll copy the ring in the correct |
350 | 0 | order and NUL terminate */ |
351 | 0 | order = get_order_from_bytes(conring_size + 1); |
352 | 0 | buf = alloc_xenheap_pages(order, 0); |
353 | 0 | if ( buf == NULL ) |
354 | 0 | { |
355 | 0 | printk("unable to allocate memory!\n"); |
356 | 0 | return; |
357 | 0 | } |
358 | 0 |
|
359 | 0 | c = conringc; |
360 | 0 | sofar = 0; |
361 | 0 | while ( (c != conringp) ) |
362 | 0 | { |
363 | 0 | idx = CONRING_IDX_MASK(c); |
364 | 0 | len = conringp - c; |
365 | 0 | if ( (idx + len) > conring_size ) |
366 | 0 | len = conring_size - idx; |
367 | 0 | memcpy(buf + sofar, &conring[idx], len); |
368 | 0 | sofar += len; |
369 | 0 | c += len; |
370 | 0 | } |
371 | 0 | buf[sofar] = '\0'; |
372 | 0 |
|
373 | 0 | sercon_puts(buf); |
374 | 0 | video_puts(buf); |
375 | 0 |
|
376 | 0 | free_xenheap_pages(buf, order); |
377 | 0 | } |
378 | | |
379 | | /* CTRL-<switch_char> switches input direction between Xen and DOM0. */ |
380 | 1 | #define switch_code (opt_conswitch[0]-'a'+1) |
381 | | static int __read_mostly xen_rx = 1; /* FALSE => input passed to domain 0. */ |
382 | | |
383 | | static void switch_serial_input(void) |
384 | 1 | { |
385 | 1 | static char *input_str[2] = { "DOM0", "Xen" }; |
386 | 1 | xen_rx = !xen_rx; |
387 | 1 | printk("*** Serial input -> %s", input_str[xen_rx]); |
388 | 1 | if ( switch_code ) |
389 | 1 | printk(" (type 'CTRL-%c' three times to switch input to %s)", |
390 | 1 | opt_conswitch[0], input_str[!xen_rx]); |
391 | 1 | printk("\n"); |
392 | 1 | } |
393 | | |
394 | | static void __serial_rx(char c, struct cpu_user_regs *regs) |
395 | 0 | { |
396 | 0 | if ( xen_rx ) |
397 | 0 | return handle_keypress(c, regs); |
398 | 0 |
|
399 | 0 | /* Deliver input to guest buffer, unless it is already full. */ |
400 | 0 | if ( (serial_rx_prod-serial_rx_cons) != SERIAL_RX_SIZE ) |
401 | 0 | serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c; |
402 | 0 | /* Always notify the guest: prevents receive path from getting stuck. */ |
403 | 0 | send_global_virq(VIRQ_CONSOLE); |
404 | 0 | } |
405 | | |
406 | | static void serial_rx(char c, struct cpu_user_regs *regs) |
407 | 0 | { |
408 | 0 | static int switch_code_count = 0; |
409 | 0 |
|
410 | 0 | if ( switch_code && (c == switch_code) ) |
411 | 0 | { |
412 | 0 | /* We eat CTRL-<switch_char> in groups of 3 to switch console input. */ |
413 | 0 | if ( ++switch_code_count == 3 ) |
414 | 0 | { |
415 | 0 | switch_serial_input(); |
416 | 0 | switch_code_count = 0; |
417 | 0 | } |
418 | 0 | return; |
419 | 0 | } |
420 | 0 |
|
421 | 0 | for ( ; switch_code_count != 0; switch_code_count-- ) |
422 | 0 | __serial_rx(switch_code, regs); |
423 | 0 |
|
424 | 0 | /* Finally process the just-received character. */ |
425 | 0 | __serial_rx(c, regs); |
426 | 0 | } |
427 | | |
428 | | static void notify_dom0_con_ring(unsigned long unused) |
429 | 131 | { |
430 | 131 | send_global_virq(VIRQ_CON_RING); |
431 | 131 | } |
432 | | static DECLARE_SOFTIRQ_TASKLET(notify_dom0_con_ring_tasklet, |
433 | | notify_dom0_con_ring, 0); |
434 | | |
435 | | static long guest_console_write(XEN_GUEST_HANDLE_PARAM(char) buffer, int count) |
436 | 118k | { |
437 | 118k | char kbuf[128]; |
438 | 118k | int kcount = 0; |
439 | 118k | struct domain *cd = current->domain; |
440 | 118k | |
441 | 237k | while ( count > 0 ) |
442 | 118k | { |
443 | 118k | if ( kcount && hypercall_preempt_check() ) |
444 | 0 | return hypercall_create_continuation( |
445 | 0 | __HYPERVISOR_console_io, "iih", |
446 | 0 | CONSOLEIO_write, count, buffer); |
447 | 118k | |
448 | 118k | kcount = min_t(int, count, sizeof(kbuf)-1); |
449 | 118k | if ( copy_from_guest(kbuf, buffer, kcount) ) |
450 | 0 | return -EFAULT; |
451 | 118k | kbuf[kcount] = '\0'; |
452 | 118k | |
453 | 118k | if ( is_hardware_domain(cd) ) |
454 | 118k | { |
455 | 118k | /* Use direct console output as it could be interactive */ |
456 | 118k | spin_lock_irq(&console_lock); |
457 | 118k | |
458 | 118k | sercon_puts(kbuf); |
459 | 118k | video_puts(kbuf); |
460 | 118k | |
461 | 118k | if ( opt_console_to_ring ) |
462 | 0 | { |
463 | 0 | conring_puts(kbuf); |
464 | 0 | tasklet_schedule(¬ify_dom0_con_ring_tasklet); |
465 | 0 | } |
466 | 118k | |
467 | 118k | spin_unlock_irq(&console_lock); |
468 | 118k | } |
469 | 118k | else |
470 | 0 | { |
471 | 0 | char *kin = kbuf, *kout = kbuf, c; |
472 | 0 |
|
473 | 0 | /* Strip non-printable characters */ |
474 | 0 | for ( ; ; ) |
475 | 0 | { |
476 | 0 | c = *kin++; |
477 | 0 | if ( c == '\0' || c == '\n' ) |
478 | 0 | break; |
479 | 0 | if ( isprint(c) || c == '\t' ) |
480 | 0 | *kout++ = c; |
481 | 0 | } |
482 | 0 | *kout = '\0'; |
483 | 0 | spin_lock(&cd->pbuf_lock); |
484 | 0 | if ( c == '\n' ) |
485 | 0 | { |
486 | 0 | kcount = kin - kbuf; |
487 | 0 | cd->pbuf[cd->pbuf_idx] = '\0'; |
488 | 0 | guest_printk(cd, XENLOG_G_DEBUG "%s%s\n", cd->pbuf, kbuf); |
489 | 0 | cd->pbuf_idx = 0; |
490 | 0 | } |
491 | 0 | else if ( cd->pbuf_idx + kcount < (DOMAIN_PBUF_SIZE - 1) ) |
492 | 0 | { |
493 | 0 | /* buffer the output until a newline */ |
494 | 0 | memcpy(cd->pbuf + cd->pbuf_idx, kbuf, kcount); |
495 | 0 | cd->pbuf_idx += kcount; |
496 | 0 | } |
497 | 0 | else |
498 | 0 | { |
499 | 0 | cd->pbuf[cd->pbuf_idx] = '\0'; |
500 | 0 | guest_printk(cd, XENLOG_G_DEBUG "%s%s\n", cd->pbuf, kbuf); |
501 | 0 | cd->pbuf_idx = 0; |
502 | 0 | } |
503 | 0 | spin_unlock(&cd->pbuf_lock); |
504 | 0 | } |
505 | 118k | |
506 | 118k | guest_handle_add_offset(buffer, kcount); |
507 | 118k | count -= kcount; |
508 | 118k | } |
509 | 118k | |
510 | 118k | return 0; |
511 | 118k | } |
512 | | |
513 | | long do_console_io(int cmd, int count, XEN_GUEST_HANDLE_PARAM(char) buffer) |
514 | 118k | { |
515 | 118k | long rc; |
516 | 118k | unsigned int idx, len; |
517 | 118k | |
518 | 118k | rc = xsm_console_io(XSM_OTHER, current->domain, cmd); |
519 | 118k | if ( rc ) |
520 | 0 | return rc; |
521 | 118k | |
522 | 118k | switch ( cmd ) |
523 | 118k | { |
524 | 118k | case CONSOLEIO_write: |
525 | 118k | rc = guest_console_write(buffer, count); |
526 | 118k | break; |
527 | 0 | case CONSOLEIO_read: |
528 | 0 | rc = 0; |
529 | 0 | while ( (serial_rx_cons != serial_rx_prod) && (rc < count) ) |
530 | 0 | { |
531 | 0 | idx = SERIAL_RX_MASK(serial_rx_cons); |
532 | 0 | len = serial_rx_prod - serial_rx_cons; |
533 | 0 | if ( (idx + len) > SERIAL_RX_SIZE ) |
534 | 0 | len = SERIAL_RX_SIZE - idx; |
535 | 0 | if ( (rc + len) > count ) |
536 | 0 | len = count - rc; |
537 | 0 | if ( copy_to_guest_offset(buffer, rc, &serial_rx_ring[idx], len) ) |
538 | 0 | { |
539 | 0 | rc = -EFAULT; |
540 | 0 | break; |
541 | 0 | } |
542 | 0 | rc += len; |
543 | 0 | serial_rx_cons += len; |
544 | 0 | } |
545 | 0 | break; |
546 | 0 | default: |
547 | 0 | rc = -ENOSYS; |
548 | 0 | break; |
549 | 118k | } |
550 | 118k | |
551 | 118k | return rc; |
552 | 118k | } |
553 | | |
554 | | |
555 | | /* |
556 | | * ***************************************************** |
557 | | * *************** GENERIC CONSOLE I/O ***************** |
558 | | * ***************************************************** |
559 | | */ |
560 | | |
561 | | static bool_t console_locks_busted; |
562 | | |
563 | | static void __putstr(const char *str) |
564 | 1.02k | { |
565 | 1.02k | ASSERT(spin_is_locked(&console_lock)); |
566 | 1.02k | |
567 | 1.02k | sercon_puts(str); |
568 | 1.02k | video_puts(str); |
569 | 1.02k | |
570 | 1.02k | conring_puts(str); |
571 | 1.02k | |
572 | 1.02k | if ( !console_locks_busted ) |
573 | 1.02k | tasklet_schedule(¬ify_dom0_con_ring_tasklet); |
574 | 1.02k | } |
575 | | |
576 | | static int printk_prefix_check(char *p, char **pp) |
577 | 308 | { |
578 | 308 | int loglvl = -1; |
579 | 308 | int upper_thresh = xenlog_upper_thresh; |
580 | 308 | int lower_thresh = xenlog_lower_thresh; |
581 | 308 | |
582 | 463 | while ( (p[0] == '<') && (p[1] != '\0') && (p[2] == '>') ) |
583 | 155 | { |
584 | 155 | switch ( p[1] ) |
585 | 155 | { |
586 | 41 | case 'G': |
587 | 41 | upper_thresh = xenlog_guest_upper_thresh; |
588 | 41 | lower_thresh = xenlog_guest_lower_thresh; |
589 | 41 | if ( loglvl == -1 ) |
590 | 41 | loglvl = XENLOG_GUEST_DEFAULT; |
591 | 41 | break; |
592 | 114 | case '0' ... '3': |
593 | 114 | loglvl = p[1] - '0'; |
594 | 114 | break; |
595 | 155 | } |
596 | 155 | p += 3; |
597 | 155 | } |
598 | 308 | |
599 | 308 | if ( loglvl == -1 ) |
600 | 194 | loglvl = XENLOG_DEFAULT; |
601 | 308 | |
602 | 308 | *pp = p; |
603 | 308 | |
604 | 308 | return ((atomic_read(&print_everything) != 0) || |
605 | 308 | (loglvl < lower_thresh) || |
606 | 0 | ((loglvl < upper_thresh) && printk_ratelimit())); |
607 | 308 | } |
608 | | |
609 | | static int parse_console_timestamps(const char *s) |
610 | 0 | { |
611 | 0 | switch ( parse_bool(s, NULL) ) |
612 | 0 | { |
613 | 0 | case 0: |
614 | 0 | opt_con_timestamp_mode = TSM_NONE; |
615 | 0 | return 0; |
616 | 0 | case 1: |
617 | 0 | opt_con_timestamp_mode = TSM_DATE; |
618 | 0 | return 0; |
619 | 0 | } |
620 | 0 | if ( *s == '\0' || /* Compat for old booleanparam() */ |
621 | 0 | !strcmp(s, "date") ) |
622 | 0 | opt_con_timestamp_mode = TSM_DATE; |
623 | 0 | else if ( !strcmp(s, "datems") ) |
624 | 0 | opt_con_timestamp_mode = TSM_DATE_MS; |
625 | 0 | else if ( !strcmp(s, "boot") ) |
626 | 0 | opt_con_timestamp_mode = TSM_BOOT; |
627 | 0 | else if ( !strcmp(s, "none") ) |
628 | 0 | opt_con_timestamp_mode = TSM_NONE; |
629 | 0 | else |
630 | 0 | return -EINVAL; |
631 | 0 |
|
632 | 0 | return 0; |
633 | 0 | } |
634 | | |
635 | | static void printk_start_of_line(const char *prefix) |
636 | 308 | { |
637 | 308 | struct tm tm; |
638 | 308 | char tstr[32]; |
639 | 308 | uint64_t sec, nsec; |
640 | 308 | |
641 | 308 | __putstr(prefix); |
642 | 308 | |
643 | 308 | switch ( opt_con_timestamp_mode ) |
644 | 308 | { |
645 | 0 | case TSM_DATE: |
646 | 0 | case TSM_DATE_MS: |
647 | 0 | tm = wallclock_time(&nsec); |
648 | 0 |
|
649 | 0 | if ( tm.tm_mday == 0 ) |
650 | 0 | return; |
651 | 0 |
|
652 | 0 | if ( opt_con_timestamp_mode == TSM_DATE ) |
653 | 0 | snprintf(tstr, sizeof(tstr), "[%04u-%02u-%02u %02u:%02u:%02u] ", |
654 | 0 | 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, |
655 | 0 | tm.tm_hour, tm.tm_min, tm.tm_sec); |
656 | 0 | else |
657 | 0 | snprintf(tstr, sizeof(tstr), |
658 | 0 | "[%04u-%02u-%02u %02u:%02u:%02u.%03"PRIu64"] ", |
659 | 0 | 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, |
660 | 0 | tm.tm_hour, tm.tm_min, tm.tm_sec, nsec / 1000000); |
661 | 0 | break; |
662 | 0 |
|
663 | 0 | case TSM_BOOT: |
664 | 0 | sec = NOW(); |
665 | 0 | nsec = do_div(sec, 1000000000); |
666 | 0 |
|
667 | 0 | snprintf(tstr, sizeof(tstr), "[%5"PRIu64".%06"PRIu64"] ", |
668 | 0 | sec, nsec / 1000); |
669 | 0 | break; |
670 | 0 |
|
671 | 308 | case TSM_NONE: |
672 | 308 | default: |
673 | 308 | return; |
674 | 308 | } |
675 | 308 | |
676 | 0 | __putstr(tstr); |
677 | 0 | } |
678 | | |
679 | | static void vprintk_common(const char *prefix, const char *fmt, va_list args) |
680 | 401 | { |
681 | 401 | struct vps { |
682 | 401 | bool_t continued, do_print; |
683 | 401 | } *state; |
684 | 401 | static DEFINE_PER_CPU(struct vps, state); |
685 | 401 | static char buf[1024]; |
686 | 401 | char *p, *q; |
687 | 401 | unsigned long flags; |
688 | 401 | |
689 | 401 | /* console_lock can be acquired recursively from __printk_ratelimit(). */ |
690 | 401 | local_irq_save(flags); |
691 | 401 | spin_lock_recursive(&console_lock); |
692 | 401 | state = &this_cpu(state); |
693 | 401 | |
694 | 401 | (void)vsnprintf(buf, sizeof(buf), fmt, args); |
695 | 401 | |
696 | 401 | p = buf; |
697 | 401 | |
698 | 709 | while ( (q = strchr(p, '\n')) != NULL ) |
699 | 308 | { |
700 | 308 | *q = '\0'; |
701 | 308 | if ( !state->continued ) |
702 | 259 | state->do_print = printk_prefix_check(p, &p); |
703 | 308 | if ( state->do_print ) |
704 | 308 | { |
705 | 308 | if ( !state->continued ) |
706 | 259 | printk_start_of_line(prefix); |
707 | 308 | __putstr(p); |
708 | 308 | __putstr("\n"); |
709 | 308 | } |
710 | 308 | state->continued = 0; |
711 | 308 | p = q + 1; |
712 | 308 | } |
713 | 401 | |
714 | 401 | if ( *p != '\0' ) |
715 | 98 | { |
716 | 98 | if ( !state->continued ) |
717 | 49 | state->do_print = printk_prefix_check(p, &p); |
718 | 98 | if ( state->do_print ) |
719 | 98 | { |
720 | 98 | if ( !state->continued ) |
721 | 49 | printk_start_of_line(prefix); |
722 | 98 | __putstr(p); |
723 | 98 | } |
724 | 98 | state->continued = 1; |
725 | 98 | } |
726 | 401 | |
727 | 401 | spin_unlock_recursive(&console_lock); |
728 | 401 | local_irq_restore(flags); |
729 | 401 | } |
730 | | |
731 | | void printk(const char *fmt, ...) |
732 | 401 | { |
733 | 401 | va_list args; |
734 | 401 | va_start(args, fmt); |
735 | 401 | vprintk_common("(XEN) ", fmt, args); |
736 | 401 | va_end(args); |
737 | 401 | } |
738 | | |
739 | | void guest_printk(const struct domain *d, const char *fmt, ...) |
740 | 0 | { |
741 | 0 | va_list args; |
742 | 0 | char prefix[16]; |
743 | 0 |
|
744 | 0 | snprintf(prefix, sizeof(prefix), "(d%d) ", d->domain_id); |
745 | 0 |
|
746 | 0 | va_start(args, fmt); |
747 | 0 | vprintk_common(prefix, fmt, args); |
748 | 0 | va_end(args); |
749 | 0 | } |
750 | | |
751 | | void __init console_init_preirq(void) |
752 | 1 | { |
753 | 1 | char *p; |
754 | 1 | int sh; |
755 | 1 | |
756 | 1 | serial_init_preirq(); |
757 | 1 | |
758 | 1 | /* Where should console output go? */ |
759 | 3 | for ( p = opt_console; p != NULL; p = strchr(p, ',') ) |
760 | 2 | { |
761 | 2 | if ( *p == ',' ) |
762 | 1 | p++; |
763 | 2 | if ( !strncmp(p, "vga", 3) ) |
764 | 1 | video_init(); |
765 | 1 | else if ( !strncmp(p, "none", 4) ) |
766 | 0 | continue; |
767 | 1 | else if ( (sh = serial_parse_handle(p)) >= 0 ) |
768 | 1 | { |
769 | 1 | sercon_handle = sh; |
770 | 1 | serial_steal_fn = NULL; |
771 | 1 | } |
772 | 1 | else |
773 | 0 | { |
774 | 0 | char *q = strchr(p, ','); |
775 | 0 | if ( q != NULL ) |
776 | 0 | *q = '\0'; |
777 | 0 | printk("Bad console= option '%s'\n", p); |
778 | 0 | if ( q != NULL ) |
779 | 0 | *q = ','; |
780 | 0 | } |
781 | 2 | } |
782 | 1 | |
783 | 1 | serial_set_rx_handler(sercon_handle, serial_rx); |
784 | 1 | |
785 | 1 | /* HELLO WORLD --- start-of-day banner text. */ |
786 | 1 | spin_lock(&console_lock); |
787 | 1 | __putstr(xen_banner()); |
788 | 1 | spin_unlock(&console_lock); |
789 | 1 | printk("Xen version %d.%d%s (%s@%s) (%s) debug=%c " gcov_string " %s\n", |
790 | 1 | xen_major_version(), xen_minor_version(), xen_extra_version(), |
791 | 1 | xen_compile_by(), xen_compile_domain(), |
792 | 1 | xen_compiler(), debug_build() ? 'y' : 'n', xen_compile_date()); |
793 | 1 | printk("Latest ChangeSet: %s\n", xen_changeset()); |
794 | 1 | |
795 | 1 | if ( opt_sync_console ) |
796 | 1 | { |
797 | 1 | serial_start_sync(sercon_handle); |
798 | 1 | add_taint(TAINT_SYNC_CONSOLE); |
799 | 1 | printk("Console output is synchronous.\n"); |
800 | 1 | warning_add(warning_sync_console); |
801 | 1 | } |
802 | 1 | } |
803 | | |
804 | | void __init console_init_ring(void) |
805 | 2 | { |
806 | 2 | char *ring; |
807 | 2 | unsigned int i, order, memflags; |
808 | 2 | unsigned long flags; |
809 | 2 | |
810 | 2 | if ( !opt_conring_size ) |
811 | 1 | return; |
812 | 2 | |
813 | 1 | order = get_order_from_bytes(max(opt_conring_size, conring_size)); |
814 | 1 | memflags = MEMF_bits(crashinfo_maxaddr_bits); |
815 | 1 | while ( (ring = alloc_xenheap_pages(order, memflags)) == NULL ) |
816 | 0 | { |
817 | 0 | BUG_ON(order == 0); |
818 | 0 | order--; |
819 | 0 | } |
820 | 1 | opt_conring_size = PAGE_SIZE << order; |
821 | 1 | |
822 | 1 | spin_lock_irqsave(&console_lock, flags); |
823 | 8.18k | for ( i = conringc ; i != conringp; i++ ) |
824 | 8.18k | ring[i & (opt_conring_size - 1)] = conring[i & (conring_size - 1)]; |
825 | 1 | conring = ring; |
826 | 1 | smp_wmb(); /* Allow users of console_force_unlock() to see larger buffer. */ |
827 | 1 | conring_size = opt_conring_size; |
828 | 1 | spin_unlock_irqrestore(&console_lock, flags); |
829 | 1 | |
830 | 1 | printk("Allocated console ring of %u KiB.\n", opt_conring_size >> 10); |
831 | 1 | } |
832 | | |
833 | | void __init console_init_postirq(void) |
834 | 1 | { |
835 | 1 | serial_init_postirq(); |
836 | 1 | |
837 | 1 | if ( conring != _conring ) |
838 | 0 | return; |
839 | 1 | |
840 | 1 | if ( !opt_conring_size ) |
841 | 1 | opt_conring_size = num_present_cpus() << (9 + xenlog_lower_thresh); |
842 | 1 | |
843 | 1 | console_init_ring(); |
844 | 1 | } |
845 | | |
846 | | void __init console_endboot(void) |
847 | 1 | { |
848 | 1 | printk("Std. Loglevel: %s", loglvl_str(xenlog_lower_thresh)); |
849 | 1 | if ( xenlog_upper_thresh != xenlog_lower_thresh ) |
850 | 0 | printk(" (Rate-limited: %s)", loglvl_str(xenlog_upper_thresh)); |
851 | 1 | printk("\nGuest Loglevel: %s", loglvl_str(xenlog_guest_lower_thresh)); |
852 | 1 | if ( xenlog_guest_upper_thresh != xenlog_guest_lower_thresh ) |
853 | 0 | printk(" (Rate-limited: %s)", loglvl_str(xenlog_guest_upper_thresh)); |
854 | 1 | printk("\n"); |
855 | 1 | |
856 | 1 | warning_print(); |
857 | 1 | |
858 | 1 | video_endboot(); |
859 | 1 | |
860 | 1 | /* |
861 | 1 | * If user specifies so, we fool the switch routine to redirect input |
862 | 1 | * straight back to Xen. I use this convoluted method so we still print |
863 | 1 | * a useful 'how to switch' message. |
864 | 1 | */ |
865 | 1 | if ( opt_conswitch[1] == 'x' ) |
866 | 0 | xen_rx = !xen_rx; |
867 | 1 | |
868 | 1 | register_keyhandler('w', dump_console_ring_key, |
869 | 1 | "synchronously dump console ring buffer (dmesg)", 0); |
870 | 1 | register_irq_keyhandler('+', &do_inc_thresh, |
871 | 1 | "increase log level threshold", 0); |
872 | 1 | register_irq_keyhandler('-', &do_dec_thresh, |
873 | 1 | "decrease log level threshold", 0); |
874 | 1 | register_irq_keyhandler('G', &do_toggle_guest, |
875 | 1 | "toggle host/guest log level adjustment", 0); |
876 | 1 | |
877 | 1 | /* Serial input is directed to DOM0 by default. */ |
878 | 1 | switch_serial_input(); |
879 | 1 | } |
880 | | |
881 | | int __init console_has(const char *device) |
882 | 2 | { |
883 | 2 | char *p; |
884 | 2 | |
885 | 4 | for ( p = opt_console; p != NULL; p = strchr(p, ',') ) |
886 | 3 | { |
887 | 3 | if ( *p == ',' ) |
888 | 1 | p++; |
889 | 3 | if ( strncmp(p, device, strlen(device)) == 0 ) |
890 | 1 | return 1; |
891 | 3 | } |
892 | 2 | |
893 | 1 | return 0; |
894 | 2 | } |
895 | | |
896 | | void console_start_log_everything(void) |
897 | 0 | { |
898 | 0 | serial_start_log_everything(sercon_handle); |
899 | 0 | atomic_inc(&print_everything); |
900 | 0 | } |
901 | | |
902 | | void console_end_log_everything(void) |
903 | 0 | { |
904 | 0 | serial_end_log_everything(sercon_handle); |
905 | 0 | atomic_dec(&print_everything); |
906 | 0 | } |
907 | | |
908 | | unsigned long console_lock_recursive_irqsave(void) |
909 | 0 | { |
910 | 0 | unsigned long flags; |
911 | 0 |
|
912 | 0 | local_irq_save(flags); |
913 | 0 | spin_lock_recursive(&console_lock); |
914 | 0 |
|
915 | 0 | return flags; |
916 | 0 | } |
917 | | |
918 | | void console_unlock_recursive_irqrestore(unsigned long flags) |
919 | 0 | { |
920 | 0 | spin_unlock_recursive(&console_lock); |
921 | 0 | local_irq_restore(flags); |
922 | 0 | } |
923 | | |
924 | | void console_force_unlock(void) |
925 | 0 | { |
926 | 0 | watchdog_disable(); |
927 | 0 | spin_lock_init(&console_lock); |
928 | 0 | serial_force_unlock(sercon_handle); |
929 | 0 | console_locks_busted = 1; |
930 | 0 | console_start_sync(); |
931 | 0 | } |
932 | | |
933 | | void console_start_sync(void) |
934 | 0 | { |
935 | 0 | atomic_inc(&print_everything); |
936 | 0 | serial_start_sync(sercon_handle); |
937 | 0 | } |
938 | | |
939 | | void console_end_sync(void) |
940 | 0 | { |
941 | 0 | serial_end_sync(sercon_handle); |
942 | 0 | atomic_dec(&print_everything); |
943 | 0 | } |
944 | | |
945 | | /* |
946 | | * printk rate limiting, lifted from Linux. |
947 | | * |
948 | | * This enforces a rate limit: not more than one kernel message |
949 | | * every printk_ratelimit_ms (millisecs). |
950 | | */ |
951 | | int __printk_ratelimit(int ratelimit_ms, int ratelimit_burst) |
952 | 0 | { |
953 | 0 | static DEFINE_SPINLOCK(ratelimit_lock); |
954 | 0 | static unsigned long toks = 10 * 5 * 1000; |
955 | 0 | static unsigned long last_msg; |
956 | 0 | static int missed; |
957 | 0 | unsigned long flags; |
958 | 0 | unsigned long long now = NOW(); /* ns */ |
959 | 0 | unsigned long ms; |
960 | 0 |
|
961 | 0 | do_div(now, 1000000); |
962 | 0 | ms = (unsigned long)now; |
963 | 0 |
|
964 | 0 | spin_lock_irqsave(&ratelimit_lock, flags); |
965 | 0 | toks += ms - last_msg; |
966 | 0 | last_msg = ms; |
967 | 0 | if ( toks > (ratelimit_burst * ratelimit_ms)) |
968 | 0 | toks = ratelimit_burst * ratelimit_ms; |
969 | 0 | if ( toks >= ratelimit_ms ) |
970 | 0 | { |
971 | 0 | int lost = missed; |
972 | 0 | missed = 0; |
973 | 0 | toks -= ratelimit_ms; |
974 | 0 | spin_unlock(&ratelimit_lock); |
975 | 0 | if ( lost ) |
976 | 0 | { |
977 | 0 | char lost_str[8]; |
978 | 0 | snprintf(lost_str, sizeof(lost_str), "%d", lost); |
979 | 0 | /* console_lock may already be acquired by printk(). */ |
980 | 0 | spin_lock_recursive(&console_lock); |
981 | 0 | printk_start_of_line("(XEN) "); |
982 | 0 | __putstr("printk: "); |
983 | 0 | __putstr(lost_str); |
984 | 0 | __putstr(" messages suppressed.\n"); |
985 | 0 | spin_unlock_recursive(&console_lock); |
986 | 0 | } |
987 | 0 | local_irq_restore(flags); |
988 | 0 | return 1; |
989 | 0 | } |
990 | 0 | missed++; |
991 | 0 | spin_unlock_irqrestore(&ratelimit_lock, flags); |
992 | 0 | return 0; |
993 | 0 | } |
994 | | |
995 | | /* minimum time in ms between messages */ |
996 | | static int __read_mostly printk_ratelimit_ms = 5 * 1000; |
997 | | |
998 | | /* number of messages we send before ratelimiting */ |
999 | | static int __read_mostly printk_ratelimit_burst = 10; |
1000 | | |
1001 | | int printk_ratelimit(void) |
1002 | 0 | { |
1003 | 0 | return __printk_ratelimit(printk_ratelimit_ms, printk_ratelimit_burst); |
1004 | 0 | } |
1005 | | |
1006 | | /* |
1007 | | * ************************************************************** |
1008 | | * *************** Serial console ring buffer ******************* |
1009 | | * ************************************************************** |
1010 | | */ |
1011 | | |
1012 | | #ifdef DEBUG_TRACE_DUMP |
1013 | | |
1014 | | /* Send output direct to console, or buffer it? */ |
1015 | | static volatile int debugtrace_send_to_console; |
1016 | | |
1017 | | static char *debugtrace_buf; /* Debug-trace buffer */ |
1018 | | static unsigned int debugtrace_prd; /* Producer index */ |
1019 | | static unsigned int debugtrace_kilobytes = 128, debugtrace_bytes; |
1020 | | static unsigned int debugtrace_used; |
1021 | | static DEFINE_SPINLOCK(debugtrace_lock); |
1022 | | integer_param("debugtrace", debugtrace_kilobytes); |
1023 | | |
1024 | | static void debugtrace_dump_worker(void) |
1025 | | { |
1026 | | if ( (debugtrace_bytes == 0) || !debugtrace_used ) |
1027 | | return; |
1028 | | |
1029 | | printk("debugtrace_dump() starting\n"); |
1030 | | |
1031 | | /* Print oldest portion of the ring. */ |
1032 | | ASSERT(debugtrace_buf[debugtrace_bytes - 1] == 0); |
1033 | | sercon_puts(&debugtrace_buf[debugtrace_prd]); |
1034 | | |
1035 | | /* Print youngest portion of the ring. */ |
1036 | | debugtrace_buf[debugtrace_prd] = '\0'; |
1037 | | sercon_puts(&debugtrace_buf[0]); |
1038 | | |
1039 | | memset(debugtrace_buf, '\0', debugtrace_bytes); |
1040 | | |
1041 | | printk("debugtrace_dump() finished\n"); |
1042 | | } |
1043 | | |
1044 | | static void debugtrace_toggle(void) |
1045 | | { |
1046 | | unsigned long flags; |
1047 | | |
1048 | | watchdog_disable(); |
1049 | | spin_lock_irqsave(&debugtrace_lock, flags); |
1050 | | |
1051 | | /* |
1052 | | * Dump the buffer *before* toggling, in case the act of dumping the |
1053 | | * buffer itself causes more printk() invocations. |
1054 | | */ |
1055 | | printk("debugtrace_printk now writing to %s.\n", |
1056 | | !debugtrace_send_to_console ? "console": "buffer"); |
1057 | | if ( !debugtrace_send_to_console ) |
1058 | | debugtrace_dump_worker(); |
1059 | | |
1060 | | debugtrace_send_to_console = !debugtrace_send_to_console; |
1061 | | |
1062 | | spin_unlock_irqrestore(&debugtrace_lock, flags); |
1063 | | watchdog_enable(); |
1064 | | |
1065 | | } |
1066 | | |
1067 | | void debugtrace_dump(void) |
1068 | | { |
1069 | | unsigned long flags; |
1070 | | |
1071 | | watchdog_disable(); |
1072 | | spin_lock_irqsave(&debugtrace_lock, flags); |
1073 | | |
1074 | | debugtrace_dump_worker(); |
1075 | | |
1076 | | spin_unlock_irqrestore(&debugtrace_lock, flags); |
1077 | | watchdog_enable(); |
1078 | | } |
1079 | | |
1080 | | void debugtrace_printk(const char *fmt, ...) |
1081 | | { |
1082 | | static char buf[1024]; |
1083 | | static u32 count; |
1084 | | |
1085 | | va_list args; |
1086 | | char *p; |
1087 | | unsigned long flags; |
1088 | | |
1089 | | if ( debugtrace_bytes == 0 ) |
1090 | | return; |
1091 | | |
1092 | | debugtrace_used = 1; |
1093 | | |
1094 | | spin_lock_irqsave(&debugtrace_lock, flags); |
1095 | | |
1096 | | ASSERT(debugtrace_buf[debugtrace_bytes - 1] == 0); |
1097 | | |
1098 | | snprintf(buf, sizeof(buf), "%u ", ++count); |
1099 | | |
1100 | | va_start(args, fmt); |
1101 | | (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, args); |
1102 | | va_end(args); |
1103 | | |
1104 | | if ( debugtrace_send_to_console ) |
1105 | | { |
1106 | | serial_puts(sercon_handle, buf); |
1107 | | } |
1108 | | else |
1109 | | { |
1110 | | for ( p = buf; *p != '\0'; p++ ) |
1111 | | { |
1112 | | debugtrace_buf[debugtrace_prd++] = *p; |
1113 | | /* Always leave a nul byte at the end of the buffer. */ |
1114 | | if ( debugtrace_prd == (debugtrace_bytes - 1) ) |
1115 | | debugtrace_prd = 0; |
1116 | | } |
1117 | | } |
1118 | | |
1119 | | spin_unlock_irqrestore(&debugtrace_lock, flags); |
1120 | | } |
1121 | | |
1122 | | static void debugtrace_key(unsigned char key) |
1123 | | { |
1124 | | debugtrace_toggle(); |
1125 | | } |
1126 | | |
1127 | | static int __init debugtrace_init(void) |
1128 | | { |
1129 | | int order; |
1130 | | unsigned int kbytes, bytes; |
1131 | | |
1132 | | /* Round size down to next power of two. */ |
1133 | | while ( (kbytes = (debugtrace_kilobytes & (debugtrace_kilobytes-1))) != 0 ) |
1134 | | debugtrace_kilobytes = kbytes; |
1135 | | |
1136 | | bytes = debugtrace_kilobytes << 10; |
1137 | | if ( bytes == 0 ) |
1138 | | return 0; |
1139 | | |
1140 | | order = get_order_from_bytes(bytes); |
1141 | | debugtrace_buf = alloc_xenheap_pages(order, 0); |
1142 | | ASSERT(debugtrace_buf != NULL); |
1143 | | |
1144 | | memset(debugtrace_buf, '\0', bytes); |
1145 | | |
1146 | | debugtrace_bytes = bytes; |
1147 | | |
1148 | | register_keyhandler('T', debugtrace_key, |
1149 | | "toggle debugtrace to console/buffer", 0); |
1150 | | |
1151 | | return 0; |
1152 | | } |
1153 | | __initcall(debugtrace_init); |
1154 | | |
1155 | | #endif /* !NDEBUG */ |
1156 | | |
1157 | | |
1158 | | /* |
1159 | | * ************************************************************** |
1160 | | * *************** Debugging/tracing/error-report *************** |
1161 | | * ************************************************************** |
1162 | | */ |
1163 | | |
1164 | | void panic(const char *fmt, ...) |
1165 | 0 | { |
1166 | 0 | va_list args; |
1167 | 0 | unsigned long flags; |
1168 | 0 | static DEFINE_SPINLOCK(lock); |
1169 | 0 | static char buf[128]; |
1170 | 0 | |
1171 | 0 | debugtrace_dump(); |
1172 | 0 |
|
1173 | 0 | /* Protects buf[] and ensure multi-line message prints atomically. */ |
1174 | 0 | spin_lock_irqsave(&lock, flags); |
1175 | 0 |
|
1176 | 0 | va_start(args, fmt); |
1177 | 0 | (void)vsnprintf(buf, sizeof(buf), fmt, args); |
1178 | 0 | va_end(args); |
1179 | 0 |
|
1180 | 0 | console_start_sync(); |
1181 | 0 | printk("\n****************************************\n"); |
1182 | 0 | printk("Panic on CPU %d:\n", smp_processor_id()); |
1183 | 0 | printk("%s\n", buf); |
1184 | 0 | printk("****************************************\n\n"); |
1185 | 0 | if ( opt_noreboot ) |
1186 | 0 | printk("Manual reset required ('noreboot' specified)\n"); |
1187 | 0 | else |
1188 | 0 | printk("Reboot in five seconds...\n"); |
1189 | 0 |
|
1190 | 0 | spin_unlock_irqrestore(&lock, flags); |
1191 | 0 |
|
1192 | 0 | debugger_trap_immediate(); |
1193 | 0 |
|
1194 | 0 | #ifdef CONFIG_KEXEC |
1195 | 0 | kexec_crash(); |
1196 | 0 | #endif |
1197 | 0 |
|
1198 | 0 | if ( opt_noreboot ) |
1199 | 0 | machine_halt(); |
1200 | 0 | else |
1201 | 0 | machine_restart(5000); |
1202 | 0 | } |
1203 | | |
1204 | | /* |
1205 | | * ************************************************************** |
1206 | | * ****************** Console suspend/resume ******************** |
1207 | | * ************************************************************** |
1208 | | */ |
1209 | | |
1210 | 0 | static void suspend_steal_fn(const char *str) { } |
1211 | | static int suspend_steal_id; |
1212 | | |
1213 | | int console_suspend(void) |
1214 | 0 | { |
1215 | 0 | suspend_steal_id = console_steal(sercon_handle, suspend_steal_fn); |
1216 | 0 | serial_suspend(); |
1217 | 0 | return 0; |
1218 | 0 | } |
1219 | | |
1220 | | int console_resume(void) |
1221 | 0 | { |
1222 | 0 | serial_resume(); |
1223 | 0 | console_giveback(suspend_steal_id); |
1224 | 0 | return 0; |
1225 | 0 | } |
1226 | | |
1227 | | /* |
1228 | | * Local variables: |
1229 | | * mode: C |
1230 | | * c-file-style: "BSD" |
1231 | | * c-basic-offset: 4 |
1232 | | * tab-width: 4 |
1233 | | * indent-tabs-mode: nil |
1234 | | * End: |
1235 | | */ |
1236 | | |