Linux Audio

Check our new training course

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