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