blob: 12c95368124afa58940bba1326ecc4cc1352c4c7 [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
37static int do_unlink_socket(struct notifier_block *notifier,
38 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
49/* Safe without explicit locking for now. Tasklets provide their own
50 * 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);
63 req = list_entry(mc_requests.next, struct mconsole_entry,
64 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
106 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
107 system_utsname.nodename, system_utsname.release,
108 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
351static void mconsole_get_config(int (*get_config)(char *, char *, int,
352 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);
392
393}
394
395void mconsole_config(struct mc_request *req)
396{
397 struct mc_device *dev;
398 char *ptr = req->request.data, *name;
399 int err;
400
401 ptr += strlen("config");
402 while(isspace(*ptr)) ptr++;
403 dev = mconsole_find_dev(ptr);
404 if(dev == NULL){
405 mconsole_reply(req, "Bad configuration option", 1, 0);
406 return;
407 }
408
409 name = &ptr[strlen(dev->name)];
410 ptr = name;
411 while((*ptr != '=') && (*ptr != '\0'))
412 ptr++;
413
414 if(*ptr == '='){
415 err = (*dev->config)(name);
416 mconsole_reply(req, "", err, 0);
417 }
418 else mconsole_get_config(dev->get_config, req, name);
419}
420
421void mconsole_remove(struct mc_request *req)
422{
423 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700424 char *ptr = req->request.data, *err_msg = "";
425 char error[256];
426 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
428 ptr += strlen("remove");
429 while(isspace(*ptr)) ptr++;
430 dev = mconsole_find_dev(ptr);
431 if(dev == NULL){
432 mconsole_reply(req, "Bad remove option", 1, 0);
433 return;
434 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700435
436 ptr = &ptr[strlen(dev->name)];
437
438 err = 1;
439 n = (*dev->id)(&ptr, &start, &end);
440 if(n < 0){
441 err_msg = "Couldn't parse device number";
442 goto out;
443 }
444 else if((n < start) || (n > end)){
445 sprintf(error, "Invalid device number - must be between "
446 "%d and %d", start, end);
447 err_msg = error;
448 goto out;
449 }
450
451 err = (*dev->remove)(n);
452 switch(err){
453 case -ENODEV:
454 err_msg = "Device doesn't exist";
455 break;
456 case -EBUSY:
457 err_msg = "Device is currently open";
458 break;
459 default:
460 break;
461 }
462 out:
463 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464}
465
466#ifdef CONFIG_MAGIC_SYSRQ
467void mconsole_sysrq(struct mc_request *req)
468{
469 char *ptr = req->request.data;
470
471 ptr += strlen("sysrq");
472 while(isspace(*ptr)) ptr++;
473
474 mconsole_reply(req, "", 0, 0);
475 handle_sysrq(*ptr, &current->thread.regs, NULL);
476}
477#else
478void mconsole_sysrq(struct mc_request *req)
479{
480 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
481}
482#endif
483
Jeff Dike3eddddc2005-09-16 19:27:46 -0700484/* Mconsole stack trace
485 * Added by Allan Graves, Jeff Dike
486 * Dumps a stacks registers to the linux console.
487 * Usage stack <pid>.
488 */
489void do_stack(struct mc_request *req)
490{
491 char *ptr = req->request.data;
492 int pid_requested= -1;
493 struct task_struct *from = NULL;
494 struct task_struct *to = NULL;
495
496 /* Would be nice:
497 * 1) Send showregs output to mconsole.
498 * 2) Add a way to stack dump all pids.
499 */
500
501 ptr += strlen("stack");
502 while(isspace(*ptr)) ptr++;
503
504 /* Should really check for multiple pids or reject bad args here */
505 /* What do the arguments in mconsole_reply mean? */
506 if(sscanf(ptr, "%d", &pid_requested) == 0){
507 mconsole_reply(req, "Please specify a pid", 1, 0);
508 return;
509 }
510
511 from = current;
512 to = find_task_by_pid(pid_requested);
513
514 if((to == NULL) || (pid_requested == 0)) {
515 mconsole_reply(req, "Couldn't find that pid", 1, 0);
516 return;
517 }
518 to->thread.saved_task = current;
519
520 switch_to(from, to, from);
521 mconsole_reply(req, "Stack Dumped to console and message log", 0, 0);
522}
523
524void mconsole_stack(struct mc_request *req)
525{
526 /* This command doesn't work in TT mode, so let's check and then
527 * get out of here
528 */
529 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
530 1, 0),
531 do_stack(req));
532}
533
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534/* Changed by mconsole_setup, which is __setup, and called before SMP is
535 * active.
536 */
537static char *notify_socket = NULL;
538
539int mconsole_init(void)
540{
541 /* long to avoid size mismatch warnings from gcc */
542 long sock;
543 int err;
544 char file[256];
545
546 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
547 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
548
549 sock = os_create_unix_socket(file, sizeof(file), 1);
550 if (sock < 0){
551 printk("Failed to initialize management console\n");
552 return(1);
553 }
554
555 register_reboot_notifier(&reboot_notifier);
556
557 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
558 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
559 "mconsole", (void *)sock);
560 if (err){
561 printk("Failed to get IRQ for management console\n");
562 return(1);
563 }
564
565 if(notify_socket != NULL){
566 notify_socket = uml_strdup(notify_socket);
567 if(notify_socket != NULL)
568 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
569 mconsole_socket_name,
570 strlen(mconsole_socket_name) + 1);
571 else printk(KERN_ERR "mconsole_setup failed to strdup "
572 "string\n");
573 }
574
575 printk("mconsole (version %d) initialized on %s\n",
576 MCONSOLE_VERSION, mconsole_socket_name);
577 return(0);
578}
579
580__initcall(mconsole_init);
581
582static int write_proc_mconsole(struct file *file, const char __user *buffer,
583 unsigned long count, void *data)
584{
585 char *buf;
586
587 buf = kmalloc(count + 1, GFP_KERNEL);
588 if(buf == NULL)
589 return(-ENOMEM);
590
591 if(copy_from_user(buf, buffer, count)){
592 count = -EFAULT;
593 goto out;
594 }
595
596 buf[count] = '\0';
597
598 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
599 out:
600 kfree(buf);
601 return(count);
602}
603
604static int create_proc_mconsole(void)
605{
606 struct proc_dir_entry *ent;
607
608 if(notify_socket == NULL) return(0);
609
610 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
611 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700612 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 return(0);
614 }
615
616 ent->read_proc = NULL;
617 ent->write_proc = write_proc_mconsole;
618 return(0);
619}
620
621static DEFINE_SPINLOCK(notify_spinlock);
622
623void lock_notify(void)
624{
625 spin_lock(&notify_spinlock);
626}
627
628void unlock_notify(void)
629{
630 spin_unlock(&notify_spinlock);
631}
632
633__initcall(create_proc_mconsole);
634
635#define NOTIFY "=notify:"
636
637static int mconsole_setup(char *str)
638{
639 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
640 str += strlen(NOTIFY);
641 notify_socket = str;
642 }
643 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
644 return(1);
645}
646
647__setup("mconsole", mconsole_setup);
648
649__uml_help(mconsole_setup,
650"mconsole=notify:<socket>\n"
651" Requests that the mconsole driver send a message to the named Unix\n"
652" socket containing the name of the mconsole socket. This also serves\n"
653" to notify outside processes when UML has booted far enough to respond\n"
654" to mconsole requests.\n\n"
655);
656
657static int notify_panic(struct notifier_block *self, unsigned long unused1,
658 void *ptr)
659{
660 char *message = ptr;
661
662 if(notify_socket == NULL) return(0);
663
664 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
665 strlen(message) + 1);
666 return(0);
667}
668
669static struct notifier_block panic_exit_notifier = {
670 .notifier_call = notify_panic,
671 .next = NULL,
672 .priority = 1
673};
674
675static int add_notifier(void)
676{
677 notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
678 return(0);
679}
680
681__initcall(add_notifier);
682
683char *mconsole_notify_socket(void)
684{
685 return(notify_socket);
686}
687
688EXPORT_SYMBOL(mconsole_notify_socket);
689
690/*
691 * Overrides for Emacs so that we follow Linus's tabbing style.
692 * Emacs will notice this stuff at the end of the file and automatically
693 * adjust the settings for this buffer only. This must remain at the end
694 * of the file.
695 * ---------------------------------------------------------------------------
696 * Local variables:
697 * c-file-style: "linux"
698 * End:
699 */