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