blob: f0fa47f10e6cb0eaa94ea0c14bfa2acc79a80413 [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>
9#include <linux/interrupt.h>
10#include <linux/list.h>
11#include <linux/mm.h>
12#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>
Balbir Singh36137122008-12-09 13:14:07 -080019#include <linux/socket.h>
20#include <linux/un.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080021#include <linux/workqueue.h>
22#include <linux/mutex.h>
23#include <asm/uaccess.h>
24
Jeff Dikeae2587e2007-10-16 01:26:57 -070025#include "init.h"
26#include "irq_kern.h"
27#include "irq_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include "mconsole.h"
30#include "mconsole_kern.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Jeff Diked50084a2006-01-06 00:18:50 -080033static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 unsigned long what, void *data)
35{
Jeff Dikeae2587e2007-10-16 01:26:57 -070036 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070037}
38
39
40static struct notifier_block reboot_notifier = {
41 .notifier_call = do_unlink_socket,
42 .priority = 0,
43};
44
Jeff Diked50084a2006-01-06 00:18:50 -080045/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 * locking, and the interrupt handler is safe because it can't interrupt
47 * itself and it can only happen on CPU 0.
48 */
49
Jeff Dike90107722006-01-06 00:18:54 -080050static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
David Howells6d5aefb2006-12-05 19:36:26 +000052static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070053{
54 struct mconsole_entry *req;
55 unsigned long flags;
56
Jeff Dikeae2587e2007-10-16 01:26:57 -070057 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070058 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070059 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 list_del(&req->list);
61 local_irq_restore(flags);
62 req->request.cmd->handler(&req->request);
63 kfree(req);
64 }
65}
66
David Howells6d5aefb2006-12-05 19:36:26 +000067static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Al Viro7bea96f2006-10-08 22:49:34 +010069static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070070{
71 /* long to avoid size mismatch warnings from gcc */
72 long fd;
73 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010074 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070077 while (mconsole_get_request(fd, &req)) {
78 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 (*req.cmd->handler)(&req);
80 else {
Jeff Dike60baa152006-04-10 22:53:28 -070081 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070082 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 mconsole_reply(&req, "Out of memory", 1, 0);
84 else {
85 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010086 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 list_add(&new->list, &mc_requests);
88 }
89 }
90 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070091 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 schedule_work(&mconsole_work);
93 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -070094 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095}
96
97void mconsole_version(struct mc_request *req)
98{
99 char version[256];
100
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700101 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -0700102 utsname()->nodename, utsname()->release, utsname()->version,
103 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 mconsole_reply(req, version, 0, 0);
105}
106
107void mconsole_log(struct mc_request *req)
108{
109 int len;
110 char *ptr = req->request.data;
111
112 ptr += strlen("log ");
113
114 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700115 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 mconsole_reply(req, "", 0, 0);
117}
118
119/* This is a more convoluted version of mconsole_proc, which has some stability
120 * problems; however, we need it fixed, because it is expected that UML users
121 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
122 * show the real procfs content, not the ones from hppfs.*/
123#if 0
124void mconsole_proc(struct mc_request *req)
125{
126 struct nameidata nd;
127 struct file_system_type *proc;
128 struct super_block *super;
129 struct file *file;
130 int n, err;
131 char *ptr = req->request.data, *buf;
132
133 ptr += strlen("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700134 while (isspace(*ptr)) ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
136 proc = get_fs_type("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700137 if (proc == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 mconsole_reply(req, "procfs not registered", 1, 0);
139 goto out;
140 }
141
142 super = (*proc->get_sb)(proc, 0, NULL, NULL);
143 put_filesystem(proc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700144 if (super == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
146 goto out;
147 }
148 up_write(&super->s_umount);
149
Jan Blunck4ac91372008-02-14 19:34:32 -0800150 nd.path.dentry = super->s_root;
151 nd.path.mnt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 nd.flags = O_RDONLY + 1;
153 nd.last_type = LAST_ROOT;
154
155 /* START: it was experienced that the stability problems are closed
156 * if commenting out these two calls + the below read cycle. To
157 * make UML crash again, it was enough to readd either one.*/
158 err = link_path_walk(ptr, &nd);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700159 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 mconsole_reply(req, "Failed to look up file", 1, 0);
161 goto out_kill;
162 }
163
David Howells745ca242008-11-14 10:39:22 +1100164 file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
165 current_cred());
Jeff Dikeae2587e2007-10-16 01:26:57 -0700166 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 mconsole_reply(req, "Failed to open file", 1, 0);
168 goto out_kill;
169 }
170 /*END*/
171
172 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700173 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
175 goto out_fput;
176 }
177
Jeff Dikeae2587e2007-10-16 01:26:57 -0700178 if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 do {
180 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
181 &file->f_pos);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700182 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 buf[n] = '\0';
184 mconsole_reply(req, buf, 0, (n > 0));
185 }
186 else {
187 mconsole_reply(req, "Read of file failed",
188 1, 0);
189 goto out_free;
190 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700191 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 }
193 else mconsole_reply(req, "", 0, 0);
194
195 out_free:
196 kfree(buf);
197 out_fput:
198 fput(file);
199 out_kill:
200 deactivate_super(super);
201 out: ;
202}
203#endif
204
205void mconsole_proc(struct mc_request *req)
206{
207 char path[64];
208 char *buf;
209 int len;
210 int fd;
211 int first_chunk = 1;
212 char *ptr = req->request.data;
213
214 ptr += strlen("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700215 while (isspace(*ptr))
216 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");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700563 while (isspace(*ptr))
564 ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700566 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 mconsole_reply(req, "Bad configuration option", 1, 0);
568 return;
569 }
570
571 name = &ptr[strlen(dev->name)];
572 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700573 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 ptr++;
575
Jeff Dikeae2587e2007-10-16 01:26:57 -0700576 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800577 err = (*dev->config)(name, &error_string);
578 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 }
580 else mconsole_get_config(dev->get_config, req, name);
581}
582
583void mconsole_remove(struct mc_request *req)
584{
Jeff Diked50084a2006-01-06 00:18:50 -0800585 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700586 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800587 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700588 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 ptr += strlen("remove");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700591 while (isspace(*ptr)) ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700593 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 mconsole_reply(req, "Bad remove option", 1, 0);
595 return;
596 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700597
Jeff Dike3a331a52006-01-06 00:19:05 -0800598 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700599
Jeff Dike3a331a52006-01-06 00:19:05 -0800600 err = 1;
601 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700602 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800603 err_msg = "Couldn't parse device number";
604 goto out;
605 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700606 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800607 sprintf(error, "Invalid device number - must be between "
608 "%d and %d", start, end);
609 err_msg = error;
610 goto out;
611 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700612
Jeff Dikef28169d2007-02-10 01:43:53 -0800613 err_msg = NULL;
614 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700615 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700616 case 0:
617 err_msg = "";
618 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800619 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700620 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800621 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800622 break;
623 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700624 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800625 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800626 break;
627 default:
628 break;
629 }
630out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700631 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632}
633
Jeff Dikef92afe52006-09-29 01:58:52 -0700634struct mconsole_output {
635 struct list_head list;
636 struct mc_request *req;
637};
638
Jeff Dike84f48d42007-02-10 01:44:01 -0800639static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800640static LIST_HEAD(clients);
641static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800642
643static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700644 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800645{
646 struct list_head *ele;
647 int n;
648
Jeff Dikeae2587e2007-10-16 01:26:57 -0700649 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800650 return;
651
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700652 while (len > 0) {
653 n = min((size_t) len, ARRAY_SIZE(console_buf));
654 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800655 string += n;
656 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800657
Jeff Dikeae2587e2007-10-16 01:26:57 -0700658 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700659 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800660
Jeff Dikef92afe52006-09-29 01:58:52 -0700661 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700662 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800663 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800664 }
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
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700693 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800694
Jeff Dike84f48d42007-02-10 01:44:01 -0800695 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800696 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800697 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800698}
699
Jeff Dike4111b022006-01-06 00:19:05 -0800700#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700701
702#include <linux/sysrq.h>
703
Jeff Dike4111b022006-01-06 00:19:05 -0800704static void sysrq_proc(void *arg)
705{
706 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100707 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800708}
709
710void mconsole_sysrq(struct mc_request *req)
711{
712 char *ptr = req->request.data;
713
714 ptr += strlen("sysrq");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700715 while (isspace(*ptr)) ptr++;
Jeff Dike4111b022006-01-06 00:19:05 -0800716
Jeff Dikeae2587e2007-10-16 01:26:57 -0700717 /*
718 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800719 * so in this case, we reply first.
720 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700721 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800722 mconsole_reply(req, "", 0, 0);
723
724 with_console(req, sysrq_proc, ptr);
725}
726#else
727void mconsole_sysrq(struct mc_request *req)
728{
729 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
730}
731#endif
732
Jeff Dike6f517d32006-01-06 00:19:04 -0800733static void stack_proc(void *arg)
734{
735 struct task_struct *from = current, *to = arg;
736
737 to->thread.saved_task = from;
738 switch_to(from, to, from);
739}
740
Jeff Dikeae2587e2007-10-16 01:26:57 -0700741/*
742 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700743 * Added by Allan Graves, Jeff Dike
744 * Dumps a stacks registers to the linux console.
745 * Usage stack <pid>.
746 */
Jeff Dike42fda662007-10-16 01:26:50 -0700747void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700748{
Jeff Dike3a331a52006-01-06 00:19:05 -0800749 char *ptr = req->request.data;
750 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700751 struct task_struct *to = NULL;
752
Jeff Dikeae2587e2007-10-16 01:26:57 -0700753 /*
754 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800755 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700756 * 2) Add a way to stack dump all pids.
757 */
758
Jeff Dike3a331a52006-01-06 00:19:05 -0800759 ptr += strlen("stack");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700760 while (isspace(*ptr))
761 ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700762
Jeff Dikeae2587e2007-10-16 01:26:57 -0700763 /*
764 * Should really check for multiple pids or reject bad args here
765 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800766 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700767 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800768 mconsole_reply(req, "Please specify a pid", 1, 0);
769 return;
770 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700771
Jeff Dike827b3f62008-02-04 22:31:29 -0800772 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700773 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800774 mconsole_reply(req, "Couldn't find that pid", 1, 0);
775 return;
776 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800777 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700778}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700779
Jeff Dikeae2587e2007-10-16 01:26:57 -0700780/*
781 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 * active.
783 */
Jeff Diked50084a2006-01-06 00:18:50 -0800784static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700786static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787{
788 /* long to avoid size mismatch warnings from gcc */
789 long sock;
790 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800791 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
Jeff Dikeae2587e2007-10-16 01:26:57 -0700793 if (umid_file_name("mconsole", file, sizeof(file)))
794 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
796
797 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700798 if (sock < 0) {
799 printk(KERN_ERR "Failed to initialize management console\n");
800 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 }
Jeff Dike438ee672008-02-04 22:31:19 -0800802 if (os_set_fd_block(sock, 0))
803 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
805 register_reboot_notifier(&reboot_notifier);
806
807 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700808 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700810 if (err) {
811 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800812 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 }
814
Jeff Dikeae2587e2007-10-16 01:26:57 -0700815 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800816 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700817 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800819 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 strlen(mconsole_socket_name) + 1);
821 else printk(KERN_ERR "mconsole_setup failed to strdup "
822 "string\n");
823 }
824
Jeff Dikeae2587e2007-10-16 01:26:57 -0700825 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700827 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800828
829 out:
830 os_close_file(sock);
831 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832}
833
834__initcall(mconsole_init);
835
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800836static ssize_t mconsole_proc_write(struct file *file,
837 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838{
839 char *buf;
840
841 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700842 if (buf == NULL)
843 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Jeff Dikeae2587e2007-10-16 01:26:57 -0700845 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 count = -EFAULT;
847 goto out;
848 }
849
850 buf[count] = '\0';
851
852 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
853 out:
854 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700855 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856}
857
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800858static const struct file_operations mconsole_proc_fops = {
859 .owner = THIS_MODULE,
860 .write = mconsole_proc_write,
861};
862
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863static int create_proc_mconsole(void)
864{
865 struct proc_dir_entry *ent;
866
Jeff Dikeae2587e2007-10-16 01:26:57 -0700867 if (notify_socket == NULL)
868 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800870 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700871 if (ent == NULL) {
872 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
873 "failed\n");
874 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700876 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877}
878
879static DEFINE_SPINLOCK(notify_spinlock);
880
881void lock_notify(void)
882{
883 spin_lock(&notify_spinlock);
884}
885
886void unlock_notify(void)
887{
888 spin_unlock(&notify_spinlock);
889}
890
891__initcall(create_proc_mconsole);
892
Jeff Dike088bec42007-10-16 01:27:20 -0700893#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895static int mconsole_setup(char *str)
896{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700897 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 str += strlen(NOTIFY);
899 notify_socket = str;
900 }
901 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700902 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903}
904
Jeff Dike088bec42007-10-16 01:27:20 -0700905__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
907__uml_help(mconsole_setup,
908"mconsole=notify:<socket>\n"
909" Requests that the mconsole driver send a message to the named Unix\n"
910" socket containing the name of the mconsole socket. This also serves\n"
911" to notify outside processes when UML has booted far enough to respond\n"
912" to mconsole requests.\n\n"
913);
914
915static int notify_panic(struct notifier_block *self, unsigned long unused1,
916 void *ptr)
917{
918 char *message = ptr;
919
Jeff Dikeae2587e2007-10-16 01:26:57 -0700920 if (notify_socket == NULL)
921 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
Jeff Diked50084a2006-01-06 00:18:50 -0800923 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700925 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926}
927
928static struct notifier_block panic_exit_notifier = {
929 .notifier_call = notify_panic,
930 .next = NULL,
931 .priority = 1
932};
933
934static int add_notifier(void)
935{
Alan Sterne041c682006-03-27 01:16:30 -0800936 atomic_notifier_chain_register(&panic_notifier_list,
937 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700938 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939}
940
941__initcall(add_notifier);
942
943char *mconsole_notify_socket(void)
944{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700945 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946}
947
948EXPORT_SYMBOL(mconsole_notify_socket);