Loading...
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(¬ify_spinlock);
808}
809
810void unlock_notify(void)
811{
812 spin_unlock(¬ify_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);
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(¬ify_spinlock);
783}
784
785void unlock_notify(void)
786{
787 spin_unlock(¬ify_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);