[PATCH] uml: capture printk output for mconsole stack
[deliverable/linux.git] / arch / um / drivers / mconsole_kern.c
CommitLineData
1da177e4
LT
1/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
3 * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com)
4 * Licensed under the GPL
5 */
6
7#include "linux/kernel.h"
8#include "linux/slab.h"
9#include "linux/init.h"
10#include "linux/notifier.h"
11#include "linux/reboot.h"
12#include "linux/utsname.h"
13#include "linux/ctype.h"
14#include "linux/interrupt.h"
15#include "linux/sysrq.h"
16#include "linux/workqueue.h"
17#include "linux/module.h"
18#include "linux/file.h"
19#include "linux/fs.h"
20#include "linux/namei.h"
21#include "linux/proc_fs.h"
22#include "linux/syscalls.h"
6f517d3f 23#include "linux/console.h"
1da177e4
LT
24#include "asm/irq.h"
25#include "asm/uaccess.h"
26#include "user_util.h"
27#include "kern_util.h"
28#include "kern.h"
29#include "mconsole.h"
30#include "mconsole_kern.h"
31#include "irq_user.h"
32#include "init.h"
33#include "os.h"
34#include "umid.h"
35#include "irq_kern.h"
3eddddcf 36#include "choose-mode.h"
1da177e4 37
d50084a2 38static int do_unlink_socket(struct notifier_block *notifier,
1da177e4
LT
39 unsigned long what, void *data)
40{
41 return(mconsole_unlink_socket());
42}
43
44
45static struct notifier_block reboot_notifier = {
46 .notifier_call = do_unlink_socket,
47 .priority = 0,
48};
49
d50084a2 50/* Safe without explicit locking for now. Tasklets provide their own
1da177e4
LT
51 * locking, and the interrupt handler is safe because it can't interrupt
52 * itself and it can only happen on CPU 0.
53 */
54
9010772c 55static LIST_HEAD(mc_requests);
1da177e4
LT
56
57static void mc_work_proc(void *unused)
58{
59 struct mconsole_entry *req;
60 unsigned long flags;
61
62 while(!list_empty(&mc_requests)){
63 local_save_flags(flags);
d50084a2 64 req = list_entry(mc_requests.next, struct mconsole_entry,
1da177e4
LT
65 list);
66 list_del(&req->list);
67 local_irq_restore(flags);
68 req->request.cmd->handler(&req->request);
69 kfree(req);
70 }
71}
72
9010772c 73static DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
1da177e4
LT
74
75static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
76 struct pt_regs *regs)
77{
78 /* long to avoid size mismatch warnings from gcc */
79 long fd;
80 struct mconsole_entry *new;
81 struct mc_request req;
82
83 fd = (long) dev_id;
84 while (mconsole_get_request(fd, &req)){
85 if(req.cmd->context == MCONSOLE_INTR)
86 (*req.cmd->handler)(&req);
87 else {
88 new = kmalloc(sizeof(*new), GFP_ATOMIC);
89 if(new == NULL)
90 mconsole_reply(&req, "Out of memory", 1, 0);
91 else {
92 new->request = req;
93 list_add(&new->list, &mc_requests);
94 }
95 }
96 }
97 if(!list_empty(&mc_requests))
98 schedule_work(&mconsole_work);
99 reactivate_fd(fd, MCONSOLE_IRQ);
100 return(IRQ_HANDLED);
101}
102
103void mconsole_version(struct mc_request *req)
104{
105 char version[256];
106
d50084a2
JD
107 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
108 system_utsname.nodename, system_utsname.release,
1da177e4
LT
109 system_utsname.version, system_utsname.machine);
110 mconsole_reply(req, version, 0, 0);
111}
112
113void mconsole_log(struct mc_request *req)
114{
115 int len;
116 char *ptr = req->request.data;
117
118 ptr += strlen("log ");
119
120 len = req->len - (ptr - req->request.data);
121 printk("%.*s", len, ptr);
122 mconsole_reply(req, "", 0, 0);
123}
124
125/* This is a more convoluted version of mconsole_proc, which has some stability
126 * problems; however, we need it fixed, because it is expected that UML users
127 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
128 * show the real procfs content, not the ones from hppfs.*/
129#if 0
130void mconsole_proc(struct mc_request *req)
131{
132 struct nameidata nd;
133 struct file_system_type *proc;
134 struct super_block *super;
135 struct file *file;
136 int n, err;
137 char *ptr = req->request.data, *buf;
138
139 ptr += strlen("proc");
140 while(isspace(*ptr)) ptr++;
141
142 proc = get_fs_type("proc");
143 if(proc == NULL){
144 mconsole_reply(req, "procfs not registered", 1, 0);
145 goto out;
146 }
147
148 super = (*proc->get_sb)(proc, 0, NULL, NULL);
149 put_filesystem(proc);
150 if(super == NULL){
151 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
152 goto out;
153 }
154 up_write(&super->s_umount);
155
156 nd.dentry = super->s_root;
157 nd.mnt = NULL;
158 nd.flags = O_RDONLY + 1;
159 nd.last_type = LAST_ROOT;
160
161 /* START: it was experienced that the stability problems are closed
162 * if commenting out these two calls + the below read cycle. To
163 * make UML crash again, it was enough to readd either one.*/
164 err = link_path_walk(ptr, &nd);
165 if(err){
166 mconsole_reply(req, "Failed to look up file", 1, 0);
167 goto out_kill;
168 }
169
170 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
171 if(IS_ERR(file)){
172 mconsole_reply(req, "Failed to open file", 1, 0);
173 goto out_kill;
174 }
175 /*END*/
176
177 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
178 if(buf == NULL){
179 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
180 goto out_fput;
181 }
182
183 if((file->f_op != NULL) && (file->f_op->read != NULL)){
184 do {
185 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
186 &file->f_pos);
187 if(n >= 0){
188 buf[n] = '\0';
189 mconsole_reply(req, buf, 0, (n > 0));
190 }
191 else {
192 mconsole_reply(req, "Read of file failed",
193 1, 0);
194 goto out_free;
195 }
196 } while(n > 0);
197 }
198 else mconsole_reply(req, "", 0, 0);
199
200 out_free:
201 kfree(buf);
202 out_fput:
203 fput(file);
204 out_kill:
205 deactivate_super(super);
206 out: ;
207}
208#endif
209
210void mconsole_proc(struct mc_request *req)
211{
212 char path[64];
213 char *buf;
214 int len;
215 int fd;
216 int first_chunk = 1;
217 char *ptr = req->request.data;
218
219 ptr += strlen("proc");
220 while(isspace(*ptr)) ptr++;
221 snprintf(path, sizeof(path), "/proc/%s", ptr);
222
223 fd = sys_open(path, 0, 0);
224 if (fd < 0) {
225 mconsole_reply(req, "Failed to open file", 1, 0);
226 printk("open %s: %d\n",path,fd);
227 goto out;
228 }
229
230 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
231 if(buf == NULL){
232 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
233 goto out_close;
234 }
235
236 for (;;) {
237 len = sys_read(fd, buf, PAGE_SIZE-1);
238 if (len < 0) {
239 mconsole_reply(req, "Read of file failed", 1, 0);
240 goto out_free;
241 }
242 /*Begin the file content on his own line.*/
243 if (first_chunk) {
244 mconsole_reply(req, "\n", 0, 1);
245 first_chunk = 0;
246 }
247 if (len == PAGE_SIZE-1) {
248 buf[len] = '\0';
249 mconsole_reply(req, buf, 0, 1);
250 } else {
251 buf[len] = '\0';
252 mconsole_reply(req, buf, 0, 0);
253 break;
254 }
255 }
256
257 out_free:
258 kfree(buf);
259 out_close:
260 sys_close(fd);
261 out:
262 /* nothing */;
263}
264
265#define UML_MCONSOLE_HELPTEXT \
266"Commands: \n\
267 version - Get kernel version \n\
268 help - Print this message \n\
269 halt - Halt UML \n\
270 reboot - Reboot UML \n\
271 config <dev>=<config> - Add a new device to UML; \n\
272 same syntax as command line \n\
273 config <dev> - Query the configuration of a device \n\
274 remove <dev> - Remove a device from UML \n\
275 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
276 cad - invoke the Ctl-Alt-Del handler \n\
277 stop - pause the UML; it will do nothing until it receives a 'go' \n\
278 go - continue the UML after a 'stop' \n\
279 log <string> - make UML enter <string> into the kernel log\n\
280 proc <file> - returns the contents of the UML's /proc/<file>\n\
3eddddcf 281 stack <pid> - returns the stack of the specified pid\n\
1da177e4
LT
282"
283
284void mconsole_help(struct mc_request *req)
285{
286 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
287}
288
289void mconsole_halt(struct mc_request *req)
290{
291 mconsole_reply(req, "", 0, 0);
292 machine_halt();
293}
294
295void mconsole_reboot(struct mc_request *req)
296{
297 mconsole_reply(req, "", 0, 0);
298 machine_restart(NULL);
299}
300
301extern void ctrl_alt_del(void);
302
303void mconsole_cad(struct mc_request *req)
304{
305 mconsole_reply(req, "", 0, 0);
306 ctrl_alt_del();
307}
308
309void mconsole_go(struct mc_request *req)
310{
311 mconsole_reply(req, "Not stopped", 1, 0);
312}
313
314void mconsole_stop(struct mc_request *req)
315{
316 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
317 os_set_fd_block(req->originating_fd, 1);
318 mconsole_reply(req, "", 0, 0);
319 while(mconsole_get_request(req->originating_fd, req)){
320 if(req->cmd->handler == mconsole_go) break;
321 (*req->cmd->handler)(req);
322 }
323 os_set_fd_block(req->originating_fd, 0);
324 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
325 mconsole_reply(req, "", 0, 0);
326}
327
328/* This list is populated by __initcall routines. */
329
330LIST_HEAD(mconsole_devices);
331
332void mconsole_register_dev(struct mc_device *new)
333{
334 list_add(&new->list, &mconsole_devices);
335}
336
337static struct mc_device *mconsole_find_dev(char *name)
338{
339 struct list_head *ele;
340 struct mc_device *dev;
341
342 list_for_each(ele, &mconsole_devices){
343 dev = list_entry(ele, struct mc_device, list);
344 if(!strncmp(name, dev->name, strlen(dev->name)))
345 return(dev);
346 }
347 return(NULL);
348}
349
350#define CONFIG_BUF_SIZE 64
351
d50084a2 352static void mconsole_get_config(int (*get_config)(char *, char *, int,
1da177e4
LT
353 char **),
354 struct mc_request *req, char *name)
355{
356 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
357 int n, size;
358
359 if(get_config == NULL){
360 mconsole_reply(req, "No get_config routine defined", 1, 0);
361 return;
362 }
363
364 error = NULL;
365 size = sizeof(default_buf)/sizeof(default_buf[0]);
366 buf = default_buf;
367
368 while(1){
369 n = (*get_config)(name, buf, size, &error);
370 if(error != NULL){
371 mconsole_reply(req, error, 1, 0);
372 goto out;
373 }
374
375 if(n <= size){
376 mconsole_reply(req, buf, 0, 0);
377 goto out;
378 }
379
380 if(buf != default_buf)
381 kfree(buf);
382
383 size = n;
384 buf = kmalloc(size, GFP_KERNEL);
385 if(buf == NULL){
386 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
387 return;
388 }
389 }
390 out:
391 if(buf != default_buf)
392 kfree(buf);
1da177e4
LT
393}
394
395void mconsole_config(struct mc_request *req)
396{
397 struct mc_device *dev;
398 char *ptr = req->request.data, *name;
399 int err;
400
401 ptr += strlen("config");
402 while(isspace(*ptr)) ptr++;
403 dev = mconsole_find_dev(ptr);
404 if(dev == NULL){
405 mconsole_reply(req, "Bad configuration option", 1, 0);
406 return;
407 }
408
409 name = &ptr[strlen(dev->name)];
410 ptr = name;
411 while((*ptr != '=') && (*ptr != '\0'))
412 ptr++;
413
414 if(*ptr == '='){
415 err = (*dev->config)(name);
416 mconsole_reply(req, "", err, 0);
417 }
418 else mconsole_get_config(dev->get_config, req, name);
419}
420
421void mconsole_remove(struct mc_request *req)
422{
d50084a2 423 struct mc_device *dev;
29d56cfe
JD
424 char *ptr = req->request.data, *err_msg = "";
425 char error[256];
426 int err, start, end, n;
1da177e4
LT
427
428 ptr += strlen("remove");
429 while(isspace(*ptr)) ptr++;
430 dev = mconsole_find_dev(ptr);
431 if(dev == NULL){
432 mconsole_reply(req, "Bad remove option", 1, 0);
433 return;
434 }
29d56cfe
JD
435
436 ptr = &ptr[strlen(dev->name)];
437
438 err = 1;
439 n = (*dev->id)(&ptr, &start, &end);
440 if(n < 0){
441 err_msg = "Couldn't parse device number";
442 goto out;
443 }
444 else if((n < start) || (n > end)){
445 sprintf(error, "Invalid device number - must be between "
446 "%d and %d", start, end);
447 err_msg = error;
448 goto out;
449 }
450
451 err = (*dev->remove)(n);
452 switch(err){
453 case -ENODEV:
454 err_msg = "Device doesn't exist";
455 break;
456 case -EBUSY:
457 err_msg = "Device is currently open";
458 break;
459 default:
460 break;
461 }
462 out:
463 mconsole_reply(req, err_msg, err, 0);
1da177e4
LT
464}
465
466#ifdef CONFIG_MAGIC_SYSRQ
467void mconsole_sysrq(struct mc_request *req)
468{
469 char *ptr = req->request.data;
470
471 ptr += strlen("sysrq");
472 while(isspace(*ptr)) ptr++;
473
474 mconsole_reply(req, "", 0, 0);
475 handle_sysrq(*ptr, &current->thread.regs, NULL);
476}
477#else
478void mconsole_sysrq(struct mc_request *req)
479{
480 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
481}
482#endif
483
6f517d3f
JD
484static DEFINE_SPINLOCK(console_lock);
485static LIST_HEAD(clients);
486static char console_buf[MCONSOLE_MAX_DATA];
487static int console_index = 0;
488
489static void console_write(struct console *console, const char *string,
490 unsigned len)
491{
492 struct list_head *ele;
493 int n;
494
495 if(list_empty(&clients))
496 return;
497
498 while(1){
499 n = min(len, ARRAY_SIZE(console_buf) - console_index);
500 strncpy(&console_buf[console_index], string, n);
501 console_index += n;
502 string += n;
503 len -= n;
504 if(len == 0)
505 return;
506
507 list_for_each(ele, &clients){
508 struct mconsole_entry *entry;
509
510 entry = list_entry(ele, struct mconsole_entry, list);
511 mconsole_reply_len(&entry->request, console_buf,
512 console_index, 0, 1);
513 }
514
515 console_index = 0;
516 }
517}
518
519static struct console mc_console = { .name = "mc",
520 .write = console_write,
521 .flags = CON_PRINTBUFFER | CON_ENABLED,
522 .index = -1 };
523
524static int mc_add_console(void)
525{
526 register_console(&mc_console);
527 return 0;
528}
529
530late_initcall(mc_add_console);
531
532static void with_console(struct mc_request *req, void (*proc)(void *),
533 void *arg)
534{
535 struct mconsole_entry entry;
536 unsigned long flags;
537
538 INIT_LIST_HEAD(&entry.list);
539 entry.request = *req;
540 list_add(&entry.list, &clients);
541 spin_lock_irqsave(&console_lock, flags);
542
543 (*proc)(arg);
544
545 mconsole_reply_len(req, console_buf, console_index, 0, 0);
546 console_index = 0;
547
548 spin_unlock_irqrestore(&console_lock, flags);
549 list_del(&entry.list);
550}
551
552static void stack_proc(void *arg)
553{
554 struct task_struct *from = current, *to = arg;
555
556 to->thread.saved_task = from;
557 switch_to(from, to, from);
558}
559
3eddddcf
JD
560/* Mconsole stack trace
561 * Added by Allan Graves, Jeff Dike
562 * Dumps a stacks registers to the linux console.
563 * Usage stack <pid>.
564 */
565void do_stack(struct mc_request *req)
566{
567 char *ptr = req->request.data;
568 int pid_requested= -1;
6f517d3f 569 struct task_struct *from = NULL;
3eddddcf
JD
570 struct task_struct *to = NULL;
571
572 /* Would be nice:
573 * 1) Send showregs output to mconsole.
574 * 2) Add a way to stack dump all pids.
575 */
576
577 ptr += strlen("stack");
578 while(isspace(*ptr)) ptr++;
579
580 /* Should really check for multiple pids or reject bad args here */
581 /* What do the arguments in mconsole_reply mean? */
582 if(sscanf(ptr, "%d", &pid_requested) == 0){
583 mconsole_reply(req, "Please specify a pid", 1, 0);
584 return;
585 }
586
6f517d3f 587 from = current;
3eddddcf 588
6f517d3f 589 to = find_task_by_pid(pid_requested);
3eddddcf
JD
590 if((to == NULL) || (pid_requested == 0)) {
591 mconsole_reply(req, "Couldn't find that pid", 1, 0);
592 return;
593 }
3eddddcf 594
6f517d3f 595 with_console(req, stack_proc, to);
3eddddcf
JD
596}
597
598void mconsole_stack(struct mc_request *req)
599{
600 /* This command doesn't work in TT mode, so let's check and then
601 * get out of here
602 */
603 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
604 1, 0),
605 do_stack(req));
606}
607
1da177e4
LT
608/* Changed by mconsole_setup, which is __setup, and called before SMP is
609 * active.
610 */
d50084a2 611static char *notify_socket = NULL;
1da177e4 612
9010772c 613static int mconsole_init(void)
1da177e4
LT
614{
615 /* long to avoid size mismatch warnings from gcc */
616 long sock;
617 int err;
618 char file[256];
619
620 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
621 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
622
623 sock = os_create_unix_socket(file, sizeof(file), 1);
624 if (sock < 0){
625 printk("Failed to initialize management console\n");
626 return(1);
627 }
628
629 register_reboot_notifier(&reboot_notifier);
630
631 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
632 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
633 "mconsole", (void *)sock);
634 if (err){
635 printk("Failed to get IRQ for management console\n");
636 return(1);
637 }
638
639 if(notify_socket != NULL){
970d6e3a 640 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
1da177e4
LT
641 if(notify_socket != NULL)
642 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
d50084a2 643 mconsole_socket_name,
1da177e4
LT
644 strlen(mconsole_socket_name) + 1);
645 else printk(KERN_ERR "mconsole_setup failed to strdup "
646 "string\n");
647 }
648
d50084a2 649 printk("mconsole (version %d) initialized on %s\n",
1da177e4
LT
650 MCONSOLE_VERSION, mconsole_socket_name);
651 return(0);
652}
653
654__initcall(mconsole_init);
655
656static int write_proc_mconsole(struct file *file, const char __user *buffer,
657 unsigned long count, void *data)
658{
659 char *buf;
660
661 buf = kmalloc(count + 1, GFP_KERNEL);
d50084a2 662 if(buf == NULL)
1da177e4
LT
663 return(-ENOMEM);
664
665 if(copy_from_user(buf, buffer, count)){
666 count = -EFAULT;
667 goto out;
668 }
669
670 buf[count] = '\0';
671
672 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
673 out:
674 kfree(buf);
675 return(count);
676}
677
678static int create_proc_mconsole(void)
679{
680 struct proc_dir_entry *ent;
681
682 if(notify_socket == NULL) return(0);
683
684 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
685 if(ent == NULL){
30f417c6 686 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
1da177e4
LT
687 return(0);
688 }
689
690 ent->read_proc = NULL;
691 ent->write_proc = write_proc_mconsole;
692 return(0);
693}
694
695static DEFINE_SPINLOCK(notify_spinlock);
696
697void lock_notify(void)
698{
699 spin_lock(&notify_spinlock);
700}
701
702void unlock_notify(void)
703{
704 spin_unlock(&notify_spinlock);
705}
706
707__initcall(create_proc_mconsole);
708
709#define NOTIFY "=notify:"
710
711static int mconsole_setup(char *str)
712{
713 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
714 str += strlen(NOTIFY);
715 notify_socket = str;
716 }
717 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
718 return(1);
719}
720
721__setup("mconsole", mconsole_setup);
722
723__uml_help(mconsole_setup,
724"mconsole=notify:<socket>\n"
725" Requests that the mconsole driver send a message to the named Unix\n"
726" socket containing the name of the mconsole socket. This also serves\n"
727" to notify outside processes when UML has booted far enough to respond\n"
728" to mconsole requests.\n\n"
729);
730
731static int notify_panic(struct notifier_block *self, unsigned long unused1,
732 void *ptr)
733{
734 char *message = ptr;
735
736 if(notify_socket == NULL) return(0);
737
d50084a2 738 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
1da177e4
LT
739 strlen(message) + 1);
740 return(0);
741}
742
743static struct notifier_block panic_exit_notifier = {
744 .notifier_call = notify_panic,
745 .next = NULL,
746 .priority = 1
747};
748
749static int add_notifier(void)
750{
751 notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
752 return(0);
753}
754
755__initcall(add_notifier);
756
757char *mconsole_notify_socket(void)
758{
759 return(notify_socket);
760}
761
762EXPORT_SYMBOL(mconsole_notify_socket);
763
764/*
765 * Overrides for Emacs so that we follow Linus's tabbing style.
766 * Emacs will notice this stuff at the end of the file and automatically
767 * adjust the settings for this buffer only. This must remain at the end
768 * of the file.
769 * ---------------------------------------------------------------------------
770 * Local variables:
771 * c-file-style: "linux"
772 * End:
773 */
This page took 0.147564 seconds and 5 git commands to generate.