blob: b4dbd102da0ee779cfbbe42c5f87a28872622f2e [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
Jeff Dike84f48d42007-02-10 01:44:01 -0800340static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800341static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
343void mconsole_register_dev(struct mc_device *new)
344{
Jeff Dike84f48d42007-02-10 01:44:01 -0800345 spin_lock(&mc_devices_lock);
346 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800348 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349}
350
351static struct mc_device *mconsole_find_dev(char *name)
352{
353 struct list_head *ele;
354 struct mc_device *dev;
355
356 list_for_each(ele, &mconsole_devices){
357 dev = list_entry(ele, struct mc_device, list);
358 if(!strncmp(name, dev->name, strlen(dev->name)))
359 return(dev);
360 }
361 return(NULL);
362}
363
Jeff Dike02dea082006-03-31 02:30:08 -0800364#define UNPLUGGED_PER_PAGE \
365 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
366
367struct unplugged_pages {
368 struct list_head list;
369 void *pages[UNPLUGGED_PER_PAGE];
370};
371
Jeff Dike84f48d42007-02-10 01:44:01 -0800372static DECLARE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800373static unsigned long long unplugged_pages_count = 0;
374static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
375static int unplug_index = UNPLUGGED_PER_PAGE;
376
Jeff Dikef28169d2007-02-10 01:43:53 -0800377static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800378{
379 unsigned long long diff;
380 int err = -EINVAL, i, add;
381 char *ret;
382
Jeff Dikef28169d2007-02-10 01:43:53 -0800383 if(str[0] != '='){
384 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800385 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800386 }
Jeff Dike02dea082006-03-31 02:30:08 -0800387
388 str++;
389 if(str[0] == '-')
390 add = 0;
391 else if(str[0] == '+'){
392 add = 1;
393 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800394 else {
395 *error_out = "Expected increment to start with '-' or '+'";
396 goto out;
397 }
Jeff Dike02dea082006-03-31 02:30:08 -0800398
399 str++;
400 diff = memparse(str, &ret);
Jeff Dikef28169d2007-02-10 01:43:53 -0800401 if(*ret != '\0'){
402 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800403 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800404 }
Jeff Dike02dea082006-03-31 02:30:08 -0800405
406 diff /= PAGE_SIZE;
407
Jeff Dike84f48d42007-02-10 01:44:01 -0800408 down(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800409 for(i = 0; i < diff; i++){
410 struct unplugged_pages *unplugged;
411 void *addr;
412
413 if(add){
414 if(list_empty(&unplugged_pages))
415 break;
416
417 unplugged = list_entry(unplugged_pages.next,
418 struct unplugged_pages, list);
419 if(unplug_index > 0)
420 addr = unplugged->pages[--unplug_index];
421 else {
422 list_del(&unplugged->list);
423 addr = unplugged;
424 unplug_index = UNPLUGGED_PER_PAGE;
425 }
426
427 free_page((unsigned long) addr);
428 unplugged_pages_count--;
429 }
430 else {
431 struct page *page;
432
433 page = alloc_page(GFP_ATOMIC);
434 if(page == NULL)
435 break;
436
437 unplugged = page_address(page);
438 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800439 list_add(&unplugged->list, &unplugged_pages);
440 unplug_index = 0;
441 }
442 else {
443 struct list_head *entry = unplugged_pages.next;
444 addr = unplugged;
445
446 unplugged = list_entry(entry,
447 struct unplugged_pages,
448 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800449 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikef28169d2007-02-10 01:43:53 -0800450 if(err){
Jeff Dike02dea082006-03-31 02:30:08 -0800451 printk("Failed to release memory - "
452 "errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800453 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800454 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800455 }
456 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800457 }
458
459 unplugged_pages_count++;
460 }
461 }
462
463 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800464out_unlock:
465 up(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800466out:
467 return err;
468}
469
470static int mem_get_config(char *name, char *str, int size, char **error_out)
471{
472 char buf[sizeof("18446744073709551615")];
473 int len = 0;
474
475 sprintf(buf, "%ld", uml_physmem);
476 CONFIG_CHUNK(str, size, len, buf, 1);
477
478 return len;
479}
480
481static int mem_id(char **str, int *start_out, int *end_out)
482{
483 *start_out = 0;
484 *end_out = 0;
485
486 return 0;
487}
488
Jeff Dikef28169d2007-02-10 01:43:53 -0800489static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800490{
Jeff Dikef28169d2007-02-10 01:43:53 -0800491 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800492 return -EBUSY;
493}
494
495static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800496 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800497 .name = "mem",
498 .config = mem_config,
499 .get_config = mem_get_config,
500 .id = mem_id,
501 .remove = mem_remove,
502};
503
504static int mem_mc_init(void)
505{
506 if(can_drop_memory())
507 mconsole_register_dev(&mem_mc);
508 else printk("Can't release memory to the host - memory hotplug won't "
509 "be supported\n");
510 return 0;
511}
512
513__initcall(mem_mc_init);
514
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515#define CONFIG_BUF_SIZE 64
516
Jeff Diked50084a2006-01-06 00:18:50 -0800517static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 char **),
519 struct mc_request *req, char *name)
520{
521 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
522 int n, size;
523
524 if(get_config == NULL){
525 mconsole_reply(req, "No get_config routine defined", 1, 0);
526 return;
527 }
528
529 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700530 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 buf = default_buf;
532
533 while(1){
534 n = (*get_config)(name, buf, size, &error);
535 if(error != NULL){
536 mconsole_reply(req, error, 1, 0);
537 goto out;
538 }
539
540 if(n <= size){
541 mconsole_reply(req, buf, 0, 0);
542 goto out;
543 }
544
545 if(buf != default_buf)
546 kfree(buf);
547
548 size = n;
549 buf = kmalloc(size, GFP_KERNEL);
550 if(buf == NULL){
551 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
552 return;
553 }
554 }
555 out:
556 if(buf != default_buf)
557 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558}
559
560void mconsole_config(struct mc_request *req)
561{
562 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800563 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 int err;
565
566 ptr += strlen("config");
567 while(isspace(*ptr)) ptr++;
568 dev = mconsole_find_dev(ptr);
569 if(dev == NULL){
570 mconsole_reply(req, "Bad configuration option", 1, 0);
571 return;
572 }
573
574 name = &ptr[strlen(dev->name)];
575 ptr = name;
576 while((*ptr != '=') && (*ptr != '\0'))
577 ptr++;
578
579 if(*ptr == '='){
Jeff Dikef28169d2007-02-10 01:43:53 -0800580 err = (*dev->config)(name, &error_string);
581 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 }
583 else mconsole_get_config(dev->get_config, req, name);
584}
585
586void mconsole_remove(struct mc_request *req)
587{
Jeff Diked50084a2006-01-06 00:18:50 -0800588 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700589 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800590 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700591 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
593 ptr += strlen("remove");
594 while(isspace(*ptr)) ptr++;
595 dev = mconsole_find_dev(ptr);
596 if(dev == NULL){
597 mconsole_reply(req, "Bad remove option", 1, 0);
598 return;
599 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700600
Jeff Dike3a331a52006-01-06 00:19:05 -0800601 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700602
Jeff Dike3a331a52006-01-06 00:19:05 -0800603 err = 1;
604 n = (*dev->id)(&ptr, &start, &end);
605 if(n < 0){
606 err_msg = "Couldn't parse device number";
607 goto out;
608 }
609 else if((n < start) || (n > end)){
610 sprintf(error, "Invalid device number - must be between "
611 "%d and %d", start, end);
612 err_msg = error;
613 goto out;
614 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700615
Jeff Dikef28169d2007-02-10 01:43:53 -0800616 err_msg = NULL;
617 err = (*dev->remove)(n, &err_msg);
Jeff Dike3a331a52006-01-06 00:19:05 -0800618 switch(err){
619 case -ENODEV:
Jeff Dikef28169d2007-02-10 01:43:53 -0800620 if(err_msg == NULL)
621 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800622 break;
623 case -EBUSY:
Jeff Dikef28169d2007-02-10 01:43:53 -0800624 if(err_msg == NULL)
625 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800626 break;
627 default:
628 break;
629 }
630out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700631 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632}
633
Jeff Dikef92afe52006-09-29 01:58:52 -0700634struct mconsole_output {
635 struct list_head list;
636 struct mc_request *req;
637};
638
Jeff Dike84f48d42007-02-10 01:44:01 -0800639static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800640static LIST_HEAD(clients);
641static char console_buf[MCONSOLE_MAX_DATA];
642static int console_index = 0;
643
644static void console_write(struct console *console, const char *string,
645 unsigned len)
646{
647 struct list_head *ele;
648 int n;
649
650 if(list_empty(&clients))
651 return;
652
653 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700654 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800655 strncpy(&console_buf[console_index], string, n);
656 console_index += n;
657 string += n;
658 len -= n;
659 if(len == 0)
660 return;
661
662 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700663 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800664
Jeff Dikef92afe52006-09-29 01:58:52 -0700665 entry = list_entry(ele, struct mconsole_output, list);
666 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800667 console_index, 0, 1);
668 }
669
670 console_index = 0;
671 }
672}
673
674static struct console mc_console = { .name = "mc",
675 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800676 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800677 .index = -1 };
678
679static int mc_add_console(void)
680{
681 register_console(&mc_console);
682 return 0;
683}
684
685late_initcall(mc_add_console);
686
687static void with_console(struct mc_request *req, void (*proc)(void *),
688 void *arg)
689{
Jeff Dikef92afe52006-09-29 01:58:52 -0700690 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800691 unsigned long flags;
692
Jeff Dikef92afe52006-09-29 01:58:52 -0700693 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800694 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800695 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800696 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800697
698 (*proc)(arg);
699
700 mconsole_reply_len(req, console_buf, console_index, 0, 0);
701 console_index = 0;
702
Jeff Dike84f48d42007-02-10 01:44:01 -0800703 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800704 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800705 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800706}
707
Jeff Dike4111b022006-01-06 00:19:05 -0800708#ifdef CONFIG_MAGIC_SYSRQ
709static void sysrq_proc(void *arg)
710{
711 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100712 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800713}
714
715void mconsole_sysrq(struct mc_request *req)
716{
717 char *ptr = req->request.data;
718
719 ptr += strlen("sysrq");
720 while(isspace(*ptr)) ptr++;
721
722 /* With 'b', the system will shut down without a chance to reply,
723 * so in this case, we reply first.
724 */
725 if(*ptr == 'b')
726 mconsole_reply(req, "", 0, 0);
727
728 with_console(req, sysrq_proc, ptr);
729}
730#else
731void mconsole_sysrq(struct mc_request *req)
732{
733 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
734}
735#endif
736
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800737#ifdef CONFIG_MODE_SKAS
738
Jeff Dike6f517d32006-01-06 00:19:04 -0800739static void stack_proc(void *arg)
740{
741 struct task_struct *from = current, *to = arg;
742
743 to->thread.saved_task = from;
744 switch_to(from, to, from);
745}
746
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747/* Mconsole stack trace
748 * Added by Allan Graves, Jeff Dike
749 * Dumps a stacks registers to the linux console.
750 * Usage stack <pid>.
751 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800752static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700753{
Jeff Dike3a331a52006-01-06 00:19:05 -0800754 char *ptr = req->request.data;
755 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800756 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700757 struct task_struct *to = NULL;
758
Jeff Dike3a331a52006-01-06 00:19:05 -0800759 /* Would be nice:
760 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700761 * 2) Add a way to stack dump all pids.
762 */
763
Jeff Dike3a331a52006-01-06 00:19:05 -0800764 ptr += strlen("stack");
765 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700766
Jeff Dike3a331a52006-01-06 00:19:05 -0800767 /* Should really check for multiple pids or reject bad args here */
768 /* What do the arguments in mconsole_reply mean? */
769 if(sscanf(ptr, "%d", &pid_requested) == 0){
770 mconsole_reply(req, "Please specify a pid", 1, 0);
771 return;
772 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700773
Jeff Dike6f517d32006-01-06 00:19:04 -0800774 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700775
Jeff Dike6f517d32006-01-06 00:19:04 -0800776 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800777 if((to == NULL) || (pid_requested == 0)) {
778 mconsole_reply(req, "Couldn't find that pid", 1, 0);
779 return;
780 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800781 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700782}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800783#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700784
785void mconsole_stack(struct mc_request *req)
786{
787 /* This command doesn't work in TT mode, so let's check and then
788 * get out of here
789 */
790 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
791 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800792 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700793}
794
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795/* Changed by mconsole_setup, which is __setup, and called before SMP is
796 * active.
797 */
Jeff Diked50084a2006-01-06 00:18:50 -0800798static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Jeff Dike90107722006-01-06 00:18:54 -0800800static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801{
802 /* long to avoid size mismatch warnings from gcc */
803 long sock;
804 int err;
805 char file[256];
806
807 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
808 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
809
810 sock = os_create_unix_socket(file, sizeof(file), 1);
811 if (sock < 0){
812 printk("Failed to initialize management console\n");
813 return(1);
814 }
815
816 register_reboot_notifier(&reboot_notifier);
817
818 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700819 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 "mconsole", (void *)sock);
821 if (err){
822 printk("Failed to get IRQ for management console\n");
823 return(1);
824 }
825
826 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800827 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 if(notify_socket != NULL)
829 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800830 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 strlen(mconsole_socket_name) + 1);
832 else printk(KERN_ERR "mconsole_setup failed to strdup "
833 "string\n");
834 }
835
Jeff Diked50084a2006-01-06 00:18:50 -0800836 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 MCONSOLE_VERSION, mconsole_socket_name);
838 return(0);
839}
840
841__initcall(mconsole_init);
842
843static int write_proc_mconsole(struct file *file, const char __user *buffer,
844 unsigned long count, void *data)
845{
846 char *buf;
847
848 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800849 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 return(-ENOMEM);
851
852 if(copy_from_user(buf, buffer, count)){
853 count = -EFAULT;
854 goto out;
855 }
856
857 buf[count] = '\0';
858
859 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
860 out:
861 kfree(buf);
862 return(count);
863}
864
865static int create_proc_mconsole(void)
866{
867 struct proc_dir_entry *ent;
868
869 if(notify_socket == NULL) return(0);
870
871 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
872 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700873 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 return(0);
875 }
876
877 ent->read_proc = NULL;
878 ent->write_proc = write_proc_mconsole;
879 return(0);
880}
881
882static DEFINE_SPINLOCK(notify_spinlock);
883
884void lock_notify(void)
885{
886 spin_lock(&notify_spinlock);
887}
888
889void unlock_notify(void)
890{
891 spin_unlock(&notify_spinlock);
892}
893
894__initcall(create_proc_mconsole);
895
896#define NOTIFY "=notify:"
897
898static int mconsole_setup(char *str)
899{
900 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
901 str += strlen(NOTIFY);
902 notify_socket = str;
903 }
904 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
905 return(1);
906}
907
908__setup("mconsole", mconsole_setup);
909
910__uml_help(mconsole_setup,
911"mconsole=notify:<socket>\n"
912" Requests that the mconsole driver send a message to the named Unix\n"
913" socket containing the name of the mconsole socket. This also serves\n"
914" to notify outside processes when UML has booted far enough to respond\n"
915" to mconsole requests.\n\n"
916);
917
918static int notify_panic(struct notifier_block *self, unsigned long unused1,
919 void *ptr)
920{
921 char *message = ptr;
922
923 if(notify_socket == NULL) return(0);
924
Jeff Diked50084a2006-01-06 00:18:50 -0800925 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 strlen(message) + 1);
927 return(0);
928}
929
930static struct notifier_block panic_exit_notifier = {
931 .notifier_call = notify_panic,
932 .next = NULL,
933 .priority = 1
934};
935
936static int add_notifier(void)
937{
Alan Sterne041c682006-03-27 01:16:30 -0800938 atomic_notifier_chain_register(&panic_notifier_list,
939 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 return(0);
941}
942
943__initcall(add_notifier);
944
945char *mconsole_notify_socket(void)
946{
947 return(notify_socket);
948}
949
950EXPORT_SYMBOL(mconsole_notify_socket);