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