blob: 0f3c7d14a6e32977d1731a7c46daa51e08182534 [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];
Jeff Dike6f517d32006-01-06 00:19:04 -0800635
636static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700637 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800638{
639 struct list_head *ele;
640 int n;
641
Jeff Dikeae2587e2007-10-16 01:26:57 -0700642 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800643 return;
644
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700645 while (len > 0) {
646 n = min((size_t) len, ARRAY_SIZE(console_buf));
647 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800648 string += n;
649 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800650
Jeff Dikeae2587e2007-10-16 01:26:57 -0700651 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700652 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800653
Jeff Dikef92afe52006-09-29 01:58:52 -0700654 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700655 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800656 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800657 }
658}
659
660static struct console mc_console = { .name = "mc",
661 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800662 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800663 .index = -1 };
664
665static int mc_add_console(void)
666{
667 register_console(&mc_console);
668 return 0;
669}
670
671late_initcall(mc_add_console);
672
673static void with_console(struct mc_request *req, void (*proc)(void *),
674 void *arg)
675{
Jeff Dikef92afe52006-09-29 01:58:52 -0700676 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800677 unsigned long flags;
678
Jeff Dikef92afe52006-09-29 01:58:52 -0700679 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800680 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800681 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800682 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800683
684 (*proc)(arg);
685
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700686 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800687
Jeff Dike84f48d42007-02-10 01:44:01 -0800688 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800689 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800690 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800691}
692
Jeff Dike4111b022006-01-06 00:19:05 -0800693#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700694
695#include <linux/sysrq.h>
696
Jeff Dike4111b022006-01-06 00:19:05 -0800697static void sysrq_proc(void *arg)
698{
699 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100700 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800701}
702
703void mconsole_sysrq(struct mc_request *req)
704{
705 char *ptr = req->request.data;
706
707 ptr += strlen("sysrq");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700708 while (isspace(*ptr)) ptr++;
Jeff Dike4111b022006-01-06 00:19:05 -0800709
Jeff Dikeae2587e2007-10-16 01:26:57 -0700710 /*
711 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800712 * so in this case, we reply first.
713 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700714 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800715 mconsole_reply(req, "", 0, 0);
716
717 with_console(req, sysrq_proc, ptr);
718}
719#else
720void mconsole_sysrq(struct mc_request *req)
721{
722 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
723}
724#endif
725
Jeff Dike6f517d32006-01-06 00:19:04 -0800726static void stack_proc(void *arg)
727{
728 struct task_struct *from = current, *to = arg;
729
730 to->thread.saved_task = from;
731 switch_to(from, to, from);
732}
733
Jeff Dikeae2587e2007-10-16 01:26:57 -0700734/*
735 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700736 * Added by Allan Graves, Jeff Dike
737 * Dumps a stacks registers to the linux console.
738 * Usage stack <pid>.
739 */
Jeff Dike42fda662007-10-16 01:26:50 -0700740void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700741{
Jeff Dike3a331a52006-01-06 00:19:05 -0800742 char *ptr = req->request.data;
743 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800744 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700745 struct task_struct *to = NULL;
746
Jeff Dikeae2587e2007-10-16 01:26:57 -0700747 /*
748 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800749 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700750 * 2) Add a way to stack dump all pids.
751 */
752
Jeff Dike3a331a52006-01-06 00:19:05 -0800753 ptr += strlen("stack");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700754 while (isspace(*ptr))
755 ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700756
Jeff Dikeae2587e2007-10-16 01:26:57 -0700757 /*
758 * Should really check for multiple pids or reject bad args here
759 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800760 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700761 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800762 mconsole_reply(req, "Please specify a pid", 1, 0);
763 return;
764 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700765
Jeff Dike6f517d32006-01-06 00:19:04 -0800766 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700767
Jeff Dike6f517d32006-01-06 00:19:04 -0800768 to = find_task_by_pid(pid_requested);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700769 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800770 mconsole_reply(req, "Couldn't find that pid", 1, 0);
771 return;
772 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800773 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700774}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700775
Jeff Dikeae2587e2007-10-16 01:26:57 -0700776/*
777 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 * active.
779 */
Jeff Diked50084a2006-01-06 00:18:50 -0800780static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700782static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783{
784 /* long to avoid size mismatch warnings from gcc */
785 long sock;
786 int err;
787 char file[256];
788
Jeff Dikeae2587e2007-10-16 01:26:57 -0700789 if (umid_file_name("mconsole", file, sizeof(file)))
790 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
792
793 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700794 if (sock < 0) {
795 printk(KERN_ERR "Failed to initialize management console\n");
796 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 }
798
799 register_reboot_notifier(&reboot_notifier);
800
801 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700802 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700804 if (err) {
805 printk(KERN_ERR "Failed to get IRQ for management console\n");
806 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 }
808
Jeff Dikeae2587e2007-10-16 01:26:57 -0700809 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800810 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700811 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800813 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 strlen(mconsole_socket_name) + 1);
815 else printk(KERN_ERR "mconsole_setup failed to strdup "
816 "string\n");
817 }
818
Jeff Dikeae2587e2007-10-16 01:26:57 -0700819 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700821 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822}
823
824__initcall(mconsole_init);
825
826static int write_proc_mconsole(struct file *file, const char __user *buffer,
827 unsigned long count, void *data)
828{
829 char *buf;
830
831 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700832 if (buf == NULL)
833 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Jeff Dikeae2587e2007-10-16 01:26:57 -0700835 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 count = -EFAULT;
837 goto out;
838 }
839
840 buf[count] = '\0';
841
842 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
843 out:
844 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700845 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846}
847
848static int create_proc_mconsole(void)
849{
850 struct proc_dir_entry *ent;
851
Jeff Dikeae2587e2007-10-16 01:26:57 -0700852 if (notify_socket == NULL)
853 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
855 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700856 if (ent == NULL) {
857 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
858 "failed\n");
859 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 }
861
862 ent->read_proc = NULL;
863 ent->write_proc = write_proc_mconsole;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700864 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865}
866
867static DEFINE_SPINLOCK(notify_spinlock);
868
869void lock_notify(void)
870{
871 spin_lock(&notify_spinlock);
872}
873
874void unlock_notify(void)
875{
876 spin_unlock(&notify_spinlock);
877}
878
879__initcall(create_proc_mconsole);
880
Jeff Dike088bec42007-10-16 01:27:20 -0700881#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882
883static int mconsole_setup(char *str)
884{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700885 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 str += strlen(NOTIFY);
887 notify_socket = str;
888 }
889 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700890 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891}
892
Jeff Dike088bec42007-10-16 01:27:20 -0700893__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895__uml_help(mconsole_setup,
896"mconsole=notify:<socket>\n"
897" Requests that the mconsole driver send a message to the named Unix\n"
898" socket containing the name of the mconsole socket. This also serves\n"
899" to notify outside processes when UML has booted far enough to respond\n"
900" to mconsole requests.\n\n"
901);
902
903static int notify_panic(struct notifier_block *self, unsigned long unused1,
904 void *ptr)
905{
906 char *message = ptr;
907
Jeff Dikeae2587e2007-10-16 01:26:57 -0700908 if (notify_socket == NULL)
909 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Jeff Diked50084a2006-01-06 00:18:50 -0800911 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700913 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914}
915
916static struct notifier_block panic_exit_notifier = {
917 .notifier_call = notify_panic,
918 .next = NULL,
919 .priority = 1
920};
921
922static int add_notifier(void)
923{
Alan Sterne041c682006-03-27 01:16:30 -0800924 atomic_notifier_chain_register(&panic_notifier_list,
925 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700926 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927}
928
929__initcall(add_notifier);
930
931char *mconsole_notify_socket(void)
932{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700933 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934}
935
936EXPORT_SYMBOL(mconsole_notify_socket);