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