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