1
2
3
4
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
52
53
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
78 long fd;
79 struct mconsole_entry *new;
80 static struct mc_request req;
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
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
629
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
652
653
654
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
664
665
666
667
668 ptr += strlen("stack");
669 ptr = skip_spaces(ptr);
670
671
672
673
674
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
690
691
692static char *notify_socket = NULL;
693
694static int __init mconsole_init(void)
695{
696
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);
848