diff options
Diffstat (limited to 'arch/um/drivers/mconsole_kern.c')
-rw-r--r-- | arch/um/drivers/mconsole_kern.c | 232 |
1 files changed, 153 insertions, 79 deletions
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 12c95368124a..be610125429f 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "linux/namei.h" | 20 | #include "linux/namei.h" |
21 | #include "linux/proc_fs.h" | 21 | #include "linux/proc_fs.h" |
22 | #include "linux/syscalls.h" | 22 | #include "linux/syscalls.h" |
23 | #include "linux/console.h" | ||
23 | #include "asm/irq.h" | 24 | #include "asm/irq.h" |
24 | #include "asm/uaccess.h" | 25 | #include "asm/uaccess.h" |
25 | #include "user_util.h" | 26 | #include "user_util.h" |
@@ -34,7 +35,7 @@ | |||
34 | #include "irq_kern.h" | 35 | #include "irq_kern.h" |
35 | #include "choose-mode.h" | 36 | #include "choose-mode.h" |
36 | 37 | ||
37 | static int do_unlink_socket(struct notifier_block *notifier, | 38 | static int do_unlink_socket(struct notifier_block *notifier, |
38 | unsigned long what, void *data) | 39 | unsigned long what, void *data) |
39 | { | 40 | { |
40 | return(mconsole_unlink_socket()); | 41 | return(mconsole_unlink_socket()); |
@@ -46,12 +47,12 @@ static struct notifier_block reboot_notifier = { | |||
46 | .priority = 0, | 47 | .priority = 0, |
47 | }; | 48 | }; |
48 | 49 | ||
49 | /* Safe without explicit locking for now. Tasklets provide their own | 50 | /* Safe without explicit locking for now. Tasklets provide their own |
50 | * locking, and the interrupt handler is safe because it can't interrupt | 51 | * locking, and the interrupt handler is safe because it can't interrupt |
51 | * itself and it can only happen on CPU 0. | 52 | * itself and it can only happen on CPU 0. |
52 | */ | 53 | */ |
53 | 54 | ||
54 | LIST_HEAD(mc_requests); | 55 | static LIST_HEAD(mc_requests); |
55 | 56 | ||
56 | static void mc_work_proc(void *unused) | 57 | static void mc_work_proc(void *unused) |
57 | { | 58 | { |
@@ -60,7 +61,7 @@ static void mc_work_proc(void *unused) | |||
60 | 61 | ||
61 | while(!list_empty(&mc_requests)){ | 62 | while(!list_empty(&mc_requests)){ |
62 | local_save_flags(flags); | 63 | local_save_flags(flags); |
63 | req = list_entry(mc_requests.next, struct mconsole_entry, | 64 | req = list_entry(mc_requests.next, struct mconsole_entry, |
64 | list); | 65 | list); |
65 | list_del(&req->list); | 66 | list_del(&req->list); |
66 | local_irq_restore(flags); | 67 | local_irq_restore(flags); |
@@ -69,7 +70,7 @@ static void mc_work_proc(void *unused) | |||
69 | } | 70 | } |
70 | } | 71 | } |
71 | 72 | ||
72 | DECLARE_WORK(mconsole_work, mc_work_proc, NULL); | 73 | static DECLARE_WORK(mconsole_work, mc_work_proc, NULL); |
73 | 74 | ||
74 | static irqreturn_t mconsole_interrupt(int irq, void *dev_id, | 75 | static irqreturn_t mconsole_interrupt(int irq, void *dev_id, |
75 | struct pt_regs *regs) | 76 | struct pt_regs *regs) |
@@ -103,8 +104,8 @@ void mconsole_version(struct mc_request *req) | |||
103 | { | 104 | { |
104 | char version[256]; | 105 | char version[256]; |
105 | 106 | ||
106 | sprintf(version, "%s %s %s %s %s", system_utsname.sysname, | 107 | sprintf(version, "%s %s %s %s %s", system_utsname.sysname, |
107 | system_utsname.nodename, system_utsname.release, | 108 | system_utsname.nodename, system_utsname.release, |
108 | system_utsname.version, system_utsname.machine); | 109 | system_utsname.version, system_utsname.machine); |
109 | mconsole_reply(req, version, 0, 0); | 110 | mconsole_reply(req, version, 0, 0); |
110 | } | 111 | } |
@@ -348,7 +349,7 @@ static struct mc_device *mconsole_find_dev(char *name) | |||
348 | 349 | ||
349 | #define CONFIG_BUF_SIZE 64 | 350 | #define CONFIG_BUF_SIZE 64 |
350 | 351 | ||
351 | static void mconsole_get_config(int (*get_config)(char *, char *, int, | 352 | static void mconsole_get_config(int (*get_config)(char *, char *, int, |
352 | char **), | 353 | char **), |
353 | struct mc_request *req, char *name) | 354 | struct mc_request *req, char *name) |
354 | { | 355 | { |
@@ -389,7 +390,6 @@ static void mconsole_get_config(int (*get_config)(char *, char *, int, | |||
389 | out: | 390 | out: |
390 | if(buf != default_buf) | 391 | if(buf != default_buf) |
391 | kfree(buf); | 392 | kfree(buf); |
392 | |||
393 | } | 393 | } |
394 | 394 | ||
395 | void mconsole_config(struct mc_request *req) | 395 | void mconsole_config(struct mc_request *req) |
@@ -420,9 +420,9 @@ void mconsole_config(struct mc_request *req) | |||
420 | 420 | ||
421 | void mconsole_remove(struct mc_request *req) | 421 | void mconsole_remove(struct mc_request *req) |
422 | { | 422 | { |
423 | struct mc_device *dev; | 423 | struct mc_device *dev; |
424 | char *ptr = req->request.data, *err_msg = ""; | 424 | char *ptr = req->request.data, *err_msg = ""; |
425 | char error[256]; | 425 | char error[256]; |
426 | int err, start, end, n; | 426 | int err, start, end, n; |
427 | 427 | ||
428 | ptr += strlen("remove"); | 428 | ptr += strlen("remove"); |
@@ -433,37 +433,112 @@ void mconsole_remove(struct mc_request *req) | |||
433 | return; | 433 | return; |
434 | } | 434 | } |
435 | 435 | ||
436 | ptr = &ptr[strlen(dev->name)]; | 436 | ptr = &ptr[strlen(dev->name)]; |
437 | 437 | ||
438 | err = 1; | 438 | err = 1; |
439 | n = (*dev->id)(&ptr, &start, &end); | 439 | n = (*dev->id)(&ptr, &start, &end); |
440 | if(n < 0){ | 440 | if(n < 0){ |
441 | err_msg = "Couldn't parse device number"; | 441 | err_msg = "Couldn't parse device number"; |
442 | goto out; | 442 | goto out; |
443 | } | 443 | } |
444 | else if((n < start) || (n > end)){ | 444 | else if((n < start) || (n > end)){ |
445 | sprintf(error, "Invalid device number - must be between " | 445 | sprintf(error, "Invalid device number - must be between " |
446 | "%d and %d", start, end); | 446 | "%d and %d", start, end); |
447 | err_msg = error; | 447 | err_msg = error; |
448 | goto out; | 448 | goto out; |
449 | } | 449 | } |
450 | 450 | ||
451 | err = (*dev->remove)(n); | 451 | err = (*dev->remove)(n); |
452 | switch(err){ | 452 | switch(err){ |
453 | case -ENODEV: | 453 | case -ENODEV: |
454 | err_msg = "Device doesn't exist"; | 454 | err_msg = "Device doesn't exist"; |
455 | break; | 455 | break; |
456 | case -EBUSY: | 456 | case -EBUSY: |
457 | err_msg = "Device is currently open"; | 457 | err_msg = "Device is currently open"; |
458 | break; | 458 | break; |
459 | default: | 459 | default: |
460 | break; | 460 | break; |
461 | } | 461 | } |
462 | out: | 462 | out: |
463 | mconsole_reply(req, err_msg, err, 0); | 463 | mconsole_reply(req, err_msg, err, 0); |
464 | } | 464 | } |
465 | 465 | ||
466 | static DEFINE_SPINLOCK(console_lock); | ||
467 | static LIST_HEAD(clients); | ||
468 | static char console_buf[MCONSOLE_MAX_DATA]; | ||
469 | static int console_index = 0; | ||
470 | |||
471 | static void console_write(struct console *console, const char *string, | ||
472 | unsigned len) | ||
473 | { | ||
474 | struct list_head *ele; | ||
475 | int n; | ||
476 | |||
477 | if(list_empty(&clients)) | ||
478 | return; | ||
479 | |||
480 | while(1){ | ||
481 | n = min(len, ARRAY_SIZE(console_buf) - console_index); | ||
482 | strncpy(&console_buf[console_index], string, n); | ||
483 | console_index += n; | ||
484 | string += n; | ||
485 | len -= n; | ||
486 | if(len == 0) | ||
487 | return; | ||
488 | |||
489 | list_for_each(ele, &clients){ | ||
490 | struct mconsole_entry *entry; | ||
491 | |||
492 | entry = list_entry(ele, struct mconsole_entry, list); | ||
493 | mconsole_reply_len(&entry->request, console_buf, | ||
494 | console_index, 0, 1); | ||
495 | } | ||
496 | |||
497 | console_index = 0; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | static struct console mc_console = { .name = "mc", | ||
502 | .write = console_write, | ||
503 | .flags = CON_PRINTBUFFER | CON_ENABLED, | ||
504 | .index = -1 }; | ||
505 | |||
506 | static int mc_add_console(void) | ||
507 | { | ||
508 | register_console(&mc_console); | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | late_initcall(mc_add_console); | ||
513 | |||
514 | static void with_console(struct mc_request *req, void (*proc)(void *), | ||
515 | void *arg) | ||
516 | { | ||
517 | struct mconsole_entry entry; | ||
518 | unsigned long flags; | ||
519 | |||
520 | INIT_LIST_HEAD(&entry.list); | ||
521 | entry.request = *req; | ||
522 | list_add(&entry.list, &clients); | ||
523 | spin_lock_irqsave(&console_lock, flags); | ||
524 | |||
525 | (*proc)(arg); | ||
526 | |||
527 | mconsole_reply_len(req, console_buf, console_index, 0, 0); | ||
528 | console_index = 0; | ||
529 | |||
530 | spin_unlock_irqrestore(&console_lock, flags); | ||
531 | list_del(&entry.list); | ||
532 | } | ||
533 | |||
466 | #ifdef CONFIG_MAGIC_SYSRQ | 534 | #ifdef CONFIG_MAGIC_SYSRQ |
535 | static void sysrq_proc(void *arg) | ||
536 | { | ||
537 | char *op = arg; | ||
538 | |||
539 | handle_sysrq(*op, ¤t->thread.regs, NULL); | ||
540 | } | ||
541 | |||
467 | void mconsole_sysrq(struct mc_request *req) | 542 | void mconsole_sysrq(struct mc_request *req) |
468 | { | 543 | { |
469 | char *ptr = req->request.data; | 544 | char *ptr = req->request.data; |
@@ -471,8 +546,13 @@ void mconsole_sysrq(struct mc_request *req) | |||
471 | ptr += strlen("sysrq"); | 546 | ptr += strlen("sysrq"); |
472 | while(isspace(*ptr)) ptr++; | 547 | while(isspace(*ptr)) ptr++; |
473 | 548 | ||
474 | mconsole_reply(req, "", 0, 0); | 549 | /* With 'b', the system will shut down without a chance to reply, |
475 | handle_sysrq(*ptr, ¤t->thread.regs, NULL); | 550 | * so in this case, we reply first. |
551 | */ | ||
552 | if(*ptr == 'b') | ||
553 | mconsole_reply(req, "", 0, 0); | ||
554 | |||
555 | with_console(req, sysrq_proc, ptr); | ||
476 | } | 556 | } |
477 | #else | 557 | #else |
478 | void mconsole_sysrq(struct mc_request *req) | 558 | void mconsole_sysrq(struct mc_request *req) |
@@ -481,6 +561,14 @@ void mconsole_sysrq(struct mc_request *req) | |||
481 | } | 561 | } |
482 | #endif | 562 | #endif |
483 | 563 | ||
564 | static void stack_proc(void *arg) | ||
565 | { | ||
566 | struct task_struct *from = current, *to = arg; | ||
567 | |||
568 | to->thread.saved_task = from; | ||
569 | switch_to(from, to, from); | ||
570 | } | ||
571 | |||
484 | /* Mconsole stack trace | 572 | /* Mconsole stack trace |
485 | * Added by Allan Graves, Jeff Dike | 573 | * Added by Allan Graves, Jeff Dike |
486 | * Dumps a stacks registers to the linux console. | 574 | * Dumps a stacks registers to the linux console. |
@@ -488,37 +576,34 @@ void mconsole_sysrq(struct mc_request *req) | |||
488 | */ | 576 | */ |
489 | void do_stack(struct mc_request *req) | 577 | void do_stack(struct mc_request *req) |
490 | { | 578 | { |
491 | char *ptr = req->request.data; | 579 | char *ptr = req->request.data; |
492 | int pid_requested= -1; | 580 | int pid_requested= -1; |
493 | struct task_struct *from = NULL; | 581 | struct task_struct *from = NULL; |
494 | struct task_struct *to = NULL; | 582 | struct task_struct *to = NULL; |
495 | 583 | ||
496 | /* Would be nice: | 584 | /* Would be nice: |
497 | * 1) Send showregs output to mconsole. | 585 | * 1) Send showregs output to mconsole. |
498 | * 2) Add a way to stack dump all pids. | 586 | * 2) Add a way to stack dump all pids. |
499 | */ | 587 | */ |
500 | 588 | ||
501 | ptr += strlen("stack"); | 589 | ptr += strlen("stack"); |
502 | while(isspace(*ptr)) ptr++; | 590 | while(isspace(*ptr)) ptr++; |
503 | |||
504 | /* Should really check for multiple pids or reject bad args here */ | ||
505 | /* What do the arguments in mconsole_reply mean? */ | ||
506 | if(sscanf(ptr, "%d", &pid_requested) == 0){ | ||
507 | mconsole_reply(req, "Please specify a pid", 1, 0); | ||
508 | return; | ||
509 | } | ||
510 | 591 | ||
511 | from = current; | 592 | /* Should really check for multiple pids or reject bad args here */ |
512 | to = find_task_by_pid(pid_requested); | 593 | /* What do the arguments in mconsole_reply mean? */ |
594 | if(sscanf(ptr, "%d", &pid_requested) == 0){ | ||
595 | mconsole_reply(req, "Please specify a pid", 1, 0); | ||
596 | return; | ||
597 | } | ||
513 | 598 | ||
514 | if((to == NULL) || (pid_requested == 0)) { | 599 | from = current; |
515 | mconsole_reply(req, "Couldn't find that pid", 1, 0); | ||
516 | return; | ||
517 | } | ||
518 | to->thread.saved_task = current; | ||
519 | 600 | ||
520 | switch_to(from, to, from); | 601 | to = find_task_by_pid(pid_requested); |
521 | mconsole_reply(req, "Stack Dumped to console and message log", 0, 0); | 602 | if((to == NULL) || (pid_requested == 0)) { |
603 | mconsole_reply(req, "Couldn't find that pid", 1, 0); | ||
604 | return; | ||
605 | } | ||
606 | with_console(req, stack_proc, to); | ||
522 | } | 607 | } |
523 | 608 | ||
524 | void mconsole_stack(struct mc_request *req) | 609 | void mconsole_stack(struct mc_request *req) |
@@ -534,9 +619,9 @@ void mconsole_stack(struct mc_request *req) | |||
534 | /* Changed by mconsole_setup, which is __setup, and called before SMP is | 619 | /* Changed by mconsole_setup, which is __setup, and called before SMP is |
535 | * active. | 620 | * active. |
536 | */ | 621 | */ |
537 | static char *notify_socket = NULL; | 622 | static char *notify_socket = NULL; |
538 | 623 | ||
539 | int mconsole_init(void) | 624 | static int mconsole_init(void) |
540 | { | 625 | { |
541 | /* long to avoid size mismatch warnings from gcc */ | 626 | /* long to avoid size mismatch warnings from gcc */ |
542 | long sock; | 627 | long sock; |
@@ -563,16 +648,16 @@ int mconsole_init(void) | |||
563 | } | 648 | } |
564 | 649 | ||
565 | if(notify_socket != NULL){ | 650 | if(notify_socket != NULL){ |
566 | notify_socket = uml_strdup(notify_socket); | 651 | notify_socket = kstrdup(notify_socket, GFP_KERNEL); |
567 | if(notify_socket != NULL) | 652 | if(notify_socket != NULL) |
568 | mconsole_notify(notify_socket, MCONSOLE_SOCKET, | 653 | mconsole_notify(notify_socket, MCONSOLE_SOCKET, |
569 | mconsole_socket_name, | 654 | mconsole_socket_name, |
570 | strlen(mconsole_socket_name) + 1); | 655 | strlen(mconsole_socket_name) + 1); |
571 | else printk(KERN_ERR "mconsole_setup failed to strdup " | 656 | else printk(KERN_ERR "mconsole_setup failed to strdup " |
572 | "string\n"); | 657 | "string\n"); |
573 | } | 658 | } |
574 | 659 | ||
575 | printk("mconsole (version %d) initialized on %s\n", | 660 | printk("mconsole (version %d) initialized on %s\n", |
576 | MCONSOLE_VERSION, mconsole_socket_name); | 661 | MCONSOLE_VERSION, mconsole_socket_name); |
577 | return(0); | 662 | return(0); |
578 | } | 663 | } |
@@ -585,7 +670,7 @@ static int write_proc_mconsole(struct file *file, const char __user *buffer, | |||
585 | char *buf; | 670 | char *buf; |
586 | 671 | ||
587 | buf = kmalloc(count + 1, GFP_KERNEL); | 672 | buf = kmalloc(count + 1, GFP_KERNEL); |
588 | if(buf == NULL) | 673 | if(buf == NULL) |
589 | return(-ENOMEM); | 674 | return(-ENOMEM); |
590 | 675 | ||
591 | if(copy_from_user(buf, buffer, count)){ | 676 | if(copy_from_user(buf, buffer, count)){ |
@@ -661,7 +746,7 @@ static int notify_panic(struct notifier_block *self, unsigned long unused1, | |||
661 | 746 | ||
662 | if(notify_socket == NULL) return(0); | 747 | if(notify_socket == NULL) return(0); |
663 | 748 | ||
664 | mconsole_notify(notify_socket, MCONSOLE_PANIC, message, | 749 | mconsole_notify(notify_socket, MCONSOLE_PANIC, message, |
665 | strlen(message) + 1); | 750 | strlen(message) + 1); |
666 | return(0); | 751 | return(0); |
667 | } | 752 | } |
@@ -686,14 +771,3 @@ char *mconsole_notify_socket(void) | |||
686 | } | 771 | } |
687 | 772 | ||
688 | EXPORT_SYMBOL(mconsole_notify_socket); | 773 | EXPORT_SYMBOL(mconsole_notify_socket); |
689 | |||
690 | /* | ||
691 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
692 | * Emacs will notice this stuff at the end of the file and automatically | ||
693 | * adjust the settings for this buffer only. This must remain at the end | ||
694 | * of the file. | ||
695 | * --------------------------------------------------------------------------- | ||
696 | * Local variables: | ||
697 | * c-file-style: "linux" | ||
698 | * End: | ||
699 | */ | ||