mconsole_kern.c 20.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
J
Jeff Dike 已提交
3
 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
L
Linus Torvalds 已提交
4 5 6
 * Licensed under the GPL
 */

J
Jeff Dike 已提交
7
#include "linux/console.h"
L
Linus Torvalds 已提交
8 9
#include "linux/ctype.h"
#include "linux/interrupt.h"
J
Jeff Dike 已提交
10 11
#include "linux/list.h"
#include "linux/mm.h"
L
Linus Torvalds 已提交
12
#include "linux/module.h"
J
Jeff Dike 已提交
13 14
#include "linux/notifier.h"
#include "linux/reboot.h"
L
Linus Torvalds 已提交
15
#include "linux/proc_fs.h"
J
Jeff Dike 已提交
16
#include "linux/slab.h"
L
Linus Torvalds 已提交
17
#include "linux/syscalls.h"
J
Jeff Dike 已提交
18 19
#include "linux/utsname.h"
#include "linux/workqueue.h"
L
Linus Torvalds 已提交
20
#include "asm/uaccess.h"
J
Jeff Dike 已提交
21 22 23
#include "init.h"
#include "irq_kern.h"
#include "irq_user.h"
L
Linus Torvalds 已提交
24 25 26 27 28
#include "kern_util.h"
#include "mconsole.h"
#include "mconsole_kern.h"
#include "os.h"

J
Jeff Dike 已提交
29
static int do_unlink_socket(struct notifier_block *notifier,
L
Linus Torvalds 已提交
30 31
			    unsigned long what, void *data)
{
J
Jeff Dike 已提交
32
	return mconsole_unlink_socket();
L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40
}


static struct notifier_block reboot_notifier = {
	.notifier_call		= do_unlink_socket,
	.priority		= 0,
};

J
Jeff Dike 已提交
41
/* Safe without explicit locking for now.  Tasklets provide their own
L
Linus Torvalds 已提交
42 43 44 45
 * locking, and the interrupt handler is safe because it can't interrupt
 * itself and it can only happen on CPU 0.
 */

46
static LIST_HEAD(mc_requests);
L
Linus Torvalds 已提交
47

48
static void mc_work_proc(struct work_struct *unused)
L
Linus Torvalds 已提交
49 50 51 52
{
	struct mconsole_entry *req;
	unsigned long flags;

J
Jeff Dike 已提交
53
	while (!list_empty(&mc_requests)) {
54
		local_irq_save(flags);
J
Jeff Dike 已提交
55
		req = list_entry(mc_requests.next, struct mconsole_entry, list);
L
Linus Torvalds 已提交
56 57 58 59 60 61 62
		list_del(&req->list);
		local_irq_restore(flags);
		req->request.cmd->handler(&req->request);
		kfree(req);
	}
}

63
static DECLARE_WORK(mconsole_work, mc_work_proc);
L
Linus Torvalds 已提交
64

A
Al Viro 已提交
65
static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
66 67 68 69
{
	/* long to avoid size mismatch warnings from gcc */
	long fd;
	struct mconsole_entry *new;
A
Al Viro 已提交
70
	static struct mc_request req;	/* that's OK */
L
Linus Torvalds 已提交
71 72

	fd = (long) dev_id;
J
Jeff Dike 已提交
73 74
	while (mconsole_get_request(fd, &req)) {
		if (req.cmd->context == MCONSOLE_INTR)
L
Linus Torvalds 已提交
75 76
			(*req.cmd->handler)(&req);
		else {
J
Jeff Dike 已提交
77
			new = kmalloc(sizeof(*new), GFP_NOWAIT);
J
Jeff Dike 已提交
78
			if (new == NULL)
L
Linus Torvalds 已提交
79 80 81
				mconsole_reply(&req, "Out of memory", 1, 0);
			else {
				new->request = req;
A
Al Viro 已提交
82
				new->request.regs = get_irq_regs()->regs;
L
Linus Torvalds 已提交
83 84 85 86
				list_add(&new->list, &mc_requests);
			}
		}
	}
J
Jeff Dike 已提交
87
	if (!list_empty(&mc_requests))
L
Linus Torvalds 已提交
88 89
		schedule_work(&mconsole_work);
	reactivate_fd(fd, MCONSOLE_IRQ);
J
Jeff Dike 已提交
90
	return IRQ_HANDLED;
L
Linus Torvalds 已提交
91 92 93 94 95 96
}

void mconsole_version(struct mc_request *req)
{
	char version[256];

97
	sprintf(version, "%s %s %s %s %s", utsname()->sysname,
J
Jeff Dike 已提交
98 99
		utsname()->nodename, utsname()->release, utsname()->version,
		utsname()->machine);
L
Linus Torvalds 已提交
100 101 102 103 104 105 106 107 108 109 110
	mconsole_reply(req, version, 0, 0);
}

void mconsole_log(struct mc_request *req)
{
	int len;
	char *ptr = req->request.data;

	ptr += strlen("log ");

	len = req->len - (ptr - req->request.data);
J
Jeff Dike 已提交
111
	printk(KERN_WARNING "%.*s", len, ptr);
L
Linus Torvalds 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
	mconsole_reply(req, "", 0, 0);
}

/* This is a more convoluted version of mconsole_proc, which has some stability
 * problems; however, we need it fixed, because it is expected that UML users
 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
 * show the real procfs content, not the ones from hppfs.*/
#if 0
void mconsole_proc(struct mc_request *req)
{
	struct nameidata nd;
	struct file_system_type *proc;
	struct super_block *super;
	struct file *file;
	int n, err;
	char *ptr = req->request.data, *buf;

	ptr += strlen("proc");
J
Jeff Dike 已提交
130
	while (isspace(*ptr)) ptr++;
L
Linus Torvalds 已提交
131 132

	proc = get_fs_type("proc");
J
Jeff Dike 已提交
133
	if (proc == NULL) {
L
Linus Torvalds 已提交
134 135 136 137 138 139
		mconsole_reply(req, "procfs not registered", 1, 0);
		goto out;
	}

	super = (*proc->get_sb)(proc, 0, NULL, NULL);
	put_filesystem(proc);
J
Jeff Dike 已提交
140
	if (super == NULL) {
L
Linus Torvalds 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154
		mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
		goto out;
	}
	up_write(&super->s_umount);

	nd.dentry = super->s_root;
	nd.mnt = NULL;
	nd.flags = O_RDONLY + 1;
	nd.last_type = LAST_ROOT;

	/* START: it was experienced that the stability problems are closed
	 * if commenting out these two calls + the below read cycle. To
	 * make UML crash again, it was enough to readd either one.*/
	err = link_path_walk(ptr, &nd);
J
Jeff Dike 已提交
155
	if (err) {
L
Linus Torvalds 已提交
156 157 158 159 160
		mconsole_reply(req, "Failed to look up file", 1, 0);
		goto out_kill;
	}

	file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
J
Jeff Dike 已提交
161
	if (IS_ERR(file)) {
L
Linus Torvalds 已提交
162 163 164 165 166 167
		mconsole_reply(req, "Failed to open file", 1, 0);
		goto out_kill;
	}
	/*END*/

	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
J
Jeff Dike 已提交
168
	if (buf == NULL) {
L
Linus Torvalds 已提交
169 170 171 172
		mconsole_reply(req, "Failed to allocate buffer", 1, 0);
		goto out_fput;
	}

J
Jeff Dike 已提交
173
	if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
L
Linus Torvalds 已提交
174 175 176
		do {
			n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
						&file->f_pos);
J
Jeff Dike 已提交
177
			if (n >= 0) {
L
Linus Torvalds 已提交
178 179 180 181 182 183 184 185
				buf[n] = '\0';
				mconsole_reply(req, buf, 0, (n > 0));
			}
			else {
				mconsole_reply(req, "Read of file failed",
					       1, 0);
				goto out_free;
			}
J
Jeff Dike 已提交
186
		} while (n > 0);
L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
	}
	else mconsole_reply(req, "", 0, 0);

 out_free:
	kfree(buf);
 out_fput:
	fput(file);
 out_kill:
	deactivate_super(super);
 out: ;
}
#endif

void mconsole_proc(struct mc_request *req)
{
	char path[64];
	char *buf;
	int len;
	int fd;
	int first_chunk = 1;
	char *ptr = req->request.data;

	ptr += strlen("proc");
J
Jeff Dike 已提交
210 211
	while (isspace(*ptr))
		ptr++;
L
Linus Torvalds 已提交
212 213 214 215 216
	snprintf(path, sizeof(path), "/proc/%s", ptr);

	fd = sys_open(path, 0, 0);
	if (fd < 0) {
		mconsole_reply(req, "Failed to open file", 1, 0);
J
Jeff Dike 已提交
217
		printk(KERN_ERR "open %s: %d\n",path,fd);
L
Linus Torvalds 已提交
218 219 220 221
		goto out;
	}

	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
J
Jeff Dike 已提交
222
	if (buf == NULL) {
L
Linus Torvalds 已提交
223 224 225 226 227 228 229 230 231 232
		mconsole_reply(req, "Failed to allocate buffer", 1, 0);
		goto out_close;
	}

	for (;;) {
		len = sys_read(fd, buf, PAGE_SIZE-1);
		if (len < 0) {
			mconsole_reply(req, "Read of file failed", 1, 0);
			goto out_free;
		}
J
Jeff Dike 已提交
233
		/* Begin the file content on his own line. */
L
Linus Torvalds 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
		if (first_chunk) {
			mconsole_reply(req, "\n", 0, 1);
			first_chunk = 0;
		}
		if (len == PAGE_SIZE-1) {
			buf[len] = '\0';
			mconsole_reply(req, buf, 0, 1);
		} else {
			buf[len] = '\0';
			mconsole_reply(req, buf, 0, 0);
			break;
		}
	}

 out_free:
	kfree(buf);
 out_close:
	sys_close(fd);
 out:
	/* nothing */;
}

#define UML_MCONSOLE_HELPTEXT \
"Commands: \n\
    version - Get kernel version \n\
    help - Print this message \n\
    halt - Halt UML \n\
    reboot - Reboot UML \n\
    config <dev>=<config> - Add a new device to UML;  \n\
	same syntax as command line \n\
    config <dev> - Query the configuration of a device \n\
    remove <dev> - Remove a device from UML \n\
    sysrq <letter> - Performs the SysRq action controlled by the letter \n\
J
Jeff Dike 已提交
267
    cad - invoke the Ctrl-Alt-Del handler \n\
L
Linus Torvalds 已提交
268 269 270 271
    stop - pause the UML; it will do nothing until it receives a 'go' \n\
    go - continue the UML after a 'stop' \n\
    log <string> - make UML enter <string> into the kernel log\n\
    proc <file> - returns the contents of the UML's /proc/<file>\n\
272
    stack <pid> - returns the stack of the specified pid\n\
L
Linus Torvalds 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
"

void mconsole_help(struct mc_request *req)
{
	mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
}

void mconsole_halt(struct mc_request *req)
{
	mconsole_reply(req, "", 0, 0);
	machine_halt();
}

void mconsole_reboot(struct mc_request *req)
{
	mconsole_reply(req, "", 0, 0);
	machine_restart(NULL);
}

void mconsole_cad(struct mc_request *req)
{
	mconsole_reply(req, "", 0, 0);
	ctrl_alt_del();
}

void mconsole_go(struct mc_request *req)
{
	mconsole_reply(req, "Not stopped", 1, 0);
}

void mconsole_stop(struct mc_request *req)
{
	deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
	os_set_fd_block(req->originating_fd, 1);
A
Al Viro 已提交
307
	mconsole_reply(req, "stopped", 0, 0);
308 309 310
	for (;;) {
		if (!mconsole_get_request(req->originating_fd, req))
			continue;
A
Al Viro 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323
		if (req->cmd->handler == mconsole_go)
			break;
		if (req->cmd->handler == mconsole_stop) {
			mconsole_reply(req, "Already stopped", 1, 0);
			continue;
		}
		if (req->cmd->handler == mconsole_sysrq) {
			struct pt_regs *old_regs;
			old_regs = set_irq_regs((struct pt_regs *)&req->regs);
			mconsole_sysrq(req);
			set_irq_regs(old_regs);
			continue;
		}
L
Linus Torvalds 已提交
324 325 326 327 328 329 330
		(*req->cmd->handler)(req);
	}
	os_set_fd_block(req->originating_fd, 0);
	reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
	mconsole_reply(req, "", 0, 0);
}

J
Jeff Dike 已提交
331
static DEFINE_SPINLOCK(mc_devices_lock);
332
static LIST_HEAD(mconsole_devices);
L
Linus Torvalds 已提交
333 334 335

void mconsole_register_dev(struct mc_device *new)
{
J
Jeff Dike 已提交
336 337
	spin_lock(&mc_devices_lock);
	BUG_ON(!list_empty(&new->list));
L
Linus Torvalds 已提交
338
	list_add(&new->list, &mconsole_devices);
J
Jeff Dike 已提交
339
	spin_unlock(&mc_devices_lock);
L
Linus Torvalds 已提交
340 341 342 343 344 345 346
}

static struct mc_device *mconsole_find_dev(char *name)
{
	struct list_head *ele;
	struct mc_device *dev;

J
Jeff Dike 已提交
347
	list_for_each(ele, &mconsole_devices) {
L
Linus Torvalds 已提交
348
		dev = list_entry(ele, struct mc_device, list);
J
Jeff Dike 已提交
349 350
		if (!strncmp(name, dev->name, strlen(dev->name)))
			return dev;
L
Linus Torvalds 已提交
351
	}
J
Jeff Dike 已提交
352
	return NULL;
L
Linus Torvalds 已提交
353 354
}

J
Jeff Dike 已提交
355 356 357 358 359 360 361 362
#define UNPLUGGED_PER_PAGE \
	((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))

struct unplugged_pages {
	struct list_head list;
	void *pages[UNPLUGGED_PER_PAGE];
};

J
Jeff Dike 已提交
363
static DECLARE_MUTEX(plug_mem_mutex);
J
Jeff Dike 已提交
364
static unsigned long long unplugged_pages_count = 0;
365
static LIST_HEAD(unplugged_pages);
J
Jeff Dike 已提交
366 367
static int unplug_index = UNPLUGGED_PER_PAGE;

368
static int mem_config(char *str, char **error_out)
J
Jeff Dike 已提交
369 370 371 372 373
{
	unsigned long long diff;
	int err = -EINVAL, i, add;
	char *ret;

J
Jeff Dike 已提交
374
	if (str[0] != '=') {
375
		*error_out = "Expected '=' after 'mem'";
J
Jeff Dike 已提交
376
		goto out;
377
	}
J
Jeff Dike 已提交
378 379

	str++;
J
Jeff Dike 已提交
380
	if (str[0] == '-')
J
Jeff Dike 已提交
381
		add = 0;
J
Jeff Dike 已提交
382
	else if (str[0] == '+') {
J
Jeff Dike 已提交
383 384
		add = 1;
	}
385 386 387 388
	else {
		*error_out = "Expected increment to start with '-' or '+'";
		goto out;
	}
J
Jeff Dike 已提交
389 390 391

	str++;
	diff = memparse(str, &ret);
J
Jeff Dike 已提交
392
	if (*ret != '\0') {
393
		*error_out = "Failed to parse memory increment";
J
Jeff Dike 已提交
394
		goto out;
395
	}
J
Jeff Dike 已提交
396 397 398

	diff /= PAGE_SIZE;

J
Jeff Dike 已提交
399
	down(&plug_mem_mutex);
J
Jeff Dike 已提交
400
	for (i = 0; i < diff; i++) {
J
Jeff Dike 已提交
401 402 403
		struct unplugged_pages *unplugged;
		void *addr;

J
Jeff Dike 已提交
404 405
		if (add) {
			if (list_empty(&unplugged_pages))
J
Jeff Dike 已提交
406 407 408 409
				break;

			unplugged = list_entry(unplugged_pages.next,
					       struct unplugged_pages, list);
J
Jeff Dike 已提交
410
			if (unplug_index > 0)
J
Jeff Dike 已提交
411 412 413 414 415 416 417 418 419 420 421 422 423 424
				addr = unplugged->pages[--unplug_index];
			else {
				list_del(&unplugged->list);
				addr = unplugged;
				unplug_index = UNPLUGGED_PER_PAGE;
			}

			free_page((unsigned long) addr);
			unplugged_pages_count--;
		}
		else {
			struct page *page;

			page = alloc_page(GFP_ATOMIC);
J
Jeff Dike 已提交
425
			if (page == NULL)
J
Jeff Dike 已提交
426 427 428
				break;

			unplugged = page_address(page);
J
Jeff Dike 已提交
429
			if (unplug_index == UNPLUGGED_PER_PAGE) {
J
Jeff Dike 已提交
430 431 432 433 434 435 436 437 438 439 440
				list_add(&unplugged->list, &unplugged_pages);
				unplug_index = 0;
			}
			else {
				struct list_head *entry = unplugged_pages.next;
				addr = unplugged;

				unplugged = list_entry(entry,
						       struct unplugged_pages,
						       list);
				err = os_drop_memory(addr, PAGE_SIZE);
J
Jeff Dike 已提交
441 442 443
				if (err) {
					printk(KERN_ERR "Failed to release "
					       "memory - errno = %d\n", err);
444
					*error_out = "Failed to release memory";
J
Jeff Dike 已提交
445
					goto out_unlock;
446 447
				}
				unplugged->pages[unplug_index++] = addr;
J
Jeff Dike 已提交
448 449 450 451 452 453 454
			}

			unplugged_pages_count++;
		}
	}

	err = 0;
J
Jeff Dike 已提交
455 456
out_unlock:
	up(&plug_mem_mutex);
J
Jeff Dike 已提交
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
out:
	return err;
}

static int mem_get_config(char *name, char *str, int size, char **error_out)
{
	char buf[sizeof("18446744073709551615")];
	int len = 0;

	sprintf(buf, "%ld", uml_physmem);
	CONFIG_CHUNK(str, size, len, buf, 1);

	return len;
}

static int mem_id(char **str, int *start_out, int *end_out)
{
	*start_out = 0;
	*end_out = 0;

	return 0;
}

480
static int mem_remove(int n, char **error_out)
J
Jeff Dike 已提交
481
{
482
	*error_out = "Memory doesn't support the remove operation";
J
Jeff Dike 已提交
483 484 485 486
	return -EBUSY;
}

static struct mc_device mem_mc = {
J
Jeff Dike 已提交
487
	.list		= LIST_HEAD_INIT(mem_mc.list),
J
Jeff Dike 已提交
488 489 490 491 492 493 494
	.name		= "mem",
	.config		= mem_config,
	.get_config	= mem_get_config,
	.id		= mem_id,
	.remove		= mem_remove,
};

J
Jeff Dike 已提交
495
static int __init mem_mc_init(void)
J
Jeff Dike 已提交
496
{
J
Jeff Dike 已提交
497
	if (can_drop_memory())
J
Jeff Dike 已提交
498
		mconsole_register_dev(&mem_mc);
J
Jeff Dike 已提交
499 500
	else printk(KERN_ERR "Can't release memory to the host - memory "
		    "hotplug won't be supported\n");
J
Jeff Dike 已提交
501 502 503 504 505
	return 0;
}

__initcall(mem_mc_init);

L
Linus Torvalds 已提交
506 507
#define CONFIG_BUF_SIZE 64

J
Jeff Dike 已提交
508
static void mconsole_get_config(int (*get_config)(char *, char *, int,
L
Linus Torvalds 已提交
509 510 511 512 513 514
						  char **),
				struct mc_request *req, char *name)
{
	char default_buf[CONFIG_BUF_SIZE], *error, *buf;
	int n, size;

J
Jeff Dike 已提交
515
	if (get_config == NULL) {
L
Linus Torvalds 已提交
516 517 518 519 520
		mconsole_reply(req, "No get_config routine defined", 1, 0);
		return;
	}

	error = NULL;
521
	size = ARRAY_SIZE(default_buf);
L
Linus Torvalds 已提交
522 523
	buf = default_buf;

J
Jeff Dike 已提交
524
	while (1) {
L
Linus Torvalds 已提交
525
		n = (*get_config)(name, buf, size, &error);
J
Jeff Dike 已提交
526
		if (error != NULL) {
L
Linus Torvalds 已提交
527 528 529 530
			mconsole_reply(req, error, 1, 0);
			goto out;
		}

J
Jeff Dike 已提交
531
		if (n <= size) {
L
Linus Torvalds 已提交
532 533 534 535
			mconsole_reply(req, buf, 0, 0);
			goto out;
		}

J
Jeff Dike 已提交
536
		if (buf != default_buf)
L
Linus Torvalds 已提交
537 538 539 540
			kfree(buf);

		size = n;
		buf = kmalloc(size, GFP_KERNEL);
J
Jeff Dike 已提交
541
		if (buf == NULL) {
L
Linus Torvalds 已提交
542 543 544 545 546
			mconsole_reply(req, "Failed to allocate buffer", 1, 0);
			return;
		}
	}
 out:
J
Jeff Dike 已提交
547
	if (buf != default_buf)
L
Linus Torvalds 已提交
548 549 550 551 552 553
		kfree(buf);
}

void mconsole_config(struct mc_request *req)
{
	struct mc_device *dev;
554
	char *ptr = req->request.data, *name, *error_string = "";
L
Linus Torvalds 已提交
555 556 557
	int err;

	ptr += strlen("config");
J
Jeff Dike 已提交
558 559
	while (isspace(*ptr))
		ptr++;
L
Linus Torvalds 已提交
560
	dev = mconsole_find_dev(ptr);
J
Jeff Dike 已提交
561
	if (dev == NULL) {
L
Linus Torvalds 已提交
562 563 564 565 566 567
		mconsole_reply(req, "Bad configuration option", 1, 0);
		return;
	}

	name = &ptr[strlen(dev->name)];
	ptr = name;
J
Jeff Dike 已提交
568
	while ((*ptr != '=') && (*ptr != '\0'))
L
Linus Torvalds 已提交
569 570
		ptr++;

J
Jeff Dike 已提交
571
	if (*ptr == '=') {
572 573
		err = (*dev->config)(name, &error_string);
		mconsole_reply(req, error_string, err, 0);
L
Linus Torvalds 已提交
574 575 576 577 578 579
	}
	else mconsole_get_config(dev->get_config, req, name);
}

void mconsole_remove(struct mc_request *req)
{
J
Jeff Dike 已提交
580
	struct mc_device *dev;
J
Jeff Dike 已提交
581
	char *ptr = req->request.data, *err_msg = "";
582
	char error[256];
J
Jeff Dike 已提交
583
	int err, start, end, n;
L
Linus Torvalds 已提交
584 585

	ptr += strlen("remove");
J
Jeff Dike 已提交
586
	while (isspace(*ptr)) ptr++;
L
Linus Torvalds 已提交
587
	dev = mconsole_find_dev(ptr);
J
Jeff Dike 已提交
588
	if (dev == NULL) {
L
Linus Torvalds 已提交
589 590 591
		mconsole_reply(req, "Bad remove option", 1, 0);
		return;
	}
J
Jeff Dike 已提交
592

593 594 595 596
	ptr = &ptr[strlen(dev->name)];

	err = 1;
	n = (*dev->id)(&ptr, &start, &end);
J
Jeff Dike 已提交
597
	if (n < 0) {
598 599 600
		err_msg = "Couldn't parse device number";
		goto out;
	}
J
Jeff Dike 已提交
601
	else if ((n < start) || (n > end)) {
602 603 604 605 606
		sprintf(error, "Invalid device number - must be between "
			"%d and %d", start, end);
		err_msg = error;
		goto out;
	}
J
Jeff Dike 已提交
607

608 609
	err_msg = NULL;
	err = (*dev->remove)(n, &err_msg);
J
Jeff Dike 已提交
610
	switch(err) {
J
Jeff Dike 已提交
611 612 613
	case 0:
		err_msg = "";
		break;
614
	case -ENODEV:
J
Jeff Dike 已提交
615
		if (err_msg == NULL)
616
			err_msg = "Device doesn't exist";
617 618
		break;
	case -EBUSY:
J
Jeff Dike 已提交
619
		if (err_msg == NULL)
620
			err_msg = "Device is currently open";
621 622 623 624 625
		break;
	default:
		break;
	}
out:
J
Jeff Dike 已提交
626
	mconsole_reply(req, err_msg, err, 0);
L
Linus Torvalds 已提交
627 628
}

629 630 631 632 633
struct mconsole_output {
	struct list_head list;
	struct mc_request *req;
};

J
Jeff Dike 已提交
634
static DEFINE_SPINLOCK(client_lock);
635 636 637 638
static LIST_HEAD(clients);
static char console_buf[MCONSOLE_MAX_DATA];

static void console_write(struct console *console, const char *string,
J
Jeff Dike 已提交
639
			  unsigned int len)
640 641 642 643
{
	struct list_head *ele;
	int n;

J
Jeff Dike 已提交
644
	if (list_empty(&clients))
645 646
		return;

J
Jeff Dike 已提交
647 648 649
	while (len > 0) {
		n = min((size_t) len, ARRAY_SIZE(console_buf));
		strncpy(console_buf, string, n);
650 651 652
		string += n;
		len -= n;

J
Jeff Dike 已提交
653
		list_for_each(ele, &clients) {
654
			struct mconsole_output *entry;
655

656
			entry = list_entry(ele, struct mconsole_output, list);
J
Jeff Dike 已提交
657
			mconsole_reply_len(entry->req, console_buf, n, 0, 1);
658 659 660 661 662 663
		}
	}
}

static struct console mc_console = { .name	= "mc",
				     .write	= console_write,
664
				     .flags	= CON_ENABLED,
665 666 667 668 669 670 671 672 673 674 675 676 677
				     .index	= -1 };

static int mc_add_console(void)
{
	register_console(&mc_console);
	return 0;
}

late_initcall(mc_add_console);

static void with_console(struct mc_request *req, void (*proc)(void *),
			 void *arg)
{
678
	struct mconsole_output entry;
679 680
	unsigned long flags;

681
	entry.req = req;
J
Jeff Dike 已提交
682
	spin_lock_irqsave(&client_lock, flags);
683
	list_add(&entry.list, &clients);
J
Jeff Dike 已提交
684
	spin_unlock_irqrestore(&client_lock, flags);
685 686 687

	(*proc)(arg);

J
Jeff Dike 已提交
688
	mconsole_reply_len(req, "", 0, 0, 0);
689

J
Jeff Dike 已提交
690
	spin_lock_irqsave(&client_lock, flags);
691
	list_del(&entry.list);
J
Jeff Dike 已提交
692
	spin_unlock_irqrestore(&client_lock, flags);
693 694
}

695
#ifdef CONFIG_MAGIC_SYSRQ
J
Jeff Dike 已提交
696 697 698

#include <linux/sysrq.h>

699 700 701
static void sysrq_proc(void *arg)
{
	char *op = arg;
A
Al Viro 已提交
702
	handle_sysrq(*op, NULL);
703 704 705 706 707 708 709
}

void mconsole_sysrq(struct mc_request *req)
{
	char *ptr = req->request.data;

	ptr += strlen("sysrq");
J
Jeff Dike 已提交
710
	while (isspace(*ptr)) ptr++;
711

J
Jeff Dike 已提交
712 713
	/*
	 * With 'b', the system will shut down without a chance to reply,
714 715
	 * so in this case, we reply first.
	 */
J
Jeff Dike 已提交
716
	if (*ptr == 'b')
717 718 719 720 721 722 723 724 725 726 727
		mconsole_reply(req, "", 0, 0);

	with_console(req, sysrq_proc, ptr);
}
#else
void mconsole_sysrq(struct mc_request *req)
{
	mconsole_reply(req, "Sysrq not compiled in", 1, 0);
}
#endif

728 729 730 731 732 733 734 735
static void stack_proc(void *arg)
{
	struct task_struct *from = current, *to = arg;

	to->thread.saved_task = from;
	switch_to(from, to, from);
}

J
Jeff Dike 已提交
736 737
/*
 * Mconsole stack trace
738 739 740 741
 *  Added by Allan Graves, Jeff Dike
 *  Dumps a stacks registers to the linux console.
 *  Usage stack <pid>.
 */
J
Jeff Dike 已提交
742
void mconsole_stack(struct mc_request *req)
743
{
744 745
	char *ptr = req->request.data;
	int pid_requested= -1;
746 747
	struct task_struct *to = NULL;

J
Jeff Dike 已提交
748 749
	/*
	 * Would be nice:
750
	 * 1) Send showregs output to mconsole.
751 752 753
	 * 2) Add a way to stack dump all pids.
	 */

754
	ptr += strlen("stack");
J
Jeff Dike 已提交
755 756
	while (isspace(*ptr))
		ptr++;
757

J
Jeff Dike 已提交
758 759 760
	/*
	 * Should really check for multiple pids or reject bad args here
	 */
761
	/* What do the arguments in mconsole_reply mean? */
J
Jeff Dike 已提交
762
	if (sscanf(ptr, "%d", &pid_requested) == 0) {
763 764 765
		mconsole_reply(req, "Please specify a pid", 1, 0);
		return;
	}
766

767
	to = find_task_by_pid(pid_requested);
J
Jeff Dike 已提交
768
	if ((to == NULL) || (pid_requested == 0)) {
769 770 771
		mconsole_reply(req, "Couldn't find that pid", 1, 0);
		return;
	}
772
	with_console(req, stack_proc, to);
773 774
}

J
Jeff Dike 已提交
775 776
/*
 * Changed by mconsole_setup, which is __setup, and called before SMP is
L
Linus Torvalds 已提交
777 778
 * active.
 */
J
Jeff Dike 已提交
779
static char *notify_socket = NULL;
L
Linus Torvalds 已提交
780

J
Jeff Dike 已提交
781
static int __init mconsole_init(void)
L
Linus Torvalds 已提交
782 783 784 785 786 787
{
	/* long to avoid size mismatch warnings from gcc */
	long sock;
	int err;
	char file[256];

J
Jeff Dike 已提交
788 789
	if (umid_file_name("mconsole", file, sizeof(file)))
		return -1;
L
Linus Torvalds 已提交
790 791 792
	snprintf(mconsole_socket_name, sizeof(file), "%s", file);

	sock = os_create_unix_socket(file, sizeof(file), 1);
J
Jeff Dike 已提交
793 794 795
	if (sock < 0) {
		printk(KERN_ERR "Failed to initialize management console\n");
		return 1;
L
Linus Torvalds 已提交
796
	}
J
Jeff Dike 已提交
797 798
	if (os_set_fd_block(sock, 0))
		goto out;
L
Linus Torvalds 已提交
799 800 801 802

	register_reboot_notifier(&reboot_notifier);

	err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
803
			     IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
L
Linus Torvalds 已提交
804
			     "mconsole", (void *)sock);
J
Jeff Dike 已提交
805 806
	if (err) {
		printk(KERN_ERR "Failed to get IRQ for management console\n");
J
Jeff Dike 已提交
807
		goto out;
L
Linus Torvalds 已提交
808 809
	}

J
Jeff Dike 已提交
810
	if (notify_socket != NULL) {
J
Jeff Dike 已提交
811
		notify_socket = kstrdup(notify_socket, GFP_KERNEL);
J
Jeff Dike 已提交
812
		if (notify_socket != NULL)
L
Linus Torvalds 已提交
813
			mconsole_notify(notify_socket, MCONSOLE_SOCKET,
J
Jeff Dike 已提交
814
					mconsole_socket_name,
L
Linus Torvalds 已提交
815 816 817 818 819
					strlen(mconsole_socket_name) + 1);
		else printk(KERN_ERR "mconsole_setup failed to strdup "
			    "string\n");
	}

J
Jeff Dike 已提交
820
	printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
L
Linus Torvalds 已提交
821
	       MCONSOLE_VERSION, mconsole_socket_name);
J
Jeff Dike 已提交
822
	return 0;
J
Jeff Dike 已提交
823 824 825 826

 out:
	os_close_file(sock);
	return 1;
L
Linus Torvalds 已提交
827 828 829 830 831 832 833 834 835 836
}

__initcall(mconsole_init);

static int write_proc_mconsole(struct file *file, const char __user *buffer,
			       unsigned long count, void *data)
{
	char *buf;

	buf = kmalloc(count + 1, GFP_KERNEL);
J
Jeff Dike 已提交
837 838
	if (buf == NULL)
		return -ENOMEM;
L
Linus Torvalds 已提交
839

J
Jeff Dike 已提交
840
	if (copy_from_user(buf, buffer, count)) {
L
Linus Torvalds 已提交
841 842 843 844 845 846 847 848 849
		count = -EFAULT;
		goto out;
	}

	buf[count] = '\0';

	mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
 out:
	kfree(buf);
J
Jeff Dike 已提交
850
	return count;
L
Linus Torvalds 已提交
851 852 853 854 855 856
}

static int create_proc_mconsole(void)
{
	struct proc_dir_entry *ent;

J
Jeff Dike 已提交
857 858
	if (notify_socket == NULL)
		return 0;
L
Linus Torvalds 已提交
859 860

	ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
J
Jeff Dike 已提交
861 862 863 864
	if (ent == NULL) {
		printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
		       "failed\n");
		return 0;
L
Linus Torvalds 已提交
865 866 867 868
	}

	ent->read_proc = NULL;
	ent->write_proc = write_proc_mconsole;
J
Jeff Dike 已提交
869
	return 0;
L
Linus Torvalds 已提交
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
}

static DEFINE_SPINLOCK(notify_spinlock);

void lock_notify(void)
{
	spin_lock(&notify_spinlock);
}

void unlock_notify(void)
{
	spin_unlock(&notify_spinlock);
}

__initcall(create_proc_mconsole);

J
Jeff Dike 已提交
886
#define NOTIFY "notify:"
L
Linus Torvalds 已提交
887 888 889

static int mconsole_setup(char *str)
{
J
Jeff Dike 已提交
890
	if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
L
Linus Torvalds 已提交
891 892 893 894
		str += strlen(NOTIFY);
		notify_socket = str;
	}
	else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
J
Jeff Dike 已提交
895
	return 1;
L
Linus Torvalds 已提交
896 897
}

J
Jeff Dike 已提交
898
__setup("mconsole=", mconsole_setup);
L
Linus Torvalds 已提交
899 900 901 902 903 904 905 906 907 908 909 910 911 912

__uml_help(mconsole_setup,
"mconsole=notify:<socket>\n"
"    Requests that the mconsole driver send a message to the named Unix\n"
"    socket containing the name of the mconsole socket.  This also serves\n"
"    to notify outside processes when UML has booted far enough to respond\n"
"    to mconsole requests.\n\n"
);

static int notify_panic(struct notifier_block *self, unsigned long unused1,
			void *ptr)
{
	char *message = ptr;

J
Jeff Dike 已提交
913 914
	if (notify_socket == NULL)
		return 0;
L
Linus Torvalds 已提交
915

J
Jeff Dike 已提交
916
	mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
L
Linus Torvalds 已提交
917
			strlen(message) + 1);
J
Jeff Dike 已提交
918
	return 0;
L
Linus Torvalds 已提交
919 920 921 922 923 924 925 926 927 928
}

static struct notifier_block panic_exit_notifier = {
	.notifier_call 		= notify_panic,
	.next 			= NULL,
	.priority 		= 1
};

static int add_notifier(void)
{
929 930
	atomic_notifier_chain_register(&panic_notifier_list,
			&panic_exit_notifier);
J
Jeff Dike 已提交
931
	return 0;
L
Linus Torvalds 已提交
932 933 934 935 936 937
}

__initcall(add_notifier);

char *mconsole_notify_socket(void)
{
J
Jeff Dike 已提交
938
	return notify_socket;
L
Linus Torvalds 已提交
939 940 941
}

EXPORT_SYMBOL(mconsole_notify_socket);