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