blob: 8f44ebb0dec82286e85838369d8fdfd9de929ae6 [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
Jan Blunck4ac91372008-02-14 19:34:32 -0800164 file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700165 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 mconsole_reply(req, "Failed to open file", 1, 0);
167 goto out_kill;
168 }
169 /*END*/
170
171 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700172 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
174 goto out_fput;
175 }
176
Jeff Dikeae2587e2007-10-16 01:26:57 -0700177 if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 do {
179 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
180 &file->f_pos);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700181 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 buf[n] = '\0';
183 mconsole_reply(req, buf, 0, (n > 0));
184 }
185 else {
186 mconsole_reply(req, "Read of file failed",
187 1, 0);
188 goto out_free;
189 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700190 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
192 else mconsole_reply(req, "", 0, 0);
193
194 out_free:
195 kfree(buf);
196 out_fput:
197 fput(file);
198 out_kill:
199 deactivate_super(super);
200 out: ;
201}
202#endif
203
204void mconsole_proc(struct mc_request *req)
205{
206 char path[64];
207 char *buf;
208 int len;
209 int fd;
210 int first_chunk = 1;
211 char *ptr = req->request.data;
212
213 ptr += strlen("proc");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700214 while (isspace(*ptr))
215 ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 snprintf(path, sizeof(path), "/proc/%s", ptr);
217
218 fd = sys_open(path, 0, 0);
219 if (fd < 0) {
220 mconsole_reply(req, "Failed to open file", 1, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700221 printk(KERN_ERR "open %s: %d\n",path,fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 goto out;
223 }
224
225 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700226 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
228 goto out_close;
229 }
230
231 for (;;) {
232 len = sys_read(fd, buf, PAGE_SIZE-1);
233 if (len < 0) {
234 mconsole_reply(req, "Read of file failed", 1, 0);
235 goto out_free;
236 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700237 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 if (first_chunk) {
239 mconsole_reply(req, "\n", 0, 1);
240 first_chunk = 0;
241 }
242 if (len == PAGE_SIZE-1) {
243 buf[len] = '\0';
244 mconsole_reply(req, buf, 0, 1);
245 } else {
246 buf[len] = '\0';
247 mconsole_reply(req, buf, 0, 0);
248 break;
249 }
250 }
251
252 out_free:
253 kfree(buf);
254 out_close:
255 sys_close(fd);
256 out:
257 /* nothing */;
258}
259
260#define UML_MCONSOLE_HELPTEXT \
261"Commands: \n\
262 version - Get kernel version \n\
263 help - Print this message \n\
264 halt - Halt UML \n\
265 reboot - Reboot UML \n\
266 config <dev>=<config> - Add a new device to UML; \n\
267 same syntax as command line \n\
268 config <dev> - Query the configuration of a device \n\
269 remove <dev> - Remove a device from UML \n\
270 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800271 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 stop - pause the UML; it will do nothing until it receives a 'go' \n\
273 go - continue the UML after a 'stop' \n\
274 log <string> - make UML enter <string> into the kernel log\n\
275 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700276 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277"
278
279void mconsole_help(struct mc_request *req)
280{
281 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
282}
283
284void mconsole_halt(struct mc_request *req)
285{
286 mconsole_reply(req, "", 0, 0);
287 machine_halt();
288}
289
290void mconsole_reboot(struct mc_request *req)
291{
292 mconsole_reply(req, "", 0, 0);
293 machine_restart(NULL);
294}
295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296void mconsole_cad(struct mc_request *req)
297{
298 mconsole_reply(req, "", 0, 0);
299 ctrl_alt_del();
300}
301
302void mconsole_go(struct mc_request *req)
303{
304 mconsole_reply(req, "Not stopped", 1, 0);
305}
306
307void mconsole_stop(struct mc_request *req)
308{
309 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
310 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100311 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800312 for (;;) {
313 if (!mconsole_get_request(req->originating_fd, req))
314 continue;
Al Viro3a512372006-10-24 11:15:29 +0100315 if (req->cmd->handler == mconsole_go)
316 break;
317 if (req->cmd->handler == mconsole_stop) {
318 mconsole_reply(req, "Already stopped", 1, 0);
319 continue;
320 }
321 if (req->cmd->handler == mconsole_sysrq) {
322 struct pt_regs *old_regs;
323 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
324 mconsole_sysrq(req);
325 set_irq_regs(old_regs);
326 continue;
327 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 (*req->cmd->handler)(req);
329 }
330 os_set_fd_block(req->originating_fd, 0);
331 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
332 mconsole_reply(req, "", 0, 0);
333}
334
Jeff Dike84f48d42007-02-10 01:44:01 -0800335static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800336static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338void mconsole_register_dev(struct mc_device *new)
339{
Jeff Dike84f48d42007-02-10 01:44:01 -0800340 spin_lock(&mc_devices_lock);
341 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800343 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344}
345
346static struct mc_device *mconsole_find_dev(char *name)
347{
348 struct list_head *ele;
349 struct mc_device *dev;
350
Jeff Dikeae2587e2007-10-16 01:26:57 -0700351 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700353 if (!strncmp(name, dev->name, strlen(dev->name)))
354 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700356 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357}
358
Jeff Dike02dea082006-03-31 02:30:08 -0800359#define UNPLUGGED_PER_PAGE \
360 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
361
362struct unplugged_pages {
363 struct list_head list;
364 void *pages[UNPLUGGED_PER_PAGE];
365};
366
Daniel Walkere98fa282008-02-04 22:31:27 -0800367static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800368static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800369static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800370static int unplug_index = UNPLUGGED_PER_PAGE;
371
Jeff Dikef28169d2007-02-10 01:43:53 -0800372static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800373{
374 unsigned long long diff;
375 int err = -EINVAL, i, add;
376 char *ret;
377
Jeff Dikeae2587e2007-10-16 01:26:57 -0700378 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800379 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800380 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800381 }
Jeff Dike02dea082006-03-31 02:30:08 -0800382
383 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700384 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800385 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700386 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800387 add = 1;
388 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800389 else {
390 *error_out = "Expected increment to start with '-' or '+'";
391 goto out;
392 }
Jeff Dike02dea082006-03-31 02:30:08 -0800393
394 str++;
395 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700396 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800397 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800398 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800399 }
Jeff Dike02dea082006-03-31 02:30:08 -0800400
401 diff /= PAGE_SIZE;
402
Daniel Walkere98fa282008-02-04 22:31:27 -0800403 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700404 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800405 struct unplugged_pages *unplugged;
406 void *addr;
407
Jeff Dikeae2587e2007-10-16 01:26:57 -0700408 if (add) {
409 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800410 break;
411
412 unplugged = list_entry(unplugged_pages.next,
413 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700414 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800415 addr = unplugged->pages[--unplug_index];
416 else {
417 list_del(&unplugged->list);
418 addr = unplugged;
419 unplug_index = UNPLUGGED_PER_PAGE;
420 }
421
422 free_page((unsigned long) addr);
423 unplugged_pages_count--;
424 }
425 else {
426 struct page *page;
427
428 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700429 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800430 break;
431
432 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700433 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800434 list_add(&unplugged->list, &unplugged_pages);
435 unplug_index = 0;
436 }
437 else {
438 struct list_head *entry = unplugged_pages.next;
439 addr = unplugged;
440
441 unplugged = list_entry(entry,
442 struct unplugged_pages,
443 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800444 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700445 if (err) {
446 printk(KERN_ERR "Failed to release "
447 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800448 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800449 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800450 }
451 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800452 }
453
454 unplugged_pages_count++;
455 }
456 }
457
458 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800459out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800460 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800461out:
462 return err;
463}
464
465static int mem_get_config(char *name, char *str, int size, char **error_out)
466{
467 char buf[sizeof("18446744073709551615")];
468 int len = 0;
469
470 sprintf(buf, "%ld", uml_physmem);
471 CONFIG_CHUNK(str, size, len, buf, 1);
472
473 return len;
474}
475
476static int mem_id(char **str, int *start_out, int *end_out)
477{
478 *start_out = 0;
479 *end_out = 0;
480
481 return 0;
482}
483
Jeff Dikef28169d2007-02-10 01:43:53 -0800484static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800485{
Jeff Dikef28169d2007-02-10 01:43:53 -0800486 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800487 return -EBUSY;
488}
489
490static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800491 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800492 .name = "mem",
493 .config = mem_config,
494 .get_config = mem_get_config,
495 .id = mem_id,
496 .remove = mem_remove,
497};
498
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700499static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800500{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700501 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800502 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700503 else printk(KERN_ERR "Can't release memory to the host - memory "
504 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800505 return 0;
506}
507
508__initcall(mem_mc_init);
509
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510#define CONFIG_BUF_SIZE 64
511
Jeff Diked50084a2006-01-06 00:18:50 -0800512static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 char **),
514 struct mc_request *req, char *name)
515{
516 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
517 int n, size;
518
Jeff Dikeae2587e2007-10-16 01:26:57 -0700519 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 mconsole_reply(req, "No get_config routine defined", 1, 0);
521 return;
522 }
523
524 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700525 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 buf = default_buf;
527
Jeff Dikeae2587e2007-10-16 01:26:57 -0700528 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700530 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 mconsole_reply(req, error, 1, 0);
532 goto out;
533 }
534
Jeff Dikeae2587e2007-10-16 01:26:57 -0700535 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 mconsole_reply(req, buf, 0, 0);
537 goto out;
538 }
539
Jeff Dikeae2587e2007-10-16 01:26:57 -0700540 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 kfree(buf);
542
543 size = n;
544 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700545 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
547 return;
548 }
549 }
550 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700551 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553}
554
555void mconsole_config(struct mc_request *req)
556{
557 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800558 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 int err;
560
561 ptr += strlen("config");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700562 while (isspace(*ptr))
563 ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700565 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 mconsole_reply(req, "Bad configuration option", 1, 0);
567 return;
568 }
569
570 name = &ptr[strlen(dev->name)];
571 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700572 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 ptr++;
574
Jeff Dikeae2587e2007-10-16 01:26:57 -0700575 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800576 err = (*dev->config)(name, &error_string);
577 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
579 else mconsole_get_config(dev->get_config, req, name);
580}
581
582void mconsole_remove(struct mc_request *req)
583{
Jeff Diked50084a2006-01-06 00:18:50 -0800584 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700585 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800586 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700587 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
589 ptr += strlen("remove");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700590 while (isspace(*ptr)) ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700592 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 mconsole_reply(req, "Bad remove option", 1, 0);
594 return;
595 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700596
Jeff Dike3a331a52006-01-06 00:19:05 -0800597 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700598
Jeff Dike3a331a52006-01-06 00:19:05 -0800599 err = 1;
600 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700601 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800602 err_msg = "Couldn't parse device number";
603 goto out;
604 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700605 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800606 sprintf(error, "Invalid device number - must be between "
607 "%d and %d", start, end);
608 err_msg = error;
609 goto out;
610 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700611
Jeff Dikef28169d2007-02-10 01:43:53 -0800612 err_msg = NULL;
613 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700614 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700615 case 0:
616 err_msg = "";
617 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800618 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700619 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800620 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800621 break;
622 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700623 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800624 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800625 break;
626 default:
627 break;
628 }
629out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700630 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
Jeff Dikef92afe52006-09-29 01:58:52 -0700633struct mconsole_output {
634 struct list_head list;
635 struct mc_request *req;
636};
637
Jeff Dike84f48d42007-02-10 01:44:01 -0800638static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800639static LIST_HEAD(clients);
640static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800641
642static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700643 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800644{
645 struct list_head *ele;
646 int n;
647
Jeff Dikeae2587e2007-10-16 01:26:57 -0700648 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800649 return;
650
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700651 while (len > 0) {
652 n = min((size_t) len, ARRAY_SIZE(console_buf));
653 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800654 string += n;
655 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800656
Jeff Dikeae2587e2007-10-16 01:26:57 -0700657 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700658 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800659
Jeff Dikef92afe52006-09-29 01:58:52 -0700660 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700661 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800662 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800663 }
664}
665
666static struct console mc_console = { .name = "mc",
667 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800668 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800669 .index = -1 };
670
671static int mc_add_console(void)
672{
673 register_console(&mc_console);
674 return 0;
675}
676
677late_initcall(mc_add_console);
678
679static void with_console(struct mc_request *req, void (*proc)(void *),
680 void *arg)
681{
Jeff Dikef92afe52006-09-29 01:58:52 -0700682 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800683 unsigned long flags;
684
Jeff Dikef92afe52006-09-29 01:58:52 -0700685 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800686 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800687 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800688 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800689
690 (*proc)(arg);
691
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700692 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800693
Jeff Dike84f48d42007-02-10 01:44:01 -0800694 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800695 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800696 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800697}
698
Jeff Dike4111b022006-01-06 00:19:05 -0800699#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700700
701#include <linux/sysrq.h>
702
Jeff Dike4111b022006-01-06 00:19:05 -0800703static void sysrq_proc(void *arg)
704{
705 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100706 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800707}
708
709void mconsole_sysrq(struct mc_request *req)
710{
711 char *ptr = req->request.data;
712
713 ptr += strlen("sysrq");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700714 while (isspace(*ptr)) ptr++;
Jeff Dike4111b022006-01-06 00:19:05 -0800715
Jeff Dikeae2587e2007-10-16 01:26:57 -0700716 /*
717 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800718 * so in this case, we reply first.
719 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700720 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800721 mconsole_reply(req, "", 0, 0);
722
723 with_console(req, sysrq_proc, ptr);
724}
725#else
726void mconsole_sysrq(struct mc_request *req)
727{
728 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
729}
730#endif
731
Jeff Dike6f517d32006-01-06 00:19:04 -0800732static void stack_proc(void *arg)
733{
734 struct task_struct *from = current, *to = arg;
735
736 to->thread.saved_task = from;
737 switch_to(from, to, from);
738}
739
Jeff Dikeae2587e2007-10-16 01:26:57 -0700740/*
741 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700742 * Added by Allan Graves, Jeff Dike
743 * Dumps a stacks registers to the linux console.
744 * Usage stack <pid>.
745 */
Jeff Dike42fda662007-10-16 01:26:50 -0700746void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747{
Jeff Dike3a331a52006-01-06 00:19:05 -0800748 char *ptr = req->request.data;
749 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700750 struct task_struct *to = NULL;
751
Jeff Dikeae2587e2007-10-16 01:26:57 -0700752 /*
753 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800754 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700755 * 2) Add a way to stack dump all pids.
756 */
757
Jeff Dike3a331a52006-01-06 00:19:05 -0800758 ptr += strlen("stack");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700759 while (isspace(*ptr))
760 ptr++;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700761
Jeff Dikeae2587e2007-10-16 01:26:57 -0700762 /*
763 * Should really check for multiple pids or reject bad args here
764 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800765 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700766 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800767 mconsole_reply(req, "Please specify a pid", 1, 0);
768 return;
769 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700770
Jeff Dike827b3f62008-02-04 22:31:29 -0800771 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700772 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800773 mconsole_reply(req, "Couldn't find that pid", 1, 0);
774 return;
775 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800776 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700777}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700778
Jeff Dikeae2587e2007-10-16 01:26:57 -0700779/*
780 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 * active.
782 */
Jeff Diked50084a2006-01-06 00:18:50 -0800783static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700785static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786{
787 /* long to avoid size mismatch warnings from gcc */
788 long sock;
789 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800790 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Jeff Dikeae2587e2007-10-16 01:26:57 -0700792 if (umid_file_name("mconsole", file, sizeof(file)))
793 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
795
796 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700797 if (sock < 0) {
798 printk(KERN_ERR "Failed to initialize management console\n");
799 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 }
Jeff Dike438ee672008-02-04 22:31:19 -0800801 if (os_set_fd_block(sock, 0))
802 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
804 register_reboot_notifier(&reboot_notifier);
805
806 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700807 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700809 if (err) {
810 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800811 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 }
813
Jeff Dikeae2587e2007-10-16 01:26:57 -0700814 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800815 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700816 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800818 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 strlen(mconsole_socket_name) + 1);
820 else printk(KERN_ERR "mconsole_setup failed to strdup "
821 "string\n");
822 }
823
Jeff Dikeae2587e2007-10-16 01:26:57 -0700824 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700826 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800827
828 out:
829 os_close_file(sock);
830 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831}
832
833__initcall(mconsole_init);
834
835static int write_proc_mconsole(struct file *file, const char __user *buffer,
836 unsigned long count, void *data)
837{
838 char *buf;
839
840 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700841 if (buf == NULL)
842 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Jeff Dikeae2587e2007-10-16 01:26:57 -0700844 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 count = -EFAULT;
846 goto out;
847 }
848
849 buf[count] = '\0';
850
851 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
852 out:
853 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700854 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855}
856
857static int create_proc_mconsole(void)
858{
859 struct proc_dir_entry *ent;
860
Jeff Dikeae2587e2007-10-16 01:26:57 -0700861 if (notify_socket == NULL)
862 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
864 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700865 if (ent == NULL) {
866 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
867 "failed\n");
868 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 }
870
871 ent->read_proc = NULL;
872 ent->write_proc = write_proc_mconsole;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700873 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874}
875
876static DEFINE_SPINLOCK(notify_spinlock);
877
878void lock_notify(void)
879{
880 spin_lock(&notify_spinlock);
881}
882
883void unlock_notify(void)
884{
885 spin_unlock(&notify_spinlock);
886}
887
888__initcall(create_proc_mconsole);
889
Jeff Dike088bec42007-10-16 01:27:20 -0700890#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
892static int mconsole_setup(char *str)
893{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700894 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 str += strlen(NOTIFY);
896 notify_socket = str;
897 }
898 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700899 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900}
901
Jeff Dike088bec42007-10-16 01:27:20 -0700902__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
904__uml_help(mconsole_setup,
905"mconsole=notify:<socket>\n"
906" Requests that the mconsole driver send a message to the named Unix\n"
907" socket containing the name of the mconsole socket. This also serves\n"
908" to notify outside processes when UML has booted far enough to respond\n"
909" to mconsole requests.\n\n"
910);
911
912static int notify_panic(struct notifier_block *self, unsigned long unused1,
913 void *ptr)
914{
915 char *message = ptr;
916
Jeff Dikeae2587e2007-10-16 01:26:57 -0700917 if (notify_socket == NULL)
918 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
Jeff Diked50084a2006-01-06 00:18:50 -0800920 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700922 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923}
924
925static struct notifier_block panic_exit_notifier = {
926 .notifier_call = notify_panic,
927 .next = NULL,
928 .priority = 1
929};
930
931static int add_notifier(void)
932{
Alan Sterne041c682006-03-27 01:16:30 -0800933 atomic_notifier_chain_register(&panic_notifier_list,
934 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700935 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936}
937
938__initcall(add_notifier);
939
940char *mconsole_notify_socket(void)
941{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700942 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943}
944
945EXPORT_SYMBOL(mconsole_notify_socket);