blob: 773a134e7fdb806fd6b63c80ceb3576ede57a370 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303void mconsole_cad(struct mc_request *req)
304{
305 mconsole_reply(req, "", 0, 0);
306 ctrl_alt_del();
307}
308
309void mconsole_go(struct mc_request *req)
310{
311 mconsole_reply(req, "Not stopped", 1, 0);
312}
313
314void mconsole_stop(struct mc_request *req)
315{
316 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
317 os_set_fd_block(req->originating_fd, 1);
318 mconsole_reply(req, "", 0, 0);
319 while(mconsole_get_request(req->originating_fd, req)){
320 if(req->cmd->handler == mconsole_go) break;
321 (*req->cmd->handler)(req);
322 }
323 os_set_fd_block(req->originating_fd, 0);
324 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
325 mconsole_reply(req, "", 0, 0);
326}
327
328/* This list is populated by __initcall routines. */
329
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800330static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
332void mconsole_register_dev(struct mc_device *new)
333{
334 list_add(&new->list, &mconsole_devices);
335}
336
337static struct mc_device *mconsole_find_dev(char *name)
338{
339 struct list_head *ele;
340 struct mc_device *dev;
341
342 list_for_each(ele, &mconsole_devices){
343 dev = list_entry(ele, struct mc_device, list);
344 if(!strncmp(name, dev->name, strlen(dev->name)))
345 return(dev);
346 }
347 return(NULL);
348}
349
Jeff Dike02dea082006-03-31 02:30:08 -0800350#define UNPLUGGED_PER_PAGE \
351 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
352
353struct unplugged_pages {
354 struct list_head list;
355 void *pages[UNPLUGGED_PER_PAGE];
356};
357
358static unsigned long long unplugged_pages_count = 0;
359static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
360static int unplug_index = UNPLUGGED_PER_PAGE;
361
362static int mem_config(char *str)
363{
364 unsigned long long diff;
365 int err = -EINVAL, i, add;
366 char *ret;
367
368 if(str[0] != '=')
369 goto out;
370
371 str++;
372 if(str[0] == '-')
373 add = 0;
374 else if(str[0] == '+'){
375 add = 1;
376 }
377 else goto out;
378
379 str++;
380 diff = memparse(str, &ret);
381 if(*ret != '\0')
382 goto out;
383
384 diff /= PAGE_SIZE;
385
386 for(i = 0; i < diff; i++){
387 struct unplugged_pages *unplugged;
388 void *addr;
389
390 if(add){
391 if(list_empty(&unplugged_pages))
392 break;
393
394 unplugged = list_entry(unplugged_pages.next,
395 struct unplugged_pages, list);
396 if(unplug_index > 0)
397 addr = unplugged->pages[--unplug_index];
398 else {
399 list_del(&unplugged->list);
400 addr = unplugged;
401 unplug_index = UNPLUGGED_PER_PAGE;
402 }
403
404 free_page((unsigned long) addr);
405 unplugged_pages_count--;
406 }
407 else {
408 struct page *page;
409
410 page = alloc_page(GFP_ATOMIC);
411 if(page == NULL)
412 break;
413
414 unplugged = page_address(page);
415 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800416 list_add(&unplugged->list, &unplugged_pages);
417 unplug_index = 0;
418 }
419 else {
420 struct list_head *entry = unplugged_pages.next;
421 addr = unplugged;
422
423 unplugged = list_entry(entry,
424 struct unplugged_pages,
425 list);
426 unplugged->pages[unplug_index++] = addr;
427 err = os_drop_memory(addr, PAGE_SIZE);
428 if(err)
429 printk("Failed to release memory - "
430 "errno = %d\n", err);
431 }
432
433 unplugged_pages_count++;
434 }
435 }
436
437 err = 0;
438out:
439 return err;
440}
441
442static int mem_get_config(char *name, char *str, int size, char **error_out)
443{
444 char buf[sizeof("18446744073709551615")];
445 int len = 0;
446
447 sprintf(buf, "%ld", uml_physmem);
448 CONFIG_CHUNK(str, size, len, buf, 1);
449
450 return len;
451}
452
453static int mem_id(char **str, int *start_out, int *end_out)
454{
455 *start_out = 0;
456 *end_out = 0;
457
458 return 0;
459}
460
461static int mem_remove(int n)
462{
463 return -EBUSY;
464}
465
466static struct mc_device mem_mc = {
467 .name = "mem",
468 .config = mem_config,
469 .get_config = mem_get_config,
470 .id = mem_id,
471 .remove = mem_remove,
472};
473
474static int mem_mc_init(void)
475{
476 if(can_drop_memory())
477 mconsole_register_dev(&mem_mc);
478 else printk("Can't release memory to the host - memory hotplug won't "
479 "be supported\n");
480 return 0;
481}
482
483__initcall(mem_mc_init);
484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485#define CONFIG_BUF_SIZE 64
486
Jeff Diked50084a2006-01-06 00:18:50 -0800487static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 char **),
489 struct mc_request *req, char *name)
490{
491 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
492 int n, size;
493
494 if(get_config == NULL){
495 mconsole_reply(req, "No get_config routine defined", 1, 0);
496 return;
497 }
498
499 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700500 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 buf = default_buf;
502
503 while(1){
504 n = (*get_config)(name, buf, size, &error);
505 if(error != NULL){
506 mconsole_reply(req, error, 1, 0);
507 goto out;
508 }
509
510 if(n <= size){
511 mconsole_reply(req, buf, 0, 0);
512 goto out;
513 }
514
515 if(buf != default_buf)
516 kfree(buf);
517
518 size = n;
519 buf = kmalloc(size, GFP_KERNEL);
520 if(buf == NULL){
521 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
522 return;
523 }
524 }
525 out:
526 if(buf != default_buf)
527 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528}
529
530void mconsole_config(struct mc_request *req)
531{
532 struct mc_device *dev;
533 char *ptr = req->request.data, *name;
534 int err;
535
536 ptr += strlen("config");
537 while(isspace(*ptr)) ptr++;
538 dev = mconsole_find_dev(ptr);
539 if(dev == NULL){
540 mconsole_reply(req, "Bad configuration option", 1, 0);
541 return;
542 }
543
544 name = &ptr[strlen(dev->name)];
545 ptr = name;
546 while((*ptr != '=') && (*ptr != '\0'))
547 ptr++;
548
549 if(*ptr == '='){
550 err = (*dev->config)(name);
551 mconsole_reply(req, "", err, 0);
552 }
553 else mconsole_get_config(dev->get_config, req, name);
554}
555
556void mconsole_remove(struct mc_request *req)
557{
Jeff Diked50084a2006-01-06 00:18:50 -0800558 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700559 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800560 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700561 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
563 ptr += strlen("remove");
564 while(isspace(*ptr)) ptr++;
565 dev = mconsole_find_dev(ptr);
566 if(dev == NULL){
567 mconsole_reply(req, "Bad remove option", 1, 0);
568 return;
569 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700570
Jeff Dike3a331a52006-01-06 00:19:05 -0800571 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700572
Jeff Dike3a331a52006-01-06 00:19:05 -0800573 err = 1;
574 n = (*dev->id)(&ptr, &start, &end);
575 if(n < 0){
576 err_msg = "Couldn't parse device number";
577 goto out;
578 }
579 else if((n < start) || (n > end)){
580 sprintf(error, "Invalid device number - must be between "
581 "%d and %d", start, end);
582 err_msg = error;
583 goto out;
584 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700585
586 err = (*dev->remove)(n);
Jeff Dike3a331a52006-01-06 00:19:05 -0800587 switch(err){
588 case -ENODEV:
589 err_msg = "Device doesn't exist";
590 break;
591 case -EBUSY:
592 err_msg = "Device is currently open";
593 break;
594 default:
595 break;
596 }
597out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700598 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
600
Jeff Dikef92afe52006-09-29 01:58:52 -0700601struct mconsole_output {
602 struct list_head list;
603 struct mc_request *req;
604};
605
Jeff Dike6f517d32006-01-06 00:19:04 -0800606static DEFINE_SPINLOCK(console_lock);
607static LIST_HEAD(clients);
608static char console_buf[MCONSOLE_MAX_DATA];
609static int console_index = 0;
610
611static void console_write(struct console *console, const char *string,
612 unsigned len)
613{
614 struct list_head *ele;
615 int n;
616
617 if(list_empty(&clients))
618 return;
619
620 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700621 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800622 strncpy(&console_buf[console_index], string, n);
623 console_index += n;
624 string += n;
625 len -= n;
626 if(len == 0)
627 return;
628
629 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700630 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800631
Jeff Dikef92afe52006-09-29 01:58:52 -0700632 entry = list_entry(ele, struct mconsole_output, list);
633 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800634 console_index, 0, 1);
635 }
636
637 console_index = 0;
638 }
639}
640
641static struct console mc_console = { .name = "mc",
642 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800643 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800644 .index = -1 };
645
646static int mc_add_console(void)
647{
648 register_console(&mc_console);
649 return 0;
650}
651
652late_initcall(mc_add_console);
653
654static void with_console(struct mc_request *req, void (*proc)(void *),
655 void *arg)
656{
Jeff Dikef92afe52006-09-29 01:58:52 -0700657 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800658 unsigned long flags;
659
Jeff Dikef92afe52006-09-29 01:58:52 -0700660 entry.req = req;
Jeff Dike6f517d32006-01-06 00:19:04 -0800661 list_add(&entry.list, &clients);
662 spin_lock_irqsave(&console_lock, flags);
663
664 (*proc)(arg);
665
666 mconsole_reply_len(req, console_buf, console_index, 0, 0);
667 console_index = 0;
668
669 spin_unlock_irqrestore(&console_lock, flags);
670 list_del(&entry.list);
671}
672
Jeff Dike4111b022006-01-06 00:19:05 -0800673#ifdef CONFIG_MAGIC_SYSRQ
674static void sysrq_proc(void *arg)
675{
676 char *op = arg;
677
678 handle_sysrq(*op, &current->thread.regs, NULL);
679}
680
681void mconsole_sysrq(struct mc_request *req)
682{
683 char *ptr = req->request.data;
684
685 ptr += strlen("sysrq");
686 while(isspace(*ptr)) ptr++;
687
688 /* With 'b', the system will shut down without a chance to reply,
689 * so in this case, we reply first.
690 */
691 if(*ptr == 'b')
692 mconsole_reply(req, "", 0, 0);
693
694 with_console(req, sysrq_proc, ptr);
695}
696#else
697void mconsole_sysrq(struct mc_request *req)
698{
699 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
700}
701#endif
702
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800703#ifdef CONFIG_MODE_SKAS
704
Jeff Dike6f517d32006-01-06 00:19:04 -0800705static void stack_proc(void *arg)
706{
707 struct task_struct *from = current, *to = arg;
708
709 to->thread.saved_task = from;
710 switch_to(from, to, from);
711}
712
Jeff Dike3eddddc2005-09-16 19:27:46 -0700713/* Mconsole stack trace
714 * Added by Allan Graves, Jeff Dike
715 * Dumps a stacks registers to the linux console.
716 * Usage stack <pid>.
717 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800718static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700719{
Jeff Dike3a331a52006-01-06 00:19:05 -0800720 char *ptr = req->request.data;
721 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800722 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700723 struct task_struct *to = NULL;
724
Jeff Dike3a331a52006-01-06 00:19:05 -0800725 /* Would be nice:
726 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700727 * 2) Add a way to stack dump all pids.
728 */
729
Jeff Dike3a331a52006-01-06 00:19:05 -0800730 ptr += strlen("stack");
731 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700732
Jeff Dike3a331a52006-01-06 00:19:05 -0800733 /* Should really check for multiple pids or reject bad args here */
734 /* What do the arguments in mconsole_reply mean? */
735 if(sscanf(ptr, "%d", &pid_requested) == 0){
736 mconsole_reply(req, "Please specify a pid", 1, 0);
737 return;
738 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700739
Jeff Dike6f517d32006-01-06 00:19:04 -0800740 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700741
Jeff Dike6f517d32006-01-06 00:19:04 -0800742 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800743 if((to == NULL) || (pid_requested == 0)) {
744 mconsole_reply(req, "Couldn't find that pid", 1, 0);
745 return;
746 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800747 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700748}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800749#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700750
751void mconsole_stack(struct mc_request *req)
752{
753 /* This command doesn't work in TT mode, so let's check and then
754 * get out of here
755 */
756 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
757 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800758 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700759}
760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761/* Changed by mconsole_setup, which is __setup, and called before SMP is
762 * active.
763 */
Jeff Diked50084a2006-01-06 00:18:50 -0800764static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Jeff Dike90107722006-01-06 00:18:54 -0800766static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767{
768 /* long to avoid size mismatch warnings from gcc */
769 long sock;
770 int err;
771 char file[256];
772
773 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
774 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
775
776 sock = os_create_unix_socket(file, sizeof(file), 1);
777 if (sock < 0){
778 printk("Failed to initialize management console\n");
779 return(1);
780 }
781
782 register_reboot_notifier(&reboot_notifier);
783
784 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700785 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 "mconsole", (void *)sock);
787 if (err){
788 printk("Failed to get IRQ for management console\n");
789 return(1);
790 }
791
792 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800793 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 if(notify_socket != NULL)
795 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800796 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 strlen(mconsole_socket_name) + 1);
798 else printk(KERN_ERR "mconsole_setup failed to strdup "
799 "string\n");
800 }
801
Jeff Diked50084a2006-01-06 00:18:50 -0800802 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 MCONSOLE_VERSION, mconsole_socket_name);
804 return(0);
805}
806
807__initcall(mconsole_init);
808
809static int write_proc_mconsole(struct file *file, const char __user *buffer,
810 unsigned long count, void *data)
811{
812 char *buf;
813
814 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800815 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 return(-ENOMEM);
817
818 if(copy_from_user(buf, buffer, count)){
819 count = -EFAULT;
820 goto out;
821 }
822
823 buf[count] = '\0';
824
825 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
826 out:
827 kfree(buf);
828 return(count);
829}
830
831static int create_proc_mconsole(void)
832{
833 struct proc_dir_entry *ent;
834
835 if(notify_socket == NULL) return(0);
836
837 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
838 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700839 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 return(0);
841 }
842
843 ent->read_proc = NULL;
844 ent->write_proc = write_proc_mconsole;
845 return(0);
846}
847
848static DEFINE_SPINLOCK(notify_spinlock);
849
850void lock_notify(void)
851{
852 spin_lock(&notify_spinlock);
853}
854
855void unlock_notify(void)
856{
857 spin_unlock(&notify_spinlock);
858}
859
860__initcall(create_proc_mconsole);
861
862#define NOTIFY "=notify:"
863
864static int mconsole_setup(char *str)
865{
866 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
867 str += strlen(NOTIFY);
868 notify_socket = str;
869 }
870 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
871 return(1);
872}
873
874__setup("mconsole", mconsole_setup);
875
876__uml_help(mconsole_setup,
877"mconsole=notify:<socket>\n"
878" Requests that the mconsole driver send a message to the named Unix\n"
879" socket containing the name of the mconsole socket. This also serves\n"
880" to notify outside processes when UML has booted far enough to respond\n"
881" to mconsole requests.\n\n"
882);
883
884static int notify_panic(struct notifier_block *self, unsigned long unused1,
885 void *ptr)
886{
887 char *message = ptr;
888
889 if(notify_socket == NULL) return(0);
890
Jeff Diked50084a2006-01-06 00:18:50 -0800891 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 strlen(message) + 1);
893 return(0);
894}
895
896static struct notifier_block panic_exit_notifier = {
897 .notifier_call = notify_panic,
898 .next = NULL,
899 .priority = 1
900};
901
902static int add_notifier(void)
903{
Alan Sterne041c682006-03-27 01:16:30 -0800904 atomic_notifier_chain_register(&panic_notifier_list,
905 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 return(0);
907}
908
909__initcall(add_notifier);
910
911char *mconsole_notify_socket(void)
912{
913 return(notify_socket);
914}
915
916EXPORT_SYMBOL(mconsole_notify_socket);