blob: 5f6dbb148218301cb22685c248534bcc84518389 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
Jeff Dikeae2587e2007-10-16 01:26:57 -07003 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Licensed under the GPL
5 */
6
Jeff Dikeae2587e2007-10-16 01:26:57 -07007#include "linux/console.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include "linux/ctype.h"
9#include "linux/interrupt.h"
Jeff Dike02dea082006-03-31 02:30:08 -080010#include "linux/list.h"
11#include "linux/mm.h"
Jeff Dikeae2587e2007-10-16 01:26:57 -070012#include "linux/module.h"
13#include "linux/notifier.h"
14#include "linux/reboot.h"
15#include "linux/proc_fs.h"
16#include "linux/slab.h"
17#include "linux/syscalls.h"
18#include "linux/utsname.h"
19#include "linux/workqueue.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include "asm/uaccess.h"
Jeff Dikeae2587e2007-10-16 01:26:57 -070021#include "init.h"
22#include "irq_kern.h"
23#include "irq_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include "mconsole.h"
26#include "mconsole_kern.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Jeff Diked50084a2006-01-06 00:18:50 -080029static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070030 unsigned long what, void *data)
31{
Jeff Dikeae2587e2007-10-16 01:26:57 -070032 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070033}
34
35
36static struct notifier_block reboot_notifier = {
37 .notifier_call = do_unlink_socket,
38 .priority = 0,
39};
40
Jeff Diked50084a2006-01-06 00:18:50 -080041/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 * locking, and the interrupt handler is safe because it can't interrupt
43 * itself and it can only happen on CPU 0.
44 */
45
Jeff Dike90107722006-01-06 00:18:54 -080046static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
David Howells6d5aefb2006-12-05 19:36:26 +000048static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049{
50 struct mconsole_entry *req;
51 unsigned long flags;
52
Jeff Dikeae2587e2007-10-16 01:26:57 -070053 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070054 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070055 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 list_del(&req->list);
57 local_irq_restore(flags);
58 req->request.cmd->handler(&req->request);
59 kfree(req);
60 }
61}
62
David Howells6d5aefb2006-12-05 19:36:26 +000063static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Al Viro7bea96f2006-10-08 22:49:34 +010065static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066{
67 /* long to avoid size mismatch warnings from gcc */
68 long fd;
69 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010070 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
72 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070073 while (mconsole_get_request(fd, &req)) {
74 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 (*req.cmd->handler)(&req);
76 else {
Jeff Dike60baa152006-04-10 22:53:28 -070077 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070078 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 mconsole_reply(&req, "Out of memory", 1, 0);
80 else {
81 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010082 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 list_add(&new->list, &mc_requests);
84 }
85 }
86 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070087 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 schedule_work(&mconsole_work);
89 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -070090 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091}
92
93void mconsole_version(struct mc_request *req)
94{
95 char version[256];
96
Serge E. Hallyne9ff3992006-10-02 02:18:11 -070097 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -070098 utsname()->nodename, utsname()->release, utsname()->version,
99 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 mconsole_reply(req, version, 0, 0);
101}
102
103void mconsole_log(struct mc_request *req)
104{
105 int len;
106 char *ptr = req->request.data;
107
108 ptr += strlen("log ");
109
110 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700111 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 mconsole_reply(req, "", 0, 0);
113}
114
115/* This is a more convoluted version of mconsole_proc, which has some stability
116 * problems; however, we need it fixed, because it is expected that UML users
117 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
118 * show the real procfs content, not the ones from hppfs.*/
119#if 0
120void mconsole_proc(struct mc_request *req)
121{
122 struct nameidata nd;
123 struct file_system_type *proc;
124 struct super_block *super;
125 struct file *file;
126 int n, err;
127 char *ptr = req->request.data, *buf;
128
129 ptr += strlen("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700130 while (isspace(*ptr)) ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132 proc = get_fs_type("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700133 if (proc == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 mconsole_reply(req, "procfs not registered", 1, 0);
135 goto out;
136 }
137
138 super = (*proc->get_sb)(proc, 0, NULL, NULL);
139 put_filesystem(proc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700140 if (super == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
142 goto out;
143 }
144 up_write(&super->s_umount);
145
146 nd.dentry = super->s_root;
147 nd.mnt = NULL;
148 nd.flags = O_RDONLY + 1;
149 nd.last_type = LAST_ROOT;
150
151 /* START: it was experienced that the stability problems are closed
152 * if commenting out these two calls + the below read cycle. To
153 * make UML crash again, it was enough to readd either one.*/
154 err = link_path_walk(ptr, &nd);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700155 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 mconsole_reply(req, "Failed to look up file", 1, 0);
157 goto out_kill;
158 }
159
160 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700161 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 mconsole_reply(req, "Failed to open file", 1, 0);
163 goto out_kill;
164 }
165 /*END*/
166
167 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700168 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
170 goto out_fput;
171 }
172
Jeff Dikeae2587e2007-10-16 01:26:57 -0700173 if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 do {
175 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
176 &file->f_pos);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700177 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 buf[n] = '\0';
179 mconsole_reply(req, buf, 0, (n > 0));
180 }
181 else {
182 mconsole_reply(req, "Read of file failed",
183 1, 0);
184 goto out_free;
185 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700186 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
188 else mconsole_reply(req, "", 0, 0);
189
190 out_free:
191 kfree(buf);
192 out_fput:
193 fput(file);
194 out_kill:
195 deactivate_super(super);
196 out: ;
197}
198#endif
199
200void mconsole_proc(struct mc_request *req)
201{
202 char path[64];
203 char *buf;
204 int len;
205 int fd;
206 int first_chunk = 1;
207 char *ptr = req->request.data;
208
209 ptr += strlen("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700210 while (isspace(*ptr))
211 ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 snprintf(path, sizeof(path), "/proc/%s", ptr);
213
214 fd = sys_open(path, 0, 0);
215 if (fd < 0) {
216 mconsole_reply(req, "Failed to open file", 1, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700217 printk(KERN_ERR "open %s: %d\n",path,fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 goto out;
219 }
220
221 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700222 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
224 goto out_close;
225 }
226
227 for (;;) {
228 len = sys_read(fd, buf, PAGE_SIZE-1);
229 if (len < 0) {
230 mconsole_reply(req, "Read of file failed", 1, 0);
231 goto out_free;
232 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700233 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 if (first_chunk) {
235 mconsole_reply(req, "\n", 0, 1);
236 first_chunk = 0;
237 }
238 if (len == PAGE_SIZE-1) {
239 buf[len] = '\0';
240 mconsole_reply(req, buf, 0, 1);
241 } else {
242 buf[len] = '\0';
243 mconsole_reply(req, buf, 0, 0);
244 break;
245 }
246 }
247
248 out_free:
249 kfree(buf);
250 out_close:
251 sys_close(fd);
252 out:
253 /* nothing */;
254}
255
256#define UML_MCONSOLE_HELPTEXT \
257"Commands: \n\
258 version - Get kernel version \n\
259 help - Print this message \n\
260 halt - Halt UML \n\
261 reboot - Reboot UML \n\
262 config <dev>=<config> - Add a new device to UML; \n\
263 same syntax as command line \n\
264 config <dev> - Query the configuration of a device \n\
265 remove <dev> - Remove a device from UML \n\
266 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800267 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 stop - pause the UML; it will do nothing until it receives a 'go' \n\
269 go - continue the UML after a 'stop' \n\
270 log <string> - make UML enter <string> into the kernel log\n\
271 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700272 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273"
274
275void mconsole_help(struct mc_request *req)
276{
277 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
278}
279
280void mconsole_halt(struct mc_request *req)
281{
282 mconsole_reply(req, "", 0, 0);
283 machine_halt();
284}
285
286void mconsole_reboot(struct mc_request *req)
287{
288 mconsole_reply(req, "", 0, 0);
289 machine_restart(NULL);
290}
291
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292void mconsole_cad(struct mc_request *req)
293{
294 mconsole_reply(req, "", 0, 0);
295 ctrl_alt_del();
296}
297
298void mconsole_go(struct mc_request *req)
299{
300 mconsole_reply(req, "Not stopped", 1, 0);
301}
302
303void mconsole_stop(struct mc_request *req)
304{
305 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
306 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100307 mconsole_reply(req, "stopped", 0, 0);
308 while (mconsole_get_request(req->originating_fd, req)) {
309 if (req->cmd->handler == mconsole_go)
310 break;
311 if (req->cmd->handler == mconsole_stop) {
312 mconsole_reply(req, "Already stopped", 1, 0);
313 continue;
314 }
315 if (req->cmd->handler == mconsole_sysrq) {
316 struct pt_regs *old_regs;
317 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
318 mconsole_sysrq(req);
319 set_irq_regs(old_regs);
320 continue;
321 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 (*req->cmd->handler)(req);
323 }
324 os_set_fd_block(req->originating_fd, 0);
325 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
326 mconsole_reply(req, "", 0, 0);
327}
328
Jeff Dike84f48d42007-02-10 01:44:01 -0800329static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800330static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
332void mconsole_register_dev(struct mc_device *new)
333{
Jeff Dike84f48d42007-02-10 01:44:01 -0800334 spin_lock(&mc_devices_lock);
335 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800337 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
340static struct mc_device *mconsole_find_dev(char *name)
341{
342 struct list_head *ele;
343 struct mc_device *dev;
344
Jeff Dikeae2587e2007-10-16 01:26:57 -0700345 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700347 if (!strncmp(name, dev->name, strlen(dev->name)))
348 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700350 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351}
352
Jeff Dike02dea082006-03-31 02:30:08 -0800353#define UNPLUGGED_PER_PAGE \
354 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
355
356struct unplugged_pages {
357 struct list_head list;
358 void *pages[UNPLUGGED_PER_PAGE];
359};
360
Jeff Dike84f48d42007-02-10 01:44:01 -0800361static DECLARE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800362static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800363static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800364static int unplug_index = UNPLUGGED_PER_PAGE;
365
Jeff Dikef28169d2007-02-10 01:43:53 -0800366static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800367{
368 unsigned long long diff;
369 int err = -EINVAL, i, add;
370 char *ret;
371
Jeff Dikeae2587e2007-10-16 01:26:57 -0700372 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800373 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800374 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800375 }
Jeff Dike02dea082006-03-31 02:30:08 -0800376
377 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700378 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800379 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700380 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800381 add = 1;
382 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800383 else {
384 *error_out = "Expected increment to start with '-' or '+'";
385 goto out;
386 }
Jeff Dike02dea082006-03-31 02:30:08 -0800387
388 str++;
389 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700390 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800391 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800392 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800393 }
Jeff Dike02dea082006-03-31 02:30:08 -0800394
395 diff /= PAGE_SIZE;
396
Jeff Dike84f48d42007-02-10 01:44:01 -0800397 down(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700398 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800399 struct unplugged_pages *unplugged;
400 void *addr;
401
Jeff Dikeae2587e2007-10-16 01:26:57 -0700402 if (add) {
403 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800404 break;
405
406 unplugged = list_entry(unplugged_pages.next,
407 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700408 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800409 addr = unplugged->pages[--unplug_index];
410 else {
411 list_del(&unplugged->list);
412 addr = unplugged;
413 unplug_index = UNPLUGGED_PER_PAGE;
414 }
415
416 free_page((unsigned long) addr);
417 unplugged_pages_count--;
418 }
419 else {
420 struct page *page;
421
422 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700423 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800424 break;
425
426 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700427 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800428 list_add(&unplugged->list, &unplugged_pages);
429 unplug_index = 0;
430 }
431 else {
432 struct list_head *entry = unplugged_pages.next;
433 addr = unplugged;
434
435 unplugged = list_entry(entry,
436 struct unplugged_pages,
437 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800438 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700439 if (err) {
440 printk(KERN_ERR "Failed to release "
441 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800442 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800443 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800444 }
445 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800446 }
447
448 unplugged_pages_count++;
449 }
450 }
451
452 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800453out_unlock:
454 up(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800455out:
456 return err;
457}
458
459static int mem_get_config(char *name, char *str, int size, char **error_out)
460{
461 char buf[sizeof("18446744073709551615")];
462 int len = 0;
463
464 sprintf(buf, "%ld", uml_physmem);
465 CONFIG_CHUNK(str, size, len, buf, 1);
466
467 return len;
468}
469
470static int mem_id(char **str, int *start_out, int *end_out)
471{
472 *start_out = 0;
473 *end_out = 0;
474
475 return 0;
476}
477
Jeff Dikef28169d2007-02-10 01:43:53 -0800478static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800479{
Jeff Dikef28169d2007-02-10 01:43:53 -0800480 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800481 return -EBUSY;
482}
483
484static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800485 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800486 .name = "mem",
487 .config = mem_config,
488 .get_config = mem_get_config,
489 .id = mem_id,
490 .remove = mem_remove,
491};
492
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700493static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800494{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700495 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800496 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700497 else printk(KERN_ERR "Can't release memory to the host - memory "
498 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800499 return 0;
500}
501
502__initcall(mem_mc_init);
503
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504#define CONFIG_BUF_SIZE 64
505
Jeff Diked50084a2006-01-06 00:18:50 -0800506static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 char **),
508 struct mc_request *req, char *name)
509{
510 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
511 int n, size;
512
Jeff Dikeae2587e2007-10-16 01:26:57 -0700513 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 mconsole_reply(req, "No get_config routine defined", 1, 0);
515 return;
516 }
517
518 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700519 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 buf = default_buf;
521
Jeff Dikeae2587e2007-10-16 01:26:57 -0700522 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700524 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 mconsole_reply(req, error, 1, 0);
526 goto out;
527 }
528
Jeff Dikeae2587e2007-10-16 01:26:57 -0700529 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 mconsole_reply(req, buf, 0, 0);
531 goto out;
532 }
533
Jeff Dikeae2587e2007-10-16 01:26:57 -0700534 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 kfree(buf);
536
537 size = n;
538 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700539 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
541 return;
542 }
543 }
544 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700545 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547}
548
549void mconsole_config(struct mc_request *req)
550{
551 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800552 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 int err;
554
555 ptr += strlen("config");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700556 while (isspace(*ptr))
557 ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700559 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 mconsole_reply(req, "Bad configuration option", 1, 0);
561 return;
562 }
563
564 name = &ptr[strlen(dev->name)];
565 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700566 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 ptr++;
568
Jeff Dikeae2587e2007-10-16 01:26:57 -0700569 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800570 err = (*dev->config)(name, &error_string);
571 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 }
573 else mconsole_get_config(dev->get_config, req, name);
574}
575
576void mconsole_remove(struct mc_request *req)
577{
Jeff Diked50084a2006-01-06 00:18:50 -0800578 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700579 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800580 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700581 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
583 ptr += strlen("remove");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700584 while (isspace(*ptr)) ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700586 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 mconsole_reply(req, "Bad remove option", 1, 0);
588 return;
589 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700590
Jeff Dike3a331a52006-01-06 00:19:05 -0800591 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700592
Jeff Dike3a331a52006-01-06 00:19:05 -0800593 err = 1;
594 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700595 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800596 err_msg = "Couldn't parse device number";
597 goto out;
598 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700599 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800600 sprintf(error, "Invalid device number - must be between "
601 "%d and %d", start, end);
602 err_msg = error;
603 goto out;
604 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700605
Jeff Dikef28169d2007-02-10 01:43:53 -0800606 err_msg = NULL;
607 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700608 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700609 case 0:
610 err_msg = "";
611 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800612 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700613 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800614 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800615 break;
616 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700617 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800618 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800619 break;
620 default:
621 break;
622 }
623out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700624 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625}
626
Jeff Dikef92afe52006-09-29 01:58:52 -0700627struct mconsole_output {
628 struct list_head list;
629 struct mc_request *req;
630};
631
Jeff Dike84f48d42007-02-10 01:44:01 -0800632static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800633static LIST_HEAD(clients);
634static char console_buf[MCONSOLE_MAX_DATA];
635static int console_index = 0;
636
637static void console_write(struct console *console, const char *string,
638 unsigned len)
639{
640 struct list_head *ele;
641 int n;
642
Jeff Dikeae2587e2007-10-16 01:26:57 -0700643 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800644 return;
645
Jeff Dikeae2587e2007-10-16 01:26:57 -0700646 while (1) {
Paolo 'Blaisorblade' Giarrusso6dad2d32006-04-10 22:53:31 -0700647 n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index);
Jeff Dike6f517d32006-01-06 00:19:04 -0800648 strncpy(&console_buf[console_index], string, n);
649 console_index += n;
650 string += n;
651 len -= n;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700652 if (len == 0)
Jeff Dike6f517d32006-01-06 00:19:04 -0800653 return;
654
Jeff Dikeae2587e2007-10-16 01:26:57 -0700655 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700656 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800657
Jeff Dikef92afe52006-09-29 01:58:52 -0700658 entry = list_entry(ele, struct mconsole_output, list);
659 mconsole_reply_len(entry->req, console_buf,
Jeff Dike6f517d32006-01-06 00:19:04 -0800660 console_index, 0, 1);
661 }
662
663 console_index = 0;
664 }
665}
666
667static struct console mc_console = { .name = "mc",
668 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800669 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800670 .index = -1 };
671
672static int mc_add_console(void)
673{
674 register_console(&mc_console);
675 return 0;
676}
677
678late_initcall(mc_add_console);
679
680static void with_console(struct mc_request *req, void (*proc)(void *),
681 void *arg)
682{
Jeff Dikef92afe52006-09-29 01:58:52 -0700683 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800684 unsigned long flags;
685
Jeff Dikef92afe52006-09-29 01:58:52 -0700686 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800687 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800688 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800689 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800690
691 (*proc)(arg);
692
693 mconsole_reply_len(req, console_buf, console_index, 0, 0);
694 console_index = 0;
695
Jeff Dike84f48d42007-02-10 01:44:01 -0800696 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800697 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800698 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800699}
700
Jeff Dike4111b022006-01-06 00:19:05 -0800701#ifdef CONFIG_MAGIC_SYSRQ
702static void sysrq_proc(void *arg)
703{
704 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100705 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800706}
707
708void mconsole_sysrq(struct mc_request *req)
709{
710 char *ptr = req->request.data;
711
712 ptr += strlen("sysrq");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700713 while (isspace(*ptr)) ptr++;
Jeff Dike4111b022006-01-06 00:19:05 -0800714
Jeff Dikeae2587e2007-10-16 01:26:57 -0700715 /*
716 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800717 * so in this case, we reply first.
718 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700719 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800720 mconsole_reply(req, "", 0, 0);
721
722 with_console(req, sysrq_proc, ptr);
723}
724#else
725void mconsole_sysrq(struct mc_request *req)
726{
727 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
728}
729#endif
730
Jeff Dike6f517d32006-01-06 00:19:04 -0800731static void stack_proc(void *arg)
732{
733 struct task_struct *from = current, *to = arg;
734
735 to->thread.saved_task = from;
736 switch_to(from, to, from);
737}
738
Jeff Dikeae2587e2007-10-16 01:26:57 -0700739/*
740 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700741 * Added by Allan Graves, Jeff Dike
742 * Dumps a stacks registers to the linux console.
743 * Usage stack <pid>.
744 */
Jeff Dike42fda662007-10-16 01:26:50 -0700745void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700746{
Jeff Dike3a331a52006-01-06 00:19:05 -0800747 char *ptr = req->request.data;
748 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800749 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700750 struct task_struct *to = NULL;
751
Jeff Dikeae2587e2007-10-16 01:26:57 -0700752 /*
753 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800754 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700755 * 2) Add a way to stack dump all pids.
756 */
757
Jeff Dike3a331a52006-01-06 00:19:05 -0800758 ptr += strlen("stack");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700759 while (isspace(*ptr))
760 ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700761
Jeff Dikeae2587e2007-10-16 01:26:57 -0700762 /*
763 * Should really check for multiple pids or reject bad args here
764 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800765 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700766 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800767 mconsole_reply(req, "Please specify a pid", 1, 0);
768 return;
769 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700770
Jeff Dike6f517d32006-01-06 00:19:04 -0800771 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700772
Jeff Dike6f517d32006-01-06 00:19:04 -0800773 to = find_task_by_pid(pid_requested);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700774 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800775 mconsole_reply(req, "Couldn't find that pid", 1, 0);
776 return;
777 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800778 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700779}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700780
Jeff Dikeae2587e2007-10-16 01:26:57 -0700781/*
782 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 * active.
784 */
Jeff Diked50084a2006-01-06 00:18:50 -0800785static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700787static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
789 /* long to avoid size mismatch warnings from gcc */
790 long sock;
791 int err;
792 char file[256];
793
Jeff Dikeae2587e2007-10-16 01:26:57 -0700794 if (umid_file_name("mconsole", file, sizeof(file)))
795 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
797
798 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700799 if (sock < 0) {
800 printk(KERN_ERR "Failed to initialize management console\n");
801 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 }
803
804 register_reboot_notifier(&reboot_notifier);
805
806 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700807 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700809 if (err) {
810 printk(KERN_ERR "Failed to get IRQ for management console\n");
811 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 }
813
Jeff Dikeae2587e2007-10-16 01:26:57 -0700814 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800815 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700816 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800818 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 strlen(mconsole_socket_name) + 1);
820 else printk(KERN_ERR "mconsole_setup failed to strdup "
821 "string\n");
822 }
823
Jeff Dikeae2587e2007-10-16 01:26:57 -0700824 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700826 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827}
828
829__initcall(mconsole_init);
830
831static int write_proc_mconsole(struct file *file, const char __user *buffer,
832 unsigned long count, void *data)
833{
834 char *buf;
835
836 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700837 if (buf == NULL)
838 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Jeff Dikeae2587e2007-10-16 01:26:57 -0700840 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 count = -EFAULT;
842 goto out;
843 }
844
845 buf[count] = '\0';
846
847 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
848 out:
849 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700850 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851}
852
853static int create_proc_mconsole(void)
854{
855 struct proc_dir_entry *ent;
856
Jeff Dikeae2587e2007-10-16 01:26:57 -0700857 if (notify_socket == NULL)
858 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
860 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700861 if (ent == NULL) {
862 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
863 "failed\n");
864 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 }
866
867 ent->read_proc = NULL;
868 ent->write_proc = write_proc_mconsole;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700869 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870}
871
872static DEFINE_SPINLOCK(notify_spinlock);
873
874void lock_notify(void)
875{
876 spin_lock(&notify_spinlock);
877}
878
879void unlock_notify(void)
880{
881 spin_unlock(&notify_spinlock);
882}
883
884__initcall(create_proc_mconsole);
885
886#define NOTIFY "=notify:"
887
888static int mconsole_setup(char *str)
889{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700890 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 str += strlen(NOTIFY);
892 notify_socket = str;
893 }
894 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700895 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896}
897
898__setup("mconsole", mconsole_setup);
899
900__uml_help(mconsole_setup,
901"mconsole=notify:<socket>\n"
902" Requests that the mconsole driver send a message to the named Unix\n"
903" socket containing the name of the mconsole socket. This also serves\n"
904" to notify outside processes when UML has booted far enough to respond\n"
905" to mconsole requests.\n\n"
906);
907
908static int notify_panic(struct notifier_block *self, unsigned long unused1,
909 void *ptr)
910{
911 char *message = ptr;
912
Jeff Dikeae2587e2007-10-16 01:26:57 -0700913 if (notify_socket == NULL)
914 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Jeff Diked50084a2006-01-06 00:18:50 -0800916 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700918 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919}
920
921static struct notifier_block panic_exit_notifier = {
922 .notifier_call = notify_panic,
923 .next = NULL,
924 .priority = 1
925};
926
927static int add_notifier(void)
928{
Alan Sterne041c682006-03-27 01:16:30 -0800929 atomic_notifier_chain_register(&panic_notifier_list,
930 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700931 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932}
933
934__initcall(add_notifier);
935
936char *mconsole_notify_socket(void)
937{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700938 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939}
940
941EXPORT_SYMBOL(mconsole_notify_socket);