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