blob: 51069245b79a207274547c51ce904206f5ffb939 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
Jeff Dike827b3f62008-02-04 22:31:29 -08003 * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Licensed under the GPL
5 */
6
Jeff Dike827b3f62008-02-04 22:31:29 -08007#include <linux/console.h>
8#include <linux/ctype.h>
André Goddard Rosae7d28602009-12-14 18:01:06 -08009#include <linux/string.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080010#include <linux/interrupt.h>
11#include <linux/list.h>
12#include <linux/mm.h>
13#include <linux/module.h>
14#include <linux/notifier.h>
15#include <linux/reboot.h>
16#include <linux/proc_fs.h>
17#include <linux/slab.h>
18#include <linux/syscalls.h>
19#include <linux/utsname.h>
Balbir Singh36137122008-12-09 13:14:07 -080020#include <linux/socket.h>
21#include <linux/un.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080022#include <linux/workqueue.h>
23#include <linux/mutex.h>
24#include <asm/uaccess.h>
25
Jeff Dikeae2587e2007-10-16 01:26:57 -070026#include "init.h"
27#include "irq_kern.h"
28#include "irq_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include "mconsole.h"
31#include "mconsole_kern.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Jeff Diked50084a2006-01-06 00:18:50 -080034static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 unsigned long what, void *data)
36{
Jeff Dikeae2587e2007-10-16 01:26:57 -070037 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070038}
39
40
41static struct notifier_block reboot_notifier = {
42 .notifier_call = do_unlink_socket,
43 .priority = 0,
44};
45
Jeff Diked50084a2006-01-06 00:18:50 -080046/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 * locking, and the interrupt handler is safe because it can't interrupt
48 * itself and it can only happen on CPU 0.
49 */
50
Jeff Dike90107722006-01-06 00:18:54 -080051static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
David Howells6d5aefb2006-12-05 19:36:26 +000053static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054{
55 struct mconsole_entry *req;
56 unsigned long flags;
57
Jeff Dikeae2587e2007-10-16 01:26:57 -070058 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070059 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070060 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 list_del(&req->list);
62 local_irq_restore(flags);
63 req->request.cmd->handler(&req->request);
64 kfree(req);
65 }
66}
67
David Howells6d5aefb2006-12-05 19:36:26 +000068static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
Al Viro7bea96f2006-10-08 22:49:34 +010070static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
72 /* long to avoid size mismatch warnings from gcc */
73 long fd;
74 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010075 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070078 while (mconsole_get_request(fd, &req)) {
79 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 (*req.cmd->handler)(&req);
81 else {
Jeff Dike60baa152006-04-10 22:53:28 -070082 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070083 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 mconsole_reply(&req, "Out of memory", 1, 0);
85 else {
86 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010087 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 list_add(&new->list, &mc_requests);
89 }
90 }
91 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070092 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 schedule_work(&mconsole_work);
94 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -070095 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
98void mconsole_version(struct mc_request *req)
99{
100 char version[256];
101
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700102 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -0700103 utsname()->nodename, utsname()->release, utsname()->version,
104 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 mconsole_reply(req, version, 0, 0);
106}
107
108void mconsole_log(struct mc_request *req)
109{
110 int len;
111 char *ptr = req->request.data;
112
113 ptr += strlen("log ");
114
115 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700116 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 mconsole_reply(req, "", 0, 0);
118}
119
120/* This is a more convoluted version of mconsole_proc, which has some stability
121 * problems; however, we need it fixed, because it is expected that UML users
122 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
123 * show the real procfs content, not the ones from hppfs.*/
124#if 0
125void mconsole_proc(struct mc_request *req)
126{
127 struct nameidata nd;
128 struct file_system_type *proc;
129 struct super_block *super;
130 struct file *file;
131 int n, err;
132 char *ptr = req->request.data, *buf;
133
134 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800135 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137 proc = get_fs_type("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700138 if (proc == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 mconsole_reply(req, "procfs not registered", 1, 0);
140 goto out;
141 }
142
143 super = (*proc->get_sb)(proc, 0, NULL, NULL);
144 put_filesystem(proc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700145 if (super == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
147 goto out;
148 }
149 up_write(&super->s_umount);
150
Jan Blunck4ac91372008-02-14 19:34:32 -0800151 nd.path.dentry = super->s_root;
152 nd.path.mnt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 nd.flags = O_RDONLY + 1;
154 nd.last_type = LAST_ROOT;
155
156 /* START: it was experienced that the stability problems are closed
157 * if commenting out these two calls + the below read cycle. To
158 * make UML crash again, it was enough to readd either one.*/
159 err = link_path_walk(ptr, &nd);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700160 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 mconsole_reply(req, "Failed to look up file", 1, 0);
162 goto out_kill;
163 }
164
David Howells745ca242008-11-14 10:39:22 +1100165 file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
166 current_cred());
Jeff Dikeae2587e2007-10-16 01:26:57 -0700167 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 mconsole_reply(req, "Failed to open file", 1, 0);
169 goto out_kill;
170 }
171 /*END*/
172
173 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700174 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
176 goto out_fput;
177 }
178
Jeff Dikeae2587e2007-10-16 01:26:57 -0700179 if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 do {
181 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
182 &file->f_pos);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700183 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 buf[n] = '\0';
185 mconsole_reply(req, buf, 0, (n > 0));
186 }
187 else {
188 mconsole_reply(req, "Read of file failed",
189 1, 0);
190 goto out_free;
191 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700192 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 }
194 else mconsole_reply(req, "", 0, 0);
195
196 out_free:
197 kfree(buf);
198 out_fput:
199 fput(file);
200 out_kill:
201 deactivate_super(super);
202 out: ;
203}
204#endif
205
206void mconsole_proc(struct mc_request *req)
207{
208 char path[64];
209 char *buf;
210 int len;
211 int fd;
212 int first_chunk = 1;
213 char *ptr = req->request.data;
214
215 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800216 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 snprintf(path, sizeof(path), "/proc/%s", ptr);
218
219 fd = sys_open(path, 0, 0);
220 if (fd < 0) {
221 mconsole_reply(req, "Failed to open file", 1, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700222 printk(KERN_ERR "open %s: %d\n",path,fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 goto out;
224 }
225
226 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700227 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
229 goto out_close;
230 }
231
232 for (;;) {
233 len = sys_read(fd, buf, PAGE_SIZE-1);
234 if (len < 0) {
235 mconsole_reply(req, "Read of file failed", 1, 0);
236 goto out_free;
237 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700238 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 if (first_chunk) {
240 mconsole_reply(req, "\n", 0, 1);
241 first_chunk = 0;
242 }
243 if (len == PAGE_SIZE-1) {
244 buf[len] = '\0';
245 mconsole_reply(req, buf, 0, 1);
246 } else {
247 buf[len] = '\0';
248 mconsole_reply(req, buf, 0, 0);
249 break;
250 }
251 }
252
253 out_free:
254 kfree(buf);
255 out_close:
256 sys_close(fd);
257 out:
258 /* nothing */;
259}
260
261#define UML_MCONSOLE_HELPTEXT \
262"Commands: \n\
263 version - Get kernel version \n\
264 help - Print this message \n\
265 halt - Halt UML \n\
266 reboot - Reboot UML \n\
267 config <dev>=<config> - Add a new device to UML; \n\
268 same syntax as command line \n\
269 config <dev> - Query the configuration of a device \n\
270 remove <dev> - Remove a device from UML \n\
271 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800272 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 stop - pause the UML; it will do nothing until it receives a 'go' \n\
274 go - continue the UML after a 'stop' \n\
275 log <string> - make UML enter <string> into the kernel log\n\
276 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700277 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278"
279
280void mconsole_help(struct mc_request *req)
281{
282 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
283}
284
285void mconsole_halt(struct mc_request *req)
286{
287 mconsole_reply(req, "", 0, 0);
288 machine_halt();
289}
290
291void mconsole_reboot(struct mc_request *req)
292{
293 mconsole_reply(req, "", 0, 0);
294 machine_restart(NULL);
295}
296
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297void mconsole_cad(struct mc_request *req)
298{
299 mconsole_reply(req, "", 0, 0);
300 ctrl_alt_del();
301}
302
303void mconsole_go(struct mc_request *req)
304{
305 mconsole_reply(req, "Not stopped", 1, 0);
306}
307
308void mconsole_stop(struct mc_request *req)
309{
310 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
311 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100312 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800313 for (;;) {
314 if (!mconsole_get_request(req->originating_fd, req))
315 continue;
Al Viro3a512372006-10-24 11:15:29 +0100316 if (req->cmd->handler == mconsole_go)
317 break;
318 if (req->cmd->handler == mconsole_stop) {
319 mconsole_reply(req, "Already stopped", 1, 0);
320 continue;
321 }
322 if (req->cmd->handler == mconsole_sysrq) {
323 struct pt_regs *old_regs;
324 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
325 mconsole_sysrq(req);
326 set_irq_regs(old_regs);
327 continue;
328 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 (*req->cmd->handler)(req);
330 }
331 os_set_fd_block(req->originating_fd, 0);
332 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
333 mconsole_reply(req, "", 0, 0);
334}
335
Jeff Dike84f48d42007-02-10 01:44:01 -0800336static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800337static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
339void mconsole_register_dev(struct mc_device *new)
340{
Jeff Dike84f48d42007-02-10 01:44:01 -0800341 spin_lock(&mc_devices_lock);
342 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800344 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
347static struct mc_device *mconsole_find_dev(char *name)
348{
349 struct list_head *ele;
350 struct mc_device *dev;
351
Jeff Dikeae2587e2007-10-16 01:26:57 -0700352 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700354 if (!strncmp(name, dev->name, strlen(dev->name)))
355 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700357 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358}
359
Jeff Dike02dea082006-03-31 02:30:08 -0800360#define UNPLUGGED_PER_PAGE \
361 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
362
363struct unplugged_pages {
364 struct list_head list;
365 void *pages[UNPLUGGED_PER_PAGE];
366};
367
Daniel Walkere98fa282008-02-04 22:31:27 -0800368static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800369static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800370static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800371static int unplug_index = UNPLUGGED_PER_PAGE;
372
Jeff Dikef28169d2007-02-10 01:43:53 -0800373static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800374{
375 unsigned long long diff;
376 int err = -EINVAL, i, add;
377 char *ret;
378
Jeff Dikeae2587e2007-10-16 01:26:57 -0700379 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800380 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800381 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800382 }
Jeff Dike02dea082006-03-31 02:30:08 -0800383
384 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700385 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800386 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700387 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800388 add = 1;
389 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800390 else {
391 *error_out = "Expected increment to start with '-' or '+'";
392 goto out;
393 }
Jeff Dike02dea082006-03-31 02:30:08 -0800394
395 str++;
396 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700397 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800398 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800399 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800400 }
Jeff Dike02dea082006-03-31 02:30:08 -0800401
402 diff /= PAGE_SIZE;
403
Daniel Walkere98fa282008-02-04 22:31:27 -0800404 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700405 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800406 struct unplugged_pages *unplugged;
407 void *addr;
408
Jeff Dikeae2587e2007-10-16 01:26:57 -0700409 if (add) {
410 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800411 break;
412
413 unplugged = list_entry(unplugged_pages.next,
414 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700415 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800416 addr = unplugged->pages[--unplug_index];
417 else {
418 list_del(&unplugged->list);
419 addr = unplugged;
420 unplug_index = UNPLUGGED_PER_PAGE;
421 }
422
423 free_page((unsigned long) addr);
424 unplugged_pages_count--;
425 }
426 else {
427 struct page *page;
428
429 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700430 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800431 break;
432
433 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700434 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800435 list_add(&unplugged->list, &unplugged_pages);
436 unplug_index = 0;
437 }
438 else {
439 struct list_head *entry = unplugged_pages.next;
440 addr = unplugged;
441
442 unplugged = list_entry(entry,
443 struct unplugged_pages,
444 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800445 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700446 if (err) {
447 printk(KERN_ERR "Failed to release "
448 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800449 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800450 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800451 }
452 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800453 }
454
455 unplugged_pages_count++;
456 }
457 }
458
459 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800460out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800461 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800462out:
463 return err;
464}
465
466static int mem_get_config(char *name, char *str, int size, char **error_out)
467{
468 char buf[sizeof("18446744073709551615")];
469 int len = 0;
470
471 sprintf(buf, "%ld", uml_physmem);
472 CONFIG_CHUNK(str, size, len, buf, 1);
473
474 return len;
475}
476
477static int mem_id(char **str, int *start_out, int *end_out)
478{
479 *start_out = 0;
480 *end_out = 0;
481
482 return 0;
483}
484
Jeff Dikef28169d2007-02-10 01:43:53 -0800485static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800486{
Jeff Dikef28169d2007-02-10 01:43:53 -0800487 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800488 return -EBUSY;
489}
490
491static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800492 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800493 .name = "mem",
494 .config = mem_config,
495 .get_config = mem_get_config,
496 .id = mem_id,
497 .remove = mem_remove,
498};
499
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700500static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800501{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700502 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800503 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700504 else printk(KERN_ERR "Can't release memory to the host - memory "
505 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800506 return 0;
507}
508
509__initcall(mem_mc_init);
510
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511#define CONFIG_BUF_SIZE 64
512
Jeff Diked50084a2006-01-06 00:18:50 -0800513static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 char **),
515 struct mc_request *req, char *name)
516{
517 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
518 int n, size;
519
Jeff Dikeae2587e2007-10-16 01:26:57 -0700520 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 mconsole_reply(req, "No get_config routine defined", 1, 0);
522 return;
523 }
524
525 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700526 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 buf = default_buf;
528
Jeff Dikeae2587e2007-10-16 01:26:57 -0700529 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700531 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 mconsole_reply(req, error, 1, 0);
533 goto out;
534 }
535
Jeff Dikeae2587e2007-10-16 01:26:57 -0700536 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 mconsole_reply(req, buf, 0, 0);
538 goto out;
539 }
540
Jeff Dikeae2587e2007-10-16 01:26:57 -0700541 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 kfree(buf);
543
544 size = n;
545 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700546 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
548 return;
549 }
550 }
551 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700552 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554}
555
556void mconsole_config(struct mc_request *req)
557{
558 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800559 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 int err;
561
562 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800563 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700565 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 mconsole_reply(req, "Bad configuration option", 1, 0);
567 return;
568 }
569
570 name = &ptr[strlen(dev->name)];
571 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700572 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 ptr++;
574
Jeff Dikeae2587e2007-10-16 01:26:57 -0700575 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800576 err = (*dev->config)(name, &error_string);
577 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
579 else mconsole_get_config(dev->get_config, req, name);
580}
581
582void mconsole_remove(struct mc_request *req)
583{
Jeff Diked50084a2006-01-06 00:18:50 -0800584 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700585 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800586 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700587 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
589 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800590 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700592 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 mconsole_reply(req, "Bad remove option", 1, 0);
594 return;
595 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700596
Jeff Dike3a331a52006-01-06 00:19:05 -0800597 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700598
Jeff Dike3a331a52006-01-06 00:19:05 -0800599 err = 1;
600 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700601 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800602 err_msg = "Couldn't parse device number";
603 goto out;
604 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700605 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800606 sprintf(error, "Invalid device number - must be between "
607 "%d and %d", start, end);
608 err_msg = error;
609 goto out;
610 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700611
Jeff Dikef28169d2007-02-10 01:43:53 -0800612 err_msg = NULL;
613 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700614 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700615 case 0:
616 err_msg = "";
617 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800618 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700619 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800620 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800621 break;
622 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700623 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800624 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800625 break;
626 default:
627 break;
628 }
629out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700630 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
Jeff Dikef92afe52006-09-29 01:58:52 -0700633struct mconsole_output {
634 struct list_head list;
635 struct mc_request *req;
636};
637
Jeff Dike84f48d42007-02-10 01:44:01 -0800638static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800639static LIST_HEAD(clients);
640static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800641
642static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700643 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800644{
645 struct list_head *ele;
646 int n;
647
Jeff Dikeae2587e2007-10-16 01:26:57 -0700648 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800649 return;
650
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700651 while (len > 0) {
652 n = min((size_t) len, ARRAY_SIZE(console_buf));
653 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800654 string += n;
655 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800656
Jeff Dikeae2587e2007-10-16 01:26:57 -0700657 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700658 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800659
Jeff Dikef92afe52006-09-29 01:58:52 -0700660 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700661 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800662 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800663 }
664}
665
666static struct console mc_console = { .name = "mc",
667 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800668 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800669 .index = -1 };
670
671static int mc_add_console(void)
672{
673 register_console(&mc_console);
674 return 0;
675}
676
677late_initcall(mc_add_console);
678
679static void with_console(struct mc_request *req, void (*proc)(void *),
680 void *arg)
681{
Jeff Dikef92afe52006-09-29 01:58:52 -0700682 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800683 unsigned long flags;
684
Jeff Dikef92afe52006-09-29 01:58:52 -0700685 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800686 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800687 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800688 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800689
690 (*proc)(arg);
691
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700692 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800693
Jeff Dike84f48d42007-02-10 01:44:01 -0800694 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800695 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800696 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800697}
698
Jeff Dike4111b022006-01-06 00:19:05 -0800699#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700700
701#include <linux/sysrq.h>
702
Jeff Dike4111b022006-01-06 00:19:05 -0800703static void sysrq_proc(void *arg)
704{
705 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100706 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800707}
708
709void mconsole_sysrq(struct mc_request *req)
710{
711 char *ptr = req->request.data;
712
713 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800714 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800715
Jeff Dikeae2587e2007-10-16 01:26:57 -0700716 /*
717 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800718 * so in this case, we reply first.
719 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700720 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800721 mconsole_reply(req, "", 0, 0);
722
723 with_console(req, sysrq_proc, ptr);
724}
725#else
726void mconsole_sysrq(struct mc_request *req)
727{
728 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
729}
730#endif
731
Jeff Dike6f517d32006-01-06 00:19:04 -0800732static void stack_proc(void *arg)
733{
734 struct task_struct *from = current, *to = arg;
735
736 to->thread.saved_task = from;
737 switch_to(from, to, from);
738}
739
Jeff Dikeae2587e2007-10-16 01:26:57 -0700740/*
741 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700742 * Added by Allan Graves, Jeff Dike
743 * Dumps a stacks registers to the linux console.
744 * Usage stack <pid>.
745 */
Jeff Dike42fda662007-10-16 01:26:50 -0700746void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747{
Jeff Dike3a331a52006-01-06 00:19:05 -0800748 char *ptr = req->request.data;
749 int pid_requested= -1;
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");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800759 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700760
Jeff Dikeae2587e2007-10-16 01:26:57 -0700761 /*
762 * Should really check for multiple pids or reject bad args here
763 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800764 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700765 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800766 mconsole_reply(req, "Please specify a pid", 1, 0);
767 return;
768 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700769
Jeff Dike827b3f62008-02-04 22:31:29 -0800770 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700771 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800772 mconsole_reply(req, "Couldn't find that pid", 1, 0);
773 return;
774 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800775 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700776}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700777
Jeff Dikeae2587e2007-10-16 01:26:57 -0700778/*
779 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 * active.
781 */
Jeff Diked50084a2006-01-06 00:18:50 -0800782static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700784static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
786 /* long to avoid size mismatch warnings from gcc */
787 long sock;
788 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800789 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Jeff Dikeae2587e2007-10-16 01:26:57 -0700791 if (umid_file_name("mconsole", file, sizeof(file)))
792 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
794
795 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700796 if (sock < 0) {
797 printk(KERN_ERR "Failed to initialize management console\n");
798 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 }
Jeff Dike438ee672008-02-04 22:31:19 -0800800 if (os_set_fd_block(sock, 0))
801 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
803 register_reboot_notifier(&reboot_notifier);
804
805 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700806 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700808 if (err) {
809 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800810 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 }
812
Jeff Dikeae2587e2007-10-16 01:26:57 -0700813 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800814 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700815 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800817 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 strlen(mconsole_socket_name) + 1);
819 else printk(KERN_ERR "mconsole_setup failed to strdup "
820 "string\n");
821 }
822
Jeff Dikeae2587e2007-10-16 01:26:57 -0700823 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700825 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800826
827 out:
828 os_close_file(sock);
829 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830}
831
832__initcall(mconsole_init);
833
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800834static ssize_t mconsole_proc_write(struct file *file,
835 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836{
837 char *buf;
838
839 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700840 if (buf == NULL)
841 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Jeff Dikeae2587e2007-10-16 01:26:57 -0700843 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 count = -EFAULT;
845 goto out;
846 }
847
848 buf[count] = '\0';
849
850 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
851 out:
852 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700853 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854}
855
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800856static const struct file_operations mconsole_proc_fops = {
857 .owner = THIS_MODULE,
858 .write = mconsole_proc_write,
859};
860
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861static int create_proc_mconsole(void)
862{
863 struct proc_dir_entry *ent;
864
Jeff Dikeae2587e2007-10-16 01:26:57 -0700865 if (notify_socket == NULL)
866 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800868 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700869 if (ent == NULL) {
870 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
871 "failed\n");
872 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700874 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875}
876
877static DEFINE_SPINLOCK(notify_spinlock);
878
879void lock_notify(void)
880{
881 spin_lock(&notify_spinlock);
882}
883
884void unlock_notify(void)
885{
886 spin_unlock(&notify_spinlock);
887}
888
889__initcall(create_proc_mconsole);
890
Jeff Dike088bec42007-10-16 01:27:20 -0700891#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
893static int mconsole_setup(char *str)
894{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700895 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 str += strlen(NOTIFY);
897 notify_socket = str;
898 }
899 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700900 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901}
902
Jeff Dike088bec42007-10-16 01:27:20 -0700903__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
905__uml_help(mconsole_setup,
906"mconsole=notify:<socket>\n"
907" Requests that the mconsole driver send a message to the named Unix\n"
908" socket containing the name of the mconsole socket. This also serves\n"
909" to notify outside processes when UML has booted far enough to respond\n"
910" to mconsole requests.\n\n"
911);
912
913static int notify_panic(struct notifier_block *self, unsigned long unused1,
914 void *ptr)
915{
916 char *message = ptr;
917
Jeff Dikeae2587e2007-10-16 01:26:57 -0700918 if (notify_socket == NULL)
919 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
Jeff Diked50084a2006-01-06 00:18:50 -0800921 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700923 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924}
925
926static struct notifier_block panic_exit_notifier = {
927 .notifier_call = notify_panic,
928 .next = NULL,
929 .priority = 1
930};
931
932static int add_notifier(void)
933{
Alan Sterne041c682006-03-27 01:16:30 -0800934 atomic_notifier_chain_register(&panic_notifier_list,
935 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700936 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937}
938
939__initcall(add_notifier);
940
941char *mconsole_notify_socket(void)
942{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700943 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944}
945
946EXPORT_SYMBOL(mconsole_notify_socket);