blob: 43b39d61b538698229a23f654b7fb44a335187b3 [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>
Richard Weinbergera3a85a72012-03-29 18:47:46 +020025#include <asm/switch_to.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080026
Jeff Dikeae2587e2007-10-16 01:26:57 -070027#include "init.h"
28#include "irq_kern.h"
29#include "irq_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include "mconsole.h"
32#include "mconsole_kern.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Jeff Diked50084a2006-01-06 00:18:50 -080035static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 unsigned long what, void *data)
37{
Jeff Dikeae2587e2007-10-16 01:26:57 -070038 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070039}
40
41
42static struct notifier_block reboot_notifier = {
43 .notifier_call = do_unlink_socket,
44 .priority = 0,
45};
46
Jeff Diked50084a2006-01-06 00:18:50 -080047/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 * locking, and the interrupt handler is safe because it can't interrupt
49 * itself and it can only happen on CPU 0.
50 */
51
Jeff Dike90107722006-01-06 00:18:54 -080052static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
David Howells6d5aefb2006-12-05 19:36:26 +000054static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070055{
56 struct mconsole_entry *req;
57 unsigned long flags;
58
Jeff Dikeae2587e2007-10-16 01:26:57 -070059 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070060 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070061 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 list_del(&req->list);
63 local_irq_restore(flags);
64 req->request.cmd->handler(&req->request);
65 kfree(req);
66 }
67}
68
David Howells6d5aefb2006-12-05 19:36:26 +000069static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
Al Viro7bea96f2006-10-08 22:49:34 +010071static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070072{
73 /* long to avoid size mismatch warnings from gcc */
74 long fd;
75 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010076 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070079 while (mconsole_get_request(fd, &req)) {
80 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 (*req.cmd->handler)(&req);
82 else {
Jeff Dike60baa152006-04-10 22:53:28 -070083 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070084 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 mconsole_reply(&req, "Out of memory", 1, 0);
86 else {
87 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010088 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 list_add(&new->list, &mc_requests);
90 }
91 }
92 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070093 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 schedule_work(&mconsole_work);
95 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -070096 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097}
98
99void mconsole_version(struct mc_request *req)
100{
101 char version[256];
102
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700103 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -0700104 utsname()->nodename, utsname()->release, utsname()->version,
105 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 mconsole_reply(req, version, 0, 0);
107}
108
109void mconsole_log(struct mc_request *req)
110{
111 int len;
112 char *ptr = req->request.data;
113
114 ptr += strlen("log ");
115
116 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700117 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 mconsole_reply(req, "", 0, 0);
119}
120
121/* This is a more convoluted version of mconsole_proc, which has some stability
122 * problems; however, we need it fixed, because it is expected that UML users
123 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
124 * show the real procfs content, not the ones from hppfs.*/
125#if 0
126void mconsole_proc(struct mc_request *req)
127{
Al Viro4ecf09f2009-12-24 00:44:44 -0500128 struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 struct file *file;
Al Viro73d049a2011-03-11 12:08:24 -0500130 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 char *ptr = req->request.data, *buf;
Al Viro4ecf09f2009-12-24 00:44:44 -0500132 mm_segment_t old_fs = get_fs();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
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
Al Viro73d049a2011-03-11 12:08:24 -0500137 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700138 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 mconsole_reply(req, "Failed to open file", 1, 0);
Al Viro4ecf09f2009-12-24 00:44:44 -0500140 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
143 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700144 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
146 goto out_fput;
147 }
148
Al Viro4ecf09f2009-12-24 00:44:44 -0500149 if (file->f_op->read) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 do {
Al Viro4ecf09f2009-12-24 00:44:44 -0500151 loff_t pos;
152 set_fs(KERNEL_DS);
153 n = vfs_read(file, buf, PAGE_SIZE - 1, &pos);
154 file_pos_write(file, pos);
155 set_fs(old_fs);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700156 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 buf[n] = '\0';
158 mconsole_reply(req, buf, 0, (n > 0));
159 }
160 else {
161 mconsole_reply(req, "Read of file failed",
162 1, 0);
163 goto out_free;
164 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700165 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 }
167 else mconsole_reply(req, "", 0, 0);
168
169 out_free:
170 kfree(buf);
171 out_fput:
172 fput(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 out: ;
174}
175#endif
176
177void mconsole_proc(struct mc_request *req)
178{
179 char path[64];
180 char *buf;
181 int len;
182 int fd;
183 int first_chunk = 1;
184 char *ptr = req->request.data;
185
186 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800187 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 snprintf(path, sizeof(path), "/proc/%s", ptr);
189
190 fd = sys_open(path, 0, 0);
191 if (fd < 0) {
192 mconsole_reply(req, "Failed to open file", 1, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700193 printk(KERN_ERR "open %s: %d\n",path,fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 goto out;
195 }
196
197 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700198 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
200 goto out_close;
201 }
202
203 for (;;) {
204 len = sys_read(fd, buf, PAGE_SIZE-1);
205 if (len < 0) {
206 mconsole_reply(req, "Read of file failed", 1, 0);
207 goto out_free;
208 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700209 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 if (first_chunk) {
211 mconsole_reply(req, "\n", 0, 1);
212 first_chunk = 0;
213 }
214 if (len == PAGE_SIZE-1) {
215 buf[len] = '\0';
216 mconsole_reply(req, buf, 0, 1);
217 } else {
218 buf[len] = '\0';
219 mconsole_reply(req, buf, 0, 0);
220 break;
221 }
222 }
223
224 out_free:
225 kfree(buf);
226 out_close:
227 sys_close(fd);
228 out:
229 /* nothing */;
230}
231
232#define UML_MCONSOLE_HELPTEXT \
233"Commands: \n\
234 version - Get kernel version \n\
235 help - Print this message \n\
236 halt - Halt UML \n\
237 reboot - Reboot UML \n\
238 config <dev>=<config> - Add a new device to UML; \n\
239 same syntax as command line \n\
240 config <dev> - Query the configuration of a device \n\
241 remove <dev> - Remove a device from UML \n\
242 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800243 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 stop - pause the UML; it will do nothing until it receives a 'go' \n\
245 go - continue the UML after a 'stop' \n\
246 log <string> - make UML enter <string> into the kernel log\n\
247 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700248 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249"
250
251void mconsole_help(struct mc_request *req)
252{
253 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
254}
255
256void mconsole_halt(struct mc_request *req)
257{
258 mconsole_reply(req, "", 0, 0);
259 machine_halt();
260}
261
262void mconsole_reboot(struct mc_request *req)
263{
264 mconsole_reply(req, "", 0, 0);
265 machine_restart(NULL);
266}
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268void mconsole_cad(struct mc_request *req)
269{
270 mconsole_reply(req, "", 0, 0);
271 ctrl_alt_del();
272}
273
274void mconsole_go(struct mc_request *req)
275{
276 mconsole_reply(req, "Not stopped", 1, 0);
277}
278
279void mconsole_stop(struct mc_request *req)
280{
281 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
282 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100283 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800284 for (;;) {
285 if (!mconsole_get_request(req->originating_fd, req))
286 continue;
Al Viro3a512372006-10-24 11:15:29 +0100287 if (req->cmd->handler == mconsole_go)
288 break;
289 if (req->cmd->handler == mconsole_stop) {
290 mconsole_reply(req, "Already stopped", 1, 0);
291 continue;
292 }
293 if (req->cmd->handler == mconsole_sysrq) {
294 struct pt_regs *old_regs;
295 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
296 mconsole_sysrq(req);
297 set_irq_regs(old_regs);
298 continue;
299 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 (*req->cmd->handler)(req);
301 }
302 os_set_fd_block(req->originating_fd, 0);
303 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
304 mconsole_reply(req, "", 0, 0);
305}
306
Jeff Dike84f48d42007-02-10 01:44:01 -0800307static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800308static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310void mconsole_register_dev(struct mc_device *new)
311{
Jeff Dike84f48d42007-02-10 01:44:01 -0800312 spin_lock(&mc_devices_lock);
313 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800315 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316}
317
318static struct mc_device *mconsole_find_dev(char *name)
319{
320 struct list_head *ele;
321 struct mc_device *dev;
322
Jeff Dikeae2587e2007-10-16 01:26:57 -0700323 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700325 if (!strncmp(name, dev->name, strlen(dev->name)))
326 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700328 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
Jeff Dike02dea082006-03-31 02:30:08 -0800331#define UNPLUGGED_PER_PAGE \
332 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
333
334struct unplugged_pages {
335 struct list_head list;
336 void *pages[UNPLUGGED_PER_PAGE];
337};
338
Daniel Walkere98fa282008-02-04 22:31:27 -0800339static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800340static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800341static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800342static int unplug_index = UNPLUGGED_PER_PAGE;
343
Jeff Dikef28169d2007-02-10 01:43:53 -0800344static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800345{
346 unsigned long long diff;
347 int err = -EINVAL, i, add;
348 char *ret;
349
Jeff Dikeae2587e2007-10-16 01:26:57 -0700350 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800351 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800352 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800353 }
Jeff Dike02dea082006-03-31 02:30:08 -0800354
355 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700356 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800357 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700358 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800359 add = 1;
360 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800361 else {
362 *error_out = "Expected increment to start with '-' or '+'";
363 goto out;
364 }
Jeff Dike02dea082006-03-31 02:30:08 -0800365
366 str++;
367 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700368 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800369 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800370 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800371 }
Jeff Dike02dea082006-03-31 02:30:08 -0800372
373 diff /= PAGE_SIZE;
374
Daniel Walkere98fa282008-02-04 22:31:27 -0800375 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700376 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800377 struct unplugged_pages *unplugged;
378 void *addr;
379
Jeff Dikeae2587e2007-10-16 01:26:57 -0700380 if (add) {
381 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800382 break;
383
384 unplugged = list_entry(unplugged_pages.next,
385 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700386 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800387 addr = unplugged->pages[--unplug_index];
388 else {
389 list_del(&unplugged->list);
390 addr = unplugged;
391 unplug_index = UNPLUGGED_PER_PAGE;
392 }
393
394 free_page((unsigned long) addr);
395 unplugged_pages_count--;
396 }
397 else {
398 struct page *page;
399
400 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700401 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800402 break;
403
404 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700405 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800406 list_add(&unplugged->list, &unplugged_pages);
407 unplug_index = 0;
408 }
409 else {
410 struct list_head *entry = unplugged_pages.next;
411 addr = unplugged;
412
413 unplugged = list_entry(entry,
414 struct unplugged_pages,
415 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800416 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700417 if (err) {
418 printk(KERN_ERR "Failed to release "
419 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800420 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800421 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800422 }
423 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800424 }
425
426 unplugged_pages_count++;
427 }
428 }
429
430 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800431out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800432 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800433out:
434 return err;
435}
436
437static int mem_get_config(char *name, char *str, int size, char **error_out)
438{
439 char buf[sizeof("18446744073709551615")];
440 int len = 0;
441
442 sprintf(buf, "%ld", uml_physmem);
443 CONFIG_CHUNK(str, size, len, buf, 1);
444
445 return len;
446}
447
448static int mem_id(char **str, int *start_out, int *end_out)
449{
450 *start_out = 0;
451 *end_out = 0;
452
453 return 0;
454}
455
Jeff Dikef28169d2007-02-10 01:43:53 -0800456static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800457{
Jeff Dikef28169d2007-02-10 01:43:53 -0800458 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800459 return -EBUSY;
460}
461
462static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800463 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800464 .name = "mem",
465 .config = mem_config,
466 .get_config = mem_get_config,
467 .id = mem_id,
468 .remove = mem_remove,
469};
470
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700471static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800472{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700473 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800474 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700475 else printk(KERN_ERR "Can't release memory to the host - memory "
476 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800477 return 0;
478}
479
480__initcall(mem_mc_init);
481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482#define CONFIG_BUF_SIZE 64
483
Jeff Diked50084a2006-01-06 00:18:50 -0800484static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 char **),
486 struct mc_request *req, char *name)
487{
488 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
489 int n, size;
490
Jeff Dikeae2587e2007-10-16 01:26:57 -0700491 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 mconsole_reply(req, "No get_config routine defined", 1, 0);
493 return;
494 }
495
496 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700497 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 buf = default_buf;
499
Jeff Dikeae2587e2007-10-16 01:26:57 -0700500 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700502 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 mconsole_reply(req, error, 1, 0);
504 goto out;
505 }
506
Jeff Dikeae2587e2007-10-16 01:26:57 -0700507 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 mconsole_reply(req, buf, 0, 0);
509 goto out;
510 }
511
Jeff Dikeae2587e2007-10-16 01:26:57 -0700512 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 kfree(buf);
514
515 size = n;
516 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700517 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
519 return;
520 }
521 }
522 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700523 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525}
526
527void mconsole_config(struct mc_request *req)
528{
529 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800530 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 int err;
532
533 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800534 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700536 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 mconsole_reply(req, "Bad configuration option", 1, 0);
538 return;
539 }
540
541 name = &ptr[strlen(dev->name)];
542 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700543 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 ptr++;
545
Jeff Dikeae2587e2007-10-16 01:26:57 -0700546 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800547 err = (*dev->config)(name, &error_string);
548 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 }
550 else mconsole_get_config(dev->get_config, req, name);
551}
552
553void mconsole_remove(struct mc_request *req)
554{
Jeff Diked50084a2006-01-06 00:18:50 -0800555 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700556 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800557 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700558 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800561 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700563 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 mconsole_reply(req, "Bad remove option", 1, 0);
565 return;
566 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700567
Jeff Dike3a331a52006-01-06 00:19:05 -0800568 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700569
Jeff Dike3a331a52006-01-06 00:19:05 -0800570 err = 1;
571 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700572 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800573 err_msg = "Couldn't parse device number";
574 goto out;
575 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700576 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800577 sprintf(error, "Invalid device number - must be between "
578 "%d and %d", start, end);
579 err_msg = error;
580 goto out;
581 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700582
Jeff Dikef28169d2007-02-10 01:43:53 -0800583 err_msg = NULL;
584 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700585 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700586 case 0:
587 err_msg = "";
588 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800589 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700590 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800591 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800592 break;
593 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700594 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800595 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800596 break;
597 default:
598 break;
599 }
600out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700601 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602}
603
Jeff Dikef92afe52006-09-29 01:58:52 -0700604struct mconsole_output {
605 struct list_head list;
606 struct mc_request *req;
607};
608
Jeff Dike84f48d42007-02-10 01:44:01 -0800609static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800610static LIST_HEAD(clients);
611static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800612
613static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700614 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800615{
616 struct list_head *ele;
617 int n;
618
Jeff Dikeae2587e2007-10-16 01:26:57 -0700619 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800620 return;
621
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700622 while (len > 0) {
623 n = min((size_t) len, ARRAY_SIZE(console_buf));
624 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800625 string += n;
626 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800627
Jeff Dikeae2587e2007-10-16 01:26:57 -0700628 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700629 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800630
Jeff Dikef92afe52006-09-29 01:58:52 -0700631 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700632 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800633 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800634 }
635}
636
637static struct console mc_console = { .name = "mc",
638 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800639 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800640 .index = -1 };
641
642static int mc_add_console(void)
643{
644 register_console(&mc_console);
645 return 0;
646}
647
648late_initcall(mc_add_console);
649
650static void with_console(struct mc_request *req, void (*proc)(void *),
651 void *arg)
652{
Jeff Dikef92afe52006-09-29 01:58:52 -0700653 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800654 unsigned long flags;
655
Jeff Dikef92afe52006-09-29 01:58:52 -0700656 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800657 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800658 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800659 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800660
661 (*proc)(arg);
662
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700663 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800664
Jeff Dike84f48d42007-02-10 01:44:01 -0800665 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800666 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800667 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800668}
669
Jeff Dike4111b022006-01-06 00:19:05 -0800670#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700671
672#include <linux/sysrq.h>
673
Jeff Dike4111b022006-01-06 00:19:05 -0800674static void sysrq_proc(void *arg)
675{
676 char *op = arg;
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700677 handle_sysrq(*op);
Jeff Dike4111b022006-01-06 00:19:05 -0800678}
679
680void mconsole_sysrq(struct mc_request *req)
681{
682 char *ptr = req->request.data;
683
684 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800685 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800686
Jeff Dikeae2587e2007-10-16 01:26:57 -0700687 /*
688 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800689 * so in this case, we reply first.
690 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700691 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800692 mconsole_reply(req, "", 0, 0);
693
694 with_console(req, sysrq_proc, ptr);
695}
696#else
697void mconsole_sysrq(struct mc_request *req)
698{
699 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
700}
701#endif
702
Jeff Dike6f517d32006-01-06 00:19:04 -0800703static void stack_proc(void *arg)
704{
705 struct task_struct *from = current, *to = arg;
706
707 to->thread.saved_task = from;
708 switch_to(from, to, from);
709}
710
Jeff Dikeae2587e2007-10-16 01:26:57 -0700711/*
712 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700713 * Added by Allan Graves, Jeff Dike
714 * Dumps a stacks registers to the linux console.
715 * Usage stack <pid>.
716 */
Jeff Dike42fda662007-10-16 01:26:50 -0700717void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700718{
Jeff Dike3a331a52006-01-06 00:19:05 -0800719 char *ptr = req->request.data;
720 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700721 struct task_struct *to = NULL;
722
Jeff Dikeae2587e2007-10-16 01:26:57 -0700723 /*
724 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800725 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700726 * 2) Add a way to stack dump all pids.
727 */
728
Jeff Dike3a331a52006-01-06 00:19:05 -0800729 ptr += strlen("stack");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800730 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700731
Jeff Dikeae2587e2007-10-16 01:26:57 -0700732 /*
733 * Should really check for multiple pids or reject bad args here
734 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800735 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700736 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800737 mconsole_reply(req, "Please specify a pid", 1, 0);
738 return;
739 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700740
Jeff Dike827b3f62008-02-04 22:31:29 -0800741 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700742 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800743 mconsole_reply(req, "Couldn't find that pid", 1, 0);
744 return;
745 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800746 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700748
Jeff Dikeae2587e2007-10-16 01:26:57 -0700749/*
750 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 * active.
752 */
Jeff Diked50084a2006-01-06 00:18:50 -0800753static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700755static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756{
757 /* long to avoid size mismatch warnings from gcc */
758 long sock;
759 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800760 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Jeff Dikeae2587e2007-10-16 01:26:57 -0700762 if (umid_file_name("mconsole", file, sizeof(file)))
763 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
765
766 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700767 if (sock < 0) {
768 printk(KERN_ERR "Failed to initialize management console\n");
769 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 }
Jeff Dike438ee672008-02-04 22:31:19 -0800771 if (os_set_fd_block(sock, 0))
772 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
774 register_reboot_notifier(&reboot_notifier);
775
776 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Yong Zhangc0b79a92011-09-22 16:58:46 +0800777 IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700779 if (err) {
780 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800781 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 }
783
Jeff Dikeae2587e2007-10-16 01:26:57 -0700784 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800785 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700786 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800788 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 strlen(mconsole_socket_name) + 1);
790 else printk(KERN_ERR "mconsole_setup failed to strdup "
791 "string\n");
792 }
793
Jeff Dikeae2587e2007-10-16 01:26:57 -0700794 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700796 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800797
798 out:
799 os_close_file(sock);
800 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801}
802
803__initcall(mconsole_init);
804
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800805static ssize_t mconsole_proc_write(struct file *file,
806 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807{
808 char *buf;
809
810 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700811 if (buf == NULL)
812 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Jeff Dikeae2587e2007-10-16 01:26:57 -0700814 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 count = -EFAULT;
816 goto out;
817 }
818
819 buf[count] = '\0';
820
821 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
822 out:
823 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700824 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800827static const struct file_operations mconsole_proc_fops = {
828 .owner = THIS_MODULE,
829 .write = mconsole_proc_write,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200830 .llseek = noop_llseek,
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800831};
832
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833static int create_proc_mconsole(void)
834{
835 struct proc_dir_entry *ent;
836
Jeff Dikeae2587e2007-10-16 01:26:57 -0700837 if (notify_socket == NULL)
838 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800840 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700841 if (ent == NULL) {
842 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
843 "failed\n");
844 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700846 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847}
848
849static DEFINE_SPINLOCK(notify_spinlock);
850
851void lock_notify(void)
852{
853 spin_lock(&notify_spinlock);
854}
855
856void unlock_notify(void)
857{
858 spin_unlock(&notify_spinlock);
859}
860
861__initcall(create_proc_mconsole);
862
Jeff Dike088bec42007-10-16 01:27:20 -0700863#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
865static int mconsole_setup(char *str)
866{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700867 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 str += strlen(NOTIFY);
869 notify_socket = str;
870 }
871 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700872 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873}
874
Jeff Dike088bec42007-10-16 01:27:20 -0700875__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
877__uml_help(mconsole_setup,
878"mconsole=notify:<socket>\n"
879" Requests that the mconsole driver send a message to the named Unix\n"
880" socket containing the name of the mconsole socket. This also serves\n"
881" to notify outside processes when UML has booted far enough to respond\n"
882" to mconsole requests.\n\n"
883);
884
885static int notify_panic(struct notifier_block *self, unsigned long unused1,
886 void *ptr)
887{
888 char *message = ptr;
889
Jeff Dikeae2587e2007-10-16 01:26:57 -0700890 if (notify_socket == NULL)
891 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
Jeff Diked50084a2006-01-06 00:18:50 -0800893 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700895 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896}
897
898static struct notifier_block panic_exit_notifier = {
899 .notifier_call = notify_panic,
900 .next = NULL,
901 .priority = 1
902};
903
904static int add_notifier(void)
905{
Alan Sterne041c682006-03-27 01:16:30 -0800906 atomic_notifier_chain_register(&panic_notifier_list,
907 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700908 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909}
910
911__initcall(add_notifier);
912
913char *mconsole_notify_socket(void)
914{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700915 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916}
917
918EXPORT_SYMBOL(mconsole_notify_socket);