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
134 ptr += strlen("proc");
135 ptr = skip_spaces(ptr);
136
137 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0);
138 if (IS_ERR(file)) {
139 mconsole_reply(req, "Failed to open file", 1, 0);
140 printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
141 goto out;
142 }
143
144 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
145 if (buf == NULL) {
146 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
147 goto out_fput;
148 }
149
150 do {
151 loff_t pos = file->f_pos;
152 mm_segment_t old_fs = get_fs();
153 set_fs(KERNEL_DS);
154 len = vfs_read(file, buf, PAGE_SIZE - 1, &pos);
155 set_fs(old_fs);
156 file->f_pos = pos;
157 if (len < 0) {
158 mconsole_reply(req, "Read of file failed", 1, 0);
159 goto out_free;
160 }
161
162 if (first_chunk) {
163 mconsole_reply(req, "\n", 0, 1);
164 first_chunk = 0;
165 }
166 buf[len] = '\0';
167 mconsole_reply(req, buf, 0, (len > 0));
168 } while (len > 0);
169 out_free:
170 kfree(buf);
171 out_fput:
172 fput(file);
173 out: ;
174}
175
176#define UML_MCONSOLE_HELPTEXT \
177"Commands: \n\
178 version - Get kernel version \n\
179 help - Print this message \n\
180 halt - Halt UML \n\
181 reboot - Reboot UML \n\
182 config <dev>=<config> - Add a new device to UML; \n\
183 same syntax as command line \n\
184 config <dev> - Query the configuration of a device \n\
185 remove <dev> - Remove a device from UML \n\
186 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
187 cad - invoke the Ctrl-Alt-Del handler \n\
188 stop - pause the UML; it will do nothing until it receives a 'go' \n\
189 go - continue the UML after a 'stop' \n\
190 log <string> - make UML enter <string> into the kernel log\n\
191 proc <file> - returns the contents of the UML's /proc/<file>\n\
192 stack <pid> - returns the stack of the specified pid\n\
193"
194
195void mconsole_help(struct mc_request *req)
196{
197 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
198}
199
200void mconsole_halt(struct mc_request *req)
201{
202 mconsole_reply(req, "", 0, 0);
203 machine_halt();
204}
205
206void mconsole_reboot(struct mc_request *req)
207{
208 mconsole_reply(req, "", 0, 0);
209 machine_restart(NULL);
210}
211
212void mconsole_cad(struct mc_request *req)
213{
214 mconsole_reply(req, "", 0, 0);
215 ctrl_alt_del();
216}
217
218void mconsole_go(struct mc_request *req)
219{
220 mconsole_reply(req, "Not stopped", 1, 0);
221}
222
223void mconsole_stop(struct mc_request *req)
224{
225 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
226 os_set_fd_block(req->originating_fd, 1);
227 mconsole_reply(req, "stopped", 0, 0);
228 for (;;) {
229 if (!mconsole_get_request(req->originating_fd, req))
230 continue;
231 if (req->cmd->handler == mconsole_go)
232 break;
233 if (req->cmd->handler == mconsole_stop) {
234 mconsole_reply(req, "Already stopped", 1, 0);
235 continue;
236 }
237 if (req->cmd->handler == mconsole_sysrq) {
238 struct pt_regs *old_regs;
239 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
240 mconsole_sysrq(req);
241 set_irq_regs(old_regs);
242 continue;
243 }
244 (*req->cmd->handler)(req);
245 }
246 os_set_fd_block(req->originating_fd, 0);
247 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
248 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
633
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
651 show_stack(task, NULL);
652}
653
654
655
656
657
658
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
668
669
670
671
672 ptr += strlen("stack");
673 ptr = skip_spaces(ptr);
674
675
676
677
678
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
692
693
694
695
696static char *notify_socket = NULL;
697
698static int __init mconsole_init(void)
699{
700
701 long sock;
702 int err;
703 char file[UNIX_PATH_MAX];
704
705 if (umid_file_name("mconsole", file, sizeof(file)))
706 return -1;
707 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
708
709 sock = os_create_unix_socket(file, sizeof(file), 1);
710 if (sock < 0) {
711 printk(KERN_ERR "Failed to initialize management console\n");
712 return 1;
713 }
714 if (os_set_fd_block(sock, 0))
715 goto out;
716
717 register_reboot_notifier(&reboot_notifier);
718
719 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
720 IRQF_SHARED, "mconsole", (void *)sock);
721 if (err) {
722 printk(KERN_ERR "Failed to get IRQ for management console\n");
723 goto out;
724 }
725
726 if (notify_socket != NULL) {
727 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
728 if (notify_socket != NULL)
729 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
730 mconsole_socket_name,
731 strlen(mconsole_socket_name) + 1);
732 else printk(KERN_ERR "mconsole_setup failed to strdup "
733 "string\n");
734 }
735
736 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
737 MCONSOLE_VERSION, mconsole_socket_name);
738 return 0;
739
740 out:
741 os_close_file(sock);
742 return 1;
743}
744
745__initcall(mconsole_init);
746
747static ssize_t mconsole_proc_write(struct file *file,
748 const char __user *buffer, size_t count, loff_t *pos)
749{
750 char *buf;
751
752 buf = memdup_user_nul(buffer, count);
753 if (IS_ERR(buf))
754 return PTR_ERR(buf);
755
756 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
757 kfree(buf);
758 return count;
759}
760
761static const struct file_operations mconsole_proc_fops = {
762 .owner = THIS_MODULE,
763 .write = mconsole_proc_write,
764 .llseek = noop_llseek,
765};
766
767static int create_proc_mconsole(void)
768{
769 struct proc_dir_entry *ent;
770
771 if (notify_socket == NULL)
772 return 0;
773
774 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
775 if (ent == NULL) {
776 printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
777 return 0;
778 }
779 return 0;
780}
781
782static DEFINE_SPINLOCK(notify_spinlock);
783
784void lock_notify(void)
785{
786 spin_lock(¬ify_spinlock);
787}
788
789void unlock_notify(void)
790{
791 spin_unlock(¬ify_spinlock);
792}
793
794__initcall(create_proc_mconsole);
795
796#define NOTIFY "notify:"
797
798static int mconsole_setup(char *str)
799{
800 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
801 str += strlen(NOTIFY);
802 notify_socket = str;
803 }
804 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
805 return 1;
806}
807
808__setup("mconsole=", mconsole_setup);
809
810__uml_help(mconsole_setup,
811"mconsole=notify:<socket>\n"
812" Requests that the mconsole driver send a message to the named Unix\n"
813" socket containing the name of the mconsole socket. This also serves\n"
814" to notify outside processes when UML has booted far enough to respond\n"
815" to mconsole requests.\n\n"
816);
817
818static int notify_panic(struct notifier_block *self, unsigned long unused1,
819 void *ptr)
820{
821 char *message = ptr;
822
823 if (notify_socket == NULL)
824 return 0;
825
826 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
827 strlen(message) + 1);
828 return 0;
829}
830
831static struct notifier_block panic_exit_notifier = {
832 .notifier_call = notify_panic,
833 .next = NULL,
834 .priority = 1
835};
836
837static int add_notifier(void)
838{
839 atomic_notifier_chain_register(&panic_notifier_list,
840 &panic_exit_notifier);
841 return 0;
842}
843
844__initcall(add_notifier);
845
846char *mconsole_notify_socket(void)
847{
848 return notify_socket;
849}
850
851EXPORT_SYMBOL(mconsole_notify_socket);
852