blob: de317d0c329486b57efb3568f7c4514393ac5677 [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{
127 struct nameidata nd;
Al Viro4ecf09f2009-12-24 00:44:44 -0500128 struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 struct file *file;
130 int n, err;
131 char *ptr = req->request.data, *buf;
Al Viro4ecf09f2009-12-24 00:44:44 -0500132 mm_segment_t old_fs = get_fs();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
134 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800135 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
Al Viro4ecf09f2009-12-24 00:44:44 -0500137 err = vfs_path_lookup(mnt->mnt_root, mnt, ptr, LOOKUP_FOLLOW, &nd);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700138 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 mconsole_reply(req, "Failed to look up file", 1, 0);
Al Viro4ecf09f2009-12-24 00:44:44 -0500140 goto out;
141 }
142
Al Viro8737c932009-12-24 06:47:55 -0500143 err = may_open(&nd.path, MAY_READ, O_RDONLY);
Al Viro4ecf09f2009-12-24 00:44:44 -0500144 if (result) {
145 mconsole_reply(req, "Failed to open file", 1, 0);
146 path_put(&nd.path);
147 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 }
149
David Howells745ca242008-11-14 10:39:22 +1100150 file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
151 current_cred());
Al Viro4ecf09f2009-12-24 00:44:44 -0500152 err = PTR_ERR(file);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700153 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 mconsole_reply(req, "Failed to open file", 1, 0);
Al Viro4ecf09f2009-12-24 00:44:44 -0500155 path_put(&nd.path);
156 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700160 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
162 goto out_fput;
163 }
164
Al Viro4ecf09f2009-12-24 00:44:44 -0500165 if (file->f_op->read) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 do {
Al Viro4ecf09f2009-12-24 00:44:44 -0500167 loff_t pos;
168 set_fs(KERNEL_DS);
169 n = vfs_read(file, buf, PAGE_SIZE - 1, &pos);
170 file_pos_write(file, pos);
171 set_fs(old_fs);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700172 if (n >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 buf[n] = '\0';
174 mconsole_reply(req, buf, 0, (n > 0));
175 }
176 else {
177 mconsole_reply(req, "Read of file failed",
178 1, 0);
179 goto out_free;
180 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700181 } while (n > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 }
183 else mconsole_reply(req, "", 0, 0);
184
185 out_free:
186 kfree(buf);
187 out_fput:
188 fput(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 out: ;
190}
191#endif
192
193void mconsole_proc(struct mc_request *req)
194{
195 char path[64];
196 char *buf;
197 int len;
198 int fd;
199 int first_chunk = 1;
200 char *ptr = req->request.data;
201
202 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800203 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 snprintf(path, sizeof(path), "/proc/%s", ptr);
205
206 fd = sys_open(path, 0, 0);
207 if (fd < 0) {
208 mconsole_reply(req, "Failed to open file", 1, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700209 printk(KERN_ERR "open %s: %d\n",path,fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 goto out;
211 }
212
213 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700214 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
216 goto out_close;
217 }
218
219 for (;;) {
220 len = sys_read(fd, buf, PAGE_SIZE-1);
221 if (len < 0) {
222 mconsole_reply(req, "Read of file failed", 1, 0);
223 goto out_free;
224 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700225 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 if (first_chunk) {
227 mconsole_reply(req, "\n", 0, 1);
228 first_chunk = 0;
229 }
230 if (len == PAGE_SIZE-1) {
231 buf[len] = '\0';
232 mconsole_reply(req, buf, 0, 1);
233 } else {
234 buf[len] = '\0';
235 mconsole_reply(req, buf, 0, 0);
236 break;
237 }
238 }
239
240 out_free:
241 kfree(buf);
242 out_close:
243 sys_close(fd);
244 out:
245 /* nothing */;
246}
247
248#define UML_MCONSOLE_HELPTEXT \
249"Commands: \n\
250 version - Get kernel version \n\
251 help - Print this message \n\
252 halt - Halt UML \n\
253 reboot - Reboot UML \n\
254 config <dev>=<config> - Add a new device to UML; \n\
255 same syntax as command line \n\
256 config <dev> - Query the configuration of a device \n\
257 remove <dev> - Remove a device from UML \n\
258 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800259 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 stop - pause the UML; it will do nothing until it receives a 'go' \n\
261 go - continue the UML after a 'stop' \n\
262 log <string> - make UML enter <string> into the kernel log\n\
263 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700264 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265"
266
267void mconsole_help(struct mc_request *req)
268{
269 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
270}
271
272void mconsole_halt(struct mc_request *req)
273{
274 mconsole_reply(req, "", 0, 0);
275 machine_halt();
276}
277
278void mconsole_reboot(struct mc_request *req)
279{
280 mconsole_reply(req, "", 0, 0);
281 machine_restart(NULL);
282}
283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284void mconsole_cad(struct mc_request *req)
285{
286 mconsole_reply(req, "", 0, 0);
287 ctrl_alt_del();
288}
289
290void mconsole_go(struct mc_request *req)
291{
292 mconsole_reply(req, "Not stopped", 1, 0);
293}
294
295void mconsole_stop(struct mc_request *req)
296{
297 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
298 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100299 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800300 for (;;) {
301 if (!mconsole_get_request(req->originating_fd, req))
302 continue;
Al Viro3a512372006-10-24 11:15:29 +0100303 if (req->cmd->handler == mconsole_go)
304 break;
305 if (req->cmd->handler == mconsole_stop) {
306 mconsole_reply(req, "Already stopped", 1, 0);
307 continue;
308 }
309 if (req->cmd->handler == mconsole_sysrq) {
310 struct pt_regs *old_regs;
311 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
312 mconsole_sysrq(req);
313 set_irq_regs(old_regs);
314 continue;
315 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 (*req->cmd->handler)(req);
317 }
318 os_set_fd_block(req->originating_fd, 0);
319 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
320 mconsole_reply(req, "", 0, 0);
321}
322
Jeff Dike84f48d42007-02-10 01:44:01 -0800323static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800324static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
326void mconsole_register_dev(struct mc_device *new)
327{
Jeff Dike84f48d42007-02-10 01:44:01 -0800328 spin_lock(&mc_devices_lock);
329 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800331 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332}
333
334static struct mc_device *mconsole_find_dev(char *name)
335{
336 struct list_head *ele;
337 struct mc_device *dev;
338
Jeff Dikeae2587e2007-10-16 01:26:57 -0700339 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700341 if (!strncmp(name, dev->name, strlen(dev->name)))
342 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700344 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
Jeff Dike02dea082006-03-31 02:30:08 -0800347#define UNPLUGGED_PER_PAGE \
348 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
349
350struct unplugged_pages {
351 struct list_head list;
352 void *pages[UNPLUGGED_PER_PAGE];
353};
354
Daniel Walkere98fa282008-02-04 22:31:27 -0800355static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800356static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800357static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800358static int unplug_index = UNPLUGGED_PER_PAGE;
359
Jeff Dikef28169d2007-02-10 01:43:53 -0800360static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800361{
362 unsigned long long diff;
363 int err = -EINVAL, i, add;
364 char *ret;
365
Jeff Dikeae2587e2007-10-16 01:26:57 -0700366 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800367 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800368 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800369 }
Jeff Dike02dea082006-03-31 02:30:08 -0800370
371 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700372 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800373 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700374 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800375 add = 1;
376 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800377 else {
378 *error_out = "Expected increment to start with '-' or '+'";
379 goto out;
380 }
Jeff Dike02dea082006-03-31 02:30:08 -0800381
382 str++;
383 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700384 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800385 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800386 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800387 }
Jeff Dike02dea082006-03-31 02:30:08 -0800388
389 diff /= PAGE_SIZE;
390
Daniel Walkere98fa282008-02-04 22:31:27 -0800391 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700392 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800393 struct unplugged_pages *unplugged;
394 void *addr;
395
Jeff Dikeae2587e2007-10-16 01:26:57 -0700396 if (add) {
397 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800398 break;
399
400 unplugged = list_entry(unplugged_pages.next,
401 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700402 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800403 addr = unplugged->pages[--unplug_index];
404 else {
405 list_del(&unplugged->list);
406 addr = unplugged;
407 unplug_index = UNPLUGGED_PER_PAGE;
408 }
409
410 free_page((unsigned long) addr);
411 unplugged_pages_count--;
412 }
413 else {
414 struct page *page;
415
416 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700417 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800418 break;
419
420 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700421 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800422 list_add(&unplugged->list, &unplugged_pages);
423 unplug_index = 0;
424 }
425 else {
426 struct list_head *entry = unplugged_pages.next;
427 addr = unplugged;
428
429 unplugged = list_entry(entry,
430 struct unplugged_pages,
431 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800432 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700433 if (err) {
434 printk(KERN_ERR "Failed to release "
435 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800436 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800437 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800438 }
439 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800440 }
441
442 unplugged_pages_count++;
443 }
444 }
445
446 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800447out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800448 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800449out:
450 return err;
451}
452
453static int mem_get_config(char *name, char *str, int size, char **error_out)
454{
455 char buf[sizeof("18446744073709551615")];
456 int len = 0;
457
458 sprintf(buf, "%ld", uml_physmem);
459 CONFIG_CHUNK(str, size, len, buf, 1);
460
461 return len;
462}
463
464static int mem_id(char **str, int *start_out, int *end_out)
465{
466 *start_out = 0;
467 *end_out = 0;
468
469 return 0;
470}
471
Jeff Dikef28169d2007-02-10 01:43:53 -0800472static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800473{
Jeff Dikef28169d2007-02-10 01:43:53 -0800474 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800475 return -EBUSY;
476}
477
478static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800479 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800480 .name = "mem",
481 .config = mem_config,
482 .get_config = mem_get_config,
483 .id = mem_id,
484 .remove = mem_remove,
485};
486
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700487static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800488{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700489 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800490 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700491 else printk(KERN_ERR "Can't release memory to the host - memory "
492 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800493 return 0;
494}
495
496__initcall(mem_mc_init);
497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498#define CONFIG_BUF_SIZE 64
499
Jeff Diked50084a2006-01-06 00:18:50 -0800500static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 char **),
502 struct mc_request *req, char *name)
503{
504 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
505 int n, size;
506
Jeff Dikeae2587e2007-10-16 01:26:57 -0700507 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 mconsole_reply(req, "No get_config routine defined", 1, 0);
509 return;
510 }
511
512 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700513 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 buf = default_buf;
515
Jeff Dikeae2587e2007-10-16 01:26:57 -0700516 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700518 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 mconsole_reply(req, error, 1, 0);
520 goto out;
521 }
522
Jeff Dikeae2587e2007-10-16 01:26:57 -0700523 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 mconsole_reply(req, buf, 0, 0);
525 goto out;
526 }
527
Jeff Dikeae2587e2007-10-16 01:26:57 -0700528 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 kfree(buf);
530
531 size = n;
532 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700533 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
535 return;
536 }
537 }
538 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700539 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541}
542
543void mconsole_config(struct mc_request *req)
544{
545 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800546 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 int err;
548
549 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800550 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700552 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 mconsole_reply(req, "Bad configuration option", 1, 0);
554 return;
555 }
556
557 name = &ptr[strlen(dev->name)];
558 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700559 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 ptr++;
561
Jeff Dikeae2587e2007-10-16 01:26:57 -0700562 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800563 err = (*dev->config)(name, &error_string);
564 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 }
566 else mconsole_get_config(dev->get_config, req, name);
567}
568
569void mconsole_remove(struct mc_request *req)
570{
Jeff Diked50084a2006-01-06 00:18:50 -0800571 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700572 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800573 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700574 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
576 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800577 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700579 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 mconsole_reply(req, "Bad remove option", 1, 0);
581 return;
582 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700583
Jeff Dike3a331a52006-01-06 00:19:05 -0800584 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700585
Jeff Dike3a331a52006-01-06 00:19:05 -0800586 err = 1;
587 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700588 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800589 err_msg = "Couldn't parse device number";
590 goto out;
591 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700592 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800593 sprintf(error, "Invalid device number - must be between "
594 "%d and %d", start, end);
595 err_msg = error;
596 goto out;
597 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700598
Jeff Dikef28169d2007-02-10 01:43:53 -0800599 err_msg = NULL;
600 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700601 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700602 case 0:
603 err_msg = "";
604 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800605 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700606 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800607 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800608 break;
609 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700610 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800611 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800612 break;
613 default:
614 break;
615 }
616out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700617 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618}
619
Jeff Dikef92afe52006-09-29 01:58:52 -0700620struct mconsole_output {
621 struct list_head list;
622 struct mc_request *req;
623};
624
Jeff Dike84f48d42007-02-10 01:44:01 -0800625static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800626static LIST_HEAD(clients);
627static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800628
629static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700630 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800631{
632 struct list_head *ele;
633 int n;
634
Jeff Dikeae2587e2007-10-16 01:26:57 -0700635 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800636 return;
637
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700638 while (len > 0) {
639 n = min((size_t) len, ARRAY_SIZE(console_buf));
640 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800641 string += n;
642 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800643
Jeff Dikeae2587e2007-10-16 01:26:57 -0700644 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700645 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800646
Jeff Dikef92afe52006-09-29 01:58:52 -0700647 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700648 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800649 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800650 }
651}
652
653static struct console mc_console = { .name = "mc",
654 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800655 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800656 .index = -1 };
657
658static int mc_add_console(void)
659{
660 register_console(&mc_console);
661 return 0;
662}
663
664late_initcall(mc_add_console);
665
666static void with_console(struct mc_request *req, void (*proc)(void *),
667 void *arg)
668{
Jeff Dikef92afe52006-09-29 01:58:52 -0700669 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800670 unsigned long flags;
671
Jeff Dikef92afe52006-09-29 01:58:52 -0700672 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800673 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800674 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800675 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800676
677 (*proc)(arg);
678
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700679 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800680
Jeff Dike84f48d42007-02-10 01:44:01 -0800681 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800682 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800683 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800684}
685
Jeff Dike4111b022006-01-06 00:19:05 -0800686#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700687
688#include <linux/sysrq.h>
689
Jeff Dike4111b022006-01-06 00:19:05 -0800690static void sysrq_proc(void *arg)
691{
692 char *op = arg;
Al Viro7bea96f2006-10-08 22:49:34 +0100693 handle_sysrq(*op, NULL);
Jeff Dike4111b022006-01-06 00:19:05 -0800694}
695
696void mconsole_sysrq(struct mc_request *req)
697{
698 char *ptr = req->request.data;
699
700 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800701 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800702
Jeff Dikeae2587e2007-10-16 01:26:57 -0700703 /*
704 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800705 * so in this case, we reply first.
706 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700707 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800708 mconsole_reply(req, "", 0, 0);
709
710 with_console(req, sysrq_proc, ptr);
711}
712#else
713void mconsole_sysrq(struct mc_request *req)
714{
715 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
716}
717#endif
718
Jeff Dike6f517d32006-01-06 00:19:04 -0800719static void stack_proc(void *arg)
720{
721 struct task_struct *from = current, *to = arg;
722
723 to->thread.saved_task = from;
724 switch_to(from, to, from);
725}
726
Jeff Dikeae2587e2007-10-16 01:26:57 -0700727/*
728 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700729 * Added by Allan Graves, Jeff Dike
730 * Dumps a stacks registers to the linux console.
731 * Usage stack <pid>.
732 */
Jeff Dike42fda662007-10-16 01:26:50 -0700733void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700734{
Jeff Dike3a331a52006-01-06 00:19:05 -0800735 char *ptr = req->request.data;
736 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700737 struct task_struct *to = NULL;
738
Jeff Dikeae2587e2007-10-16 01:26:57 -0700739 /*
740 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800741 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700742 * 2) Add a way to stack dump all pids.
743 */
744
Jeff Dike3a331a52006-01-06 00:19:05 -0800745 ptr += strlen("stack");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800746 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700747
Jeff Dikeae2587e2007-10-16 01:26:57 -0700748 /*
749 * Should really check for multiple pids or reject bad args here
750 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800751 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700752 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800753 mconsole_reply(req, "Please specify a pid", 1, 0);
754 return;
755 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700756
Jeff Dike827b3f62008-02-04 22:31:29 -0800757 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700758 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800759 mconsole_reply(req, "Couldn't find that pid", 1, 0);
760 return;
761 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800762 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700763}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700764
Jeff Dikeae2587e2007-10-16 01:26:57 -0700765/*
766 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 * active.
768 */
Jeff Diked50084a2006-01-06 00:18:50 -0800769static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700771static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772{
773 /* long to avoid size mismatch warnings from gcc */
774 long sock;
775 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800776 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
Jeff Dikeae2587e2007-10-16 01:26:57 -0700778 if (umid_file_name("mconsole", file, sizeof(file)))
779 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
781
782 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700783 if (sock < 0) {
784 printk(KERN_ERR "Failed to initialize management console\n");
785 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 }
Jeff Dike438ee672008-02-04 22:31:19 -0800787 if (os_set_fd_block(sock, 0))
788 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
790 register_reboot_notifier(&reboot_notifier);
791
792 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Thomas Gleixnerbd6aa652006-07-01 19:29:27 -0700793 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700795 if (err) {
796 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800797 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 }
799
Jeff Dikeae2587e2007-10-16 01:26:57 -0700800 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800801 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700802 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800804 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 strlen(mconsole_socket_name) + 1);
806 else printk(KERN_ERR "mconsole_setup failed to strdup "
807 "string\n");
808 }
809
Jeff Dikeae2587e2007-10-16 01:26:57 -0700810 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700812 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800813
814 out:
815 os_close_file(sock);
816 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817}
818
819__initcall(mconsole_init);
820
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800821static ssize_t mconsole_proc_write(struct file *file,
822 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823{
824 char *buf;
825
826 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700827 if (buf == NULL)
828 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Jeff Dikeae2587e2007-10-16 01:26:57 -0700830 if (copy_from_user(buf, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 count = -EFAULT;
832 goto out;
833 }
834
835 buf[count] = '\0';
836
837 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
838 out:
839 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700840 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841}
842
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800843static const struct file_operations mconsole_proc_fops = {
844 .owner = THIS_MODULE,
845 .write = mconsole_proc_write,
846};
847
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848static int create_proc_mconsole(void)
849{
850 struct proc_dir_entry *ent;
851
Jeff Dikeae2587e2007-10-16 01:26:57 -0700852 if (notify_socket == NULL)
853 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800855 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700856 if (ent == NULL) {
857 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
858 "failed\n");
859 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700861 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862}
863
864static DEFINE_SPINLOCK(notify_spinlock);
865
866void lock_notify(void)
867{
868 spin_lock(&notify_spinlock);
869}
870
871void unlock_notify(void)
872{
873 spin_unlock(&notify_spinlock);
874}
875
876__initcall(create_proc_mconsole);
877
Jeff Dike088bec42007-10-16 01:27:20 -0700878#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
880static int mconsole_setup(char *str)
881{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700882 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 str += strlen(NOTIFY);
884 notify_socket = str;
885 }
886 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700887 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888}
889
Jeff Dike088bec42007-10-16 01:27:20 -0700890__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
892__uml_help(mconsole_setup,
893"mconsole=notify:<socket>\n"
894" Requests that the mconsole driver send a message to the named Unix\n"
895" socket containing the name of the mconsole socket. This also serves\n"
896" to notify outside processes when UML has booted far enough to respond\n"
897" to mconsole requests.\n\n"
898);
899
900static int notify_panic(struct notifier_block *self, unsigned long unused1,
901 void *ptr)
902{
903 char *message = ptr;
904
Jeff Dikeae2587e2007-10-16 01:26:57 -0700905 if (notify_socket == NULL)
906 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Jeff Diked50084a2006-01-06 00:18:50 -0800908 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700910 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911}
912
913static struct notifier_block panic_exit_notifier = {
914 .notifier_call = notify_panic,
915 .next = NULL,
916 .priority = 1
917};
918
919static int add_notifier(void)
920{
Alan Sterne041c682006-03-27 01:16:30 -0800921 atomic_notifier_chain_register(&panic_notifier_list,
922 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700923 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924}
925
926__initcall(add_notifier);
927
928char *mconsole_notify_socket(void)
929{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700930 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931}
932
933EXPORT_SYMBOL(mconsole_notify_socket);