blob: 3591c3806670ea196bba06e543f12fb4ef65c9ea [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include "kern_util.h"
29#include "kern.h"
30#include "mconsole.h"
31#include "mconsole_kern.h"
32#include "irq_user.h"
33#include "init.h"
34#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include "irq_kern.h"
36
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
Jeff Dike90107722006-01-06 00:18:54 -080054static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
David Howells6d5aefb2006-12-05 19:36:26 +000056static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070057{
58 struct mconsole_entry *req;
59 unsigned long flags;
60
61 while(!list_empty(&mc_requests)){
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070062 local_irq_save(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
David Howells6d5aefb2006-12-05 19:36:26 +000072static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Al Viro7bea96f2006-10-08 22:49:34 +010074static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
76 /* long to avoid size mismatch warnings from gcc */
77 long fd;
78 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010079 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81 fd = (long) dev_id;
82 while (mconsole_get_request(fd, &req)){
83 if(req.cmd->context == MCONSOLE_INTR)
84 (*req.cmd->handler)(&req);
85 else {
Jeff Dike60baa152006-04-10 22:53:28 -070086 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 if(new == NULL)
88 mconsole_reply(&req, "Out of memory", 1, 0);
89 else {
90 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010091 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 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
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700106 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
107 utsname()->nodename, utsname()->release,
108 utsname()->version, utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 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\
Jeff Dikedb805812006-02-01 03:06:23 -0800275 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300void mconsole_cad(struct mc_request *req)
301{
302 mconsole_reply(req, "", 0, 0);
303 ctrl_alt_del();
304}
305
306void mconsole_go(struct mc_request *req)
307{
308 mconsole_reply(req, "Not stopped", 1, 0);
309}
310
311void mconsole_stop(struct mc_request *req)
312{
313 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
314 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100315 mconsole_reply(req, "stopped", 0, 0);
316 while (mconsole_get_request(req->originating_fd, req)) {
317 if (req->cmd->handler == mconsole_go)
318 break;
319 if (req->cmd->handler == mconsole_stop) {
320 mconsole_reply(req, "Already stopped", 1, 0);
321 continue;
322 }
323 if (req->cmd->handler == mconsole_sysrq) {
324 struct pt_regs *old_regs;
325 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
326 mconsole_sysrq(req);
327 set_irq_regs(old_regs);
328 continue;
329 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 (*req->cmd->handler)(req);
331 }
332 os_set_fd_block(req->originating_fd, 0);
333 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
334 mconsole_reply(req, "", 0, 0);
335}
336
Jeff Dike84f48d42007-02-10 01:44:01 -0800337static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800338static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340void mconsole_register_dev(struct mc_device *new)
341{
Jeff Dike84f48d42007-02-10 01:44:01 -0800342 spin_lock(&mc_devices_lock);
343 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800345 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346}
347
348static struct mc_device *mconsole_find_dev(char *name)
349{
350 struct list_head *ele;
351 struct mc_device *dev;
352
353 list_for_each(ele, &mconsole_devices){
354 dev = list_entry(ele, struct mc_device, list);
355 if(!strncmp(name, dev->name, strlen(dev->name)))
356 return(dev);
357 }
358 return(NULL);
359}
360
Jeff Dike02dea082006-03-31 02:30:08 -0800361#define UNPLUGGED_PER_PAGE \
362 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
363
364struct unplugged_pages {
365 struct list_head list;
366 void *pages[UNPLUGGED_PER_PAGE];
367};
368
Jeff Dike84f48d42007-02-10 01:44:01 -0800369static DECLARE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800370static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800371static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800372static 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
Jeff Dike84f48d42007-02-10 01:44:01 -0800405 down(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800406 for(i = 0; i < diff; i++){
407 struct unplugged_pages *unplugged;
408 void *addr;
409
410 if(add){
411 if(list_empty(&unplugged_pages))
412 break;
413
414 unplugged = list_entry(unplugged_pages.next,
415 struct unplugged_pages, list);
416 if(unplug_index > 0)
417 addr = unplugged->pages[--unplug_index];
418 else {
419 list_del(&unplugged->list);
420 addr = unplugged;
421 unplug_index = UNPLUGGED_PER_PAGE;
422 }
423
424 free_page((unsigned long) addr);
425 unplugged_pages_count--;
426 }
427 else {
428 struct page *page;
429
430 page = alloc_page(GFP_ATOMIC);
431 if(page == NULL)
432 break;
433
434 unplugged = page_address(page);
435 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800436 list_add(&unplugged->list, &unplugged_pages);
437 unplug_index = 0;
438 }
439 else {
440 struct list_head *entry = unplugged_pages.next;
441 addr = unplugged;
442
443 unplugged = list_entry(entry,
444 struct unplugged_pages,
445 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800446 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikef28169d2007-02-10 01:43:53 -0800447 if(err){
Jeff Dike02dea082006-03-31 02:30:08 -0800448 printk("Failed to release memory - "
449 "errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800450 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800451 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800452 }
453 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800454 }
455
456 unplugged_pages_count++;
457 }
458 }
459
460 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800461out_unlock:
462 up(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800463out:
464 return err;
465}
466
467static int mem_get_config(char *name, char *str, int size, char **error_out)
468{
469 char buf[sizeof("18446744073709551615")];
470 int len = 0;
471
472 sprintf(buf, "%ld", uml_physmem);
473 CONFIG_CHUNK(str, size, len, buf, 1);
474
475 return len;
476}
477
478static int mem_id(char **str, int *start_out, int *end_out)
479{
480 *start_out = 0;
481 *end_out = 0;
482
483 return 0;
484}
485
Jeff Dikef28169d2007-02-10 01:43:53 -0800486static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800487{
Jeff Dikef28169d2007-02-10 01:43:53 -0800488 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800489 return -EBUSY;
490}
491
492static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800493 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800494 .name = "mem",
495 .config = mem_config,
496 .get_config = mem_get_config,
497 .id = mem_id,
498 .remove = mem_remove,
499};
500
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700501static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800502{
503 if(can_drop_memory())
504 mconsole_register_dev(&mem_mc);
505 else printk("Can't release memory to the host - memory hotplug won't "
506 "be supported\n");
507 return 0;
508}
509
510__initcall(mem_mc_init);
511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512#define CONFIG_BUF_SIZE 64
513
Jeff Diked50084a2006-01-06 00:18:50 -0800514static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 char **),
516 struct mc_request *req, char *name)
517{
518 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
519 int n, size;
520
521 if(get_config == NULL){
522 mconsole_reply(req, "No get_config routine defined", 1, 0);
523 return;
524 }
525
526 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700527 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 buf = default_buf;
529
530 while(1){
531 n = (*get_config)(name, buf, size, &error);
532 if(error != NULL){
533 mconsole_reply(req, error, 1, 0);
534 goto out;
535 }
536
537 if(n <= size){
538 mconsole_reply(req, buf, 0, 0);
539 goto out;
540 }
541
542 if(buf != default_buf)
543 kfree(buf);
544
545 size = n;
546 buf = kmalloc(size, GFP_KERNEL);
547 if(buf == NULL){
548 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
549 return;
550 }
551 }
552 out:
553 if(buf != default_buf)
554 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555}
556
557void mconsole_config(struct mc_request *req)
558{
559 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800560 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 int err;
562
563 ptr += strlen("config");
564 while(isspace(*ptr)) ptr++;
565 dev = mconsole_find_dev(ptr);
566 if(dev == NULL){
567 mconsole_reply(req, "Bad configuration option", 1, 0);
568 return;
569 }
570
571 name = &ptr[strlen(dev->name)];
572 ptr = name;
573 while((*ptr != '=') && (*ptr != '\0'))
574 ptr++;
575
576 if(*ptr == '='){
Jeff Dikef28169d2007-02-10 01:43:53 -0800577 err = (*dev->config)(name, &error_string);
578 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 }
580 else mconsole_get_config(dev->get_config, req, name);
581}
582
583void mconsole_remove(struct mc_request *req)
584{
Jeff Diked50084a2006-01-06 00:18:50 -0800585 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700586 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800587 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700588 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 ptr += strlen("remove");
591 while(isspace(*ptr)) ptr++;
592 dev = mconsole_find_dev(ptr);
593 if(dev == NULL){
594 mconsole_reply(req, "Bad remove option", 1, 0);
595 return;
596 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700597
Jeff Dike3a331a52006-01-06 00:19:05 -0800598 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700599
Jeff Dike3a331a52006-01-06 00:19:05 -0800600 err = 1;
601 n = (*dev->id)(&ptr, &start, &end);
602 if(n < 0){
603 err_msg = "Couldn't parse device number";
604 goto out;
605 }
606 else if((n < start) || (n > end)){
607 sprintf(error, "Invalid device number - must be between "
608 "%d and %d", start, end);
609 err_msg = error;
610 goto out;
611 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700612
Jeff Dikef28169d2007-02-10 01:43:53 -0800613 err_msg = NULL;
614 err = (*dev->remove)(n, &err_msg);
Jeff Dike3a331a52006-01-06 00:19:05 -0800615 switch(err){
Jeff Diked40f6d72007-03-29 01:20:28 -0700616 case 0:
617 err_msg = "";
618 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800619 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
Jeff Dike6f517d32006-01-06 00:19:04 -0800737static void stack_proc(void *arg)
738{
739 struct task_struct *from = current, *to = arg;
740
741 to->thread.saved_task = from;
742 switch_to(from, to, from);
743}
744
Jeff Dike3eddddc2005-09-16 19:27:46 -0700745/* Mconsole stack trace
746 * Added by Allan Graves, Jeff Dike
747 * Dumps a stacks registers to the linux console.
748 * Usage stack <pid>.
749 */
Jeff Dike42fda662007-10-16 01:26:50 -0700750void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700751{
Jeff Dike3a331a52006-01-06 00:19:05 -0800752 char *ptr = req->request.data;
753 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800754 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700755 struct task_struct *to = NULL;
756
Jeff Dike3a331a52006-01-06 00:19:05 -0800757 /* Would be nice:
758 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700759 * 2) Add a way to stack dump all pids.
760 */
761
Jeff Dike3a331a52006-01-06 00:19:05 -0800762 ptr += strlen("stack");
763 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700764
Jeff Dike3a331a52006-01-06 00:19:05 -0800765 /* Should really check for multiple pids or reject bad args here */
766 /* What do the arguments in mconsole_reply mean? */
767 if(sscanf(ptr, "%d", &pid_requested) == 0){
768 mconsole_reply(req, "Please specify a pid", 1, 0);
769 return;
770 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700771
Jeff Dike6f517d32006-01-06 00:19:04 -0800772 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700773
Jeff Dike6f517d32006-01-06 00:19:04 -0800774 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800775 if((to == NULL) || (pid_requested == 0)) {
776 mconsole_reply(req, "Couldn't find that pid", 1, 0);
777 return;
778 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800779 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700780}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700781
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782/* Changed by mconsole_setup, which is __setup, and called before SMP is
783 * active.
784 */
Jeff Diked50084a2006-01-06 00:18:50 -0800785static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700787static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
789 /* long to avoid size mismatch warnings from gcc */
790 long sock;
791 int err;
792 char file[256];
793
794 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
795 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
796
797 sock = os_create_unix_socket(file, sizeof(file), 1);
798 if (sock < 0){
799 printk("Failed to initialize management console\n");
800 return(1);
801 }
802
803 register_reboot_notifier(&reboot_notifier);
804
805 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700806 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 "mconsole", (void *)sock);
808 if (err){
809 printk("Failed to get IRQ for management console\n");
810 return(1);
811 }
812
813 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800814 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 if(notify_socket != NULL)
816 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800817 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 strlen(mconsole_socket_name) + 1);
819 else printk(KERN_ERR "mconsole_setup failed to strdup "
820 "string\n");
821 }
822
Jeff Diked50084a2006-01-06 00:18:50 -0800823 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 MCONSOLE_VERSION, mconsole_socket_name);
825 return(0);
826}
827
828__initcall(mconsole_init);
829
830static int write_proc_mconsole(struct file *file, const char __user *buffer,
831 unsigned long count, void *data)
832{
833 char *buf;
834
835 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800836 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 return(-ENOMEM);
838
839 if(copy_from_user(buf, buffer, count)){
840 count = -EFAULT;
841 goto out;
842 }
843
844 buf[count] = '\0';
845
846 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
847 out:
848 kfree(buf);
849 return(count);
850}
851
852static int create_proc_mconsole(void)
853{
854 struct proc_dir_entry *ent;
855
856 if(notify_socket == NULL) return(0);
857
858 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
859 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700860 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 return(0);
862 }
863
864 ent->read_proc = NULL;
865 ent->write_proc = write_proc_mconsole;
866 return(0);
867}
868
869static DEFINE_SPINLOCK(notify_spinlock);
870
871void lock_notify(void)
872{
873 spin_lock(&notify_spinlock);
874}
875
876void unlock_notify(void)
877{
878 spin_unlock(&notify_spinlock);
879}
880
881__initcall(create_proc_mconsole);
882
883#define NOTIFY "=notify:"
884
885static int mconsole_setup(char *str)
886{
887 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
888 str += strlen(NOTIFY);
889 notify_socket = str;
890 }
891 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
892 return(1);
893}
894
895__setup("mconsole", mconsole_setup);
896
897__uml_help(mconsole_setup,
898"mconsole=notify:<socket>\n"
899" Requests that the mconsole driver send a message to the named Unix\n"
900" socket containing the name of the mconsole socket. This also serves\n"
901" to notify outside processes when UML has booted far enough to respond\n"
902" to mconsole requests.\n\n"
903);
904
905static int notify_panic(struct notifier_block *self, unsigned long unused1,
906 void *ptr)
907{
908 char *message = ptr;
909
910 if(notify_socket == NULL) return(0);
911
Jeff Diked50084a2006-01-06 00:18:50 -0800912 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 strlen(message) + 1);
914 return(0);
915}
916
917static struct notifier_block panic_exit_notifier = {
918 .notifier_call = notify_panic,
919 .next = NULL,
920 .priority = 1
921};
922
923static int add_notifier(void)
924{
Alan Sterne041c682006-03-27 01:16:30 -0800925 atomic_notifier_chain_register(&panic_notifier_list,
926 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 return(0);
928}
929
930__initcall(add_notifier);
931
932char *mconsole_notify_socket(void)
933{
934 return(notify_socket);
935}
936
937EXPORT_SYMBOL(mconsole_notify_socket);