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