/arch/um/drivers/mconsole_kern.c
C | 933 lines | 745 code | 149 blank | 39 comment | 122 complexity | 37d18107c5b72a26c1008149d9d389da MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
1/* 2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) 3 * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) 4 * Licensed under the GPL 5 */ 6 7#include <linux/console.h> 8#include <linux/ctype.h> 9#include <linux/string.h> 10#include <linux/interrupt.h> 11#include <linux/list.h> 12#include <linux/mm.h> 13#include <linux/module.h> 14#include <linux/notifier.h> 15#include <linux/reboot.h> 16#include <linux/proc_fs.h> 17#include <linux/slab.h> 18#include <linux/syscalls.h> 19#include <linux/utsname.h> 20#include <linux/socket.h> 21#include <linux/un.h> 22#include <linux/workqueue.h> 23#include <linux/mutex.h> 24#include <asm/uaccess.h> 25 26#include "init.h" 27#include "irq_kern.h" 28#include "irq_user.h" 29#include "kern_util.h" 30#include "mconsole.h" 31#include "mconsole_kern.h" 32#include "os.h" 33 34static int do_unlink_socket(struct notifier_block *notifier, 35 unsigned long what, void *data) 36{ 37 return mconsole_unlink_socket(); 38} 39 40 41static struct notifier_block reboot_notifier = { 42 .notifier_call = do_unlink_socket, 43 .priority = 0, 44}; 45 46/* Safe without explicit locking for now. Tasklets provide their own 47 * locking, and the interrupt handler is safe because it can't interrupt 48 * itself and it can only happen on CPU 0. 49 */ 50 51static LIST_HEAD(mc_requests); 52 53static void mc_work_proc(struct work_struct *unused) 54{ 55 struct mconsole_entry *req; 56 unsigned long flags; 57 58 while (!list_empty(&mc_requests)) { 59 local_irq_save(flags); 60 req = list_entry(mc_requests.next, struct mconsole_entry, list); 61 list_del(&req->list); 62 local_irq_restore(flags); 63 req->request.cmd->handler(&req->request); 64 kfree(req); 65 } 66} 67 68static DECLARE_WORK(mconsole_work, mc_work_proc); 69 70static irqreturn_t mconsole_interrupt(int irq, void *dev_id) 71{ 72 /* long to avoid size mismatch warnings from gcc */ 73 long fd; 74 struct mconsole_entry *new; 75 static struct mc_request req; /* that's OK */ 76 77 fd = (long) dev_id; 78 while (mconsole_get_request(fd, &req)) { 79 if (req.cmd->context == MCONSOLE_INTR) 80 (*req.cmd->handler)(&req); 81 else { 82 new = kmalloc(sizeof(*new), GFP_NOWAIT); 83 if (new == NULL) 84 mconsole_reply(&req, "Out of memory", 1, 0); 85 else { 86 new->request = req; 87 new->request.regs = get_irq_regs()->regs; 88 list_add(&new->list, &mc_requests); 89 } 90 } 91 } 92 if (!list_empty(&mc_requests)) 93 schedule_work(&mconsole_work); 94 reactivate_fd(fd, MCONSOLE_IRQ); 95 return IRQ_HANDLED; 96} 97 98void mconsole_version(struct mc_request *req) 99{ 100 char version[256]; 101 102 sprintf(version, "%s %s %s %s %s", utsname()->sysname, 103 utsname()->nodename, utsname()->release, utsname()->version, 104 utsname()->machine); 105 mconsole_reply(req, version, 0, 0); 106} 107 108void mconsole_log(struct mc_request *req) 109{ 110 int len; 111 char *ptr = req->request.data; 112 113 ptr += strlen("log "); 114 115 len = req->len - (ptr - req->request.data); 116 printk(KERN_WARNING "%.*s", len, ptr); 117 mconsole_reply(req, "", 0, 0); 118} 119 120/* This is a more convoluted version of mconsole_proc, which has some stability 121 * problems; however, we need it fixed, because it is expected that UML users 122 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still 123 * show the real procfs content, not the ones from hppfs.*/ 124#if 0 125void mconsole_proc(struct mc_request *req) 126{ 127 struct nameidata nd; 128 struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt; 129 struct file *file; 130 int n, err; 131 char *ptr = req->request.data, *buf; 132 mm_segment_t old_fs = get_fs(); 133 134 ptr += strlen("proc"); 135 ptr = skip_spaces(ptr); 136 137 err = vfs_path_lookup(mnt->mnt_root, mnt, ptr, LOOKUP_FOLLOW, &nd); 138 if (err) { 139 mconsole_reply(req, "Failed to look up file", 1, 0); 140 goto out; 141 } 142 143 err = may_open(&nd.path, MAY_READ, O_RDONLY); 144 if (result) { 145 mconsole_reply(req, "Failed to open file", 1, 0); 146 path_put(&nd.path); 147 goto out; 148 } 149 150 file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY, 151 current_cred()); 152 err = PTR_ERR(file); 153 if (IS_ERR(file)) { 154 mconsole_reply(req, "Failed to open file", 1, 0); 155 path_put(&nd.path); 156 goto out; 157 } 158 159 buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 160 if (buf == NULL) { 161 mconsole_reply(req, "Failed to allocate buffer", 1, 0); 162 goto out_fput; 163 } 164 165 if (file->f_op->read) { 166 do { 167 loff_t pos; 168 set_fs(KERNEL_DS); 169 n = vfs_read(file, buf, PAGE_SIZE - 1, &pos); 170 file_pos_write(file, pos); 171 set_fs(old_fs); 172 if (n >= 0) { 173 buf[n] = '\0'; 174 mconsole_reply(req, buf, 0, (n > 0)); 175 } 176 else { 177 mconsole_reply(req, "Read of file failed", 178 1, 0); 179 goto out_free; 180 } 181 } while (n > 0); 182 } 183 else mconsole_reply(req, "", 0, 0); 184 185 out_free: 186 kfree(buf); 187 out_fput: 188 fput(file); 189 out: ; 190} 191#endif 192 193void mconsole_proc(struct mc_request *req) 194{ 195 char path[64]; 196 char *buf; 197 int len; 198 int fd; 199 int first_chunk = 1; 200 char *ptr = req->request.data; 201 202 ptr += strlen("proc"); 203 ptr = skip_spaces(ptr); 204 snprintf(path, sizeof(path), "/proc/%s", ptr); 205 206 fd = sys_open(path, 0, 0); 207 if (fd < 0) { 208 mconsole_reply(req, "Failed to open file", 1, 0); 209 printk(KERN_ERR "open %s: %d\n",path,fd); 210 goto out; 211 } 212 213 buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 214 if (buf == NULL) { 215 mconsole_reply(req, "Failed to allocate buffer", 1, 0); 216 goto out_close; 217 } 218 219 for (;;) { 220 len = sys_read(fd, buf, PAGE_SIZE-1); 221 if (len < 0) { 222 mconsole_reply(req, "Read of file failed", 1, 0); 223 goto out_free; 224 } 225 /* Begin the file content on his own line. */ 226 if (first_chunk) { 227 mconsole_reply(req, "\n", 0, 1); 228 first_chunk = 0; 229 } 230 if (len == PAGE_SIZE-1) { 231 buf[len] = '\0'; 232 mconsole_reply(req, buf, 0, 1); 233 } else { 234 buf[len] = '\0'; 235 mconsole_reply(req, buf, 0, 0); 236 break; 237 } 238 } 239 240 out_free: 241 kfree(buf); 242 out_close: 243 sys_close(fd); 244 out: 245 /* nothing */; 246} 247 248#define UML_MCONSOLE_HELPTEXT \ 249"Commands: \n\ 250 version - Get kernel version \n\ 251 help - Print this message \n\ 252 halt - Halt UML \n\ 253 reboot - Reboot UML \n\ 254 config <dev>=<config> - Add a new device to UML; \n\ 255 same syntax as command line \n\ 256 config <dev> - Query the configuration of a device \n\ 257 remove <dev> - Remove a device from UML \n\ 258 sysrq <letter> - Performs the SysRq action controlled by the letter \n\ 259 cad - invoke the Ctrl-Alt-Del handler \n\ 260 stop - pause the UML; it will do nothing until it receives a 'go' \n\ 261 go - continue the UML after a 'stop' \n\ 262 log <string> - make UML enter <string> into the kernel log\n\ 263 proc <file> - returns the contents of the UML's /proc/<file>\n\ 264 stack <pid> - returns the stack of the specified pid\n\ 265" 266 267void mconsole_help(struct mc_request *req) 268{ 269 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0); 270} 271 272void mconsole_halt(struct mc_request *req) 273{ 274 mconsole_reply(req, "", 0, 0); 275 machine_halt(); 276} 277 278void mconsole_reboot(struct mc_request *req) 279{ 280 mconsole_reply(req, "", 0, 0); 281 machine_restart(NULL); 282} 283 284void mconsole_cad(struct mc_request *req) 285{ 286 mconsole_reply(req, "", 0, 0); 287 ctrl_alt_del(); 288} 289 290void mconsole_go(struct mc_request *req) 291{ 292 mconsole_reply(req, "Not stopped", 1, 0); 293} 294 295void mconsole_stop(struct mc_request *req) 296{ 297 deactivate_fd(req->originating_fd, MCONSOLE_IRQ); 298 os_set_fd_block(req->originating_fd, 1); 299 mconsole_reply(req, "stopped", 0, 0); 300 for (;;) { 301 if (!mconsole_get_request(req->originating_fd, req)) 302 continue; 303 if (req->cmd->handler == mconsole_go) 304 break; 305 if (req->cmd->handler == mconsole_stop) { 306 mconsole_reply(req, "Already stopped", 1, 0); 307 continue; 308 } 309 if (req->cmd->handler == mconsole_sysrq) { 310 struct pt_regs *old_regs; 311 old_regs = set_irq_regs((struct pt_regs *)&req->regs); 312 mconsole_sysrq(req); 313 set_irq_regs(old_regs); 314 continue; 315 } 316 (*req->cmd->handler)(req); 317 } 318 os_set_fd_block(req->originating_fd, 0); 319 reactivate_fd(req->originating_fd, MCONSOLE_IRQ); 320 mconsole_reply(req, "", 0, 0); 321} 322 323static DEFINE_SPINLOCK(mc_devices_lock); 324static LIST_HEAD(mconsole_devices); 325 326void mconsole_register_dev(struct mc_device *new) 327{ 328 spin_lock(&mc_devices_lock); 329 BUG_ON(!list_empty(&new->list)); 330 list_add(&new->list, &mconsole_devices); 331 spin_unlock(&mc_devices_lock); 332} 333 334static struct mc_device *mconsole_find_dev(char *name) 335{ 336 struct list_head *ele; 337 struct mc_device *dev; 338 339 list_for_each(ele, &mconsole_devices) { 340 dev = list_entry(ele, struct mc_device, list); 341 if (!strncmp(name, dev->name, strlen(dev->name))) 342 return dev; 343 } 344 return NULL; 345} 346 347#define UNPLUGGED_PER_PAGE \ 348 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long)) 349 350struct unplugged_pages { 351 struct list_head list; 352 void *pages[UNPLUGGED_PER_PAGE]; 353}; 354 355static DEFINE_MUTEX(plug_mem_mutex); 356static unsigned long long unplugged_pages_count = 0; 357static LIST_HEAD(unplugged_pages); 358static int unplug_index = UNPLUGGED_PER_PAGE; 359 360static int mem_config(char *str, char **error_out) 361{ 362 unsigned long long diff; 363 int err = -EINVAL, i, add; 364 char *ret; 365 366 if (str[0] != '=') { 367 *error_out = "Expected '=' after 'mem'"; 368 goto out; 369 } 370 371 str++; 372 if (str[0] == '-') 373 add = 0; 374 else if (str[0] == '+') { 375 add = 1; 376 } 377 else { 378 *error_out = "Expected increment to start with '-' or '+'"; 379 goto out; 380 } 381 382 str++; 383 diff = memparse(str, &ret); 384 if (*ret != '\0') { 385 *error_out = "Failed to parse memory increment"; 386 goto out; 387 } 388 389 diff /= PAGE_SIZE; 390 391 mutex_lock(&plug_mem_mutex); 392 for (i = 0; i < diff; i++) { 393 struct unplugged_pages *unplugged; 394 void *addr; 395 396 if (add) { 397 if (list_empty(&unplugged_pages)) 398 break; 399 400 unplugged = list_entry(unplugged_pages.next, 401 struct unplugged_pages, list); 402 if (unplug_index > 0) 403 addr = unplugged->pages[--unplug_index]; 404 else { 405 list_del(&unplugged->list); 406 addr = unplugged; 407 unplug_index = UNPLUGGED_PER_PAGE; 408 } 409 410 free_page((unsigned long) addr); 411 unplugged_pages_count--; 412 } 413 else { 414 struct page *page; 415 416 page = alloc_page(GFP_ATOMIC); 417 if (page == NULL) 418 break; 419 420 unplugged = page_address(page); 421 if (unplug_index == UNPLUGGED_PER_PAGE) { 422 list_add(&unplugged->list, &unplugged_pages); 423 unplug_index = 0; 424 } 425 else { 426 struct list_head *entry = unplugged_pages.next; 427 addr = unplugged; 428 429 unplugged = list_entry(entry, 430 struct unplugged_pages, 431 list); 432 err = os_drop_memory(addr, PAGE_SIZE); 433 if (err) { 434 printk(KERN_ERR "Failed to release " 435 "memory - errno = %d\n", err); 436 *error_out = "Failed to release memory"; 437 goto out_unlock; 438 } 439 unplugged->pages[unplug_index++] = addr; 440 } 441 442 unplugged_pages_count++; 443 } 444 } 445 446 err = 0; 447out_unlock: 448 mutex_unlock(&plug_mem_mutex); 449out: 450 return err; 451} 452 453static int mem_get_config(char *name, char *str, int size, char **error_out) 454{ 455 char buf[sizeof("18446744073709551615")]; 456 int len = 0; 457 458 sprintf(buf, "%ld", uml_physmem); 459 CONFIG_CHUNK(str, size, len, buf, 1); 460 461 return len; 462} 463 464static int mem_id(char **str, int *start_out, int *end_out) 465{ 466 *start_out = 0; 467 *end_out = 0; 468 469 return 0; 470} 471 472static int mem_remove(int n, char **error_out) 473{ 474 *error_out = "Memory doesn't support the remove operation"; 475 return -EBUSY; 476} 477 478static struct mc_device mem_mc = { 479 .list = LIST_HEAD_INIT(mem_mc.list), 480 .name = "mem", 481 .config = mem_config, 482 .get_config = mem_get_config, 483 .id = mem_id, 484 .remove = mem_remove, 485}; 486 487static int __init mem_mc_init(void) 488{ 489 if (can_drop_memory()) 490 mconsole_register_dev(&mem_mc); 491 else printk(KERN_ERR "Can't release memory to the host - memory " 492 "hotplug won't be supported\n"); 493 return 0; 494} 495 496__initcall(mem_mc_init); 497 498#define CONFIG_BUF_SIZE 64 499 500static void mconsole_get_config(int (*get_config)(char *, char *, int, 501 char **), 502 struct mc_request *req, char *name) 503{ 504 char default_buf[CONFIG_BUF_SIZE], *error, *buf; 505 int n, size; 506 507 if (get_config == NULL) { 508 mconsole_reply(req, "No get_config routine defined", 1, 0); 509 return; 510 } 511 512 error = NULL; 513 size = ARRAY_SIZE(default_buf); 514 buf = default_buf; 515 516 while (1) { 517 n = (*get_config)(name, buf, size, &error); 518 if (error != NULL) { 519 mconsole_reply(req, error, 1, 0); 520 goto out; 521 } 522 523 if (n <= size) { 524 mconsole_reply(req, buf, 0, 0); 525 goto out; 526 } 527 528 if (buf != default_buf) 529 kfree(buf); 530 531 size = n; 532 buf = kmalloc(size, GFP_KERNEL); 533 if (buf == NULL) { 534 mconsole_reply(req, "Failed to allocate buffer", 1, 0); 535 return; 536 } 537 } 538 out: 539 if (buf != default_buf) 540 kfree(buf); 541} 542 543void mconsole_config(struct mc_request *req) 544{ 545 struct mc_device *dev; 546 char *ptr = req->request.data, *name, *error_string = ""; 547 int err; 548 549 ptr += strlen("config"); 550 ptr = skip_spaces(ptr); 551 dev = mconsole_find_dev(ptr); 552 if (dev == NULL) { 553 mconsole_reply(req, "Bad configuration option", 1, 0); 554 return; 555 } 556 557 name = &ptr[strlen(dev->name)]; 558 ptr = name; 559 while ((*ptr != '=') && (*ptr != '\0')) 560 ptr++; 561 562 if (*ptr == '=') { 563 err = (*dev->config)(name, &error_string); 564 mconsole_reply(req, error_string, err, 0); 565 } 566 else mconsole_get_config(dev->get_config, req, name); 567} 568 569void mconsole_remove(struct mc_request *req) 570{ 571 struct mc_device *dev; 572 char *ptr = req->request.data, *err_msg = ""; 573 char error[256]; 574 int err, start, end, n; 575 576 ptr += strlen("remove"); 577 ptr = skip_spaces(ptr); 578 dev = mconsole_find_dev(ptr); 579 if (dev == NULL) { 580 mconsole_reply(req, "Bad remove option", 1, 0); 581 return; 582 } 583 584 ptr = &ptr[strlen(dev->name)]; 585 586 err = 1; 587 n = (*dev->id)(&ptr, &start, &end); 588 if (n < 0) { 589 err_msg = "Couldn't parse device number"; 590 goto out; 591 } 592 else if ((n < start) || (n > end)) { 593 sprintf(error, "Invalid device number - must be between " 594 "%d and %d", start, end); 595 err_msg = error; 596 goto out; 597 } 598 599 err_msg = NULL; 600 err = (*dev->remove)(n, &err_msg); 601 switch(err) { 602 case 0: 603 err_msg = ""; 604 break; 605 case -ENODEV: 606 if (err_msg == NULL) 607 err_msg = "Device doesn't exist"; 608 break; 609 case -EBUSY: 610 if (err_msg == NULL) 611 err_msg = "Device is currently open"; 612 break; 613 default: 614 break; 615 } 616out: 617 mconsole_reply(req, err_msg, err, 0); 618} 619 620struct mconsole_output { 621 struct list_head list; 622 struct mc_request *req; 623}; 624 625static DEFINE_SPINLOCK(client_lock); 626static LIST_HEAD(clients); 627static char console_buf[MCONSOLE_MAX_DATA]; 628 629static void console_write(struct console *console, const char *string, 630 unsigned int len) 631{ 632 struct list_head *ele; 633 int n; 634 635 if (list_empty(&clients)) 636 return; 637 638 while (len > 0) { 639 n = min((size_t) len, ARRAY_SIZE(console_buf)); 640 strncpy(console_buf, string, n); 641 string += n; 642 len -= n; 643 644 list_for_each(ele, &clients) { 645 struct mconsole_output *entry; 646 647 entry = list_entry(ele, struct mconsole_output, list); 648 mconsole_reply_len(entry->req, console_buf, n, 0, 1); 649 } 650 } 651} 652 653static struct console mc_console = { .name = "mc", 654 .write = console_write, 655 .flags = CON_ENABLED, 656 .index = -1 }; 657 658static int mc_add_console(void) 659{ 660 register_console(&mc_console); 661 return 0; 662} 663 664late_initcall(mc_add_console); 665 666static void with_console(struct mc_request *req, void (*proc)(void *), 667 void *arg) 668{ 669 struct mconsole_output entry; 670 unsigned long flags; 671 672 entry.req = req; 673 spin_lock_irqsave(&client_lock, flags); 674 list_add(&entry.list, &clients); 675 spin_unlock_irqrestore(&client_lock, flags); 676 677 (*proc)(arg); 678 679 mconsole_reply_len(req, "", 0, 0, 0); 680 681 spin_lock_irqsave(&client_lock, flags); 682 list_del(&entry.list); 683 spin_unlock_irqrestore(&client_lock, flags); 684} 685 686#ifdef CONFIG_MAGIC_SYSRQ 687 688#include <linux/sysrq.h> 689 690static void sysrq_proc(void *arg) 691{ 692 char *op = arg; 693 handle_sysrq(*op, NULL); 694} 695 696void mconsole_sysrq(struct mc_request *req) 697{ 698 char *ptr = req->request.data; 699 700 ptr += strlen("sysrq"); 701 ptr = skip_spaces(ptr); 702 703 /* 704 * With 'b', the system will shut down without a chance to reply, 705 * so in this case, we reply first. 706 */ 707 if (*ptr == 'b') 708 mconsole_reply(req, "", 0, 0); 709 710 with_console(req, sysrq_proc, ptr); 711} 712#else 713void mconsole_sysrq(struct mc_request *req) 714{ 715 mconsole_reply(req, "Sysrq not compiled in", 1, 0); 716} 717#endif 718 719static void stack_proc(void *arg) 720{ 721 struct task_struct *from = current, *to = arg; 722 723 to->thread.saved_task = from; 724 switch_to(from, to, from); 725} 726 727/* 728 * Mconsole stack trace 729 * Added by Allan Graves, Jeff Dike 730 * Dumps a stacks registers to the linux console. 731 * Usage stack <pid>. 732 */ 733void mconsole_stack(struct mc_request *req) 734{ 735 char *ptr = req->request.data; 736 int pid_requested= -1; 737 struct task_struct *to = NULL; 738 739 /* 740 * Would be nice: 741 * 1) Send showregs output to mconsole. 742 * 2) Add a way to stack dump all pids. 743 */ 744 745 ptr += strlen("stack"); 746 ptr = skip_spaces(ptr); 747 748 /* 749 * Should really check for multiple pids or reject bad args here 750 */ 751 /* What do the arguments in mconsole_reply mean? */ 752 if (sscanf(ptr, "%d", &pid_requested) == 0) { 753 mconsole_reply(req, "Please specify a pid", 1, 0); 754 return; 755 } 756 757 to = find_task_by_pid_ns(pid_requested, &init_pid_ns); 758 if ((to == NULL) || (pid_requested == 0)) { 759 mconsole_reply(req, "Couldn't find that pid", 1, 0); 760 return; 761 } 762 with_console(req, stack_proc, to); 763} 764 765/* 766 * Changed by mconsole_setup, which is __setup, and called before SMP is 767 * active. 768 */ 769static char *notify_socket = NULL; 770 771static int __init mconsole_init(void) 772{ 773 /* long to avoid size mismatch warnings from gcc */ 774 long sock; 775 int err; 776 char file[UNIX_PATH_MAX]; 777 778 if (umid_file_name("mconsole", file, sizeof(file))) 779 return -1; 780 snprintf(mconsole_socket_name, sizeof(file), "%s", file); 781 782 sock = os_create_unix_socket(file, sizeof(file), 1); 783 if (sock < 0) { 784 printk(KERN_ERR "Failed to initialize management console\n"); 785 return 1; 786 } 787 if (os_set_fd_block(sock, 0)) 788 goto out; 789 790 register_reboot_notifier(&reboot_notifier); 791 792 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt, 793 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM, 794 "mconsole", (void *)sock); 795 if (err) { 796 printk(KERN_ERR "Failed to get IRQ for management console\n"); 797 goto out; 798 } 799 800 if (notify_socket != NULL) { 801 notify_socket = kstrdup(notify_socket, GFP_KERNEL); 802 if (notify_socket != NULL) 803 mconsole_notify(notify_socket, MCONSOLE_SOCKET, 804 mconsole_socket_name, 805 strlen(mconsole_socket_name) + 1); 806 else printk(KERN_ERR "mconsole_setup failed to strdup " 807 "string\n"); 808 } 809 810 printk(KERN_INFO "mconsole (version %d) initialized on %s\n", 811 MCONSOLE_VERSION, mconsole_socket_name); 812 return 0; 813 814 out: 815 os_close_file(sock); 816 return 1; 817} 818 819__initcall(mconsole_init); 820 821static ssize_t mconsole_proc_write(struct file *file, 822 const char __user *buffer, size_t count, loff_t *pos) 823{ 824 char *buf; 825 826 buf = kmalloc(count + 1, GFP_KERNEL); 827 if (buf == NULL) 828 return -ENOMEM; 829 830 if (copy_from_user(buf, buffer, count)) { 831 count = -EFAULT; 832 goto out; 833 } 834 835 buf[count] = '\0'; 836 837 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count); 838 out: 839 kfree(buf); 840 return count; 841} 842 843static const struct file_operations mconsole_proc_fops = { 844 .owner = THIS_MODULE, 845 .write = mconsole_proc_write, 846}; 847 848static int create_proc_mconsole(void) 849{ 850 struct proc_dir_entry *ent; 851 852 if (notify_socket == NULL) 853 return 0; 854 855 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops); 856 if (ent == NULL) { 857 printk(KERN_INFO "create_proc_mconsole : create_proc_entry " 858 "failed\n"); 859 return 0; 860 } 861 return 0; 862} 863 864static DEFINE_SPINLOCK(notify_spinlock); 865 866void lock_notify(void) 867{ 868 spin_lock(¬ify_spinlock); 869} 870 871void unlock_notify(void) 872{ 873 spin_unlock(¬ify_spinlock); 874} 875 876__initcall(create_proc_mconsole); 877 878#define NOTIFY "notify:" 879 880static int mconsole_setup(char *str) 881{ 882 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) { 883 str += strlen(NOTIFY); 884 notify_socket = str; 885 } 886 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str); 887 return 1; 888} 889 890__setup("mconsole=", mconsole_setup); 891 892__uml_help(mconsole_setup, 893"mconsole=notify:<socket>\n" 894" Requests that the mconsole driver send a message to the named Unix\n" 895" socket containing the name of the mconsole socket. This also serves\n" 896" to notify outside processes when UML has booted far enough to respond\n" 897" to mconsole requests.\n\n" 898); 899 900static int notify_panic(struct notifier_block *self, unsigned long unused1, 901 void *ptr) 902{ 903 char *message = ptr; 904 905 if (notify_socket == NULL) 906 return 0; 907 908 mconsole_notify(notify_socket, MCONSOLE_PANIC, message, 909 strlen(message) + 1); 910 return 0; 911} 912 913static struct notifier_block panic_exit_notifier = { 914 .notifier_call = notify_panic, 915 .next = NULL, 916 .priority = 1 917}; 918 919static int add_notifier(void) 920{ 921 atomic_notifier_chain_register(&panic_notifier_list, 922 &panic_exit_notifier); 923 return 0; 924} 925 926__initcall(add_notifier); 927 928char *mconsole_notify_socket(void) 929{ 930 return notify_socket; 931} 932 933EXPORT_SYMBOL(mconsole_notify_socket);