blob: 9820fdbb9c73488560ea9df6a25ba058d9ca3292 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
Jeff Dikeae2587e2007-10-16 01:26:57 -07003 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Licensed under the GPL
5 */
6
Jeff Dikeae2587e2007-10-16 01:26:57 -07007#include "linux/console.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include "linux/ctype.h"
9#include "linux/interrupt.h"
Jeff Dike02dea082006-03-31 02:30:08 -080010#include "linux/list.h"
11#include "linux/mm.h"
Jeff Dikeae2587e2007-10-16 01:26:57 -070012#include "linux/module.h"
13#include "linux/notifier.h"
14#include "linux/reboot.h"
15#include "linux/proc_fs.h"
16#include "linux/slab.h"
17#include "linux/syscalls.h"
18#include "linux/utsname.h"
19#include "linux/workqueue.h"
Daniel Walkere98fa282008-02-04 22:31:27 -080020#include "linux/mutex.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include "asm/uaccess.h"
Jeff Dikeae2587e2007-10-16 01:26:57 -070022#include "init.h"
23#include "irq_kern.h"
24#include "irq_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include "mconsole.h"
27#include "mconsole_kern.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Jeff Diked50084a2006-01-06 00:18:50 -080030static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 unsigned long what, void *data)
32{
Jeff Dikeae2587e2007-10-16 01:26:57 -070033 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070034}
35
36
37static struct notifier_block reboot_notifier = {
38 .notifier_call = do_unlink_socket,
39 .priority = 0,
40};
41
Jeff Diked50084a2006-01-06 00:18:50 -080042/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 * locking, and the interrupt handler is safe because it can't interrupt
44 * itself and it can only happen on CPU 0.
45 */
46
Jeff Dike90107722006-01-06 00:18:54 -080047static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
David Howells6d5aefb2006-12-05 19:36:26 +000049static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070050{
51 struct mconsole_entry *req;
52 unsigned long flags;
53
Jeff Dikeae2587e2007-10-16 01:26:57 -070054 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070055 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070056 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 list_del(&req->list);
58 local_irq_restore(flags);
59 req->request.cmd->handler(&req->request);
60 kfree(req);
61 }
62}
63
David Howells6d5aefb2006-12-05 19:36:26 +000064static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Al Viro7bea96f2006-10-08 22:49:34 +010066static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070067{
68 /* long to avoid size mismatch warnings from gcc */
69 long fd;
70 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010071 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070074 while (mconsole_get_request(fd, &req)) {
75 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 (*req.cmd->handler)(&req);
77 else {
Jeff Dike60baa152006-04-10 22:53:28 -070078 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070079 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 mconsole_reply(&req, "Out of memory", 1, 0);
81 else {
82 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010083 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 list_add(&new->list, &mc_requests);
85 }
86 }
87 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070088 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 schedule_work(&mconsole_work);
90 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -070091 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092}
93
94void mconsole_version(struct mc_request *req)
95{
96 char version[256];
97
Serge E. Hallyne9ff3992006-10-02 02:18:11 -070098 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -070099 utsname()->nodename, utsname()->release, utsname()->version,
100 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 mconsole_reply(req, version, 0, 0);
102}
103
104void mconsole_log(struct mc_request *req)
105{
106 int len;
107 char *ptr = req->request.data;
108
109 ptr += strlen("log ");
110
111 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700112 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 mconsole_reply(req, "", 0, 0);
114}
115
116/* This is a more convoluted version of mconsole_proc, which has some stability
117 * problems; however, we need it fixed, because it is expected that UML users
118 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
119 * show the real procfs content, not the ones from hppfs.*/
120#if 0
121void mconsole_proc(struct mc_request *req)
122{
123 struct nameidata nd;
124 struct file_system_type *proc;
125 struct super_block *super;
126 struct file *file;
127 int n, err;
128 char *ptr = req->request.data, *buf;
129
130 ptr += strlen("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700131 while (isspace(*ptr)) ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 proc = get_fs_type("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700134 if (proc == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 mconsole_reply(req, "procfs not registered", 1, 0);
136 goto out;
137 }
138
139 super = (*proc->get_sb)(proc, 0, NULL, NULL);
140 put_filesystem(proc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700141 if (super == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
143 goto out;
144 }
145 up_write(&super->s_umount);
146
147 nd.dentry = super->s_root;
148 nd.mnt = NULL;
149 nd.flags = O_RDONLY + 1;
150 nd.last_type = LAST_ROOT;
151
152 /* START: it was experienced that the stability problems are closed
153 * if commenting out these two calls + the below read cycle. To
154 * make UML crash again, it was enough to readd either one.*/
155 err = link_path_walk(ptr, &nd);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700156 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 mconsole_reply(req, "Failed to look up file", 1, 0);
158 goto out_kill;
159 }
160
161 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700162 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 mconsole_reply(req, "Failed to open file", 1, 0);
164 goto out_kill;
165 }
166 /*END*/
167
168 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700169 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
171 goto out_fput;
172 }
173
Jeff Dikeae2587e2007-10-16 01:26:57 -0700174 if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 do {
176 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
177 &file->f_pos);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700178 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 buf[n] = '\0';
180 mconsole_reply(req, buf, 0, (n > 0));
181 }
182 else {
183 mconsole_reply(req, "Read of file failed",
184 1, 0);
185 goto out_free;
186 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700187 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 }
189 else mconsole_reply(req, "", 0, 0);
190
191 out_free:
192 kfree(buf);
193 out_fput:
194 fput(file);
195 out_kill:
196 deactivate_super(super);
197 out: ;
198}
199#endif
200
201void mconsole_proc(struct mc_request *req)
202{
203 char path[64];
204 char *buf;
205 int len;
206 int fd;
207 int first_chunk = 1;
208 char *ptr = req->request.data;
209
210 ptr += strlen("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700211 while (isspace(*ptr))
212 ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 snprintf(path, sizeof(path), "/proc/%s", ptr);
214
215 fd = sys_open(path, 0, 0);
216 if (fd < 0) {
217 mconsole_reply(req, "Failed to open file", 1, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700218 printk(KERN_ERR "open %s: %d\n",path,fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 goto out;
220 }
221
222 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700223 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
225 goto out_close;
226 }
227
228 for (;;) {
229 len = sys_read(fd, buf, PAGE_SIZE-1);
230 if (len < 0) {
231 mconsole_reply(req, "Read of file failed", 1, 0);
232 goto out_free;
233 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700234 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 if (first_chunk) {
236 mconsole_reply(req, "\n", 0, 1);
237 first_chunk = 0;
238 }
239 if (len == PAGE_SIZE-1) {
240 buf[len] = '\0';
241 mconsole_reply(req, buf, 0, 1);
242 } else {
243 buf[len] = '\0';
244 mconsole_reply(req, buf, 0, 0);
245 break;
246 }
247 }
248
249 out_free:
250 kfree(buf);
251 out_close:
252 sys_close(fd);
253 out:
254 /* nothing */;
255}
256
257#define UML_MCONSOLE_HELPTEXT \
258"Commands: \n\
259 version - Get kernel version \n\
260 help - Print this message \n\
261 halt - Halt UML \n\
262 reboot - Reboot UML \n\
263 config <dev>=<config> - Add a new device to UML; \n\
264 same syntax as command line \n\
265 config <dev> - Query the configuration of a device \n\
266 remove <dev> - Remove a device from UML \n\
267 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800268 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 stop - pause the UML; it will do nothing until it receives a 'go' \n\
270 go - continue the UML after a 'stop' \n\
271 log <string> - make UML enter <string> into the kernel log\n\
272 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700273 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274"
275
276void mconsole_help(struct mc_request *req)
277{
278 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
279}
280
281void mconsole_halt(struct mc_request *req)
282{
283 mconsole_reply(req, "", 0, 0);
284 machine_halt();
285}
286
287void mconsole_reboot(struct mc_request *req)
288{
289 mconsole_reply(req, "", 0, 0);
290 machine_restart(NULL);
291}
292
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293void mconsole_cad(struct mc_request *req)
294{
295 mconsole_reply(req, "", 0, 0);
296 ctrl_alt_del();
297}
298
299void mconsole_go(struct mc_request *req)
300{
301 mconsole_reply(req, "Not stopped", 1, 0);
302}
303
304void mconsole_stop(struct mc_request *req)
305{
306 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
307 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100308 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800309 for (;;) {
310 if (!mconsole_get_request(req->originating_fd, req))
311 continue;
Al Viro3a512372006-10-24 11:15:29 +0100312 if (req->cmd->handler == mconsole_go)
313 break;
314 if (req->cmd->handler == mconsole_stop) {
315 mconsole_reply(req, "Already stopped", 1, 0);
316 continue;
317 }
318 if (req->cmd->handler == mconsole_sysrq) {
319 struct pt_regs *old_regs;
320 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
321 mconsole_sysrq(req);
322 set_irq_regs(old_regs);
323 continue;
324 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 (*req->cmd->handler)(req);
326 }
327 os_set_fd_block(req->originating_fd, 0);
328 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
329 mconsole_reply(req, "", 0, 0);
330}
331
Jeff Dike84f48d42007-02-10 01:44:01 -0800332static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800333static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334
335void mconsole_register_dev(struct mc_device *new)
336{
Jeff Dike84f48d42007-02-10 01:44:01 -0800337 spin_lock(&mc_devices_lock);
338 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800340 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341}
342
343static struct mc_device *mconsole_find_dev(char *name)
344{
345 struct list_head *ele;
346 struct mc_device *dev;
347
Jeff Dikeae2587e2007-10-16 01:26:57 -0700348 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700350 if (!strncmp(name, dev->name, strlen(dev->name)))
351 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700353 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354}
355
Jeff Dike02dea082006-03-31 02:30:08 -0800356#define UNPLUGGED_PER_PAGE \
357 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
358
359struct unplugged_pages {
360 struct list_head list;
361 void *pages[UNPLUGGED_PER_PAGE];
362};
363
Daniel Walkere98fa282008-02-04 22:31:27 -0800364static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800365static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800366static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800367static int unplug_index = UNPLUGGED_PER_PAGE;
368
Jeff Dikef28169d2007-02-10 01:43:53 -0800369static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800370{
371 unsigned long long diff;
372 int err = -EINVAL, i, add;
373 char *ret;
374
Jeff Dikeae2587e2007-10-16 01:26:57 -0700375 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800376 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800377 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800378 }
Jeff Dike02dea082006-03-31 02:30:08 -0800379
380 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700381 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800382 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700383 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800384 add = 1;
385 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800386 else {
387 *error_out = "Expected increment to start with '-' or '+'";
388 goto out;
389 }
Jeff Dike02dea082006-03-31 02:30:08 -0800390
391 str++;
392 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700393 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800394 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800395 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800396 }
Jeff Dike02dea082006-03-31 02:30:08 -0800397
398 diff /= PAGE_SIZE;
399
Daniel Walkere98fa282008-02-04 22:31:27 -0800400 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700401 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800402 struct unplugged_pages *unplugged;
403 void *addr;
404
Jeff Dikeae2587e2007-10-16 01:26:57 -0700405 if (add) {
406 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800407 break;
408
409 unplugged = list_entry(unplugged_pages.next,
410 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700411 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800412 addr = unplugged->pages[--unplug_index];
413 else {
414 list_del(&unplugged->list);
415 addr = unplugged;
416 unplug_index = UNPLUGGED_PER_PAGE;
417 }
418
419 free_page((unsigned long) addr);
420 unplugged_pages_count--;
421 }
422 else {
423 struct page *page;
424
425 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700426 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800427 break;
428
429 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700430 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800431 list_add(&unplugged->list, &unplugged_pages);
432 unplug_index = 0;
433 }
434 else {
435 struct list_head *entry = unplugged_pages.next;
436 addr = unplugged;
437
438 unplugged = list_entry(entry,
439 struct unplugged_pages,
440 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800441 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700442 if (err) {
443 printk(KERN_ERR "Failed to release "
444 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800445 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800446 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800447 }
448 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800449 }
450
451 unplugged_pages_count++;
452 }
453 }
454
455 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800456out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800457 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800458out:
459 return err;
460}
461
462static int mem_get_config(char *name, char *str, int size, char **error_out)
463{
464 char buf[sizeof("18446744073709551615")];
465 int len = 0;
466
467 sprintf(buf, "%ld", uml_physmem);
468 CONFIG_CHUNK(str, size, len, buf, 1);
469
470 return len;
471}
472
473static int mem_id(char **str, int *start_out, int *end_out)
474{
475 *start_out = 0;
476 *end_out = 0;
477
478 return 0;
479}
480
Jeff Dikef28169d2007-02-10 01:43:53 -0800481static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800482{
Jeff Dikef28169d2007-02-10 01:43:53 -0800483 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800484 return -EBUSY;
485}
486
487static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800488 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800489 .name = "mem",
490 .config = mem_config,
491 .get_config = mem_get_config,
492 .id = mem_id,
493 .remove = mem_remove,
494};
495
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700496static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800497{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700498 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800499 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700500 else printk(KERN_ERR "Can't release memory to the host - memory "
501 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800502 return 0;
503}
504
505__initcall(mem_mc_init);
506
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507#define CONFIG_BUF_SIZE 64
508
Jeff Diked50084a2006-01-06 00:18:50 -0800509static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 char **),
511 struct mc_request *req, char *name)
512{
513 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
514 int n, size;
515
Jeff Dikeae2587e2007-10-16 01:26:57 -0700516 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 mconsole_reply(req, "No get_config routine defined", 1, 0);
518 return;
519 }
520
521 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700522 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 buf = default_buf;
524
Jeff Dikeae2587e2007-10-16 01:26:57 -0700525 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700527 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 mconsole_reply(req, error, 1, 0);
529 goto out;
530 }
531
Jeff Dikeae2587e2007-10-16 01:26:57 -0700532 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 mconsole_reply(req, buf, 0, 0);
534 goto out;
535 }
536
Jeff Dikeae2587e2007-10-16 01:26:57 -0700537 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 kfree(buf);
539
540 size = n;
541 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700542 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
544 return;
545 }
546 }
547 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700548 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550}
551
552void mconsole_config(struct mc_request *req)
553{
554 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800555 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 int err;
557
558 ptr += strlen("config");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700559 while (isspace(*ptr))
560 ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700562 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 mconsole_reply(req, "Bad configuration option", 1, 0);
564 return;
565 }
566
567 name = &ptr[strlen(dev->name)];
568 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700569 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 ptr++;
571
Jeff Dikeae2587e2007-10-16 01:26:57 -0700572 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800573 err = (*dev->config)(name, &error_string);
574 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
576 else mconsole_get_config(dev->get_config, req, name);
577}
578
579void mconsole_remove(struct mc_request *req)
580{
Jeff Diked50084a2006-01-06 00:18:50 -0800581 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700582 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800583 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700584 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
586 ptr += strlen("remove");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700587 while (isspace(*ptr)) ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700589 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 mconsole_reply(req, "Bad remove option", 1, 0);
591 return;
592 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700593
Jeff Dike3a331a52006-01-06 00:19:05 -0800594 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700595
Jeff Dike3a331a52006-01-06 00:19:05 -0800596 err = 1;
597 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700598 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800599 err_msg = "Couldn't parse device number";
600 goto out;
601 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700602 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800603 sprintf(error, "Invalid device number - must be between "
604 "%d and %d", start, end);
605 err_msg = error;
606 goto out;
607 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700608
Jeff Dikef28169d2007-02-10 01:43:53 -0800609 err_msg = NULL;
610 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700611 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700612 case 0:
613 err_msg = "";
614 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800615 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700616 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800617 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800618 break;
619 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700620 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800621 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800622 break;
623 default:
624 break;
625 }
626out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700627 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628}
629
Jeff Dikef92afe52006-09-29 01:58:52 -0700630struct mconsole_output {
631 struct list_head list;
632 struct mc_request *req;
633};
634
Jeff Dike84f48d42007-02-10 01:44:01 -0800635static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800636static LIST_HEAD(clients);
637static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800638
639static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700640 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800641{
642 struct list_head *ele;
643 int n;
644
Jeff Dikeae2587e2007-10-16 01:26:57 -0700645 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800646 return;
647
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700648 while (len > 0) {
649 n = min((size_t) len, ARRAY_SIZE(console_buf));
650 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800651 string += n;
652 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800653
Jeff Dikeae2587e2007-10-16 01:26:57 -0700654 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700655 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800656
Jeff Dikef92afe52006-09-29 01:58:52 -0700657 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700658 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800659 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800660 }
661}
662
663static struct console mc_console = { .name = "mc",
664 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800665 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800666 .index = -1 };
667
668static int mc_add_console(void)
669{
670 register_console(&mc_console);
671 return 0;
672}
673
674late_initcall(mc_add_console);
675
676static void with_console(struct mc_request *req, void (*proc)(void *),
677 void *arg)
678{
Jeff Dikef92afe52006-09-29 01:58:52 -0700679 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800680 unsigned long flags;
681
Jeff Dikef92afe52006-09-29 01:58:52 -0700682 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800683 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800684 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800685 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800686
687 (*proc)(arg);
688
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700689 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800690
Jeff Dike84f48d42007-02-10 01:44:01 -0800691 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800692 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800693 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800694}
695
Jeff Dike4111b022006-01-06 00:19:05 -0800696#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700697
698#include <linux/sysrq.h>
699
Jeff Dike4111b022006-01-06 00:19:05 -0800700static void sysrq_proc(void *arg)
701{
702 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100703 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800704}
705
706void mconsole_sysrq(struct mc_request *req)
707{
708 char *ptr = req->request.data;
709
710 ptr += strlen("sysrq");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700711 while (isspace(*ptr)) ptr++;
Jeff Dike4111b022006-01-06 00:19:05 -0800712
Jeff Dikeae2587e2007-10-16 01:26:57 -0700713 /*
714 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800715 * so in this case, we reply first.
716 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700717 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800718 mconsole_reply(req, "", 0, 0);
719
720 with_console(req, sysrq_proc, ptr);
721}
722#else
723void mconsole_sysrq(struct mc_request *req)
724{
725 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
726}
727#endif
728
Jeff Dike6f517d32006-01-06 00:19:04 -0800729static void stack_proc(void *arg)
730{
731 struct task_struct *from = current, *to = arg;
732
733 to->thread.saved_task = from;
734 switch_to(from, to, from);
735}
736
Jeff Dikeae2587e2007-10-16 01:26:57 -0700737/*
738 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700739 * Added by Allan Graves, Jeff Dike
740 * Dumps a stacks registers to the linux console.
741 * Usage stack <pid>.
742 */
Jeff Dike42fda662007-10-16 01:26:50 -0700743void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700744{
Jeff Dike3a331a52006-01-06 00:19:05 -0800745 char *ptr = req->request.data;
746 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747 struct task_struct *to = NULL;
748
Jeff Dikeae2587e2007-10-16 01:26:57 -0700749 /*
750 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800751 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700752 * 2) Add a way to stack dump all pids.
753 */
754
Jeff Dike3a331a52006-01-06 00:19:05 -0800755 ptr += strlen("stack");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700756 while (isspace(*ptr))
757 ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700758
Jeff Dikeae2587e2007-10-16 01:26:57 -0700759 /*
760 * Should really check for multiple pids or reject bad args here
761 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800762 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700763 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800764 mconsole_reply(req, "Please specify a pid", 1, 0);
765 return;
766 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700767
Jeff Dike6f517d32006-01-06 00:19:04 -0800768 to = find_task_by_pid(pid_requested);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700769 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800770 mconsole_reply(req, "Couldn't find that pid", 1, 0);
771 return;
772 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800773 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700774}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700775
Jeff Dikeae2587e2007-10-16 01:26:57 -0700776/*
777 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 * active.
779 */
Jeff Diked50084a2006-01-06 00:18:50 -0800780static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700782static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783{
784 /* long to avoid size mismatch warnings from gcc */
785 long sock;
786 int err;
787 char file[256];
788
Jeff Dikeae2587e2007-10-16 01:26:57 -0700789 if (umid_file_name("mconsole", file, sizeof(file)))
790 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
792
793 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700794 if (sock < 0) {
795 printk(KERN_ERR "Failed to initialize management console\n");
796 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 }
Jeff Dike438ee672008-02-04 22:31:19 -0800798 if (os_set_fd_block(sock, 0))
799 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 register_reboot_notifier(&reboot_notifier);
802
803 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700804 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700806 if (err) {
807 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800808 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 }
810
Jeff Dikeae2587e2007-10-16 01:26:57 -0700811 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800812 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700813 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800815 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 strlen(mconsole_socket_name) + 1);
817 else printk(KERN_ERR "mconsole_setup failed to strdup "
818 "string\n");
819 }
820
Jeff Dikeae2587e2007-10-16 01:26:57 -0700821 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700823 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800824
825 out:
826 os_close_file(sock);
827 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828}
829
830__initcall(mconsole_init);
831
832static int write_proc_mconsole(struct file *file, const char __user *buffer,
833 unsigned long count, void *data)
834{
835 char *buf;
836
837 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700838 if (buf == NULL)
839 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Jeff Dikeae2587e2007-10-16 01:26:57 -0700841 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 count = -EFAULT;
843 goto out;
844 }
845
846 buf[count] = '\0';
847
848 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
849 out:
850 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700851 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852}
853
854static int create_proc_mconsole(void)
855{
856 struct proc_dir_entry *ent;
857
Jeff Dikeae2587e2007-10-16 01:26:57 -0700858 if (notify_socket == NULL)
859 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
861 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700862 if (ent == NULL) {
863 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
864 "failed\n");
865 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 }
867
868 ent->read_proc = NULL;
869 ent->write_proc = write_proc_mconsole;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700870 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871}
872
873static DEFINE_SPINLOCK(notify_spinlock);
874
875void lock_notify(void)
876{
877 spin_lock(&notify_spinlock);
878}
879
880void unlock_notify(void)
881{
882 spin_unlock(&notify_spinlock);
883}
884
885__initcall(create_proc_mconsole);
886
Jeff Dike088bec42007-10-16 01:27:20 -0700887#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
889static int mconsole_setup(char *str)
890{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700891 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 str += strlen(NOTIFY);
893 notify_socket = str;
894 }
895 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700896 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897}
898
Jeff Dike088bec42007-10-16 01:27:20 -0700899__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901__uml_help(mconsole_setup,
902"mconsole=notify:<socket>\n"
903" Requests that the mconsole driver send a message to the named Unix\n"
904" socket containing the name of the mconsole socket. This also serves\n"
905" to notify outside processes when UML has booted far enough to respond\n"
906" to mconsole requests.\n\n"
907);
908
909static int notify_panic(struct notifier_block *self, unsigned long unused1,
910 void *ptr)
911{
912 char *message = ptr;
913
Jeff Dikeae2587e2007-10-16 01:26:57 -0700914 if (notify_socket == NULL)
915 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Jeff Diked50084a2006-01-06 00:18:50 -0800917 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700919 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920}
921
922static struct notifier_block panic_exit_notifier = {
923 .notifier_call = notify_panic,
924 .next = NULL,
925 .priority = 1
926};
927
928static int add_notifier(void)
929{
Alan Sterne041c682006-03-27 01:16:30 -0800930 atomic_notifier_chain_register(&panic_notifier_list,
931 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700932 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933}
934
935__initcall(add_notifier);
936
937char *mconsole_notify_socket(void)
938{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700939 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940}
941
942EXPORT_SYMBOL(mconsole_notify_socket);