blob: 7b172160fe0415375d3e5bc1bbbcb00c6ed71105 [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;
Al Viro3a512372006-10-24 11:15:29 +010082 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
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;
Al Viro3a512372006-10-24 11:15:29 +010094 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 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
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700109 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
110 utsname()->nodename, utsname()->release,
111 utsname()->version, utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 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);
Al Viro3a512372006-10-24 11:15:29 +0100318 mconsole_reply(req, "stopped", 0, 0);
319 while (mconsole_get_request(req->originating_fd, req)) {
320 if (req->cmd->handler == mconsole_go)
321 break;
322 if (req->cmd->handler == mconsole_stop) {
323 mconsole_reply(req, "Already stopped", 1, 0);
324 continue;
325 }
326 if (req->cmd->handler == mconsole_sysrq) {
327 struct pt_regs *old_regs;
328 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
329 mconsole_sysrq(req);
330 set_irq_regs(old_regs);
331 continue;
332 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 (*req->cmd->handler)(req);
334 }
335 os_set_fd_block(req->originating_fd, 0);
336 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
337 mconsole_reply(req, "", 0, 0);
338}
339
340/* This list is populated by __initcall routines. */
341
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800342static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344void mconsole_register_dev(struct mc_device *new)
345{
346 list_add(&new->list, &mconsole_devices);
347}
348
349static struct mc_device *mconsole_find_dev(char *name)
350{
351 struct list_head *ele;
352 struct mc_device *dev;
353
354 list_for_each(ele, &mconsole_devices){
355 dev = list_entry(ele, struct mc_device, list);
356 if(!strncmp(name, dev->name, strlen(dev->name)))
357 return(dev);
358 }
359 return(NULL);
360}
361
Jeff Dike02dea082006-03-31 02:30:08 -0800362#define UNPLUGGED_PER_PAGE \
363 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
364
365struct unplugged_pages {
366 struct list_head list;
367 void *pages[UNPLUGGED_PER_PAGE];
368};
369
370static unsigned long long unplugged_pages_count = 0;
371static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
372static int unplug_index = UNPLUGGED_PER_PAGE;
373
374static int mem_config(char *str)
375{
376 unsigned long long diff;
377 int err = -EINVAL, i, add;
378 char *ret;
379
380 if(str[0] != '=')
381 goto out;
382
383 str++;
384 if(str[0] == '-')
385 add = 0;
386 else if(str[0] == '+'){
387 add = 1;
388 }
389 else goto out;
390
391 str++;
392 diff = memparse(str, &ret);
393 if(*ret != '\0')
394 goto out;
395
396 diff /= PAGE_SIZE;
397
398 for(i = 0; i < diff; i++){
399 struct unplugged_pages *unplugged;
400 void *addr;
401
402 if(add){
403 if(list_empty(&unplugged_pages))
404 break;
405
406 unplugged = list_entry(unplugged_pages.next,
407 struct unplugged_pages, list);
408 if(unplug_index > 0)
409 addr = unplugged->pages[--unplug_index];
410 else {
411 list_del(&unplugged->list);
412 addr = unplugged;
413 unplug_index = UNPLUGGED_PER_PAGE;
414 }
415
416 free_page((unsigned long) addr);
417 unplugged_pages_count--;
418 }
419 else {
420 struct page *page;
421
422 page = alloc_page(GFP_ATOMIC);
423 if(page == NULL)
424 break;
425
426 unplugged = page_address(page);
427 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800428 list_add(&unplugged->list, &unplugged_pages);
429 unplug_index = 0;
430 }
431 else {
432 struct list_head *entry = unplugged_pages.next;
433 addr = unplugged;
434
435 unplugged = list_entry(entry,
436 struct unplugged_pages,
437 list);
438 unplugged->pages[unplug_index++] = addr;
439 err = os_drop_memory(addr, PAGE_SIZE);
440 if(err)
441 printk("Failed to release memory - "
442 "errno = %d\n", err);
443 }
444
445 unplugged_pages_count++;
446 }
447 }
448
449 err = 0;
450out:
451 return err;
452}
453
454static int mem_get_config(char *name, char *str, int size, char **error_out)
455{
456 char buf[sizeof("18446744073709551615")];
457 int len = 0;
458
459 sprintf(buf, "%ld", uml_physmem);
460 CONFIG_CHUNK(str, size, len, buf, 1);
461
462 return len;
463}
464
465static int mem_id(char **str, int *start_out, int *end_out)
466{
467 *start_out = 0;
468 *end_out = 0;
469
470 return 0;
471}
472
473static int mem_remove(int n)
474{
475 return -EBUSY;
476}
477
478static struct mc_device mem_mc = {
479 .name = "mem",
480 .config = mem_config,
481 .get_config = mem_get_config,
482 .id = mem_id,
483 .remove = mem_remove,
484};
485
486static int mem_mc_init(void)
487{
488 if(can_drop_memory())
489 mconsole_register_dev(&mem_mc);
490 else printk("Can't release memory to the host - memory hotplug won't "
491 "be supported\n");
492 return 0;
493}
494
495__initcall(mem_mc_init);
496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497#define CONFIG_BUF_SIZE 64
498
Jeff Diked50084a2006-01-06 00:18:50 -0800499static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 char **),
501 struct mc_request *req, char *name)
502{
503 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
504 int n, size;
505
506 if(get_config == NULL){
507 mconsole_reply(req, "No get_config routine defined", 1, 0);
508 return;
509 }
510
511 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700512 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 buf = default_buf;
514
515 while(1){
516 n = (*get_config)(name, buf, size, &error);
517 if(error != NULL){
518 mconsole_reply(req, error, 1, 0);
519 goto out;
520 }
521
522 if(n <= size){
523 mconsole_reply(req, buf, 0, 0);
524 goto out;
525 }
526
527 if(buf != default_buf)
528 kfree(buf);
529
530 size = n;
531 buf = kmalloc(size, GFP_KERNEL);
532 if(buf == NULL){
533 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
534 return;
535 }
536 }
537 out:
538 if(buf != default_buf)
539 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540}
541
542void mconsole_config(struct mc_request *req)
543{
544 struct mc_device *dev;
545 char *ptr = req->request.data, *name;
546 int err;
547
548 ptr += strlen("config");
549 while(isspace(*ptr)) ptr++;
550 dev = mconsole_find_dev(ptr);
551 if(dev == NULL){
552 mconsole_reply(req, "Bad configuration option", 1, 0);
553 return;
554 }
555
556 name = &ptr[strlen(dev->name)];
557 ptr = name;
558 while((*ptr != '=') && (*ptr != '\0'))
559 ptr++;
560
561 if(*ptr == '='){
562 err = (*dev->config)(name);
563 mconsole_reply(req, "", err, 0);
564 }
565 else mconsole_get_config(dev->get_config, req, name);
566}
567
568void mconsole_remove(struct mc_request *req)
569{
Jeff Diked50084a2006-01-06 00:18:50 -0800570 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700571 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800572 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700573 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
575 ptr += strlen("remove");
576 while(isspace(*ptr)) ptr++;
577 dev = mconsole_find_dev(ptr);
578 if(dev == NULL){
579 mconsole_reply(req, "Bad remove option", 1, 0);
580 return;
581 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700582
Jeff Dike3a331a52006-01-06 00:19:05 -0800583 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700584
Jeff Dike3a331a52006-01-06 00:19:05 -0800585 err = 1;
586 n = (*dev->id)(&ptr, &start, &end);
587 if(n < 0){
588 err_msg = "Couldn't parse device number";
589 goto out;
590 }
591 else if((n < start) || (n > end)){
592 sprintf(error, "Invalid device number - must be between "
593 "%d and %d", start, end);
594 err_msg = error;
595 goto out;
596 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700597
598 err = (*dev->remove)(n);
Jeff Dike3a331a52006-01-06 00:19:05 -0800599 switch(err){
600 case -ENODEV:
601 err_msg = "Device doesn't exist";
602 break;
603 case -EBUSY:
604 err_msg = "Device is currently open";
605 break;
606 default:
607 break;
608 }
609out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700610 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
612
Jeff Dikef92afe52006-09-29 01:58:52 -0700613struct mconsole_output {
614 struct list_head list;
615 struct mc_request *req;
616};
617
Jeff Dike6f517d32006-01-06 00:19:04 -0800618static DEFINE_SPINLOCK(console_lock);
619static LIST_HEAD(clients);
620static char console_buf[MCONSOLE_MAX_DATA];
621static int console_index = 0;
622
623static void console_write(struct console *console, const char *string,
624 unsigned len)
625{
626 struct list_head *ele;
627 int n;
628
629 if(list_empty(&clients))
630 return;
631
632 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700633 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800634 strncpy(&console_buf[console_index], string, n);
635 console_index += n;
636 string += n;
637 len -= n;
638 if(len == 0)
639 return;
640
641 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700642 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800643
Jeff Dikef92afe52006-09-29 01:58:52 -0700644 entry = list_entry(ele, struct mconsole_output, list);
645 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800646 console_index, 0, 1);
647 }
648
649 console_index = 0;
650 }
651}
652
653static struct console mc_console = { .name = "mc",
654 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800655 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800656 .index = -1 };
657
658static int mc_add_console(void)
659{
660 register_console(&mc_console);
661 return 0;
662}
663
664late_initcall(mc_add_console);
665
666static void with_console(struct mc_request *req, void (*proc)(void *),
667 void *arg)
668{
Jeff Dikef92afe52006-09-29 01:58:52 -0700669 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800670 unsigned long flags;
671
Jeff Dikef92afe52006-09-29 01:58:52 -0700672 entry.req = req;
Jeff Dike6f517d32006-01-06 00:19:04 -0800673 list_add(&entry.list, &clients);
674 spin_lock_irqsave(&console_lock, flags);
675
676 (*proc)(arg);
677
678 mconsole_reply_len(req, console_buf, console_index, 0, 0);
679 console_index = 0;
680
681 spin_unlock_irqrestore(&console_lock, flags);
682 list_del(&entry.list);
683}
684
Jeff Dike4111b022006-01-06 00:19:05 -0800685#ifdef CONFIG_MAGIC_SYSRQ
686static void sysrq_proc(void *arg)
687{
688 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100689 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800690}
691
692void mconsole_sysrq(struct mc_request *req)
693{
694 char *ptr = req->request.data;
695
696 ptr += strlen("sysrq");
697 while(isspace(*ptr)) ptr++;
698
699 /* With 'b', the system will shut down without a chance to reply,
700 * so in this case, we reply first.
701 */
702 if(*ptr == 'b')
703 mconsole_reply(req, "", 0, 0);
704
705 with_console(req, sysrq_proc, ptr);
706}
707#else
708void mconsole_sysrq(struct mc_request *req)
709{
710 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
711}
712#endif
713
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800714#ifdef CONFIG_MODE_SKAS
715
Jeff Dike6f517d32006-01-06 00:19:04 -0800716static void stack_proc(void *arg)
717{
718 struct task_struct *from = current, *to = arg;
719
720 to->thread.saved_task = from;
721 switch_to(from, to, from);
722}
723
Jeff Dike3eddddc2005-09-16 19:27:46 -0700724/* Mconsole stack trace
725 * Added by Allan Graves, Jeff Dike
726 * Dumps a stacks registers to the linux console.
727 * Usage stack <pid>.
728 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800729static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700730{
Jeff Dike3a331a52006-01-06 00:19:05 -0800731 char *ptr = req->request.data;
732 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800733 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700734 struct task_struct *to = NULL;
735
Jeff Dike3a331a52006-01-06 00:19:05 -0800736 /* Would be nice:
737 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700738 * 2) Add a way to stack dump all pids.
739 */
740
Jeff Dike3a331a52006-01-06 00:19:05 -0800741 ptr += strlen("stack");
742 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700743
Jeff Dike3a331a52006-01-06 00:19:05 -0800744 /* Should really check for multiple pids or reject bad args here */
745 /* What do the arguments in mconsole_reply mean? */
746 if(sscanf(ptr, "%d", &pid_requested) == 0){
747 mconsole_reply(req, "Please specify a pid", 1, 0);
748 return;
749 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700750
Jeff Dike6f517d32006-01-06 00:19:04 -0800751 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700752
Jeff Dike6f517d32006-01-06 00:19:04 -0800753 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800754 if((to == NULL) || (pid_requested == 0)) {
755 mconsole_reply(req, "Couldn't find that pid", 1, 0);
756 return;
757 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800758 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700759}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800760#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700761
762void mconsole_stack(struct mc_request *req)
763{
764 /* This command doesn't work in TT mode, so let's check and then
765 * get out of here
766 */
767 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
768 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800769 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700770}
771
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772/* Changed by mconsole_setup, which is __setup, and called before SMP is
773 * active.
774 */
Jeff Diked50084a2006-01-06 00:18:50 -0800775static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Jeff Dike90107722006-01-06 00:18:54 -0800777static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
779 /* long to avoid size mismatch warnings from gcc */
780 long sock;
781 int err;
782 char file[256];
783
784 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
785 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
786
787 sock = os_create_unix_socket(file, sizeof(file), 1);
788 if (sock < 0){
789 printk("Failed to initialize management console\n");
790 return(1);
791 }
792
793 register_reboot_notifier(&reboot_notifier);
794
795 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700796 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 "mconsole", (void *)sock);
798 if (err){
799 printk("Failed to get IRQ for management console\n");
800 return(1);
801 }
802
803 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800804 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 if(notify_socket != NULL)
806 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800807 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 strlen(mconsole_socket_name) + 1);
809 else printk(KERN_ERR "mconsole_setup failed to strdup "
810 "string\n");
811 }
812
Jeff Diked50084a2006-01-06 00:18:50 -0800813 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 MCONSOLE_VERSION, mconsole_socket_name);
815 return(0);
816}
817
818__initcall(mconsole_init);
819
820static int write_proc_mconsole(struct file *file, const char __user *buffer,
821 unsigned long count, void *data)
822{
823 char *buf;
824
825 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800826 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 return(-ENOMEM);
828
829 if(copy_from_user(buf, buffer, count)){
830 count = -EFAULT;
831 goto out;
832 }
833
834 buf[count] = '\0';
835
836 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
837 out:
838 kfree(buf);
839 return(count);
840}
841
842static int create_proc_mconsole(void)
843{
844 struct proc_dir_entry *ent;
845
846 if(notify_socket == NULL) return(0);
847
848 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
849 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700850 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 return(0);
852 }
853
854 ent->read_proc = NULL;
855 ent->write_proc = write_proc_mconsole;
856 return(0);
857}
858
859static DEFINE_SPINLOCK(notify_spinlock);
860
861void lock_notify(void)
862{
863 spin_lock(&notify_spinlock);
864}
865
866void unlock_notify(void)
867{
868 spin_unlock(&notify_spinlock);
869}
870
871__initcall(create_proc_mconsole);
872
873#define NOTIFY "=notify:"
874
875static int mconsole_setup(char *str)
876{
877 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
878 str += strlen(NOTIFY);
879 notify_socket = str;
880 }
881 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
882 return(1);
883}
884
885__setup("mconsole", mconsole_setup);
886
887__uml_help(mconsole_setup,
888"mconsole=notify:<socket>\n"
889" Requests that the mconsole driver send a message to the named Unix\n"
890" socket containing the name of the mconsole socket. This also serves\n"
891" to notify outside processes when UML has booted far enough to respond\n"
892" to mconsole requests.\n\n"
893);
894
895static int notify_panic(struct notifier_block *self, unsigned long unused1,
896 void *ptr)
897{
898 char *message = ptr;
899
900 if(notify_socket == NULL) return(0);
901
Jeff Diked50084a2006-01-06 00:18:50 -0800902 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 strlen(message) + 1);
904 return(0);
905}
906
907static struct notifier_block panic_exit_notifier = {
908 .notifier_call = notify_panic,
909 .next = NULL,
910 .priority = 1
911};
912
913static int add_notifier(void)
914{
Alan Sterne041c682006-03-27 01:16:30 -0800915 atomic_notifier_chain_register(&panic_notifier_list,
916 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 return(0);
918}
919
920__initcall(add_notifier);
921
922char *mconsole_notify_socket(void)
923{
924 return(notify_socket);
925}
926
927EXPORT_SYMBOL(mconsole_notify_socket);