blob: d870905074015dc95dbe59b73f21e03869d2c565 [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"
Jeff Dike3eddddc2005-09-16 19:27:46 -070036#include "choose-mode.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Jeff Diked50084a2006-01-06 00:18:50 -080038static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 unsigned long what, void *data)
40{
41 return(mconsole_unlink_socket());
42}
43
44
45static struct notifier_block reboot_notifier = {
46 .notifier_call = do_unlink_socket,
47 .priority = 0,
48};
49
Jeff Diked50084a2006-01-06 00:18:50 -080050/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * locking, and the interrupt handler is safe because it can't interrupt
52 * itself and it can only happen on CPU 0.
53 */
54
Jeff Dike90107722006-01-06 00:18:54 -080055static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
David Howells6d5aefb2006-12-05 19:36:26 +000057static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058{
59 struct mconsole_entry *req;
60 unsigned long flags;
61
62 while(!list_empty(&mc_requests)){
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070063 local_irq_save(flags);
Jeff Diked50084a2006-01-06 00:18:50 -080064 req = list_entry(mc_requests.next, struct mconsole_entry,
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 list);
66 list_del(&req->list);
67 local_irq_restore(flags);
68 req->request.cmd->handler(&req->request);
69 kfree(req);
70 }
71}
72
David Howells6d5aefb2006-12-05 19:36:26 +000073static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
Al Viro7bea96f2006-10-08 22:49:34 +010075static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076{
77 /* long to avoid size mismatch warnings from gcc */
78 long fd;
79 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010080 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
82 fd = (long) dev_id;
83 while (mconsole_get_request(fd, &req)){
84 if(req.cmd->context == MCONSOLE_INTR)
85 (*req.cmd->handler)(&req);
86 else {
Jeff Dike60baa152006-04-10 22:53:28 -070087 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 if(new == NULL)
89 mconsole_reply(&req, "Out of memory", 1, 0);
90 else {
91 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010092 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 list_add(&new->list, &mc_requests);
94 }
95 }
96 }
97 if(!list_empty(&mc_requests))
98 schedule_work(&mconsole_work);
99 reactivate_fd(fd, MCONSOLE_IRQ);
100 return(IRQ_HANDLED);
101}
102
103void mconsole_version(struct mc_request *req)
104{
105 char version[256];
106
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700107 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
108 utsname()->nodename, utsname()->release,
109 utsname()->version, utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 mconsole_reply(req, version, 0, 0);
111}
112
113void mconsole_log(struct mc_request *req)
114{
115 int len;
116 char *ptr = req->request.data;
117
118 ptr += strlen("log ");
119
120 len = req->len - (ptr - req->request.data);
121 printk("%.*s", len, ptr);
122 mconsole_reply(req, "", 0, 0);
123}
124
125/* This is a more convoluted version of mconsole_proc, which has some stability
126 * problems; however, we need it fixed, because it is expected that UML users
127 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
128 * show the real procfs content, not the ones from hppfs.*/
129#if 0
130void mconsole_proc(struct mc_request *req)
131{
132 struct nameidata nd;
133 struct file_system_type *proc;
134 struct super_block *super;
135 struct file *file;
136 int n, err;
137 char *ptr = req->request.data, *buf;
138
139 ptr += strlen("proc");
140 while(isspace(*ptr)) ptr++;
141
142 proc = get_fs_type("proc");
143 if(proc == NULL){
144 mconsole_reply(req, "procfs not registered", 1, 0);
145 goto out;
146 }
147
148 super = (*proc->get_sb)(proc, 0, NULL, NULL);
149 put_filesystem(proc);
150 if(super == NULL){
151 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
152 goto out;
153 }
154 up_write(&super->s_umount);
155
156 nd.dentry = super->s_root;
157 nd.mnt = NULL;
158 nd.flags = O_RDONLY + 1;
159 nd.last_type = LAST_ROOT;
160
161 /* START: it was experienced that the stability problems are closed
162 * if commenting out these two calls + the below read cycle. To
163 * make UML crash again, it was enough to readd either one.*/
164 err = link_path_walk(ptr, &nd);
165 if(err){
166 mconsole_reply(req, "Failed to look up file", 1, 0);
167 goto out_kill;
168 }
169
170 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
171 if(IS_ERR(file)){
172 mconsole_reply(req, "Failed to open file", 1, 0);
173 goto out_kill;
174 }
175 /*END*/
176
177 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
178 if(buf == NULL){
179 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
180 goto out_fput;
181 }
182
183 if((file->f_op != NULL) && (file->f_op->read != NULL)){
184 do {
185 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
186 &file->f_pos);
187 if(n >= 0){
188 buf[n] = '\0';
189 mconsole_reply(req, buf, 0, (n > 0));
190 }
191 else {
192 mconsole_reply(req, "Read of file failed",
193 1, 0);
194 goto out_free;
195 }
196 } while(n > 0);
197 }
198 else mconsole_reply(req, "", 0, 0);
199
200 out_free:
201 kfree(buf);
202 out_fput:
203 fput(file);
204 out_kill:
205 deactivate_super(super);
206 out: ;
207}
208#endif
209
210void mconsole_proc(struct mc_request *req)
211{
212 char path[64];
213 char *buf;
214 int len;
215 int fd;
216 int first_chunk = 1;
217 char *ptr = req->request.data;
218
219 ptr += strlen("proc");
220 while(isspace(*ptr)) ptr++;
221 snprintf(path, sizeof(path), "/proc/%s", ptr);
222
223 fd = sys_open(path, 0, 0);
224 if (fd < 0) {
225 mconsole_reply(req, "Failed to open file", 1, 0);
226 printk("open %s: %d\n",path,fd);
227 goto out;
228 }
229
230 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
231 if(buf == NULL){
232 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
233 goto out_close;
234 }
235
236 for (;;) {
237 len = sys_read(fd, buf, PAGE_SIZE-1);
238 if (len < 0) {
239 mconsole_reply(req, "Read of file failed", 1, 0);
240 goto out_free;
241 }
242 /*Begin the file content on his own line.*/
243 if (first_chunk) {
244 mconsole_reply(req, "\n", 0, 1);
245 first_chunk = 0;
246 }
247 if (len == PAGE_SIZE-1) {
248 buf[len] = '\0';
249 mconsole_reply(req, buf, 0, 1);
250 } else {
251 buf[len] = '\0';
252 mconsole_reply(req, buf, 0, 0);
253 break;
254 }
255 }
256
257 out_free:
258 kfree(buf);
259 out_close:
260 sys_close(fd);
261 out:
262 /* nothing */;
263}
264
265#define UML_MCONSOLE_HELPTEXT \
266"Commands: \n\
267 version - Get kernel version \n\
268 help - Print this message \n\
269 halt - Halt UML \n\
270 reboot - Reboot UML \n\
271 config <dev>=<config> - Add a new device to UML; \n\
272 same syntax as command line \n\
273 config <dev> - Query the configuration of a device \n\
274 remove <dev> - Remove a device from UML \n\
275 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800276 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 stop - pause the UML; it will do nothing until it receives a 'go' \n\
278 go - continue the UML after a 'stop' \n\
279 log <string> - make UML enter <string> into the kernel log\n\
280 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700281 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282"
283
284void mconsole_help(struct mc_request *req)
285{
286 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
287}
288
289void mconsole_halt(struct mc_request *req)
290{
291 mconsole_reply(req, "", 0, 0);
292 machine_halt();
293}
294
295void mconsole_reboot(struct mc_request *req)
296{
297 mconsole_reply(req, "", 0, 0);
298 machine_restart(NULL);
299}
300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301void mconsole_cad(struct mc_request *req)
302{
303 mconsole_reply(req, "", 0, 0);
304 ctrl_alt_del();
305}
306
307void mconsole_go(struct mc_request *req)
308{
309 mconsole_reply(req, "Not stopped", 1, 0);
310}
311
312void mconsole_stop(struct mc_request *req)
313{
314 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
315 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100316 mconsole_reply(req, "stopped", 0, 0);
317 while (mconsole_get_request(req->originating_fd, req)) {
318 if (req->cmd->handler == mconsole_go)
319 break;
320 if (req->cmd->handler == mconsole_stop) {
321 mconsole_reply(req, "Already stopped", 1, 0);
322 continue;
323 }
324 if (req->cmd->handler == mconsole_sysrq) {
325 struct pt_regs *old_regs;
326 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
327 mconsole_sysrq(req);
328 set_irq_regs(old_regs);
329 continue;
330 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 (*req->cmd->handler)(req);
332 }
333 os_set_fd_block(req->originating_fd, 0);
334 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
335 mconsole_reply(req, "", 0, 0);
336}
337
Jeff Dike84f48d42007-02-10 01:44:01 -0800338static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800339static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
341void mconsole_register_dev(struct mc_device *new)
342{
Jeff Dike84f48d42007-02-10 01:44:01 -0800343 spin_lock(&mc_devices_lock);
344 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800346 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347}
348
349static struct mc_device *mconsole_find_dev(char *name)
350{
351 struct list_head *ele;
352 struct mc_device *dev;
353
354 list_for_each(ele, &mconsole_devices){
355 dev = list_entry(ele, struct mc_device, list);
356 if(!strncmp(name, dev->name, strlen(dev->name)))
357 return(dev);
358 }
359 return(NULL);
360}
361
Jeff Dike02dea082006-03-31 02:30:08 -0800362#define UNPLUGGED_PER_PAGE \
363 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
364
365struct unplugged_pages {
366 struct list_head list;
367 void *pages[UNPLUGGED_PER_PAGE];
368};
369
Jeff Dike84f48d42007-02-10 01:44:01 -0800370static DECLARE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800371static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800372static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800373static int unplug_index = UNPLUGGED_PER_PAGE;
374
Jeff Dikef28169d2007-02-10 01:43:53 -0800375static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800376{
377 unsigned long long diff;
378 int err = -EINVAL, i, add;
379 char *ret;
380
Jeff Dikef28169d2007-02-10 01:43:53 -0800381 if(str[0] != '='){
382 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800383 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800384 }
Jeff Dike02dea082006-03-31 02:30:08 -0800385
386 str++;
387 if(str[0] == '-')
388 add = 0;
389 else if(str[0] == '+'){
390 add = 1;
391 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800392 else {
393 *error_out = "Expected increment to start with '-' or '+'";
394 goto out;
395 }
Jeff Dike02dea082006-03-31 02:30:08 -0800396
397 str++;
398 diff = memparse(str, &ret);
Jeff Dikef28169d2007-02-10 01:43:53 -0800399 if(*ret != '\0'){
400 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800401 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800402 }
Jeff Dike02dea082006-03-31 02:30:08 -0800403
404 diff /= PAGE_SIZE;
405
Jeff Dike84f48d42007-02-10 01:44:01 -0800406 down(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800407 for(i = 0; i < diff; i++){
408 struct unplugged_pages *unplugged;
409 void *addr;
410
411 if(add){
412 if(list_empty(&unplugged_pages))
413 break;
414
415 unplugged = list_entry(unplugged_pages.next,
416 struct unplugged_pages, list);
417 if(unplug_index > 0)
418 addr = unplugged->pages[--unplug_index];
419 else {
420 list_del(&unplugged->list);
421 addr = unplugged;
422 unplug_index = UNPLUGGED_PER_PAGE;
423 }
424
425 free_page((unsigned long) addr);
426 unplugged_pages_count--;
427 }
428 else {
429 struct page *page;
430
431 page = alloc_page(GFP_ATOMIC);
432 if(page == NULL)
433 break;
434
435 unplugged = page_address(page);
436 if(unplug_index == UNPLUGGED_PER_PAGE){
Jeff Dike02dea082006-03-31 02:30:08 -0800437 list_add(&unplugged->list, &unplugged_pages);
438 unplug_index = 0;
439 }
440 else {
441 struct list_head *entry = unplugged_pages.next;
442 addr = unplugged;
443
444 unplugged = list_entry(entry,
445 struct unplugged_pages,
446 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800447 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikef28169d2007-02-10 01:43:53 -0800448 if(err){
Jeff Dike02dea082006-03-31 02:30:08 -0800449 printk("Failed to release memory - "
450 "errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800451 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800452 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800453 }
454 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800455 }
456
457 unplugged_pages_count++;
458 }
459 }
460
461 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800462out_unlock:
463 up(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800464out:
465 return err;
466}
467
468static int mem_get_config(char *name, char *str, int size, char **error_out)
469{
470 char buf[sizeof("18446744073709551615")];
471 int len = 0;
472
473 sprintf(buf, "%ld", uml_physmem);
474 CONFIG_CHUNK(str, size, len, buf, 1);
475
476 return len;
477}
478
479static int mem_id(char **str, int *start_out, int *end_out)
480{
481 *start_out = 0;
482 *end_out = 0;
483
484 return 0;
485}
486
Jeff Dikef28169d2007-02-10 01:43:53 -0800487static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800488{
Jeff Dikef28169d2007-02-10 01:43:53 -0800489 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800490 return -EBUSY;
491}
492
493static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800494 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800495 .name = "mem",
496 .config = mem_config,
497 .get_config = mem_get_config,
498 .id = mem_id,
499 .remove = mem_remove,
500};
501
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700502static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800503{
504 if(can_drop_memory())
505 mconsole_register_dev(&mem_mc);
506 else printk("Can't release memory to the host - memory hotplug won't "
507 "be supported\n");
508 return 0;
509}
510
511__initcall(mem_mc_init);
512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513#define CONFIG_BUF_SIZE 64
514
Jeff Diked50084a2006-01-06 00:18:50 -0800515static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 char **),
517 struct mc_request *req, char *name)
518{
519 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
520 int n, size;
521
522 if(get_config == NULL){
523 mconsole_reply(req, "No get_config routine defined", 1, 0);
524 return;
525 }
526
527 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700528 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 buf = default_buf;
530
531 while(1){
532 n = (*get_config)(name, buf, size, &error);
533 if(error != NULL){
534 mconsole_reply(req, error, 1, 0);
535 goto out;
536 }
537
538 if(n <= size){
539 mconsole_reply(req, buf, 0, 0);
540 goto out;
541 }
542
543 if(buf != default_buf)
544 kfree(buf);
545
546 size = n;
547 buf = kmalloc(size, GFP_KERNEL);
548 if(buf == NULL){
549 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
550 return;
551 }
552 }
553 out:
554 if(buf != default_buf)
555 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556}
557
558void mconsole_config(struct mc_request *req)
559{
560 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800561 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 int err;
563
564 ptr += strlen("config");
565 while(isspace(*ptr)) ptr++;
566 dev = mconsole_find_dev(ptr);
567 if(dev == NULL){
568 mconsole_reply(req, "Bad configuration option", 1, 0);
569 return;
570 }
571
572 name = &ptr[strlen(dev->name)];
573 ptr = name;
574 while((*ptr != '=') && (*ptr != '\0'))
575 ptr++;
576
577 if(*ptr == '='){
Jeff Dikef28169d2007-02-10 01:43:53 -0800578 err = (*dev->config)(name, &error_string);
579 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
581 else mconsole_get_config(dev->get_config, req, name);
582}
583
584void mconsole_remove(struct mc_request *req)
585{
Jeff Diked50084a2006-01-06 00:18:50 -0800586 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700587 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800588 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700589 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
591 ptr += strlen("remove");
592 while(isspace(*ptr)) ptr++;
593 dev = mconsole_find_dev(ptr);
594 if(dev == NULL){
595 mconsole_reply(req, "Bad remove option", 1, 0);
596 return;
597 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700598
Jeff Dike3a331a52006-01-06 00:19:05 -0800599 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700600
Jeff Dike3a331a52006-01-06 00:19:05 -0800601 err = 1;
602 n = (*dev->id)(&ptr, &start, &end);
603 if(n < 0){
604 err_msg = "Couldn't parse device number";
605 goto out;
606 }
607 else if((n < start) || (n > end)){
608 sprintf(error, "Invalid device number - must be between "
609 "%d and %d", start, end);
610 err_msg = error;
611 goto out;
612 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700613
Jeff Dikef28169d2007-02-10 01:43:53 -0800614 err_msg = NULL;
615 err = (*dev->remove)(n, &err_msg);
Jeff Dike3a331a52006-01-06 00:19:05 -0800616 switch(err){
Jeff Diked40f6d72007-03-29 01:20:28 -0700617 case 0:
618 err_msg = "";
619 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800620 case -ENODEV:
Jeff Dikef28169d2007-02-10 01:43:53 -0800621 if(err_msg == NULL)
622 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800623 break;
624 case -EBUSY:
Jeff Dikef28169d2007-02-10 01:43:53 -0800625 if(err_msg == NULL)
626 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800627 break;
628 default:
629 break;
630 }
631out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700632 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633}
634
Jeff Dikef92afe52006-09-29 01:58:52 -0700635struct mconsole_output {
636 struct list_head list;
637 struct mc_request *req;
638};
639
Jeff Dike84f48d42007-02-10 01:44:01 -0800640static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800641static LIST_HEAD(clients);
642static char console_buf[MCONSOLE_MAX_DATA];
643static int console_index = 0;
644
645static void console_write(struct console *console, const char *string,
646 unsigned len)
647{
648 struct list_head *ele;
649 int n;
650
651 if(list_empty(&clients))
652 return;
653
654 while(1){
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700655 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800656 strncpy(&console_buf[console_index], string, n);
657 console_index += n;
658 string += n;
659 len -= n;
660 if(len == 0)
661 return;
662
663 list_for_each(ele, &clients){
Jeff Dikef92afe52006-09-29 01:58:52 -0700664 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800665
Jeff Dikef92afe52006-09-29 01:58:52 -0700666 entry = list_entry(ele, struct mconsole_output, list);
667 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800668 console_index, 0, 1);
669 }
670
671 console_index = 0;
672 }
673}
674
675static struct console mc_console = { .name = "mc",
676 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800677 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800678 .index = -1 };
679
680static int mc_add_console(void)
681{
682 register_console(&mc_console);
683 return 0;
684}
685
686late_initcall(mc_add_console);
687
688static void with_console(struct mc_request *req, void (*proc)(void *),
689 void *arg)
690{
Jeff Dikef92afe52006-09-29 01:58:52 -0700691 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800692 unsigned long flags;
693
Jeff Dikef92afe52006-09-29 01:58:52 -0700694 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800695 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800696 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800697 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800698
699 (*proc)(arg);
700
701 mconsole_reply_len(req, console_buf, console_index, 0, 0);
702 console_index = 0;
703
Jeff Dike84f48d42007-02-10 01:44:01 -0800704 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800705 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800706 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800707}
708
Jeff Dike4111b022006-01-06 00:19:05 -0800709#ifdef CONFIG_MAGIC_SYSRQ
710static void sysrq_proc(void *arg)
711{
712 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100713 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800714}
715
716void mconsole_sysrq(struct mc_request *req)
717{
718 char *ptr = req->request.data;
719
720 ptr += strlen("sysrq");
721 while(isspace(*ptr)) ptr++;
722
723 /* With 'b', the system will shut down without a chance to reply,
724 * so in this case, we reply first.
725 */
726 if(*ptr == 'b')
727 mconsole_reply(req, "", 0, 0);
728
729 with_console(req, sysrq_proc, ptr);
730}
731#else
732void mconsole_sysrq(struct mc_request *req)
733{
734 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
735}
736#endif
737
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800738#ifdef CONFIG_MODE_SKAS
739
Jeff Dike6f517d32006-01-06 00:19:04 -0800740static void stack_proc(void *arg)
741{
742 struct task_struct *from = current, *to = arg;
743
744 to->thread.saved_task = from;
745 switch_to(from, to, from);
746}
747
Jeff Dike3eddddc2005-09-16 19:27:46 -0700748/* Mconsole stack trace
749 * Added by Allan Graves, Jeff Dike
750 * Dumps a stacks registers to the linux console.
751 * Usage stack <pid>.
752 */
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800753static void do_stack_trace(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700754{
Jeff Dike3a331a52006-01-06 00:19:05 -0800755 char *ptr = req->request.data;
756 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800757 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700758 struct task_struct *to = NULL;
759
Jeff Dike3a331a52006-01-06 00:19:05 -0800760 /* Would be nice:
761 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700762 * 2) Add a way to stack dump all pids.
763 */
764
Jeff Dike3a331a52006-01-06 00:19:05 -0800765 ptr += strlen("stack");
766 while(isspace(*ptr)) ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700767
Jeff Dike3a331a52006-01-06 00:19:05 -0800768 /* Should really check for multiple pids or reject bad args here */
769 /* What do the arguments in mconsole_reply mean? */
770 if(sscanf(ptr, "%d", &pid_requested) == 0){
771 mconsole_reply(req, "Please specify a pid", 1, 0);
772 return;
773 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700774
Jeff Dike6f517d32006-01-06 00:19:04 -0800775 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700776
Jeff Dike6f517d32006-01-06 00:19:04 -0800777 to = find_task_by_pid(pid_requested);
Jeff Dike3a331a52006-01-06 00:19:05 -0800778 if((to == NULL) || (pid_requested == 0)) {
779 mconsole_reply(req, "Couldn't find that pid", 1, 0);
780 return;
781 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800782 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700783}
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800784#endif /* CONFIG_MODE_SKAS */
Jeff Dike3eddddc2005-09-16 19:27:46 -0700785
786void mconsole_stack(struct mc_request *req)
787{
788 /* This command doesn't work in TT mode, so let's check and then
789 * get out of here
790 */
791 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
792 1, 0),
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800793 do_stack_trace(req));
Jeff Dike3eddddc2005-09-16 19:27:46 -0700794}
795
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796/* Changed by mconsole_setup, which is __setup, and called before SMP is
797 * active.
798 */
Jeff Diked50084a2006-01-06 00:18:50 -0800799static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700801static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802{
803 /* long to avoid size mismatch warnings from gcc */
804 long sock;
805 int err;
806 char file[256];
807
808 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
809 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
810
811 sock = os_create_unix_socket(file, sizeof(file), 1);
812 if (sock < 0){
813 printk("Failed to initialize management console\n");
814 return(1);
815 }
816
817 register_reboot_notifier(&reboot_notifier);
818
819 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700820 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 "mconsole", (void *)sock);
822 if (err){
823 printk("Failed to get IRQ for management console\n");
824 return(1);
825 }
826
827 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800828 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if(notify_socket != NULL)
830 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800831 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 strlen(mconsole_socket_name) + 1);
833 else printk(KERN_ERR "mconsole_setup failed to strdup "
834 "string\n");
835 }
836
Jeff Diked50084a2006-01-06 00:18:50 -0800837 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 MCONSOLE_VERSION, mconsole_socket_name);
839 return(0);
840}
841
842__initcall(mconsole_init);
843
844static int write_proc_mconsole(struct file *file, const char __user *buffer,
845 unsigned long count, void *data)
846{
847 char *buf;
848
849 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800850 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 return(-ENOMEM);
852
853 if(copy_from_user(buf, buffer, count)){
854 count = -EFAULT;
855 goto out;
856 }
857
858 buf[count] = '\0';
859
860 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
861 out:
862 kfree(buf);
863 return(count);
864}
865
866static int create_proc_mconsole(void)
867{
868 struct proc_dir_entry *ent;
869
870 if(notify_socket == NULL) return(0);
871
872 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
873 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700874 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 return(0);
876 }
877
878 ent->read_proc = NULL;
879 ent->write_proc = write_proc_mconsole;
880 return(0);
881}
882
883static DEFINE_SPINLOCK(notify_spinlock);
884
885void lock_notify(void)
886{
887 spin_lock(&notify_spinlock);
888}
889
890void unlock_notify(void)
891{
892 spin_unlock(&notify_spinlock);
893}
894
895__initcall(create_proc_mconsole);
896
897#define NOTIFY "=notify:"
898
899static int mconsole_setup(char *str)
900{
901 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
902 str += strlen(NOTIFY);
903 notify_socket = str;
904 }
905 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
906 return(1);
907}
908
909__setup("mconsole", mconsole_setup);
910
911__uml_help(mconsole_setup,
912"mconsole=notify:<socket>\n"
913" Requests that the mconsole driver send a message to the named Unix\n"
914" socket containing the name of the mconsole socket. This also serves\n"
915" to notify outside processes when UML has booted far enough to respond\n"
916" to mconsole requests.\n\n"
917);
918
919static int notify_panic(struct notifier_block *self, unsigned long unused1,
920 void *ptr)
921{
922 char *message = ptr;
923
924 if(notify_socket == NULL) return(0);
925
Jeff Diked50084a2006-01-06 00:18:50 -0800926 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 strlen(message) + 1);
928 return(0);
929}
930
931static struct notifier_block panic_exit_notifier = {
932 .notifier_call = notify_panic,
933 .next = NULL,
934 .priority = 1
935};
936
937static int add_notifier(void)
938{
Alan Sterne041c682006-03-27 01:16:30 -0800939 atomic_notifier_chain_register(&panic_notifier_list,
940 &panic_exit_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 return(0);
942}
943
944__initcall(add_notifier);
945
946char *mconsole_notify_socket(void)
947{
948 return(notify_socket);
949}
950
951EXPORT_SYMBOL(mconsole_notify_socket);