]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - arch/um/drivers/mconsole_kern.c
Merge tag 'efi-urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi into...
[mirror_ubuntu-artful-kernel.git] / arch / um / drivers / mconsole_kern.c
CommitLineData
1da177e4
LT
1/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
827b3f6a 3 * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
1da177e4
LT
4 * Licensed under the GPL
5 */
6
827b3f6a
JD
7#include <linux/console.h>
8#include <linux/ctype.h>
e7d2860b 9#include <linux/string.h>
827b3f6a
JD
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>
b17b0153 16#include <linux/sched/debug.h>
827b3f6a
JD
17#include <linux/proc_fs.h>
18#include <linux/slab.h>
19#include <linux/syscalls.h>
20#include <linux/utsname.h>
36137120
BS
21#include <linux/socket.h>
22#include <linux/un.h>
827b3f6a
JD
23#include <linux/workqueue.h>
24#include <linux/mutex.h>
723a1d77
AV
25#include <linux/fs.h>
26#include <linux/mount.h>
27#include <linux/file.h>
7c0f6ba6 28#include <linux/uaccess.h>
a3a85a76 29#include <asm/switch_to.h>
827b3f6a 30
37185b33
AV
31#include <init.h>
32#include <irq_kern.h>
33#include <irq_user.h>
34#include <kern_util.h>
1da177e4
LT
35#include "mconsole.h"
36#include "mconsole_kern.h"
37185b33 37#include <os.h>
1da177e4 38
d50084a2 39static int do_unlink_socket(struct notifier_block *notifier,
1da177e4
LT
40 unsigned long what, void *data)
41{
ae2587e4 42 return mconsole_unlink_socket();
1da177e4
LT
43}
44
45
46static struct notifier_block reboot_notifier = {
47 .notifier_call = do_unlink_socket,
48 .priority = 0,
49};
50
d50084a2 51/* Safe without explicit locking for now. Tasklets provide their own
1da177e4
LT
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
9010772c 56static LIST_HEAD(mc_requests);
1da177e4 57
6d5aefb8 58static void mc_work_proc(struct work_struct *unused)
1da177e4
LT
59{
60 struct mconsole_entry *req;
61 unsigned long flags;
62
ae2587e4 63 while (!list_empty(&mc_requests)) {
dbdb4c06 64 local_irq_save(flags);
ae2587e4 65 req = list_entry(mc_requests.next, struct mconsole_entry, list);
1da177e4
LT
66 list_del(&req->list);
67 local_irq_restore(flags);
68 req->request.cmd->handler(&req->request);
69 kfree(req);
70 }
71}
72
6d5aefb8 73static DECLARE_WORK(mconsole_work, mc_work_proc);
1da177e4 74
7bea96fd 75static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
1da177e4
LT
76{
77 /* long to avoid size mismatch warnings from gcc */
78 long fd;
79 struct mconsole_entry *new;
3a51237d 80 static struct mc_request req; /* that's OK */
1da177e4
LT
81
82 fd = (long) dev_id;
ae2587e4
JD
83 while (mconsole_get_request(fd, &req)) {
84 if (req.cmd->context == MCONSOLE_INTR)
1da177e4
LT
85 (*req.cmd->handler)(&req);
86 else {
60baa158 87 new = kmalloc(sizeof(*new), GFP_NOWAIT);
ae2587e4 88 if (new == NULL)
1da177e4
LT
89 mconsole_reply(&req, "Out of memory", 1, 0);
90 else {
91 new->request = req;
3a51237d 92 new->request.regs = get_irq_regs()->regs;
1da177e4
LT
93 list_add(&new->list, &mc_requests);
94 }
95 }
96 }
ae2587e4 97 if (!list_empty(&mc_requests))
1da177e4
LT
98 schedule_work(&mconsole_work);
99 reactivate_fd(fd, MCONSOLE_IRQ);
ae2587e4 100 return IRQ_HANDLED;
1da177e4
LT
101}
102
103void mconsole_version(struct mc_request *req)
104{
105 char version[256];
106
e9ff3990 107 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
ae2587e4
JD
108 utsname()->nodename, utsname()->release, utsname()->version,
109 utsname()->machine);
1da177e4
LT
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);
ae2587e4 121 printk(KERN_WARNING "%.*s", len, ptr);
1da177e4
LT
122 mconsole_reply(req, "", 0, 0);
123}
124
1da177e4
LT
125void mconsole_proc(struct mc_request *req)
126{
17cf22c3 127 struct vfsmount *mnt = task_active_pid_ns(current)->proc_mnt;
1da177e4
LT
128 char *buf;
129 int len;
723a1d77 130 struct file *file;
1da177e4
LT
131 int first_chunk = 1;
132 char *ptr = req->request.data;
133
134 ptr += strlen("proc");
e7d2860b 135 ptr = skip_spaces(ptr);
1da177e4 136
378c6520 137 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0);
723a1d77 138 if (IS_ERR(file)) {
1da177e4 139 mconsole_reply(req, "Failed to open file", 1, 0);
723a1d77 140 printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
1da177e4
LT
141 goto out;
142 }
143
144 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
ae2587e4 145 if (buf == NULL) {
1da177e4 146 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
723a1d77 147 goto out_fput;
1da177e4
LT
148 }
149
723a1d77 150 do {
c9036e9f 151 loff_t pos = file->f_pos;
723a1d77
AV
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;
1da177e4
LT
157 if (len < 0) {
158 mconsole_reply(req, "Read of file failed", 1, 0);
159 goto out_free;
160 }
ae2587e4 161 /* Begin the file content on his own line. */
1da177e4
LT
162 if (first_chunk) {
163 mconsole_reply(req, "\n", 0, 1);
164 first_chunk = 0;
165 }
723a1d77
AV
166 buf[len] = '\0';
167 mconsole_reply(req, buf, 0, (len > 0));
168 } while (len > 0);
1da177e4
LT
169 out_free:
170 kfree(buf);
723a1d77
AV
171 out_fput:
172 fput(file);
173 out: ;
1da177e4
LT
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\
db805812 187 cad - invoke the Ctrl-Alt-Del handler \n\
1da177e4
LT
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\
3eddddcf 192 stack <pid> - returns the stack of the specified pid\n\
1da177e4
LT
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
1da177e4
LT
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);
3a51237d 227 mconsole_reply(req, "stopped", 0, 0);
cc0be0fb
KS
228 for (;;) {
229 if (!mconsole_get_request(req->originating_fd, req))
230 continue;
3a51237d
AV
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 }
1da177e4
LT
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
84f48d4f 251static DEFINE_SPINLOCK(mc_devices_lock);
42947cb9 252static LIST_HEAD(mconsole_devices);
1da177e4
LT
253
254void mconsole_register_dev(struct mc_device *new)
255{
84f48d4f
JD
256 spin_lock(&mc_devices_lock);
257 BUG_ON(!list_empty(&new->list));
1da177e4 258 list_add(&new->list, &mconsole_devices);
84f48d4f 259 spin_unlock(&mc_devices_lock);
1da177e4
LT
260}
261
262static struct mc_device *mconsole_find_dev(char *name)
263{
264 struct list_head *ele;
265 struct mc_device *dev;
266
ae2587e4 267 list_for_each(ele, &mconsole_devices) {
1da177e4 268 dev = list_entry(ele, struct mc_device, list);
ae2587e4
JD
269 if (!strncmp(name, dev->name, strlen(dev->name)))
270 return dev;
1da177e4 271 }
ae2587e4 272 return NULL;
1da177e4
LT
273}
274
02dea087
JD
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
e98fa281 283static DEFINE_MUTEX(plug_mem_mutex);
02dea087 284static unsigned long long unplugged_pages_count = 0;
c59bce62 285static LIST_HEAD(unplugged_pages);
02dea087
JD
286static int unplug_index = UNPLUGGED_PER_PAGE;
287
f28169d2 288static int mem_config(char *str, char **error_out)
02dea087
JD
289{
290 unsigned long long diff;
291 int err = -EINVAL, i, add;
292 char *ret;
293
ae2587e4 294 if (str[0] != '=') {
f28169d2 295 *error_out = "Expected '=' after 'mem'";
02dea087 296 goto out;
f28169d2 297 }
02dea087
JD
298
299 str++;
ae2587e4 300 if (str[0] == '-')
02dea087 301 add = 0;
ae2587e4 302 else if (str[0] == '+') {
02dea087
JD
303 add = 1;
304 }
f28169d2
JD
305 else {
306 *error_out = "Expected increment to start with '-' or '+'";
307 goto out;
308 }
02dea087
JD
309
310 str++;
311 diff = memparse(str, &ret);
ae2587e4 312 if (*ret != '\0') {
f28169d2 313 *error_out = "Failed to parse memory increment";
02dea087 314 goto out;
f28169d2 315 }
02dea087
JD
316
317 diff /= PAGE_SIZE;
318
e98fa281 319 mutex_lock(&plug_mem_mutex);
ae2587e4 320 for (i = 0; i < diff; i++) {
02dea087
JD
321 struct unplugged_pages *unplugged;
322 void *addr;
323
ae2587e4
JD
324 if (add) {
325 if (list_empty(&unplugged_pages))
02dea087
JD
326 break;
327
328 unplugged = list_entry(unplugged_pages.next,
329 struct unplugged_pages, list);
ae2587e4 330 if (unplug_index > 0)
02dea087
JD
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);
ae2587e4 345 if (page == NULL)
02dea087
JD
346 break;
347
348 unplugged = page_address(page);
ae2587e4 349 if (unplug_index == UNPLUGGED_PER_PAGE) {
02dea087
JD
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);
02dea087 360 err = os_drop_memory(addr, PAGE_SIZE);
ae2587e4
JD
361 if (err) {
362 printk(KERN_ERR "Failed to release "
363 "memory - errno = %d\n", err);
f28169d2 364 *error_out = "Failed to release memory";
84f48d4f 365 goto out_unlock;
f28169d2
JD
366 }
367 unplugged->pages[unplug_index++] = addr;
02dea087
JD
368 }
369
370 unplugged_pages_count++;
371 }
372 }
373
374 err = 0;
84f48d4f 375out_unlock:
e98fa281 376 mutex_unlock(&plug_mem_mutex);
02dea087
JD
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
f28169d2 400static int mem_remove(int n, char **error_out)
02dea087 401{
f28169d2 402 *error_out = "Memory doesn't support the remove operation";
02dea087
JD
403 return -EBUSY;
404}
405
406static struct mc_device mem_mc = {
84f48d4f 407 .list = LIST_HEAD_INIT(mem_mc.list),
02dea087
JD
408 .name = "mem",
409 .config = mem_config,
410 .get_config = mem_get_config,
411 .id = mem_id,
412 .remove = mem_remove,
413};
414
97a1fcbb 415static int __init mem_mc_init(void)
02dea087 416{
ae2587e4 417 if (can_drop_memory())
02dea087 418 mconsole_register_dev(&mem_mc);
ae2587e4
JD
419 else printk(KERN_ERR "Can't release memory to the host - memory "
420 "hotplug won't be supported\n");
02dea087
JD
421 return 0;
422}
423
424__initcall(mem_mc_init);
425
1da177e4
LT
426#define CONFIG_BUF_SIZE 64
427
d50084a2 428static void mconsole_get_config(int (*get_config)(char *, char *, int,
1da177e4
LT
429 char **),
430 struct mc_request *req, char *name)
431{
432 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
433 int n, size;
434
ae2587e4 435 if (get_config == NULL) {
1da177e4
LT
436 mconsole_reply(req, "No get_config routine defined", 1, 0);
437 return;
438 }
439
440 error = NULL;
91b165c0 441 size = ARRAY_SIZE(default_buf);
1da177e4
LT
442 buf = default_buf;
443
ae2587e4 444 while (1) {
1da177e4 445 n = (*get_config)(name, buf, size, &error);
ae2587e4 446 if (error != NULL) {
1da177e4
LT
447 mconsole_reply(req, error, 1, 0);
448 goto out;
449 }
450
ae2587e4 451 if (n <= size) {
1da177e4
LT
452 mconsole_reply(req, buf, 0, 0);
453 goto out;
454 }
455
ae2587e4 456 if (buf != default_buf)
1da177e4
LT
457 kfree(buf);
458
459 size = n;
460 buf = kmalloc(size, GFP_KERNEL);
ae2587e4 461 if (buf == NULL) {
1da177e4
LT
462 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
463 return;
464 }
465 }
466 out:
ae2587e4 467 if (buf != default_buf)
1da177e4 468 kfree(buf);
1da177e4
LT
469}
470
471void mconsole_config(struct mc_request *req)
472{
473 struct mc_device *dev;
f28169d2 474 char *ptr = req->request.data, *name, *error_string = "";
1da177e4
LT
475 int err;
476
477 ptr += strlen("config");
e7d2860b 478 ptr = skip_spaces(ptr);
1da177e4 479 dev = mconsole_find_dev(ptr);
ae2587e4 480 if (dev == NULL) {
1da177e4
LT
481 mconsole_reply(req, "Bad configuration option", 1, 0);
482 return;
483 }
484
485 name = &ptr[strlen(dev->name)];
486 ptr = name;
ae2587e4 487 while ((*ptr != '=') && (*ptr != '\0'))
1da177e4
LT
488 ptr++;
489
ae2587e4 490 if (*ptr == '=') {
f28169d2
JD
491 err = (*dev->config)(name, &error_string);
492 mconsole_reply(req, error_string, err, 0);
1da177e4
LT
493 }
494 else mconsole_get_config(dev->get_config, req, name);
495}
496
497void mconsole_remove(struct mc_request *req)
498{
d50084a2 499 struct mc_device *dev;
29d56cfe 500 char *ptr = req->request.data, *err_msg = "";
3a331a51 501 char error[256];
29d56cfe 502 int err, start, end, n;
1da177e4
LT
503
504 ptr += strlen("remove");
e7d2860b 505 ptr = skip_spaces(ptr);
1da177e4 506 dev = mconsole_find_dev(ptr);
ae2587e4 507 if (dev == NULL) {
1da177e4
LT
508 mconsole_reply(req, "Bad remove option", 1, 0);
509 return;
510 }
29d56cfe 511
3a331a51
JD
512 ptr = &ptr[strlen(dev->name)];
513
514 err = 1;
515 n = (*dev->id)(&ptr, &start, &end);
ae2587e4 516 if (n < 0) {
3a331a51
JD
517 err_msg = "Couldn't parse device number";
518 goto out;
519 }
ae2587e4 520 else if ((n < start) || (n > end)) {
3a331a51
JD
521 sprintf(error, "Invalid device number - must be between "
522 "%d and %d", start, end);
523 err_msg = error;
524 goto out;
525 }
29d56cfe 526
f28169d2
JD
527 err_msg = NULL;
528 err = (*dev->remove)(n, &err_msg);
ae2587e4 529 switch(err) {
d40f6d71
JD
530 case 0:
531 err_msg = "";
532 break;
3a331a51 533 case -ENODEV:
ae2587e4 534 if (err_msg == NULL)
f28169d2 535 err_msg = "Device doesn't exist";
3a331a51
JD
536 break;
537 case -EBUSY:
ae2587e4 538 if (err_msg == NULL)
f28169d2 539 err_msg = "Device is currently open";
3a331a51
JD
540 break;
541 default:
542 break;
543 }
544out:
29d56cfe 545 mconsole_reply(req, err_msg, err, 0);
1da177e4
LT
546}
547
f92afe56
JD
548struct mconsole_output {
549 struct list_head list;
550 struct mc_request *req;
551};
552
84f48d4f 553static DEFINE_SPINLOCK(client_lock);
6f517d3f
JD
554static LIST_HEAD(clients);
555static char console_buf[MCONSOLE_MAX_DATA];
6f517d3f
JD
556
557static void console_write(struct console *console, const char *string,
54fa0ba4 558 unsigned int len)
6f517d3f
JD
559{
560 struct list_head *ele;
561 int n;
562
ae2587e4 563 if (list_empty(&clients))
6f517d3f
JD
564 return;
565
54fa0ba4
JD
566 while (len > 0) {
567 n = min((size_t) len, ARRAY_SIZE(console_buf));
568 strncpy(console_buf, string, n);
6f517d3f
JD
569 string += n;
570 len -= n;
6f517d3f 571
ae2587e4 572 list_for_each(ele, &clients) {
f92afe56 573 struct mconsole_output *entry;
6f517d3f 574
f92afe56 575 entry = list_entry(ele, struct mconsole_output, list);
54fa0ba4 576 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
6f517d3f 577 }
6f517d3f
JD
578 }
579}
580
581static struct console mc_console = { .name = "mc",
582 .write = console_write,
a174b30e 583 .flags = CON_ENABLED,
6f517d3f
JD
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{
f92afe56 597 struct mconsole_output entry;
6f517d3f
JD
598 unsigned long flags;
599
f92afe56 600 entry.req = req;
84f48d4f 601 spin_lock_irqsave(&client_lock, flags);
6f517d3f 602 list_add(&entry.list, &clients);
84f48d4f 603 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
604
605 (*proc)(arg);
606
54fa0ba4 607 mconsole_reply_len(req, "", 0, 0, 0);
6f517d3f 608
84f48d4f 609 spin_lock_irqsave(&client_lock, flags);
6f517d3f 610 list_del(&entry.list);
84f48d4f 611 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
612}
613
4111b025 614#ifdef CONFIG_MAGIC_SYSRQ
54fa0ba4
JD
615
616#include <linux/sysrq.h>
617
4111b025
JD
618static void sysrq_proc(void *arg)
619{
620 char *op = arg;
f335397d 621 handle_sysrq(*op);
4111b025
JD
622}
623
624void mconsole_sysrq(struct mc_request *req)
625{
626 char *ptr = req->request.data;
627
628 ptr += strlen("sysrq");
e7d2860b 629 ptr = skip_spaces(ptr);
4111b025 630
ae2587e4
JD
631 /*
632 * With 'b', the system will shut down without a chance to reply,
4111b025
JD
633 * so in this case, we reply first.
634 */
ae2587e4 635 if (*ptr == 'b')
4111b025
JD
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
6f517d3f
JD
647static void stack_proc(void *arg)
648{
a1850e9c 649 struct task_struct *task = arg;
6f517d3f 650
a1850e9c 651 show_stack(task, NULL);
6f517d3f
JD
652}
653
ae2587e4
JD
654/*
655 * Mconsole stack trace
3eddddcf
JD
656 * Added by Allan Graves, Jeff Dike
657 * Dumps a stacks registers to the linux console.
658 * Usage stack <pid>.
659 */
42fda663 660void mconsole_stack(struct mc_request *req)
3eddddcf 661{
3a331a51
JD
662 char *ptr = req->request.data;
663 int pid_requested= -1;
3eddddcf
JD
664 struct task_struct *to = NULL;
665
ae2587e4
JD
666 /*
667 * Would be nice:
3a331a51 668 * 1) Send showregs output to mconsole.
3eddddcf
JD
669 * 2) Add a way to stack dump all pids.
670 */
671
3a331a51 672 ptr += strlen("stack");
e7d2860b 673 ptr = skip_spaces(ptr);
3eddddcf 674
ae2587e4
JD
675 /*
676 * Should really check for multiple pids or reject bad args here
677 */
3a331a51 678 /* What do the arguments in mconsole_reply mean? */
ae2587e4 679 if (sscanf(ptr, "%d", &pid_requested) == 0) {
3a331a51
JD
680 mconsole_reply(req, "Please specify a pid", 1, 0);
681 return;
682 }
3eddddcf 683
827b3f6a 684 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
ae2587e4 685 if ((to == NULL) || (pid_requested == 0)) {
3a331a51
JD
686 mconsole_reply(req, "Couldn't find that pid", 1, 0);
687 return;
688 }
6f517d3f 689 with_console(req, stack_proc, to);
3eddddcf 690}
3eddddcf 691
ae2587e4
JD
692/*
693 * Changed by mconsole_setup, which is __setup, and called before SMP is
1da177e4
LT
694 * active.
695 */
d50084a2 696static char *notify_socket = NULL;
1da177e4 697
97a1fcbb 698static int __init mconsole_init(void)
1da177e4
LT
699{
700 /* long to avoid size mismatch warnings from gcc */
701 long sock;
702 int err;
36137120 703 char file[UNIX_PATH_MAX];
1da177e4 704
ae2587e4
JD
705 if (umid_file_name("mconsole", file, sizeof(file)))
706 return -1;
1da177e4
LT
707 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
708
709 sock = os_create_unix_socket(file, sizeof(file), 1);
ae2587e4
JD
710 if (sock < 0) {
711 printk(KERN_ERR "Failed to initialize management console\n");
712 return 1;
1da177e4 713 }
438ee679
JD
714 if (os_set_fd_block(sock, 0))
715 goto out;
1da177e4
LT
716
717 register_reboot_notifier(&reboot_notifier);
718
719 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
aab94460 720 IRQF_SHARED, "mconsole", (void *)sock);
ae2587e4
JD
721 if (err) {
722 printk(KERN_ERR "Failed to get IRQ for management console\n");
438ee679 723 goto out;
1da177e4
LT
724 }
725
ae2587e4 726 if (notify_socket != NULL) {
970d6e3a 727 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
ae2587e4 728 if (notify_socket != NULL)
1da177e4 729 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
d50084a2 730 mconsole_socket_name,
1da177e4
LT
731 strlen(mconsole_socket_name) + 1);
732 else printk(KERN_ERR "mconsole_setup failed to strdup "
733 "string\n");
734 }
735
ae2587e4 736 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
1da177e4 737 MCONSOLE_VERSION, mconsole_socket_name);
ae2587e4 738 return 0;
438ee679
JD
739
740 out:
741 os_close_file(sock);
742 return 1;
1da177e4
LT
743}
744
745__initcall(mconsole_init);
746
6613c5e8
AD
747static ssize_t mconsole_proc_write(struct file *file,
748 const char __user *buffer, size_t count, loff_t *pos)
1da177e4
LT
749{
750 char *buf;
751
793b796e
AV
752 buf = memdup_user_nul(buffer, count);
753 if (IS_ERR(buf))
754 return PTR_ERR(buf);
1da177e4
LT
755
756 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
1da177e4 757 kfree(buf);
ae2587e4 758 return count;
1da177e4
LT
759}
760
6613c5e8
AD
761static const struct file_operations mconsole_proc_fops = {
762 .owner = THIS_MODULE,
763 .write = mconsole_proc_write,
6038f373 764 .llseek = noop_llseek,
6613c5e8
AD
765};
766
1da177e4
LT
767static int create_proc_mconsole(void)
768{
769 struct proc_dir_entry *ent;
770
ae2587e4
JD
771 if (notify_socket == NULL)
772 return 0;
1da177e4 773
6613c5e8 774 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
ae2587e4 775 if (ent == NULL) {
4554eb90 776 printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
ae2587e4 777 return 0;
1da177e4 778 }
ae2587e4 779 return 0;
1da177e4
LT
780}
781
782static DEFINE_SPINLOCK(notify_spinlock);
783
784void lock_notify(void)
785{
786 spin_lock(&notify_spinlock);
787}
788
789void unlock_notify(void)
790{
791 spin_unlock(&notify_spinlock);
792}
793
794__initcall(create_proc_mconsole);
795
088bec41 796#define NOTIFY "notify:"
1da177e4
LT
797
798static int mconsole_setup(char *str)
799{
ae2587e4 800 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
1da177e4
LT
801 str += strlen(NOTIFY);
802 notify_socket = str;
803 }
804 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
ae2587e4 805 return 1;
1da177e4
LT
806}
807
088bec41 808__setup("mconsole=", mconsole_setup);
1da177e4
LT
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
ae2587e4
JD
823 if (notify_socket == NULL)
824 return 0;
1da177e4 825
d50084a2 826 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
1da177e4 827 strlen(message) + 1);
ae2587e4 828 return 0;
1da177e4
LT
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{
e041c683
AS
839 atomic_notifier_chain_register(&panic_notifier_list,
840 &panic_exit_notifier);
ae2587e4 841 return 0;
1da177e4
LT
842}
843
844__initcall(add_notifier);
845
846char *mconsole_notify_socket(void)
847{
ae2587e4 848 return notify_socket;
1da177e4
LT
849}
850
851EXPORT_SYMBOL(mconsole_notify_socket);