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 34 static int do_unlink_socket(struct notifier_block *notifier, 35 unsigned long what, void *data) 36 { 37 return mconsole_unlink_socket(); 38 } 39 40 41 static 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 51 static LIST_HEAD(mc_requests); 52 53 static 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 68 static DECLARE_WORK(mconsole_work, mc_work_proc); 69 70 static 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 98 void 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 108 void 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 125 void 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 193 void 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 267 void mconsole_help(struct mc_request *req) 268 { 269 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0); 270 } 271 272 void mconsole_halt(struct mc_request *req) 273 { 274 mconsole_reply(req, "", 0, 0); 275 machine_halt(); 276 } 277 278 void mconsole_reboot(struct mc_request *req) 279 { 280 mconsole_reply(req, "", 0, 0); 281 machine_restart(NULL); 282 } 283 284 void mconsole_cad(struct mc_request *req) 285 { 286 mconsole_reply(req, "", 0, 0); 287 ctrl_alt_del(); 288 } 289 290 void mconsole_go(struct mc_request *req) 291 { 292 mconsole_reply(req, "Not stopped", 1, 0); 293 } 294 295 void 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 323 static DEFINE_SPINLOCK(mc_devices_lock); 324 static LIST_HEAD(mconsole_devices); 325 326 void 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 334 static 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 350 struct unplugged_pages { 351 struct list_head list; 352 void *pages[UNPLUGGED_PER_PAGE]; 353 }; 354 355 static DEFINE_MUTEX(plug_mem_mutex); 356 static unsigned long long unplugged_pages_count = 0; 357 static LIST_HEAD(unplugged_pages); 358 static int unplug_index = UNPLUGGED_PER_PAGE; 359 360 static 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; 447 out_unlock: 448 mutex_unlock(&plug_mem_mutex); 449 out: 450 return err; 451 } 452 453 static 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 464 static 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 472 static int mem_remove(int n, char **error_out) 473 { 474 *error_out = "Memory doesn't support the remove operation"; 475 return -EBUSY; 476 } 477 478 static 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 487 static 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 500 static 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 543 void 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 569 void 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 } 616 out: 617 mconsole_reply(req, err_msg, err, 0); 618 } 619 620 struct mconsole_output { 621 struct list_head list; 622 struct mc_request *req; 623 }; 624 625 static DEFINE_SPINLOCK(client_lock); 626 static LIST_HEAD(clients); 627 static char console_buf[MCONSOLE_MAX_DATA]; 628 629 static 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 653 static struct console mc_console = { .name = "mc", 654 .write = console_write, 655 .flags = CON_ENABLED, 656 .index = -1 }; 657 658 static int mc_add_console(void) 659 { 660 register_console(&mc_console); 661 return 0; 662 } 663 664 late_initcall(mc_add_console); 665 666 static 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 690 static void sysrq_proc(void *arg) 691 { 692 char *op = arg; 693 handle_sysrq(*op, NULL); 694 } 695 696 void 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 713 void mconsole_sysrq(struct mc_request *req) 714 { 715 mconsole_reply(req, "Sysrq not compiled in", 1, 0); 716 } 717 #endif 718 719 static 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 */ 733 void 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 */ 769 static char *notify_socket = NULL; 770 771 static 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 821 static 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 843 static const struct file_operations mconsole_proc_fops = { 844 .owner = THIS_MODULE, 845 .write = mconsole_proc_write, 846 }; 847 848 static 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 864 static DEFINE_SPINLOCK(notify_spinlock); 865 866 void lock_notify(void) 867 { 868 spin_lock(¬ify_spinlock); 869 } 870 871 void unlock_notify(void) 872 { 873 spin_unlock(¬ify_spinlock); 874 } 875 876 __initcall(create_proc_mconsole); 877 878 #define NOTIFY "notify:" 879 880 static 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 900 static 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 913 static struct notifier_block panic_exit_notifier = { 914 .notifier_call = notify_panic, 915 .next = NULL, 916 .priority = 1 917 }; 918 919 static 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 928 char *mconsole_notify_socket(void) 929 { 930 return notify_socket; 931 } 932 933 EXPORT_SYMBOL(mconsole_notify_socket); 934