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