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