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