]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - arch/um/drivers/mconsole_kern.c
uml: remove fakehd
[mirror_ubuntu-artful-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
AV
307 mconsole_reply(req, "stopped", 0, 0);
308 while (mconsole_get_request(req->originating_fd, req)) {
309 if (req->cmd->handler == mconsole_go)
310 break;
311 if (req->cmd->handler == mconsole_stop) {
312 mconsole_reply(req, "Already stopped", 1, 0);
313 continue;
314 }
315 if (req->cmd->handler == mconsole_sysrq) {
316 struct pt_regs *old_regs;
317 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
318 mconsole_sysrq(req);
319 set_irq_regs(old_regs);
320 continue;
321 }
1da177e4
LT
322 (*req->cmd->handler)(req);
323 }
324 os_set_fd_block(req->originating_fd, 0);
325 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
326 mconsole_reply(req, "", 0, 0);
327}
328
84f48d4f 329static DEFINE_SPINLOCK(mc_devices_lock);
42947cb9 330static LIST_HEAD(mconsole_devices);
1da177e4
LT
331
332void mconsole_register_dev(struct mc_device *new)
333{
84f48d4f
JD
334 spin_lock(&mc_devices_lock);
335 BUG_ON(!list_empty(&new->list));
1da177e4 336 list_add(&new->list, &mconsole_devices);
84f48d4f 337 spin_unlock(&mc_devices_lock);
1da177e4
LT
338}
339
340static struct mc_device *mconsole_find_dev(char *name)
341{
342 struct list_head *ele;
343 struct mc_device *dev;
344
ae2587e4 345 list_for_each(ele, &mconsole_devices) {
1da177e4 346 dev = list_entry(ele, struct mc_device, list);
ae2587e4
JD
347 if (!strncmp(name, dev->name, strlen(dev->name)))
348 return dev;
1da177e4 349 }
ae2587e4 350 return NULL;
1da177e4
LT
351}
352
02dea087
JD
353#define UNPLUGGED_PER_PAGE \
354 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
355
356struct unplugged_pages {
357 struct list_head list;
358 void *pages[UNPLUGGED_PER_PAGE];
359};
360
84f48d4f 361static DECLARE_MUTEX(plug_mem_mutex);
02dea087 362static unsigned long long unplugged_pages_count = 0;
c59bce62 363static LIST_HEAD(unplugged_pages);
02dea087
JD
364static int unplug_index = UNPLUGGED_PER_PAGE;
365
f28169d2 366static int mem_config(char *str, char **error_out)
02dea087
JD
367{
368 unsigned long long diff;
369 int err = -EINVAL, i, add;
370 char *ret;
371
ae2587e4 372 if (str[0] != '=') {
f28169d2 373 *error_out = "Expected '=' after 'mem'";
02dea087 374 goto out;
f28169d2 375 }
02dea087
JD
376
377 str++;
ae2587e4 378 if (str[0] == '-')
02dea087 379 add = 0;
ae2587e4 380 else if (str[0] == '+') {
02dea087
JD
381 add = 1;
382 }
f28169d2
JD
383 else {
384 *error_out = "Expected increment to start with '-' or '+'";
385 goto out;
386 }
02dea087
JD
387
388 str++;
389 diff = memparse(str, &ret);
ae2587e4 390 if (*ret != '\0') {
f28169d2 391 *error_out = "Failed to parse memory increment";
02dea087 392 goto out;
f28169d2 393 }
02dea087
JD
394
395 diff /= PAGE_SIZE;
396
84f48d4f 397 down(&plug_mem_mutex);
ae2587e4 398 for (i = 0; i < diff; i++) {
02dea087
JD
399 struct unplugged_pages *unplugged;
400 void *addr;
401
ae2587e4
JD
402 if (add) {
403 if (list_empty(&unplugged_pages))
02dea087
JD
404 break;
405
406 unplugged = list_entry(unplugged_pages.next,
407 struct unplugged_pages, list);
ae2587e4 408 if (unplug_index > 0)
02dea087
JD
409 addr = unplugged->pages[--unplug_index];
410 else {
411 list_del(&unplugged->list);
412 addr = unplugged;
413 unplug_index = UNPLUGGED_PER_PAGE;
414 }
415
416 free_page((unsigned long) addr);
417 unplugged_pages_count--;
418 }
419 else {
420 struct page *page;
421
422 page = alloc_page(GFP_ATOMIC);
ae2587e4 423 if (page == NULL)
02dea087
JD
424 break;
425
426 unplugged = page_address(page);
ae2587e4 427 if (unplug_index == UNPLUGGED_PER_PAGE) {
02dea087
JD
428 list_add(&unplugged->list, &unplugged_pages);
429 unplug_index = 0;
430 }
431 else {
432 struct list_head *entry = unplugged_pages.next;
433 addr = unplugged;
434
435 unplugged = list_entry(entry,
436 struct unplugged_pages,
437 list);
02dea087 438 err = os_drop_memory(addr, PAGE_SIZE);
ae2587e4
JD
439 if (err) {
440 printk(KERN_ERR "Failed to release "
441 "memory - errno = %d\n", err);
f28169d2 442 *error_out = "Failed to release memory";
84f48d4f 443 goto out_unlock;
f28169d2
JD
444 }
445 unplugged->pages[unplug_index++] = addr;
02dea087
JD
446 }
447
448 unplugged_pages_count++;
449 }
450 }
451
452 err = 0;
84f48d4f
JD
453out_unlock:
454 up(&plug_mem_mutex);
02dea087
JD
455out:
456 return err;
457}
458
459static int mem_get_config(char *name, char *str, int size, char **error_out)
460{
461 char buf[sizeof("18446744073709551615")];
462 int len = 0;
463
464 sprintf(buf, "%ld", uml_physmem);
465 CONFIG_CHUNK(str, size, len, buf, 1);
466
467 return len;
468}
469
470static int mem_id(char **str, int *start_out, int *end_out)
471{
472 *start_out = 0;
473 *end_out = 0;
474
475 return 0;
476}
477
f28169d2 478static int mem_remove(int n, char **error_out)
02dea087 479{
f28169d2 480 *error_out = "Memory doesn't support the remove operation";
02dea087
JD
481 return -EBUSY;
482}
483
484static struct mc_device mem_mc = {
84f48d4f 485 .list = LIST_HEAD_INIT(mem_mc.list),
02dea087
JD
486 .name = "mem",
487 .config = mem_config,
488 .get_config = mem_get_config,
489 .id = mem_id,
490 .remove = mem_remove,
491};
492
97a1fcbb 493static int __init mem_mc_init(void)
02dea087 494{
ae2587e4 495 if (can_drop_memory())
02dea087 496 mconsole_register_dev(&mem_mc);
ae2587e4
JD
497 else printk(KERN_ERR "Can't release memory to the host - memory "
498 "hotplug won't be supported\n");
02dea087
JD
499 return 0;
500}
501
502__initcall(mem_mc_init);
503
1da177e4
LT
504#define CONFIG_BUF_SIZE 64
505
d50084a2 506static void mconsole_get_config(int (*get_config)(char *, char *, int,
1da177e4
LT
507 char **),
508 struct mc_request *req, char *name)
509{
510 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
511 int n, size;
512
ae2587e4 513 if (get_config == NULL) {
1da177e4
LT
514 mconsole_reply(req, "No get_config routine defined", 1, 0);
515 return;
516 }
517
518 error = NULL;
91b165c0 519 size = ARRAY_SIZE(default_buf);
1da177e4
LT
520 buf = default_buf;
521
ae2587e4 522 while (1) {
1da177e4 523 n = (*get_config)(name, buf, size, &error);
ae2587e4 524 if (error != NULL) {
1da177e4
LT
525 mconsole_reply(req, error, 1, 0);
526 goto out;
527 }
528
ae2587e4 529 if (n <= size) {
1da177e4
LT
530 mconsole_reply(req, buf, 0, 0);
531 goto out;
532 }
533
ae2587e4 534 if (buf != default_buf)
1da177e4
LT
535 kfree(buf);
536
537 size = n;
538 buf = kmalloc(size, GFP_KERNEL);
ae2587e4 539 if (buf == NULL) {
1da177e4
LT
540 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
541 return;
542 }
543 }
544 out:
ae2587e4 545 if (buf != default_buf)
1da177e4 546 kfree(buf);
1da177e4
LT
547}
548
549void mconsole_config(struct mc_request *req)
550{
551 struct mc_device *dev;
f28169d2 552 char *ptr = req->request.data, *name, *error_string = "";
1da177e4
LT
553 int err;
554
555 ptr += strlen("config");
ae2587e4
JD
556 while (isspace(*ptr))
557 ptr++;
1da177e4 558 dev = mconsole_find_dev(ptr);
ae2587e4 559 if (dev == NULL) {
1da177e4
LT
560 mconsole_reply(req, "Bad configuration option", 1, 0);
561 return;
562 }
563
564 name = &ptr[strlen(dev->name)];
565 ptr = name;
ae2587e4 566 while ((*ptr != '=') && (*ptr != '\0'))
1da177e4
LT
567 ptr++;
568
ae2587e4 569 if (*ptr == '=') {
f28169d2
JD
570 err = (*dev->config)(name, &error_string);
571 mconsole_reply(req, error_string, err, 0);
1da177e4
LT
572 }
573 else mconsole_get_config(dev->get_config, req, name);
574}
575
576void mconsole_remove(struct mc_request *req)
577{
d50084a2 578 struct mc_device *dev;
29d56cfe 579 char *ptr = req->request.data, *err_msg = "";
3a331a51 580 char error[256];
29d56cfe 581 int err, start, end, n;
1da177e4
LT
582
583 ptr += strlen("remove");
ae2587e4 584 while (isspace(*ptr)) ptr++;
1da177e4 585 dev = mconsole_find_dev(ptr);
ae2587e4 586 if (dev == NULL) {
1da177e4
LT
587 mconsole_reply(req, "Bad remove option", 1, 0);
588 return;
589 }
29d56cfe 590
3a331a51
JD
591 ptr = &ptr[strlen(dev->name)];
592
593 err = 1;
594 n = (*dev->id)(&ptr, &start, &end);
ae2587e4 595 if (n < 0) {
3a331a51
JD
596 err_msg = "Couldn't parse device number";
597 goto out;
598 }
ae2587e4 599 else if ((n < start) || (n > end)) {
3a331a51
JD
600 sprintf(error, "Invalid device number - must be between "
601 "%d and %d", start, end);
602 err_msg = error;
603 goto out;
604 }
29d56cfe 605
f28169d2
JD
606 err_msg = NULL;
607 err = (*dev->remove)(n, &err_msg);
ae2587e4 608 switch(err) {
d40f6d71
JD
609 case 0:
610 err_msg = "";
611 break;
3a331a51 612 case -ENODEV:
ae2587e4 613 if (err_msg == NULL)
f28169d2 614 err_msg = "Device doesn't exist";
3a331a51
JD
615 break;
616 case -EBUSY:
ae2587e4 617 if (err_msg == NULL)
f28169d2 618 err_msg = "Device is currently open";
3a331a51
JD
619 break;
620 default:
621 break;
622 }
623out:
29d56cfe 624 mconsole_reply(req, err_msg, err, 0);
1da177e4
LT
625}
626
f92afe56
JD
627struct mconsole_output {
628 struct list_head list;
629 struct mc_request *req;
630};
631
84f48d4f 632static DEFINE_SPINLOCK(client_lock);
6f517d3f
JD
633static LIST_HEAD(clients);
634static char console_buf[MCONSOLE_MAX_DATA];
6f517d3f
JD
635
636static void console_write(struct console *console, const char *string,
54fa0ba4 637 unsigned int len)
6f517d3f
JD
638{
639 struct list_head *ele;
640 int n;
641
ae2587e4 642 if (list_empty(&clients))
6f517d3f
JD
643 return;
644
54fa0ba4
JD
645 while (len > 0) {
646 n = min((size_t) len, ARRAY_SIZE(console_buf));
647 strncpy(console_buf, string, n);
6f517d3f
JD
648 string += n;
649 len -= n;
6f517d3f 650
ae2587e4 651 list_for_each(ele, &clients) {
f92afe56 652 struct mconsole_output *entry;
6f517d3f 653
f92afe56 654 entry = list_entry(ele, struct mconsole_output, list);
54fa0ba4 655 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
6f517d3f 656 }
6f517d3f
JD
657 }
658}
659
660static struct console mc_console = { .name = "mc",
661 .write = console_write,
a174b30e 662 .flags = CON_ENABLED,
6f517d3f
JD
663 .index = -1 };
664
665static int mc_add_console(void)
666{
667 register_console(&mc_console);
668 return 0;
669}
670
671late_initcall(mc_add_console);
672
673static void with_console(struct mc_request *req, void (*proc)(void *),
674 void *arg)
675{
f92afe56 676 struct mconsole_output entry;
6f517d3f
JD
677 unsigned long flags;
678
f92afe56 679 entry.req = req;
84f48d4f 680 spin_lock_irqsave(&client_lock, flags);
6f517d3f 681 list_add(&entry.list, &clients);
84f48d4f 682 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
683
684 (*proc)(arg);
685
54fa0ba4 686 mconsole_reply_len(req, "", 0, 0, 0);
6f517d3f 687
84f48d4f 688 spin_lock_irqsave(&client_lock, flags);
6f517d3f 689 list_del(&entry.list);
84f48d4f 690 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
691}
692
4111b025 693#ifdef CONFIG_MAGIC_SYSRQ
54fa0ba4
JD
694
695#include <linux/sysrq.h>
696
4111b025
JD
697static void sysrq_proc(void *arg)
698{
699 char *op = arg;
7bea96fd 700 handle_sysrq(*op, NULL);
4111b025
JD
701}
702
703void mconsole_sysrq(struct mc_request *req)
704{
705 char *ptr = req->request.data;
706
707 ptr += strlen("sysrq");
ae2587e4 708 while (isspace(*ptr)) ptr++;
4111b025 709
ae2587e4
JD
710 /*
711 * With 'b', the system will shut down without a chance to reply,
4111b025
JD
712 * so in this case, we reply first.
713 */
ae2587e4 714 if (*ptr == 'b')
4111b025
JD
715 mconsole_reply(req, "", 0, 0);
716
717 with_console(req, sysrq_proc, ptr);
718}
719#else
720void mconsole_sysrq(struct mc_request *req)
721{
722 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
723}
724#endif
725
6f517d3f
JD
726static void stack_proc(void *arg)
727{
728 struct task_struct *from = current, *to = arg;
729
730 to->thread.saved_task = from;
731 switch_to(from, to, from);
732}
733
ae2587e4
JD
734/*
735 * Mconsole stack trace
3eddddcf
JD
736 * Added by Allan Graves, Jeff Dike
737 * Dumps a stacks registers to the linux console.
738 * Usage stack <pid>.
739 */
42fda663 740void mconsole_stack(struct mc_request *req)
3eddddcf 741{
3a331a51
JD
742 char *ptr = req->request.data;
743 int pid_requested= -1;
3eddddcf
JD
744 struct task_struct *to = NULL;
745
ae2587e4
JD
746 /*
747 * Would be nice:
3a331a51 748 * 1) Send showregs output to mconsole.
3eddddcf
JD
749 * 2) Add a way to stack dump all pids.
750 */
751
3a331a51 752 ptr += strlen("stack");
ae2587e4
JD
753 while (isspace(*ptr))
754 ptr++;
3eddddcf 755
ae2587e4
JD
756 /*
757 * Should really check for multiple pids or reject bad args here
758 */
3a331a51 759 /* What do the arguments in mconsole_reply mean? */
ae2587e4 760 if (sscanf(ptr, "%d", &pid_requested) == 0) {
3a331a51
JD
761 mconsole_reply(req, "Please specify a pid", 1, 0);
762 return;
763 }
3eddddcf 764
6f517d3f 765 to = find_task_by_pid(pid_requested);
ae2587e4 766 if ((to == NULL) || (pid_requested == 0)) {
3a331a51
JD
767 mconsole_reply(req, "Couldn't find that pid", 1, 0);
768 return;
769 }
6f517d3f 770 with_console(req, stack_proc, to);
3eddddcf 771}
3eddddcf 772
ae2587e4
JD
773/*
774 * Changed by mconsole_setup, which is __setup, and called before SMP is
1da177e4
LT
775 * active.
776 */
d50084a2 777static char *notify_socket = NULL;
1da177e4 778
97a1fcbb 779static int __init mconsole_init(void)
1da177e4
LT
780{
781 /* long to avoid size mismatch warnings from gcc */
782 long sock;
783 int err;
784 char file[256];
785
ae2587e4
JD
786 if (umid_file_name("mconsole", file, sizeof(file)))
787 return -1;
1da177e4
LT
788 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
789
790 sock = os_create_unix_socket(file, sizeof(file), 1);
ae2587e4
JD
791 if (sock < 0) {
792 printk(KERN_ERR "Failed to initialize management console\n");
793 return 1;
1da177e4
LT
794 }
795
796 register_reboot_notifier(&reboot_notifier);
797
798 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
bd6aa650 799 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
1da177e4 800 "mconsole", (void *)sock);
ae2587e4
JD
801 if (err) {
802 printk(KERN_ERR "Failed to get IRQ for management console\n");
803 return 1;
1da177e4
LT
804 }
805
ae2587e4 806 if (notify_socket != NULL) {
970d6e3a 807 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
ae2587e4 808 if (notify_socket != NULL)
1da177e4 809 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
d50084a2 810 mconsole_socket_name,
1da177e4
LT
811 strlen(mconsole_socket_name) + 1);
812 else printk(KERN_ERR "mconsole_setup failed to strdup "
813 "string\n");
814 }
815
ae2587e4 816 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
1da177e4 817 MCONSOLE_VERSION, mconsole_socket_name);
ae2587e4 818 return 0;
1da177e4
LT
819}
820
821__initcall(mconsole_init);
822
823static int write_proc_mconsole(struct file *file, const char __user *buffer,
824 unsigned long count, void *data)
825{
826 char *buf;
827
828 buf = kmalloc(count + 1, GFP_KERNEL);
ae2587e4
JD
829 if (buf == NULL)
830 return -ENOMEM;
1da177e4 831
ae2587e4 832 if (copy_from_user(buf, buffer, count)) {
1da177e4
LT
833 count = -EFAULT;
834 goto out;
835 }
836
837 buf[count] = '\0';
838
839 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
840 out:
841 kfree(buf);
ae2587e4 842 return count;
1da177e4
LT
843}
844
845static int create_proc_mconsole(void)
846{
847 struct proc_dir_entry *ent;
848
ae2587e4
JD
849 if (notify_socket == NULL)
850 return 0;
1da177e4
LT
851
852 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
ae2587e4
JD
853 if (ent == NULL) {
854 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
855 "failed\n");
856 return 0;
1da177e4
LT
857 }
858
859 ent->read_proc = NULL;
860 ent->write_proc = write_proc_mconsole;
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);