Linux Audio

Check our new training course

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