blob: 355866af5e0b4430a76f43f6c17d905b2a544b91 [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"
23#include "asm/irq.h"
24#include "asm/uaccess.h"
25#include "user_util.h"
26#include "kern_util.h"
27#include "kern.h"
28#include "mconsole.h"
29#include "mconsole_kern.h"
30#include "irq_user.h"
31#include "init.h"
32#include "os.h"
33#include "umid.h"
34#include "irq_kern.h"
Jeff Dike3eddddc2005-09-16 19:27:46 -070035#include "choose-mode.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
Jeff Diked50084a2006-01-06 00:18:50 -080037static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 unsigned long what, void *data)
39{
40 return(mconsole_unlink_socket());
41}
42
43
44static struct notifier_block reboot_notifier = {
45 .notifier_call = do_unlink_socket,
46 .priority = 0,
47};
48
Jeff Diked50084a2006-01-06 00:18:50 -080049/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 * locking, and the interrupt handler is safe because it can't interrupt
51 * itself and it can only happen on CPU 0.
52 */
53
54LIST_HEAD(mc_requests);
55
56static void mc_work_proc(void *unused)
57{
58 struct mconsole_entry *req;
59 unsigned long flags;
60
61 while(!list_empty(&mc_requests)){
62 local_save_flags(flags);
Jeff Diked50084a2006-01-06 00:18:50 -080063 req = list_entry(mc_requests.next, struct mconsole_entry,
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 list);
65 list_del(&req->list);
66 local_irq_restore(flags);
67 req->request.cmd->handler(&req->request);
68 kfree(req);
69 }
70}
71
72DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
73
74static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
75 struct pt_regs *regs)
76{
77 /* long to avoid size mismatch warnings from gcc */
78 long fd;
79 struct mconsole_entry *new;
80 struct mc_request req;
81
82 fd = (long) dev_id;
83 while (mconsole_get_request(fd, &req)){
84 if(req.cmd->context == MCONSOLE_INTR)
85 (*req.cmd->handler)(&req);
86 else {
87 new = kmalloc(sizeof(*new), GFP_ATOMIC);
88 if(new == NULL)
89 mconsole_reply(&req, "Out of memory", 1, 0);
90 else {
91 new->request = req;
92 list_add(&new->list, &mc_requests);
93 }
94 }
95 }
96 if(!list_empty(&mc_requests))
97 schedule_work(&mconsole_work);
98 reactivate_fd(fd, MCONSOLE_IRQ);
99 return(IRQ_HANDLED);
100}
101
102void mconsole_version(struct mc_request *req)
103{
104 char version[256];
105
Jeff Diked50084a2006-01-06 00:18:50 -0800106 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
107 system_utsname.nodename, system_utsname.release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 system_utsname.version, system_utsname.machine);
109 mconsole_reply(req, version, 0, 0);
110}
111
112void mconsole_log(struct mc_request *req)
113{
114 int len;
115 char *ptr = req->request.data;
116
117 ptr += strlen("log ");
118
119 len = req->len - (ptr - req->request.data);
120 printk("%.*s", len, ptr);
121 mconsole_reply(req, "", 0, 0);
122}
123
124/* This is a more convoluted version of mconsole_proc, which has some stability
125 * problems; however, we need it fixed, because it is expected that UML users
126 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
127 * show the real procfs content, not the ones from hppfs.*/
128#if 0
129void mconsole_proc(struct mc_request *req)
130{
131 struct nameidata nd;
132 struct file_system_type *proc;
133 struct super_block *super;
134 struct file *file;
135 int n, err;
136 char *ptr = req->request.data, *buf;
137
138 ptr += strlen("proc");
139 while(isspace(*ptr)) ptr++;
140
141 proc = get_fs_type("proc");
142 if(proc == NULL){
143 mconsole_reply(req, "procfs not registered", 1, 0);
144 goto out;
145 }
146
147 super = (*proc->get_sb)(proc, 0, NULL, NULL);
148 put_filesystem(proc);
149 if(super == NULL){
150 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
151 goto out;
152 }
153 up_write(&super->s_umount);
154
155 nd.dentry = super->s_root;
156 nd.mnt = NULL;
157 nd.flags = O_RDONLY + 1;
158 nd.last_type = LAST_ROOT;
159
160 /* START: it was experienced that the stability problems are closed
161 * if commenting out these two calls + the below read cycle. To
162 * make UML crash again, it was enough to readd either one.*/
163 err = link_path_walk(ptr, &nd);
164 if(err){
165 mconsole_reply(req, "Failed to look up file", 1, 0);
166 goto out_kill;
167 }
168
169 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
170 if(IS_ERR(file)){
171 mconsole_reply(req, "Failed to open file", 1, 0);
172 goto out_kill;
173 }
174 /*END*/
175
176 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
177 if(buf == NULL){
178 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
179 goto out_fput;
180 }
181
182 if((file->f_op != NULL) && (file->f_op->read != NULL)){
183 do {
184 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
185 &file->f_pos);
186 if(n >= 0){
187 buf[n] = '\0';
188 mconsole_reply(req, buf, 0, (n > 0));
189 }
190 else {
191 mconsole_reply(req, "Read of file failed",
192 1, 0);
193 goto out_free;
194 }
195 } while(n > 0);
196 }
197 else mconsole_reply(req, "", 0, 0);
198
199 out_free:
200 kfree(buf);
201 out_fput:
202 fput(file);
203 out_kill:
204 deactivate_super(super);
205 out: ;
206}
207#endif
208
209void mconsole_proc(struct mc_request *req)
210{
211 char path[64];
212 char *buf;
213 int len;
214 int fd;
215 int first_chunk = 1;
216 char *ptr = req->request.data;
217
218 ptr += strlen("proc");
219 while(isspace(*ptr)) ptr++;
220 snprintf(path, sizeof(path), "/proc/%s", ptr);
221
222 fd = sys_open(path, 0, 0);
223 if (fd < 0) {
224 mconsole_reply(req, "Failed to open file", 1, 0);
225 printk("open %s: %d\n",path,fd);
226 goto out;
227 }
228
229 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
230 if(buf == NULL){
231 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
232 goto out_close;
233 }
234
235 for (;;) {
236 len = sys_read(fd, buf, PAGE_SIZE-1);
237 if (len < 0) {
238 mconsole_reply(req, "Read of file failed", 1, 0);
239 goto out_free;
240 }
241 /*Begin the file content on his own line.*/
242 if (first_chunk) {
243 mconsole_reply(req, "\n", 0, 1);
244 first_chunk = 0;
245 }
246 if (len == PAGE_SIZE-1) {
247 buf[len] = '\0';
248 mconsole_reply(req, buf, 0, 1);
249 } else {
250 buf[len] = '\0';
251 mconsole_reply(req, buf, 0, 0);
252 break;
253 }
254 }
255
256 out_free:
257 kfree(buf);
258 out_close:
259 sys_close(fd);
260 out:
261 /* nothing */;
262}
263
264#define UML_MCONSOLE_HELPTEXT \
265"Commands: \n\
266 version - Get kernel version \n\
267 help - Print this message \n\
268 halt - Halt UML \n\
269 reboot - Reboot UML \n\
270 config <dev>=<config> - Add a new device to UML; \n\
271 same syntax as command line \n\
272 config <dev> - Query the configuration of a device \n\
273 remove <dev> - Remove a device from UML \n\
274 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
275 cad - invoke the Ctl-Alt-Del handler \n\
276 stop - pause the UML; it will do nothing until it receives a 'go' \n\
277 go - continue the UML after a 'stop' \n\
278 log <string> - make UML enter <string> into the kernel log\n\
279 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700280 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281"
282
283void mconsole_help(struct mc_request *req)
284{
285 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
286}
287
288void mconsole_halt(struct mc_request *req)
289{
290 mconsole_reply(req, "", 0, 0);
291 machine_halt();
292}
293
294void mconsole_reboot(struct mc_request *req)
295{
296 mconsole_reply(req, "", 0, 0);
297 machine_restart(NULL);
298}
299
300extern void ctrl_alt_del(void);
301
302void 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
329LIST_HEAD(mconsole_devices);
330
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
349#define CONFIG_BUF_SIZE 64
350
Jeff Diked50084a2006-01-06 00:18:50 -0800351static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 char **),
353 struct mc_request *req, char *name)
354{
355 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
356 int n, size;
357
358 if(get_config == NULL){
359 mconsole_reply(req, "No get_config routine defined", 1, 0);
360 return;
361 }
362
363 error = NULL;
364 size = sizeof(default_buf)/sizeof(default_buf[0]);
365 buf = default_buf;
366
367 while(1){
368 n = (*get_config)(name, buf, size, &error);
369 if(error != NULL){
370 mconsole_reply(req, error, 1, 0);
371 goto out;
372 }
373
374 if(n <= size){
375 mconsole_reply(req, buf, 0, 0);
376 goto out;
377 }
378
379 if(buf != default_buf)
380 kfree(buf);
381
382 size = n;
383 buf = kmalloc(size, GFP_KERNEL);
384 if(buf == NULL){
385 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
386 return;
387 }
388 }
389 out:
390 if(buf != default_buf)
391 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392}
393
394void mconsole_config(struct mc_request *req)
395{
396 struct mc_device *dev;
397 char *ptr = req->request.data, *name;
398 int err;
399
400 ptr += strlen("config");
401 while(isspace(*ptr)) ptr++;
402 dev = mconsole_find_dev(ptr);
403 if(dev == NULL){
404 mconsole_reply(req, "Bad configuration option", 1, 0);
405 return;
406 }
407
408 name = &ptr[strlen(dev->name)];
409 ptr = name;
410 while((*ptr != '=') && (*ptr != '\0'))
411 ptr++;
412
413 if(*ptr == '='){
414 err = (*dev->config)(name);
415 mconsole_reply(req, "", err, 0);
416 }
417 else mconsole_get_config(dev->get_config, req, name);
418}
419
420void mconsole_remove(struct mc_request *req)
421{
Jeff Diked50084a2006-01-06 00:18:50 -0800422 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700423 char *ptr = req->request.data, *err_msg = "";
424 char error[256];
425 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
427 ptr += strlen("remove");
428 while(isspace(*ptr)) ptr++;
429 dev = mconsole_find_dev(ptr);
430 if(dev == NULL){
431 mconsole_reply(req, "Bad remove option", 1, 0);
432 return;
433 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700434
435 ptr = &ptr[strlen(dev->name)];
436
437 err = 1;
438 n = (*dev->id)(&ptr, &start, &end);
439 if(n < 0){
440 err_msg = "Couldn't parse device number";
441 goto out;
442 }
443 else if((n < start) || (n > end)){
444 sprintf(error, "Invalid device number - must be between "
445 "%d and %d", start, end);
446 err_msg = error;
447 goto out;
448 }
449
450 err = (*dev->remove)(n);
451 switch(err){
452 case -ENODEV:
453 err_msg = "Device doesn't exist";
454 break;
455 case -EBUSY:
456 err_msg = "Device is currently open";
457 break;
458 default:
459 break;
460 }
461 out:
462 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
465#ifdef CONFIG_MAGIC_SYSRQ
466void mconsole_sysrq(struct mc_request *req)
467{
468 char *ptr = req->request.data;
469
470 ptr += strlen("sysrq");
471 while(isspace(*ptr)) ptr++;
472
473 mconsole_reply(req, "", 0, 0);
474 handle_sysrq(*ptr, &current->thread.regs, NULL);
475}
476#else
477void mconsole_sysrq(struct mc_request *req)
478{
479 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
480}
481#endif
482
Jeff Dike3eddddc2005-09-16 19:27:46 -0700483/* Mconsole stack trace
484 * Added by Allan Graves, Jeff Dike
485 * Dumps a stacks registers to the linux console.
486 * Usage stack <pid>.
487 */
488void do_stack(struct mc_request *req)
489{
490 char *ptr = req->request.data;
491 int pid_requested= -1;
492 struct task_struct *from = NULL;
493 struct task_struct *to = NULL;
494
495 /* Would be nice:
496 * 1) Send showregs output to mconsole.
497 * 2) Add a way to stack dump all pids.
498 */
499
500 ptr += strlen("stack");
501 while(isspace(*ptr)) ptr++;
502
503 /* Should really check for multiple pids or reject bad args here */
504 /* What do the arguments in mconsole_reply mean? */
505 if(sscanf(ptr, "%d", &pid_requested) == 0){
506 mconsole_reply(req, "Please specify a pid", 1, 0);
507 return;
508 }
509
510 from = current;
511 to = find_task_by_pid(pid_requested);
512
513 if((to == NULL) || (pid_requested == 0)) {
514 mconsole_reply(req, "Couldn't find that pid", 1, 0);
515 return;
516 }
517 to->thread.saved_task = current;
518
519 switch_to(from, to, from);
520 mconsole_reply(req, "Stack Dumped to console and message log", 0, 0);
521}
522
523void mconsole_stack(struct mc_request *req)
524{
525 /* This command doesn't work in TT mode, so let's check and then
526 * get out of here
527 */
528 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
529 1, 0),
530 do_stack(req));
531}
532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533/* Changed by mconsole_setup, which is __setup, and called before SMP is
534 * active.
535 */
Jeff Diked50084a2006-01-06 00:18:50 -0800536static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
538int mconsole_init(void)
539{
540 /* long to avoid size mismatch warnings from gcc */
541 long sock;
542 int err;
543 char file[256];
544
545 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
546 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
547
548 sock = os_create_unix_socket(file, sizeof(file), 1);
549 if (sock < 0){
550 printk("Failed to initialize management console\n");
551 return(1);
552 }
553
554 register_reboot_notifier(&reboot_notifier);
555
556 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
557 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
558 "mconsole", (void *)sock);
559 if (err){
560 printk("Failed to get IRQ for management console\n");
561 return(1);
562 }
563
564 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800565 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 if(notify_socket != NULL)
567 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800568 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 strlen(mconsole_socket_name) + 1);
570 else printk(KERN_ERR "mconsole_setup failed to strdup "
571 "string\n");
572 }
573
Jeff Diked50084a2006-01-06 00:18:50 -0800574 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 MCONSOLE_VERSION, mconsole_socket_name);
576 return(0);
577}
578
579__initcall(mconsole_init);
580
581static int write_proc_mconsole(struct file *file, const char __user *buffer,
582 unsigned long count, void *data)
583{
584 char *buf;
585
586 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800587 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 return(-ENOMEM);
589
590 if(copy_from_user(buf, buffer, count)){
591 count = -EFAULT;
592 goto out;
593 }
594
595 buf[count] = '\0';
596
597 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
598 out:
599 kfree(buf);
600 return(count);
601}
602
603static int create_proc_mconsole(void)
604{
605 struct proc_dir_entry *ent;
606
607 if(notify_socket == NULL) return(0);
608
609 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
610 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700611 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return(0);
613 }
614
615 ent->read_proc = NULL;
616 ent->write_proc = write_proc_mconsole;
617 return(0);
618}
619
620static DEFINE_SPINLOCK(notify_spinlock);
621
622void lock_notify(void)
623{
624 spin_lock(&notify_spinlock);
625}
626
627void unlock_notify(void)
628{
629 spin_unlock(&notify_spinlock);
630}
631
632__initcall(create_proc_mconsole);
633
634#define NOTIFY "=notify:"
635
636static int mconsole_setup(char *str)
637{
638 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
639 str += strlen(NOTIFY);
640 notify_socket = str;
641 }
642 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
643 return(1);
644}
645
646__setup("mconsole", mconsole_setup);
647
648__uml_help(mconsole_setup,
649"mconsole=notify:<socket>\n"
650" Requests that the mconsole driver send a message to the named Unix\n"
651" socket containing the name of the mconsole socket. This also serves\n"
652" to notify outside processes when UML has booted far enough to respond\n"
653" to mconsole requests.\n\n"
654);
655
656static int notify_panic(struct notifier_block *self, unsigned long unused1,
657 void *ptr)
658{
659 char *message = ptr;
660
661 if(notify_socket == NULL) return(0);
662
Jeff Diked50084a2006-01-06 00:18:50 -0800663 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 strlen(message) + 1);
665 return(0);
666}
667
668static struct notifier_block panic_exit_notifier = {
669 .notifier_call = notify_panic,
670 .next = NULL,
671 .priority = 1
672};
673
674static int add_notifier(void)
675{
676 notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
677 return(0);
678}
679
680__initcall(add_notifier);
681
682char *mconsole_notify_socket(void)
683{
684 return(notify_socket);
685}
686
687EXPORT_SYMBOL(mconsole_notify_socket);
688
689/*
690 * Overrides for Emacs so that we follow Linus's tabbing style.
691 * Emacs will notice this stuff at the end of the file and automatically
692 * adjust the settings for this buffer only. This must remain at the end
693 * of the file.
694 * ---------------------------------------------------------------------------
695 * Local variables:
696 * c-file-style: "linux"
697 * End:
698 */