blob: 178b2eff4a8ce836b5286aae5e32429480a2cf66 [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include "irq_kern.h"
Jeff Dike3eddddc2005-09-16 19:27:46 -070037#include "choose-mode.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Jeff Diked50084a2006-01-06 00:18:50 -080039static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 unsigned long what, void *data)
41{
42 return(mconsole_unlink_socket());
43}
44
45
46static struct notifier_block reboot_notifier = {
47 .notifier_call = do_unlink_socket,
48 .priority = 0,
49};
50
Jeff Diked50084a2006-01-06 00:18:50 -080051/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 * locking, and the interrupt handler is safe because it can't interrupt
53 * itself and it can only happen on CPU 0.
54 */
55
Jeff Dike90107722006-01-06 00:18:54 -080056static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
David Howells6d5aefb2006-12-05 19:36:26 +000058static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059{
60 struct mconsole_entry *req;
61 unsigned long flags;
62
63 while(!list_empty(&mc_requests)){
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070064 local_irq_save(flags);
Jeff Diked50084a2006-01-06 00:18:50 -080065 req = list_entry(mc_requests.next, struct mconsole_entry,
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 list);
67 list_del(&req->list);
68 local_irq_restore(flags);
69 req->request.cmd->handler(&req->request);
70 kfree(req);
71 }
72}
73
David Howells6d5aefb2006-12-05 19:36:26 +000074static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
Al Viro7bea96f2006-10-08 22:49:34 +010076static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 /* long to avoid size mismatch warnings from gcc */
79 long fd;
80 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010081 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83 fd = (long) dev_id;
84 while (mconsole_get_request(fd, &req)){
85 if(req.cmd->context == MCONSOLE_INTR)
86 (*req.cmd->handler)(&req);
87 else {
Jeff Dike60baa152006-04-10 22:53:28 -070088 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 if(new == NULL)
90 mconsole_reply(&req, "Out of memory", 1, 0);
91 else {
92 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010093 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 list_add(&new->list, &mc_requests);
95 }
96 }
97 }
98 if(!list_empty(&mc_requests))
99 schedule_work(&mconsole_work);
100 reactivate_fd(fd, MCONSOLE_IRQ);
101 return(IRQ_HANDLED);
102}
103
104void mconsole_version(struct mc_request *req)
105{
106 char version[256];
107
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700108 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
109 utsname()->nodename, utsname()->release,
110 utsname()->version, utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 mconsole_reply(req, version, 0, 0);
112}
113
114void mconsole_log(struct mc_request *req)
115{
116 int len;
117 char *ptr = req->request.data;
118
119 ptr += strlen("log ");
120
121 len = req->len - (ptr - req->request.data);
122 printk("%.*s", len, ptr);
123 mconsole_reply(req, "", 0, 0);
124}
125
126/* This is a more convoluted version of mconsole_proc, which has some stability
127 * problems; however, we need it fixed, because it is expected that UML users
128 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
129 * show the real procfs content, not the ones from hppfs.*/
130#if 0
131void mconsole_proc(struct mc_request *req)
132{
133 struct nameidata nd;
134 struct file_system_type *proc;
135 struct super_block *super;
136 struct file *file;
137 int n, err;
138 char *ptr = req->request.data, *buf;
139
140 ptr += strlen("proc");
141 while(isspace(*ptr)) ptr++;
142
143 proc = get_fs_type("proc");
144 if(proc == NULL){
145 mconsole_reply(req, "procfs not registered", 1, 0);
146 goto out;
147 }
148
149 super = (*proc->get_sb)(proc, 0, NULL, NULL);
150 put_filesystem(proc);
151 if(super == NULL){
152 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
153 goto out;
154 }
155 up_write(&super->s_umount);
156
157 nd.dentry = super->s_root;
158 nd.mnt = NULL;
159 nd.flags = O_RDONLY + 1;
160 nd.last_type = LAST_ROOT;
161
162 /* START: it was experienced that the stability problems are closed
163 * if commenting out these two calls + the below read cycle. To
164 * make UML crash again, it was enough to readd either one.*/
165 err = link_path_walk(ptr, &nd);
166 if(err){
167 mconsole_reply(req, "Failed to look up file", 1, 0);
168 goto out_kill;
169 }
170
171 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
172 if(IS_ERR(file)){
173 mconsole_reply(req, "Failed to open file", 1, 0);
174 goto out_kill;
175 }
176 /*END*/
177
178 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
179 if(buf == NULL){
180 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
181 goto out_fput;
182 }
183
184 if((file->f_op != NULL) && (file->f_op->read != NULL)){
185 do {
186 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
187 &file->f_pos);
188 if(n >= 0){
189 buf[n] = '\0';
190 mconsole_reply(req, buf, 0, (n > 0));
191 }
192 else {
193 mconsole_reply(req, "Read of file failed",
194 1, 0);
195 goto out_free;
196 }
197 } while(n > 0);
198 }
199 else mconsole_reply(req, "", 0, 0);
200
201 out_free:
202 kfree(buf);
203 out_fput:
204 fput(file);
205 out_kill:
206 deactivate_super(super);
207 out: ;
208}
209#endif
210
211void mconsole_proc(struct mc_request *req)
212{
213 char path[64];
214 char *buf;
215 int len;
216 int fd;
217 int first_chunk = 1;
218 char *ptr = req->request.data;
219
220 ptr += strlen("proc");
221 while(isspace(*ptr)) ptr++;
222 snprintf(path, sizeof(path), "/proc/%s", ptr);
223
224 fd = sys_open(path, 0, 0);
225 if (fd < 0) {
226 mconsole_reply(req, "Failed to open file", 1, 0);
227 printk("open %s: %d\n",path,fd);
228 goto out;
229 }
230
231 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
232 if(buf == NULL){
233 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
234 goto out_close;
235 }
236
237 for (;;) {
238 len = sys_read(fd, buf, PAGE_SIZE-1);
239 if (len < 0) {
240 mconsole_reply(req, "Read of file failed", 1, 0);
241 goto out_free;
242 }
243 /*Begin the file content on his own line.*/
244 if (first_chunk) {
245 mconsole_reply(req, "\n", 0, 1);
246 first_chunk = 0;
247 }
248 if (len == PAGE_SIZE-1) {
249 buf[len] = '\0';
250 mconsole_reply(req, buf, 0, 1);
251 } else {
252 buf[len] = '\0';
253 mconsole_reply(req, buf, 0, 0);
254 break;
255 }
256 }
257
258 out_free:
259 kfree(buf);
260 out_close:
261 sys_close(fd);
262 out:
263 /* nothing */;
264}
265
266#define UML_MCONSOLE_HELPTEXT \
267"Commands: \n\
268 version - Get kernel version \n\
269 help - Print this message \n\
270 halt - Halt UML \n\
271 reboot - Reboot UML \n\
272 config <dev>=<config> - Add a new device to UML; \n\
273 same syntax as command line \n\
274 config <dev> - Query the configuration of a device \n\
275 remove <dev> - Remove a device from UML \n\
276 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800277 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 stop - pause the UML; it will do nothing until it receives a 'go' \n\
279 go - continue the UML after a 'stop' \n\
280 log <string> - make UML enter <string> into the kernel log\n\
281 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700282 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283"
284
285void mconsole_help(struct mc_request *req)
286{
287 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
288}
289
290void mconsole_halt(struct mc_request *req)
291{
292 mconsole_reply(req, "", 0, 0);
293 machine_halt();
294}
295
296void mconsole_reboot(struct mc_request *req)
297{
298 mconsole_reply(req, "", 0, 0);
299 machine_restart(NULL);
300}
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302void 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);
Al Viro3a512372006-10-24 11:15:29 +0100317 mconsole_reply(req, "stopped", 0, 0);
318 while (mconsole_get_request(req->originating_fd, req)) {
319 if (req->cmd->handler == mconsole_go)
320 break;
321 if (req->cmd->handler == mconsole_stop) {
322 mconsole_reply(req, "Already stopped", 1, 0);
323 continue;
324 }
325 if (req->cmd->handler == mconsole_sysrq) {
326 struct pt_regs *old_regs;
327 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
328 mconsole_sysrq(req);
329 set_irq_regs(old_regs);
330 continue;
331 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 (*req->cmd->handler)(req);
333 }
334 os_set_fd_block(req->originating_fd, 0);
335 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
336 mconsole_reply(req, "", 0, 0);
337}
338
Jeff Dike84f48d42007-02-10 01:44:01 -0800339static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800340static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
342void mconsole_register_dev(struct mc_device *new)
343{
Jeff Dike84f48d42007-02-10 01:44:01 -0800344 spin_lock(&mc_devices_lock);
345 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800347 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348}
349
350static struct mc_device *mconsole_find_dev(char *name)
351{
352 struct list_head *ele;
353 struct mc_device *dev;
354
355 list_for_each(ele, &mconsole_devices){
356 dev = list_entry(ele, struct mc_device, list);
357 if(!strncmp(name, dev->name, strlen(dev->name)))
358 return(dev);
359 }
360 return(NULL);
361}
362
Jeff Dike02dea082006-03-31 02:30:08 -0800363#define UNPLUGGED_PER_PAGE \
364 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
365
366struct unplugged_pages {
367 struct list_head list;
368 void *pages[UNPLUGGED_PER_PAGE];
369};
370
Jeff Dike84f48d42007-02-10 01:44:01 -0800371static DECLARE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800372static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800373static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800374static int unplug_index = UNPLUGGED_PER_PAGE;
375
Jeff Dikef28169d2007-02-10 01:43:53 -0800376static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800377{
378 unsigned long long diff;
379 int err = -EINVAL, i, add;
380 char *ret;
381
Jeff Dikef28169d2007-02-10 01:43:53 -0800382 if(str[0] != '='){
383 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800384 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800385 }
Jeff Dike02dea082006-03-31 02:30:08 -0800386
387 str++;
388 if(str[0] == '-')
389 add = 0;
390 else if(str[0] == '+'){
391 add = 1;
392 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800393 else {
394 *error_out = "Expected increment to start with '-' or '+'";
395 goto out;
396 }
Jeff Dike02dea082006-03-31 02:30:08 -0800397
398 str++;
399 diff = memparse(str, &ret);
Jeff Dikef28169d2007-02-10 01:43:53 -0800400 if(*ret != '\0'){
401 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800402 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800403 }
Jeff Dike02dea082006-03-31 02:30:08 -0800404
405 diff /= PAGE_SIZE;
406
Jeff Dike84f48d42007-02-10 01:44:01 -0800407 down(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800408 for(i = 0; i < diff; i++){
409 struct unplugged_pages *unplugged;
410 void *addr;
411
412 if(add){
413 if(list_empty(&unplugged_pages))
414 break;
415
416 unplugged = list_entry(unplugged_pages.next,
417 struct unplugged_pages, list);
418 if(unplug_index > 0)
419 addr = unplugged->pages[--unplug_index];
420 else {
421 list_del(&unplugged->list);
422 addr = unplugged;
423 unplug_index = UNPLUGGED_PER_PAGE;
424 }
425
426 free_page((unsigned long) addr);
427 unplugged_pages_count--;
428 }
429 else {
430 struct page *page;
431
432 page = alloc_page(GFP_ATOMIC);
433 if(page == NULL)
434 break;
435
436 unplugged = page_address(page);
437 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800438 list_add(&unplugged->list, &unplugged_pages);
439 unplug_index = 0;
440 }
441 else {
442 struct list_head *entry = unplugged_pages.next;
443 addr = unplugged;
444
445 unplugged = list_entry(entry,
446 struct unplugged_pages,
447 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800448 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikef28169d2007-02-10 01:43:53 -0800449 if(err){
Jeff Dike02dea082006-03-31 02:30:08 -0800450 printk("Failed to release memory - "
451 "errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800452 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800453 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800454 }
455 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800456 }
457
458 unplugged_pages_count++;
459 }
460 }
461
462 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800463out_unlock:
464 up(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800465out:
466 return err;
467}
468
469static int mem_get_config(char *name, char *str, int size, char **error_out)
470{
471 char buf[sizeof("18446744073709551615")];
472 int len = 0;
473
474 sprintf(buf, "%ld", uml_physmem);
475 CONFIG_CHUNK(str, size, len, buf, 1);
476
477 return len;
478}
479
480static int mem_id(char **str, int *start_out, int *end_out)
481{
482 *start_out = 0;
483 *end_out = 0;
484
485 return 0;
486}
487
Jeff Dikef28169d2007-02-10 01:43:53 -0800488static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800489{
Jeff Dikef28169d2007-02-10 01:43:53 -0800490 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800491 return -EBUSY;
492}
493
494static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800495 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800496 .name = "mem",
497 .config = mem_config,
498 .get_config = mem_get_config,
499 .id = mem_id,
500 .remove = mem_remove,
501};
502
503static int mem_mc_init(void)
504{
505 if(can_drop_memory())
506 mconsole_register_dev(&mem_mc);
507 else printk("Can't release memory to the host - memory hotplug won't "
508 "be supported\n");
509 return 0;
510}
511
512__initcall(mem_mc_init);
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514#define CONFIG_BUF_SIZE 64
515
Jeff Diked50084a2006-01-06 00:18:50 -0800516static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 char **),
518 struct mc_request *req, char *name)
519{
520 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
521 int n, size;
522
523 if(get_config == NULL){
524 mconsole_reply(req, "No get_config routine defined", 1, 0);
525 return;
526 }
527
528 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700529 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 buf = default_buf;
531
532 while(1){
533 n = (*get_config)(name, buf, size, &error);
534 if(error != NULL){
535 mconsole_reply(req, error, 1, 0);
536 goto out;
537 }
538
539 if(n <= size){
540 mconsole_reply(req, buf, 0, 0);
541 goto out;
542 }
543
544 if(buf != default_buf)
545 kfree(buf);
546
547 size = n;
548 buf = kmalloc(size, GFP_KERNEL);
549 if(buf == NULL){
550 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
551 return;
552 }
553 }
554 out:
555 if(buf != default_buf)
556 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557}
558
559void mconsole_config(struct mc_request *req)
560{
561 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800562 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 int err;
564
565 ptr += strlen("config");
566 while(isspace(*ptr)) ptr++;
567 dev = mconsole_find_dev(ptr);
568 if(dev == NULL){
569 mconsole_reply(req, "Bad configuration option", 1, 0);
570 return;
571 }
572
573 name = &ptr[strlen(dev->name)];
574 ptr = name;
575 while((*ptr != '=') && (*ptr != '\0'))
576 ptr++;
577
578 if(*ptr == '='){
Jeff Dikef28169d2007-02-10 01:43:53 -0800579 err = (*dev->config)(name, &error_string);
580 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
582 else mconsole_get_config(dev->get_config, req, name);
583}
584
585void mconsole_remove(struct mc_request *req)
586{
Jeff Diked50084a2006-01-06 00:18:50 -0800587 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700588 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800589 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700590 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
592 ptr += strlen("remove");
593 while(isspace(*ptr)) ptr++;
594 dev = mconsole_find_dev(ptr);
595 if(dev == NULL){
596 mconsole_reply(req, "Bad remove option", 1, 0);
597 return;
598 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700599
Jeff Dike3a331a52006-01-06 00:19:05 -0800600 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700601
Jeff Dike3a331a52006-01-06 00:19:05 -0800602 err = 1;
603 n = (*dev->id)(&ptr, &start, &end);
604 if(n < 0){
605 err_msg = "Couldn't parse device number";
606 goto out;
607 }
608 else if((n < start) || (n > end)){
609 sprintf(error, "Invalid device number - must be between "
610 "%d and %d", start, end);
611 err_msg = error;
612 goto out;
613 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700614
Jeff Dikef28169d2007-02-10 01:43:53 -0800615 err_msg = NULL;
616 err = (*dev->remove)(n, &err_msg);
Jeff Dike3a331a52006-01-06 00:19:05 -0800617 switch(err){
618 case -ENODEV:
Jeff Dikef28169d2007-02-10 01:43:53 -0800619 if(err_msg == NULL)
620 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800621 break;
622 case -EBUSY:
Jeff Dikef28169d2007-02-10 01:43:53 -0800623 if(err_msg == NULL)
624 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800625 break;
626 default:
627 break;
628 }
629out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700630 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
Jeff Dikef92afe52006-09-29 01:58:52 -0700633struct mconsole_output {
634 struct list_head list;
635 struct mc_request *req;
636};
637
Jeff Dike84f48d42007-02-10 01:44:01 -0800638static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800639static LIST_HEAD(clients);
640static char console_buf[MCONSOLE_MAX_DATA];
641static int console_index = 0;
642
643static void console_write(struct console *console, const char *string,
644 unsigned len)
645{
646 struct list_head *ele;
647 int n;
648
649 if(list_empty(&clients))
650 return;
651
652 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700653 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800654 strncpy(&console_buf[console_index], string, n);
655 console_index += n;
656 string += n;
657 len -= n;
658 if(len == 0)
659 return;
660
661 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700662 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800663
Jeff Dikef92afe52006-09-29 01:58:52 -0700664 entry = list_entry(ele, struct mconsole_output, list);
665 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800666 console_index, 0, 1);
667 }
668
669 console_index = 0;
670 }
671}
672
673static struct console mc_console = { .name = "mc",
674 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800675 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800676 .index = -1 };
677
678static int mc_add_console(void)
679{
680 register_console(&mc_console);
681 return 0;
682}
683
684late_initcall(mc_add_console);
685
686static void with_console(struct mc_request *req, void (*proc)(void *),
687 void *arg)
688{
Jeff Dikef92afe52006-09-29 01:58:52 -0700689 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800690 unsigned long flags;
691
Jeff Dikef92afe52006-09-29 01:58:52 -0700692 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800693 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800694 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800695 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800696
697 (*proc)(arg);
698
699 mconsole_reply_len(req, console_buf, console_index, 0, 0);
700 console_index = 0;
701
Jeff Dike84f48d42007-02-10 01:44:01 -0800702 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800703 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800704 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800705}
706
Jeff Dike4111b022006-01-06 00:19:05 -0800707#ifdef CONFIG_MAGIC_SYSRQ
708static void sysrq_proc(void *arg)
709{
710 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100711 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800712}
713
714void mconsole_sysrq(struct mc_request *req)
715{
716 char *ptr = req->request.data;
717
718 ptr += strlen("sysrq");
719 while(isspace(*ptr)) ptr++;
720
721 /* With 'b', the system will shut down without a chance to reply,
722 * so in this case, we reply first.
723 */
724 if(*ptr == 'b')
725 mconsole_reply(req, "", 0, 0);
726
727 with_console(req, sysrq_proc, ptr);
728}
729#else
730void mconsole_sysrq(struct mc_request *req)
731{
732 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
733}
734#endif
735
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800736#ifdef CONFIG_MODE_SKAS
737
Jeff Dike6f517d32006-01-06 00:19:04 -0800738static void stack_proc(void *arg)
739{
740 struct task_struct *from = current, *to = arg;
741
742 to->thread.saved_task = from;
743 switch_to(from, to, from);
744}
745
Jeff Dike3eddddc2005-09-16 19:27:46 -0700746/* Mconsole stack trace
747 * Added by Allan Graves, Jeff Dike
748 * Dumps a stacks registers to the linux console.
749 * Usage stack <pid>.
750 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800751static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700752{
Jeff Dike3a331a52006-01-06 00:19:05 -0800753 char *ptr = req->request.data;
754 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800755 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700756 struct task_struct *to = NULL;
757
Jeff Dike3a331a52006-01-06 00:19:05 -0800758 /* Would be nice:
759 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700760 * 2) Add a way to stack dump all pids.
761 */
762
Jeff Dike3a331a52006-01-06 00:19:05 -0800763 ptr += strlen("stack");
764 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700765
Jeff Dike3a331a52006-01-06 00:19:05 -0800766 /* Should really check for multiple pids or reject bad args here */
767 /* What do the arguments in mconsole_reply mean? */
768 if(sscanf(ptr, "%d", &pid_requested) == 0){
769 mconsole_reply(req, "Please specify a pid", 1, 0);
770 return;
771 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700772
Jeff Dike6f517d32006-01-06 00:19:04 -0800773 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700774
Jeff Dike6f517d32006-01-06 00:19:04 -0800775 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800776 if((to == NULL) || (pid_requested == 0)) {
777 mconsole_reply(req, "Couldn't find that pid", 1, 0);
778 return;
779 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800780 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700781}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800782#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700783
784void mconsole_stack(struct mc_request *req)
785{
786 /* This command doesn't work in TT mode, so let's check and then
787 * get out of here
788 */
789 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
790 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800791 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700792}
793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794/* Changed by mconsole_setup, which is __setup, and called before SMP is
795 * active.
796 */
Jeff Diked50084a2006-01-06 00:18:50 -0800797static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Jeff Dike90107722006-01-06 00:18:54 -0800799static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800{
801 /* long to avoid size mismatch warnings from gcc */
802 long sock;
803 int err;
804 char file[256];
805
806 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
807 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
808
809 sock = os_create_unix_socket(file, sizeof(file), 1);
810 if (sock < 0){
811 printk("Failed to initialize management console\n");
812 return(1);
813 }
814
815 register_reboot_notifier(&reboot_notifier);
816
817 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700818 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 "mconsole", (void *)sock);
820 if (err){
821 printk("Failed to get IRQ for management console\n");
822 return(1);
823 }
824
825 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800826 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 if(notify_socket != NULL)
828 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800829 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 strlen(mconsole_socket_name) + 1);
831 else printk(KERN_ERR "mconsole_setup failed to strdup "
832 "string\n");
833 }
834
Jeff Diked50084a2006-01-06 00:18:50 -0800835 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 MCONSOLE_VERSION, mconsole_socket_name);
837 return(0);
838}
839
840__initcall(mconsole_init);
841
842static int write_proc_mconsole(struct file *file, const char __user *buffer,
843 unsigned long count, void *data)
844{
845 char *buf;
846
847 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800848 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 return(-ENOMEM);
850
851 if(copy_from_user(buf, buffer, count)){
852 count = -EFAULT;
853 goto out;
854 }
855
856 buf[count] = '\0';
857
858 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
859 out:
860 kfree(buf);
861 return(count);
862}
863
864static int create_proc_mconsole(void)
865{
866 struct proc_dir_entry *ent;
867
868 if(notify_socket == NULL) return(0);
869
870 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
871 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700872 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 return(0);
874 }
875
876 ent->read_proc = NULL;
877 ent->write_proc = write_proc_mconsole;
878 return(0);
879}
880
881static DEFINE_SPINLOCK(notify_spinlock);
882
883void lock_notify(void)
884{
885 spin_lock(&notify_spinlock);
886}
887
888void unlock_notify(void)
889{
890 spin_unlock(&notify_spinlock);
891}
892
893__initcall(create_proc_mconsole);
894
895#define NOTIFY "=notify:"
896
897static int mconsole_setup(char *str)
898{
899 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
900 str += strlen(NOTIFY);
901 notify_socket = str;
902 }
903 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
904 return(1);
905}
906
907__setup("mconsole", mconsole_setup);
908
909__uml_help(mconsole_setup,
910"mconsole=notify:<socket>\n"
911" Requests that the mconsole driver send a message to the named Unix\n"
912" socket containing the name of the mconsole socket. This also serves\n"
913" to notify outside processes when UML has booted far enough to respond\n"
914" to mconsole requests.\n\n"
915);
916
917static int notify_panic(struct notifier_block *self, unsigned long unused1,
918 void *ptr)
919{
920 char *message = ptr;
921
922 if(notify_socket == NULL) return(0);
923
Jeff Diked50084a2006-01-06 00:18:50 -0800924 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 strlen(message) + 1);
926 return(0);
927}
928
929static struct notifier_block panic_exit_notifier = {
930 .notifier_call = notify_panic,
931 .next = NULL,
932 .priority = 1
933};
934
935static int add_notifier(void)
936{
Alan Sterne041c682006-03-27 01:16:30 -0800937 atomic_notifier_chain_register(&panic_notifier_list,
938 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 return(0);
940}
941
942__initcall(add_notifier);
943
944char *mconsole_notify_socket(void)
945{
946 return(notify_socket);
947}
948
949EXPORT_SYMBOL(mconsole_notify_socket);