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