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

7 8
#include <linux/console.h>
#include <linux/ctype.h>
9
#include <linux/string.h>
10 11 12 13 14 15 16 17 18 19
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/utsname.h>
20 21
#include <linux/socket.h>
#include <linux/un.h>
22 23 24 25
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>

J
Jeff Dike 已提交
26 27 28
#include "init.h"
#include "irq_kern.h"
#include "irq_user.h"
L
Linus Torvalds 已提交
29 30 31 32 33
#include "kern_util.h"
#include "mconsole.h"
#include "mconsole_kern.h"
#include "os.h"

J
Jeff Dike 已提交
34
static int do_unlink_socket(struct notifier_block *notifier,
L
Linus Torvalds 已提交
35 36
			    unsigned long what, void *data)
{
J
Jeff Dike 已提交
37
	return mconsole_unlink_socket();
L
Linus Torvalds 已提交
38 39 40 41 42 43 44 45
}


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

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

51
static LIST_HEAD(mc_requests);
L
Linus Torvalds 已提交
52

53
static void mc_work_proc(struct work_struct *unused)
L
Linus Torvalds 已提交
54 55 56 57
{
	struct mconsole_entry *req;
	unsigned long flags;

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

68
static DECLARE_WORK(mconsole_work, mc_work_proc);
L
Linus Torvalds 已提交
69

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

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

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

102
	sprintf(version, "%s %s %s %s %s", utsname()->sysname,
J
Jeff Dike 已提交
103 104
		utsname()->nodename, utsname()->release, utsname()->version,
		utsname()->machine);
L
Linus Torvalds 已提交
105 106 107 108 109 110 111 112 113 114 115
	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 已提交
116
	printk(KERN_WARNING "%.*s", len, ptr);
L
Linus Torvalds 已提交
117 118 119 120 121 122 123 124 125 126 127
	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;
A
Al Viro 已提交
128
	struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt;
L
Linus Torvalds 已提交
129 130 131
	struct file *file;
	int n, err;
	char *ptr = req->request.data, *buf;
A
Al Viro 已提交
132
	mm_segment_t old_fs = get_fs();
L
Linus Torvalds 已提交
133 134

	ptr += strlen("proc");
135
	ptr = skip_spaces(ptr);
L
Linus Torvalds 已提交
136

A
Al Viro 已提交
137 138 139
	err = vfs_path_lookup(mnt->mnt_root, mnt, ptr, LOOKUP_FOLLOW, &nd);
	if (err) {
		mconsole_reply(req, "Failed to look up file", 1, 0);
L
Linus Torvalds 已提交
140 141 142
		goto out;
	}

A
Al Viro 已提交
143 144 145 146
	err = may_open(&nd.path, MAY_READ, FMODE_READ);
	if (result) {
		mconsole_reply(req, "Failed to open file", 1, 0);
		path_put(&nd.path);
L
Linus Torvalds 已提交
147 148 149
		goto out;
	}

150 151
	file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
			   current_cred());
A
Al Viro 已提交
152
	err = PTR_ERR(file);
J
Jeff Dike 已提交
153
	if (IS_ERR(file)) {
L
Linus Torvalds 已提交
154
		mconsole_reply(req, "Failed to open file", 1, 0);
A
Al Viro 已提交
155 156
		path_put(&nd.path);
		goto out;
L
Linus Torvalds 已提交
157 158 159
	}

	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
J
Jeff Dike 已提交
160
	if (buf == NULL) {
L
Linus Torvalds 已提交
161 162 163 164
		mconsole_reply(req, "Failed to allocate buffer", 1, 0);
		goto out_fput;
	}

A
Al Viro 已提交
165
	if (file->f_op->read) {
L
Linus Torvalds 已提交
166
		do {
A
Al Viro 已提交
167 168 169 170 171
			loff_t pos;
			set_fs(KERNEL_DS);
			n = vfs_read(file, buf, PAGE_SIZE - 1, &pos);
			file_pos_write(file, pos);
			set_fs(old_fs);
J
Jeff Dike 已提交
172
			if (n >= 0) {
L
Linus Torvalds 已提交
173 174 175 176 177 178 179 180
				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 已提交
181
		} while (n > 0);
L
Linus Torvalds 已提交
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
	}
	else mconsole_reply(req, "", 0, 0);

 out_free:
	kfree(buf);
 out_fput:
	fput(file);
 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");
203
	ptr = skip_spaces(ptr);
L
Linus Torvalds 已提交
204 205 206 207 208
	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 已提交
209
		printk(KERN_ERR "open %s: %d\n",path,fd);
L
Linus Torvalds 已提交
210 211 212 213
		goto out;
	}

	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
J
Jeff Dike 已提交
214
	if (buf == NULL) {
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223 224
		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 已提交
225
		/* Begin the file content on his own line. */
L
Linus Torvalds 已提交
226 227 228 229 230 231 232 233 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
		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 已提交
259
    cad - invoke the Ctrl-Alt-Del handler \n\
L
Linus Torvalds 已提交
260 261 262 263
    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\
264
    stack <pid> - returns the stack of the specified pid\n\
L
Linus Torvalds 已提交
265 266 267 268 269 270 271 272 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
"

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 已提交
299
	mconsole_reply(req, "stopped", 0, 0);
300 301 302
	for (;;) {
		if (!mconsole_get_request(req->originating_fd, req))
			continue;
A
Al Viro 已提交
303 304 305 306 307 308 309 310 311 312 313 314 315
		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 已提交
316 317 318 319 320 321 322
		(*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 已提交
323
static DEFINE_SPINLOCK(mc_devices_lock);
324
static LIST_HEAD(mconsole_devices);
L
Linus Torvalds 已提交
325 326 327

void mconsole_register_dev(struct mc_device *new)
{
J
Jeff Dike 已提交
328 329
	spin_lock(&mc_devices_lock);
	BUG_ON(!list_empty(&new->list));
L
Linus Torvalds 已提交
330
	list_add(&new->list, &mconsole_devices);
J
Jeff Dike 已提交
331
	spin_unlock(&mc_devices_lock);
L
Linus Torvalds 已提交
332 333 334 335 336 337 338
}

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

J
Jeff Dike 已提交
339
	list_for_each(ele, &mconsole_devices) {
L
Linus Torvalds 已提交
340
		dev = list_entry(ele, struct mc_device, list);
J
Jeff Dike 已提交
341 342
		if (!strncmp(name, dev->name, strlen(dev->name)))
			return dev;
L
Linus Torvalds 已提交
343
	}
J
Jeff Dike 已提交
344
	return NULL;
L
Linus Torvalds 已提交
345 346
}

J
Jeff Dike 已提交
347 348 349 350 351 352 353 354
#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];
};

D
Daniel Walker 已提交
355
static DEFINE_MUTEX(plug_mem_mutex);
J
Jeff Dike 已提交
356
static unsigned long long unplugged_pages_count = 0;
357
static LIST_HEAD(unplugged_pages);
J
Jeff Dike 已提交
358 359
static int unplug_index = UNPLUGGED_PER_PAGE;

360
static int mem_config(char *str, char **error_out)
J
Jeff Dike 已提交
361 362 363 364 365
{
	unsigned long long diff;
	int err = -EINVAL, i, add;
	char *ret;

J
Jeff Dike 已提交
366
	if (str[0] != '=') {
367
		*error_out = "Expected '=' after 'mem'";
J
Jeff Dike 已提交
368
		goto out;
369
	}
J
Jeff Dike 已提交
370 371

	str++;
J
Jeff Dike 已提交
372
	if (str[0] == '-')
J
Jeff Dike 已提交
373
		add = 0;
J
Jeff Dike 已提交
374
	else if (str[0] == '+') {
J
Jeff Dike 已提交
375 376
		add = 1;
	}
377 378 379 380
	else {
		*error_out = "Expected increment to start with '-' or '+'";
		goto out;
	}
J
Jeff Dike 已提交
381 382 383

	str++;
	diff = memparse(str, &ret);
J
Jeff Dike 已提交
384
	if (*ret != '\0') {
385
		*error_out = "Failed to parse memory increment";
J
Jeff Dike 已提交
386
		goto out;
387
	}
J
Jeff Dike 已提交
388 389 390

	diff /= PAGE_SIZE;

D
Daniel Walker 已提交
391
	mutex_lock(&plug_mem_mutex);
J
Jeff Dike 已提交
392
	for (i = 0; i < diff; i++) {
J
Jeff Dike 已提交
393 394 395
		struct unplugged_pages *unplugged;
		void *addr;

J
Jeff Dike 已提交
396 397
		if (add) {
			if (list_empty(&unplugged_pages))
J
Jeff Dike 已提交
398 399 400 401
				break;

			unplugged = list_entry(unplugged_pages.next,
					       struct unplugged_pages, list);
J
Jeff Dike 已提交
402
			if (unplug_index > 0)
J
Jeff Dike 已提交
403 404 405 406 407 408 409 410 411 412 413 414 415 416
				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 已提交
417
			if (page == NULL)
J
Jeff Dike 已提交
418 419 420
				break;

			unplugged = page_address(page);
J
Jeff Dike 已提交
421
			if (unplug_index == UNPLUGGED_PER_PAGE) {
J
Jeff Dike 已提交
422 423 424 425 426 427 428 429 430 431 432
				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 已提交
433 434 435
				if (err) {
					printk(KERN_ERR "Failed to release "
					       "memory - errno = %d\n", err);
436
					*error_out = "Failed to release memory";
J
Jeff Dike 已提交
437
					goto out_unlock;
438 439
				}
				unplugged->pages[unplug_index++] = addr;
J
Jeff Dike 已提交
440 441 442 443 444 445 446
			}

			unplugged_pages_count++;
		}
	}

	err = 0;
J
Jeff Dike 已提交
447
out_unlock:
D
Daniel Walker 已提交
448
	mutex_unlock(&plug_mem_mutex);
J
Jeff Dike 已提交
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
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;
}

472
static int mem_remove(int n, char **error_out)
J
Jeff Dike 已提交
473
{
474
	*error_out = "Memory doesn't support the remove operation";
J
Jeff Dike 已提交
475 476 477 478
	return -EBUSY;
}

static struct mc_device mem_mc = {
J
Jeff Dike 已提交
479
	.list		= LIST_HEAD_INIT(mem_mc.list),
J
Jeff Dike 已提交
480 481 482 483 484 485 486
	.name		= "mem",
	.config		= mem_config,
	.get_config	= mem_get_config,
	.id		= mem_id,
	.remove		= mem_remove,
};

J
Jeff Dike 已提交
487
static int __init mem_mc_init(void)
J
Jeff Dike 已提交
488
{
J
Jeff Dike 已提交
489
	if (can_drop_memory())
J
Jeff Dike 已提交
490
		mconsole_register_dev(&mem_mc);
J
Jeff Dike 已提交
491 492
	else printk(KERN_ERR "Can't release memory to the host - memory "
		    "hotplug won't be supported\n");
J
Jeff Dike 已提交
493 494 495 496 497
	return 0;
}

__initcall(mem_mc_init);

L
Linus Torvalds 已提交
498 499
#define CONFIG_BUF_SIZE 64

J
Jeff Dike 已提交
500
static void mconsole_get_config(int (*get_config)(char *, char *, int,
L
Linus Torvalds 已提交
501 502 503 504 505 506
						  char **),
				struct mc_request *req, char *name)
{
	char default_buf[CONFIG_BUF_SIZE], *error, *buf;
	int n, size;

J
Jeff Dike 已提交
507
	if (get_config == NULL) {
L
Linus Torvalds 已提交
508 509 510 511 512
		mconsole_reply(req, "No get_config routine defined", 1, 0);
		return;
	}

	error = NULL;
513
	size = ARRAY_SIZE(default_buf);
L
Linus Torvalds 已提交
514 515
	buf = default_buf;

J
Jeff Dike 已提交
516
	while (1) {
L
Linus Torvalds 已提交
517
		n = (*get_config)(name, buf, size, &error);
J
Jeff Dike 已提交
518
		if (error != NULL) {
L
Linus Torvalds 已提交
519 520 521 522
			mconsole_reply(req, error, 1, 0);
			goto out;
		}

J
Jeff Dike 已提交
523
		if (n <= size) {
L
Linus Torvalds 已提交
524 525 526 527
			mconsole_reply(req, buf, 0, 0);
			goto out;
		}

J
Jeff Dike 已提交
528
		if (buf != default_buf)
L
Linus Torvalds 已提交
529 530 531 532
			kfree(buf);

		size = n;
		buf = kmalloc(size, GFP_KERNEL);
J
Jeff Dike 已提交
533
		if (buf == NULL) {
L
Linus Torvalds 已提交
534 535 536 537 538
			mconsole_reply(req, "Failed to allocate buffer", 1, 0);
			return;
		}
	}
 out:
J
Jeff Dike 已提交
539
	if (buf != default_buf)
L
Linus Torvalds 已提交
540 541 542 543 544 545
		kfree(buf);
}

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

	ptr += strlen("config");
550
	ptr = skip_spaces(ptr);
L
Linus Torvalds 已提交
551
	dev = mconsole_find_dev(ptr);
J
Jeff Dike 已提交
552
	if (dev == NULL) {
L
Linus Torvalds 已提交
553 554 555 556 557 558
		mconsole_reply(req, "Bad configuration option", 1, 0);
		return;
	}

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

J
Jeff Dike 已提交
562
	if (*ptr == '=') {
563 564
		err = (*dev->config)(name, &error_string);
		mconsole_reply(req, error_string, err, 0);
L
Linus Torvalds 已提交
565 566 567 568 569 570
	}
	else mconsole_get_config(dev->get_config, req, name);
}

void mconsole_remove(struct mc_request *req)
{
J
Jeff Dike 已提交
571
	struct mc_device *dev;
J
Jeff Dike 已提交
572
	char *ptr = req->request.data, *err_msg = "";
573
	char error[256];
J
Jeff Dike 已提交
574
	int err, start, end, n;
L
Linus Torvalds 已提交
575 576

	ptr += strlen("remove");
577
	ptr = skip_spaces(ptr);
L
Linus Torvalds 已提交
578
	dev = mconsole_find_dev(ptr);
J
Jeff Dike 已提交
579
	if (dev == NULL) {
L
Linus Torvalds 已提交
580 581 582
		mconsole_reply(req, "Bad remove option", 1, 0);
		return;
	}
J
Jeff Dike 已提交
583

584 585 586 587
	ptr = &ptr[strlen(dev->name)];

	err = 1;
	n = (*dev->id)(&ptr, &start, &end);
J
Jeff Dike 已提交
588
	if (n < 0) {
589 590 591
		err_msg = "Couldn't parse device number";
		goto out;
	}
J
Jeff Dike 已提交
592
	else if ((n < start) || (n > end)) {
593 594 595 596 597
		sprintf(error, "Invalid device number - must be between "
			"%d and %d", start, end);
		err_msg = error;
		goto out;
	}
J
Jeff Dike 已提交
598

599 600
	err_msg = NULL;
	err = (*dev->remove)(n, &err_msg);
J
Jeff Dike 已提交
601
	switch(err) {
J
Jeff Dike 已提交
602 603 604
	case 0:
		err_msg = "";
		break;
605
	case -ENODEV:
J
Jeff Dike 已提交
606
		if (err_msg == NULL)
607
			err_msg = "Device doesn't exist";
608 609
		break;
	case -EBUSY:
J
Jeff Dike 已提交
610
		if (err_msg == NULL)
611
			err_msg = "Device is currently open";
612 613 614 615 616
		break;
	default:
		break;
	}
out:
J
Jeff Dike 已提交
617
	mconsole_reply(req, err_msg, err, 0);
L
Linus Torvalds 已提交
618 619
}

620 621 622 623 624
struct mconsole_output {
	struct list_head list;
	struct mc_request *req;
};

J
Jeff Dike 已提交
625
static DEFINE_SPINLOCK(client_lock);
626 627 628 629
static LIST_HEAD(clients);
static char console_buf[MCONSOLE_MAX_DATA];

static void console_write(struct console *console, const char *string,
J
Jeff Dike 已提交
630
			  unsigned int len)
631 632 633 634
{
	struct list_head *ele;
	int n;

J
Jeff Dike 已提交
635
	if (list_empty(&clients))
636 637
		return;

J
Jeff Dike 已提交
638 639 640
	while (len > 0) {
		n = min((size_t) len, ARRAY_SIZE(console_buf));
		strncpy(console_buf, string, n);
641 642 643
		string += n;
		len -= n;

J
Jeff Dike 已提交
644
		list_for_each(ele, &clients) {
645
			struct mconsole_output *entry;
646

647
			entry = list_entry(ele, struct mconsole_output, list);
J
Jeff Dike 已提交
648
			mconsole_reply_len(entry->req, console_buf, n, 0, 1);
649 650 651 652 653 654
		}
	}
}

static struct console mc_console = { .name	= "mc",
				     .write	= console_write,
655
				     .flags	= CON_ENABLED,
656 657 658 659 660 661 662 663 664 665 666 667 668
				     .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)
{
669
	struct mconsole_output entry;
670 671
	unsigned long flags;

672
	entry.req = req;
J
Jeff Dike 已提交
673
	spin_lock_irqsave(&client_lock, flags);
674
	list_add(&entry.list, &clients);
J
Jeff Dike 已提交
675
	spin_unlock_irqrestore(&client_lock, flags);
676 677 678

	(*proc)(arg);

J
Jeff Dike 已提交
679
	mconsole_reply_len(req, "", 0, 0, 0);
680

J
Jeff Dike 已提交
681
	spin_lock_irqsave(&client_lock, flags);
682
	list_del(&entry.list);
J
Jeff Dike 已提交
683
	spin_unlock_irqrestore(&client_lock, flags);
684 685
}

686
#ifdef CONFIG_MAGIC_SYSRQ
J
Jeff Dike 已提交
687 688 689

#include <linux/sysrq.h>

690 691 692
static void sysrq_proc(void *arg)
{
	char *op = arg;
A
Al Viro 已提交
693
	handle_sysrq(*op, NULL);
694 695 696 697 698 699 700
}

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

	ptr += strlen("sysrq");
701
	ptr = skip_spaces(ptr);
702

J
Jeff Dike 已提交
703 704
	/*
	 * With 'b', the system will shut down without a chance to reply,
705 706
	 * so in this case, we reply first.
	 */
J
Jeff Dike 已提交
707
	if (*ptr == 'b')
708 709 710 711 712 713 714 715 716 717 718
		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

719 720 721 722 723 724 725 726
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 已提交
727 728
/*
 * Mconsole stack trace
729 730 731 732
 *  Added by Allan Graves, Jeff Dike
 *  Dumps a stacks registers to the linux console.
 *  Usage stack <pid>.
 */
J
Jeff Dike 已提交
733
void mconsole_stack(struct mc_request *req)
734
{
735 736
	char *ptr = req->request.data;
	int pid_requested= -1;
737 738
	struct task_struct *to = NULL;

J
Jeff Dike 已提交
739 740
	/*
	 * Would be nice:
741
	 * 1) Send showregs output to mconsole.
742 743 744
	 * 2) Add a way to stack dump all pids.
	 */

745
	ptr += strlen("stack");
746
	ptr = skip_spaces(ptr);
747

J
Jeff Dike 已提交
748 749 750
	/*
	 * Should really check for multiple pids or reject bad args here
	 */
751
	/* What do the arguments in mconsole_reply mean? */
J
Jeff Dike 已提交
752
	if (sscanf(ptr, "%d", &pid_requested) == 0) {
753 754 755
		mconsole_reply(req, "Please specify a pid", 1, 0);
		return;
	}
756

757
	to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
J
Jeff Dike 已提交
758
	if ((to == NULL) || (pid_requested == 0)) {
759 760 761
		mconsole_reply(req, "Couldn't find that pid", 1, 0);
		return;
	}
762
	with_console(req, stack_proc, to);
763 764
}

J
Jeff Dike 已提交
765 766
/*
 * Changed by mconsole_setup, which is __setup, and called before SMP is
L
Linus Torvalds 已提交
767 768
 * active.
 */
J
Jeff Dike 已提交
769
static char *notify_socket = NULL;
L
Linus Torvalds 已提交
770

J
Jeff Dike 已提交
771
static int __init mconsole_init(void)
L
Linus Torvalds 已提交
772 773 774 775
{
	/* long to avoid size mismatch warnings from gcc */
	long sock;
	int err;
776
	char file[UNIX_PATH_MAX];
L
Linus Torvalds 已提交
777

J
Jeff Dike 已提交
778 779
	if (umid_file_name("mconsole", file, sizeof(file)))
		return -1;
L
Linus Torvalds 已提交
780 781 782
	snprintf(mconsole_socket_name, sizeof(file), "%s", file);

	sock = os_create_unix_socket(file, sizeof(file), 1);
J
Jeff Dike 已提交
783 784 785
	if (sock < 0) {
		printk(KERN_ERR "Failed to initialize management console\n");
		return 1;
L
Linus Torvalds 已提交
786
	}
J
Jeff Dike 已提交
787 788
	if (os_set_fd_block(sock, 0))
		goto out;
L
Linus Torvalds 已提交
789 790 791 792

	register_reboot_notifier(&reboot_notifier);

	err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
793
			     IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
L
Linus Torvalds 已提交
794
			     "mconsole", (void *)sock);
J
Jeff Dike 已提交
795 796
	if (err) {
		printk(KERN_ERR "Failed to get IRQ for management console\n");
J
Jeff Dike 已提交
797
		goto out;
L
Linus Torvalds 已提交
798 799
	}

J
Jeff Dike 已提交
800
	if (notify_socket != NULL) {
J
Jeff Dike 已提交
801
		notify_socket = kstrdup(notify_socket, GFP_KERNEL);
J
Jeff Dike 已提交
802
		if (notify_socket != NULL)
L
Linus Torvalds 已提交
803
			mconsole_notify(notify_socket, MCONSOLE_SOCKET,
J
Jeff Dike 已提交
804
					mconsole_socket_name,
L
Linus Torvalds 已提交
805 806 807 808 809
					strlen(mconsole_socket_name) + 1);
		else printk(KERN_ERR "mconsole_setup failed to strdup "
			    "string\n");
	}

J
Jeff Dike 已提交
810
	printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
L
Linus Torvalds 已提交
811
	       MCONSOLE_VERSION, mconsole_socket_name);
J
Jeff Dike 已提交
812
	return 0;
J
Jeff Dike 已提交
813 814 815 816

 out:
	os_close_file(sock);
	return 1;
L
Linus Torvalds 已提交
817 818 819 820
}

__initcall(mconsole_init);

821 822
static ssize_t mconsole_proc_write(struct file *file,
		const char __user *buffer, size_t count, loff_t *pos)
L
Linus Torvalds 已提交
823 824 825 826
{
	char *buf;

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

J
Jeff Dike 已提交
830
	if (copy_from_user(buf, buffer, count)) {
L
Linus Torvalds 已提交
831 832 833 834 835 836 837 838 839
		count = -EFAULT;
		goto out;
	}

	buf[count] = '\0';

	mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
 out:
	kfree(buf);
J
Jeff Dike 已提交
840
	return count;
L
Linus Torvalds 已提交
841 842
}

843 844 845 846 847
static const struct file_operations mconsole_proc_fops = {
	.owner		= THIS_MODULE,
	.write		= mconsole_proc_write,
};

L
Linus Torvalds 已提交
848 849 850 851
static int create_proc_mconsole(void)
{
	struct proc_dir_entry *ent;

J
Jeff Dike 已提交
852 853
	if (notify_socket == NULL)
		return 0;
L
Linus Torvalds 已提交
854

855
	ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
J
Jeff Dike 已提交
856 857 858 859
	if (ent == NULL) {
		printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
		       "failed\n");
		return 0;
L
Linus Torvalds 已提交
860
	}
J
Jeff Dike 已提交
861
	return 0;
L
Linus Torvalds 已提交
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
}

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 已提交
878
#define NOTIFY "notify:"
L
Linus Torvalds 已提交
879 880 881

static int mconsole_setup(char *str)
{
J
Jeff Dike 已提交
882
	if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
L
Linus Torvalds 已提交
883 884 885 886
		str += strlen(NOTIFY);
		notify_socket = str;
	}
	else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
J
Jeff Dike 已提交
887
	return 1;
L
Linus Torvalds 已提交
888 889
}

J
Jeff Dike 已提交
890
__setup("mconsole=", mconsole_setup);
L
Linus Torvalds 已提交
891 892 893 894 895 896 897 898 899 900 901 902 903 904

__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 已提交
905 906
	if (notify_socket == NULL)
		return 0;
L
Linus Torvalds 已提交
907

J
Jeff Dike 已提交
908
	mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
L
Linus Torvalds 已提交
909
			strlen(message) + 1);
J
Jeff Dike 已提交
910
	return 0;
L
Linus Torvalds 已提交
911 912 913 914 915 916 917 918 919 920
}

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

static int add_notifier(void)
{
921 922
	atomic_notifier_chain_register(&panic_notifier_list,
			&panic_exit_notifier);
J
Jeff Dike 已提交
923
	return 0;
L
Linus Torvalds 已提交
924 925 926 927 928 929
}

__initcall(add_notifier);

char *mconsole_notify_socket(void)
{
J
Jeff Dike 已提交
930
	return notify_socket;
L
Linus Torvalds 已提交
931 932 933
}

EXPORT_SYMBOL(mconsole_notify_socket);