blob: 6d7173fc55a395e3a1e1ef7f83263e1fa2711808 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
3 * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com)
4 * Licensed under the GPL
5 */
6
7#include "linux/kernel.h"
8#include "linux/slab.h"
9#include "linux/init.h"
10#include "linux/notifier.h"
11#include "linux/reboot.h"
12#include "linux/utsname.h"
13#include "linux/ctype.h"
14#include "linux/interrupt.h"
15#include "linux/sysrq.h"
16#include "linux/workqueue.h"
17#include "linux/module.h"
18#include "linux/file.h"
19#include "linux/fs.h"
20#include "linux/namei.h"
21#include "linux/proc_fs.h"
22#include "linux/syscalls.h"
Jeff Dike02dea082006-03-31 02:30:08 -080023#include "linux/list.h"
24#include "linux/mm.h"
Jeff Dike6f517d32006-01-06 00:19:04 -080025#include "linux/console.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include "asm/irq.h"
27#include "asm/uaccess.h"
28#include "user_util.h"
29#include "kern_util.h"
30#include "kern.h"
31#include "mconsole.h"
32#include "mconsole_kern.h"
33#include "irq_user.h"
34#include "init.h"
35#include "os.h"
36#include "umid.h"
37#include "irq_kern.h"
Jeff Dike3eddddc2005-09-16 19:27:46 -070038#include "choose-mode.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
Jeff Diked50084a2006-01-06 00:18:50 -080040static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 unsigned long what, void *data)
42{
43 return(mconsole_unlink_socket());
44}
45
46
47static struct notifier_block reboot_notifier = {
48 .notifier_call = do_unlink_socket,
49 .priority = 0,
50};
51
Jeff Diked50084a2006-01-06 00:18:50 -080052/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 * locking, and the interrupt handler is safe because it can't interrupt
54 * itself and it can only happen on CPU 0.
55 */
56
Jeff Dike90107722006-01-06 00:18:54 -080057static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59static void mc_work_proc(void *unused)
60{
61 struct mconsole_entry *req;
62 unsigned long flags;
63
64 while(!list_empty(&mc_requests)){
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070065 local_irq_save(flags);
Jeff Diked50084a2006-01-06 00:18:50 -080066 req = list_entry(mc_requests.next, struct mconsole_entry,
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 list);
68 list_del(&req->list);
69 local_irq_restore(flags);
70 req->request.cmd->handler(&req->request);
71 kfree(req);
72 }
73}
74
Jeff Dike90107722006-01-06 00:18:54 -080075static DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
78 struct pt_regs *regs)
79{
80 /* long to avoid size mismatch warnings from gcc */
81 long fd;
82 struct mconsole_entry *new;
83 struct mc_request req;
84
85 fd = (long) dev_id;
86 while (mconsole_get_request(fd, &req)){
87 if(req.cmd->context == MCONSOLE_INTR)
88 (*req.cmd->handler)(&req);
89 else {
Jeff Dike60baa152006-04-10 22:53:28 -070090 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 if(new == NULL)
92 mconsole_reply(&req, "Out of memory", 1, 0);
93 else {
94 new->request = req;
95 list_add(&new->list, &mc_requests);
96 }
97 }
98 }
99 if(!list_empty(&mc_requests))
100 schedule_work(&mconsole_work);
101 reactivate_fd(fd, MCONSOLE_IRQ);
102 return(IRQ_HANDLED);
103}
104
105void mconsole_version(struct mc_request *req)
106{
107 char version[256];
108
Jeff Diked50084a2006-01-06 00:18:50 -0800109 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
110 system_utsname.nodename, system_utsname.release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 system_utsname.version, system_utsname.machine);
112 mconsole_reply(req, version, 0, 0);
113}
114
115void mconsole_log(struct mc_request *req)
116{
117 int len;
118 char *ptr = req->request.data;
119
120 ptr += strlen("log ");
121
122 len = req->len - (ptr - req->request.data);
123 printk("%.*s", len, ptr);
124 mconsole_reply(req, "", 0, 0);
125}
126
127/* This is a more convoluted version of mconsole_proc, which has some stability
128 * problems; however, we need it fixed, because it is expected that UML users
129 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
130 * show the real procfs content, not the ones from hppfs.*/
131#if 0
132void mconsole_proc(struct mc_request *req)
133{
134 struct nameidata nd;
135 struct file_system_type *proc;
136 struct super_block *super;
137 struct file *file;
138 int n, err;
139 char *ptr = req->request.data, *buf;
140
141 ptr += strlen("proc");
142 while(isspace(*ptr)) ptr++;
143
144 proc = get_fs_type("proc");
145 if(proc == NULL){
146 mconsole_reply(req, "procfs not registered", 1, 0);
147 goto out;
148 }
149
150 super = (*proc->get_sb)(proc, 0, NULL, NULL);
151 put_filesystem(proc);
152 if(super == NULL){
153 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
154 goto out;
155 }
156 up_write(&super->s_umount);
157
158 nd.dentry = super->s_root;
159 nd.mnt = NULL;
160 nd.flags = O_RDONLY + 1;
161 nd.last_type = LAST_ROOT;
162
163 /* START: it was experienced that the stability problems are closed
164 * if commenting out these two calls + the below read cycle. To
165 * make UML crash again, it was enough to readd either one.*/
166 err = link_path_walk(ptr, &nd);
167 if(err){
168 mconsole_reply(req, "Failed to look up file", 1, 0);
169 goto out_kill;
170 }
171
172 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
173 if(IS_ERR(file)){
174 mconsole_reply(req, "Failed to open file", 1, 0);
175 goto out_kill;
176 }
177 /*END*/
178
179 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
180 if(buf == NULL){
181 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
182 goto out_fput;
183 }
184
185 if((file->f_op != NULL) && (file->f_op->read != NULL)){
186 do {
187 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
188 &file->f_pos);
189 if(n >= 0){
190 buf[n] = '\0';
191 mconsole_reply(req, buf, 0, (n > 0));
192 }
193 else {
194 mconsole_reply(req, "Read of file failed",
195 1, 0);
196 goto out_free;
197 }
198 } while(n > 0);
199 }
200 else mconsole_reply(req, "", 0, 0);
201
202 out_free:
203 kfree(buf);
204 out_fput:
205 fput(file);
206 out_kill:
207 deactivate_super(super);
208 out: ;
209}
210#endif
211
212void mconsole_proc(struct mc_request *req)
213{
214 char path[64];
215 char *buf;
216 int len;
217 int fd;
218 int first_chunk = 1;
219 char *ptr = req->request.data;
220
221 ptr += strlen("proc");
222 while(isspace(*ptr)) ptr++;
223 snprintf(path, sizeof(path), "/proc/%s", ptr);
224
225 fd = sys_open(path, 0, 0);
226 if (fd < 0) {
227 mconsole_reply(req, "Failed to open file", 1, 0);
228 printk("open %s: %d\n",path,fd);
229 goto out;
230 }
231
232 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
233 if(buf == NULL){
234 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
235 goto out_close;
236 }
237
238 for (;;) {
239 len = sys_read(fd, buf, PAGE_SIZE-1);
240 if (len < 0) {
241 mconsole_reply(req, "Read of file failed", 1, 0);
242 goto out_free;
243 }
244 /*Begin the file content on his own line.*/
245 if (first_chunk) {
246 mconsole_reply(req, "\n", 0, 1);
247 first_chunk = 0;
248 }
249 if (len == PAGE_SIZE-1) {
250 buf[len] = '\0';
251 mconsole_reply(req, buf, 0, 1);
252 } else {
253 buf[len] = '\0';
254 mconsole_reply(req, buf, 0, 0);
255 break;
256 }
257 }
258
259 out_free:
260 kfree(buf);
261 out_close:
262 sys_close(fd);
263 out:
264 /* nothing */;
265}
266
267#define UML_MCONSOLE_HELPTEXT \
268"Commands: \n\
269 version - Get kernel version \n\
270 help - Print this message \n\
271 halt - Halt UML \n\
272 reboot - Reboot UML \n\
273 config <dev>=<config> - Add a new device to UML; \n\
274 same syntax as command line \n\
275 config <dev> - Query the configuration of a device \n\
276 remove <dev> - Remove a device from UML \n\
277 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800278 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 stop - pause the UML; it will do nothing until it receives a 'go' \n\
280 go - continue the UML after a 'stop' \n\
281 log <string> - make UML enter <string> into the kernel log\n\
282 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700283 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284"
285
286void mconsole_help(struct mc_request *req)
287{
288 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
289}
290
291void mconsole_halt(struct mc_request *req)
292{
293 mconsole_reply(req, "", 0, 0);
294 machine_halt();
295}
296
297void mconsole_reboot(struct mc_request *req)
298{
299 mconsole_reply(req, "", 0, 0);
300 machine_restart(NULL);
301}
302
303extern void ctrl_alt_del(void);
304
305void mconsole_cad(struct mc_request *req)
306{
307 mconsole_reply(req, "", 0, 0);
308 ctrl_alt_del();
309}
310
311void mconsole_go(struct mc_request *req)
312{
313 mconsole_reply(req, "Not stopped", 1, 0);
314}
315
316void mconsole_stop(struct mc_request *req)
317{
318 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
319 os_set_fd_block(req->originating_fd, 1);
320 mconsole_reply(req, "", 0, 0);
321 while(mconsole_get_request(req->originating_fd, req)){
322 if(req->cmd->handler == mconsole_go) break;
323 (*req->cmd->handler)(req);
324 }
325 os_set_fd_block(req->originating_fd, 0);
326 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
327 mconsole_reply(req, "", 0, 0);
328}
329
330/* This list is populated by __initcall routines. */
331
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800332static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
334void mconsole_register_dev(struct mc_device *new)
335{
336 list_add(&new->list, &mconsole_devices);
337}
338
339static struct mc_device *mconsole_find_dev(char *name)
340{
341 struct list_head *ele;
342 struct mc_device *dev;
343
344 list_for_each(ele, &mconsole_devices){
345 dev = list_entry(ele, struct mc_device, list);
346 if(!strncmp(name, dev->name, strlen(dev->name)))
347 return(dev);
348 }
349 return(NULL);
350}
351
Jeff Dike02dea082006-03-31 02:30:08 -0800352#define UNPLUGGED_PER_PAGE \
353 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
354
355struct unplugged_pages {
356 struct list_head list;
357 void *pages[UNPLUGGED_PER_PAGE];
358};
359
360static unsigned long long unplugged_pages_count = 0;
361static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
362static int unplug_index = UNPLUGGED_PER_PAGE;
363
364static int mem_config(char *str)
365{
366 unsigned long long diff;
367 int err = -EINVAL, i, add;
368 char *ret;
369
370 if(str[0] != '=')
371 goto out;
372
373 str++;
374 if(str[0] == '-')
375 add = 0;
376 else if(str[0] == '+'){
377 add = 1;
378 }
379 else goto out;
380
381 str++;
382 diff = memparse(str, &ret);
383 if(*ret != '\0')
384 goto out;
385
386 diff /= PAGE_SIZE;
387
388 for(i = 0; i < diff; i++){
389 struct unplugged_pages *unplugged;
390 void *addr;
391
392 if(add){
393 if(list_empty(&unplugged_pages))
394 break;
395
396 unplugged = list_entry(unplugged_pages.next,
397 struct unplugged_pages, list);
398 if(unplug_index > 0)
399 addr = unplugged->pages[--unplug_index];
400 else {
401 list_del(&unplugged->list);
402 addr = unplugged;
403 unplug_index = UNPLUGGED_PER_PAGE;
404 }
405
406 free_page((unsigned long) addr);
407 unplugged_pages_count--;
408 }
409 else {
410 struct page *page;
411
412 page = alloc_page(GFP_ATOMIC);
413 if(page == NULL)
414 break;
415
416 unplugged = page_address(page);
417 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800418 list_add(&unplugged->list, &unplugged_pages);
419 unplug_index = 0;
420 }
421 else {
422 struct list_head *entry = unplugged_pages.next;
423 addr = unplugged;
424
425 unplugged = list_entry(entry,
426 struct unplugged_pages,
427 list);
428 unplugged->pages[unplug_index++] = addr;
429 err = os_drop_memory(addr, PAGE_SIZE);
430 if(err)
431 printk("Failed to release memory - "
432 "errno = %d\n", err);
433 }
434
435 unplugged_pages_count++;
436 }
437 }
438
439 err = 0;
440out:
441 return err;
442}
443
444static int mem_get_config(char *name, char *str, int size, char **error_out)
445{
446 char buf[sizeof("18446744073709551615")];
447 int len = 0;
448
449 sprintf(buf, "%ld", uml_physmem);
450 CONFIG_CHUNK(str, size, len, buf, 1);
451
452 return len;
453}
454
455static int mem_id(char **str, int *start_out, int *end_out)
456{
457 *start_out = 0;
458 *end_out = 0;
459
460 return 0;
461}
462
463static int mem_remove(int n)
464{
465 return -EBUSY;
466}
467
468static struct mc_device mem_mc = {
469 .name = "mem",
470 .config = mem_config,
471 .get_config = mem_get_config,
472 .id = mem_id,
473 .remove = mem_remove,
474};
475
476static int mem_mc_init(void)
477{
478 if(can_drop_memory())
479 mconsole_register_dev(&mem_mc);
480 else printk("Can't release memory to the host - memory hotplug won't "
481 "be supported\n");
482 return 0;
483}
484
485__initcall(mem_mc_init);
486
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487#define CONFIG_BUF_SIZE 64
488
Jeff Diked50084a2006-01-06 00:18:50 -0800489static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 char **),
491 struct mc_request *req, char *name)
492{
493 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
494 int n, size;
495
496 if(get_config == NULL){
497 mconsole_reply(req, "No get_config routine defined", 1, 0);
498 return;
499 }
500
501 error = NULL;
502 size = sizeof(default_buf)/sizeof(default_buf[0]);
503 buf = default_buf;
504
505 while(1){
506 n = (*get_config)(name, buf, size, &error);
507 if(error != NULL){
508 mconsole_reply(req, error, 1, 0);
509 goto out;
510 }
511
512 if(n <= size){
513 mconsole_reply(req, buf, 0, 0);
514 goto out;
515 }
516
517 if(buf != default_buf)
518 kfree(buf);
519
520 size = n;
521 buf = kmalloc(size, GFP_KERNEL);
522 if(buf == NULL){
523 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
524 return;
525 }
526 }
527 out:
528 if(buf != default_buf)
529 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
532void mconsole_config(struct mc_request *req)
533{
534 struct mc_device *dev;
535 char *ptr = req->request.data, *name;
536 int err;
537
538 ptr += strlen("config");
539 while(isspace(*ptr)) ptr++;
540 dev = mconsole_find_dev(ptr);
541 if(dev == NULL){
542 mconsole_reply(req, "Bad configuration option", 1, 0);
543 return;
544 }
545
546 name = &ptr[strlen(dev->name)];
547 ptr = name;
548 while((*ptr != '=') && (*ptr != '\0'))
549 ptr++;
550
551 if(*ptr == '='){
552 err = (*dev->config)(name);
553 mconsole_reply(req, "", err, 0);
554 }
555 else mconsole_get_config(dev->get_config, req, name);
556}
557
558void mconsole_remove(struct mc_request *req)
559{
Jeff Diked50084a2006-01-06 00:18:50 -0800560 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700561 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800562 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700563 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565 ptr += strlen("remove");
566 while(isspace(*ptr)) ptr++;
567 dev = mconsole_find_dev(ptr);
568 if(dev == NULL){
569 mconsole_reply(req, "Bad remove option", 1, 0);
570 return;
571 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700572
Jeff Dike3a331a52006-01-06 00:19:05 -0800573 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700574
Jeff Dike3a331a52006-01-06 00:19:05 -0800575 err = 1;
576 n = (*dev->id)(&ptr, &start, &end);
577 if(n < 0){
578 err_msg = "Couldn't parse device number";
579 goto out;
580 }
581 else if((n < start) || (n > end)){
582 sprintf(error, "Invalid device number - must be between "
583 "%d and %d", start, end);
584 err_msg = error;
585 goto out;
586 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700587
588 err = (*dev->remove)(n);
Jeff Dike3a331a52006-01-06 00:19:05 -0800589 switch(err){
590 case -ENODEV:
591 err_msg = "Device doesn't exist";
592 break;
593 case -EBUSY:
594 err_msg = "Device is currently open";
595 break;
596 default:
597 break;
598 }
599out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700600 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601}
602
Jeff Dike6f517d32006-01-06 00:19:04 -0800603static DEFINE_SPINLOCK(console_lock);
604static LIST_HEAD(clients);
605static char console_buf[MCONSOLE_MAX_DATA];
606static int console_index = 0;
607
608static void console_write(struct console *console, const char *string,
609 unsigned len)
610{
611 struct list_head *ele;
612 int n;
613
614 if(list_empty(&clients))
615 return;
616
617 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700618 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800619 strncpy(&console_buf[console_index], string, n);
620 console_index += n;
621 string += n;
622 len -= n;
623 if(len == 0)
624 return;
625
626 list_for_each(ele, &clients){
627 struct mconsole_entry *entry;
628
629 entry = list_entry(ele, struct mconsole_entry, list);
630 mconsole_reply_len(&entry->request, console_buf,
631 console_index, 0, 1);
632 }
633
634 console_index = 0;
635 }
636}
637
638static struct console mc_console = { .name = "mc",
639 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800640 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800641 .index = -1 };
642
643static int mc_add_console(void)
644{
645 register_console(&mc_console);
646 return 0;
647}
648
649late_initcall(mc_add_console);
650
651static void with_console(struct mc_request *req, void (*proc)(void *),
652 void *arg)
653{
654 struct mconsole_entry entry;
655 unsigned long flags;
656
Jeff Dike6f517d32006-01-06 00:19:04 -0800657 entry.request = *req;
658 list_add(&entry.list, &clients);
659 spin_lock_irqsave(&console_lock, flags);
660
661 (*proc)(arg);
662
663 mconsole_reply_len(req, console_buf, console_index, 0, 0);
664 console_index = 0;
665
666 spin_unlock_irqrestore(&console_lock, flags);
667 list_del(&entry.list);
668}
669
Jeff Dike4111b022006-01-06 00:19:05 -0800670#ifdef CONFIG_MAGIC_SYSRQ
671static void sysrq_proc(void *arg)
672{
673 char *op = arg;
674
675 handle_sysrq(*op, &current->thread.regs, NULL);
676}
677
678void mconsole_sysrq(struct mc_request *req)
679{
680 char *ptr = req->request.data;
681
682 ptr += strlen("sysrq");
683 while(isspace(*ptr)) ptr++;
684
685 /* With 'b', the system will shut down without a chance to reply,
686 * so in this case, we reply first.
687 */
688 if(*ptr == 'b')
689 mconsole_reply(req, "", 0, 0);
690
691 with_console(req, sysrq_proc, ptr);
692}
693#else
694void mconsole_sysrq(struct mc_request *req)
695{
696 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
697}
698#endif
699
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800700#ifdef CONFIG_MODE_SKAS
701
Jeff Dike6f517d32006-01-06 00:19:04 -0800702static void stack_proc(void *arg)
703{
704 struct task_struct *from = current, *to = arg;
705
706 to->thread.saved_task = from;
707 switch_to(from, to, from);
708}
709
Jeff Dike3eddddc2005-09-16 19:27:46 -0700710/* Mconsole stack trace
711 * Added by Allan Graves, Jeff Dike
712 * Dumps a stacks registers to the linux console.
713 * Usage stack <pid>.
714 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800715static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700716{
Jeff Dike3a331a52006-01-06 00:19:05 -0800717 char *ptr = req->request.data;
718 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800719 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700720 struct task_struct *to = NULL;
721
Jeff Dike3a331a52006-01-06 00:19:05 -0800722 /* Would be nice:
723 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700724 * 2) Add a way to stack dump all pids.
725 */
726
Jeff Dike3a331a52006-01-06 00:19:05 -0800727 ptr += strlen("stack");
728 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700729
Jeff Dike3a331a52006-01-06 00:19:05 -0800730 /* Should really check for multiple pids or reject bad args here */
731 /* What do the arguments in mconsole_reply mean? */
732 if(sscanf(ptr, "%d", &pid_requested) == 0){
733 mconsole_reply(req, "Please specify a pid", 1, 0);
734 return;
735 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700736
Jeff Dike6f517d32006-01-06 00:19:04 -0800737 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700738
Jeff Dike6f517d32006-01-06 00:19:04 -0800739 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800740 if((to == NULL) || (pid_requested == 0)) {
741 mconsole_reply(req, "Couldn't find that pid", 1, 0);
742 return;
743 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800744 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700745}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800746#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747
748void mconsole_stack(struct mc_request *req)
749{
750 /* This command doesn't work in TT mode, so let's check and then
751 * get out of here
752 */
753 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
754 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800755 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700756}
757
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758/* Changed by mconsole_setup, which is __setup, and called before SMP is
759 * active.
760 */
Jeff Diked50084a2006-01-06 00:18:50 -0800761static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Jeff Dike90107722006-01-06 00:18:54 -0800763static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764{
765 /* long to avoid size mismatch warnings from gcc */
766 long sock;
767 int err;
768 char file[256];
769
770 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
771 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
772
773 sock = os_create_unix_socket(file, sizeof(file), 1);
774 if (sock < 0){
775 printk("Failed to initialize management console\n");
776 return(1);
777 }
778
779 register_reboot_notifier(&reboot_notifier);
780
781 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
782 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
783 "mconsole", (void *)sock);
784 if (err){
785 printk("Failed to get IRQ for management console\n");
786 return(1);
787 }
788
789 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800790 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 if(notify_socket != NULL)
792 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800793 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 strlen(mconsole_socket_name) + 1);
795 else printk(KERN_ERR "mconsole_setup failed to strdup "
796 "string\n");
797 }
798
Jeff Diked50084a2006-01-06 00:18:50 -0800799 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 MCONSOLE_VERSION, mconsole_socket_name);
801 return(0);
802}
803
804__initcall(mconsole_init);
805
806static int write_proc_mconsole(struct file *file, const char __user *buffer,
807 unsigned long count, void *data)
808{
809 char *buf;
810
811 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800812 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 return(-ENOMEM);
814
815 if(copy_from_user(buf, buffer, count)){
816 count = -EFAULT;
817 goto out;
818 }
819
820 buf[count] = '\0';
821
822 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
823 out:
824 kfree(buf);
825 return(count);
826}
827
828static int create_proc_mconsole(void)
829{
830 struct proc_dir_entry *ent;
831
832 if(notify_socket == NULL) return(0);
833
834 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
835 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700836 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 return(0);
838 }
839
840 ent->read_proc = NULL;
841 ent->write_proc = write_proc_mconsole;
842 return(0);
843}
844
845static DEFINE_SPINLOCK(notify_spinlock);
846
847void lock_notify(void)
848{
849 spin_lock(&notify_spinlock);
850}
851
852void unlock_notify(void)
853{
854 spin_unlock(&notify_spinlock);
855}
856
857__initcall(create_proc_mconsole);
858
859#define NOTIFY "=notify:"
860
861static int mconsole_setup(char *str)
862{
863 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
864 str += strlen(NOTIFY);
865 notify_socket = str;
866 }
867 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
868 return(1);
869}
870
871__setup("mconsole", mconsole_setup);
872
873__uml_help(mconsole_setup,
874"mconsole=notify:<socket>\n"
875" Requests that the mconsole driver send a message to the named Unix\n"
876" socket containing the name of the mconsole socket. This also serves\n"
877" to notify outside processes when UML has booted far enough to respond\n"
878" to mconsole requests.\n\n"
879);
880
881static int notify_panic(struct notifier_block *self, unsigned long unused1,
882 void *ptr)
883{
884 char *message = ptr;
885
886 if(notify_socket == NULL) return(0);
887
Jeff Diked50084a2006-01-06 00:18:50 -0800888 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 strlen(message) + 1);
890 return(0);
891}
892
893static struct notifier_block panic_exit_notifier = {
894 .notifier_call = notify_panic,
895 .next = NULL,
896 .priority = 1
897};
898
899static int add_notifier(void)
900{
Alan Sterne041c682006-03-27 01:16:30 -0800901 atomic_notifier_chain_register(&panic_notifier_list,
902 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 return(0);
904}
905
906__initcall(add_notifier);
907
908char *mconsole_notify_socket(void)
909{
910 return(notify_socket);
911}
912
913EXPORT_SYMBOL(mconsole_notify_socket);