PageRenderTime 138ms CodeModel.GetById 38ms app.highlight 88ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/um/drivers/mconsole_kern.c

https://bitbucket.org/thekraven/iscream_thunderc-2.6.35
C | 933 lines | 745 code | 149 blank | 39 comment | 122 complexity | 37d18107c5b72a26c1008149d9d389da MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.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(&notify_spinlock);
869}
870
871void unlock_notify(void)
872{
873	spin_unlock(&notify_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);