blob: 65ad2932672cad5b4b86dc7504023f8fdcb50c98 [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){
Jeff Diked40f6d72007-03-29 01:20:28 -0700618 case 0:
619 err_msg = "";
620 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800621 case -ENODEV:
Jeff Dikef28169d2007-02-10 01:43:53 -0800622 if(err_msg == NULL)
623 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800624 break;
625 case -EBUSY:
Jeff Dikef28169d2007-02-10 01:43:53 -0800626 if(err_msg == NULL)
627 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800628 break;
629 default:
630 break;
631 }
632out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700633 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
635
Jeff Dikef92afe52006-09-29 01:58:52 -0700636struct mconsole_output {
637 struct list_head list;
638 struct mc_request *req;
639};
640
Jeff Dike84f48d42007-02-10 01:44:01 -0800641static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800642static LIST_HEAD(clients);
643static char console_buf[MCONSOLE_MAX_DATA];
644static int console_index = 0;
645
646static void console_write(struct console *console, const char *string,
647 unsigned len)
648{
649 struct list_head *ele;
650 int n;
651
652 if(list_empty(&clients))
653 return;
654
655 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700656 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800657 strncpy(&console_buf[console_index], string, n);
658 console_index += n;
659 string += n;
660 len -= n;
661 if(len == 0)
662 return;
663
664 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700665 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800666
Jeff Dikef92afe52006-09-29 01:58:52 -0700667 entry = list_entry(ele, struct mconsole_output, list);
668 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800669 console_index, 0, 1);
670 }
671
672 console_index = 0;
673 }
674}
675
676static struct console mc_console = { .name = "mc",
677 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800678 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800679 .index = -1 };
680
681static int mc_add_console(void)
682{
683 register_console(&mc_console);
684 return 0;
685}
686
687late_initcall(mc_add_console);
688
689static void with_console(struct mc_request *req, void (*proc)(void *),
690 void *arg)
691{
Jeff Dikef92afe52006-09-29 01:58:52 -0700692 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800693 unsigned long flags;
694
Jeff Dikef92afe52006-09-29 01:58:52 -0700695 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800696 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800697 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800698 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800699
700 (*proc)(arg);
701
702 mconsole_reply_len(req, console_buf, console_index, 0, 0);
703 console_index = 0;
704
Jeff Dike84f48d42007-02-10 01:44:01 -0800705 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800706 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800707 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800708}
709
Jeff Dike4111b022006-01-06 00:19:05 -0800710#ifdef CONFIG_MAGIC_SYSRQ
711static void sysrq_proc(void *arg)
712{
713 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100714 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800715}
716
717void mconsole_sysrq(struct mc_request *req)
718{
719 char *ptr = req->request.data;
720
721 ptr += strlen("sysrq");
722 while(isspace(*ptr)) ptr++;
723
724 /* With 'b', the system will shut down without a chance to reply,
725 * so in this case, we reply first.
726 */
727 if(*ptr == 'b')
728 mconsole_reply(req, "", 0, 0);
729
730 with_console(req, sysrq_proc, ptr);
731}
732#else
733void mconsole_sysrq(struct mc_request *req)
734{
735 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
736}
737#endif
738
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800739#ifdef CONFIG_MODE_SKAS
740
Jeff Dike6f517d32006-01-06 00:19:04 -0800741static void stack_proc(void *arg)
742{
743 struct task_struct *from = current, *to = arg;
744
745 to->thread.saved_task = from;
746 switch_to(from, to, from);
747}
748
Jeff Dike3eddddc2005-09-16 19:27:46 -0700749/* Mconsole stack trace
750 * Added by Allan Graves, Jeff Dike
751 * Dumps a stacks registers to the linux console.
752 * Usage stack <pid>.
753 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800754static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700755{
Jeff Dike3a331a52006-01-06 00:19:05 -0800756 char *ptr = req->request.data;
757 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800758 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700759 struct task_struct *to = NULL;
760
Jeff Dike3a331a52006-01-06 00:19:05 -0800761 /* Would be nice:
762 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700763 * 2) Add a way to stack dump all pids.
764 */
765
Jeff Dike3a331a52006-01-06 00:19:05 -0800766 ptr += strlen("stack");
767 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700768
Jeff Dike3a331a52006-01-06 00:19:05 -0800769 /* Should really check for multiple pids or reject bad args here */
770 /* What do the arguments in mconsole_reply mean? */
771 if(sscanf(ptr, "%d", &pid_requested) == 0){
772 mconsole_reply(req, "Please specify a pid", 1, 0);
773 return;
774 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700775
Jeff Dike6f517d32006-01-06 00:19:04 -0800776 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700777
Jeff Dike6f517d32006-01-06 00:19:04 -0800778 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800779 if((to == NULL) || (pid_requested == 0)) {
780 mconsole_reply(req, "Couldn't find that pid", 1, 0);
781 return;
782 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800783 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700784}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800785#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700786
787void mconsole_stack(struct mc_request *req)
788{
789 /* This command doesn't work in TT mode, so let's check and then
790 * get out of here
791 */
792 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
793 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800794 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700795}
796
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797/* Changed by mconsole_setup, which is __setup, and called before SMP is
798 * active.
799 */
Jeff Diked50084a2006-01-06 00:18:50 -0800800static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Jeff Dike90107722006-01-06 00:18:54 -0800802static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803{
804 /* long to avoid size mismatch warnings from gcc */
805 long sock;
806 int err;
807 char file[256];
808
809 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
810 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
811
812 sock = os_create_unix_socket(file, sizeof(file), 1);
813 if (sock < 0){
814 printk("Failed to initialize management console\n");
815 return(1);
816 }
817
818 register_reboot_notifier(&reboot_notifier);
819
820 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700821 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 "mconsole", (void *)sock);
823 if (err){
824 printk("Failed to get IRQ for management console\n");
825 return(1);
826 }
827
828 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800829 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 if(notify_socket != NULL)
831 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800832 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 strlen(mconsole_socket_name) + 1);
834 else printk(KERN_ERR "mconsole_setup failed to strdup "
835 "string\n");
836 }
837
Jeff Diked50084a2006-01-06 00:18:50 -0800838 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 MCONSOLE_VERSION, mconsole_socket_name);
840 return(0);
841}
842
843__initcall(mconsole_init);
844
845static int write_proc_mconsole(struct file *file, const char __user *buffer,
846 unsigned long count, void *data)
847{
848 char *buf;
849
850 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800851 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 return(-ENOMEM);
853
854 if(copy_from_user(buf, buffer, count)){
855 count = -EFAULT;
856 goto out;
857 }
858
859 buf[count] = '\0';
860
861 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
862 out:
863 kfree(buf);
864 return(count);
865}
866
867static int create_proc_mconsole(void)
868{
869 struct proc_dir_entry *ent;
870
871 if(notify_socket == NULL) return(0);
872
873 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
874 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700875 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 return(0);
877 }
878
879 ent->read_proc = NULL;
880 ent->write_proc = write_proc_mconsole;
881 return(0);
882}
883
884static DEFINE_SPINLOCK(notify_spinlock);
885
886void lock_notify(void)
887{
888 spin_lock(&notify_spinlock);
889}
890
891void unlock_notify(void)
892{
893 spin_unlock(&notify_spinlock);
894}
895
896__initcall(create_proc_mconsole);
897
898#define NOTIFY "=notify:"
899
900static int mconsole_setup(char *str)
901{
902 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
903 str += strlen(NOTIFY);
904 notify_socket = str;
905 }
906 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
907 return(1);
908}
909
910__setup("mconsole", mconsole_setup);
911
912__uml_help(mconsole_setup,
913"mconsole=notify:<socket>\n"
914" Requests that the mconsole driver send a message to the named Unix\n"
915" socket containing the name of the mconsole socket. This also serves\n"
916" to notify outside processes when UML has booted far enough to respond\n"
917" to mconsole requests.\n\n"
918);
919
920static int notify_panic(struct notifier_block *self, unsigned long unused1,
921 void *ptr)
922{
923 char *message = ptr;
924
925 if(notify_socket == NULL) return(0);
926
Jeff Diked50084a2006-01-06 00:18:50 -0800927 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 strlen(message) + 1);
929 return(0);
930}
931
932static struct notifier_block panic_exit_notifier = {
933 .notifier_call = notify_panic,
934 .next = NULL,
935 .priority = 1
936};
937
938static int add_notifier(void)
939{
Alan Sterne041c682006-03-27 01:16:30 -0800940 atomic_notifier_chain_register(&panic_notifier_list,
941 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 return(0);
943}
944
945__initcall(add_notifier);
946
947char *mconsole_notify_socket(void)
948{
949 return(notify_socket);
950}
951
952EXPORT_SYMBOL(mconsole_notify_socket);