blob: 28e3760e8b981ed20d1f46453c8405dbfc31bd42 [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)){
65 local_save_flags(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 {
90 new = kmalloc(sizeof(*new), GFP_ATOMIC);
91 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){
418 INIT_LIST_HEAD(&unplugged->list);
419 list_add(&unplugged->list, &unplugged_pages);
420 unplug_index = 0;
421 }
422 else {
423 struct list_head *entry = unplugged_pages.next;
424 addr = unplugged;
425
426 unplugged = list_entry(entry,
427 struct unplugged_pages,
428 list);
429 unplugged->pages[unplug_index++] = addr;
430 err = os_drop_memory(addr, PAGE_SIZE);
431 if(err)
432 printk("Failed to release memory - "
433 "errno = %d\n", err);
434 }
435
436 unplugged_pages_count++;
437 }
438 }
439
440 err = 0;
441out:
442 return err;
443}
444
445static int mem_get_config(char *name, char *str, int size, char **error_out)
446{
447 char buf[sizeof("18446744073709551615")];
448 int len = 0;
449
450 sprintf(buf, "%ld", uml_physmem);
451 CONFIG_CHUNK(str, size, len, buf, 1);
452
453 return len;
454}
455
456static int mem_id(char **str, int *start_out, int *end_out)
457{
458 *start_out = 0;
459 *end_out = 0;
460
461 return 0;
462}
463
464static int mem_remove(int n)
465{
466 return -EBUSY;
467}
468
469static struct mc_device mem_mc = {
470 .name = "mem",
471 .config = mem_config,
472 .get_config = mem_get_config,
473 .id = mem_id,
474 .remove = mem_remove,
475};
476
477static int mem_mc_init(void)
478{
479 if(can_drop_memory())
480 mconsole_register_dev(&mem_mc);
481 else printk("Can't release memory to the host - memory hotplug won't "
482 "be supported\n");
483 return 0;
484}
485
486__initcall(mem_mc_init);
487
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488#define CONFIG_BUF_SIZE 64
489
Jeff Diked50084a2006-01-06 00:18:50 -0800490static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 char **),
492 struct mc_request *req, char *name)
493{
494 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
495 int n, size;
496
497 if(get_config == NULL){
498 mconsole_reply(req, "No get_config routine defined", 1, 0);
499 return;
500 }
501
502 error = NULL;
503 size = sizeof(default_buf)/sizeof(default_buf[0]);
504 buf = default_buf;
505
506 while(1){
507 n = (*get_config)(name, buf, size, &error);
508 if(error != NULL){
509 mconsole_reply(req, error, 1, 0);
510 goto out;
511 }
512
513 if(n <= size){
514 mconsole_reply(req, buf, 0, 0);
515 goto out;
516 }
517
518 if(buf != default_buf)
519 kfree(buf);
520
521 size = n;
522 buf = kmalloc(size, GFP_KERNEL);
523 if(buf == NULL){
524 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
525 return;
526 }
527 }
528 out:
529 if(buf != default_buf)
530 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531}
532
533void mconsole_config(struct mc_request *req)
534{
535 struct mc_device *dev;
536 char *ptr = req->request.data, *name;
537 int err;
538
539 ptr += strlen("config");
540 while(isspace(*ptr)) ptr++;
541 dev = mconsole_find_dev(ptr);
542 if(dev == NULL){
543 mconsole_reply(req, "Bad configuration option", 1, 0);
544 return;
545 }
546
547 name = &ptr[strlen(dev->name)];
548 ptr = name;
549 while((*ptr != '=') && (*ptr != '\0'))
550 ptr++;
551
552 if(*ptr == '='){
553 err = (*dev->config)(name);
554 mconsole_reply(req, "", err, 0);
555 }
556 else mconsole_get_config(dev->get_config, req, name);
557}
558
559void mconsole_remove(struct mc_request *req)
560{
Jeff Diked50084a2006-01-06 00:18:50 -0800561 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700562 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800563 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700564 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
566 ptr += strlen("remove");
567 while(isspace(*ptr)) ptr++;
568 dev = mconsole_find_dev(ptr);
569 if(dev == NULL){
570 mconsole_reply(req, "Bad remove option", 1, 0);
571 return;
572 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700573
Jeff Dike3a331a52006-01-06 00:19:05 -0800574 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700575
Jeff Dike3a331a52006-01-06 00:19:05 -0800576 err = 1;
577 n = (*dev->id)(&ptr, &start, &end);
578 if(n < 0){
579 err_msg = "Couldn't parse device number";
580 goto out;
581 }
582 else if((n < start) || (n > end)){
583 sprintf(error, "Invalid device number - must be between "
584 "%d and %d", start, end);
585 err_msg = error;
586 goto out;
587 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700588
589 err = (*dev->remove)(n);
Jeff Dike3a331a52006-01-06 00:19:05 -0800590 switch(err){
591 case -ENODEV:
592 err_msg = "Device doesn't exist";
593 break;
594 case -EBUSY:
595 err_msg = "Device is currently open";
596 break;
597 default:
598 break;
599 }
600out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700601 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602}
603
Jeff Dike6f517d32006-01-06 00:19:04 -0800604static DEFINE_SPINLOCK(console_lock);
605static LIST_HEAD(clients);
606static char console_buf[MCONSOLE_MAX_DATA];
607static int console_index = 0;
608
609static void console_write(struct console *console, const char *string,
610 unsigned len)
611{
612 struct list_head *ele;
613 int n;
614
615 if(list_empty(&clients))
616 return;
617
618 while(1){
Al Viroe11c0cd2006-03-31 02:30:17 -0800619 n = min((size_t)len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800620 strncpy(&console_buf[console_index], string, n);
621 console_index += n;
622 string += n;
623 len -= n;
624 if(len == 0)
625 return;
626
627 list_for_each(ele, &clients){
628 struct mconsole_entry *entry;
629
630 entry = list_entry(ele, struct mconsole_entry, list);
631 mconsole_reply_len(&entry->request, console_buf,
632 console_index, 0, 1);
633 }
634
635 console_index = 0;
636 }
637}
638
639static struct console mc_console = { .name = "mc",
640 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800641 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800642 .index = -1 };
643
644static int mc_add_console(void)
645{
646 register_console(&mc_console);
647 return 0;
648}
649
650late_initcall(mc_add_console);
651
652static void with_console(struct mc_request *req, void (*proc)(void *),
653 void *arg)
654{
655 struct mconsole_entry entry;
656 unsigned long flags;
657
658 INIT_LIST_HEAD(&entry.list);
659 entry.request = *req;
660 list_add(&entry.list, &clients);
661 spin_lock_irqsave(&console_lock, flags);
662
663 (*proc)(arg);
664
665 mconsole_reply_len(req, console_buf, console_index, 0, 0);
666 console_index = 0;
667
668 spin_unlock_irqrestore(&console_lock, flags);
669 list_del(&entry.list);
670}
671
Jeff Dike4111b022006-01-06 00:19:05 -0800672#ifdef CONFIG_MAGIC_SYSRQ
673static void sysrq_proc(void *arg)
674{
675 char *op = arg;
676
677 handle_sysrq(*op, &current->thread.regs, NULL);
678}
679
680void mconsole_sysrq(struct mc_request *req)
681{
682 char *ptr = req->request.data;
683
684 ptr += strlen("sysrq");
685 while(isspace(*ptr)) ptr++;
686
687 /* With 'b', the system will shut down without a chance to reply,
688 * so in this case, we reply first.
689 */
690 if(*ptr == 'b')
691 mconsole_reply(req, "", 0, 0);
692
693 with_console(req, sysrq_proc, ptr);
694}
695#else
696void mconsole_sysrq(struct mc_request *req)
697{
698 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
699}
700#endif
701
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800702#ifdef CONFIG_MODE_SKAS
703
Jeff Dike6f517d32006-01-06 00:19:04 -0800704static void stack_proc(void *arg)
705{
706 struct task_struct *from = current, *to = arg;
707
708 to->thread.saved_task = from;
709 switch_to(from, to, from);
710}
711
Jeff Dike3eddddc2005-09-16 19:27:46 -0700712/* Mconsole stack trace
713 * Added by Allan Graves, Jeff Dike
714 * Dumps a stacks registers to the linux console.
715 * Usage stack <pid>.
716 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800717static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700718{
Jeff Dike3a331a52006-01-06 00:19:05 -0800719 char *ptr = req->request.data;
720 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800721 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700722 struct task_struct *to = NULL;
723
Jeff Dike3a331a52006-01-06 00:19:05 -0800724 /* Would be nice:
725 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700726 * 2) Add a way to stack dump all pids.
727 */
728
Jeff Dike3a331a52006-01-06 00:19:05 -0800729 ptr += strlen("stack");
730 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700731
Jeff Dike3a331a52006-01-06 00:19:05 -0800732 /* Should really check for multiple pids or reject bad args here */
733 /* What do the arguments in mconsole_reply mean? */
734 if(sscanf(ptr, "%d", &pid_requested) == 0){
735 mconsole_reply(req, "Please specify a pid", 1, 0);
736 return;
737 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700738
Jeff Dike6f517d32006-01-06 00:19:04 -0800739 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700740
Jeff Dike6f517d32006-01-06 00:19:04 -0800741 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800742 if((to == NULL) || (pid_requested == 0)) {
743 mconsole_reply(req, "Couldn't find that pid", 1, 0);
744 return;
745 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800746 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800748#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700749
750void mconsole_stack(struct mc_request *req)
751{
752 /* This command doesn't work in TT mode, so let's check and then
753 * get out of here
754 */
755 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
756 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800757 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700758}
759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760/* Changed by mconsole_setup, which is __setup, and called before SMP is
761 * active.
762 */
Jeff Diked50084a2006-01-06 00:18:50 -0800763static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Jeff Dike90107722006-01-06 00:18:54 -0800765static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766{
767 /* long to avoid size mismatch warnings from gcc */
768 long sock;
769 int err;
770 char file[256];
771
772 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
773 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
774
775 sock = os_create_unix_socket(file, sizeof(file), 1);
776 if (sock < 0){
777 printk("Failed to initialize management console\n");
778 return(1);
779 }
780
781 register_reboot_notifier(&reboot_notifier);
782
783 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
784 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
785 "mconsole", (void *)sock);
786 if (err){
787 printk("Failed to get IRQ for management console\n");
788 return(1);
789 }
790
791 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800792 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 if(notify_socket != NULL)
794 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800795 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 strlen(mconsole_socket_name) + 1);
797 else printk(KERN_ERR "mconsole_setup failed to strdup "
798 "string\n");
799 }
800
Jeff Diked50084a2006-01-06 00:18:50 -0800801 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 MCONSOLE_VERSION, mconsole_socket_name);
803 return(0);
804}
805
806__initcall(mconsole_init);
807
808static int write_proc_mconsole(struct file *file, const char __user *buffer,
809 unsigned long count, void *data)
810{
811 char *buf;
812
813 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800814 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 return(-ENOMEM);
816
817 if(copy_from_user(buf, buffer, count)){
818 count = -EFAULT;
819 goto out;
820 }
821
822 buf[count] = '\0';
823
824 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
825 out:
826 kfree(buf);
827 return(count);
828}
829
830static int create_proc_mconsole(void)
831{
832 struct proc_dir_entry *ent;
833
834 if(notify_socket == NULL) return(0);
835
836 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
837 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700838 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 return(0);
840 }
841
842 ent->read_proc = NULL;
843 ent->write_proc = write_proc_mconsole;
844 return(0);
845}
846
847static DEFINE_SPINLOCK(notify_spinlock);
848
849void lock_notify(void)
850{
851 spin_lock(&notify_spinlock);
852}
853
854void unlock_notify(void)
855{
856 spin_unlock(&notify_spinlock);
857}
858
859__initcall(create_proc_mconsole);
860
861#define NOTIFY "=notify:"
862
863static int mconsole_setup(char *str)
864{
865 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
866 str += strlen(NOTIFY);
867 notify_socket = str;
868 }
869 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
870 return(1);
871}
872
873__setup("mconsole", mconsole_setup);
874
875__uml_help(mconsole_setup,
876"mconsole=notify:<socket>\n"
877" Requests that the mconsole driver send a message to the named Unix\n"
878" socket containing the name of the mconsole socket. This also serves\n"
879" to notify outside processes when UML has booted far enough to respond\n"
880" to mconsole requests.\n\n"
881);
882
883static int notify_panic(struct notifier_block *self, unsigned long unused1,
884 void *ptr)
885{
886 char *message = ptr;
887
888 if(notify_socket == NULL) return(0);
889
Jeff Diked50084a2006-01-06 00:18:50 -0800890 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 strlen(message) + 1);
892 return(0);
893}
894
895static struct notifier_block panic_exit_notifier = {
896 .notifier_call = notify_panic,
897 .next = NULL,
898 .priority = 1
899};
900
901static int add_notifier(void)
902{
Alan Sterne041c682006-03-27 01:16:30 -0800903 atomic_notifier_chain_register(&panic_notifier_list,
904 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 return(0);
906}
907
908__initcall(add_notifier);
909
910char *mconsole_notify_socket(void)
911{
912 return(notify_socket);
913}
914
915EXPORT_SYMBOL(mconsole_notify_socket);