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