blob: e672bd6d43e3b2aa72b4c1c51029db9510233f52 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
Jeff Dike827b3f62008-02-04 22:31:29 -08003 * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Licensed under the GPL
5 */
6
Jeff Dike827b3f62008-02-04 22:31:29 -08007#include <linux/console.h>
8#include <linux/ctype.h>
André Goddard Rosae7d28602009-12-14 18:01:06 -08009#include <linux/string.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080010#include <linux/interrupt.h>
11#include <linux/list.h>
12#include <linux/mm.h>
13#include <linux/module.h>
14#include <linux/notifier.h>
15#include <linux/reboot.h>
16#include <linux/proc_fs.h>
17#include <linux/slab.h>
18#include <linux/syscalls.h>
19#include <linux/utsname.h>
Balbir Singh36137122008-12-09 13:14:07 -080020#include <linux/socket.h>
21#include <linux/un.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080022#include <linux/workqueue.h>
23#include <linux/mutex.h>
24#include <asm/uaccess.h>
25
Jeff Dikeae2587e2007-10-16 01:26:57 -070026#include "init.h"
27#include "irq_kern.h"
28#include "irq_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include "mconsole.h"
31#include "mconsole_kern.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Jeff Diked50084a2006-01-06 00:18:50 -080034static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 unsigned long what, void *data)
36{
Jeff Dikeae2587e2007-10-16 01:26:57 -070037 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070038}
39
40
41static struct notifier_block reboot_notifier = {
42 .notifier_call = do_unlink_socket,
43 .priority = 0,
44};
45
Jeff Diked50084a2006-01-06 00:18:50 -080046/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 * locking, and the interrupt handler is safe because it can't interrupt
48 * itself and it can only happen on CPU 0.
49 */
50
Jeff Dike90107722006-01-06 00:18:54 -080051static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
David Howells6d5aefb2006-12-05 19:36:26 +000053static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054{
55 struct mconsole_entry *req;
56 unsigned long flags;
57
Jeff Dikeae2587e2007-10-16 01:26:57 -070058 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070059 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070060 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 list_del(&req->list);
62 local_irq_restore(flags);
63 req->request.cmd->handler(&req->request);
64 kfree(req);
65 }
66}
67
David Howells6d5aefb2006-12-05 19:36:26 +000068static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
Al Viro7bea96f2006-10-08 22:49:34 +010070static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
72 /* long to avoid size mismatch warnings from gcc */
73 long fd;
74 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010075 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070078 while (mconsole_get_request(fd, &req)) {
79 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 (*req.cmd->handler)(&req);
81 else {
Jeff Dike60baa152006-04-10 22:53:28 -070082 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070083 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 mconsole_reply(&req, "Out of memory", 1, 0);
85 else {
86 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010087 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 list_add(&new->list, &mc_requests);
89 }
90 }
91 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070092 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 schedule_work(&mconsole_work);
94 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -070095 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
98void mconsole_version(struct mc_request *req)
99{
100 char version[256];
101
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700102 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -0700103 utsname()->nodename, utsname()->release, utsname()->version,
104 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 mconsole_reply(req, version, 0, 0);
106}
107
108void mconsole_log(struct mc_request *req)
109{
110 int len;
111 char *ptr = req->request.data;
112
113 ptr += strlen("log ");
114
115 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700116 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 mconsole_reply(req, "", 0, 0);
118}
119
120/* This is a more convoluted version of mconsole_proc, which has some stability
121 * problems; however, we need it fixed, because it is expected that UML users
122 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
123 * show the real procfs content, not the ones from hppfs.*/
124#if 0
125void mconsole_proc(struct mc_request *req)
126{
Al Viro4ecf09f2009-12-24 00:44:44 -0500127 struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 struct file *file;
Al Viro73d049a2011-03-11 12:08:24 -0500129 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 char *ptr = req->request.data, *buf;
Al Viro4ecf09f2009-12-24 00:44:44 -0500131 mm_segment_t old_fs = get_fs();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800134 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Al Viro73d049a2011-03-11 12:08:24 -0500136 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700137 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 mconsole_reply(req, "Failed to open file", 1, 0);
Al Viro4ecf09f2009-12-24 00:44:44 -0500139 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
142 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700143 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
145 goto out_fput;
146 }
147
Al Viro4ecf09f2009-12-24 00:44:44 -0500148 if (file->f_op->read) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 do {
Al Viro4ecf09f2009-12-24 00:44:44 -0500150 loff_t pos;
151 set_fs(KERNEL_DS);
152 n = vfs_read(file, buf, PAGE_SIZE - 1, &pos);
153 file_pos_write(file, pos);
154 set_fs(old_fs);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700155 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 buf[n] = '\0';
157 mconsole_reply(req, buf, 0, (n > 0));
158 }
159 else {
160 mconsole_reply(req, "Read of file failed",
161 1, 0);
162 goto out_free;
163 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700164 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 }
166 else mconsole_reply(req, "", 0, 0);
167
168 out_free:
169 kfree(buf);
170 out_fput:
171 fput(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 out: ;
173}
174#endif
175
176void mconsole_proc(struct mc_request *req)
177{
178 char path[64];
179 char *buf;
180 int len;
181 int fd;
182 int first_chunk = 1;
183 char *ptr = req->request.data;
184
185 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800186 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 snprintf(path, sizeof(path), "/proc/%s", ptr);
188
189 fd = sys_open(path, 0, 0);
190 if (fd < 0) {
191 mconsole_reply(req, "Failed to open file", 1, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700192 printk(KERN_ERR "open %s: %d\n",path,fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 goto out;
194 }
195
196 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700197 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
199 goto out_close;
200 }
201
202 for (;;) {
203 len = sys_read(fd, buf, PAGE_SIZE-1);
204 if (len < 0) {
205 mconsole_reply(req, "Read of file failed", 1, 0);
206 goto out_free;
207 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700208 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 if (first_chunk) {
210 mconsole_reply(req, "\n", 0, 1);
211 first_chunk = 0;
212 }
213 if (len == PAGE_SIZE-1) {
214 buf[len] = '\0';
215 mconsole_reply(req, buf, 0, 1);
216 } else {
217 buf[len] = '\0';
218 mconsole_reply(req, buf, 0, 0);
219 break;
220 }
221 }
222
223 out_free:
224 kfree(buf);
225 out_close:
226 sys_close(fd);
227 out:
228 /* nothing */;
229}
230
231#define UML_MCONSOLE_HELPTEXT \
232"Commands: \n\
233 version - Get kernel version \n\
234 help - Print this message \n\
235 halt - Halt UML \n\
236 reboot - Reboot UML \n\
237 config <dev>=<config> - Add a new device to UML; \n\
238 same syntax as command line \n\
239 config <dev> - Query the configuration of a device \n\
240 remove <dev> - Remove a device from UML \n\
241 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800242 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 stop - pause the UML; it will do nothing until it receives a 'go' \n\
244 go - continue the UML after a 'stop' \n\
245 log <string> - make UML enter <string> into the kernel log\n\
246 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700247 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248"
249
250void mconsole_help(struct mc_request *req)
251{
252 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
253}
254
255void mconsole_halt(struct mc_request *req)
256{
257 mconsole_reply(req, "", 0, 0);
258 machine_halt();
259}
260
261void mconsole_reboot(struct mc_request *req)
262{
263 mconsole_reply(req, "", 0, 0);
264 machine_restart(NULL);
265}
266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267void mconsole_cad(struct mc_request *req)
268{
269 mconsole_reply(req, "", 0, 0);
270 ctrl_alt_del();
271}
272
273void mconsole_go(struct mc_request *req)
274{
275 mconsole_reply(req, "Not stopped", 1, 0);
276}
277
278void mconsole_stop(struct mc_request *req)
279{
280 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
281 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100282 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800283 for (;;) {
284 if (!mconsole_get_request(req->originating_fd, req))
285 continue;
Al Viro3a512372006-10-24 11:15:29 +0100286 if (req->cmd->handler == mconsole_go)
287 break;
288 if (req->cmd->handler == mconsole_stop) {
289 mconsole_reply(req, "Already stopped", 1, 0);
290 continue;
291 }
292 if (req->cmd->handler == mconsole_sysrq) {
293 struct pt_regs *old_regs;
294 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
295 mconsole_sysrq(req);
296 set_irq_regs(old_regs);
297 continue;
298 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 (*req->cmd->handler)(req);
300 }
301 os_set_fd_block(req->originating_fd, 0);
302 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
303 mconsole_reply(req, "", 0, 0);
304}
305
Jeff Dike84f48d42007-02-10 01:44:01 -0800306static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800307static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309void mconsole_register_dev(struct mc_device *new)
310{
Jeff Dike84f48d42007-02-10 01:44:01 -0800311 spin_lock(&mc_devices_lock);
312 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800314 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315}
316
317static struct mc_device *mconsole_find_dev(char *name)
318{
319 struct list_head *ele;
320 struct mc_device *dev;
321
Jeff Dikeae2587e2007-10-16 01:26:57 -0700322 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700324 if (!strncmp(name, dev->name, strlen(dev->name)))
325 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700327 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
Jeff Dike02dea082006-03-31 02:30:08 -0800330#define UNPLUGGED_PER_PAGE \
331 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
332
333struct unplugged_pages {
334 struct list_head list;
335 void *pages[UNPLUGGED_PER_PAGE];
336};
337
Daniel Walkere98fa282008-02-04 22:31:27 -0800338static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800339static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800340static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800341static int unplug_index = UNPLUGGED_PER_PAGE;
342
Jeff Dikef28169d2007-02-10 01:43:53 -0800343static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800344{
345 unsigned long long diff;
346 int err = -EINVAL, i, add;
347 char *ret;
348
Jeff Dikeae2587e2007-10-16 01:26:57 -0700349 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800350 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800351 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800352 }
Jeff Dike02dea082006-03-31 02:30:08 -0800353
354 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700355 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800356 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700357 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800358 add = 1;
359 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800360 else {
361 *error_out = "Expected increment to start with '-' or '+'";
362 goto out;
363 }
Jeff Dike02dea082006-03-31 02:30:08 -0800364
365 str++;
366 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700367 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800368 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800369 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800370 }
Jeff Dike02dea082006-03-31 02:30:08 -0800371
372 diff /= PAGE_SIZE;
373
Daniel Walkere98fa282008-02-04 22:31:27 -0800374 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700375 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800376 struct unplugged_pages *unplugged;
377 void *addr;
378
Jeff Dikeae2587e2007-10-16 01:26:57 -0700379 if (add) {
380 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800381 break;
382
383 unplugged = list_entry(unplugged_pages.next,
384 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700385 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800386 addr = unplugged->pages[--unplug_index];
387 else {
388 list_del(&unplugged->list);
389 addr = unplugged;
390 unplug_index = UNPLUGGED_PER_PAGE;
391 }
392
393 free_page((unsigned long) addr);
394 unplugged_pages_count--;
395 }
396 else {
397 struct page *page;
398
399 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700400 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800401 break;
402
403 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700404 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800405 list_add(&unplugged->list, &unplugged_pages);
406 unplug_index = 0;
407 }
408 else {
409 struct list_head *entry = unplugged_pages.next;
410 addr = unplugged;
411
412 unplugged = list_entry(entry,
413 struct unplugged_pages,
414 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800415 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700416 if (err) {
417 printk(KERN_ERR "Failed to release "
418 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800419 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800420 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800421 }
422 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800423 }
424
425 unplugged_pages_count++;
426 }
427 }
428
429 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800430out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800431 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800432out:
433 return err;
434}
435
436static int mem_get_config(char *name, char *str, int size, char **error_out)
437{
438 char buf[sizeof("18446744073709551615")];
439 int len = 0;
440
441 sprintf(buf, "%ld", uml_physmem);
442 CONFIG_CHUNK(str, size, len, buf, 1);
443
444 return len;
445}
446
447static int mem_id(char **str, int *start_out, int *end_out)
448{
449 *start_out = 0;
450 *end_out = 0;
451
452 return 0;
453}
454
Jeff Dikef28169d2007-02-10 01:43:53 -0800455static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800456{
Jeff Dikef28169d2007-02-10 01:43:53 -0800457 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800458 return -EBUSY;
459}
460
461static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800462 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800463 .name = "mem",
464 .config = mem_config,
465 .get_config = mem_get_config,
466 .id = mem_id,
467 .remove = mem_remove,
468};
469
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700470static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800471{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700472 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800473 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700474 else printk(KERN_ERR "Can't release memory to the host - memory "
475 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800476 return 0;
477}
478
479__initcall(mem_mc_init);
480
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481#define CONFIG_BUF_SIZE 64
482
Jeff Diked50084a2006-01-06 00:18:50 -0800483static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 char **),
485 struct mc_request *req, char *name)
486{
487 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
488 int n, size;
489
Jeff Dikeae2587e2007-10-16 01:26:57 -0700490 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 mconsole_reply(req, "No get_config routine defined", 1, 0);
492 return;
493 }
494
495 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700496 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 buf = default_buf;
498
Jeff Dikeae2587e2007-10-16 01:26:57 -0700499 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700501 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 mconsole_reply(req, error, 1, 0);
503 goto out;
504 }
505
Jeff Dikeae2587e2007-10-16 01:26:57 -0700506 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 mconsole_reply(req, buf, 0, 0);
508 goto out;
509 }
510
Jeff Dikeae2587e2007-10-16 01:26:57 -0700511 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 kfree(buf);
513
514 size = n;
515 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700516 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
518 return;
519 }
520 }
521 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700522 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524}
525
526void mconsole_config(struct mc_request *req)
527{
528 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800529 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 int err;
531
532 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800533 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700535 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 mconsole_reply(req, "Bad configuration option", 1, 0);
537 return;
538 }
539
540 name = &ptr[strlen(dev->name)];
541 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700542 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 ptr++;
544
Jeff Dikeae2587e2007-10-16 01:26:57 -0700545 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800546 err = (*dev->config)(name, &error_string);
547 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
549 else mconsole_get_config(dev->get_config, req, name);
550}
551
552void mconsole_remove(struct mc_request *req)
553{
Jeff Diked50084a2006-01-06 00:18:50 -0800554 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700555 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800556 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700557 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800560 ptr = skip_spaces(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 remove option", 1, 0);
564 return;
565 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700566
Jeff Dike3a331a52006-01-06 00:19:05 -0800567 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700568
Jeff Dike3a331a52006-01-06 00:19:05 -0800569 err = 1;
570 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700571 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800572 err_msg = "Couldn't parse device number";
573 goto out;
574 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700575 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800576 sprintf(error, "Invalid device number - must be between "
577 "%d and %d", start, end);
578 err_msg = error;
579 goto out;
580 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700581
Jeff Dikef28169d2007-02-10 01:43:53 -0800582 err_msg = NULL;
583 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700584 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700585 case 0:
586 err_msg = "";
587 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800588 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700589 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800590 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800591 break;
592 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700593 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800594 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800595 break;
596 default:
597 break;
598 }
599out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700600 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601}
602
Jeff Dikef92afe52006-09-29 01:58:52 -0700603struct mconsole_output {
604 struct list_head list;
605 struct mc_request *req;
606};
607
Jeff Dike84f48d42007-02-10 01:44:01 -0800608static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800609static LIST_HEAD(clients);
610static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800611
612static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700613 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800614{
615 struct list_head *ele;
616 int n;
617
Jeff Dikeae2587e2007-10-16 01:26:57 -0700618 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800619 return;
620
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700621 while (len > 0) {
622 n = min((size_t) len, ARRAY_SIZE(console_buf));
623 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800624 string += n;
625 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800626
Jeff Dikeae2587e2007-10-16 01:26:57 -0700627 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700628 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800629
Jeff Dikef92afe52006-09-29 01:58:52 -0700630 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700631 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800632 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800633 }
634}
635
636static struct console mc_console = { .name = "mc",
637 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800638 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800639 .index = -1 };
640
641static int mc_add_console(void)
642{
643 register_console(&mc_console);
644 return 0;
645}
646
647late_initcall(mc_add_console);
648
649static void with_console(struct mc_request *req, void (*proc)(void *),
650 void *arg)
651{
Jeff Dikef92afe52006-09-29 01:58:52 -0700652 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800653 unsigned long flags;
654
Jeff Dikef92afe52006-09-29 01:58:52 -0700655 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800656 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800657 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800658 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800659
660 (*proc)(arg);
661
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700662 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800663
Jeff Dike84f48d42007-02-10 01:44:01 -0800664 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800665 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800666 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800667}
668
Jeff Dike4111b022006-01-06 00:19:05 -0800669#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700670
671#include <linux/sysrq.h>
672
Jeff Dike4111b022006-01-06 00:19:05 -0800673static void sysrq_proc(void *arg)
674{
675 char *op = arg;
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700676 handle_sysrq(*op);
Jeff Dike4111b022006-01-06 00:19:05 -0800677}
678
679void mconsole_sysrq(struct mc_request *req)
680{
681 char *ptr = req->request.data;
682
683 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800684 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800685
Jeff Dikeae2587e2007-10-16 01:26:57 -0700686 /*
687 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800688 * so in this case, we reply first.
689 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700690 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800691 mconsole_reply(req, "", 0, 0);
692
693 with_console(req, sysrq_proc, ptr);
694}
695#else
696void mconsole_sysrq(struct mc_request *req)
697{
698 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
699}
700#endif
701
Jeff Dike6f517d32006-01-06 00:19:04 -0800702static void stack_proc(void *arg)
703{
704 struct task_struct *from = current, *to = arg;
705
706 to->thread.saved_task = from;
707 switch_to(from, to, from);
708}
709
Jeff Dikeae2587e2007-10-16 01:26:57 -0700710/*
711 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700712 * Added by Allan Graves, Jeff Dike
713 * Dumps a stacks registers to the linux console.
714 * Usage stack <pid>.
715 */
Jeff Dike42fda662007-10-16 01:26:50 -0700716void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700717{
Jeff Dike3a331a52006-01-06 00:19:05 -0800718 char *ptr = req->request.data;
719 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700720 struct task_struct *to = NULL;
721
Jeff Dikeae2587e2007-10-16 01:26:57 -0700722 /*
723 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800724 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700725 * 2) Add a way to stack dump all pids.
726 */
727
Jeff Dike3a331a52006-01-06 00:19:05 -0800728 ptr += strlen("stack");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800729 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700730
Jeff Dikeae2587e2007-10-16 01:26:57 -0700731 /*
732 * Should really check for multiple pids or reject bad args here
733 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800734 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700735 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800736 mconsole_reply(req, "Please specify a pid", 1, 0);
737 return;
738 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700739
Jeff Dike827b3f62008-02-04 22:31:29 -0800740 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700741 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800742 mconsole_reply(req, "Couldn't find that pid", 1, 0);
743 return;
744 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800745 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700746}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747
Jeff Dikeae2587e2007-10-16 01:26:57 -0700748/*
749 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 * active.
751 */
Jeff Diked50084a2006-01-06 00:18:50 -0800752static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700754static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755{
756 /* long to avoid size mismatch warnings from gcc */
757 long sock;
758 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800759 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Jeff Dikeae2587e2007-10-16 01:26:57 -0700761 if (umid_file_name("mconsole", file, sizeof(file)))
762 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
764
765 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700766 if (sock < 0) {
767 printk(KERN_ERR "Failed to initialize management console\n");
768 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 }
Jeff Dike438ee672008-02-04 22:31:19 -0800770 if (os_set_fd_block(sock, 0))
771 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
773 register_reboot_notifier(&reboot_notifier);
774
775 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Yong Zhangc0b79a92011-09-22 16:58:46 +0800776 IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700778 if (err) {
779 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800780 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 }
782
Jeff Dikeae2587e2007-10-16 01:26:57 -0700783 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800784 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700785 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800787 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 strlen(mconsole_socket_name) + 1);
789 else printk(KERN_ERR "mconsole_setup failed to strdup "
790 "string\n");
791 }
792
Jeff Dikeae2587e2007-10-16 01:26:57 -0700793 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700795 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800796
797 out:
798 os_close_file(sock);
799 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800}
801
802__initcall(mconsole_init);
803
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800804static ssize_t mconsole_proc_write(struct file *file,
805 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806{
807 char *buf;
808
809 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700810 if (buf == NULL)
811 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Jeff Dikeae2587e2007-10-16 01:26:57 -0700813 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 count = -EFAULT;
815 goto out;
816 }
817
818 buf[count] = '\0';
819
820 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
821 out:
822 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700823 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824}
825
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800826static const struct file_operations mconsole_proc_fops = {
827 .owner = THIS_MODULE,
828 .write = mconsole_proc_write,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200829 .llseek = noop_llseek,
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800830};
831
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832static int create_proc_mconsole(void)
833{
834 struct proc_dir_entry *ent;
835
Jeff Dikeae2587e2007-10-16 01:26:57 -0700836 if (notify_socket == NULL)
837 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800839 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700840 if (ent == NULL) {
841 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
842 "failed\n");
843 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700845 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846}
847
848static DEFINE_SPINLOCK(notify_spinlock);
849
850void lock_notify(void)
851{
852 spin_lock(&notify_spinlock);
853}
854
855void unlock_notify(void)
856{
857 spin_unlock(&notify_spinlock);
858}
859
860__initcall(create_proc_mconsole);
861
Jeff Dike088bec42007-10-16 01:27:20 -0700862#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
864static int mconsole_setup(char *str)
865{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700866 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 str += strlen(NOTIFY);
868 notify_socket = str;
869 }
870 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700871 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872}
873
Jeff Dike088bec42007-10-16 01:27:20 -0700874__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876__uml_help(mconsole_setup,
877"mconsole=notify:<socket>\n"
878" Requests that the mconsole driver send a message to the named Unix\n"
879" socket containing the name of the mconsole socket. This also serves\n"
880" to notify outside processes when UML has booted far enough to respond\n"
881" to mconsole requests.\n\n"
882);
883
884static int notify_panic(struct notifier_block *self, unsigned long unused1,
885 void *ptr)
886{
887 char *message = ptr;
888
Jeff Dikeae2587e2007-10-16 01:26:57 -0700889 if (notify_socket == NULL)
890 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
Jeff Diked50084a2006-01-06 00:18:50 -0800892 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700894 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895}
896
897static struct notifier_block panic_exit_notifier = {
898 .notifier_call = notify_panic,
899 .next = NULL,
900 .priority = 1
901};
902
903static int add_notifier(void)
904{
Alan Sterne041c682006-03-27 01:16:30 -0800905 atomic_notifier_chain_register(&panic_notifier_list,
906 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700907 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908}
909
910__initcall(add_notifier);
911
912char *mconsole_notify_socket(void)
913{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700914 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915}
916
917EXPORT_SYMBOL(mconsole_notify_socket);