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