blob: 832d5c766ca88b717623a83b855882fcf646b0df [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
David Howells6d5aefb2006-12-05 19:36:26 +000059static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060{
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
David Howells6d5aefb2006-12-05 19:36:26 +000075static DECLARE_WORK(mconsole_work, mc_work_proc);
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
Jeff Dikef28169d2007-02-10 01:43:53 -0800374static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800375{
376 unsigned long long diff;
377 int err = -EINVAL, i, add;
378 char *ret;
379
Jeff Dikef28169d2007-02-10 01:43:53 -0800380 if(str[0] != '='){
381 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800382 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800383 }
Jeff Dike02dea082006-03-31 02:30:08 -0800384
385 str++;
386 if(str[0] == '-')
387 add = 0;
388 else if(str[0] == '+'){
389 add = 1;
390 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800391 else {
392 *error_out = "Expected increment to start with '-' or '+'";
393 goto out;
394 }
Jeff Dike02dea082006-03-31 02:30:08 -0800395
396 str++;
397 diff = memparse(str, &ret);
Jeff Dikef28169d2007-02-10 01:43:53 -0800398 if(*ret != '\0'){
399 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800400 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800401 }
Jeff Dike02dea082006-03-31 02:30:08 -0800402
403 diff /= PAGE_SIZE;
404
405 for(i = 0; i < diff; i++){
406 struct unplugged_pages *unplugged;
407 void *addr;
408
409 if(add){
410 if(list_empty(&unplugged_pages))
411 break;
412
413 unplugged = list_entry(unplugged_pages.next,
414 struct unplugged_pages, list);
415 if(unplug_index > 0)
416 addr = unplugged->pages[--unplug_index];
417 else {
418 list_del(&unplugged->list);
419 addr = unplugged;
420 unplug_index = UNPLUGGED_PER_PAGE;
421 }
422
423 free_page((unsigned long) addr);
424 unplugged_pages_count--;
425 }
426 else {
427 struct page *page;
428
429 page = alloc_page(GFP_ATOMIC);
430 if(page == NULL)
431 break;
432
433 unplugged = page_address(page);
434 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800435 list_add(&unplugged->list, &unplugged_pages);
436 unplug_index = 0;
437 }
438 else {
439 struct list_head *entry = unplugged_pages.next;
440 addr = unplugged;
441
442 unplugged = list_entry(entry,
443 struct unplugged_pages,
444 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800445 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikef28169d2007-02-10 01:43:53 -0800446 if(err){
Jeff Dike02dea082006-03-31 02:30:08 -0800447 printk("Failed to release memory - "
448 "errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800449 *error_out = "Failed to release memory";
450 goto out;
451 }
452 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800453 }
454
455 unplugged_pages_count++;
456 }
457 }
458
459 err = 0;
460out:
461 return err;
462}
463
464static int mem_get_config(char *name, char *str, int size, char **error_out)
465{
466 char buf[sizeof("18446744073709551615")];
467 int len = 0;
468
469 sprintf(buf, "%ld", uml_physmem);
470 CONFIG_CHUNK(str, size, len, buf, 1);
471
472 return len;
473}
474
475static int mem_id(char **str, int *start_out, int *end_out)
476{
477 *start_out = 0;
478 *end_out = 0;
479
480 return 0;
481}
482
Jeff Dikef28169d2007-02-10 01:43:53 -0800483static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800484{
Jeff Dikef28169d2007-02-10 01:43:53 -0800485 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800486 return -EBUSY;
487}
488
489static struct mc_device mem_mc = {
490 .name = "mem",
491 .config = mem_config,
492 .get_config = mem_get_config,
493 .id = mem_id,
494 .remove = mem_remove,
495};
496
497static int mem_mc_init(void)
498{
499 if(can_drop_memory())
500 mconsole_register_dev(&mem_mc);
501 else printk("Can't release memory to the host - memory hotplug won't "
502 "be supported\n");
503 return 0;
504}
505
506__initcall(mem_mc_init);
507
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508#define CONFIG_BUF_SIZE 64
509
Jeff Diked50084a2006-01-06 00:18:50 -0800510static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 char **),
512 struct mc_request *req, char *name)
513{
514 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
515 int n, size;
516
517 if(get_config == NULL){
518 mconsole_reply(req, "No get_config routine defined", 1, 0);
519 return;
520 }
521
522 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700523 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 buf = default_buf;
525
526 while(1){
527 n = (*get_config)(name, buf, size, &error);
528 if(error != NULL){
529 mconsole_reply(req, error, 1, 0);
530 goto out;
531 }
532
533 if(n <= size){
534 mconsole_reply(req, buf, 0, 0);
535 goto out;
536 }
537
538 if(buf != default_buf)
539 kfree(buf);
540
541 size = n;
542 buf = kmalloc(size, GFP_KERNEL);
543 if(buf == NULL){
544 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
545 return;
546 }
547 }
548 out:
549 if(buf != default_buf)
550 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551}
552
553void mconsole_config(struct mc_request *req)
554{
555 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800556 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 int err;
558
559 ptr += strlen("config");
560 while(isspace(*ptr)) ptr++;
561 dev = mconsole_find_dev(ptr);
562 if(dev == NULL){
563 mconsole_reply(req, "Bad configuration option", 1, 0);
564 return;
565 }
566
567 name = &ptr[strlen(dev->name)];
568 ptr = name;
569 while((*ptr != '=') && (*ptr != '\0'))
570 ptr++;
571
572 if(*ptr == '='){
Jeff Dikef28169d2007-02-10 01:43:53 -0800573 err = (*dev->config)(name, &error_string);
574 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
576 else mconsole_get_config(dev->get_config, req, name);
577}
578
579void mconsole_remove(struct mc_request *req)
580{
Jeff Diked50084a2006-01-06 00:18:50 -0800581 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700582 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800583 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700584 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
586 ptr += strlen("remove");
587 while(isspace(*ptr)) ptr++;
588 dev = mconsole_find_dev(ptr);
589 if(dev == NULL){
590 mconsole_reply(req, "Bad remove option", 1, 0);
591 return;
592 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700593
Jeff Dike3a331a52006-01-06 00:19:05 -0800594 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700595
Jeff Dike3a331a52006-01-06 00:19:05 -0800596 err = 1;
597 n = (*dev->id)(&ptr, &start, &end);
598 if(n < 0){
599 err_msg = "Couldn't parse device number";
600 goto out;
601 }
602 else if((n < start) || (n > end)){
603 sprintf(error, "Invalid device number - must be between "
604 "%d and %d", start, end);
605 err_msg = error;
606 goto out;
607 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700608
Jeff Dikef28169d2007-02-10 01:43:53 -0800609 err_msg = NULL;
610 err = (*dev->remove)(n, &err_msg);
Jeff Dike3a331a52006-01-06 00:19:05 -0800611 switch(err){
612 case -ENODEV:
Jeff Dikef28169d2007-02-10 01:43:53 -0800613 if(err_msg == NULL)
614 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800615 break;
616 case -EBUSY:
Jeff Dikef28169d2007-02-10 01:43:53 -0800617 if(err_msg == NULL)
618 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800619 break;
620 default:
621 break;
622 }
623out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700624 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625}
626
Jeff Dikef92afe52006-09-29 01:58:52 -0700627struct mconsole_output {
628 struct list_head list;
629 struct mc_request *req;
630};
631
Jeff Dike6f517d32006-01-06 00:19:04 -0800632static DEFINE_SPINLOCK(console_lock);
633static LIST_HEAD(clients);
634static char console_buf[MCONSOLE_MAX_DATA];
635static int console_index = 0;
636
637static void console_write(struct console *console, const char *string,
638 unsigned len)
639{
640 struct list_head *ele;
641 int n;
642
643 if(list_empty(&clients))
644 return;
645
646 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700647 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800648 strncpy(&console_buf[console_index], string, n);
649 console_index += n;
650 string += n;
651 len -= n;
652 if(len == 0)
653 return;
654
655 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700656 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800657
Jeff Dikef92afe52006-09-29 01:58:52 -0700658 entry = list_entry(ele, struct mconsole_output, list);
659 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800660 console_index, 0, 1);
661 }
662
663 console_index = 0;
664 }
665}
666
667static struct console mc_console = { .name = "mc",
668 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800669 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800670 .index = -1 };
671
672static int mc_add_console(void)
673{
674 register_console(&mc_console);
675 return 0;
676}
677
678late_initcall(mc_add_console);
679
680static void with_console(struct mc_request *req, void (*proc)(void *),
681 void *arg)
682{
Jeff Dikef92afe52006-09-29 01:58:52 -0700683 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800684 unsigned long flags;
685
Jeff Dikef92afe52006-09-29 01:58:52 -0700686 entry.req = req;
Jeff Dike6f517d32006-01-06 00:19:04 -0800687 list_add(&entry.list, &clients);
688 spin_lock_irqsave(&console_lock, flags);
689
690 (*proc)(arg);
691
692 mconsole_reply_len(req, console_buf, console_index, 0, 0);
693 console_index = 0;
694
695 spin_unlock_irqrestore(&console_lock, flags);
696 list_del(&entry.list);
697}
698
Jeff Dike4111b022006-01-06 00:19:05 -0800699#ifdef CONFIG_MAGIC_SYSRQ
700static void sysrq_proc(void *arg)
701{
702 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100703 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800704}
705
706void mconsole_sysrq(struct mc_request *req)
707{
708 char *ptr = req->request.data;
709
710 ptr += strlen("sysrq");
711 while(isspace(*ptr)) ptr++;
712
713 /* With 'b', the system will shut down without a chance to reply,
714 * so in this case, we reply first.
715 */
716 if(*ptr == 'b')
717 mconsole_reply(req, "", 0, 0);
718
719 with_console(req, sysrq_proc, ptr);
720}
721#else
722void mconsole_sysrq(struct mc_request *req)
723{
724 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
725}
726#endif
727
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800728#ifdef CONFIG_MODE_SKAS
729
Jeff Dike6f517d32006-01-06 00:19:04 -0800730static void stack_proc(void *arg)
731{
732 struct task_struct *from = current, *to = arg;
733
734 to->thread.saved_task = from;
735 switch_to(from, to, from);
736}
737
Jeff Dike3eddddc2005-09-16 19:27:46 -0700738/* Mconsole stack trace
739 * Added by Allan Graves, Jeff Dike
740 * Dumps a stacks registers to the linux console.
741 * Usage stack <pid>.
742 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800743static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700744{
Jeff Dike3a331a52006-01-06 00:19:05 -0800745 char *ptr = req->request.data;
746 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800747 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700748 struct task_struct *to = NULL;
749
Jeff Dike3a331a52006-01-06 00:19:05 -0800750 /* Would be nice:
751 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700752 * 2) Add a way to stack dump all pids.
753 */
754
Jeff Dike3a331a52006-01-06 00:19:05 -0800755 ptr += strlen("stack");
756 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700757
Jeff Dike3a331a52006-01-06 00:19:05 -0800758 /* Should really check for multiple pids or reject bad args here */
759 /* What do the arguments in mconsole_reply mean? */
760 if(sscanf(ptr, "%d", &pid_requested) == 0){
761 mconsole_reply(req, "Please specify a pid", 1, 0);
762 return;
763 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700764
Jeff Dike6f517d32006-01-06 00:19:04 -0800765 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700766
Jeff Dike6f517d32006-01-06 00:19:04 -0800767 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800768 if((to == NULL) || (pid_requested == 0)) {
769 mconsole_reply(req, "Couldn't find that pid", 1, 0);
770 return;
771 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800772 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700773}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800774#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700775
776void mconsole_stack(struct mc_request *req)
777{
778 /* This command doesn't work in TT mode, so let's check and then
779 * get out of here
780 */
781 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
782 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800783 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700784}
785
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786/* Changed by mconsole_setup, which is __setup, and called before SMP is
787 * active.
788 */
Jeff Diked50084a2006-01-06 00:18:50 -0800789static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Jeff Dike90107722006-01-06 00:18:54 -0800791static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792{
793 /* long to avoid size mismatch warnings from gcc */
794 long sock;
795 int err;
796 char file[256];
797
798 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
799 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
800
801 sock = os_create_unix_socket(file, sizeof(file), 1);
802 if (sock < 0){
803 printk("Failed to initialize management console\n");
804 return(1);
805 }
806
807 register_reboot_notifier(&reboot_notifier);
808
809 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700810 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 "mconsole", (void *)sock);
812 if (err){
813 printk("Failed to get IRQ for management console\n");
814 return(1);
815 }
816
817 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800818 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 if(notify_socket != NULL)
820 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800821 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 strlen(mconsole_socket_name) + 1);
823 else printk(KERN_ERR "mconsole_setup failed to strdup "
824 "string\n");
825 }
826
Jeff Diked50084a2006-01-06 00:18:50 -0800827 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 MCONSOLE_VERSION, mconsole_socket_name);
829 return(0);
830}
831
832__initcall(mconsole_init);
833
834static int write_proc_mconsole(struct file *file, const char __user *buffer,
835 unsigned long count, void *data)
836{
837 char *buf;
838
839 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800840 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return(-ENOMEM);
842
843 if(copy_from_user(buf, buffer, count)){
844 count = -EFAULT;
845 goto out;
846 }
847
848 buf[count] = '\0';
849
850 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
851 out:
852 kfree(buf);
853 return(count);
854}
855
856static int create_proc_mconsole(void)
857{
858 struct proc_dir_entry *ent;
859
860 if(notify_socket == NULL) return(0);
861
862 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
863 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700864 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 return(0);
866 }
867
868 ent->read_proc = NULL;
869 ent->write_proc = write_proc_mconsole;
870 return(0);
871}
872
873static DEFINE_SPINLOCK(notify_spinlock);
874
875void lock_notify(void)
876{
877 spin_lock(&notify_spinlock);
878}
879
880void unlock_notify(void)
881{
882 spin_unlock(&notify_spinlock);
883}
884
885__initcall(create_proc_mconsole);
886
887#define NOTIFY "=notify:"
888
889static int mconsole_setup(char *str)
890{
891 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
892 str += strlen(NOTIFY);
893 notify_socket = str;
894 }
895 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
896 return(1);
897}
898
899__setup("mconsole", mconsole_setup);
900
901__uml_help(mconsole_setup,
902"mconsole=notify:<socket>\n"
903" Requests that the mconsole driver send a message to the named Unix\n"
904" socket containing the name of the mconsole socket. This also serves\n"
905" to notify outside processes when UML has booted far enough to respond\n"
906" to mconsole requests.\n\n"
907);
908
909static int notify_panic(struct notifier_block *self, unsigned long unused1,
910 void *ptr)
911{
912 char *message = ptr;
913
914 if(notify_socket == NULL) return(0);
915
Jeff Diked50084a2006-01-06 00:18:50 -0800916 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 strlen(message) + 1);
918 return(0);
919}
920
921static struct notifier_block panic_exit_notifier = {
922 .notifier_call = notify_panic,
923 .next = NULL,
924 .priority = 1
925};
926
927static int add_notifier(void)
928{
Alan Sterne041c682006-03-27 01:16:30 -0800929 atomic_notifier_chain_register(&panic_notifier_list,
930 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 return(0);
932}
933
934__initcall(add_notifier);
935
936char *mconsole_notify_socket(void)
937{
938 return(notify_socket);
939}
940
941EXPORT_SYMBOL(mconsole_notify_socket);