blob: d08bd036ccb84268aaab9ad386849ab28b5ffc53 [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
Al Viro7bea96f2006-10-08 22:49:34 +010077static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070078{
79 /* long to avoid size mismatch warnings from gcc */
80 long fd;
81 struct mconsole_entry *new;
82 struct mc_request req;
83
84 fd = (long) dev_id;
85 while (mconsole_get_request(fd, &req)){
86 if(req.cmd->context == MCONSOLE_INTR)
87 (*req.cmd->handler)(&req);
88 else {
Jeff Dike60baa152006-04-10 22:53:28 -070089 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 if(new == NULL)
91 mconsole_reply(&req, "Out of memory", 1, 0);
92 else {
93 new->request = req;
94 list_add(&new->list, &mc_requests);
95 }
96 }
97 }
98 if(!list_empty(&mc_requests))
99 schedule_work(&mconsole_work);
100 reactivate_fd(fd, MCONSOLE_IRQ);
101 return(IRQ_HANDLED);
102}
103
104void mconsole_version(struct mc_request *req)
105{
106 char version[256];
107
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700108 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
109 utsname()->nodename, utsname()->release,
110 utsname()->version, utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 mconsole_reply(req, version, 0, 0);
112}
113
114void mconsole_log(struct mc_request *req)
115{
116 int len;
117 char *ptr = req->request.data;
118
119 ptr += strlen("log ");
120
121 len = req->len - (ptr - req->request.data);
122 printk("%.*s", len, ptr);
123 mconsole_reply(req, "", 0, 0);
124}
125
126/* This is a more convoluted version of mconsole_proc, which has some stability
127 * problems; however, we need it fixed, because it is expected that UML users
128 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
129 * show the real procfs content, not the ones from hppfs.*/
130#if 0
131void mconsole_proc(struct mc_request *req)
132{
133 struct nameidata nd;
134 struct file_system_type *proc;
135 struct super_block *super;
136 struct file *file;
137 int n, err;
138 char *ptr = req->request.data, *buf;
139
140 ptr += strlen("proc");
141 while(isspace(*ptr)) ptr++;
142
143 proc = get_fs_type("proc");
144 if(proc == NULL){
145 mconsole_reply(req, "procfs not registered", 1, 0);
146 goto out;
147 }
148
149 super = (*proc->get_sb)(proc, 0, NULL, NULL);
150 put_filesystem(proc);
151 if(super == NULL){
152 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
153 goto out;
154 }
155 up_write(&super->s_umount);
156
157 nd.dentry = super->s_root;
158 nd.mnt = NULL;
159 nd.flags = O_RDONLY + 1;
160 nd.last_type = LAST_ROOT;
161
162 /* START: it was experienced that the stability problems are closed
163 * if commenting out these two calls + the below read cycle. To
164 * make UML crash again, it was enough to readd either one.*/
165 err = link_path_walk(ptr, &nd);
166 if(err){
167 mconsole_reply(req, "Failed to look up file", 1, 0);
168 goto out_kill;
169 }
170
171 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
172 if(IS_ERR(file)){
173 mconsole_reply(req, "Failed to open file", 1, 0);
174 goto out_kill;
175 }
176 /*END*/
177
178 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
179 if(buf == NULL){
180 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
181 goto out_fput;
182 }
183
184 if((file->f_op != NULL) && (file->f_op->read != NULL)){
185 do {
186 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
187 &file->f_pos);
188 if(n >= 0){
189 buf[n] = '\0';
190 mconsole_reply(req, buf, 0, (n > 0));
191 }
192 else {
193 mconsole_reply(req, "Read of file failed",
194 1, 0);
195 goto out_free;
196 }
197 } while(n > 0);
198 }
199 else mconsole_reply(req, "", 0, 0);
200
201 out_free:
202 kfree(buf);
203 out_fput:
204 fput(file);
205 out_kill:
206 deactivate_super(super);
207 out: ;
208}
209#endif
210
211void mconsole_proc(struct mc_request *req)
212{
213 char path[64];
214 char *buf;
215 int len;
216 int fd;
217 int first_chunk = 1;
218 char *ptr = req->request.data;
219
220 ptr += strlen("proc");
221 while(isspace(*ptr)) ptr++;
222 snprintf(path, sizeof(path), "/proc/%s", ptr);
223
224 fd = sys_open(path, 0, 0);
225 if (fd < 0) {
226 mconsole_reply(req, "Failed to open file", 1, 0);
227 printk("open %s: %d\n",path,fd);
228 goto out;
229 }
230
231 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
232 if(buf == NULL){
233 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
234 goto out_close;
235 }
236
237 for (;;) {
238 len = sys_read(fd, buf, PAGE_SIZE-1);
239 if (len < 0) {
240 mconsole_reply(req, "Read of file failed", 1, 0);
241 goto out_free;
242 }
243 /*Begin the file content on his own line.*/
244 if (first_chunk) {
245 mconsole_reply(req, "\n", 0, 1);
246 first_chunk = 0;
247 }
248 if (len == PAGE_SIZE-1) {
249 buf[len] = '\0';
250 mconsole_reply(req, buf, 0, 1);
251 } else {
252 buf[len] = '\0';
253 mconsole_reply(req, buf, 0, 0);
254 break;
255 }
256 }
257
258 out_free:
259 kfree(buf);
260 out_close:
261 sys_close(fd);
262 out:
263 /* nothing */;
264}
265
266#define UML_MCONSOLE_HELPTEXT \
267"Commands: \n\
268 version - Get kernel version \n\
269 help - Print this message \n\
270 halt - Halt UML \n\
271 reboot - Reboot UML \n\
272 config <dev>=<config> - Add a new device to UML; \n\
273 same syntax as command line \n\
274 config <dev> - Query the configuration of a device \n\
275 remove <dev> - Remove a device from UML \n\
276 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800277 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 stop - pause the UML; it will do nothing until it receives a 'go' \n\
279 go - continue the UML after a 'stop' \n\
280 log <string> - make UML enter <string> into the kernel log\n\
281 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700282 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283"
284
285void mconsole_help(struct mc_request *req)
286{
287 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
288}
289
290void mconsole_halt(struct mc_request *req)
291{
292 mconsole_reply(req, "", 0, 0);
293 machine_halt();
294}
295
296void mconsole_reboot(struct mc_request *req)
297{
298 mconsole_reply(req, "", 0, 0);
299 machine_restart(NULL);
300}
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302void mconsole_cad(struct mc_request *req)
303{
304 mconsole_reply(req, "", 0, 0);
305 ctrl_alt_del();
306}
307
308void mconsole_go(struct mc_request *req)
309{
310 mconsole_reply(req, "Not stopped", 1, 0);
311}
312
313void mconsole_stop(struct mc_request *req)
314{
315 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
316 os_set_fd_block(req->originating_fd, 1);
317 mconsole_reply(req, "", 0, 0);
318 while(mconsole_get_request(req->originating_fd, req)){
319 if(req->cmd->handler == mconsole_go) break;
320 (*req->cmd->handler)(req);
321 }
322 os_set_fd_block(req->originating_fd, 0);
323 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
324 mconsole_reply(req, "", 0, 0);
325}
326
327/* This list is populated by __initcall routines. */
328
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800329static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
331void mconsole_register_dev(struct mc_device *new)
332{
333 list_add(&new->list, &mconsole_devices);
334}
335
336static struct mc_device *mconsole_find_dev(char *name)
337{
338 struct list_head *ele;
339 struct mc_device *dev;
340
341 list_for_each(ele, &mconsole_devices){
342 dev = list_entry(ele, struct mc_device, list);
343 if(!strncmp(name, dev->name, strlen(dev->name)))
344 return(dev);
345 }
346 return(NULL);
347}
348
Jeff Dike02dea082006-03-31 02:30:08 -0800349#define UNPLUGGED_PER_PAGE \
350 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
351
352struct unplugged_pages {
353 struct list_head list;
354 void *pages[UNPLUGGED_PER_PAGE];
355};
356
357static unsigned long long unplugged_pages_count = 0;
358static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
359static int unplug_index = UNPLUGGED_PER_PAGE;
360
361static int mem_config(char *str)
362{
363 unsigned long long diff;
364 int err = -EINVAL, i, add;
365 char *ret;
366
367 if(str[0] != '=')
368 goto out;
369
370 str++;
371 if(str[0] == '-')
372 add = 0;
373 else if(str[0] == '+'){
374 add = 1;
375 }
376 else goto out;
377
378 str++;
379 diff = memparse(str, &ret);
380 if(*ret != '\0')
381 goto out;
382
383 diff /= PAGE_SIZE;
384
385 for(i = 0; i < diff; i++){
386 struct unplugged_pages *unplugged;
387 void *addr;
388
389 if(add){
390 if(list_empty(&unplugged_pages))
391 break;
392
393 unplugged = list_entry(unplugged_pages.next,
394 struct unplugged_pages, list);
395 if(unplug_index > 0)
396 addr = unplugged->pages[--unplug_index];
397 else {
398 list_del(&unplugged->list);
399 addr = unplugged;
400 unplug_index = UNPLUGGED_PER_PAGE;
401 }
402
403 free_page((unsigned long) addr);
404 unplugged_pages_count--;
405 }
406 else {
407 struct page *page;
408
409 page = alloc_page(GFP_ATOMIC);
410 if(page == NULL)
411 break;
412
413 unplugged = page_address(page);
414 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800415 list_add(&unplugged->list, &unplugged_pages);
416 unplug_index = 0;
417 }
418 else {
419 struct list_head *entry = unplugged_pages.next;
420 addr = unplugged;
421
422 unplugged = list_entry(entry,
423 struct unplugged_pages,
424 list);
425 unplugged->pages[unplug_index++] = addr;
426 err = os_drop_memory(addr, PAGE_SIZE);
427 if(err)
428 printk("Failed to release memory - "
429 "errno = %d\n", err);
430 }
431
432 unplugged_pages_count++;
433 }
434 }
435
436 err = 0;
437out:
438 return err;
439}
440
441static int mem_get_config(char *name, char *str, int size, char **error_out)
442{
443 char buf[sizeof("18446744073709551615")];
444 int len = 0;
445
446 sprintf(buf, "%ld", uml_physmem);
447 CONFIG_CHUNK(str, size, len, buf, 1);
448
449 return len;
450}
451
452static int mem_id(char **str, int *start_out, int *end_out)
453{
454 *start_out = 0;
455 *end_out = 0;
456
457 return 0;
458}
459
460static int mem_remove(int n)
461{
462 return -EBUSY;
463}
464
465static struct mc_device mem_mc = {
466 .name = "mem",
467 .config = mem_config,
468 .get_config = mem_get_config,
469 .id = mem_id,
470 .remove = mem_remove,
471};
472
473static int mem_mc_init(void)
474{
475 if(can_drop_memory())
476 mconsole_register_dev(&mem_mc);
477 else printk("Can't release memory to the host - memory hotplug won't "
478 "be supported\n");
479 return 0;
480}
481
482__initcall(mem_mc_init);
483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484#define CONFIG_BUF_SIZE 64
485
Jeff Diked50084a2006-01-06 00:18:50 -0800486static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 char **),
488 struct mc_request *req, char *name)
489{
490 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
491 int n, size;
492
493 if(get_config == NULL){
494 mconsole_reply(req, "No get_config routine defined", 1, 0);
495 return;
496 }
497
498 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700499 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 buf = default_buf;
501
502 while(1){
503 n = (*get_config)(name, buf, size, &error);
504 if(error != NULL){
505 mconsole_reply(req, error, 1, 0);
506 goto out;
507 }
508
509 if(n <= size){
510 mconsole_reply(req, buf, 0, 0);
511 goto out;
512 }
513
514 if(buf != default_buf)
515 kfree(buf);
516
517 size = n;
518 buf = kmalloc(size, GFP_KERNEL);
519 if(buf == NULL){
520 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
521 return;
522 }
523 }
524 out:
525 if(buf != default_buf)
526 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527}
528
529void mconsole_config(struct mc_request *req)
530{
531 struct mc_device *dev;
532 char *ptr = req->request.data, *name;
533 int err;
534
535 ptr += strlen("config");
536 while(isspace(*ptr)) ptr++;
537 dev = mconsole_find_dev(ptr);
538 if(dev == NULL){
539 mconsole_reply(req, "Bad configuration option", 1, 0);
540 return;
541 }
542
543 name = &ptr[strlen(dev->name)];
544 ptr = name;
545 while((*ptr != '=') && (*ptr != '\0'))
546 ptr++;
547
548 if(*ptr == '='){
549 err = (*dev->config)(name);
550 mconsole_reply(req, "", err, 0);
551 }
552 else mconsole_get_config(dev->get_config, req, name);
553}
554
555void mconsole_remove(struct mc_request *req)
556{
Jeff Diked50084a2006-01-06 00:18:50 -0800557 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700558 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800559 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700560 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
562 ptr += strlen("remove");
563 while(isspace(*ptr)) ptr++;
564 dev = mconsole_find_dev(ptr);
565 if(dev == NULL){
566 mconsole_reply(req, "Bad remove option", 1, 0);
567 return;
568 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700569
Jeff Dike3a331a52006-01-06 00:19:05 -0800570 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700571
Jeff Dike3a331a52006-01-06 00:19:05 -0800572 err = 1;
573 n = (*dev->id)(&ptr, &start, &end);
574 if(n < 0){
575 err_msg = "Couldn't parse device number";
576 goto out;
577 }
578 else if((n < start) || (n > end)){
579 sprintf(error, "Invalid device number - must be between "
580 "%d and %d", start, end);
581 err_msg = error;
582 goto out;
583 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700584
585 err = (*dev->remove)(n);
Jeff Dike3a331a52006-01-06 00:19:05 -0800586 switch(err){
587 case -ENODEV:
588 err_msg = "Device doesn't exist";
589 break;
590 case -EBUSY:
591 err_msg = "Device is currently open";
592 break;
593 default:
594 break;
595 }
596out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700597 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598}
599
Jeff Dikef92afe52006-09-29 01:58:52 -0700600struct mconsole_output {
601 struct list_head list;
602 struct mc_request *req;
603};
604
Jeff Dike6f517d32006-01-06 00:19:04 -0800605static DEFINE_SPINLOCK(console_lock);
606static LIST_HEAD(clients);
607static char console_buf[MCONSOLE_MAX_DATA];
608static int console_index = 0;
609
610static void console_write(struct console *console, const char *string,
611 unsigned len)
612{
613 struct list_head *ele;
614 int n;
615
616 if(list_empty(&clients))
617 return;
618
619 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700620 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800621 strncpy(&console_buf[console_index], string, n);
622 console_index += n;
623 string += n;
624 len -= n;
625 if(len == 0)
626 return;
627
628 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700629 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800630
Jeff Dikef92afe52006-09-29 01:58:52 -0700631 entry = list_entry(ele, struct mconsole_output, list);
632 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800633 console_index, 0, 1);
634 }
635
636 console_index = 0;
637 }
638}
639
640static struct console mc_console = { .name = "mc",
641 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800642 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800643 .index = -1 };
644
645static int mc_add_console(void)
646{
647 register_console(&mc_console);
648 return 0;
649}
650
651late_initcall(mc_add_console);
652
653static void with_console(struct mc_request *req, void (*proc)(void *),
654 void *arg)
655{
Jeff Dikef92afe52006-09-29 01:58:52 -0700656 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800657 unsigned long flags;
658
Jeff Dikef92afe52006-09-29 01:58:52 -0700659 entry.req = req;
Jeff Dike6f517d32006-01-06 00:19:04 -0800660 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;
Al Viro7bea96f2006-10-08 22:49:34 +0100676 struct pt_regs *old_regs = set_irq_regs(&current->thread.regs);
677 handle_sysrq(*op, NULL);
678 set_irq_regs(old_regs);
Jeff Dike4111b022006-01-06 00:19:05 -0800679}
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);